Validation for Composite Extract and Insert.
authorEhsan Nasiri <ehsann@google.com>
Wed, 4 Jan 2017 20:56:17 +0000 (15:56 -0500)
committerDavid Neto <dneto@google.com>
Fri, 6 Jan 2017 22:14:38 +0000 (17:14 -0500)
The validity of each command is checked based on the descripton in
SPIR-V Spec Section 3.32.12 (Composite Instructions).

Also checked that the number of indexes passed to these commands does
not exceed the limit described in 2.17 (Universal Limits).

Also added unit tests for each one.

source/validate_id.cpp
test/val/val_id_test.cpp

index 1ab7327..34a5b7a 100644 (file)
@@ -1612,17 +1612,205 @@ bool idUsage::isValid<OpCompositeConstruct>(
     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 <>
@@ -2619,8 +2807,8 @@ bool idUsage::isValid(const spv_instruction_t* inst) {
     TODO(OpVectorInsertDynamic)
     TODO(OpVectorShuffle)
     TODO(OpCompositeConstruct)
-    TODO(OpCompositeExtract)
-    TODO(OpCompositeInsert)
+    CASE(OpCompositeExtract)
+    CASE(OpCompositeInsert)
     TODO(OpCopyObject)
     TODO(OpTranspose)
     TODO(OpSNegate)
index bc26b33..fa58ea5 100644 (file)
@@ -1837,7 +1837,7 @@ TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSizeTypeBad) {
   EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
 }
 
-string opAccessChainSpirvSetup = R"(
+const char kDeeplyNestedStructureSetup[] = R"(
 %void = OpTypeVoid
 %void_f  = OpTypeFunction %void
 %int = OpTypeInt 32 0
@@ -1855,7 +1855,7 @@ string opAccessChainSpirvSetup = R"(
 %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;
@@ -1905,7 +1905,7 @@ bool AccessChainRequiresElemId(const std::string& instr) {
 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
@@ -1919,7 +1919,7 @@ TEST_P(AccessChainInstructionTest, AccessChainGood) {
 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
@@ -1938,7 +1938,7 @@ OpFunctionEnd
 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
@@ -1956,7 +1956,7 @@ OpFunctionEnd
 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
@@ -1976,7 +1976,7 @@ TEST_P(AccessChainInstructionTest,
        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
@@ -1997,7 +1997,7 @@ TEST_P(AccessChainInstructionTest,
        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
@@ -2015,7 +2015,7 @@ OpFunctionEnd
 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
@@ -2032,7 +2032,7 @@ TEST_P(AccessChainInstructionTest, AccessChainTooManyIndexesGood) {
   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";
@@ -2074,7 +2074,7 @@ TEST_P(AccessChainInstructionTest, AccessChainTooManyIndexesBad) {
   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";
@@ -2095,7 +2095,7 @@ TEST_P(AccessChainInstructionTest, AccessChainTooManyIndexesBad) {
 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
@@ -2113,7 +2113,7 @@ OpFunctionEnd
 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
@@ -2132,7 +2132,7 @@ TEST_P(AccessChainInstructionTest,
        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
@@ -2152,7 +2152,7 @@ OpFunctionEnd
 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
@@ -2171,7 +2171,7 @@ OpFunctionEnd
 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
@@ -2198,7 +2198,7 @@ TEST_P(AccessChainInstructionTest, AccessChainIndexIntoAllTypesGood) {
   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 "
@@ -2221,7 +2221,7 @@ OpFunctionEnd
 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 +
@@ -2237,7 +2237,7 @@ OpFunctionEnd
 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 +
@@ -2258,7 +2258,7 @@ OpFunctionEnd
 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
@@ -2278,7 +2278,7 @@ TEST_P(AccessChainInstructionTest,
        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
@@ -2531,6 +2531,327 @@ OpFunctionEnd)";
                         "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"(