Kaynağa Gözat

Updated spirv-tools.

Бранимир Караџић 3 yıl önce
ebeveyn
işleme
6c09cb564a
45 değiştirilmiş dosya ile 1479 ekleme ve 267 silme
  1. 1 1
      3rdparty/spirv-tools/include/generated/build-version.inc
  2. 2 0
      3rdparty/spirv-tools/include/generated/enum_string_mapping.inc
  3. 1 0
      3rdparty/spirv-tools/include/generated/extension_enum.inc
  4. 9 1
      3rdparty/spirv-tools/include/generated/nonsemantic.clspvreflection.insts.inc
  5. 9 0
      3rdparty/spirv-tools/include/generated/operand.kinds-unified1.inc
  6. 11 5
      3rdparty/spirv-tools/source/cfa.h
  7. 4 3
      3rdparty/spirv-tools/source/disassemble.h
  8. 3 1
      3rdparty/spirv-tools/source/libspirv.cpp
  9. 12 5
      3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp
  10. 20 2
      3rdparty/spirv-tools/source/opt/cfg.cpp
  11. 8 0
      3rdparty/spirv-tools/source/opt/cfg.h
  12. 189 0
      3rdparty/spirv-tools/source/opt/const_folding_rules.cpp
  13. 3 1
      3rdparty/spirv-tools/source/opt/dominator_tree.cpp
  14. 289 6
      3rdparty/spirv-tools/source/opt/folding_rules.cpp
  15. 5 0
      3rdparty/spirv-tools/source/opt/if_conversion.cpp
  16. 42 1
      3rdparty/spirv-tools/source/opt/inline_pass.cpp
  17. 6 0
      3rdparty/spirv-tools/source/opt/inline_pass.h
  18. 6 2
      3rdparty/spirv-tools/source/opt/instruction.cpp
  19. 5 1
      3rdparty/spirv-tools/source/opt/interface_var_sroa.cpp
  20. 46 1
      3rdparty/spirv-tools/source/opt/local_access_chain_convert_pass.cpp
  21. 11 0
      3rdparty/spirv-tools/source/opt/local_access_chain_convert_pass.h
  22. 1 0
      3rdparty/spirv-tools/source/opt/local_single_block_elim_pass.cpp
  23. 1 0
      3rdparty/spirv-tools/source/opt/local_single_store_elim_pass.cpp
  24. 2 1
      3rdparty/spirv-tools/source/opt/loop_descriptor.cpp
  25. 15 0
      3rdparty/spirv-tools/source/opt/loop_unroller.cpp
  26. 10 4
      3rdparty/spirv-tools/source/opt/optimizer.cpp
  27. 9 2
      3rdparty/spirv-tools/source/opt/reduce_load_size.cpp
  28. 19 4
      3rdparty/spirv-tools/source/opt/scalar_replacement_pass.h
  29. 30 0
      3rdparty/spirv-tools/source/opt/types.cpp
  30. 4 0
      3rdparty/spirv-tools/source/opt/types.h
  31. 64 19
      3rdparty/spirv-tools/source/val/basic_block.cpp
  32. 84 13
      3rdparty/spirv-tools/source/val/basic_block.h
  33. 36 50
      3rdparty/spirv-tools/source/val/construct.cpp
  34. 9 0
      3rdparty/spirv-tools/source/val/decoration.h
  35. 27 10
      3rdparty/spirv-tools/source/val/function.cpp
  36. 4 4
      3rdparty/spirv-tools/source/val/function.h
  37. 9 4
      3rdparty/spirv-tools/source/val/validate_annotation.cpp
  38. 91 4
      3rdparty/spirv-tools/source/val/validate_builtins.cpp
  39. 139 59
      3rdparty/spirv-tools/source/val/validate_cfg.cpp
  40. 107 28
      3rdparty/spirv-tools/source/val/validate_decorations.cpp
  41. 16 4
      3rdparty/spirv-tools/source/val/validate_interfaces.cpp
  42. 14 16
      3rdparty/spirv-tools/source/val/validate_memory.cpp
  43. 45 0
      3rdparty/spirv-tools/source/val/validate_mode_setting.cpp
  44. 27 1
      3rdparty/spirv-tools/source/val/validation_state.cpp
  45. 34 14
      3rdparty/spirv-tools/source/val/validation_state.h

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

@@ -1 +1 @@
-"v2022.3-dev", "SPIRV-Tools v2022.3-dev b8091498a3d8f2f7a46df2009d7c340eef1939b6"
+"v2022.3-dev", "SPIRV-Tools v2022.3-dev 05862b9695f45c8a8810d69c52ced4440446c1b7"

Dosya farkı çok büyük olduğundan ihmal edildi
+ 2 - 0
3rdparty/spirv-tools/include/generated/enum_string_mapping.inc


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

@@ -3,6 +3,7 @@ kSPV_AMD_gpu_shader_half_float,
 kSPV_AMD_gpu_shader_half_float_fetch,
 kSPV_AMD_gpu_shader_half_float_fetch,
 kSPV_AMD_gpu_shader_int16,
 kSPV_AMD_gpu_shader_int16,
 kSPV_AMD_shader_ballot,
 kSPV_AMD_shader_ballot,
+kSPV_AMD_shader_early_and_late_fragment_tests,
 kSPV_AMD_shader_explicit_vertex_parameter,
 kSPV_AMD_shader_explicit_vertex_parameter,
 kSPV_AMD_shader_fragment_mask,
 kSPV_AMD_shader_fragment_mask,
 kSPV_AMD_shader_image_load_store_lod,
 kSPV_AMD_shader_image_load_store_lod,

+ 9 - 1
3rdparty/spirv-tools/include/generated/nonsemantic.clspvreflection.insts.inc

