Optimization: Add type manager.
Type manager will construct a map of types gradually from
instructions.
diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt
index ef1b0c6..ed9b99f 100644
--- a/source/opt/CMakeLists.txt
+++ b/source/opt/CMakeLists.txt
@@ -11,6 +11,7 @@
passes.h
pass_manager.h
types.h
+ type_manager.h
def_use_manager.cpp
function.cpp
@@ -20,6 +21,7 @@
module.cpp
passes.cpp
types.cpp
+ type_manager.cpp
)
spvtools_default_compile_options(SPIRV-Tools-opt)
diff --git a/source/opt/module.cpp b/source/opt/module.cpp
index 67385e9..d83d21e 100644
--- a/source/opt/module.cpp
+++ b/source/opt/module.cpp
@@ -30,7 +30,7 @@
namespace spvtools {
namespace ir {
-std::vector<Instruction*> Module::types() {
+std::vector<Instruction*> Module::GetTypes() {
std::vector<Instruction*> insts;
for (uint32_t i = 0; i < types_values_.size(); ++i) {
if (IsTypeInst(types_values_[i]->opcode()))
@@ -39,6 +39,15 @@
return insts;
};
+std::vector<const Instruction*> Module::GetTypes() const {
+ std::vector<const Instruction*> insts;
+ for (uint32_t i = 0; i < types_values_.size(); ++i) {
+ if (IsTypeInst(types_values_[i]->opcode()))
+ insts.push_back(types_values_[i].get());
+ }
+ return insts;
+};
+
std::vector<Instruction*> Module::GetConstants() {
std::vector<Instruction*> insts;
for (uint32_t i = 0; i < types_values_.size(); ++i) {
diff --git a/source/opt/module.h b/source/opt/module.h
index 8eca515..a463296 100644
--- a/source/opt/module.h
+++ b/source/opt/module.h
@@ -83,7 +83,8 @@
// Returns a vector of pointers to type-declaration instructions in this
// module.
- std::vector<Instruction*> types();
+ std::vector<Instruction*> GetTypes();
+ std::vector<const Instruction*> GetTypes() const;
// Returns the constant-defining instructions.
std::vector<Instruction*> GetConstants();
const std::vector<std::unique_ptr<Instruction>>& debugs() const {
diff --git a/source/opt/type_manager.cpp b/source/opt/type_manager.cpp
new file mode 100644
index 0000000..c01ae4a
--- /dev/null
+++ b/source/opt/type_manager.cpp
@@ -0,0 +1,226 @@
+// Copyright (c) 2016 Google Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and/or associated documentation files (the
+// "Materials"), to deal in the Materials without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Materials, and to
+// permit persons to whom the Materials are furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
+// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
+// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
+// https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
+#include <algorithm>
+#include <cassert>
+
+#include "reflect.h"
+#include "type_manager.h"
+
+namespace spvtools {
+namespace opt {
+namespace analysis {
+
+Type* TypeManager::GetType(uint32_t id) const {
+ if (id_to_type_.count(id) != 0) return id_to_type_.at(id).get();
+ return nullptr;
+}
+
+ForwardPointer* TypeManager::GetForwardPointer(uint32_t index) const {
+ if (index >= forward_pointers_.size()) return nullptr;
+ return forward_pointers_.at(index).get();
+}
+
+void TypeManager::AnalyzeType(const spvtools::ir::Module& module) {
+ for (const auto* inst : module.GetTypes()) RecordIfTypeDefinition(*inst);
+ for (const auto& inst : module.annotations()) AttachIfTypeDecoration(*inst);
+}
+
+Type* TypeManager::RecordIfTypeDefinition(const spvtools::ir::Instruction& inst) {
+ if (!spvtools::ir::IsTypeInst(inst.opcode())) return nullptr;
+
+ Type* type = nullptr;
+ switch (inst.opcode()) {
+ case SpvOpTypeVoid:
+ type = new Void();
+ break;
+ case SpvOpTypeBool:
+ type = new Bool();
+ break;
+ case SpvOpTypeInt:
+ type = new Integer(inst.GetSingleWordInOperand(0),
+ inst.GetSingleWordInOperand(1));
+ break;
+ case SpvOpTypeFloat:
+ type = new Float(inst.GetSingleWordInOperand(0));
+ break;
+ case SpvOpTypeVector:
+ type = new Vector(GetType(inst.GetSingleWordInOperand(0)),
+ inst.GetSingleWordInOperand(1));
+ break;
+ case SpvOpTypeMatrix:
+ type = new Matrix(GetType(inst.GetSingleWordInOperand(0)),
+ inst.GetSingleWordInOperand(1));
+ break;
+ case SpvOpTypeImage: {
+ const SpvAccessQualifier access =
+ inst.NumInOperands() < 8
+ ? SpvAccessQualifierReadOnly
+ : static_cast<SpvAccessQualifier>(inst.GetSingleWordInOperand(7));
+ type = new Image(
+ GetType(inst.GetSingleWordInOperand(0)),
+ static_cast<SpvDim>(inst.GetSingleWordInOperand(1)),
+ inst.GetSingleWordInOperand(2), inst.GetSingleWordInOperand(3),
+ inst.GetSingleWordInOperand(4), inst.GetSingleWordInOperand(5),
+ static_cast<SpvImageFormat>(inst.GetSingleWordInOperand(6)), access);
+ } break;
+ case SpvOpTypeSampler:
+ type = new Sampler();
+ break;
+ case SpvOpTypeSampledImage:
+ type = new SampledImage(GetType(inst.GetSingleWordInOperand(0)));
+ break;
+ case SpvOpTypeArray:
+ type = new Array(GetType(inst.GetSingleWordInOperand(0)),
+ inst.GetSingleWordInOperand(1));
+ break;
+ case SpvOpTypeRuntimeArray:
+ type = new RuntimeArray(GetType(inst.GetSingleWordInOperand(0)));
+ break;
+ case SpvOpTypeStruct: {
+ std::vector<Type*> element_types;
+ for (uint32_t i = 0; i < inst.NumInOperands(); ++i) {
+ element_types.push_back(GetType(inst.GetSingleWordInOperand(i)));
+ }
+ type = new Struct(element_types);
+ } break;
+ case SpvOpTypeOpaque: {
+ const uint32_t* data = inst.GetInOperand(0).words.data();
+ type = new Opaque(reinterpret_cast<const char*>(data));
+ } break;
+ case SpvOpTypePointer: {
+ auto* ptr = new Pointer(
+ GetType(inst.GetSingleWordInOperand(1)),
+ static_cast<SpvStorageClass>(inst.GetSingleWordInOperand(0)));
+ // Let's see if somebody forward references this pointer.
+ for (auto* fp : unresolved_forward_pointers_) {
+ if (fp->target_id() == inst.result_id()) {
+ fp->SetTargetPointer(ptr);
+ unresolved_forward_pointers_.erase(fp);
+ break;
+ }
+ }
+ type = ptr;
+ } break;
+ case SpvOpTypeFunction: {
+ Type* return_type = GetType(inst.GetSingleWordInOperand(0));
+ std::vector<Type*> param_types;
+ for (uint32_t i = 1; i < inst.NumInOperands(); ++i) {
+ param_types.push_back(GetType(inst.GetSingleWordInOperand(i)));
+ }
+ type = new Function(return_type, param_types);
+ } break;
+ case SpvOpTypeEvent:
+ type = new Event();
+ break;
+ case SpvOpTypeDeviceEvent:
+ type = new DeviceEvent();
+ break;
+ case SpvOpTypeReserveId:
+ type = new ReserveId();
+ break;
+ case SpvOpTypeQueue:
+ type = new Queue();
+ break;
+ case SpvOpTypePipe:
+ type = new Pipe(
+ static_cast<SpvAccessQualifier>(inst.GetSingleWordInOperand(0)));
+ break;
+ case SpvOpTypeForwardPointer: {
+ // Handling of forward pointers is different from the other types.
+ auto* fp = new ForwardPointer(
+ inst.GetSingleWordInOperand(0),
+ static_cast<SpvStorageClass>(inst.GetSingleWordInOperand(1)));
+ forward_pointers_.emplace_back(fp);
+ unresolved_forward_pointers_.insert(fp);
+ return fp;
+ }
+ case SpvOpTypePipeStorage:
+ type = new PipeStorage();
+ break;
+ case SpvOpTypeNamedBarrier:
+ type = new NamedBarrier();
+ break;
+ default:
+ assert(0 && "unhandled type found");
+ break;
+ }
+
+ uint32_t id = inst.result_id();
+ if (id == 0) {
+ assert(inst.opcode() == SpvOpTypeForwardPointer &&
+ "instruction without result id found");
+ } else {
+ assert(type != nullptr && "type should not be nullptr at this point");
+ id_to_type_[id].reset(type);
+ }
+ return type;
+}
+
+void TypeManager::AttachIfTypeDecoration(const ir::Instruction& inst) {
+ const SpvOp opcode = inst.opcode();
+ if (!ir::IsAnnotationInst(opcode)) return;
+ const uint32_t id = inst.GetSingleWordOperand(0);
+ // Do nothing if the id to be decorated is not for a known type.
+ if (!id_to_type_.count(id)) return;
+
+ Type* target_type = id_to_type_[id].get();
+ switch (opcode) {
+ case SpvOpDecorate: {
+ const auto count = inst.NumOperands();
+ std::vector<uint32_t> data;
+ for (uint32_t i = 1; i < count; ++i) {
+ data.push_back(inst.GetSingleWordOperand(i));
+ }
+ target_type->AddDecoration(std::move(data));
+ } break;
+ case SpvOpMemberDecorate: {
+ const auto count = inst.NumOperands();
+ const uint32_t index = inst.GetSingleWordOperand(1);
+ std::vector<uint32_t> data;
+ for (uint32_t i = 2; i < count; ++i) {
+ data.push_back(inst.GetSingleWordOperand(i));
+ }
+ if (Struct* st = target_type->AsStruct()) {
+ st->AddMemeberDecoration(index, std::move(data));
+ } else {
+ assert(0 && "OpMemberDecorate on non-struct type");
+ }
+ } break;
+ case SpvOpDecorationGroup:
+ case SpvOpGroupDecorate:
+ case SpvOpGroupMemberDecorate:
+ assert(0 && "unhandled decoration");
+ break;
+ default:
+ assert(0 && "unreachable");
+ break;
+ }
+}
+
+} // namespace analysis
+} // namespace opt
+} // namespace spvtools
diff --git a/source/opt/type_manager.h b/source/opt/type_manager.h
new file mode 100644
index 0000000..9fb3330
--- /dev/null
+++ b/source/opt/type_manager.h
@@ -0,0 +1,86 @@
+// Copyright (c) 2016 Google Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and/or associated documentation files (the
+// "Materials"), to deal in the Materials without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Materials, and to
+// permit persons to whom the Materials are furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
+// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
+// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
+// https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
+#ifndef LIBSPIRV_OPT_TYPE_MANAGER_H_
+#define LIBSPIRV_OPT_TYPE_MANAGER_H_
+
+#include <memory>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "module.h"
+#include "types.h"
+
+namespace spvtools {
+namespace opt {
+namespace analysis {
+
+// A class for managing the SPIR-V type hierarchy.
+class TypeManager {
+ public:
+ using IdToTypeMap = std::unordered_map<uint32_t, std::unique_ptr<Type>>;
+ using ForwardPointerVector = std::vector<std::unique_ptr<ForwardPointer>>;
+
+ TypeManager() = default;
+ TypeManager(const TypeManager&) = delete;
+ TypeManager(TypeManager&&) = delete;
+ TypeManager& operator=(const TypeManager&) = delete;
+ TypeManager& operator=(TypeManager&&) = delete;
+
+ // Returns the type for the given type |id|. Returns nullptr if the given |id|
+ // does not define a type.
+ Type* GetType(uint32_t id) const;
+ // Returns the number of types hold in this manager.
+ size_t NumTypes() const { return id_to_type_.size(); }
+
+ // Returns the forward pointer type at the given |index|.
+ ForwardPointer* GetForwardPointer(uint32_t index) const;
+ // Returns the number of forward pointer types hold in this manager.
+ size_t NumForwardPointers() const { return forward_pointers_.size(); }
+
+ // Analyzes the types and decorations on types in the given |module|.
+ void AnalyzeType(const spvtools::ir::Module& module);
+
+ private:
+ // Creates and returns a type from the given SPIR-V |inst|. Returns nullptr if
+ // the given instruction is not for defining a type.
+ Type* RecordIfTypeDefinition(const spvtools::ir::Instruction& inst);
+ // Attaches the decoration encoded in |inst| to a type. Does nothing if the
+ // given instruction is not a decoration instruction or not decorating a type.
+ void AttachIfTypeDecoration(const spvtools::ir::Instruction& inst);
+
+ IdToTypeMap id_to_type_; // Mapping from ids to their type representations.
+ ForwardPointerVector forward_pointers_; // All forward pointer declarations.
+ // All unresolved forward pointer declarations.
+ // Refers the contents in the above vector.
+ std::unordered_set<ForwardPointer*> unresolved_forward_pointers_;
+};
+
+} // namespace analysis
+} // namespace opt
+} // namespace spvtools
+
+#endif // LIBSPIRV_OPT_TYPE_MANAGER_H_
diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt
index f2fa17e..1167e0d 100644
--- a/test/opt/CMakeLists.txt
+++ b/test/opt/CMakeLists.txt
@@ -68,3 +68,8 @@
SRCS test_types.cpp
LIBS SPIRV-Tools-opt
)
+
+add_spvtools_unittest(TARGET type_manager
+ SRCS test_type_manager.cpp
+ LIBS SPIRV-Tools-opt ${SPIRV_TOOLS}
+)
diff --git a/test/opt/test_type_manager.cpp b/test/opt/test_type_manager.cpp
new file mode 100644
index 0000000..b5f460c
--- /dev/null
+++ b/test/opt/test_type_manager.cpp
@@ -0,0 +1,212 @@
+// Copyright (c) 2016 Google Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and/or associated documentation files (the
+// "Materials"), to deal in the Materials without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Materials, and to
+// permit persons to whom the Materials are furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
+// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
+// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
+// https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "opt/instruction.h"
+#include "opt/libspirv.hpp"
+#include "opt/type_manager.h"
+
+namespace {
+
+using namespace spvtools;
+
+TEST(TypeManager, TypeStrings) {
+ const std::string text = R"(
+ OpTypeForwardPointer !20 !2 ; id for %p is 20, Uniform is 2
+ OpTypeForwardPointer !10000 !1
+ %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
+ )";
+
+ std::vector<std::pair<uint32_t, std::string>> type_id_strs = {
+ {1, "void"},
+ {2, "bool"},
+ {3, "uint32"},
+ // Id 4 is used by the constant.
+ {5, "sint32"},
+ {6, "float64"},
+ {7, "<uint32, 3>"},
+ {8, "<<uint32, 3>, 3>"},
+ {9, "image(sint32, 3, 0, 1, 1, 0, 3, 2)"},
+ {10, "image(sint32, 3, 0, 1, 1, 0, 3, 0)"},
+ {11, "sampler"},
+ {12, "sampled_image(image(sint32, 3, 0, 1, 1, 0, 3, 2))"},
+ {13, "sampled_image(image(sint32, 3, 0, 1, 1, 0, 3, 0))"},
+ {14, "[uint32, id(4)]"},
+ {15, "[float64]"},
+ {16, "{uint32}"},
+ {17, "{float64, sint32, <uint32, 3>}"},
+ {18, "opaque('')"},
+ {19, "opaque('opaque')"},
+ {20, "{uint32}*"},
+ {21, "(uint32, uint32) -> void"},
+ {22, "event"},
+ {23, "device_event"},
+ {24, "reserve_id"},
+ {25, "queue"},
+ {26, "pipe(0)"},
+ {27, "pipe_storage"},
+ {28, "named_barrier"},
+ };
+
+ opt::analysis::TypeManager manager;
+ std::unique_ptr<ir::Module> module =
+ SpvTools(SPV_ENV_UNIVERSAL_1_1).BuildModule(text);
+ manager.AnalyzeType(*module);
+
+ EXPECT_EQ(type_id_strs.size(), manager.NumTypes());
+ EXPECT_EQ(2u, manager.NumForwardPointers());
+
+ for (const auto& p : type_id_strs) {
+ EXPECT_EQ(p.second, manager.GetType(p.first)->str());
+ }
+ EXPECT_EQ("forward_pointer({uint32}*)", manager.GetForwardPointer(0)->str());
+ EXPECT_EQ("forward_pointer(10000)", manager.GetForwardPointer(1)->str());
+}
+
+TEST(Struct, 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
+ )";
+ opt::analysis::TypeManager manager;
+ std::unique_ptr<ir::Module> module =
+ SpvTools(SPV_ENV_UNIVERSAL_1_1).BuildModule(text);
+ manager.AnalyzeType(*module);
+
+ ASSERT_EQ(7u, manager.NumTypes());
+ ASSERT_EQ(0u, manager.NumForwardPointers());
+ // 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(Struct, 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
+ )";
+ opt::analysis::TypeManager manager;
+ std::unique_ptr<ir::Module> module =
+ SpvTools(SPV_ENV_UNIVERSAL_1_1).BuildModule(text);
+ manager.AnalyzeType(*module);
+
+ ASSERT_EQ(10u, manager.NumTypes());
+ ASSERT_EQ(0u, manager.NumForwardPointers());
+ // 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;
+ }
+ }
+ }
+}
+
+} // anonymous namespace