소스 검색

Updated spirv-tools.

Бранимир Караџић 3 주 전
부모
커밋
a3df232816
40개의 변경된 파일4283개의 추가작업 그리고 1758개의 파일을 삭제
  1. 1 1
      3rdparty/spirv-tools/include/generated/build-version.inc
  2. 1562 1513
      3rdparty/spirv-tools/include/generated/core_tables_body.inc
  3. 22 0
      3rdparty/spirv-tools/include/generated/core_tables_header.inc
  4. 1 0
      3rdparty/spirv-tools/source/opcode.cpp
  5. 2 1
      3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp
  6. 136 45
      3rdparty/spirv-tools/source/opt/amd_ext_to_khr.cpp
  7. 6 2
      3rdparty/spirv-tools/source/opt/constants.cpp
  8. 230 2
      3rdparty/spirv-tools/source/opt/folding_rules.cpp
  9. 4 1
      3rdparty/spirv-tools/source/opt/ir_context.cpp
  10. 1 0
      3rdparty/spirv-tools/source/opt/local_access_chain_convert_pass.cpp
  11. 1 0
      3rdparty/spirv-tools/source/opt/local_single_block_elim_pass.cpp
  12. 1 0
      3rdparty/spirv-tools/source/opt/local_single_store_elim_pass.cpp
  13. 2 2
      3rdparty/spirv-tools/source/opt/loop_descriptor.cpp
  14. 2 1
      3rdparty/spirv-tools/source/opt/loop_descriptor.h
  15. 2 2
      3rdparty/spirv-tools/source/opt/loop_fusion_pass.cpp
  16. 5 0
      3rdparty/spirv-tools/source/opt/loop_unroller.cpp
  17. 2 5
      3rdparty/spirv-tools/source/opt/pass.h
  18. 34 12
      3rdparty/spirv-tools/source/opt/split_combined_image_sampler_pass.cpp
  19. 5 0
      3rdparty/spirv-tools/source/opt/type_manager.cpp
  20. 5 1
      3rdparty/spirv-tools/source/opt/type_manager.h
  21. 3 0
      3rdparty/spirv-tools/source/opt/types.cpp
  22. 4 0
      3rdparty/spirv-tools/source/opt/types.h
  23. 40 1
      3rdparty/spirv-tools/source/opt/value_number_table.cpp
  24. 12 0
      3rdparty/spirv-tools/source/opt/value_number_table.h
  25. 17 0
      3rdparty/spirv-tools/source/util/bitutils.h
  26. 31 0
      3rdparty/spirv-tools/source/util/status.h
  27. 2 0
      3rdparty/spirv-tools/source/val/validate.cpp
  28. 6 0
      3rdparty/spirv-tools/source/val/validate.h
  29. 1 0
      3rdparty/spirv-tools/source/val/validate_annotation.cpp
  30. 2 1
      3rdparty/spirv-tools/source/val/validate_arithmetics.cpp
  31. 58 2
      3rdparty/spirv-tools/source/val/validate_builtins.cpp
  32. 1 2
      3rdparty/spirv-tools/source/val/validate_composites.cpp
  33. 19 3
      3rdparty/spirv-tools/source/val/validate_extensions.cpp
  34. 74 38
      3rdparty/spirv-tools/source/val/validate_function.cpp
  35. 5 6
      3rdparty/spirv-tools/source/val/validate_image.cpp
  36. 1010 0
      3rdparty/spirv-tools/source/val/validate_logical_pointers.cpp
  37. 259 101
      3rdparty/spirv-tools/source/val/validate_memory.cpp
  38. 1 3
      3rdparty/spirv-tools/source/val/validate_mode_setting.cpp
  39. 688 10
      3rdparty/spirv-tools/source/val/validate_ray_tracing_reorder.cpp
  40. 26 3
      3rdparty/spirv-tools/source/val/validation_state.cpp

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

@@ -1 +1 @@
-"v2025.4", "SPIRV-Tools v2025.4 v2025.4-28-g8b4ee452"
+"v2025.5", "SPIRV-Tools v2025.5 v2025.4-64-gd2a11ec9"

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1562 - 1513
3rdparty/spirv-tools/include/generated/core_tables_body.inc


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