@@ -25,5 +25,13 @@ static const spv_ext_inst_desc_t nonsemantic_clspvreflection_entries[] = {
   {"ConstantDataUniform", 22, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
   {"ConstantDataUniform", 22, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
   {"LiteralSampler", 23, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
   {"LiteralSampler", 23, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
   {"PropertyRequiredWorkgroupSize", 24, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
   {"PropertyRequiredWorkgroupSize", 24, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
-  {"SpecConstantSubgroupMaxSize", 25, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}}
+  {"SpecConstantSubgroupMaxSize", 25, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"ArgumentPointerPushConstant", 26, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
+  {"ArgumentPointerUniform", 27, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
+  {"ProgramScopeVariablesStorageBuffer", 28, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"ProgramScopeVariablePointerRelocation", 29, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"ImageArgumentInfoChannelOrderPushConstant", 30, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"ImageArgumentInfoChannelDataTypePushConstant", 31, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"ImageArgumentInfoChannelOrderUniform", 32, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"ImageArgumentInfoChannelDataTypeUniform", 33, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}}
 };
 };

+ 9 - 0
3rdparty/spirv-tools/include/generated/operand.kinds-unified1.inc

@@ -123,6 +123,8 @@ static const SpvCapability pygen_variable_caps_WorkgroupMemoryExplicitLayoutKHR[
 
 
 static const spvtools::Extension pygen_variable_exts_SPV_AMD_gpu_shader_half_float_fetch[] = {spvtools::Extension::kSPV_AMD_gpu_shader_half_float_fetch};
 static const spvtools::Extension pygen_variable_exts_SPV_AMD_gpu_shader_half_float_fetch[] = {spvtools::Extension::kSPV_AMD_gpu_shader_half_float_fetch};
 static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_ballot[] = {spvtools::Extension::kSPV_AMD_shader_ballot};
 static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_ballot[] = {spvtools::Extension::kSPV_AMD_shader_ballot};
+static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_early_and_late_fragment_tests[] = {spvtools::Extension::kSPV_AMD_shader_early_and_late_fragment_tests};
+static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_early_and_late_fragment_testsSPV_EXT_shader_stencil_export[] = {spvtools::Extension::kSPV_AMD_shader_early_and_late_fragment_tests, spvtools::Extension::kSPV_EXT_shader_stencil_export};
 static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_explicit_vertex_parameter[] = {spvtools::Extension::kSPV_AMD_shader_explicit_vertex_parameter};
 static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_explicit_vertex_parameter[] = {spvtools::Extension::kSPV_AMD_shader_explicit_vertex_parameter};
 static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_fragment_mask[] = {spvtools::Extension::kSPV_AMD_shader_fragment_mask};
 static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_fragment_mask[] = {spvtools::Extension::kSPV_AMD_shader_fragment_mask};
 static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_image_load_store_lod[] = {spvtools::Extension::kSPV_AMD_shader_image_load_store_lod};
 static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_image_load_store_lod[] = {spvtools::Extension::kSPV_AMD_shader_image_load_store_lod};
@@ -450,7 +452,14 @@ static const spv_operand_desc_t pygen_variable_ExecutionModeEntries[] = {
   {"SignedZeroInfNanPreserve", 4461, 1, pygen_variable_caps_SignedZeroInfNanPreserve, 1, pygen_variable_exts_SPV_KHR_float_controls, {SPV_OPERAND_TYPE_LITERAL_INTEGER}, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu},
   {"SignedZeroInfNanPreserve", 4461, 1, pygen_variable_caps_SignedZeroInfNanPreserve, 1, pygen_variable_exts_SPV_KHR_float_controls, {SPV_OPERAND_TYPE_LITERAL_INTEGER}, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu},
   {"RoundingModeRTE", 4462, 1, pygen_variable_caps_RoundingModeRTE, 1, pygen_variable_exts_SPV_KHR_float_controls, {SPV_OPERAND_TYPE_LITERAL_INTEGER}, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu},
   {"RoundingModeRTE", 4462, 1, pygen_variable_caps_RoundingModeRTE, 1, pygen_variable_exts_SPV_KHR_float_controls, {SPV_OPERAND_TYPE_LITERAL_INTEGER}, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu},
   {"RoundingModeRTZ", 4463, 1, pygen_variable_caps_RoundingModeRTZ, 1, pygen_variable_exts_SPV_KHR_float_controls, {SPV_OPERAND_TYPE_LITERAL_INTEGER}, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu},
   {"RoundingModeRTZ", 4463, 1, pygen_variable_caps_RoundingModeRTZ, 1, pygen_variable_exts_SPV_KHR_float_controls, {SPV_OPERAND_TYPE_LITERAL_INTEGER}, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu},
+  {"EarlyAndLateFragmentTestsAMD", 5017, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_AMD_shader_early_and_late_fragment_tests, {}, 0xffffffffu, 0xffffffffu},
   {"StencilRefReplacingEXT", 5027, 1, pygen_variable_caps_StencilExportEXT, 1, pygen_variable_exts_SPV_EXT_shader_stencil_export, {}, 0xffffffffu, 0xffffffffu},
   {"StencilRefReplacingEXT", 5027, 1, pygen_variable_caps_StencilExportEXT, 1, pygen_variable_exts_SPV_EXT_shader_stencil_export, {}, 0xffffffffu, 0xffffffffu},
+  {"StencilRefUnchangedFrontAMD", 5079, 1, pygen_variable_caps_StencilExportEXT, 2, pygen_variable_exts_SPV_AMD_shader_early_and_late_fragment_testsSPV_EXT_shader_stencil_export, {}, 0xffffffffu, 0xffffffffu},
+  {"StencilRefGreaterFrontAMD", 5080, 1, pygen_variable_caps_StencilExportEXT, 2, pygen_variable_exts_SPV_AMD_shader_early_and_late_fragment_testsSPV_EXT_shader_stencil_export, {}, 0xffffffffu, 0xffffffffu},
+  {"StencilRefLessFrontAMD", 5081, 1, pygen_variable_caps_StencilExportEXT, 2, pygen_variable_exts_SPV_AMD_shader_early_and_late_fragment_testsSPV_EXT_shader_stencil_export, {}, 0xffffffffu, 0xffffffffu},
+  {"StencilRefUnchangedBackAMD", 5082, 1, pygen_variable_caps_StencilExportEXT, 2, pygen_variable_exts_SPV_AMD_shader_early_and_late_fragment_testsSPV_EXT_shader_stencil_export, {}, 0xffffffffu, 0xffffffffu},
+  {"StencilRefGreaterBackAMD", 5083, 1, pygen_variable_caps_StencilExportEXT, 2, pygen_variable_exts_SPV_AMD_shader_early_and_late_fragment_testsSPV_EXT_shader_stencil_export, {}, 0xffffffffu, 0xffffffffu},
+  {"StencilRefLessBackAMD", 5084, 1, pygen_variable_caps_StencilExportEXT, 2, pygen_variable_exts_SPV_AMD_shader_early_and_late_fragment_testsSPV_EXT_shader_stencil_export, {}, 0xffffffffu, 0xffffffffu},
   {"OutputLinesNV", 5269, 1, pygen_variable_caps_MeshShadingNV, 1, pygen_variable_exts_SPV_NV_mesh_shader, {}, 0xffffffffu, 0xffffffffu},
   {"OutputLinesNV", 5269, 1, pygen_variable_caps_MeshShadingNV, 1, pygen_variable_exts_SPV_NV_mesh_shader, {}, 0xffffffffu, 0xffffffffu},
   {"OutputPrimitivesNV", 5270, 1, pygen_variable_caps_MeshShadingNV, 1, pygen_variable_exts_SPV_NV_mesh_shader, {SPV_OPERAND_TYPE_LITERAL_INTEGER}, 0xffffffffu, 0xffffffffu},
   {"OutputPrimitivesNV", 5270, 1, pygen_variable_caps_MeshShadingNV, 1, pygen_variable_exts_SPV_NV_mesh_shader, {SPV_OPERAND_TYPE_LITERAL_INTEGER}, 0xffffffffu, 0xffffffffu},
   {"DerivativeGroupQuadsNV", 5289, 1, pygen_variable_caps_ComputeDerivativeGroupQuadsNV, 1, pygen_variable_exts_SPV_NV_compute_shader_derivatives, {}, 0xffffffffu, 0xffffffffu},
   {"DerivativeGroupQuadsNV", 5289, 1, pygen_variable_caps_ComputeDerivativeGroupQuadsNV, 1, pygen_variable_exts_SPV_NV_compute_shader_derivatives, {}, 0xffffffffu, 0xffffffffu},

+ 11 - 5
3rdparty/spirv-tools/source/cfa.h

@@ -68,6 +68,8 @@ class CFA {
   ///                       CFG following postorder traversal semantics
   ///                       CFG following postorder traversal semantics
   /// @param[in] backedge   A function that will be called when a backedge is
   /// @param[in] backedge   A function that will be called when a backedge is
   ///                       encountered during a traversal
   ///                       encountered during a traversal
+  /// @param[in] terminal   A function that will be called to determine if the
+  ///                       search should stop at the given node.
   /// NOTE: The @p successor_func and predecessor_func each return a pointer to
   /// NOTE: The @p successor_func and predecessor_func each return a pointer to
   /// a
   /// a
   /// collection such that iterators to that collection remain valid for the
   /// collection such that iterators to that collection remain valid for the
@@ -76,7 +78,8 @@ class CFA {
       const BB* entry, get_blocks_func successor_func,
       const BB* entry, get_blocks_func successor_func,
       std::function<void(cbb_ptr)> preorder,
       std::function<void(cbb_ptr)> preorder,
       std::function<void(cbb_ptr)> postorder,
       std::function<void(cbb_ptr)> postorder,
-      std::function<void(cbb_ptr, cbb_ptr)> backedge);
+      std::function<void(cbb_ptr, cbb_ptr)> backedge,
+      std::function<bool(cbb_ptr)> terminal);
 
 
   /// @brief Calculates dominator edges for a set of blocks
   /// @brief Calculates dominator edges for a set of blocks
   ///
   ///
@@ -138,7 +141,8 @@ void CFA<BB>::DepthFirstTraversal(
     const BB* entry, get_blocks_func successor_func,
     const BB* entry, get_blocks_func successor_func,
     std::function<void(cbb_ptr)> preorder,
     std::function<void(cbb_ptr)> preorder,
     std::function<void(cbb_ptr)> postorder,
     std::function<void(cbb_ptr)> postorder,
-    std::function<void(cbb_ptr, cbb_ptr)> backedge) {
+    std::function<void(cbb_ptr, cbb_ptr)> backedge,
+    std::function<bool(cbb_ptr)> terminal) {
   std::unordered_set<uint32_t> processed;
   std::unordered_set<uint32_t> processed;
 
 
   /// NOTE: work_list is the sequence of nodes from the root node to the node
   /// NOTE: work_list is the sequence of nodes from the root node to the node
@@ -152,7 +156,7 @@ void CFA<BB>::DepthFirstTraversal(
 
 
   while (!work_list.empty()) {
   while (!work_list.empty()) {
     block_info& top = work_list.back();
     block_info& top = work_list.back();
-    if (top.iter == end(*successor_func(top.block))) {
+    if (terminal(top.block) || top.iter == end(*successor_func(top.block))) {
       postorder(top.block);
       postorder(top.block);
       work_list.pop_back();
       work_list.pop_back();
     } else {
     } else {
@@ -266,11 +270,13 @@ std::vector<BB*> CFA<BB>::TraversalRoots(const std::vector<BB*>& blocks,
   auto mark_visited = [&visited](const BB* b) { visited.insert(b); };
   auto mark_visited = [&visited](const BB* b) { visited.insert(b); };
   auto ignore_block = [](const BB*) {};
   auto ignore_block = [](const BB*) {};
   auto ignore_blocks = [](const BB*, const BB*) {};
   auto ignore_blocks = [](const BB*, const BB*) {};
+  auto no_terminal_blocks = [](const BB*) { return false; };
 
 
   auto traverse_from_root = [&mark_visited, &succ_func, &ignore_block,
   auto traverse_from_root = [&mark_visited, &succ_func, &ignore_block,
-                             &ignore_blocks](const BB* entry) {
+                             &ignore_blocks,
+                             &no_terminal_blocks](const BB* entry) {
     DepthFirstTraversal(entry, succ_func, mark_visited, ignore_block,
     DepthFirstTraversal(entry, succ_func, mark_visited, ignore_block,
-                        ignore_blocks);
+                        ignore_blocks, no_terminal_blocks);
   };
   };
 
 
   std::vector<BB*> result;
   std::vector<BB*> result;

+ 4 - 3
3rdparty/spirv-tools/source/disassemble.h

@@ -25,9 +25,10 @@ namespace spvtools {
 
 
 // Decodes the given SPIR-V instruction binary representation to its assembly
 // Decodes the given SPIR-V instruction binary representation to its assembly
 // text. The context is inferred from the provided module binary. The options
 // text. The context is inferred from the provided module binary. The options
-// parameter is a bit field of spv_binary_to_text_options_t. Decoded text will
-// be stored into *text. Any error will be written into *diagnostic if
-// diagnostic is non-null.
+// parameter is a bit field of spv_binary_to_text_options_t (note: the option
+// SPV_BINARY_TO_TEXT_OPTION_PRINT will be ignored). Decoded text will be
+// stored into *text. Any error will be written into *diagnostic if diagnostic
+// is non-null.
 std::string spvInstructionBinaryToText(const spv_target_env env,
 std::string spvInstructionBinaryToText(const spv_target_env env,
                                        const uint32_t* inst_binary,
                                        const uint32_t* inst_binary,
                                        const size_t inst_word_count,
                                        const size_t inst_word_count,

+ 3 - 1
3rdparty/spirv-tools/source/libspirv.cpp

@@ -99,7 +99,9 @@ bool SpirvTools::Disassemble(const uint32_t* binary, const size_t binary_size,
   spv_text spvtext = nullptr;
   spv_text spvtext = nullptr;
   spv_result_t status = spvBinaryToText(impl_->context, binary, binary_size,
   spv_result_t status = spvBinaryToText(impl_->context, binary, binary_size,
                                         options, &spvtext, nullptr);
                                         options, &spvtext, nullptr);
-  if (status == SPV_SUCCESS) {
+  if (status == SPV_SUCCESS &&
+      (options & SPV_BINARY_TO_TEXT_OPTION_PRINT) == 0) {
+    assert(spvtext);
     text->assign(spvtext->str, spvtext->str + spvtext->length);
     text->assign(spvtext->str, spvtext->str + spvtext->length);
   }
   }
   spvTextDestroy(spvtext);
   spvTextDestroy(spvtext);

+ 12 - 5
3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp

@@ -659,9 +659,14 @@ Pass::Status AggressiveDCEPass::ProcessImpl() {
 
 
   InitializeModuleScopeLiveInstructions();
   InitializeModuleScopeLiveInstructions();
 
 
-  // Process all entry point functions.
-  ProcessFunction pfn = [this](Function* fp) { return AggressiveDCE(fp); };
-  modified |= context()->ProcessReachableCallTree(pfn);
+  // Run |AggressiveDCE| on the remaining functions.  The order does not matter,
+  // since |AggressiveDCE| is intra-procedural.  This can mean that function
+  // will become dead if all function call to them are removed.  These dead
+  // function will still be in the module after this pass.  We expect this to be
+  // rare.
+  for (Function& fp : *context()->module()) {
+    modified |= AggressiveDCE(&fp);
+  }
 
 
   // If the decoration manager is kept live then the context will try to keep it
   // If the decoration manager is kept live then the context will try to keep it
   // up to date.  ADCE deals with group decorations by changing the operands in
   // up to date.  ADCE deals with group decorations by changing the operands in
@@ -687,8 +692,9 @@ Pass::Status AggressiveDCEPass::ProcessImpl() {
   }
   }
 
 
   // Cleanup all CFG including all unreachable blocks.
   // Cleanup all CFG including all unreachable blocks.
-  ProcessFunction cleanup = [this](Function* f) { return CFGCleanup(f); };
-  modified |= context()->ProcessReachableCallTree(cleanup);
+  for (Function& fp : *context()->module()) {
+    modified |= CFGCleanup(&fp);
+  }
 
 
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 }
@@ -968,6 +974,7 @@ void AggressiveDCEPass::InitExtensions() {
       "SPV_EXT_shader_image_int64",
       "SPV_EXT_shader_image_int64",
       "SPV_KHR_non_semantic_info",
       "SPV_KHR_non_semantic_info",
       "SPV_KHR_uniform_group_instructions",
       "SPV_KHR_uniform_group_instructions",
+      "SPV_KHR_fragment_shader_barycentric",
   });
   });
 }
 }
 
 

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

@@ -74,6 +74,12 @@ void CFG::RemoveNonExistingEdges(uint32_t blk_id) {
 
 
 void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root,
 void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root,
                                  std::list<BasicBlock*>* order) {
                                  std::list<BasicBlock*>* order) {
+  ComputeStructuredOrder(func, root, nullptr, order);
+}
+
+void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root,
+                                 BasicBlock* end,
+                                 std::list<BasicBlock*>* order) {
   assert(module_->context()->get_feature_mgr()->HasCapability(
   assert(module_->context()->get_feature_mgr()->HasCapability(
              SpvCapabilityShader) &&
              SpvCapabilityShader) &&
          "This only works on structured control flow");
          "This only works on structured control flow");
@@ -82,6 +88,8 @@ void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root,
   ComputeStructuredSuccessors(func);
   ComputeStructuredSuccessors(func);
   auto ignore_block = [](cbb_ptr) {};
   auto ignore_block = [](cbb_ptr) {};
   auto ignore_edge = [](cbb_ptr, cbb_ptr) {};
   auto ignore_edge = [](cbb_ptr, cbb_ptr) {};
+  auto terminal = [end](cbb_ptr bb) { return bb == end; };
+
   auto get_structured_successors = [this](const BasicBlock* b) {
   auto get_structured_successors = [this](const BasicBlock* b) {
     return &(block2structured_succs_[b]);
     return &(block2structured_succs_[b]);
   };
   };
@@ -92,7 +100,8 @@ void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root,
     order->push_front(const_cast<BasicBlock*>(b));
     order->push_front(const_cast<BasicBlock*>(b));
   };
   };
   CFA<BasicBlock>::DepthFirstTraversal(root, get_structured_successors,
   CFA<BasicBlock>::DepthFirstTraversal(root, get_structured_successors,
-                                       ignore_block, post_order, ignore_edge);
+                                       ignore_block, post_order, ignore_edge,
+                                       terminal);
 }
 }
 
 
 void CFG::ForEachBlockInPostOrder(BasicBlock* bb,
 void CFG::ForEachBlockInPostOrder(BasicBlock* bb,
@@ -205,7 +214,7 @@ BasicBlock* CFG::SplitLoopHeader(BasicBlock* bb) {
   // Find the back edge
   // Find the back edge
   BasicBlock* latch_block = nullptr;
   BasicBlock* latch_block = nullptr;
   Function::iterator latch_block_iter = header_it;
   Function::iterator latch_block_iter = header_it;
-  while (++latch_block_iter != fn->end()) {
+  for (; latch_block_iter != fn->end(); ++latch_block_iter) {
     // If blocks are in the proper order, then the only branch that appears
     // If blocks are in the proper order, then the only branch that appears
     // after the header is the latch.
     // after the header is the latch.
     if (std::find(pred.begin(), pred.end(), latch_block_iter->id()) !=
     if (std::find(pred.begin(), pred.end(), latch_block_iter->id()) !=
@@ -237,6 +246,15 @@ BasicBlock* CFG::SplitLoopHeader(BasicBlock* bb) {
     context->set_instr_block(inst, new_header);
     context->set_instr_block(inst, new_header);
   });
   });
 
 
+  // If |bb| was the latch block, the branch back to the header is not in
+  // |new_header|.
+  if (latch_block == bb) {
+    if (new_header->ContinueBlockId() == bb->id()) {
+      new_header->GetLoopMergeInst()->SetInOperand(1, {new_header_id});
+    }
+    latch_block = new_header;
+  }
+
   // Adjust the OpPhi instructions as needed.
   // Adjust the OpPhi instructions as needed.
   bb->ForEachPhiInst([latch_block, bb, new_header, context](Instruction* phi) {
   bb->ForEachPhiInst([latch_block, bb, new_header, context](Instruction* phi) {
     std::vector<uint32_t> preheader_phi_ops;
     std::vector<uint32_t> preheader_phi_ops;

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

@@ -66,6 +66,14 @@ class CFG {
   void ComputeStructuredOrder(Function* func, BasicBlock* root,
   void ComputeStructuredOrder(Function* func, BasicBlock* root,
                               std::list<BasicBlock*>* order);
                               std::list<BasicBlock*>* order);
 
 
+  // Compute structured block order into |order| for |func| starting at |root|
+  // and ending at |end|. This order has the property that dominators come
+  // before all blocks they dominate, merge blocks come after all blocks that
+  // are in the control constructs of their header, and continue blocks come
+  // after all the blocks in the body of their loop.
+  void ComputeStructuredOrder(Function* func, BasicBlock* root, BasicBlock* end,
+                              std::list<BasicBlock*>* order);
+
   // Applies |f| to all blocks that can be reach from |bb| in post order.
   // Applies |f| to all blocks that can be reach from |bb| in post order.
   void ForEachBlockInPostOrder(BasicBlock* bb,
   void ForEachBlockInPostOrder(BasicBlock* bb,
                                const std::function<void(BasicBlock*)>& f);
                                const std::function<void(BasicBlock*)>& f);

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

@@ -251,6 +251,193 @@ ConstantFoldingRule FoldVectorTimesScalar() {
   };
   };
 }
 }
 
 
+ConstantFoldingRule FoldVectorTimesMatrix() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants)
+             -> const analysis::Constant* {
+    assert(inst->opcode() == SpvOpVectorTimesMatrix);
+    analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+    analysis::TypeManager* type_mgr = context->get_type_mgr();
+
+    if (!inst->IsFloatingPointFoldingAllowed()) {
+      if (HasFloatingPoint(type_mgr->GetType(inst->type_id()))) {
+        return nullptr;
+      }
+    }
+
+    const analysis::Constant* c1 = constants[0];
+    const analysis::Constant* c2 = constants[1];
+
+    if (c1 == nullptr || c2 == nullptr) {
+      return nullptr;
+    }
+
+    // Check result type.
+    const analysis::Type* result_type = type_mgr->GetType(inst->type_id());
+    const analysis::Vector* vector_type = result_type->AsVector();
+    assert(vector_type != nullptr);
+    const analysis::Type* element_type = vector_type->element_type();
+    assert(element_type != nullptr);
+    const analysis::Float* float_type = element_type->AsFloat();
+    assert(float_type != nullptr);
+
+    // Check types of c1 and c2.
+    assert(c1->type()->AsVector() == vector_type);
+    assert(c1->type()->AsVector()->element_type() == element_type &&
+           c2->type()->AsMatrix()->element_type() == vector_type);
+
+    // Get a float vector that is the result of vector-times-matrix.
+    std::vector<const analysis::Constant*> c1_components =
+        c1->GetVectorComponents(const_mgr);
+    std::vector<const analysis::Constant*> c2_components =
+        c2->AsMatrixConstant()->GetComponents();
+    uint32_t resultVectorSize = result_type->AsVector()->element_count();
+
+    std::vector<uint32_t> ids;
+
+    if ((c1 && c1->IsZero()) || (c2 && c2->IsZero())) {
+      std::vector<uint32_t> words(float_type->width() / 32, 0);
+      for (uint32_t i = 0; i < resultVectorSize; ++i) {
+        const analysis::Constant* new_elem =
+            const_mgr->GetConstant(float_type, words);
+        ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id());
+      }
+      return const_mgr->GetConstant(vector_type, ids);
+    }
+
+    if (float_type->width() == 32) {
+      for (uint32_t i = 0; i < resultVectorSize; ++i) {
+        float result_scalar = 0.0f;
+        const analysis::VectorConstant* c2_vec =
+            c2_components[i]->AsVectorConstant();
+        for (uint32_t j = 0; j < c2_vec->GetComponents().size(); ++j) {
+          float c1_scalar = c1_components[j]->GetFloat();
+          float c2_scalar = c2_vec->GetComponents()[j]->GetFloat();
+          result_scalar += c1_scalar * c2_scalar;
+        }
+        utils::FloatProxy<float> result(result_scalar);
+        std::vector<uint32_t> words = result.GetWords();
+        const analysis::Constant* new_elem =
+            const_mgr->GetConstant(float_type, words);
+        ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id());
+      }
+      return const_mgr->GetConstant(vector_type, ids);
+    } else if (float_type->width() == 64) {
+      for (uint32_t i = 0; i < c2_components.size(); ++i) {
+        double result_scalar = 0.0;
+        const analysis::VectorConstant* c2_vec =
+            c2_components[i]->AsVectorConstant();
+        for (uint32_t j = 0; j < c2_vec->GetComponents().size(); ++j) {
+          double c1_scalar = c1_components[j]->GetDouble();
+          double c2_scalar = c2_vec->GetComponents()[j]->GetDouble();
+          result_scalar += c1_scalar * c2_scalar;
+        }
+        utils::FloatProxy<double> result(result_scalar);
+        std::vector<uint32_t> words = result.GetWords();
+        const analysis::Constant* new_elem =
+            const_mgr->GetConstant(float_type, words);
+        ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id());
+      }
+      return const_mgr->GetConstant(vector_type, ids);
+    }
+    return nullptr;
+  };
+}
+
+ConstantFoldingRule FoldMatrixTimesVector() {
+  return [](IRContext* context, Instruction* inst,
+            const std::vector<const analysis::Constant*>& constants)
+             -> const analysis::Constant* {
+    assert(inst->opcode() == SpvOpMatrixTimesVector);
+    analysis::ConstantManager* const_mgr = context->get_constant_mgr();
+    analysis::TypeManager* type_mgr = context->get_type_mgr();
+
+    if (!inst->IsFloatingPointFoldingAllowed()) {
+      if (HasFloatingPoint(type_mgr->GetType(inst->type_id()))) {
+        return nullptr;
+      }
+    }
+
+    const analysis::Constant* c1 = constants[0];
+    const analysis::Constant* c2 = constants[1];
+
+    if (c1 == nullptr || c2 == nullptr) {
+      return nullptr;
+    }
+
+    // Check result type.
+    const analysis::Type* result_type = type_mgr->GetType(inst->type_id());
+    const analysis::Vector* vector_type = result_type->AsVector();
+    assert(vector_type != nullptr);
+    const analysis::Type* element_type = vector_type->element_type();
+    assert(element_type != nullptr);
+    const analysis::Float* float_type = element_type->AsFloat();
+    assert(float_type != nullptr);
+
+    // Check types of c1 and c2.
+    assert(c1->type()->AsMatrix()->element_type() == vector_type);
+    assert(c2->type()->AsVector()->element_type() == element_type);
+
+    // Get a float vector that is the result of matrix-times-vector.
+    std::vector<const analysis::Constant*> c1_components =
+        c1->AsMatrixConstant()->GetComponents();
+    std::vector<const analysis::Constant*> c2_components =
+        c2->GetVectorComponents(const_mgr);
+    uint32_t resultVectorSize = result_type->AsVector()->element_count();
+
+    std::vector<uint32_t> ids;
+
+    if ((c1 && c1->IsZero()) || (c2 && c2->IsZero())) {
+      std::vector<uint32_t> words(float_type->width() / 32, 0);
+      for (uint32_t i = 0; i < resultVectorSize; ++i) {
+        const analysis::Constant* new_elem =
+            const_mgr->GetConstant(float_type, words);
+        ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id());
+      }
+      return const_mgr->GetConstant(vector_type, ids);
+    }
+
+    if (float_type->width() == 32) {
+      for (uint32_t i = 0; i < resultVectorSize; ++i) {
+        float result_scalar = 0.0f;
+        for (uint32_t j = 0; j < c1_components.size(); ++j) {
+          float c1_scalar = c1_components[j]
+                                ->AsVectorConstant()
+                                ->GetComponents()[i]
+                                ->GetFloat();
+          float c2_scalar = c2_components[j]->GetFloat();
+          result_scalar += c1_scalar * c2_scalar;
+        }
+        utils::FloatProxy<float> result(result_scalar);
+        std::vector<uint32_t> words = result.GetWords();
+        const analysis::Constant* new_elem =
+            const_mgr->GetConstant(float_type, words);
+        ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id());
+      }
+      return const_mgr->GetConstant(vector_type, ids);
+    } else if (float_type->width() == 64) {
+      for (uint32_t i = 0; i < resultVectorSize; ++i) {
+        double result_scalar = 0.0;
+        for (uint32_t j = 0; j < c1_components.size(); ++j) {
+          double c1_scalar = c1_components[j]
+                                 ->AsVectorConstant()
+                                 ->GetComponents()[i]
+                                 ->GetDouble();
+          double c2_scalar = c2_components[j]->GetDouble();
+          result_scalar += c1_scalar * c2_scalar;
+        }
+        utils::FloatProxy<double> result(result_scalar);
+        std::vector<uint32_t> words = result.GetWords();
+        const analysis::Constant* new_elem =
+            const_mgr->GetConstant(float_type, words);
+        ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id());
+      }
+      return const_mgr->GetConstant(vector_type, ids);
+    }
+    return nullptr;
+  };
+}
+
 ConstantFoldingRule FoldCompositeWithConstants() {
 ConstantFoldingRule FoldCompositeWithConstants() {
   // Folds an OpCompositeConstruct where all of the inputs are constants to a
   // Folds an OpCompositeConstruct where all of the inputs are constants to a
   // constant.  A new constant is created if necessary.
   // constant.  A new constant is created if necessary.
@@ -1288,6 +1475,8 @@ void ConstantFoldingRules::AddFoldingRules() {
 
 
   rules_[SpvOpVectorShuffle].push_back(FoldVectorShuffleWithConstants());
   rules_[SpvOpVectorShuffle].push_back(FoldVectorShuffleWithConstants());
   rules_[SpvOpVectorTimesScalar].push_back(FoldVectorTimesScalar());
   rules_[SpvOpVectorTimesScalar].push_back(FoldVectorTimesScalar());
+  rules_[SpvOpVectorTimesMatrix].push_back(FoldVectorTimesMatrix());
+  rules_[SpvOpMatrixTimesVector].push_back(FoldMatrixTimesVector());
 
 
   rules_[SpvOpFNegate].push_back(FoldFNegate());
   rules_[SpvOpFNegate].push_back(FoldFNegate());
   rules_[SpvOpQuantizeToF16].push_back(FoldQuantizeToF16());
   rules_[SpvOpQuantizeToF16].push_back(FoldQuantizeToF16());

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

@@ -59,7 +59,9 @@ static void DepthFirstSearch(const BBType* bb, SuccessorLambda successors,
                              PreLambda pre, PostLambda post) {
                              PreLambda pre, PostLambda post) {
   // Ignore backedge operation.
   // Ignore backedge operation.
   auto nop_backedge = [](const BBType*, const BBType*) {};
   auto nop_backedge = [](const BBType*, const BBType*) {};
-  CFA<BBType>::DepthFirstTraversal(bb, successors, pre, post, nop_backedge);
+  auto no_terminal_blocks = [](const BBType*) { return false; };
+  CFA<BBType>::DepthFirstTraversal(bb, successors, pre, post, nop_backedge,
+                                   no_terminal_blocks);
 }
 }
 
 
 // Wrapper around CFA::DepthFirstTraversal to provide an interface to perform
 // Wrapper around CFA::DepthFirstTraversal to provide an interface to perform

+ 289 - 6
3rdparty/spirv-tools/source/opt/folding_rules.cpp

@@ -277,6 +277,11 @@ uint32_t Reciprocal(analysis::ConstantManager* const_mgr,
   uint32_t width = c->type()->AsFloat()->width();
   uint32_t width = c->type()->AsFloat()->width();
   assert(width == 32 || width == 64);
   assert(width == 32 || width == 64);
   std::vector<uint32_t> words;
   std::vector<uint32_t> words;
+
+  if (c->IsZero()) {
+    return 0;
+  }
+
   if (width == 64) {
   if (width == 64) {
     spvtools::utils::FloatProxy<double> result(1.0 / c->GetDouble());
     spvtools::utils::FloatProxy<double> result(1.0 / c->GetDouble());
     if (!IsValidResult(result.getAsFloat())) return 0;
     if (!IsValidResult(result.getAsFloat())) return 0;
@@ -1488,6 +1493,74 @@ bool MergeMulAddArithmetic(IRContext* context, Instruction* inst,
   return false;
   return false;
 }
 }
 
 
+// Replaces |sub| inplace with an FMA instruction |(x*y)+a| where |a| first gets
+// negated if |negate_addition| is true, otherwise |x| gets negated.
+void ReplaceWithFmaAndNegate(Instruction* sub, uint32_t x, uint32_t y,
+                             uint32_t a, bool negate_addition) {
+  uint32_t ext =
+      sub->context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
+
+  if (ext == 0) {
+    sub->context()->AddExtInstImport("GLSL.std.450");
+    ext = sub->context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450();
+    assert(ext != 0 &&
+           "Could not add the GLSL.std.450 extended instruction set");
+  }
+
+  InstructionBuilder ir_builder(
+      sub->context(), sub,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+
+  Instruction* neg = ir_builder.AddUnaryOp(sub->type_id(), SpvOpFNegate,
+                                           negate_addition ? a : x);
+  uint32_t neg_op = neg->result_id();  // -a : -x
+
+  std::vector<Operand> operands;
+  operands.push_back({SPV_OPERAND_TYPE_ID, {ext}});
+  operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {GLSLstd450Fma}});
+  operands.push_back({SPV_OPERAND_TYPE_ID, {negate_addition ? x : neg_op}});
+  operands.push_back({SPV_OPERAND_TYPE_ID, {y}});
+  operands.push_back({SPV_OPERAND_TYPE_ID, {negate_addition ? neg_op : a}});
+
+  sub->SetOpcode(SpvOpExtInst);
+  sub->SetInOperands(std::move(operands));
+}
+
+// Folds a multiply and subtract into an Fma and negation.
+//
+// Cases:
+// (x * y) - a = Fma x y -a
+// a - (x * y) = Fma -x y a
+bool MergeMulSubArithmetic(IRContext* context, Instruction* sub,
+                           const std::vector<const analysis::Constant*>&) {
+  assert(sub->opcode() == SpvOpFSub);
+
+  if (!sub->IsFloatingPointFoldingAllowed()) {
+    return false;
+  }
+
+  analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
+  for (int i = 0; i < 2; i++) {
+    uint32_t op_id = sub->GetSingleWordInOperand(i);
+    Instruction* mul = def_use_mgr->GetDef(op_id);
+
+    if (mul->opcode() != SpvOpFMul) {
+      continue;
+    }
+
+    if (!mul->IsFloatingPointFoldingAllowed()) {
+      continue;
+    }
+
+    uint32_t x = mul->GetSingleWordInOperand(0);
+    uint32_t y = mul->GetSingleWordInOperand(1);
+    uint32_t a = sub->GetSingleWordInOperand((i + 1) % 2);
+    ReplaceWithFmaAndNegate(sub, x, y, a, i == 0);
+    return true;
+  }
+  return false;
+}
+
 FoldingRule IntMultipleBy1() {
 FoldingRule IntMultipleBy1() {
   return [](IRContext*, Instruction* inst,
   return [](IRContext*, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
             const std::vector<const analysis::Constant*>& constants) {
@@ -1631,6 +1704,57 @@ bool CompositeConstructFeedingExtract(
   return true;
   return true;
 }
 }
 
 
+// Walks the indexes chain from |start| to |end| of an OpCompositeInsert or
+// OpCompositeExtract instruction, and returns the type of the final element
+// being accessed.
+const analysis::Type* GetElementType(uint32_t type_id,
+                                     Instruction::iterator start,
+                                     Instruction::iterator end,
+                                     const analysis::TypeManager* type_mgr) {
+  const analysis::Type* type = type_mgr->GetType(type_id);
+  for (auto index : make_range(std::move(start), std::move(end))) {
+    assert(index.type == SPV_OPERAND_TYPE_LITERAL_INTEGER &&
+           index.words.size() == 1);
+    if (auto* array_type = type->AsArray()) {
+      type = array_type->element_type();
+    } else if (auto* matrix_type = type->AsMatrix()) {
+      type = matrix_type->element_type();
+    } else if (auto* struct_type = type->AsStruct()) {
+      type = struct_type->element_types()[index.words[0]];
+    } else {
+      type = nullptr;
+    }
+  }
+  return type;
+}
+
+// Returns true of |inst_1| and |inst_2| have the same indexes that will be used
+// to index into a composite object, excluding the last index.  The two
+// instructions must have the same opcode, and be either OpCompositeExtract or
+// OpCompositeInsert instructions.
+bool HaveSameIndexesExceptForLast(Instruction* inst_1, Instruction* inst_2) {
+  assert(inst_1->opcode() == inst_2->opcode() &&
+         "Expecting the opcodes to be the same.");
+  assert((inst_1->opcode() == SpvOpCompositeInsert ||
+          inst_1->opcode() == SpvOpCompositeExtract) &&
+         "Instructions must be OpCompositeInsert or OpCompositeExtract.");
+
+  if (inst_1->NumInOperands() != inst_2->NumInOperands()) {
+    return false;
+  }
+
+  uint32_t first_index_position =
+      (inst_1->opcode() == SpvOpCompositeInsert ? 2 : 1);
+  for (uint32_t i = first_index_position; i < inst_1->NumInOperands() - 1;
+       i++) {
+    if (inst_1->GetSingleWordInOperand(i) !=
+        inst_2->GetSingleWordInOperand(i)) {
+      return false;
+    }
+  }
+  return true;
+}
+
 // If the OpCompositeConstruct is simply putting back together elements that
 // If the OpCompositeConstruct is simply putting back together elements that
 // where extracted from the same source, we can simply reuse the source.
 // where extracted from the same source, we can simply reuse the source.
 //
 //
@@ -1653,19 +1777,24 @@ bool CompositeExtractFeedingConstruct(
   // - extractions
   // - extractions
   // - extracting the same position they are inserting
   // - extracting the same position they are inserting
   // - all extract from the same id.
   // - all extract from the same id.
+  Instruction* first_element_inst = nullptr;
   for (uint32_t i = 0; i < inst->NumInOperands(); ++i) {
   for (uint32_t i = 0; i < inst->NumInOperands(); ++i) {
     const uint32_t element_id = inst->GetSingleWordInOperand(i);
     const uint32_t element_id = inst->GetSingleWordInOperand(i);
     Instruction* element_inst = def_use_mgr->GetDef(element_id);
     Instruction* element_inst = def_use_mgr->GetDef(element_id);
+    if (first_element_inst == nullptr) {
+      first_element_inst = element_inst;
+    }
 
 
     if (element_inst->opcode() != SpvOpCompositeExtract) {
     if (element_inst->opcode() != SpvOpCompositeExtract) {
       return false;
       return false;
     }
     }
 
 
-    if (element_inst->NumInOperands() != 2) {
+    if (!HaveSameIndexesExceptForLast(element_inst, first_element_inst)) {
       return false;
       return false;
     }
     }
 
 
-    if (element_inst->GetSingleWordInOperand(1) != i) {
+    if (element_inst->GetSingleWordInOperand(element_inst->NumInOperands() -
+                                             1) != i) {
       return false;
       return false;
     }
     }
 
 
@@ -1681,13 +1810,31 @@ bool CompositeExtractFeedingConstruct(
   // The last check it to see that the object being extracted from is the
   // The last check it to see that the object being extracted from is the
   // correct type.
   // correct type.
   Instruction* original_inst = def_use_mgr->GetDef(original_id);
   Instruction* original_inst = def_use_mgr->GetDef(original_id);
-  if (original_inst->type_id() != inst->type_id()) {
+  analysis::TypeManager* type_mgr = context->get_type_mgr();
+  const analysis::Type* original_type =
+      GetElementType(original_inst->type_id(), first_element_inst->begin() + 3,
+                     first_element_inst->end() - 1, type_mgr);
+
+  if (original_type == nullptr) {
     return false;
     return false;
   }
   }
 
 
-  // Simplify by using the original object.
-  inst->SetOpcode(SpvOpCopyObject);
-  inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {original_id}}});
+  if (inst->type_id() != type_mgr->GetId(original_type)) {
+    return false;
+  }
+
+  if (first_element_inst->NumInOperands() == 2) {
+    // Simplify by using the original object.
+    inst->SetOpcode(SpvOpCopyObject);
+    inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {original_id}}});
+    return true;
+  }
+
+  // Copies the original id and all indexes except for the last to the new
+  // extract instruction.
+  inst->SetOpcode(SpvOpCompositeExtract);
+  inst->SetInOperands(std::vector<Operand>(first_element_inst->begin() + 2,
+                                           first_element_inst->end() - 1));
   return true;
   return true;
 }
 }
 
 
@@ -1891,6 +2038,139 @@ FoldingRule FMixFeedingExtract() {
   };
   };
 }
 }
 
 
+// Returns the number of elements in the composite type |type|.  Returns 0 if
+// |type| is a scalar value.
+uint32_t GetNumberOfElements(const analysis::Type* type) {
+  if (auto* vector_type = type->AsVector()) {
+    return vector_type->element_count();
+  }
+  if (auto* matrix_type = type->AsMatrix()) {
+    return matrix_type->element_count();
+  }
+  if (auto* struct_type = type->AsStruct()) {
+    return static_cast<uint32_t>(struct_type->element_types().size());
+  }
+  if (auto* array_type = type->AsArray()) {
+    return array_type->length_info().words[0];
+  }
+  return 0;
+}
+
+// Returns a map with the set of values that were inserted into an object by
+// the chain of OpCompositeInsertInstruction starting with |inst|.
+// The map will map the index to the value inserted at that index.
+std::map<uint32_t, uint32_t> GetInsertedValues(Instruction* inst) {
+  analysis::DefUseManager* def_use_mgr = inst->context()->get_def_use_mgr();
+  std::map<uint32_t, uint32_t> values_inserted;
+  Instruction* current_inst = inst;
+  while (current_inst->opcode() == SpvOpCompositeInsert) {
+    if (current_inst->NumInOperands() > inst->NumInOperands()) {
+      // This is the catch the case
+      //   %2 = OpCompositeInsert %m2x2int %v2int_1_0 %m2x2int_undef 0
+      //   %3 = OpCompositeInsert %m2x2int %int_4 %2 0 0
+      //   %4 = OpCompositeInsert %m2x2int %v2int_2_3 %3 1
+      // In this case we cannot do a single construct to get the matrix.
+      uint32_t partially_inserted_element_index =
+          current_inst->GetSingleWordInOperand(inst->NumInOperands() - 1);
+      if (values_inserted.count(partially_inserted_element_index) == 0)
+        return {};
+    }
+    if (HaveSameIndexesExceptForLast(inst, current_inst)) {
+      values_inserted.insert(
+          {current_inst->GetSingleWordInOperand(current_inst->NumInOperands() -
+                                                1),
+           current_inst->GetSingleWordInOperand(kInsertObjectIdInIdx)});
+    }
+    current_inst = def_use_mgr->GetDef(
+        current_inst->GetSingleWordInOperand(kInsertCompositeIdInIdx));
+  }
+  return values_inserted;
+}
+
+// Returns true of there is an entry in |values_inserted| for every element of
+// |Type|.
+bool DoInsertedValuesCoverEntireObject(
+    const analysis::Type* type, std::map<uint32_t, uint32_t>& values_inserted) {
+  uint32_t container_size = GetNumberOfElements(type);
+  if (container_size != values_inserted.size()) {
+    return false;
+  }
+
+  if (values_inserted.rbegin()->first >= container_size) {
+    return false;
+  }
+  return true;
+}
+
+// Returns the type of the element that immediately contains the element being
+// inserted by the OpCompositeInsert instruction |inst|.
+const analysis::Type* GetContainerType(Instruction* inst) {
+  assert(inst->opcode() == SpvOpCompositeInsert);
+  analysis::TypeManager* type_mgr = inst->context()->get_type_mgr();
+  return GetElementType(inst->type_id(), inst->begin() + 4, inst->end() - 1,
+                        type_mgr);
+}
+
+// Returns an OpCompositeConstruct instruction that build an object with
+// |type_id| out of the values in |values_inserted|.  Each value will be
+// placed at the index corresponding to the value.  The new instruction will
+// be placed before |insert_before|.
+Instruction* BuildCompositeConstruct(
+    uint32_t type_id, const std::map<uint32_t, uint32_t>& values_inserted,
+    Instruction* insert_before) {
+  InstructionBuilder ir_builder(
+      insert_before->context(), insert_before,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+
+  std::vector<uint32_t> ids_in_order;
+  for (auto it : values_inserted) {
+    ids_in_order.push_back(it.second);
+  }
+  Instruction* construct =
+      ir_builder.AddCompositeConstruct(type_id, ids_in_order);
+  return construct;
+}
+
+// Replaces the OpCompositeInsert |inst| that inserts |construct| into the same
+// object as |inst| with final index removed.  If the resulting
+// OpCompositeInsert instruction would have no remaining indexes, the
+// instruction is replaced with an OpCopyObject instead.
+void InsertConstructedObject(Instruction* inst, const Instruction* construct) {
+  if (inst->NumInOperands() == 3) {
+    inst->SetOpcode(SpvOpCopyObject);
+    inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {construct->result_id()}}});
+  } else {
+    inst->SetInOperand(kInsertObjectIdInIdx, {construct->result_id()});
+    inst->RemoveOperand(inst->NumOperands() - 1);
+  }
+}
+
+// Replaces a series of |OpCompositeInsert| instruction that cover the entire
+// object with an |OpCompositeConstruct|.
+bool CompositeInsertToCompositeConstruct(
+    IRContext* context, Instruction* inst,
+    const std::vector<const analysis::Constant*>&) {
+  assert(inst->opcode() == SpvOpCompositeInsert &&
+         "Wrong opcode.  Should be OpCompositeInsert.");
+  if (inst->NumInOperands() < 3) return false;
+
+  std::map<uint32_t, uint32_t> values_inserted = GetInsertedValues(inst);
+  const analysis::Type* container_type = GetContainerType(inst);
+  if (container_type == nullptr) {
+    return false;
+  }
+
+  if (!DoInsertedValuesCoverEntireObject(container_type, values_inserted)) {
+    return false;
+  }
+
+  analysis::TypeManager* type_mgr = context->get_type_mgr();
+  Instruction* construct = BuildCompositeConstruct(
+      type_mgr->GetId(container_type), values_inserted, inst);
+  InsertConstructedObject(inst, construct);
+  return true;
+}
+
 FoldingRule RedundantPhi() {
 FoldingRule RedundantPhi() {
   // An OpPhi instruction where all values are the same or the result of the phi
   // An OpPhi instruction where all values are the same or the result of the phi
   // itself, can be replaced by the value itself.
   // itself, can be replaced by the value itself.
@@ -2591,6 +2871,8 @@ void FoldingRules::AddFoldingRules() {
   rules_[SpvOpCompositeExtract].push_back(VectorShuffleFeedingExtract());
   rules_[SpvOpCompositeExtract].push_back(VectorShuffleFeedingExtract());
   rules_[SpvOpCompositeExtract].push_back(FMixFeedingExtract());
   rules_[SpvOpCompositeExtract].push_back(FMixFeedingExtract());
 
 
+  rules_[SpvOpCompositeInsert].push_back(CompositeInsertToCompositeConstruct);
+
   rules_[SpvOpDot].push_back(DotProductDoingExtract());
   rules_[SpvOpDot].push_back(DotProductDoingExtract());
 
 
   rules_[SpvOpEntryPoint].push_back(RemoveRedundantOperands());
   rules_[SpvOpEntryPoint].push_back(RemoveRedundantOperands());
@@ -2622,6 +2904,7 @@ void FoldingRules::AddFoldingRules() {
   rules_[SpvOpFSub].push_back(MergeSubNegateArithmetic());
   rules_[SpvOpFSub].push_back(MergeSubNegateArithmetic());
   rules_[SpvOpFSub].push_back(MergeSubAddArithmetic());
   rules_[SpvOpFSub].push_back(MergeSubAddArithmetic());
   rules_[SpvOpFSub].push_back(MergeSubSubArithmetic());
   rules_[SpvOpFSub].push_back(MergeSubSubArithmetic());
+  rules_[SpvOpFSub].push_back(MergeMulSubArithmetic);
 
 
   rules_[SpvOpIAdd].push_back(RedundantIAdd());
   rules_[SpvOpIAdd].push_back(RedundantIAdd());
   rules_[SpvOpIAdd].push_back(MergeAddNegateArithmetic());
   rules_[SpvOpIAdd].push_back(MergeAddNegateArithmetic());

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

@@ -160,6 +160,11 @@ bool IfConversion::CheckBlock(BasicBlock* block, DominatorAnalysis* dominators,
   BasicBlock* inc1 = context()->get_instr_block(preds[1]);
   BasicBlock* inc1 = context()->get_instr_block(preds[1]);
   if (dominators->Dominates(block, inc1)) return false;
   if (dominators->Dominates(block, inc1)) return false;
 
 
+  if (inc0 == inc1) {
+    // If the predecessor blocks are the same, then there is only 1 value for
+    // the OpPhi.  Other transformation should be able to simplify that.
+    return false;
+  }
   // All phis will have the same common dominator, so cache the result
   // All phis will have the same common dominator, so cache the result
   // for this block. If there is no common dominator, then we cannot transform
   // for this block. If there is no common dominator, then we cannot transform
   // any phi in this basic block.
   // any phi in this basic block.

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

@@ -508,6 +508,37 @@ void InlinePass::MoveLoopMergeInstToFirstBlock(
   delete &*loop_merge_itr;
   delete &*loop_merge_itr;
 }
 }
 
 
+void InlinePass::UpdateSingleBlockLoopContinueTarget(
+    uint32_t new_id, std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
+  auto& header = new_blocks->front();
+  auto* merge_inst = header->GetLoopMergeInst();
+
+  // The back-edge block is split at the branch to create a new back-edge
+  // block. The old block is modified to branch to the new block. The loop
+  // merge instruction is updated to declare the new block as the continue
+  // target. This has the effect of changing the loop from being a large
+  // continue construct and an empty loop construct to being a loop with a loop
+  // construct and a trivial continue construct. This change is made to satisfy
+  // structural dominance.
+
+  // Add the new basic block.
+  std::unique_ptr<BasicBlock> new_block =
+      MakeUnique<BasicBlock>(NewLabel(new_id));
+  auto& old_backedge = new_blocks->back();
+  auto old_branch = old_backedge->tail();
+
+  // Move the old back edge into the new block.
+  std::unique_ptr<Instruction> br(&*old_branch);
+  new_block->AddInstruction(std::move(br));
+
+  // Add a branch to the new block from the old back-edge block.
+  AddBranch(new_id, &old_backedge);
+  new_blocks->push_back(std::move(new_block));
+
+  // Update the loop's continue target to the new block.
+  merge_inst->SetInOperand(1u, {new_id});
+}
+
 bool InlinePass::GenInlineCode(
 bool InlinePass::GenInlineCode(
     std::vector<std::unique_ptr<BasicBlock>>* new_blocks,
     std::vector<std::unique_ptr<BasicBlock>>* new_blocks,
     std::vector<std::unique_ptr<Instruction>>* new_vars,
     std::vector<std::unique_ptr<Instruction>>* new_vars,
@@ -639,9 +670,19 @@ bool InlinePass::GenInlineCode(
   // Finalize inline code.
   // Finalize inline code.
   new_blocks->push_back(std::move(new_blk_ptr));
   new_blocks->push_back(std::move(new_blk_ptr));
 
 
-  if (caller_is_loop_header && (new_blocks->size() > 1))
+  if (caller_is_loop_header && (new_blocks->size() > 1)) {
     MoveLoopMergeInstToFirstBlock(new_blocks);
     MoveLoopMergeInstToFirstBlock(new_blocks);
 
 
+    // If the loop was a single basic block previously, update it's structure.
+    auto& header = new_blocks->front();
+    auto* merge_inst = header->GetLoopMergeInst();
+    if (merge_inst->GetSingleWordInOperand(1u) == header->id()) {
+      auto new_id = context()->TakeNextId();
+      if (new_id == 0) return false;
+      UpdateSingleBlockLoopContinueTarget(new_id, new_blocks);
+    }
+  }
+
   // Update block map given replacement blocks.
   // Update block map given replacement blocks.
   for (auto& blk : *new_blocks) {
   for (auto& blk : *new_blocks) {
     id2block_[blk->id()] = &*blk;
     id2block_[blk->id()] = &*blk;

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

@@ -235,6 +235,12 @@ class InlinePass : public Pass {
   // Move the OpLoopMerge from the last block back to the first.
   // Move the OpLoopMerge from the last block back to the first.
   void MoveLoopMergeInstToFirstBlock(
   void MoveLoopMergeInstToFirstBlock(
       std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
       std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
+
+  // Update the structure of single block loops so that the inlined code ends
+  // up in the loop construct and a new continue target is added to satisfy
+  // structural dominance.
+  void UpdateSingleBlockLoopContinueTarget(
+      uint32_t new_id, std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
 };
 };
 
 
 }  // namespace opt
 }  // namespace opt

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

@@ -693,8 +693,12 @@ NonSemanticShaderDebugInfo100Instructions Instruction::GetShader100DebugOpcode()
     return NonSemanticShaderDebugInfo100InstructionsMax;
     return NonSemanticShaderDebugInfo100InstructionsMax;
   }
   }
 
 
-  return NonSemanticShaderDebugInfo100Instructions(
-      GetSingleWordInOperand(kExtInstInstructionInIdx));
+  uint32_t opcode = GetSingleWordInOperand(kExtInstInstructionInIdx);
+  if (opcode >= NonSemanticShaderDebugInfo100InstructionsMax) {
+    return NonSemanticShaderDebugInfo100InstructionsMax;
+  }
+
+  return NonSemanticShaderDebugInfo100Instructions(opcode);
 }
 }
 
 
 CommonDebugInfoInstructions Instruction::GetCommonDebugOpcode() const {
 CommonDebugInfoInstructions Instruction::GetCommonDebugOpcode() const {

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

@@ -212,8 +212,12 @@ void InterfaceVariableScalarReplacement::KillInstructionAndUsers(
     context()->KillInst(inst);
     context()->KillInst(inst);
     return;
     return;
   }
   }
+  std::vector<Instruction*> users;
   context()->get_def_use_mgr()->ForEachUser(
   context()->get_def_use_mgr()->ForEachUser(
-      inst, [this](Instruction* user) { KillInstructionAndUsers(user); });
+      inst, [&users](Instruction* user) { users.push_back(user); });
+  for (auto user : users) {
+    context()->KillInst(user);
+  }
   context()->KillInst(inst);
   context()->KillInst(inst);
 }
 }
 
 

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

@@ -237,7 +237,8 @@ void LocalAccessChainConvertPass::FindTargetVars(Function* func) {
           }
           }
           // Rule out variables with nested access chains
           // Rule out variables with nested access chains
           // TODO(): Convert nested access chains
           // TODO(): Convert nested access chains
-          if (IsNonPtrAccessChain(op) && ptrInst->GetSingleWordInOperand(
+          bool is_non_ptr_access_chain = IsNonPtrAccessChain(op);
+          if (is_non_ptr_access_chain && ptrInst->GetSingleWordInOperand(
                                              kAccessChainPtrIdInIdx) != varId) {
                                              kAccessChainPtrIdInIdx) != varId) {
             seen_non_target_vars_.insert(varId);
             seen_non_target_vars_.insert(varId);
             seen_target_vars_.erase(varId);
             seen_target_vars_.erase(varId);
@@ -249,6 +250,12 @@ void LocalAccessChainConvertPass::FindTargetVars(Function* func) {
             seen_target_vars_.erase(varId);
             seen_target_vars_.erase(varId);
             break;
             break;
           }
           }
+
+          if (is_non_ptr_access_chain && AnyIndexIsOutOfBounds(ptrInst)) {
+            seen_non_target_vars_.insert(varId);
+            seen_target_vars_.erase(varId);
+            break;
+          }
         } break;
         } break;
         default:
         default:
           break;
           break;
@@ -442,8 +449,46 @@ void LocalAccessChainConvertPass::InitExtensions() {
       "SPV_EXT_shader_image_int64",
       "SPV_EXT_shader_image_int64",
       "SPV_KHR_non_semantic_info",
       "SPV_KHR_non_semantic_info",
       "SPV_KHR_uniform_group_instructions",
       "SPV_KHR_uniform_group_instructions",
+      "SPV_KHR_fragment_shader_barycentric",
   });
   });
 }
 }
 
 
