SPIRV-Tools/test/opt/ir_builder.cpp
Stephen McGroarty 20b8cdb7c6 Make IR builder use the type manager for constants
This change makes the IR builder use the type manager to generate
OpTypeInts when creating OpConstants. This avoids dangling references
being stored by the created OpConstants.
2018-02-27 12:59:26 -05:00

416 lines
13 KiB
C++

// Copyright (c) 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <algorithm>
#include <vector>
#ifdef SPIRV_EFFCEE
#include "effcee/effcee.h"
#endif
#include "opt/basic_block.h"
#include "opt/ir_builder.h"
#include "opt/build_module.h"
#include "opt/instruction.h"
#include "opt/type_manager.h"
#include "spirv-tools/libspirv.hpp"
namespace {
using namespace spvtools;
using ir::IRContext;
using Analysis = IRContext::Analysis;
#ifdef SPIRV_EFFCEE
using IRBuilderTest = ::testing::Test;
bool Validate(const std::vector<uint32_t>& bin) {
spv_target_env target_env = SPV_ENV_UNIVERSAL_1_2;
spv_context spvContext = spvContextCreate(target_env);
spv_diagnostic diagnostic = nullptr;
spv_const_binary_t binary = {bin.data(), bin.size()};
spv_result_t error = spvValidate(spvContext, &binary, &diagnostic);
if (error != 0) spvDiagnosticPrint(diagnostic);
spvDiagnosticDestroy(diagnostic);
spvContextDestroy(spvContext);
return error == 0;
}
void Match(const std::string& original, ir::IRContext* context,
bool do_validation = true) {
std::vector<uint32_t> bin;
context->module()->ToBinary(&bin, true);
if (do_validation) {
EXPECT_TRUE(Validate(bin));
}
std::string assembly;
SpirvTools tools(SPV_ENV_UNIVERSAL_1_2);
EXPECT_TRUE(
tools.Disassemble(bin, &assembly, SpirvTools::kDefaultDisassembleOption))
<< "Disassembling failed for shader:\n"
<< assembly << std::endl;
auto match_result = effcee::Match(assembly, original);
EXPECT_EQ(effcee::Result::Status::Ok, match_result.status())
<< match_result.message() << "\nChecking result:\n"
<< assembly;
}
TEST_F(IRBuilderTest, TestInsnAddition) {
const std::string text = R"(
; CHECK: %18 = OpLabel
; CHECK: OpPhi %int %int_0 %14
; CHECK: OpPhi %bool %16 %14
; CHECK: OpBranch %17
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main" %3
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 330
OpName %2 "main"
OpName %4 "i"
OpName %3 "c"
OpDecorate %3 Location 0
%5 = OpTypeVoid
%6 = OpTypeFunction %5
%7 = OpTypeInt 32 1
%8 = OpTypePointer Function %7
%9 = OpConstant %7 0
%10 = OpTypeBool
%11 = OpTypeFloat 32
%12 = OpTypeVector %11 4
%13 = OpTypePointer Output %12
%3 = OpVariable %13 Output
%2 = OpFunction %5 None %6
%14 = OpLabel
%4 = OpVariable %8 Function
OpStore %4 %9
%15 = OpLoad %7 %4
%16 = OpINotEqual %10 %15 %9
OpSelectionMerge %17 None
OpBranchConditional %16 %18 %17
%18 = OpLabel
OpBranch %17
%17 = OpLabel
OpReturn
OpFunctionEnd
)";
{
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
ir::BasicBlock* bb = context->cfg()->block(18);
// Build managers.
context->get_def_use_mgr();
context->get_instr_block(nullptr);
opt::InstructionBuilder builder(context.get(), &*bb->begin());
ir::Instruction* phi1 = builder.AddPhi(7, {9, 14});
ir::Instruction* phi2 = builder.AddPhi(10, {16, 14});
// Make sure the InstructionBuilder did not update the def/use manager.
EXPECT_EQ(context->get_def_use_mgr()->GetDef(phi1->result_id()), nullptr);
EXPECT_EQ(context->get_def_use_mgr()->GetDef(phi2->result_id()), nullptr);
EXPECT_EQ(context->get_instr_block(phi1), nullptr);
EXPECT_EQ(context->get_instr_block(phi2), nullptr);
Match(text, context.get());
}
{
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
// Build managers.
context->get_def_use_mgr();
context->get_instr_block(nullptr);
ir::BasicBlock* bb = context->cfg()->block(18);
opt::InstructionBuilder builder(
context.get(), &*bb->begin(),
ir::IRContext::kAnalysisDefUse |
ir::IRContext::kAnalysisInstrToBlockMapping);
ir::Instruction* phi1 = builder.AddPhi(7, {9, 14});
ir::Instruction* phi2 = builder.AddPhi(10, {16, 14});
// Make sure InstructionBuilder updated the def/use manager
EXPECT_NE(context->get_def_use_mgr()->GetDef(phi1->result_id()), nullptr);
EXPECT_NE(context->get_def_use_mgr()->GetDef(phi2->result_id()), nullptr);
EXPECT_NE(context->get_instr_block(phi1), nullptr);
EXPECT_NE(context->get_instr_block(phi2), nullptr);
Match(text, context.get());
}
}
TEST_F(IRBuilderTest, TestCondBranchAddition) {
const std::string text = R"(
; CHECK: %main = OpFunction %void None %6
; CHECK-NEXT: %15 = OpLabel
; CHECK-NEXT: OpSelectionMerge %13 None
; CHECK-NEXT: OpBranchConditional %true %14 %13
; CHECK-NEXT: %14 = OpLabel
; CHECK-NEXT: OpBranch %13
; CHECK-NEXT: %13 = OpLabel
; CHECK-NEXT: OpReturn
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main" %3
OpExecutionMode %2 OriginUpperLeft
OpSource GLSL 330
OpName %2 "main"
OpName %4 "i"
OpName %3 "c"
OpDecorate %3 Location 0
%5 = OpTypeVoid
%6 = OpTypeFunction %5
%7 = OpTypeBool
%8 = OpTypePointer Function %7
%9 = OpConstantTrue %7
%10 = OpTypeFloat 32
%11 = OpTypeVector %10 4
%12 = OpTypePointer Output %11
%3 = OpVariable %12 Output
%4 = OpVariable %8 Private
%2 = OpFunction %5 None %6
%13 = OpLabel
OpReturn
OpFunctionEnd
)";
{
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
ir::Function& fn = *context->module()->begin();
ir::BasicBlock& bb_merge = *fn.begin();
fn.begin().InsertBefore(std::unique_ptr<ir::BasicBlock>(
new ir::BasicBlock(std::unique_ptr<ir::Instruction>(new ir::Instruction(
context.get(), SpvOpLabel, 0, context->TakeNextId(), {})))));
ir::BasicBlock& bb_true = *fn.begin();
{
opt::InstructionBuilder builder(context.get(), &*bb_true.begin());
builder.AddBranch(bb_merge.id());
}
fn.begin().InsertBefore(std::unique_ptr<ir::BasicBlock>(
new ir::BasicBlock(std::unique_ptr<ir::Instruction>(new ir::Instruction(
context.get(), SpvOpLabel, 0, context->TakeNextId(), {})))));
ir::BasicBlock& bb_cond = *fn.begin();
opt::InstructionBuilder builder(context.get(), &bb_cond);
// This also test consecutive instruction insertion: merge selection +
// branch.
builder.AddConditionalBranch(9, bb_true.id(), bb_merge.id(), bb_merge.id());
Match(text, context.get());
}
}
TEST_F(IRBuilderTest, AddSelect) {
const std::string text = R"(
; CHECK: [[bool:%\w+]] = OpTypeBool
; CHECK: [[uint:%\w+]] = OpTypeInt 32 0
; CHECK: [[true:%\w+]] = OpConstantTrue [[bool]]
; CHECK: [[u0:%\w+]] = OpConstant [[uint]] 0
; CHECK: [[u1:%\w+]] = OpConstant [[uint]] 1
; CHECK: OpSelect [[uint]] [[true]] [[u0]] [[u1]]
OpCapability Kernel
OpCapability Linkage
OpMemoryModel Logical OpenCL
%1 = OpTypeVoid
%2 = OpTypeBool
%3 = OpTypeInt 32 0
%4 = OpConstantTrue %2
%5 = OpConstant %3 0
%6 = OpConstant %3 1
%7 = OpTypeFunction %1
%8 = OpFunction %1 None %7
%9 = OpLabel
OpReturn
OpFunctionEnd
)";
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
EXPECT_NE(nullptr, context);
opt::InstructionBuilder builder(
context.get(), &*context->module()->begin()->begin()->begin());
EXPECT_NE(nullptr, builder.AddSelect(3u, 4u, 5u, 6u));
Match(text, context.get());
}
TEST_F(IRBuilderTest, AddCompositeConstruct) {
const std::string text = R"(
; CHECK: [[uint:%\w+]] = OpTypeInt
; CHECK: [[u0:%\w+]] = OpConstant [[uint]] 0
; CHECK: [[u1:%\w+]] = OpConstant [[uint]] 1
; CHECK: [[struct:%\w+]] = OpTypeStruct [[uint]] [[uint]] [[uint]] [[uint]]
; CHECK: OpCompositeConstruct [[struct]] [[u0]] [[u1]] [[u1]] [[u0]]
OpCapability Kernel
OpCapability Linkage
OpMemoryModel Logical OpenCL
%1 = OpTypeVoid
%2 = OpTypeInt 32 0
%3 = OpConstant %2 0
%4 = OpConstant %2 1
%5 = OpTypeStruct %2 %2 %2 %2
%6 = OpTypeFunction %1
%7 = OpFunction %1 None %6
%8 = OpLabel
OpReturn
OpFunctionEnd
)";
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
EXPECT_NE(nullptr, context);
opt::InstructionBuilder builder(
context.get(), &*context->module()->begin()->begin()->begin());
std::vector<uint32_t> ids = {3u, 4u, 4u, 3u};
EXPECT_NE(nullptr, builder.AddCompositeConstruct(5u, ids));
Match(text, context.get());
}
TEST_F(IRBuilderTest, ConstantAdder) {
const std::string text = R"(
; CHECK: [[uint:%\w+]] = OpTypeInt 32 0
; CHECK: OpConstant [[uint]] 13
; CHECK: [[sint:%\w+]] = OpTypeInt 32 1
; CHECK: OpConstant [[sint]] -1
; CHECK: OpConstant [[uint]] 1
; CHECK: OpConstant [[sint]] 34
; CHECK: OpConstant [[uint]] 0
; CHECK: OpConstant [[sint]] 0
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpFunction %1 None %2
%4 = OpLabel
OpReturn
OpFunctionEnd
)";
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
EXPECT_NE(nullptr, context);
opt::InstructionBuilder builder(
context.get(), &*context->module()->begin()->begin()->begin());
EXPECT_NE(nullptr, builder.Add32BitUnsignedIntegerConstant(13));
EXPECT_NE(nullptr, builder.Add32BitSignedIntegerConstant(-1));
// Try adding the same constants again to make sure they aren't added.
EXPECT_NE(nullptr, builder.Add32BitUnsignedIntegerConstant(13));
EXPECT_NE(nullptr, builder.Add32BitSignedIntegerConstant(-1));
// Try adding different constants to make sure the type is reused.
EXPECT_NE(nullptr, builder.Add32BitUnsignedIntegerConstant(1));
EXPECT_NE(nullptr, builder.Add32BitSignedIntegerConstant(34));
// Try adding 0 as both signed and unsigned.
EXPECT_NE(nullptr, builder.Add32BitUnsignedIntegerConstant(0));
EXPECT_NE(nullptr, builder.Add32BitSignedIntegerConstant(0));
Match(text, context.get());
}
TEST_F(IRBuilderTest, ConstantAdderTypeAlreadyExists) {
const std::string text = R"(
; CHECK: OpConstant %uint 13
; CHECK: OpConstant %int -1
; CHECK: OpConstant %uint 1
; CHECK: OpConstant %int 34
; CHECK: OpConstant %uint 0
; CHECK: OpConstant %int 0
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
%1 = OpTypeVoid
%uint = OpTypeInt 32 0
%int = OpTypeInt 32 1
%4 = OpTypeFunction %1
%5 = OpFunction %1 None %4
%6 = OpLabel
OpReturn
OpFunctionEnd
)";
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
EXPECT_NE(nullptr, context);
opt::InstructionBuilder builder(
context.get(), &*context->module()->begin()->begin()->begin());
ir::Instruction* const_1 = builder.Add32BitUnsignedIntegerConstant(13);
ir::Instruction* const_2 = builder.Add32BitSignedIntegerConstant(-1);
EXPECT_NE(nullptr, const_1);
EXPECT_NE(nullptr, const_2);
// Try adding the same constants again to make sure they aren't added.
EXPECT_EQ(const_1, builder.Add32BitUnsignedIntegerConstant(13));
EXPECT_EQ(const_2, builder.Add32BitSignedIntegerConstant(-1));
ir::Instruction* const_3 = builder.Add32BitUnsignedIntegerConstant(1);
ir::Instruction* const_4 = builder.Add32BitSignedIntegerConstant(34);
// Try adding different constants to make sure the type is reused.
EXPECT_NE(nullptr, const_3);
EXPECT_NE(nullptr, const_4);
ir::Instruction* const_5 = builder.Add32BitUnsignedIntegerConstant(0);
ir::Instruction* const_6 = builder.Add32BitSignedIntegerConstant(0);
// Try adding 0 as both signed and unsigned.
EXPECT_NE(nullptr, const_5);
EXPECT_NE(nullptr, const_6);
// They have the same value but different types so should be unique.
EXPECT_NE(const_5, const_6);
// Check the types are correct.
uint32_t type_id_unsigned = const_1->GetSingleWordOperand(0);
uint32_t type_id_signed = const_2->GetSingleWordOperand(0);
EXPECT_NE(type_id_unsigned, type_id_signed);
EXPECT_EQ(const_3->GetSingleWordOperand(0), type_id_unsigned);
EXPECT_EQ(const_5->GetSingleWordOperand(0), type_id_unsigned);
EXPECT_EQ(const_4->GetSingleWordOperand(0), type_id_signed);
EXPECT_EQ(const_6->GetSingleWordOperand(0), type_id_signed);
Match(text, context.get());
}
#endif // SPIRV_EFFCEE
} // anonymous namespace