@@ -30,6 +30,26 @@ enum class PrintingClass : uint32_t {
 };
 
 enum Extension : uint32_t {
+  kSPV_ALTERA_arbitrary_precision_fixed_point,
+  kSPV_ALTERA_arbitrary_precision_floating_point,
+  kSPV_ALTERA_arbitrary_precision_integers,
+  kSPV_ALTERA_blocking_pipes,
+  kSPV_ALTERA_fpga_argument_interfaces,
+  kSPV_ALTERA_fpga_buffer_location,
+  kSPV_ALTERA_fpga_cluster_attributes,
+  kSPV_ALTERA_fpga_dsp_control,
+  kSPV_ALTERA_fpga_invocation_pipelining_attributes,
+  kSPV_ALTERA_fpga_latency_control,
+  kSPV_ALTERA_fpga_loop_controls,
+  kSPV_ALTERA_fpga_memory_accesses,
+  kSPV_ALTERA_fpga_memory_attributes,
+  kSPV_ALTERA_fpga_reg,
+  kSPV_ALTERA_global_variable_fpga_decorations,
+  kSPV_ALTERA_io_pipes,
+  kSPV_ALTERA_loop_fuse,
+  kSPV_ALTERA_runtime_aligned,
+  kSPV_ALTERA_task_sequence,
+  kSPV_ALTERA_usm_storage_classes,
   kSPV_AMDX_shader_enqueue,
   kSPV_AMD_gcn_shader,
   kSPV_AMD_gpu_shader_half_float,
@@ -59,10 +79,12 @@ enum Extension : uint32_t {
   kSPV_EXT_physical_storage_buffer,
   kSPV_EXT_relaxed_printf_string_address_space,
   kSPV_EXT_replicated_composites,
+  kSPV_EXT_shader_64bit_indexing,
   kSPV_EXT_shader_atomic_float16_add,
   kSPV_EXT_shader_atomic_float_add,
   kSPV_EXT_shader_atomic_float_min_max,
   kSPV_EXT_shader_image_int64,
+  kSPV_EXT_shader_invocation_reorder,
   kSPV_EXT_shader_stencil_export,
   kSPV_EXT_shader_tile_image,
   kSPV_EXT_shader_viewport_index_layer,

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

@@ -267,6 +267,7 @@ int32_t spvOpcodeGeneratesType(spv::Op op) {
     // spv::Op::OpTypeAccelerationStructureNV
     case spv::Op::OpTypeRayQueryKHR:
     case spv::Op::OpTypeHitObjectNV:
+    case spv::Op::OpTypeHitObjectEXT:
     case spv::Op::OpTypeUntypedPointerKHR:
     case spv::Op::OpTypeNodePayloadArrayAMDX:
     case spv::Op::OpTypeTensorLayoutNV:

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

@@ -305,7 +305,7 @@ Pass::Status AggressiveDCEPass::ProcessDebugInformation(
               NonSemanticShaderDebugInfo100DebugValue) {
         uint32_t id = inst->GetSingleWordInOperand(kDebugValueValue);
         auto def = get_def_use_mgr()->GetDef(id);
-        if (!live_insts_.Set(def->unique_id())) {
+        if (!IsLive(def)) {
           AddToWorklist(inst);
           uint32_t undef_id = Type2Undef(def->type_id());
           if (undef_id == 0) {
@@ -1123,6 +1123,7 @@ void AggressiveDCEPass::InitExtensions() {
       "SPV_NV_shader_invocation_reorder",
       "SPV_NV_cluster_acceleration_structure",
       "SPV_NV_linear_swept_spheres",
+      "SPV_KHR_maximal_reconvergence",
   });
 }
 

+ 136 - 45
3rdparty/spirv-tools/source/opt/amd_ext_to_khr.cpp

@@ -76,6 +76,7 @@ bool ReplaceTrinaryMinMax(IRContext* ctx, Instruction* inst,
 
   Instruction* temp = ir_builder.AddNaryExtendedInstruction(
       inst->type_id(), glsl405_ext_inst_id, opcode, {op1, op2});
+  if (temp == nullptr) return false;
 
   Instruction::OperandList new_operands;
   new_operands.push_back({SPV_OPERAND_TYPE_ID, {glsl405_ext_inst_id}});
@@ -114,9 +115,12 @@ bool ReplaceTrinaryMid(IRContext* ctx, Instruction* inst,
   Instruction* min = ir_builder.AddNaryExtendedInstruction(
       inst->type_id(), glsl405_ext_inst_id, static_cast<uint32_t>(min_opcode),
       {op2, op3});
+  if (min == nullptr) return false;
+
   Instruction* max = ir_builder.AddNaryExtendedInstruction(
       inst->type_id(), glsl405_ext_inst_id, static_cast<uint32_t>(max_opcode),
       {op2, op3});
+  if (max == nullptr) return false;
 
   Instruction::OperandList new_operands;
   new_operands.push_back({SPV_OPERAND_TYPE_ID, {glsl405_ext_inst_id}});
@@ -226,16 +230,20 @@ bool ReplaceSwizzleInvocations(IRContext* ctx, Instruction* inst,
   // Get the subgroup invocation id.
   uint32_t var_id = ctx->GetBuiltinInputVarId(
       uint32_t(spv::BuiltIn::SubgroupLocalInvocationId));
-  assert(var_id != 0 && "Could not get SubgroupLocalInvocationId variable.");
+  if (var_id == 0) return false;
   Instruction* var_inst = ctx->get_def_use_mgr()->GetDef(var_id);
+  if (var_inst == nullptr) return false;
   Instruction* var_ptr_type =
       ctx->get_def_use_mgr()->GetDef(var_inst->type_id());
+  if (var_ptr_type == nullptr) return false;
   uint32_t uint_type_id = var_ptr_type->GetSingleWordInOperand(1);
+  if (uint_type_id == 0) return false;
 
-  // TODO(1841): Handle id overflow.
   Instruction* id = ir_builder.AddLoad(uint_type_id, var_id);
+  if (id == nullptr) return false;
 
   uint32_t quad_mask = ir_builder.GetUintConstantId(3);
+  if (quad_mask == 0) return false;
 
   // This gives the offset in the group of 4 of this invocation.
   Instruction* quad_idx = ir_builder.AddBinaryOp(
@@ -262,26 +270,41 @@ bool ReplaceSwizzleInvocations(IRContext* ctx, Instruction* inst,
 
   // Do the group operations
   uint32_t uint_max_id = ir_builder.GetUintConstantId(0xFFFFFFFF);
+  if (uint_max_id == 0) return false;
   uint32_t subgroup_scope =
       ir_builder.GetUintConstantId(uint32_t(spv::Scope::Subgroup));
+  if (subgroup_scope == 0) return false;
+  const auto* vec_type = type_mgr->GetUIntVectorType(4);
+  if (vec_type == nullptr) return false;
   const auto* ballot_value_const = const_mgr->GetConstant(
-      type_mgr->GetUIntVectorType(4),
-      {uint_max_id, uint_max_id, uint_max_id, uint_max_id});
+      vec_type, {uint_max_id, uint_max_id, uint_max_id, uint_max_id});
+  if (ballot_value_const == nullptr) return false;
   Instruction* ballot_value =
       const_mgr->GetDefiningInstruction(ballot_value_const);
-  // TODO(1841): Handle id overflow.
+  if (ballot_value == nullptr) return false;
+  uint32_t bool_type_id = type_mgr->GetBoolTypeId();
+  if (bool_type_id == 0) return false;
   Instruction* is_active = ir_builder.AddNaryOp(
-      type_mgr->GetBoolTypeId(), spv::Op::OpGroupNonUniformBallotBitExtract,
+      bool_type_id, spv::Op::OpGroupNonUniformBallotBitExtract,
       {subgroup_scope, ballot_value->result_id(), target_inv->result_id()});
-  // TODO(1841): Handle id overflow.
+  if (is_active == nullptr) return false;
   Instruction* shuffle =
       ir_builder.AddNaryOp(inst->type_id(), spv::Op::OpGroupNonUniformShuffle,
                            {subgroup_scope, data_id, target_inv->result_id()});
+  if (shuffle == nullptr) return false;
 
   // Create the null constant to use in the select.
-  const auto* null = const_mgr->GetConstant(type_mgr->GetType(inst->type_id()),
-                                            std::vector<uint32_t>());
+  const auto* result_type = type_mgr->GetType(inst->type_id());
+  if (result_type == nullptr) return false;
+  const auto* null =
+      const_mgr->GetConstant(result_type, std::vector<uint32_t>());
+  if (null == nullptr) {
+    return false;
+  }
   Instruction* null_inst = const_mgr->GetDefiningInstruction(null);
+  if (null_inst == nullptr) {
+    return false;
+  }
 
   // Build the select.
   inst->SetOpcode(spv::Op::OpSelect);
@@ -346,30 +369,38 @@ bool ReplaceSwizzleInvocationsMasked(
   uint32_t data_id = inst->GetSingleWordInOperand(2);
 
   Instruction* mask_inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(3));
+  if (mask_inst == nullptr) return false;
   assert(mask_inst->opcode() == spv::Op::OpConstantComposite &&
          "The mask is suppose to be a vector constant.");
   assert(mask_inst->NumInOperands() == 3 &&
          "The mask is suppose to have 3 components.");
 
   uint32_t uint_x = mask_inst->GetSingleWordInOperand(0);
+  if (uint_x == 0) return false;
   uint32_t uint_y = mask_inst->GetSingleWordInOperand(1);
+  if (uint_y == 0) return false;
   uint32_t uint_z = mask_inst->GetSingleWordInOperand(2);
+  if (uint_z == 0) return false;
 
   // Get the subgroup invocation id.
   uint32_t var_id = ctx->GetBuiltinInputVarId(
       uint32_t(spv::BuiltIn::SubgroupLocalInvocationId));
+  if (var_id == 0) return false;
   ctx->AddExtension("SPV_KHR_shader_ballot");
-  assert(var_id != 0 && "Could not get SubgroupLocalInvocationId variable.");
   Instruction* var_inst = ctx->get_def_use_mgr()->GetDef(var_id);
+  if (var_inst == nullptr) return false;
   Instruction* var_ptr_type =
       ctx->get_def_use_mgr()->GetDef(var_inst->type_id());
+  if (var_ptr_type == nullptr) return false;
   uint32_t uint_type_id = var_ptr_type->GetSingleWordInOperand(1);
+  if (uint_type_id == 0) return false;
 
-  // TODO(1841): Handle id overflow.
   Instruction* id = ir_builder.AddLoad(uint_type_id, var_id);
+  if (id == nullptr) return false;
 
   // Do the bitwise operations.
   uint32_t mask_extended = ir_builder.GetUintConstantId(0xFFFFFFE0);
+  if (mask_extended == 0) return false;
   Instruction* and_mask = ir_builder.AddBinaryOp(
       uint_type_id, spv::Op::OpBitwiseOr, uint_x, mask_extended);
   if (and_mask == nullptr) return false;
@@ -386,26 +417,37 @@ bool ReplaceSwizzleInvocationsMasked(
 
   // Do the group operations
   uint32_t uint_max_id = ir_builder.GetUintConstantId(0xFFFFFFFF);
+  if (uint_max_id == 0) return false;
   uint32_t subgroup_scope =
       ir_builder.GetUintConstantId(uint32_t(spv::Scope::Subgroup));
+  if (subgroup_scope == 0) return false;
+  const auto* vec_type = type_mgr->GetUIntVectorType(4);
+  if (vec_type == nullptr) return false;
   const auto* ballot_value_const = const_mgr->GetConstant(
-      type_mgr->GetUIntVectorType(4),
-      {uint_max_id, uint_max_id, uint_max_id, uint_max_id});
+      vec_type, {uint_max_id, uint_max_id, uint_max_id, uint_max_id});
+  if (ballot_value_const == nullptr) return false;
   Instruction* ballot_value =
       const_mgr->GetDefiningInstruction(ballot_value_const);
-  // TODO(1841): Handle id overflow.
+  if (ballot_value == nullptr) return false;
+  uint32_t bool_type_id = type_mgr->GetBoolTypeId();
+  if (bool_type_id == 0) return false;
   Instruction* is_active = ir_builder.AddNaryOp(
-      type_mgr->GetBoolTypeId(), spv::Op::OpGroupNonUniformBallotBitExtract,
+      bool_type_id, spv::Op::OpGroupNonUniformBallotBitExtract,
       {subgroup_scope, ballot_value->result_id(), target_inv->result_id()});
-  // TODO(1841): Handle id overflow.
+  if (is_active == nullptr) return false;
   Instruction* shuffle =
       ir_builder.AddNaryOp(inst->type_id(), spv::Op::OpGroupNonUniformShuffle,
                            {subgroup_scope, data_id, target_inv->result_id()});
+  if (shuffle == nullptr) return false;
 
   // Create the null constant to use in the select.
-  const auto* null = const_mgr->GetConstant(type_mgr->GetType(inst->type_id()),
-                                            std::vector<uint32_t>());
+  const auto* result_type = type_mgr->GetType(inst->type_id());
+  if (result_type == nullptr) return false;
+  const auto* null =
+      const_mgr->GetConstant(result_type, std::vector<uint32_t>());
+  if (null == nullptr) return false;
   Instruction* null_inst = const_mgr->GetDefiningInstruction(null);
+  if (null_inst == nullptr) return false;
 
   // Build the select.
   inst->SetOpcode(spv::Op::OpSelect);
@@ -439,21 +481,24 @@ bool ReplaceWriteInvocation(IRContext* ctx, Instruction* inst,
                             const std::vector<const analysis::Constant*>&) {
   uint32_t var_id = ctx->GetBuiltinInputVarId(
       uint32_t(spv::BuiltIn::SubgroupLocalInvocationId));
-  ctx->AddCapability(spv::Capability::SubgroupBallotKHR);
-  ctx->AddExtension("SPV_KHR_shader_ballot");
-  assert(var_id != 0 && "Could not get SubgroupLocalInvocationId variable.");
+  if (var_id == 0) return false;
   Instruction* var_inst = ctx->get_def_use_mgr()->GetDef(var_id);
+  if (var_inst == nullptr) return false;
   Instruction* var_ptr_type =
       ctx->get_def_use_mgr()->GetDef(var_inst->type_id());
+  if (var_ptr_type == nullptr) return false;
+  ctx->AddCapability(spv::Capability::SubgroupBallotKHR);
+  ctx->AddExtension("SPV_KHR_shader_ballot");
 
   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);
+  if (t == nullptr) return false;
   analysis::Bool bool_type;
   uint32_t bool_type_id = ctx->get_type_mgr()->GetTypeInstruction(&bool_type);
+  if (bool_type_id == 0) return false;
   Instruction* cmp =
       ir_builder.AddBinaryOp(bool_type_id, spv::Op::OpIEqual, t->result_id(),
                              inst->GetSingleWordInOperand(4));
@@ -500,7 +545,8 @@ bool ReplaceMbcnt(IRContext* context, Instruction* inst,
 
   uint32_t var_id =
       context->GetBuiltinInputVarId(uint32_t(spv::BuiltIn::SubgroupLtMask));
-  assert(var_id != 0 && "Could not get SubgroupLtMask variable.");
+  if (var_id == 0) return false;
+
   context->AddCapability(spv::Capability::GroupNonUniformBallot);
   Instruction* var_inst = def_use_mgr->GetDef(var_id);
   Instruction* var_ptr_type = def_use_mgr->GetDef(var_inst->type_id());
@@ -513,6 +559,7 @@ bool ReplaceMbcnt(IRContext* context, Instruction* inst,
   analysis::Vector temp_type(GetUIntType(context), 2);
   const analysis::Type* shuffle_type =
       context->get_type_mgr()->GetRegisteredType(&temp_type);
+  if (shuffle_type == nullptr) return false;
   uint32_t shuffle_type_id = type_mgr->GetTypeInstruction(shuffle_type);
 
   uint32_t mask_id = inst->GetSingleWordInOperand(2);
@@ -526,10 +573,13 @@ bool ReplaceMbcnt(IRContext* context, Instruction* inst,
       context, inst,
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
   Instruction* load = ir_builder.AddLoad(var_type->result_id(), var_id);
+  if (load == nullptr) return false;
   Instruction* shuffle = ir_builder.AddVectorShuffle(
       shuffle_type_id, load->result_id(), load->result_id(), {0, 1});
+  if (shuffle == nullptr) return false;
   Instruction* bitcast = ir_builder.AddUnaryOp(
       mask_inst->type_id(), spv::Op::OpBitcast, shuffle->result_id());
+  if (bitcast == nullptr) return false;
   Instruction* t =
       ir_builder.AddBinaryOp(mask_inst->type_id(), spv::Op::OpBitwiseAnd,
                              bitcast->result_id(), mask_id);
@@ -588,9 +638,13 @@ bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst,
   analysis::ConstantManager* const_mgr = ctx->get_constant_mgr();
 
   uint32_t float_type_id = type_mgr->GetFloatTypeId();
+  if (float_type_id == 0) return false;
   const analysis::Type* v2_float_type = type_mgr->GetFloatVectorType(2);
+  if (v2_float_type == nullptr) return false;
   uint32_t v2_float_type_id = type_mgr->GetId(v2_float_type);
+  if (v2_float_type_id == 0) return false;
   uint32_t bool_id = type_mgr->GetBoolTypeId();
+  if (bool_id == 0) return false;
 
   InstructionBuilder ir_builder(
       ctx, inst,
@@ -604,23 +658,29 @@ bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst,
     glsl405_ext_inst_id =
         ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
   }
+  if (glsl405_ext_inst_id == 0) return false;
 
   // Get the constants that will be used.
   uint32_t f0_const_id = const_mgr->GetFloatConstId(0.0);
+  if (f0_const_id == 0) return false;
   uint32_t f2_const_id = const_mgr->GetFloatConstId(2.0);
+  if (f2_const_id == 0) return false;
   uint32_t f0_5_const_id = const_mgr->GetFloatConstId(0.5);
+  if (f0_5_const_id == 0) return false;
   const analysis::Constant* vec_const =
       const_mgr->GetConstant(v2_float_type, {f0_5_const_id, f0_5_const_id});
-  uint32_t vec_const_id =
-      const_mgr->GetDefiningInstruction(vec_const)->result_id();
+  if (vec_const == nullptr) return false;
+  Instruction* vec_const_inst = const_mgr->GetDefiningInstruction(vec_const);
+  if (vec_const_inst == nullptr) return false;
+  uint32_t vec_const_id = vec_const_inst->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.
+  if (x == nullptr) return false;
   Instruction* y = ir_builder.AddCompositeExtract(float_type_id, input_id, {1});
-  // TODO(1-841): Handle id overflow.
+  if (y == nullptr) return false;
   Instruction* z = ir_builder.AddCompositeExtract(float_type_id, input_id, {2});
+  if (z == nullptr) return false;
 
   // Negate the input values.
   Instruction* nx =
@@ -629,14 +689,20 @@ bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst,
       ir_builder.AddUnaryOp(float_type_id, spv::Op::OpFNegate, y->result_id());
   Instruction* nz =
       ir_builder.AddUnaryOp(float_type_id, spv::Op::OpFNegate, z->result_id());
+  if (nx == nullptr) return false;
+  if (ny == nullptr) return false;
+  if (nz == nullptr) return false;
 
   // Get the abolsute values of the inputs.
   Instruction* ax = ir_builder.AddNaryExtendedInstruction(
       float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {x->result_id()});
+  if (ax == nullptr) return false;
   Instruction* ay = ir_builder.AddNaryExtendedInstruction(
       float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {y->result_id()});
+  if (ay == nullptr) return false;
   Instruction* az = ir_builder.AddNaryExtendedInstruction(
       float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {z->result_id()});
+  if (az == nullptr) return false;
 
   // Find which values are negative.  Used in later computations.
   Instruction* is_z_neg = ir_builder.AddBinaryOp(
@@ -653,9 +719,11 @@ bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst,
   Instruction* amax_x_y = ir_builder.AddNaryExtendedInstruction(
       float_type_id, glsl405_ext_inst_id, GLSLstd450FMax,
       {ax->result_id(), ay->result_id()});
+  if (amax_x_y == nullptr) return false;
   Instruction* amax = ir_builder.AddNaryExtendedInstruction(
       float_type_id, glsl405_ext_inst_id, GLSLstd450FMax,
       {az->result_id(), amax_x_y->result_id()});
+  if (amax == nullptr) return false;
   Instruction* cubema = ir_builder.AddBinaryOp(float_type_id, spv::Op::OpFMul,
                                                f2_const_id, amax->result_id());
   if (cubema == nullptr) return false;
@@ -667,6 +735,7 @@ bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst,
   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());
+  if (not_is_z_max == nullptr) return false;
   Instruction* y_gr_x =
       ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual,
                              ay->result_id(), ax->result_id());
@@ -677,35 +746,37 @@ bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst,
   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.
+  if (cubesc_case_1 == nullptr) return false;
   Instruction* cubesc_case_2 = ir_builder.AddSelect(
       float_type_id, is_x_neg->result_id(), z->result_id(), nz->result_id());
+  if (cubesc_case_2 == nullptr) return false;
   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());
+  if (sel == nullptr) return false;
   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());
+  if (cubesc == nullptr) return false;
 
   // 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());
+  if (cubetc_case_1 == nullptr) return false;
   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());
+  if (cubetc == nullptr) return false;
 
   // Do the division
   Instruction* cube = ir_builder.AddCompositeConstruct(
       v2_float_type_id, {cubesc->result_id(), cubetc->result_id()});
+  if (cube == nullptr) return false;
   Instruction* denom = ir_builder.AddCompositeConstruct(
       v2_float_type_id, {cubema->result_id(), cubema->result_id()});
+  if (denom == nullptr) return false;
   Instruction* div = ir_builder.AddBinaryOp(
       v2_float_type_id, spv::Op::OpFDiv, cube->result_id(), denom->result_id());
   if (div == nullptr) return false;
@@ -756,7 +827,9 @@ bool ReplaceCubeFaceIndex(IRContext* ctx, Instruction* inst,
   analysis::ConstantManager* const_mgr = ctx->get_constant_mgr();
 
   uint32_t float_type_id = type_mgr->GetFloatTypeId();
+  if (float_type_id == 0) return false;
   uint32_t bool_id = type_mgr->GetBoolTypeId();
+  if (bool_id == 0) return false;
 
   InstructionBuilder ir_builder(
       ctx, inst,
@@ -773,27 +846,37 @@ bool ReplaceCubeFaceIndex(IRContext* ctx, Instruction* inst,
 
   // Get the constants that will be used.
   uint32_t f0_const_id = const_mgr->GetFloatConstId(0.0);
+  if (f0_const_id == 0) return false;
   uint32_t f1_const_id = const_mgr->GetFloatConstId(1.0);
+  if (f1_const_id == 0) return false;
   uint32_t f2_const_id = const_mgr->GetFloatConstId(2.0);
+  if (f2_const_id == 0) return false;
   uint32_t f3_const_id = const_mgr->GetFloatConstId(3.0);
+  if (f3_const_id == 0) return false;
   uint32_t f4_const_id = const_mgr->GetFloatConstId(4.0);
+  if (f4_const_id == 0) return false;
   uint32_t f5_const_id = const_mgr->GetFloatConstId(5.0);
+  if (f5_const_id == 0) return false;
 
   // 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.
+  if (x == nullptr) return false;
   Instruction* y = ir_builder.AddCompositeExtract(float_type_id, input_id, {1});
+  if (y == nullptr) return false;
   // TODO(1-841): Handle id overflow.
   Instruction* z = ir_builder.AddCompositeExtract(float_type_id, input_id, {2});
+  if (z == nullptr) return false;
 
   // Get the absolute values of the inputs.
   Instruction* ax = ir_builder.AddNaryExtendedInstruction(
       float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {x->result_id()});
+  if (ax == nullptr) return false;
   Instruction* ay = ir_builder.AddNaryExtendedInstruction(
       float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {y->result_id()});
+  if (ay == nullptr) return false;
   Instruction* az = ir_builder.AddNaryExtendedInstruction(
       float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {z->result_id()});
+  if (az == nullptr) return false;
 
   // Find which values are negative.  Used in later computations.
   Instruction* is_z_neg = ir_builder.AddBinaryOp(
@@ -810,6 +893,7 @@ bool ReplaceCubeFaceIndex(IRContext* ctx, Instruction* inst,
   Instruction* amax_x_y = ir_builder.AddNaryExtendedInstruction(
       float_type_id, glsl405_ext_inst_id, GLSLstd450FMax,
       {ax->result_id(), ay->result_id()});
+  if (amax_x_y == nullptr) return false;
   Instruction* is_z_max =
       ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual,
                              az->result_id(), amax_x_y->result_id());
@@ -820,21 +904,21 @@ bool ReplaceCubeFaceIndex(IRContext* ctx, Instruction* inst,
   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.
+  if (case_z == nullptr) return false;
   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.
+  if (case_y == nullptr) return false;
   Instruction* case_x = ir_builder.AddSelect(
       float_type_id, is_x_neg->result_id(), f1_const_id, f0_const_id);
+  if (case_x == nullptr) return false;
 
   // 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());
+  if (sel == nullptr) return false;
 
   // Get the final result by adding 0.5 to |div|.
   inst->SetOpcode(spv::Op::OpSelect);
@@ -974,11 +1058,18 @@ Pass::Status AmdExtensionToKhrPass::Process() {
       std::unique_ptr<AmdExtFoldingRules>(new AmdExtFoldingRules(context())),
       MakeUnique<AmdExtConstFoldingRules>(context()));
   for (Function& func : *get_module()) {
-    func.ForEachInst([&changed, &folder](Instruction* inst) {
-      if (folder.FoldInstruction(inst)) {
-        changed = true;
-      }
-    });
+    bool failed =
+        !func.WhileEachInst([&changed, &folder, this](Instruction* inst) {
+          if (folder.FoldInstruction(inst)) {
+            changed = true;
+            return true;
+          } else if (context()->id_overflow()) {
+            return false;
+          }
+          return true;
+        });
+
+    if (failed) return Status::Failure;
   }
 
   // Now that instruction that require the extensions have been removed, we can

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

@@ -462,7 +462,9 @@ const Constant* ConstantManager::GetNumericVectorConstantWithWords(
 
 uint32_t ConstantManager::GetFloatConstId(float val) {
   const Constant* c = GetFloatConst(val);
-  return GetDefiningInstruction(c)->result_id();
+  Instruction* inst = GetDefiningInstruction(c);
+  if (inst == nullptr) return 0;
+  return inst->result_id();
 }
 
 const Constant* ConstantManager::GetFloatConst(float val) {
@@ -474,7 +476,9 @@ const Constant* ConstantManager::GetFloatConst(float val) {
 
 uint32_t ConstantManager::GetDoubleConstId(double val) {
   const Constant* c = GetDoubleConst(val);
-  return GetDefiningInstruction(c)->result_id();
+  Instruction* inst = GetDefiningInstruction(c);
+  if (inst == nullptr) return 0;
+  return inst->result_id();
 }
 
 const Constant* ConstantManager::GetDoubleConst(double val) {

+ 230 - 2
3rdparty/spirv-tools/source/opt/folding_rules.cpp

@@ -194,7 +194,7 @@ std::vector<uint32_t> GetWordsFromNumericScalarOrVectorConstant(
       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];
+        compacted_word |= (words[i] & 0xFF);
       }
       return {compacted_word};
     } else if (ElementWidth(c->type()) == 16) {
@@ -205,7 +205,7 @@ std::vector<uint32_t> GetWordsFromNumericScalarOrVectorConstant(
       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;
+        uint32_t compacted_word = (word2 << 16) | (word1 & 0xFFFF);
         compacted_words.push_back(compacted_word);
       }
       return compacted_words;
@@ -2819,6 +2819,230 @@ FoldingRule RedundantSUMod() {
   };
 }
 
+// Utility function for applying |callback| to |input1| and |input2|.
+// If they are vectors it applies element wise.
+// The constants |input1| and |input2| must be integers or a vector of integers.
+template <typename Callback>
+void ForEachIntegerConstantPair(analysis::ConstantManager* const_mgr,
+                                const analysis::Constant* input1,
+                                const analysis::Constant* input2,
+                                Callback&& callback) {
+  assert(input1 && input2);
+
+  auto Dispatch = [&callback](const analysis::Constant* lhs,
+                              const analysis::Constant* rhs) {
+    assert(lhs->type()->AsInteger());
+    const analysis::Integer* type = lhs->type()->AsInteger();
+    uint32_t width = type->AsInteger()->width();
+    assert(width == 32 || width == 64);
+    if (width == 32) {
+      callback(lhs->GetU32(), rhs->GetU32());
+    } else {
+      callback(lhs->GetU64(), rhs->GetU64());
+    }
+  };
+
+  const analysis::Type* type = input1->type();
+  if (const analysis::Vector* vector_type = type->AsVector()) {
+    const analysis::Type* ele_type = vector_type->element_type();
+    assert(ele_type->AsInteger());
+    for (uint32_t i = 0; i != vector_type->element_count(); ++i) {
+      const analysis::Constant* input1_comp = nullptr;
+      if (const analysis::VectorConstant* input1_vector =
+              input1->AsVectorConstant()) {
+        input1_comp = input1_vector->GetComponents()[i];
+      } else {
+        assert(input1->AsNullConstant());
+        input1_comp = const_mgr->GetConstant(ele_type, {});
+      }
+
+      const analysis::Constant* input2_comp = nullptr;
+      if (const analysis::VectorConstant* input2_vector =
+              input2->AsVectorConstant()) {
+        input2_comp = input2_vector->GetComponents()[i];
+      } else {
+        assert(input2->AsNullConstant());
+        input2_comp = const_mgr->GetConstant(ele_type, {});
+      }
+
+      assert(ele_type->AsInteger());
+      Dispatch(input1_comp, input2_comp);
+    }
+
+  } else {
+    assert(type->AsInteger());
+    Dispatch(input1, input2);
+  }
+}
+
+// Folds redundant xor and or ops that are part of an and.
+// Cases handled:
+// 0b1110 & (a | 0b0001) = a & 0b1110
+// 0b1110 & (a ^ 0b0001) = a & 0b1110
+// 0b0110 & (a | 0b1110) = 0b0110
+FoldingRule RedundantAndOrXor() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants) {
+    assert(inst->opcode() == spv::Op::OpBitwiseAnd && "Wrong opcode.");
+    const analysis::Type* type =
+        context->get_type_mgr()->GetType(inst->type_id());
+    uint32_t width = ElementWidth(type);
+    if ((width != 32) && (width != 64)) 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() == spv::Op::OpBitwiseOr ||
+        other_inst->opcode() == spv::Op::OpBitwiseXor) {
+      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;
+
+      bool can_convert_to_const = other_inst->opcode() == spv::Op::OpBitwiseOr;
+      bool can_remove_inner = true;
+
+      ForEachIntegerConstantPair(
+          const_mgr, const_input1, const_input2,
+          [&can_remove_inner, &can_convert_to_const](auto lhs, auto rhs) {
+            // Only convert to const if 'and' is a subset of 'or'
+            can_convert_to_const = can_convert_to_const && ((lhs & rhs) == lhs);
+            // Only remove 'xor'/'or' if no bits intersect with 'and'
+            can_remove_inner = can_remove_inner && ((lhs & rhs) == 0);
+          });
+
+      if (can_convert_to_const) {
+        Instruction* const_inst =
+            const_mgr->GetDefiningInstruction(const_input1);
+        inst->SetOpcode(spv::Op::OpCopyObject);
+        inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {const_inst->result_id()}}});
+        return true;
+      } else if (can_remove_inner) {
+        Instruction* non_const_input =
+            NonConstInput(context, other_constants[0], other_inst);
+        Instruction* const_inst =
+            const_mgr->GetDefiningInstruction(const_input1);
+        inst->SetInOperands(
+            {{SPV_OPERAND_TYPE_ID, {non_const_input->result_id()}},
+             {SPV_OPERAND_TYPE_ID, {const_inst->result_id()}}});
+        return true;
+      }
+    }
+    return false;
+  };
+}
+
+// Folds redundant add and sub ops that are part of an and.
+// Cases handled:
+// 1 & (b + 2) = b & 1
+// 1 & (b - 2) = b & 1
+FoldingRule RedundantAndAddSub() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants) {
+    assert(inst->opcode() == spv::Op::OpBitwiseAnd && "Wrong opcode.");
+    const analysis::Type* type =
+        context->get_type_mgr()->GetType(inst->type_id());
+    uint32_t width = ElementWidth(type);
+    if ((width != 32) && (width != 64)) 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() != spv::Op::OpIAdd &&
+        other_inst->opcode() != spv::Op::OpISub) {
+      return false;
+    }
+    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;
+
+    // Only valid for subtraction if const is on the right
+    if ((other_inst->opcode() == spv::Op::OpISub) && other_constants[0]) {
+      return false;
+    }
+
+    bool can_remove_inner = true;
+    ForEachIntegerConstantPair(const_mgr, const_input1, const_input2,
+                               [&can_remove_inner](auto and_op, auto add_op) {
+                                 if (can_remove_inner) {
+                                   // Only valid if no bits from the +/- could
+                                   // affect bits from the & operation.
+                                   can_remove_inner =
+                                       utils::LSB(add_op) > and_op;
+                                 }
+                               });
+
+    if (can_remove_inner) {
+      Instruction* non_const_input =
+          NonConstInput(context, other_constants[0], other_inst);
+      Instruction* const_inst = const_mgr->GetDefiningInstruction(const_input1);
+      inst->SetInOperands(
+          {{SPV_OPERAND_TYPE_ID, {non_const_input->result_id()}},
+           {SPV_OPERAND_TYPE_ID, {const_inst->result_id()}}});
+      return true;
+    }
+    return false;
+  };
+}
+
+// Folds redundant shift ops that are part of an and.
+// Cases handled:
+// 1 & (b << 1) = 0
+// 0x80000000 & (b >> 1) = 0
+FoldingRule RedundantAndShift() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants) {
+    assert(inst->opcode() == spv::Op::OpBitwiseAnd && "Wrong opcode.");
+    const analysis::Type* type =
+        context->get_type_mgr()->GetType(inst->type_id());
+    uint32_t width = ElementWidth(type);
+    if ((width != 32) && (width != 64)) 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);
+
+    spv::Op other_op = other_inst->opcode();
+    if (other_op == spv::Op::OpShiftLeftLogical ||
+        other_op == spv::Op::OpShiftRightLogical) {
+      std::vector<const analysis::Constant*> other_constants =
+          const_mgr->GetOperandConstants(other_inst);
+
+      // Only valid  if const is on the right
+      if (other_constants[0]) {
+        return false;
+      }
+      const analysis::Constant* const_input2 = other_constants[1];
+      if (!const_input2) return false;
+
+      bool can_convert_to_zero = true;
+      ForEachIntegerConstantPair(
+          const_mgr, const_input1, const_input2,
+          [&can_convert_to_zero, other_op](auto lhs, auto rhs) {
+            if (other_op == spv::Op::OpShiftRightLogical) {
+              can_convert_to_zero = can_convert_to_zero && (lhs << rhs) == 0;
+            } else {
+              can_convert_to_zero = can_convert_to_zero && (lhs >> rhs) == 0;
+            }
+          });
+
+      if (can_convert_to_zero) {
+        auto zero_id = context->get_constant_mgr()->GetNullConstId(type);
+        inst->SetOpcode(spv::Op::OpCopyObject);
+        inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {zero_id}}});
+        return true;
+      }
+    }
+    return false;
+  };
+}
+
 // This rule look for a dot with a constant vector containing a single 1 and
 // the rest 0s.  This is the same as doing an extract.
 FoldingRule DotProductDoingExtract() {
@@ -3231,6 +3455,10 @@ void FoldingRules::AddFoldingRules() {
   rules_[spv::Op::OpISub].push_back(MergeSubAddArithmetic());
   rules_[spv::Op::OpISub].push_back(MergeSubSubArithmetic());
 
+  rules_[spv::Op::OpBitwiseAnd].push_back(RedundantAndOrXor());
+  rules_[spv::Op::OpBitwiseAnd].push_back(RedundantAndAddSub());
+  rules_[spv::Op::OpBitwiseAnd].push_back(RedundantAndShift());
+
   rules_[spv::Op::OpPhi].push_back(RedundantPhi());
 
   rules_[spv::Op::OpSNegate].push_back(MergeNegateArithmetic());

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

@@ -557,6 +557,7 @@ void IRContext::AddCombinatorsForCapability(uint32_t capability) {
          (uint32_t)spv::Op::OpTypeAccelerationStructureKHR,
          (uint32_t)spv::Op::OpTypeRayQueryKHR,
          (uint32_t)spv::Op::OpTypeHitObjectNV,
+         (uint32_t)spv::Op::OpTypeHitObjectEXT,
          (uint32_t)spv::Op::OpTypeArray,
          (uint32_t)spv::Op::OpTypeRuntimeArray,
          (uint32_t)spv::Op::OpTypeNodePayloadArrayAMDX,
@@ -927,11 +928,13 @@ uint32_t IRContext::GetBuiltinInputVarId(uint32_t builtin) {
         return 0;
       }
     }
+    if (reg_type == nullptr) return 0;  // Error
+
     uint32_t type_id = type_mgr->GetTypeInstruction(reg_type);
     uint32_t varTyPtrId =
         type_mgr->FindPointerToType(type_id, spv::StorageClass::Input);
-    // TODO(1841): Handle id overflow.
     var_id = TakeNextId();
+    if (var_id == 0) return 0;  // Error
     std::unique_ptr<Instruction> newVarOp(
         new Instruction(this, spv::Op::OpVariable, varTyPtrId, var_id,
                         {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,

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

@@ -467,6 +467,7 @@ void LocalAccessChainConvertPass::InitExtensions() {
       "SPV_NV_shader_invocation_reorder",
       "SPV_NV_cluster_acceleration_structure",
       "SPV_NV_linear_swept_spheres",
+      "SPV_KHR_maximal_reconvergence",
   });
 }
 

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

@@ -304,6 +304,7 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() {
       "SPV_NV_shader_invocation_reorder",
       "SPV_NV_cluster_acceleration_structure",
       "SPV_NV_linear_swept_spheres",
+      "SPV_KHR_maximal_reconvergence",
   });
 }
 

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

@@ -156,6 +156,7 @@ void LocalSingleStoreElimPass::InitExtensionAllowList() {
       "SPV_NV_shader_invocation_reorder",
       "SPV_NV_cluster_acceleration_structure",
       "SPV_NV_linear_swept_spheres",
+      "SPV_KHR_maximal_reconvergence",
   });
 }
 bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) {

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

@@ -930,13 +930,13 @@ LoopDescriptor::Status LoopDescriptor::CreatePreHeaderBlocksIfMissing() {
   for (auto& loop : *this) {
     if (!loop.GetPreHeaderBlock()) {
       if (!loop.GetOrCreatePreHeaderBlock()) {
-        return Status::kFailure;
+        return Status::Failure;
       }
       modified = true;
     }
   }
 
-  return modified ? Status::kSuccessWithChange : Status::kSuccessWithoutChange;
+  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
 // Add and remove loops which have been marked for addition and removal to

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

@@ -28,6 +28,7 @@
 #include "source/opt/dominator_analysis.h"
 #include "source/opt/module.h"
 #include "source/opt/tree_iterator.h"
+#include "source/util/status.h"
 
 namespace spvtools {
 namespace opt {
@@ -426,7 +427,7 @@ class LoopDescriptor {
   using const_pre_iterator = TreeDFIterator<const Loop>;
 
   // The status of processing a module.
-  enum class Status { kSuccessWithChange, kSuccessWithoutChange, kFailure };
+  using Status = utils::Status;
 
   // Creates a loop object for all loops found in |f|.
   LoopDescriptor(IRContext* context, const Function* f);

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

@@ -41,8 +41,8 @@ Pass::Status LoopFusionPass::ProcessFunction(Function* function) {
   // sure to return Status::SuccessWithChange in that case.
   bool modified = false;
   auto status = ld.CreatePreHeaderBlocksIfMissing();
-  if (status == LoopDescriptor::Status::kFailure) return Status::Failure;
-  modified = status == LoopDescriptor::Status::kSuccessWithChange;
+  if (status == LoopDescriptor::Status::Failure) return Status::Failure;
+  modified = status == LoopDescriptor::Status::SuccessWithChange;
 
   // TODO(tremmelg): Could the only loop that |loop| could possibly be fused be
   // picked out so don't have to check every loop

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

@@ -938,6 +938,11 @@ bool LoopUnrollerUtilsImpl::AssignNewResultIds(BasicBlock* basic_block) {
     inst.SetResultId(new_id);
     def_use_mgr->AnalyzeInstDef(&inst);
 
+    // All decorations that can apply to an instruction in a function body
+    // modify the behaviour of the instruction, and should be on the
+    // new instruction to keep the same results.
+    context_->get_decoration_mgr()->CloneDecorations(old_id, new_id);
+
     // Save the mapping of old_id -> new_id.
     state_.new_inst[old_id] = inst.result_id();
     // Check if this instruction is the induction variable.

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

@@ -25,6 +25,7 @@
 #include "source/opt/def_use_manager.h"
 #include "source/opt/ir_context.h"
 #include "source/opt/module.h"
+#include "source/util/status.h"
 #include "spirv-tools/libspirv.hpp"
 #include "types.h"
 
@@ -46,11 +47,7 @@ class Pass {
   //
   // The numbers for the cases are assigned to make sure that Failure & anything
   // is Failure, SuccessWithChange & any success is SuccessWithChange.
-  enum class Status {
-    Failure = 0x00,
-    SuccessWithChange = 0x10,
-    SuccessWithoutChange = 0x11,
-  };
+  using Status = utils::Status;
 
   using ProcessFunction = std::function<bool(Function*)>;
 

+ 34 - 12
3rdparty/spirv-tools/source/opt/split_combined_image_sampler_pass.cpp

@@ -142,6 +142,7 @@ Instruction* SplitCombinedImageSamplerPass::GetSamplerType() {
     analysis::Sampler s;
     uint32_t sampler_type_id = type_mgr_->GetTypeInstruction(&s);
     sampler_type_ = def_use_mgr_->GetDef(sampler_type_id);
+    if (sampler_type_ == nullptr) return nullptr;
     assert(first_sampled_image_type_);
     sampler_type_->InsertBefore(first_sampled_image_type_);
     RegisterNewGlobal(sampler_type_->result_id());
@@ -169,6 +170,7 @@ std::pair<Instruction*, Instruction*> SplitCombinedImageSamplerPass::SplitType(
       auto* image_type =
           def_use_mgr_->GetDef(combined_kind_type.GetSingleWordInOperand(0));
       auto* sampler_type = GetSamplerType();
+      if (!sampler_type) return {nullptr, nullptr};
       type_remap_[combined_kind_type.result_id()] = {&combined_kind_type,
                                                      image_type, sampler_type};
       return {image_type, sampler_type};
@@ -187,7 +189,9 @@ std::pair<Instruction*, Instruction*> SplitCombinedImageSamplerPass::SplitType(
         // this defensively.
         if (image_pointee && sampler_pointee) {
           auto* ptr_image = MakeUniformConstantPointer(image_pointee);
+          if (!ptr_image) return {nullptr, nullptr};
           auto* ptr_sampler = MakeUniformConstantPointer(sampler_pointee);
+          if (!ptr_sampler) return {nullptr, nullptr};
           type_remap_[combined_kind_type.result_id()] = {
               &combined_kind_type, ptr_image, ptr_sampler};
           return {ptr_image, ptr_sampler};
@@ -207,6 +211,7 @@ std::pair<Instruction*, Instruction*> SplitCombinedImageSamplerPass::SplitType(
       analysis::Array array_image_ty(image_ty, array_ty->length_info());
       const uint32_t array_image_ty_id =
           type_mgr_->GetTypeInstruction(&array_image_ty);
+      if (array_image_ty_id == 0) return {nullptr, nullptr};
       auto* array_image_ty_inst = def_use_mgr_->GetDef(array_image_ty_id);
       if (!IsKnownGlobal(array_image_ty_id)) {
         array_image_ty_inst->InsertBefore(&combined_kind_type);
@@ -214,11 +219,14 @@ std::pair<Instruction*, Instruction*> SplitCombinedImageSamplerPass::SplitType(
         // GetTypeInstruction also updated the def-use manager.
       }
 
+      auto* sampler_ty_inst = GetSamplerType();
+      if (!sampler_ty_inst) return {nullptr, nullptr};
       analysis::Array sampler_array_ty(
-          type_mgr_->GetType(GetSamplerType()->result_id()),
+          type_mgr_->GetType(sampler_ty_inst->result_id()),
           array_ty->length_info());
       const uint32_t array_sampler_ty_id =
           type_mgr_->GetTypeInstruction(&sampler_array_ty);
+      if (array_sampler_ty_id == 0) return {nullptr, nullptr};
       auto* array_sampler_ty_inst = def_use_mgr_->GetDef(array_sampler_ty_id);
       if (!IsKnownGlobal(array_sampler_ty_id)) {
         array_sampler_ty_inst->InsertBefore(&combined_kind_type);
@@ -240,6 +248,7 @@ std::pair<Instruction*, Instruction*> SplitCombinedImageSamplerPass::SplitType(
       analysis::RuntimeArray array_image_ty(image_ty);
       const uint32_t array_image_ty_id =
           type_mgr_->GetTypeInstruction(&array_image_ty);
+      if (array_image_ty_id == 0) return {nullptr, nullptr};
       auto* array_image_ty_inst = def_use_mgr_->GetDef(array_image_ty_id);
       if (!IsKnownGlobal(array_image_ty_id)) {
         array_image_ty_inst->InsertBefore(&combined_kind_type);
@@ -247,10 +256,13 @@ std::pair<Instruction*, Instruction*> SplitCombinedImageSamplerPass::SplitType(
         // GetTypeInstruction also updated the def-use manager.
       }
 
+      auto* sampler_ty_inst = GetSamplerType();
+      if (!sampler_ty_inst) return {nullptr, nullptr};
       analysis::RuntimeArray sampler_array_ty(
-          type_mgr_->GetType(GetSamplerType()->result_id()));
+          type_mgr_->GetType(sampler_ty_inst->result_id()));
       const uint32_t array_sampler_ty_id =
           type_mgr_->GetTypeInstruction(&sampler_array_ty);
+      if (array_sampler_ty_id == 0) return {nullptr, nullptr};
       auto* array_sampler_ty_inst = def_use_mgr_->GetDef(array_sampler_ty_id);
       if (!IsKnownGlobal(array_sampler_ty_id)) {
         array_sampler_ty_inst->InsertBefore(&combined_kind_type);
@@ -273,14 +285,14 @@ spv_result_t SplitCombinedImageSamplerPass::RemapVar(
   // Create an image variable, and a sampler variable.
   auto* combined_var_type = def_use_mgr_->GetDef(combined_var->type_id());
   auto [ptr_image_ty, ptr_sampler_ty] = SplitType(*combined_var_type);
-  assert(ptr_image_ty);
-  assert(ptr_sampler_ty);
-  // TODO(1841): Handle id overflow.
+  if (!ptr_image_ty || !ptr_sampler_ty) return SPV_ERROR_INTERNAL;
   Instruction* sampler_var = builder.AddVariable(
       ptr_sampler_ty->result_id(), SpvStorageClassUniformConstant);
-  // TODO(1841): Handle id overflow.
+  if (sampler_var == nullptr) return SPV_ERROR_INTERNAL;
   Instruction* image_var = builder.AddVariable(ptr_image_ty->result_id(),
                                                SpvStorageClassUniformConstant);
+  if (image_var == nullptr) return SPV_ERROR_INTERNAL;
+
   modified_ = true;
   return RemapUses(combined_var, image_var, sampler_var);
 }
@@ -356,12 +368,12 @@ 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.
+        if (!image) return SPV_ERROR_INTERNAL;
         auto* sampler = builder.AddLoad(PointeeTypeId(use.sampler_part),
                                         use.sampler_part->result_id());
+        if (!sampler) return SPV_ERROR_INTERNAL;
 
         // Move decorations, such as RelaxedPrecision.
         auto* deco_mgr = context()->get_decoration_mgr();
@@ -372,6 +384,7 @@ spv_result_t SplitCombinedImageSamplerPass::RemapUses(
         // Create a sampled image from the loads of the two parts.
         auto* sampled_image = builder.AddSampledImage(
             load->type_id(), image->result_id(), sampler->result_id());
+        if (!sampled_image) return SPV_ERROR_INTERNAL;
         // Replace the original sampled image value with the new one.
         std::unordered_set<Instruction*> users;
         def_use_mgr_->ForEachUse(
@@ -463,14 +476,18 @@ 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.
+        if (!result_image_part_ty || !result_sampler_part_ty)
+          return Fail() << "failed to split type for access chain";
         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.
+        if (!result_image_part)
+          return Fail() << "failed to create access chain for image part";
         auto* result_sampler_part = builder.AddOpcodeAccessChain(
             use.user->opcode(), result_sampler_part_ty->result_id(),
             use.sampler_part->result_id(), indices);
+        if (!result_sampler_part)
+          return Fail() << "failed to create access chain for sampler part";
 
         // Remap uses of the original access chain.
         add_remap(original_access_chain, result_image_part,
@@ -521,8 +538,7 @@ spv_result_t SplitCombinedImageSamplerPass::RemapFunctions() {
         if (combined_types_.find(param_ty_id) != combined_types_.end()) {
           auto* param_type = def_use_mgr_->GetDef(param_ty_id);
           auto [image_type, sampler_type] = SplitType(*param_type);
-          assert(image_type);
-          assert(sampler_type);
+          if (!image_type || !sampler_type) return SPV_ERROR_INTERNAL;
           // The image and sampler types must already exist, so there is no
           // need to move them to the right spot.
           new_params.push_back(type_mgr_->GetType(image_type->result_id()));
@@ -579,6 +595,11 @@ spv_result_t SplitCombinedImageSamplerPass::RemapFunctions() {
           auto* combined_inst = param.release();
           auto* combined_type = def_use_mgr_->GetDef(combined_inst->type_id());
           auto [image_type, sampler_type] = SplitType(*combined_type);
+          if (!image_type || !sampler_type) {
+            error = true;
+            return;
+          }
+
           uint32_t image_param_id = context()->TakeNextId();
           if (image_param_id == 0) {
             error = true;
@@ -621,6 +642,7 @@ Instruction* SplitCombinedImageSamplerPass::MakeUniformConstantPointer(
     Instruction* pointee) {
   uint32_t ptr_id = type_mgr_->FindPointerToType(
       pointee->result_id(), spv::StorageClass::UniformConstant);
+  if (ptr_id == 0) return nullptr;
   auto* ptr = def_use_mgr_->GetDef(ptr_id);
   if (!IsKnownGlobal(ptr_id)) {
     // The pointer type was created at the end. Put it right after the

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

@@ -237,6 +237,7 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
     DefineParameterlessCase(AccelerationStructureNV);
     DefineParameterlessCase(RayQueryKHR);
     DefineParameterlessCase(HitObjectNV);
+    DefineParameterlessCase(HitObjectEXT);
 #undef DefineParameterlessCase
     case Type::kInteger:
       typeInst = MakeUnique<Instruction>(
@@ -654,6 +655,7 @@ Type* TypeManager::RebuildType(uint32_t type_id, const Type& type) {
     DefineNoSubtypeCase(AccelerationStructureNV);
     DefineNoSubtypeCase(RayQueryKHR);
     DefineNoSubtypeCase(HitObjectNV);
+    DefineNoSubtypeCase(HitObjectEXT);
 #undef DefineNoSubtypeCase
     case Type::kVector: {
       const Vector* vec_ty = type.AsVector();
@@ -1082,6 +1084,9 @@ Type* TypeManager::RecordIfTypeDefinition(const Instruction& inst) {
     case spv::Op::OpTypeHitObjectNV:
       type = new HitObjectNV();
       break;
+    case spv::Op::OpTypeHitObjectEXT:
+      type = new HitObjectEXT();
+      break;
     case spv::Op::OpTypeTensorLayoutNV:
       type = new TensorLayoutNV(inst.GetSingleWordInOperand(0),
                                 inst.GetSingleWordInOperand(1));

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

@@ -203,7 +203,11 @@ class TypeManager {
     return GetRegisteredType(&bool_type);
   }
 
-  uint32_t GetBoolTypeId() { return GetTypeInstruction(GetBoolType()); }
+  uint32_t GetBoolTypeId() {
+    Type* bool_type = GetBoolType();
+    if (bool_type == nullptr) return 0;
+    return GetTypeInstruction(bool_type);
+  }
 
   Type* GetVoidType() {
     Void void_type;

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

@@ -135,6 +135,7 @@ std::unique_ptr<Type> Type::Clone() const {
     DeclareKindCase(CooperativeVectorNV);
     DeclareKindCase(RayQueryKHR);
     DeclareKindCase(HitObjectNV);
+    DeclareKindCase(HitObjectEXT);
     DeclareKindCase(TensorARM);
     DeclareKindCase(GraphARM);
 #undef DeclareKindCase
@@ -187,6 +188,7 @@ bool Type::operator==(const Type& other) const {
     DeclareKindCase(CooperativeVectorNV);
     DeclareKindCase(RayQueryKHR);
     DeclareKindCase(HitObjectNV);
+    DeclareKindCase(HitObjectEXT);
     DeclareKindCase(TensorLayoutNV);
     DeclareKindCase(TensorViewNV);
     DeclareKindCase(TensorARM);
@@ -249,6 +251,7 @@ size_t Type::ComputeHashValue(size_t hash, SeenTypes* seen) const {
     DeclareKindCase(CooperativeVectorNV);
     DeclareKindCase(RayQueryKHR);
     DeclareKindCase(HitObjectNV);
+    DeclareKindCase(HitObjectEXT);
     DeclareKindCase(TensorLayoutNV);
     DeclareKindCase(TensorViewNV);
     DeclareKindCase(TensorARM);

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

@@ -67,6 +67,7 @@ class CooperativeMatrixKHR;
 class CooperativeVectorNV;
 class RayQueryKHR;
 class HitObjectNV;
+class HitObjectEXT;
 class TensorLayoutNV;
 class TensorViewNV;
 class TensorARM;
@@ -114,6 +115,7 @@ class Type {
     kCooperativeVectorNV,
     kRayQueryKHR,
     kHitObjectNV,
+    kHitObjectEXT,
     kTensorLayoutNV,
     kTensorViewNV,
     kTensorARM,
@@ -222,6 +224,7 @@ class Type {
   DeclareCastMethod(CooperativeVectorNV)
   DeclareCastMethod(RayQueryKHR)
   DeclareCastMethod(HitObjectNV)
+  DeclareCastMethod(HitObjectEXT)
   DeclareCastMethod(TensorLayoutNV)
   DeclareCastMethod(TensorViewNV)
   DeclareCastMethod(TensorARM)
@@ -862,6 +865,7 @@ DefineParameterlessType(NamedBarrier, named_barrier);
 DefineParameterlessType(AccelerationStructureNV, accelerationStructureNV);
 DefineParameterlessType(RayQueryKHR, rayQueryKHR);
 DefineParameterlessType(HitObjectNV, hitObjectNV);
+DefineParameterlessType(HitObjectEXT, hitObjectEXT);
 #undef DefineParameterlessType
 
 }  // namespace analysis

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

@@ -38,6 +38,45 @@ uint32_t ValueNumberTable::GetValueNumber(uint32_t id) const {
   return GetValueNumber(context()->get_def_use_mgr()->GetDef(id));
 }
 
+bool ValueNumberTable::IsReadOnlyLoad(Instruction* inst) {
+  if (!inst->IsLoad()) {
+    return false;
+  }
+
+  Instruction* address_def = inst->GetBaseAddress();
+  if (!address_def) {
+    return false;
+  }
+
+  auto cached_result = read_only_variable_cache_.find(address_def->result_id());
+  if (cached_result != read_only_variable_cache_.end()) {
+    return cached_result->second;
+  }
+
+  bool is_read_only = IsReadOnlyVariable(address_def);
+  read_only_variable_cache_[address_def->result_id()] = is_read_only;
+  return is_read_only;
+}
+
+bool ValueNumberTable::IsReadOnlyVariable(Instruction* address_def) {
+  if (address_def->opcode() == spv::Op::OpVariable) {
+    if (address_def->IsReadOnlyPointer()) {
+      return true;
+    }
+  }
+
+  if (address_def->opcode() == spv::Op::OpLoad) {
+    const analysis::Type* address_type =
+        context()->get_type_mgr()->GetType(address_def->type_id());
+    if (address_type->AsSampledImage() != nullptr) {
+      const auto* image_type =
+          address_type->AsSampledImage()->image_type()->AsImage();
+      return image_type->sampled() == 1;
+    }
+  }
+  return false;
+}
+
 uint32_t ValueNumberTable::AssignValueNumber(Instruction* inst) {
   // If it already has a value return that.
   uint32_t value = GetValueNumber(inst);
@@ -88,7 +127,7 @@ uint32_t ValueNumberTable::AssignValueNumber(Instruction* inst) {
   // Note that this test will also handle volatile loads because they are not
   // read only.  However, if this is ever relaxed because we analyze stores, we
   // will have to add a new case for volatile loads.
-  if (inst->IsLoad() && !inst->IsReadOnlyLoad()) {
+  if (inst->IsLoad() && !IsReadOnlyLoad(inst)) {
     return assign_new_number(inst);
   }
 

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

@@ -70,6 +70,14 @@ class ValueNumberTable {
   // Assigns a value number to every result id in the module.
   void BuildDominatorTreeValueNumberTable();
 
+  // Returns true if |inst| is a load from read-only memory. This is a cached
+  // version of |Instruction::IsReadOnlyLoad| that is local to this pass.
+  bool IsReadOnlyLoad(Instruction* inst);
+
+  // Returns true if the variable pointed to by |address_def| is read-only.
+  // This is the part of |IsReadOnlyLoad| that is cached.
+  bool IsReadOnlyVariable(Instruction* address_def);
+
   // Returns the new value number.
   uint32_t TakeNextValueNumber() { return next_value_number_++; }
 
@@ -81,6 +89,10 @@ class ValueNumberTable {
   std::unordered_map<Instruction, uint32_t, ValueTableHash, ComputeSameValue>
       instruction_to_value_;
   std::unordered_map<uint32_t, uint32_t> id_to_value_;
+  // A cache for the results of |IsReadOnlyVariable|. The key is the base
+  // variable of a load.
+  std::unordered_map<uint32_t, bool> read_only_variable_cache_;
+
   IRContext* context_;
   uint32_t next_value_number_;
 };

+ 17 - 0
3rdparty/spirv-tools/source/util/bitutils.h

@@ -206,6 +206,23 @@ T ZeroExtendValue(T value, uint32_t number_of_bits) {
   return utils::ClearHighBits(value, bit_width - number_of_bits);
 }
 
+// Returns the the least significant bit from |value|.
+template <typename T>
+constexpr T LSB(T value) {
+  static_assert(std::is_integral<T>::value, "LSB requires integer type");
+  if constexpr (std::is_unsigned_v<T>) {
+    // Prevent warnings about doing a -x on unsigned values.
+    return value & (~value + 1);
+  } else {
+    return value & -value;
+  }
+}
+
+static_assert(LSB<uint32_t>(UINT32_MAX) == uint32_t(0x00000001), "LSB failed");
+static_assert(LSB<uint32_t>(0x10001000) == uint32_t(0x00001000), "LSB failed");
+static_assert(LSB<uint32_t>(0x10000000) == uint32_t(0x10000000), "LSB failed");
+static_assert(LSB<int32_t>(-1) == int32_t(0x00000001), "LSB failed");
+
 }  // namespace utils
 }  // namespace spvtools
 

+ 31 - 0
3rdparty/spirv-tools/source/util/status.h

@@ -0,0 +1,31 @@
+// Copyright (c) 2025 Google LLC
+//
+// 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_UTIL_STATUS_H_
+#define SOURCE_UTIL_STATUS_H_
+
+namespace spvtools {
+namespace utils {
+
+// The result of processing a module.
+enum class Status {
+  Failure = 0x0,
+  SuccessWithChange = 0x10,
+  SuccessWithoutChange = 0x11
+};
+
+}  // namespace utils
+}  // namespace spvtools
+
+#endif  // SOURCE_UTIL_STATUS_H_

+ 2 - 0
3rdparty/spirv-tools/source/val/validate.cpp

@@ -399,6 +399,7 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
     if (auto error = RayQueryPass(*vstate, &instruction)) return error;
     if (auto error = RayTracingPass(*vstate, &instruction)) return error;
     if (auto error = RayReorderNVPass(*vstate, &instruction)) return error;
+    if (auto error = RayReorderEXTPass(*vstate, &instruction)) return error;
     if (auto error = MeshShadingPass(*vstate, &instruction)) return error;
     if (auto error = TensorLayoutPass(*vstate, &instruction)) return error;
     if (auto error = TensorPass(*vstate, &instruction)) return error;
@@ -430,6 +431,7 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
     if (auto error = ValidateQCOMImageProcessingTextureUsages(*vstate, &inst))
       return error;
   }
+  if (auto error = ValidateLogicalPointers(*vstate)) return error;
 
   return SPV_SUCCESS;
 }

+ 6 - 0
3rdparty/spirv-tools/source/val/validate.h

@@ -220,6 +220,9 @@ spv_result_t RayTracingPass(ValidationState_t& _, const Instruction* inst);
 /// Validates correctness of shader execution reorder instructions.
 spv_result_t RayReorderNVPass(ValidationState_t& _, const Instruction* inst);
 
+/// Validates correctness of shader execution reorder EXT instructions.
+spv_result_t RayReorderEXTPass(ValidationState_t& _, const Instruction* inst);
+
 /// Validates correctness of mesh shading instructions.
 spv_result_t MeshShadingPass(ValidationState_t& _, const Instruction* inst);
 
@@ -259,6 +262,9 @@ spv_result_t ValidateSmallTypeUses(ValidationState_t& _,
 spv_result_t ValidateQCOMImageProcessingTextureUsages(ValidationState_t& _,
                                                       const Instruction* inst);
 
+/// Validates logical pointer restrictions.
+spv_result_t ValidateLogicalPointers(ValidationState_t& _);
+
 /// @brief Validate the ID's within a SPIR-V binary
 ///
 /// @param[in] pInstructions array of instructions

+ 1 - 0
3rdparty/spirv-tools/source/val/validate_annotation.cpp

@@ -217,6 +217,7 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, spv::Decoration dec,
             sc != spv::StorageClass::IncomingCallableDataKHR &&
             sc != spv::StorageClass::ShaderRecordBufferKHR &&
             sc != spv::StorageClass::HitObjectAttributeNV &&
+            sc != spv::StorageClass::HitObjectAttributeEXT &&
             sc != spv::StorageClass::TileImageEXT) {
           return _.diag(SPV_ERROR_INVALID_ID, target)
                  << _.VkErrorID(6672) << _.SpvDecorationString(dec)

+ 2 - 1
3rdparty/spirv-tools/source/val/validate_arithmetics.cpp

@@ -36,7 +36,8 @@ spv_result_t ArithmeticsPass(ValidationState_t& _, const Instruction* inst) {
     case spv::Op::OpFDiv:
     case spv::Op::OpFRem:
     case spv::Op::OpFMod:
-    case spv::Op::OpFNegate: {
+    case spv::Op::OpFNegate:
+    case spv::Op::OpFmaKHR: {
       bool supportsCoopMat =
           (opcode != spv::Op::OpFMul && opcode != spv::Op::OpFRem &&
            opcode != spv::Op::OpFMod);

+ 58 - 2
3rdparty/spirv-tools/source/val/validate_builtins.cpp

@@ -123,7 +123,7 @@ typedef enum VUIDError_ {
   VUIDErrorMax,
 } VUIDError;
 
-const static uint32_t NumVUIDBuiltins = 40;
+const static uint32_t NumVUIDBuiltins = 41;
 
 typedef struct {
   spv::BuiltIn builtIn;
@@ -170,6 +170,7 @@ std::array<BuiltinVUIDMapping, NumVUIDBuiltins> builtinVUIDInfo = {{
     {spv::BuiltIn::CullMaskKHR,               {6735, 6736, 6737}},
     {spv::BuiltIn::BaryCoordKHR,              {4154, 4155, 4156}},
     {spv::BuiltIn::BaryCoordNoPerspKHR,       {4160, 4161, 4162}},
+    {spv::BuiltIn::LocalInvocationIndex,      {4284, 4285, 4286}},
     {spv::BuiltIn::PrimitivePointIndicesEXT,  {7041, 7043, 7044}},
     {spv::BuiltIn::PrimitiveLineIndicesEXT,   {7047, 7049, 7050}},
     {spv::BuiltIn::PrimitiveTriangleIndicesEXT, {7053, 7055, 7056}},
@@ -2829,14 +2830,69 @@ spv_result_t BuiltInsValidator::ValidateVertexIdAtDefinition(
 
 spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtDefinition(
     const Decoration& decoration, const Instruction& inst) {
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    if (spv_result_t error = ValidateI32(
+            decoration, inst,
+            [this, &inst](const std::string& message) -> spv_result_t {
+              uint32_t vuid = GetVUIDForBuiltin(
+                  spv::BuiltIn::LocalInvocationIndex, VUIDErrorType);
+              return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                     << _.VkErrorID(vuid)
+                     << "According to the Vulkan spec BuiltIn "
+                        "LocalInvocationIndex variable needs to be a 32-bit "
+                        "int scalar. "
+                     << message;
+            })) {
+      return error;
+    }
+  }
+
   // Seed at reference checks with this built-in.
   return ValidateLocalInvocationIndexAtReference(decoration, inst, inst, inst);
 }
 
 spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtReference(
     const Decoration& decoration, const Instruction& built_in_inst,
-    const Instruction&,
+    const Instruction& referenced_inst,
     const Instruction& referenced_from_inst) {
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    const spv::StorageClass storage_class =
+        GetStorageClass(referenced_from_inst);
+    if (storage_class != spv::StorageClass::Max &&
+        storage_class != spv::StorageClass::Input) {
+      uint32_t vuid = GetVUIDForBuiltin(spv::BuiltIn::LocalInvocationIndex,
+                                        VUIDErrorStorageClass);
+      return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
+             << _.VkErrorID(vuid)
+             << "Vulkan spec allows BuiltIn LocalInvocationIndex to be only "
+                "used for variables with Input storage class. "
+             << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
+                                 referenced_from_inst)
+             << " " << GetStorageClassDesc(referenced_from_inst);
+    }
+
+    for (const spv::ExecutionModel execution_model : execution_models_) {
+      bool has_vulkan_model =
+          execution_model == spv::ExecutionModel::GLCompute ||
+          execution_model == spv::ExecutionModel::TaskNV ||
+          execution_model == spv::ExecutionModel::MeshNV ||
+          execution_model == spv::ExecutionModel::TaskEXT ||
+          execution_model == spv::ExecutionModel::MeshEXT;
+
+      if (spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) {
+        uint32_t vuid = GetVUIDForBuiltin(spv::BuiltIn::LocalInvocationIndex,
+                                          VUIDErrorExecutionModel);
+        return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
+               << _.VkErrorID(vuid)
+               << "Vulkan spec allows BuiltIn LocalInvocationIndex to be used "
+                  "only with GLCompute, MeshNV, TaskNV, MeshEXT or"
+               << " TaskEXT execution model. "
+               << GetReferenceDesc(decoration, built_in_inst, referenced_inst,
+                                   referenced_from_inst, execution_model);
+      }
+    }
+  }
+
   if (function_id_ == 0) {
     // Propagate this rule to all dependant ids in the global scope.
     id_to_at_reference_checks_[referenced_from_inst.id()].push_back(

+ 1 - 2
3rdparty/spirv-tools/source/val/validate_composites.cpp

@@ -532,8 +532,7 @@ spv_result_t ValidateVectorShuffle(ValidationState_t& _,
   if (!resultType || resultType->opcode() != spv::Op::OpTypeVector) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "The Result Type of OpVectorShuffle must be"
-           << " OpTypeVector. Found Op"
-           << spvOpcodeString(static_cast<spv::Op>(resultType->opcode()))
+           << " OpTypeVector. Found Op" << spvOpcodeString(resultType->opcode())
            << ".";
   }
 

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

@@ -13,6 +13,7 @@
 // limitations under the License.
 
 // Validates correctness of extension SPIR-V instructions.
+#include <algorithm>
 #include <cstdlib>
 #include <sstream>
 #include <string>
@@ -1087,6 +1088,7 @@ spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) {
             ExtensionToString(kSPV_KHR_workgroup_memory_explicit_layout) ||
         extension == ExtensionToString(kSPV_EXT_mesh_shader) ||
         extension == ExtensionToString(kSPV_NV_shader_invocation_reorder) ||
+        extension == ExtensionToString(kSPV_EXT_shader_invocation_reorder) ||
         extension ==
             ExtensionToString(kSPV_NV_cluster_acceleration_structure) ||
         extension == ExtensionToString(kSPV_NV_linear_swept_spheres) ||
@@ -3753,12 +3755,26 @@ spv_result_t ValidateExtInstDebugInfo100(ValidationState_t& _,
                 },
                 inst, 12)) {
           auto* operand = _.FindDef(inst->word(12));
-          if (operand->opcode() != spv::Op::OpVariable &&
-              operand->opcode() != spv::Op::OpConstant) {
+          std::initializer_list<spv::Op> allowed_opcodes = {
+              spv::Op::OpVariable,
+              spv::Op::OpConstantTrue,
+              spv::Op::OpConstantFalse,
+              spv::Op::OpConstant,
+              spv::Op::OpConstantComposite,
+              spv::Op::OpConstantSampler,
+              spv::Op::OpConstantNull,
+              spv::Op::OpSpecConstantTrue,
+              spv::Op::OpSpecConstantFalse,
+              spv::Op::OpSpecConstant,
+              spv::Op::OpSpecConstantComposite,
+              spv::Op::OpSpecConstantOp};
+          if (std::find(allowed_opcodes.begin(), allowed_opcodes.end(),
+                        operand->opcode()) == allowed_opcodes.end()) {
             return _.diag(SPV_ERROR_INVALID_DATA, inst)
                    << GetExtInstName(_, inst) << ": "
                    << "expected operand Variable must be a result id of "
-                      "OpVariable or OpConstant or DebugInfoNone";
+                      "OpVariable, OpConstant variant, OpSpecConstant variant "
+                      "or DebugInfoNone";
           }
         }
         if (num_words == 15) {

+ 74 - 38
3rdparty/spirv-tools/source/val/validate_function.cpp

@@ -170,6 +170,34 @@ spv_result_t ValidateFunctionCall(ValidationState_t& _,
            << "s type does not match Function <id> "
            << _.getIdName(return_type->id()) << "s return type.";
   }
+  if (!_.options()->relax_logical_pointer &&
+      (_.addressing_model() == spv::AddressingModel::Logical ||
+       _.addressing_model() == spv::AddressingModel::PhysicalStorageBuffer64)) {
+    if (return_type->opcode() == spv::Op::OpTypePointer ||
+        return_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) {
+      const auto sc = return_type->GetOperandAs<spv::StorageClass>(1);
+      if (sc != spv::StorageClass::PhysicalStorageBuffer) {
+        if (!_.HasCapability(spv::Capability::VariablePointersStorageBuffer) &&
+            sc == spv::StorageClass::StorageBuffer) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << "In Logical addressing, functions may only return a "
+                    "storage buffer pointer if the "
+                    "VariablePointersStorageBuffer capability is declared";
+        } else if (!_.HasCapability(spv::Capability::VariablePointers) &&
+                   sc == spv::StorageClass::Workgroup) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << "In Logical addressing, functions may only return a "
+                    "workgroup pointer if the VariablePointers capability is "
+                    "declared";
+        } else if (sc != spv::StorageClass::StorageBuffer &&
+                   sc != spv::StorageClass::Workgroup) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << "In Logical addressing, functions may not return a pointer "
+                    "in this storage class";
+        }
+      }
+    }
+  }
 
   const auto function_type_id = function->GetOperandAs<uint32_t>(3);
   const auto function_type = _.FindDef(function_type_id);
@@ -216,51 +244,59 @@ spv_result_t ValidateFunctionCall(ValidationState_t& _,
       }
     }
 
-    if (_.addressing_model() == spv::AddressingModel::Logical) {
+    if (_.addressing_model() == spv::AddressingModel::Logical ||
+        _.addressing_model() == spv::AddressingModel::PhysicalStorageBuffer64) {
       if ((parameter_type->opcode() == spv::Op::OpTypePointer ||
            parameter_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) &&
           !_.options()->relax_logical_pointer) {
         spv::StorageClass sc =
             parameter_type->GetOperandAs<spv::StorageClass>(1u);
-        // Validate which storage classes can be pointer operands.
-        switch (sc) {
-          case spv::StorageClass::UniformConstant:
-          case spv::StorageClass::Function:
-          case spv::StorageClass::Private:
-          case spv::StorageClass::Workgroup:
-          case spv::StorageClass::AtomicCounter:
-            // These are always allowed.
-            break;
-          case spv::StorageClass::StorageBuffer:
-            if (!_.features().variable_pointers) {
+        if (sc != spv::StorageClass::PhysicalStorageBuffer) {
+          // Validate which storage classes can be pointer operands.
+          switch (sc) {
+            case spv::StorageClass::UniformConstant:
+            case spv::StorageClass::Function:
+            case spv::StorageClass::Private:
+            case spv::StorageClass::Workgroup:
+            case spv::StorageClass::AtomicCounter:
+            // SPV_EXT_tile_image
+            case spv::StorageClass::TileImageEXT:
+            // SPV_KHR_ray_tracing
+            case spv::StorageClass::ShaderRecordBufferKHR:
+              // These are always allowed.
+              break;
+            case spv::StorageClass::StorageBuffer:
+              if (!_.features().variable_pointers) {
+                return _.diag(SPV_ERROR_INVALID_ID, inst)
+                       << "StorageBuffer pointer operand "
+                       << _.getIdName(argument_id)
+                       << " requires a variable pointers capability";
+              }
+              break;
+            default:
               return _.diag(SPV_ERROR_INVALID_ID, inst)
-                     << "StorageBuffer pointer operand "
-                     << _.getIdName(argument_id)
-                     << " requires a variable pointers capability";
-            }
-            break;
-          default:
-            return _.diag(SPV_ERROR_INVALID_ID, inst)
-                   << "Invalid storage class for pointer operand "
-                   << _.getIdName(argument_id);
-        }
+                     << "Invalid storage class for pointer operand "
+                     << _.getIdName(argument_id);
+          }
 
-        // Validate memory object declaration requirements.
-        if (argument->opcode() != spv::Op::OpVariable &&
-            argument->opcode() != spv::Op::OpUntypedVariableKHR &&
-            argument->opcode() != spv::Op::OpFunctionParameter) {
-          const bool ssbo_vptr =
-              _.HasCapability(spv::Capability::VariablePointersStorageBuffer) &&
-              sc == spv::StorageClass::StorageBuffer;
-          const bool wg_vptr =
-              _.HasCapability(spv::Capability::VariablePointers) &&
-              sc == spv::StorageClass::Workgroup;
-          const bool uc_ptr = sc == spv::StorageClass::UniformConstant;
-          if (!_.options()->before_hlsl_legalization && !ssbo_vptr &&
-              !wg_vptr && !uc_ptr) {
-            return _.diag(SPV_ERROR_INVALID_ID, inst)
-                   << "Pointer operand " << _.getIdName(argument_id)
-                   << " must be a memory object declaration";
+          // Validate memory object declaration requirements.
+          if (argument->opcode() != spv::Op::OpVariable &&
+              argument->opcode() != spv::Op::OpUntypedVariableKHR &&
+              argument->opcode() != spv::Op::OpFunctionParameter) {
+            const bool ssbo_vptr =
+                _.HasCapability(
+                    spv::Capability::VariablePointersStorageBuffer) &&
+                sc == spv::StorageClass::StorageBuffer;
+            const bool wg_vptr =
+                _.HasCapability(spv::Capability::VariablePointers) &&
+                sc == spv::StorageClass::Workgroup;
+            const bool uc_ptr = sc == spv::StorageClass::UniformConstant;
+            if (!_.options()->before_hlsl_legalization && !ssbo_vptr &&
+                !wg_vptr && !uc_ptr) {
+              return _.diag(SPV_ERROR_INVALID_ID, inst)
+                     << "Pointer operand " << _.getIdName(argument_id)
+                     << " must be a memory object declaration";
+            }
           }
         }
       }

+ 5 - 6
3rdparty/spirv-tools/source/val/validate_image.cpp

@@ -1100,7 +1100,7 @@ spv_result_t ValidateSampledImage(ValidationState_t& _,
                << "Result <id> from OpSampledImage instruction must not appear "
                   "as "
                   "operands of Op"
-               << spvOpcodeString(static_cast<spv::Op>(consumer_opcode)) << "."
+               << spvOpcodeString(consumer_opcode) << "."
                << " Found result <id> " << _.getIdName(inst->id())
                << " as an operand of <id> " << _.getIdName(consumer_instr->id())
                << ".";
@@ -1110,12 +1110,11 @@ spv_result_t ValidateSampledImage(ValidationState_t& _,
         return _.diag(SPV_ERROR_INVALID_ID, inst)
                << "Result <id> from OpSampledImage instruction must not appear "
                   "as operand for Op"
-               << spvOpcodeString(static_cast<spv::Op>(consumer_opcode))
+               << spvOpcodeString(consumer_opcode)
                << ", since it is not specified as taking an "
-               << "OpTypeSampledImage."
-               << " Found result <id> " << _.getIdName(inst->id())
-               << " as an operand of <id> " << _.getIdName(consumer_instr->id())
-               << ".";
+               << "OpTypeSampledImage." << " Found result <id> "
+               << _.getIdName(inst->id()) << " as an operand of <id> "
+               << _.getIdName(consumer_instr->id()) << ".";
       }
     }
   }

+ 1010 - 0
3rdparty/spirv-tools/source/val/validate_logical_pointers.cpp

@@ -0,0 +1,1010 @@
+// Copyright (c) 2025 Google LLC.
+//
+// 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 <iostream>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "source/opcode.h"
+#include "source/val/validate.h"
+#include "source/val/validation_state.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+// Returns true if inst is a logical pointer.
+bool IsLogicalPointer(const ValidationState_t& _, const Instruction* inst) {
+  if (!_.IsPointerType(inst->type_id())) {
+    return false;
+  }
+
+  // Physical storage buffer pointers are not logical pointers.
+  auto type_inst = _.FindDef(inst->type_id());
+  auto sc = type_inst->GetOperandAs<spv::StorageClass>(1);
+  if (sc == spv::StorageClass::PhysicalStorageBuffer) {
+    return false;
+  }
+
+  return true;
+}
+
+// Returns true if inst is a variable pointer.
+// Caches the result in variable_pointers.
+bool IsVariablePointer(const ValidationState_t& _,
+                       std::unordered_map<uint32_t, bool>& variable_pointers,
+                       const Instruction* inst) {
+  const auto iter = variable_pointers.find(inst->id());
+  if (iter != variable_pointers.end()) {
+    return iter->second;
+  }
+
+  bool is_var_ptr = false;
+  switch (inst->opcode()) {
+    case spv::Op::OpPtrAccessChain:
+    case spv::Op::OpUntypedPtrAccessChainKHR:
+    case spv::Op::OpUntypedInBoundsPtrAccessChainKHR:
+    case spv::Op::OpLoad:
+    case spv::Op::OpSelect:
+    case spv::Op::OpPhi:
+    case spv::Op::OpFunctionCall:
+    case spv::Op::OpConstantNull:
+      is_var_ptr = true;
+      break;
+    case spv::Op::OpFunctionParameter:
+      // Special case: skip to function calls.
+      if (IsLogicalPointer(_, inst)) {
+        auto func = inst->function();
+        auto func_inst = _.FindDef(func->id());
+
+        const auto param_inst_num = inst - &_.ordered_instructions()[0];
+        uint32_t param_index = 0;
+        uint32_t inst_index = 1;
+        while (_.ordered_instructions()[param_inst_num - inst_index].opcode() !=
+               spv::Op::OpFunction) {
+          if (_.ordered_instructions()[param_inst_num - inst_index].opcode() ==
+              spv::Op::OpFunctionParameter) {
+            param_index++;
+          }
+          ++inst_index;
+        }
+
+        for (const auto& use_pair : func_inst->uses()) {
+          const auto use_inst = use_pair.first;
+          if (use_inst->opcode() == spv::Op::OpFunctionCall) {
+            const auto arg_id =
+                use_inst->GetOperandAs<uint32_t>(3 + param_index);
+            const auto arg_inst = _.FindDef(arg_id);
+            is_var_ptr |= IsVariablePointer(_, variable_pointers, arg_inst);
+          }
+        }
+      }
+      break;
+    default: {
+      for (uint32_t i = 0; i < inst->operands().size(); ++i) {
+        if (inst->operands()[i].type != SPV_OPERAND_TYPE_ID) {
+          continue;
+        }
+
+        auto op_inst = _.FindDef(inst->GetOperandAs<uint32_t>(i));
+        if (IsLogicalPointer(_, op_inst)) {
+          is_var_ptr |= IsVariablePointer(_, variable_pointers, op_inst);
+        }
+      }
+      break;
+    }
+  }
+  variable_pointers[inst->id()] = is_var_ptr;
+  return is_var_ptr;
+}
+
+spv_result_t ValidateLogicalPointerOperands(ValidationState_t& _,
+                                            const Instruction* inst) {
+  bool has_pointer_operand = false;
+  spv::StorageClass sc = spv::StorageClass::Function;
+  for (uint32_t i = 0; i < inst->operands().size(); ++i) {
+    if (inst->operands()[i].type != SPV_OPERAND_TYPE_ID) {
+      continue;
+    }
+
+    auto op_inst = _.FindDef(inst->GetOperandAs<uint32_t>(i));
+    if (IsLogicalPointer(_, op_inst)) {
+      has_pointer_operand = true;
+
+      // Assume that there are not mixed storage classes in the instruction.
+      // This is not true for OpCopyMemory and OpCopyMemorySized, but they allow
+      // all storage classes.
+      auto type_inst = _.FindDef(op_inst->type_id());
+      sc = type_inst->GetOperandAs<spv::StorageClass>(1);
+      break;
+    }
+  }
+
+  if (!has_pointer_operand) {
+    return SPV_SUCCESS;
+  }
+
+  switch (inst->opcode()) {
+    // The following instructions allow logical pointer operands in all cases
+    // without capabilities.
+    case spv::Op::OpLoad:
+    case spv::Op::OpStore:
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpFunctionCall:
+    case spv::Op::OpImageTexelPointer:
+    case spv::Op::OpCopyMemory:
+    case spv::Op::OpCopyObject:
+    case spv::Op::OpArrayLength:
+    case spv::Op::OpExtInst:
+    // Core spec bugs
+    case spv::Op::OpDecorate:
+    case spv::Op::OpDecorateId:
+    case spv::Op::OpGroupDecorate:
+    case spv::Op::OpEntryPoint:
+    case spv::Op::OpName:
+    case spv::Op::OpDecorateString:
+    // SPV_KHR_untyped_pointers
+    case spv::Op::OpUntypedArrayLengthKHR:
+    case spv::Op::OpUntypedAccessChainKHR:
+    case spv::Op::OpUntypedInBoundsAccessChainKHR:
+    case spv::Op::OpCopyMemorySized:
+    // Cooperative matrix KHR/NV
+    case spv::Op::OpCooperativeMatrixLoadKHR:
+    case spv::Op::OpCooperativeMatrixLoadNV:
+    case spv::Op::OpCooperativeMatrixStoreKHR:
+    case spv::Op::OpCooperativeMatrixStoreNV:
+    // SPV_KHR_ray_tracing
+    case spv::Op::OpTraceRayKHR:
+    case spv::Op::OpExecuteCallableKHR:
+    // SPV_KHR_ray_query
+    case spv::Op::OpRayQueryConfirmIntersectionKHR:
+    case spv::Op::OpRayQueryInitializeKHR:
+    case spv::Op::OpRayQueryTerminateKHR:
+    case spv::Op::OpRayQueryGenerateIntersectionKHR:
+    case spv::Op::OpRayQueryProceedKHR:
+    case spv::Op::OpRayQueryGetIntersectionTypeKHR:
+    case spv::Op::OpRayQueryGetRayTMinKHR:
+    case spv::Op::OpRayQueryGetRayFlagsKHR:
+    case spv::Op::OpRayQueryGetIntersectionTKHR:
+    case spv::Op::OpRayQueryGetIntersectionInstanceCustomIndexKHR:
+    case spv::Op::OpRayQueryGetIntersectionInstanceIdKHR:
+    case spv::Op::
+        OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR:
+    case spv::Op::OpRayQueryGetIntersectionGeometryIndexKHR:
+    case spv::Op::OpRayQueryGetIntersectionPrimitiveIndexKHR:
+    case spv::Op::OpRayQueryGetIntersectionBarycentricsKHR:
+    case spv::Op::OpRayQueryGetIntersectionFrontFaceKHR:
+    case spv::Op::OpRayQueryGetIntersectionCandidateAABBOpaqueKHR:
+    case spv::Op::OpRayQueryGetIntersectionObjectRayDirectionKHR:
+    case spv::Op::OpRayQueryGetIntersectionObjectRayOriginKHR:
+    case spv::Op::OpRayQueryGetWorldRayDirectionKHR:
+    case spv::Op::OpRayQueryGetWorldRayOriginKHR:
+    case spv::Op::OpRayQueryGetIntersectionObjectToWorldKHR:
+    case spv::Op::OpRayQueryGetIntersectionWorldToObjectKHR:
+    // SPV_KHR_ray_tracing_position_fetch
+    case spv::Op::OpRayQueryGetIntersectionTriangleVertexPositionsKHR:
+    // SPV_NV_cluster_acceleration_structure
+    case spv::Op::OpRayQueryGetClusterIdNV:
+    case spv::Op::OpHitObjectGetClusterIdNV:
+    // SPV_NV_ray_tracing_motion_blur
+    case spv::Op::OpTraceMotionNV:
+    case spv::Op::OpTraceRayMotionNV:
+    // SPV_NV_linear_swept_spheres
+    case spv::Op::OpRayQueryGetIntersectionSpherePositionNV:
+    case spv::Op::OpRayQueryGetIntersectionSphereRadiusNV:
+    case spv::Op::OpRayQueryGetIntersectionLSSPositionsNV:
+    case spv::Op::OpRayQueryGetIntersectionLSSRadiiNV:
+    case spv::Op::OpRayQueryGetIntersectionLSSHitValueNV:
+    case spv::Op::OpRayQueryIsSphereHitNV:
+    case spv::Op::OpRayQueryIsLSSHitNV:
+    case spv::Op::OpHitObjectGetSpherePositionNV:
+    case spv::Op::OpHitObjectGetSphereRadiusNV:
+    case spv::Op::OpHitObjectGetLSSPositionsNV:
+    case spv::Op::OpHitObjectGetLSSRadiiNV:
+    case spv::Op::OpHitObjectIsSphereHitNV:
+    case spv::Op::OpHitObjectIsLSSHitNV:
+    // SPV_NV_shader_invocation_reorder
+    case spv::Op::OpReorderThreadWithHitObjectNV:
+    case spv::Op::OpHitObjectTraceRayNV:
+    case spv::Op::OpHitObjectTraceRayMotionNV:
+    case spv::Op::OpHitObjectRecordHitNV:
+    case spv::Op::OpHitObjectRecordHitMotionNV:
+    case spv::Op::OpHitObjectRecordHitWithIndexNV:
+    case spv::Op::OpHitObjectRecordHitWithIndexMotionNV:
+    case spv::Op::OpHitObjectRecordMissNV:
+    case spv::Op::OpHitObjectRecordMissMotionNV:
+    case spv::Op::OpHitObjectRecordEmptyNV:
+    case spv::Op::OpHitObjectExecuteShaderNV:
+    case spv::Op::OpHitObjectGetCurrentTimeNV:
+    case spv::Op::OpHitObjectGetAttributesNV:
+    case spv::Op::OpHitObjectGetHitKindNV:
+    case spv::Op::OpHitObjectGetPrimitiveIndexNV:
+    case spv::Op::OpHitObjectGetGeometryIndexNV:
+    case spv::Op::OpHitObjectGetInstanceIdNV:
+    case spv::Op::OpHitObjectGetInstanceCustomIndexNV:
+    case spv::Op::OpHitObjectGetObjectRayOriginNV:
+    case spv::Op::OpHitObjectGetObjectRayDirectionNV:
+    case spv::Op::OpHitObjectGetWorldRayDirectionNV:
+    case spv::Op::OpHitObjectGetWorldRayOriginNV:
+    case spv::Op::OpHitObjectGetObjectToWorldNV:
+    case spv::Op::OpHitObjectGetWorldToObjectNV:
+    case spv::Op::OpHitObjectGetRayTMaxNV:
+    case spv::Op::OpHitObjectGetRayTMinNV:
+    case spv::Op::OpHitObjectGetShaderBindingTableRecordIndexNV:
+    case spv::Op::OpHitObjectGetShaderRecordBufferHandleNV:
+    case spv::Op::OpHitObjectIsEmptyNV:
+    case spv::Op::OpHitObjectIsHitNV:
+    case spv::Op::OpHitObjectIsMissNV:
+    // SPV_EXT_shader_invocation_reorder
+    case spv::Op::OpHitObjectRecordFromQueryEXT:
+    case spv::Op::OpHitObjectRecordMissEXT:
+    case spv::Op::OpHitObjectRecordMissMotionEXT:
+    case spv::Op::OpHitObjectGetIntersectionTriangleVertexPositionsEXT:
+    case spv::Op::OpHitObjectGetRayFlagsEXT:
+    case spv::Op::OpHitObjectSetShaderBindingTableRecordIndexEXT:
+    case spv::Op::OpHitObjectReorderExecuteShaderEXT:
+    case spv::Op::OpHitObjectTraceReorderExecuteEXT:
+    case spv::Op::OpHitObjectTraceMotionReorderExecuteEXT:
+    case spv::Op::OpReorderThreadWithHintEXT:
+    case spv::Op::OpReorderThreadWithHitObjectEXT:
+    case spv::Op::OpHitObjectTraceRayEXT:
+    case spv::Op::OpHitObjectTraceRayMotionEXT:
+    case spv::Op::OpHitObjectRecordEmptyEXT:
+    case spv::Op::OpHitObjectExecuteShaderEXT:
+    case spv::Op::OpHitObjectGetCurrentTimeEXT:
+    case spv::Op::OpHitObjectGetAttributesEXT:
+    case spv::Op::OpHitObjectGetHitKindEXT:
+    case spv::Op::OpHitObjectGetPrimitiveIndexEXT:
+    case spv::Op::OpHitObjectGetGeometryIndexEXT:
+    case spv::Op::OpHitObjectGetInstanceIdEXT:
+    case spv::Op::OpHitObjectGetInstanceCustomIndexEXT:
+    case spv::Op::OpHitObjectGetObjectRayOriginEXT:
+    case spv::Op::OpHitObjectGetObjectRayDirectionEXT:
+    case spv::Op::OpHitObjectGetWorldRayDirectionEXT:
+    case spv::Op::OpHitObjectGetWorldRayOriginEXT:
+    case spv::Op::OpHitObjectGetObjectToWorldEXT:
+    case spv::Op::OpHitObjectGetWorldToObjectEXT:
+    case spv::Op::OpHitObjectGetRayTMaxEXT:
+    case spv::Op::OpHitObjectGetRayTMinEXT:
+    case spv::Op::OpHitObjectGetShaderBindingTableRecordIndexEXT:
+    case spv::Op::OpHitObjectGetShaderRecordBufferHandleEXT:
+    case spv::Op::OpHitObjectIsEmptyEXT:
+    case spv::Op::OpHitObjectIsHitEXT:
+    case spv::Op::OpHitObjectIsMissEXT:
+    // SPV_NV_raw_access_chains
+    case spv::Op::OpRawAccessChainNV:
+    // SPV_NV_cooperative_matrix2
+    case spv::Op::OpCooperativeMatrixLoadTensorNV:
+    case spv::Op::OpCooperativeMatrixStoreTensorNV:
+    // SPV_NV_cooperative_vector
+    case spv::Op::OpCooperativeVectorLoadNV:
+    case spv::Op::OpCooperativeVectorStoreNV:
+    case spv::Op::OpCooperativeVectorMatrixMulNV:
+    case spv::Op::OpCooperativeVectorMatrixMulAddNV:
+    case spv::Op::OpCooperativeVectorOuterProductAccumulateNV:
+    case spv::Op::OpCooperativeVectorReduceSumAccumulateNV:
+    // SPV_EXT_mesh_shader
+    case spv::Op::OpEmitMeshTasksEXT:
+    // SPV_AMD_shader_enqueue (spec bugs)
+    case spv::Op::OpEnqueueNodePayloadsAMDX:
+    case spv::Op::OpNodePayloadArrayLengthAMDX:
+    case spv::Op::OpIsNodePayloadValidAMDX:
+    case spv::Op::OpFinishWritingNodePayloadAMDX:
+    // SPV_ARM_graph
+    case spv::Op::OpGraphEntryPointARM:
+      return SPV_SUCCESS;
+    // The following cases require a variable pointer capability. Since all
+    // instructions are for variable pointers, the storage class and capability
+    // are also checked.
+    case spv::Op::OpReturnValue:
+    case spv::Op::OpPtrAccessChain:
+    case spv::Op::OpPtrEqual:
+    case spv::Op::OpPtrNotEqual:
+    case spv::Op::OpPtrDiff:
+    // Core spec bugs
+    case spv::Op::OpSelect:
+    case spv::Op::OpPhi:
+    case spv::Op::OpVariable:
+    // SPV_KHR_untyped_pointers
+    case spv::Op::OpUntypedPtrAccessChainKHR:
+      if ((_.HasCapability(spv::Capability::VariablePointersStorageBuffer) &&
+           sc == spv::StorageClass ::StorageBuffer) ||
+          (_.HasCapability(spv::Capability::VariablePointers) &&
+           sc == spv::StorageClass::Workgroup)) {
+        return SPV_SUCCESS;
+      }
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Instruction may only have a logical pointer operand in the "
+                "StorageBuffer or Workgroup storage classes with appropriate "
+                "variable pointers capability";
+    default:
+      if (spvOpcodeIsAtomicOp(inst->opcode())) {
+        return SPV_SUCCESS;
+      }
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Instruction may not have a logical pointer operand";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateLogicalPointerReturns(ValidationState_t& _,
+                                           const Instruction* inst) {
+  if (!IsLogicalPointer(_, inst)) {
+    return SPV_SUCCESS;
+  }
+
+  const auto type_inst = _.FindDef(inst->type_id());
+  const auto sc = type_inst->GetOperandAs<spv::StorageClass>(1u);
+
+  switch (inst->opcode()) {
+    // Core spec without an variable pointer capability.
+    case spv::Op::OpVariable:
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpFunctionParameter:
+    case spv::Op::OpImageTexelPointer:
+    case spv::Op::OpCopyObject:
+    // Core spec bugs
+    case spv::Op::OpUndef:
+    // SPV_KHR_untyped_pointers
+    case spv::Op::OpUntypedAccessChainKHR:
+    case spv::Op::OpUntypedInBoundsAccessChainKHR:
+    case spv::Op::OpUntypedVariableKHR:
+    // SPV_NV_raw_access_chains
+    case spv::Op::OpRawAccessChainNV:
+    // SPV_AMD_shader_enqueue (spec bugs)
+    case spv::Op::OpAllocateNodePayloadsAMDX:
+      return SPV_SUCCESS;
+    // Core spec with variable pointer capability. Check storage classes since
+    // variable pointers can only be in certain storage classes.
+    case spv::Op::OpSelect:
+    case spv::Op::OpPhi:
+    case spv::Op::OpFunctionCall:
+    case spv::Op::OpPtrAccessChain:
+    case spv::Op::OpLoad:
+    case spv::Op::OpConstantNull:
+    case spv::Op::OpFunction:
+    // SPV_KHR_untyped_pointers
+    case spv::Op::OpUntypedPtrAccessChainKHR:
+      if ((_.HasCapability(spv::Capability::VariablePointersStorageBuffer) &&
+           sc == spv::StorageClass ::StorageBuffer) ||
+          (_.HasCapability(spv::Capability::VariablePointers) &&
+           sc == spv::StorageClass::Workgroup)) {
+        return SPV_SUCCESS;
+      }
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Instruction may only return a logical pointer in the "
+                "StorageBuffer or Workgroup storage classes with appropriate "
+                "variable pointers capability";
+    default:
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Instruction may not return a logical pointer";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t IsBlockArray(ValidationState_t& _, const Instruction* type) {
+  if (type->opcode() == spv::Op::OpTypeArray ||
+      type->opcode() == spv::Op::OpTypeRuntimeArray) {
+    const auto element_type = _.FindDef(type->GetOperandAs<uint32_t>(1));
+    if (element_type->opcode() == spv::Op::OpTypeStruct &&
+        (_.HasDecoration(element_type->id(), spv::Decoration::Block) ||
+         _.HasDecoration(element_type->id(), spv::Decoration::BufferBlock))) {
+      return SPV_ERROR_INVALID_DATA;
+    }
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t CheckMatrixElementTyped(ValidationState_t& _,
+                                     const Instruction* inst) {
+  switch (inst->opcode()) {
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpPtrAccessChain: {
+      // Get the type of the base operand.
+      uint32_t start_index =
+          inst->opcode() == spv::Op::OpPtrAccessChain ? 4 : 3;
+      const auto access_type_id = _.GetOperandTypeId(inst, 2);
+      auto access_type = _.FindDef(access_type_id);
+      access_type = _.FindDef(access_type->GetOperandAs<uint32_t>(2));
+
+      // If the base operand is a matrix, then it was definitely pointing to a
+      // sub-component.
+      if (access_type->opcode() == spv::Op::OpTypeMatrix) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Variable pointer must not point to a column or a "
+                  "component of a column of a matrix";
+      }
+
+      // Otherwise, step through the indices to see if we pass a matrix.
+      for (uint32_t i = start_index; i < inst->operands().size(); ++i) {
+        const auto index = inst->GetOperandAs<uint32_t>(i);
+        if (access_type->opcode() == spv::Op::OpTypeStruct) {
+          uint64_t val = 0;
+          _.EvalConstantValUint64(index, &val);
+          access_type = _.FindDef(access_type->GetOperandAs<uint32_t>(
+              1 + static_cast<uint32_t>(val)));
+        } else {
+          access_type = _.FindDef(_.GetComponentType(access_type->id()));
+        }
+
+        if (access_type->opcode() == spv::Op::OpTypeMatrix) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Variable pointer must not point to a column or a "
+                    "component of a column of a matrix";
+        }
+      }
+      break;
+    }
+    default:
+      break;
+  }
+  return SPV_SUCCESS;
+}
+
+spv_result_t CheckMatrixElementUntyped(ValidationState_t& _,
+                                       const Instruction* inst) {
+  switch (inst->opcode()) {
+    case spv::Op::OpAccessChain:
+    case spv::Op::OpInBoundsAccessChain:
+    case spv::Op::OpPtrAccessChain:
+    case spv::Op::OpUntypedAccessChainKHR:
+    case spv::Op::OpUntypedInBoundsAccessChainKHR:
+    case spv::Op::OpUntypedPtrAccessChainKHR: {
+      const bool untyped = spvOpcodeGeneratesUntypedPointer(inst->opcode());
+      uint32_t start_index;
+      Instruction* access_type = nullptr;
+      if (untyped) {
+        // Get the type of the base operand.
+        start_index =
+            inst->opcode() == spv::Op::OpUntypedPtrAccessChainKHR ? 5 : 4;
+        const auto access_type_id = inst->GetOperandAs<uint32_t>(2);
+        access_type = _.FindDef(access_type_id);
+      } else {
+        start_index = inst->opcode() == spv::Op::OpPtrAccessChain ? 4 : 3;
+        const auto access_type_id = _.GetOperandTypeId(inst, 2);
+        access_type = _.FindDef(access_type_id);
+        access_type = _.FindDef(access_type->GetOperandAs<uint32_t>(2));
+      }
+
+      // If the base operand is a matrix, then it was definitely pointing to a
+      // sub-component.
+      if (access_type->opcode() == spv::Op::OpTypeMatrix) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Variable pointer must not point to a column or a "
+                  "component of a column of a matrix.";
+      }
+
+      // Otherwise, step through the indices to see if we pass a matrix.
+      for (uint32_t i = start_index; i < inst->operands().size(); ++i) {
+        const auto index = inst->GetOperandAs<uint32_t>(i);
+        if (access_type->opcode() == spv::Op::OpTypeStruct) {
+          uint64_t val = 0;
+          _.EvalConstantValUint64(index, &val);
+          access_type = _.FindDef(access_type->GetOperandAs<uint32_t>(
+              1 + static_cast<uint32_t>(val)));
+        } else {
+          access_type = _.FindDef(_.GetComponentType(access_type->id()));
+        }
+
+        if (access_type->opcode() == spv::Op::OpTypeMatrix) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Variable pointer must not point to a column or a "
+                    "component of a column of a matrix.";
+        }
+      }
+      break;
+    }
+    default:
+      break;
+  }
+  return SPV_SUCCESS;
+}
+
+// Traces the variable pointer inst backwards.
+// checker is called on each visited instruction.
+spv_result_t TraceVariablePointers(
+    ValidationState_t& _, const Instruction* inst,
+    const std::function<spv_result_t(ValidationState_t&, const Instruction*)>&
+        checker) {
+  std::vector<const Instruction*> stack;
+  std::unordered_set<const Instruction*> seen;
+  stack.push_back(inst);
+  while (!stack.empty()) {
+    const Instruction* trace_inst = stack.back();
+    stack.pop_back();
+
+    if (!seen.insert(trace_inst).second) {
+      continue;
+    }
+
+    if (auto error = checker(_, trace_inst)) {
+      return error;
+    }
+
+    const auto untyped = spvOpcodeGeneratesUntypedPointer(trace_inst->opcode());
+    switch (trace_inst->opcode()) {
+      case spv::Op::OpAccessChain:
+      case spv::Op::OpInBoundsAccessChain:
+      case spv::Op::OpPtrAccessChain:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(2)));
+        break;
+      case spv::Op::OpUntypedAccessChainKHR:
+      case spv::Op::OpUntypedInBoundsAccessChainKHR:
+      case spv::Op::OpUntypedPtrAccessChainKHR:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(3)));
+        break;
+      case spv::Op::OpPhi:
+        for (uint32_t i = 2; i < trace_inst->operands().size(); i += 2) {
+          stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(i)));
+        }
+        break;
+      case spv::Op::OpSelect:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(3)));
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(4)));
+        break;
+      case spv::Op::OpFunctionParameter: {
+        // Jump to function calls
+        auto func = trace_inst->function();
+        auto func_inst = _.FindDef(func->id());
+
+        const auto param_inst_num = trace_inst - &_.ordered_instructions()[0];
+        uint32_t param_index = 0;
+        uint32_t inst_index = 1;
+        while (_.ordered_instructions()[param_inst_num - inst_index].opcode() !=
+               spv::Op::OpFunction) {
+          if (_.ordered_instructions()[param_inst_num - inst_index].opcode() ==
+              spv::Op::OpFunctionParameter) {
+            param_index++;
+          }
+          ++inst_index;
+        }
+
+        for (const auto& use_pair : func_inst->uses()) {
+          const auto use_inst = use_pair.first;
+          if (use_inst->opcode() == spv::Op::OpFunctionCall) {
+            const auto arg_id =
+                use_inst->GetOperandAs<uint32_t>(3 + param_index);
+            const auto arg_inst = _.FindDef(arg_id);
+            stack.push_back(arg_inst);
+          }
+        }
+        break;
+      }
+      case spv::Op::OpFunctionCall: {
+        // Jump to return values.
+        const auto* func = _.function(trace_inst->GetOperandAs<uint32_t>(2));
+        for (auto* bb : func->ordered_blocks()) {
+          const auto* terminator = bb->terminator();
+          if (terminator->opcode() == spv::Op::OpReturnValue) {
+            stack.push_back(terminator);
+          }
+        }
+        break;
+      }
+      case spv::Op::OpReturnValue:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(0)));
+        break;
+      case spv::Op::OpCopyObject:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(2)));
+        break;
+      case spv::Op::OpLoad:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(2)));
+        break;
+      case spv::Op::OpStore:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(0)));
+        break;
+      case spv::Op::OpVariable:
+      case spv::Op::OpUntypedVariableKHR: {
+        const auto sc = trace_inst->GetOperandAs<spv::StorageClass>(2);
+        if (sc == spv::StorageClass::Function ||
+            sc == spv::StorageClass::Private) {
+          // Add the initializer
+          const uint32_t init_operand = untyped ? 4 : 3;
+          if (trace_inst->operands().size() > init_operand) {
+            stack.push_back(
+                _.FindDef(trace_inst->GetOperandAs<uint32_t>(init_operand)));
+          }
+          // Jump to stores
+          std::vector<std::pair<const Instruction*, uint32_t>> store_stack(
+              trace_inst->uses());
+          std::unordered_set<const Instruction*> store_seen;
+          while (!store_stack.empty()) {
+            const auto& use = store_stack.back();
+            store_stack.pop_back();
+
+            if (!store_seen.insert(use.first).second) {
+              continue;
+            }
+
+            // If the use is a store pointer, trace the store object.
+            // Note: use.second is a word index.
+            if (use.first->opcode() == spv::Op::OpStore && use.second == 1) {
+              stack.push_back(_.FindDef(use.first->GetOperandAs<uint32_t>(1)));
+            } else {
+              // Most likely a gep so keep tracing.
+              for (auto& next_use : use.first->uses()) {
+                store_stack.push_back(next_use);
+              }
+            }
+          }
+        }
+        break;
+      }
+      default:
+        break;
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+// Traces the variable pointer inst backwards, but only unmodified pointers.
+// checker is called on each visited instruction.
+spv_result_t TraceUnmodifiedVariablePointers(
+    ValidationState_t& _, const Instruction* inst,
+    const std::function<spv_result_t(ValidationState_t&, const Instruction*)>&
+        checker) {
+  std::vector<const Instruction*> stack;
+  std::unordered_set<const Instruction*> seen;
+  stack.push_back(inst);
+  while (!stack.empty()) {
+    const Instruction* trace_inst = stack.back();
+    stack.pop_back();
+
+    if (!seen.insert(trace_inst).second) {
+      continue;
+    }
+
+    if (auto error = checker(_, trace_inst)) {
+      return error;
+    }
+
+    const auto untyped = spvOpcodeGeneratesUntypedPointer(trace_inst->opcode());
+    switch (trace_inst->opcode()) {
+      case spv::Op::OpAccessChain:
+      case spv::Op::OpInBoundsAccessChain:
+        if (trace_inst->operands().size() == 2) {
+          stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(2)));
+        }
+        break;
+      case spv::Op::OpUntypedAccessChainKHR:
+      case spv::Op::OpUntypedInBoundsAccessChainKHR:
+      case spv::Op::OpUntypedPtrAccessChainKHR:
+        if (trace_inst->operands().size() == 3) {
+          stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(3)));
+        }
+        break;
+      case spv::Op::OpPhi:
+        for (uint32_t i = 2; i < trace_inst->operands().size(); i += 2) {
+          stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(i)));
+        }
+        break;
+      case spv::Op::OpSelect:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(3)));
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(4)));
+        break;
+      case spv::Op::OpFunctionParameter: {
+        // Jump to function calls
+        auto func = trace_inst->function();
+        auto func_inst = _.FindDef(func->id());
+
+        const auto param_inst_num = trace_inst - &_.ordered_instructions()[0];
+        uint32_t param_index = 0;
+        uint32_t inst_index = 1;
+        while (_.ordered_instructions()[param_inst_num - inst_index].opcode() !=
+               spv::Op::OpFunction) {
+          if (_.ordered_instructions()[param_inst_num - inst_index].opcode() ==
+              spv::Op::OpFunctionParameter) {
+            param_index++;
+          }
+          ++inst_index;
+        }
+
+        for (const auto& use_pair : func_inst->uses()) {
+          const auto use_inst = use_pair.first;
+          if (use_inst->opcode() == spv::Op::OpFunctionCall) {
+            const auto arg_id =
+                use_inst->GetOperandAs<uint32_t>(3 + param_index);
+            const auto arg_inst = _.FindDef(arg_id);
+            stack.push_back(arg_inst);
+          }
+        }
+        break;
+      }
+      case spv::Op::OpFunctionCall: {
+        // Jump to return values.
+        const auto* func = _.function(trace_inst->GetOperandAs<uint32_t>(2));
+        for (auto* bb : func->ordered_blocks()) {
+          const auto* terminator = bb->terminator();
+          if (terminator->opcode() == spv::Op::OpReturnValue) {
+            stack.push_back(terminator);
+          }
+        }
+        break;
+      }
+      case spv::Op::OpReturnValue:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(0)));
+        break;
+      case spv::Op::OpCopyObject:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(2)));
+        break;
+      case spv::Op::OpLoad:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(2)));
+        break;
+      case spv::Op::OpStore:
+        stack.push_back(_.FindDef(trace_inst->GetOperandAs<uint32_t>(0)));
+        break;
+      case spv::Op::OpVariable:
+      case spv::Op::OpUntypedVariableKHR: {
+        const auto sc = trace_inst->GetOperandAs<spv::StorageClass>(2);
+        if (sc == spv::StorageClass::Function ||
+            sc == spv::StorageClass::Private) {
+          // Add the initializer
+          const uint32_t init_operand = untyped ? 4 : 3;
+          if (trace_inst->operands().size() > init_operand) {
+            stack.push_back(
+                _.FindDef(trace_inst->GetOperandAs<uint32_t>(init_operand)));
+          }
+          // Jump to stores
+          std::vector<std::pair<const Instruction*, uint32_t>> store_stack(
+              trace_inst->uses());
+          std::unordered_set<const Instruction*> store_seen;
+          while (!store_stack.empty()) {
+            const auto& use = store_stack.back();
+            store_stack.pop_back();
+
+            if (!store_seen.insert(use.first).second) {
+              continue;
+            }
+
+            // If the use is a store pointer, trace the store object.
+            // Note: use.second is a word index.
+            if (use.first->opcode() == spv::Op::OpStore && use.second == 1) {
+              stack.push_back(_.FindDef(use.first->GetOperandAs<uint32_t>(1)));
+            } else {
+              // Most likely a gep so keep tracing.
+              for (auto& next_use : use.first->uses()) {
+                store_stack.push_back(next_use);
+              }
+            }
+          }
+        }
+        break;
+      }
+      default:
+        break;
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateVariablePointers(
+    ValidationState_t& _, std::unordered_map<uint32_t, bool>& variable_pointers,
+    const Instruction* inst) {
+  // Variable pointers cannot be operands to array length.
+  if (inst->opcode() == spv::Op::OpArrayLength ||
+      inst->opcode() == spv::Op::OpUntypedArrayLengthKHR) {
+    const auto ptr_index = inst->opcode() == spv::Op::OpArrayLength ? 2 : 3;
+    const auto ptr_id = inst->GetOperandAs<uint32_t>(ptr_index);
+    const auto ptr_inst = _.FindDef(ptr_id);
+    if (IsVariablePointer(_, variable_pointers, ptr_inst)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Pointer operand must not be a variable pointer";
+    }
+    return SPV_SUCCESS;
+  }
+
+  // Check untyped loads and stores of variable pointers for matrix types.
+  // Neither instruction would be a variable pointer in a such a case.
+  if (inst->opcode() == spv::Op::OpLoad) {
+    const auto pointer = _.FindDef(inst->GetOperandAs<uint32_t>(2));
+    const auto pointer_type = _.FindDef(pointer->type_id());
+    if (pointer_type->opcode() == spv::Op::OpTypeUntypedPointerKHR &&
+        IsVariablePointer(_, variable_pointers, pointer)) {
+      const auto data_type = _.FindDef(inst->type_id());
+      if (_.ContainsType(
+              data_type->id(),
+              [](const Instruction* type_inst) {
+                return type_inst->opcode() == spv::Op::OpTypeMatrix;
+              },
+              /* traverse_all_types = */ false)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Variable pointer must not point to an object that is or "
+                  "contains a matrix";
+      }
+    }
+  } else if (inst->opcode() == spv::Op::OpStore) {
+    const auto pointer = _.FindDef(inst->GetOperandAs<uint32_t>(0));
+    const auto pointer_type = _.FindDef(pointer->type_id());
+    if (pointer_type->opcode() == spv::Op::OpTypeUntypedPointerKHR &&
+        IsVariablePointer(_, variable_pointers, pointer)) {
+      const auto data_type_id = _.GetOperandTypeId(inst, 1);
+      const auto data_type = _.FindDef(data_type_id);
+      if (_.ContainsType(
+              data_type->id(),
+              [](const Instruction* type_inst) {
+                return type_inst->opcode() == spv::Op::OpTypeMatrix;
+              },
+              /* traverse_all_types = */ false)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Variable pointer must not point to an object that is or "
+                  "contains a matrix";
+      }
+    }
+  }
+
+  if (!IsLogicalPointer(_, inst) ||
+      !IsVariablePointer(_, variable_pointers, inst)) {
+    return SPV_SUCCESS;
+  }
+
+  const auto result_type = _.FindDef(inst->type_id());
+  const auto untyped =
+      result_type->opcode() == spv::Op::OpTypeUntypedPointerKHR;
+
+  // Pointers must be selected from the same buffer unless the VariablePointers
+  // capability is declared.
+  if (!_.HasCapability(spv::Capability::VariablePointers) &&
+      (inst->opcode() == spv::Op::OpSelect ||
+       inst->opcode() == spv::Op::OpPhi)) {
+    std::unordered_set<const Instruction*> sources;
+    const auto checker = [&sources, &inst](
+                             ValidationState_t& vstate,
+                             const Instruction* check_inst) -> spv_result_t {
+      switch (check_inst->opcode()) {
+        case spv::Op::OpVariable:
+        case spv::Op::OpUntypedVariableKHR:
+          if (check_inst->GetOperandAs<spv::StorageClass>(2) ==
+                  spv::StorageClass::StorageBuffer ||
+              check_inst->GetOperandAs<spv::StorageClass>(2) ==
+                  spv::StorageClass::Workgroup) {
+            sources.insert(check_inst);
+          }
+          if (sources.size() > 1) {
+            return vstate.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << "Variable pointers must point into the same structure "
+                      "(or OpConstantNull)";
+          }
+          break;
+        default:
+          break;
+      }
+      return SPV_SUCCESS;
+    };
+    if (auto error = TraceVariablePointers(_, inst, checker)) {
+      return error;
+    }
+  }
+
+  // Variable pointers must not:
+  // * point to array of Block- or BufferBlock-decorated structs
+  // * point to an object that is or contains a matrix
+  // * point to a column, or component in a column, of a matrix
+  if (untyped) {
+    if (auto error =
+            TraceVariablePointers(_, inst, CheckMatrixElementUntyped)) {
+      return error;
+    }
+
+    // Block arrays can only really appear as the top most type so only look at
+    // unmodified pointers to determine if one is used.
+    const auto num_operands = inst->operands().size();
+    if (!(num_operands == 3 &&
+          (inst->opcode() == spv::Op::OpUntypedAccessChainKHR ||
+           inst->opcode() == spv::Op::OpUntypedInBoundsAccessChainKHR ||
+           inst->opcode() == spv::Op::OpUntypedPtrAccessChainKHR))) {
+      const auto checker = [&inst](
+                               ValidationState_t& vstate,
+                               const Instruction* check_inst) -> spv_result_t {
+        bool fail = false;
+        if (check_inst->opcode() == spv::Op::OpUntypedVariableKHR) {
+          if (check_inst->operands().size() > 3) {
+            const auto type =
+                vstate.FindDef(check_inst->GetOperandAs<uint32_t>(3));
+            fail = IsBlockArray(vstate, type);
+          }
+        } else if (check_inst->opcode() == spv::Op::OpVariable) {
+          const auto res_type = vstate.FindDef(check_inst->type_id());
+          const auto pointee_type =
+              vstate.FindDef(res_type->GetOperandAs<uint32_t>(2));
+          fail = IsBlockArray(vstate, pointee_type);
+        }
+
+        if (fail) {
+          return vstate.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Variable pointer must not point to an array of Block- or "
+                    "BufferBlock-decorated structs";
+        }
+        return SPV_SUCCESS;
+      };
+
+      if (auto error = TraceUnmodifiedVariablePointers(_, inst, checker)) {
+        return error;
+      }
+    }
+  } else {
+    const auto pointee_type = _.FindDef(result_type->GetOperandAs<uint32_t>(2));
+    if (IsBlockArray(_, pointee_type)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Variable pointer must not point to an array of Block- or "
+                "BufferBlock-decorated structs";
+    } else if (_.ContainsType(
+                   pointee_type->id(),
+                   [](const Instruction* type_inst) {
+                     return type_inst->opcode() == spv::Op::OpTypeMatrix;
+                   },
+                   /* traverse_all_types = */ false)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << "Variable pointer must not point to an object that is or "
+                "contains a matrix";
+    } else if (_.IsFloatScalarOrVectorType(pointee_type->id())) {
+      // Pointing to a column or component in a column is trickier to detect.
+      // Trace backwards and check encountered access chains to determine if
+      // this pointer is pointing into a matrix.
+      if (auto error =
+              TraceVariablePointers(_, inst, CheckMatrixElementTyped)) {
+        return error;
+      }
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+}  // namespace
+
+spv_result_t ValidateLogicalPointers(ValidationState_t& _) {
+  // Only the following addressing models have logical pointers.
+  if (_.addressing_model() != spv::AddressingModel::Logical &&
+      _.addressing_model() != spv::AddressingModel::PhysicalStorageBuffer64) {
+    return SPV_SUCCESS;
+  }
+
+  if (_.options()->relax_logical_pointer) {
+    return SPV_SUCCESS;
+  }
+
+  // Cache all variable pointers
+  std::unordered_map<uint32_t, bool> variable_pointers;
+  for (auto& inst : _.ordered_instructions()) {
+    if (!IsLogicalPointer(_, &inst)) {
+      continue;
+    }
+
+    IsVariablePointer(_, variable_pointers, &inst);
+  }
+
+  for (auto& inst : _.ordered_instructions()) {
+    if (auto error = ValidateLogicalPointerOperands(_, &inst)) {
+      return error;
+    }
+    if (auto error = ValidateLogicalPointerReturns(_, &inst)) {
+      return error;
+    }
+    if (auto error = ValidateVariablePointers(_, variable_pointers, &inst)) {
+      return error;
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+}  // namespace val
+}  // namespace spvtools

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

@@ -515,6 +515,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
       storage_class != spv::StorageClass::IncomingCallableDataKHR &&
       storage_class != spv::StorageClass::TaskPayloadWorkgroupEXT &&
       storage_class != spv::StorageClass::HitObjectAttributeNV &&
+      storage_class != spv::StorageClass::HitObjectAttributeEXT &&
       storage_class != spv::StorageClass::NodePayloadAMDX) {
     bool storage_input_or_output = storage_class == spv::StorageClass::Input ||
                                    storage_class == spv::StorageClass::Output;
@@ -586,20 +587,38 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
   const auto pointee = untyped_pointer
                            ? value_id == 0 ? nullptr : _.FindDef(value_id)
                            : _.FindDef(result_type->word(3));
-  if (_.addressing_model() == spv::AddressingModel::Logical &&
+  if ((_.addressing_model() == spv::AddressingModel::Logical ||
+       _.addressing_model() == spv::AddressingModel::PhysicalStorageBuffer64) &&
       !_.options()->relax_logical_pointer) {
-    // VariablePointersStorageBuffer is implied by VariablePointers.
-    if (pointee && pointee->opcode() == spv::Op::OpTypePointer) {
-      if (!_.HasCapability(spv::Capability::VariablePointersStorageBuffer)) {
-        return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << "In Logical addressing, variables may not allocate a pointer "
-               << "type";
-      } else if (storage_class != spv::StorageClass::Function &&
-                 storage_class != spv::StorageClass::Private) {
-        return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << "In Logical addressing with variable pointers, variables "
-               << "that allocate pointers must be in Function or Private "
-               << "storage classes";
+    if (pointee && (pointee->opcode() == spv::Op::OpTypePointer ||
+                    pointee->opcode() == spv::Op::OpTypeUntypedPointerKHR)) {
+      const auto sc = pointee->GetOperandAs<spv::StorageClass>(1u);
+      if (sc != spv::StorageClass::PhysicalStorageBuffer) {
+        if (sc != spv::StorageClass::StorageBuffer &&
+            sc != spv::StorageClass::Workgroup) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << "In Logical addressing, variables can only allocate a "
+                    "pointer to the StorageBuffer or Workgroup storage classes";
+        } else if (!_.HasCapability(
+                       spv::Capability::VariablePointersStorageBuffer) &&
+                   sc == spv::StorageClass::StorageBuffer) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << "In Logical addressing, variables can only allocate a "
+                    "storage buffer pointer if the "
+                    "VariablePointersStorageBuffer capability is declared";
+        } else if (!_.HasCapability(spv::Capability::VariablePointers) &&
+                   sc == spv::StorageClass::Workgroup) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << "In Logical addressing, variables can only allocate a "
+                    "workgroup pointer if the VariablePointers capability is "
+                    "declared";
+        } else if (storage_class != spv::StorageClass::Function &&
+                   storage_class != spv::StorageClass::Private) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << "In Logical addressing with variable pointers, variables "
+                 << "that allocate pointers must be in Function or Private "
+                 << "storage classes";
+        }
       }
     }
   }
@@ -738,6 +757,11 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
              << "OpVariable, <id> " << _.getIdName(inst->id())
              << ", initializer are not allowed for HitObjectAttributeNV";
     }
+    if (storage_class == spv::StorageClass::HitObjectAttributeEXT) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "OpVariable, <id> " << _.getIdName(inst->id())
+             << ", initializer are not allowed for HitObjectAttributeEXT";
+    }
   }
 
   if (storage_class == spv::StorageClass::PhysicalStorageBuffer) {
@@ -776,8 +800,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
 
     // If an OpStruct has an OpTypeRuntimeArray somewhere within it, then it
     // must either have the storage class StorageBuffer and be decorated
-    // with Block, or it must be in the Uniform storage class and be decorated
-    // as BufferBlock.
+    // with Block, or it must be in the Uniform storage class
     if (value_type && value_type->opcode() == spv::Op::OpTypeStruct) {
       if (DoesStructContainRTA(_, value_type)) {
         if (storage_class == spv::StorageClass::StorageBuffer ||
@@ -791,13 +814,14 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
                       "PhysicalStorageBuffer.";
           }
         } else if (storage_class == spv::StorageClass::Uniform) {
-          if (!_.HasDecoration(value_id, spv::Decoration::BufferBlock)) {
-            return _.diag(SPV_ERROR_INVALID_ID, inst)
-                   << _.VkErrorID(4680)
-                   << "For Vulkan, an OpTypeStruct variable containing an "
-                   << "OpTypeRuntimeArray must be decorated with BufferBlock "
-                   << "if it has storage class Uniform.";
-          }
+          // BufferBlock Uniform were always allowed.
+          //
+          // Block Uniform use to be invalid, but Vulkan added
+          // VK_EXT_shader_uniform_buffer_unsized_array and now this is
+          // validated at runtime
+          //
+          // The uniform must have either the Block or BufferBlock decoration
+          // (see VUID-StandaloneSpirv-Uniform-06676)
         } else {
           return _.diag(SPV_ERROR_INVALID_ID, inst)
                  << _.VkErrorID(4680)
@@ -1299,7 +1323,7 @@ spv_result_t ValidateCopyMemoryMemoryAccess(ValidationState_t& _,
         }
       } else {
         return _.diag(SPV_ERROR_INVALID_DATA, inst)
-               << spvOpcodeString(static_cast<spv::Op>(inst->opcode()))
+               << spvOpcodeString(inst->opcode())
                << " with two memory access operands requires SPIR-V 1.4 or "
                   "later";
       }
@@ -1551,9 +1575,7 @@ spv_result_t ValidateCopyMemory(ValidationState_t& _, const Instruction* inst) {
 
 spv_result_t ValidateAccessChain(ValidationState_t& _,
                                  const Instruction* inst) {
-  std::string instr_name =
-      "Op" + std::string(spvOpcodeString(static_cast<spv::Op>(inst->opcode())));
-
+  const spv::Op opcode = inst->opcode();
   const bool untyped_pointer = spvOpcodeGeneratesUntypedPointer(inst->opcode());
 
   // The result type must be OpTypePointer for regular access chains and an
@@ -1563,19 +1585,17 @@ spv_result_t ValidateAccessChain(ValidationState_t& _,
     if (!result_type ||
         spv::Op::OpTypeUntypedPointerKHR != result_type->opcode()) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
-             << "The Result Type of " << instr_name << " <id> "
+             << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> "
              << _.getIdName(inst->id())
              << " must be OpTypeUntypedPointerKHR. Found Op"
-             << spvOpcodeString(static_cast<spv::Op>(result_type->opcode()))
-             << ".";
+             << spvOpcodeString(result_type->opcode()) << ".";
     }
   } else {
     if (!result_type || spv::Op::OpTypePointer != result_type->opcode()) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
-             << "The Result Type of " << instr_name << " <id> "
+             << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> "
              << _.getIdName(inst->id()) << " must be OpTypePointer. Found Op"
-             << spvOpcodeString(static_cast<spv::Op>(result_type->opcode()))
-             << ".";
+             << spvOpcodeString(result_type->opcode()) << ".";
     }
   }
 
@@ -1653,8 +1673,8 @@ spv_result_t ValidateAccessChain(ValidationState_t& _,
                       (untyped_pointer && spv::Op::OpTypeUntypedPointerKHR ==
                                               base_type->opcode()))) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The Base <id> " << _.getIdName(base_id) << " in " << instr_name
-           << " instruction must be a pointer.";
+           << "The Base <id> " << _.getIdName(base_id) << " in Op"
+           << spvOpcodeString(opcode) << " instruction must be a pointer.";
   }
 
   // The result pointer storage class and base pointer storage class must match.
@@ -1664,8 +1684,8 @@ spv_result_t ValidateAccessChain(ValidationState_t& _,
   if (result_type_storage_class != base_type_storage_class) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "The result pointer storage class and base "
-              "pointer storage class in "
-           << instr_name << " do not match.";
+              "pointer storage class in Op"
+           << spvOpcodeString(opcode) << " do not match.";
   }
 
   // The type pointed to by OpTypePointer (word 3) must be a composite type.
@@ -1689,8 +1709,9 @@ spv_result_t ValidateAccessChain(ValidationState_t& _,
       _.options()->universal_limits_.max_access_chain_indexes;
   if (num_indexes > num_indexes_limit) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The number of indexes in " << instr_name << " may not exceed "
-           << num_indexes_limit << ". Found " << num_indexes << " indexes.";
+           << "The number of indexes in Op" << spvOpcodeString(opcode)
+           << " may not exceed " << num_indexes_limit << ". Found "
+           << num_indexes << " indexes.";
   }
   // Indexes walk the type hierarchy to the desired depth, potentially down to
   // scalar granularity. The first index in Indexes will select the top-level
@@ -1714,9 +1735,29 @@ spv_result_t ValidateAccessChain(ValidationState_t& _,
     auto index_type = _.FindDef(cur_word_instr->type_id());
     if (!index_type || spv::Op::OpTypeInt != index_type->opcode()) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
-             << "Indexes passed to " << instr_name
+             << "Indexes passed to Op" << spvOpcodeString(opcode)
              << " must be of type integer.";
     }
+
+    // Logical pointer restrictions: any constant index with a signed integer
+    // type must not have its sign bit set.
+    if (!_.options()->relax_logical_pointer &&
+        (_.addressing_model() == spv::AddressingModel::Logical ||
+         _.addressing_model() ==
+             spv::AddressingModel::PhysicalStorageBuffer64) &&
+        result_type_storage_class !=
+            static_cast<uint32_t>(spv::StorageClass::PhysicalStorageBuffer)) {
+      if (index_type->GetOperandAs<uint32_t>(2) == 1) {
+        int64_t val = 0;
+        if (_.EvalConstantValInt64(cur_word, &val)) {
+          if (val < 0) {
+            return _.diag(SPV_ERROR_INVALID_ID, inst)
+                   << "Index at word " << i << " may not have a negative value";
+          }
+        }
+      }
+    }
+
     switch (type_pointee->opcode()) {
       case spv::Op::OpTypeMatrix:
       case spv::Op::OpTypeVector:
@@ -1738,8 +1779,8 @@ spv_result_t ValidateAccessChain(ValidationState_t& _,
         int64_t cur_index;
         if (!_.EvalConstantValInt64(cur_word, &cur_index)) {
           return _.diag(SPV_ERROR_INVALID_ID, inst)
-                 << "The <id> passed to " << instr_name << " to index "
-                 << _.getIdName(cur_word)
+                 << "The <id> passed to Op" << spvOpcodeString(opcode)
+                 << " to index " << _.getIdName(cur_word)
                  << " into a "
                     "structure must be an OpConstant.";
         }
@@ -1750,8 +1791,8 @@ spv_result_t ValidateAccessChain(ValidationState_t& _,
             static_cast<int64_t>(type_pointee->words().size() - 2);
         if (cur_index >= num_struct_members || cur_index < 0) {
           return _.diag(SPV_ERROR_INVALID_ID, inst)
-                 << "Index " << _.getIdName(cur_word)
-                 << " is out of bounds: " << instr_name << " cannot find index "
+                 << "Index " << _.getIdName(cur_word) << " is out of bounds: Op"
+                 << spvOpcodeString(opcode) << " cannot find index "
                  << cur_index << " into the structure <id> "
                  << _.getIdName(type_pointee->id()) << ". This structure has "
                  << num_struct_members << " members. Largest valid index is "
@@ -1766,7 +1807,7 @@ spv_result_t ValidateAccessChain(ValidationState_t& _,
       default: {
         // Give an error. reached non-composite type while indexes still remain.
         return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << instr_name
+               << "Op" << spvOpcodeString(opcode)
                << " reached non-composite type while indexes "
                   "still remain to be traversed.";
       }
@@ -1782,14 +1823,12 @@ spv_result_t ValidateAccessChain(ValidationState_t& _,
     // The type being pointed to should be the same as the result type.
     if (type_pointee->id() != result_type_pointee->id()) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
-             << instr_name << " result type (Op"
-             << spvOpcodeString(
-                    static_cast<spv::Op>(result_type_pointee->opcode()))
+             << "Op" << spvOpcodeString(opcode) << " result type (Op"
+             << spvOpcodeString(result_type_pointee->opcode())
              << ") does not match the type that results from indexing into the "
                 "base "
                 "<id> (Op"
-             << spvOpcodeString(static_cast<spv::Op>(type_pointee->opcode()))
-             << ").";
+             << spvOpcodeString(type_pointee->opcode()) << ").";
     }
   }
 
@@ -1798,13 +1837,12 @@ spv_result_t ValidateAccessChain(ValidationState_t& _,
 
 spv_result_t ValidateRawAccessChain(ValidationState_t& _,
                                     const Instruction* inst) {
-  std::string instr_name = "Op" + std::string(spvOpcodeString(inst->opcode()));
-
+  const spv::Op opcode = inst->opcode();
   // The result type must be OpTypePointer.
   const auto result_type = _.FindDef(inst->type_id());
   if (spv::Op::OpTypePointer != result_type->opcode()) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "The Result Type of " << instr_name << " <id> "
+           << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> "
            << _.getIdName(inst->id()) << " must be OpTypePointer. Found Op"
            << spvOpcodeString(result_type->opcode()) << '.';
   }
@@ -1815,7 +1853,7 @@ spv_result_t ValidateRawAccessChain(ValidationState_t& _,
       storage_class != spv::StorageClass::PhysicalStorageBuffer &&
       storage_class != spv::StorageClass::Uniform) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "The Result Type of " << instr_name << " <id> "
+           << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> "
            << _.getIdName(inst->id())
            << " must point to a storage class of "
               "StorageBuffer, PhysicalStorageBuffer, or Uniform.";
@@ -1828,7 +1866,7 @@ spv_result_t ValidateRawAccessChain(ValidationState_t& _,
       result_type_pointee->opcode() == spv::Op::OpTypeMatrix ||
       result_type_pointee->opcode() == spv::Op::OpTypeStruct) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "The Result Type of " << instr_name << " <id> "
+           << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> "
            << _.getIdName(inst->id())
            << " must not point to "
               "OpTypeArray, OpTypeMatrix, or OpTypeStruct.";
@@ -1838,7 +1876,7 @@ spv_result_t ValidateRawAccessChain(ValidationState_t& _,
   const auto stride = _.FindDef(inst->GetOperandAs<uint32_t>(3));
   if (stride->opcode() != spv::Op::OpConstant) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "The Stride of " << instr_name << " <id> "
+           << "The Stride of Op" << spvOpcodeString(opcode) << " <id> "
            << _.getIdName(inst->id()) << " must be OpConstant. Found Op"
            << spvOpcodeString(stride->opcode()) << '.';
   }
@@ -1846,7 +1884,7 @@ spv_result_t ValidateRawAccessChain(ValidationState_t& _,
   const auto stride_type = _.FindDef(stride->type_id());
   if (stride_type->opcode() != spv::Op::OpTypeInt) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "The type of Stride of " << instr_name << " <id> "
+           << "The type of Stride of Op" << spvOpcodeString(opcode) << " <id> "
            << _.getIdName(inst->id()) << " must be OpTypeInt. Found Op"
            << spvOpcodeString(stride_type->opcode()) << '.';
   }
@@ -1858,16 +1896,17 @@ spv_result_t ValidateRawAccessChain(ValidationState_t& _,
     const auto value_type = _.FindDef(value->type_id());
     if (value_type->opcode() != spv::Op::OpTypeInt) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "The type of " << name << " of " << instr_name << " <id> "
-             << _.getIdName(inst->id()) << " must be OpTypeInt. Found Op"
+             << "The type of " << name << " of Op" << spvOpcodeString(opcode)
+             << " <id> " << _.getIdName(inst->id())
+             << " must be OpTypeInt. Found Op"
              << spvOpcodeString(value_type->opcode()) << '.';
     }
     const auto width = value_type->GetOperandAs<uint32_t>(1);
     if (width != 32) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "The integer width of " << name << " of " << instr_name
-             << " <id> " << _.getIdName(inst->id()) << " must be 32. Found "
-             << width << '.';
+             << "The integer width of " << name << " of Op"
+             << spvOpcodeString(opcode) << " <id> " << _.getIdName(inst->id())
+             << " must be 32. Found " << width << '.';
     }
     return SPV_SUCCESS;
   };
@@ -1918,15 +1957,6 @@ spv_result_t ValidateRawAccessChain(ValidationState_t& _,
 
 spv_result_t ValidatePtrAccessChain(ValidationState_t& _,
                                     const Instruction* inst) {
-  if (_.addressing_model() == spv::AddressingModel::Logical &&
-      inst->opcode() == spv::Op::OpPtrAccessChain) {
-    if (!_.features().variable_pointers) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Generating variable pointers requires capability "
-             << "VariablePointers or VariablePointersStorageBuffer";
-    }
-  }
-
   // Need to call first, will make sure Base is a valid ID
   if (auto error = ValidateAccessChain(_, inst)) return error;
 
@@ -2006,18 +2036,20 @@ spv_result_t ValidatePtrAccessChain(ValidationState_t& _,
 
 spv_result_t ValidateArrayLength(ValidationState_t& state,
                                  const Instruction* inst) {
-  std::string instr_name =
-      "Op" + std::string(spvOpcodeString(static_cast<spv::Op>(inst->opcode())));
+  const spv::Op opcode = inst->opcode();
 
-  // Result type must be a 32-bit unsigned int.
+  // Result type must be a 32- or 64-bit unsigned int.
+  // 64-bit requires CapabilityShader64BitIndexingEXT or a pipeline/shader
+  // flag and is validated in VVL.
   auto result_type = state.FindDef(inst->type_id());
   if (result_type->opcode() != spv::Op::OpTypeInt ||
-      result_type->GetOperandAs<uint32_t>(1) != 32 ||
+      !(result_type->GetOperandAs<uint32_t>(1) == 32 ||
+        result_type->GetOperandAs<uint32_t>(1) == 64) ||
       result_type->GetOperandAs<uint32_t>(2) != 0) {
     return state.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The Result Type of " << instr_name << " <id> "
+           << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> "
            << state.getIdName(inst->id())
-           << " must be OpTypeInt with width 32 and signedness 0.";
+           << " must be OpTypeInt with width 32 or 64 and signedness 0.";
   }
 
   const bool untyped = inst->opcode() == spv::Op::OpUntypedArrayLengthKHR;
@@ -2030,8 +2062,8 @@ spv_result_t ValidateArrayLength(ValidationState_t& state,
     }
   } else if (pointer_ty->opcode() != spv::Op::OpTypePointer) {
     return state.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The Structure's type in " << instr_name << " <id> "
-           << state.getIdName(inst->id())
+           << "The Structure's type in Op" << spvOpcodeString(opcode)
+           << " <id> " << state.getIdName(inst->id())
            << " must be a pointer to an OpTypeStruct.";
   }
 
@@ -2044,8 +2076,8 @@ spv_result_t ValidateArrayLength(ValidationState_t& state,
 
   if (structure_type->opcode() != spv::Op::OpTypeStruct) {
     return state.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The Structure's type in " << instr_name << " <id> "
-           << state.getIdName(inst->id())
+           << "The Structure's type in Op" << spvOpcodeString(opcode)
+           << " <id> " << state.getIdName(inst->id())
            << " must be a pointer to an OpTypeStruct.";
   }
 
@@ -2054,8 +2086,9 @@ spv_result_t ValidateArrayLength(ValidationState_t& state,
       state.FindDef(structure_type->GetOperandAs<uint32_t>(num_of_members));
   if (last_member->opcode() != spv::Op::OpTypeRuntimeArray) {
     return state.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The Structure's last member in " << instr_name << " <id> "
-           << state.getIdName(inst->id()) << " must be an OpTypeRuntimeArray.";
+           << "The Structure's last member in Op" << spvOpcodeString(opcode)
+           << " <id> " << state.getIdName(inst->id())
+           << " must be an OpTypeRuntimeArray.";
   }
 
   // The array member must the index of the last element (the run time
@@ -2063,25 +2096,35 @@ spv_result_t ValidateArrayLength(ValidationState_t& state,
   const auto index = untyped ? 4 : 3;
   if (inst->GetOperandAs<uint32_t>(index) != num_of_members - 1) {
     return state.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The array member in " << instr_name << " <id> "
+           << "The array member in Op" << spvOpcodeString(opcode) << " <id> "
            << state.getIdName(inst->id())
            << " must be the last member of the struct.";
   }
+
+  if (spvIsVulkanEnv(state.context()->target_env)) {
+    const auto storage_class = pointer_ty->GetOperandAs<spv::StorageClass>(1);
+    if (storage_class == spv::StorageClass::Uniform &&
+        state.HasDecoration(structure_type->id(), spv::Decoration::Block)) {
+      return state.diag(SPV_ERROR_INVALID_ID, inst)
+             << state.VkErrorID(11805) << "Op" << spvOpcodeString(opcode)
+             << " must not be used on the OpTypeRuntimeArray inside a Uniform "
+                "block";
+    }
+  }
+
   return SPV_SUCCESS;
 }
 
 spv_result_t ValidateCooperativeMatrixLengthNV(ValidationState_t& state,
                                                const Instruction* inst) {
-  std::string instr_name =
-      "Op" + std::string(spvOpcodeString(static_cast<spv::Op>(inst->opcode())));
-
+  const spv::Op opcode = inst->opcode();
   // Result type must be a 32-bit unsigned int.
   auto result_type = state.FindDef(inst->type_id());
   if (result_type->opcode() != spv::Op::OpTypeInt ||
       result_type->GetOperandAs<uint32_t>(1) != 32 ||
       result_type->GetOperandAs<uint32_t>(2) != 0) {
     return state.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The Result Type of " << instr_name << " <id> "
+           << "The Result Type of Op" << spvOpcodeString(opcode) << " <id> "
            << state.getIdName(inst->id())
            << " must be OpTypeInt with width 32 and signedness 0.";
   }
@@ -2091,12 +2134,12 @@ spv_result_t ValidateCooperativeMatrixLengthNV(ValidationState_t& state,
   auto type = state.FindDef(type_id);
   if (isKhr && type->opcode() != spv::Op::OpTypeCooperativeMatrixKHR) {
     return state.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The type in " << instr_name << " <id> "
+           << "The type in Op" << spvOpcodeString(opcode) << " <id> "
            << state.getIdName(type_id)
            << " must be OpTypeCooperativeMatrixKHR.";
   } else if (!isKhr && type->opcode() != spv::Op::OpTypeCooperativeMatrixNV) {
     return state.diag(SPV_ERROR_INVALID_ID, inst)
-           << "The type in " << instr_name << " <id> "
+           << "The type in Op" << spvOpcodeString(opcode) << " <id> "
            << state.getIdName(type_id) << " must be OpTypeCooperativeMatrixNV.";
   }
   return SPV_SUCCESS;
@@ -2299,23 +2342,96 @@ spv_result_t ValidateCooperativeMatrixLoadStoreKHR(ValidationState_t& _,
   }
 
   bool stride_required = false;
+  bool layout_requires_constant_stride = false;
   uint64_t layout;
   if (_.EvalConstantValUint64(layout_id, &layout)) {
+    const bool is_arm_layout =
+        (layout ==
+         (uint64_t)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM) ||
+        (layout ==
+         (uint64_t)spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM);
+
+    if (is_arm_layout) {
+      if (!_.HasCapability(spv::Capability::CooperativeMatrixLayoutsARM)) {
+        return _.diag(SPV_ERROR_INVALID_ID, inst)
+               << "Using the RowBlockedInterleavedARM or "
+                  "ColumnBlockedInterleavedARM MemoryLayout requires the "
+                  "CooperativeMatrixLayoutsARM capability be declared";
+      }
+    }
+
     stride_required =
         (layout == (uint64_t)spv::CooperativeMatrixLayout::RowMajorKHR) ||
-        (layout == (uint64_t)spv::CooperativeMatrixLayout::ColumnMajorKHR);
+        (layout == (uint64_t)spv::CooperativeMatrixLayout::ColumnMajorKHR) ||
+        is_arm_layout;
+    layout_requires_constant_stride = is_arm_layout;
   }
 
   const auto stride_index =
       (inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) ? 4u : 3u;
   if (inst->operands().size() > stride_index) {
     const auto stride_id = inst->GetOperandAs<uint32_t>(stride_index);
-    const auto stride = _.FindDef(stride_id);
-    if (!stride || !_.IsIntScalarType(stride->type_id())) {
+    const auto stride_inst = _.FindDef(stride_id);
+    if (!stride_inst || !_.IsIntScalarType(stride_inst->type_id())) {
       return _.diag(SPV_ERROR_INVALID_ID, inst)
              << "Stride operand <id> " << _.getIdName(stride_id)
              << " must be a scalar integer type.";
     }
+    // Check SPV_ARM_cooperative_matrix_layouts constraints
+    if (layout_requires_constant_stride &&
+        !spvOpcodeIsConstant(stride_inst->opcode())) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "MemoryLayout " << layout
+             << " requires Stride come from a constant instruction.";
+    }
+    if (layout_requires_constant_stride) {
+      uint64_t stride;
+      if (_.EvalConstantValUint64(stride_id, &stride)) {
+        if ((layout ==
+             (uint64_t)
+                 spv::CooperativeMatrixLayout::RowBlockedInterleavedARM) ||
+            (layout ==
+             (uint64_t)
+                 spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM)) {
+          if ((stride != 1) && (stride != 2) && (stride != 4)) {
+            return _.diag(SPV_ERROR_INVALID_ID, inst)
+                   << "MemoryLayout " << layout
+                   << " requires Stride be 1, 2, or 4.";
+          }
+        }
+        const uint32_t elty_id = matrix_type->GetOperandAs<uint32_t>(1);
+        const uint32_t rows_id = matrix_type->GetOperandAs<uint32_t>(3);
+        const uint32_t cols_id = matrix_type->GetOperandAs<uint32_t>(4);
+        uint64_t rows = 0, cols = 0;
+        _.EvalConstantValUint64(rows_id, &rows);
+        _.EvalConstantValUint64(cols_id, &cols);
+        uint32_t sizeof_component_in_bytes = _.GetBitWidth(elty_id) / 8;
+        uint64_t rows_required_multiple = 4;
+        uint64_t cols_required_multiple = 16 / sizeof_component_in_bytes;
+
+        if (layout ==
+            (uint64_t)spv::CooperativeMatrixLayout::RowBlockedInterleavedARM) {
+          cols_required_multiple *= stride;
+        }
+        if (layout ==
+            (uint64_t)
+                spv::CooperativeMatrixLayout::ColumnBlockedInterleavedARM) {
+          rows_required_multiple *= stride;
+        }
+        if ((rows != 0) && (rows % rows_required_multiple != 0)) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << "MemoryLayout " << layout << " with a Stride of " << stride
+                 << " requires that the number of rows be a multiple of "
+                 << rows_required_multiple;
+        }
+        if ((cols != 0) && (cols % cols_required_multiple != 0)) {
+          return _.diag(SPV_ERROR_INVALID_ID, inst)
+                 << "MemoryLayout " << layout << " with a Stride of " << stride
+                 << " requires that the number of columns be a multiple of "
+                 << cols_required_multiple;
+        }
+      }
+    }
   } else if (stride_required) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "MemoryLayout " << layout << " requires a Stride.";
@@ -2561,6 +2677,23 @@ spv_result_t ValidateInt32Operand(ValidationState_t& _, const Instruction* inst,
   return SPV_SUCCESS;
 }
 
+spv_result_t ValidateInt32Or64Operand(ValidationState_t& _,
+                                      const Instruction* inst,
+                                      uint32_t operand_index,
+                                      const char* opcode_name,
+                                      const char* operand_name) {
+  const auto type_id =
+      _.FindDef(inst->GetOperandAs<uint32_t>(operand_index))->type_id();
+  if (!_.IsIntScalarType(type_id) ||
+      !(_.GetBitWidth(type_id) == 32 || _.GetBitWidth(type_id) == 64)) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << opcode_name << " " << operand_name << " type <id> "
+           << _.getIdName(type_id) << " is not a 32 or 64 bit integer.";
+  }
+
+  return SPV_SUCCESS;
+}
+
 spv_result_t ValidateCooperativeVectorPointer(ValidationState_t& _,
                                               const Instruction* inst,
                                               const char* opname,
@@ -2651,11 +2784,19 @@ spv_result_t ValidateCooperativeVectorLoadStoreNV(ValidationState_t& _,
   const auto pointer_index =
       (inst->opcode() == spv::Op::OpCooperativeVectorLoadNV) ? 2u : 0u;
 
+  const auto offset_index =
+      (inst->opcode() == spv::Op::OpCooperativeVectorLoadNV) ? 3u : 1u;
+
   if (auto error =
           ValidateCooperativeVectorPointer(_, inst, opname, pointer_index)) {
     return error;
   }
 
+  if (auto error =
+          ValidateInt32Or64Operand(_, inst, offset_index, opname, "Offset")) {
+    return error;
+  }
+
   const auto memory_access_index =
       (inst->opcode() == spv::Op::OpCooperativeVectorLoadNV) ? 4u : 3u;
   if (inst->operands().size() > memory_access_index) {
@@ -2705,7 +2846,8 @@ spv_result_t ValidateCooperativeVectorOuterProductNV(ValidationState_t& _,
            << _.getIdName(b_component_type_id) << " do not match.";
   }
 
-  if (auto error = ValidateInt32Operand(_, inst, 1, opcode_name, "Offset")) {
+  if (auto error =
+          ValidateInt32Or64Operand(_, inst, 1, opcode_name, "Offset")) {
     return error;
   }
 
@@ -2748,7 +2890,8 @@ spv_result_t ValidateCooperativeVectorReduceSumNV(ValidationState_t& _,
            << " is not a cooperative vector type.";
   }
 
-  if (auto error = ValidateInt32Operand(_, inst, 1, opcode_name, "Offset")) {
+  if (auto error =
+          ValidateInt32Or64Operand(_, inst, 1, opcode_name, "Offset")) {
     return error;
   }
 
@@ -2781,8 +2924,10 @@ spv_result_t ValidateCooperativeVectorMatrixMulNV(ValidationState_t& _,
   const auto input_index = 2u;
   const auto input_interpretation_index = 3u;
   const auto matrix_index = 4u;
+  const auto matrix_offset_index = 5u;
   const auto matrix_interpretation_index = 6u;
   const auto bias_index = 7u;
+  const auto bias_offset_index = 8u;
   const auto bias_interpretation_index = 9u;
   const auto m_index = 7u + bias_offset;
   const auto k_index = 8u + bias_offset;
@@ -2930,15 +3075,33 @@ spv_result_t ValidateCooperativeVectorMatrixMulNV(ValidationState_t& _,
     return error;
   }
 
+  if (auto error = ValidateInt32Or64Operand(_, inst, matrix_offset_index,
+                                            opcode_name, "MatrixOffset")) {
+    return error;
+  }
+  if (has_bias) {
+    if (auto error = ValidateInt32Or64Operand(_, inst, bias_offset_index,
+                                              opcode_name, "BiasOffset")) {
+      return error;
+    }
+  }
+
   return SPV_SUCCESS;
 }
 
 spv_result_t ValidatePtrComparison(ValidationState_t& _,
                                    const Instruction* inst) {
-  if (_.addressing_model() == spv::AddressingModel::Logical &&
+  const auto op1 = _.FindDef(inst->GetOperandAs<uint32_t>(2u));
+  const auto op2 = _.FindDef(inst->GetOperandAs<uint32_t>(3u));
+  const auto op1_type = _.FindDef(op1->type_id());
+  const auto op2_type = _.FindDef(op2->type_id());
+  spv::StorageClass sc = op1_type->GetOperandAs<spv::StorageClass>(1u);
+  if ((_.addressing_model() == spv::AddressingModel::Logical ||
+       _.addressing_model() == spv::AddressingModel::PhysicalStorageBuffer64) &&
+      sc != spv::StorageClass::PhysicalStorageBuffer &&
       !_.features().variable_pointers) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
-           << "Instruction cannot for logical addressing model be used without "
+           << "Instruction on logical pointers cannot be used without "
               "a variable pointers capability";
   }
 
@@ -2955,10 +3118,6 @@ spv_result_t ValidatePtrComparison(ValidationState_t& _,
     }
   }
 
-  const auto op1 = _.FindDef(inst->GetOperandAs<uint32_t>(2u));
-  const auto op2 = _.FindDef(inst->GetOperandAs<uint32_t>(3u));
-  const auto op1_type = _.FindDef(op1->type_id());
-  const auto op2_type = _.FindDef(op2->type_id());
   if (!op1_type || (op1_type->opcode() != spv::Op::OpTypePointer &&
                     op1_type->opcode() != spv::Op::OpTypeUntypedPointerKHR)) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
@@ -2993,7 +3152,6 @@ spv_result_t ValidatePtrComparison(ValidationState_t& _,
     }
   }
 
-  spv::StorageClass sc = op1_type->GetOperandAs<spv::StorageClass>(1u);
   if (_.addressing_model() == spv::AddressingModel::Logical) {
     if (sc != spv::StorageClass::Workgroup &&
         sc != spv::StorageClass::StorageBuffer) {

+ 1 - 3
3rdparty/spirv-tools/source/val/validate_mode_setting.cpp

@@ -315,9 +315,7 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) {
           }
           if (!ok) {
             return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << (_.HasCapability(spv::Capability::TileShadingQCOM)
-                           ? _.VkErrorID(10685)
-                           : _.VkErrorID(6426))
+                   << _.VkErrorID(10685)
                    << "In the Vulkan environment, GLCompute execution model "
                       "entry points require either the "
                    << (_.HasCapability(spv::Capability::TileShadingQCOM)

+ 688 - 10
3rdparty/spirv-tools/source/val/validate_ray_tracing_reorder.cpp

@@ -12,7 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Validates ray tracing instructions from SPV_NV_shader_execution_reorder
+// Validates ray tracing instructions from SPV_NV_shader_invocation_reorder and
+// SPV_EXT_shader_invocation_reorder
 
 #include "source/opcode.h"
 #include "source/val/instruction.h"
@@ -37,18 +38,29 @@ uint32_t GetArrayLength(ValidationState_t& _, const Instruction* array_type) {
   return array_length;
 }
 
+spv_result_t ValidateRayQueryPointer(ValidationState_t& _,
+                                     const Instruction* inst,
+                                     uint32_t ray_query_index) {
+  const uint32_t ray_query_id = inst->GetOperandAs<uint32_t>(ray_query_index);
+  auto variable = _.FindDef(ray_query_id);
+  auto pointer = _.FindDef(variable->GetOperandAs<uint32_t>(0));
+  if (!pointer || pointer->opcode() != spv::Op::OpTypePointer) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Ray Query must be a pointer";
+  }
+  auto type = _.FindDef(pointer->GetOperandAs<uint32_t>(2));
+  if (!type || type->opcode() != spv::Op::OpTypeRayQueryKHR) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Ray Query must be a pointer to OpTypeRayQueryKHR";
+  }
+  return SPV_SUCCESS;
+}
+
 spv_result_t ValidateHitObjectPointer(ValidationState_t& _,
                                       const Instruction* inst,
                                       uint32_t hit_object_index) {
   const uint32_t hit_object_id = inst->GetOperandAs<uint32_t>(hit_object_index);
   auto variable = _.FindDef(hit_object_id);
-  const auto var_opcode = variable->opcode();
-  if (!variable || (var_opcode != spv::Op::OpVariable &&
-                    var_opcode != spv::Op::OpFunctionParameter &&
-                    var_opcode != spv::Op::OpAccessChain)) {
-    return _.diag(SPV_ERROR_INVALID_DATA, inst)
-           << "Hit Object must be a memory object declaration";
-  }
   auto pointer = _.FindDef(variable->GetOperandAs<uint32_t>(0));
   if (!pointer || pointer->opcode() != spv::Op::OpTypePointer) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -62,6 +74,24 @@ spv_result_t ValidateHitObjectPointer(ValidationState_t& _,
   return SPV_SUCCESS;
 }
 
+spv_result_t ValidateHitObjectPointerEXT(ValidationState_t& _,
+                                         const Instruction* inst,
+                                         uint32_t hit_object_index) {
+  const uint32_t hit_object_id = inst->GetOperandAs<uint32_t>(hit_object_index);
+  auto variable = _.FindDef(hit_object_id);
+  auto pointer = _.FindDef(variable->GetOperandAs<uint32_t>(0));
+  if (!pointer || pointer->opcode() != spv::Op::OpTypePointer) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Hit Object must be a pointer";
+  }
+  auto type = _.FindDef(pointer->GetOperandAs<uint32_t>(2));
+  if (!type || type->opcode() != spv::Op::OpTypeHitObjectEXT) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << "Type must be OpTypeHitObjectEXT";
+  }
+  return SPV_SUCCESS;
+}
+
 spv_result_t ValidateHitObjectInstructionCommonParameters(
     ValidationState_t& _, const Instruction* inst,
     uint32_t acceleration_struct_index, uint32_t instance_id_index,
@@ -247,8 +277,10 @@ spv_result_t ValidateHitObjectInstructionCommonParameters(
     auto variable = _.FindDef(hit_object_attr_id);
     const auto var_opcode = variable->opcode();
     if (!variable || var_opcode != spv::Op::OpVariable ||
-        (variable->GetOperandAs<spv::StorageClass>(2)) !=
-            spv::StorageClass::HitObjectAttributeNV) {
+        !((variable->GetOperandAs<spv::StorageClass>(2) ==
+           spv::StorageClass::HitObjectAttributeNV) ||
+          (variable->GetOperandAs<spv::StorageClass>(2) ==
+           spv::StorageClass::HitObjectAttributeEXT))) {
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Hit Object Attributes id must be a OpVariable of storage "
                 "class HitObjectAttributeNV";
@@ -728,5 +760,651 @@ spv_result_t RayReorderNVPass(ValidationState_t& _, const Instruction* inst) {
   }
   return SPV_SUCCESS;
 }
+
+spv_result_t RayReorderEXTPass(ValidationState_t& _, const Instruction* inst) {
+  const spv::Op opcode = inst->opcode();
+  const uint32_t result_type = inst->type_id();
+
+  auto RegisterOpcodeForValidModel = [](ValidationState_t& vs,
+                                        const Instruction* rtinst) {
+    std::string opcode_name = spvOpcodeString(rtinst->opcode());
+    vs.function(rtinst->function()->id())
+        ->RegisterExecutionModelLimitation(
+            [opcode_name](spv::ExecutionModel model, std::string* message) {
+              if (model != spv::ExecutionModel::RayGenerationKHR &&
+                  model != spv::ExecutionModel::ClosestHitKHR &&
+                  model != spv::ExecutionModel::MissKHR) {
+                if (message) {
+                  *message = opcode_name +
+                             " requires RayGenerationKHR, ClosestHitKHR and "
+                             "MissKHR execution models";
+                }
+                return false;
+              }
+              return true;
+            });
+    return;
+  };
+
+  switch (opcode) {
+    case spv::Op::OpHitObjectIsMissEXT:
+    case spv::Op::OpHitObjectIsHitEXT:
+    case spv::Op::OpHitObjectIsEmptyEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (!_.IsBoolScalarType(result_type)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "expected Result Type to be bool scalar type";
+      }
+
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 2)) return error;
+      break;
+    }
+
+    case spv::Op::OpHitObjectGetShaderRecordBufferHandleEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 2)) return error;
+
+      if (!_.IsIntVectorType(result_type) ||
+          (_.GetDimension(result_type) != 2) ||
+          (_.GetBitWidth(result_type) != 32))
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected 32-bit integer type 2-component vector as Result "
+                  "Type: "
+               << spvOpcodeString(opcode);
+      break;
+    }
+
+    case spv::Op::OpHitObjectGetHitKindEXT:
+    case spv::Op::OpHitObjectGetPrimitiveIndexEXT:
+    case spv::Op::OpHitObjectGetGeometryIndexEXT:
+    case spv::Op::OpHitObjectGetInstanceIdEXT:
+    case spv::Op::OpHitObjectGetInstanceCustomIndexEXT:
+    case spv::Op::OpHitObjectGetShaderBindingTableRecordIndexEXT:
+    case spv::Op::OpHitObjectGetRayFlagsEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 2)) return error;
+
+      if (!_.IsIntScalarType(result_type) || _.GetBitWidth(result_type) != 32)
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected 32-bit integer type scalar as Result Type: "
+               << spvOpcodeString(opcode);
+      break;
+    }
+
+    case spv::Op::OpHitObjectGetCurrentTimeEXT:
+    case spv::Op::OpHitObjectGetRayTMaxEXT:
+    case spv::Op::OpHitObjectGetRayTMinEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 2)) return error;
+
+      if (!_.IsFloatScalarType(result_type) || _.GetBitWidth(result_type) != 32)
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected 32-bit floating-point type scalar as Result Type: "
+               << spvOpcodeString(opcode);
+      break;
+    }
+
+    case spv::Op::OpHitObjectGetObjectToWorldEXT:
+    case spv::Op::OpHitObjectGetWorldToObjectEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 2)) return error;
+
+      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)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "expected matrix type as Result Type: "
+               << spvOpcodeString(opcode);
+      }
+
+      if (num_cols != 4) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "expected Result Type matrix to have a Column Count of 4"
+               << spvOpcodeString(opcode);
+      }
+
+      if (!_.IsFloatScalarType(component_type) ||
+          _.GetBitWidth(result_type) != 32 || num_rows != 3) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "expected Result Type matrix to have a Column Type of "
+                  "3-component 32-bit float vectors: "
+               << spvOpcodeString(opcode);
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectGetObjectRayOriginEXT:
+    case spv::Op::OpHitObjectGetObjectRayDirectionEXT:
+    case spv::Op::OpHitObjectGetWorldRayDirectionEXT:
+    case spv::Op::OpHitObjectGetWorldRayOriginEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 2)) return error;
+
+      if (!_.IsFloatVectorType(result_type) ||
+          (_.GetDimension(result_type) != 3) ||
+          (_.GetBitWidth(result_type) != 32))
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected 32-bit floating-point type 3-component vector as "
+                  "Result Type: "
+               << spvOpcodeString(opcode);
+      break;
+    }
+
+    case spv::Op::OpHitObjectGetIntersectionTriangleVertexPositionsEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 2)) return error;
+
+      auto result_id = _.FindDef(result_type);
+      if ((result_id->opcode() != spv::Op::OpTypeArray) ||
+          (GetArrayLength(_, result_id) != 3) ||
+          !_.IsFloatVectorType(_.GetComponentType(result_type)) ||
+          _.GetDimension(_.GetComponentType(result_type)) != 3 ||
+          _.GetBitWidth(_.GetComponentType(result_type)) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Expected 3 element array of 32-bit 3 component float "
+                  "vectors as Result Type: "
+               << spvOpcodeString(opcode);
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectGetAttributesEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+
+      const uint32_t hit_object_attr_id = inst->GetOperandAs<uint32_t>(1);
+      auto variable = _.FindDef(hit_object_attr_id);
+      const auto var_opcode = variable->opcode();
+      if (!variable || var_opcode != spv::Op::OpVariable ||
+          variable->GetOperandAs<spv::StorageClass>(2) !=
+              spv::StorageClass::HitObjectAttributeEXT) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Hit Object Attributes id must be a OpVariable of storage "
+                  "class HitObjectAttributeEXT";
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectSetShaderBindingTableRecordIndexEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+
+      const uint32_t sbt_index_id = _.GetOperandTypeId(inst, 1);
+      if (!_.IsIntScalarType(sbt_index_id) ||
+          _.GetBitWidth(sbt_index_id) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "SBT Index must be a 32-bit integer scalar";
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectExecuteShaderEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+
+      const uint32_t payload_id = inst->GetOperandAs<uint32_t>(1);
+      auto variable = _.FindDef(payload_id);
+      const auto var_opcode = variable->opcode();
+      if (!variable || var_opcode != spv::Op::OpVariable ||
+          (variable->GetOperandAs<spv::StorageClass>(2) !=
+               spv::StorageClass::RayPayloadKHR &&
+           variable->GetOperandAs<spv::StorageClass>(2) !=
+               spv::StorageClass::IncomingRayPayloadKHR)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Payload must be a OpVariable of storage "
+                  "class RayPayloadKHR or IncomingRayPayloadKHR";
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectRecordEmptyEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+      break;
+    }
+
+    case spv::Op::OpHitObjectRecordFromQueryEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+      if (auto error = ValidateRayQueryPointer(_, inst, 1)) return error;
+
+      if (!_.HasCapability(spv::Capability::RayQueryKHR))
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << spvOpcodeString(opcode)
+               << ": requires RayQueryKHR capability";
+
+      // Validate SBT Record Index (operand 2)
+      const uint32_t sbt_record_index_id = _.GetOperandTypeId(inst, 2);
+      if (!_.IsIntScalarType(sbt_record_index_id) ||
+          _.GetBitWidth(sbt_record_index_id) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "SBT Record Index must be a 32-bit integer scalar";
+      }
+
+      // Validate Hit Object Attributes (operand 3)
+      const uint32_t hit_object_attr_id = inst->GetOperandAs<uint32_t>(3);
+      auto attr_variable = _.FindDef(hit_object_attr_id);
+      const auto attr_var_opcode = attr_variable->opcode();
+      if (!attr_variable || attr_var_opcode != spv::Op::OpVariable ||
+          attr_variable->GetOperandAs<spv::StorageClass>(2) !=
+              spv::StorageClass::HitObjectAttributeEXT) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Hit Object Attributes id must be a OpVariable of storage "
+                  "class HitObjectAttributeEXT";
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectRecordMissEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+
+      // Ray Flags (operand 1)
+      const uint32_t ray_flags_id = _.GetOperandTypeId(inst, 1);
+      if (!_.IsIntScalarType(ray_flags_id) ||
+          _.GetBitWidth(ray_flags_id) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ray Flags must be a 32-bit int scalar";
+      }
+
+      // Miss Index (operand 2)
+      const uint32_t miss_index = _.GetOperandTypeId(inst, 2);
+      if (!_.IsUnsignedIntScalarType(miss_index) ||
+          _.GetBitWidth(miss_index) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Miss Index must be a 32-bit unsigned int scalar";
+      }
+
+      // Ray Origin (operand 3)
+      const uint32_t ray_origin = _.GetOperandTypeId(inst, 3);
+      if (!_.IsFloatVectorType(ray_origin) || _.GetDimension(ray_origin) != 3 ||
+          _.GetBitWidth(ray_origin) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ray Origin must be a 32-bit float 3-component vector";
+      }
+
+      // Ray TMin (operand 4)
+      const uint32_t ray_tmin = _.GetOperandTypeId(inst, 4);
+      if (!_.IsFloatScalarType(ray_tmin) || _.GetBitWidth(ray_tmin) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ray TMin must be a 32-bit float scalar";
+      }
+
+      // Ray Direction (operand 5)
+      const uint32_t ray_direction = _.GetOperandTypeId(inst, 5);
+      if (!_.IsFloatVectorType(ray_direction) ||
+          _.GetDimension(ray_direction) != 3 ||
+          _.GetBitWidth(ray_direction) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ray Direction must be a 32-bit float 3-component vector";
+      }
+
+      // Ray TMax (operand 6)
+      const uint32_t ray_tmax = _.GetOperandTypeId(inst, 6);
+      if (!_.IsFloatScalarType(ray_tmax) || _.GetBitWidth(ray_tmax) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ray TMax must be a 32-bit float scalar";
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectRecordMissMotionEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+
+      // Ray Flags (operand 1)
+      const uint32_t ray_flags_id = _.GetOperandTypeId(inst, 1);
+      if (!_.IsIntScalarType(ray_flags_id) ||
+          _.GetBitWidth(ray_flags_id) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ray Flags must be a 32-bit int scalar";
+      }
+
+      // Miss Index (operand 2)
+      const uint32_t miss_index = _.GetOperandTypeId(inst, 2);
+      if (!_.IsUnsignedIntScalarType(miss_index) ||
+          _.GetBitWidth(miss_index) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Miss Index must be a 32-bit unsigned int scalar";
+      }
+
+      // Ray Origin (operand 3)
+      const uint32_t ray_origin = _.GetOperandTypeId(inst, 3);
+      if (!_.IsFloatVectorType(ray_origin) || _.GetDimension(ray_origin) != 3 ||
+          _.GetBitWidth(ray_origin) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ray Origin must be a 32-bit float 3-component vector";
+      }
+
+      // Ray TMin (operand 4)
+      const uint32_t ray_tmin = _.GetOperandTypeId(inst, 4);
+      if (!_.IsFloatScalarType(ray_tmin) || _.GetBitWidth(ray_tmin) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ray TMin must be a 32-bit float scalar";
+      }
+
+      // Ray Direction (operand 5)
+      const uint32_t ray_direction = _.GetOperandTypeId(inst, 5);
+      if (!_.IsFloatVectorType(ray_direction) ||
+          _.GetDimension(ray_direction) != 3 ||
+          _.GetBitWidth(ray_direction) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ray Direction must be a 32-bit float 3-component vector";
+      }
+
+      // Ray TMax (operand 6)
+      const uint32_t ray_tmax = _.GetOperandTypeId(inst, 6);
+      if (!_.IsFloatScalarType(ray_tmax) || _.GetBitWidth(ray_tmax) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Ray TMax must be a 32-bit float scalar";
+      }
+
+      // Current Time (operand 7)
+      const uint32_t current_time_id = _.GetOperandTypeId(inst, 7);
+      if (!_.IsFloatScalarType(current_time_id) ||
+          _.GetBitWidth(current_time_id) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Current Time must be a 32-bit float scalar";
+      }
+      break;
+    }
+
+    case spv::Op::OpReorderThreadWithHintEXT: {
+      std::string opcode_name = spvOpcodeString(inst->opcode());
+      _.function(inst->function()->id())
+          ->RegisterExecutionModelLimitation(
+              [opcode_name](spv::ExecutionModel model, std::string* message) {
+                if (model != spv::ExecutionModel::RayGenerationKHR) {
+                  if (message) {
+                    *message = opcode_name +
+                               " requires RayGenerationKHR execution model";
+                  }
+                  return false;
+                }
+                return true;
+              });
+
+      const uint32_t hint_id = _.GetOperandTypeId(inst, 0);
+      if (!_.IsIntScalarType(hint_id) || _.GetBitWidth(hint_id) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Hint must be a 32-bit int scalar";
+      }
+
+      const uint32_t bits_id = _.GetOperandTypeId(inst, 1);
+      if (!_.IsIntScalarType(bits_id) || _.GetBitWidth(bits_id) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Bits must be a 32-bit int scalar";
+      }
+      break;
+    }
+
+    case spv::Op::OpReorderThreadWithHitObjectEXT: {
+      std::string opcode_name = spvOpcodeString(inst->opcode());
+      _.function(inst->function()->id())
+          ->RegisterExecutionModelLimitation(
+              [opcode_name](spv::ExecutionModel model, std::string* message) {
+                if (model != spv::ExecutionModel::RayGenerationKHR) {
+                  if (message) {
+                    *message = opcode_name +
+                               " requires RayGenerationKHR execution model";
+                  }
+                  return false;
+                }
+                return true;
+              });
+
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+
+      if (inst->operands().size() > 1) {
+        if (inst->operands().size() != 3) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Hint and Bits are optional together i.e "
+                 << " Either both Hint and Bits should be provided or neither.";
+        }
+
+        // Validate the optional operands Hint and Bits
+        const uint32_t hint_id = _.GetOperandTypeId(inst, 1);
+        if (!_.IsIntScalarType(hint_id) || _.GetBitWidth(hint_id) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Hint must be a 32-bit int scalar";
+        }
+        const uint32_t bits_id = _.GetOperandTypeId(inst, 2);
+        if (!_.IsIntScalarType(bits_id) || _.GetBitWidth(bits_id) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Bits must be a 32-bit int scalar";
+        }
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectTraceRayEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+
+      if (auto error = ValidateHitObjectInstructionCommonParameters(
+              _, inst, 1 /* Acceleration Struct */,
+              KRayParamInvalidId /* Instance Id */,
+              KRayParamInvalidId /* Primitive Id */,
+              KRayParamInvalidId /* Geometry Index */, 2 /* Ray Flags */,
+              3 /* Cull Mask */, KRayParamInvalidId /* Hit Kind*/,
+              KRayParamInvalidId /* SBT index */, 4 /* SBT Offset */,
+              5 /* SBT Stride */, KRayParamInvalidId /* SBT Record Offset */,
+              KRayParamInvalidId /* SBT Record Stride */, 6 /* Miss Index */,
+              7 /* Ray Origin */, 8 /* Ray TMin */, 9 /* Ray Direction */,
+              10 /* Ray TMax */, 11 /* Payload */,
+              KRayParamInvalidId /* Hit Object Attribute */))
+        return error;
+      break;
+    }
+
+    case spv::Op::OpHitObjectTraceRayMotionEXT: {
+      RegisterOpcodeForValidModel(_, inst);
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+
+      if (auto error = ValidateHitObjectInstructionCommonParameters(
+              _, inst, 1 /* Acceleration Struct */,
+              KRayParamInvalidId /* Instance Id */,
+              KRayParamInvalidId /* Primitive Id */,
+              KRayParamInvalidId /* Geometry Index */, 2 /* Ray Flags */,
+              3 /* Cull Mask */, KRayParamInvalidId /* Hit Kind*/,
+              KRayParamInvalidId /* SBT index */, 4 /* SBT Offset */,
+              5 /* SBT Stride */, KRayParamInvalidId /* SBT Record Offset */,
+              KRayParamInvalidId /* SBT Record Stride */, 6 /* Miss Index */,
+              7 /* Ray Origin */, 8 /* Ray TMin */, 9 /* Ray Direction */,
+              10 /* Ray TMax */, 12 /* Payload */,
+              KRayParamInvalidId /* Hit Object Attribute */))
+        return error;
+
+      // Current Time (operand 11)
+      const uint32_t current_time_id = _.GetOperandTypeId(inst, 11);
+      if (!_.IsFloatScalarType(current_time_id) ||
+          _.GetBitWidth(current_time_id) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Current Time must be a 32-bit float scalar";
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectReorderExecuteShaderEXT: {
+      std::string opcode_name = spvOpcodeString(inst->opcode());
+      _.function(inst->function()->id())
+          ->RegisterExecutionModelLimitation(
+              [opcode_name](spv::ExecutionModel model, std::string* message) {
+                if (model != spv::ExecutionModel::RayGenerationKHR) {
+                  if (message) {
+                    *message = opcode_name +
+                               " requires RayGenerationKHR execution model";
+                  }
+                  return false;
+                }
+                return true;
+              });
+
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+
+      // Validate Payload (operand 1)
+      const uint32_t payload_id = inst->GetOperandAs<uint32_t>(1);
+      auto variable = _.FindDef(payload_id);
+      const auto var_opcode = variable->opcode();
+      if (!variable || var_opcode != spv::Op::OpVariable ||
+          (variable->GetOperandAs<spv::StorageClass>(2) !=
+               spv::StorageClass::RayPayloadKHR &&
+           variable->GetOperandAs<spv::StorageClass>(2) !=
+               spv::StorageClass::IncomingRayPayloadKHR)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Payload must be a OpVariable of storage "
+                  "class RayPayloadKHR or IncomingRayPayloadKHR";
+      }
+
+      // Check for optional Hint and Bits (operands 2 and 3)
+      if (inst->operands().size() > 2) {
+        if (inst->operands().size() != 4) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Hint and Bits are optional together i.e "
+                 << " Either both Hint and Bits should be provided or neither.";
+        }
+
+        // Validate optional Hint and Bits
+        const uint32_t hint_id = _.GetOperandTypeId(inst, 2);
+        if (!_.IsIntScalarType(hint_id) || _.GetBitWidth(hint_id) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Hint must be a 32-bit int scalar";
+        }
+        const uint32_t bits_id = _.GetOperandTypeId(inst, 3);
+        if (!_.IsIntScalarType(bits_id) || _.GetBitWidth(bits_id) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Bits must be a 32-bit int scalar";
+        }
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectTraceReorderExecuteEXT: {
+      std::string opcode_name = spvOpcodeString(inst->opcode());
+      _.function(inst->function()->id())
+          ->RegisterExecutionModelLimitation(
+              [opcode_name](spv::ExecutionModel model, std::string* message) {
+                if (model != spv::ExecutionModel::RayGenerationKHR) {
+                  if (message) {
+                    *message = opcode_name +
+                               " requires RayGenerationKHR execution model";
+                  }
+                  return false;
+                }
+                return true;
+              });
+
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+
+      // Validate base trace ray parameters (operands 1-11)
+      if (auto error = ValidateHitObjectInstructionCommonParameters(
+              _, inst, 1 /* Acceleration Struct */,
+              KRayParamInvalidId /* Instance Id */,
+              KRayParamInvalidId /* Primitive Id */,
+              KRayParamInvalidId /* Geometry Index */, 2 /* Ray Flags */,
+              3 /* Cull Mask */, KRayParamInvalidId /* Hit Kind*/,
+              KRayParamInvalidId /* SBT index */, 4 /* SBT Offset */,
+              5 /* SBT Stride */, KRayParamInvalidId /* SBT Record Offset */,
+              KRayParamInvalidId /* SBT Record Stride */, 6 /* Miss Index */,
+              7 /* Ray Origin */, 8 /* Ray TMin */, 9 /* Ray Direction */,
+              10 /* Ray TMax */, 11 /* Payload */,
+              KRayParamInvalidId /* Hit Object Attribute */))
+        return error;
+
+      // Check for optional Hint and Bits (operands 12 and 13)
+      if (inst->operands().size() > 12) {
+        if (inst->operands().size() != 14) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Hint and Bits are optional together i.e "
+                 << " Either both Hint and Bits should be provided or neither.";
+        }
+
+        // Validate optional Hint and Bits
+        const uint32_t hint_id = _.GetOperandTypeId(inst, 12);
+        if (!_.IsIntScalarType(hint_id) || _.GetBitWidth(hint_id) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Hint must be a 32-bit int scalar";
+        }
+        const uint32_t bits_id = _.GetOperandTypeId(inst, 13);
+        if (!_.IsIntScalarType(bits_id) || _.GetBitWidth(bits_id) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Bits must be a 32-bit int scalar";
+        }
+      }
+      break;
+    }
+
+    case spv::Op::OpHitObjectTraceMotionReorderExecuteEXT: {
+      std::string opcode_name = spvOpcodeString(inst->opcode());
+      _.function(inst->function()->id())
+          ->RegisterExecutionModelLimitation(
+              [opcode_name](spv::ExecutionModel model, std::string* message) {
+                if (model != spv::ExecutionModel::RayGenerationKHR) {
+                  if (message) {
+                    *message = opcode_name +
+                               " requires RayGenerationKHR execution model";
+                  }
+                  return false;
+                }
+                return true;
+              });
+
+      if (auto error = ValidateHitObjectPointerEXT(_, inst, 0)) return error;
+
+      // Validate base trace ray parameters (operands 1-12)
+      if (auto error = ValidateHitObjectInstructionCommonParameters(
+              _, inst, 1 /* Acceleration Struct */,
+              KRayParamInvalidId /* Instance Id */,
+              KRayParamInvalidId /* Primitive Id */,
+              KRayParamInvalidId /* Geometry Index */, 2 /* Ray Flags */,
+              3 /* Cull Mask */, KRayParamInvalidId /* Hit Kind*/,
+              KRayParamInvalidId /* SBT index */, 4 /* SBT Offset */,
+              5 /* SBT Stride */, KRayParamInvalidId /* SBT Record Offset */,
+              KRayParamInvalidId /* SBT Record Stride */, 6 /* Miss Index */,
+              7 /* Ray Origin */, 8 /* Ray TMin */, 9 /* Ray Direction */,
+              10 /* Ray TMax */, 12 /* Payload */,
+              KRayParamInvalidId /* Hit Object Attribute */))
+        return error;
+
+      // Current Time (operand 11)
+      const uint32_t current_time_id = _.GetOperandTypeId(inst, 11);
+      if (!_.IsFloatScalarType(current_time_id) ||
+          _.GetBitWidth(current_time_id) != 32) {
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
+               << "Current Time must be a 32-bit float scalar";
+      }
+
+      // Check for optional Hint and Bits (operands 13 and 14)
+      if (inst->operands().size() > 13) {
+        if (inst->operands().size() != 15) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Hint and Bits are optional together i.e "
+                 << " Either both Hint and Bits should be provided or neither.";
+        }
+
+        // Validate optional Hint and Bits
+        const uint32_t hint_id = _.GetOperandTypeId(inst, 13);
+        if (!_.IsIntScalarType(hint_id) || _.GetBitWidth(hint_id) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Hint must be a 32-bit int scalar";
+        }
+        const uint32_t bits_id = _.GetOperandTypeId(inst, 14);
+        if (!_.IsIntScalarType(bits_id) || _.GetBitWidth(bits_id) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Bits must be a 32-bit int scalar";
+        }
+      }
+      break;
+    }
+
+    default:
+      break;
+  }
+  return SPV_SUCCESS;
+}
 }  // namespace val
 }  // namespace spvtools

+ 26 - 3
3rdparty/spirv-tools/source/val/validation_state.cpp

@@ -859,6 +859,22 @@ void ValidationState_t::RegisterStorageClassConsumer(
           }
           return true;
         });
+  } else if (storage_class == spv::StorageClass::HitObjectAttributeEXT) {
+    function(consumer->function()->id())
+        ->RegisterExecutionModelLimitation([](spv::ExecutionModel model,
+                                              std::string* message) {
+          if (model != spv::ExecutionModel::RayGenerationKHR &&
+              model != spv::ExecutionModel::ClosestHitKHR &&
+              model != spv::ExecutionModel::MissKHR) {
+            if (message) {
+              *message =
+                  "HitObjectAttributeEXT Storage Class is limited to "
+                  "RayGenerationKHR, ClosestHitKHR or MissKHR execution model";
+            }
+            return false;
+          }
+          return true;
+        });
   }
 }
 
@@ -2032,6 +2048,7 @@ bool ValidationState_t::IsValidStorageClass(
       case spv::StorageClass::ShaderRecordBufferKHR:
       case spv::StorageClass::TaskPayloadWorkgroupEXT:
       case spv::StorageClass::HitObjectAttributeNV:
+      case spv::StorageClass::HitObjectAttributeEXT:
       case spv::StorageClass::TileImageEXT:
       case spv::StorageClass::NodePayloadAMDX:
       case spv::StorageClass::TileAttachmentQCOM:
@@ -2254,6 +2271,12 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
       return VUID_WRAP(VUID-LocalInvocationId-LocalInvocationId-04282);
     case 4283:
       return VUID_WRAP(VUID-LocalInvocationId-LocalInvocationId-04283);
+    case 4284:
+      return VUID_WRAP(VUID-LocalInvocationIndex-LocalInvocationIndex-04284);
+    case 4285:
+      return VUID_WRAP(VUID-LocalInvocationIndex-LocalInvocationIndex-04285);
+    case 4286:
+      return VUID_WRAP(VUID-LocalInvocationIndex-LocalInvocationIndex-04286);
     case 4293:
       return VUID_WRAP(VUID-NumSubgroups-NumSubgroups-04293);
     case 4294:
@@ -2538,8 +2561,6 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
       return VUID_WRAP(VUID-StandaloneSpirv-OpTypeRuntimeArray-04680);
     case 4682:
       return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04682);
-    case 6426:
-      return VUID_WRAP(VUID-StandaloneSpirv-LocalSize-06426); // formally 04683
     case 4685:
       return VUID_WRAP(VUID-StandaloneSpirv-OpGroupNonUniformBallotBitCount-04685);
     case 4686:
@@ -2752,7 +2773,7 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
     case 10684:
       return VUID_WRAP(VUID-StandaloneSpirv-None-10684);
     case 10685:
-      return VUID_WRAP(VUID-StandaloneSpirv-None-10685);
+      return VUID_WRAP(VUID-StandaloneSpirv-None-10685); // formally 04683/06426
     case 10824:
       // This use to be a standalone, but maintenance9 will set allow_vulkan_32_bit_bitwise now
       return VUID_WRAP(VUID-RuntimeSpirv-None-10824);
@@ -2790,6 +2811,8 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
       return VUID_WRAP(VUID-StandaloneSpirv-TessLevelInner-10880);
     case 11167:
       return VUID_WRAP(VUID-StandaloneSpirv-OpUntypedVariableKHR-11167);
+    case 11805:
+      return VUID_WRAP(VUID-StandaloneSpirv-OpArrayLength-11805);
     default:
       return "";  // unknown id
   }

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.