Linker: Better type comparison for OpTypeArray and OpTypeForwardPointer (#2580)
* Types: Avoid comparing IDs for in Type::IsSameImpl
When linking, we end up with duplicate types for imported and exported
types, that needs to be removed. The current code would reject valid
import/export pairs of symbols due to IDs mismatch, even if the types or
constants behind those ID were the same.
Enabled remaining type_match_test
Fixes #2442
diff --git a/source/link/linker.cpp b/source/link/linker.cpp
index f28b759..d99a1e8 100644
--- a/source/link/linker.cpp
+++ b/source/link/linker.cpp
@@ -33,6 +33,7 @@
#include "source/opt/ir_loader.h"
#include "source/opt/pass_manager.h"
#include "source/opt/remove_duplicates_pass.h"
+#include "source/opt/type_manager.h"
#include "source/spirv_target_env.h"
#include "source/util/make_unique.h"
#include "spirv-tools/libspirv.hpp"
@@ -40,14 +41,15 @@
namespace spvtools {
namespace {
-using opt::IRContext;
using opt::Instruction;
+using opt::IRContext;
using opt::Module;
-using opt::Operand;
using opt::PassManager;
using opt::RemoveDuplicatesPass;
using opt::analysis::DecorationManager;
using opt::analysis::DefUseManager;
+using opt::analysis::Type;
+using opt::analysis::TypeManager;
// Stores various information about an imported or exported symbol.
struct LinkageSymbolInfo {
@@ -472,14 +474,15 @@
opt::IRContext* context) {
spv_position_t position = {};
- // Ensure th import and export types are the same.
- const DefUseManager& def_use_manager = *context->get_def_use_mgr();
+ // Ensure the import and export types are the same.
const DecorationManager& decoration_manager = *context->get_decoration_mgr();
+ const TypeManager& type_manager = *context->get_type_mgr();
for (const auto& linking_entry : linkings_to_do) {
- if (!RemoveDuplicatesPass::AreTypesEqual(
- *def_use_manager.GetDef(linking_entry.imported_symbol.type_id),
- *def_use_manager.GetDef(linking_entry.exported_symbol.type_id),
- context))
+ Type* imported_symbol_type =
+ type_manager.GetType(linking_entry.imported_symbol.type_id);
+ Type* exported_symbol_type =
+ type_manager.GetType(linking_entry.exported_symbol.type_id);
+ if (!(*imported_symbol_type == *exported_symbol_type))
return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY)
<< "Type mismatch on symbol \""
<< linking_entry.imported_symbol.name
diff --git a/source/opt/remove_duplicates_pass.cpp b/source/opt/remove_duplicates_pass.cpp
index a37e9df..0e65cc8 100644
--- a/source/opt/remove_duplicates_pass.cpp
+++ b/source/opt/remove_duplicates_pass.cpp
@@ -96,35 +96,67 @@
return modified;
}
+ analysis::TypeManager type_manager(context()->consumer(), context());
+
std::vector<Instruction*> visited_types;
+ std::vector<analysis::ForwardPointer> visited_forward_pointers;
std::vector<Instruction*> to_delete;
for (auto* i = &*context()->types_values_begin(); i; i = i->NextNode()) {
+ const bool is_i_forward_pointer = i->opcode() == SpvOpTypeForwardPointer;
+
// We only care about types.
- if (!spvOpcodeGeneratesType((i->opcode())) &&
- i->opcode() != SpvOpTypeForwardPointer) {
+ if (!spvOpcodeGeneratesType(i->opcode()) && !is_i_forward_pointer) {
continue;
}
- // Is the current type equal to one of the types we have aready visited?
- SpvId id_to_keep = 0u;
- // TODO(dneto0): Use a trie to avoid quadratic behaviour? Extract the
- // ResultIdTrie from unify_const_pass.cpp for this.
- for (auto j : visited_types) {
- if (AreTypesEqual(*i, *j, context())) {
- id_to_keep = j->result_id();
- break;
+ if (!is_i_forward_pointer) {
+ // Is the current type equal to one of the types we have already visited?
+ SpvId id_to_keep = 0u;
+ analysis::Type* i_type = type_manager.GetType(i->result_id());
+ assert(i_type);
+ // TODO(dneto0): Use a trie to avoid quadratic behaviour? Extract the
+ // ResultIdTrie from unify_const_pass.cpp for this.
+ for (auto j : visited_types) {
+ analysis::Type* j_type = type_manager.GetType(j->result_id());
+ assert(j_type);
+ if (*i_type == *j_type) {
+ id_to_keep = j->result_id();
+ break;
+ }
}
- }
- if (id_to_keep == 0u) {
- // This is a never seen before type, keep it around.
- visited_types.emplace_back(i);
+ if (id_to_keep == 0u) {
+ // This is a never seen before type, keep it around.
+ visited_types.emplace_back(i);
+ } else {
+ // The same type has already been seen before, remove this one.
+ context()->KillNamesAndDecorates(i->result_id());
+ context()->ReplaceAllUsesWith(i->result_id(), id_to_keep);
+ modified = true;
+ to_delete.emplace_back(i);
+ }
} else {
- // The same type has already been seen before, remove this one.
- context()->KillNamesAndDecorates(i->result_id());
- context()->ReplaceAllUsesWith(i->result_id(), id_to_keep);
- modified = true;
- to_delete.emplace_back(i);
+ analysis::ForwardPointer i_type(
+ i->GetSingleWordInOperand(0u),
+ (SpvStorageClass)i->GetSingleWordInOperand(1u));
+ i_type.SetTargetPointer(
+ type_manager.GetType(i_type.target_id())->AsPointer());
+
+ // TODO(dneto0): Use a trie to avoid quadratic behaviour? Extract the
+ // ResultIdTrie from unify_const_pass.cpp for this.
+ const bool found_a_match =
+ std::find(std::begin(visited_forward_pointers),
+ std::end(visited_forward_pointers),
+ i_type) != std::end(visited_forward_pointers);
+
+ if (!found_a_match) {
+ // This is a never seen before type, keep it around.
+ visited_forward_pointers.emplace_back(i_type);
+ } else {
+ // The same type has already been seen before, remove this one.
+ modified = true;
+ to_delete.emplace_back(i);
+ }
}
}
@@ -151,8 +183,8 @@
analysis::DecorationManager decoration_manager(context()->module());
for (auto* i = &*context()->annotation_begin(); i;) {
- // Is the current decoration equal to one of the decorations we have aready
- // visited?
+ // Is the current decoration equal to one of the decorations we have
+ // already visited?
bool already_visited = false;
// TODO(dneto0): Use a trie to avoid quadratic behaviour? Extract the
// ResultIdTrie from unify_const_pass.cpp for this.
@@ -177,20 +209,5 @@
return modified;
}
-bool RemoveDuplicatesPass::AreTypesEqual(const Instruction& inst1,
- const Instruction& inst2,
- IRContext* context) {
- if (inst1.opcode() != inst2.opcode()) return false;
- if (!IsTypeInst(inst1.opcode())) return false;
-
- const analysis::Type* type1 =
- context->get_type_mgr()->GetType(inst1.result_id());
- const analysis::Type* type2 =
- context->get_type_mgr()->GetType(inst2.result_id());
- if (type1 && type2 && *type1 == *type2) return true;
-
- return false;
-}
-
} // namespace opt
} // namespace spvtools
diff --git a/source/opt/remove_duplicates_pass.h b/source/opt/remove_duplicates_pass.h
index 8554a98..038caa8 100644
--- a/source/opt/remove_duplicates_pass.h
+++ b/source/opt/remove_duplicates_pass.h
@@ -36,12 +36,6 @@
const char* name() const override { return "remove-duplicates"; }
Status Process() override;
- // TODO(pierremoreau): Move this function somewhere else (e.g. pass.h or
- // within the type manager)
- // Returns whether two types are equal, and have the same decorations.
- static bool AreTypesEqual(const Instruction& inst1, const Instruction& inst2,
- IRContext* context);
-
private:
// Remove duplicate capabilities from the module
//
diff --git a/source/opt/type_manager.cpp b/source/opt/type_manager.cpp
index 001883c..113dc80 100644
--- a/source/opt/type_manager.cpp
+++ b/source/opt/type_manager.cpp
@@ -66,7 +66,13 @@
}
void TypeManager::AnalyzeTypes(const Module& module) {
- // First pass through the types. Any types that reference a forward pointer
+ // First pass through the constants, as some will be needed when traversing
+ // the types in the next pass.
+ for (const auto* inst : module.GetConstants()) {
+ id_to_constant_inst_[inst->result_id()] = inst;
+ }
+
+ // Then pass through the types. Any types that reference a forward pointer
// (directly or indirectly) are incomplete, and are added to incomplete types.
for (const auto* inst : module.GetTypes()) {
RecordIfTypeDefinition(*inst);
@@ -154,7 +160,7 @@
#ifndef NDEBUG
// Check if the type pool contains two types that are the same. This
- // is an indication that the hashing and comparision are wrong. It
+ // is an indication that the hashing and comparison are wrong. It
// will cause a problem if the type pool gets resized and everything
// is rehashed.
for (auto& i : type_pool_) {
@@ -505,8 +511,15 @@
case Type::kArray: {
const Array* array_ty = type.AsArray();
const Type* ele_ty = array_ty->element_type();
- rebuilt_ty =
- MakeUnique<Array>(RebuildType(*ele_ty), array_ty->LengthId());
+ if (array_ty->length_spec_id() != 0u)
+ rebuilt_ty =
+ MakeUnique<Array>(RebuildType(*ele_ty), array_ty->LengthId(),
+ array_ty->length_spec_id());
+ else
+ rebuilt_ty =
+ MakeUnique<Array>(RebuildType(*ele_ty), array_ty->LengthId(),
+ array_ty->length_constant_type(),
+ array_ty->length_constant_words());
break;
}
case Type::kRuntimeArray: {
@@ -636,15 +649,39 @@
case SpvOpTypeSampledImage:
type = new SampledImage(GetType(inst.GetSingleWordInOperand(0)));
break;
- case SpvOpTypeArray:
- type = new Array(GetType(inst.GetSingleWordInOperand(0)),
- inst.GetSingleWordInOperand(1));
+ case SpvOpTypeArray: {
+ const uint32_t length_id = inst.GetSingleWordInOperand(1);
+ const Instruction* length_constant_inst = id_to_constant_inst_[length_id];
+ assert(length_constant_inst);
+
+ // If it is a specialised constants, retrieve its SpecId.
+ uint32_t spec_id = 0u;
+ Type* length_type = nullptr;
+ Operand::OperandData length_words;
+ if (spvOpcodeIsSpecConstant(length_constant_inst->opcode())) {
+ context()->get_decoration_mgr()->ForEachDecoration(
+ length_id, SpvDecorationSpecId,
+ [&spec_id](const Instruction& decoration) {
+ assert(decoration.opcode() == SpvOpDecorate);
+ spec_id = decoration.GetSingleWordOperand(2u);
+ });
+ } else {
+ length_type = GetType(length_constant_inst->type_id());
+ length_words = length_constant_inst->GetOperand(2u).words;
+ }
+
+ if (spec_id != 0u)
+ type = new Array(GetType(inst.GetSingleWordInOperand(0)), length_id,
+ spec_id);
+ else
+ type = new Array(GetType(inst.GetSingleWordInOperand(0)), length_id,
+ length_type, length_words);
if (id_to_incomplete_type_.count(inst.GetSingleWordInOperand(0))) {
incomplete_types_.emplace_back(inst.result_id(), type);
id_to_incomplete_type_[inst.result_id()] = type;
return type;
}
- break;
+ } break;
case SpvOpTypeRuntimeArray:
type = new RuntimeArray(GetType(inst.GetSingleWordInOperand(0)));
if (id_to_incomplete_type_.count(inst.GetSingleWordInOperand(0))) {
diff --git a/source/opt/type_manager.h b/source/opt/type_manager.h
index c44969e..ecc7858 100644
--- a/source/opt/type_manager.h
+++ b/source/opt/type_manager.h
@@ -209,6 +209,8 @@
IdToTypeMap id_to_incomplete_type_; // Maps ids to their type representations
// for incomplete types.
+
+ std::unordered_map<uint32_t, const Instruction*> id_to_constant_inst_;
};
} // namespace analysis
diff --git a/source/opt/types.cpp b/source/opt/types.cpp
index cfafc7d..af747cf 100644
--- a/source/opt/types.cpp
+++ b/source/opt/types.cpp
@@ -383,17 +383,46 @@
image_type_->GetHashWords(words, seen);
}
-Array::Array(Type* type, uint32_t length_id)
- : Type(kArray), element_type_(type), length_id_(length_id) {
+Array::Array(Type* type, uint32_t length_id, uint32_t spec_id)
+ : Type(kArray),
+ element_type_(type),
+ length_id_(length_id),
+ length_spec_id_(spec_id),
+ length_constant_type_(nullptr),
+ length_constant_words_() {
assert(!type->AsVoid());
+ assert(spec_id != 0u);
+}
+
+Array::Array(Type* type, uint32_t length_id, const Type* constant_type,
+ Operand::OperandData constant_words)
+ : Type(kArray),
+ element_type_(type),
+ length_id_(length_id),
+ length_spec_id_(0u),
+ length_constant_type_(constant_type),
+ length_constant_words_(constant_words) {
+ assert(!type->AsVoid());
+ assert(constant_type && constant_type->AsInteger());
}
bool Array::IsSameImpl(const Type* that, IsSameCache* seen) const {
const Array* at = that->AsArray();
if (!at) return false;
- return length_id_ == at->length_id_ &&
- element_type_->IsSameImpl(at->element_type_, seen) &&
- HasSameDecorations(that);
+ bool is_same = element_type_->IsSameImpl(at->element_type_, seen) &&
+ HasSameDecorations(that);
+ // If it is a specialized constant
+ if (length_spec_id_ != 0u) {
+ // ensure they have the same SpecId
+ is_same = is_same && length_spec_id_ == at->length_spec_id_;
+ } else {
+ // else, ensure they have the same length literal number.
+ is_same =
+ is_same &&
+ length_constant_type_->IsSameImpl(at->length_constant_type_, seen) &&
+ length_constant_words_ == at->length_constant_words_;
+ }
+ return is_same;
}
std::string Array::str() const {
@@ -405,7 +434,13 @@
void Array::GetExtraHashWords(std::vector<uint32_t>* words,
std::unordered_set<const Type*>* seen) const {
element_type_->GetHashWords(words, seen);
- words->push_back(length_id_);
+ if (length_spec_id_ != 0u) {
+ words->push_back(length_spec_id_);
+ } else {
+ length_constant_type_->GetHashWords(words, seen);
+ words->insert(words->end(), length_constant_words_.begin(),
+ length_constant_words_.end());
+ }
}
void Array::ReplaceElementType(const Type* type) { element_type_ = type; }
@@ -609,7 +644,8 @@
bool ForwardPointer::IsSameImpl(const Type* that, IsSameCache*) const {
const ForwardPointer* fpt = that->AsForwardPointer();
if (!fpt) return false;
- return target_id_ == fpt->target_id_ &&
+ return (pointer_ && fpt->pointer_ ? *pointer_ == *fpt->pointer_
+ : target_id_ == fpt->target_id_) &&
storage_class_ == fpt->storage_class_ && HasSameDecorations(that);
}
diff --git a/source/opt/types.h b/source/opt/types.h
index fe0f39a..381cab6 100644
--- a/source/opt/types.h
+++ b/source/opt/types.h
@@ -27,6 +27,7 @@
#include <vector>
#include "source/latest_version_spirv_header.h"
+#include "source/opt/instruction.h"
#include "spirv-tools/libspirv.h"
namespace spvtools {
@@ -356,12 +357,19 @@
class Array : public Type {
public:
- Array(Type* element_type, uint32_t length_id);
+ Array(Type* element_type, uint32_t length_id, uint32_t spec_id);
+ Array(Type* element_type, uint32_t length_id, const Type* constant_type,
+ Operand::OperandData constant_words);
Array(const Array&) = default;
std::string str() const override;
const Type* element_type() const { return element_type_; }
uint32_t LengthId() const { return length_id_; }
+ uint32_t length_spec_id() const { return length_spec_id_; }
+ const Type* length_constant_type() const { return length_constant_type_; }
+ Operand::OperandData length_constant_words() const {
+ return length_constant_words_;
+ }
Array* AsArray() override { return this; }
const Array* AsArray() const override { return this; }
@@ -376,6 +384,9 @@
const Type* element_type_;
uint32_t length_id_;
+ uint32_t length_spec_id_;
+ const Type* length_constant_type_;
+ Operand::OperandData length_constant_words_;
};
class RuntimeArray : public Type {
diff --git a/test/link/type_match_test.cpp b/test/link/type_match_test.cpp
index 291d13d..c12ffb3 100644
--- a/test/link/type_match_test.cpp
+++ b/test/link/type_match_test.cpp
@@ -21,34 +21,39 @@
using TypeMatch = spvtest::LinkerTest;
// Basic types
-#define PartInt(N) N " = OpTypeInt 32 0"
-#define PartFloat(N) N " = OpTypeFloat 32"
-#define PartOpaque(N) N " = OpTypeOpaque \"bar\""
-#define PartSampler(N) N " = OpTypeSampler"
-#define PartEvent(N) N " = OpTypeEvent"
-#define PartDeviceEvent(N) N " = OpTypeDeviceEvent"
-#define PartReserveId(N) N " = OpTypeReserveId"
-#define PartQueue(N) N " = OpTypeQueue"
-#define PartPipe(N) N " = OpTypePipe ReadWrite"
-#define PartPipeStorage(N) N " = OpTypePipeStorage"
-#define PartNamedBarrier(N) N " = OpTypeNamedBarrier"
+#define PartInt(D, N) D(N) " = OpTypeInt 32 0"
+#define PartFloat(D, N) D(N) " = OpTypeFloat 32"
+#define PartOpaque(D, N) D(N) " = OpTypeOpaque \"bar\""
+#define PartSampler(D, N) D(N) " = OpTypeSampler"
+#define PartEvent(D, N) D(N) " = OpTypeEvent"
+#define PartDeviceEvent(D, N) D(N) " = OpTypeDeviceEvent"
+#define PartReserveId(D, N) D(N) " = OpTypeReserveId"
+#define PartQueue(D, N) D(N) " = OpTypeQueue"
+#define PartPipe(D, N) D(N) " = OpTypePipe ReadWrite"
+#define PartPipeStorage(D, N) D(N) " = OpTypePipeStorage"
+#define PartNamedBarrier(D, N) D(N) " = OpTypeNamedBarrier"
// Compound types
-#define PartVector(N, T) N " = OpTypeVector " T " 3"
-#define PartMatrix(N, T) N " = OpTypeMatrix " T " 4"
-#define PartImage(N, T) N " = OpTypeImage " T " 2D 0 0 0 0 Rgba32f"
-#define PartSampledImage(N, T) N " = OpTypeSampledImage " T
-#define PartArray(N, T) N " = OpTypeArray " T " %const"
-#define PartRuntimeArray(N, T) N " = OpTypeRuntimeArray " T
-#define PartStruct(N, T) N " = OpTypeStruct " T " " T
-#define PartPointer(N, T) N " = OpTypePointer Workgroup " T
-#define PartFunction(N, T) N " = OpTypeFunction " T " " T
+#define PartVector(DR, DA, N, T) DR(N) " = OpTypeVector " DA(T) " 3"
+#define PartMatrix(DR, DA, N, T) DR(N) " = OpTypeMatrix " DA(T) " 4"
+#define PartImage(DR, DA, N, T) \
+ DR(N) " = OpTypeImage " DA(T) " 2D 0 0 0 0 Rgba32f"
+#define PartSampledImage(DR, DA, N, T) DR(N) " = OpTypeSampledImage " DA(T)
+#define PartArray(DR, DA, N, T) DR(N) " = OpTypeArray " DA(T) " " DA(const)
+#define PartRuntimeArray(DR, DA, N, T) DR(N) " = OpTypeRuntimeArray " DA(T)
+#define PartStruct(DR, DA, N, T) DR(N) " = OpTypeStruct " DA(T) " " DA(T)
+#define PartPointer(DR, DA, N, T) DR(N) " = OpTypePointer Workgroup " DA(T)
+#define PartFunction(DR, DA, N, T) DR(N) " = OpTypeFunction " DA(T) " " DA(T)
+
+#define CheckDecoRes(S) "[[" #S ":%\\w+]]"
+#define CheckDecoArg(S) "[[" #S "]]"
+#define InstDeco(S) "%" #S
#define MatchPart1(F, N) \
- "; CHECK: " Part##F("[[" #N ":%\\w+]]") "\n" Part##F("%" #N) "\n"
-#define MatchPart2(F, N, T) \
- "; CHECK: " Part##F("[[" #N ":%\\w+]]", "[[" #T ":%\\w+]]") "\n" Part##F( \
- "%" #N, "%" #T) "\n"
+ "; CHECK: " Part##F(CheckDecoRes, N) "\n" Part##F(InstDeco, N) "\n"
+#define MatchPart2(F, N, T) \
+ "; CHECK: " Part##F(CheckDecoRes, CheckDecoArg, N, T) "\n" Part##F( \
+ InstDeco, InstDeco, N, T) "\n"
#define MatchF(N, CODE) \
TEST_F(TypeMatch, N) { \
@@ -98,47 +103,42 @@
Match2(Image, Float);
// Unrestricted compound types
-// The following skip Array as it causes issues
#define MatchCompounds1(A) \
Match2(RuntimeArray, A); \
Match2(Struct, A); \
Match2(Pointer, A); \
Match2(Function, A); \
-// Match2(Array, A); // Disabled as it fails currently
+ Match2(Array, A);
#define MatchCompounds2(A, B) \
Match3(RuntimeArray, A, B); \
Match3(Struct, A, B); \
Match3(Pointer, A, B); \
Match3(Function, A, B); \
- // Match3(Array, A, B); // Disabled as it fails currently
+ Match3(Array, A, B);
MatchCompounds1(Float);
-// MatchCompounds2(Array, Float);
+MatchCompounds2(Array, Float);
MatchCompounds2(RuntimeArray, Float);
MatchCompounds2(Struct, Float);
MatchCompounds2(Pointer, Float);
MatchCompounds2(Function, Float);
// ForwardPointer tests, which don't fit into the previous mold
-#define MatchFpF(N, CODE) \
- MatchF(N, \
- "; CHECK: [[type:%\\w+]] = OpTypeForwardPointer [[pointer:%\\w+]] " \
- "Workgroup\n" \
- "%type = OpTypeForwardPointer %pointer Workgroup\n" CODE \
- "; CHECK: [[pointer]] = OpTypePointer Workgroup [[realtype]]\n" \
- "%pointer = OpTypePointer Workgroup %realtype\n")
+#define MatchFpF(N, CODE) \
+ MatchF(N, \
+ "; CHECK: OpTypeForwardPointer [[type:%\\w+]] Workgroup\n" \
+ "OpTypeForwardPointer %type Workgroup\n" CODE \
+ "; CHECK: [[type]] = OpTypePointer Workgroup [[realtype]]\n" \
+ "%type = OpTypePointer Workgroup %realtype\n")
#define MatchFp1(T) MatchFpF(ForwardPointerOf##T, MatchPart1(T, realtype))
#define MatchFp2(T, A) \
MatchFpF(ForwardPointerOf##T, MatchPart1(A, a) MatchPart2(T, realtype, a))
-// Disabled currently, causes assertion failures
-/*
MatchFp1(Float);
MatchFp2(Array, Float);
MatchFp2(RuntimeArray, Float);
MatchFp2(Struct, Float);
MatchFp2(Function, Float);
-// */
} // namespace
} // namespace spvtools
diff --git a/test/opt/type_manager_test.cpp b/test/opt/type_manager_test.cpp
index 1072c36..c5ff9e6 100644
--- a/test/opt/type_manager_test.cpp
+++ b/test/opt/type_manager_test.cpp
@@ -117,10 +117,10 @@
types.emplace_back(new SampledImage(image2));
// Array
- types.emplace_back(new Array(f32, 100));
- types.emplace_back(new Array(f32, 42));
+ types.emplace_back(new Array(f32, 100, 1u));
+ types.emplace_back(new Array(f32, 42, 2u));
auto* a42f32 = types.back().get();
- types.emplace_back(new Array(u64, 24));
+ types.emplace_back(new Array(u64, 24, s32, {42}));
// RuntimeArray
types.emplace_back(new RuntimeArray(v3f32));
diff --git a/test/opt/types_test.cpp b/test/opt/types_test.cpp
index 7426ed7..2c0d8db 100644
--- a/test/opt/types_test.cpp
+++ b/test/opt/types_test.cpp
@@ -72,7 +72,7 @@
SpvAccessQualifierWriteOnly);
TestMultipleInstancesOfTheSameType(Sampler);
TestMultipleInstancesOfTheSameType(SampledImage, image_t_.get());
-TestMultipleInstancesOfTheSameType(Array, u32_t_.get(), 10);
+TestMultipleInstancesOfTheSameType(Array, u32_t_.get(), 10, 3);
TestMultipleInstancesOfTheSameType(RuntimeArray, u32_t_.get());
TestMultipleInstancesOfTheSameType(Struct, std::vector<const Type*>{
u32_t_.get(), f64_t_.get()});
@@ -151,10 +151,10 @@
types.emplace_back(new SampledImage(image2));
// Array
- types.emplace_back(new Array(f32, 100));
- types.emplace_back(new Array(f32, 42));
+ types.emplace_back(new Array(f32, 100, 1u));
+ types.emplace_back(new Array(f32, 42, 2u));
auto* a42f32 = types.back().get();
- types.emplace_back(new Array(u64, 24));
+ types.emplace_back(new Array(u64, 24, s32, {42}));
// RuntimeArray
types.emplace_back(new RuntimeArray(v3f32));