+bool LocalAccessChainConvertPass::AnyIndexIsOutOfBounds(
+    const Instruction* access_chain_inst) {
+  assert(IsNonPtrAccessChain(access_chain_inst->opcode()));
+
+  analysis::TypeManager* type_mgr = context()->get_type_mgr();
+  analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
+  auto constants = const_mgr->GetOperandConstants(access_chain_inst);
+  uint32_t base_pointer_id = access_chain_inst->GetSingleWordInOperand(0);
+  Instruction* base_pointer = get_def_use_mgr()->GetDef(base_pointer_id);
+  const analysis::Pointer* base_pointer_type =
+      type_mgr->GetType(base_pointer->type_id())->AsPointer();
+  assert(base_pointer_type != nullptr &&
+         "The base of the access chain is not a pointer.");
+  const analysis::Type* current_type = base_pointer_type->pointee_type();
+  for (uint32_t i = 1; i < access_chain_inst->NumInOperands(); ++i) {
+    if (IsIndexOutOfBounds(constants[i], current_type)) {
+      return true;
+    }
+
+    uint32_t index =
+        (constants[i]
+             ? static_cast<uint32_t>(constants[i]->GetZeroExtendedValue())
+             : 0);
+    current_type = type_mgr->GetMemberType(current_type, {index});
+  }
+
+  return false;
+}
+
+bool LocalAccessChainConvertPass::IsIndexOutOfBounds(
+    const analysis::Constant* index, const analysis::Type* type) const {
+  if (index == nullptr) {
+    return false;
+  }
+  return index->GetZeroExtendedValue() >= type->NumberOfComponents();
+}
+
 }  // namespace opt
 }  // namespace opt
 }  // namespace spvtools
 }  // namespace spvtools

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

