const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {}
#endif
-#if 0
+// Walks the composite type hierarchy starting from the base.
+// At each step, the iterator is dereferenced to get the next literal index.
+// Indexes walk the type hierarchy to the desired depth, potentially down to
+// scalar granularity. The first index in Indexes will select the top-level
+// member/element/component/element of the base composite. All composite
+// constituents use zero-based numbering, as described by their OpType...
+// instruction. The second index will apply similarly to that result, and so
+// on. Once any non-composite type is reached, there must be no remaining
+// (unused) indexes.
+// Returns true on success and false otherwise.
+// If successful, the final type reached by indexing is returned by reference.
+// If an error occurs, the error string is returned by reference.
+bool walkCompositeTypeHierarchy(
+ const ValidationState_t& module,
+ std::vector<uint32_t>::const_iterator word_iter,
+ std::vector<uint32_t>::const_iterator word_iter_end,
+ const libspirv::Instruction* base,
+ const libspirv::Instruction** result_type_instr,
+ std::function<std::string(void)> instr_name, std::ostream* error) {
+ auto cur_type = base;
+ for (; word_iter != word_iter_end; ++word_iter) {
+ switch (cur_type->opcode()) {
+ case SpvOpTypeMatrix:
+ case SpvOpTypeVector:
+ case SpvOpTypeArray:
+ case SpvOpTypeRuntimeArray: {
+ // In OpTypeMatrix, OpTypeVector, OpTypeArray, and OpTypeRuntimeArray,
+ // word 2 is the Element Type.
+ cur_type = module.FindDef(cur_type->word(2));
+ break;
+ }
+ case SpvOpTypeStruct: {
+ // Get the index into the structure.
+ const uint32_t cur_index = *word_iter;
+ // The index points to the struct member we want, therefore, the index
+ // should be less than the number of struct members.
+ const uint32_t num_struct_members =
+ static_cast<uint32_t>(cur_type->words().size() - 2);
+ if (cur_index >= num_struct_members) {
+ *error << "Index is out of bound: " << instr_name()
+ << " can not find index " << cur_index
+ << " into the structure <id> '" << cur_type->id()
+ << "'. This structure has " << num_struct_members
+ << " members. Largest valid index is "
+ << num_struct_members - 1 << ".";
+ return false;
+ }
+ // Struct members IDs start at word 2 of OpTypeStruct.
+ auto structMemberId = cur_type->word(cur_index + 2);
+ cur_type = module.FindDef(structMemberId);
+ break;
+ }
+ default: {
+ // Give an error. reached non-composite type while indexes still remain.
+ *error << instr_name() << " reached non-composite type while indexes "
+ "still remain to be traversed.";
+ return false;
+ }
+ }
+ }
+ *result_type_instr = cur_type;
+ return true;
+}
+
template <>
-bool idUsage::isValid<OpCompositeExtract>(
- const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {}
-#endif
+bool idUsage::isValid<SpvOpCompositeExtract>(const spv_instruction_t* inst,
+ const spv_opcode_desc) {
+ auto instr_name = [&inst]() {
+ std::string name =
+ "Op" + std::string(spvOpcodeString(static_cast<SpvOp>(inst->opcode)));
+ return name;
+ };
+
+ // Remember the result type. Result Type is at word 1.
+ // This will be used to make sure the indexing results in the same type.
+ const size_t resultTypeIndex = 1;
+ auto resultTypeInstr = module_.FindDef(inst->words[resultTypeIndex]);
+
+ // The Composite <id> is at word 3. ID definition checks ensure this id is
+ // already defined.
+ auto baseInstr = module_.FindDef(inst->words[3]);
+ auto curTypeInstr = module_.FindDef(baseInstr->type_id());
+
+ // Check Universal Limit (SPIR-V Spec. Section 2.17).
+ // The number of indexes passed to OpCompositeExtract may not exceed 255.
+ // The instruction includes 4 words + N words (for N indexes)
+ const size_t num_indexes = inst->words.size() - 4;
+ const size_t num_indexes_limit = 255;
+ if (num_indexes > num_indexes_limit) {
+ DIAG(resultTypeIndex) << "The number of indexes in " << instr_name()
+ << " may not exceed " << num_indexes_limit
+ << ". Found " << num_indexes << " indexes.";
+ return false;
+ }
+ if (num_indexes <= 0) {
+ DIAG(resultTypeIndex) << "No Indexes were passed to " << instr_name()
+ << ".";
+ return false;
+ }
+
+ // Walk down the composite type structure. Indexes start at word 4.
+ const libspirv::Instruction* indexedTypeInstr = nullptr;
+ std::ostringstream error;
+ bool success = walkCompositeTypeHierarchy(
+ module_, inst->words.begin() + 4, inst->words.end(), curTypeInstr,
+ &indexedTypeInstr, instr_name, &error);
+ if (!success) {
+ DIAG(resultTypeIndex) << error.str();
+ return success;
+ }
+
+ // At this point, we have fully walked down from the base using the indexes.
+ // The type being pointed to should be the same as the result type.
+ if (indexedTypeInstr->id() != resultTypeInstr->id()) {
+ DIAG(resultTypeIndex)
+ << instr_name() << " result type (Op"
+ << spvOpcodeString(static_cast<SpvOp>(resultTypeInstr->opcode()))
+ << ") does not match the type that results from indexing into the "
+ "composite (Op"
+ << spvOpcodeString(static_cast<SpvOp>(indexedTypeInstr->opcode()))
+ << ").";
+ return false;
+ }
+
+ return true;
+}
-#if 0
template <>
-bool idUsage::isValid<OpCompositeInsert>(
- const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {}
-#endif
+bool idUsage::isValid<SpvOpCompositeInsert>(const spv_instruction_t* inst,
+ const spv_opcode_desc) {
+ auto instr_name = [&inst]() {
+ std::string name =
+ "Op" + std::string(spvOpcodeString(static_cast<SpvOp>(inst->opcode)));
+ return name;
+ };
+
+ // Result Type must be the same as Composite type. Result Type <id> is the
+ // word at index 1. Composite is at word 4.
+ // The grammar guarantees that the instruction has at least 5 words.
+ // ID definition checks ensure these IDs are already defined.
+ const size_t resultTypeIndex = 1;
+ const size_t compositeIndex = 4;
+ auto resultTypeInstr = module_.FindDef(inst->words[resultTypeIndex]);
+ auto compositeInstr = module_.FindDef(inst->words[compositeIndex]);
+ auto compositeTypeInstr = module_.FindDef(compositeInstr->type_id());
+ if (resultTypeInstr != compositeTypeInstr) {
+ DIAG(resultTypeIndex)
+ << "The Result Type must be the same as Composite type in "
+ << instr_name() << ".";
+ return false;
+ }
+
+ // Check Universal Limit (SPIR-V Spec. Section 2.17).
+ // The number of indexes passed to OpCompositeInsert may not exceed 255.
+ // The instruction includes 5 words + N words (for N indexes)
+ const size_t num_indexes = inst->words.size() - 5;
+ const size_t num_indexes_limit = 255;
+ if (num_indexes > num_indexes_limit) {
+ DIAG(resultTypeIndex) << "The number of indexes in " << instr_name()
+ << " may not exceed " << num_indexes_limit
+ << ". Found " << num_indexes << " indexes.";
+ return false;
+ }
+ if (num_indexes <= 0) {
+ DIAG(resultTypeIndex) << "No Indexes were passed to " << instr_name()
+ << ".";
+ return false;
+ }
+
+ // Walk the composite type structure. Indexes start at word 5.
+ const libspirv::Instruction* indexedTypeInstr = nullptr;
+ std::ostringstream error;
+ bool success = walkCompositeTypeHierarchy(
+ module_, inst->words.begin() + 5, inst->words.end(), compositeTypeInstr,
+ &indexedTypeInstr, instr_name, &error);
+ if (!success) {
+ DIAG(resultTypeIndex) << error.str();
+ return success;
+ }
+
+ // At this point, we have fully walked down from the base using the indexes.
+ // The type being pointed to should be the same as the object type that is
+ // about to be inserted.
+ auto objectIdIndex = 3;
+ auto objectInstr = module_.FindDef(inst->words[objectIdIndex]);
+ auto objectTypeInstr = module_.FindDef(objectInstr->type_id());
+ if (indexedTypeInstr->id() != objectTypeInstr->id()) {
+ DIAG(objectIdIndex)
+ << "The Object type (Op"
+ << spvOpcodeString(static_cast<SpvOp>(objectTypeInstr->opcode()))
+ << ") in " << instr_name() << " does not match the type that results "
+ "from indexing into the Composite (Op"
+ << spvOpcodeString(static_cast<SpvOp>(indexedTypeInstr->opcode()))
+ << ").";
+ return false;
+ }
+
+ return true;
+}
#if 0
template <>
TODO(OpVectorInsertDynamic)
TODO(OpVectorShuffle)
TODO(OpCompositeConstruct)
- TODO(OpCompositeExtract)
- TODO(OpCompositeInsert)
+ CASE(OpCompositeExtract)
+ CASE(OpCompositeInsert)
TODO(OpCopyObject)
TODO(OpTranspose)
TODO(OpSNegate)
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
}
-string opAccessChainSpirvSetup = R"(
+const char kDeeplyNestedStructureSetup[] = R"(
%void = OpTypeVoid
%void_f = OpTypeFunction %void
%int = OpTypeInt 32 0
%int_3 = OpConstant %int 3
%int_5 = OpConstant %int 5
-; Let's make the following structures to test OpAccessChain
+; Making the following nested structures.
;
; struct S {
; bool b;
TEST_P(AccessChainInstructionTest, AccessChainGood) {
const std::string instr = GetParam();
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
- string spirv = kGLSL450MemoryModel + opAccessChainSpirvSetup +
+ string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup +
"%float_entry = " + instr +
R"( %_ptr_Private_float %my_matrix )" + elem + R"(%int_0 %int_1
OpReturn
TEST_P(AccessChainInstructionTest, AccessChainResultTypeBad) {
const std::string instr = GetParam();
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
- string spirv = kGLSL450MemoryModel + opAccessChainSpirvSetup + R"(
+ string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%float_entry = )" +
instr + R"( %float %my_matrix )" + elem + R"(%int_0 %int_1
OpReturn
TEST_P(AccessChainInstructionTest, AccessChainBaseTypeVoidBad) {
const std::string instr = GetParam();
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
- string spirv = kGLSL450MemoryModel + opAccessChainSpirvSetup + R"(
+ string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%float_entry = )" +
instr + " %_ptr_Private_float %void " + elem + R"(%int_0 %int_1
OpReturn
TEST_P(AccessChainInstructionTest, AccessChainBaseTypeNonPtrVariableBad) {
const std::string instr = GetParam();
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
- string spirv = kGLSL450MemoryModel + opAccessChainSpirvSetup + R"(
+ string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%entry = )" + instr +
R"( %_ptr_Private_float %_ptr_Private_float )" + elem +
R"(%int_0 %int_1
AccessChainResultAndBaseStorageClassDoesntMatchBad) {
const std::string instr = GetParam();
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
- string spirv = kGLSL450MemoryModel + opAccessChainSpirvSetup + R"(
+ string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%entry = )" + instr +
R"( %_ptr_Function_float %my_matrix )" + elem +
R"(%int_0 %int_1
AccessChainBasePtrNotPointingToCompositeBad) {
const std::string instr = GetParam();
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
- string spirv = kGLSL450MemoryModel + opAccessChainSpirvSetup + R"(
+ string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%entry = )" + instr +
R"( %_ptr_Private_float %my_float_var )" + elem + R"(%int_0
OpReturn
TEST_P(AccessChainInstructionTest, AccessChainMissingIndexesBad) {
const std::string instr = GetParam();
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
- string spirv = kGLSL450MemoryModel + opAccessChainSpirvSetup + R"(
+ string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%entry = )" + instr +
R"( %_ptr_Private_float %my_float_var )" + elem + R"(
OpReturn
const std::string instr = GetParam();
const std::string elem = AccessChainRequiresElemId(instr) ? " %int_0 " : "";
int depth = 255;
- std::string header = kGLSL450MemoryModel + opAccessChainSpirvSetup;
+ std::string header = kGLSL450MemoryModel + kDeeplyNestedStructureSetup;
header.erase(header.find("%func"));
std::ostringstream spirv;
spirv << header << "\n";
const std::string instr = GetParam();
const std::string elem = AccessChainRequiresElemId(instr) ? " %int_0 " : "";
std::ostringstream spirv;
- spirv << kGLSL450MemoryModel << opAccessChainSpirvSetup;
+ spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup;
spirv << "%entry = " << instr << " %_ptr_Private_float %my_matrix" << elem;
for (int i = 0; i < 256; ++i) {
spirv << " %int_0";
TEST_P(AccessChainInstructionTest, AccessChainUndefinedIndexBad) {
const std::string instr = GetParam();
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
- string spirv = kGLSL450MemoryModel + opAccessChainSpirvSetup + R"(
+ string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%entry = )" + instr +
R"( %_ptr_Private_float %my_matrix )" + elem + R"(%float %int_1
OpReturn
TEST_P(AccessChainInstructionTest, AccessChainStructIndexNotConstantBad) {
const std::string instr = GetParam();
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
- string spirv = kGLSL450MemoryModel + opAccessChainSpirvSetup + R"(
+ string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%f = )" + instr + R"( %_ptr_Uniform_float %blockName_var )" +
elem + R"(%int_0 %spec_int %int_2
OpReturn
AccessChainStructResultTypeDoesntMatchIndexedTypeBad) {
const std::string instr = GetParam();
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
- string spirv = kGLSL450MemoryModel + opAccessChainSpirvSetup + R"(
+ string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%entry = )" + instr +
R"( %_ptr_Uniform_float %blockName_var )" + elem +
R"(%int_0 %int_1 %int_2
TEST_P(AccessChainInstructionTest, AccessChainStructTooManyIndexesBad) {
const std::string instr = GetParam();
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
- string spirv = kGLSL450MemoryModel + opAccessChainSpirvSetup + R"(
+ string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%entry = )" + instr +
R"( %_ptr_Uniform_float %blockName_var )" + elem +
R"(%int_0 %int_2 %int_2
TEST_P(AccessChainInstructionTest, AccessChainStructIndexOutOfBoundBad) {
const std::string instr = GetParam();
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
- string spirv = kGLSL450MemoryModel + opAccessChainSpirvSetup + R"(
+ string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%entry = )" + instr +
R"( %_ptr_Uniform_float %blockName_var )" + elem +
R"(%int_3 %int_2 %int_2
const std::string instr = GetParam();
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
ostringstream spirv;
- spirv << kGLSL450MemoryModel << opAccessChainSpirvSetup << std::endl;
+ spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup << std::endl;
spirv << "%ss = " << instr << " %_ptr_Uniform_struct_s %blockName_var "
<< elem << "%int_0" << std::endl;
spirv << "%sa = " << instr << " %_ptr_Uniform_array5_mat4x3 %blockName_var "
TEST_P(AccessChainInstructionTest, AccessChainIndexIntoRuntimeArrayGood) {
const std::string instr = GetParam();
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
- string spirv = kGLSL450MemoryModel + opAccessChainSpirvSetup + R"(
+ string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%runtime_arr_entry = )" +
instr +
R"( %_ptr_Uniform_float %blockName_var )" + elem +
TEST_P(AccessChainInstructionTest, AccessChainIndexIntoRuntimeArrayBad) {
const std::string instr = GetParam();
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
- string spirv = kGLSL450MemoryModel + opAccessChainSpirvSetup + R"(
+ string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%runtime_arr_entry = )" +
instr +
R"( %_ptr_Uniform_float %blockName_var )" + elem +
TEST_P(AccessChainInstructionTest, AccessChainMatrixMoreArgsThanNeededBad) {
const std::string instr = GetParam();
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
- string spirv = kGLSL450MemoryModel + opAccessChainSpirvSetup + R"(
+ string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%entry = )" + instr +
R"( %_ptr_Private_float %my_matrix )" + elem +
R"(%int_0 %int_1 %int_0
AccessChainResultTypeDoesntMatchIndexedTypeBad) {
const std::string instr = GetParam();
const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : "";
- string spirv = kGLSL450MemoryModel + opAccessChainSpirvSetup + R"(
+ string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"(
%entry = )" + instr +
R"( %_ptr_Private_mat4x3 %my_matrix )" + elem +
R"(%int_0 %int_1
"as an operand of <id> '24'."));
}
+// Valid: Get a float in a matrix using CompositeExtract.
+// Valid: Insert float into a matrix using CompositeInsert.
+TEST_F(ValidateIdWithMessage, CompositeExtractInsertGood) {
+ ostringstream spirv;
+ spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup << std::endl;
+ spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl;
+ spirv << "%float_entry = OpCompositeExtract %float %matrix 0 1" << std::endl;
+
+ // To test CompositeInsert, insert the object back in after extraction.
+ spirv << "%new_composite = OpCompositeInsert %mat4x3 %float_entry %matrix 0 1"
+ << std::endl;
+ spirv << R"(OpReturn
+ OpFunctionEnd)";
+ CompileSuccessfully(spirv.str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+// Valid. Tests both CompositeExtract and CompositeInsert with 255 indexes.
+TEST_F(ValidateIdWithMessage, CompositeExtractInsertLimitsGood) {
+ int depth = 255;
+ std::string header = kGLSL450MemoryModel + kDeeplyNestedStructureSetup;
+ header.erase(header.find("%func"));
+ std::ostringstream spirv;
+ spirv << header << std::endl;
+
+ // Build nested structures. Struct 'i' contains struct 'i-1'
+ spirv << "%s_depth_1 = OpTypeStruct %float\n";
+ for (int i = 2; i <= depth; ++i) {
+ spirv << "%s_depth_" << i << " = OpTypeStruct %s_depth_" << i - 1 << "\n";
+ }
+
+ // Define Pointer and Variable to use for CompositeExtract/Insert.
+ spirv << "%_ptr_Uniform_deep_struct = OpTypePointer Uniform %s_depth_"
+ << depth << "\n";
+ spirv << "%deep_var = OpVariable %_ptr_Uniform_deep_struct Uniform\n";
+
+ // Function Start
+ spirv << R"(
+ %func = OpFunction %void None %void_f
+ %my_label = OpLabel
+ )";
+
+ // OpCompositeExtract/Insert with 'n' indexes (n = depth)
+ spirv << "%deep = OpLoad %s_depth_" << depth << " %deep_var" << std::endl;
+ spirv << "%entry = OpCompositeExtract %float %deep";
+ for (int i = 0; i < depth; ++i) {
+ spirv << " 0";
+ }
+ spirv << std::endl;
+ spirv << "%new_composite = OpCompositeInsert %s_depth_" << depth
+ << " %entry %deep";
+ for (int i = 0; i < depth; ++i) {
+ spirv << " 0";
+ }
+ spirv << std::endl;
+
+ // Function end
+ spirv << R"(
+ OpReturn
+ OpFunctionEnd
+ )";
+ CompileSuccessfully(spirv.str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+// Invalid: 256 indexes passed to OpCompositeExtract. Limit is 255.
+TEST_F(ValidateIdWithMessage, CompositeExtractArgCountExceededLimitBad) {
+ std::ostringstream spirv;
+ spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup;
+ spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl;
+ spirv << "%entry = OpCompositeExtract %float %matrix";
+ for (int i = 0; i < 256; ++i) {
+ spirv << " 0";
+ }
+ spirv << R"(
+ OpReturn
+ OpFunctionEnd
+ )";
+ CompileSuccessfully(spirv.str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("The number of indexes in OpCompositeExtract may not "
+ "exceed 255. Found 256 indexes."));
+}
+
+// Invalid: 256 indexes passed to OpCompositeInsert. Limit is 255.
+TEST_F(ValidateIdWithMessage, CompositeInsertArgCountExceededLimitBad) {
+ std::ostringstream spirv;
+ spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup;
+ spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl;
+ spirv << "%new_composite = OpCompositeInsert %mat4x3 %int_0 %matrix";
+ for (int i = 0; i < 256; ++i) {
+ spirv << " 0";
+ }
+ spirv << R"(
+ OpReturn
+ OpFunctionEnd
+ )";
+ CompileSuccessfully(spirv.str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("The number of indexes in OpCompositeInsert may not "
+ "exceed 255. Found 256 indexes."));
+}
+
+// Invalid: In OpCompositeInsert, result type must be the same as composite type
+TEST_F(ValidateIdWithMessage, CompositeInsertWrongResultTypeBad) {
+ ostringstream spirv;
+ spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup << std::endl;
+ spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl;
+ spirv << "%float_entry = OpCompositeExtract %float %matrix 0 1" << std::endl;
+ spirv << "%new_composite = OpCompositeInsert %float %float_entry %matrix 0 1"
+ << std::endl;
+ spirv << R"(OpReturn
+ OpFunctionEnd)";
+ CompileSuccessfully(spirv.str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("The Result Type must be the same as Composite type"));
+}
+
+// Invalid: No Indexes were passed to OpCompositeExtract.
+TEST_F(ValidateIdWithMessage, CompositeExtractMissingIndexesBad) {
+ ostringstream spirv;
+ spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup << std::endl;
+ spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl;
+ spirv << "%float_entry = OpCompositeExtract %float %matrix" << std::endl;
+ spirv << R"(OpReturn
+ OpFunctionEnd)";
+ CompileSuccessfully(spirv.str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("No Indexes were passed to OpCompositeExtract"));
+}
+
+// Invalid: No Indexes were passed to OpCompositeInsert.
+TEST_F(ValidateIdWithMessage, CompositeInsertMissingIndexesBad) {
+ ostringstream spirv;
+ spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup << std::endl;
+ spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl;
+ spirv << "%new_composite = OpCompositeInsert %mat4x3 %int_0 %matrix";
+ spirv << R"(
+ OpReturn
+ OpFunctionEnd)";
+ CompileSuccessfully(spirv.str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("No Indexes were passed to OpCompositeInsert"));
+}
+
+// Valid: Tests that we can index into Struct, Array, Matrix, and Vector!
+TEST_F(ValidateIdWithMessage, CompositeExtractInsertIndexIntoAllTypesGood) {
+ // indexes that we are passing are: 0, 3, 1, 2, 0
+ // 0 will select the struct_s within the base struct (blockName)
+ // 3 will select the Array that contains 5 matrices
+ // 1 will select the Matrix that is at index 1 of the array
+ // 2 will select the column (which is a vector) within the matrix at index 2
+ // 0 will select the element at the index 0 of the vector. (which is a float).
+ ostringstream spirv;
+ spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup << R"(
+ %myblock = OpLoad %struct_blockName %blockName_var
+ %ss = OpCompositeExtract %struct_s %myblock 0
+ %sa = OpCompositeExtract %array5_mat4x3 %myblock 0 3
+ %sm = OpCompositeExtract %mat4x3 %myblock 0 3 1
+ %sc = OpCompositeExtract %v3float %myblock 0 3 1 2
+ %fl = OpCompositeExtract %float %myblock 0 3 1 2 0
+ ;
+ ; Now let's insert back at different levels...
+ ;
+ %b1 = OpCompositeInsert %struct_blockName %ss %myblock 0
+ %b2 = OpCompositeInsert %struct_blockName %sa %myblock 0 3
+ %b3 = OpCompositeInsert %struct_blockName %sm %myblock 0 3 1
+ %b4 = OpCompositeInsert %struct_blockName %sc %myblock 0 3 1 2
+ %b5 = OpCompositeInsert %struct_blockName %fl %myblock 0 3 1 2 0
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv.str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+// Invalid. More indexes are provided than needed for OpCompositeExtract.
+TEST_F(ValidateIdWithMessage, CompositeExtractReachedScalarBad) {
+ // indexes that we are passing are: 0, 3, 1, 2, 0
+ // 0 will select the struct_s within the base struct (blockName)
+ // 3 will select the Array that contains 5 matrices
+ // 1 will select the Matrix that is at index 1 of the array
+ // 2 will select the column (which is a vector) within the matrix at index 2
+ // 0 will select the element at the index 0 of the vector. (which is a float).
+ ostringstream spirv;
+ spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup << R"(
+ %myblock = OpLoad %struct_blockName %blockName_var
+ %fl = OpCompositeExtract %float %myblock 0 3 1 2 0 1
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv.str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("OpCompositeExtract reached non-composite type while "
+ "indexes still remain to be traversed."));
+}
+
+// Invalid. More indexes are provided than needed for OpCompositeInsert.
+TEST_F(ValidateIdWithMessage, CompositeInsertReachedScalarBad) {
+ // indexes that we are passing are: 0, 3, 1, 2, 0
+ // 0 will select the struct_s within the base struct (blockName)
+ // 3 will select the Array that contains 5 matrices
+ // 1 will select the Matrix that is at index 1 of the array
+ // 2 will select the column (which is a vector) within the matrix at index 2
+ // 0 will select the element at the index 0 of the vector. (which is a float).
+ ostringstream spirv;
+ spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup << R"(
+ %myblock = OpLoad %struct_blockName %blockName_var
+ %fl = OpCompositeExtract %float %myblock 0 3 1 2 0
+ %b5 = OpCompositeInsert %struct_blockName %fl %myblock 0 3 1 2 0 1
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv.str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("OpCompositeInsert reached non-composite type while "
+ "indexes still remain to be traversed."));
+}
+
+// Invalid. Result type doesn't match the type we get from indexing into
+// the composite.
+TEST_F(ValidateIdWithMessage,
+ CompositeExtractResultTypeDoesntMatchIndexedTypeBad) {
+ // indexes that we are passing are: 0, 3, 1, 2, 0
+ // 0 will select the struct_s within the base struct (blockName)
+ // 3 will select the Array that contains 5 matrices
+ // 1 will select the Matrix that is at index 1 of the array
+ // 2 will select the column (which is a vector) within the matrix at index 2
+ // 0 will select the element at the index 0 of the vector. (which is a float).
+ ostringstream spirv;
+ spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup << R"(
+ %myblock = OpLoad %struct_blockName %blockName_var
+ %fl = OpCompositeExtract %int %myblock 0 3 1 2 0
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv.str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("OpCompositeExtract result type (OpTypeInt) does not "
+ "match the type that results from indexing into the "
+ "composite (OpTypeFloat)."));
+}
+
+// Invalid. Given object type doesn't match the type we get from indexing into
+// the composite.
+TEST_F(ValidateIdWithMessage,
+ CompositeInsertObjectTypeDoesntMatchIndexedTypeBad) {
+ // indexes that we are passing are: 0, 3, 1, 2, 0
+ // 0 will select the struct_s within the base struct (blockName)
+ // 3 will select the Array that contains 5 matrices
+ // 1 will select the Matrix that is at index 1 of the array
+ // 2 will select the column (which is a vector) within the matrix at index 2
+ // 0 will select the element at the index 0 of the vector. (which is a float).
+ // We are trying to insert an integer where we should be inserting a float.
+ ostringstream spirv;
+ spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup << R"(
+ %myblock = OpLoad %struct_blockName %blockName_var
+ %b5 = OpCompositeInsert %struct_blockName %int_0 %myblock 0 3 1 2 0
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv.str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("he Object type (OpTypeInt) in OpCompositeInsert does "
+ "not match the type that results from indexing into "
+ "the Composite (OpTypeFloat)."));
+}
+
+// Invalid. Index into a struct is larger than the number of struct members.
+TEST_F(ValidateIdWithMessage, CompositeExtractStructIndexOutOfBoundBad) {
+ // struct_blockName has 3 members (index 0,1,2). We'll try to access index 3.
+ ostringstream spirv;
+ spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup << R"(
+ %myblock = OpLoad %struct_blockName %blockName_var
+ %ss = OpCompositeExtract %struct_s %myblock 3
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv.str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Index is out of bound: OpCompositeExtract can not "
+ "find index 3 into the structure <id> '26'. This "
+ "structure has 3 members. Largest valid index is 2."));
+}
+
+// Invalid. Index into a struct is larger than the number of struct members.
+TEST_F(ValidateIdWithMessage, CompositeInsertStructIndexOutOfBoundBad) {
+ // struct_blockName has 3 members (index 0,1,2). We'll try to access index 3.
+ ostringstream spirv;
+ spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup << R"(
+ %myblock = OpLoad %struct_blockName %blockName_var
+ %ss = OpCompositeExtract %struct_s %myblock 0
+ %new_composite = OpCompositeInsert %struct_blockName %ss %myblock 3
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CompileSuccessfully(spirv.str());
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Index is out of bound: OpCompositeInsert can not find "
+ "index 3 into the structure <id> '26'. This structure "
+ "has 3 members. Largest valid index is 2."));
+}
+
#if 0
TEST_F(ValidateIdWithMessage, OpFunctionCallArgumentCountBar) {
const char *spirv = R"(