// Copyright (c) 2016 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 "source/opt/type_manager.h" #include #include #include #include "effcee/effcee.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "source/opt/build_module.h" #include "source/opt/instruction.h" #include "spirv-tools/libspirv.hpp" namespace spvtools { namespace opt { namespace analysis { namespace { bool Validate(const std::vector& 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, IRContext* context, bool do_validation = true) { std::vector 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; } std::vector> GenerateAllTypes() { // Types in this test case are only equal to themselves, nothing else. std::vector> types; // Void, Bool types.emplace_back(new Void()); auto* voidt = types.back().get(); types.emplace_back(new Bool()); auto* boolt = types.back().get(); // Integer types.emplace_back(new Integer(32, true)); auto* s32 = types.back().get(); types.emplace_back(new Integer(32, false)); types.emplace_back(new Integer(64, true)); types.emplace_back(new Integer(64, false)); auto* u64 = types.back().get(); // Float types.emplace_back(new Float(32)); auto* f32 = types.back().get(); types.emplace_back(new Float(64)); // Vector types.emplace_back(new Vector(s32, 2)); types.emplace_back(new Vector(s32, 3)); auto* v3s32 = types.back().get(); types.emplace_back(new Vector(u64, 4)); types.emplace_back(new Vector(f32, 3)); auto* v3f32 = types.back().get(); // Matrix types.emplace_back(new Matrix(v3s32, 3)); types.emplace_back(new Matrix(v3s32, 4)); types.emplace_back(new Matrix(v3f32, 4)); // Images types.emplace_back(new Image(s32, spv::Dim::Dim2D, 0, 0, 0, 0, spv::ImageFormat::Rg8, spv::AccessQualifier::ReadOnly)); auto* image1 = types.back().get(); types.emplace_back(new Image(s32, spv::Dim::Dim2D, 0, 1, 0, 0, spv::ImageFormat::Rg8, spv::AccessQualifier::ReadOnly)); types.emplace_back(new Image(s32, spv::Dim::Dim3D, 0, 1, 0, 0, spv::ImageFormat::Rg8, spv::AccessQualifier::ReadOnly)); types.emplace_back(new Image(voidt, spv::Dim::Dim3D, 0, 1, 0, 1, spv::ImageFormat::Rg8, spv::AccessQualifier::ReadWrite)); auto* image2 = types.back().get(); // Sampler types.emplace_back(new Sampler()); // Sampled Image types.emplace_back(new SampledImage(image1)); types.emplace_back(new SampledImage(image2)); // Array types.emplace_back(new Array(f32, Array::LengthInfo{100, {0, 100u}})); types.emplace_back(new Array(f32, Array::LengthInfo{42, {0, 42u}})); auto* a42f32 = types.back().get(); types.emplace_back(new Array(u64, Array::LengthInfo{24, {0, 24u}})); // RuntimeArray types.emplace_back(new RuntimeArray(v3f32)); types.emplace_back(new RuntimeArray(v3s32)); auto* rav3s32 = types.back().get(); // Struct types.emplace_back(new Struct(std::vector{s32})); types.emplace_back(new Struct(std::vector{s32, f32})); auto* sts32f32 = types.back().get(); types.emplace_back( new Struct(std::vector{u64, a42f32, rav3s32})); // Opaque types.emplace_back(new Opaque("")); types.emplace_back(new Opaque("hello")); types.emplace_back(new Opaque("world")); // Pointer types.emplace_back(new Pointer(f32, spv::StorageClass::Input)); types.emplace_back(new Pointer(sts32f32, spv::StorageClass::Function)); types.emplace_back(new Pointer(a42f32, spv::StorageClass::Function)); // Function types.emplace_back(new Function(voidt, {})); types.emplace_back(new Function(voidt, {boolt})); types.emplace_back(new Function(voidt, {boolt, s32})); types.emplace_back(new Function(s32, {boolt, s32})); // Event, Device Event, Reserve Id, Queue, types.emplace_back(new Event()); types.emplace_back(new DeviceEvent()); types.emplace_back(new ReserveId()); types.emplace_back(new Queue()); // Pipe, Forward Pointer, PipeStorage, NamedBarrier, AccelerationStructureNV, // CooperativeMatrixNV types.emplace_back(new Pipe(spv::AccessQualifier::ReadWrite)); types.emplace_back(new Pipe(spv::AccessQualifier::ReadOnly)); types.emplace_back(new ForwardPointer(1, spv::StorageClass::Input)); types.emplace_back(new ForwardPointer(2, spv::StorageClass::Input)); types.emplace_back(new ForwardPointer(2, spv::StorageClass::Uniform)); types.emplace_back(new PipeStorage()); types.emplace_back(new NamedBarrier()); types.emplace_back(new AccelerationStructureNV()); types.emplace_back(new CooperativeMatrixNV(f32, 24, 24, 24)); types.emplace_back(new CooperativeMatrixKHR(f32, 8, 8, 8, 1002)); types.emplace_back(new RayQueryKHR()); types.emplace_back(new HitObjectNV()); return types; } TEST(TypeManager, GenerateAllTypesGeneratesAllTypes) { std::set generated_types; for (auto& type : GenerateAllTypes()) { generated_types.insert(type->kind()); } std::vector all_types; for (uint32_t kind = 0; kind != Type::Kind::kLast; ++kind) { all_types.push_back(static_cast(kind)); } EXPECT_THAT(generated_types, testing::UnorderedElementsAreArray(all_types)); } TEST(TypeManager, TypeStrings) { const std::string text = R"( OpDecorate %spec_const_with_id SpecId 99 OpTypeForwardPointer %p Uniform %void = OpTypeVoid %bool = OpTypeBool %u32 = OpTypeInt 32 0 %id4 = OpConstant %u32 4 %s32 = OpTypeInt 32 1 %f64 = OpTypeFloat 64 %v3u32 = OpTypeVector %u32 3 %m3x3 = OpTypeMatrix %v3u32 3 %img1 = OpTypeImage %s32 Cube 0 1 1 0 R32f ReadWrite %img2 = OpTypeImage %s32 Cube 0 1 1 0 R32f %sampler = OpTypeSampler %si1 = OpTypeSampledImage %img1 %si2 = OpTypeSampledImage %img2 %a5u32 = OpTypeArray %u32 %id4 %af64 = OpTypeRuntimeArray %f64 %st1 = OpTypeStruct %u32 %st2 = OpTypeStruct %f64 %s32 %v3u32 %opaque1 = OpTypeOpaque "" %opaque2 = OpTypeOpaque "opaque" %p = OpTypePointer Uniform %st1 %f = OpTypeFunction %void %u32 %u32 %event = OpTypeEvent %de = OpTypeDeviceEvent %ri = OpTypeReserveId %queue = OpTypeQueue %pipe = OpTypePipe ReadOnly %ps = OpTypePipeStorage %nb = OpTypeNamedBarrier %rtacc = OpTypeAccelerationStructureNV ; Set up other kinds of OpTypeArray %s64 = OpTypeInt 64 1 ; ID 32 %spec_const_without_id = OpSpecConstant %s32 44 %spec_const_with_id = OpSpecConstant %s32 42 ;; This is ID 1 %long_constant = OpConstant %s64 5000000000 %spec_const_op = OpSpecConstantOp %s32 IAdd %id4 %id4 ; ID 35 %arr_spec_const_without_id = OpTypeArray %s32 %spec_const_without_id %arr_spec_const_with_id = OpTypeArray %s32 %spec_const_with_id %arr_long_constant = OpTypeArray %s32 %long_constant %arr_spec_const_op = OpTypeArray %s32 %spec_const_op %cm = OpTypeCooperativeMatrixNV %f64 %id4 %id4 %id4 %id2 = OpConstant %u32 2 %cmkhr = OpTypeCooperativeMatrixKHR %f64 %id4 %id4 %id4 %id2 )"; std::vector> type_id_strs = { {3, "void"}, {4, "bool"}, {5, "uint32"}, // Id 6 is used by the constant. {7, "sint32"}, {8, "float64"}, {9, ""}, {10, "<, 3>"}, {11, "image(sint32, 3, 0, 1, 1, 0, 3, 2)"}, {12, "image(sint32, 3, 0, 1, 1, 0, 3, 0)"}, {13, "sampler"}, {14, "sampled_image(image(sint32, 3, 0, 1, 1, 0, 3, 2))"}, {15, "sampled_image(image(sint32, 3, 0, 1, 1, 0, 3, 0))"}, {16, "[uint32, id(6), words(0,4)]"}, {17, "[float64]"}, {18, "{uint32}"}, {19, "{float64, sint32, }"}, {20, "opaque('')"}, {21, "opaque('opaque')"}, {2, "{uint32} 2*"}, // Include storage class number {22, "(uint32, uint32) -> void"}, {23, "event"}, {24, "device_event"}, {25, "reserve_id"}, {26, "queue"}, {27, "pipe(0)"}, {28, "pipe_storage"}, {29, "named_barrier"}, {30, "accelerationStructureNV"}, {31, "sint64"}, {35, "[sint32, id(32), words(0,44)]"}, {36, "[sint32, id(1), words(1,99,42)]"}, {37, "[sint32, id(33), words(0,705032704,1)]"}, {38, "[sint32, id(34), words(2,34)]"}, {39, ""}, {41, ""}, }; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text); ASSERT_NE(nullptr, context.get()); // It assembled TypeManager manager(nullptr, context.get()); EXPECT_EQ(type_id_strs.size(), manager.NumTypes()); for (const auto& p : type_id_strs) { ASSERT_NE(nullptr, manager.GetType(p.first)); EXPECT_EQ(p.second, manager.GetType(p.first)->str()) << " id is " << p.first; EXPECT_EQ(p.first, manager.GetId(manager.GetType(p.first))); } } TEST(TypeManager, StructWithFwdPtr) { const std::string text = R"( OpCapability Addresses OpCapability Kernel %1 = OpExtInstImport "OpenCL.std" OpMemoryModel Physical64 OpenCL OpEntryPoint Kernel %7 "test" OpSource OpenCL_C 102000 OpDecorate %11 FuncParamAttr NoCapture %11 = OpDecorationGroup OpGroupDecorate %11 %8 %9 OpTypeForwardPointer %100 CrossWorkgroup %void = OpTypeVoid %150 = OpTypeStruct %100 %100 = OpTypePointer CrossWorkgroup %150 %6 = OpTypeFunction %void %100 %100 %7 = OpFunction %void Pure %6 %8 = OpFunctionParameter %100 %9 = OpFunctionParameter %100 %10 = OpLabel OpReturn OpFunctionEnd )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); TypeManager manager(nullptr, context.get()); Type* p100 = manager.GetType(100); Type* s150 = manager.GetType(150); EXPECT_TRUE(p100->AsPointer()); EXPECT_EQ(p100->AsPointer()->pointee_type(), s150); EXPECT_TRUE(s150->AsStruct()); EXPECT_EQ(s150->AsStruct()->element_types()[0], p100); } TEST(TypeManager, CircularFwdPtr) { const std::string text = R"( OpCapability Addresses OpCapability Kernel %1 = OpExtInstImport "OpenCL.std" OpMemoryModel Physical64 OpenCL OpEntryPoint Kernel %7 "test" OpSource OpenCL_C 102000 OpDecorate %11 FuncParamAttr NoCapture %11 = OpDecorationGroup OpGroupDecorate %11 %8 %9 OpTypeForwardPointer %100 CrossWorkgroup OpTypeForwardPointer %200 CrossWorkgroup %void = OpTypeVoid %int = OpTypeInt 32 0 %float = OpTypeFloat 32 %150 = OpTypeStruct %200 %int %250 = OpTypeStruct %100 %float %100 = OpTypePointer CrossWorkgroup %150 %200 = OpTypePointer CrossWorkgroup %250 %6 = OpTypeFunction %void %100 %200 %7 = OpFunction %void Pure %6 %8 = OpFunctionParameter %100 %9 = OpFunctionParameter %200 %10 = OpLabel OpReturn OpFunctionEnd )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); TypeManager manager(nullptr, context.get()); Type* p100 = manager.GetType(100); Type* s150 = manager.GetType(150); Type* p200 = manager.GetType(200); Type* s250 = manager.GetType(250); EXPECT_TRUE(p100->AsPointer()); EXPECT_EQ(p100->AsPointer()->pointee_type(), s150); EXPECT_TRUE(p200->AsPointer()); EXPECT_EQ(p200->AsPointer()->pointee_type(), s250); EXPECT_TRUE(s150->AsStruct()); EXPECT_EQ(s150->AsStruct()->element_types()[0], p200); EXPECT_TRUE(s250->AsStruct()); EXPECT_EQ(s250->AsStruct()->element_types()[0], p100); } TEST(TypeManager, IsomorphicStructWithFwdPtr) { const std::string text = R"( OpCapability Addresses OpCapability Kernel %1 = OpExtInstImport "OpenCL.std" OpMemoryModel Physical64 OpenCL OpEntryPoint Kernel %7 "test" OpSource OpenCL_C 102000 OpDecorate %11 FuncParamAttr NoCapture %11 = OpDecorationGroup OpGroupDecorate %11 %8 %9 OpTypeForwardPointer %100 CrossWorkgroup OpTypeForwardPointer %200 CrossWorkgroup %void = OpTypeVoid %_struct_1 = OpTypeStruct %100 %_struct_2 = OpTypeStruct %200 %100 = OpTypePointer CrossWorkgroup %_struct_1 %200 = OpTypePointer CrossWorkgroup %_struct_2 %6 = OpTypeFunction %void %100 %200 %7 = OpFunction %void Pure %6 %8 = OpFunctionParameter %100 %9 = OpFunctionParameter %200 %10 = OpLabel OpReturn OpFunctionEnd )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); TypeManager manager(nullptr, context.get()); EXPECT_EQ(manager.GetType(100), manager.GetType(200)); } TEST(TypeManager, IsomorphicCircularFwdPtr) { const std::string text = R"( OpCapability Addresses OpCapability Kernel %1 = OpExtInstImport "OpenCL.std" OpMemoryModel Physical64 OpenCL OpEntryPoint Kernel %7 "test" OpSource OpenCL_C 102000 OpDecorate %11 FuncParamAttr NoCapture %11 = OpDecorationGroup OpGroupDecorate %11 %8 %9 OpTypeForwardPointer %100 CrossWorkgroup OpTypeForwardPointer %200 CrossWorkgroup OpTypeForwardPointer %300 CrossWorkgroup OpTypeForwardPointer %400 CrossWorkgroup %void = OpTypeVoid %int = OpTypeInt 32 0 %float = OpTypeFloat 32 %150 = OpTypeStruct %200 %int %250 = OpTypeStruct %100 %float %350 = OpTypeStruct %400 %int %450 = OpTypeStruct %300 %float %100 = OpTypePointer CrossWorkgroup %150 %200 = OpTypePointer CrossWorkgroup %250 %300 = OpTypePointer CrossWorkgroup %350 %400 = OpTypePointer CrossWorkgroup %450 %6 = OpTypeFunction %void %100 %200 %7 = OpFunction %void Pure %6 %8 = OpFunctionParameter %100 %9 = OpFunctionParameter %200 %10 = OpLabel OpReturn OpFunctionEnd )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); TypeManager manager(nullptr, context.get()); Type* p100 = manager.GetType(100); Type* p300 = manager.GetType(300); EXPECT_EQ(p100, p300); Type* p200 = manager.GetType(200); Type* p400 = manager.GetType(400); EXPECT_EQ(p200, p400); Type* p150 = manager.GetType(150); Type* p350 = manager.GetType(350); EXPECT_EQ(p150, p350); Type* p250 = manager.GetType(250); Type* p450 = manager.GetType(450); EXPECT_EQ(p250, p450); } TEST(TypeManager, PartialIsomorphicFwdPtr) { const std::string text = R"( OpCapability Addresses OpCapability Kernel %1 = OpExtInstImport "OpenCL.std" OpMemoryModel Physical64 OpenCL OpEntryPoint Kernel %7 "test" OpSource OpenCL_C 102000 OpDecorate %11 FuncParamAttr NoCapture %11 = OpDecorationGroup OpGroupDecorate %11 %8 %9 OpTypeForwardPointer %100 CrossWorkgroup OpTypeForwardPointer %200 CrossWorkgroup %void = OpTypeVoid %int = OpTypeInt 32 0 %float = OpTypeFloat 32 %150 = OpTypeStruct %200 %int %250 = OpTypeStruct %200 %int %100 = OpTypePointer CrossWorkgroup %150 %200 = OpTypePointer CrossWorkgroup %250 %6 = OpTypeFunction %void %100 %200 %7 = OpFunction %void Pure %6 %8 = OpFunctionParameter %100 %9 = OpFunctionParameter %200 %10 = OpLabel OpReturn OpFunctionEnd )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); TypeManager manager(nullptr, context.get()); Type* p100 = manager.GetType(100); Type* p200 = manager.GetType(200); EXPECT_EQ(p100->AsPointer()->pointee_type(), p200->AsPointer()->pointee_type()); } TEST(TypeManager, DecorationOnStruct) { const std::string text = R"( OpDecorate %struct1 Block OpDecorate %struct2 Block OpDecorate %struct3 Block OpDecorate %struct4 Block %u32 = OpTypeInt 32 0 ; id: 5 %f32 = OpTypeFloat 32 ; id: 6 %struct1 = OpTypeStruct %u32 %f32 ; base %struct2 = OpTypeStruct %f32 %u32 ; different member order %struct3 = OpTypeStruct %f32 ; different member list %struct4 = OpTypeStruct %u32 %f32 ; the same %struct7 = OpTypeStruct %f32 ; no decoration )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text); TypeManager manager(nullptr, context.get()); ASSERT_EQ(7u, manager.NumTypes()); // Make sure we get ids correct. ASSERT_EQ("uint32", manager.GetType(5)->str()); ASSERT_EQ("float32", manager.GetType(6)->str()); // Try all combinations of pairs. Expect to be the same type only when the // same id or (1, 4). for (const auto id1 : {1, 2, 3, 4, 7}) { for (const auto id2 : {1, 2, 3, 4, 7}) { if (id1 == id2 || (id1 == 1 && id2 == 4) || (id1 == 4 && id2 == 1)) { EXPECT_TRUE(manager.GetType(id1)->IsSame(manager.GetType(id2))) << "%struct" << id1 << " is expected to be the same as %struct" << id2; } else { EXPECT_FALSE(manager.GetType(id1)->IsSame(manager.GetType(id2))) << "%struct" << id1 << " is expected to be different with %struct" << id2; } } } } TEST(TypeManager, DecorationOnMember) { const std::string text = R"( OpMemberDecorate %struct1 0 Offset 0 OpMemberDecorate %struct2 0 Offset 0 OpMemberDecorate %struct3 0 Offset 0 OpMemberDecorate %struct4 0 Offset 0 OpMemberDecorate %struct5 1 Offset 0 OpMemberDecorate %struct6 0 Offset 4 OpDecorate %struct7 Block OpMemberDecorate %struct7 0 Offset 0 %u32 = OpTypeInt 32 0 ; id: 8 %f32 = OpTypeFloat 32 ; id: 9 %struct1 = OpTypeStruct %u32 %f32 ; base %struct2 = OpTypeStruct %f32 %u32 ; different member order %struct3 = OpTypeStruct %f32 ; different member list %struct4 = OpTypeStruct %u32 %f32 ; the same %struct5 = OpTypeStruct %u32 %f32 ; member decorate different field %struct6 = OpTypeStruct %u32 %f32 ; different member decoration parameter %struct7 = OpTypeStruct %u32 %f32 ; extra decoration on the struct %struct10 = OpTypeStruct %u32 %f32 ; no member decoration )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text); TypeManager manager(nullptr, context.get()); ASSERT_EQ(10u, manager.NumTypes()); // Make sure we get ids correct. ASSERT_EQ("uint32", manager.GetType(8)->str()); ASSERT_EQ("float32", manager.GetType(9)->str()); // Try all combinations of pairs. Expect to be the same type only when the // same id or (1, 4). for (const auto id1 : {1, 2, 3, 4, 5, 6, 7, 10}) { for (const auto id2 : {1, 2, 3, 4, 5, 6, 7, 10}) { if (id1 == id2 || (id1 == 1 && id2 == 4) || (id1 == 4 && id2 == 1)) { EXPECT_TRUE(manager.GetType(id1)->IsSame(manager.GetType(id2))) << "%struct" << id1 << " is expected to be the same as %struct" << id2; } else { EXPECT_FALSE(manager.GetType(id1)->IsSame(manager.GetType(id2))) << "%struct" << id1 << " is expected to be different with %struct" << id2; } } } } TEST(TypeManager, DecorationEmpty) { const std::string text = R"( OpDecorate %struct1 Block OpMemberDecorate %struct2 0 Offset 0 %u32 = OpTypeInt 32 0 ; id: 3 %f32 = OpTypeFloat 32 ; id: 4 %struct1 = OpTypeStruct %u32 %f32 %struct2 = OpTypeStruct %f32 %u32 %struct5 = OpTypeStruct %f32 )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text); TypeManager manager(nullptr, context.get()); ASSERT_EQ(5u, manager.NumTypes()); // Make sure we get ids correct. ASSERT_EQ("uint32", manager.GetType(3)->str()); ASSERT_EQ("float32", manager.GetType(4)->str()); // %struct1 with decoration on itself EXPECT_FALSE(manager.GetType(1)->decoration_empty()); // %struct2 with decoration on its member EXPECT_FALSE(manager.GetType(2)->decoration_empty()); EXPECT_TRUE(manager.GetType(3)->decoration_empty()); EXPECT_TRUE(manager.GetType(4)->decoration_empty()); // %struct5 has no decorations EXPECT_TRUE(manager.GetType(5)->decoration_empty()); } TEST(TypeManager, BeginEndForEmptyModule) { const std::string text = ""; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text); TypeManager manager(nullptr, context.get()); ASSERT_EQ(0u, manager.NumTypes()); EXPECT_EQ(manager.begin(), manager.end()); } TEST(TypeManager, BeginEnd) { const std::string text = R"( %void1 = OpTypeVoid %void2 = OpTypeVoid %bool = OpTypeBool %u32 = OpTypeInt 32 0 %f64 = OpTypeFloat 64 )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text); TypeManager manager(nullptr, context.get()); ASSERT_EQ(5u, manager.NumTypes()); EXPECT_NE(manager.begin(), manager.end()); for (const auto& t : manager) { switch (t.first) { case 1: case 2: EXPECT_EQ("void", t.second->str()); break; case 3: EXPECT_EQ("bool", t.second->str()); break; case 4: EXPECT_EQ("uint32", t.second->str()); break; case 5: EXPECT_EQ("float64", t.second->str()); break; default: EXPECT_TRUE(false && "unreachable"); break; } } } TEST(TypeManager, LookupType) { const std::string text = R"( %void = OpTypeVoid %uint = OpTypeInt 32 0 %int = OpTypeInt 32 1 %vec2 = OpTypeVector %int 2 )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); EXPECT_NE(context, nullptr); TypeManager manager(nullptr, context.get()); Void voidTy; EXPECT_EQ(manager.GetId(&voidTy), 1u); Integer uintTy(32, false); EXPECT_EQ(manager.GetId(&uintTy), 2u); Integer intTy(32, true); EXPECT_EQ(manager.GetId(&intTy), 3u); Integer intTy2(32, true); Vector vecTy(&intTy2, 2u); EXPECT_EQ(manager.GetId(&vecTy), 4u); } TEST(TypeManager, RemoveId) { const std::string text = R"( OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 %1 = OpTypeInt 32 0 %2 = OpTypeInt 32 1 )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); EXPECT_NE(context, nullptr); context->get_type_mgr()->RemoveId(1u); ASSERT_EQ(context->get_type_mgr()->GetType(1u), nullptr); ASSERT_NE(context->get_type_mgr()->GetType(2u), nullptr); context->get_type_mgr()->RemoveId(2u); ASSERT_EQ(context->get_type_mgr()->GetType(1u), nullptr); ASSERT_EQ(context->get_type_mgr()->GetType(2u), nullptr); } TEST(TypeManager, RemoveIdNonDuplicateAmbiguousType) { const std::string text = R"( OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 %1 = OpTypeInt 32 0 %2 = OpTypeStruct %1 )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); EXPECT_NE(context, nullptr); Integer u32(32, false); Struct st({&u32}); ASSERT_EQ(context->get_type_mgr()->GetId(&st), 2u); context->get_type_mgr()->RemoveId(2u); ASSERT_EQ(context->get_type_mgr()->GetType(2u), nullptr); ASSERT_EQ(context->get_type_mgr()->GetId(&st), 0u); } TEST(TypeManager, RemoveIdDuplicateAmbiguousType) { const std::string text = R"( OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 %1 = OpTypeInt 32 0 %2 = OpTypeStruct %1 %3 = OpTypeStruct %1 )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); EXPECT_NE(context, nullptr); Integer u32(32, false); Struct st({&u32}); uint32_t id = context->get_type_mgr()->GetId(&st); ASSERT_NE(id, 0u); uint32_t toRemove = id == 2u ? 2u : 3u; uint32_t toStay = id == 2u ? 3u : 2u; context->get_type_mgr()->RemoveId(toRemove); ASSERT_EQ(context->get_type_mgr()->GetType(toRemove), nullptr); ASSERT_EQ(context->get_type_mgr()->GetId(&st), toStay); } TEST(TypeManager, RemoveIdDoesntUnmapOtherTypes) { const std::string text = R"( OpCapability Shader OpMemoryModel Logical GLSL450 %1 = OpTypeInt 32 0 %2 = OpTypeStruct %1 %3 = OpTypeStruct %1 )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); EXPECT_NE(context, nullptr); Integer u32(32, false); Struct st({&u32}); EXPECT_EQ(1u, context->get_type_mgr()->GetId(&u32)); uint32_t id = context->get_type_mgr()->GetId(&st); ASSERT_NE(id, 0u); uint32_t toRemove = id == 2u ? 3u : 2u; uint32_t toStay = id == 2u ? 2u : 3u; context->get_type_mgr()->RemoveId(toRemove); ASSERT_EQ(context->get_type_mgr()->GetType(toRemove), nullptr); ASSERT_EQ(context->get_type_mgr()->GetId(&st), toStay); } TEST(TypeManager, GetTypeAndPointerType) { const std::string text = R"( OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 %1 = OpTypeInt 32 0 %2 = OpTypeStruct %1 )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); EXPECT_NE(context, nullptr); Integer u32(32, false); Pointer u32Ptr(&u32, spv::StorageClass::Function); Struct st({&u32}); Pointer stPtr(&st, spv::StorageClass::Input); auto pair = context->get_type_mgr()->GetTypeAndPointerType( 3u, spv::StorageClass::Function); ASSERT_EQ(nullptr, pair.first); ASSERT_EQ(nullptr, pair.second); pair = context->get_type_mgr()->GetTypeAndPointerType( 1u, spv::StorageClass::Function); ASSERT_TRUE(pair.first->IsSame(&u32)); ASSERT_TRUE(pair.second->IsSame(&u32Ptr)); pair = context->get_type_mgr()->GetTypeAndPointerType( 2u, spv::StorageClass::Input); ASSERT_TRUE(pair.first->IsSame(&st)); ASSERT_TRUE(pair.second->IsSame(&stPtr)); } TEST(TypeManager, DuplicateType) { const std::string text = R"( OpCapability Shader OpMemoryModel Logical GLSL450 %1 = OpTypeInt 32 0 %2 = OpTypeInt 32 0 )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); EXPECT_NE(context, nullptr); const Type* type1 = context->get_type_mgr()->GetType(1u); const Type* type2 = context->get_type_mgr()->GetType(2u); EXPECT_NE(type1, nullptr); EXPECT_NE(type2, nullptr); EXPECT_EQ(*type1, *type2); } TEST(TypeManager, MultipleStructs) { const std::string text = R"( OpCapability Shader OpMemoryModel Logical GLSL450 OpDecorate %3 Constant %1 = OpTypeInt 32 0 %2 = OpTypeStruct %1 %3 = OpTypeStruct %1 )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); EXPECT_NE(context, nullptr); const Type* type1 = context->get_type_mgr()->GetType(2u); const Type* type2 = context->get_type_mgr()->GetType(3u); EXPECT_NE(type1, nullptr); EXPECT_NE(type2, nullptr); EXPECT_FALSE(type1->IsSame(type2)); } TEST(TypeManager, RemovingIdAvoidsUseAfterFree) { const std::string text = R"( OpCapability Shader OpMemoryModel Logical GLSL450 %1 = OpTypeInt 32 0 %2 = OpTypeStruct %1 )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); EXPECT_NE(context, nullptr); Integer u32(32, false); Struct st({&u32}); const Type* type = context->get_type_mgr()->GetType(2u); EXPECT_NE(type, nullptr); context->get_type_mgr()->RemoveId(1u); EXPECT_TRUE(type->IsSame(&st)); } TEST(TypeManager, RegisterAndRemoveId) { const std::string text = R"( OpCapability Shader OpMemoryModel Logical GLSL450 %1 = OpTypeInt 32 0 )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); EXPECT_NE(context, nullptr); uint32_t id = 2u; { // Ensure that u32 goes out of scope. Integer u32(32, false); Struct st({&u32}); context->get_type_mgr()->RegisterType(id, st); } context->get_type_mgr()->RemoveId(id); EXPECT_EQ(nullptr, context->get_type_mgr()->GetType(id)); } TEST(TypeManager, RegisterAndRemoveIdAllTypes) { const std::string text = R"( OpCapability Shader OpMemoryModel Logical GLSL450 )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); EXPECT_NE(context, nullptr); std::vector> types = GenerateAllTypes(); uint32_t id = 0u; for (auto& t : types) { context->get_type_mgr()->RegisterType(++id, *t); EXPECT_EQ(*t, *context->get_type_mgr()->GetType(id)); EXPECT_EQ(id, context->get_type_mgr()->GetId(t.get())); } types.clear(); for (; id > 0; --id) { context->get_type_mgr()->RemoveId(id); EXPECT_EQ(nullptr, context->get_type_mgr()->GetType(id)); } } TEST(TypeManager, RegisterAndRemoveIdWithDecorations) { const std::string text = R"( OpCapability Shader OpMemoryModel Logical GLSL450 %1 = OpTypeInt 32 0 )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); EXPECT_NE(context, nullptr); uint32_t id = 2u; { Integer u32(32, false); Struct st({&u32, &u32}); st.AddDecoration({10}); st.AddDecoration({11}); st.AddMemberDecoration(0, {{35, 4}}); st.AddMemberDecoration(1, {{35, 4}}); st.AddMemberDecoration(1, {{36, 5}}); context->get_type_mgr()->RegisterType(id, st); EXPECT_EQ(st, *context->get_type_mgr()->GetType(id)); } context->get_type_mgr()->RemoveId(id); EXPECT_EQ(nullptr, context->get_type_mgr()->GetType(id)); } TEST(TypeManager, GetTypeInstructionInt) { const std::string text = R"( ; CHECK: OpTypeInt 32 0 ; CHECK: OpTypeInt 16 1 OpCapability Shader OpCapability Int16 OpCapability Linkage OpMemoryModel Logical GLSL450 )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); EXPECT_NE(context, nullptr); Integer uint_32(32, false); context->get_type_mgr()->GetTypeInstruction(&uint_32); Integer int_16(16, true); context->get_type_mgr()->GetTypeInstruction(&int_16); Match(text, context.get()); } TEST(TypeManager, GetTypeInstructionDuplicateInts) { const std::string text = R"( ; CHECK: OpTypeInt 32 0 ; CHECK-NOT: OpType OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); EXPECT_NE(context, nullptr); Integer uint_32(32, false); uint32_t id = context->get_type_mgr()->GetTypeInstruction(&uint_32); Integer other(32, false); EXPECT_EQ(context->get_type_mgr()->GetTypeInstruction(&other), id); Match(text, context.get()); } TEST(TypeManager, GetTypeInstructionAllTypes) { const std::string text = R"( ; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 ; CHECK: [[input_ptr:%\w+]] = OpTypePointer Input [[uint]] ; CHECK: [[uniform_ptr:%\w+]] = OpTypePointer Uniform [[uint]] ; CHECK: [[uint2:%\w+]] = OpConstant [[uint]] 2 ; CHECK: [[uint8:%\w+]] = OpConstant [[uint]] 8 ; CHECK: [[uint24:%\w+]] = OpConstant [[uint]] 24 ; CHECK: [[uint42:%\w+]] = OpConstant [[uint]] 42 ; CHECK: [[uint100:%\w+]] = OpConstant [[uint]] 100 ; CHECK: [[void:%\w+]] = OpTypeVoid ; CHECK: [[bool:%\w+]] = OpTypeBool ; CHECK: [[s32:%\w+]] = OpTypeInt 32 1 ; CHECK: OpTypeInt 64 1 ; CHECK: [[u64:%\w+]] = OpTypeInt 64 0 ; CHECK: [[f32:%\w+]] = OpTypeFloat 32 ; CHECK: OpTypeFloat 64 ; CHECK: OpTypeVector [[s32]] 2 ; CHECK: [[v3s32:%\w+]] = OpTypeVector [[s32]] 3 ; CHECK: OpTypeVector [[u64]] 4 ; CHECK: [[v3f32:%\w+]] = OpTypeVector [[f32]] 3 ; CHECK: OpTypeMatrix [[v3s32]] 3 ; CHECK: OpTypeMatrix [[v3s32]] 4 ; CHECK: OpTypeMatrix [[v3f32]] 4 ; CHECK: [[image1:%\w+]] = OpTypeImage [[s32]] 2D 0 0 0 0 Rg8 ReadOnly ; CHECK: OpTypeImage [[s32]] 2D 0 1 0 0 Rg8 ReadOnly ; CHECK: OpTypeImage [[s32]] 3D 0 1 0 0 Rg8 ReadOnly ; CHECK: [[image2:%\w+]] = OpTypeImage [[void]] 3D 0 1 0 1 Rg8 ReadWrite ; CHECK: OpTypeSampler ; CHECK: OpTypeSampledImage [[image1]] ; CHECK: OpTypeSampledImage [[image2]] ; CHECK: OpTypeArray [[f32]] [[uint100]] ; CHECK: [[a42f32:%\w+]] = OpTypeArray [[f32]] [[uint42]] ; CHECK: OpTypeArray [[u64]] [[uint24]] ; CHECK: OpTypeRuntimeArray [[v3f32]] ; CHECK: [[rav3s32:%\w+]] = OpTypeRuntimeArray [[v3s32]] ; CHECK: OpTypeStruct [[s32]] ; CHECK: [[sts32f32:%\w+]] = OpTypeStruct [[s32]] [[f32]] ; CHECK: OpTypeStruct [[u64]] [[a42f32]] [[rav3s32]] ; CHECK: OpTypeOpaque "" ; CHECK: OpTypeOpaque "hello" ; CHECK: OpTypeOpaque "world" ; CHECK: OpTypePointer Input [[f32]] ; CHECK: OpTypePointer Function [[sts32f32]] ; CHECK: OpTypePointer Function [[a42f32]] ; CHECK: OpTypeFunction [[void]] ; CHECK: OpTypeFunction [[void]] [[bool]] ; CHECK: OpTypeFunction [[void]] [[bool]] [[s32]] ; CHECK: OpTypeFunction [[s32]] [[bool]] [[s32]] ; CHECK: OpTypeEvent ; CHECK: OpTypeDeviceEvent ; CHECK: OpTypeReserveId ; CHECK: OpTypeQueue ; CHECK: OpTypePipe ReadWrite ; CHECK: OpTypePipe ReadOnly ; CHECK: OpTypeForwardPointer [[input_ptr]] Input ; CHECK: OpTypeForwardPointer [[uniform_ptr]] Input ; CHECK: OpTypeForwardPointer [[uniform_ptr]] Uniform ; CHECK: OpTypePipeStorage ; CHECK: OpTypeNamedBarrier ; CHECK: OpTypeAccelerationStructureKHR ; CHECK: OpTypeCooperativeMatrixNV [[f32]] [[uint24]] [[uint24]] [[uint24]] ; CHECK: OpTypeCooperativeMatrixKHR [[f32]] [[uint8]] [[uint8]] [[uint8]] [[uint2]] ; CHECK: OpTypeRayQueryKHR ; CHECK: OpTypeHitObjectNV OpCapability Shader OpCapability Int64 OpCapability Linkage OpMemoryModel Logical GLSL450 %uint = OpTypeInt 32 0 %1 = OpTypePointer Input %uint %2 = OpTypePointer Uniform %uint %1002 = OpConstant %uint 2 %8 = OpConstant %uint 8 %24 = OpConstant %uint 24 %42 = OpConstant %uint 42 %100 = OpConstant %uint 100 )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); EXPECT_NE(context, nullptr); std::vector> types = GenerateAllTypes(); for (auto& t : types) { context->get_type_mgr()->GetTypeInstruction(t.get()); } Match(text, context.get(), false); } TEST(TypeManager, GetTypeInstructionWithDecorations) { const std::string text = R"( ; CHECK: OpDecorate [[struct:%\w+]] CPacked ; CHECK: OpMemberDecorate [[struct]] 1 Offset 4 ; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 ; CHECK: [[struct]] = OpTypeStruct [[uint]] [[uint]] OpCapability Shader OpCapability Kernel OpCapability Linkage OpMemoryModel Logical GLSL450 %uint = OpTypeInt 32 0 )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); EXPECT_NE(context, nullptr); Integer u32(32, false); Struct st({&u32, &u32}); st.AddDecoration({10}); st.AddMemberDecoration(1, {{35, 4}}); (void)context->get_def_use_mgr(); context->get_type_mgr()->GetTypeInstruction(&st); Match(text, context.get()); } TEST(TypeManager, GetPointerToAmbiguousType1) { const std::string text = R"( ; CHECK: [[struct1:%\w+]] = OpTypeStruct ; CHECK: [[struct2:%\w+]] = OpTypeStruct ; CHECK: OpTypePointer Function [[struct2]] ; CHECK: OpTypePointer Function [[struct1]] OpCapability Shader OpCapability Kernel OpCapability Linkage OpMemoryModel Logical GLSL450 %uint = OpTypeInt 32 0 %1 = OpTypeStruct %uint %2 = OpTypeStruct %uint %3 = OpTypePointer Function %2 )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); EXPECT_NE(context, nullptr); context->get_type_mgr()->FindPointerToType(1, spv::StorageClass::Function); Match(text, context.get()); } TEST(TypeManager, GetPointerToAmbiguousType2) { const std::string text = R"( ; CHECK: [[struct1:%\w+]] = OpTypeStruct ; CHECK: [[struct2:%\w+]] = OpTypeStruct ; CHECK: OpTypePointer Function [[struct1]] ; CHECK: OpTypePointer Function [[struct2]] OpCapability Shader OpCapability Kernel OpCapability Linkage OpMemoryModel Logical GLSL450 %uint = OpTypeInt 32 0 %1 = OpTypeStruct %uint %2 = OpTypeStruct %uint %3 = OpTypePointer Function %1 )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); EXPECT_NE(context, nullptr); context->get_type_mgr()->FindPointerToType(2, spv::StorageClass::Function); Match(text, context.get()); } // Structures containing circular type references // (from https://github.com/KhronosGroup/SPIRV-Tools/issues/5623). TEST(TypeManager, CircularPointerToStruct) { const std::string text = R"( OpCapability VariablePointers OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_KHR_variable_pointers" OpExtension "SPV_KHR_physical_storage_buffer" OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %1 "main" OpExecutionMode %1 OriginUpperLeft OpExecutionMode %1 DepthReplacing OpDecorate %1200 ArrayStride 24 OpMemberDecorate %600 0 Offset 0 OpMemberDecorate %800 0 Offset 0 OpMemberDecorate %120 0 Offset 16 OpTypeForwardPointer %1200 PhysicalStorageBuffer %600 = OpTypeStruct %1200 %800 = OpTypeStruct %1200 %120 = OpTypeStruct %800 %1200 = OpTypePointer PhysicalStorageBuffer %120 )"; std::unique_ptr context = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); TypeManager manager(nullptr, context.get()); uint32_t id = manager.FindPointerToType(600, spv::StorageClass::Function); EXPECT_EQ(id, 1201); } } // namespace } // namespace analysis } // namespace opt } // namespace spvtools