@@ -111,6 +111,17 @@ class LocalAccessChainConvertPass : public MemPass {
   // Returns a status to indicate success or failure, and change or no change.
   // Returns a status to indicate success or failure, and change or no change.
   Status ConvertLocalAccessChains(Function* func);
   Status ConvertLocalAccessChains(Function* func);
 
 
+  // Returns true one of the indexes in the |access_chain_inst| is definitly out
+  // of bounds.  If the size of the type or the value of the index is unknown,
+  // then it will be considered in-bounds.
+  bool AnyIndexIsOutOfBounds(const Instruction* access_chain_inst);
+
+  // Returns true if getting element |index| from |type| would be out-of-bounds.
+  // If |index| is nullptr or the size of the type are unknown, then it will be
+  // considered in-bounds.
+  bool IsIndexOutOfBounds(const analysis::Constant* index,
+                          const analysis::Type* type) const;
+
   // Initialize extensions allowlist
   // Initialize extensions allowlist
   void InitExtensions();
   void InitExtensions();
 
 

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

@@ -287,6 +287,7 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() {
       "SPV_EXT_shader_image_int64",
       "SPV_EXT_shader_image_int64",
       "SPV_KHR_non_semantic_info",
       "SPV_KHR_non_semantic_info",
       "SPV_KHR_uniform_group_instructions",
       "SPV_KHR_uniform_group_instructions",
+      "SPV_KHR_fragment_shader_barycentric",
   });
   });
 }
 }
 
 

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

