mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-30 15:00:06 +00:00
20b8cdb7c6
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.
416 lines
13 KiB
C++
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
|