瀏覽代碼

Updated spirv-tools.

Бранимир Караџић 1 月之前
父節點
當前提交
a13637f390
共有 63 個文件被更改,包括 5333 次插入4092 次删除
  1. 1 1
      3rdparty/spirv-tools/include/generated/build-version.inc
  2. 1313 1310
      3rdparty/spirv-tools/include/generated/core_tables_body.inc
  3. 1 0
      3rdparty/spirv-tools/include/generated/core_tables_header.inc
  4. 2 1
      3rdparty/spirv-tools/include/generated/generators.inc
  5. 1 0
      3rdparty/spirv-tools/include/spirv-tools/libspirv.h
  6. 3 0
      3rdparty/spirv-tools/source/ext_inst.cpp
  7. 38 16
      3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp
  8. 14 6
      3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.h
  9. 46 0
      3rdparty/spirv-tools/source/opt/amd_ext_to_khr.cpp
  10. 11 3
      3rdparty/spirv-tools/source/opt/basic_block.cpp
  11. 4 0
      3rdparty/spirv-tools/source/opt/ccp_pass.cpp
  12. 16 7
      3rdparty/spirv-tools/source/opt/cfg.cpp
  13. 1 0
      3rdparty/spirv-tools/source/opt/combine_access_chains.cpp
  14. 6 2
      3rdparty/spirv-tools/source/opt/const_folding_rules.cpp
  15. 102 9
      3rdparty/spirv-tools/source/opt/convert_to_half_pass.cpp
  16. 3 0
      3rdparty/spirv-tools/source/opt/convert_to_half_pass.h
  17. 13 2
      3rdparty/spirv-tools/source/opt/convert_to_sampled_image_pass.cpp
  18. 1 0
      3rdparty/spirv-tools/source/opt/debug_info_manager.cpp
  19. 3 0
      3rdparty/spirv-tools/source/opt/fix_func_call_arguments.cpp
  20. 3 0
      3rdparty/spirv-tools/source/opt/fold.cpp
  21. 24 6
      3rdparty/spirv-tools/source/opt/fold_spec_constant_op_and_composite_pass.cpp
  22. 26 8
      3rdparty/spirv-tools/source/opt/fold_spec_constant_op_and_composite_pass.h
  23. 153 1
      3rdparty/spirv-tools/source/opt/folding_rules.cpp
  24. 4 0
      3rdparty/spirv-tools/source/opt/function.cpp
  25. 1 1
      3rdparty/spirv-tools/source/opt/function.h
  26. 90 0
      3rdparty/spirv-tools/source/opt/graph.cpp
  27. 124 0
      3rdparty/spirv-tools/source/opt/graph.h
  28. 2 0
      3rdparty/spirv-tools/source/opt/if_conversion.cpp
  29. 26 10
      3rdparty/spirv-tools/source/opt/instruction.cpp
  30. 2 2
      3rdparty/spirv-tools/source/opt/instruction.h
  31. 64 34
      3rdparty/spirv-tools/source/opt/ir_builder.h
  32. 14 0
      3rdparty/spirv-tools/source/opt/ir_context.cpp
  33. 50 3
      3rdparty/spirv-tools/source/opt/ir_context.h
  34. 36 0
      3rdparty/spirv-tools/source/opt/ir_loader.cpp
  35. 3 0
      3rdparty/spirv-tools/source/opt/ir_loader.h
  36. 0 1
      3rdparty/spirv-tools/source/opt/licm_pass.cpp
  37. 10 5
      3rdparty/spirv-tools/source/opt/loop_descriptor.cpp
  38. 8 3
      3rdparty/spirv-tools/source/opt/loop_descriptor.h
  39. 11 7
      3rdparty/spirv-tools/source/opt/loop_fusion_pass.cpp
  40. 1 1
      3rdparty/spirv-tools/source/opt/loop_fusion_pass.h
  41. 10 4
      3rdparty/spirv-tools/source/opt/loop_peeling.cpp
  42. 109 43
      3rdparty/spirv-tools/source/opt/loop_unroller.cpp
  43. 3 1
      3rdparty/spirv-tools/source/opt/loop_unswitch_pass.cpp
  44. 49 18
      3rdparty/spirv-tools/source/opt/loop_utils.cpp
  45. 2 2
      3rdparty/spirv-tools/source/opt/loop_utils.h
  46. 119 33
      3rdparty/spirv-tools/source/opt/merge_return_pass.cpp
  47. 30 33
      3rdparty/spirv-tools/source/opt/merge_return_pass.h
  48. 11 0
      3rdparty/spirv-tools/source/opt/module.cpp
  49. 39 0
      3rdparty/spirv-tools/source/opt/module.h
  50. 2 0
      3rdparty/spirv-tools/source/opt/pass.cpp
  51. 40 15
      3rdparty/spirv-tools/source/opt/propagator.cpp
  52. 7 4
      3rdparty/spirv-tools/source/opt/propagator.h
  53. 1 0
      3rdparty/spirv-tools/source/opt/reduce_load_size.cpp
  54. 54 28
      3rdparty/spirv-tools/source/opt/replace_desc_array_access_using_var_index.cpp
  55. 9 9
      3rdparty/spirv-tools/source/opt/replace_desc_array_access_using_var_index.h
  56. 6 0
      3rdparty/spirv-tools/source/opt/split_combined_image_sampler_pass.cpp
  57. 2 0
      3rdparty/spirv-tools/source/opt/upgrade_memory_model.cpp
  58. 25 2
      3rdparty/spirv-tools/source/opt/value_number_table.cpp
  59. 65 0
      3rdparty/spirv-tools/source/table2.cpp
  60. 3 0
      3rdparty/spirv-tools/source/table2.h
  61. 64 39
      3rdparty/spirv-tools/source/val/validate_decorations.cpp
  62. 2440 2419
      3rdparty/spirv-tools/source/val/validate_extensions.cpp
  63. 12 3
      3rdparty/spirv-tools/source/val/validate_memory.cpp

+ 1 - 1
3rdparty/spirv-tools/include/generated/build-version.inc

@@ -1 +1 @@
-"v2025.3", "SPIRV-Tools v2025.3 v2025.3.rc1-110-g8fbe2387"
+"v2025.4", "SPIRV-Tools v2025.4 v2025.4-28-g8b4ee452"

文件差異過大導致無法顯示
+ 1313 - 1310
3rdparty/spirv-tools/include/generated/core_tables_body.inc


+ 1 - 0
3rdparty/spirv-tools/include/generated/core_tables_header.inc

@@ -130,6 +130,7 @@ enum Extension : uint32_t {
   kSPV_KHR_expect_assume,
   kSPV_KHR_float_controls,
   kSPV_KHR_float_controls2,
+  kSPV_KHR_fma,
   kSPV_KHR_fragment_shader_barycentric,
   kSPV_KHR_fragment_shading_rate,
   kSPV_KHR_integer_dot_product,

+ 2 - 1
3rdparty/spirv-tools/include/generated/generators.inc

@@ -46,4 +46,5 @@
 {45, "Kitsunebi Games", "Nuvk SPIR-V Emitter and DLSL compiler", "Kitsunebi Games Nuvk SPIR-V Emitter and DLSL compiler"},
 {46, "Nintendo", "", "Nintendo"},
 {47, "ARM", "", "ARM"},
-{48, "Goopax", "", "Goopax"},
+{48, "Goopax", "", "Goopax"},
+{49, "Icyllis Milica", "Arc3D Shader Compiler", "Icyllis Milica Arc3D Shader Compiler"},

+ 1 - 0
3rdparty/spirv-tools/include/spirv-tools/libspirv.h

@@ -365,6 +365,7 @@ typedef enum spv_ext_inst_type_t {
   SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100,
   SPV_EXT_INST_TYPE_NONSEMANTIC_VKSPREFLECTION,
   SPV_EXT_INST_TYPE_TOSA_001000_1,
+  SPV_EXT_INST_TYPE_ARM_MOTION_ENGINE_100,
 
   // Multiple distinct extended instruction set types could return this
   // value, if they are prefixed with NonSemantic. and are otherwise

+ 3 - 0
3rdparty/spirv-tools/source/ext_inst.cpp

@@ -58,6 +58,9 @@ spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) {
   if (!strcmp("TOSA.001000.1", name)) {
     return SPV_EXT_INST_TYPE_TOSA_001000_1;
   }
+  if (!strcmp("Arm.MotionEngine.100", name)) {
+    return SPV_EXT_INST_TYPE_ARM_MOTION_ENGINE_100;
+  }
   // ensure to add any known non-semantic extended instruction sets
   // above this point, and update spvExtInstIsNonSemantic()
   if (!strncmp("NonSemantic.", name, 12)) {

+ 38 - 16
3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp

@@ -273,29 +273,30 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist(
   });
 }
 
-bool AggressiveDCEPass::AggressiveDCE(Function* func) {
-  if (func->IsDeclaration()) return false;
+Pass::Status AggressiveDCEPass::AggressiveDCE(Function* func) {
+  if (func->IsDeclaration()) return Pass::Status::SuccessWithoutChange;
   std::list<BasicBlock*> structured_order;
   cfg()->ComputeStructuredOrder(func, &*func->begin(), &structured_order);
   live_local_vars_.clear();
   InitializeWorkList(func, structured_order);
   ProcessWorkList(func);
-  ProcessDebugInformation(structured_order);
+  if (ProcessDebugInformation(structured_order) == Pass::Status::Failure)
+    return Pass::Status::Failure;
   ProcessWorkList(func);
   return KillDeadInstructions(func, structured_order);
 }
 
-void AggressiveDCEPass::ProcessDebugInformation(
+Pass::Status AggressiveDCEPass::ProcessDebugInformation(
     std::list<BasicBlock*>& structured_order) {
   for (auto bi = structured_order.begin(); bi != structured_order.end(); bi++) {
-    (*bi)->ForEachInst([this](Instruction* inst) {
+    bool succeeded = (*bi)->WhileEachInst([this](Instruction* inst) {
       // DebugDeclare is not dead. It must be converted to DebugValue in a
       // later pass
       if (inst->IsNonSemanticInstruction() &&
           inst->GetShader100DebugOpcode() ==
               NonSemanticShaderDebugInfo100DebugDeclare) {
         AddToWorklist(inst);
-        return;
+        return true;
       }
 
       // If the Value of a DebugValue is killed, set Value operand to Undef
@@ -306,28 +307,33 @@ void AggressiveDCEPass::ProcessDebugInformation(
         auto def = get_def_use_mgr()->GetDef(id);
         if (!live_insts_.Set(def->unique_id())) {
           AddToWorklist(inst);
+          uint32_t undef_id = Type2Undef(def->type_id());
+          if (undef_id == 0) {
+            return false;
+          }
+          inst->SetInOperand(kDebugValueValue, {undef_id});
           context()->get_def_use_mgr()->UpdateDefUse(inst);
-          worklist_.push(def);
-          def->SetOpcode(spv::Op::OpUndef);
-          def->SetInOperands({});
           id = inst->GetSingleWordInOperand(kDebugValueLocalVariable);
           auto localVar = get_def_use_mgr()->GetDef(id);
           AddToWorklist(localVar);
           context()->get_def_use_mgr()->UpdateDefUse(localVar);
           AddOperandsToWorkList(localVar);
-          context()->get_def_use_mgr()->UpdateDefUse(def);
           id = inst->GetSingleWordInOperand(kDebugValueExpression);
           auto expression = get_def_use_mgr()->GetDef(id);
           AddToWorklist(expression);
           context()->get_def_use_mgr()->UpdateDefUse(expression);
-          return;
+          return true;
         }
       }
+      return true;
     });
+
+    if (!succeeded) return Pass::Status::Failure;
   }
+  return Pass::Status::SuccessWithoutChange;
 }
 
-bool AggressiveDCEPass::KillDeadInstructions(
+Pass::Status AggressiveDCEPass::KillDeadInstructions(
     const Function* func, std::list<BasicBlock*>& structured_order) {
   bool modified = false;
   for (auto bi = structured_order.begin(); bi != structured_order.end();) {
@@ -362,6 +368,9 @@ bool AggressiveDCEPass::KillDeadInstructions(
           // Find an undef for the return value and make sure it gets kept by
           // the pass.
           auto undef_id = Type2Undef(func->type_id());
+          if (undef_id == 0) {
+            return Pass::Status::Failure;
+          }
           auto undef = get_def_use_mgr()->GetDef(undef_id);
           live_insts_.Set(undef->unique_id());
           merge_terminator->SetOpcode(spv::Op::OpReturnValue);
@@ -380,7 +389,8 @@ bool AggressiveDCEPass::KillDeadInstructions(
       ++bi;
     }
   }
-  return modified;
+  return modified ? Pass::Status::SuccessWithChange
+                  : Pass::Status::SuccessWithoutChange;
 }
 
 void AggressiveDCEPass::ProcessWorkList(Function* func) {
@@ -640,7 +650,7 @@ void AggressiveDCEPass::InitializeWorkList(
   }
 }
 
-void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
+Pass::Status AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
   // Keep all execution modes.
   for (auto& exec : get_module()->execution_modes()) {
     AddToWorklist(&exec);
@@ -715,6 +725,9 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
   }
   if (debug_global_seen) {
     auto dbg_none = context()->get_debug_info_mgr()->GetDebugInfoNone();
+    if (dbg_none == nullptr) {
+      return Pass::Status::Failure;
+    }
     AddToWorklist(dbg_none);
   }
 
@@ -728,6 +741,7 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
       AddToWorklist(&dbg);
     }
   }
+  return Pass::Status::SuccessWithoutChange;
 }
 
 Pass::Status AggressiveDCEPass::ProcessImpl() {
@@ -755,7 +769,9 @@ Pass::Status AggressiveDCEPass::ProcessImpl() {
   // Eliminate Dead functions.
   bool modified = EliminateDeadFunctions();
 
-  InitializeModuleScopeLiveInstructions();
+  if (InitializeModuleScopeLiveInstructions() == Pass::Status::Failure) {
+    return Pass::Status::Failure;
+  }
 
   // Run |AggressiveDCE| on the remaining functions.  The order does not matter,
   // since |AggressiveDCE| is intra-procedural.  This can mean that function
@@ -763,7 +779,13 @@ Pass::Status AggressiveDCEPass::ProcessImpl() {
   // function will still be in the module after this pass.  We expect this to be
   // rare.
   for (Function& fp : *context()->module()) {
-    modified |= AggressiveDCE(&fp);
+    Pass::Status function_status = AggressiveDCE(&fp);
+    if (function_status == Pass::Status::Failure) {
+      return Pass::Status::Failure;
+    }
+    if (function_status == Pass::Status::SuccessWithChange) {
+      modified = true;
+    }
   }
 
   // If the decoration manager is kept live then the context will try to keep it

+ 14 - 6
3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.h

@@ -85,8 +85,10 @@ class AggressiveDCEPass : public MemPass {
   }
 
   // Adds entry points, execution modes and workgroup size decorations to the
-  // worklist for processing with the first function.
-  void InitializeModuleScopeLiveInstructions();
+  // worklist for processing with the first function. Returns
+  // Pass::Status::Failure if it could not create a required debug instruction.
+  // Returns Pass::Status::SuccessWithoutChange otherwise.
+  Pass::Status InitializeModuleScopeLiveInstructions();
 
   // Add |inst| to worklist_ and live_insts_.
   void AddToWorklist(Instruction* inst) {
@@ -136,7 +138,7 @@ class AggressiveDCEPass : public MemPass {
   // existing control structures will remain. This can leave not-insignificant
   // sequences of ultimately useless code.
   // TODO(): Remove useless control constructs.
-  bool AggressiveDCE(Function* func);
+  Pass::Status AggressiveDCE(Function* func);
 
   Pass::Status ProcessImpl();
 
@@ -154,11 +156,17 @@ class AggressiveDCEPass : public MemPass {
   // marked as live in the work list. DebugDeclare's are marked live now, and
   // DebugValue Value operands are set to OpUndef.  The work list will be empty
   // at the end.
-  void ProcessDebugInformation(std::list<BasicBlock*>& structured_order);
+  // Returns Pass::Status::Failure if it could not create an OpUndef.
+  // Otherwise, returns Pass::Status::SuccessWithChange if it made changes,
+  Pass::Status ProcessDebugInformation(
+      std::list<BasicBlock*>& structured_order);
 
   // Kills any instructions in |func| that have not been marked as live.
-  bool KillDeadInstructions(const Function* func,
-                            std::list<BasicBlock*>& structured_order);
+  // Returns Pass::Status::Failure if it could not create an OpUndef.
+  // Otherwise, returns Pass::Status::SuccessWithChange if it made changes,
+  // and Pass::Status::SuccessWithoutChange otherwise.
+  Pass::Status KillDeadInstructions(const Function* func,
+                                    std::list<BasicBlock*>& structured_order);
 
   // Adds the instructions that define the operands of |inst| to the work list.
   void AddOperandsToWorkList(const Instruction* inst);

+ 46 - 0
3rdparty/spirv-tools/source/opt/amd_ext_to_khr.cpp

@@ -232,6 +232,7 @@ bool ReplaceSwizzleInvocations(IRContext* ctx, Instruction* inst,
       ctx->get_def_use_mgr()->GetDef(var_inst->type_id());
   uint32_t uint_type_id = var_ptr_type->GetSingleWordInOperand(1);
 
+  // TODO(1841): Handle id overflow.
   Instruction* id = ir_builder.AddLoad(uint_type_id, var_id);
 
   uint32_t quad_mask = ir_builder.GetUintConstantId(3);
@@ -239,21 +240,25 @@ bool ReplaceSwizzleInvocations(IRContext* ctx, Instruction* inst,
   // This gives the offset in the group of 4 of this invocation.
   Instruction* quad_idx = ir_builder.AddBinaryOp(
       uint_type_id, spv::Op::OpBitwiseAnd, id->result_id(), quad_mask);
+  if (quad_idx == nullptr) return false;
 
   // Get the invocation id of the first invocation in the group of 4.
   Instruction* quad_ldr =
       ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpBitwiseXor,
                              id->result_id(), quad_idx->result_id());
+  if (quad_ldr == nullptr) return false;
 
   // Get the offset of the target invocation from the offset vector.
   Instruction* my_offset =
       ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpVectorExtractDynamic,
                              offset_id, quad_idx->result_id());
+  if (my_offset == nullptr) return false;
 
   // Determine the index of the invocation to read from.
   Instruction* target_inv =
       ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpIAdd,
                              quad_ldr->result_id(), my_offset->result_id());
+  if (target_inv == nullptr) return false;
 
   // Do the group operations
   uint32_t uint_max_id = ir_builder.GetUintConstantId(0xFFFFFFFF);
@@ -264,9 +269,11 @@ bool ReplaceSwizzleInvocations(IRContext* ctx, Instruction* inst,
       {uint_max_id, uint_max_id, uint_max_id, uint_max_id});
   Instruction* ballot_value =
       const_mgr->GetDefiningInstruction(ballot_value_const);
+  // TODO(1841): Handle id overflow.
   Instruction* is_active = ir_builder.AddNaryOp(
       type_mgr->GetBoolTypeId(), spv::Op::OpGroupNonUniformBallotBitExtract,
       {subgroup_scope, ballot_value->result_id(), target_inv->result_id()});
+  // TODO(1841): Handle id overflow.
   Instruction* shuffle =
       ir_builder.AddNaryOp(inst->type_id(), spv::Op::OpGroupNonUniformShuffle,
                            {subgroup_scope, data_id, target_inv->result_id()});
@@ -358,19 +365,24 @@ bool ReplaceSwizzleInvocationsMasked(
       ctx->get_def_use_mgr()->GetDef(var_inst->type_id());
   uint32_t uint_type_id = var_ptr_type->GetSingleWordInOperand(1);
 
+  // TODO(1841): Handle id overflow.
   Instruction* id = ir_builder.AddLoad(uint_type_id, var_id);
 
   // Do the bitwise operations.
   uint32_t mask_extended = ir_builder.GetUintConstantId(0xFFFFFFE0);
   Instruction* and_mask = ir_builder.AddBinaryOp(
       uint_type_id, spv::Op::OpBitwiseOr, uint_x, mask_extended);
+  if (and_mask == nullptr) return false;
   Instruction* and_result =
       ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpBitwiseAnd,
                              id->result_id(), and_mask->result_id());
+  if (and_result == nullptr) return false;
   Instruction* or_result = ir_builder.AddBinaryOp(
       uint_type_id, spv::Op::OpBitwiseOr, and_result->result_id(), uint_y);
+  if (or_result == nullptr) return false;
   Instruction* target_inv = ir_builder.AddBinaryOp(
       uint_type_id, spv::Op::OpBitwiseXor, or_result->result_id(), uint_z);
+  if (target_inv == nullptr) return false;
 
   // Do the group operations
   uint32_t uint_max_id = ir_builder.GetUintConstantId(0xFFFFFFFF);
@@ -381,9 +393,11 @@ bool ReplaceSwizzleInvocationsMasked(
       {uint_max_id, uint_max_id, uint_max_id, uint_max_id});
   Instruction* ballot_value =
       const_mgr->GetDefiningInstruction(ballot_value_const);
+  // TODO(1841): Handle id overflow.
   Instruction* is_active = ir_builder.AddNaryOp(
       type_mgr->GetBoolTypeId(), spv::Op::OpGroupNonUniformBallotBitExtract,
       {subgroup_scope, ballot_value->result_id(), target_inv->result_id()});
+  // TODO(1841): Handle id overflow.
   Instruction* shuffle =
       ir_builder.AddNaryOp(inst->type_id(), spv::Op::OpGroupNonUniformShuffle,
                            {subgroup_scope, data_id, target_inv->result_id()});
@@ -435,6 +449,7 @@ bool ReplaceWriteInvocation(IRContext* ctx, Instruction* inst,
   InstructionBuilder ir_builder(
       ctx, inst,
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  // TODO(1841): Handle id overflow.
   Instruction* t =
       ir_builder.AddLoad(var_ptr_type->GetSingleWordInOperand(1), var_id);
   analysis::Bool bool_type;
@@ -442,6 +457,7 @@ bool ReplaceWriteInvocation(IRContext* ctx, Instruction* inst,
   Instruction* cmp =
       ir_builder.AddBinaryOp(bool_type_id, spv::Op::OpIEqual, t->result_id(),
                              inst->GetSingleWordInOperand(4));
+  if (cmp == nullptr) return false;
 
   // Build a select.
   inst->SetOpcode(spv::Op::OpSelect);
@@ -517,6 +533,7 @@ bool ReplaceMbcnt(IRContext* context, Instruction* inst,
   Instruction* t =
       ir_builder.AddBinaryOp(mask_inst->type_id(), spv::Op::OpBitwiseAnd,
                              bitcast->result_id(), mask_id);
+  if (t == nullptr) return false;
 
   inst->SetOpcode(spv::Op::OpBitCount);
   inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {t->result_id()}}});
@@ -598,8 +615,11 @@ bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst,
       const_mgr->GetDefiningInstruction(vec_const)->result_id();
 
   // Extract the input values.
+  // TODO(1841): Handle id overflow.
   Instruction* x = ir_builder.AddCompositeExtract(float_type_id, input_id, {0});
+  // TODO(1841): Handle id overflow.
   Instruction* y = ir_builder.AddCompositeExtract(float_type_id, input_id, {1});
+  // TODO(1-841): Handle id overflow.
   Instruction* z = ir_builder.AddCompositeExtract(float_type_id, input_id, {2});
 
   // Negate the input values.
@@ -621,10 +641,13 @@ bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst,
   // Find which values are negative.  Used in later computations.
   Instruction* is_z_neg = ir_builder.AddBinaryOp(
       bool_id, spv::Op::OpFOrdLessThan, z->result_id(), f0_const_id);
+  if (is_z_neg == nullptr) return false;
   Instruction* is_y_neg = ir_builder.AddBinaryOp(
       bool_id, spv::Op::OpFOrdLessThan, y->result_id(), f0_const_id);
+  if (is_y_neg == nullptr) return false;
   Instruction* is_x_neg = ir_builder.AddBinaryOp(
       bool_id, spv::Op::OpFOrdLessThan, x->result_id(), f0_const_id);
+  if (is_x_neg == nullptr) return false;
 
   // Compute cubema
   Instruction* amax_x_y = ir_builder.AddNaryExtendedInstruction(
@@ -635,36 +658,46 @@ bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst,
       {az->result_id(), amax_x_y->result_id()});
   Instruction* cubema = ir_builder.AddBinaryOp(float_type_id, spv::Op::OpFMul,
                                                f2_const_id, amax->result_id());
+  if (cubema == nullptr) return false;
 
   // Do the comparisons needed for computing cubesc and cubetc.
   Instruction* is_z_max =
       ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual,
                              az->result_id(), amax_x_y->result_id());
+  if (is_z_max == nullptr) return false;
   Instruction* not_is_z_max = ir_builder.AddUnaryOp(
       bool_id, spv::Op::OpLogicalNot, is_z_max->result_id());
   Instruction* y_gr_x =
       ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual,
                              ay->result_id(), ax->result_id());
+  if (y_gr_x == nullptr) return false;
   Instruction* is_y_max =
       ir_builder.AddBinaryOp(bool_id, spv::Op::OpLogicalAnd,
                              not_is_z_max->result_id(), y_gr_x->result_id());
+  if (is_y_max == nullptr) return false;
 
   // Select the correct value for cubesc.
+  // TODO(1841): Handle id overflow.
   Instruction* cubesc_case_1 = ir_builder.AddSelect(
       float_type_id, is_z_neg->result_id(), nx->result_id(), x->result_id());
+  // TODO(1841): Handle id overflow.
   Instruction* cubesc_case_2 = ir_builder.AddSelect(
       float_type_id, is_x_neg->result_id(), z->result_id(), nz->result_id());
   Instruction* sel =
+      // TODO(1841): Handle id overflow.
       ir_builder.AddSelect(float_type_id, is_y_max->result_id(), x->result_id(),
                            cubesc_case_2->result_id());
   Instruction* cubesc =
+      // TODO(1841): Handle id overflow.
       ir_builder.AddSelect(float_type_id, is_z_max->result_id(),
                            cubesc_case_1->result_id(), sel->result_id());
 
   // Select the correct value for cubetc.
+  // TODO(1841): Handle id overflow.
   Instruction* cubetc_case_1 = ir_builder.AddSelect(
       float_type_id, is_y_neg->result_id(), nz->result_id(), z->result_id());
   Instruction* cubetc =
+      // TODO(1841): Handle id overflow.
       ir_builder.AddSelect(float_type_id, is_y_max->result_id(),
                            cubetc_case_1->result_id(), ny->result_id());
 
@@ -675,6 +708,7 @@ bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst,
       v2_float_type_id, {cubema->result_id(), cubema->result_id()});
   Instruction* div = ir_builder.AddBinaryOp(
       v2_float_type_id, spv::Op::OpFDiv, cube->result_id(), denom->result_id());
+  if (div == nullptr) return false;
 
   // Get the final result by adding 0.5 to |div|.
   inst->SetOpcode(spv::Op::OpFAdd);
@@ -746,8 +780,11 @@ bool ReplaceCubeFaceIndex(IRContext* ctx, Instruction* inst,
   uint32_t f5_const_id = const_mgr->GetFloatConstId(5.0);
 
   // Extract the input values.
+  // TODO(1841): Handle id overflow.
   Instruction* x = ir_builder.AddCompositeExtract(float_type_id, input_id, {0});
+  // TODO(1841): Handle id overflow.
   Instruction* y = ir_builder.AddCompositeExtract(float_type_id, input_id, {1});
+  // TODO(1-841): Handle id overflow.
   Instruction* z = ir_builder.AddCompositeExtract(float_type_id, input_id, {2});
 
   // Get the absolute values of the inputs.
@@ -761,10 +798,13 @@ bool ReplaceCubeFaceIndex(IRContext* ctx, Instruction* inst,
   // Find which values are negative.  Used in later computations.
   Instruction* is_z_neg = ir_builder.AddBinaryOp(
       bool_id, spv::Op::OpFOrdLessThan, z->result_id(), f0_const_id);
+  if (is_z_neg == nullptr) return false;
   Instruction* is_y_neg = ir_builder.AddBinaryOp(
       bool_id, spv::Op::OpFOrdLessThan, y->result_id(), f0_const_id);
+  if (is_y_neg == nullptr) return false;
   Instruction* is_x_neg = ir_builder.AddBinaryOp(
       bool_id, spv::Op::OpFOrdLessThan, x->result_id(), f0_const_id);
+  if (is_x_neg == nullptr) return false;
 
   // Find the max value.
   Instruction* amax_x_y = ir_builder.AddNaryExtendedInstruction(
@@ -773,20 +813,26 @@ bool ReplaceCubeFaceIndex(IRContext* ctx, Instruction* inst,
   Instruction* is_z_max =
       ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual,
                              az->result_id(), amax_x_y->result_id());
+  if (is_z_max == nullptr) return false;
   Instruction* y_gr_x =
       ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual,
                              ay->result_id(), ax->result_id());
+  if (y_gr_x == nullptr) return false;
 
   // Get the value for each case.
+  // TODO(1841): Handle id overflow.
   Instruction* case_z = ir_builder.AddSelect(
       float_type_id, is_z_neg->result_id(), f5_const_id, f4_const_id);
+  // TODO(1841): Handle id overflow.
   Instruction* case_y = ir_builder.AddSelect(
       float_type_id, is_y_neg->result_id(), f3_const_id, f2_const_id);
+  // TODO(1841): Handle id overflow.
   Instruction* case_x = ir_builder.AddSelect(
       float_type_id, is_x_neg->result_id(), f1_const_id, f0_const_id);
 
   // Select the correct case.
   Instruction* sel =
+      // TODO(1841): Handle id overflow.
       ir_builder.AddSelect(float_type_id, y_gr_x->result_id(),
                            case_y->result_id(), case_x->result_id());
 

+ 11 - 3
3rdparty/spirv-tools/source/opt/basic_block.cpp

@@ -29,11 +29,19 @@ constexpr uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
 }  // namespace
 
 BasicBlock* BasicBlock::Clone(IRContext* context) const {
-  BasicBlock* clone = new BasicBlock(
-      std::unique_ptr<Instruction>(GetLabelInst()->Clone(context)));
+  Instruction* label_clone = GetLabelInst()->Clone(context);
+  if (!label_clone) {
+    return nullptr;
+  }
+  BasicBlock* clone = new BasicBlock(std::unique_ptr<Instruction>(label_clone));
   for (const auto& inst : insts_) {
     // Use the incoming context
-    clone->AddInstruction(std::unique_ptr<Instruction>(inst.Clone(context)));
+    Instruction* inst_clone = inst.Clone(context);
+    if (!inst_clone) {
+      delete clone;
+      return nullptr;
+    }
+    clone->AddInstruction(std::unique_ptr<Instruction>(inst_clone));
   }
 
   if (context->AreAnalysesValid(

+ 4 - 0
3rdparty/spirv-tools/source/opt/ccp_pass.cpp

@@ -165,6 +165,9 @@ SSAPropagator::PropStatus CCPPass::VisitAssignment(Instruction* instr) {
       context()->get_instruction_folder().FoldInstructionToConstant(instr,
                                                                     map_func);
 
+  if (folded_inst && context()->id_overflow()) {
+    return SSAPropagator::kFailed;
+  }
   if (folded_inst != nullptr) {
     // We do not want to change the body of the function by adding new
     // instructions.  When folding we can only generate new constants.
@@ -376,6 +379,7 @@ Pass::Status CCPPass::Process() {
   // Process all entry point functions.
   ProcessFunction pfn = [this](Function* fp) { return PropagateConstants(fp); };
   bool modified = context()->ProcessReachableCallTree(pfn);
+  if (context()->id_overflow()) return Pass::Status::Failure;
   return modified ? Pass::Status::SuccessWithChange
                   : Pass::Status::SuccessWithoutChange;
 }

+ 16 - 7
3rdparty/spirv-tools/source/opt/cfg.cpp

@@ -254,12 +254,13 @@ BasicBlock* CFG::SplitLoopHeader(BasicBlock* bb) {
   }
 
   // Adjust the OpPhi instructions as needed.
-  bb->ForEachPhiInst([latch_block, bb, new_header, context](Instruction* phi) {
+  bool ok = bb->WhileEachPhiInst([latch_block, bb, new_header,
+                                  context](Instruction* phi) -> bool {
     std::vector<uint32_t> preheader_phi_ops;
     std::vector<Operand> header_phi_ops;
 
-    // Identify where the original inputs to original OpPhi belong: header or
-    // preheader.
+    // Identify where the original inputs to original OpPhi belong: header
+    // or preheader.
     for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) {
       uint32_t def_id = phi->GetSingleWordInOperand(i);
       uint32_t branch_id = phi->GetSingleWordInOperand(i + 1);
@@ -272,21 +273,24 @@ BasicBlock* CFG::SplitLoopHeader(BasicBlock* bb) {
       }
     }
 
-    // Create a phi instruction if and only if the preheader_phi_ops has more
-    // than one pair.
+    // Create a phi instruction if and only if the preheader_phi_ops has
+    // more than one pair.
     if (preheader_phi_ops.size() > 2) {
       InstructionBuilder builder(
           context, &*bb->begin(),
           IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
 
       Instruction* new_phi = builder.AddPhi(phi->type_id(), preheader_phi_ops);
+      if (!new_phi) {
+        return false;
+      }
 
       // Add the OpPhi to the header bb.
       header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {new_phi->result_id()}});
       header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {bb->id()}});
     } else {
-      // An OpPhi with a single entry is just a copy.  In this case use the same
-      // instruction in the new header.
+      // An OpPhi with a single entry is just a copy.  In this case use the
+      // same instruction in the new header.
       header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {preheader_phi_ops[0]}});
       header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {bb->id()}});
     }
@@ -297,8 +301,13 @@ BasicBlock* CFG::SplitLoopHeader(BasicBlock* bb) {
     new_header->begin()->InsertBefore(std::move(phi_owner));
     context->set_instr_block(phi, new_header);
     context->AnalyzeUses(phi);
+    return true;
   });
 
+  if (!ok) {
+    return nullptr;
+  }
+
   // Add a branch to the new header.
   InstructionBuilder branch_builder(
       context, bb,

+ 1 - 0
3rdparty/spirv-tools/source/opt/combine_access_chains.cpp

@@ -158,6 +158,7 @@ bool CombineAccessChains::CombineIndices(Instruction* ptr_input,
     InstructionBuilder builder(
         context(), inst,
         IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+    // TODO(1841): Handle id overflow.
     Instruction* addition = builder.AddIAdd(last_index_inst->type_id(),
                                             last_index_inst->result_id(),
                                             element_inst->result_id());

+ 6 - 2
3rdparty/spirv-tools/source/opt/const_folding_rules.cpp

@@ -829,7 +829,9 @@ const analysis::Constant* FoldFPBinaryOp(
     // Build the constant object and return it.
     std::vector<uint32_t> ids;
     for (const analysis::Constant* member : results_components) {
-      ids.push_back(const_mgr->GetDefiningInstruction(member)->result_id());
+      Instruction* def = const_mgr->GetDefiningInstruction(member);
+      if (!def) return nullptr;
+      ids.push_back(def->result_id());
     }
     return const_mgr->GetConstant(vector_type, ids);
   } else {
@@ -1404,7 +1406,9 @@ ConstantFoldingRule FoldFMix() {
     }
 
     if (is_vector) {
-      uint32_t one_id = const_mgr->GetDefiningInstruction(one)->result_id();
+      Instruction* one_inst = const_mgr->GetDefiningInstruction(one);
+      if (one_inst == nullptr) return nullptr;
+      uint32_t one_id = one_inst->result_id();
       one =
           const_mgr->GetConstant(result_type, std::vector<uint32_t>(4, one_id));
     }

+ 102 - 9
3rdparty/spirv-tools/source/opt/convert_to_half_pass.cpp

@@ -75,6 +75,9 @@ analysis::Type* ConvertToHalfPass::FloatScalarType(uint32_t width) {
 analysis::Type* ConvertToHalfPass::FloatVectorType(uint32_t v_len,
                                                    uint32_t width) {
   analysis::Type* reg_float_ty = FloatScalarType(width);
+  if (reg_float_ty == nullptr) {
+    return nullptr;
+  }
   analysis::Vector vec_ty(reg_float_ty, v_len);
   return context()->get_type_mgr()->GetRegisteredType(&vec_ty);
 }
@@ -85,6 +88,9 @@ analysis::Type* ConvertToHalfPass::FloatMatrixType(uint32_t v_cnt,
   Instruction* vty_inst = get_def_use_mgr()->GetDef(vty_id);
   uint32_t v_len = vty_inst->GetSingleWordInOperand(1);
   analysis::Type* reg_vec_ty = FloatVectorType(v_len, width);
+  if (reg_vec_ty == nullptr) {
+    return nullptr;
+  }
   analysis::Matrix mat_ty(reg_vec_ty, v_cnt);
   return context()->get_type_mgr()->GetRegisteredType(&mat_ty);
 }
@@ -99,6 +105,9 @@ uint32_t ConvertToHalfPass::EquivFloatTypeId(uint32_t ty_id, uint32_t width) {
     reg_equiv_ty = FloatVectorType(ty_inst->GetSingleWordInOperand(1), width);
   else  // spv::Op::OpTypeFloat
     reg_equiv_ty = FloatScalarType(width);
+  if (reg_equiv_ty == nullptr) {
+    return 0;
+  }
   return context()->get_type_mgr()->GetTypeInstruction(reg_equiv_ty);
 }
 
@@ -107,6 +116,10 @@ void ConvertToHalfPass::GenConvert(uint32_t* val_idp, uint32_t width,
   Instruction* val_inst = get_def_use_mgr()->GetDef(*val_idp);
   uint32_t ty_id = val_inst->type_id();
   uint32_t nty_id = EquivFloatTypeId(ty_id, width);
+  if (nty_id == 0) {
+    status_ = Status::Failure;
+    return;
+  }
   if (nty_id == ty_id) return;
   Instruction* cvt_inst;
   InstructionBuilder builder(
@@ -116,6 +129,10 @@ void ConvertToHalfPass::GenConvert(uint32_t* val_idp, uint32_t width,
     cvt_inst = builder.AddNullaryOp(nty_id, spv::Op::OpUndef);
   else
     cvt_inst = builder.AddUnaryOp(nty_id, spv::Op::OpFConvert, *val_idp);
+  if (cvt_inst == nullptr) {
+    status_ = Status::Failure;
+    return;
+  }
   *val_idp = cvt_inst->result_id();
 }
 
@@ -137,22 +154,43 @@ bool ConvertToHalfPass::MatConvertCleanup(Instruction* inst) {
   uint32_t orig_width = (cty_inst->GetSingleWordInOperand(0) == 16) ? 32 : 16;
   uint32_t orig_mat_id = inst->GetSingleWordInOperand(0);
   uint32_t orig_vty_id = EquivFloatTypeId(vty_id, orig_width);
+  if (orig_vty_id == 0) {
+    status_ = Status::Failure;
+    return false;
+  }
   std::vector<Operand> opnds = {};
   for (uint32_t vidx = 0; vidx < v_cnt; ++vidx) {
     Instruction* ext_inst = builder.AddIdLiteralOp(
         orig_vty_id, spv::Op::OpCompositeExtract, orig_mat_id, vidx);
+    if (ext_inst == nullptr) {
+      status_ = Status::Failure;
+      return false;
+    }
     Instruction* cvt_inst =
         builder.AddUnaryOp(vty_id, spv::Op::OpFConvert, ext_inst->result_id());
+    if (cvt_inst == nullptr) {
+      status_ = Status::Failure;
+      return false;
+    }
     opnds.push_back({SPV_OPERAND_TYPE_ID, {cvt_inst->result_id()}});
   }
   uint32_t mat_id = TakeNextId();
+  if (mat_id == 0) {
+    status_ = Status::Failure;
+    return false;
+  }
   std::unique_ptr<Instruction> mat_inst(new Instruction(
       context(), spv::Op::OpCompositeConstruct, mty_id, mat_id, opnds));
   (void)builder.AddInstruction(std::move(mat_inst));
   context()->ReplaceAllUsesWith(inst->result_id(), mat_id);
   // Turn original instruction into copy so it is valid.
+  uint32_t new_type_id = EquivFloatTypeId(mty_id, orig_width);
+  if (new_type_id == 0) {
+    status_ = Status::Failure;
+    return false;
+  }
   inst->SetOpcode(spv::Op::OpCopyObject);
-  inst->SetResultType(EquivFloatTypeId(mty_id, orig_width));
+  inst->SetResultType(new_type_id);
   get_def_use_mgr()->AnalyzeInstUse(inst);
   return true;
 }
@@ -187,13 +225,24 @@ bool ConvertToHalfPass::GenHalfArith(Instruction* inst) {
   // Convert all float32 based operands to float16 equivalent and change
   // instruction type to float16 equivalent.
   inst->ForEachInId([&inst, &modified, this](uint32_t* idp) {
+    if (status_ == Status::Failure) {
+      return;
+    }
     Instruction* op_inst = get_def_use_mgr()->GetDef(*idp);
     if (!IsFloat(op_inst, 32)) return;
     GenConvert(idp, 16, inst);
     modified = true;
   });
+  if (status_ == Status::Failure) {
+    return false;
+  }
   if (IsFloat(inst, 32)) {
-    inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
+    uint32_t new_type_id = EquivFloatTypeId(inst->type_id(), 16);
+    if (new_type_id == 0) {
+      status_ = Status::Failure;
+      return false;
+    }
+    inst->SetResultType(new_type_id);
     converted_ids_.insert(inst->result_id());
     modified = true;
   }
@@ -211,6 +260,9 @@ bool ConvertToHalfPass::ProcessPhi(Instruction* inst, uint32_t from_width,
   bool modified = false;
   inst->ForEachInId([&ocnt, &prev_idp, &from_width, &to_width, &modified,
                      this](uint32_t* idp) {
+    if (status_ == Status::Failure) {
+      return;
+    }
     if (ocnt % 2 == 0) {
       prev_idp = idp;
     } else {
@@ -230,8 +282,16 @@ bool ConvertToHalfPass::ProcessPhi(Instruction* inst, uint32_t from_width,
     }
     ++ocnt;
   });
+  if (status_ == Status::Failure) {
+    return false;
+  }
   if (to_width == 16u) {
-    inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16u));
+    uint32_t new_type_id = EquivFloatTypeId(inst->type_id(), 16u);
+    if (new_type_id == 0) {
+      status_ = Status::Failure;
+      return false;
+    }
+    inst->SetResultType(new_type_id);
     converted_ids_.insert(inst->result_id());
     modified = true;
   }
@@ -242,7 +302,12 @@ bool ConvertToHalfPass::ProcessPhi(Instruction* inst, uint32_t from_width,
 bool ConvertToHalfPass::ProcessConvert(Instruction* inst) {
   // If float32 and relaxed, change to float16 convert
   if (IsFloat(inst, 32) && IsRelaxed(inst->result_id())) {
-    inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
+    uint32_t new_type_id = EquivFloatTypeId(inst->type_id(), 16);
+    if (new_type_id == 0) {
+      status_ = Status::Failure;
+      return false;
+    }
+    inst->SetResultType(new_type_id);
     get_def_use_mgr()->AnalyzeInstUse(inst);
     converted_ids_.insert(inst->result_id());
   }
@@ -255,7 +320,7 @@ bool ConvertToHalfPass::ProcessConvert(Instruction* inst) {
   Instruction* val_inst = get_def_use_mgr()->GetDef(val_id);
   if (inst->type_id() == val_inst->type_id())
     inst->SetOpcode(spv::Op::OpCopyObject);
-  return true;  // modified
+  return true;
 }
 
 bool ConvertToHalfPass::ProcessImageRef(Instruction* inst) {
@@ -265,6 +330,9 @@ bool ConvertToHalfPass::ProcessImageRef(Instruction* inst) {
     uint32_t dref_id = inst->GetSingleWordInOperand(kImageSampleDrefIdInIdx);
     if (converted_ids_.count(dref_id) > 0) {
       GenConvert(&dref_id, 32, inst);
+      if (status_ == Status::Failure) {
+        return false;
+      }
       inst->SetInOperand(kImageSampleDrefIdInIdx, {dref_id});
       get_def_use_mgr()->AnalyzeInstUse(inst);
       modified = true;
@@ -279,11 +347,17 @@ bool ConvertToHalfPass::ProcessDefault(Instruction* inst) {
   if (inst->opcode() == spv::Op::OpPhi) return ProcessPhi(inst, 16u, 32u);
   bool modified = false;
   inst->ForEachInId([&inst, &modified, this](uint32_t* idp) {
+    if (status_ == Status::Failure) {
+      return;
+    }
     if (converted_ids_.count(*idp) == 0) return;
     uint32_t old_id = *idp;
     GenConvert(idp, 32, inst);
     if (*idp != old_id) modified = true;
   });
+  if (status_ == Status::Failure) {
+    return false;
+  }
   if (modified) get_def_use_mgr()->AnalyzeInstUse(inst);
   return modified;
 }
@@ -370,19 +444,38 @@ bool ConvertToHalfPass::ProcessFunction(Function* func) {
       });
   // Replace invalid converts of matrix into equivalent vector extracts,
   // converts and finally a composite construct
+  bool ok = true;
   cfg()->ForEachBlockInReversePostOrder(
-      func->entry().get(), [&modified, this](BasicBlock* bb) {
-        for (auto ii = bb->begin(); ii != bb->end(); ++ii)
-          modified |= MatConvertCleanup(&*ii);
+      func->entry().get(), [&modified, &ok, this](BasicBlock* bb) {
+        if (!ok) {
+          return;
+        }
+        for (auto ii = bb->begin(); ii != bb->end(); ++ii) {
+          bool Mmodified = MatConvertCleanup(&*ii);
+          if (status_ == Status::Failure) {
+            ok = false;
+            break;
+          }
+          modified |= Mmodified;
+        }
       });
+
+  if (!ok) {
+    return false;
+  }
   return modified;
 }
 
 Pass::Status ConvertToHalfPass::ProcessImpl() {
+  status_ = Status::SuccessWithoutChange;
   Pass::ProcessFunction pfn = [this](Function* fp) {
     return ProcessFunction(fp);
   };
   bool modified = context()->ProcessReachableCallTree(pfn);
+  if (status_ == Status::Failure) {
+    return status_;
+  }
+
   // If modified, make sure module has Float16 capability
   if (modified) context()->AddCapability(spv::Capability::Float16);
   // Remove all RelaxedPrecision decorations from instructions and globals
@@ -514,4 +607,4 @@ void ConvertToHalfPass::Initialize() {
 }
 
 }  // namespace opt
-}  // namespace spvtools
+}  // namespace spvtools

+ 3 - 0
3rdparty/spirv-tools/source/opt/convert_to_half_pass.h

@@ -130,6 +130,9 @@ class ConvertToHalfPass : public Pass {
     }
   };
 
+  // The status of the pass.
+  Pass::Status status_;
+
   // Set of core operations to be processed
   std::unordered_set<spv::Op, hasher> target_ops_core_;
 

+ 13 - 2
3rdparty/spirv-tools/source/opt/convert_to_sampled_image_pass.cpp

@@ -246,9 +246,10 @@ Instruction* ConvertToSampledImagePass::CreateImageExtraction(
   InstructionBuilder builder(
       context(), sampled_image->NextNode(),
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-  return builder.AddUnaryOp(
+  Instruction* result = builder.AddUnaryOp(
       GetImageTypeOfSampledImage(context()->get_type_mgr(), sampled_image),
       spv::Op::OpImage, sampled_image->result_id());
+  return result;
 }
 
 uint32_t ConvertToSampledImagePass::GetSampledImageTypeForImage(
@@ -270,6 +271,9 @@ Instruction* ConvertToSampledImagePass::UpdateImageUses(
   if (uses_of_load.empty()) return nullptr;
 
   auto* extracted_image = CreateImageExtraction(sampled_image_load);
+  if (extracted_image == nullptr) {
+    return nullptr;
+  }
   for (auto* user : uses_of_load) {
     user->SetInOperand(0, {extracted_image->result_id()});
     context()->get_def_use_mgr()->AnalyzeInstUse(user);
@@ -306,8 +310,12 @@ void ConvertToSampledImagePass::UpdateSampledImageUses(
       def_use_mgr->AnalyzeInstUse(image_load);
       context()->KillInst(sampled_image_inst);
     } else {
-      if (!image_extraction)
+      if (!image_extraction) {
         image_extraction = CreateImageExtraction(image_load);
+        if (image_extraction == nullptr) {
+          return;
+        }
+      }
       sampled_image_inst->SetInOperand(0, {image_extraction->result_id()});
       def_use_mgr->AnalyzeInstUse(sampled_image_inst);
     }
@@ -333,6 +341,9 @@ bool ConvertToSampledImagePass::ConvertImageVariableToSampledImage(
   // reference.
   uint32_t type_id = context()->get_type_mgr()->FindPointerToType(
       sampled_image_type_id, storage_class);
+  if (type_id == 0) {
+    return false;
+  }
   MoveInstructionNextToType(image_variable, type_id);
   return true;
 }

+ 1 - 0
3rdparty/spirv-tools/source/opt/debug_info_manager.cpp

@@ -390,6 +390,7 @@ Instruction* DebugInfoManager::GetDebugInfoNone() {
   if (debug_info_none_inst_ != nullptr) return debug_info_none_inst_;
 
   uint32_t result_id = context()->TakeNextId();
+  if (result_id == 0) return nullptr;
   std::unique_ptr<Instruction> dbg_info_none_inst(new Instruction(
       context(), spv::Op::OpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
       result_id,

+ 3 - 0
3rdparty/spirv-tools/source/opt/fix_func_call_arguments.cpp

@@ -74,16 +74,19 @@ uint32_t FixFuncCallArgumentsPass::ReplaceAccessChainFuncCallArguments(
       op_type->result_id(), spv::StorageClass::Function);
   // Create new variable
   builder.SetInsertPoint(variable_insertion_point);
+  // TODO(1841): Handle id overflow.
   Instruction* var =
       builder.AddVariable(varType, uint32_t(spv::StorageClass::Function));
   // Load access chain to the new variable before function call
   builder.SetInsertPoint(func_call_inst);
 
   uint32_t operand_id = operand_inst->result_id();
+  // TODO(1841): Handle id overflow.
   Instruction* load = builder.AddLoad(op_type->result_id(), operand_id);
   builder.AddStore(var->result_id(), load->result_id());
   // Load return value to the acesschain after function call
   builder.SetInsertPoint(next_insert_point);
+  // TODO(1841): Handle id overflow.
   load = builder.AddLoad(op_type->result_id(), var->result_id());
   builder.AddStore(operand_id, load->result_id());
 

+ 3 - 0
3rdparty/spirv-tools/source/opt/fold.cpp

@@ -597,6 +597,9 @@ Instruction* InstructionFolder::FoldInstructionToConstant(
   const analysis::Constant* folded_const = nullptr;
   for (auto rule : GetConstantFoldingRules().GetRulesForInstruction(inst)) {
     folded_const = rule(context_, inst, constants);
+    if (folded_const == nullptr && inst->context()->id_overflow()) {
+      return nullptr;
+    }
     if (folded_const != nullptr) {
       Instruction* const_inst =
           const_mgr->GetDefiningInstruction(folded_const, inst->type_id());

+ 24 - 6
3rdparty/spirv-tools/source/opt/fold_spec_constant_op_and_composite_pass.cpp

@@ -105,9 +105,16 @@ Pass::Status FoldSpecConstantOpAndCompositePass::Process() {
       // Constants will be added to id_to_const_val_ and const_val_to_id_ so
       // that we can use the new Normal Constants when folding following Spec
       // Constants.
-      case spv::Op::OpSpecConstantOp:
-        modified |= ProcessOpSpecConstantOp(&inst_iter);
+      case spv::Op::OpSpecConstantOp: {
+        const auto status = ProcessOpSpecConstantOp(&inst_iter);
+        if (status == Status::Failure) {
+          return Status::Failure;
+        }
+        if (status == Status::SuccessWithChange) {
+          modified = true;
+        }
         break;
+      }
       default:
         break;
     }
@@ -115,7 +122,7 @@ Pass::Status FoldSpecConstantOpAndCompositePass::Process() {
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
-bool FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp(
+Pass::Status FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp(
     Module::inst_iterator* pos) {
   Instruction* inst = &**pos;
   Instruction* folded_inst = nullptr;
@@ -125,10 +132,17 @@ bool FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp(
          "SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER type");
 
   folded_inst = FoldWithInstructionFolder(pos);
+  if (context()->id_overflow()) {
+    return Status::Failure;
+  }
+
   if (!folded_inst) {
     folded_inst = DoComponentWiseOperation(pos);
+    if (context()->id_overflow()) {
+      return Status::Failure;
+    }
   }
-  if (!folded_inst) return false;
+  if (!folded_inst) return Status::SuccessWithoutChange;
 
   // Replace the original constant with the new folded constant, kill the
   // original constant.
@@ -136,7 +150,7 @@ bool FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp(
   uint32_t old_id = inst->result_id();
   context()->ReplaceAllUsesWith(old_id, new_id);
   context()->KillDef(old_id);
-  return true;
+  return Status::SuccessWithChange;
 }
 
 Instruction* FoldSpecConstantOpAndCompositePass::FoldWithInstructionFolder(
@@ -195,7 +209,11 @@ Instruction* FoldSpecConstantOpAndCompositePass::FoldWithInstructionFolder(
 
   if (need_to_clone) {
     new_const_inst = new_const_inst->Clone(context());
-    new_const_inst->SetResultId(TakeNextId());
+    uint32_t new_id = TakeNextId();
+    if (new_id == 0) {
+      return nullptr;
+    }
+    new_const_inst->SetResultId(new_id);
     new_const_inst->InsertAfter(insert_pos);
     get_def_use_mgr()->AnalyzeInstDefUse(new_const_inst);
   }

+ 26 - 8
3rdparty/spirv-tools/source/opt/fold_spec_constant_op_and_composite_pass.h

@@ -19,6 +19,7 @@
 #include <unordered_map>
 #include <vector>
 
+#include "source/diagnostic.h"
 #include "source/opt/constants.h"
 #include "source/opt/def_use_manager.h"
 #include "source/opt/ir_context.h"
@@ -45,14 +46,14 @@ class FoldSpecConstantOpAndCompositePass : public Pass {
  private:
   // Processes the OpSpecConstantOp instruction pointed by the given
   // instruction iterator, folds it to normal constants if possible. Returns
-  // true if the spec constant is folded to normal constants. New instructions
-  // will be inserted before the OpSpecConstantOp instruction pointed by the
-  // instruction iterator. The instruction iterator, which is passed by
-  // pointer, will still point to the original OpSpecConstantOp instruction. If
-  // folding is done successfully, the original OpSpecConstantOp instruction
-  // will be changed to Nop and new folded instruction will be inserted before
-  // it.
-  bool ProcessOpSpecConstantOp(Module::inst_iterator* pos);
+  // kSuccess if the spec constant is folded to normal constants. New
+  // instructions will be inserted before the OpSpecConstantOp instruction
+  // pointed by the instruction iterator. The instruction iterator, which is
+  // passed by pointer, will still point to the original OpSpecConstantOp
+  // instruction. If folding is done successfully, the original OpSpecConstantOp
+  // instruction will be changed to Nop and new folded instruction will be
+  // inserted before it. Returns kFail if an id overflow occurs.
+  Status ProcessOpSpecConstantOp(Module::inst_iterator* pos);
 
   // Returns the result of folding the OpSpecConstantOp instruction
   // |inst_iter_ptr| using the instruction folder.
@@ -62,7 +63,24 @@ class FoldSpecConstantOpAndCompositePass : public Pass {
   // pointed by the given instruction iterator to a normal constant defining
   // instruction. Returns the pointer to the new constant defining instruction
   // if succeeded, otherwise return nullptr.
+  // instruction if succeeded, otherwise return nullptr.
   Instruction* DoComponentWiseOperation(Module::inst_iterator* inst_iter_ptr);
+
+  // Returns the next available id, or 0 if the id overflows.
+  uint32_t TakeNextId() {
+    uint32_t next_id = context()->TakeNextId();
+    if (next_id == 0) {
+      Fail() << "ID overflow. Try running compact-ids.";
+    }
+    return next_id;
+  }
+
+  // Records failure for the current module and returns a stream for printing
+  // diagnostics.
+  spvtools::DiagnosticStream Fail() {
+    return spvtools::DiagnosticStream({}, context()->consumer(), "",
+                                      SPV_ERROR_INTERNAL);
+  }
 };
 
 }  // namespace opt

+ 153 - 1
3rdparty/spirv-tools/source/opt/folding_rules.cpp

@@ -176,12 +176,57 @@ std::vector<uint32_t> GetWordsFromNumericScalarOrVectorConstant(
     return GetWordsFromScalarIntConstant(int_constant);
   } else if (const auto* vec_constant = c->AsVectorConstant()) {
     std::vector<uint32_t> words;
+    // Retrieve all the components as 32bit words.
     for (const auto* comp : vec_constant->GetComponents()) {
       auto comp_in_words =
           GetWordsFromNumericScalarOrVectorConstant(const_mgr, comp);
       words.insert(words.end(), comp_in_words.begin(), comp_in_words.end());
     }
-    return words;
+
+    if (ElementWidth(c->type()) >= 32) {
+      return words;
+    }
+    // Check the element width and concactenate if the width is less than 32.
+    if (ElementWidth(c->type()) == 8) {
+      assert(words.size() <= 4);
+      // Each 32-bit word will comprise 4 8-bit integers.
+      // reverse the order when compacting.
+      uint32_t compacted_word = 0;
+      for (int32_t i = static_cast<int32_t>(words.size()) - 1; i >= 0; --i) {
+        compacted_word <<= 8;
+        compacted_word |= words[i];
+      }
+      return {compacted_word};
+    } else if (ElementWidth(c->type()) == 16) {
+      assert(words.size() <= 4);
+      std::vector<uint32_t> compacted_words;
+      // Each 32-bit word will comprise 2 16-bit integers.
+      // reverse the order pair-wise when compacting.
+      for (uint32_t i = 0; i < words.size(); i += 2) {
+        uint32_t word1 = words[i];
+        uint32_t word2 = (i + 1 < words.size()) ? words[i + 1] : 0;
+        uint32_t compacted_word = (word2 << 16) | word1;
+        compacted_words.push_back(compacted_word);
+      }
+      return compacted_words;
+    }
+    assert(false && "Unhandled element width");
+  } else if (c->AsNullConstant()) {
+    uint32_t num_elements = 1;
+
+    if (const auto* vec_type = c->type()->AsVector()) {
+      num_elements = vec_type->element_count();
+    }
+
+    // We need to check the element width to determine how many 32-bit words are
+    // needed.
+    uint32_t element_width = ElementWidth(c->type());
+    if (element_width < 32) {
+      num_elements = (num_elements + 1) / 2;
+    } else if (element_width == 64) {
+      num_elements = num_elements * 2;
+    }
+    return std::vector<uint32_t>(num_elements, 0);
   }
   return {};
 }
@@ -634,6 +679,15 @@ uint32_t PerformIntegerOperation(analysis::ConstantManager* const_mgr,
     case spv::Op::OpISub:
       FOLD_OP(-);
       break;
+    case spv::Op::OpBitwiseXor:
+      FOLD_OP(^);
+      break;
+    case spv::Op::OpBitwiseOr:
+      FOLD_OP(|);
+      break;
+    case spv::Op::OpBitwiseAnd:
+      FOLD_OP(&);
+      break;
     default:
       assert(false && "Unexpected operation");
       break;
@@ -1488,6 +1542,9 @@ bool FactorAddMulsOpnds(uint32_t factor0_0, uint32_t factor0_1,
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
   Instruction* new_add_inst = ir_builder.AddBinaryOp(
       inst->type_id(), inst->opcode(), factor0_1, factor1_1);
+  if (!new_add_inst) {
+    return false;
+  }
   inst->SetOpcode(inst->opcode() == spv::Op::OpFAdd ? spv::Op::OpFMul
                                                     : spv::Op::OpIMul);
   inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {factor0_0}},
@@ -2239,6 +2296,48 @@ FoldingRule BitCastScalarOrVector() {
   };
 }
 
+FoldingRule BitReverseScalarOrVector() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants) {
+    assert(inst->opcode() == spv::Op::OpBitReverse && constants.size() == 1);
+    if (constants[0] == nullptr) return false;
+
+    const analysis::Type* type =
+        context->get_type_mgr()->GetType(inst->type_id());
+    assert(!HasFloatingPoint(type) &&
+           "BitReverse cannot be applied to floating point types.");
+    assert((type->AsInteger() || type->AsVector()) &&
+           "BitReverse can only be applied to integer scalars or vectors.");
+    assert((ElementWidth(type) == 32) &&
+           "BitReverse can only be applied to integer types of width 32");
+
+    analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+    std::vector<uint32_t> words =
+        GetWordsFromNumericScalarOrVectorConstant(const_mgr, constants[0]);
+    if (words.size() == 0) return false;
+
+    for (uint32_t& word : words) {
+      // Reverse the bits in each word.
+      word = ((word & 0x55555555) << 1) | ((word >> 1) & 0x55555555);
+      word = ((word & 0x33333333) << 2) | ((word >> 2) & 0x33333333);
+      word = ((word & 0x0F0F0F0F) << 4) | ((word >> 4) & 0x0F0F0F0F);
+      word = ((word & 0x00FF00FF) << 8) | ((word >> 8) & 0x00FF00FF);
+      word = (word << 16) | (word >> 16);
+    }
+
+    const analysis::Constant* bitreversed_constant =
+        ConvertWordsToNumericScalarOrVectorConstant(const_mgr, words, type);
+    if (!bitreversed_constant) return false;
+
+    auto new_feeder_id =
+        const_mgr->GetDefiningInstruction(bitreversed_constant, inst->type_id())
+            ->result_id();
+    inst->SetOpcode(spv::Op::OpCopyObject);
+    inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {new_feeder_id}}});
+    return true;
+  };
+}
+
 FoldingRule RedundantSelect() {
   // An OpSelect instruction where both values are the same or the condition is
   // constant can be replaced by one of the values
@@ -2610,6 +2709,56 @@ FoldingRule RedundantBinaryLhs0To0(spv::Op op) {
   return RedundantBinaryOpWithZeroOperand(0, 0);
 }
 
+FoldingRule ReassociateCommutiveOp() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants) {
+    const analysis::Type* type =
+        context->get_type_mgr()->GetType(inst->type_id());
+    uint32_t width = ElementWidth(type);
+    if (width != 32) return false;
+
+    analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+    const analysis::Constant* const_input1 = ConstInput(constants);
+    if (!const_input1) return false;
+    Instruction* other_inst = NonConstInput(context, constants[0], inst);
+
+    if (other_inst->opcode() == inst->opcode()) {
+      std::vector<const analysis::Constant*> other_constants =
+          const_mgr->GetOperandConstants(other_inst);
+      const analysis::Constant* const_input2 = ConstInput(other_constants);
+      if (!const_input2) return false;
+
+      Instruction* non_const_input =
+          NonConstInput(context, other_constants[0], other_inst);
+      uint32_t merged_id = PerformOperation(const_mgr, inst->opcode(),
+                                            const_input1, const_input2);
+
+      if (merged_id == 0) return false;
+      inst->SetInOperands(
+          {{SPV_OPERAND_TYPE_ID, {non_const_input->result_id()}},
+           {SPV_OPERAND_TYPE_ID, {merged_id}}});
+      return true;
+    }
+
+    return false;
+  };
+}
+
+// A | (b | C) = b | (A | C)
+// A ^ (b ^ C) = b ^ (A ^ C)
+// A & (b & C) = b & (A & C)
+// Where A and C are constants
+static const constexpr spv::Op ReassociateCommutiveBitwiseOps[] = {
+    spv::Op::OpBitwiseOr, spv::Op::OpBitwiseXor, spv::Op::OpBitwiseAnd};
+FoldingRule ReassociateCommutiveBitwise(spv::Op op) {
+  assert(std::find(std::begin(ReassociateCommutiveBitwiseOps),
+                   std::end(ReassociateCommutiveBitwiseOps),
+                   op) != std::end(ReassociateCommutiveBitwiseOps) &&
+         "Wrong opcode.");
+  (void)op;
+  return ReassociateCommutiveOp();
+}
+
 // Returns true if all elements in |c| are 1.
 bool IsAllInt1(const analysis::Constant* c) {
   if (auto composite = c->AsCompositeConstant()) {
@@ -3013,12 +3162,15 @@ void FoldingRules::AddFoldingRules() {
     rules_[op].push_back(RedundantBinaryLhs0(op));
   for (auto op : RedundantBinaryLhs0To0Ops)
     rules_[op].push_back(RedundantBinaryLhs0To0(op));
+  for (auto op : ReassociateCommutiveBitwiseOps)
+    rules_[op].push_back(ReassociateCommutiveBitwise(op));
   rules_[spv::Op::OpSDiv].push_back(RedundantSUDiv());
   rules_[spv::Op::OpUDiv].push_back(RedundantSUDiv());
   rules_[spv::Op::OpSMod].push_back(RedundantSUMod());
   rules_[spv::Op::OpUMod].push_back(RedundantSUMod());
 
   rules_[spv::Op::OpBitcast].push_back(BitCastScalarOrVector());
+  rules_[spv::Op::OpBitReverse].push_back(BitReverseScalarOrVector());
 
   rules_[spv::Op::OpCompositeConstruct].push_back(
       CompositeExtractFeedingConstruct);

+ 4 - 0
3rdparty/spirv-tools/source/opt/function.cpp

@@ -40,6 +40,10 @@ Function* Function::Clone(IRContext* ctx) const {
   clone->blocks_.reserve(blocks_.size());
   for (const auto& b : blocks_) {
     std::unique_ptr<BasicBlock> bb(b->Clone(ctx));
+    if (!bb) {
+      delete clone;
+      return nullptr;
+    }
     clone->AddBasicBlock(std::move(bb));
   }
 

+ 1 - 1
3rdparty/spirv-tools/source/opt/function.h

@@ -48,7 +48,7 @@ class Function {
 
   explicit Function(const Function& f) = delete;
 
-  // Creates a clone of the instruction in the given |context|
+  // Creates a clone of the function in the given |context|
   //
   // The parent module will default to null and needs to be explicitly set by
   // the user.

+ 90 - 0
3rdparty/spirv-tools/source/opt/graph.cpp

@@ -0,0 +1,90 @@
+// Copyright (c) 2022-2025 Arm Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/opt/graph.h"
+
+namespace spvtools {
+namespace opt {
+
+Graph* Graph::Clone(IRContext* ctx) const {
+  Graph* clone = new Graph(std::unique_ptr<Instruction>(DefInst().Clone(ctx)));
+
+  clone->inputs_.reserve(inputs_.size());
+  for (const auto& i : inputs()) {
+    clone->AddInput(std::unique_ptr<Instruction>(i->Clone(ctx)));
+  }
+
+  clone->insts_.reserve(insts_.size());
+  for (const auto& i : instructions()) {
+    clone->AddInstruction(std::unique_ptr<Instruction>(i->Clone(ctx)));
+  }
+
+  clone->outputs_.reserve(outputs_.size());
+  for (const auto& i : outputs()) {
+    clone->AddOutput(std::unique_ptr<Instruction>(i->Clone(ctx)));
+  }
+
+  clone->SetGraphEnd(std::unique_ptr<Instruction>(EndInst()->Clone(ctx)));
+
+  return clone;
+}
+
+void Graph::ForEachInst(const std::function<void(Instruction*)>& f,
+                        bool run_on_debug_line_insts,
+                        bool run_on_non_semantic_insts) {
+  (void)run_on_debug_line_insts;
+  (void)run_on_non_semantic_insts;
+
+  f(def_inst_.get());
+
+  for (auto& inst : inputs_) {
+    f(inst.get());
+  }
+
+  for (auto& inst : insts_) {
+    f(inst.get());
+  }
+
+  for (auto& inst : outputs_) {
+    f(inst.get());
+  }
+
+  f(end_inst_.get());
+}
+
+void Graph::ForEachInst(const std::function<void(const Instruction*)>& f,
+                        bool run_on_debug_line_insts,
+                        bool run_on_non_semantic_insts) const {
+  (void)run_on_debug_line_insts;
+  (void)run_on_non_semantic_insts;
+
+  f(def_inst_.get());
+
+  for (auto& inst : inputs_) {
+    f(inst.get());
+  }
+
+  for (auto& inst : insts_) {
+    f(inst.get());
+  }
+
+  for (auto& inst : outputs_) {
+    f(inst.get());
+  }
+
+  f(end_inst_.get());
+}
+
+}  // namespace opt
+}  // namespace spvtools

+ 124 - 0
3rdparty/spirv-tools/source/opt/graph.h

@@ -0,0 +1,124 @@
+// Copyright (c) 2022-2025 Arm Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_OPT_GRAPH_H_
+#define SOURCE_OPT_GRAPH_H_
+
+#include "source/opt/instruction.h"
+
+namespace spvtools {
+namespace opt {
+
+struct Graph {
+  // Creates a graph instance declared by the given OpGraph instruction
+  // |def_inst|.
+  inline explicit Graph(std::unique_ptr<Instruction> def_inst);
+  explicit Graph(const Graph& f) = delete;
+
+  // Creates a clone of the graph in the given |context|
+  //
+  // The parent module will default to null and needs to be explicitly set by
+  // the user.
+  Graph* Clone(IRContext*) const;
+
+  // The OpGraph instruction that begins the definition of this graph.
+  Instruction& DefInst() { return *def_inst_; }
+  const Instruction& DefInst() const { return *def_inst_; }
+
+  // Appends an input to this graph.
+  inline void AddInput(std::unique_ptr<Instruction> inst);
+
+  // Appends an instruction to this graph.
+  inline void AddInstruction(std::unique_ptr<Instruction> inst);
+
+  // Appends an output to this graph.
+  inline void AddOutput(std::unique_ptr<Instruction> inst);
+
+  // Saves the given graph end instruction.
+  void SetGraphEnd(std::unique_ptr<Instruction> end_inst);
+
+  // Returns the given graph end instruction.
+  inline Instruction* EndInst() { return end_inst_.get(); }
+  inline const Instruction* EndInst() const { return end_inst_.get(); }
+
+  // Returns graph's id
+  inline uint32_t result_id() const { return def_inst_->result_id(); }
+
+  // Returns graph's return type id
+  inline uint32_t type_id() const { return def_inst_->type_id(); }
+
+  // Return a read-only reference to the instructions that define the body of
+  // the graph.
+  const std::vector<std::unique_ptr<Instruction>>& instructions() const {
+    return insts_;
+  }
+
+  // Return a read-only reference to the instructions that define the inputs
+  // of the graph.
+  const std::vector<std::unique_ptr<Instruction>>& inputs() const {
+    return inputs_;
+  }
+
+  // Return a read-only reference to the instructions that define the outputs
+  // of the graph.
+  const std::vector<std::unique_ptr<Instruction>>& outputs() const {
+    return outputs_;
+  }
+
+  // Runs the given function |f| on instructions in this graph, in order,
+  // and optionally on debug line instructions that might precede them and
+  // non-semantic instructions that succceed the function.
+  void ForEachInst(const std::function<void(Instruction*)>& f,
+                   bool run_on_debug_line_insts = false,
+                   bool run_on_non_semantic_insts = false);
+  void ForEachInst(const std::function<void(const Instruction*)>& f,
+                   bool run_on_debug_line_insts = false,
+                   bool run_on_non_semantic_insts = false) const;
+
+ private:
+  // The OpGraph instruction that begins the definition of this graph.
+  std::unique_ptr<Instruction> def_inst_;
+  // All inputs to this graph.
+  std::vector<std::unique_ptr<Instruction>> inputs_;
+  // All instructions describing this graph
+  std::vector<std::unique_ptr<Instruction>> insts_;
+  // All outputs of this graph.
+  std::vector<std::unique_ptr<Instruction>> outputs_;
+  // The OpGraphEnd instruction.
+  std::unique_ptr<Instruction> end_inst_;
+};
+
+inline Graph::Graph(std::unique_ptr<Instruction> def_inst)
+    : def_inst_(std::move(def_inst)) {}
+
+inline void Graph::AddInput(std::unique_ptr<Instruction> inst) {
+  inputs_.emplace_back(std::move(inst));
+}
+
+inline void Graph::AddInstruction(std::unique_ptr<Instruction> inst) {
+  insts_.emplace_back(std::move(inst));
+}
+
+inline void Graph::AddOutput(std::unique_ptr<Instruction> inst) {
+  outputs_.emplace_back(std::move(inst));
+}
+
+inline void Graph::SetGraphEnd(std::unique_ptr<Instruction> end_inst) {
+  end_inst_ = std::move(end_inst);
+}
+
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // SOURCE_OPT_GRAPH_H_

+ 2 - 0
3rdparty/spirv-tools/source/opt/if_conversion.cpp

@@ -126,6 +126,7 @@ Pass::Status IfConversion::Process() {
           condition = SplatCondition(vec_data_ty, condition, &builder);
         }
 
+        // TODO(1841): Handle id overflow.
         Instruction* select = builder.AddSelect(phi->type_id(), condition,
                                                 true_value->result_id(),
                                                 false_value->result_id());
@@ -205,6 +206,7 @@ uint32_t IfConversion::SplatCondition(analysis::Vector* vec_data_ty,
   uint32_t bool_vec_id =
       context()->get_type_mgr()->GetTypeInstruction(&bool_vec_ty);
   std::vector<uint32_t> ids(vec_data_ty->element_count(), cond);
+  // TODO(1841): Handle id overflow.
   return builder->AddCompositeConstruct(bool_vec_id, ids)->result_id();
 }
 

+ 26 - 10
3rdparty/spirv-tools/source/opt/instruction.cpp

@@ -168,7 +168,13 @@ Instruction* Instruction::Clone(IRContext* c) const {
   clone->dbg_line_insts_ = dbg_line_insts_;
   for (auto& i : clone->dbg_line_insts_) {
     i.unique_id_ = c->TakeNextUniqueId();
-    if (i.IsDebugLineInst()) i.SetResultId(c->TakeNextId());
+    if (i.IsDebugLineInst()) {
+      uint32_t new_id = c->TakeNextId();
+      if (new_id == 0) {
+        return nullptr;
+      }
+      i.SetResultId(new_id);
+    }
   }
   clone->dbg_scope_ = dbg_scope_;
   return clone;
@@ -546,27 +552,37 @@ void Instruction::ClearDbgLineInsts() {
   clear_dbg_line_insts();
 }
 
-void Instruction::UpdateDebugInfoFrom(const Instruction* from,
+bool Instruction::UpdateDebugInfoFrom(const Instruction* from,
                                       const Instruction* line) {
-  if (from == nullptr) return;
+  if (from == nullptr) return true;
   ClearDbgLineInsts();
   const Instruction* fromLine = line != nullptr ? line : from;
-  if (!fromLine->dbg_line_insts().empty())
-    AddDebugLine(&fromLine->dbg_line_insts().back());
+  if (!fromLine->dbg_line_insts().empty()) {
+    if (!AddDebugLine(&fromLine->dbg_line_insts().back())) {
+      return false;
+    }
+  }
   SetDebugScope(from->GetDebugScope());
   if (!IsLineInst() &&
       context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) {
     context()->get_debug_info_mgr()->AnalyzeDebugInst(this);
   }
+  return true;
 }
 
-void Instruction::AddDebugLine(const Instruction* inst) {
+bool Instruction::AddDebugLine(const Instruction* inst) {
   dbg_line_insts_.push_back(*inst);
   dbg_line_insts_.back().unique_id_ = context()->TakeNextUniqueId();
-  if (inst->IsDebugLineInst())
-    dbg_line_insts_.back().SetResultId(context_->TakeNextId());
+  if (inst->IsDebugLineInst()) {
+    uint32_t new_id = context()->TakeNextId();
+    if (new_id == 0) {
+      return false;
+    }
+    dbg_line_insts_.back().SetResultId(new_id);
+  }
   if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse))
     context()->get_def_use_mgr()->AnalyzeInstDefUse(&dbg_line_insts_.back());
+  return true;
 }
 
 bool Instruction::IsDebugLineInst() const {
@@ -770,7 +786,7 @@ bool Instruction::IsFoldableByFoldScalar() const {
   // Even if the type of the instruction is foldable, its operands may not be
   // foldable (e.g., comparisons of 64bit types).  Check that all operand types
   // are foldable before accepting the instruction.
-  return WhileEachInOperand([&folder, this](const uint32_t* op_id) {
+  return WhileEachInId([&folder, this](const uint32_t* op_id) {
     Instruction* def_inst = context()->get_def_use_mgr()->GetDef(*op_id);
     Instruction* def_inst_type =
         context()->get_def_use_mgr()->GetDef(def_inst->type_id());
@@ -792,7 +808,7 @@ bool Instruction::IsFoldableByFoldVector() const {
   // Even if the type of the instruction is foldable, its operands may not be
   // foldable (e.g., comparisons of 64bit types).  Check that all operand types
   // are foldable before accepting the instruction.
-  return WhileEachInOperand([&folder, this](const uint32_t* op_id) {
+  return WhileEachInId([&folder, this](const uint32_t* op_id) {
     Instruction* def_inst = context()->get_def_use_mgr()->GetDef(*op_id);
     Instruction* def_inst_type =
         context()->get_def_use_mgr()->GetDef(def_inst->type_id());

+ 2 - 2
3rdparty/spirv-tools/source/opt/instruction.h

@@ -318,7 +318,7 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
   inline void SetDebugScope(const DebugScope& scope);
   inline const DebugScope& GetDebugScope() const { return dbg_scope_; }
   // Add debug line inst. Renew result id if Debug[No]Line
-  void AddDebugLine(const Instruction* inst);
+  bool AddDebugLine(const Instruction* inst);
   // Updates DebugInlinedAt of DebugScope and OpLine.
   void UpdateDebugInlinedAt(uint32_t new_inlined_at);
   // Clear line-related debug instructions attached to this instruction
@@ -338,7 +338,7 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
   // Updates lexical scope of DebugScope and OpLine.
   void UpdateLexicalScope(uint32_t scope);
   // Updates OpLine and DebugScope based on the information of |from|.
-  void UpdateDebugInfoFrom(const Instruction* from,
+  bool UpdateDebugInfoFrom(const Instruction* from,
                            const Instruction* line = nullptr);
   // Remove the |index|-th operand
   void RemoveOperand(uint32_t index) {

+ 64 - 34
3rdparty/spirv-tools/source/opt/ir_builder.h

@@ -166,10 +166,14 @@ class InstructionBuilder {
     for (size_t i = 0; i < operands.size(); i++) {
       ops.push_back({SPV_OPERAND_TYPE_ID, {operands[i]}});
     }
-    // TODO(1841): Handle id overflow.
-    std::unique_ptr<Instruction> new_inst(new Instruction(
-        GetContext(), opcode, type_id,
-        result != 0 ? result : GetContext()->TakeNextId(), ops));
+    if (result == 0) {
+      result = GetContext()->TakeNextId();
+      if (result == 0) {
+        return nullptr;
+      }
+    }
+    std::unique_ptr<Instruction> new_inst(
+        new Instruction(GetContext(), opcode, type_id, result, ops));
     return AddInstruction(std::move(new_inst));
   }
 
@@ -297,9 +301,12 @@ class InstructionBuilder {
   // The id |op1| is the left hand side of the operation.
   // The id |op2| is the right hand side of the operation.
   Instruction* AddIAdd(uint32_t type, uint32_t op1, uint32_t op2) {
-    // TODO(1841): Handle id overflow.
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
     std::unique_ptr<Instruction> inst(new Instruction(
-        GetContext(), spv::Op::OpIAdd, type, GetContext()->TakeNextId(),
+        GetContext(), spv::Op::OpIAdd, type, result_id,
         {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}));
     return AddInstruction(std::move(inst));
   }
@@ -311,9 +318,12 @@ class InstructionBuilder {
   Instruction* AddULessThan(uint32_t op1, uint32_t op2) {
     analysis::Bool bool_type;
     uint32_t type = GetContext()->get_type_mgr()->GetId(&bool_type);
-    // TODO(1841): Handle id overflow.
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
     std::unique_ptr<Instruction> inst(new Instruction(
-        GetContext(), spv::Op::OpULessThan, type, GetContext()->TakeNextId(),
+        GetContext(), spv::Op::OpULessThan, type, result_id,
         {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}));
     return AddInstruction(std::move(inst));
   }
@@ -325,9 +335,12 @@ class InstructionBuilder {
   Instruction* AddSLessThan(uint32_t op1, uint32_t op2) {
     analysis::Bool bool_type;
     uint32_t type = GetContext()->get_type_mgr()->GetId(&bool_type);
-    // TODO(1841): Handle id overflow.
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
     std::unique_ptr<Instruction> inst(new Instruction(
-        GetContext(), spv::Op::OpSLessThan, type, GetContext()->TakeNextId(),
+        GetContext(), spv::Op::OpSLessThan, type, result_id,
         {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}));
     return AddInstruction(std::move(inst));
   }
@@ -355,9 +368,12 @@ class InstructionBuilder {
   // bool) for |type|.
   Instruction* AddSelect(uint32_t type, uint32_t cond, uint32_t true_value,
                          uint32_t false_value) {
-    // TODO(1841): Handle id overflow.
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
     std::unique_ptr<Instruction> select(new Instruction(
-        GetContext(), spv::Op::OpSelect, type, GetContext()->TakeNextId(),
+        GetContext(), spv::Op::OpSelect, type, result_id,
         std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {cond}},
                                        {SPV_OPERAND_TYPE_ID, {true_value}},
                                        {SPV_OPERAND_TYPE_ID, {false_value}}}));
@@ -381,10 +397,12 @@ class InstructionBuilder {
       ops.emplace_back(SPV_OPERAND_TYPE_ID,
                        std::initializer_list<uint32_t>{id});
     }
-    // TODO(1841): Handle id overflow.
-    std::unique_ptr<Instruction> construct(
-        new Instruction(GetContext(), spv::Op::OpCompositeConstruct, type,
-                        GetContext()->TakeNextId(), ops));
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
+    std::unique_ptr<Instruction> construct(new Instruction(
+        GetContext(), spv::Op::OpCompositeConstruct, type, result_id, ops));
     return AddInstruction(std::move(construct));
   }
 
@@ -466,10 +484,12 @@ class InstructionBuilder {
       operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}});
     }
 
-    // TODO(1841): Handle id overflow.
-    std::unique_ptr<Instruction> new_inst(
-        new Instruction(GetContext(), spv::Op::OpCompositeExtract, type,
-                        GetContext()->TakeNextId(), operands));
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
+    std::unique_ptr<Instruction> new_inst(new Instruction(
+        GetContext(), spv::Op::OpCompositeExtract, type, result_id, operands));
     return AddInstruction(std::move(new_inst));
   }
 
@@ -493,9 +513,12 @@ class InstructionBuilder {
       operands.push_back({SPV_OPERAND_TYPE_ID, {index_id}});
     }
 
-    // TODO(1841): Handle id overflow.
-    std::unique_ptr<Instruction> new_inst(new Instruction(
-        GetContext(), opcode, type_id, GetContext()->TakeNextId(), operands));
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
+    std::unique_ptr<Instruction> new_inst(
+        new Instruction(GetContext(), opcode, type_id, result_id, operands));
     return AddInstruction(std::move(new_inst));
   }
 
@@ -521,29 +544,36 @@ class InstructionBuilder {
       operands.push_back({SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, {alignment}});
     }
 
-    // TODO(1841): Handle id overflow.
-    std::unique_ptr<Instruction> new_inst(
-        new Instruction(GetContext(), spv::Op::OpLoad, type_id,
-                        GetContext()->TakeNextId(), operands));
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
+    std::unique_ptr<Instruction> new_inst(new Instruction(
+        GetContext(), spv::Op::OpLoad, type_id, result_id, operands));
     return AddInstruction(std::move(new_inst));
   }
 
   Instruction* AddCopyObject(uint32_t type_id, uint32_t value_id) {
     std::vector<Operand> operands{{SPV_OPERAND_TYPE_ID, {value_id}}};
 
-    // TODO(1841): Handle id overflow.
-    std::unique_ptr<Instruction> new_inst(
-        new Instruction(GetContext(), spv::Op::OpCopyObject, type_id,
-                        GetContext()->TakeNextId(), operands));
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
+    std::unique_ptr<Instruction> new_inst(new Instruction(
+        GetContext(), spv::Op::OpCopyObject, type_id, result_id, operands));
     return AddInstruction(std::move(new_inst));
   }
 
   Instruction* AddVariable(uint32_t type_id, uint32_t storage_class) {
     std::vector<Operand> operands;
     operands.push_back({SPV_OPERAND_TYPE_STORAGE_CLASS, {storage_class}});
-    std::unique_ptr<Instruction> new_inst(
-        new Instruction(GetContext(), spv::Op::OpVariable, type_id,
-                        GetContext()->TakeNextId(), operands));
+    uint32_t result_id = GetContext()->TakeNextId();
+    if (result_id == 0) {
+      return nullptr;
+    }
+    std::unique_ptr<Instruction> new_inst(new Instruction(
+        GetContext(), spv::Op::OpVariable, type_id, result_id, operands));
     return AddInstruction(std::move(new_inst));
   }
 

+ 14 - 0
3rdparty/spirv-tools/source/opt/ir_context.cpp

@@ -93,6 +93,9 @@ void IRContext::BuildInvalidAnalyses(IRContext::Analysis set) {
   if (set & kAnalysisLiveness) {
     BuildLivenessManager();
   }
+  if (set & kAnalysisIdToGraphMapping) {
+    BuildIdToGraphMapping();
+  }
 }
 
 void IRContext::InvalidateAnalysesExceptFor(
@@ -164,6 +167,9 @@ void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) {
   if (analyses_to_invalidate & kAnalysisDebugInfo) {
     debug_info_mgr_.reset(nullptr);
   }
+  if (analyses_to_invalidate & kAnalysisIdToGraphMapping) {
+    id_to_graph_.clear();
+  }
 
   valid_analyses_ = Analysis(valid_analyses_ & ~analyses_to_invalidate);
 }
@@ -393,6 +399,14 @@ bool IRContext::IsConsistent() {
     }
   }
 
+  if (AreAnalysesValid(kAnalysisIdToGraphMapping)) {
+    for (auto& g : module_->graphs()) {
+      if (id_to_graph_[g->DefInst().result_id()] != g.get()) {
+        return false;
+      }
+    }
+  }
+
   if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) {
     for (auto& func : *module()) {
       for (auto& block : func) {

+ 50 - 3
3rdparty/spirv-tools/source/opt/ir_context.h

@@ -84,7 +84,8 @@ class IRContext {
     kAnalysisTypes = 1 << 15,
     kAnalysisDebugInfo = 1 << 16,
     kAnalysisLiveness = 1 << 17,
-    kAnalysisEnd = 1 << 18
+    kAnalysisIdToGraphMapping = 1 << 18,
+    kAnalysisEnd = 1 << 19
   };
 
   using ProcessFunction = std::function<bool(Function*)>;
@@ -109,7 +110,8 @@ class IRContext {
         id_to_name_(nullptr),
         max_id_bound_(kDefaultMaxIdBound),
         preserve_bindings_(false),
-        preserve_spec_constants_(false) {
+        preserve_spec_constants_(false),
+        id_overflow_(false) {
     SetContextMessageConsumer(syntax_context_, consumer_);
     module_->SetContext(this);
   }
@@ -127,7 +129,8 @@ class IRContext {
         id_to_name_(nullptr),
         max_id_bound_(kDefaultMaxIdBound),
         preserve_bindings_(false),
-        preserve_spec_constants_(false) {
+        preserve_spec_constants_(false),
+        id_overflow_(false) {
     SetContextMessageConsumer(syntax_context_, consumer_);
     module_->SetContext(this);
     InitializeCombinators();
@@ -563,6 +566,7 @@ class IRContext {
   inline uint32_t TakeNextId() {
     uint32_t next_id = module()->TakeNextIdBound();
     if (next_id == 0) {
+      id_overflow_ = true;
       if (consumer()) {
         std::string message = "ID overflow. Try running compact-ids.";
         consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str());
@@ -583,6 +587,13 @@ class IRContext {
     return next_id;
   }
 
+  // Returns true if an ID overflow has occurred since the last time the flag
+  // was cleared.
+  bool id_overflow() const { return id_overflow_; }
+
+  // Clears the ID overflow flag.
+  void clear_id_overflow() { id_overflow_ = false; }
+
   FeatureManager* get_feature_mgr() {
     if (!feature_mgr_.get()) {
       AnalyzeFeatures();
@@ -641,6 +652,23 @@ class IRContext {
     return GetFunction(inst->result_id());
   }
 
+  // Returns the graph whose id is |id|, if one exists.  Returns |nullptr|
+  // otherwise.
+  Graph* GetGraph(uint32_t id) {
+    if (!AreAnalysesValid(kAnalysisIdToGraphMapping)) {
+      BuildIdToGraphMapping();
+    }
+    auto entry = id_to_graph_.find(id);
+    return (entry != id_to_graph_.end()) ? entry->second : nullptr;
+  }
+
+  Graph* GetGraph(Instruction* inst) {
+    if (inst->opcode() != spv::Op::OpGraphARM) {
+      return nullptr;
+    }
+    return GetGraph(inst->result_id());
+  }
+
   // Add to |todo| all ids of functions called directly from |func|.
   void AddCalls(const Function* func, std::queue<uint32_t>* todo);
 
@@ -719,6 +747,15 @@ class IRContext {
     valid_analyses_ = valid_analyses_ | kAnalysisIdToFuncMapping;
   }
 
+  // Builds the instruction-graph map for the whole module.
+  void BuildIdToGraphMapping() {
+    id_to_graph_.clear();
+    for (auto& g : module_->graphs()) {
+      id_to_graph_[g->DefInst().result_id()] = g.get();
+    }
+    valid_analyses_ = valid_analyses_ | kAnalysisIdToGraphMapping;
+  }
+
   void BuildDecorationManager() {
     decoration_mgr_ = MakeUnique<analysis::DecorationManager>(module());
     valid_analyses_ = valid_analyses_ | kAnalysisDecorations;
@@ -872,6 +909,13 @@ class IRContext {
   // iterators to traverse instructions.
   std::unordered_map<uint32_t, Function*> id_to_func_;
 
+  // A map from ids to the graph they define. This mapping is
+  // built on-demand when GetGraph() is called.
+  //
+  // NOTE: Do not traverse this map. Ever. Use the graph iterators to
+  // traverse instructions.
+  std::unordered_map<uint32_t, Graph*> id_to_graph_;
+
   // A bitset indicating which analyzes are currently valid.
   Analysis valid_analyses_;
 
@@ -930,6 +974,9 @@ class IRContext {
   // Whether all specialization constants within |module_|
   // should be preserved.
   bool preserve_spec_constants_;
+
+  // Set to true if TakeNextId() fails.
+  bool id_overflow_;
 };
 
 inline IRContext::Analysis operator|(IRContext::Analysis lhs,

+ 36 - 0
3rdparty/spirv-tools/source/opt/ir_loader.cpp

@@ -178,6 +178,40 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
     last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
     last_line_inst_.reset();
     dbg_line_info_.clear();
+  } else if (opcode == spv::Op::OpGraphARM) {
+    if (graph_ != nullptr) {
+      Error(consumer_, src, loc, "graph inside graph");
+      return false;
+    }
+    graph_ = MakeUnique<Graph>(std::move(spv_inst));
+  } else if (opcode == spv::Op::OpGraphEndARM) {
+    if (graph_ == nullptr) {
+      Error(consumer_, src, loc,
+            "OpGraphEndARM without corresponding OpGraphARM");
+      return false;
+    }
+    graph_->SetGraphEnd(std::move(spv_inst));
+    module_->AddGraph(std::move(graph_));
+    graph_ = nullptr;
+  } else if (opcode == spv::Op::OpGraphConstantARM) {
+    module_->AddGlobalValue(std::move(spv_inst));
+  } else if (graph_ != nullptr) {
+    if (opcode == spv::Op::OpGraphInputARM) {
+      graph_->AddInput(std::move(spv_inst));
+    } else if (opcode == spv::Op::OpGraphSetOutputARM) {
+      graph_->AddOutput(std::move(spv_inst));
+    } else {
+      switch (opcode) {
+        case spv::Op::OpExtInst:
+        case spv::Op::OpCompositeExtract:
+          graph_->AddInstruction(std::move(spv_inst));
+          break;
+        default:
+          Errorf(consumer_, src, loc,
+                 "unhandled instruction (opcode %d) inside graph", opcode);
+          return false;
+      }
+    }
   } else {
     if (function_ == nullptr) {  // Outside function definition
       SPIRV_ASSERT(consumer_, block_ == nullptr);
@@ -195,6 +229,8 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
         module_->SetSampledImageAddressMode(std::move(spv_inst));
       } else if (opcode == spv::Op::OpEntryPoint) {
         module_->AddEntryPoint(std::move(spv_inst));
+      } else if (opcode == spv::Op::OpGraphEntryPointARM) {
+        module_->AddGraphEntryPoint(std::move(spv_inst));
       } else if (opcode == spv::Op::OpExecutionMode ||
                  opcode == spv::Op::OpExecutionModeId) {
         module_->AddExecutionMode(std::move(spv_inst));

+ 3 - 0
3rdparty/spirv-tools/source/opt/ir_loader.h

@@ -20,6 +20,7 @@
 #include <vector>
 
 #include "source/opt/basic_block.h"
+#include "source/opt/graph.h"
 #include "source/opt/instruction.h"
 #include "source/opt/module.h"
 #include "spirv-tools/libspirv.hpp"
@@ -80,6 +81,8 @@ class IrLoader {
   std::unique_ptr<Function> function_;
   // The current BasicBlock under construction.
   std::unique_ptr<BasicBlock> block_;
+  // The current Graph under construction.
+  std::unique_ptr<Graph> graph_;
   // Line related debug instructions accumulated thus far.
   std::vector<Instruction> dbg_line_info_;
   // If doing extra line tracking, this is the line instruction that should be

+ 0 - 1
3rdparty/spirv-tools/source/opt/licm_pass.cpp

@@ -118,7 +118,6 @@ bool LICMPass::IsImmediatelyContainedInLoop(Loop* loop, Function* f,
 }
 
 bool LICMPass::HoistInstruction(Loop* loop, Instruction* inst) {
-  // TODO(1841): Handle failure to create pre-header.
   BasicBlock* pre_header_bb = loop->GetOrCreatePreHeaderBlock();
   if (!pre_header_bb) {
     return false;

+ 10 - 5
3rdparty/spirv-tools/source/opt/loop_descriptor.cpp

@@ -25,6 +25,7 @@
 #include "source/opt/dominator_tree.h"
 #include "source/opt/ir_context.h"
 #include "source/opt/iterator.h"
+#include "source/opt/pass.h"
 #include "source/opt/tree_iterator.h"
 #include "source/util/make_unique.h"
 
@@ -278,6 +279,9 @@ BasicBlock* Loop::GetOrCreatePreHeaderBlock() {
 
   CFG* cfg = context_->cfg();
   loop_header_ = cfg->SplitLoopHeader(loop_header_);
+  if (loop_header_ == nullptr) {
+    return nullptr;
+  }
   return loop_preheader_;
 }
 
@@ -920,18 +924,19 @@ Instruction* Loop::FindConditionVariable(
   return induction;
 }
 
-bool LoopDescriptor::CreatePreHeaderBlocksIfMissing() {
-  auto modified = false;
+LoopDescriptor::Status LoopDescriptor::CreatePreHeaderBlocksIfMissing() {
+  bool modified = false;
 
   for (auto& loop : *this) {
     if (!loop.GetPreHeaderBlock()) {
+      if (!loop.GetOrCreatePreHeaderBlock()) {
+        return Status::kFailure;
+      }
       modified = true;
-      // TODO(1841): Handle failure to create pre-header.
-      loop.GetOrCreatePreHeaderBlock();
     }
   }
 
-  return modified;
+  return modified ? Status::kSuccessWithChange : Status::kSuccessWithoutChange;
 }
 
 // Add and remove loops which have been marked for addition and removal to

+ 8 - 3
3rdparty/spirv-tools/source/opt/loop_descriptor.h

@@ -425,6 +425,9 @@ class LoopDescriptor {
   using pre_iterator = TreeDFIterator<Loop>;
   using const_pre_iterator = TreeDFIterator<const Loop>;
 
+  // The status of processing a module.
+  enum class Status { kSuccessWithChange, kSuccessWithoutChange, kFailure };
+
   // Creates a loop object for all loops found in |f|.
   LoopDescriptor(IRContext* context, const Function* f);
 
@@ -506,9 +509,11 @@ class LoopDescriptor {
     loops_to_add_.emplace_back(std::make_pair(parent, std::move(loop_to_add)));
   }
 
-  // Checks all loops in |this| and will create pre-headers for all loops
-  // that don't have one. Returns |true| if any blocks were created.
-  bool CreatePreHeaderBlocksIfMissing();
+  // Creates pre-header blocks for all loops in the function that do not have
+  // one. Returns `LoopDescriptor::Status::kSuccessWithChange` if any change is
+  // made, `LoopDescriptor::Status::kSuccessWithoutChange` if no change is made,
+  // and `LoopDescriptor::Status::kFailure` if it fails to create a pre-header.
+  Status CreatePreHeaderBlocksIfMissing();
 
   // Should be called to preserve the LoopAnalysis after loops have been marked
   // for addition with AddLoop or MarkLoopForRemoval.

+ 11 - 7
3rdparty/spirv-tools/source/opt/loop_fusion_pass.cpp

@@ -22,23 +22,27 @@ namespace spvtools {
 namespace opt {
 
 Pass::Status LoopFusionPass::Process() {
-  bool modified = false;
+  Status status = Status::SuccessWithoutChange;
   Module* module = context()->module();
 
   // Process each function in the module
   for (Function& f : *module) {
-    modified |= ProcessFunction(&f);
+    status = CombineStatus(status, ProcessFunction(&f));
+    if (status == Status::Failure) return Status::Failure;
   }
 
-  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
+  return status;
 }
 
-bool LoopFusionPass::ProcessFunction(Function* function) {
+Pass::Status LoopFusionPass::ProcessFunction(Function* function) {
   LoopDescriptor& ld = *context()->GetLoopDescriptor(function);
 
   // If a loop doesn't have a preheader needs then it needs to be created. Make
   // sure to return Status::SuccessWithChange in that case.
-  auto modified = ld.CreatePreHeaderBlocksIfMissing();
+  bool modified = false;
+  auto status = ld.CreatePreHeaderBlocksIfMissing();
+  if (status == LoopDescriptor::Status::kFailure) return Status::Failure;
+  modified = status == LoopDescriptor::Status::kSuccessWithChange;
 
   // TODO(tremmelg): Could the only loop that |loop| could possibly be fused be
   // picked out so don't have to check every loop
@@ -55,13 +59,13 @@ bool LoopFusionPass::ProcessFunction(Function* function) {
           fusion.Fuse();
           // Recurse, as the current iterators will have been invalidated.
           ProcessFunction(function);
-          return true;
+          return Status::SuccessWithChange;
         }
       }
     }
   }
 
-  return modified;
+  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
 }  // namespace opt

+ 1 - 1
3rdparty/spirv-tools/source/opt/loop_fusion_pass.h

@@ -39,7 +39,7 @@ class LoopFusionPass : public Pass {
  private:
   // Fuse loops in |function| if compatible, legal and the fused loop won't use
   // too many registers.
-  bool ProcessFunction(Function* function);
+  Status ProcessFunction(Function* function);
 
   // The maximum number of registers a fused loop is allowed to use.
   size_t max_registers_per_loop_;

+ 10 - 4
3rdparty/spirv-tools/source/opt/loop_peeling.cpp

@@ -564,11 +564,15 @@ bool LoopPeeling::PeelAfter(uint32_t peel_factor) {
   // factor < loop_iteration_count_.
 
   // The original loop's pre-header was the cloned loop merge block.
-  GetClonedLoop()->SetMergeBlock(
-      CreateBlockBefore(GetOriginalLoop()->GetPreHeaderBlock()));
-  if (!GetClonedLoop()->GetMergeBlock()) {
+  BasicBlock* pre_header = GetOriginalLoop()->GetPreHeaderBlock();
+  if (!pre_header) {
+    return false;
+  }
+  BasicBlock* new_merge_block = CreateBlockBefore(pre_header);
+  if (!new_merge_block) {
     return false;
   }
+  GetClonedLoop()->SetMergeBlock(new_merge_block);
   // Use the second loop preheader as if merge block.
 
   // Prevent the first loop if only the peeled loop needs it.
@@ -665,7 +669,9 @@ Pass::Status LoopPeelingPass::ProcessFunction(Function* f) {
     auto try_peel = [&loop_size, &modified, this](
                         Loop* loop_to_peel) -> std::pair<Pass::Status, Loop*> {
       if (!loop_to_peel->IsLCSSA()) {
-        LoopUtils(context(), loop_to_peel).MakeLoopClosedSSA();
+        if (!LoopUtils(context(), loop_to_peel).MakeLoopClosedSSA()) {
+          return {Pass::Status::Failure, nullptr};
+        }
       }
 
       Pass::Status status;

+ 109 - 43
3rdparty/spirv-tools/source/opt/loop_unroller.cpp

@@ -178,7 +178,7 @@ class LoopUnrollerUtilsImpl {
 
   // Unroll the |loop| by given |factor| by copying the whole body |factor|
   // times. The resulting basicblock structure will remain a loop.
-  void PartiallyUnroll(Loop*, size_t factor);
+  bool PartiallyUnroll(Loop*, size_t factor);
 
   // If partially unrolling the |loop| would leave the loop with too many bodies
   // for its number of iterations then this method should be used. This method
@@ -186,12 +186,12 @@ class LoopUnrollerUtilsImpl {
   // successor of the original's merge block. The original loop will have its
   // condition changed to loop over the residual part and the duplicate will be
   // partially unrolled. The resulting structure will be two loops.
-  void PartiallyUnrollResidualFactor(Loop* loop, size_t factor);
+  bool PartiallyUnrollResidualFactor(Loop* loop, size_t factor);
 
   // Fully unroll the |loop| by copying the full body by the total number of
   // loop iterations, folding all conditions, and removing the backedge from the
   // continue block to the header.
-  void FullyUnroll(Loop* loop);
+  bool FullyUnroll(Loop* loop);
 
   // Get the ID of the variable in the |phi| paired with |label|.
   uint32_t GetPhiDefID(const Instruction* phi, uint32_t label) const;
@@ -203,7 +203,7 @@ class LoopUnrollerUtilsImpl {
   // Remove the OpConditionalBranch instruction inside |conditional_block| used
   // to branch to either exit or continue the loop and replace it with an
   // unconditional OpBranch to block |new_target|.
-  void FoldConditionBlock(BasicBlock* condtion_block, uint32_t new_target);
+  bool FoldConditionBlock(BasicBlock* condtion_block, uint32_t new_target);
 
   // Add all blocks_to_add_ to function_ at the |insert_point|.
   void AddBlocksToFunction(const BasicBlock* insert_point);
@@ -211,7 +211,7 @@ class LoopUnrollerUtilsImpl {
   // Duplicates the |old_loop|, cloning each body and remapping the ids without
   // removing instructions or changing relative structure. Result will be stored
   // in |new_loop|.
-  void DuplicateLoop(Loop* old_loop, Loop* new_loop);
+  bool DuplicateLoop(Loop* old_loop, Loop* new_loop);
 
   inline size_t GetLoopIterationCount() const {
     return number_of_loop_iterations_;
@@ -241,7 +241,7 @@ class LoopUnrollerUtilsImpl {
   // to old
   // ids. |loop| is used to identify special loop blocks (header, continue,
   // etc).
-  void AssignNewResultIds(BasicBlock* basic_block);
+  bool AssignNewResultIds(BasicBlock* basic_block);
 
   // Using the map built by AssignNewResultIds, replace the uses in |inst|
   // by the id that the use maps to.
@@ -258,18 +258,18 @@ class LoopUnrollerUtilsImpl {
   // the old |loop| continue block and the new body will link to the |loop|
   // header via the new continue block. |eliminate_conditions| is used to decide
   // whether or not to fold all the condition blocks other than the last one.
-  void CopyBody(Loop* loop, bool eliminate_conditions);
+  bool CopyBody(Loop* loop, bool eliminate_conditions);
 
   // Copy a given |block_to_copy| in the |loop| and record the mapping of the
   // old/new ids. |preserve_instructions| determines whether or not the method
   // will modify (other than result_id) instructions which are copied.
-  void CopyBasicBlock(Loop* loop, const BasicBlock* block_to_copy,
+  bool CopyBasicBlock(Loop* loop, const BasicBlock* block_to_copy,
                       bool preserve_instructions);
 
   // The actual implementation of the unroll step. Unrolls |loop| by given
   // |factor| by copying the body by |factor| times. Also propagates the
   // induction variable value throughout the copies.
-  void Unroll(Loop* loop, size_t factor);
+  bool Unroll(Loop* loop, size_t factor);
 
   // Fills the loop_blocks_inorder_ field with the ordered list of basic blocks
   // as computed by the method ComputeLoopOrderedBlocks.
@@ -376,11 +376,12 @@ void LoopUnrollerUtilsImpl::Init(Loop* loop) {
 // loop it creates two loops and unrolls one and adjusts the condition on the
 // other. The end result being that the new loop pair iterates over the correct
 // number of bodies.
-void LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop,
+bool LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop,
                                                           size_t factor) {
-  // TODO(1841): Handle id overflow.
-  std::unique_ptr<Instruction> new_label{new Instruction(
-      context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {})};
+  uint32_t new_label_id = context_->TakeNextId();
+  if (new_label_id == 0) return false;
+  std::unique_ptr<Instruction> new_label{
+      new Instruction(context_, spv::Op::OpLabel, 0, new_label_id, {})};
   std::unique_ptr<BasicBlock> new_exit_bb{new BasicBlock(std::move(new_label))};
   new_exit_bb->SetParent(&function_);
 
@@ -401,7 +402,9 @@ void LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop,
   // Clear the basic blocks of the new loop.
   new_loop->ClearBlocks();
 
-  DuplicateLoop(loop, new_loop.get());
+  if (!DuplicateLoop(loop, new_loop.get())) {
+    return false;
+  }
 
   // Add the blocks to the function.
   AddBlocksToFunction(loop->GetMergeBlock());
@@ -416,7 +419,9 @@ void LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop,
   loop_induction_variable_ = state_.new_phi;
   // Unroll the new loop by the factor with the usual -1 to account for the
   // existing block iteration.
-  Unroll(new_loop.get(), factor);
+  if (!Unroll(new_loop.get(), factor)) {
+    return false;
+  }
 
   LinkLastPhisToStart(new_loop.get());
   AddBlocksToLoop(new_loop.get());
@@ -460,6 +465,10 @@ void LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop,
     new_constant = builder.GetUintConstant(static_cast<int32_t>(remainder));
   }
 
+  if (!new_constant) {
+    return false;
+  }
+
   uint32_t constant_id = new_constant->result_id();
 
   // Update the condition check.
@@ -477,6 +486,9 @@ void LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop,
   for (size_t index = 0; index < new_inductions.size(); ++index) {
     Instruction* new_induction = new_inductions[index];
     Instruction* old_induction = old_inductions[index];
+    if (!new_induction || !old_induction) {
+      return false;
+    }
     // Get the index of the loop initalizer, the value coming in from the
     // preheader.
     uint32_t initalizer_index =
@@ -512,6 +524,7 @@ void LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop,
   loop_descriptor.AddLoop(std::move(new_loop), loop->GetParent());
 
   RemoveDeadInstructions();
+  return true;
 }
 
 // Mark this loop as DontUnroll as it will already be unrolled and it may not
@@ -528,7 +541,7 @@ void LoopUnrollerUtilsImpl::MarkLoopControlAsDontUnroll(Loop* loop) const {
 // Duplicate the |loop| body |factor| - 1 number of times while keeping the loop
 // backedge intact. This will leave the loop with |factor| number of bodies
 // after accounting for the initial body.
-void LoopUnrollerUtilsImpl::Unroll(Loop* loop, size_t factor) {
+bool LoopUnrollerUtilsImpl::Unroll(Loop* loop, size_t factor) {
   // If we unroll a loop partially it will not be safe to unroll it further.
   // This is due to the current method of calculating the number of loop
   // iterations.
@@ -539,8 +552,11 @@ void LoopUnrollerUtilsImpl::Unroll(Loop* loop, size_t factor) {
   state_ = LoopUnrollState{loop_induction_variable_, loop->GetLatchBlock(),
                            loop_condition_block_, std::move(inductions)};
   for (size_t i = 0; i < factor - 1; ++i) {
-    CopyBody(loop, true);
+    if (!CopyBody(loop, true)) {
+      return false;
+    }
   }
+  return true;
 }
 
 void LoopUnrollerUtilsImpl::RemoveDeadInstructions() {
@@ -573,12 +589,16 @@ void LoopUnrollerUtilsImpl::ReplaceInductionUseWithFinalValue(Loop* loop) {
 
 // Fully unroll the loop by partially unrolling it by the number of loop
 // iterations minus one for the body already accounted for.
-void LoopUnrollerUtilsImpl::FullyUnroll(Loop* loop) {
+bool LoopUnrollerUtilsImpl::FullyUnroll(Loop* loop) {
   // We unroll the loop by number of iterations in the loop.
-  Unroll(loop, number_of_loop_iterations_);
+  if (!Unroll(loop, number_of_loop_iterations_)) {
+    return false;
+  }
 
   // The first condition block is preserved until now so it can be copied.
-  FoldConditionBlock(loop_condition_block_, 1);
+  if (!FoldConditionBlock(loop_condition_block_, 1)) {
+    return false;
+  }
 
   // Delete the OpLoopMerge and remove the backedge to the header.
   CloseUnrolledLoop(loop);
@@ -602,6 +622,7 @@ void LoopUnrollerUtilsImpl::FullyUnroll(Loop* loop) {
   context_->InvalidateAnalysesExceptFor(
       IRContext::Analysis::kAnalysisLoopAnalysis |
       IRContext::Analysis::kAnalysisDefUse);
+  return true;
 }
 
 void LoopUnrollerUtilsImpl::KillDebugDeclares(BasicBlock* bb) {
@@ -622,10 +643,11 @@ void LoopUnrollerUtilsImpl::KillDebugDeclares(BasicBlock* bb) {
 // and the id mapping in the state. |preserve_instructions| is used to determine
 // whether or not this function should edit instructions other than the
 // |result_id|.
-void LoopUnrollerUtilsImpl::CopyBasicBlock(Loop* loop, const BasicBlock* itr,
+bool LoopUnrollerUtilsImpl::CopyBasicBlock(Loop* loop, const BasicBlock* itr,
                                            bool preserve_instructions) {
   // Clone the block exactly, including the IDs.
   BasicBlock* basic_block = itr->Clone(context_);
+  if (!basic_block) return false;
   basic_block->SetParent(itr->GetParent());
 
   // We do not want to duplicate DebugDeclare.
@@ -633,7 +655,9 @@ void LoopUnrollerUtilsImpl::CopyBasicBlock(Loop* loop, const BasicBlock* itr,
 
   // Assign each result a new unique ID and keep a mapping of the old ids to
   // the new ones.
-  AssignNewResultIds(basic_block);
+  if (!AssignNewResultIds(basic_block)) {
+    return false;
+  }
 
   // If this is the continue block we are copying.
   if (itr == loop->GetContinueBlock()) {
@@ -672,13 +696,16 @@ void LoopUnrollerUtilsImpl::CopyBasicBlock(Loop* loop, const BasicBlock* itr,
 
   // Keep tracking the old block via a map.
   state_.new_blocks[itr->id()] = basic_block;
+  return true;
 }
 
-void LoopUnrollerUtilsImpl::CopyBody(Loop* loop, bool eliminate_conditions) {
+bool LoopUnrollerUtilsImpl::CopyBody(Loop* loop, bool eliminate_conditions) {
   // Copy each basic block in the loop, give them new ids, and save state
   // information.
   for (const BasicBlock* itr : loop_blocks_inorder_) {
-    CopyBasicBlock(loop, itr, false);
+    if (!CopyBasicBlock(loop, itr, false)) {
+      return false;
+    }
   }
 
   // Set the previous latch block to point to the new header.
@@ -717,7 +744,9 @@ void LoopUnrollerUtilsImpl::CopyBody(Loop* loop, bool eliminate_conditions) {
 
   if (eliminate_conditions &&
       state_.new_condition_block != loop_condition_block_) {
-    FoldConditionBlock(state_.new_condition_block, 1);
+    if (!FoldConditionBlock(state_.new_condition_block, 1)) {
+      return false;
+    }
   }
 
   // Only reference to the header block is the backedge in the latch block,
@@ -733,6 +762,7 @@ void LoopUnrollerUtilsImpl::CopyBody(Loop* loop, bool eliminate_conditions) {
 
   // Swap the state so the new is now the previous.
   state_.NextIterationState();
+  return true;
 }
 
 uint32_t LoopUnrollerUtilsImpl::GetPhiDefID(const Instruction* phi,
@@ -746,7 +776,7 @@ uint32_t LoopUnrollerUtilsImpl::GetPhiDefID(const Instruction* phi,
   return 0;
 }
 
-void LoopUnrollerUtilsImpl::FoldConditionBlock(BasicBlock* condition_block,
+bool LoopUnrollerUtilsImpl::FoldConditionBlock(BasicBlock* condition_block,
                                                uint32_t operand_label) {
   // Remove the old conditional branch to the merge and continue blocks.
   Instruction& old_branch = *condition_block->tail();
@@ -763,8 +793,13 @@ void LoopUnrollerUtilsImpl::FoldConditionBlock(BasicBlock* condition_block,
           IRContext::Analysis::kAnalysisInstrToBlockMapping);
   Instruction* new_branch = builder.AddBranch(new_target);
 
-  if (!lines.empty()) new_branch->AddDebugLine(&lines.back());
+  if (!lines.empty()) {
+    if (!new_branch->AddDebugLine(&lines.back())) {
+      return false;
+    }
+  }
   new_branch->SetDebugScope(scope);
+  return true;
 }
 
 void LoopUnrollerUtilsImpl::CloseUnrolledLoop(Loop* loop) {
@@ -811,19 +846,24 @@ void LoopUnrollerUtilsImpl::CloseUnrolledLoop(Loop* loop) {
 }
 
 // Uses the first loop to create a copy of the loop with new IDs.
-void LoopUnrollerUtilsImpl::DuplicateLoop(Loop* old_loop, Loop* new_loop) {
+bool LoopUnrollerUtilsImpl::DuplicateLoop(Loop* old_loop, Loop* new_loop) {
   std::vector<BasicBlock*> new_block_order;
 
   // Copy every block in the old loop.
   for (const BasicBlock* itr : loop_blocks_inorder_) {
-    CopyBasicBlock(old_loop, itr, true);
+    if (!CopyBasicBlock(old_loop, itr, true)) {
+      return false;
+    }
     new_block_order.push_back(blocks_to_add_.back().get());
   }
 
   // Clone the merge block, give it a new id and record it in the state.
   BasicBlock* new_merge = old_loop->GetMergeBlock()->Clone(context_);
+  if (!new_merge) return false;
   new_merge->SetParent(old_loop->GetMergeBlock()->GetParent());
-  AssignNewResultIds(new_merge);
+  if (!AssignNewResultIds(new_merge)) {
+    return false;
+  }
   state_.new_blocks[old_loop->GetMergeBlock()->id()] = new_merge;
 
   // Remap the operands of every instruction in the loop to point to the new
@@ -840,6 +880,7 @@ void LoopUnrollerUtilsImpl::DuplicateLoop(Loop* old_loop, Loop* new_loop) {
   new_loop->SetContinueBlock(state_.new_continue_block);
   new_loop->SetLatchBlock(state_.new_latch_block);
   new_loop->SetMergeBlock(new_merge);
+  return true;
 }
 
 // Whenever the utility copies a block it stores it in a temporary buffer, this
@@ -862,13 +903,15 @@ void LoopUnrollerUtilsImpl::AddBlocksToFunction(
 
 // Assign all result_ids in |basic_block| instructions to new IDs and preserve
 // the mapping of new ids to old ones.
-void LoopUnrollerUtilsImpl::AssignNewResultIds(BasicBlock* basic_block) {
+bool LoopUnrollerUtilsImpl::AssignNewResultIds(BasicBlock* basic_block) {
   analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr();
 
   // Label instructions aren't covered by normal traversal of the
   // instructions.
-  // TODO(1841): Handle id overflow.
   uint32_t new_label_id = context_->TakeNextId();
+  if (new_label_id == 0) {
+    return false;
+  }
 
   // Assign a new id to the label.
   state_.new_inst[basic_block->GetLabelInst()->result_id()] = new_label_id;
@@ -888,8 +931,11 @@ void LoopUnrollerUtilsImpl::AssignNewResultIds(BasicBlock* basic_block) {
     }
 
     // Give the instruction a new id.
-    // TODO(1841): Handle id overflow.
-    inst.SetResultId(context_->TakeNextId());
+    uint32_t new_id = context_->TakeNextId();
+    if (new_id == 0) {
+      return false;
+    }
+    inst.SetResultId(new_id);
     def_use_mgr->AnalyzeInstDef(&inst);
 
     // Save the mapping of old_id -> new_id.
@@ -901,6 +947,7 @@ void LoopUnrollerUtilsImpl::AssignNewResultIds(BasicBlock* basic_block) {
     }
     state_.ids_to_new_inst[inst.result_id()] = &inst;
   }
+  return true;
 }
 
 void LoopUnrollerUtilsImpl::RemapOperands(Instruction* inst) {
@@ -961,12 +1008,13 @@ void LoopUnrollerUtilsImpl::LinkLastPhisToStart(Loop* loop) const {
 
 // Duplicate the |loop| body |factor| number of times while keeping the loop
 // backedge intact.
-void LoopUnrollerUtilsImpl::PartiallyUnroll(Loop* loop, size_t factor) {
-  Unroll(loop, factor);
+bool LoopUnrollerUtilsImpl::PartiallyUnroll(Loop* loop, size_t factor) {
+  if (!Unroll(loop, factor)) return false;
   LinkLastPhisToStart(loop);
   AddBlocksToLoop(loop);
   AddBlocksToFunction(loop->GetMergeBlock());
   RemoveDeadInstructions();
+  return true;
 }
 
 /*
@@ -1071,7 +1119,9 @@ bool LoopUtils::PartiallyUnroll(size_t factor) {
   // If the unrolling factor is larger than or the same size as the loop just
   // fully unroll the loop.
   if (factor >= unroller.GetLoopIterationCount()) {
-    unroller.FullyUnroll(loop_);
+    if (!unroller.FullyUnroll(loop_)) {
+      return false;
+    }
     return true;
   }
 
@@ -1080,9 +1130,13 @@ bool LoopUtils::PartiallyUnroll(size_t factor) {
   // remaining part. We add one when calucating the remainder to take into
   // account the one iteration already in the loop.
   if (unroller.GetLoopIterationCount() % factor != 0) {
-    unroller.PartiallyUnrollResidualFactor(loop_, factor);
+    if (!unroller.PartiallyUnrollResidualFactor(loop_, factor)) {
+      return false;
+    }
   } else {
-    unroller.PartiallyUnroll(loop_, factor);
+    if (!unroller.PartiallyUnroll(loop_, factor)) {
+      return false;
+    }
   }
 
   return true;
@@ -1098,7 +1152,9 @@ bool LoopUtils::FullyUnroll() {
                                  loop_->GetHeaderBlock()->GetParent()};
 
   unroller.Init(loop_);
-  unroller.FullyUnroll(loop_);
+  if (!unroller.FullyUnroll(loop_)) {
+    return false;
+  }
 
   return true;
 }
@@ -1131,15 +1187,25 @@ Pass::Status LoopUnroller::Process() {
       }
 
       if (fully_unroll_) {
-        loop_utils.FullyUnroll();
+        if (!loop_utils.FullyUnroll()) {
+          return Status::Failure;
+        }
+        changed = true;
       } else {
-        loop_utils.PartiallyUnroll(unroll_factor_);
+        if (!loop_utils.PartiallyUnroll(unroll_factor_)) {
+          return Status::Failure;
+        }
+        changed = true;
       }
-      changed = true;
     }
     LD->PostModificationCleanup();
   }
 
+  if (changed) {
+    context()->InvalidateAnalysesExceptFor(
+        IRContext::Analysis::kAnalysisLoopAnalysis);
+  }
+
   return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 

+ 3 - 1
3rdparty/spirv-tools/source/opt/loop_unswitch_pass.cpp

@@ -623,7 +623,9 @@ Pass::Status LoopUnswitchPass::ProcessFunction(Function* f) {
       LoopUnswitch unswitcher(context(), f, &loop, &loop_descriptor);
       while (unswitcher.CanUnswitchLoop()) {
         if (!loop.IsLCSSA()) {
-          LoopUtils(context(), &loop).MakeLoopClosedSSA();
+          if (!LoopUtils(context(), &loop).MakeLoopClosedSSA()) {
+            return Status::Failure;
+          }
         }
         if (!unswitcher.PerformUnswitch()) {
           return Status::Failure;

+ 49 - 18
3rdparty/spirv-tools/source/opt/loop_utils.cpp

@@ -68,7 +68,7 @@ class LCSSARewriter {
     // block. This operation does not update the def/use manager, instead it
     // records what needs to be updated. The actual update is performed by
     // UpdateManagers.
-    void RewriteUse(BasicBlock* bb, Instruction* user, uint32_t operand_index) {
+    bool RewriteUse(BasicBlock* bb, Instruction* user, uint32_t operand_index) {
       assert(
           (user->opcode() != spv::Op::OpPhi || bb != GetParent(user)) &&
           "The root basic block must be the incoming edge if |user| is a phi "
@@ -79,9 +79,13 @@ class LCSSARewriter {
              "phi instruction");
 
       Instruction* new_def = GetOrBuildIncoming(bb->id());
+      if (!new_def) {
+        return false;
+      }
 
       user->SetOperand(operand_index, {new_def->result_id()});
       rewritten_.insert(user);
+      return true;
     }
 
     // In-place update of some managers (avoid full invalidation).
@@ -120,6 +124,9 @@ class LCSSARewriter {
                                  IRContext::kAnalysisInstrToBlockMapping);
       Instruction* incoming_phi =
           builder.AddPhi(def_insn_.type_id(), incomings);
+      if (!incoming_phi) {
+        return nullptr;
+      }
 
       rewritten_.insert(incoming_phi);
       return incoming_phi;
@@ -139,6 +146,9 @@ class LCSSARewriter {
                                  IRContext::kAnalysisInstrToBlockMapping);
       Instruction* incoming_phi =
           builder.AddPhi(def_insn_.type_id(), incomings);
+      if (!incoming_phi) {
+        return nullptr;
+      }
 
       rewritten_.insert(incoming_phi);
       return incoming_phi;
@@ -270,7 +280,7 @@ class LCSSARewriter {
 // Make the set |blocks| closed SSA. The set is closed SSA if all the uses
 // outside the set are phi instructions in exiting basic block set (hold by
 // |lcssa_rewriter|).
-inline void MakeSetClosedSSA(IRContext* context, Function* function,
+inline bool MakeSetClosedSSA(IRContext* context, Function* function,
                              const std::unordered_set<uint32_t>& blocks,
                              const std::unordered_set<BasicBlock*>& exit_bb,
                              LCSSARewriter* lcssa_rewriter) {
@@ -285,18 +295,18 @@ inline void MakeSetClosedSSA(IRContext* context, Function* function,
     if (!DominatesAnExit(bb, exit_bb, dom_tree)) continue;
     for (Instruction& inst : *bb) {
       LCSSARewriter::UseRewriter rewriter(lcssa_rewriter, inst);
-      def_use_manager->ForEachUse(
+      bool success = def_use_manager->WhileEachUse(
           &inst, [&blocks, &rewriter, &exit_bb, context](
                      Instruction* use, uint32_t operand_index) {
             BasicBlock* use_parent = context->get_instr_block(use);
             assert(use_parent);
-            if (blocks.count(use_parent->id())) return;
+            if (blocks.count(use_parent->id())) return true;
 
             if (use->opcode() == spv::Op::OpPhi) {
               // If the use is a Phi instruction and the incoming block is
               // coming from the loop, then that's consistent with LCSSA form.
               if (exit_bb.count(use_parent)) {
-                return;
+                return true;
               } else {
                 // That's not an exit block, but the user is a phi instruction.
                 // Consider the incoming branch only.
@@ -306,16 +316,20 @@ inline void MakeSetClosedSSA(IRContext* context, Function* function,
             }
             // Rewrite the use. Note that this call does not invalidate the
             // def/use manager. So this operation is safe.
-            rewriter.RewriteUse(use_parent, use, operand_index);
+            return rewriter.RewriteUse(use_parent, use, operand_index);
           });
+      if (!success) {
+        return false;
+      }
       rewriter.UpdateManagers();
     }
   }
+  return true;
 }
 
 }  // namespace
 
-void LoopUtils::CreateLoopDedicatedExits() {
+bool LoopUtils::CreateLoopDedicatedExits() {
   Function* function = loop_->GetHeaderBlock()->GetParent();
   LoopDescriptor& loop_desc = *context_->GetLoopDescriptor(function);
   CFG& cfg = *context_->cfg();
@@ -351,10 +365,13 @@ void LoopUtils::CreateLoopDedicatedExits() {
     assert(insert_pt != function->end() && "Basic Block not found");
 
     // Create the dedicate exit basic block.
-    // TODO(1841): Handle id overflow.
-    BasicBlock& exit = *insert_pt.InsertBefore(std::unique_ptr<BasicBlock>(
-        new BasicBlock(std::unique_ptr<Instruction>(new Instruction(
-            context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {})))));
+    uint32_t exit_id = context_->TakeNextId();
+    if (exit_id == 0) {
+      return false;
+    }
+    BasicBlock& exit = *insert_pt.InsertBefore(
+        std::unique_ptr<BasicBlock>(new BasicBlock(std::unique_ptr<Instruction>(
+            new Instruction(context_, spv::Op::OpLabel, 0, exit_id, {})))));
     exit.SetParent(function);
 
     // Redirect in loop predecessors to |exit| block.
@@ -380,7 +397,7 @@ void LoopUtils::CreateLoopDedicatedExits() {
     // We also reset the insert point so all instructions are inserted before
     // the branch.
     builder.SetInsertPoint(builder.AddBranch(non_dedicate->id()));
-    non_dedicate->ForEachPhiInst(
+    bool succeeded = non_dedicate->WhileEachPhiInst(
         [&builder, &exit, def_use_mgr, this](Instruction* phi) {
           // New phi operands for this instruction.
           std::vector<uint32_t> new_phi_op;
@@ -400,6 +417,9 @@ void LoopUtils::CreateLoopDedicatedExits() {
 
           // Build the new phi instruction dedicated exit block.
           Instruction* exit_phi = builder.AddPhi(phi->type_id(), exit_phi_op);
+          if (!exit_phi) {
+            return false;
+          }
           // Build the new incoming branch.
           new_phi_op.push_back(exit_phi->result_id());
           new_phi_op.push_back(exit.id());
@@ -412,7 +432,9 @@ void LoopUtils::CreateLoopDedicatedExits() {
             phi->RemoveInOperand(j);
           // Update the def/use manager for this |phi|.
           def_use_mgr->AnalyzeInstUse(phi);
+          return true;
         });
+    if (!succeeded) return false;
     // Update the CFG.
     cfg.RegisterBlock(&exit);
     cfg.RemoveNonExistingEdges(non_dedicate->id());
@@ -431,10 +453,13 @@ void LoopUtils::CreateLoopDedicatedExits() {
         PreservedAnalyses | IRContext::kAnalysisCFG |
         IRContext::Analysis::kAnalysisLoopAnalysis);
   }
+  return true;
 }
 
-void LoopUtils::MakeLoopClosedSSA() {
-  CreateLoopDedicatedExits();
+bool LoopUtils::MakeLoopClosedSSA() {
+  if (!CreateLoopDedicatedExits()) {
+    return false;
+  }
 
   Function* function = loop_->GetHeaderBlock()->GetParent();
   CFG& cfg = *context_->cfg();
@@ -452,8 +477,10 @@ void LoopUtils::MakeLoopClosedSSA() {
 
   LCSSARewriter lcssa_rewriter(context_, dom_tree, exit_bb,
                                loop_->GetMergeBlock());
-  MakeSetClosedSSA(context_, function, loop_->GetBlocks(), exit_bb,
-                   &lcssa_rewriter);
+  if (!MakeSetClosedSSA(context_, function, loop_->GetBlocks(), exit_bb,
+                        &lcssa_rewriter)) {
+    return false;
+  }
 
   // Make sure all defs post-dominated by the merge block have their last use no
   // further than the merge block.
@@ -466,14 +493,17 @@ void LoopUtils::MakeLoopClosedSSA() {
     exit_bb.insert(loop_->GetMergeBlock());
     // LCSSARewriter is reusable here only because it forces the creation of a
     // phi instruction in the merge block.
-    MakeSetClosedSSA(context_, function, merging_bb_id, exit_bb,
-                     &lcssa_rewriter);
+    if (!MakeSetClosedSSA(context_, function, merging_bb_id, exit_bb,
+                          &lcssa_rewriter)) {
+      return false;
+    }
   }
 
   context_->InvalidateAnalysesExceptFor(
       IRContext::Analysis::kAnalysisCFG |
       IRContext::Analysis::kAnalysisDominatorAnalysis |
       IRContext::Analysis::kAnalysisLoopAnalysis);
+  return true;
 }
 
 Loop* LoopUtils::CloneLoop(LoopCloningResult* cloning_result) const {
@@ -569,6 +599,7 @@ Loop* LoopUtils::CloneLoop(
     // For each basic block in the loop, we clone it and register the mapping
     // between old and new ids.
     BasicBlock* new_bb = old_bb->Clone(context_);
+    if (!new_bb) return nullptr;
     new_bb->SetParent(&function_);
     uint32_t new_label_id = context_->TakeNextId();
     if (new_label_id == 0) {

+ 2 - 2
3rdparty/spirv-tools/source/opt/loop_utils.h

@@ -95,14 +95,14 @@ class LoopUtils {
   //
   // This makes some loop transformations (such as loop unswitch) simpler
   // (removes the needs to take care of exiting variables).
-  void MakeLoopClosedSSA();
+  bool MakeLoopClosedSSA();
 
   // Create dedicate exit basic block. This ensure all exit basic blocks has the
   // loop as sole predecessors.
   // By construction, structured control flow already has a dedicated exit
   // block.
   // Preserves: CFG, def/use and instruction to block mapping.
-  void CreateLoopDedicatedExits();
+  bool CreateLoopDedicatedExits();
 
   // Clone |loop_| and remap its instructions. Newly created blocks
   // will be added to the |cloning_result.cloned_bb_| list, correctly ordered to

+ 119 - 33
3rdparty/spirv-tools/source/opt/merge_return_pass.cpp

@@ -134,7 +134,9 @@ bool MergeReturnPass::ProcessStructured(
       state_.pop_back();
     }
 
-    ProcessStructuredBlock(block);
+    if (!ProcessStructuredBlock(block)) {
+      return false;
+    }
 
     // Generate state for next block if warranted
     GenerateState(block);
@@ -169,7 +171,9 @@ bool MergeReturnPass::ProcessStructured(
   // We have not kept the dominator tree up-to-date.
   // Invalidate it at this point to make sure it will be rebuilt.
   context()->RemoveDominatorAnalysis(function);
-  AddNewPhiNodes();
+  if (!AddNewPhiNodes()) {
+    return false;
+  }
   return true;
 }
 
@@ -196,7 +200,9 @@ bool MergeReturnPass::CreateReturnBlock() {
 }
 
 bool MergeReturnPass::CreateReturn(BasicBlock* block) {
-  AddReturnValue();
+  if (!AddReturnValue()) {
+    return false;
+  }
 
   if (return_value_) {
     // Load and return the final return value
@@ -229,12 +235,18 @@ bool MergeReturnPass::CreateReturn(BasicBlock* block) {
   return true;
 }
 
-void MergeReturnPass::ProcessStructuredBlock(BasicBlock* block) {
+bool MergeReturnPass::ProcessStructuredBlock(BasicBlock* block) {
+  if (block->tail() == block->end()) {
+    return true;
+  }
+
   spv::Op tail_opcode = block->tail()->opcode();
   if (tail_opcode == spv::Op::OpReturn ||
       tail_opcode == spv::Op::OpReturnValue) {
     if (!return_flag_) {
-      AddReturnFlag();
+      if (!AddReturnFlag()) {
+        return false;
+      }
     }
   }
 
@@ -243,15 +255,20 @@ void MergeReturnPass::ProcessStructuredBlock(BasicBlock* block) {
       tail_opcode == spv::Op::OpUnreachable) {
     assert(CurrentState().InBreakable() &&
            "Should be in the placeholder construct.");
-    BranchToBlock(block, CurrentState().BreakMergeId());
+    if (!BranchToBlock(block, CurrentState().BreakMergeId())) {
+      return false;
+    }
     return_blocks_.insert(block->id());
   }
+  return true;
 }
 
-void MergeReturnPass::BranchToBlock(BasicBlock* block, uint32_t target) {
+bool MergeReturnPass::BranchToBlock(BasicBlock* block, uint32_t target) {
   if (block->tail()->opcode() == spv::Op::OpReturn ||
       block->tail()->opcode() == spv::Op::OpReturnValue) {
-    RecordReturned(block);
+    if (!RecordReturned(block)) {
+      return false;
+    }
     RecordReturnValue(block);
   }
 
@@ -259,7 +276,9 @@ void MergeReturnPass::BranchToBlock(BasicBlock* block, uint32_t target) {
   if (target_block->GetLoopMergeInst()) {
     cfg()->SplitLoopHeader(target_block);
   }
-  UpdatePhiNodes(block, target_block);
+  if (!UpdatePhiNodes(block, target_block)) {
+    return false;
+  }
 
   Instruction* return_inst = block->terminator();
   return_inst->SetOpcode(spv::Op::OpBranch);
@@ -267,19 +286,26 @@ void MergeReturnPass::BranchToBlock(BasicBlock* block, uint32_t target) {
   context()->get_def_use_mgr()->AnalyzeInstDefUse(return_inst);
   new_edges_[target_block].insert(block->id());
   cfg()->AddEdge(block->id(), target);
+  return true;
 }
 
-void MergeReturnPass::UpdatePhiNodes(BasicBlock* new_source,
+bool MergeReturnPass::UpdatePhiNodes(BasicBlock* new_source,
                                      BasicBlock* target) {
-  target->ForEachPhiInst([this, new_source](Instruction* inst) {
+  bool succeeded = true;
+  target->ForEachPhiInst([this, new_source, &succeeded](Instruction* inst) {
     uint32_t undefId = Type2Undef(inst->type_id());
+    if (undefId == 0) {
+      succeeded = false;
+      return;
+    }
     inst->AddOperand({SPV_OPERAND_TYPE_ID, {undefId}});
     inst->AddOperand({SPV_OPERAND_TYPE_ID, {new_source->id()}});
     context()->UpdateDefUse(inst);
   });
+  return succeeded;
 }
 
-void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
+bool MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
                                             Instruction& inst) {
   DominatorAnalysis* dom_tree =
       context()->GetDominatorAnalysis(merge_block->GetParent());
@@ -313,7 +339,7 @@ void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
         });
 
     if (users_to_update.empty()) {
-      return;
+      return true;
     }
 
     // There is at least one values that needs to be replaced.
@@ -357,6 +383,9 @@ void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
     if (regenerateInstruction) {
       std::unique_ptr<Instruction> regen_inst(inst.Clone(context()));
       uint32_t new_id = TakeNextId();
+      if (new_id == 0) {
+        return false;
+      }
       regen_inst->SetResultId(new_id);
       Instruction* insert_pos = &*merge_block->begin();
       while (insert_pos->opcode() == spv::Op::OpPhi) {
@@ -366,18 +395,31 @@ void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
       get_def_use_mgr()->AnalyzeInstDefUse(new_phi);
       context()->set_instr_block(new_phi, merge_block);
 
-      new_phi->ForEachInId([dom_tree, merge_block, this](uint32_t* use_id) {
+      bool succeeded = true;
+      new_phi->ForEachInId([dom_tree, merge_block, this,
+                            &succeeded](uint32_t* use_id) {
+        if (!succeeded) {
+          return;
+        }
         Instruction* use = get_def_use_mgr()->GetDef(*use_id);
         BasicBlock* use_bb = context()->get_instr_block(use);
         if (use_bb != nullptr && !dom_tree->Dominates(use_bb, merge_block)) {
-          CreatePhiNodesForInst(merge_block, *use);
+          if (!CreatePhiNodesForInst(merge_block, *use)) {
+            succeeded = false;
+          }
         }
       });
+      if (!succeeded) {
+        return false;
+      }
     } else {
       InstructionBuilder builder(
           context(), &*merge_block->begin(),
           IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
       new_phi = builder.AddPhi(inst.type_id(), phi_operands);
+      if (new_phi == nullptr) {
+        return false;
+      }
     }
     uint32_t result_of_phi = new_phi->result_id();
 
@@ -391,6 +433,7 @@ void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
       context()->AnalyzeUses(user);
     }
   }
+  return true;
 }
 
 bool MergeReturnPass::PredicateBlocks(
@@ -483,6 +526,9 @@ bool MergeReturnPass::BreakFromConstruct(
   cfg()->RemoveSuccessorEdges(block);
 
   auto old_body_id = TakeNextId();
+  if (old_body_id == 0) {
+    return false;
+  }
   BasicBlock* old_body = block->SplitBasicBlock(context(), old_body_id, iter);
   predicated->insert(old_body);
 
@@ -519,8 +565,11 @@ bool MergeReturnPass::BreakFromConstruct(
   analysis::Bool bool_type;
   uint32_t bool_id = context()->get_type_mgr()->GetId(&bool_type);
   assert(bool_id != 0);
-  uint32_t load_id =
-      builder.AddLoad(bool_id, return_flag_->result_id())->result_id();
+  Instruction* load_inst = builder.AddLoad(bool_id, return_flag_->result_id());
+  if (load_inst == nullptr) {
+    return false;
+  }
+  uint32_t load_id = load_inst->result_id();
 
   // 2. Branch to |merge_block| (true) or |old_body| (false)
   builder.AddConditionalBranch(load_id, merge_block->id(), old_body->id(),
@@ -533,7 +582,9 @@ bool MergeReturnPass::BreakFromConstruct(
   }
 
   // 3. Update OpPhi instructions in |merge_block|.
-  UpdatePhiNodes(block, merge_block);
+  if (!UpdatePhiNodes(block, merge_block)) {
+    return false;
+  }
 
   // 4. Update the CFG.  We do this after updating the OpPhi instructions
   // because |UpdatePhiNodes| assumes the edge from |block| has not been added
@@ -546,10 +597,10 @@ bool MergeReturnPass::BreakFromConstruct(
   return true;
 }
 
-void MergeReturnPass::RecordReturned(BasicBlock* block) {
+bool MergeReturnPass::RecordReturned(BasicBlock* block) {
   if (block->tail()->opcode() != spv::Op::OpReturn &&
       block->tail()->opcode() != spv::Op::OpReturnValue)
-    return;
+    return true;
 
   assert(return_flag_ && "Did not generate the return flag variable.");
 
@@ -562,6 +613,9 @@ void MergeReturnPass::RecordReturned(BasicBlock* block) {
     const analysis::Constant* true_const =
         const_mgr->GetConstant(bool_type, {true});
     constant_true_ = const_mgr->GetDefiningInstruction(true_const);
+    if (!constant_true_) {
+      return false;
+    }
     context()->UpdateDefUse(constant_true_);
   }
 
@@ -575,6 +629,7 @@ void MergeReturnPass::RecordReturned(BasicBlock* block) {
       &*block->tail().InsertBefore(std::move(return_store));
   context()->set_instr_block(store_inst, block);
   context()->AnalyzeDefUse(store_inst);
+  return true;
 }
 
 void MergeReturnPass::RecordReturnValue(BasicBlock* block) {
@@ -598,18 +653,21 @@ void MergeReturnPass::RecordReturnValue(BasicBlock* block) {
   context()->AnalyzeDefUse(store_inst);
 }
 
-void MergeReturnPass::AddReturnValue() {
-  if (return_value_) return;
+bool MergeReturnPass::AddReturnValue() {
+  if (return_value_) return true;
 
   uint32_t return_type_id = function_->type_id();
   if (get_def_use_mgr()->GetDef(return_type_id)->opcode() ==
       spv::Op::OpTypeVoid)
-    return;
+    return true;
 
   uint32_t return_ptr_type = context()->get_type_mgr()->FindPointerToType(
       return_type_id, spv::StorageClass::Function);
 
   uint32_t var_id = TakeNextId();
+  if (var_id == 0) {
+    return false;
+  }
   std::unique_ptr<Instruction> returnValue(
       new Instruction(context(), spv::Op::OpVariable, return_ptr_type, var_id,
                       std::initializer_list<Operand>{
@@ -625,27 +683,44 @@ void MergeReturnPass::AddReturnValue() {
 
   context()->get_decoration_mgr()->CloneDecorations(
       function_->result_id(), var_id, {spv::Decoration::RelaxedPrecision});
+  return true;
 }
 
-void MergeReturnPass::AddReturnFlag() {
-  if (return_flag_) return;
+bool MergeReturnPass::AddReturnFlag() {
+  if (return_flag_) return true;
 
   analysis::TypeManager* type_mgr = context()->get_type_mgr();
   analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
 
   analysis::Bool temp;
   uint32_t bool_id = type_mgr->GetTypeInstruction(&temp);
+  if (bool_id == 0) {
+    return false;
+  }
   analysis::Bool* bool_type = type_mgr->GetType(bool_id)->AsBool();
 
   const analysis::Constant* false_const =
       const_mgr->GetConstant(bool_type, {false});
-  uint32_t const_false_id =
-      const_mgr->GetDefiningInstruction(false_const)->result_id();
+  Instruction* false_inst = const_mgr->GetDefiningInstruction(false_const);
+  if (false_inst == nullptr) {
+    return false;
+  }
+  uint32_t const_false_id = false_inst->result_id();
 
   uint32_t bool_ptr_id =
       type_mgr->FindPointerToType(bool_id, spv::StorageClass::Function);
 
+  if (bool_ptr_id == 0) {
+    return false;
+    ;
+  }
+
   uint32_t var_id = TakeNextId();
+
+  if (var_id == 0) {
+    return false;
+  }
+
   std::unique_ptr<Instruction> returnFlag(new Instruction(
       context(), spv::Op::OpVariable, bool_ptr_id, var_id,
       std::initializer_list<Operand>{{SPV_OPERAND_TYPE_STORAGE_CLASS,
@@ -659,6 +734,7 @@ void MergeReturnPass::AddReturnFlag() {
   return_flag_ = &*entry_block->begin();
   context()->AnalyzeDefUse(return_flag_);
   context()->set_instr_block(return_flag_, entry_block);
+  return true;
 }
 
 std::vector<BasicBlock*> MergeReturnPass::CollectReturnBlocks(
@@ -737,16 +813,19 @@ bool MergeReturnPass::MergeReturnBlocks(
   return true;
 }
 
-void MergeReturnPass::AddNewPhiNodes() {
+bool MergeReturnPass::AddNewPhiNodes() {
   std::list<BasicBlock*> order;
   cfg()->ComputeStructuredOrder(function_, &*function_->begin(), &order);
 
   for (BasicBlock* bb : order) {
-    AddNewPhiNodes(bb);
+    if (!AddNewPhiNodes(bb)) {
+      return false;
+    }
   }
+  return true;
 }
 
-void MergeReturnPass::AddNewPhiNodes(BasicBlock* bb) {
+bool MergeReturnPass::AddNewPhiNodes(BasicBlock* bb) {
   // New phi nodes are needed for any id whose definition used to dominate |bb|,
   // but no longer dominates |bb|.  These are found by walking the dominator
   // tree starting at the original immediate dominator of |bb| and ending at its
@@ -764,16 +843,19 @@ void MergeReturnPass::AddNewPhiNodes(BasicBlock* bb) {
 
   BasicBlock* dominator = dom_tree->ImmediateDominator(bb);
   if (dominator == nullptr) {
-    return;
+    return true;
   }
 
   BasicBlock* current_bb = context()->get_instr_block(original_dominator_[bb]);
   while (current_bb != nullptr && current_bb != dominator) {
     for (Instruction& inst : *current_bb) {
-      CreatePhiNodesForInst(bb, inst);
+      if (!CreatePhiNodesForInst(bb, inst)) {
+        return false;
+      }
     }
     current_bb = dom_tree->ImmediateDominator(current_bb);
   }
+  return true;
 }
 
 void MergeReturnPass::RecordImmediateDominators(Function* function) {
@@ -857,8 +939,12 @@ bool MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) {
     ++split_pos;
   }
 
+  uint32_t new_block_id = TakeNextId();
+  if (new_block_id == 0) {
+    return false;
+  }
   BasicBlock* old_block =
-      start_block->SplitBasicBlock(context(), TakeNextId(), split_pos);
+      start_block->SplitBasicBlock(context(), new_block_id, split_pos);
 
   // Find DebugFunctionDefinition inst in the old block, and if we can find it,
   // move it to the entry block. Since DebugFunctionDefinition is not necessary

+ 30 - 33
3rdparty/spirv-tools/source/opt/merge_return_pass.h

@@ -173,21 +173,22 @@ class MergeReturnPass : public MemPass {
   //
   // Note this will break the semantics.  To fix this, PredicateBlock will have
   // to be called on the merge block the branch targets.
-  void ProcessStructuredBlock(BasicBlock* block);
+  bool ProcessStructuredBlock(BasicBlock* block);
 
   // Creates a variable used to store whether or not the control flow has
   // traversed a block that used to have a return.  A pointer to the instruction
-  // declaring the variable is stored in |return_flag_|.
-  void AddReturnFlag();
+  // declaring the variable is stored in |return_flag_|. Returns true if it
+  // succeeds.
+  bool AddReturnFlag();
 
   // Creates the variable used to store the return value when passing through
-  // a block that use to contain an OpReturnValue.
-  void AddReturnValue();
+  // a block that use to contain an OpReturnValue. Returns true if it succeeds.
+  bool AddReturnValue();
 
-  // Adds a store that stores true to |return_flag_| immediately before the
-  // terminator of |block|. It is assumed that |AddReturnFlag| has already been
-  // called.
-  void RecordReturned(BasicBlock* block);
+  // Records that |block| used to be a return.  This is done by adding an
+  // instruction to store true to the |return_flag_|. Returns true if it
+  // succeeds.
+  bool RecordReturned(BasicBlock* block);
 
   // Adds an instruction that stores the value being returned in the
   // OpReturnValue in |block|.  The value is stored to |return_value_|, and the
@@ -198,10 +199,10 @@ class MergeReturnPass : public MemPass {
   // have already been called to create the variable to store to.
   void RecordReturnValue(BasicBlock* block);
 
-  // Adds an unconditional branch in |block| that branches to |target|.  It also
-  // adds stores to |return_flag_| and |return_value_| as needed.
-  // |AddReturnFlag| and |AddReturnValue| must have already been called.
-  void BranchToBlock(BasicBlock* block, uint32_t target);
+  // Replaces the terminator of |block| with a branch to |target|.  If the
+  // terminator was a return, it will first call RecordReturned and
+  // RecordReturnValue. Returns true if it succeeds.
+  bool BranchToBlock(BasicBlock* block, uint32_t target);
 
   // For every basic block that is reachable from |return_block|, extra code is
   // added to jump around any code that should not be executed because the
@@ -239,32 +240,28 @@ class MergeReturnPass : public MemPass {
   // return block at the end of the pass.
   bool CreateReturnBlock();
 
-  // Creates a Phi node in |merge_block| for the result of |inst|.
-  // Any uses of the result of |inst| that are no longer
-  // dominated by |inst|, are replaced with the result of the new |OpPhi|
-  // instruction.
-  void CreatePhiNodesForInst(BasicBlock* merge_block, Instruction& inst);
+  // For each use of |inst| that is no longer dominated by |inst|, a phi node
+  // is created in |merge_block|.  The original use is replaced by the result
+  // of the phi node. Returns true if it succeeds.
+  bool CreatePhiNodesForInst(BasicBlock* merge_block, Instruction& inst);
 
-  // Add new phi nodes for any id that no longer dominate all of it uses.  A phi
-  // node is added to a block |bb| for an id if the id is defined between the
-  // original immediate dominator of |bb| and its new immediate dominator.  It
-  // is assumed that at this point there are no unreachable blocks in the
-  // control flow graph.
-  void AddNewPhiNodes();
+  // Adds new phi nodes as needed to the function.  This is necessary because
+  // adding the predication code can change the dominator tree.  Returns false
+  // if there is a failure.
+  bool AddNewPhiNodes();
 
-  // Creates any new phi nodes that are needed in |bb|.  |AddNewPhiNodes| must
-  // have already been called on the original dominators of |bb|.
-  void AddNewPhiNodes(BasicBlock* bb);
+  // Adds new phi nodes to |bb| as needed.  This is necessary because adding
+  // the predication code can change the dominator tree.  Returns false if
+  // there is a failure.
+  bool AddNewPhiNodes(BasicBlock* bb);
 
   // Records the terminator of immediate dominator for every basic block in
   // |function|.
   void RecordImmediateDominators(Function* function);
 
-  // Modifies existing OpPhi instruction in |target| block to account for the
-  // new edge from |new_source|.  The value for that edge will be an Undef.
-  //
-  // The CFG must not include the edge from |new_source| to |target| yet.
-  void UpdatePhiNodes(BasicBlock* new_source, BasicBlock* target);
+  // For each OpPhi instruction in |target|, this function adds an operand for
+  // |new_source|.  The value will be OpUndef. Returns true if it succeeds.
+  bool UpdatePhiNodes(BasicBlock* new_source, BasicBlock* target);
 
   StructuredControlState& CurrentState() { return state_.back(); }
 
@@ -334,4 +331,4 @@ class MergeReturnPass : public MemPass {
 }  // namespace opt
 }  // namespace spvtools
 
-#endif  // SOURCE_OPT_MERGE_RETURN_PASS_H_
+#endif  // SOURCE_OPT_MERGE_RETURN_PASS_H_

+ 11 - 0
3rdparty/spirv-tools/source/opt/module.cpp

@@ -93,6 +93,7 @@ void Module::ForEachInst(const std::function<void(Instruction*)>& f,
   if (sampled_image_address_mode_)
     sampled_image_address_mode_->ForEachInst(f, run_on_debug_line_insts);
   DELEGATE(entry_points_);
+  DELEGATE(graph_entry_points_);
   DELEGATE(execution_modes_);
   DELEGATE(debugs1_);
   DELEGATE(debugs2_);
@@ -104,6 +105,10 @@ void Module::ForEachInst(const std::function<void(Instruction*)>& f,
     i->ForEachInst(f, run_on_debug_line_insts,
                    /* run_on_non_semantic_insts = */ true);
   }
+  for (auto& g : graphs_) {
+    g->ForEachInst(f, run_on_debug_line_insts,
+                   /* run_on_non_semantic_insts = */ true);
+  }
 #undef DELEGATE
 }
 
@@ -132,6 +137,12 @@ void Module::ForEachInst(const std::function<void(const Instruction*)>& f,
         f, run_on_debug_line_insts,
         /* run_on_non_semantic_insts = */ true);
   }
+  for (auto& i : graph_entry_points_) DELEGATE(i);
+  for (auto& i : graphs_) {
+    static_cast<const Graph*>(i.get())->ForEachInst(
+        f, run_on_debug_line_insts,
+        /* run_on_non_semantic_insts = */ true);
+  }
   if (run_on_debug_line_insts) {
     for (auto& i : trailing_dbg_line_info_) DELEGATE(i);
   }

+ 39 - 0
3rdparty/spirv-tools/source/opt/module.h

@@ -23,6 +23,7 @@
 #include <vector>
 
 #include "source/opt/function.h"
+#include "source/opt/graph.h"
 #include "source/opt/instruction.h"
 #include "source/opt/iterator.h"
 
@@ -48,6 +49,8 @@ class Module {
   using const_iterator = UptrVectorIterator<Function, true>;
   using inst_iterator = InstructionList::iterator;
   using const_inst_iterator = InstructionList::const_iterator;
+  using graph_iterator = UptrVectorIterator<Graph>;
+  using const_graph_iterator = UptrVectorIterator<Graph, true>;
 
   // Creates an empty module with zero'd header.
   Module() : header_({}), contains_debug_info_(false) {}
@@ -90,6 +93,9 @@ class Module {
   // Appends an entry point instruction to this module.
   inline void AddEntryPoint(std::unique_ptr<Instruction> e);
 
+  // Appends a graph entry point instruction to this module.
+  inline void AddGraphEntryPoint(std::unique_ptr<Instruction> e);
+
   // Appends an execution mode instruction to this module.
   inline void AddExecutionMode(std::unique_ptr<Instruction> e);
 
@@ -126,6 +132,9 @@ class Module {
   // Appends a function to this module.
   inline void AddFunction(std::unique_ptr<Function> f);
 
+  // Appends a graph to this module.
+  inline void AddGraph(std::unique_ptr<Graph> g);
+
   // Sets |contains_debug_info_| as true.
   inline void SetContainsDebugInfo();
   inline bool ContainsDebugInfo() { return contains_debug_info_; }
@@ -220,6 +229,10 @@ class Module {
   inline IteratorRange<inst_iterator> entry_points();
   inline IteratorRange<const_inst_iterator> entry_points() const;
 
+  // Iterators for graph entry point instructions contained in this module
+  inline IteratorRange<inst_iterator> graph_entry_points();
+  inline IteratorRange<const_inst_iterator> graph_entry_points() const;
+
   // Iterators for execution_modes instructions contained in this module.
   inline inst_iterator execution_mode_begin();
   inline inst_iterator execution_mode_end();
@@ -252,6 +265,9 @@ class Module {
   inline const_iterator cbegin() const;
   inline const_iterator cend() const;
 
+  // Iterators for graphs contained in this module.
+  inline const std::vector<std::unique_ptr<Graph>>& graphs() const;
+
   // Invokes function |f| on all instructions in this module, and optionally on
   // the debug line instructions that precede them.
   void ForEachInst(const std::function<void(Instruction*)>& f,
@@ -306,6 +322,7 @@ class Module {
   // A module can only have one optional sampled image addressing mode
   std::unique_ptr<Instruction> sampled_image_address_mode_;
   InstructionList entry_points_;
+  InstructionList graph_entry_points_;
   InstructionList execution_modes_;
   InstructionList debugs1_;
   InstructionList debugs2_;
@@ -315,6 +332,7 @@ class Module {
   // Type declarations, constants, and global variable declarations.
   InstructionList types_values_;
   std::vector<std::unique_ptr<Function>> functions_;
+  std::vector<std::unique_ptr<Graph>> graphs_;
 
   // If the module ends with Op*Line instruction, they will not be attached to
   // any instruction.  We record them here, so they will not be lost.
@@ -351,6 +369,10 @@ inline void Module::AddEntryPoint(std::unique_ptr<Instruction> e) {
   entry_points_.push_back(std::move(e));
 }
 
+inline void Module::AddGraphEntryPoint(std::unique_ptr<Instruction> e) {
+  graph_entry_points_.push_back(std::move(e));
+}
+
 inline void Module::AddExecutionMode(std::unique_ptr<Instruction> e) {
   execution_modes_.push_back(std::move(e));
 }
@@ -392,6 +414,10 @@ inline void Module::AddFunction(std::unique_ptr<Function> f) {
   functions_.emplace_back(std::move(f));
 }
 
+inline void Module::AddGraph(std::unique_ptr<Graph> g) {
+  graphs_.emplace_back(std::move(g));
+}
+
 inline void Module::SetContainsDebugInfo() { contains_debug_info_ = true; }
 
 inline Module::inst_iterator Module::capability_begin() {
@@ -482,6 +508,15 @@ inline IteratorRange<Module::const_inst_iterator> Module::entry_points() const {
   return make_range(entry_points_.begin(), entry_points_.end());
 }
 
+inline IteratorRange<Module::inst_iterator> Module::graph_entry_points() {
+  return make_range(graph_entry_points_.begin(), graph_entry_points_.end());
+}
+
+inline IteratorRange<Module::const_inst_iterator> Module::graph_entry_points()
+    const {
+  return make_range(graph_entry_points_.begin(), graph_entry_points_.end());
+}
+
 inline Module::inst_iterator Module::execution_mode_begin() {
   return execution_modes_.begin();
 }
@@ -544,6 +579,10 @@ inline IteratorRange<Module::const_inst_iterator> Module::types_values() const {
   return make_range(types_values_.begin(), types_values_.end());
 }
 
+inline const std::vector<std::unique_ptr<Graph>>& Module::graphs() const {
+  return graphs_;
+}
+
 inline Module::const_iterator Module::cbegin() const {
   return const_iterator(&functions_, functions_.cbegin());
 }

+ 2 - 0
3rdparty/spirv-tools/source/opt/pass.cpp

@@ -114,6 +114,7 @@ uint32_t Pass::GenerateCopy(Instruction* object_to_copy, uint32_t new_type_id,
       assert(length_const->AsIntConstant());
       uint32_t array_length = length_const->AsIntConstant()->GetU32();
       for (uint32_t i = 0; i < array_length; i++) {
+        // TODO(1841): Handle id overflow.
         Instruction* extract = ir_builder.AddCompositeExtract(
             original_element_type_id, object_to_copy->result_id(), {i});
         uint32_t new_id =
@@ -132,6 +133,7 @@ uint32_t Pass::GenerateCopy(Instruction* object_to_copy, uint32_t new_type_id,
       for (uint32_t i = 0; i < original_type->NumInOperands(); i++) {
         uint32_t orig_member_type_id = original_type->GetSingleWordInOperand(i);
         uint32_t new_member_type_id = new_type->GetSingleWordInOperand(i);
+        // TODO(1841): Handle id overflow.
         Instruction* extract = ir_builder.AddCompositeExtract(
             orig_member_type_id, object_to_copy->result_id(), {i});
         uint32_t new_id =

+ 40 - 15
3rdparty/spirv-tools/source/opt/propagator.cpp

@@ -84,16 +84,19 @@ bool SSAPropagator::SetStatus(Instruction* inst, PropStatus status) {
   return status_changed;
 }
 
-bool SSAPropagator::Simulate(Instruction* instr) {
+Pass::Status SSAPropagator::Simulate(Instruction* instr) {
   bool changed = false;
 
   // Don't bother visiting instructions that should not be simulated again.
   if (!ShouldSimulateAgain(instr)) {
-    return changed;
+    return Pass::Status::SuccessWithoutChange;
   }
 
   BasicBlock* dest_bb = nullptr;
   PropStatus status = visit_fn_(instr, &dest_bb);
+  if (status == SSAPropagator::kFailed) {
+    return Pass::Status::Failure;
+  }
   bool status_changed = SetStatus(instr, status);
 
   if (status == kVarying) {
@@ -112,7 +115,7 @@ bool SSAPropagator::Simulate(Instruction* instr) {
         AddControlEdge(e);
       }
     }
-    return false;
+    return Pass::Status::SuccessWithoutChange;
   } else if (status == kInteresting) {
     // Add the SSA edges coming out of this instruction if the propagation
     // status has changed.
@@ -169,12 +172,13 @@ bool SSAPropagator::Simulate(Instruction* instr) {
     DontSimulateAgain(instr);
   }
 
-  return changed;
+  return changed ? Pass::Status::SuccessWithChange
+                 : Pass::Status::SuccessWithoutChange;
 }
 
-bool SSAPropagator::Simulate(BasicBlock* block) {
+Pass::Status SSAPropagator::Simulate(BasicBlock* block) {
   if (block == ctx_->cfg()->pseudo_exit_block()) {
-    return false;
+    return Pass::Status::SuccessWithoutChange;
   }
 
   // Always simulate Phi instructions, even if we have simulated this block
@@ -182,17 +186,29 @@ bool SSAPropagator::Simulate(BasicBlock* block) {
   // incoming edges. When those edges are marked executable, the corresponding
   // operand can be simulated.
   bool changed = false;
-  block->ForEachPhiInst(
-      [&changed, this](Instruction* instr) { changed |= Simulate(instr); });
+  bool succeeded =
+      block->WhileEachPhiInst([&changed, this](Instruction* instr) {
+        auto Status = Simulate(instr);
+        if (Status == Pass::Status::Failure) return false;
+        changed |= Status == Pass::Status::SuccessWithChange;
+        return true;
+      });
+  if (!succeeded) {
+    return Pass::Status::Failure;
+  }
 
   // If this is the first time this block is being simulated, simulate every
   // statement in it.
   if (!BlockHasBeenSimulated(block)) {
-    block->ForEachInst([this, &changed](Instruction* instr) {
-      if (instr->opcode() != spv::Op::OpPhi) {
-        changed |= Simulate(instr);
-      }
+    succeeded = block->WhileEachInst([&changed, this](Instruction* instr) {
+      auto Status = Simulate(instr);
+      if (Status == Pass::Status::Failure) return false;
+      changed |= Status == Pass::Status::SuccessWithChange;
+      return true;
     });
+    if (!succeeded) {
+      return Pass::Status::Failure;
+    }
 
     MarkBlockSimulated(block);
 
@@ -203,7 +219,8 @@ bool SSAPropagator::Simulate(BasicBlock* block) {
     }
   }
 
-  return changed;
+  return changed ? Pass::Status::SuccessWithChange
+                 : Pass::Status::SuccessWithoutChange;
 }
 
 void SSAPropagator::Initialize(Function* fn) {
@@ -245,7 +262,11 @@ bool SSAPropagator::Run(Function* fn) {
     // follow after all the blocks have been simulated.
     if (!blocks_.empty()) {
       auto block = blocks_.front();
-      changed |= Simulate(block);
+      Pass::Status status = Simulate(block);
+      if (status == Pass::Status::Failure) {
+        return false;
+      }
+      changed |= status == Pass::Status::SuccessWithChange;
       blocks_.pop();
       continue;
     }
@@ -253,7 +274,11 @@ bool SSAPropagator::Run(Function* fn) {
     // Simulate edges from the SSA queue.
     if (!ssa_edge_uses_.empty()) {
       Instruction* instr = ssa_edge_uses_.front();
-      changed |= Simulate(instr);
+      Pass::Status status = Simulate(instr);
+      if (status == Pass::Status::Failure) {
+        return changed;
+      }
+      changed |= status == Pass::Status::SuccessWithChange;
       ssa_edge_uses_.pop();
     }
   }

+ 7 - 4
3rdparty/spirv-tools/source/opt/propagator.h

@@ -25,6 +25,7 @@
 
 #include "source/opt/ir_context.h"
 #include "source/opt/module.h"
+#include "source/opt/pass.h"
 
 namespace spvtools {
 namespace opt {
@@ -182,7 +183,7 @@ class SSAPropagator {
  public:
   // Lattice values used for propagation. See class documentation for
   // a description.
-  enum PropStatus { kNotInteresting, kInteresting, kVarying };
+  enum PropStatus { kNotInteresting, kInteresting, kVarying, kFailed };
 
   using VisitFunction = std::function<PropStatus(Instruction*, BasicBlock**)>;
 
@@ -190,7 +191,9 @@ class SSAPropagator {
       : ctx_(context), visit_fn_(visit_fn) {}
 
   // Runs the propagator on function |fn|. Returns true if changes were made to
-  // the function. Otherwise, it returns false.
+  // the function. Otherwise, it returns false. The user should check
+  // IRContext::id_overflow() to see if there was an error caused by reaching
+  // the max id.
   bool Run(Function* fn);
 
   // Returns true if the |i|th argument for |phi| comes through a CFG edge that
@@ -218,13 +221,13 @@ class SSAPropagator {
 
   // Simulate the execution |block| by calling |visit_fn_| on every instruction
   // in it.
-  bool Simulate(BasicBlock* block);
+  Pass::Status Simulate(BasicBlock* block);
 
   // Simulate the execution of |instr| by replacing all the known values in
   // every operand and determining whether the result is interesting for
   // propagation. This invokes the callback function |visit_fn_| to determine
   // the value computed by |instr|.
-  bool Simulate(Instruction* instr);
+  Pass::Status Simulate(Instruction* instr);
 
   // Returns true if |instr| should be simulated again.
   bool ShouldSimulateAgain(Instruction* instr) const {

+ 1 - 0
3rdparty/spirv-tools/source/opt/reduce_load_size.cpp

@@ -108,6 +108,7 @@ bool ReduceLoadSize::ReplaceExtract(Instruction* inst) {
   Instruction* new_access_chain = ir_builder.AddAccessChain(
       pointer_to_result_type_id,
       composite_inst->GetSingleWordInOperand(kLoadPointerInIdx), ids);
+  // TODO(1841): Handle id overflow.
   Instruction* new_load =
       ir_builder.AddLoad(inst->type_id(), new_access_chain->result_id());
 

+ 54 - 28
3rdparty/spirv-tools/source/opt/replace_desc_array_access_using_var_index.cpp

@@ -41,14 +41,15 @@ Pass::Status ReplaceDescArrayAccessUsingVarIndex::Process() {
   Status status = Status::SuccessWithoutChange;
   for (Instruction& var : context()->types_values()) {
     if (descsroautil::IsDescriptorArray(context(), &var)) {
-      if (ReplaceVariableAccessesWithConstantElements(&var))
-        status = Status::SuccessWithChange;
+      Status s = ReplaceVariableAccessesWithConstantElements(&var);
+      if (s == Status::Failure) return Status::Failure;
+      if (s == Status::SuccessWithChange) status = Status::SuccessWithChange;
     }
   }
   return status;
 }
 
-bool ReplaceDescArrayAccessUsingVarIndex::
+Pass::Status ReplaceDescArrayAccessUsingVarIndex::
     ReplaceVariableAccessesWithConstantElements(Instruction* var) const {
   std::vector<Instruction*> work_list;
   get_def_use_mgr()->ForEachUser(var, [&work_list](Instruction* use) {
@@ -66,16 +67,16 @@ bool ReplaceDescArrayAccessUsingVarIndex::
   for (Instruction* access_chain : work_list) {
     if (descsroautil::GetAccessChainIndexAsConst(context(), access_chain) ==
         nullptr) {
-      ReplaceAccessChain(var, access_chain);
+      if (!ReplaceAccessChain(var, access_chain)) return Status::Failure;
       updated = true;
     }
   }
   // Note that we do not consider OpLoad and OpCompositeExtract because
   // OpCompositeExtract always has constant literals for indices.
-  return updated;
+  return updated ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
-void ReplaceDescArrayAccessUsingVarIndex::ReplaceAccessChain(
+bool ReplaceDescArrayAccessUsingVarIndex::ReplaceAccessChain(
     Instruction* var, Instruction* access_chain) const {
   uint32_t number_of_elements =
       descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var);
@@ -83,21 +84,23 @@ void ReplaceDescArrayAccessUsingVarIndex::ReplaceAccessChain(
   if (number_of_elements == 1) {
     UseConstIndexForAccessChain(access_chain, 0);
     get_def_use_mgr()->AnalyzeInstUse(access_chain);
-    return;
+    return true;
   }
-  ReplaceUsersOfAccessChain(access_chain, number_of_elements);
+  return ReplaceUsersOfAccessChain(access_chain, number_of_elements);
 }
 
-void ReplaceDescArrayAccessUsingVarIndex::ReplaceUsersOfAccessChain(
+bool ReplaceDescArrayAccessUsingVarIndex::ReplaceUsersOfAccessChain(
     Instruction* access_chain, uint32_t number_of_elements) const {
   std::vector<Instruction*> final_users;
   CollectRecursiveUsersWithConcreteType(access_chain, &final_users);
   for (auto* inst : final_users) {
     std::deque<Instruction*> insts_to_be_cloned =
         CollectRequiredImageAndAccessInsts(inst);
-    ReplaceNonUniformAccessWithSwitchCase(
-        inst, access_chain, number_of_elements, insts_to_be_cloned);
+    if (!ReplaceNonUniformAccessWithSwitchCase(
+            inst, access_chain, number_of_elements, insts_to_be_cloned))
+      return false;
   }
+  return true;
 }
 
 void ReplaceDescArrayAccessUsingVarIndex::CollectRecursiveUsersWithConcreteType(
@@ -208,17 +211,23 @@ BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateCaseBlock(
     const std::deque<Instruction*>& insts_to_be_cloned,
     uint32_t branch_target_id,
     std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const {
-  auto* case_block = CreateNewBlock();
-  AddConstElementAccessToCaseBlock(case_block, access_chain, element_index,
-                                   old_ids_to_new_ids);
-  CloneInstsToBlock(case_block, access_chain, insts_to_be_cloned,
-                    old_ids_to_new_ids);
-  AddBranchToBlock(case_block, branch_target_id);
-  UseNewIdsInBlock(case_block, *old_ids_to_new_ids);
-  return case_block;
+  std::unique_ptr<BasicBlock> case_block(CreateNewBlock());
+  if (!case_block) return nullptr;
+
+  if (!AddConstElementAccessToCaseBlock(case_block.get(), access_chain,
+                                        element_index, old_ids_to_new_ids)) {
+    return nullptr;
+  }
+  if (!CloneInstsToBlock(case_block.get(), access_chain, insts_to_be_cloned,
+                         old_ids_to_new_ids)) {
+    return nullptr;
+  }
+  AddBranchToBlock(case_block.get(), branch_target_id);
+  UseNewIdsInBlock(case_block.get(), *old_ids_to_new_ids);
+  return case_block.release();
 }
 
-void ReplaceDescArrayAccessUsingVarIndex::CloneInstsToBlock(
+bool ReplaceDescArrayAccessUsingVarIndex::CloneInstsToBlock(
     BasicBlock* block, Instruction* inst_to_skip_cloning,
     const std::deque<Instruction*>& insts_to_be_cloned,
     std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const {
@@ -227,6 +236,7 @@ void ReplaceDescArrayAccessUsingVarIndex::CloneInstsToBlock(
     std::unique_ptr<Instruction> clone(inst_to_be_cloned->Clone(context()));
     if (inst_to_be_cloned->HasResultId()) {
       uint32_t new_id = context()->TakeNextId();
+      if (new_id == 0) return false;
       clone->SetResultId(new_id);
       (*old_ids_to_new_ids)[inst_to_be_cloned->result_id()] = new_id;
     }
@@ -234,6 +244,7 @@ void ReplaceDescArrayAccessUsingVarIndex::CloneInstsToBlock(
     context()->set_instr_block(clone.get(), block);
     block->AddInstruction(std::move(clone));
   }
+  return true;
 }
 
 void ReplaceDescArrayAccessUsingVarIndex::UseNewIdsInBlock(
@@ -250,18 +261,19 @@ void ReplaceDescArrayAccessUsingVarIndex::UseNewIdsInBlock(
   }
 }
 
-void ReplaceDescArrayAccessUsingVarIndex::ReplaceNonUniformAccessWithSwitchCase(
+bool ReplaceDescArrayAccessUsingVarIndex::ReplaceNonUniformAccessWithSwitchCase(
     Instruction* access_chain_final_user, Instruction* access_chain,
     uint32_t number_of_elements,
     const std::deque<Instruction*>& insts_to_be_cloned) const {
   auto* block = context()->get_instr_block(access_chain_final_user);
   // If the instruction does not belong to a block (i.e. in the case of
   // OpDecorate), no replacement is needed.
-  if (!block) return;
+  if (!block) return true;
 
   // Create merge block and add terminator
   auto* merge_block = SeparateInstructionsIntoNewBlock(
       block, access_chain_final_user->NextNode());
+  if (!merge_block) return false;
 
   auto* function = block->GetParent();
 
@@ -273,6 +285,7 @@ void ReplaceDescArrayAccessUsingVarIndex::ReplaceNonUniformAccessWithSwitchCase(
     std::unique_ptr<BasicBlock> case_block(CreateCaseBlock(
         access_chain, idx, insts_to_be_cloned, merge_block->id(),
         &old_ids_to_new_ids_for_cloned_insts));
+    if (!case_block) return false;
     case_block_ids.push_back(case_block->id());
     function->InsertBasicBlockBefore(std::move(case_block), merge_block);
 
@@ -288,6 +301,7 @@ void ReplaceDescArrayAccessUsingVarIndex::ReplaceNonUniformAccessWithSwitchCase(
   std::unique_ptr<BasicBlock> default_block(
       CreateDefaultBlock(access_chain_final_user->HasResultId(), &phi_operands,
                          merge_block->id()));
+  if (!default_block) return false;
   uint32_t default_block_id = default_block->id();
   function->InsertBasicBlockBefore(std::move(default_block), merge_block);
 
@@ -301,11 +315,13 @@ void ReplaceDescArrayAccessUsingVarIndex::ReplaceNonUniformAccessWithSwitchCase(
   if (!phi_operands.empty()) {
     uint32_t phi_id = CreatePhiInstruction(merge_block, phi_operands,
                                            case_block_ids, default_block_id);
+    if (phi_id == 0) return false;
     context()->ReplaceAllUsesWith(access_chain_final_user->result_id(), phi_id);
   }
 
   // Replace OpPhi incoming block operand that uses |block| with |merge_block|
   ReplacePhiIncomingBlock(block->id(), merge_block->id());
+  return true;
 }
 
 BasicBlock*
@@ -316,13 +332,16 @@ ReplaceDescArrayAccessUsingVarIndex::SeparateInstructionsIntoNewBlock(
          &*separation_begin != separation_begin_inst) {
     ++separation_begin;
   }
-  return block->SplitBasicBlock(context(), context()->TakeNextId(),
-                                separation_begin);
+  uint32_t new_id = context()->TakeNextId();
+  if (new_id == 0) return nullptr;
+  return block->SplitBasicBlock(context(), new_id, separation_begin);
 }
 
 BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateNewBlock() const {
-  auto* new_block = new BasicBlock(std::unique_ptr<Instruction>(new Instruction(
-      context(), spv::Op::OpLabel, 0, context()->TakeNextId(), {})));
+  uint32_t new_id = context()->TakeNextId();
+  if (new_id == 0) return nullptr;
+  auto* new_block = new BasicBlock(std::unique_ptr<Instruction>(
+      new Instruction(context(), spv::Op::OpLabel, 0, new_id, {})));
   get_def_use_mgr()->AnalyzeInstDefUse(new_block->GetLabelInst());
   context()->set_instr_block(new_block->GetLabelInst(), new_block);
   return new_block;
@@ -336,7 +355,7 @@ void ReplaceDescArrayAccessUsingVarIndex::UseConstIndexForAccessChain(
                              {const_element_idx_id});
 }
 
-void ReplaceDescArrayAccessUsingVarIndex::AddConstElementAccessToCaseBlock(
+bool ReplaceDescArrayAccessUsingVarIndex::AddConstElementAccessToCaseBlock(
     BasicBlock* case_block, Instruction* access_chain,
     uint32_t const_element_idx,
     std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const {
@@ -344,12 +363,14 @@ void ReplaceDescArrayAccessUsingVarIndex::AddConstElementAccessToCaseBlock(
   UseConstIndexForAccessChain(access_clone.get(), const_element_idx);
 
   uint32_t new_access_id = context()->TakeNextId();
+  if (new_access_id == 0) return false;
   (*old_ids_to_new_ids)[access_clone->result_id()] = new_access_id;
   access_clone->SetResultId(new_access_id);
   get_def_use_mgr()->AnalyzeInstDefUse(access_clone.get());
 
   context()->set_instr_block(access_clone.get(), case_block);
   case_block->AddInstruction(std::move(access_clone));
+  return true;
 }
 
 void ReplaceDescArrayAccessUsingVarIndex::AddBranchToBlock(
@@ -363,6 +384,7 @@ BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateDefaultBlock(
     bool null_const_for_phi_is_needed, std::vector<uint32_t>* phi_operands,
     uint32_t merge_block_id) const {
   auto* default_block = CreateNewBlock();
+  if (!default_block) return nullptr;
   AddBranchToBlock(default_block, merge_block_id);
   if (!null_const_for_phi_is_needed) return default_block;
 
@@ -413,7 +435,11 @@ uint32_t ReplaceDescArrayAccessUsingVarIndex::CreatePhiInstruction(
                              kAnalysisDefUseAndInstrToBlockMapping};
   uint32_t phi_result_type_id =
       context()->get_def_use_mgr()->GetDef(phi_operands[0])->type_id();
-  auto* phi = builder.AddPhi(phi_result_type_id, incomings);
+  Instruction* phi = builder.AddPhi(phi_result_type_id, incomings);
+  if (!phi) {
+    return 0;
+  }
+  context()->get_def_use_mgr()->AnalyzeInstDefUse(phi);
   return phi->result_id();
 }
 

+ 9 - 9
3rdparty/spirv-tools/source/opt/replace_desc_array_access_using_var_index.h

@@ -49,14 +49,13 @@ class ReplaceDescArrayAccessUsingVarIndex : public Pass {
  private:
   // Replaces all accesses to |var| using variable indices with constant
   // elements of the array |var|. Creates switch-case statements to determine
-  // the value of the variable index for all the possible cases. Returns
-  // whether replacement is done or not.
-  bool ReplaceVariableAccessesWithConstantElements(Instruction* var) const;
+  // the value of the variable index for all the possible cases.
+  Status ReplaceVariableAccessesWithConstantElements(Instruction* var) const;
 
   // Replaces the OpAccessChain or OpInBoundsAccessChain instruction |use| that
   // uses the descriptor variable |var| with the OpAccessChain or
   // OpInBoundsAccessChain instruction with a constant Indexes operand.
-  void ReplaceAccessChain(Instruction* var, Instruction* use) const;
+  bool ReplaceAccessChain(Instruction* var, Instruction* use) const;
 
   // Updates the first Indexes operand of the OpAccessChain or
   // OpInBoundsAccessChain instruction |access_chain| to let it use a constant
@@ -68,7 +67,7 @@ class ReplaceDescArrayAccessUsingVarIndex : public Pass {
   // |access_chain| that accesses an array descriptor variable using variable
   // indices with constant elements. |number_of_elements| is the number
   // of array elements.
-  void ReplaceUsersOfAccessChain(Instruction* access_chain,
+  bool ReplaceUsersOfAccessChain(Instruction* access_chain,
                                  uint32_t number_of_elements) const;
 
   // Puts all the recursive users of |access_chain| with concrete result types
@@ -102,7 +101,7 @@ class ReplaceDescArrayAccessUsingVarIndex : public Pass {
   // OpInBoundsAccessChain) will have a constant index for its first index. The
   // OpSwitch instruction will have the cases for the variable index of
   // |access_chain| from 0 to |number_of_elements| - 1.
-  void ReplaceNonUniformAccessWithSwitchCase(
+  bool ReplaceNonUniformAccessWithSwitchCase(
       Instruction* access_chain_final_user, Instruction* access_chain,
       uint32_t number_of_elements,
       const std::deque<Instruction*>& non_uniform_accesses_to_clone) const;
@@ -124,7 +123,7 @@ class ReplaceDescArrayAccessUsingVarIndex : public Pass {
   // |access_chain| to |case_block|. The clone of |access_chain| will use
   // |const_element_idx| for its first index. |old_ids_to_new_ids| keeps the
   // mapping from the result id of |access_chain| to the result of its clone.
-  void AddConstElementAccessToCaseBlock(
+  bool AddConstElementAccessToCaseBlock(
       BasicBlock* case_block, Instruction* access_chain,
       uint32_t const_element_idx,
       std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const;
@@ -132,7 +131,7 @@ class ReplaceDescArrayAccessUsingVarIndex : public Pass {
   // Clones all instructions in |insts_to_be_cloned| and put them to |block|.
   // |old_ids_to_new_ids| keeps the mapping from the result id of each
   // instruction of |insts_to_be_cloned| to the result of their clones.
-  void CloneInstsToBlock(
+  bool CloneInstsToBlock(
       BasicBlock* block, Instruction* inst_to_skip_cloning,
       const std::deque<Instruction*>& insts_to_be_cloned,
       std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const;
@@ -183,7 +182,8 @@ class ReplaceDescArrayAccessUsingVarIndex : public Pass {
   // |case_block_ids| and |default_block_id| as incoming blocks. The size of
   // |phi_operands| must be exactly 1 larger than the size of |case_block_ids|.
   // The last element of |phi_operands| will be used for |default_block_id|. It
-  // adds the phi instruction to the beginning of |parent_block|.
+  // adds the phi instruction to the beginning of |parent_block|. Returns 0 if
+  // it fails to create the Phi instruction.
   uint32_t CreatePhiInstruction(BasicBlock* parent_block,
                                 const std::vector<uint32_t>& phi_operands,
                                 const std::vector<uint32_t>& case_block_ids,

+ 6 - 0
3rdparty/spirv-tools/source/opt/split_combined_image_sampler_pass.cpp

@@ -275,8 +275,10 @@ spv_result_t SplitCombinedImageSamplerPass::RemapVar(
   auto [ptr_image_ty, ptr_sampler_ty] = SplitType(*combined_var_type);
   assert(ptr_image_ty);
   assert(ptr_sampler_ty);
+  // TODO(1841): Handle id overflow.
   Instruction* sampler_var = builder.AddVariable(
       ptr_sampler_ty->result_id(), SpvStorageClassUniformConstant);
+  // TODO(1841): Handle id overflow.
   Instruction* image_var = builder.AddVariable(ptr_image_ty->result_id(),
                                                SpvStorageClassUniformConstant);
   modified_ = true;
@@ -354,8 +356,10 @@ spv_result_t SplitCombinedImageSamplerPass::RemapUses(
 
         // Create loads for the image part and sampler part.
         builder.SetInsertPoint(load);
+        // TODO(1841): Handle id overflow.
         auto* image = builder.AddLoad(PointeeTypeId(use.image_part),
                                       use.image_part->result_id());
+        // TODO(1841): Handle id overflow.
         auto* sampler = builder.AddLoad(PointeeTypeId(use.sampler_part),
                                         use.sampler_part->result_id());
 
@@ -459,9 +463,11 @@ spv_result_t SplitCombinedImageSamplerPass::RemapUses(
 
         auto [result_image_part_ty, result_sampler_part_ty] =
             SplitType(*def_use_mgr_->GetDef(original_access_chain->type_id()));
+        // TODO(1841): Handle id overflow.
         auto* result_image_part = builder.AddOpcodeAccessChain(
             use.user->opcode(), result_image_part_ty->result_id(),
             use.image_part->result_id(), indices);
+        // TODO(1841): Handle id overflow.
         auto* result_sampler_part = builder.AddOpcodeAccessChain(
             use.user->opcode(), result_sampler_part_ty->result_id(),
             use.sampler_part->result_id(), indices);

+ 2 - 0
3rdparty/spirv-tools/source/opt/upgrade_memory_model.cpp

@@ -804,11 +804,13 @@ void UpgradeMemoryModel::UpgradeExtInst(Instruction* ext_inst) {
   InstructionBuilder builder(
       context(), where,
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  // TODO(1841): Handle id overflow.
   auto extract_0 =
       builder.AddCompositeExtract(element_type_id, ext_inst->result_id(), {0});
   context()->ReplaceAllUsesWith(ext_inst->result_id(), extract_0->result_id());
   // The extract's input was just changed to itself, so fix that.
   extract_0->SetInOperand(0u, {ext_inst->result_id()});
+  // TODO(1841): Handle id overflow.
   auto extract_1 =
       builder.AddCompositeExtract(pointee_type_id, ext_inst->result_id(), {1});
   builder.AddStore(ptr_id, extract_1->result_id());

+ 25 - 2
3rdparty/spirv-tools/source/opt/value_number_table.cpp

@@ -143,8 +143,31 @@ uint32_t ValueNumberTable::AssignValueNumber(Instruction* inst) {
     }
   }
 
-  // TODO: Implement a normal form for opcodes that commute like integer
-  // addition.  This will let us know that a+b is the same value as b+a.
+  // Apply normal form, so a+b == b+a
+  switch (value_ins.opcode()) {
+    case spv::Op::OpIAdd:
+    case spv::Op::OpFAdd:
+    case spv::Op::OpIMul:
+    case spv::Op::OpFMul:
+    case spv::Op::OpDot:
+    case spv::Op::OpLogicalEqual:
+    case spv::Op::OpLogicalNotEqual:
+    case spv::Op::OpLogicalOr:
+    case spv::Op::OpLogicalAnd:
+    case spv::Op::OpIEqual:
+    case spv::Op::OpINotEqual:
+    case spv::Op::OpBitwiseOr:
+    case spv::Op::OpBitwiseXor:
+    case spv::Op::OpBitwiseAnd:
+      if (value_ins.GetSingleWordInOperand(0) >
+          value_ins.GetSingleWordInOperand(1)) {
+        value_ins.SetInOperands(
+            {{SPV_OPERAND_TYPE_ID, {value_ins.GetSingleWordInOperand(1)}},
+             {SPV_OPERAND_TYPE_ID, {value_ins.GetSingleWordInOperand(0)}}});
+      }
+    default:
+      break;
+  }
 
   // Otherwise, we check if this value has been computed before.
   auto value_iterator = instruction_to_value_.find(value_ins);

+ 65 - 0
3rdparty/spirv-tools/source/table2.cpp

@@ -372,4 +372,69 @@ bool GetExtensionFromString(const char* name, Extension* extension) {
   return false;
 }
 
+// This is dirty copy of the spirv.hpp11 function
+// TODO - Use a generated version of this function
+const char* StorageClassToString(spv::StorageClass value) {
+  switch (value) {
+    case spv::StorageClass::UniformConstant:
+      return "UniformConstant";
+    case spv::StorageClass::Input:
+      return "Input";
+    case spv::StorageClass::Uniform:
+      return "Uniform";
+    case spv::StorageClass::Output:
+      return "Output";
+    case spv::StorageClass::Workgroup:
+      return "Workgroup";
+    case spv::StorageClass::CrossWorkgroup:
+      return "CrossWorkgroup";
+    case spv::StorageClass::Private:
+      return "Private";
+    case spv::StorageClass::Function:
+      return "Function";
+    case spv::StorageClass::Generic:
+      return "Generic";
+    case spv::StorageClass::PushConstant:
+      return "PushConstant";
+    case spv::StorageClass::AtomicCounter:
+      return "AtomicCounter";
+    case spv::StorageClass::Image:
+      return "Image";
+    case spv::StorageClass::StorageBuffer:
+      return "StorageBuffer";
+    case spv::StorageClass::TileImageEXT:
+      return "TileImageEXT";
+    case spv::StorageClass::TileAttachmentQCOM:
+      return "TileAttachmentQCOM";
+    case spv::StorageClass::NodePayloadAMDX:
+      return "NodePayloadAMDX";
+    case spv::StorageClass::CallableDataKHR:
+      return "CallableDataKHR";
+    case spv::StorageClass::IncomingCallableDataKHR:
+      return "IncomingCallableDataKHR";
+    case spv::StorageClass::RayPayloadKHR:
+      return "RayPayloadKHR";
+    case spv::StorageClass::HitAttributeKHR:
+      return "HitAttributeKHR";
+    case spv::StorageClass::IncomingRayPayloadKHR:
+      return "IncomingRayPayloadKHR";
+    case spv::StorageClass::ShaderRecordBufferKHR:
+      return "ShaderRecordBufferKHR";
+    case spv::StorageClass::PhysicalStorageBuffer:
+      return "PhysicalStorageBuffer";
+    case spv::StorageClass::HitObjectAttributeNV:
+      return "HitObjectAttributeNV";
+    case spv::StorageClass::TaskPayloadWorkgroupEXT:
+      return "TaskPayloadWorkgroupEXT";
+    case spv::StorageClass::CodeSectionINTEL:
+      return "CodeSectionINTEL";
+    case spv::StorageClass::DeviceOnlyINTEL:
+      return "DeviceOnlyINTEL";
+    case spv::StorageClass::HostOnlyINTEL:
+      return "HostOnlyINTEL";
+    default:
+      return "Unknown";
+  }
+}
+
 }  // namespace spvtools

+ 3 - 0
3rdparty/spirv-tools/source/table2.h

@@ -256,5 +256,8 @@ bool GetExtensionFromString(const char* str, Extension* extension);
 // Returns text string corresponding to |extension|.
 const char* ExtensionToString(Extension extension);
 
+/// Used to provide better error message
+const char* StorageClassToString(spv::StorageClass value);
+
 }  // namespace spvtools
 #endif  // SOURCE_TABLE2_H_

+ 64 - 39
3rdparty/spirv-tools/source/val/validate_decorations.cpp

@@ -2176,17 +2176,19 @@ bool AllowsLayout(ValidationState_t& vstate, const spv::StorageClass sc) {
   }
 }
 
-bool UsesExplicitLayout(ValidationState_t& vstate, uint32_t type_id,
-                        std::unordered_map<uint32_t, bool>& cache) {
+// Returns a decoration used to make it explicit
+spv::Decoration UsesExplicitLayout(
+    ValidationState_t& vstate, uint32_t type_id,
+    std::unordered_map<uint32_t, spv::Decoration>& cache) {
   if (type_id == 0) {
-    return false;
+    return spv::Decoration::Max;
   }
 
   if (cache.count(type_id)) {
     return cache[type_id];
   }
 
-  bool res = false;
+  spv::Decoration res = spv::Decoration::Max;
   const auto type_inst = vstate.FindDef(type_id);
   if (type_inst->opcode() == spv::Op::OpTypeStruct ||
       type_inst->opcode() == spv::Op::OpTypeArray ||
@@ -2202,21 +2204,26 @@ bool UsesExplicitLayout(ValidationState_t& vstate, uint32_t type_id,
         allowLayoutDecorations = AllowsLayout(vstate, sc);
       }
       if (!allowLayoutDecorations) {
-        res = std::any_of(
-            iter->second.begin(), iter->second.end(), [](const Decoration& d) {
-              return d.dec_type() == spv::Decoration::Block ||
-                     d.dec_type() == spv::Decoration::BufferBlock ||
-                     d.dec_type() == spv::Decoration::Offset ||
-                     d.dec_type() == spv::Decoration::ArrayStride ||
-                     d.dec_type() == spv::Decoration::MatrixStride;
-            });
+        for (const auto& d : iter->second) {
+          const spv::Decoration dec = d.dec_type();
+          if (dec == spv::Decoration::Block ||
+              dec == spv::Decoration::BufferBlock ||
+              dec == spv::Decoration::Offset ||
+              dec == spv::Decoration::ArrayStride ||
+              dec == spv::Decoration::MatrixStride) {
+            res = dec;
+            break;
+          }
+        }
       }
     }
 
-    if (!res) {
+    if (res == spv::Decoration::Max) {
       switch (type_inst->opcode()) {
         case spv::Op::OpTypeStruct:
-          for (uint32_t i = 1; !res && i < type_inst->operands().size(); i++) {
+          for (uint32_t i = 1;
+               res == spv::Decoration::Max && i < type_inst->operands().size();
+               i++) {
             res = UsesExplicitLayout(
                 vstate, type_inst->GetOperandAs<uint32_t>(i), cache);
           }
@@ -2248,10 +2255,13 @@ spv_result_t CheckInvalidVulkanExplicitLayout(ValidationState_t& vstate) {
     return SPV_SUCCESS;
   }
 
-  std::unordered_map<uint32_t, bool> cache;
+  std::unordered_map<uint32_t, spv::Decoration> cache;
   for (const auto& inst : vstate.ordered_instructions()) {
     const auto type_id = inst.type_id();
     const auto type_inst = vstate.FindDef(type_id);
+
+    spv::StorageClass sc = spv::StorageClass::Max;
+    spv::Decoration layout_dec = spv::Decoration::Max;
     uint32_t fail_id = 0;
     // Variables are the main place to check for improper decorations, but some
     // untyped pointer instructions must also be checked since those types may
@@ -2261,16 +2271,18 @@ spv_result_t CheckInvalidVulkanExplicitLayout(ValidationState_t& vstate) {
     switch (inst.opcode()) {
       case spv::Op::OpVariable:
       case spv::Op::OpUntypedVariableKHR: {
-        const auto sc = inst.GetOperandAs<spv::StorageClass>(2);
+        sc = inst.GetOperandAs<spv::StorageClass>(2);
         auto check_id = type_id;
         if (inst.opcode() == spv::Op::OpUntypedVariableKHR) {
           if (inst.operands().size() > 3) {
             check_id = inst.GetOperandAs<uint32_t>(3);
           }
         }
-        if (!AllowsLayout(vstate, sc) &&
-            UsesExplicitLayout(vstate, check_id, cache)) {
-          fail_id = check_id;
+        if (!AllowsLayout(vstate, sc)) {
+          layout_dec = UsesExplicitLayout(vstate, check_id, cache);
+          if (layout_dec != spv::Decoration::Max) {
+            fail_id = check_id;
+          }
         }
         break;
       }
@@ -2280,13 +2292,17 @@ spv_result_t CheckInvalidVulkanExplicitLayout(ValidationState_t& vstate) {
       case spv::Op::OpUntypedInBoundsPtrAccessChainKHR: {
         // Check both the base type and return type. The return type may have an
         // invalid array stride.
-        const auto sc = type_inst->GetOperandAs<spv::StorageClass>(1);
-        const auto base_type_id = inst.GetOperandAs<uint32_t>(2);
+        sc = type_inst->GetOperandAs<spv::StorageClass>(1);
         if (!AllowsLayout(vstate, sc)) {
-          if (UsesExplicitLayout(vstate, base_type_id, cache)) {
+          const auto base_type_id = inst.GetOperandAs<uint32_t>(2);
+          layout_dec = UsesExplicitLayout(vstate, base_type_id, cache);
+          if (layout_dec != spv::Decoration::Max) {
             fail_id = base_type_id;
-          } else if (UsesExplicitLayout(vstate, type_id, cache)) {
-            fail_id = type_id;
+          } else {
+            layout_dec = UsesExplicitLayout(vstate, type_id, cache);
+            if (layout_dec != spv::Decoration::Max) {
+              fail_id = type_id;
+            }
           }
         }
         break;
@@ -2296,11 +2312,13 @@ spv_result_t CheckInvalidVulkanExplicitLayout(ValidationState_t& vstate) {
         const auto ptr_ty_id =
             vstate.FindDef(inst.GetOperandAs<uint32_t>(3))->type_id();
         const auto ptr_ty = vstate.FindDef(ptr_ty_id);
-        const auto sc = ptr_ty->GetOperandAs<spv::StorageClass>(1);
-        const auto base_type_id = inst.GetOperandAs<uint32_t>(2);
-        if (!AllowsLayout(vstate, sc) &&
-            UsesExplicitLayout(vstate, base_type_id, cache)) {
-          fail_id = base_type_id;
+        sc = ptr_ty->GetOperandAs<spv::StorageClass>(1);
+        if (!AllowsLayout(vstate, sc)) {
+          const auto base_type_id = inst.GetOperandAs<uint32_t>(2);
+          layout_dec = UsesExplicitLayout(vstate, base_type_id, cache);
+          if (layout_dec != spv::Decoration::Max) {
+            fail_id = base_type_id;
+          }
         }
         break;
       }
@@ -2309,10 +2327,12 @@ spv_result_t CheckInvalidVulkanExplicitLayout(ValidationState_t& vstate) {
         const auto ptr_type = vstate.FindDef(vstate.FindDef(ptr_id)->type_id());
         if (ptr_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) {
           // For untyped pointers check the return type for an invalid layout.
-          const auto sc = ptr_type->GetOperandAs<spv::StorageClass>(1);
-          if (!AllowsLayout(vstate, sc) &&
-              UsesExplicitLayout(vstate, type_id, cache)) {
-            fail_id = type_id;
+          sc = ptr_type->GetOperandAs<spv::StorageClass>(1);
+          if (!AllowsLayout(vstate, sc)) {
+            layout_dec = UsesExplicitLayout(vstate, type_id, cache);
+            if (layout_dec != spv::Decoration::Max) {
+              fail_id = type_id;
+            }
           }
         }
         break;
@@ -2323,11 +2343,13 @@ spv_result_t CheckInvalidVulkanExplicitLayout(ValidationState_t& vstate) {
         if (ptr_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) {
           // For untyped pointers, check the type of the data operand for an
           // invalid layout.
-          const auto sc = ptr_type->GetOperandAs<spv::StorageClass>(1);
-          const auto data_type_id = vstate.GetOperandTypeId(&inst, 1);
-          if (!AllowsLayout(vstate, sc) &&
-              UsesExplicitLayout(vstate, data_type_id, cache)) {
-            fail_id = inst.GetOperandAs<uint32_t>(2);
+          sc = ptr_type->GetOperandAs<spv::StorageClass>(1);
+          if (!AllowsLayout(vstate, sc)) {
+            const auto data_type_id = vstate.GetOperandTypeId(&inst, 1);
+            layout_dec = UsesExplicitLayout(vstate, data_type_id, cache);
+            if (layout_dec != spv::Decoration::Max) {
+              fail_id = inst.GetOperandAs<uint32_t>(2);
+            }
           }
         }
         break;
@@ -2339,7 +2361,10 @@ spv_result_t CheckInvalidVulkanExplicitLayout(ValidationState_t& vstate) {
       return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
              << vstate.VkErrorID(10684)
              << "Invalid explicit layout decorations on type for operand "
-             << vstate.getIdName(fail_id);
+             << vstate.getIdName(fail_id) << ", the "
+             << spvtools::StorageClassToString(sc)
+             << " storage class has a explicit layout from the "
+             << vstate.SpvDecorationString(layout_dec) << " decoration.";
     }
   }
 

+ 2440 - 2419
3rdparty/spirv-tools/source/val/validate_extensions.cpp

@@ -84,12 +84,37 @@ uint32_t GetUint32Constant(ValidationState_t& _, uint32_t id) {
   return inst->word(3);
 }
 
+std::string GetExtInstName(const ValidationState_t& _,
+                           const Instruction* inst) {
+  const uint32_t ext_inst_set = inst->word(3);
+  const uint32_t ext_inst_index = inst->word(4);
+  const spv_ext_inst_type_t ext_inst_type =
+      spv_ext_inst_type_t(inst->ext_inst_type());
+
+  const ExtInstDesc* desc = nullptr;
+  if (LookupExtInst(ext_inst_type, ext_inst_index, &desc) != SPV_SUCCESS ||
+      !desc) {
+    return std::string("Unknown ExtInst");
+  }
+
+  auto* import_inst = _.FindDef(ext_inst_set);
+  assert(import_inst);
+
+  std::ostringstream ss;
+  ss << import_inst->GetOperandAs<std::string>(1);
+  ss << " ";
+  ss << desc->name().data();
+
+  return ss.str();
+}
+
 // Check that the operand of a debug info instruction |inst| at |word_index|
 // is a result id of an instruction with |expected_opcode|.
-spv_result_t ValidateOperandForDebugInfo(
-    ValidationState_t& _, const std::string& operand_name,
-    spv::Op expected_opcode, const Instruction* inst, uint32_t word_index,
-    const std::function<std::string()>& ext_inst_name) {
+spv_result_t ValidateOperandForDebugInfo(ValidationState_t& _,
+                                         const std::string& operand_name,
+                                         spv::Op expected_opcode,
+                                         const Instruction* inst,
+                                         uint32_t word_index) {
   auto* operand = _.FindDef(inst->word(word_index));
   if (operand->opcode() != expected_opcode) {
     const spvtools::InstructionDesc* desc = nullptr;
@@ -97,12 +122,13 @@ spv_result_t ValidateOperandForDebugInfo(
                                      &desc) != SPV_SUCCESS ||
         !desc) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << ext_inst_name() << ": "
-             << "expected operand " << operand_name << " is invalid";
+             << GetExtInstName(_, inst) << ": " << "expected operand "
+             << operand_name << " is invalid";
     }
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << ext_inst_name() << ": " << "expected operand " << operand_name
-           << " must be a result id of " << "Op" << desc->name().data();
+           << GetExtInstName(_, inst) << ": " << "expected operand "
+           << operand_name << " must be a result id of " << "Op"
+           << desc->name().data();
   }
   return SPV_SUCCESS;
 }
@@ -113,28 +139,26 @@ spv_result_t ValidateOperandForDebugInfo(
 // word so cannot be validated.
 spv_result_t ValidateUint32ConstantOperandForDebugInfo(
     ValidationState_t& _, const std::string& operand_name,
-    const Instruction* inst, uint32_t word_index,
-    const std::function<std::string()>& ext_inst_name) {
+    const Instruction* inst, uint32_t word_index) {
   if (!IsUint32Constant(_, inst->word(word_index))) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << ext_inst_name() << ": expected operand " << operand_name
+           << GetExtInstName(_, inst) << ": expected operand " << operand_name
            << " must be a result id of 32-bit unsigned OpConstant";
   }
   return SPV_SUCCESS;
 }
 
-#define CHECK_OPERAND(NAME, opcode, index)                                  \
-  do {                                                                      \
-    auto result = ValidateOperandForDebugInfo(_, NAME, opcode, inst, index, \
-                                              ext_inst_name);               \
-    if (result != SPV_SUCCESS) return result;                               \
+#define CHECK_OPERAND(NAME, opcode, index)                                   \
+  do {                                                                       \
+    auto result = ValidateOperandForDebugInfo(_, NAME, opcode, inst, index); \
+    if (result != SPV_SUCCESS) return result;                                \
   } while (0)
 
-#define CHECK_CONST_UINT_OPERAND(NAME, index)                \
-  if (vulkanDebugInfo) {                                     \
-    auto result = ValidateUint32ConstantOperandForDebugInfo( \
-        _, NAME, inst, index, ext_inst_name);                \
-    if (result != SPV_SUCCESS) return result;                \
+#define CHECK_CONST_UINT_OPERAND(NAME, index)                            \
+  if (vulkanDebugInfo) {                                                 \
+    auto result =                                                        \
+        ValidateUint32ConstantOperandForDebugInfo(_, NAME, inst, index); \
+    if (result != SPV_SUCCESS) return result;                            \
   }
 
 // True if the operand of a debug info instruction |inst| at |word_index|
@@ -180,7 +204,7 @@ bool DoesDebugInfoOperandMatchExpectation(
 spv_result_t ValidateDebugInfoOperand(
     ValidationState_t& _, const std::string& debug_inst_name,
     CommonDebugInfoInstructions expected_debug_inst, const Instruction* inst,
-    uint32_t word_index, const std::function<std::string()>& ext_inst_name) {
+    uint32_t word_index) {
   std::function<bool(CommonDebugInfoInstructions)> expectation =
       [expected_debug_inst](CommonDebugInfoInstructions dbg_inst) {
         return dbg_inst == expected_debug_inst;
@@ -193,38 +217,39 @@ spv_result_t ValidateDebugInfoOperand(
           SPV_SUCCESS ||
       !desc) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << ext_inst_name() << ": "
-           << "expected operand " << debug_inst_name << " is invalid";
+           << GetExtInstName(_, inst) << ": " << "expected operand "
+           << debug_inst_name << " is invalid";
   }
   return _.diag(SPV_ERROR_INVALID_DATA, inst)
-         << ext_inst_name() << ": " << "expected operand " << debug_inst_name
-         << " must be a result id of " << desc->name().data();
+         << GetExtInstName(_, inst) << ": " << "expected operand "
+         << debug_inst_name << " must be a result id of "
+         << desc->name().data();
 }
 
-#define CHECK_DEBUG_OPERAND(NAME, debug_opcode, index)                         \
-  do {                                                                         \
-    auto result = ValidateDebugInfoOperand(_, NAME, debug_opcode, inst, index, \
-                                           ext_inst_name);                     \
-    if (result != SPV_SUCCESS) return result;                                  \
+#define CHECK_DEBUG_OPERAND(NAME, debug_opcode, index)                \
+  do {                                                                \
+    auto result =                                                     \
+        ValidateDebugInfoOperand(_, NAME, debug_opcode, inst, index); \
+    if (result != SPV_SUCCESS) return result;                         \
   } while (0)
 
 // Check that the operand of a debug info instruction |inst| at |word_index|
 // is a result id of an debug info instruction with DebugTypeBasic.
-spv_result_t ValidateOperandBaseType(
-    ValidationState_t& _, const Instruction* inst, uint32_t word_index,
-    const std::function<std::string()>& ext_inst_name) {
+spv_result_t ValidateOperandBaseType(ValidationState_t& _,
+                                     const Instruction* inst,
+                                     uint32_t word_index) {
   return ValidateDebugInfoOperand(_, "Base Type", CommonDebugInfoDebugTypeBasic,
-                                  inst, word_index, ext_inst_name);
+                                  inst, word_index);
 }
 
 // Check that the operand of a debug info instruction |inst| at |word_index|
 // is a result id of a debug lexical scope instruction which is one of
 // DebugCompilationUnit, DebugFunction, DebugLexicalBlock, or
 // DebugTypeComposite.
-spv_result_t ValidateOperandLexicalScope(
-    ValidationState_t& _, const std::string& debug_inst_name,
-    const Instruction* inst, uint32_t word_index,
-    const std::function<std::string()>& ext_inst_name) {
+spv_result_t ValidateOperandLexicalScope(ValidationState_t& _,
+                                         const std::string& debug_inst_name,
+                                         const Instruction* inst,
+                                         uint32_t word_index) {
   std::function<bool(CommonDebugInfoInstructions)> expectation =
       [](CommonDebugInfoInstructions dbg_inst) {
         return dbg_inst == CommonDebugInfoDebugCompilationUnit ||
@@ -236,19 +261,18 @@ spv_result_t ValidateOperandLexicalScope(
     return SPV_SUCCESS;
 
   return _.diag(SPV_ERROR_INVALID_DATA, inst)
-         << ext_inst_name() << ": "
-         << "expected operand " << debug_inst_name
-         << " must be a result id of a lexical scope";
+         << GetExtInstName(_, inst) << ": " << "expected operand "
+         << debug_inst_name << " must be a result id of a lexical scope";
 }
 
 // Check that the operand of a debug info instruction |inst| at |word_index|
 // is a result id of a debug type instruction (See DebugTypeXXX in
 // "4.3. Type instructions" section of OpenCL.DebugInfo.100 spec.
-spv_result_t ValidateOperandDebugType(
-    ValidationState_t& _, const std::string& debug_inst_name,
-    const Instruction* inst, uint32_t word_index,
-    const std::function<std::string()>& ext_inst_name,
-    bool allow_template_param) {
+spv_result_t ValidateOperandDebugType(ValidationState_t& _,
+                                      const std::string& debug_inst_name,
+                                      const Instruction* inst,
+                                      uint32_t word_index,
+                                      bool allow_template_param) {
   // Check for NonSemanticShaderDebugInfo100 specific types.
   if (inst->ext_inst_type() ==
       SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
@@ -275,9 +299,8 @@ spv_result_t ValidateOperandDebugType(
     return SPV_SUCCESS;
 
   return _.diag(SPV_ERROR_INVALID_DATA, inst)
-         << ext_inst_name() << ": "
-         << "expected operand " << debug_inst_name
-         << " is not a valid debug type";
+         << GetExtInstName(_, inst) << ": " << "expected operand "
+         << debug_inst_name << " is not a valid debug type";
 }
 
 spv_result_t ValidateClspvReflectionKernel(ValidationState_t& _,
@@ -1095,2747 +1118,2745 @@ spv_result_t ValidateExtInstImport(ValidationState_t& _,
   return SPV_SUCCESS;
 }
 
-spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
+spv_result_t ValidateExtInstGlslStd450(ValidationState_t& _,
+                                       const Instruction* inst) {
   const uint32_t result_type = inst->type_id();
   const uint32_t num_operands = static_cast<uint32_t>(inst->operands().size());
 
-  const uint32_t ext_inst_set = inst->word(3);
   const uint32_t ext_inst_index = inst->word(4);
-  const spv_ext_inst_type_t ext_inst_type =
-      spv_ext_inst_type_t(inst->ext_inst_type());
-
-  auto ext_inst_name = [&_, ext_inst_set, ext_inst_type, ext_inst_index]() {
-    const ExtInstDesc* desc = nullptr;
-    if (LookupExtInst(ext_inst_type, ext_inst_index, &desc) != SPV_SUCCESS ||
-        !desc) {
-      return std::string("Unknown ExtInst");
-    }
-
-    auto* import_inst = _.FindDef(ext_inst_set);
-    assert(import_inst);
-
-    std::ostringstream ss;
-    ss << import_inst->GetOperandAs<std::string>(1);
-    ss << " ";
-    ss << desc->name().data();
-
-    return ss.str();
-  };
-
-  if (ext_inst_type == SPV_EXT_INST_TYPE_GLSL_STD_450) {
-    const GLSLstd450 ext_inst_key = GLSLstd450(ext_inst_index);
-    switch (ext_inst_key) {
-      case GLSLstd450Round:
-      case GLSLstd450RoundEven:
-      case GLSLstd450FAbs:
-      case GLSLstd450Trunc:
-      case GLSLstd450FSign:
-      case GLSLstd450Floor:
-      case GLSLstd450Ceil:
-      case GLSLstd450Fract:
-      case GLSLstd450Sqrt:
-      case GLSLstd450InverseSqrt:
-      case GLSLstd450FMin:
-      case GLSLstd450FMax:
-      case GLSLstd450FClamp:
-      case GLSLstd450FMix:
-      case GLSLstd450Step:
-      case GLSLstd450SmoothStep:
-      case GLSLstd450Fma:
-      case GLSLstd450Normalize:
-      case GLSLstd450FaceForward:
-      case GLSLstd450Reflect:
-      case GLSLstd450NMin:
-      case GLSLstd450NMax:
-      case GLSLstd450NClamp: {
-        bool supportsCoopVec =
-            (ext_inst_key == GLSLstd450FMin || ext_inst_key == GLSLstd450FMax ||
-             ext_inst_key == GLSLstd450FClamp ||
-             ext_inst_key == GLSLstd450NMin || ext_inst_key == GLSLstd450NMax ||
-             ext_inst_key == GLSLstd450NClamp ||
-             ext_inst_key == GLSLstd450Step || ext_inst_key == GLSLstd450Fma);
-
-        if (!_.IsFloatScalarOrVectorType(result_type) &&
-            !(supportsCoopVec &&
-              _.IsFloatCooperativeVectorNVType(result_type))) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar or vector type";
-        }
-
-        for (uint32_t operand_index = 4; operand_index < num_operands;
-             ++operand_index) {
-          const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
-          if (result_type != operand_type) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected types of all operands to be equal to Result "
-                      "Type";
-          }
-        }
-        break;
+  const GLSLstd450 ext_inst_key = GLSLstd450(ext_inst_index);
+  switch (ext_inst_key) {
+    case GLSLstd450Round:
+    case GLSLstd450RoundEven:
+    case GLSLstd450FAbs:
+    case GLSLstd450Trunc:
+    case GLSLstd450FSign:
+    case GLSLstd450Floor:
+    case GLSLstd450Ceil:
+    case GLSLstd450Fract:
+    case GLSLstd450Sqrt:
+    case GLSLstd450InverseSqrt:
+    case GLSLstd450FMin:
+    case GLSLstd450FMax:
+    case GLSLstd450FClamp:
+    case GLSLstd450FMix:
+    case GLSLstd450Step:
+    case GLSLstd450SmoothStep:
+    case GLSLstd450Fma:
+    case GLSLstd450Normalize:
+    case GLSLstd450FaceForward:
+    case GLSLstd450Reflect:
+    case GLSLstd450NMin:
+    case GLSLstd450NMax:
+    case GLSLstd450NClamp: {
+      bool supportsCoopVec =
+          (ext_inst_key == GLSLstd450FMin || ext_inst_key == GLSLstd450FMax ||
+           ext_inst_key == GLSLstd450FClamp || ext_inst_key == GLSLstd450NMin ||
+           ext_inst_key == GLSLstd450NMax || ext_inst_key == GLSLstd450NClamp ||
+           ext_inst_key == GLSLstd450Step || ext_inst_key == GLSLstd450Fma);
+
+      if (!_.IsFloatScalarOrVectorType(result_type) &&
+          !(supportsCoopVec && _.IsFloatCooperativeVectorNVType(result_type))) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar or vector type";
       }
 
-      case GLSLstd450SAbs:
-      case GLSLstd450SSign:
-      case GLSLstd450UMin:
-      case GLSLstd450SMin:
-      case GLSLstd450UMax:
-      case GLSLstd450SMax:
-      case GLSLstd450UClamp:
-      case GLSLstd450SClamp:
-      case GLSLstd450FindILsb:
-      case GLSLstd450FindUMsb:
-      case GLSLstd450FindSMsb: {
-        bool supportsCoopVec =
-            (ext_inst_key == GLSLstd450UMin || ext_inst_key == GLSLstd450UMax ||
-             ext_inst_key == GLSLstd450UClamp ||
-             ext_inst_key == GLSLstd450SMin || ext_inst_key == GLSLstd450SMax ||
-             ext_inst_key == GLSLstd450SClamp);
-
-        if (!_.IsIntScalarOrVectorType(result_type) &&
-            !(supportsCoopVec && _.IsIntCooperativeVectorNVType(result_type))) {
+      for (uint32_t operand_index = 4; operand_index < num_operands;
+           ++operand_index) {
+        const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
+        if (result_type != operand_type) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be an int scalar or vector type";
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected types of all operands to be equal to Result "
+                    "Type";
         }
+      }
+      break;
+    }
 
-        const uint32_t result_type_bit_width = _.GetBitWidth(result_type);
-        const uint32_t result_type_dimension = _.GetDimension(result_type);
-
-        for (uint32_t operand_index = 4; operand_index < num_operands;
-             ++operand_index) {
-          const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
-          if (!operand_type ||
-              (!_.IsIntScalarOrVectorType(operand_type) &&
-               !(supportsCoopVec &&
-                 _.IsIntCooperativeVectorNVType(operand_type)))) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected all operands to be int scalars or vectors";
-          }
-
-          if (result_type_dimension != _.GetDimension(operand_type)) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected all operands to have the same dimension as "
-                   << "Result Type";
-          }
+    case GLSLstd450SAbs:
+    case GLSLstd450SSign:
+    case GLSLstd450UMin:
+    case GLSLstd450SMin:
+    case GLSLstd450UMax:
+    case GLSLstd450SMax:
+    case GLSLstd450UClamp:
+    case GLSLstd450SClamp:
+    case GLSLstd450FindILsb:
+    case GLSLstd450FindUMsb:
+    case GLSLstd450FindSMsb: {
+      bool supportsCoopVec =
+          (ext_inst_key == GLSLstd450UMin || ext_inst_key == GLSLstd450UMax ||
+           ext_inst_key == GLSLstd450UClamp || ext_inst_key == GLSLstd450SMin ||
+           ext_inst_key == GLSLstd450SMax || ext_inst_key == GLSLstd450SClamp);
+
+      if (!_.IsIntScalarOrVectorType(result_type) &&
+          !(supportsCoopVec && _.IsIntCooperativeVectorNVType(result_type))) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be an int scalar or vector type";
+      }
 
-          if (result_type_bit_width != _.GetBitWidth(operand_type)) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected all operands to have the same bit width as "
-                   << "Result Type";
-          }
+      const uint32_t result_type_bit_width = _.GetBitWidth(result_type);
+      const uint32_t result_type_dimension = _.GetDimension(result_type);
 
-          if (ext_inst_key == GLSLstd450FindUMsb ||
-              ext_inst_key == GLSLstd450FindSMsb) {
-            if (result_type_bit_width != 32) {
-              return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                     << ext_inst_name() << ": "
-                     << "this instruction is currently limited to 32-bit width "
-                     << "components";
-            }
-          }
+      for (uint32_t operand_index = 4; operand_index < num_operands;
+           ++operand_index) {
+        const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
+        if (!operand_type ||
+            (!_.IsIntScalarOrVectorType(operand_type) &&
+             !(supportsCoopVec &&
+               _.IsIntCooperativeVectorNVType(operand_type)))) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected all operands to be int scalars or vectors";
         }
-        break;
-      }
 
-      case GLSLstd450Radians:
-      case GLSLstd450Degrees:
-      case GLSLstd450Sin:
-      case GLSLstd450Cos:
-      case GLSLstd450Tan:
-      case GLSLstd450Asin:
-      case GLSLstd450Acos:
-      case GLSLstd450Atan:
-      case GLSLstd450Sinh:
-      case GLSLstd450Cosh:
-      case GLSLstd450Tanh:
-      case GLSLstd450Asinh:
-      case GLSLstd450Acosh:
-      case GLSLstd450Atanh:
-      case GLSLstd450Exp:
-      case GLSLstd450Exp2:
-      case GLSLstd450Log:
-      case GLSLstd450Log2:
-      case GLSLstd450Atan2:
-      case GLSLstd450Pow: {
-        bool supportsCoopVec =
-            (ext_inst_key == GLSLstd450Atan || ext_inst_key == GLSLstd450Tanh ||
-             ext_inst_key == GLSLstd450Exp || ext_inst_key == GLSLstd450Log);
-
-        if (!_.IsFloatScalarOrVectorType(result_type) &&
-            !(supportsCoopVec &&
-              _.IsFloatCooperativeVectorNVType(result_type))) {
+        if (result_type_dimension != _.GetDimension(operand_type)) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a 16 or 32-bit scalar or "
-                    "vector float type";
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected all operands to have the same dimension as "
+                 << "Result Type";
         }
 
-        const uint32_t result_type_bit_width = _.GetBitWidth(result_type);
-        if (result_type_bit_width != 16 && result_type_bit_width != 32) {
+        if (result_type_bit_width != _.GetBitWidth(operand_type)) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a 16 or 32-bit scalar or "
-                    "vector float type";
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected all operands to have the same bit width as "
+                 << "Result Type";
         }
 
-        for (uint32_t operand_index = 4; operand_index < num_operands;
-             ++operand_index) {
-          const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
-          if (result_type != operand_type) {
+        if (ext_inst_key == GLSLstd450FindUMsb ||
+            ext_inst_key == GLSLstd450FindSMsb) {
+          if (result_type_bit_width != 32) {
             return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected types of all operands to be equal to Result "
-                      "Type";
+                   << GetExtInstName(_, inst) << ": "
+                   << "this instruction is currently limited to 32-bit width "
+                   << "components";
           }
         }
-        break;
       }
+      break;
+    }
 
-      case GLSLstd450Determinant: {
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        uint32_t num_rows = 0;
-        uint32_t num_cols = 0;
-        uint32_t col_type = 0;
-        uint32_t component_type = 0;
-        if (!_.GetMatrixTypeInfo(x_type, &num_rows, &num_cols, &col_type,
-                                 &component_type) ||
-            num_rows != num_cols) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X to be a square matrix";
-        }
+    case GLSLstd450Radians:
+    case GLSLstd450Degrees:
+    case GLSLstd450Sin:
+    case GLSLstd450Cos:
+    case GLSLstd450Tan:
+    case GLSLstd450Asin:
+    case GLSLstd450Acos:
+    case GLSLstd450Atan:
+    case GLSLstd450Sinh:
+    case GLSLstd450Cosh:
+    case GLSLstd450Tanh:
+    case GLSLstd450Asinh:
+    case GLSLstd450Acosh:
+    case GLSLstd450Atanh:
+    case GLSLstd450Exp:
+    case GLSLstd450Exp2:
+    case GLSLstd450Log:
+    case GLSLstd450Log2:
+    case GLSLstd450Atan2:
+    case GLSLstd450Pow: {
+      bool supportsCoopVec =
+          (ext_inst_key == GLSLstd450Atan || ext_inst_key == GLSLstd450Tanh ||
+           ext_inst_key == GLSLstd450Exp || ext_inst_key == GLSLstd450Log);
+
+      if (!_.IsFloatScalarOrVectorType(result_type) &&
+          !(supportsCoopVec && _.IsFloatCooperativeVectorNVType(result_type))) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a 16 or 32-bit scalar or "
+                  "vector float type";
+      }
 
-        if (result_type != component_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X component type to be equal to "
-                 << "Result Type";
-        }
-        break;
+      const uint32_t result_type_bit_width = _.GetBitWidth(result_type);
+      if (result_type_bit_width != 16 && result_type_bit_width != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a 16 or 32-bit scalar or "
+                  "vector float type";
       }
 
-      case GLSLstd450MatrixInverse: {
-        uint32_t num_rows = 0;
-        uint32_t num_cols = 0;
-        uint32_t col_type = 0;
-        uint32_t component_type = 0;
-        if (!_.GetMatrixTypeInfo(result_type, &num_rows, &num_cols, &col_type,
-                                 &component_type) ||
-            num_rows != num_cols) {
+      for (uint32_t operand_index = 4; operand_index < num_operands;
+           ++operand_index) {
+        const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
+        if (result_type != operand_type) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a square matrix";
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected types of all operands to be equal to Result "
+                    "Type";
         }
+      }
+      break;
+    }
 
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        if (result_type != x_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X type to be equal to Result Type";
-        }
-        break;
+    case GLSLstd450Determinant: {
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      uint32_t num_rows = 0;
+      uint32_t num_cols = 0;
+      uint32_t col_type = 0;
+      uint32_t component_type = 0;
+      if (!_.GetMatrixTypeInfo(x_type, &num_rows, &num_cols, &col_type,
+                               &component_type) ||
+          num_rows != num_cols) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X to be a square matrix";
       }
 
-      case GLSLstd450Modf: {
-        if (!_.IsFloatScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or vector float type";
-        }
+      if (result_type != component_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X component type to be equal to "
+               << "Result Type";
+      }
+      break;
+    }
 
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t i_type = _.GetOperandTypeId(inst, 5);
+    case GLSLstd450MatrixInverse: {
+      uint32_t num_rows = 0;
+      uint32_t num_cols = 0;
+      uint32_t col_type = 0;
+      uint32_t component_type = 0;
+      if (!_.GetMatrixTypeInfo(result_type, &num_rows, &num_cols, &col_type,
+                               &component_type) ||
+          num_rows != num_cols) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a square matrix";
+      }
 
-        if (x_type != result_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X type to be equal to Result Type";
-        }
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      if (result_type != x_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X type to be equal to Result Type";
+      }
+      break;
+    }
 
-        spv::StorageClass i_storage_class;
-        uint32_t i_data_type = 0;
-        if (!_.GetPointerTypeInfo(i_type, &i_data_type, &i_storage_class)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand I to be a pointer";
-        }
+    case GLSLstd450Modf: {
+      if (!_.IsFloatScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or vector float type";
+      }
 
-        if (i_data_type != result_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand I data type to be equal to Result Type";
-        }
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t i_type = _.GetOperandTypeId(inst, 5);
 
-        break;
+      if (x_type != result_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X type to be equal to Result Type";
       }
 
-      case GLSLstd450ModfStruct: {
-        std::vector<uint32_t> result_types;
-        if (!_.GetStructMemberTypes(result_type, &result_types) ||
-            result_types.size() != 2 ||
-            !_.IsFloatScalarOrVectorType(result_types[0]) ||
-            result_types[1] != result_types[0]) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a struct with two identical "
-                 << "scalar or vector float type members";
-        }
+      spv::StorageClass i_storage_class;
+      uint32_t i_data_type = 0;
+      if (!_.GetPointerTypeInfo(i_type, &i_data_type, &i_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand I to be a pointer";
+      }
 
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        if (x_type != result_types[0]) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X type to be equal to members of "
-                 << "Result Type struct";
-        }
-        break;
+      if (i_data_type != result_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand I data type to be equal to Result Type";
       }
 
-      case GLSLstd450Frexp: {
-        if (!_.IsFloatScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or vector float type";
-        }
+      break;
+    }
 
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t exp_type = _.GetOperandTypeId(inst, 5);
+    case GLSLstd450ModfStruct: {
+      std::vector<uint32_t> result_types;
+      if (!_.GetStructMemberTypes(result_type, &result_types) ||
+          result_types.size() != 2 ||
+          !_.IsFloatScalarOrVectorType(result_types[0]) ||
+          result_types[1] != result_types[0]) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a struct with two identical "
+               << "scalar or vector float type members";
+      }
 
-        if (x_type != result_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X type to be equal to Result Type";
-        }
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      if (x_type != result_types[0]) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X type to be equal to members of "
+               << "Result Type struct";
+      }
+      break;
+    }
 
-        spv::StorageClass exp_storage_class;
-        uint32_t exp_data_type = 0;
-        if (!_.GetPointerTypeInfo(exp_type, &exp_data_type,
-                                  &exp_storage_class)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Exp to be a pointer";
-        }
+    case GLSLstd450Frexp: {
+      if (!_.IsFloatScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or vector float type";
+      }
 
-        if (!_.IsIntScalarOrVectorType(exp_data_type) ||
-            (!_.HasExtension(kSPV_AMD_gpu_shader_int16) &&
-             _.GetBitWidth(exp_data_type) != 32) ||
-            (_.HasExtension(kSPV_AMD_gpu_shader_int16) &&
-             _.GetBitWidth(exp_data_type) != 16 &&
-             _.GetBitWidth(exp_data_type) != 32)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Exp data type to be a "
-                 << (_.HasExtension(kSPV_AMD_gpu_shader_int16)
-                         ? "16-bit or 32-bit "
-                         : "32-bit ")
-                 << "int scalar or vector type";
-        }
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t exp_type = _.GetOperandTypeId(inst, 5);
 
-        if (_.GetDimension(result_type) != _.GetDimension(exp_data_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Exp data type to have the same component "
-                 << "number as Result Type";
-        }
+      if (x_type != result_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X type to be equal to Result Type";
+      }
 
-        break;
+      spv::StorageClass exp_storage_class;
+      uint32_t exp_data_type = 0;
+      if (!_.GetPointerTypeInfo(exp_type, &exp_data_type, &exp_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Exp to be a pointer";
       }
 
-      case GLSLstd450Ldexp: {
-        if (!_.IsFloatScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or vector float type";
-        }
+      if (!_.IsIntScalarOrVectorType(exp_data_type) ||
+          (!_.HasExtension(kSPV_AMD_gpu_shader_int16) &&
+           _.GetBitWidth(exp_data_type) != 32) ||
+          (_.HasExtension(kSPV_AMD_gpu_shader_int16) &&
+           _.GetBitWidth(exp_data_type) != 16 &&
+           _.GetBitWidth(exp_data_type) != 32)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Exp data type to be a "
+               << (_.HasExtension(kSPV_AMD_gpu_shader_int16)
+                       ? "16-bit or 32-bit "
+                       : "32-bit ")
+               << "int scalar or vector type";
+      }
 
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t exp_type = _.GetOperandTypeId(inst, 5);
+      if (_.GetDimension(result_type) != _.GetDimension(exp_data_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Exp data type to have the same component "
+               << "number as Result Type";
+      }
 
-        if (x_type != result_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X type to be equal to Result Type";
-        }
+      break;
+    }
 
-        if (!_.IsIntScalarOrVectorType(exp_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Exp to be a 32-bit int scalar "
-                 << "or vector type";
-        }
+    case GLSLstd450Ldexp: {
+      if (!_.IsFloatScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or vector float type";
+      }
 
-        if (_.GetDimension(result_type) != _.GetDimension(exp_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Exp to have the same component "
-                 << "number as Result Type";
-        }
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t exp_type = _.GetOperandTypeId(inst, 5);
 
-        break;
+      if (x_type != result_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X type to be equal to Result Type";
       }
 
-      case GLSLstd450FrexpStruct: {
-        std::vector<uint32_t> result_types;
-        if (!_.GetStructMemberTypes(result_type, &result_types) ||
-            result_types.size() != 2 ||
-            !_.IsFloatScalarOrVectorType(result_types[0]) ||
-            !_.IsIntScalarOrVectorType(result_types[1]) ||
-            (!_.HasExtension(kSPV_AMD_gpu_shader_int16) &&
-             _.GetBitWidth(result_types[1]) != 32) ||
-            (_.HasExtension(kSPV_AMD_gpu_shader_int16) &&
-             _.GetBitWidth(result_types[1]) != 16 &&
-             _.GetBitWidth(result_types[1]) != 32) ||
-            _.GetDimension(result_types[0]) !=
-                _.GetDimension(result_types[1])) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a struct with two members, "
-                 << "first member a float scalar or vector, second member a "
-                 << (_.HasExtension(kSPV_AMD_gpu_shader_int16)
-                         ? "16-bit or 32-bit "
-                         : "32-bit ")
-                 << "int scalar or vector with the same number of "
-                 << "components as the first member";
-        }
+      if (!_.IsIntScalarOrVectorType(exp_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Exp to be a 32-bit int scalar "
+               << "or vector type";
+      }
 
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        if (x_type != result_types[0]) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X type to be equal to the first member "
-                 << "of Result Type struct";
-        }
-        break;
+      if (_.GetDimension(result_type) != _.GetDimension(exp_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Exp to have the same component "
+               << "number as Result Type";
       }
 
-      case GLSLstd450PackSnorm4x8:
-      case GLSLstd450PackUnorm4x8: {
-        if (!_.IsIntScalarType(result_type) ||
-            _.GetBitWidth(result_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be 32-bit int scalar type";
-        }
+      break;
+    }
 
-        const uint32_t v_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 4 ||
-            _.GetBitWidth(v_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand V to be a 32-bit float vector of size 4";
-        }
-        break;
+    case GLSLstd450FrexpStruct: {
+      std::vector<uint32_t> result_types;
+      if (!_.GetStructMemberTypes(result_type, &result_types) ||
+          result_types.size() != 2 ||
+          !_.IsFloatScalarOrVectorType(result_types[0]) ||
+          !_.IsIntScalarOrVectorType(result_types[1]) ||
+          (!_.HasExtension(kSPV_AMD_gpu_shader_int16) &&
+           _.GetBitWidth(result_types[1]) != 32) ||
+          (_.HasExtension(kSPV_AMD_gpu_shader_int16) &&
+           _.GetBitWidth(result_types[1]) != 16 &&
+           _.GetBitWidth(result_types[1]) != 32) ||
+          _.GetDimension(result_types[0]) != _.GetDimension(result_types[1])) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a struct with two members, "
+               << "first member a float scalar or vector, second member a "
+               << (_.HasExtension(kSPV_AMD_gpu_shader_int16)
+                       ? "16-bit or 32-bit "
+                       : "32-bit ")
+               << "int scalar or vector with the same number of "
+               << "components as the first member";
       }
 
-      case GLSLstd450PackSnorm2x16:
-      case GLSLstd450PackUnorm2x16:
-      case GLSLstd450PackHalf2x16: {
-        if (!_.IsIntScalarType(result_type) ||
-            _.GetBitWidth(result_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be 32-bit int scalar type";
-        }
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      if (x_type != result_types[0]) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X type to be equal to the first member "
+               << "of Result Type struct";
+      }
+      break;
+    }
 
-        const uint32_t v_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 2 ||
-            _.GetBitWidth(v_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand V to be a 32-bit float vector of size 2";
-        }
-        break;
+    case GLSLstd450PackSnorm4x8:
+    case GLSLstd450PackUnorm4x8: {
+      if (!_.IsIntScalarType(result_type) || _.GetBitWidth(result_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be 32-bit int scalar type";
       }
 
-      case GLSLstd450PackDouble2x32: {
-        if (!_.IsFloatScalarType(result_type) ||
-            _.GetBitWidth(result_type) != 64) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be 64-bit float scalar type";
-        }
+      const uint32_t v_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 4 ||
+          _.GetBitWidth(v_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand V to be a 32-bit float vector of size 4";
+      }
+      break;
+    }
 
-        const uint32_t v_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsIntVectorType(v_type) || _.GetDimension(v_type) != 2 ||
-            _.GetBitWidth(v_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand V to be a 32-bit int vector of size 2";
-        }
-        break;
+    case GLSLstd450PackSnorm2x16:
+    case GLSLstd450PackUnorm2x16:
+    case GLSLstd450PackHalf2x16: {
+      if (!_.IsIntScalarType(result_type) || _.GetBitWidth(result_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be 32-bit int scalar type";
       }
 
-      case GLSLstd450UnpackSnorm4x8:
-      case GLSLstd450UnpackUnorm4x8: {
-        if (!_.IsFloatVectorType(result_type) ||
-            _.GetDimension(result_type) != 4 ||
-            _.GetBitWidth(result_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a 32-bit float vector of size "
-                    "4";
-        }
+      const uint32_t v_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 2 ||
+          _.GetBitWidth(v_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand V to be a 32-bit float vector of size 2";
+      }
+      break;
+    }
 
-        const uint32_t v_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsIntScalarType(v_type) || _.GetBitWidth(v_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P to be a 32-bit int scalar";
-        }
-        break;
+    case GLSLstd450PackDouble2x32: {
+      if (!_.IsFloatScalarType(result_type) ||
+          _.GetBitWidth(result_type) != 64) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be 64-bit float scalar type";
       }
 
-      case GLSLstd450UnpackSnorm2x16:
-      case GLSLstd450UnpackUnorm2x16:
-      case GLSLstd450UnpackHalf2x16: {
-        if (!_.IsFloatVectorType(result_type) ||
-            _.GetDimension(result_type) != 2 ||
-            _.GetBitWidth(result_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a 32-bit float vector of size "
-                    "2";
-        }
+      const uint32_t v_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsIntVectorType(v_type) || _.GetDimension(v_type) != 2 ||
+          _.GetBitWidth(v_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand V to be a 32-bit int vector of size 2";
+      }
+      break;
+    }
 
-        const uint32_t v_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsIntScalarType(v_type) || _.GetBitWidth(v_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P to be a 32-bit int scalar";
-        }
-        break;
+    case GLSLstd450UnpackSnorm4x8:
+    case GLSLstd450UnpackUnorm4x8: {
+      if (!_.IsFloatVectorType(result_type) ||
+          _.GetDimension(result_type) != 4 ||
+          _.GetBitWidth(result_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a 32-bit float vector of size "
+                  "4";
       }
 
-      case GLSLstd450UnpackDouble2x32: {
-        if (!_.IsIntVectorType(result_type) ||
-            _.GetDimension(result_type) != 2 ||
-            _.GetBitWidth(result_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a 32-bit int vector of size "
-                    "2";
-        }
+      const uint32_t v_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsIntScalarType(v_type) || _.GetBitWidth(v_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P to be a 32-bit int scalar";
+      }
+      break;
+    }
 
-        const uint32_t v_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsFloatScalarType(v_type) || _.GetBitWidth(v_type) != 64) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand V to be a 64-bit float scalar";
-        }
-        break;
+    case GLSLstd450UnpackSnorm2x16:
+    case GLSLstd450UnpackUnorm2x16:
+    case GLSLstd450UnpackHalf2x16: {
+      if (!_.IsFloatVectorType(result_type) ||
+          _.GetDimension(result_type) != 2 ||
+          _.GetBitWidth(result_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a 32-bit float vector of size "
+                  "2";
       }
 
-      case GLSLstd450Length: {
-        if (!_.IsFloatScalarType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar type";
-        }
+      const uint32_t v_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsIntScalarType(v_type) || _.GetBitWidth(v_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P to be a 32-bit int scalar";
+      }
+      break;
+    }
 
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsFloatScalarOrVectorType(x_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X to be of float scalar or vector type";
-        }
+    case GLSLstd450UnpackDouble2x32: {
+      if (!_.IsIntVectorType(result_type) || _.GetDimension(result_type) != 2 ||
+          _.GetBitWidth(result_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a 32-bit int vector of size "
+                  "2";
+      }
 
-        if (result_type != _.GetComponentType(x_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X component type to be equal to Result "
-                    "Type";
-        }
-        break;
+      const uint32_t v_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsFloatScalarType(v_type) || _.GetBitWidth(v_type) != 64) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand V to be a 64-bit float scalar";
       }
+      break;
+    }
 
-      case GLSLstd450Distance: {
-        if (!_.IsFloatScalarType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar type";
-        }
+    case GLSLstd450Length: {
+      if (!_.IsFloatScalarType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar type";
+      }
 
-        const uint32_t p0_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsFloatScalarOrVectorType(p0_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P0 to be of float scalar or vector type";
-        }
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsFloatScalarOrVectorType(x_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X to be of float scalar or vector type";
+      }
 
-        if (result_type != _.GetComponentType(p0_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P0 component type to be equal to "
-                 << "Result Type";
-        }
+      if (result_type != _.GetComponentType(x_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X component type to be equal to Result "
+                  "Type";
+      }
+      break;
+    }
 
-        const uint32_t p1_type = _.GetOperandTypeId(inst, 5);
-        if (!_.IsFloatScalarOrVectorType(p1_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P1 to be of float scalar or vector type";
-        }
+    case GLSLstd450Distance: {
+      if (!_.IsFloatScalarType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar type";
+      }
 
-        if (result_type != _.GetComponentType(p1_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P1 component type to be equal to "
-                 << "Result Type";
-        }
+      const uint32_t p0_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsFloatScalarOrVectorType(p0_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P0 to be of float scalar or vector type";
+      }
 
-        if (_.GetDimension(p0_type) != _.GetDimension(p1_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operands P0 and P1 to have the same number of "
-                 << "components";
-        }
-        break;
+      if (result_type != _.GetComponentType(p0_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P0 component type to be equal to "
+               << "Result Type";
       }
 
-      case GLSLstd450Cross: {
-        if (!_.IsFloatVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float vector type";
-        }
+      const uint32_t p1_type = _.GetOperandTypeId(inst, 5);
+      if (!_.IsFloatScalarOrVectorType(p1_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P1 to be of float scalar or vector type";
+      }
 
-        if (_.GetDimension(result_type) != 3) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to have 3 components";
-        }
+      if (result_type != _.GetComponentType(p1_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P1 component type to be equal to "
+               << "Result Type";
+      }
 
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t y_type = _.GetOperandTypeId(inst, 5);
+      if (_.GetDimension(p0_type) != _.GetDimension(p1_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operands P0 and P1 to have the same number of "
+               << "components";
+      }
+      break;
+    }
 
-        if (x_type != result_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X type to be equal to Result Type";
-        }
+    case GLSLstd450Cross: {
+      if (!_.IsFloatVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float vector type";
+      }
 
-        if (y_type != result_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Y type to be equal to Result Type";
-        }
-        break;
+      if (_.GetDimension(result_type) != 3) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to have 3 components";
       }
 
-      case GLSLstd450Refract: {
-        if (!_.IsFloatScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar or vector type";
-        }
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t y_type = _.GetOperandTypeId(inst, 5);
 
-        const uint32_t i_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t n_type = _.GetOperandTypeId(inst, 5);
-        const uint32_t eta_type = _.GetOperandTypeId(inst, 6);
+      if (x_type != result_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X type to be equal to Result Type";
+      }
 
-        if (result_type != i_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand I to be of type equal to Result Type";
-        }
+      if (y_type != result_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Y type to be equal to Result Type";
+      }
+      break;
+    }
 
-        if (result_type != n_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand N to be of type equal to Result Type";
-        }
+    case GLSLstd450Refract: {
+      if (!_.IsFloatScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar or vector type";
+      }
 
-        if (!_.IsFloatScalarType(eta_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Eta to be a float scalar";
-        }
-        break;
+      const uint32_t i_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t n_type = _.GetOperandTypeId(inst, 5);
+      const uint32_t eta_type = _.GetOperandTypeId(inst, 6);
+
+      if (result_type != i_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand I to be of type equal to Result Type";
       }
 
-      case GLSLstd450InterpolateAtCentroid:
-      case GLSLstd450InterpolateAtSample:
-      case GLSLstd450InterpolateAtOffset: {
-        if (!_.HasCapability(spv::Capability::InterpolationFunction)) {
-          return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
-                 << ext_inst_name()
-                 << " requires capability InterpolationFunction";
-        }
+      if (result_type != n_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand N to be of type equal to Result Type";
+      }
 
-        if (!_.IsFloatScalarOrVectorType(result_type) ||
-            _.GetBitWidth(result_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a 32-bit float scalar "
-                 << "or vector type";
-        }
+      if (!_.IsFloatScalarType(eta_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Eta to be a float scalar";
+      }
+      break;
+    }
+
+    case GLSLstd450InterpolateAtCentroid:
+    case GLSLstd450InterpolateAtSample:
+    case GLSLstd450InterpolateAtOffset: {
+      if (!_.HasCapability(spv::Capability::InterpolationFunction)) {
+        return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
+               << GetExtInstName(_, inst)
+               << " requires capability InterpolationFunction";
+      }
+
+      if (!_.IsFloatScalarOrVectorType(result_type) ||
+          _.GetBitWidth(result_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a 32-bit float scalar "
+               << "or vector type";
+      }
+
+      // If HLSL legalization and first operand is an OpLoad, use load
+      // pointer as the interpolant lvalue. Else use interpolate first
+      // operand.
+      uint32_t interp_id = inst->GetOperandAs<uint32_t>(4);
+      auto* interp_inst = _.FindDef(interp_id);
+      uint32_t interpolant_type = (_.options()->before_hlsl_legalization &&
+                                   interp_inst->opcode() == spv::Op::OpLoad)
+                                      ? _.GetOperandTypeId(interp_inst, 2)
+                                      : _.GetOperandTypeId(inst, 4);
+
+      spv::StorageClass interpolant_storage_class;
+      uint32_t interpolant_data_type = 0;
+      if (!_.GetPointerTypeInfo(interpolant_type, &interpolant_data_type,
+                                &interpolant_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Interpolant to be a pointer";
+      }
+
+      if (result_type != interpolant_data_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Interpolant data type to be equal to Result Type";
+      }
+
+      if (interpolant_storage_class != spv::StorageClass::Input) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Interpolant storage class to be Input";
+      }
 
-        // If HLSL legalization and first operand is an OpLoad, use load
-        // pointer as the interpolant lvalue. Else use interpolate first
-        // operand.
-        uint32_t interp_id = inst->GetOperandAs<uint32_t>(4);
-        auto* interp_inst = _.FindDef(interp_id);
-        uint32_t interpolant_type = (_.options()->before_hlsl_legalization &&
-                                     interp_inst->opcode() == spv::Op::OpLoad)
-                                        ? _.GetOperandTypeId(interp_inst, 2)
-                                        : _.GetOperandTypeId(inst, 4);
-
-        spv::StorageClass interpolant_storage_class;
-        uint32_t interpolant_data_type = 0;
-        if (!_.GetPointerTypeInfo(interpolant_type, &interpolant_data_type,
-                                  &interpolant_storage_class)) {
+      if (ext_inst_key == GLSLstd450InterpolateAtSample) {
+        const uint32_t sample_type = _.GetOperandTypeId(inst, 5);
+        if (!_.IsIntScalarType(sample_type) ||
+            _.GetBitWidth(sample_type) != 32) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Interpolant to be a pointer";
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected Sample to be 32-bit integer";
         }
+      }
 
-        if (result_type != interpolant_data_type) {
+      if (ext_inst_key == GLSLstd450InterpolateAtOffset) {
+        const uint32_t offset_type = _.GetOperandTypeId(inst, 5);
+        if (!_.IsFloatVectorType(offset_type) ||
+            _.GetDimension(offset_type) != 2 ||
+            _.GetBitWidth(offset_type) != 32) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Interpolant data type to be equal to Result Type";
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected Offset to be a vector of 2 32-bit floats";
         }
+      }
+
+      _.function(inst->function()->id())
+          ->RegisterExecutionModelLimitation(
+              spv::ExecutionModel::Fragment,
+              GetExtInstName(_, inst) +
+                  std::string(" requires Fragment execution model"));
+      break;
+    }
+
+    case GLSLstd450IMix: {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Extended instruction GLSLstd450IMix is not supported";
+    }
+
+    case GLSLstd450Bad: {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Encountered extended instruction GLSLstd450Bad";
+    }
+
+    case GLSLstd450Count: {
+      assert(0);
+      break;
+    }
+  }
+
+  return SPV_SUCCESS;
+}
 
-        if (interpolant_storage_class != spv::StorageClass::Input) {
+spv_result_t ValidateExtInstOpenClStd(ValidationState_t& _,
+                                      const Instruction* inst) {
+  const uint32_t result_type = inst->type_id();
+  const uint32_t num_operands = static_cast<uint32_t>(inst->operands().size());
+  const uint32_t ext_inst_index = inst->word(4);
+  const OpenCLLIB::Entrypoints ext_inst_key =
+      OpenCLLIB::Entrypoints(ext_inst_index);
+  switch (ext_inst_key) {
+    case OpenCLLIB::Acos:
+    case OpenCLLIB::Acosh:
+    case OpenCLLIB::Acospi:
+    case OpenCLLIB::Asin:
+    case OpenCLLIB::Asinh:
+    case OpenCLLIB::Asinpi:
+    case OpenCLLIB::Atan:
+    case OpenCLLIB::Atan2:
+    case OpenCLLIB::Atanh:
+    case OpenCLLIB::Atanpi:
+    case OpenCLLIB::Atan2pi:
+    case OpenCLLIB::Cbrt:
+    case OpenCLLIB::Ceil:
+    case OpenCLLIB::Copysign:
+    case OpenCLLIB::Cos:
+    case OpenCLLIB::Cosh:
+    case OpenCLLIB::Cospi:
+    case OpenCLLIB::Erfc:
+    case OpenCLLIB::Erf:
+    case OpenCLLIB::Exp:
+    case OpenCLLIB::Exp2:
+    case OpenCLLIB::Exp10:
+    case OpenCLLIB::Expm1:
+    case OpenCLLIB::Fabs:
+    case OpenCLLIB::Fdim:
+    case OpenCLLIB::Floor:
+    case OpenCLLIB::Fma:
+    case OpenCLLIB::Fmax:
+    case OpenCLLIB::Fmin:
+    case OpenCLLIB::Fmod:
+    case OpenCLLIB::Hypot:
+    case OpenCLLIB::Lgamma:
+    case OpenCLLIB::Log:
+    case OpenCLLIB::Log2:
+    case OpenCLLIB::Log10:
+    case OpenCLLIB::Log1p:
+    case OpenCLLIB::Logb:
+    case OpenCLLIB::Mad:
+    case OpenCLLIB::Maxmag:
+    case OpenCLLIB::Minmag:
+    case OpenCLLIB::Nextafter:
+    case OpenCLLIB::Pow:
+    case OpenCLLIB::Powr:
+    case OpenCLLIB::Remainder:
+    case OpenCLLIB::Rint:
+    case OpenCLLIB::Round:
+    case OpenCLLIB::Rsqrt:
+    case OpenCLLIB::Sin:
+    case OpenCLLIB::Sinh:
+    case OpenCLLIB::Sinpi:
+    case OpenCLLIB::Sqrt:
+    case OpenCLLIB::Tan:
+    case OpenCLLIB::Tanh:
+    case OpenCLLIB::Tanpi:
+    case OpenCLLIB::Tgamma:
+    case OpenCLLIB::Trunc:
+    case OpenCLLIB::Half_cos:
+    case OpenCLLIB::Half_divide:
+    case OpenCLLIB::Half_exp:
+    case OpenCLLIB::Half_exp2:
+    case OpenCLLIB::Half_exp10:
+    case OpenCLLIB::Half_log:
+    case OpenCLLIB::Half_log2:
+    case OpenCLLIB::Half_log10:
+    case OpenCLLIB::Half_powr:
+    case OpenCLLIB::Half_recip:
+    case OpenCLLIB::Half_rsqrt:
+    case OpenCLLIB::Half_sin:
+    case OpenCLLIB::Half_sqrt:
+    case OpenCLLIB::Half_tan:
+    case OpenCLLIB::Native_cos:
+    case OpenCLLIB::Native_divide:
+    case OpenCLLIB::Native_exp:
+    case OpenCLLIB::Native_exp2:
+    case OpenCLLIB::Native_exp10:
+    case OpenCLLIB::Native_log:
+    case OpenCLLIB::Native_log2:
+    case OpenCLLIB::Native_log10:
+    case OpenCLLIB::Native_powr:
+    case OpenCLLIB::Native_recip:
+    case OpenCLLIB::Native_rsqrt:
+    case OpenCLLIB::Native_sin:
+    case OpenCLLIB::Native_sqrt:
+    case OpenCLLIB::Native_tan:
+    case OpenCLLIB::FClamp:
+    case OpenCLLIB::Degrees:
+    case OpenCLLIB::FMax_common:
+    case OpenCLLIB::FMin_common:
+    case OpenCLLIB::Mix:
+    case OpenCLLIB::Radians:
+    case OpenCLLIB::Step:
+    case OpenCLLIB::Smoothstep:
+    case OpenCLLIB::Sign: {
+      if (!_.IsFloatScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar or vector type";
+      }
+
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
+      }
+
+      for (uint32_t operand_index = 4; operand_index < num_operands;
+           ++operand_index) {
+        const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
+        if (result_type != operand_type) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Interpolant storage class to be Input";
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected types of all operands to be equal to Result "
+                    "Type";
         }
+      }
+      break;
+    }
 
-        if (ext_inst_key == GLSLstd450InterpolateAtSample) {
-          const uint32_t sample_type = _.GetOperandTypeId(inst, 5);
-          if (!_.IsIntScalarType(sample_type) ||
-              _.GetBitWidth(sample_type) != 32) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected Sample to be 32-bit integer";
-          }
-        }
+    case OpenCLLIB::Fract:
+    case OpenCLLIB::Modf:
+    case OpenCLLIB::Sincos: {
+      if (!_.IsFloatScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar or vector type";
+      }
 
-        if (ext_inst_key == GLSLstd450InterpolateAtOffset) {
-          const uint32_t offset_type = _.GetOperandTypeId(inst, 5);
-          if (!_.IsFloatVectorType(offset_type) ||
-              _.GetDimension(offset_type) != 2 ||
-              _.GetBitWidth(offset_type) != 32) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected Offset to be a vector of 2 32-bit floats";
-          }
-        }
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
+      }
 
-        _.function(inst->function()->id())
-            ->RegisterExecutionModelLimitation(
-                spv::ExecutionModel::Fragment,
-                ext_inst_name() +
-                    std::string(" requires Fragment execution model"));
-        break;
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      if (result_type != x_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected type of operand X to be equal to Result Type";
       }
 
-      case GLSLstd450IMix: {
+      const uint32_t p_type = _.GetOperandTypeId(inst, 5);
+      spv::StorageClass p_storage_class;
+      uint32_t p_data_type = 0;
+      if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Extended instruction GLSLstd450IMix is not supported";
+               << GetExtInstName(_, inst) << ": "
+               << "expected the last operand to be a pointer";
       }
 
-      case GLSLstd450Bad: {
+      if (p_storage_class != spv::StorageClass::Generic &&
+          p_storage_class != spv::StorageClass::CrossWorkgroup &&
+          p_storage_class != spv::StorageClass::Workgroup &&
+          p_storage_class != spv::StorageClass::Function) {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << "Encountered extended instruction GLSLstd450Bad";
+               << GetExtInstName(_, inst) << ": "
+               << "expected storage class of the pointer to be Generic, "
+                  "CrossWorkgroup, Workgroup or Function";
       }
 
-      case GLSLstd450Count: {
-        assert(0);
-        break;
+      if (!_.ContainsUntypedPointer(p_type) && result_type != p_data_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected data type of the pointer to be equal to Result "
+                  "Type";
       }
+      break;
     }
-  } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_STD) {
-    const OpenCLLIB::Entrypoints ext_inst_key =
-        OpenCLLIB::Entrypoints(ext_inst_index);
-    switch (ext_inst_key) {
-      case OpenCLLIB::Acos:
-      case OpenCLLIB::Acosh:
-      case OpenCLLIB::Acospi:
-      case OpenCLLIB::Asin:
-      case OpenCLLIB::Asinh:
-      case OpenCLLIB::Asinpi:
-      case OpenCLLIB::Atan:
-      case OpenCLLIB::Atan2:
-      case OpenCLLIB::Atanh:
-      case OpenCLLIB::Atanpi:
-      case OpenCLLIB::Atan2pi:
-      case OpenCLLIB::Cbrt:
-      case OpenCLLIB::Ceil:
-      case OpenCLLIB::Copysign:
-      case OpenCLLIB::Cos:
-      case OpenCLLIB::Cosh:
-      case OpenCLLIB::Cospi:
-      case OpenCLLIB::Erfc:
-      case OpenCLLIB::Erf:
-      case OpenCLLIB::Exp:
-      case OpenCLLIB::Exp2:
-      case OpenCLLIB::Exp10:
-      case OpenCLLIB::Expm1:
-      case OpenCLLIB::Fabs:
-      case OpenCLLIB::Fdim:
-      case OpenCLLIB::Floor:
-      case OpenCLLIB::Fma:
-      case OpenCLLIB::Fmax:
-      case OpenCLLIB::Fmin:
-      case OpenCLLIB::Fmod:
-      case OpenCLLIB::Hypot:
-      case OpenCLLIB::Lgamma:
-      case OpenCLLIB::Log:
-      case OpenCLLIB::Log2:
-      case OpenCLLIB::Log10:
-      case OpenCLLIB::Log1p:
-      case OpenCLLIB::Logb:
-      case OpenCLLIB::Mad:
-      case OpenCLLIB::Maxmag:
-      case OpenCLLIB::Minmag:
-      case OpenCLLIB::Nextafter:
-      case OpenCLLIB::Pow:
-      case OpenCLLIB::Powr:
-      case OpenCLLIB::Remainder:
-      case OpenCLLIB::Rint:
-      case OpenCLLIB::Round:
-      case OpenCLLIB::Rsqrt:
-      case OpenCLLIB::Sin:
-      case OpenCLLIB::Sinh:
-      case OpenCLLIB::Sinpi:
-      case OpenCLLIB::Sqrt:
-      case OpenCLLIB::Tan:
-      case OpenCLLIB::Tanh:
-      case OpenCLLIB::Tanpi:
-      case OpenCLLIB::Tgamma:
-      case OpenCLLIB::Trunc:
-      case OpenCLLIB::Half_cos:
-      case OpenCLLIB::Half_divide:
-      case OpenCLLIB::Half_exp:
-      case OpenCLLIB::Half_exp2:
-      case OpenCLLIB::Half_exp10:
-      case OpenCLLIB::Half_log:
-      case OpenCLLIB::Half_log2:
-      case OpenCLLIB::Half_log10:
-      case OpenCLLIB::Half_powr:
-      case OpenCLLIB::Half_recip:
-      case OpenCLLIB::Half_rsqrt:
-      case OpenCLLIB::Half_sin:
-      case OpenCLLIB::Half_sqrt:
-      case OpenCLLIB::Half_tan:
-      case OpenCLLIB::Native_cos:
-      case OpenCLLIB::Native_divide:
-      case OpenCLLIB::Native_exp:
-      case OpenCLLIB::Native_exp2:
-      case OpenCLLIB::Native_exp10:
-      case OpenCLLIB::Native_log:
-      case OpenCLLIB::Native_log2:
-      case OpenCLLIB::Native_log10:
-      case OpenCLLIB::Native_powr:
-      case OpenCLLIB::Native_recip:
-      case OpenCLLIB::Native_rsqrt:
-      case OpenCLLIB::Native_sin:
-      case OpenCLLIB::Native_sqrt:
-      case OpenCLLIB::Native_tan:
-      case OpenCLLIB::FClamp:
-      case OpenCLLIB::Degrees:
-      case OpenCLLIB::FMax_common:
-      case OpenCLLIB::FMin_common:
-      case OpenCLLIB::Mix:
-      case OpenCLLIB::Radians:
-      case OpenCLLIB::Step:
-      case OpenCLLIB::Smoothstep:
-      case OpenCLLIB::Sign: {
-        if (!_.IsFloatScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar or vector type";
-        }
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
-        }
+    case OpenCLLIB::Frexp:
+    case OpenCLLIB::Lgamma_r:
+    case OpenCLLIB::Remquo: {
+      if (!_.IsFloatScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar or vector type";
+      }
 
-        for (uint32_t operand_index = 4; operand_index < num_operands;
-             ++operand_index) {
-          const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
-          if (result_type != operand_type) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected types of all operands to be equal to Result "
-                      "Type";
-          }
-        }
-        break;
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
       }
 
-      case OpenCLLIB::Fract:
-      case OpenCLLIB::Modf:
-      case OpenCLLIB::Sincos: {
-        if (!_.IsFloatScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar or vector type";
-        }
+      uint32_t operand_index = 4;
+      const uint32_t x_type = _.GetOperandTypeId(inst, operand_index++);
+      if (result_type != x_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected type of operand X to be equal to Result Type";
+      }
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
+      if (ext_inst_key == OpenCLLIB::Remquo) {
+        const uint32_t y_type = _.GetOperandTypeId(inst, operand_index++);
+        if (result_type != y_type) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected type of operand Y to be equal to Result Type";
         }
+      }
 
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        if (result_type != x_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected type of operand X to be equal to Result Type";
-        }
+      const uint32_t p_type = _.GetOperandTypeId(inst, operand_index++);
+      spv::StorageClass p_storage_class;
+      uint32_t p_data_type = 0;
+      if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected the last operand to be a pointer";
+      }
 
-        const uint32_t p_type = _.GetOperandTypeId(inst, 5);
-        spv::StorageClass p_storage_class;
-        uint32_t p_data_type = 0;
-        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected the last operand to be a pointer";
-        }
+      if (p_storage_class != spv::StorageClass::Generic &&
+          p_storage_class != spv::StorageClass::CrossWorkgroup &&
+          p_storage_class != spv::StorageClass::Workgroup &&
+          p_storage_class != spv::StorageClass::Function) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected storage class of the pointer to be Generic, "
+                  "CrossWorkgroup, Workgroup or Function";
+      }
 
-        if (p_storage_class != spv::StorageClass::Generic &&
-            p_storage_class != spv::StorageClass::CrossWorkgroup &&
-            p_storage_class != spv::StorageClass::Workgroup &&
-            p_storage_class != spv::StorageClass::Function) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected storage class of the pointer to be Generic, "
-                    "CrossWorkgroup, Workgroup or Function";
-        }
+      if ((!_.IsIntScalarOrVectorType(p_data_type) ||
+           _.GetBitWidth(p_data_type) != 32) &&
+          !_.ContainsUntypedPointer(p_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected data type of the pointer to be a 32-bit int "
+                  "scalar or vector type";
+      }
 
-        if (!_.ContainsUntypedPointer(p_type) && result_type != p_data_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected data type of the pointer to be equal to Result "
-                    "Type";
-        }
-        break;
+      if (!_.ContainsUntypedPointer(p_type) &&
+          _.GetDimension(p_data_type) != num_components) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected data type of the pointer to have the same number "
+                  "of components as Result Type";
       }
+      break;
+    }
 
-      case OpenCLLIB::Frexp:
-      case OpenCLLIB::Lgamma_r:
-      case OpenCLLIB::Remquo: {
-        if (!_.IsFloatScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar or vector type";
-        }
+    case OpenCLLIB::Ilogb: {
+      if (!_.IsIntScalarOrVectorType(result_type) ||
+          _.GetBitWidth(result_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a 32-bit int scalar or vector "
+                  "type";
+      }
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
-        }
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
+      }
 
-        uint32_t operand_index = 4;
-        const uint32_t x_type = _.GetOperandTypeId(inst, operand_index++);
-        if (result_type != x_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected type of operand X to be equal to Result Type";
-        }
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsFloatScalarOrVectorType(x_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X to be a float scalar or vector";
+      }
 
-        if (ext_inst_key == OpenCLLIB::Remquo) {
-          const uint32_t y_type = _.GetOperandTypeId(inst, operand_index++);
-          if (result_type != y_type) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected type of operand Y to be equal to Result Type";
-          }
-        }
+      if (_.GetDimension(x_type) != num_components) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X to have the same number of components "
+                  "as Result Type";
+      }
+      break;
+    }
 
-        const uint32_t p_type = _.GetOperandTypeId(inst, operand_index++);
-        spv::StorageClass p_storage_class;
-        uint32_t p_data_type = 0;
-        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected the last operand to be a pointer";
-        }
+    case OpenCLLIB::Ldexp:
+    case OpenCLLIB::Pown:
+    case OpenCLLIB::Rootn: {
+      if (!_.IsFloatScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar or vector type";
+      }
 
-        if (p_storage_class != spv::StorageClass::Generic &&
-            p_storage_class != spv::StorageClass::CrossWorkgroup &&
-            p_storage_class != spv::StorageClass::Workgroup &&
-            p_storage_class != spv::StorageClass::Function) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected storage class of the pointer to be Generic, "
-                    "CrossWorkgroup, Workgroup or Function";
-        }
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
+      }
 
-        if ((!_.IsIntScalarOrVectorType(p_data_type) ||
-             _.GetBitWidth(p_data_type) != 32) &&
-            !_.ContainsUntypedPointer(p_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected data type of the pointer to be a 32-bit int "
-                    "scalar or vector type";
-        }
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      if (result_type != x_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected type of operand X to be equal to Result Type";
+      }
 
-        if (!_.ContainsUntypedPointer(p_type) &&
-            _.GetDimension(p_data_type) != num_components) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected data type of the pointer to have the same number "
-                    "of components as Result Type";
-        }
-        break;
+      const uint32_t exp_type = _.GetOperandTypeId(inst, 5);
+      if (!_.IsIntScalarOrVectorType(exp_type) ||
+          _.GetBitWidth(exp_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected the exponent to be a 32-bit int scalar or vector";
       }
 
-      case OpenCLLIB::Ilogb: {
-        if (!_.IsIntScalarOrVectorType(result_type) ||
-            _.GetBitWidth(result_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a 32-bit int scalar or vector "
-                    "type";
-        }
+      if (_.GetDimension(exp_type) != num_components) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected the exponent to have the same number of "
+                  "components as Result Type";
+      }
+      break;
+    }
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
-        }
+    case OpenCLLIB::Nan: {
+      if (!_.IsFloatScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar or vector type";
+      }
 
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsFloatScalarOrVectorType(x_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X to be a float scalar or vector";
-        }
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
+      }
 
-        if (_.GetDimension(x_type) != num_components) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X to have the same number of components "
-                    "as Result Type";
-        }
-        break;
+      const uint32_t nancode_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsIntScalarOrVectorType(nancode_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Nancode to be an int scalar or vector type";
       }
 
-      case OpenCLLIB::Ldexp:
-      case OpenCLLIB::Pown:
-      case OpenCLLIB::Rootn: {
-        if (!_.IsFloatScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar or vector type";
-        }
+      if (_.GetDimension(nancode_type) != num_components) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Nancode to have the same number of components as "
+                  "Result Type";
+      }
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
-        }
+      if (_.GetBitWidth(result_type) != _.GetBitWidth(nancode_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Nancode to have the same bit width as Result "
+                  "Type";
+      }
+      break;
+    }
 
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        if (result_type != x_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected type of operand X to be equal to Result Type";
-        }
+    case OpenCLLIB::SAbs:
+    case OpenCLLIB::SAbs_diff:
+    case OpenCLLIB::SAdd_sat:
+    case OpenCLLIB::UAdd_sat:
+    case OpenCLLIB::SHadd:
+    case OpenCLLIB::UHadd:
+    case OpenCLLIB::SRhadd:
+    case OpenCLLIB::URhadd:
+    case OpenCLLIB::SClamp:
+    case OpenCLLIB::UClamp:
+    case OpenCLLIB::Clz:
+    case OpenCLLIB::Ctz:
+    case OpenCLLIB::SMad_hi:
+    case OpenCLLIB::UMad_sat:
+    case OpenCLLIB::SMad_sat:
+    case OpenCLLIB::SMax:
+    case OpenCLLIB::UMax:
+    case OpenCLLIB::SMin:
+    case OpenCLLIB::UMin:
+    case OpenCLLIB::SMul_hi:
+    case OpenCLLIB::Rotate:
+    case OpenCLLIB::SSub_sat:
+    case OpenCLLIB::USub_sat:
+    case OpenCLLIB::Popcount:
+    case OpenCLLIB::UAbs:
+    case OpenCLLIB::UAbs_diff:
+    case OpenCLLIB::UMul_hi:
+    case OpenCLLIB::UMad_hi: {
+      if (!_.IsIntScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be an int scalar or vector type";
+      }
 
-        const uint32_t exp_type = _.GetOperandTypeId(inst, 5);
-        if (!_.IsIntScalarOrVectorType(exp_type) ||
-            _.GetBitWidth(exp_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected the exponent to be a 32-bit int scalar or vector";
-        }
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
+      }
 
-        if (_.GetDimension(exp_type) != num_components) {
+      for (uint32_t operand_index = 4; operand_index < num_operands;
+           ++operand_index) {
+        const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
+        if (result_type != operand_type) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected the exponent to have the same number of "
-                    "components as Result Type";
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected types of all operands to be equal to Result "
+                    "Type";
         }
-        break;
+      }
+      break;
+    }
+
+    case OpenCLLIB::U_Upsample:
+    case OpenCLLIB::S_Upsample: {
+      if (!_.IsIntScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be an int scalar or vector "
+                  "type";
       }
 
-      case OpenCLLIB::Nan: {
-        if (!_.IsFloatScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar or vector type";
-        }
+      const uint32_t result_num_components = _.GetDimension(result_type);
+      if (result_num_components > 4 && result_num_components != 8 &&
+          result_num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
+      }
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
-        }
+      const uint32_t result_bit_width = _.GetBitWidth(result_type);
+      if (result_bit_width != 16 && result_bit_width != 32 &&
+          result_bit_width != 64) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected bit width of Result Type components to be 16, 32 "
+                  "or 64";
+      }
 
-        const uint32_t nancode_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsIntScalarOrVectorType(nancode_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Nancode to be an int scalar or vector type";
-        }
+      const uint32_t hi_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t lo_type = _.GetOperandTypeId(inst, 5);
 
-        if (_.GetDimension(nancode_type) != num_components) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Nancode to have the same number of components as "
-                    "Result Type";
-        }
+      if (hi_type != lo_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Hi and Lo operands to have the same type";
+      }
+
+      if (result_num_components != _.GetDimension(hi_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Hi and Lo operands to have the same number of "
+                  "components as Result Type";
+      }
+
+      if (result_bit_width != 2 * _.GetBitWidth(hi_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected bit width of components of Hi and Lo operands to "
+                  "be half of the bit width of components of Result Type";
+      }
+      break;
+    }
+
+    case OpenCLLIB::SMad24:
+    case OpenCLLIB::UMad24:
+    case OpenCLLIB::SMul24:
+    case OpenCLLIB::UMul24: {
+      if (!_.IsIntScalarOrVectorType(result_type) ||
+          _.GetBitWidth(result_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a 32-bit int scalar or vector "
+                  "type";
+      }
+
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
+      }
 
-        if (_.GetBitWidth(result_type) != _.GetBitWidth(nancode_type)) {
+      for (uint32_t operand_index = 4; operand_index < num_operands;
+           ++operand_index) {
+        const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
+        if (result_type != operand_type) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Nancode to have the same bit width as Result "
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected types of all operands to be equal to Result "
                     "Type";
         }
-        break;
       }
+      break;
+    }
 
-      case OpenCLLIB::SAbs:
-      case OpenCLLIB::SAbs_diff:
-      case OpenCLLIB::SAdd_sat:
-      case OpenCLLIB::UAdd_sat:
-      case OpenCLLIB::SHadd:
-      case OpenCLLIB::UHadd:
-      case OpenCLLIB::SRhadd:
-      case OpenCLLIB::URhadd:
-      case OpenCLLIB::SClamp:
-      case OpenCLLIB::UClamp:
-      case OpenCLLIB::Clz:
-      case OpenCLLIB::Ctz:
-      case OpenCLLIB::SMad_hi:
-      case OpenCLLIB::UMad_sat:
-      case OpenCLLIB::SMad_sat:
-      case OpenCLLIB::SMax:
-      case OpenCLLIB::UMax:
-      case OpenCLLIB::SMin:
-      case OpenCLLIB::UMin:
-      case OpenCLLIB::SMul_hi:
-      case OpenCLLIB::Rotate:
-      case OpenCLLIB::SSub_sat:
-      case OpenCLLIB::USub_sat:
-      case OpenCLLIB::Popcount:
-      case OpenCLLIB::UAbs:
-      case OpenCLLIB::UAbs_diff:
-      case OpenCLLIB::UMul_hi:
-      case OpenCLLIB::UMad_hi: {
-        if (!_.IsIntScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be an int scalar or vector type";
-        }
+    case OpenCLLIB::Cross: {
+      if (!_.IsFloatVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float vector type";
+      }
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
-        }
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components != 3 && num_components != 4) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to have 3 or 4 components";
+      }
 
-        for (uint32_t operand_index = 4; operand_index < num_operands;
-             ++operand_index) {
-          const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
-          if (result_type != operand_type) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected types of all operands to be equal to Result "
-                      "Type";
-          }
-        }
-        break;
+      const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t y_type = _.GetOperandTypeId(inst, 5);
+
+      if (x_type != result_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X type to be equal to Result Type";
       }
 
-      case OpenCLLIB::U_Upsample:
-      case OpenCLLIB::S_Upsample: {
-        if (!_.IsIntScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be an int scalar or vector "
-                    "type";
-        }
+      if (y_type != result_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Y type to be equal to Result Type";
+      }
+      break;
+    }
 
-        const uint32_t result_num_components = _.GetDimension(result_type);
-        if (result_num_components > 4 && result_num_components != 8 &&
-            result_num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
-        }
+    case OpenCLLIB::Distance:
+    case OpenCLLIB::Fast_distance: {
+      if (!_.IsFloatScalarType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar type";
+      }
 
-        const uint32_t result_bit_width = _.GetBitWidth(result_type);
-        if (result_bit_width != 16 && result_bit_width != 32 &&
-            result_bit_width != 64) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected bit width of Result Type components to be 16, 32 "
-                    "or 64";
-        }
+      const uint32_t p0_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsFloatScalarOrVectorType(p0_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P0 to be of float scalar or vector type";
+      }
 
-        const uint32_t hi_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t lo_type = _.GetOperandTypeId(inst, 5);
+      const uint32_t num_components = _.GetDimension(p0_type);
+      if (num_components > 4) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P0 to have no more than 4 components";
+      }
 
-        if (hi_type != lo_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Hi and Lo operands to have the same type";
-        }
+      if (result_type != _.GetComponentType(p0_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P0 component type to be equal to "
+               << "Result Type";
+      }
 
-        if (result_num_components != _.GetDimension(hi_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Hi and Lo operands to have the same number of "
-                    "components as Result Type";
-        }
+      const uint32_t p1_type = _.GetOperandTypeId(inst, 5);
+      if (p0_type != p1_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operands P0 and P1 to be of the same type";
+      }
+      break;
+    }
 
-        if (result_bit_width != 2 * _.GetBitWidth(hi_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected bit width of components of Hi and Lo operands to "
-                    "be half of the bit width of components of Result Type";
-        }
-        break;
+    case OpenCLLIB::Length:
+    case OpenCLLIB::Fast_length: {
+      if (!_.IsFloatScalarType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar type";
       }
 
-      case OpenCLLIB::SMad24:
-      case OpenCLLIB::UMad24:
-      case OpenCLLIB::SMul24:
-      case OpenCLLIB::UMul24: {
-        if (!_.IsIntScalarOrVectorType(result_type) ||
-            _.GetBitWidth(result_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a 32-bit int scalar or vector "
-                    "type";
-        }
+      const uint32_t p_type = _.GetOperandTypeId(inst, 4);
+      if (!_.IsFloatScalarOrVectorType(p_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P to be a float scalar or vector";
+      }
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
-        }
+      const uint32_t num_components = _.GetDimension(p_type);
+      if (num_components > 4) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P to have no more than 4 components";
+      }
 
-        for (uint32_t operand_index = 4; operand_index < num_operands;
-             ++operand_index) {
-          const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
-          if (result_type != operand_type) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected types of all operands to be equal to Result "
-                      "Type";
-          }
-        }
-        break;
+      if (result_type != _.GetComponentType(p_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P component type to be equal to Result "
+                  "Type";
       }
+      break;
+    }
 
-      case OpenCLLIB::Cross: {
-        if (!_.IsFloatVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float vector type";
-        }
+    case OpenCLLIB::Normalize:
+    case OpenCLLIB::Fast_normalize: {
+      if (!_.IsFloatScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar or vector type";
+      }
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components != 3 && num_components != 4) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to have 3 or 4 components";
-        }
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to have no more than 4 components";
+      }
 
-        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t y_type = _.GetOperandTypeId(inst, 5);
+      const uint32_t p_type = _.GetOperandTypeId(inst, 4);
+      if (p_type != result_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P type to be equal to Result Type";
+      }
+      break;
+    }
 
-        if (x_type != result_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X type to be equal to Result Type";
-        }
+    case OpenCLLIB::Bitselect: {
+      if (!_.IsFloatScalarOrVectorType(result_type) &&
+          !_.IsIntScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be an int or float scalar or "
+                  "vector type";
+      }
 
-        if (y_type != result_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Y type to be equal to Result Type";
-        }
-        break;
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
       }
 
-      case OpenCLLIB::Distance:
-      case OpenCLLIB::Fast_distance: {
-        if (!_.IsFloatScalarType(result_type)) {
+      for (uint32_t operand_index = 4; operand_index < num_operands;
+           ++operand_index) {
+        const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
+        if (result_type != operand_type) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar type";
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected types of all operands to be equal to Result "
+                    "Type";
         }
+      }
+      break;
+    }
 
-        const uint32_t p0_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsFloatScalarOrVectorType(p0_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P0 to be of float scalar or vector type";
-        }
+    case OpenCLLIB::Select: {
+      if (!_.IsFloatScalarOrVectorType(result_type) &&
+          !_.IsIntScalarOrVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be an int or float scalar or "
+                  "vector type";
+      }
 
-        const uint32_t num_components = _.GetDimension(p0_type);
-        if (num_components > 4) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P0 to have no more than 4 components";
-        }
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
+      }
 
-        if (result_type != _.GetComponentType(p0_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P0 component type to be equal to "
-                 << "Result Type";
-        }
+      const uint32_t a_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t b_type = _.GetOperandTypeId(inst, 5);
+      const uint32_t c_type = _.GetOperandTypeId(inst, 6);
 
-        const uint32_t p1_type = _.GetOperandTypeId(inst, 5);
-        if (p0_type != p1_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operands P0 and P1 to be of the same type";
-        }
-        break;
+      if (result_type != a_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand A type to be equal to Result Type";
       }
 
-      case OpenCLLIB::Length:
-      case OpenCLLIB::Fast_length: {
-        if (!_.IsFloatScalarType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar type";
-        }
+      if (result_type != b_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand B type to be equal to Result Type";
+      }
+
+      if (!_.IsIntScalarOrVectorType(c_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand C to be an int scalar or vector";
+      }
 
-        const uint32_t p_type = _.GetOperandTypeId(inst, 4);
-        if (!_.IsFloatScalarOrVectorType(p_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P to be a float scalar or vector";
-        }
+      if (num_components != _.GetDimension(c_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand C to have the same number of components "
+                  "as Result Type";
+      }
 
-        const uint32_t num_components = _.GetDimension(p_type);
-        if (num_components > 4) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P to have no more than 4 components";
-        }
+      if (_.GetBitWidth(result_type) != _.GetBitWidth(c_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand C to have the same bit width as Result "
+                  "Type";
+      }
+      break;
+    }
 
-        if (result_type != _.GetComponentType(p_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P component type to be equal to Result "
-                    "Type";
-        }
-        break;
+    case OpenCLLIB::Vloadn: {
+      if (!_.IsFloatVectorType(result_type) &&
+          !_.IsIntVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be an int or float vector type";
       }
 
-      case OpenCLLIB::Normalize:
-      case OpenCLLIB::Fast_normalize: {
-        if (!_.IsFloatScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar or vector type";
-        }
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to have 2, 3, 4, 8 or 16 components";
+      }
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to have no more than 4 components";
-        }
+      const uint32_t offset_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t p_type = _.GetOperandTypeId(inst, 5);
 
-        const uint32_t p_type = _.GetOperandTypeId(inst, 4);
-        if (p_type != result_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P type to be equal to Result Type";
-        }
-        break;
+      const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
+      if (!size_t_bit_width) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst)
+               << " can only be used with physical addressing models";
       }
 
-      case OpenCLLIB::Bitselect: {
-        if (!_.IsFloatScalarOrVectorType(result_type) &&
-            !_.IsIntScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be an int or float scalar or "
-                    "vector type";
-        }
+      if (!_.IsIntScalarType(offset_type) ||
+          _.GetBitWidth(offset_type) != size_t_bit_width) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Offset to be of type size_t ("
+               << size_t_bit_width
+               << "-bit integer for the addressing model used in the module)";
+      }
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
-        }
+      spv::StorageClass p_storage_class;
+      uint32_t p_data_type = 0;
+      if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P to be a pointer";
+      }
 
-        for (uint32_t operand_index = 4; operand_index < num_operands;
-             ++operand_index) {
-          const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
-          if (result_type != operand_type) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected types of all operands to be equal to Result "
-                      "Type";
-          }
-        }
-        break;
+      if (p_storage_class != spv::StorageClass::UniformConstant &&
+          p_storage_class != spv::StorageClass::Generic &&
+          p_storage_class != spv::StorageClass::CrossWorkgroup &&
+          p_storage_class != spv::StorageClass::Workgroup &&
+          p_storage_class != spv::StorageClass::Function) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P storage class to be UniformConstant, "
+                  "Generic, CrossWorkgroup, Workgroup or Function";
       }
 
-      case OpenCLLIB::Select: {
-        if (!_.IsFloatScalarOrVectorType(result_type) &&
-            !_.IsIntScalarOrVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be an int or float scalar or "
-                    "vector type";
-        }
+      if (_.GetComponentType(result_type) != p_data_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P data type to be equal to component "
+                  "type of Result Type";
+      }
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
-        }
+      const uint32_t n_value = inst->word(7);
+      if (num_components != n_value) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected literal N to be equal to the number of "
+                  "components of Result Type";
+      }
+      break;
+    }
 
-        const uint32_t a_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t b_type = _.GetOperandTypeId(inst, 5);
-        const uint32_t c_type = _.GetOperandTypeId(inst, 6);
+    case OpenCLLIB::Vstoren: {
+      if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst)
+               << ": expected Result Type to be void";
+      }
 
-        if (result_type != a_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand A type to be equal to Result Type";
-        }
+      const uint32_t data_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t offset_type = _.GetOperandTypeId(inst, 5);
+      const uint32_t p_type = _.GetOperandTypeId(inst, 6);
 
-        if (result_type != b_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand B type to be equal to Result Type";
-        }
+      if (!_.IsFloatVectorType(data_type) && !_.IsIntVectorType(data_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Data to be an int or float vector";
+      }
 
-        if (!_.IsIntScalarOrVectorType(c_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand C to be an int scalar or vector";
-        }
+      const uint32_t num_components = _.GetDimension(data_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Data to have 2, 3, 4, 8 or 16 components";
+      }
 
-        if (num_components != _.GetDimension(c_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand C to have the same number of components "
-                    "as Result Type";
-        }
+      const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
+      if (!size_t_bit_width) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst)
+               << " can only be used with physical addressing models";
+      }
 
-        if (_.GetBitWidth(result_type) != _.GetBitWidth(c_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand C to have the same bit width as Result "
-                    "Type";
-        }
-        break;
+      if (!_.IsIntScalarType(offset_type) ||
+          _.GetBitWidth(offset_type) != size_t_bit_width) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Offset to be of type size_t ("
+               << size_t_bit_width
+               << "-bit integer for the addressing model used in the module)";
       }
 
-      case OpenCLLIB::Vloadn: {
-        if (!_.IsFloatVectorType(result_type) &&
-            !_.IsIntVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be an int or float vector type";
-        }
+      spv::StorageClass p_storage_class;
+      uint32_t p_data_type = 0;
+      if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P to be a pointer";
+      }
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to have 2, 3, 4, 8 or 16 components";
-        }
+      if (p_storage_class != spv::StorageClass::Generic &&
+          p_storage_class != spv::StorageClass::CrossWorkgroup &&
+          p_storage_class != spv::StorageClass::Workgroup &&
+          p_storage_class != spv::StorageClass::Function) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P storage class to be Generic, "
+                  "CrossWorkgroup, Workgroup or Function";
+      }
 
-        const uint32_t offset_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t p_type = _.GetOperandTypeId(inst, 5);
+      if (_.GetComponentType(data_type) != p_data_type) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P data type to be equal to the type of "
+                  "operand Data components";
+      }
+      break;
+    }
 
-        const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
-        if (!size_t_bit_width) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name()
-                 << " can only be used with physical addressing models";
-        }
+    case OpenCLLIB::Vload_half: {
+      if (!_.IsFloatScalarType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float scalar type";
+      }
 
-        if (!_.IsIntScalarType(offset_type) ||
-            _.GetBitWidth(offset_type) != size_t_bit_width) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Offset to be of type size_t ("
-                 << size_t_bit_width
-                 << "-bit integer for the addressing model used in the module)";
-        }
+      const uint32_t offset_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t p_type = _.GetOperandTypeId(inst, 5);
 
-        spv::StorageClass p_storage_class;
-        uint32_t p_data_type = 0;
-        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P to be a pointer";
-        }
+      const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
+      if (!size_t_bit_width) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst)
+               << " can only be used with physical addressing models";
+      }
 
-        if (p_storage_class != spv::StorageClass::UniformConstant &&
-            p_storage_class != spv::StorageClass::Generic &&
-            p_storage_class != spv::StorageClass::CrossWorkgroup &&
-            p_storage_class != spv::StorageClass::Workgroup &&
-            p_storage_class != spv::StorageClass::Function) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P storage class to be UniformConstant, "
-                    "Generic, CrossWorkgroup, Workgroup or Function";
-        }
+      if (!_.IsIntScalarType(offset_type) ||
+          _.GetBitWidth(offset_type) != size_t_bit_width) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Offset to be of type size_t ("
+               << size_t_bit_width
+               << "-bit integer for the addressing model used in the module)";
+      }
 
-        if (_.GetComponentType(result_type) != p_data_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P data type to be equal to component "
-                    "type of Result Type";
-        }
+      spv::StorageClass p_storage_class;
+      uint32_t p_data_type = 0;
+      if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P to be a pointer";
+      }
 
-        const uint32_t n_value = inst->word(7);
-        if (num_components != n_value) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected literal N to be equal to the number of "
-                    "components of Result Type";
-        }
-        break;
+      if (p_storage_class != spv::StorageClass::UniformConstant &&
+          p_storage_class != spv::StorageClass::Generic &&
+          p_storage_class != spv::StorageClass::CrossWorkgroup &&
+          p_storage_class != spv::StorageClass::Workgroup &&
+          p_storage_class != spv::StorageClass::Function) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P storage class to be UniformConstant, "
+                  "Generic, CrossWorkgroup, Workgroup or Function";
       }
 
-      case OpenCLLIB::Vstoren: {
-        if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": expected Result Type to be void";
-        }
+      if ((!_.IsFloatScalarType(p_data_type) ||
+           _.GetBitWidth(p_data_type) != 16) &&
+          !_.ContainsUntypedPointer(p_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P data type to be 16-bit float scalar";
+      }
+      break;
+    }
 
-        const uint32_t data_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t offset_type = _.GetOperandTypeId(inst, 5);
-        const uint32_t p_type = _.GetOperandTypeId(inst, 6);
+    case OpenCLLIB::Vload_halfn:
+    case OpenCLLIB::Vloada_halfn: {
+      if (!_.IsFloatVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a float vector type";
+      }
 
-        if (!_.IsFloatVectorType(data_type) && !_.IsIntVectorType(data_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Data to be an int or float vector";
-        }
+      const uint32_t num_components = _.GetDimension(result_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to have 2, 3, 4, 8 or 16 components";
+      }
 
-        const uint32_t num_components = _.GetDimension(data_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Data to have 2, 3, 4, 8 or 16 components";
-        }
+      const uint32_t offset_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t p_type = _.GetOperandTypeId(inst, 5);
 
-        const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
-        if (!size_t_bit_width) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name()
-                 << " can only be used with physical addressing models";
-        }
+      const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
+      if (!size_t_bit_width) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst)
+               << " can only be used with physical addressing models";
+      }
 
-        if (!_.IsIntScalarType(offset_type) ||
-            _.GetBitWidth(offset_type) != size_t_bit_width) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Offset to be of type size_t ("
-                 << size_t_bit_width
-                 << "-bit integer for the addressing model used in the module)";
-        }
+      if (!_.IsIntScalarType(offset_type) ||
+          _.GetBitWidth(offset_type) != size_t_bit_width) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Offset to be of type size_t ("
+               << size_t_bit_width
+               << "-bit integer for the addressing model used in the module)";
+      }
 
-        spv::StorageClass p_storage_class;
-        uint32_t p_data_type = 0;
-        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P to be a pointer";
-        }
+      spv::StorageClass p_storage_class;
+      uint32_t p_data_type = 0;
+      if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P to be a pointer";
+      }
 
-        if (p_storage_class != spv::StorageClass::Generic &&
-            p_storage_class != spv::StorageClass::CrossWorkgroup &&
-            p_storage_class != spv::StorageClass::Workgroup &&
-            p_storage_class != spv::StorageClass::Function) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P storage class to be Generic, "
-                    "CrossWorkgroup, Workgroup or Function";
-        }
+      if (p_storage_class != spv::StorageClass::UniformConstant &&
+          p_storage_class != spv::StorageClass::Generic &&
+          p_storage_class != spv::StorageClass::CrossWorkgroup &&
+          p_storage_class != spv::StorageClass::Workgroup &&
+          p_storage_class != spv::StorageClass::Function) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P storage class to be UniformConstant, "
+                  "Generic, CrossWorkgroup, Workgroup or Function";
+      }
 
-        if (_.GetComponentType(data_type) != p_data_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P data type to be equal to the type of "
-                    "operand Data components";
-        }
-        break;
+      if ((!_.IsFloatScalarType(p_data_type) ||
+           _.GetBitWidth(p_data_type) != 16) &&
+          !_.ContainsUntypedPointer(p_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P data type to be 16-bit float scalar";
       }
 
-      case OpenCLLIB::Vload_half: {
-        if (!_.IsFloatScalarType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float scalar type";
-        }
+      const uint32_t n_value = inst->word(7);
+      if (num_components != n_value) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected literal N to be equal to the number of "
+                  "components of Result Type";
+      }
+      break;
+    }
 
-        const uint32_t offset_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t p_type = _.GetOperandTypeId(inst, 5);
+    case OpenCLLIB::Vstore_half:
+    case OpenCLLIB::Vstore_half_r:
+    case OpenCLLIB::Vstore_halfn:
+    case OpenCLLIB::Vstore_halfn_r:
+    case OpenCLLIB::Vstorea_halfn:
+    case OpenCLLIB::Vstorea_halfn_r: {
+      if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst)
+               << ": expected Result Type to be void";
+      }
 
-        const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
-        if (!size_t_bit_width) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name()
-                 << " can only be used with physical addressing models";
-        }
+      const uint32_t data_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t offset_type = _.GetOperandTypeId(inst, 5);
+      const uint32_t p_type = _.GetOperandTypeId(inst, 6);
+      const uint32_t data_type_bit_width = _.GetBitWidth(data_type);
 
-        if (!_.IsIntScalarType(offset_type) ||
-            _.GetBitWidth(offset_type) != size_t_bit_width) {
+      if (ext_inst_key == OpenCLLIB::Vstore_half ||
+          ext_inst_key == OpenCLLIB::Vstore_half_r) {
+        if (!_.IsFloatScalarType(data_type) ||
+            (data_type_bit_width != 32 && data_type_bit_width != 64)) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Offset to be of type size_t ("
-                 << size_t_bit_width
-                 << "-bit integer for the addressing model used in the module)";
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected Data to be a 32 or 64-bit float scalar";
         }
-
-        spv::StorageClass p_storage_class;
-        uint32_t p_data_type = 0;
-        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+      } else {
+        if (!_.IsFloatVectorType(data_type) ||
+            (data_type_bit_width != 32 && data_type_bit_width != 64)) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P to be a pointer";
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected Data to be a 32 or 64-bit float vector";
         }
 
-        if (p_storage_class != spv::StorageClass::UniformConstant &&
-            p_storage_class != spv::StorageClass::Generic &&
-            p_storage_class != spv::StorageClass::CrossWorkgroup &&
-            p_storage_class != spv::StorageClass::Workgroup &&
-            p_storage_class != spv::StorageClass::Function) {
+        const uint32_t num_components = _.GetDimension(data_type);
+        if (num_components > 4 && num_components != 8 && num_components != 16) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P storage class to be UniformConstant, "
-                    "Generic, CrossWorkgroup, Workgroup or Function";
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected Data to have 2, 3, 4, 8 or 16 components";
         }
+      }
 
-        if ((!_.IsFloatScalarType(p_data_type) ||
-             _.GetBitWidth(p_data_type) != 16) &&
-            !_.ContainsUntypedPointer(p_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P data type to be 16-bit float scalar";
-        }
-        break;
+      const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
+      if (!size_t_bit_width) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst)
+               << " can only be used with physical addressing models";
       }
 
-      case OpenCLLIB::Vload_halfn:
-      case OpenCLLIB::Vloada_halfn: {
-        if (!_.IsFloatVectorType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a float vector type";
-        }
+      if (!_.IsIntScalarType(offset_type) ||
+          _.GetBitWidth(offset_type) != size_t_bit_width) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Offset to be of type size_t ("
+               << size_t_bit_width
+               << "-bit integer for the addressing model used in the module)";
+      }
 
-        const uint32_t num_components = _.GetDimension(result_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to have 2, 3, 4, 8 or 16 components";
-        }
+      spv::StorageClass p_storage_class;
+      uint32_t p_data_type = 0;
+      if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P to be a pointer";
+      }
 
-        const uint32_t offset_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t p_type = _.GetOperandTypeId(inst, 5);
+      if (p_storage_class != spv::StorageClass::Generic &&
+          p_storage_class != spv::StorageClass::CrossWorkgroup &&
+          p_storage_class != spv::StorageClass::Workgroup &&
+          p_storage_class != spv::StorageClass::Function) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P storage class to be Generic, "
+                  "CrossWorkgroup, Workgroup or Function";
+      }
 
-        const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
-        if (!size_t_bit_width) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name()
-                 << " can only be used with physical addressing models";
-        }
+      if ((!_.IsFloatScalarType(p_data_type) ||
+           _.GetBitWidth(p_data_type) != 16) &&
+          !_.ContainsUntypedPointer(p_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand P data type to be 16-bit float scalar";
+      }
 
-        if (!_.IsIntScalarType(offset_type) ||
-            _.GetBitWidth(offset_type) != size_t_bit_width) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Offset to be of type size_t ("
-                 << size_t_bit_width
-                 << "-bit integer for the addressing model used in the module)";
-        }
+      // Rounding mode enum is checked by assembler.
+      break;
+    }
 
-        spv::StorageClass p_storage_class;
-        uint32_t p_data_type = 0;
-        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P to be a pointer";
-        }
+    case OpenCLLIB::Shuffle:
+    case OpenCLLIB::Shuffle2: {
+      if (!_.IsFloatVectorType(result_type) &&
+          !_.IsIntVectorType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be an int or float vector type";
+      }
 
-        if (p_storage_class != spv::StorageClass::UniformConstant &&
-            p_storage_class != spv::StorageClass::Generic &&
-            p_storage_class != spv::StorageClass::CrossWorkgroup &&
-            p_storage_class != spv::StorageClass::Workgroup &&
-            p_storage_class != spv::StorageClass::Function) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P storage class to be UniformConstant, "
-                    "Generic, CrossWorkgroup, Workgroup or Function";
-        }
+      const uint32_t result_num_components = _.GetDimension(result_type);
+      if (result_num_components != 2 && result_num_components != 4 &&
+          result_num_components != 8 && result_num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to have 2, 4, 8 or 16 components";
+      }
 
-        if ((!_.IsFloatScalarType(p_data_type) ||
-             _.GetBitWidth(p_data_type) != 16) &&
-            !_.ContainsUntypedPointer(p_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P data type to be 16-bit float scalar";
-        }
+      uint32_t operand_index = 4;
+      const uint32_t x_type = _.GetOperandTypeId(inst, operand_index++);
 
-        const uint32_t n_value = inst->word(7);
-        if (num_components != n_value) {
+      if (ext_inst_key == OpenCLLIB::Shuffle2) {
+        const uint32_t y_type = _.GetOperandTypeId(inst, operand_index++);
+        if (x_type != y_type) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected literal N to be equal to the number of "
-                    "components of Result Type";
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected operands X and Y to be of the same type";
         }
-        break;
       }
 
-      case OpenCLLIB::Vstore_half:
-      case OpenCLLIB::Vstore_half_r:
-      case OpenCLLIB::Vstore_halfn:
-      case OpenCLLIB::Vstore_halfn_r:
-      case OpenCLLIB::Vstorea_halfn:
-      case OpenCLLIB::Vstorea_halfn_r: {
-        if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": expected Result Type to be void";
-        }
+      const uint32_t shuffle_mask_type =
+          _.GetOperandTypeId(inst, operand_index++);
 
-        const uint32_t data_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t offset_type = _.GetOperandTypeId(inst, 5);
-        const uint32_t p_type = _.GetOperandTypeId(inst, 6);
-        const uint32_t data_type_bit_width = _.GetBitWidth(data_type);
+      if (!_.IsFloatVectorType(x_type) && !_.IsIntVectorType(x_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X to be an int or float vector";
+      }
 
-        if (ext_inst_key == OpenCLLIB::Vstore_half ||
-            ext_inst_key == OpenCLLIB::Vstore_half_r) {
-          if (!_.IsFloatScalarType(data_type) ||
-              (data_type_bit_width != 32 && data_type_bit_width != 64)) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected Data to be a 32 or 64-bit float scalar";
-          }
-        } else {
-          if (!_.IsFloatVectorType(data_type) ||
-              (data_type_bit_width != 32 && data_type_bit_width != 64)) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected Data to be a 32 or 64-bit float vector";
-          }
+      const uint32_t x_num_components = _.GetDimension(x_type);
+      if (x_num_components != 2 && x_num_components != 4 &&
+          x_num_components != 8 && x_num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X to have 2, 4, 8 or 16 components";
+      }
 
-          const uint32_t num_components = _.GetDimension(data_type);
-          if (num_components > 4 && num_components != 8 &&
-              num_components != 16) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected Data to have 2, 3, 4, 8 or 16 components";
-          }
-        }
+      const uint32_t result_component_type = _.GetComponentType(result_type);
 
-        const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
-        if (!size_t_bit_width) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name()
-                 << " can only be used with physical addressing models";
-        }
+      if (result_component_type != _.GetComponentType(x_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand X and Result Type to have equal "
+                  "component types";
+      }
 
-        if (!_.IsIntScalarType(offset_type) ||
-            _.GetBitWidth(offset_type) != size_t_bit_width) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Offset to be of type size_t ("
-                 << size_t_bit_width
-                 << "-bit integer for the addressing model used in the module)";
-        }
+      if (!_.IsIntVectorType(shuffle_mask_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Shuffle Mask to be an int vector";
+      }
 
-        spv::StorageClass p_storage_class;
-        uint32_t p_data_type = 0;
-        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P to be a pointer";
-        }
+      if (result_num_components != _.GetDimension(shuffle_mask_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Shuffle Mask to have the same number of "
+                  "components as Result Type";
+      }
 
-        if (p_storage_class != spv::StorageClass::Generic &&
-            p_storage_class != spv::StorageClass::CrossWorkgroup &&
-            p_storage_class != spv::StorageClass::Workgroup &&
-            p_storage_class != spv::StorageClass::Function) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P storage class to be Generic, "
-                    "CrossWorkgroup, Workgroup or Function";
-        }
+      if (_.GetBitWidth(result_component_type) !=
+          _.GetBitWidth(shuffle_mask_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Shuffle Mask components to have the same "
+                  "bit width as Result Type components";
+      }
+      break;
+    }
 
-        if ((!_.IsFloatScalarType(p_data_type) ||
-             _.GetBitWidth(p_data_type) != 16) &&
-            !_.ContainsUntypedPointer(p_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand P data type to be 16-bit float scalar";
-        }
+    case OpenCLLIB::Printf: {
+      if (!_.IsIntScalarType(result_type) || _.GetBitWidth(result_type) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a 32-bit int type";
+      }
 
-        // Rounding mode enum is checked by assembler.
-        break;
+      const uint32_t format_type = _.GetOperandTypeId(inst, 4);
+      spv::StorageClass format_storage_class;
+      uint32_t format_data_type = 0;
+      if (!_.GetPointerTypeInfo(format_type, &format_data_type,
+                                &format_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Format to be a pointer";
       }
 
-      case OpenCLLIB::Shuffle:
-      case OpenCLLIB::Shuffle2: {
-        if (!_.IsFloatVectorType(result_type) &&
-            !_.IsIntVectorType(result_type)) {
+      if (_.HasExtension(
+              Extension::kSPV_EXT_relaxed_printf_string_address_space)) {
+        if (format_storage_class != spv::StorageClass::UniformConstant &&
+            // Extension SPV_EXT_relaxed_printf_string_address_space allows
+            // format strings in Global, Local, Private and Generic address
+            // spaces
+
+            // Global
+            format_storage_class != spv::StorageClass::CrossWorkgroup &&
+            // Local
+            format_storage_class != spv::StorageClass::Workgroup &&
+            // Private
+            format_storage_class != spv::StorageClass::Function &&
+            // Generic
+            format_storage_class != spv::StorageClass::Generic) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be an int or float vector type";
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected Format storage class to be UniformConstant, "
+                    "Crossworkgroup, Workgroup, Function, or Generic";
         }
-
-        const uint32_t result_num_components = _.GetDimension(result_type);
-        if (result_num_components != 2 && result_num_components != 4 &&
-            result_num_components != 8 && result_num_components != 16) {
+      } else {
+        if (format_storage_class != spv::StorageClass::UniformConstant) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to have 2, 4, 8 or 16 components";
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected Format storage class to be UniformConstant";
         }
+      }
 
-        uint32_t operand_index = 4;
-        const uint32_t x_type = _.GetOperandTypeId(inst, operand_index++);
+      // If pointer points to an array, get the type of an element
+      if (_.IsIntArrayType(format_data_type))
+        format_data_type = _.GetComponentType(format_data_type);
 
-        if (ext_inst_key == OpenCLLIB::Shuffle2) {
-          const uint32_t y_type = _.GetOperandTypeId(inst, operand_index++);
-          if (x_type != y_type) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected operands X and Y to be of the same type";
-          }
-        }
+      if ((!_.IsIntScalarType(format_data_type) ||
+           _.GetBitWidth(format_data_type) != 8) &&
+          !_.ContainsUntypedPointer(format_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Format data type to be 8-bit int";
+      }
+      break;
+    }
 
-        const uint32_t shuffle_mask_type =
-            _.GetOperandTypeId(inst, operand_index++);
+    case OpenCLLIB::Prefetch: {
+      if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst)
+               << ": expected Result Type to be void";
+      }
 
-        if (!_.IsFloatVectorType(x_type) && !_.IsIntVectorType(x_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X to be an int or float vector";
-        }
+      const uint32_t p_type = _.GetOperandTypeId(inst, 4);
+      const uint32_t num_elements_type = _.GetOperandTypeId(inst, 5);
 
-        const uint32_t x_num_components = _.GetDimension(x_type);
-        if (x_num_components != 2 && x_num_components != 4 &&
-            x_num_components != 8 && x_num_components != 16) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X to have 2, 4, 8 or 16 components";
-        }
+      spv::StorageClass p_storage_class;
+      uint32_t p_data_type = 0;
+      if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Ptr to be a pointer";
+      }
 
-        const uint32_t result_component_type = _.GetComponentType(result_type);
+      if (p_storage_class != spv::StorageClass::CrossWorkgroup) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Ptr storage class to be CrossWorkgroup";
+      }
 
-        if (result_component_type != _.GetComponentType(x_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand X and Result Type to have equal "
-                    "component types";
-        }
+      if (!_.IsFloatScalarOrVectorType(p_data_type) &&
+          !_.IsIntScalarOrVectorType(p_data_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Ptr data type to be int or float scalar or "
+                  "vector";
+      }
 
-        if (!_.IsIntVectorType(shuffle_mask_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Shuffle Mask to be an int vector";
-        }
+      const uint32_t num_components = _.GetDimension(p_data_type);
+      if (num_components > 4 && num_components != 8 && num_components != 16) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected Result Type to be a scalar or a vector with 2, "
+                  "3, 4, 8 or 16 components";
+      }
 
-        if (result_num_components != _.GetDimension(shuffle_mask_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Shuffle Mask to have the same number of "
-                    "components as Result Type";
-        }
+      const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
+      if (!size_t_bit_width) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst)
+               << " can only be used with physical addressing models";
+      }
 
-        if (_.GetBitWidth(result_component_type) !=
-            _.GetBitWidth(shuffle_mask_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Shuffle Mask components to have the same "
-                    "bit width as Result Type components";
-        }
-        break;
+      if (!_.IsIntScalarType(num_elements_type) ||
+          _.GetBitWidth(num_elements_type) != size_t_bit_width) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << GetExtInstName(_, inst) << ": "
+               << "expected operand Num Elements to be of type size_t ("
+               << size_t_bit_width
+               << "-bit integer for the addressing model used in the module)";
       }
+      break;
+    }
+  }
+  return SPV_SUCCESS;
+}
 
-      case OpenCLLIB::Printf: {
-        if (!_.IsIntScalarType(result_type) ||
-            _.GetBitWidth(result_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a 32-bit int type";
-        }
+spv_result_t ValidateExtInstDebugInfo100(ValidationState_t& _,
+                                         const Instruction* inst) {
+  const uint32_t result_type = inst->type_id();
+  const uint32_t ext_inst_index = inst->word(4);
+  if (!_.IsVoidType(result_type)) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << GetExtInstName(_, inst) << ": "
+           << "expected result type must be a result id of " << "OpTypeVoid";
+  }
 
-        const uint32_t format_type = _.GetOperandTypeId(inst, 4);
-        spv::StorageClass format_storage_class;
-        uint32_t format_data_type = 0;
-        if (!_.GetPointerTypeInfo(format_type, &format_data_type,
-                                  &format_storage_class)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Format to be a pointer";
-        }
+  const spv_ext_inst_type_t ext_inst_type =
+      spv_ext_inst_type_t(inst->ext_inst_type());
+  const bool vulkanDebugInfo =
+      ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100;
 
-        if (_.HasExtension(
-                Extension::kSPV_EXT_relaxed_printf_string_address_space)) {
-          if (format_storage_class != spv::StorageClass::UniformConstant &&
-              // Extension SPV_EXT_relaxed_printf_string_address_space allows
-              // format strings in Global, Local, Private and Generic address
-              // spaces
-
-              // Global
-              format_storage_class != spv::StorageClass::CrossWorkgroup &&
-              // Local
-              format_storage_class != spv::StorageClass::Workgroup &&
-              // Private
-              format_storage_class != spv::StorageClass::Function &&
-              // Generic
-              format_storage_class != spv::StorageClass::Generic) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected Format storage class to be UniformConstant, "
-                      "Crossworkgroup, Workgroup, Function, or Generic";
-          }
-        } else {
-          if (format_storage_class != spv::StorageClass::UniformConstant) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected Format storage class to be UniformConstant";
-          }
-        }
+  auto num_words = inst->words().size();
 
-        // If pointer points to an array, get the type of an element
-        if (_.IsIntArrayType(format_data_type))
-          format_data_type = _.GetComponentType(format_data_type);
+  // Handle any non-common NonSemanticShaderDebugInfo instructions.
+  if (vulkanDebugInfo) {
+    const NonSemanticShaderDebugInfo100Instructions ext_inst_key =
+        NonSemanticShaderDebugInfo100Instructions(ext_inst_index);
+    switch (ext_inst_key) {
+      // The following block of instructions will be handled by the common
+      // validation.
+      case NonSemanticShaderDebugInfo100DebugInfoNone:
+      case NonSemanticShaderDebugInfo100DebugCompilationUnit:
+      case NonSemanticShaderDebugInfo100DebugTypePointer:
+      case NonSemanticShaderDebugInfo100DebugTypeQualifier:
+      case NonSemanticShaderDebugInfo100DebugTypeArray:
+      case NonSemanticShaderDebugInfo100DebugTypeVector:
+      case NonSemanticShaderDebugInfo100DebugTypedef:
+      case NonSemanticShaderDebugInfo100DebugTypeFunction:
+      case NonSemanticShaderDebugInfo100DebugTypeEnum:
+      case NonSemanticShaderDebugInfo100DebugTypeComposite:
+      case NonSemanticShaderDebugInfo100DebugTypeMember:
+      case NonSemanticShaderDebugInfo100DebugTypeInheritance:
+      case NonSemanticShaderDebugInfo100DebugTypePtrToMember:
+      case NonSemanticShaderDebugInfo100DebugTypeTemplate:
+      case NonSemanticShaderDebugInfo100DebugTypeTemplateParameter:
+      case NonSemanticShaderDebugInfo100DebugTypeTemplateTemplateParameter:
+      case NonSemanticShaderDebugInfo100DebugTypeTemplateParameterPack:
+      case NonSemanticShaderDebugInfo100DebugGlobalVariable:
+      case NonSemanticShaderDebugInfo100DebugFunctionDeclaration:
+      case NonSemanticShaderDebugInfo100DebugFunction:
+      case NonSemanticShaderDebugInfo100DebugLexicalBlock:
+      case NonSemanticShaderDebugInfo100DebugLexicalBlockDiscriminator:
+      case NonSemanticShaderDebugInfo100DebugScope:
+      case NonSemanticShaderDebugInfo100DebugNoScope:
+      case NonSemanticShaderDebugInfo100DebugInlinedAt:
+      case NonSemanticShaderDebugInfo100DebugLocalVariable:
+      case NonSemanticShaderDebugInfo100DebugInlinedVariable:
+      case NonSemanticShaderDebugInfo100DebugValue:
+      case NonSemanticShaderDebugInfo100DebugOperation:
+      case NonSemanticShaderDebugInfo100DebugExpression:
+      case NonSemanticShaderDebugInfo100DebugMacroDef:
+      case NonSemanticShaderDebugInfo100DebugMacroUndef:
+      case NonSemanticShaderDebugInfo100DebugImportedEntity:
+      case NonSemanticShaderDebugInfo100DebugSource:
+        break;
 
-        if ((!_.IsIntScalarType(format_data_type) ||
-             _.GetBitWidth(format_data_type) != 8) &&
-            !_.ContainsUntypedPointer(format_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Format data type to be 8-bit int";
-        }
+      // These checks are for operands that are differnet in
+      // ShaderDebugInfo100
+      case NonSemanticShaderDebugInfo100DebugTypeBasic: {
+        CHECK_CONST_UINT_OPERAND("Flags", 8);
         break;
       }
-
-      case OpenCLLIB::Prefetch: {
-        if (_.GetIdOpcode(result_type) != spv::Op::OpTypeVoid) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": expected Result Type to be void";
-        }
-
-        const uint32_t p_type = _.GetOperandTypeId(inst, 4);
-        const uint32_t num_elements_type = _.GetOperandTypeId(inst, 5);
-
-        spv::StorageClass p_storage_class;
-        uint32_t p_data_type = 0;
-        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Ptr to be a pointer";
+      case NonSemanticShaderDebugInfo100DebugDeclare: {
+        for (uint32_t word_index = 8; word_index < num_words; ++word_index) {
+          auto index_inst = _.FindDef(inst->word(word_index));
+          auto type_id = index_inst != nullptr ? index_inst->type_id() : 0;
+          if (type_id == 0 || !IsIntScalar(_, type_id, false, false))
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << GetExtInstName(_, inst) << ": "
+                   << "expected index must be scalar integer";
         }
+        break;
+      }
+      case NonSemanticShaderDebugInfo100DebugTypeMatrix: {
+        CHECK_DEBUG_OPERAND("Vector Type", CommonDebugInfoDebugTypeVector, 5);
 
-        if (p_storage_class != spv::StorageClass::CrossWorkgroup) {
-          return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Ptr storage class to be CrossWorkgroup";
-        }
+        CHECK_CONST_UINT_OPERAND("Vector Count", 6);
 
-        if (!_.IsFloatScalarOrVectorType(p_data_type) &&
-            !_.IsIntScalarOrVectorType(p_data_type)) {
+        uint32_t vector_count = inst->word(6);
+        uint64_t const_val;
+        if (!_.EvalConstantValUint64(vector_count, &const_val)) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Ptr data type to be int or float scalar or "
-                    "vector";
+                 << GetExtInstName(_, inst)
+                 << ": Vector Count must be 32-bit integer OpConstant";
         }
 
-        const uint32_t num_components = _.GetDimension(p_data_type);
-        if (num_components > 4 && num_components != 8 && num_components != 16) {
+        vector_count = const_val & 0xffffffff;
+        if (!vector_count || vector_count > 4) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected Result Type to be a scalar or a vector with 2, "
-                    "3, 4, 8 or 16 components";
+                 << GetExtInstName(_, inst)
+                 << ": Vector Count must be positive "
+                 << "integer less than or equal to 4";
         }
-
-        const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
-        if (!size_t_bit_width) {
+        break;
+      }
+      case NonSemanticShaderDebugInfo100DebugFunctionDefinition: {
+        CHECK_DEBUG_OPERAND("Function", CommonDebugInfoDebugFunction, 5);
+        CHECK_OPERAND("Definition", spv::Op::OpFunction, 6);
+        const auto* current_function = inst->function();
+        if (current_function->first_block()->id() != inst->block()->id()) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name()
-                 << " can only be used with physical addressing models";
+                 << GetExtInstName(_, inst)
+                 << ": must be in the entry basic block of the function";
         }
 
-        if (!_.IsIntScalarType(num_elements_type) ||
-            _.GetBitWidth(num_elements_type) != size_t_bit_width) {
+        const uint32_t definition_id = inst->word(6);
+        if (definition_id != current_function->id()) {
           return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                 << ext_inst_name() << ": "
-                 << "expected operand Num Elements to be of type size_t ("
-                 << size_t_bit_width
-                 << "-bit integer for the addressing model used in the module)";
+                 << GetExtInstName(_, inst)
+                 << ": operand Definition must point to the OpFunction it is "
+                    "inside";
         }
         break;
       }
-    }
-  } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
-             ext_inst_type ==
-                 SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
-    if (!_.IsVoidType(result_type)) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << ext_inst_name() << ": "
-             << "expected result type must be a result id of "
-             << "OpTypeVoid";
-    }
-
-    const bool vulkanDebugInfo =
-        ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100;
-
-    auto num_words = inst->words().size();
-
-    // Handle any non-common NonSemanticShaderDebugInfo instructions.
-    if (vulkanDebugInfo) {
-      const NonSemanticShaderDebugInfo100Instructions ext_inst_key =
-          NonSemanticShaderDebugInfo100Instructions(ext_inst_index);
-      switch (ext_inst_key) {
-        // The following block of instructions will be handled by the common
-        // validation.
-        case NonSemanticShaderDebugInfo100DebugInfoNone:
-        case NonSemanticShaderDebugInfo100DebugCompilationUnit:
-        case NonSemanticShaderDebugInfo100DebugTypePointer:
-        case NonSemanticShaderDebugInfo100DebugTypeQualifier:
-        case NonSemanticShaderDebugInfo100DebugTypeArray:
-        case NonSemanticShaderDebugInfo100DebugTypeVector:
-        case NonSemanticShaderDebugInfo100DebugTypedef:
-        case NonSemanticShaderDebugInfo100DebugTypeFunction:
-        case NonSemanticShaderDebugInfo100DebugTypeEnum:
-        case NonSemanticShaderDebugInfo100DebugTypeComposite:
-        case NonSemanticShaderDebugInfo100DebugTypeMember:
-        case NonSemanticShaderDebugInfo100DebugTypeInheritance:
-        case NonSemanticShaderDebugInfo100DebugTypePtrToMember:
-        case NonSemanticShaderDebugInfo100DebugTypeTemplate:
-        case NonSemanticShaderDebugInfo100DebugTypeTemplateParameter:
-        case NonSemanticShaderDebugInfo100DebugTypeTemplateTemplateParameter:
-        case NonSemanticShaderDebugInfo100DebugTypeTemplateParameterPack:
-        case NonSemanticShaderDebugInfo100DebugGlobalVariable:
-        case NonSemanticShaderDebugInfo100DebugFunctionDeclaration:
-        case NonSemanticShaderDebugInfo100DebugFunction:
-        case NonSemanticShaderDebugInfo100DebugLexicalBlock:
-        case NonSemanticShaderDebugInfo100DebugLexicalBlockDiscriminator:
-        case NonSemanticShaderDebugInfo100DebugScope:
-        case NonSemanticShaderDebugInfo100DebugNoScope:
-        case NonSemanticShaderDebugInfo100DebugInlinedAt:
-        case NonSemanticShaderDebugInfo100DebugLocalVariable:
-        case NonSemanticShaderDebugInfo100DebugInlinedVariable:
-        case NonSemanticShaderDebugInfo100DebugValue:
-        case NonSemanticShaderDebugInfo100DebugOperation:
-        case NonSemanticShaderDebugInfo100DebugExpression:
-        case NonSemanticShaderDebugInfo100DebugMacroDef:
-        case NonSemanticShaderDebugInfo100DebugMacroUndef:
-        case NonSemanticShaderDebugInfo100DebugImportedEntity:
-        case NonSemanticShaderDebugInfo100DebugSource:
-          break;
-
-        // These checks are for operands that are differnet in
-        // ShaderDebugInfo100
-        case NonSemanticShaderDebugInfo100DebugTypeBasic: {
-          CHECK_CONST_UINT_OPERAND("Flags", 8);
-          break;
-        }
-        case NonSemanticShaderDebugInfo100DebugDeclare: {
-          for (uint32_t word_index = 8; word_index < num_words; ++word_index) {
-            auto index_inst = _.FindDef(inst->word(word_index));
-            auto type_id = index_inst != nullptr ? index_inst->type_id() : 0;
-            if (type_id == 0 || !IsIntScalar(_, type_id, false, false))
-              return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                     << ext_inst_name() << ": "
-                     << "expected index must be scalar integer";
-          }
-          break;
+      case NonSemanticShaderDebugInfo100DebugLine: {
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 5);
+        CHECK_CONST_UINT_OPERAND("Line Start", 6);
+        CHECK_CONST_UINT_OPERAND("Line End", 7);
+        CHECK_CONST_UINT_OPERAND("Column Start", 8);
+        CHECK_CONST_UINT_OPERAND("Column End", 9);
+
+        // above already validates if 32-bit and non-spec constant
+        // but want to use EvalInt32IfConst to be consistent with other Eval
+        // locations
+        bool is_int32 = false, is_const_int32 = false;
+        uint32_t line_start = 0;
+        uint32_t line_end = 0;
+        uint32_t column_start = 0;
+        uint32_t column_end = 0;
+        std::tie(is_int32, is_const_int32, line_start) =
+            _.EvalInt32IfConst(inst->word(6));
+        std::tie(is_int32, is_const_int32, line_end) =
+            _.EvalInt32IfConst(inst->word(7));
+        std::tie(is_int32, is_const_int32, column_start) =
+            _.EvalInt32IfConst(inst->word(8));
+        std::tie(is_int32, is_const_int32, column_end) =
+            _.EvalInt32IfConst(inst->word(9));
+        if (line_end < line_start) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": operand Line End ("
+                 << line_end << ") is less than Line Start (" << line_start
+                 << ")";
+        } else if (line_start == line_end && column_end < column_start) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": operand Column End ("
+                 << column_end << ") is less than Column Start ("
+                 << column_start << ") when Line Start equals Line End";
         }
-        case NonSemanticShaderDebugInfo100DebugTypeMatrix: {
-          CHECK_DEBUG_OPERAND("Vector Type", CommonDebugInfoDebugTypeVector, 5);
-
-          CHECK_CONST_UINT_OPERAND("Vector Count", 6);
+        break;
+      }
+      case NonSemanticShaderDebugInfo100DebugSourceContinued: {
+        CHECK_OPERAND("Text", spv::Op::OpString, 5);
+        break;
+      }
+      case NonSemanticShaderDebugInfo100DebugBuildIdentifier: {
+        CHECK_OPERAND("Identifier", spv::Op::OpString, 5);
+        CHECK_CONST_UINT_OPERAND("Flags", 6);
+        break;
+      }
+      case NonSemanticShaderDebugInfo100DebugStoragePath: {
+        CHECK_OPERAND("Path", spv::Op::OpString, 5);
+        break;
+      }
+      case NonSemanticShaderDebugInfo100DebugEntryPoint: {
+        CHECK_DEBUG_OPERAND("Entry Point", CommonDebugInfoDebugFunction, 5);
+        CHECK_DEBUG_OPERAND("Compilation Unit",
+                            CommonDebugInfoDebugCompilationUnit, 6);
+        CHECK_OPERAND("Compiler Signature", spv::Op::OpString, 7);
+        CHECK_OPERAND("Command-line Arguments", spv::Op::OpString, 8);
+        break;
+      }
 
-          uint32_t vector_count = inst->word(6);
-          uint64_t const_val;
-          if (!_.EvalConstantValUint64(vector_count, &const_val)) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name()
-                   << ": Vector Count must be 32-bit integer OpConstant";
-          }
+        // Has no additional checks
+      case NonSemanticShaderDebugInfo100DebugNoLine:
+        break;
+      case NonSemanticShaderDebugInfo100InstructionsMax:
+        assert(0);
+        break;
+    }
+  }
 
-          vector_count = const_val & 0xffffffff;
-          if (!vector_count || vector_count > 4) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": Vector Count must be positive "
-                   << "integer less than or equal to 4";
-          }
-          break;
+  // Handle any non-common OpenCL insts, then common
+  if (ext_inst_type != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
+      OpenCLDebugInfo100Instructions(ext_inst_index) !=
+          OpenCLDebugInfo100DebugModuleINTEL) {
+    const CommonDebugInfoInstructions ext_inst_key =
+        CommonDebugInfoInstructions(ext_inst_index);
+    switch (ext_inst_key) {
+      case CommonDebugInfoDebugInfoNone:
+      case CommonDebugInfoDebugNoScope:
+        break;
+        // The binary parser validates the opcode for DebugInfoNone,
+        // DebugNoScope, DebugOperation. We just check the parameters to
+        // DebugOperation are properly constants for vulkan debug info.
+      case CommonDebugInfoDebugOperation: {
+        CHECK_CONST_UINT_OPERAND("Operation", 5);
+        for (uint32_t i = 6; i < num_words; ++i) {
+          CHECK_CONST_UINT_OPERAND("Operand", i);
         }
-        case NonSemanticShaderDebugInfo100DebugFunctionDefinition: {
-          CHECK_DEBUG_OPERAND("Function", CommonDebugInfoDebugFunction, 5);
-          CHECK_OPERAND("Definition", spv::Op::OpFunction, 6);
-          const auto* current_function = inst->function();
-          if (current_function->first_block()->id() != inst->block()->id()) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name()
-                   << ": must be in the entry basic block of the function";
-          }
+        break;
+      }
+      case CommonDebugInfoDebugCompilationUnit: {
+        CHECK_CONST_UINT_OPERAND("Version", 5);
+        CHECK_CONST_UINT_OPERAND("DWARF Version", 6);
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+        CHECK_CONST_UINT_OPERAND("Language", 8);
+        break;
+      }
+      case CommonDebugInfoDebugSource: {
+        CHECK_OPERAND("File", spv::Op::OpString, 5);
+        if (num_words == 7) CHECK_OPERAND("Text", spv::Op::OpString, 6);
+        break;
+      }
+      case CommonDebugInfoDebugTypeBasic: {
+        CHECK_OPERAND("Name", spv::Op::OpString, 5);
+        CHECK_OPERAND("Size", spv::Op::OpConstant, 6);
+        CHECK_CONST_UINT_OPERAND("Encoding", 7);
+        break;
+      }
+      case CommonDebugInfoDebugTypePointer: {
+        auto validate_base_type =
+            ValidateOperandDebugType(_, "Base Type", inst, 5, false);
+        if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+        CHECK_CONST_UINT_OPERAND("Storage Class", 6);
+        CHECK_CONST_UINT_OPERAND("Flags", 7);
+        break;
+      }
+      case CommonDebugInfoDebugTypeQualifier: {
+        auto validate_base_type =
+            ValidateOperandDebugType(_, "Base Type", inst, 5, false);
+        if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+        CHECK_CONST_UINT_OPERAND("Type Qualifier", 6);
+        break;
+      }
+      case CommonDebugInfoDebugTypeVector: {
+        auto validate_base_type = ValidateOperandBaseType(_, inst, 5);
+        if (validate_base_type != SPV_SUCCESS) return validate_base_type;
 
-          const uint32_t definition_id = inst->word(6);
-          if (definition_id != current_function->id()) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name()
-                   << ": operand Definition must point to the OpFunction it is "
-                      "inside";
-          }
-          break;
-        }
-        case NonSemanticShaderDebugInfo100DebugLine: {
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 5);
-          CHECK_CONST_UINT_OPERAND("Line Start", 6);
-          CHECK_CONST_UINT_OPERAND("Line End", 7);
-          CHECK_CONST_UINT_OPERAND("Column Start", 8);
-          CHECK_CONST_UINT_OPERAND("Column End", 9);
-
-          // above already validates if 32-bit and non-spec constant
-          // but want to use EvalInt32IfConst to be consistent with other Eval
-          // locations
-          bool is_int32 = false, is_const_int32 = false;
-          uint32_t line_start = 0;
-          uint32_t line_end = 0;
-          uint32_t column_start = 0;
-          uint32_t column_end = 0;
-          std::tie(is_int32, is_const_int32, line_start) =
-              _.EvalInt32IfConst(inst->word(6));
-          std::tie(is_int32, is_const_int32, line_end) =
-              _.EvalInt32IfConst(inst->word(7));
-          std::tie(is_int32, is_const_int32, column_start) =
-              _.EvalInt32IfConst(inst->word(8));
-          std::tie(is_int32, is_const_int32, column_end) =
-              _.EvalInt32IfConst(inst->word(9));
-          if (line_end < line_start) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": operand Line End (" << line_end
-                   << ") is less than Line Start (" << line_start << ")";
-          } else if (line_start == line_end && column_end < column_start) {
+        CHECK_CONST_UINT_OPERAND("Component Count", 6);
+        uint32_t component_count = inst->word(6);
+        if (vulkanDebugInfo) {
+          uint64_t const_val;
+          if (!_.EvalConstantValUint64(component_count, &const_val)) {
             return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": operand Column End (" << column_end
-                   << ") is less than Column Start (" << column_start
-                   << ") when Line Start equals Line End";
+                   << GetExtInstName(_, inst)
+                   << ": Component Count must be 32-bit integer OpConstant";
           }
-          break;
-        }
-        case NonSemanticShaderDebugInfo100DebugSourceContinued: {
-          CHECK_OPERAND("Text", spv::Op::OpString, 5);
-          break;
-        }
-        case NonSemanticShaderDebugInfo100DebugBuildIdentifier: {
-          CHECK_OPERAND("Identifier", spv::Op::OpString, 5);
-          CHECK_CONST_UINT_OPERAND("Flags", 6);
-          break;
-        }
-        case NonSemanticShaderDebugInfo100DebugStoragePath: {
-          CHECK_OPERAND("Path", spv::Op::OpString, 5);
-          break;
-        }
-        case NonSemanticShaderDebugInfo100DebugEntryPoint: {
-          CHECK_DEBUG_OPERAND("Entry Point", CommonDebugInfoDebugFunction, 5);
-          CHECK_DEBUG_OPERAND("Compilation Unit",
-                              CommonDebugInfoDebugCompilationUnit, 6);
-          CHECK_OPERAND("Compiler Signature", spv::Op::OpString, 7);
-          CHECK_OPERAND("Command-line Arguments", spv::Op::OpString, 8);
-          break;
+          component_count = const_val & 0xffffffff;
         }
 
-          // Has no additional checks
-        case NonSemanticShaderDebugInfo100DebugNoLine:
-          break;
-        case NonSemanticShaderDebugInfo100InstructionsMax:
-          assert(0);
-          break;
-      }
-    }
-
-    // Handle any non-common OpenCL insts, then common
-    if (ext_inst_type != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
-        OpenCLDebugInfo100Instructions(ext_inst_index) !=
-            OpenCLDebugInfo100DebugModuleINTEL) {
-      const CommonDebugInfoInstructions ext_inst_key =
-          CommonDebugInfoInstructions(ext_inst_index);
-      switch (ext_inst_key) {
-        case CommonDebugInfoDebugInfoNone:
-        case CommonDebugInfoDebugNoScope:
-          break;
-          // The binary parser validates the opcode for DebugInfoNone,
-          // DebugNoScope, DebugOperation. We just check the parameters to
-          // DebugOperation are properly constants for vulkan debug info.
-        case CommonDebugInfoDebugOperation: {
-          CHECK_CONST_UINT_OPERAND("Operation", 5);
-          for (uint32_t i = 6; i < num_words; ++i) {
-            CHECK_CONST_UINT_OPERAND("Operand", i);
-          }
-          break;
-        }
-        case CommonDebugInfoDebugCompilationUnit: {
-          CHECK_CONST_UINT_OPERAND("Version", 5);
-          CHECK_CONST_UINT_OPERAND("DWARF Version", 6);
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
-          CHECK_CONST_UINT_OPERAND("Language", 8);
-          break;
-        }
-        case CommonDebugInfoDebugSource: {
-          CHECK_OPERAND("File", spv::Op::OpString, 5);
-          if (num_words == 7) CHECK_OPERAND("Text", spv::Op::OpString, 6);
-          break;
-        }
-        case CommonDebugInfoDebugTypeBasic: {
-          CHECK_OPERAND("Name", spv::Op::OpString, 5);
-          CHECK_OPERAND("Size", spv::Op::OpConstant, 6);
-          CHECK_CONST_UINT_OPERAND("Encoding", 7);
-          break;
-        }
-        case CommonDebugInfoDebugTypePointer: {
-          auto validate_base_type = ValidateOperandDebugType(
-              _, "Base Type", inst, 5, ext_inst_name, false);
-          if (validate_base_type != SPV_SUCCESS) return validate_base_type;
-          CHECK_CONST_UINT_OPERAND("Storage Class", 6);
-          CHECK_CONST_UINT_OPERAND("Flags", 7);
-          break;
-        }
-        case CommonDebugInfoDebugTypeQualifier: {
-          auto validate_base_type = ValidateOperandDebugType(
-              _, "Base Type", inst, 5, ext_inst_name, false);
-          if (validate_base_type != SPV_SUCCESS) return validate_base_type;
-          CHECK_CONST_UINT_OPERAND("Type Qualifier", 6);
-          break;
+        if (!component_count || component_count > 4) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst)
+                 << ": Component Count must be positive "
+                 << "integer less than or equal to 4";
         }
-        case CommonDebugInfoDebugTypeVector: {
-          auto validate_base_type =
-              ValidateOperandBaseType(_, inst, 5, ext_inst_name);
-          if (validate_base_type != SPV_SUCCESS) return validate_base_type;
-
-          CHECK_CONST_UINT_OPERAND("Component Count", 6);
-          uint32_t component_count = inst->word(6);
-          if (vulkanDebugInfo) {
-            uint64_t const_val;
-            if (!_.EvalConstantValUint64(component_count, &const_val)) {
-              return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                     << ext_inst_name()
-                     << ": Component Count must be 32-bit integer OpConstant";
+        break;
+      }
+      case CommonDebugInfoDebugTypeArray: {
+        auto validate_base_type =
+            ValidateOperandDebugType(_, "Base Type", inst, 5, false);
+        if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+        for (uint32_t i = 6; i < num_words; ++i) {
+          bool invalid = false;
+          auto* component_count = _.FindDef(inst->word(i));
+          if (IsConstIntScalarTypeWith32Or64Bits(_, component_count)) {
+            // TODO: We need a spec discussion for the runtime array for
+            // OpenCL.
+            if (!vulkanDebugInfo && !component_count->word(3)) {
+              invalid = true;
             }
-            component_count = const_val & 0xffffffff;
-          }
-
-          if (!component_count || component_count > 4) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": Component Count must be positive "
-                   << "integer less than or equal to 4";
-          }
-          break;
-        }
-        case CommonDebugInfoDebugTypeArray: {
-          auto validate_base_type = ValidateOperandDebugType(
-              _, "Base Type", inst, 5, ext_inst_name, false);
-          if (validate_base_type != SPV_SUCCESS) return validate_base_type;
-          for (uint32_t i = 6; i < num_words; ++i) {
-            bool invalid = false;
-            auto* component_count = _.FindDef(inst->word(i));
-            if (IsConstIntScalarTypeWith32Or64Bits(_, component_count)) {
-              // TODO: We need a spec discussion for the runtime array for
-              // OpenCL.
-              if (!vulkanDebugInfo && !component_count->word(3)) {
+          } else if (component_count->words().size() > 6 &&
+                     (CommonDebugInfoInstructions(component_count->word(4)) ==
+                          CommonDebugInfoDebugLocalVariable ||
+                      CommonDebugInfoInstructions(component_count->word(4)) ==
+                          CommonDebugInfoDebugGlobalVariable)) {
+            auto* component_count_type = _.FindDef(component_count->word(6));
+            if (component_count_type->words().size() > 7) {
+              uint32_t encoding = component_count_type->word(7);
+              if (CommonDebugInfoInstructions(component_count_type->word(4)) !=
+                      CommonDebugInfoDebugTypeBasic ||
+                  (vulkanDebugInfo && !IsUint32Constant(_, encoding)) ||
+                  OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(
+                      vulkanDebugInfo
+                          ? GetUint32Constant(_, encoding)
+                          : encoding) != OpenCLDebugInfo100Unsigned) {
                 invalid = true;
-              }
-            } else if (component_count->words().size() > 6 &&
-                       (CommonDebugInfoInstructions(component_count->word(4)) ==
-                            CommonDebugInfoDebugLocalVariable ||
-                        CommonDebugInfoInstructions(component_count->word(4)) ==
-                            CommonDebugInfoDebugGlobalVariable)) {
-              auto* component_count_type = _.FindDef(component_count->word(6));
-              if (component_count_type->words().size() > 7) {
-                uint32_t encoding = component_count_type->word(7);
-                if (CommonDebugInfoInstructions(component_count_type->word(
-                        4)) != CommonDebugInfoDebugTypeBasic ||
-                    (vulkanDebugInfo && !IsUint32Constant(_, encoding)) ||
-                    OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(
-                        vulkanDebugInfo
-                            ? GetUint32Constant(_, encoding)
-                            : encoding) != OpenCLDebugInfo100Unsigned) {
+              } else {
+                // DebugTypeBasic for DebugLocalVariable/DebugGlobalVariable
+                // must have Unsigned encoding and 32 or 64 as its size in
+                // bits.
+                Instruction* size_in_bits =
+                    _.FindDef(component_count_type->word(6));
+                if (!_.IsIntScalarType(size_in_bits->type_id()) ||
+                    (size_in_bits->word(3) != 32 &&
+                     size_in_bits->word(3) != 64)) {
                   invalid = true;
-                } else {
-                  // DebugTypeBasic for DebugLocalVariable/DebugGlobalVariable
-                  // must have Unsigned encoding and 32 or 64 as its size in
-                  // bits.
-                  Instruction* size_in_bits =
-                      _.FindDef(component_count_type->word(6));
-                  if (!_.IsIntScalarType(size_in_bits->type_id()) ||
-                      (size_in_bits->word(3) != 32 &&
-                       size_in_bits->word(3) != 64)) {
-                    invalid = true;
-                  }
                 }
-              } else {
-                invalid = true;
               }
             } else {
               invalid = true;
             }
-            if (invalid) {
-              return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                     << ext_inst_name() << ": Component Count must be "
-                     << "OpConstant with a 32- or 64-bits integer scalar type "
-                        "or "
-                     << "DebugGlobalVariable or DebugLocalVariable with a 32- "
-                        "or "
-                     << "64-bits unsigned integer scalar type";
-            }
+          } else {
+            invalid = true;
+          }
+          if (invalid) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << GetExtInstName(_, inst) << ": Component Count must be "
+                   << "OpConstant with a 32- or 64-bits integer scalar type "
+                      "or "
+                   << "DebugGlobalVariable or DebugLocalVariable with a 32- "
+                      "or "
+                   << "64-bits unsigned integer scalar type";
           }
-          break;
         }
-        case CommonDebugInfoDebugTypedef: {
-          CHECK_OPERAND("Name", spv::Op::OpString, 5);
-          auto validate_base_type =
-              ValidateOperandBaseType(_, inst, 6, ext_inst_name);
-          if (validate_base_type != SPV_SUCCESS) return validate_base_type;
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
-          CHECK_CONST_UINT_OPERAND("Line", 8);
-          CHECK_CONST_UINT_OPERAND("Column", 9);
-          auto validate_parent =
-              ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
-          if (validate_parent != SPV_SUCCESS) return validate_parent;
-          break;
+        break;
+      }
+      case CommonDebugInfoDebugTypedef: {
+        CHECK_OPERAND("Name", spv::Op::OpString, 5);
+        auto validate_base_type = ValidateOperandBaseType(_, inst, 6);
+        if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+        CHECK_CONST_UINT_OPERAND("Line", 8);
+        CHECK_CONST_UINT_OPERAND("Column", 9);
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        break;
+      }
+      case CommonDebugInfoDebugTypeFunction: {
+        CHECK_CONST_UINT_OPERAND("Flags", 5);
+        auto* return_type = _.FindDef(inst->word(6));
+        // TODO: We need a spec discussion that we have to allow return and
+        // parameter types of a DebugTypeFunction to have template parameter.
+        if (return_type->opcode() != spv::Op::OpTypeVoid) {
+          auto validate_return =
+              ValidateOperandDebugType(_, "Return Type", inst, 6, true);
+          if (validate_return != SPV_SUCCESS) return validate_return;
+        }
+        for (uint32_t word_index = 7; word_index < num_words; ++word_index) {
+          auto validate_param = ValidateOperandDebugType(
+              _, "Parameter Types", inst, word_index, true);
+          if (validate_param != SPV_SUCCESS) return validate_param;
         }
-        case CommonDebugInfoDebugTypeFunction: {
-          CHECK_CONST_UINT_OPERAND("Flags", 5);
-          auto* return_type = _.FindDef(inst->word(6));
-          // TODO: We need a spec discussion that we have to allow return and
-          // parameter types of a DebugTypeFunction to have template parameter.
-          if (return_type->opcode() != spv::Op::OpTypeVoid) {
-            auto validate_return = ValidateOperandDebugType(
-                _, "Return Type", inst, 6, ext_inst_name, true);
-            if (validate_return != SPV_SUCCESS) return validate_return;
-          }
-          for (uint32_t word_index = 7; word_index < num_words; ++word_index) {
-            auto validate_param = ValidateOperandDebugType(
-                _, "Parameter Types", inst, word_index, ext_inst_name, true);
-            if (validate_param != SPV_SUCCESS) return validate_param;
-          }
-          break;
+        break;
+      }
+      case CommonDebugInfoDebugTypeEnum: {
+        CHECK_OPERAND("Name", spv::Op::OpString, 5);
+        if (!DoesDebugInfoOperandMatchExpectation(
+                _,
+                [](CommonDebugInfoInstructions dbg_inst) {
+                  return dbg_inst == CommonDebugInfoDebugInfoNone;
+                },
+                inst, 6)) {
+          auto validate_underlying_type =
+              ValidateOperandDebugType(_, "Underlying Types", inst, 6, false);
+          if (validate_underlying_type != SPV_SUCCESS)
+            return validate_underlying_type;
+        }
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+        CHECK_CONST_UINT_OPERAND("Line", 8);
+        CHECK_CONST_UINT_OPERAND("Column", 9);
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        CHECK_OPERAND("Size", spv::Op::OpConstant, 11);
+        auto* size = _.FindDef(inst->word(11));
+        if (!_.IsIntScalarType(size->type_id()) || !size->word(3)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": expected operand Size is a "
+                 << "positive integer";
+        }
+        CHECK_CONST_UINT_OPERAND("Flags", 12);
+        for (uint32_t word_index = 13; word_index + 1 < num_words;
+             word_index += 2) {
+          CHECK_OPERAND("Value", spv::Op::OpConstant, word_index);
+          CHECK_OPERAND("Name", spv::Op::OpString, word_index + 1);
         }
-        case CommonDebugInfoDebugTypeEnum: {
-          CHECK_OPERAND("Name", spv::Op::OpString, 5);
+        break;
+      }
+      case CommonDebugInfoDebugTypeComposite: {
+        CHECK_OPERAND("Name", spv::Op::OpString, 5);
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+        CHECK_CONST_UINT_OPERAND("Line", 8);
+        CHECK_CONST_UINT_OPERAND("Column", 9);
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11);
+        if (!DoesDebugInfoOperandMatchExpectation(
+                _,
+                [](CommonDebugInfoInstructions dbg_inst) {
+                  return dbg_inst == CommonDebugInfoDebugInfoNone;
+                },
+                inst, 12)) {
+          CHECK_OPERAND("Size", spv::Op::OpConstant, 12);
+        }
+        CHECK_CONST_UINT_OPERAND("Flags", 13);
+        for (uint32_t word_index = 14; word_index < num_words; ++word_index) {
           if (!DoesDebugInfoOperandMatchExpectation(
                   _,
                   [](CommonDebugInfoInstructions dbg_inst) {
-                    return dbg_inst == CommonDebugInfoDebugInfoNone;
+                    return dbg_inst == CommonDebugInfoDebugTypeMember ||
+                           dbg_inst == CommonDebugInfoDebugFunction ||
+                           dbg_inst == CommonDebugInfoDebugTypeInheritance;
                   },
-                  inst, 6)) {
-            auto validate_underlying_type = ValidateOperandDebugType(
-                _, "Underlying Types", inst, 6, ext_inst_name, false);
-            if (validate_underlying_type != SPV_SUCCESS)
-              return validate_underlying_type;
-          }
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
-          CHECK_CONST_UINT_OPERAND("Line", 8);
-          CHECK_CONST_UINT_OPERAND("Column", 9);
-          auto validate_parent =
-              ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
-          if (validate_parent != SPV_SUCCESS) return validate_parent;
-          CHECK_OPERAND("Size", spv::Op::OpConstant, 11);
-          auto* size = _.FindDef(inst->word(11));
-          if (!_.IsIntScalarType(size->type_id()) || !size->word(3)) {
+                  inst, word_index)) {
             return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": expected operand Size is a "
-                   << "positive integer";
+                   << GetExtInstName(_, inst) << ": "
+                   << "expected operand Members "
+                   << "must be DebugTypeMember, DebugFunction, or "
+                      "DebugTypeInheritance";
           }
+        }
+        break;
+      }
+      case CommonDebugInfoDebugTypeMember: {
+        CHECK_OPERAND("Name", spv::Op::OpString, 5);
+        // TODO: We need a spec discussion that we have to allow member types
+        // to have template parameter.
+        auto validate_type = ValidateOperandDebugType(_, "Type", inst, 6, true);
+        if (validate_type != SPV_SUCCESS) return validate_type;
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+        CHECK_CONST_UINT_OPERAND("Line", 8);
+        CHECK_CONST_UINT_OPERAND("Column", 9);
+        // NonSemantic.Shader.DebugInfo doesn't have the Parent operand
+        if (vulkanDebugInfo) {
+          CHECK_OPERAND("Offset", spv::Op::OpConstant, 10);
+          CHECK_OPERAND("Size", spv::Op::OpConstant, 11);
           CHECK_CONST_UINT_OPERAND("Flags", 12);
-          for (uint32_t word_index = 13; word_index + 1 < num_words;
-               word_index += 2) {
-            CHECK_OPERAND("Value", spv::Op::OpConstant, word_index);
-            CHECK_OPERAND("Name", spv::Op::OpString, word_index + 1);
-          }
-          break;
+          if (num_words == 14) CHECK_OPERAND("Value", spv::Op::OpConstant, 13);
+        } else {
+          CHECK_DEBUG_OPERAND("Parent", CommonDebugInfoDebugTypeComposite, 10);
+          CHECK_OPERAND("Offset", spv::Op::OpConstant, 11);
+          CHECK_OPERAND("Size", spv::Op::OpConstant, 12);
+          CHECK_CONST_UINT_OPERAND("Flags", 13);
+          if (num_words == 15) CHECK_OPERAND("Value", spv::Op::OpConstant, 14);
+        }
+        break;
+      }
+      case CommonDebugInfoDebugTypeInheritance: {
+        CHECK_DEBUG_OPERAND("Child", CommonDebugInfoDebugTypeComposite, 5);
+        auto* debug_inst = _.FindDef(inst->word(5));
+        auto composite_type =
+            OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6));
+        if (composite_type != OpenCLDebugInfo100Class &&
+            composite_type != OpenCLDebugInfo100Structure) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected operand Child must be class or struct debug "
+                    "type";
+        }
+        CHECK_DEBUG_OPERAND("Parent", CommonDebugInfoDebugTypeComposite, 6);
+        debug_inst = _.FindDef(inst->word(6));
+        composite_type =
+            OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6));
+        if (composite_type != OpenCLDebugInfo100Class &&
+            composite_type != OpenCLDebugInfo100Structure) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected operand Parent must be class or struct debug "
+                    "type";
         }
-        case CommonDebugInfoDebugTypeComposite: {
-          CHECK_OPERAND("Name", spv::Op::OpString, 5);
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
-          CHECK_CONST_UINT_OPERAND("Line", 8);
-          CHECK_CONST_UINT_OPERAND("Column", 9);
-          auto validate_parent =
-              ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
-          if (validate_parent != SPV_SUCCESS) return validate_parent;
-          CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11);
+        CHECK_OPERAND("Offset", spv::Op::OpConstant, 7);
+        CHECK_OPERAND("Size", spv::Op::OpConstant, 8);
+        CHECK_CONST_UINT_OPERAND("Flags", 9);
+        break;
+      }
+      case CommonDebugInfoDebugFunction: {
+        CHECK_OPERAND("Name", spv::Op::OpString, 5);
+        CHECK_DEBUG_OPERAND("Type", CommonDebugInfoDebugTypeFunction, 6);
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+        CHECK_CONST_UINT_OPERAND("Line", 8);
+        CHECK_CONST_UINT_OPERAND("Column", 9);
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11);
+        CHECK_CONST_UINT_OPERAND("Flags", 12);
+        CHECK_CONST_UINT_OPERAND("Scope Line", 13);
+        // NonSemantic.Shader.DebugInfo.100 doesn't include a reference to the
+        // OpFunction
+        if (vulkanDebugInfo) {
+          if (num_words == 15) {
+            CHECK_DEBUG_OPERAND("Declaration",
+                                CommonDebugInfoDebugFunctionDeclaration, 14);
+          }
+        } else {
           if (!DoesDebugInfoOperandMatchExpectation(
                   _,
                   [](CommonDebugInfoInstructions dbg_inst) {
                     return dbg_inst == CommonDebugInfoDebugInfoNone;
                   },
-                  inst, 12)) {
-            CHECK_OPERAND("Size", spv::Op::OpConstant, 12);
-          }
-          CHECK_CONST_UINT_OPERAND("Flags", 13);
-          for (uint32_t word_index = 14; word_index < num_words; ++word_index) {
-            if (!DoesDebugInfoOperandMatchExpectation(
-                    _,
-                    [](CommonDebugInfoInstructions dbg_inst) {
-                      return dbg_inst == CommonDebugInfoDebugTypeMember ||
-                             dbg_inst == CommonDebugInfoDebugFunction ||
-                             dbg_inst == CommonDebugInfoDebugTypeInheritance;
-                    },
-                    inst, word_index)) {
-              return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                     << ext_inst_name() << ": "
-                     << "expected operand Members "
-                     << "must be DebugTypeMember, DebugFunction, or "
-                        "DebugTypeInheritance";
-            }
-          }
-          break;
-        }
-        case CommonDebugInfoDebugTypeMember: {
-          CHECK_OPERAND("Name", spv::Op::OpString, 5);
-          // TODO: We need a spec discussion that we have to allow member types
-          // to have template parameter.
-          auto validate_type =
-              ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true);
-          if (validate_type != SPV_SUCCESS) return validate_type;
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
-          CHECK_CONST_UINT_OPERAND("Line", 8);
-          CHECK_CONST_UINT_OPERAND("Column", 9);
-          // NonSemantic.Shader.DebugInfo doesn't have the Parent operand
-          if (vulkanDebugInfo) {
-            CHECK_OPERAND("Offset", spv::Op::OpConstant, 10);
-            CHECK_OPERAND("Size", spv::Op::OpConstant, 11);
-            CHECK_CONST_UINT_OPERAND("Flags", 12);
-            if (num_words == 14)
-              CHECK_OPERAND("Value", spv::Op::OpConstant, 13);
-          } else {
-            CHECK_DEBUG_OPERAND("Parent", CommonDebugInfoDebugTypeComposite,
-                                10);
-            CHECK_OPERAND("Offset", spv::Op::OpConstant, 11);
-            CHECK_OPERAND("Size", spv::Op::OpConstant, 12);
-            CHECK_CONST_UINT_OPERAND("Flags", 13);
-            if (num_words == 15)
-              CHECK_OPERAND("Value", spv::Op::OpConstant, 14);
-          }
-          break;
-        }
-        case CommonDebugInfoDebugTypeInheritance: {
-          CHECK_DEBUG_OPERAND("Child", CommonDebugInfoDebugTypeComposite, 5);
-          auto* debug_inst = _.FindDef(inst->word(5));
-          auto composite_type =
-              OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6));
-          if (composite_type != OpenCLDebugInfo100Class &&
-              composite_type != OpenCLDebugInfo100Structure) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected operand Child must be class or struct debug "
-                      "type";
-          }
-          CHECK_DEBUG_OPERAND("Parent", CommonDebugInfoDebugTypeComposite, 6);
-          debug_inst = _.FindDef(inst->word(6));
-          composite_type =
-              OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6));
-          if (composite_type != OpenCLDebugInfo100Class &&
-              composite_type != OpenCLDebugInfo100Structure) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected operand Parent must be class or struct debug "
-                      "type";
+                  inst, 14)) {
+            CHECK_OPERAND("Function", spv::Op::OpFunction, 14);
           }
-          CHECK_OPERAND("Offset", spv::Op::OpConstant, 7);
-          CHECK_OPERAND("Size", spv::Op::OpConstant, 8);
-          CHECK_CONST_UINT_OPERAND("Flags", 9);
-          break;
-        }
-        case CommonDebugInfoDebugFunction: {
-          CHECK_OPERAND("Name", spv::Op::OpString, 5);
-          CHECK_DEBUG_OPERAND("Type", CommonDebugInfoDebugTypeFunction, 6);
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
-          CHECK_CONST_UINT_OPERAND("Line", 8);
-          CHECK_CONST_UINT_OPERAND("Column", 9);
-          auto validate_parent =
-              ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
-          if (validate_parent != SPV_SUCCESS) return validate_parent;
-          CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11);
-          CHECK_CONST_UINT_OPERAND("Flags", 12);
-          CHECK_CONST_UINT_OPERAND("Scope Line", 13);
-          // NonSemantic.Shader.DebugInfo.100 doesn't include a reference to the
-          // OpFunction
-          if (vulkanDebugInfo) {
-            if (num_words == 15) {
-              CHECK_DEBUG_OPERAND("Declaration",
-                                  CommonDebugInfoDebugFunctionDeclaration, 14);
-            }
-          } else {
-            if (!DoesDebugInfoOperandMatchExpectation(
-                    _,
-                    [](CommonDebugInfoInstructions dbg_inst) {
-                      return dbg_inst == CommonDebugInfoDebugInfoNone;
-                    },
-                    inst, 14)) {
-              CHECK_OPERAND("Function", spv::Op::OpFunction, 14);
-            }
-            if (num_words == 16) {
-              CHECK_DEBUG_OPERAND("Declaration",
-                                  CommonDebugInfoDebugFunctionDeclaration, 15);
-            }
+          if (num_words == 16) {
+            CHECK_DEBUG_OPERAND("Declaration",
+                                CommonDebugInfoDebugFunctionDeclaration, 15);
           }
-          break;
-        }
-        case CommonDebugInfoDebugFunctionDeclaration: {
-          CHECK_OPERAND("Name", spv::Op::OpString, 5);
-          CHECK_DEBUG_OPERAND("Type", CommonDebugInfoDebugTypeFunction, 6);
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
-          CHECK_CONST_UINT_OPERAND("Line", 8);
-          CHECK_CONST_UINT_OPERAND("Column", 9);
-          auto validate_parent =
-              ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
-          if (validate_parent != SPV_SUCCESS) return validate_parent;
-          CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11);
-          CHECK_CONST_UINT_OPERAND("Flags", 12);
-          break;
         }
-        case CommonDebugInfoDebugLexicalBlock: {
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 5);
-          CHECK_CONST_UINT_OPERAND("Line", 6);
-          CHECK_CONST_UINT_OPERAND("Column", 7);
-          auto validate_parent =
-              ValidateOperandLexicalScope(_, "Parent", inst, 8, ext_inst_name);
-          if (validate_parent != SPV_SUCCESS) return validate_parent;
-          if (num_words == 10) CHECK_OPERAND("Name", spv::Op::OpString, 9);
-          break;
+        break;
+      }
+      case CommonDebugInfoDebugFunctionDeclaration: {
+        CHECK_OPERAND("Name", spv::Op::OpString, 5);
+        CHECK_DEBUG_OPERAND("Type", CommonDebugInfoDebugTypeFunction, 6);
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+        CHECK_CONST_UINT_OPERAND("Line", 8);
+        CHECK_CONST_UINT_OPERAND("Column", 9);
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11);
+        CHECK_CONST_UINT_OPERAND("Flags", 12);
+        break;
+      }
+      case CommonDebugInfoDebugLexicalBlock: {
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 5);
+        CHECK_CONST_UINT_OPERAND("Line", 6);
+        CHECK_CONST_UINT_OPERAND("Column", 7);
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 8);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        if (num_words == 10) CHECK_OPERAND("Name", spv::Op::OpString, 9);
+        break;
+      }
+      case CommonDebugInfoDebugScope: {
+        auto validate_scope = ValidateOperandLexicalScope(_, "Scope", inst, 5);
+        if (validate_scope != SPV_SUCCESS) return validate_scope;
+        if (num_words == 7) {
+          CHECK_DEBUG_OPERAND("Inlined At", CommonDebugInfoDebugInlinedAt, 6);
         }
-        case CommonDebugInfoDebugScope: {
-          auto validate_scope =
-              ValidateOperandLexicalScope(_, "Scope", inst, 5, ext_inst_name);
-          if (validate_scope != SPV_SUCCESS) return validate_scope;
-          if (num_words == 7) {
-            CHECK_DEBUG_OPERAND("Inlined At", CommonDebugInfoDebugInlinedAt, 6);
-          }
-          break;
+        break;
+      }
+      case CommonDebugInfoDebugLocalVariable: {
+        CHECK_OPERAND("Name", spv::Op::OpString, 5);
+        // TODO: We need a spec discussion that we have to allow local
+        // variable types to have template parameter.
+        auto validate_type = ValidateOperandDebugType(_, "Type", inst, 6, true);
+        if (validate_type != SPV_SUCCESS) return validate_type;
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+        CHECK_CONST_UINT_OPERAND("Line", 8);
+        CHECK_CONST_UINT_OPERAND("Column", 9);
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        CHECK_CONST_UINT_OPERAND("Flags", 11);
+        if (num_words == 13) {
+          CHECK_CONST_UINT_OPERAND("ArgNumber", 12);
         }
-        case CommonDebugInfoDebugLocalVariable: {
-          CHECK_OPERAND("Name", spv::Op::OpString, 5);
-          // TODO: We need a spec discussion that we have to allow local
-          // variable types to have template parameter.
-          auto validate_type =
-              ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true);
-          if (validate_type != SPV_SUCCESS) return validate_type;
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
-          CHECK_CONST_UINT_OPERAND("Line", 8);
-          CHECK_CONST_UINT_OPERAND("Column", 9);
-          auto validate_parent =
-              ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
-          if (validate_parent != SPV_SUCCESS) return validate_parent;
-          CHECK_CONST_UINT_OPERAND("Flags", 11);
-          if (num_words == 13) {
-            CHECK_CONST_UINT_OPERAND("ArgNumber", 12);
-          }
-          break;
+        break;
+      }
+      case CommonDebugInfoDebugDeclare: {
+        CHECK_DEBUG_OPERAND("Local Variable", CommonDebugInfoDebugLocalVariable,
+                            5);
+        auto* operand = _.FindDef(inst->word(6));
+        if (operand->opcode() != spv::Op::OpVariable &&
+            operand->opcode() != spv::Op::OpFunctionParameter) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected operand Variable must be a result id of "
+                    "OpVariable or OpFunctionParameter";
         }
-        case CommonDebugInfoDebugDeclare: {
-          CHECK_DEBUG_OPERAND("Local Variable",
-                              CommonDebugInfoDebugLocalVariable, 5);
-          auto* operand = _.FindDef(inst->word(6));
-          if (operand->opcode() != spv::Op::OpVariable &&
-              operand->opcode() != spv::Op::OpFunctionParameter) {
-            return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected operand Variable must be a result id of "
-                      "OpVariable or OpFunctionParameter";
-          }
 
-          CHECK_DEBUG_OPERAND("Expression", CommonDebugInfoDebugExpression, 7);
-          break;
-        }
-        case CommonDebugInfoDebugExpression: {
-          for (uint32_t word_index = 5; word_index < num_words; ++word_index) {
-            CHECK_DEBUG_OPERAND("Operation", CommonDebugInfoDebugOperation,
-                                word_index);
-          }
-          break;
+        CHECK_DEBUG_OPERAND("Expression", CommonDebugInfoDebugExpression, 7);
+        break;
+      }
+      case CommonDebugInfoDebugExpression: {
+        for (uint32_t word_index = 5; word_index < num_words; ++word_index) {
+          CHECK_DEBUG_OPERAND("Operation", CommonDebugInfoDebugOperation,
+                              word_index);
         }
-        case CommonDebugInfoDebugTypeTemplate: {
+        break;
+      }
+      case CommonDebugInfoDebugTypeTemplate: {
+        if (!DoesDebugInfoOperandMatchExpectation(
+                _,
+                [](CommonDebugInfoInstructions dbg_inst) {
+                  return dbg_inst == CommonDebugInfoDebugTypeComposite ||
+                         dbg_inst == CommonDebugInfoDebugFunction;
+                },
+                inst, 5)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << GetExtInstName(_, inst) << ": "
+                 << "expected operand Target must be DebugTypeComposite "
+                 << "or DebugFunction";
+        }
+        for (uint32_t word_index = 6; word_index < num_words; ++word_index) {
           if (!DoesDebugInfoOperandMatchExpectation(
                   _,
                   [](CommonDebugInfoInstructions dbg_inst) {
-                    return dbg_inst == CommonDebugInfoDebugTypeComposite ||
-                           dbg_inst == CommonDebugInfoDebugFunction;
+                    return dbg_inst ==
+                               CommonDebugInfoDebugTypeTemplateParameter ||
+                           dbg_inst ==
+                               CommonDebugInfoDebugTypeTemplateTemplateParameter;
                   },
-                  inst, 5)) {
+                  inst, word_index)) {
             return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": "
-                   << "expected operand Target must be DebugTypeComposite "
-                   << "or DebugFunction";
-          }
-          for (uint32_t word_index = 6; word_index < num_words; ++word_index) {
-            if (!DoesDebugInfoOperandMatchExpectation(
-                    _,
-                    [](CommonDebugInfoInstructions dbg_inst) {
-                      return dbg_inst ==
-                                 CommonDebugInfoDebugTypeTemplateParameter ||
-                             dbg_inst ==
-                                 CommonDebugInfoDebugTypeTemplateTemplateParameter;
-                    },
-                    inst, word_index)) {
-              return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                     << ext_inst_name() << ": "
-                     << "expected operand Parameters must be "
-                     << "DebugTypeTemplateParameter or "
-                     << "DebugTypeTemplateTemplateParameter";
-            }
+                   << GetExtInstName(_, inst) << ": "
+                   << "expected operand Parameters must be "
+                   << "DebugTypeTemplateParameter or "
+                   << "DebugTypeTemplateTemplateParameter";
           }
-          break;
         }
-        case CommonDebugInfoDebugTypeTemplateParameter: {
-          CHECK_OPERAND("Name", spv::Op::OpString, 5);
-          auto validate_actual_type = ValidateOperandDebugType(
-              _, "Actual Type", inst, 6, ext_inst_name, false);
-          if (validate_actual_type != SPV_SUCCESS) return validate_actual_type;
-          if (!DoesDebugInfoOperandMatchExpectation(
-                  _,
-                  [](CommonDebugInfoInstructions dbg_inst) {
-                    return dbg_inst == CommonDebugInfoDebugInfoNone;
-                  },
-                  inst, 7)) {
-            CHECK_OPERAND("Value", spv::Op::OpConstant, 7);
+        break;
+      }
+      case CommonDebugInfoDebugTypeTemplateParameter: {
+        CHECK_OPERAND("Name", spv::Op::OpString, 5);
+        auto validate_actual_type =
+            ValidateOperandDebugType(_, "Actual Type", inst, 6, false);
+        if (validate_actual_type != SPV_SUCCESS) return validate_actual_type;
+        if (!DoesDebugInfoOperandMatchExpectation(
+                _,
+                [](CommonDebugInfoInstructions dbg_inst) {
+                  return dbg_inst == CommonDebugInfoDebugInfoNone;
+                },
+                inst, 7)) {
+          CHECK_OPERAND("Value", spv::Op::OpConstant, 7);
+        }
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 8);
+        CHECK_CONST_UINT_OPERAND("Line", 9);
+        CHECK_CONST_UINT_OPERAND("Column", 10);
+        break;
+      }
+      case CommonDebugInfoDebugGlobalVariable: {
+        CHECK_OPERAND("Name", spv::Op::OpString, 5);
+        auto validate_type =
+            ValidateOperandDebugType(_, "Type", inst, 6, false);
+        if (validate_type != SPV_SUCCESS) return validate_type;
+        CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
+        CHECK_CONST_UINT_OPERAND("Line", 8);
+        CHECK_CONST_UINT_OPERAND("Column", 9);
+        auto validate_scope = ValidateOperandLexicalScope(_, "Scope", inst, 10);
+        if (validate_scope != SPV_SUCCESS) return validate_scope;
+        CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11);
+        if (!DoesDebugInfoOperandMatchExpectation(
+                _,
+                [](CommonDebugInfoInstructions dbg_inst) {
+                  return dbg_inst == CommonDebugInfoDebugInfoNone;
+                },
+                inst, 12)) {
+          auto* operand = _.FindDef(inst->word(12));
+          if (operand->opcode() != spv::Op::OpVariable &&
+              operand->opcode() != spv::Op::OpConstant) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << GetExtInstName(_, inst) << ": "
+                   << "expected operand Variable must be a result id of "
+                      "OpVariable or OpConstant or DebugInfoNone";
           }
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 8);
-          CHECK_CONST_UINT_OPERAND("Line", 9);
-          CHECK_CONST_UINT_OPERAND("Column", 10);
-          break;
         }
-        case CommonDebugInfoDebugGlobalVariable: {
-          CHECK_OPERAND("Name", spv::Op::OpString, 5);
-          auto validate_type = ValidateOperandDebugType(_, "Type", inst, 6,
-                                                        ext_inst_name, false);
-          if (validate_type != SPV_SUCCESS) return validate_type;
-          CHECK_DEBUG_OPERAND("Source", CommonDebugInfoDebugSource, 7);
-          CHECK_CONST_UINT_OPERAND("Line", 8);
-          CHECK_CONST_UINT_OPERAND("Column", 9);
-          auto validate_scope =
-              ValidateOperandLexicalScope(_, "Scope", inst, 10, ext_inst_name);
-          if (validate_scope != SPV_SUCCESS) return validate_scope;
-          CHECK_OPERAND("Linkage Name", spv::Op::OpString, 11);
-          if (!DoesDebugInfoOperandMatchExpectation(
-                  _,
-                  [](CommonDebugInfoInstructions dbg_inst) {
-                    return dbg_inst == CommonDebugInfoDebugInfoNone;
-                  },
-                  inst, 12)) {
-            auto* operand = _.FindDef(inst->word(12));
-            if (operand->opcode() != spv::Op::OpVariable &&
-                operand->opcode() != spv::Op::OpConstant) {
-              return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                     << ext_inst_name() << ": "
-                     << "expected operand Variable must be a result id of "
-                        "OpVariable or OpConstant or DebugInfoNone";
-            }
-          }
-          if (num_words == 15) {
-            CHECK_DEBUG_OPERAND("Static Member Declaration",
-                                CommonDebugInfoDebugTypeMember, 14);
-          }
-          break;
+        if (num_words == 15) {
+          CHECK_DEBUG_OPERAND("Static Member Declaration",
+                              CommonDebugInfoDebugTypeMember, 14);
         }
-        case CommonDebugInfoDebugInlinedAt: {
-          CHECK_CONST_UINT_OPERAND("Line", 5);
-          auto validate_scope =
-              ValidateOperandLexicalScope(_, "Scope", inst, 6, ext_inst_name);
-          if (validate_scope != SPV_SUCCESS) return validate_scope;
-          if (num_words == 8) {
-            CHECK_DEBUG_OPERAND("Inlined", CommonDebugInfoDebugInlinedAt, 7);
-          }
-          break;
+        break;
+      }
+      case CommonDebugInfoDebugInlinedAt: {
+        CHECK_CONST_UINT_OPERAND("Line", 5);
+        auto validate_scope = ValidateOperandLexicalScope(_, "Scope", inst, 6);
+        if (validate_scope != SPV_SUCCESS) return validate_scope;
+        if (num_words == 8) {
+          CHECK_DEBUG_OPERAND("Inlined", CommonDebugInfoDebugInlinedAt, 7);
         }
-        case CommonDebugInfoDebugValue: {
-          CHECK_DEBUG_OPERAND("Local Variable",
-                              CommonDebugInfoDebugLocalVariable, 5);
-          CHECK_DEBUG_OPERAND("Expression", CommonDebugInfoDebugExpression, 7);
-
-          for (uint32_t word_index = 8; word_index < num_words; ++word_index) {
-            // TODO: The following code simply checks if it is a const int
-            // scalar or a DebugLocalVariable or DebugGlobalVariable, but we
-            // have to check it using the same validation for Indexes of
-            // OpAccessChain.
-            if (!IsConstWithIntScalarType(_, inst, word_index) &&
-                !IsDebugVariableWithIntScalarType(_, inst, word_index)) {
-              return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                     << ext_inst_name() << ": expected operand Indexes is "
-                     << "OpConstant, DebugGlobalVariable, or "
-                     << "type is OpConstant with an integer scalar type";
-            }
+        break;
+      }
+      case CommonDebugInfoDebugValue: {
+        CHECK_DEBUG_OPERAND("Local Variable", CommonDebugInfoDebugLocalVariable,
+                            5);
+        CHECK_DEBUG_OPERAND("Expression", CommonDebugInfoDebugExpression, 7);
+
+        for (uint32_t word_index = 8; word_index < num_words; ++word_index) {
+          // TODO: The following code simply checks if it is a const int
+          // scalar or a DebugLocalVariable or DebugGlobalVariable, but we
+          // have to check it using the same validation for Indexes of
+          // OpAccessChain.
+          if (!IsConstWithIntScalarType(_, inst, word_index) &&
+              !IsDebugVariableWithIntScalarType(_, inst, word_index)) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << GetExtInstName(_, inst)
+                   << ": expected operand Indexes is "
+                   << "OpConstant, DebugGlobalVariable, or "
+                   << "type is OpConstant with an integer scalar type";
           }
-          break;
         }
-
-        // TODO: Add validation rules for remaining cases as well.
-        case CommonDebugInfoDebugTypePtrToMember:
-        case CommonDebugInfoDebugTypeTemplateTemplateParameter:
-        case CommonDebugInfoDebugTypeTemplateParameterPack:
-        case CommonDebugInfoDebugLexicalBlockDiscriminator:
-        case CommonDebugInfoDebugInlinedVariable:
-        case CommonDebugInfoDebugMacroDef:
-        case CommonDebugInfoDebugMacroUndef:
-        case CommonDebugInfoDebugImportedEntity:
-          break;
-        case CommonDebugInfoInstructionsMax:
-          assert(0);
-          break;
+        break;
       }
+
+      // TODO: Add validation rules for remaining cases as well.
+      case CommonDebugInfoDebugTypePtrToMember:
+      case CommonDebugInfoDebugTypeTemplateTemplateParameter:
+      case CommonDebugInfoDebugTypeTemplateParameterPack:
+      case CommonDebugInfoDebugLexicalBlockDiscriminator:
+      case CommonDebugInfoDebugInlinedVariable:
+      case CommonDebugInfoDebugMacroDef:
+      case CommonDebugInfoDebugMacroUndef:
+      case CommonDebugInfoDebugImportedEntity:
+        break;
+      case CommonDebugInfoInstructionsMax:
+        assert(0);
+        break;
     }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateExtInstNonsemanticClspvReflection(
+    ValidationState_t& _, const Instruction* inst) {
+  auto import_inst = _.FindDef(inst->GetOperandAs<uint32_t>(2));
+  const std::string name = import_inst->GetOperandAs<std::string>(1);
+  const std::string reflection = "NonSemantic.ClspvReflection.";
+  char* end_ptr;
+  auto version_string = name.substr(reflection.size());
+  if (version_string.empty()) {
+    return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
+           << "Missing NonSemantic.ClspvReflection import version";
+  }
+  uint32_t version =
+      static_cast<uint32_t>(std::strtoul(version_string.c_str(), &end_ptr, 10));
+  if (end_ptr && *end_ptr != '\0') {
+    return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
+           << "NonSemantic.ClspvReflection import does not encode the "
+              "version correctly";
+  }
+  if (version == 0 || version > NonSemanticClspvReflectionRevision) {
+    return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
+           << "Unknown NonSemantic.ClspvReflection import version";
+  }
+
+  return ValidateClspvReflectionInstruction(_, inst, version);
+}
+
+spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
+  const spv_ext_inst_type_t ext_inst_type =
+      spv_ext_inst_type_t(inst->ext_inst_type());
+
+  if (ext_inst_type == SPV_EXT_INST_TYPE_GLSL_STD_450) {
+    return ValidateExtInstGlslStd450(_, inst);
+  } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_STD) {
+    return ValidateExtInstOpenClStd(_, inst);
+  } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
+             ext_inst_type ==
+                 SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
+    return ValidateExtInstDebugInfo100(_, inst);
   } else if (ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) {
-    auto import_inst = _.FindDef(inst->GetOperandAs<uint32_t>(2));
-    const std::string name = import_inst->GetOperandAs<std::string>(1);
-    const std::string reflection = "NonSemantic.ClspvReflection.";
-    char* end_ptr;
-    auto version_string = name.substr(reflection.size());
-    if (version_string.empty()) {
-      return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
-             << "Missing NonSemantic.ClspvReflection import version";
-    }
-    uint32_t version = static_cast<uint32_t>(
-        std::strtoul(version_string.c_str(), &end_ptr, 10));
-    if (end_ptr && *end_ptr != '\0') {
-      return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
-             << "NonSemantic.ClspvReflection import does not encode the "
-                "version correctly";
-    }
-    if (version == 0 || version > NonSemanticClspvReflectionRevision) {
-      return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
-             << "Unknown NonSemantic.ClspvReflection import version";
-    }
-
-    return ValidateClspvReflectionInstruction(_, inst, version);
+    return ValidateExtInstNonsemanticClspvReflection(_, inst);
   }
 
   return SPV_SUCCESS;

+ 12 - 3
3rdparty/spirv-tools/source/val/validate_memory.cpp

@@ -399,10 +399,19 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
 
     uint32_t largest_scalar = 0;
     if (dst_sc == spv::StorageClass::PhysicalStorageBuffer) {
-      largest_scalar =
-          _.GetLargestScalarType(dst_pointer_type->GetOperandAs<uint32_t>(2));
+      if (dst_pointer_type->opcode() != spv::Op::OpTypeUntypedPointerKHR) {
+        largest_scalar =
+            _.GetLargestScalarType(dst_pointer_type->GetOperandAs<uint32_t>(2));
+      } else if (inst->type_id() != 0) {
+        largest_scalar = _.GetLargestScalarType(inst->type_id());
+      } else {
+        // TODO need to handle cases like OpStore and OpCopyMemorySized which
+        // don't have a result type
+      }
     }
-    if (src_sc == spv::StorageClass::PhysicalStorageBuffer) {
+    // TODO - Handle Untyped in OpCopyMemory
+    if (src_sc == spv::StorageClass::PhysicalStorageBuffer &&
+        src_pointer_type->opcode() != spv::Op::OpTypeUntypedPointerKHR) {
       largest_scalar = std::max(
           largest_scalar,
           _.GetLargestScalarType(src_pointer_type->GetOperandAs<uint32_t>(2)));

部分文件因文件數量過多而無法顯示