@@ -140,6 +140,7 @@ void LocalSingleStoreElimPass::InitExtensionAllowList() {
       "SPV_EXT_shader_image_int64",
       "SPV_EXT_shader_image_int64",
       "SPV_KHR_non_semantic_info",
       "SPV_KHR_non_semantic_info",
       "SPV_KHR_uniform_group_instructions",
       "SPV_KHR_uniform_group_instructions",
+      "SPV_KHR_fragment_shader_barycentric",
   });
   });
 }
 }
 bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) {
 bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) {

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

@@ -497,7 +497,8 @@ void Loop::ComputeLoopStructuredOrder(
     // continue blocks that must be copied to retain the structured order.
     // continue blocks that must be copied to retain the structured order.
     // The structured order will include these.
     // The structured order will include these.
     std::list<BasicBlock*> order;
     std::list<BasicBlock*> order;
-    cfg.ComputeStructuredOrder(loop_header_->GetParent(), loop_header_, &order);
+    cfg.ComputeStructuredOrder(loop_header_->GetParent(), loop_header_,
+                               loop_merge_, &order);
     for (BasicBlock* bb : order) {
     for (BasicBlock* bb : order) {
       if (bb == GetMergeBlock()) {
       if (bb == GetMergeBlock()) {
         break;
         break;

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

@@ -384,6 +384,7 @@ void LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop,
   std::unique_ptr<Instruction> new_label{new Instruction(
   std::unique_ptr<Instruction> new_label{new Instruction(
       context_, SpvOp::SpvOpLabel, 0, context_->TakeNextId(), {})};
       context_, SpvOp::SpvOpLabel, 0, context_->TakeNextId(), {})};
   std::unique_ptr<BasicBlock> new_exit_bb{new BasicBlock(std::move(new_label))};
   std::unique_ptr<BasicBlock> new_exit_bb{new BasicBlock(std::move(new_label))};
+  new_exit_bb->SetParent(&function_);
 
 
   // Save the id of the block before we move it.
   // Save the id of the block before we move it.
   uint32_t new_merge_id = new_exit_bb->id();
   uint32_t new_merge_id = new_exit_bb->id();
@@ -996,6 +997,20 @@ bool LoopUtils::CanPerformUnroll() {
   if (!loop_->FindNumberOfIterations(induction, &*condition->ctail(), nullptr))
   if (!loop_->FindNumberOfIterations(induction, &*condition->ctail(), nullptr))
     return false;
     return false;
 
 
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+  // ClusterFuzz/OSS-Fuzz is likely to yield examples with very high loop
+  // iteration counts. This can cause timeouts and memouts during fuzzing that
+  // are not classed as bugs. To avoid this noise, loop unrolling is not applied
+  // to loops with large iteration counts when fuzzing.
+  const size_t kFuzzerIterationLimit = 100;
+  size_t num_iterations;
+  loop_->FindNumberOfIterations(induction, &*condition->ctail(),
+                                &num_iterations);
+  if (num_iterations > kFuzzerIterationLimit) {
+    return false;
+  }
+#endif
+
   // Make sure the latch block is a unconditional branch to the header
   // Make sure the latch block is a unconditional branch to the header
   // block.
   // block.
   const Instruction& branch = *loop_->GetLatchBlock()->ctail();
   const Instruction& branch = *loop_->GetLatchBlock()->ctail();

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

@@ -623,10 +623,16 @@ bool Optimizer::Run(const uint32_t* original_binary,
     assert(optimized_binary_with_nop.size() == original_binary_size &&
     assert(optimized_binary_with_nop.size() == original_binary_size &&
            "Binary size unexpectedly changed despite the optimizer saying "
            "Binary size unexpectedly changed despite the optimizer saying "
            "there was no change");
            "there was no change");
-    assert(memcmp(optimized_binary_with_nop.data(), original_binary,
-                  original_binary_size) == 0 &&
-           "Binary content unexpectedly changed despite the optimizer saying "
-           "there was no change");
+
+    // Compare the magic number to make sure the binaries were encoded in the
+    // endianness.  If not, the contents of the binaries will be different, so
+    // do not check the contents.
+    if (optimized_binary_with_nop[0] == original_binary[0]) {
+      assert(memcmp(optimized_binary_with_nop.data(), original_binary,
+                    original_binary_size) == 0 &&
+             "Binary content unexpectedly changed despite the optimizer saying "
+             "there was no change");
+    }
   }
   }
 #endif  // !NDEBUG
 #endif  // !NDEBUG
 
 

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

@@ -161,8 +161,15 @@ bool ReduceLoadSize::ShouldReplaceExtract(Instruction* inst) {
       case analysis::Type::kArray: {
       case analysis::Type::kArray: {
         const analysis::Constant* size_const =
         const analysis::Constant* size_const =
             const_mgr->FindDeclaredConstant(load_type->AsArray()->LengthId());
             const_mgr->FindDeclaredConstant(load_type->AsArray()->LengthId());
-        assert(size_const->AsIntConstant());
-        total_size = size_const->GetU32();
+
+        if (size_const) {
+          assert(size_const->AsIntConstant());
+          total_size = size_const->GetU32();
+        } else {
+          // The size is spec constant, so it is unknown at this time.  Assume
+          // it is very large.
+          total_size = UINT32_MAX;
+        }
       } break;
       } break;
       case analysis::Type::kStruct:
       case analysis::Type::kStruct:
         total_size = static_cast<uint32_t>(
         total_size = static_cast<uint32_t>(

+ 19 - 4
3rdparty/spirv-tools/source/opt/scalar_replacement_pass.h

@@ -15,6 +15,7 @@
 #ifndef SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_
 #ifndef SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_
 #define SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_
 #define SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_
 
 
+#include <cassert>
 #include <cstdio>
 #include <cstdio>
 #include <memory>
 #include <memory>
 #include <queue>
 #include <queue>
@@ -37,9 +38,20 @@ class ScalarReplacementPass : public MemPass {
  public:
  public:
   ScalarReplacementPass(uint32_t limit = kDefaultLimit)
   ScalarReplacementPass(uint32_t limit = kDefaultLimit)
       : max_num_elements_(limit) {
       : max_num_elements_(limit) {
-    name_[0] = '\0';
-    strcat(name_, "scalar-replacement=");
-    sprintf(&name_[strlen(name_)], "%d", max_num_elements_);
+    const auto num_to_write = snprintf(
+        name_, sizeof(name_), "scalar-replacement=%u", max_num_elements_);
+    assert(size_t(num_to_write) < sizeof(name_));
+    (void)num_to_write;  // Mark as unused
+
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+    // ClusterFuzz/OSS-Fuzz is likely to yield examples with very large arrays.
+    // This can cause timeouts and memouts during fuzzing that
+    // are not classed as bugs. To avoid this noise, we set the
+    // max_num_elements_ to a smaller value for fuzzing.
+    max_num_elements_ =
+        (max_num_elements_ > 0 && max_num_elements_ < 100 ? max_num_elements_
+                                                          : 100);
+#endif
   }
   }
 
 
   const char* name() const override { return name_; }
   const char* name() const override { return name_; }
@@ -253,7 +265,10 @@ class ScalarReplacementPass : public MemPass {
   // Limit on the number of members in an object that will be replaced.
   // Limit on the number of members in an object that will be replaced.
   // 0 means there is no limit.
   // 0 means there is no limit.
   uint32_t max_num_elements_;
   uint32_t max_num_elements_;
-  char name_[55];
+  // This has to be big enough to fit "scalar-replacement=" followed by a
+  // uint32_t number written in decimal (so 10 digits), and then a
+  // terminating nul.
+  char name_[30];
 };
 };
 
 
 }  // namespace opt
 }  // namespace opt

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

@@ -16,6 +16,7 @@
 
 
 #include <algorithm>
 #include <algorithm>
 #include <cassert>
 #include <cassert>
+#include <climits>
 #include <cstdint>
 #include <cstdint>
 #include <sstream>
 #include <sstream>
 #include <string>
 #include <string>
@@ -246,6 +247,35 @@ size_t Type::HashValue() const {
   return ComputeHashValue(0, &seen);
   return ComputeHashValue(0, &seen);
 }
 }
 
 
+uint64_t Type::NumberOfComponents() const {
+  switch (kind()) {
+    case kVector:
+      return AsVector()->element_count();
+    case kMatrix:
+      return AsMatrix()->element_count();
+    case kArray: {
+      Array::LengthInfo length_info = AsArray()->length_info();
+      if (length_info.words[0] != Array::LengthInfo::kConstant) {
+        return UINT64_MAX;
+      }
+      assert(length_info.words.size() <= 3 &&
+             "The size of the array could not fit size_t.");
+      uint64_t length = 0;
+      length |= length_info.words[1];
+      if (length_info.words.size() > 2) {
+        length |= static_cast<uint64_t>(length_info.words[2]) << 32;
+      }
+      return length;
+    }
+    case kRuntimeArray:
+      return UINT64_MAX;
+    case kStruct:
+      return AsStruct()->element_types().size();
+    default:
+      return 0;
+  }
+}
+
 bool Integer::IsSameImpl(const Type* that, IsSameCache*) const {
 bool Integer::IsSameImpl(const Type* that, IsSameCache*) const {
   const Integer* it = that->AsInteger();
   const Integer* it = that->AsInteger();
   return it && width_ == it->width_ && signed_ == it->signed_ &&
   return it && width_ == it->width_ && signed_ == it->signed_ &&

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

@@ -160,6 +160,10 @@ class Type {
 
 
   size_t ComputeHashValue(size_t hash, SeenTypes* seen) const;
   size_t ComputeHashValue(size_t hash, SeenTypes* seen) const;
 
 
+  // Returns the number of components in a composite type.  Returns 0 for a
+  // non-composite type.
+  uint64_t NumberOfComponents() const;
+
 // A bunch of methods for casting this type to a given type. Returns this if the
 // A bunch of methods for casting this type to a given type. Returns this if the
 // cast can be done, nullptr otherwise.
 // cast can be done, nullptr otherwise.
 // clang-format off
 // clang-format off

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

@@ -24,11 +24,13 @@ namespace val {
 BasicBlock::BasicBlock(uint32_t label_id)
 BasicBlock::BasicBlock(uint32_t label_id)
     : id_(label_id),
     : id_(label_id),
       immediate_dominator_(nullptr),
       immediate_dominator_(nullptr),
-      immediate_post_dominator_(nullptr),
+      immediate_structural_dominator_(nullptr),
+      immediate_structural_post_dominator_(nullptr),
       predecessors_(),
       predecessors_(),
       successors_(),
       successors_(),
       type_(0),
       type_(0),
       reachable_(false),
       reachable_(false),
+      structurally_reachable_(false),
       label_(nullptr),
       label_(nullptr),
       terminator_(nullptr) {}
       terminator_(nullptr) {}
 
 
@@ -36,21 +38,32 @@ void BasicBlock::SetImmediateDominator(BasicBlock* dom_block) {
   immediate_dominator_ = dom_block;
   immediate_dominator_ = dom_block;
 }
 }
 
 
-void BasicBlock::SetImmediatePostDominator(BasicBlock* pdom_block) {
-  immediate_post_dominator_ = pdom_block;
+void BasicBlock::SetImmediateStructuralDominator(BasicBlock* dom_block) {
+  immediate_structural_dominator_ = dom_block;
+}
+
+void BasicBlock::SetImmediateStructuralPostDominator(BasicBlock* pdom_block) {
+  immediate_structural_post_dominator_ = pdom_block;
 }
 }
 
 
 const BasicBlock* BasicBlock::immediate_dominator() const {
 const BasicBlock* BasicBlock::immediate_dominator() const {
   return immediate_dominator_;
   return immediate_dominator_;
 }
 }
 
 
-const BasicBlock* BasicBlock::immediate_post_dominator() const {
-  return immediate_post_dominator_;
+const BasicBlock* BasicBlock::immediate_structural_dominator() const {
+  return immediate_structural_dominator_;
+}
+
+const BasicBlock* BasicBlock::immediate_structural_post_dominator() const {
+  return immediate_structural_post_dominator_;
 }
 }
 
 
 BasicBlock* BasicBlock::immediate_dominator() { return immediate_dominator_; }
 BasicBlock* BasicBlock::immediate_dominator() { return immediate_dominator_; }
-BasicBlock* BasicBlock::immediate_post_dominator() {
-  return immediate_post_dominator_;
+BasicBlock* BasicBlock::immediate_structural_dominator() {
+  return immediate_structural_dominator_;
+}
+BasicBlock* BasicBlock::immediate_structural_post_dominator() {
+  return immediate_structural_post_dominator_;
 }
 }
 
 
 void BasicBlock::RegisterSuccessors(
 void BasicBlock::RegisterSuccessors(
@@ -58,6 +71,10 @@ void BasicBlock::RegisterSuccessors(
   for (auto& block : next_blocks) {
   for (auto& block : next_blocks) {
     block->predecessors_.push_back(this);
     block->predecessors_.push_back(this);
     successors_.push_back(block);
     successors_.push_back(block);
+
+    // Register structural successors/predecessors too.
+    block->structural_predecessors_.push_back(this);
+    structural_successors_.push_back(block);
   }
   }
 }
 }
 
 
@@ -67,10 +84,16 @@ bool BasicBlock::dominates(const BasicBlock& other) const {
            std::find(other.dom_begin(), other.dom_end(), this));
            std::find(other.dom_begin(), other.dom_end(), this));
 }
 }
 
 
-bool BasicBlock::postdominates(const BasicBlock& other) const {
-  return (this == &other) ||
-         !(other.pdom_end() ==
-           std::find(other.pdom_begin(), other.pdom_end(), this));
+bool BasicBlock::structurally_dominates(const BasicBlock& other) const {
+  return (this == &other) || !(other.structural_dom_end() ==
+                               std::find(other.structural_dom_begin(),
+                                         other.structural_dom_end(), this));
+}
+
+bool BasicBlock::structurally_postdominates(const BasicBlock& other) const {
+  return (this == &other) || !(other.structural_pdom_end() ==
+                               std::find(other.structural_pdom_begin(),
+                                         other.structural_pdom_end(), this));
 }
 }
 
 
 BasicBlock::DominatorIterator::DominatorIterator() : current_(nullptr) {}
 BasicBlock::DominatorIterator::DominatorIterator() : current_(nullptr) {}
@@ -107,21 +130,43 @@ BasicBlock::DominatorIterator BasicBlock::dom_end() {
   return DominatorIterator();
   return DominatorIterator();
 }
 }
 
 
-const BasicBlock::DominatorIterator BasicBlock::pdom_begin() const {
-  return DominatorIterator(
-      this, [](const BasicBlock* b) { return b->immediate_post_dominator(); });
+const BasicBlock::DominatorIterator BasicBlock::structural_dom_begin() const {
+  return DominatorIterator(this, [](const BasicBlock* b) {
+    return b->immediate_structural_dominator();
+  });
 }
 }
 
 
-BasicBlock::DominatorIterator BasicBlock::pdom_begin() {
-  return DominatorIterator(
-      this, [](const BasicBlock* b) { return b->immediate_post_dominator(); });
+BasicBlock::DominatorIterator BasicBlock::structural_dom_begin() {
+  return DominatorIterator(this, [](const BasicBlock* b) {
+    return b->immediate_structural_dominator();
+  });
+}
+
+const BasicBlock::DominatorIterator BasicBlock::structural_dom_end() const {
+  return DominatorIterator();
+}
+
+BasicBlock::DominatorIterator BasicBlock::structural_dom_end() {
+  return DominatorIterator();
+}
+
+const BasicBlock::DominatorIterator BasicBlock::structural_pdom_begin() const {
+  return DominatorIterator(this, [](const BasicBlock* b) {
+    return b->immediate_structural_post_dominator();
+  });
+}
+
+BasicBlock::DominatorIterator BasicBlock::structural_pdom_begin() {
+  return DominatorIterator(this, [](const BasicBlock* b) {
+    return b->immediate_structural_post_dominator();
+  });
 }
 }
 
 
-const BasicBlock::DominatorIterator BasicBlock::pdom_end() const {
+const BasicBlock::DominatorIterator BasicBlock::structural_pdom_end() const {
   return DominatorIterator();
   return DominatorIterator();
 }
 }
 
 
-BasicBlock::DominatorIterator BasicBlock::pdom_end() {
+BasicBlock::DominatorIterator BasicBlock::structural_pdom_end() {
   return DominatorIterator();
   return DominatorIterator();
 }
 }
 
 

+ 84 - 13
3rdparty/spirv-tools/source/val/basic_block.h

@@ -64,9 +64,32 @@ class BasicBlock {
   /// Returns the successors of the BasicBlock
   /// Returns the successors of the BasicBlock
   std::vector<BasicBlock*>* successors() { return &successors_; }
   std::vector<BasicBlock*>* successors() { return &successors_; }
 
 
-  /// Returns true if the block is reachable in the CFG
+  /// Returns the structural successors of the BasicBlock
+  std::vector<BasicBlock*>* structural_predecessors() {
+    return &structural_predecessors_;
+  }
+
+  /// Returns the structural predecessors of the BasicBlock
+  const std::vector<BasicBlock*>* structural_predecessors() const {
+    return &structural_predecessors_;
+  }
+
+  /// Returns the structural successors of the BasicBlock
+  std::vector<BasicBlock*>* structural_successors() {
+    return &structural_successors_;
+  }
+
+  /// Returns the structural predecessors of the BasicBlock
+  const std::vector<BasicBlock*>* structural_successors() const {
+    return &structural_successors_;
+  }
+
+  /// Returns true if the block is reachable in the CFG.
   bool reachable() const { return reachable_; }
   bool reachable() const { return reachable_; }
 
 
+  /// Returns true if the block is structurally reachable in the CFG.
+  bool structurally_reachable() const { return structurally_reachable_; }
+
   /// Returns true if BasicBlock is of the given type
   /// Returns true if BasicBlock is of the given type
   bool is_type(BlockType type) const {
   bool is_type(BlockType type) const {
     if (type == kBlockTypeUndefined) return type_.none();
     if (type == kBlockTypeUndefined) return type_.none();
@@ -76,6 +99,11 @@ class BasicBlock {
   /// Sets the reachability of the basic block in the CFG
   /// Sets the reachability of the basic block in the CFG
   void set_reachable(bool reachability) { reachable_ = reachability; }
   void set_reachable(bool reachability) { reachable_ = reachability; }
 
 
+  /// Sets the structural reachability of the basic block in the CFG
+  void set_structurally_reachable(bool reachability) {
+    structurally_reachable_ = reachability;
+  }
+
   /// Sets the type of the BasicBlock
   /// Sets the type of the BasicBlock
   void set_type(BlockType type) {
   void set_type(BlockType type) {
     if (type == kBlockTypeUndefined)
     if (type == kBlockTypeUndefined)
@@ -89,10 +117,15 @@ class BasicBlock {
   /// @param[in] dom_block The dominator block
   /// @param[in] dom_block The dominator block
   void SetImmediateDominator(BasicBlock* dom_block);
   void SetImmediateDominator(BasicBlock* dom_block);
 
 
+  /// Sets the immediate dominator of this basic block
+  ///
+  /// @param[in] dom_block The dominator block
+  void SetImmediateStructuralDominator(BasicBlock* dom_block);
+
   /// Sets the immediate post dominator of this basic block
   /// Sets the immediate post dominator of this basic block
   ///
   ///
   /// @param[in] pdom_block The post dominator block
   /// @param[in] pdom_block The post dominator block
-  void SetImmediatePostDominator(BasicBlock* pdom_block);
+  void SetImmediateStructuralPostDominator(BasicBlock* pdom_block);
 
 
   /// Returns the immediate dominator of this basic block
   /// Returns the immediate dominator of this basic block
   BasicBlock* immediate_dominator();
   BasicBlock* immediate_dominator();
@@ -100,11 +133,17 @@ class BasicBlock {
   /// Returns the immediate dominator of this basic block
   /// Returns the immediate dominator of this basic block
   const BasicBlock* immediate_dominator() const;
   const BasicBlock* immediate_dominator() const;
 
 
+  /// Returns the immediate dominator of this basic block
+  BasicBlock* immediate_structural_dominator();
+
+  /// Returns the immediate dominator of this basic block
+  const BasicBlock* immediate_structural_dominator() const;
+
   /// Returns the immediate post dominator of this basic block
   /// Returns the immediate post dominator of this basic block
-  BasicBlock* immediate_post_dominator();
+  BasicBlock* immediate_structural_post_dominator();
 
 
   /// Returns the immediate post dominator of this basic block
   /// Returns the immediate post dominator of this basic block
-  const BasicBlock* immediate_post_dominator() const;
+  const BasicBlock* immediate_structural_post_dominator() const;
 
 
   /// Returns the label instruction for the block, or nullptr if not set.
   /// Returns the label instruction for the block, or nullptr if not set.
   const Instruction* label() const { return label_; }
   const Instruction* label() const { return label_; }
@@ -132,9 +171,18 @@ class BasicBlock {
   /// Assumes dominators have been computed.
   /// Assumes dominators have been computed.
   bool dominates(const BasicBlock& other) const;
   bool dominates(const BasicBlock& other) const;
 
 
-  /// Returns true if this block postdominates the other block.
-  /// Assumes dominators have been computed.
-  bool postdominates(const BasicBlock& other) const;
+  /// Returns true if this block structurally dominates the other block.
+  /// Assumes structural dominators have been computed.
+  bool structurally_dominates(const BasicBlock& other) const;
+
+  /// Returns true if this block structurally postdominates the other block.
+  /// Assumes structural dominators have been computed.
+  bool structurally_postdominates(const BasicBlock& other) const;
+
+  void RegisterStructuralSuccessor(BasicBlock* block) {
+    block->structural_predecessors_.push_back(this);
+    structural_successors_.push_back(block);
+  }
 
 
   /// @brief A BasicBlock dominator iterator class
   /// @brief A BasicBlock dominator iterator class
   ///
   ///
@@ -191,18 +239,32 @@ class BasicBlock {
   /// block
   /// block
   DominatorIterator dom_end();
   DominatorIterator dom_end();
 
 
+  /// Returns a dominator iterator which points to the current block
+  const DominatorIterator structural_dom_begin() const;
+
+  /// Returns a dominator iterator which points to the current block
+  DominatorIterator structural_dom_begin();
+
+  /// Returns a dominator iterator which points to one element past the first
+  /// block
+  const DominatorIterator structural_dom_end() const;
+
+  /// Returns a dominator iterator which points to one element past the first
+  /// block
+  DominatorIterator structural_dom_end();
+
   /// Returns a post dominator iterator which points to the current block
   /// Returns a post dominator iterator which points to the current block
-  const DominatorIterator pdom_begin() const;
+  const DominatorIterator structural_pdom_begin() const;
   /// Returns a post dominator iterator which points to the current block
   /// Returns a post dominator iterator which points to the current block
-  DominatorIterator pdom_begin();
+  DominatorIterator structural_pdom_begin();
 
 
   /// Returns a post dominator iterator which points to one element past the
   /// Returns a post dominator iterator which points to one element past the
   /// last block
   /// last block
-  const DominatorIterator pdom_end() const;
+  const DominatorIterator structural_pdom_end() const;
 
 
   /// Returns a post dominator iterator which points to one element past the
   /// Returns a post dominator iterator which points to one element past the
   /// last block
   /// last block
-  DominatorIterator pdom_end();
+  DominatorIterator structural_pdom_end();
 
 
  private:
  private:
   /// Id of the BasicBlock
   /// Id of the BasicBlock
@@ -211,8 +273,11 @@ class BasicBlock {
   /// Pointer to the immediate dominator of the BasicBlock
   /// Pointer to the immediate dominator of the BasicBlock
   BasicBlock* immediate_dominator_;
   BasicBlock* immediate_dominator_;
 
 
-  /// Pointer to the immediate dominator of the BasicBlock
-  BasicBlock* immediate_post_dominator_;
+  /// Pointer to the immediate structural dominator of the BasicBlock
+  BasicBlock* immediate_structural_dominator_;
+
+  /// Pointer to the immediate structural post dominator of the BasicBlock
+  BasicBlock* immediate_structural_post_dominator_;
 
 
   /// The set of predecessors of the BasicBlock
   /// The set of predecessors of the BasicBlock
   std::vector<BasicBlock*> predecessors_;
   std::vector<BasicBlock*> predecessors_;
@@ -226,11 +291,17 @@ class BasicBlock {
   /// True if the block is reachable in the CFG
   /// True if the block is reachable in the CFG
   bool reachable_;
   bool reachable_;
 
 
+  /// True if the block is structurally reachable in the CFG
+  bool structurally_reachable_;
+
   /// label of this block, if any.
   /// label of this block, if any.
   const Instruction* label_;
   const Instruction* label_;
 
 
   /// Terminator of this block.
   /// Terminator of this block.
   const Instruction* terminator_;
   const Instruction* terminator_;
+
+  std::vector<BasicBlock*> structural_predecessors_;
+  std::vector<BasicBlock*> structural_successors_;
 };
 };
 
 
 /// @brief Returns true if the iterators point to the same element or if both
 /// @brief Returns true if the iterators point to the same element or if both

+ 36 - 50
3rdparty/spirv-tools/source/val/construct.cpp

@@ -70,60 +70,45 @@ BasicBlock* Construct::exit_block() { return exit_block_; }
 
 
 void Construct::set_exit(BasicBlock* block) { exit_block_ = block; }
 void Construct::set_exit(BasicBlock* block) { exit_block_ = block; }
 
 
-Construct::ConstructBlockSet Construct::blocks(Function* function) const {
-  auto header = entry_block();
-  auto merge = exit_block();
-  assert(header);
-  int header_depth = function->GetBlockDepth(const_cast<BasicBlock*>(header));
-  ConstructBlockSet construct_blocks;
-  std::unordered_set<BasicBlock*> corresponding_headers;
-  for (auto& other : corresponding_constructs()) {
-    // The corresponding header can be the same block as this construct's
-    // header for loops with no loop construct. In those cases, don't add the
-    // loop header as it prevents finding any blocks in the construct.
-    if (type() != ConstructType::kContinue || other->entry_block() != header) {
-      corresponding_headers.insert(other->entry_block());
-    }
+Construct::ConstructBlockSet Construct::blocks(Function* /*function*/) const {
+  const auto header = entry_block();
+  const auto exit = exit_block();
+  const bool is_continue = type() == ConstructType::kContinue;
+  const bool is_loop = type() == ConstructType::kLoop;
+  const BasicBlock* continue_header = nullptr;
+  if (is_loop) {
+    // The only corresponding construct for a loop is the continue.
+    continue_header = (*corresponding_constructs().begin())->entry_block();
   }
   }
   std::vector<BasicBlock*> stack;
   std::vector<BasicBlock*> stack;
   stack.push_back(const_cast<BasicBlock*>(header));
   stack.push_back(const_cast<BasicBlock*>(header));
+  ConstructBlockSet construct_blocks;
   while (!stack.empty()) {
   while (!stack.empty()) {
-    BasicBlock* block = stack.back();
+    auto* block = stack.back();
     stack.pop_back();
     stack.pop_back();
 
 
-    if (merge == block && ExitBlockIsMergeBlock()) {
-      // Merge block is not part of the construct.
-      continue;
-    }
-
-    if (corresponding_headers.count(block)) {
-      // Entered a corresponding construct.
-      continue;
-    }
-
-    int block_depth = function->GetBlockDepth(block);
-    if (block_depth < header_depth) {
-      // Broke to outer construct.
-      continue;
-    }
-
-    // In a loop, the continue target is at a depth of the loop construct + 1.
-    // A selection construct nested directly within the loop construct is also
-    // at the same depth. It is valid, however, to branch directly to the
-    // continue target from within the selection construct.
-    if (block != header && block_depth == header_depth &&
-        type() == ConstructType::kSelection &&
-        block->is_type(kBlockTypeContinue)) {
-      // Continued to outer construct.
-      continue;
-    }
-
-    if (!construct_blocks.insert(block).second) continue;
+    if (header->structurally_dominates(*block)) {
+      bool include = false;
+      if (is_continue && exit->structurally_postdominates(*block)) {
+        // Continue construct include blocks dominated by the continue target
+        // and post-dominated by the back-edge block.
+        include = true;
+      } else if (!exit->structurally_dominates(*block)) {
+        // Selection and loop constructs include blocks dominated by the header
+        // and not dominated by the merge.
+        include = true;
+        if (is_loop && continue_header->structurally_dominates(*block)) {
+          // Loop constructs have an additional constraint that they do not
+          // include blocks dominated by the continue construct. Since all
+          // blocks in the continue construct are dominated by the continue
+          // target, we just test for dominance by continue target.
+          include = false;
+        }
+      }
+      if (include) {
+        if (!construct_blocks.insert(block).second) continue;
 
 
-    if (merge != block) {
-      for (auto succ : *block->successors()) {
-        // All blocks in the construct must be dominated by the header.
-        if (header->dominates(*succ)) {
+        for (auto succ : *block->structural_successors()) {
           stack.push_back(succ);
           stack.push_back(succ);
         }
         }
       }
       }
@@ -181,11 +166,12 @@ bool Construct::IsStructuredExit(ValidationState_t& _, BasicBlock* dest) const {
       for (auto& use : block->label()->uses()) {
       for (auto& use : block->label()->uses()) {
         if ((use.first->opcode() == SpvOpLoopMerge ||
         if ((use.first->opcode() == SpvOpLoopMerge ||
              use.first->opcode() == SpvOpSelectionMerge) &&
              use.first->opcode() == SpvOpSelectionMerge) &&
-            use.second == 1 && use.first->block()->dominates(*block)) {
+            use.second == 1 &&
+            use.first->block()->structurally_dominates(*block)) {
           return use.first->block();
           return use.first->block();
         }
         }
       }
       }
-      return block->immediate_dominator();
+      return block->immediate_structural_dominator();
     };
     };
 
 
     bool seen_switch = false;
     bool seen_switch = false;
@@ -201,7 +187,7 @@ bool Construct::IsStructuredExit(ValidationState_t& _, BasicBlock* dest) const {
            terminator->opcode() == SpvOpSwitch)) {
            terminator->opcode() == SpvOpSwitch)) {
         auto merge_target = merge_inst->GetOperandAs<uint32_t>(0u);
         auto merge_target = merge_inst->GetOperandAs<uint32_t>(0u);
         auto merge_block = merge_inst->function()->GetBlock(merge_target).first;
         auto merge_block = merge_inst->function()->GetBlock(merge_target).first;
-        if (merge_block->dominates(*header)) {
+        if (merge_block->structurally_dominates(*header)) {
           block = NextBlock(block);
           block = NextBlock(block);
           continue;
           continue;
         }
         }

+ 9 - 0
3rdparty/spirv-tools/source/val/decoration.h

@@ -69,6 +69,15 @@ class Decoration {
   std::vector<uint32_t>& params() { return params_; }
   std::vector<uint32_t>& params() { return params_; }
   const std::vector<uint32_t>& params() const { return params_; }
   const std::vector<uint32_t>& params() const { return params_; }
 
 
+  inline bool operator<(const Decoration& rhs) const {
+    // Note: Sort by struct_member_index_ first, then type, so look up can be
+    // efficient using lower_bound() and upper_bound().
+    if (struct_member_index_ < rhs.struct_member_index_) return true;
+    if (rhs.struct_member_index_ < struct_member_index_) return false;
+    if (dec_type_ < rhs.dec_type_) return true;
+    if (rhs.dec_type_ < dec_type_) return false;
+    return params_ < rhs.params_;
+  }
   inline bool operator==(const Decoration& rhs) const {
   inline bool operator==(const Decoration& rhs) const {
     return (dec_type_ == rhs.dec_type_ && params_ == rhs.params_ &&
     return (dec_type_ == rhs.dec_type_ && params_ == rhs.params_ &&
             struct_member_index_ == rhs.struct_member_index_);
             struct_member_index_ == rhs.struct_member_index_);

+ 27 - 10
3rdparty/spirv-tools/source/val/function.cpp

@@ -73,6 +73,8 @@ spv_result_t Function::RegisterLoopMerge(uint32_t merge_id,
   BasicBlock& continue_target_block = blocks_.at(continue_id);
   BasicBlock& continue_target_block = blocks_.at(continue_id);
   assert(current_block_ &&
   assert(current_block_ &&
          "RegisterLoopMerge must be called when called within a block");
          "RegisterLoopMerge must be called when called within a block");
+  current_block_->RegisterStructuralSuccessor(&merge_block);
+  current_block_->RegisterStructuralSuccessor(&continue_target_block);
 
 
   current_block_->set_type(kBlockTypeLoop);
   current_block_->set_type(kBlockTypeLoop);
   merge_block.set_type(kBlockTypeMerge);
   merge_block.set_type(kBlockTypeMerge);
@@ -101,6 +103,7 @@ spv_result_t Function::RegisterSelectionMerge(uint32_t merge_id) {
   current_block_->set_type(kBlockTypeSelection);
   current_block_->set_type(kBlockTypeSelection);
   merge_block.set_type(kBlockTypeMerge);
   merge_block.set_type(kBlockTypeMerge);
   merge_block_header_[&merge_block] = current_block_;
   merge_block_header_[&merge_block] = current_block_;
+  current_block_->RegisterStructuralSuccessor(&merge_block);
 
 
   AddConstruct({ConstructType::kSelection, current_block(), &merge_block});
   AddConstruct({ConstructType::kSelection, current_block(), &merge_block});
 
 
@@ -251,29 +254,43 @@ Function::GetBlocksFunction Function::AugmentedCFGSuccessorsFunction() const {
   };
   };
 }
 }
 
 
-Function::GetBlocksFunction
-Function::AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge() const {
+Function::GetBlocksFunction Function::AugmentedCFGPredecessorsFunction() const {
+  return [this](const BasicBlock* block) {
+    auto where = augmented_predecessors_map_.find(block);
+    return where == augmented_predecessors_map_.end() ? block->predecessors()
+                                                      : &(*where).second;
+  };
+}
+
+Function::GetBlocksFunction Function::AugmentedStructuralCFGSuccessorsFunction()
+    const {
   return [this](const BasicBlock* block) {
   return [this](const BasicBlock* block) {
-    auto where = loop_header_successors_plus_continue_target_map_.find(block);
-    return where == loop_header_successors_plus_continue_target_map_.end()
-               ? AugmentedCFGSuccessorsFunction()(block)
+    auto where = augmented_successors_map_.find(block);
+    return where == augmented_successors_map_.end()
+               ? block->structural_successors()
                : &(*where).second;
                : &(*where).second;
   };
   };
 }
 }
 
 
-Function::GetBlocksFunction Function::AugmentedCFGPredecessorsFunction() const {
+Function::GetBlocksFunction
+Function::AugmentedStructuralCFGPredecessorsFunction() const {
   return [this](const BasicBlock* block) {
   return [this](const BasicBlock* block) {
     auto where = augmented_predecessors_map_.find(block);
     auto where = augmented_predecessors_map_.find(block);
-    return where == augmented_predecessors_map_.end() ? block->predecessors()
-                                                      : &(*where).second;
+    return where == augmented_predecessors_map_.end()
+               ? block->structural_predecessors()
+               : &(*where).second;
   };
   };
 }
 }
 
 
 void Function::ComputeAugmentedCFG() {
 void Function::ComputeAugmentedCFG() {
   // Compute the successors of the pseudo-entry block, and
   // Compute the successors of the pseudo-entry block, and
   // the predecessors of the pseudo exit block.
   // the predecessors of the pseudo exit block.
-  auto succ_func = [](const BasicBlock* b) { return b->successors(); };
-  auto pred_func = [](const BasicBlock* b) { return b->predecessors(); };
+  auto succ_func = [](const BasicBlock* b) {
+    return b->structural_successors();
+  };
+  auto pred_func = [](const BasicBlock* b) {
+    return b->structural_predecessors();
+  };
   CFA<BasicBlock>::ComputeAugmentedCFG(
   CFA<BasicBlock>::ComputeAugmentedCFG(
       ordered_blocks_, &pseudo_entry_block_, &pseudo_exit_block_,
       ordered_blocks_, &pseudo_entry_block_, &pseudo_exit_block_,
       &augmented_successors_map_, &augmented_predecessors_map_, succ_func,
       &augmented_successors_map_, &augmented_predecessors_map_, succ_func,

+ 4 - 4
3rdparty/spirv-tools/source/val/function.h

@@ -184,12 +184,12 @@ class Function {
       std::function<const std::vector<BasicBlock*>*(const BasicBlock*)>;
       std::function<const std::vector<BasicBlock*>*(const BasicBlock*)>;
   /// Returns the block successors function for the augmented CFG.
   /// Returns the block successors function for the augmented CFG.
   GetBlocksFunction AugmentedCFGSuccessorsFunction() const;
   GetBlocksFunction AugmentedCFGSuccessorsFunction() const;
-  /// Like AugmentedCFGSuccessorsFunction, but also includes a forward edge from
-  /// a loop header block to its continue target, if they are different blocks.
-  GetBlocksFunction
-  AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge() const;
   /// Returns the block predecessors function for the augmented CFG.
   /// Returns the block predecessors function for the augmented CFG.
   GetBlocksFunction AugmentedCFGPredecessorsFunction() const;
   GetBlocksFunction AugmentedCFGPredecessorsFunction() const;
+  /// Returns the block structural successors function for the augmented CFG.
+  GetBlocksFunction AugmentedStructuralCFGSuccessorsFunction() const;
+  /// Returns the block structural predecessors function for the augmented CFG.
+  GetBlocksFunction AugmentedStructuralCFGPredecessorsFunction() const;
 
 
   /// Returns the control flow nesting depth of the given basic block.
   /// Returns the control flow nesting depth of the given basic block.
   /// This function only works when you have structured control flow.
   /// This function only works when you have structured control flow.

+ 9 - 4
3rdparty/spirv-tools/source/val/validate_annotation.cpp

@@ -136,8 +136,8 @@ std::string LogStringForDecoration(uint32_t decoration) {
       return "PerViewNV";
       return "PerViewNV";
     case SpvDecorationPerTaskNV:
     case SpvDecorationPerTaskNV:
       return "PerTaskNV";
       return "PerTaskNV";
-    case SpvDecorationPerVertexNV:
-      return "PerVertexNV";
+    case SpvDecorationPerVertexKHR:
+      return "PerVertexKHR";
     case SpvDecorationNonUniform:
     case SpvDecorationNonUniform:
       return "NonUniform";
       return "NonUniform";
     case SpvDecorationRestrictPointer:
     case SpvDecorationRestrictPointer:
@@ -366,6 +366,11 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec,
           return fail(4670) << "storage class must be Input or Output";
           return fail(4670) << "storage class must be Input or Output";
         }
         }
         break;
         break;
+      case SpvDecorationPerVertexKHR:
+        if (sc != SpvStorageClassInput) {
+          return fail(6777) << "storage class must be Input";
+        }
+        break;
       default:
       default:
         break;
         break;
     }
     }
@@ -576,7 +581,7 @@ spv_result_t RegisterDecorations(ValidationState_t& _,
       // Word 1 is the group <id>. All subsequent words are target <id>s that
       // Word 1 is the group <id>. All subsequent words are target <id>s that
       // are going to be decorated with the decorations.
       // are going to be decorated with the decorations.
       const uint32_t decoration_group_id = inst->word(1);
       const uint32_t decoration_group_id = inst->word(1);
-      std::vector<Decoration>& group_decorations =
+      std::set<Decoration>& group_decorations =
           _.id_decorations(decoration_group_id);
           _.id_decorations(decoration_group_id);
       for (size_t i = 2; i < inst->words().size(); ++i) {
       for (size_t i = 2; i < inst->words().size(); ++i) {
         const uint32_t target_id = inst->word(i);
         const uint32_t target_id = inst->word(i);
@@ -590,7 +595,7 @@ spv_result_t RegisterDecorations(ValidationState_t& _,
       // pairs. All decorations of the group should be applied to all the struct
       // pairs. All decorations of the group should be applied to all the struct
       // members that are specified in the instructions.
       // members that are specified in the instructions.
       const uint32_t decoration_group_id = inst->word(1);
       const uint32_t decoration_group_id = inst->word(1);
-      std::vector<Decoration>& group_decorations =
+      std::set<Decoration>& group_decorations =
           _.id_decorations(decoration_group_id);
           _.id_decorations(decoration_group_id);
       // Grammar checks ensures that the number of arguments to this instruction
       // Grammar checks ensures that the number of arguments to this instruction
       // is an odd number: 1 decoration group + (id,literal) pairs.
       // is an odd number: 1 decoration group + (id,literal) pairs.

+ 91 - 4
3rdparty/spirv-tools/source/val/validate_builtins.cpp

@@ -120,7 +120,7 @@ typedef enum VUIDError_ {
   VUIDErrorMax,
   VUIDErrorMax,
 } VUIDError;
 } VUIDError;
 
 
-const static uint32_t NumVUIDBuiltins = 34;
+const static uint32_t NumVUIDBuiltins = 36;
 
 
 typedef struct {
 typedef struct {
   SpvBuiltIn builtIn;
   SpvBuiltIn builtIn;
@@ -163,6 +163,8 @@ std::array<BuiltinVUIDMapping, NumVUIDBuiltins> builtinVUIDInfo = {{
     {SpvBuiltInFragStencilRefEXT,         {4223, 4224, 4225}},
     {SpvBuiltInFragStencilRefEXT,         {4223, 4224, 4225}},
     {SpvBuiltInFullyCoveredEXT,           {4232, 4233, 4234}},
     {SpvBuiltInFullyCoveredEXT,           {4232, 4233, 4234}},
     {SpvBuiltInCullMaskKHR,               {6735, 6736, 6737}},
     {SpvBuiltInCullMaskKHR,               {6735, 6736, 6737}},
+    {SpvBuiltInBaryCoordKHR,              {4154, 4155, 4156}},
+    {SpvBuiltInBaryCoordNoPerspKHR,       {4160, 4161, 4162}},
     // clang-format off
     // clang-format off
 } };
 } };
 
 
@@ -333,7 +335,9 @@ class BuiltInsValidator {
       const Decoration& decoration, const Instruction& inst);
       const Decoration& decoration, const Instruction& inst);
   spv_result_t ValidateSMBuiltinsAtDefinition(const Decoration& decoration,
   spv_result_t ValidateSMBuiltinsAtDefinition(const Decoration& decoration,
                                               const Instruction& inst);
                                               const Instruction& inst);
-
+  // Used for BaryCoord, BaryCoordNoPersp.
+  spv_result_t ValidateFragmentShaderF32Vec3InputAtDefinition(
+      const Decoration& decoration, const Instruction& inst);
   // Used for SubgroupEqMask, SubgroupGeMask, SubgroupGtMask, SubgroupLtMask,
   // Used for SubgroupEqMask, SubgroupGeMask, SubgroupGtMask, SubgroupLtMask,
   // SubgroupLeMask.
   // SubgroupLeMask.
   spv_result_t ValidateI32Vec4InputAtDefinition(const Decoration& decoration,
   spv_result_t ValidateI32Vec4InputAtDefinition(const Decoration& decoration,
@@ -511,6 +515,13 @@ class BuiltInsValidator {
       const Decoration& decoration, const Instruction& built_in_inst,
       const Decoration& decoration, const Instruction& built_in_inst,
       const Instruction& referenced_inst,
       const Instruction& referenced_inst,
       const Instruction& referenced_from_inst);
       const Instruction& referenced_from_inst);
+
+  // Used for BaryCoord, BaryCoordNoPersp.
+  spv_result_t ValidateFragmentShaderF32Vec3InputAtReference(
+      const Decoration& decoration, const Instruction& built_in_inst,
+      const Instruction& referenced_inst,
+      const Instruction& referenced_from_inst);
+
   // Used for SubgroupId and NumSubgroups.
   // Used for SubgroupId and NumSubgroups.
   spv_result_t ValidateComputeI32InputAtReference(
   spv_result_t ValidateComputeI32InputAtReference(
       const Decoration& decoration, const Instruction& built_in_inst,
       const Decoration& decoration, const Instruction& built_in_inst,
@@ -2790,6 +2801,80 @@ spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference(
   return SPV_SUCCESS;
   return SPV_SUCCESS;
 }
 }
 
 
+spv_result_t BuiltInsValidator::ValidateFragmentShaderF32Vec3InputAtDefinition(
+    const Decoration& decoration, const Instruction& inst) {
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
+    if (spv_result_t error = ValidateF32Vec(
+            decoration, inst, 3,
+            [this, &inst, builtin](const std::string& message) -> spv_result_t {
+              uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorType);
+              return _.diag(SPV_ERROR_INVALID_DATA, &inst)
+                     << _.VkErrorID(vuid) << "According to the "
+                     << spvLogStringForEnv(_.context()->target_env)
+                     << " spec BuiltIn "
+                     << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
+                                                      builtin)
+                     << " variable needs to be a 3-component 32-bit float "
+                        "vector. "
+                     << message;
+            })) {
+      return error;
+    }
+  }
+
+  // Seed at reference checks with this built-in.
+  return ValidateFragmentShaderF32Vec3InputAtReference(decoration, inst, inst,
+                                                      inst);
+}
+
+spv_result_t BuiltInsValidator::ValidateFragmentShaderF32Vec3InputAtReference(
+    const Decoration& decoration, const Instruction& built_in_inst,
+    const Instruction& referenced_inst,
+    const Instruction& referenced_from_inst) {
+
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
+    const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
+    if (storage_class != SpvStorageClassMax &&
+        storage_class != SpvStorageClassInput) {
+      uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorStorageClass);
+      return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
+             << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env)
+             << " spec allows BuiltIn "
+             << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+             << " 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 SpvExecutionModel execution_model : execution_models_) {
+      if (execution_model != SpvExecutionModelFragment) {
+        uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
+        return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
+               << _.VkErrorID(vuid)
+               << spvLogStringForEnv(_.context()->target_env)
+               << " spec allows BuiltIn "
+               << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, builtin)
+               << " to be used only with Fragment 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(std::bind(
+        &BuiltInsValidator::ValidateFragmentShaderF32Vec3InputAtReference, this,
+        decoration, built_in_inst, referenced_from_inst,
+        std::placeholders::_1));
+  }
+
+  return SPV_SUCCESS;
+}
+
 spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtDefinition(
 spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtDefinition(
     const Decoration& decoration, const Instruction& inst) {
     const Decoration& decoration, const Instruction& inst) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
   if (spvIsVulkanEnv(_.context()->target_env)) {
@@ -4030,6 +4115,10 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition(
     case SpvBuiltInWorkgroupId: {
     case SpvBuiltInWorkgroupId: {
       return ValidateComputeShaderI32Vec3InputAtDefinition(decoration, inst);
       return ValidateComputeShaderI32Vec3InputAtDefinition(decoration, inst);
     }
     }
+    case SpvBuiltInBaryCoordKHR:
+    case SpvBuiltInBaryCoordNoPerspKHR: {
+      return ValidateFragmentShaderF32Vec3InputAtDefinition(decoration, inst);
+    }
     case SpvBuiltInHelperInvocation: {
     case SpvBuiltInHelperInvocation: {
       return ValidateHelperInvocationAtDefinition(decoration, inst);
       return ValidateHelperInvocationAtDefinition(decoration, inst);
     }
     }
@@ -4186,8 +4275,6 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition(
     case SpvBuiltInLayerPerViewNV:
     case SpvBuiltInLayerPerViewNV:
     case SpvBuiltInMeshViewCountNV:
     case SpvBuiltInMeshViewCountNV:
     case SpvBuiltInMeshViewIndicesNV:
     case SpvBuiltInMeshViewIndicesNV:
-    case SpvBuiltInBaryCoordNV:
-    case SpvBuiltInBaryCoordNoPerspNV:
     case SpvBuiltInCurrentRayTimeNV:
     case SpvBuiltInCurrentRayTimeNV:
       // No validation rules (for the moment).
       // No validation rules (for the moment).
       break;
       break;

+ 139 - 59
3rdparty/spirv-tools/source/val/validate_cfg.cpp

@@ -466,7 +466,7 @@ spv_result_t FindCaseFallThrough(
   std::vector<BasicBlock*> stack;
   std::vector<BasicBlock*> stack;
   stack.push_back(target_block);
   stack.push_back(target_block);
   std::unordered_set<const BasicBlock*> visited;
   std::unordered_set<const BasicBlock*> visited;
-  bool target_reachable = target_block->reachable();
+  bool target_reachable = target_block->structurally_reachable();
   int target_depth = function->GetBlockDepth(target_block);
   int target_depth = function->GetBlockDepth(target_block);
   while (!stack.empty()) {
   while (!stack.empty()) {
     auto block = stack.back();
     auto block = stack.back();
@@ -476,8 +476,8 @@ spv_result_t FindCaseFallThrough(
 
 
     if (!visited.insert(block).second) continue;
     if (!visited.insert(block).second) continue;
 
 
-    if (target_reachable && block->reachable() &&
-        target_block->dominates(*block)) {
+    if (target_reachable && block->structurally_reachable() &&
+        target_block->structurally_dominates(*block)) {
       // Still in the case construct.
       // Still in the case construct.
       for (auto successor : *block->successors()) {
       for (auto successor : *block->successors()) {
         stack.push_back(successor);
         stack.push_back(successor);
@@ -549,11 +549,12 @@ spv_result_t StructuredSwitchChecks(ValidationState_t& _, Function* function,
     if (seen_iter == seen_to_fall_through.end()) {
     if (seen_iter == seen_to_fall_through.end()) {
       const auto target_block = function->GetBlock(target).first;
       const auto target_block = function->GetBlock(target).first;
       // OpSwitch must dominate all its case constructs.
       // OpSwitch must dominate all its case constructs.
-      if (header->reachable() && target_block->reachable() &&
-          !header->dominates(*target_block)) {
+      if (header->structurally_reachable() &&
+          target_block->structurally_reachable() &&
+          !header->structurally_dominates(*target_block)) {
         return _.diag(SPV_ERROR_INVALID_CFG, header->label())
         return _.diag(SPV_ERROR_INVALID_CFG, header->label())
                << "Selection header " << _.getIdName(header->id())
                << "Selection header " << _.getIdName(header->id())
-               << " does not dominate its case construct "
+               << " does not structurally dominate its case construct "
                << _.getIdName(target);
                << _.getIdName(target);
       }
       }
 
 
@@ -653,7 +654,7 @@ spv_result_t ValidateStructuredSelections(
     }
     }
 
 
     // Skip unreachable blocks.
     // Skip unreachable blocks.
-    if (!block->reachable()) continue;
+    if (!block->structurally_reachable()) continue;
 
 
     if (terminator->opcode() == SpvOpBranchConditional) {
     if (terminator->opcode() == SpvOpBranchConditional) {
       const auto true_label = terminator->GetOperandAs<uint32_t>(1);
       const auto true_label = terminator->GetOperandAs<uint32_t>(1);
@@ -708,7 +709,7 @@ spv_result_t StructuredControlFlowChecks(
 
 
   // Check the loop headers have exactly one back-edge branching to it
   // Check the loop headers have exactly one back-edge branching to it
   for (BasicBlock* loop_header : function->ordered_blocks()) {
   for (BasicBlock* loop_header : function->ordered_blocks()) {
-    if (!loop_header->reachable()) continue;
+    if (!loop_header->structurally_reachable()) continue;
     if (!loop_header->is_type(kBlockTypeLoop)) continue;
     if (!loop_header->is_type(kBlockTypeLoop)) continue;
     auto loop_header_id = loop_header->id();
     auto loop_header_id = loop_header->id();
     auto num_latch_blocks = loop_latch_blocks[loop_header_id].size();
     auto num_latch_blocks = loop_latch_blocks[loop_header_id].size();
@@ -723,9 +724,10 @@ spv_result_t StructuredControlFlowChecks(
   // Check construct rules
   // Check construct rules
   for (const Construct& construct : function->constructs()) {
   for (const Construct& construct : function->constructs()) {
     auto header = construct.entry_block();
     auto header = construct.entry_block();
+    if (!header->structurally_reachable()) continue;
     auto merge = construct.exit_block();
     auto merge = construct.exit_block();
 
 
-    if (header->reachable() && !merge) {
+    if (!merge) {
       std::string construct_name, header_name, exit_name;
       std::string construct_name, header_name, exit_name;
       std::tie(construct_name, header_name, exit_name) =
       std::tie(construct_name, header_name, exit_name) =
           ConstructNames(construct.type());
           ConstructNames(construct.type());
@@ -735,32 +737,31 @@ spv_result_t StructuredControlFlowChecks(
                     exit_name + ". This may be a bug in the validator.";
                     exit_name + ". This may be a bug in the validator.";
     }
     }
 
 
-    // If the exit block is reachable then it's dominated by the
-    // header.
-    if (merge && merge->reachable()) {
-      if (!header->dominates(*merge)) {
-        return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id()))
-               << ConstructErrorString(construct, _.getIdName(header->id()),
-                                       _.getIdName(merge->id()),
-                                       "does not dominate");
-      }
-      // If it's really a merge block for a selection or loop, then it must be
-      // *strictly* dominated by the header.
-      if (construct.ExitBlockIsMergeBlock() && (header == merge)) {
-        return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id()))
-               << ConstructErrorString(construct, _.getIdName(header->id()),
-                                       _.getIdName(merge->id()),
-                                       "does not strictly dominate");
-      }
+    // If the header is reachable, the merge is guaranteed to be structurally
+    // reachable.
+    if (!header->structurally_dominates(*merge)) {
+      return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id()))
+             << ConstructErrorString(construct, _.getIdName(header->id()),
+                                     _.getIdName(merge->id()),
+                                     "does not structurally dominate");
+    }
+    // If it's really a merge block for a selection or loop, then it must be
+    // *strictly* structrually dominated by the header.
+    if (construct.ExitBlockIsMergeBlock() && (header == merge)) {
+      return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id()))
+             << ConstructErrorString(construct, _.getIdName(header->id()),
+                                     _.getIdName(merge->id()),
+                                     "does not strictly structurally dominate");
     }
     }
+
     // Check post-dominance for continue constructs.  But dominance and
     // Check post-dominance for continue constructs.  But dominance and
     // post-dominance only make sense when the construct is reachable.
     // post-dominance only make sense when the construct is reachable.
-    if (header->reachable() && construct.type() == ConstructType::kContinue) {
-      if (!merge->postdominates(*header)) {
+    if (construct.type() == ConstructType::kContinue) {
+      if (!merge->structurally_postdominates(*header)) {
         return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id()))
         return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id()))
                << ConstructErrorString(construct, _.getIdName(header->id()),
                << ConstructErrorString(construct, _.getIdName(header->id()),
                                        _.getIdName(merge->id()),
                                        _.getIdName(merge->id()),
-                                       "is not post dominated by");
+                                       "is not structurally post dominated by");
       }
       }
     }
     }
 
 
@@ -771,7 +772,7 @@ spv_result_t StructuredControlFlowChecks(
     for (auto block : construct_blocks) {
     for (auto block : construct_blocks) {
       // Check that all exits from the construct are via structured exits.
       // Check that all exits from the construct are via structured exits.
       for (auto succ : *block->successors()) {
       for (auto succ : *block->successors()) {
-        if (block->reachable() && !construct_blocks.count(succ) &&
+        if (!construct_blocks.count(succ) &&
             !construct.IsStructuredExit(_, succ)) {
             !construct.IsStructuredExit(_, succ)) {
           return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
           return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
                  << "block <ID> " << _.getIdName(block->id()) << " exits the "
                  << "block <ID> " << _.getIdName(block->id()) << " exits the "
@@ -784,7 +785,7 @@ spv_result_t StructuredControlFlowChecks(
       // Check that for all non-header blocks, all predecessors are within this
       // Check that for all non-header blocks, all predecessors are within this
       // construct.
       // construct.
       for (auto pred : *block->predecessors()) {
       for (auto pred : *block->predecessors()) {
-        if (pred->reachable() && !construct_blocks.count(pred)) {
+        if (pred->structurally_reachable() && !construct_blocks.count(pred)) {
           return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(pred->id()))
           return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(pred->id()))
                  << "block <ID> " << pred->id() << " branches to the "
                  << "block <ID> " << pred->id() << " branches to the "
                  << construct_name << " construct, but not to the "
                  << construct_name << " construct, but not to the "
@@ -800,7 +801,7 @@ spv_result_t StructuredControlFlowChecks(
             merge_inst.opcode() == SpvOpLoopMerge) {
             merge_inst.opcode() == SpvOpLoopMerge) {
           uint32_t merge_id = merge_inst.GetOperandAs<uint32_t>(0);
           uint32_t merge_id = merge_inst.GetOperandAs<uint32_t>(0);
           auto merge_block = function->GetBlock(merge_id).first;
           auto merge_block = function->GetBlock(merge_id).first;
-          if (merge_block->reachable() &&
+          if (merge_block->structurally_reachable() &&
               !construct_blocks.count(merge_block)) {
               !construct_blocks.count(merge_block)) {
             return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
             return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
                    << "Header block " << _.getIdName(block->id())
                    << "Header block " << _.getIdName(block->id())
@@ -813,6 +814,43 @@ spv_result_t StructuredControlFlowChecks(
       }
       }
     }
     }
 
 
+    if (construct.type() == ConstructType::kLoop) {
+      // If the continue target differs from the loop header, then check that
+      // all edges into the continue construct come from within the loop.
+      const auto index = header->terminator() - &_.ordered_instructions()[0];
+      const auto& merge_inst = _.ordered_instructions()[index - 1];
+      const auto continue_id = merge_inst.GetOperandAs<uint32_t>(1);
+      const auto* continue_inst = _.FindDef(continue_id);
+      // OpLabel instructions aren't stored as part of the basic block for
+      // legacy reaasons. Grab the next instruction and use it's block pointer
+      // instead.
+      const auto next_index =
+          (continue_inst - &_.ordered_instructions()[0]) + 1;
+      const auto& next_inst = _.ordered_instructions()[next_index];
+      const auto* continue_target = next_inst.block();
+      if (header->id() != continue_id) {
+        for (auto pred : *continue_target->predecessors()) {
+          // Ignore back-edges from within the continue construct.
+          bool is_back_edge = false;
+          for (auto back_edge : back_edges) {
+            uint32_t back_edge_block;
+            uint32_t header_block;
+            std::tie(back_edge_block, header_block) = back_edge;
+            if (header_block == continue_id && back_edge_block == pred->id())
+              is_back_edge = true;
+          }
+          if (!construct_blocks.count(pred) && !is_back_edge) {
+            return _.diag(SPV_ERROR_INVALID_CFG, pred->terminator())
+                   << "Block " << _.getIdName(pred->id())
+                   << " branches to the loop continue target "
+                   << _.getIdName(continue_id)
+                   << ", but is not contained in the associated loop construct "
+                   << _.getIdName(header->id());
+          }
+        }
+      }
+    }
+
     // Checks rules for case constructs.
     // Checks rules for case constructs.
     if (construct.type() == ConstructType::kSelection &&
     if (construct.type() == ConstructType::kSelection &&
         header->terminator()->opcode() == SpvOpSwitch) {
         header->terminator()->opcode() == SpvOpSwitch) {
@@ -850,52 +888,28 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) {
              << _.getIdName(function.id());
              << _.getIdName(function.id());
     }
     }
 
 
-    // Set each block's immediate dominator and immediate postdominator,
-    // and find all back-edges.
+    // Set each block's immediate dominator.
     //
     //
     // We want to analyze all the blocks in the function, even in degenerate
     // We want to analyze all the blocks in the function, even in degenerate
     // control flow cases including unreachable blocks.  So use the augmented
     // control flow cases including unreachable blocks.  So use the augmented
     // CFG to ensure we cover all the blocks.
     // CFG to ensure we cover all the blocks.
     std::vector<const BasicBlock*> postorder;
     std::vector<const BasicBlock*> postorder;
-    std::vector<const BasicBlock*> postdom_postorder;
-    std::vector<std::pair<uint32_t, uint32_t>> back_edges;
     auto ignore_block = [](const BasicBlock*) {};
     auto ignore_block = [](const BasicBlock*) {};
     auto ignore_edge = [](const BasicBlock*, const BasicBlock*) {};
     auto ignore_edge = [](const BasicBlock*, const BasicBlock*) {};
+    auto no_terminal_blocks = [](const BasicBlock*) { return false; };
     if (!function.ordered_blocks().empty()) {
     if (!function.ordered_blocks().empty()) {
       /// calculate dominators
       /// calculate dominators
       CFA<BasicBlock>::DepthFirstTraversal(
       CFA<BasicBlock>::DepthFirstTraversal(
           function.first_block(), function.AugmentedCFGSuccessorsFunction(),
           function.first_block(), function.AugmentedCFGSuccessorsFunction(),
           ignore_block, [&](const BasicBlock* b) { postorder.push_back(b); },
           ignore_block, [&](const BasicBlock* b) { postorder.push_back(b); },
-          ignore_edge);
+          ignore_edge, no_terminal_blocks);
       auto edges = CFA<BasicBlock>::CalculateDominators(
       auto edges = CFA<BasicBlock>::CalculateDominators(
           postorder, function.AugmentedCFGPredecessorsFunction());
           postorder, function.AugmentedCFGPredecessorsFunction());
       for (auto edge : edges) {
       for (auto edge : edges) {
         if (edge.first != edge.second)
         if (edge.first != edge.second)
           edge.first->SetImmediateDominator(edge.second);
           edge.first->SetImmediateDominator(edge.second);
       }
       }
-
-      /// calculate post dominators
-      CFA<BasicBlock>::DepthFirstTraversal(
-          function.pseudo_exit_block(),
-          function.AugmentedCFGPredecessorsFunction(), ignore_block,
-          [&](const BasicBlock* b) { postdom_postorder.push_back(b); },
-          ignore_edge);
-      auto postdom_edges = CFA<BasicBlock>::CalculateDominators(
-          postdom_postorder, function.AugmentedCFGSuccessorsFunction());
-      for (auto edge : postdom_edges) {
-        edge.first->SetImmediatePostDominator(edge.second);
-      }
-      /// calculate back edges.
-      CFA<BasicBlock>::DepthFirstTraversal(
-          function.pseudo_entry_block(),
-          function
-              .AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge(),
-          ignore_block, ignore_block,
-          [&](const BasicBlock* from, const BasicBlock* to) {
-            back_edges.emplace_back(from->id(), to->id());
-          });
     }
     }
-    UpdateContinueConstructExitBlocks(function, back_edges);
 
 
     auto& blocks = function.ordered_blocks();
     auto& blocks = function.ordered_blocks();
     if (!blocks.empty()) {
     if (!blocks.empty()) {
@@ -929,6 +943,52 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) {
 
 
     /// Structured control flow checks are only required for shader capabilities
     /// Structured control flow checks are only required for shader capabilities
     if (_.HasCapability(SpvCapabilityShader)) {
     if (_.HasCapability(SpvCapabilityShader)) {
+      // Calculate structural dominance.
+      postorder.clear();
+      std::vector<const BasicBlock*> postdom_postorder;
+      std::vector<std::pair<uint32_t, uint32_t>> back_edges;
+      if (!function.ordered_blocks().empty()) {
+        /// calculate dominators
+        CFA<BasicBlock>::DepthFirstTraversal(
+            function.first_block(),
+            function.AugmentedStructuralCFGSuccessorsFunction(), ignore_block,
+            [&](const BasicBlock* b) { postorder.push_back(b); }, ignore_edge,
+            no_terminal_blocks);
+        auto edges = CFA<BasicBlock>::CalculateDominators(
+            postorder, function.AugmentedStructuralCFGPredecessorsFunction());
+        for (auto edge : edges) {
+          if (edge.first != edge.second)
+            edge.first->SetImmediateStructuralDominator(edge.second);
+        }
+
+        /// calculate post dominators
+        CFA<BasicBlock>::DepthFirstTraversal(
+            function.pseudo_exit_block(),
+            function.AugmentedStructuralCFGPredecessorsFunction(), ignore_block,
+            [&](const BasicBlock* b) { postdom_postorder.push_back(b); },
+            ignore_edge, no_terminal_blocks);
+        auto postdom_edges = CFA<BasicBlock>::CalculateDominators(
+            postdom_postorder,
+            function.AugmentedStructuralCFGSuccessorsFunction());
+        for (auto edge : postdom_edges) {
+          edge.first->SetImmediateStructuralPostDominator(edge.second);
+        }
+        /// calculate back edges.
+        CFA<BasicBlock>::DepthFirstTraversal(
+            function.pseudo_entry_block(),
+            function.AugmentedStructuralCFGSuccessorsFunction(), ignore_block,
+            ignore_block,
+            [&](const BasicBlock* from, const BasicBlock* to) {
+              // A back edge must be a real edge. Since the augmented successors
+              // contain structural edges, filter those from consideration.
+              for (const auto* succ : *(from->successors())) {
+                if (succ == to) back_edges.emplace_back(from->id(), to->id());
+              }
+            },
+            no_terminal_blocks);
+      }
+      UpdateContinueConstructExitBlocks(function, back_edges);
+
       if (auto error =
       if (auto error =
               StructuredControlFlowChecks(_, &function, back_edges, postorder))
               StructuredControlFlowChecks(_, &function, back_edges, postorder))
         return error;
         return error;
@@ -1054,6 +1114,26 @@ void ReachabilityPass(ValidationState_t& _) {
       }
       }
     }
     }
   }
   }
+
+  // Repeat for structural reachability.
+  for (auto& f : _.functions()) {
+    std::vector<BasicBlock*> stack;
+    auto entry = f.first_block();
+    // Skip function declarations.
+    if (entry) stack.push_back(entry);
+
+    while (!stack.empty()) {
+      auto block = stack.back();
+      stack.pop_back();
+
+      if (block->structurally_reachable()) continue;
+
+      block->set_structurally_reachable(true);
+      for (auto succ : *block->structural_successors()) {
+        stack.push_back(succ);
+      }
+    }
+  }
 }
 }
 
 
 spv_result_t ControlFlowPass(ValidationState_t& _, const Instruction* inst) {
 spv_result_t ControlFlowPass(ValidationState_t& _, const Instruction* inst) {

+ 107 - 28
3rdparty/spirv-tools/source/val/validate_decorations.cpp

@@ -346,10 +346,13 @@ uint32_t getSize(uint32_t member_id, const LayoutConstraints& inherited,
       const auto& lastMember = members.back();
       const auto& lastMember = members.back();
       uint32_t offset = 0xffffffff;
       uint32_t offset = 0xffffffff;
       // Find the offset of the last element and add the size.
       // Find the offset of the last element and add the size.
-      for (auto& decoration : vstate.id_decorations(member_id)) {
-        if (SpvDecorationOffset == decoration.dec_type() &&
-            decoration.struct_member_index() == (int)lastIdx) {
-          offset = decoration.params()[0];
+      auto member_decorations =
+          vstate.id_member_decorations(member_id, lastIdx);
+      for (auto decoration = member_decorations.begin;
+           decoration != member_decorations.end; ++decoration) {
+        assert(decoration->struct_member_index() == (int)lastIdx);
+        if (SpvDecorationOffset == decoration->dec_type()) {
+          offset = decoration->params()[0];
         }
         }
       }
       }
       // This check depends on the fact that all members have offsets.  This
       // This check depends on the fact that all members have offsets.  This
@@ -445,15 +448,17 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
   for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
   for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
        memberIdx < numMembers; memberIdx++) {
        memberIdx < numMembers; memberIdx++) {
     uint32_t offset = 0xffffffff;
     uint32_t offset = 0xffffffff;
-    for (auto& decoration : vstate.id_decorations(struct_id)) {
-      if (decoration.struct_member_index() == (int)memberIdx) {
-        switch (decoration.dec_type()) {
-          case SpvDecorationOffset:
-            offset = decoration.params()[0];
-            break;
-          default:
-            break;
-        }
+    auto member_decorations =
+        vstate.id_member_decorations(struct_id, memberIdx);
+    for (auto decoration = member_decorations.begin;
+         decoration != member_decorations.end; ++decoration) {
+      assert(decoration->struct_member_index() == (int)memberIdx);
+      switch (decoration->dec_type()) {
+        case SpvDecorationOffset:
+          offset = decoration->params()[0];
+          break;
+        default:
+          break;
       }
       }
     }
     }
     member_offsets.push_back(
     member_offsets.push_back(
@@ -806,6 +811,56 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
               ++num_workgroup_variables_with_aliased;
               ++num_workgroup_variables_with_aliased;
           }
           }
         }
         }
+
+        if (spvIsVulkanEnv(vstate.context()->target_env)) {
+          const auto* models = vstate.GetExecutionModels(entry_point);
+          const bool has_frag =
+              models->find(SpvExecutionModelFragment) != models->end();
+          const bool has_vert =
+              models->find(SpvExecutionModelVertex) != models->end();
+          for (const auto& decoration :
+               vstate.id_decorations(var_instr->id())) {
+            if (decoration == SpvDecorationFlat ||
+                decoration == SpvDecorationNoPerspective ||
+                decoration == SpvDecorationSample ||
+                decoration == SpvDecorationCentroid) {
+              // VUID 04670 already validates these decorations are input/output
+              if (storage_class == SpvStorageClassInput &&
+                  (models->size() > 1 || has_vert)) {
+                return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
+                       << vstate.VkErrorID(6202)
+                       << "OpEntryPoint interfaces variable must not be vertex "
+                          "execution model with an input storage class for "
+                          "Entry Point id "
+                       << entry_point << ".";
+              } else if (storage_class == SpvStorageClassOutput &&
+                         (models->size() > 1 || has_frag)) {
+                return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
+                       << vstate.VkErrorID(6201)
+                       << "OpEntryPoint interfaces variable must not be "
+                          "fragment "
+                          "execution model with an output storage class for "
+                          "Entry Point id "
+                       << entry_point << ".";
+              }
+            }
+          }
+
+          const bool has_flat =
+              hasDecoration(var_instr->id(), SpvDecorationFlat, vstate);
+          if (has_frag && storage_class == SpvStorageClassInput && !has_flat &&
+              ((vstate.IsFloatScalarType(type_id) &&
+                vstate.GetBitWidth(type_id) == 64) ||
+               vstate.IsIntScalarOrVectorType(type_id))) {
+            return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
+                     << vstate.VkErrorID(4744)
+                     << "Fragment OpEntryPoint operand "
+                     << interface << " with Input interfaces with integer or "
+                                     "float type must have a Flat decoration "
+                                     "for Entry Point id "
+                     << entry_point << ".";
+          }
+        }
       }
       }
       if (num_builtin_block_inputs > 1 || num_builtin_block_outputs > 1) {
       if (num_builtin_block_inputs > 1 || num_builtin_block_outputs > 1) {
         return vstate.diag(SPV_ERROR_INVALID_BINARY,
         return vstate.diag(SPV_ERROR_INVALID_BINARY,
@@ -878,21 +933,23 @@ void ComputeMemberConstraintsForStruct(MemberConstraints* constraints,
     LayoutConstraints& constraint =
     LayoutConstraints& constraint =
         (*constraints)[std::make_pair(struct_id, memberIdx)];
         (*constraints)[std::make_pair(struct_id, memberIdx)];
     constraint = inherited;
     constraint = inherited;
-    for (auto& decoration : vstate.id_decorations(struct_id)) {
-      if (decoration.struct_member_index() == (int)memberIdx) {
-        switch (decoration.dec_type()) {
-          case SpvDecorationRowMajor:
-            constraint.majorness = kRowMajor;
-            break;
-          case SpvDecorationColMajor:
-            constraint.majorness = kColumnMajor;
-            break;
-          case SpvDecorationMatrixStride:
-            constraint.matrix_stride = decoration.params()[0];
-            break;
-          default:
-            break;
-        }
+    auto member_decorations =
+        vstate.id_member_decorations(struct_id, memberIdx);
+    for (auto decoration = member_decorations.begin;
+         decoration != member_decorations.end; ++decoration) {
+      assert(decoration->struct_member_index() == (int)memberIdx);
+      switch (decoration->dec_type()) {
+        case SpvDecorationRowMajor:
+          constraint.majorness = kRowMajor;
+          break;
+        case SpvDecorationColMajor:
+          constraint.majorness = kColumnMajor;
+          break;
+        case SpvDecorationMatrixStride:
+          constraint.matrix_stride = decoration->params()[0];
+          break;
+        default:
+          break;
       }
       }
     }
     }
 
 
@@ -1654,6 +1711,24 @@ spv_result_t CheckLocationDecoration(ValidationState_t& vstate,
             "of a structure type";
             "of a structure type";
 }
 }
 
 
+spv_result_t CheckRelaxPrecisionDecoration(ValidationState_t& vstate,
+                                           const Instruction& inst,
+                                           const Decoration& decoration) {
+  // This is not the most precise check, but the rules for RelaxPrecision are
+  // very general, and it will be difficult to implement precisely.  For now,
+  // I will only check for the cases that cause problems for the optimizer.
+  if (!spvOpcodeGeneratesType(inst.opcode())) {
+    return SPV_SUCCESS;
+  }
+
+  if (decoration.struct_member_index() != Decoration::kInvalidMember &&
+      inst.opcode() == SpvOpTypeStruct) {
+    return SPV_SUCCESS;
+  }
+  return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+         << "RelaxPrecision decoration cannot be applied to a type";
+}
+
 #define PASS_OR_BAIL_AT_LINE(X, LINE)           \
 #define PASS_OR_BAIL_AT_LINE(X, LINE)           \
   {                                             \
   {                                             \
     spv_result_t e##LINE = (X);                 \
     spv_result_t e##LINE = (X);                 \
@@ -1708,6 +1783,10 @@ spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) {
         case SpvDecorationLocation:
         case SpvDecorationLocation:
           PASS_OR_BAIL(CheckLocationDecoration(vstate, *inst, decoration));
           PASS_OR_BAIL(CheckLocationDecoration(vstate, *inst, decoration));
           break;
           break;
+        case SpvDecorationRelaxedPrecision:
+          PASS_OR_BAIL(
+              CheckRelaxPrecisionDecoration(vstate, *inst, decoration));
+          break;
         default:
         default:
           break;
           break;
       }
       }

+ 16 - 4
3rdparty/spirv-tools/source/val/validate_interfaces.cpp

@@ -238,7 +238,7 @@ spv_result_t GetLocationsForVariable(
   uint32_t index = 0;
   uint32_t index = 0;
   bool has_patch = false;
   bool has_patch = false;
   bool has_per_task_nv = false;
   bool has_per_task_nv = false;
-  bool has_per_vertex_nv = false;
+  bool has_per_vertex_khr = false;
   for (auto& dec : _.id_decorations(variable->id())) {
   for (auto& dec : _.id_decorations(variable->id())) {
     if (dec.dec_type() == SpvDecorationLocation) {
     if (dec.dec_type() == SpvDecorationLocation) {
       if (has_location && dec.params()[0] != location) {
       if (has_location && dec.params()[0] != location) {
@@ -272,8 +272,20 @@ spv_result_t GetLocationsForVariable(
       has_patch = true;
       has_patch = true;
     } else if (dec.dec_type() == SpvDecorationPerTaskNV) {
     } else if (dec.dec_type() == SpvDecorationPerTaskNV) {
       has_per_task_nv = true;
       has_per_task_nv = true;
-    } else if (dec.dec_type() == SpvDecorationPerVertexNV) {
-      has_per_vertex_nv = true;
+    } else if (dec.dec_type() == SpvDecorationPerVertexKHR) {
+      if (!is_fragment) {
+        return _.diag(SPV_ERROR_INVALID_DATA, variable)
+               << _.VkErrorID(6777)
+               << "PerVertexKHR can only be applied to Fragment Execution "
+                  "Models";
+      }
+      if (type->opcode() != SpvOpTypeArray &&
+          type->opcode() != SpvOpTypeRuntimeArray) {
+        return _.diag(SPV_ERROR_INVALID_DATA, variable)
+               << _.VkErrorID(6778)
+               << "PerVertexKHR must be declared as arrays";
+      }
+      has_per_vertex_khr = true;
     }
     }
   }
   }
 
 
@@ -298,7 +310,7 @@ spv_result_t GetLocationsForVariable(
       }
       }
       break;
       break;
     case SpvExecutionModelFragment:
     case SpvExecutionModelFragment:
-      if (!is_output && has_per_vertex_nv) {
+      if (!is_output && has_per_vertex_khr) {
         is_arrayed = true;
         is_arrayed = true;
       }
       }
       break;
       break;

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

@@ -35,8 +35,8 @@ bool HaveLayoutCompatibleMembers(ValidationState_t&, const Instruction*,
                                  const Instruction*);
                                  const Instruction*);
 bool HaveSameLayoutDecorations(ValidationState_t&, const Instruction*,
 bool HaveSameLayoutDecorations(ValidationState_t&, const Instruction*,
                                const Instruction*);
                                const Instruction*);
-bool HasConflictingMemberOffsets(const std::vector<Decoration>&,
-                                 const std::vector<Decoration>&);
+bool HasConflictingMemberOffsets(const std::set<Decoration>&,
+                                 const std::set<Decoration>&);
 
 
 bool IsAllowedTypeOrArrayOfSame(ValidationState_t& _, const Instruction* type,
 bool IsAllowedTypeOrArrayOfSame(ValidationState_t& _, const Instruction* type,
                                 std::initializer_list<uint32_t> allowed) {
                                 std::initializer_list<uint32_t> allowed) {
@@ -105,10 +105,8 @@ bool HaveSameLayoutDecorations(ValidationState_t& _, const Instruction* type1,
          "type1 must be an OpTypeStruct instruction.");
          "type1 must be an OpTypeStruct instruction.");
   assert(type2->opcode() == SpvOpTypeStruct &&
   assert(type2->opcode() == SpvOpTypeStruct &&
          "type2 must be an OpTypeStruct instruction.");
          "type2 must be an OpTypeStruct instruction.");
-  const std::vector<Decoration>& type1_decorations =
-      _.id_decorations(type1->id());
-  const std::vector<Decoration>& type2_decorations =
-      _.id_decorations(type2->id());
+  const std::set<Decoration>& type1_decorations = _.id_decorations(type1->id());
+  const std::set<Decoration>& type2_decorations = _.id_decorations(type2->id());
 
 
   // TODO: Will have to add other check for arrays an matricies if we want to
   // TODO: Will have to add other check for arrays an matricies if we want to
   // handle them.
   // handle them.
@@ -120,8 +118,8 @@ bool HaveSameLayoutDecorations(ValidationState_t& _, const Instruction* type1,
 }
 }
 
 
 bool HasConflictingMemberOffsets(
 bool HasConflictingMemberOffsets(
-    const std::vector<Decoration>& type1_decorations,
-    const std::vector<Decoration>& type2_decorations) {
+    const std::set<Decoration>& type1_decorations,
+    const std::set<Decoration>& type2_decorations) {
   {
   {
     // We are interested in conflicting decoration.  If a decoration is in one
     // We are interested in conflicting decoration.  If a decoration is in one
     // list but not the other, then we will assume the code is correct.  We are
     // list but not the other, then we will assume the code is correct.  We are
@@ -526,8 +524,8 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
     if (storage_class == SpvStorageClassPushConstant) {
     if (storage_class == SpvStorageClassPushConstant) {
       if (pointee->opcode() != SpvOpTypeStruct) {
       if (pointee->opcode() != SpvOpTypeStruct) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
         return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << "PushConstant OpVariable <id> '" << _.getIdName(inst->id())
-               << "' has illegal type.\n"
+               << _.VkErrorID(6808) << "PushConstant OpVariable <id> '"
+               << _.getIdName(inst->id()) << "' has illegal type.\n"
                << "From Vulkan spec, Push Constant Interface section:\n"
                << "From Vulkan spec, Push Constant Interface section:\n"
                << "Such variables must be typed as OpTypeStruct";
                << "Such variables must be typed as OpTypeStruct";
       }
       }
@@ -554,9 +552,9 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
     if (storage_class == SpvStorageClassUniform) {
     if (storage_class == SpvStorageClassUniform) {
       if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) {
       if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
         return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << "Uniform OpVariable <id> '" << _.getIdName(inst->id())
-               << "' has illegal type.\n"
-               << "From Vulkan spec, section 14.5.2:\n"
+               << _.VkErrorID(6807) << "Uniform OpVariable <id> '"
+               << _.getIdName(inst->id()) << "' has illegal type.\n"
+               << "From Vulkan spec:\n"
                << "Variables identified with the Uniform storage class are "
                << "Variables identified with the Uniform storage class are "
                << "used to access transparent buffer backed resources. Such "
                << "used to access transparent buffer backed resources. Such "
                << "variables must be typed as OpTypeStruct, or an array of "
                << "variables must be typed as OpTypeStruct, or an array of "
@@ -567,9 +565,9 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
     if (storage_class == SpvStorageClassStorageBuffer) {
     if (storage_class == SpvStorageClassStorageBuffer) {
       if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) {
       if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) {
         return _.diag(SPV_ERROR_INVALID_ID, inst)
         return _.diag(SPV_ERROR_INVALID_ID, inst)
-               << "StorageBuffer OpVariable <id> '" << _.getIdName(inst->id())
-               << "' has illegal type.\n"
-               << "From Vulkan spec, section 14.5.2:\n"
+               << _.VkErrorID(6807) << "StorageBuffer OpVariable <id> '"
+               << _.getIdName(inst->id()) << "' has illegal type.\n"
+               << "From Vulkan spec:\n"
                << "Variables identified with the StorageBuffer storage class "
                << "Variables identified with the StorageBuffer storage class "
                   "are used to access transparent buffer backed resources. "
                   "are used to access transparent buffer backed resources. "
                   "Such variables must be typed as OpTypeStruct, or an array "
                   "Such variables must be typed as OpTypeStruct, or an array "

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

@@ -112,6 +112,44 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) {
                  << "Fragment execution model entry points can specify at most "
                  << "Fragment execution model entry points can specify at most "
                     "one fragment shader interlock execution mode.";
                     "one fragment shader interlock execution mode.";
         }
         }
+        if (execution_modes &&
+            1 < std::count_if(
+                    execution_modes->begin(), execution_modes->end(),
+                    [](const SpvExecutionMode& mode) {
+                      switch (mode) {
+                        case SpvExecutionModeStencilRefUnchangedFrontAMD:
+                        case SpvExecutionModeStencilRefLessFrontAMD:
+                        case SpvExecutionModeStencilRefGreaterFrontAMD:
+                          return true;
+                        default:
+                          return false;
+                      }
+                    })) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Fragment execution model entry points can specify at most "
+                    "one of StencilRefUnchangedFrontAMD, "
+                    "StencilRefLessFrontAMD or StencilRefGreaterFrontAMD "
+                    "execution modes.";
+        }
+        if (execution_modes &&
+            1 < std::count_if(
+                    execution_modes->begin(), execution_modes->end(),
+                    [](const SpvExecutionMode& mode) {
+                      switch (mode) {
+                        case SpvExecutionModeStencilRefUnchangedBackAMD:
+                        case SpvExecutionModeStencilRefLessBackAMD:
+                        case SpvExecutionModeStencilRefGreaterBackAMD:
+                          return true;
+                        default:
+                          return false;
+                      }
+                    })) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << "Fragment execution model entry points can specify at most "
+                    "one of StencilRefUnchangedBackAMD, "
+                    "StencilRefLessBackAMD or StencilRefGreaterBackAMD "
+                    "execution modes.";
+        }
         break;
         break;
       case SpvExecutionModelTessellationControl:
       case SpvExecutionModelTessellationControl:
       case SpvExecutionModelTessellationEvaluation:
       case SpvExecutionModelTessellationEvaluation:
@@ -412,6 +450,13 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _,
     case SpvExecutionModeSampleInterlockUnorderedEXT:
     case SpvExecutionModeSampleInterlockUnorderedEXT:
     case SpvExecutionModeShadingRateInterlockOrderedEXT:
     case SpvExecutionModeShadingRateInterlockOrderedEXT:
     case SpvExecutionModeShadingRateInterlockUnorderedEXT:
     case SpvExecutionModeShadingRateInterlockUnorderedEXT:
+    case SpvExecutionModeEarlyAndLateFragmentTestsAMD:
+    case SpvExecutionModeStencilRefUnchangedFrontAMD:
+    case SpvExecutionModeStencilRefGreaterFrontAMD:
+    case SpvExecutionModeStencilRefLessFrontAMD:
+    case SpvExecutionModeStencilRefUnchangedBackAMD:
+    case SpvExecutionModeStencilRefGreaterBackAMD:
+    case SpvExecutionModeStencilRefLessBackAMD:
       if (!std::all_of(models->begin(), models->end(),
       if (!std::all_of(models->begin(), models->end(),
                        [](const SpvExecutionModel& model) {
                        [](const SpvExecutionModel& model) {
                          return model == SpvExecutionModelFragment;
                          return model == SpvExecutionModelFragment;

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

@@ -1411,6 +1411,18 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
   // Clang format adds spaces between hyphens
   // Clang format adds spaces between hyphens
   // clang-format off
   // clang-format off
   switch (id) {
   switch (id) {
+    case 4154:
+      return VUID_WRAP(VUID-BaryCoordKHR-BaryCoordKHR-04154);
+    case 4155:
+      return VUID_WRAP(VUID-BaryCoordKHR-BaryCoordKHR-04155);
+    case 4156:
+      return VUID_WRAP(VUID-BaryCoordKHR-BaryCoordKHR-04156);
+    case 4160:
+      return VUID_WRAP(VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04160);
+    case 4161:
+      return VUID_WRAP(VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04161);
+    case 4162:
+      return VUID_WRAP(VUID-BaryCoordNoPerspKHR-BaryCoordNoPerspKHR-04162);
     case 4181:
     case 4181:
       return VUID_WRAP(VUID-BaseInstance-BaseInstance-04181);
       return VUID_WRAP(VUID-BaseInstance-BaseInstance-04181);
     case 4182:
     case 4182:
@@ -1866,7 +1878,7 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
     case 4677:
     case 4677:
       return VUID_WRAP(VUID-StandaloneSpirv-Invariant-04677);
       return VUID_WRAP(VUID-StandaloneSpirv-Invariant-04677);
     case 4680:
     case 4680:
-      return VUID_WRAP( VUID-StandaloneSpirv-OpTypeRuntimeArray-04680);
+      return VUID_WRAP(VUID-StandaloneSpirv-OpTypeRuntimeArray-04680);
     case 4682:
     case 4682:
       return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04682);
       return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04682);
     case 6426:
     case 6426:
@@ -1891,6 +1903,8 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
       return VUID_WRAP(VUID-StandaloneSpirv-OpMemoryBarrier-04733);
       return VUID_WRAP(VUID-StandaloneSpirv-OpMemoryBarrier-04733);
     case 4734:
     case 4734:
       return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-04734);
       return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-04734);
+    case 4744:
+      return VUID_WRAP(VUID-StandaloneSpirv-Flat-04744);
     case 4777:
     case 4777:
       return VUID_WRAP(VUID-StandaloneSpirv-OpImage-04777);
       return VUID_WRAP(VUID-StandaloneSpirv-OpImage-04777);
     case 4780:
     case 4780:
@@ -1907,6 +1921,10 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
       return VUID_WRAP(VUID-StandaloneSpirv-Location-04918);
       return VUID_WRAP(VUID-StandaloneSpirv-Location-04918);
     case 4919:
     case 4919:
       return VUID_WRAP(VUID-StandaloneSpirv-Location-04919);
       return VUID_WRAP(VUID-StandaloneSpirv-Location-04919);
+    case 6201:
+      return VUID_WRAP(VUID-StandaloneSpirv-Flat-06201);
+    case 6202:
+      return VUID_WRAP(VUID-StandaloneSpirv-Flat-06202);
     case 6214:
     case 6214:
       return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-06214);
       return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-06214);
     case 6491:
     case 6491:
@@ -1925,6 +1943,14 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
       return VUID_WRAP(VUID-StandaloneSpirv-UniformConstant-06677);
       return VUID_WRAP(VUID-StandaloneSpirv-UniformConstant-06677);
     case 6678:
     case 6678:
       return VUID_WRAP(VUID-StandaloneSpirv-InputAttachmentIndex-06678);
       return VUID_WRAP(VUID-StandaloneSpirv-InputAttachmentIndex-06678);
+    case 6777:
+      return VUID_WRAP(VUID-StandaloneSpirv-PerVertexKHR-06777);
+    case 6778:
+      return VUID_WRAP(VUID-StandaloneSpirv-Input-06778);
+    case 6807:
+      return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06807);
+    case 6808:
+      return VUID_WRAP(VUID-StandaloneSpirv-PushConstant-06808);
     default:
     default:
       return "";  // unknown id
       return "";  // unknown id
   }
   }

+ 34 - 14
3rdparty/spirv-tools/source/val/validation_state.h

@@ -375,17 +375,14 @@ class ValidationState_t {
   /// Registers the decoration for the given <id>
   /// Registers the decoration for the given <id>
   void RegisterDecorationForId(uint32_t id, const Decoration& dec) {
   void RegisterDecorationForId(uint32_t id, const Decoration& dec) {
     auto& dec_list = id_decorations_[id];
     auto& dec_list = id_decorations_[id];
-    auto lb = std::find(dec_list.begin(), dec_list.end(), dec);
-    if (lb == dec_list.end()) {
-      dec_list.push_back(dec);
-    }
+    dec_list.insert(dec);
   }
   }
 
 
   /// Registers the list of decorations for the given <id>
   /// Registers the list of decorations for the given <id>
   template <class InputIt>
   template <class InputIt>
   void RegisterDecorationsForId(uint32_t id, InputIt begin, InputIt end) {
   void RegisterDecorationsForId(uint32_t id, InputIt begin, InputIt end) {
-    std::vector<Decoration>& cur_decs = id_decorations_[id];
-    cur_decs.insert(cur_decs.end(), begin, end);
+    std::set<Decoration>& cur_decs = id_decorations_[id];
+    cur_decs.insert(begin, end);
   }
   }
 
 
   /// Registers the list of decorations for the given member of the given
   /// Registers the list of decorations for the given member of the given
@@ -394,21 +391,44 @@ class ValidationState_t {
   void RegisterDecorationsForStructMember(uint32_t struct_id,
   void RegisterDecorationsForStructMember(uint32_t struct_id,
                                           uint32_t member_index, InputIt begin,
                                           uint32_t member_index, InputIt begin,
                                           InputIt end) {
                                           InputIt end) {
-    RegisterDecorationsForId(struct_id, begin, end);
-    for (auto& decoration : id_decorations_[struct_id]) {
-      decoration.set_struct_member_index(member_index);
+    std::set<Decoration>& cur_decs = id_decorations_[struct_id];
+    for (InputIt iter = begin; iter != end; ++iter) {
+      Decoration dec = *iter;
+      dec.set_struct_member_index(member_index);
+      cur_decs.insert(dec);
     }
     }
   }
   }
 
 
   /// Returns all the decorations for the given <id>. If no decorations exist
   /// Returns all the decorations for the given <id>. If no decorations exist
-  /// for the <id>, it registers an empty vector for it in the map and
-  /// returns the empty vector.
-  std::vector<Decoration>& id_decorations(uint32_t id) {
+  /// for the <id>, it registers an empty set for it in the map and
+  /// returns the empty set.
+  std::set<Decoration>& id_decorations(uint32_t id) {
     return id_decorations_[id];
     return id_decorations_[id];
   }
   }
 
 
+  /// Returns the range of decorations for the given field of the given <id>.
+  struct FieldDecorationsIter {
+    std::set<Decoration>::const_iterator begin;
+    std::set<Decoration>::const_iterator end;
+  };
+  FieldDecorationsIter id_member_decorations(uint32_t id,
+                                             uint32_t member_index) {
+    const auto& decorations = id_decorations_[id];
+
+    // The decorations are sorted by member_index, so this look up will give the
+    // exact range of decorations for this member index.
+    Decoration min_decoration((SpvDecoration)0, {}, member_index);
+    Decoration max_decoration(SpvDecorationMax, {}, member_index);
+
+    FieldDecorationsIter result;
+    result.begin = decorations.lower_bound(min_decoration);
+    result.end = decorations.upper_bound(max_decoration);
+
+    return result;
+  }
+
   // Returns const pointer to the internal decoration container.
   // Returns const pointer to the internal decoration container.
-  const std::map<uint32_t, std::vector<Decoration>>& id_decorations() const {
+  const std::map<uint32_t, std::set<Decoration>>& id_decorations() const {
     return id_decorations_;
     return id_decorations_;
   }
   }
 
 
@@ -826,7 +846,7 @@ class ValidationState_t {
       struct_has_nested_blockorbufferblock_struct_;
       struct_has_nested_blockorbufferblock_struct_;
 
 
   /// Stores the list of decorations for a given <id>
   /// Stores the list of decorations for a given <id>
-  std::map<uint32_t, std::vector<Decoration>> id_decorations_;
+  std::map<uint32_t, std::set<Decoration>> id_decorations_;
 
 
   /// Stores type declarations which need to be unique (i.e. non-aggregates),
   /// Stores type declarations which need to be unique (i.e. non-aggregates),
   /// in the form [opcode, operand words], result_id is not stored.
   /// in the form [opcode, operand words], result_id is not stored.

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor