Browse Source

Updated spirv-tools.

Бранимир Караџић 4 years ago
parent
commit
0abe793a67
57 changed files with 1500 additions and 445 deletions
  1. 1 1
      3rdparty/spirv-tools/include/generated/build-version.inc
  2. 8 0
      3rdparty/spirv-tools/include/generated/core.insts-unified1.inc
  3. 2 0
      3rdparty/spirv-tools/include/generated/enum_string_mapping.inc
  4. 1 0
      3rdparty/spirv-tools/include/generated/extension_enum.inc
  5. 2 1
      3rdparty/spirv-tools/include/generated/generators.inc
  6. 9 1
      3rdparty/spirv-tools/include/generated/operand.kinds-unified1.inc
  7. 1 0
      3rdparty/spirv-tools/include/spirv-tools/libspirv.h
  8. 7 0
      3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp
  9. 2 1
      3rdparty/spirv-tools/source/link/linker.cpp
  10. 304 236
      3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp
  11. 71 10
      3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.h
  12. 1 1
      3rdparty/spirv-tools/source/opt/block_merge_pass.cpp
  13. 11 6
      3rdparty/spirv-tools/source/opt/block_merge_util.cpp
  14. 4 0
      3rdparty/spirv-tools/source/opt/ccp_pass.cpp
  15. 4 0
      3rdparty/spirv-tools/source/opt/combine_access_chains.cpp
  16. 2 1
      3rdparty/spirv-tools/source/opt/constants.cpp
  17. 1 1
      3rdparty/spirv-tools/source/opt/convert_to_half_pass.cpp
  18. 4 0
      3rdparty/spirv-tools/source/opt/copy_prop_arrays.cpp
  19. 5 0
      3rdparty/spirv-tools/source/opt/dead_branch_elim_pass.cpp
  20. 1 1
      3rdparty/spirv-tools/source/opt/dead_insert_elim_pass.cpp
  21. 30 3
      3rdparty/spirv-tools/source/opt/debug_info_manager.cpp
  22. 8 0
      3rdparty/spirv-tools/source/opt/decoration_manager.cpp
  23. 4 0
      3rdparty/spirv-tools/source/opt/decoration_manager.h
  24. 13 106
      3rdparty/spirv-tools/source/opt/desc_sroa.cpp
  25. 0 9
      3rdparty/spirv-tools/source/opt/desc_sroa.h
  26. 117 0
      3rdparty/spirv-tools/source/opt/desc_sroa_util.cpp
  27. 54 0
      3rdparty/spirv-tools/source/opt/desc_sroa_util.h
  28. 4 14
      3rdparty/spirv-tools/source/opt/folding_rules.cpp
  29. 3 0
      3rdparty/spirv-tools/source/opt/function.h
  30. 1 1
      3rdparty/spirv-tools/source/opt/inline_exhaustive_pass.cpp
  31. 1 1
      3rdparty/spirv-tools/source/opt/inline_opaque_pass.cpp
  32. 1 1
      3rdparty/spirv-tools/source/opt/iterator.h
  33. 1 1
      3rdparty/spirv-tools/source/opt/local_single_block_elim_pass.cpp
  34. 1 1
      3rdparty/spirv-tools/source/opt/local_single_store_elim_pass.cpp
  35. 4 0
      3rdparty/spirv-tools/source/opt/loop_unroller.cpp
  36. 1 1
      3rdparty/spirv-tools/source/opt/mem_pass.cpp
  37. 7 0
      3rdparty/spirv-tools/source/opt/optimizer.cpp
  38. 1 0
      3rdparty/spirv-tools/source/opt/passes.h
  39. 4 0
      3rdparty/spirv-tools/source/opt/redundancy_elimination.cpp
  40. 2 1
      3rdparty/spirv-tools/source/opt/reflect.h
  41. 1 1
      3rdparty/spirv-tools/source/opt/relax_float_ops_pass.cpp
  42. 423 0
      3rdparty/spirv-tools/source/opt/replace_desc_array_access_using_var_index.cpp
  43. 204 0
      3rdparty/spirv-tools/source/opt/replace_desc_array_access_using_var_index.h
  44. 4 0
      3rdparty/spirv-tools/source/opt/scalar_replacement_pass.cpp
  45. 29 4
      3rdparty/spirv-tools/source/opt/set_spec_constant_default_value_pass.cpp
  46. 4 0
      3rdparty/spirv-tools/source/opt/simplification_pass.cpp
  47. 3 0
      3rdparty/spirv-tools/source/opt/ssa_rewrite_pass.cpp
  48. 1 0
      3rdparty/spirv-tools/source/spirv_constant.h
  49. 57 6
      3rdparty/spirv-tools/source/spirv_target_env.cpp
  50. 3 0
      3rdparty/spirv-tools/source/spirv_target_env.h
  51. 3 3
      3rdparty/spirv-tools/source/val/validate_cfg.cpp
  52. 5 1
      3rdparty/spirv-tools/source/val/validate_decorations.cpp
  53. 22 10
      3rdparty/spirv-tools/source/val/validate_image.cpp
  54. 3 4
      3rdparty/spirv-tools/source/val/validate_instruction.cpp
  55. 34 11
      3rdparty/spirv-tools/source/val/validate_interfaces.cpp
  56. 4 4
      3rdparty/spirv-tools/source/val/validate_mode_setting.cpp
  57. 2 2
      3rdparty/spirv-tools/source/val/validation_state.cpp

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

@@ -1 +1 @@
-"v2021.4-dev", "SPIRV-Tools v2021.4-dev e180849b89618fde67379735eae3a633a97e293c"
+"v2021.4-dev", "SPIRV-Tools v2021.4-dev 7e0cccc269f6476f79172f4446ab902f6c451f17"

+ 8 - 0
3rdparty/spirv-tools/include/generated/core.insts-unified1.inc

@@ -7,6 +7,7 @@ static const SpvCapability pygen_variable_caps_ArbitraryPrecisionFloatingPointIN
 static const SpvCapability pygen_variable_caps_AsmINTEL[] = {SpvCapabilityAsmINTEL};
 static const SpvCapability pygen_variable_caps_AsmINTEL[] = {SpvCapabilityAsmINTEL};
 static const SpvCapability pygen_variable_caps_AtomicFloat16AddEXTAtomicFloat32AddEXTAtomicFloat64AddEXT[] = {SpvCapabilityAtomicFloat16AddEXT, SpvCapabilityAtomicFloat32AddEXT, SpvCapabilityAtomicFloat64AddEXT};
 static const SpvCapability pygen_variable_caps_AtomicFloat16AddEXTAtomicFloat32AddEXTAtomicFloat64AddEXT[] = {SpvCapabilityAtomicFloat16AddEXT, SpvCapabilityAtomicFloat32AddEXT, SpvCapabilityAtomicFloat64AddEXT};
 static const SpvCapability pygen_variable_caps_AtomicFloat16MinMaxEXTAtomicFloat32MinMaxEXTAtomicFloat64MinMaxEXT[] = {SpvCapabilityAtomicFloat16MinMaxEXT, SpvCapabilityAtomicFloat32MinMaxEXT, SpvCapabilityAtomicFloat64MinMaxEXT};
 static const SpvCapability pygen_variable_caps_AtomicFloat16MinMaxEXTAtomicFloat32MinMaxEXTAtomicFloat64MinMaxEXT[] = {SpvCapabilityAtomicFloat16MinMaxEXT, SpvCapabilityAtomicFloat32MinMaxEXT, SpvCapabilityAtomicFloat64MinMaxEXT};
+static const SpvCapability pygen_variable_caps_BindlessTextureNV[] = {SpvCapabilityBindlessTextureNV};
 static const SpvCapability pygen_variable_caps_BlockingPipesINTEL[] = {SpvCapabilityBlockingPipesINTEL};
 static const SpvCapability pygen_variable_caps_BlockingPipesINTEL[] = {SpvCapabilityBlockingPipesINTEL};
 static const SpvCapability pygen_variable_caps_CooperativeMatrixNV[] = {SpvCapabilityCooperativeMatrixNV};
 static const SpvCapability pygen_variable_caps_CooperativeMatrixNV[] = {SpvCapabilityCooperativeMatrixNV};
 static const SpvCapability pygen_variable_caps_DemoteToHelperInvocationEXT[] = {SpvCapabilityDemoteToHelperInvocationEXT};
 static const SpvCapability pygen_variable_caps_DemoteToHelperInvocationEXT[] = {SpvCapabilityDemoteToHelperInvocationEXT};
@@ -498,6 +499,13 @@ static const spv_opcode_desc_t kOpcodeTableEntries[] = {
   {"EndInvocationInterlockEXT", SpvOpEndInvocationInterlockEXT, 3, pygen_variable_caps_FragmentShaderSampleInterlockEXTFragmentShaderPixelInterlockEXTFragmentShaderShadingRateInterlockEXT, 0, {}, 0, 0, 1, pygen_variable_exts_SPV_EXT_fragment_shader_interlock, 0xffffffffu, 0xffffffffu},
   {"EndInvocationInterlockEXT", SpvOpEndInvocationInterlockEXT, 3, pygen_variable_caps_FragmentShaderSampleInterlockEXTFragmentShaderPixelInterlockEXTFragmentShaderShadingRateInterlockEXT, 0, {}, 0, 0, 1, pygen_variable_exts_SPV_EXT_fragment_shader_interlock, 0xffffffffu, 0xffffffffu},
   {"DemoteToHelperInvocationEXT", SpvOpDemoteToHelperInvocationEXT, 1, pygen_variable_caps_DemoteToHelperInvocationEXT, 0, {}, 0, 0, 1, pygen_variable_exts_SPV_EXT_demote_to_helper_invocation, 0xffffffffu, 0xffffffffu},
   {"DemoteToHelperInvocationEXT", SpvOpDemoteToHelperInvocationEXT, 1, pygen_variable_caps_DemoteToHelperInvocationEXT, 0, {}, 0, 0, 1, pygen_variable_exts_SPV_EXT_demote_to_helper_invocation, 0xffffffffu, 0xffffffffu},
   {"IsHelperInvocationEXT", SpvOpIsHelperInvocationEXT, 1, pygen_variable_caps_DemoteToHelperInvocationEXT, 2, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID}, 1, 1, 1, pygen_variable_exts_SPV_EXT_demote_to_helper_invocation, 0xffffffffu, 0xffffffffu},
   {"IsHelperInvocationEXT", SpvOpIsHelperInvocationEXT, 1, pygen_variable_caps_DemoteToHelperInvocationEXT, 2, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID}, 1, 1, 1, pygen_variable_exts_SPV_EXT_demote_to_helper_invocation, 0xffffffffu, 0xffffffffu},
+  {"ConvertUToImageNV", SpvOpConvertUToImageNV, 1, pygen_variable_caps_BindlessTextureNV, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},
+  {"ConvertUToSamplerNV", SpvOpConvertUToSamplerNV, 1, pygen_variable_caps_BindlessTextureNV, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},
+  {"ConvertImageToUNV", SpvOpConvertImageToUNV, 1, pygen_variable_caps_BindlessTextureNV, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},
+  {"ConvertSamplerToUNV", SpvOpConvertSamplerToUNV, 1, pygen_variable_caps_BindlessTextureNV, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},
+  {"ConvertUToSampledImageNV", SpvOpConvertUToSampledImageNV, 1, pygen_variable_caps_BindlessTextureNV, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},
+  {"ConvertSampledImageToUNV", SpvOpConvertSampledImageToUNV, 1, pygen_variable_caps_BindlessTextureNV, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},
+  {"SamplerImageAddressingModeNV", SpvOpSamplerImageAddressingModeNV, 1, pygen_variable_caps_BindlessTextureNV, 1, {SPV_OPERAND_TYPE_LITERAL_INTEGER}, 0, 0, 0, nullptr, 0xffffffffu, 0xffffffffu},
   {"SubgroupShuffleINTEL", SpvOpSubgroupShuffleINTEL, 1, pygen_variable_caps_SubgroupShuffleINTEL, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},
   {"SubgroupShuffleINTEL", SpvOpSubgroupShuffleINTEL, 1, pygen_variable_caps_SubgroupShuffleINTEL, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},
   {"SubgroupShuffleDownINTEL", SpvOpSubgroupShuffleDownINTEL, 1, pygen_variable_caps_SubgroupShuffleINTEL, 5, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},
   {"SubgroupShuffleDownINTEL", SpvOpSubgroupShuffleDownINTEL, 1, pygen_variable_caps_SubgroupShuffleINTEL, 5, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},
   {"SubgroupShuffleUpINTEL", SpvOpSubgroupShuffleUpINTEL, 1, pygen_variable_caps_SubgroupShuffleINTEL, 5, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},
   {"SubgroupShuffleUpINTEL", SpvOpSubgroupShuffleUpINTEL, 1, pygen_variable_caps_SubgroupShuffleINTEL, 5, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},

File diff suppressed because it is too large
+ 2 - 0
3rdparty/spirv-tools/include/generated/enum_string_mapping.inc


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

@@ -79,6 +79,7 @@ kSPV_KHR_variable_pointers,
 kSPV_KHR_vulkan_memory_model,
 kSPV_KHR_vulkan_memory_model,
 kSPV_KHR_workgroup_memory_explicit_layout,
 kSPV_KHR_workgroup_memory_explicit_layout,
 kSPV_NVX_multiview_per_view_attributes,
 kSPV_NVX_multiview_per_view_attributes,
+kSPV_NV_bindless_texture,
 kSPV_NV_compute_shader_derivatives,
 kSPV_NV_compute_shader_derivatives,
 kSPV_NV_cooperative_matrix,
 kSPV_NV_cooperative_matrix,
 kSPV_NV_fragment_shader_barycentric,
 kSPV_NV_fragment_shader_barycentric,

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

@@ -28,4 +28,5 @@
 {27, "Embark Studios", "Rust GPU Compiler Backend", "Embark Studios Rust GPU Compiler Backend"},
 {27, "Embark Studios", "Rust GPU Compiler Backend", "Embark Studios Rust GPU Compiler Backend"},
 {28, "gfx-rs community", "Naga", "gfx-rs community Naga"},
 {28, "gfx-rs community", "Naga", "gfx-rs community Naga"},
 {29, "Mikkosoft Productions", "MSP Shader Compiler", "Mikkosoft Productions MSP Shader Compiler"},
 {29, "Mikkosoft Productions", "MSP Shader Compiler", "Mikkosoft Productions MSP Shader Compiler"},
-{30, "SpvGenTwo community", "SpvGenTwo SPIR-V IR Tools", "SpvGenTwo community SpvGenTwo SPIR-V IR Tools"},
+{30, "SpvGenTwo community", "SpvGenTwo SPIR-V IR Tools", "SpvGenTwo community SpvGenTwo SPIR-V IR Tools"},
+{31, "Google", "Skia SkSL", "Google Skia SkSL"},

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

@@ -2,6 +2,7 @@ static const SpvCapability pygen_variable_caps_Addresses[] = {SpvCapabilityAddre
 static const SpvCapability pygen_variable_caps_ArbitraryPrecisionFixedPointINTEL[] = {SpvCapabilityArbitraryPrecisionFixedPointINTEL};
 static const SpvCapability pygen_variable_caps_ArbitraryPrecisionFixedPointINTEL[] = {SpvCapabilityArbitraryPrecisionFixedPointINTEL};
 static const SpvCapability pygen_variable_caps_AsmINTEL[] = {SpvCapabilityAsmINTEL};
 static const SpvCapability pygen_variable_caps_AsmINTEL[] = {SpvCapabilityAsmINTEL};
 static const SpvCapability pygen_variable_caps_AtomicStorage[] = {SpvCapabilityAtomicStorage};
 static const SpvCapability pygen_variable_caps_AtomicStorage[] = {SpvCapabilityAtomicStorage};
+static const SpvCapability pygen_variable_caps_BindlessTextureNV[] = {SpvCapabilityBindlessTextureNV};
 static const SpvCapability pygen_variable_caps_ClipDistance[] = {SpvCapabilityClipDistance};
 static const SpvCapability pygen_variable_caps_ClipDistance[] = {SpvCapabilityClipDistance};
 static const SpvCapability pygen_variable_caps_ComputeDerivativeGroupLinearNV[] = {SpvCapabilityComputeDerivativeGroupLinearNV};
 static const SpvCapability pygen_variable_caps_ComputeDerivativeGroupLinearNV[] = {SpvCapabilityComputeDerivativeGroupLinearNV};
 static const SpvCapability pygen_variable_caps_ComputeDerivativeGroupQuadsNV[] = {SpvCapabilityComputeDerivativeGroupQuadsNV};
 static const SpvCapability pygen_variable_caps_ComputeDerivativeGroupQuadsNV[] = {SpvCapabilityComputeDerivativeGroupQuadsNV};
@@ -194,6 +195,7 @@ static const spvtools::Extension pygen_variable_exts_SPV_KHR_vulkan_memory_model
 static const spvtools::Extension pygen_variable_exts_SPV_KHR_workgroup_memory_explicit_layout[] = {spvtools::Extension::kSPV_KHR_workgroup_memory_explicit_layout};
 static const spvtools::Extension pygen_variable_exts_SPV_KHR_workgroup_memory_explicit_layout[] = {spvtools::Extension::kSPV_KHR_workgroup_memory_explicit_layout};
 static const spvtools::Extension pygen_variable_exts_SPV_NVX_multiview_per_view_attributes[] = {spvtools::Extension::kSPV_NVX_multiview_per_view_attributes};
 static const spvtools::Extension pygen_variable_exts_SPV_NVX_multiview_per_view_attributes[] = {spvtools::Extension::kSPV_NVX_multiview_per_view_attributes};
 static const spvtools::Extension pygen_variable_exts_SPV_NVX_multiview_per_view_attributesSPV_NV_mesh_shader[] = {spvtools::Extension::kSPV_NVX_multiview_per_view_attributes, spvtools::Extension::kSPV_NV_mesh_shader};
 static const spvtools::Extension pygen_variable_exts_SPV_NVX_multiview_per_view_attributesSPV_NV_mesh_shader[] = {spvtools::Extension::kSPV_NVX_multiview_per_view_attributes, spvtools::Extension::kSPV_NV_mesh_shader};
+static const spvtools::Extension pygen_variable_exts_SPV_NV_bindless_texture[] = {spvtools::Extension::kSPV_NV_bindless_texture};
 static const spvtools::Extension pygen_variable_exts_SPV_NV_compute_shader_derivatives[] = {spvtools::Extension::kSPV_NV_compute_shader_derivatives};
 static const spvtools::Extension pygen_variable_exts_SPV_NV_compute_shader_derivatives[] = {spvtools::Extension::kSPV_NV_compute_shader_derivatives};
 static const spvtools::Extension pygen_variable_exts_SPV_NV_cooperative_matrix[] = {spvtools::Extension::kSPV_NV_cooperative_matrix};
 static const spvtools::Extension pygen_variable_exts_SPV_NV_cooperative_matrix[] = {spvtools::Extension::kSPV_NV_cooperative_matrix};
 static const spvtools::Extension pygen_variable_exts_SPV_NV_fragment_shader_barycentric[] = {spvtools::Extension::kSPV_NV_fragment_shader_barycentric};
 static const spvtools::Extension pygen_variable_exts_SPV_NV_fragment_shader_barycentric[] = {spvtools::Extension::kSPV_NV_fragment_shader_barycentric};
@@ -228,7 +230,8 @@ static const spv_operand_desc_t pygen_variable_ImageOperandsEntries[] = {
   {"VolatileTexel", 0x0800, 1, pygen_variable_caps_VulkanMemoryModel, 1, pygen_variable_exts_SPV_KHR_vulkan_memory_model, {}, SPV_SPIRV_VERSION_WORD(1,5), 0xffffffffu},
   {"VolatileTexel", 0x0800, 1, pygen_variable_caps_VulkanMemoryModel, 1, pygen_variable_exts_SPV_KHR_vulkan_memory_model, {}, SPV_SPIRV_VERSION_WORD(1,5), 0xffffffffu},
   {"VolatileTexelKHR", 0x0800, 1, pygen_variable_caps_VulkanMemoryModel, 1, pygen_variable_exts_SPV_KHR_vulkan_memory_model, {}, SPV_SPIRV_VERSION_WORD(1,5), 0xffffffffu},
   {"VolatileTexelKHR", 0x0800, 1, pygen_variable_caps_VulkanMemoryModel, 1, pygen_variable_exts_SPV_KHR_vulkan_memory_model, {}, SPV_SPIRV_VERSION_WORD(1,5), 0xffffffffu},
   {"SignExtend", 0x1000, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu},
   {"SignExtend", 0x1000, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu},
-  {"ZeroExtend", 0x2000, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu}
+  {"ZeroExtend", 0x2000, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu},
+  {"Offsets", 0x10000, 0, nullptr, 0, nullptr, {SPV_OPERAND_TYPE_ID}, SPV_SPIRV_VERSION_WORD(1, 0), 0xffffffffu}
 };
 };
 
 
 static const spv_operand_desc_t pygen_variable_FPFastMathModeEntries[] = {
 static const spv_operand_desc_t pygen_variable_FPFastMathModeEntries[] = {
@@ -726,6 +729,10 @@ static const spv_operand_desc_t pygen_variable_DecorationEntries[] = {
   {"RestrictPointerEXT", 5355, 1, pygen_variable_caps_PhysicalStorageBufferAddresses, 2, pygen_variable_exts_SPV_EXT_physical_storage_bufferSPV_KHR_physical_storage_buffer, {}, SPV_SPIRV_VERSION_WORD(1,5), 0xffffffffu},
   {"RestrictPointerEXT", 5355, 1, pygen_variable_caps_PhysicalStorageBufferAddresses, 2, pygen_variable_exts_SPV_EXT_physical_storage_bufferSPV_KHR_physical_storage_buffer, {}, SPV_SPIRV_VERSION_WORD(1,5), 0xffffffffu},
   {"AliasedPointer", 5356, 1, pygen_variable_caps_PhysicalStorageBufferAddresses, 2, pygen_variable_exts_SPV_EXT_physical_storage_bufferSPV_KHR_physical_storage_buffer, {}, SPV_SPIRV_VERSION_WORD(1,5), 0xffffffffu},
   {"AliasedPointer", 5356, 1, pygen_variable_caps_PhysicalStorageBufferAddresses, 2, pygen_variable_exts_SPV_EXT_physical_storage_bufferSPV_KHR_physical_storage_buffer, {}, SPV_SPIRV_VERSION_WORD(1,5), 0xffffffffu},
   {"AliasedPointerEXT", 5356, 1, pygen_variable_caps_PhysicalStorageBufferAddresses, 2, pygen_variable_exts_SPV_EXT_physical_storage_bufferSPV_KHR_physical_storage_buffer, {}, SPV_SPIRV_VERSION_WORD(1,5), 0xffffffffu},
   {"AliasedPointerEXT", 5356, 1, pygen_variable_caps_PhysicalStorageBufferAddresses, 2, pygen_variable_exts_SPV_EXT_physical_storage_bufferSPV_KHR_physical_storage_buffer, {}, SPV_SPIRV_VERSION_WORD(1,5), 0xffffffffu},
+  {"BindlessSamplerNV", 5398, 1, pygen_variable_caps_BindlessTextureNV, 0, nullptr, {}, 0xffffffffu, 0xffffffffu},
+  {"BindlessImageNV", 5399, 1, pygen_variable_caps_BindlessTextureNV, 0, nullptr, {}, 0xffffffffu, 0xffffffffu},
+  {"BoundSamplerNV", 5400, 1, pygen_variable_caps_BindlessTextureNV, 0, nullptr, {}, 0xffffffffu, 0xffffffffu},
+  {"BoundImageNV", 5401, 1, pygen_variable_caps_BindlessTextureNV, 0, nullptr, {}, 0xffffffffu, 0xffffffffu},
   {"SIMTCallINTEL", 5599, 1, pygen_variable_caps_VectorComputeINTEL, 0, nullptr, {SPV_OPERAND_TYPE_LITERAL_INTEGER}, 0xffffffffu, 0xffffffffu},
   {"SIMTCallINTEL", 5599, 1, pygen_variable_caps_VectorComputeINTEL, 0, nullptr, {SPV_OPERAND_TYPE_LITERAL_INTEGER}, 0xffffffffu, 0xffffffffu},
   {"ReferencedIndirectlyINTEL", 5602, 1, pygen_variable_caps_IndirectReferencesINTEL, 1, pygen_variable_exts_SPV_INTEL_function_pointers, {}, 0xffffffffu, 0xffffffffu},
   {"ReferencedIndirectlyINTEL", 5602, 1, pygen_variable_caps_IndirectReferencesINTEL, 1, pygen_variable_exts_SPV_INTEL_function_pointers, {}, 0xffffffffu, 0xffffffffu},
   {"ClobberINTEL", 5607, 1, pygen_variable_caps_AsmINTEL, 0, nullptr, {SPV_OPERAND_TYPE_LITERAL_STRING}, 0xffffffffu, 0xffffffffu},
   {"ClobberINTEL", 5607, 1, pygen_variable_caps_AsmINTEL, 0, nullptr, {SPV_OPERAND_TYPE_LITERAL_STRING}, 0xffffffffu, 0xffffffffu},
@@ -1079,6 +1086,7 @@ static const spv_operand_desc_t pygen_variable_CapabilityEntries[] = {
   {"ShaderSMBuiltinsNV", 5373, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_NV_shader_sm_builtins, {}, 0xffffffffu, 0xffffffffu},
   {"ShaderSMBuiltinsNV", 5373, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_NV_shader_sm_builtins, {}, 0xffffffffu, 0xffffffffu},
   {"FragmentShaderPixelInterlockEXT", 5378, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_EXT_fragment_shader_interlock, {}, 0xffffffffu, 0xffffffffu},
   {"FragmentShaderPixelInterlockEXT", 5378, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_EXT_fragment_shader_interlock, {}, 0xffffffffu, 0xffffffffu},
   {"DemoteToHelperInvocationEXT", 5379, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_EXT_demote_to_helper_invocation, {}, 0xffffffffu, 0xffffffffu},
   {"DemoteToHelperInvocationEXT", 5379, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_EXT_demote_to_helper_invocation, {}, 0xffffffffu, 0xffffffffu},
+  {"BindlessTextureNV", 5390, 0, nullptr, 1, pygen_variable_exts_SPV_NV_bindless_texture, {}, 0xffffffffu, 0xffffffffu},
   {"SubgroupShuffleINTEL", 5568, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_subgroups, {}, 0xffffffffu, 0xffffffffu},
   {"SubgroupShuffleINTEL", 5568, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_subgroups, {}, 0xffffffffu, 0xffffffffu},
   {"SubgroupBufferBlockIOINTEL", 5569, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_subgroups, {}, 0xffffffffu, 0xffffffffu},
   {"SubgroupBufferBlockIOINTEL", 5569, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_subgroups, {}, 0xffffffffu, 0xffffffffu},
   {"SubgroupImageBlockIOINTEL", 5570, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_subgroups, {}, 0xffffffffu, 0xffffffffu},
   {"SubgroupImageBlockIOINTEL", 5570, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_subgroups, {}, 0xffffffffu, 0xffffffffu},

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

@@ -517,6 +517,7 @@ typedef enum {
 
 
   SPV_ENV_UNIVERSAL_1_5,  // SPIR-V 1.5 latest revision, no other restrictions.
   SPV_ENV_UNIVERSAL_1_5,  // SPIR-V 1.5 latest revision, no other restrictions.
   SPV_ENV_VULKAN_1_2,     // Vulkan 1.2 latest revision.
   SPV_ENV_VULKAN_1_2,     // Vulkan 1.2 latest revision.
+  SPV_ENV_MAX             // Keep this as the last enum value.
 } spv_target_env;
 } spv_target_env;
 
 
 // SPIR-V Validator can be parameterized with the following Universal Limits.
 // SPIR-V Validator can be parameterized with the following Universal Limits.

+ 7 - 0
3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp

@@ -833,6 +833,13 @@ Optimizer::PassToken CreateFixStorageClassPass();
 //   inclusive.
 //   inclusive.
 Optimizer::PassToken CreateGraphicsRobustAccessPass();
 Optimizer::PassToken CreateGraphicsRobustAccessPass();
 
 
+// Create a pass to replace a descriptor access using variable index.
+// This pass replaces every access using a variable index to array variable
+// |desc| that has a DescriptorSet and Binding decorations with a constant
+// element of the array. In order to replace the access using a variable index
+// with the constant element, it uses a switch statement.
+Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass();
+
 // Create descriptor scalar replacement pass.
 // Create descriptor scalar replacement pass.
 // This pass replaces every array variable |desc| that has a DescriptorSet and
 // This pass replaces every array variable |desc| that has a DescriptorSet and
 // Binding decorations with a new variable for each element of the array.
 // Binding decorations with a new variable for each element of the array.

+ 2 - 1
3rdparty/spirv-tools/source/link/linker.cpp

@@ -34,6 +34,7 @@
 #include "source/opt/pass_manager.h"
 #include "source/opt/pass_manager.h"
 #include "source/opt/remove_duplicates_pass.h"
 #include "source/opt/remove_duplicates_pass.h"
 #include "source/opt/type_manager.h"
 #include "source/opt/type_manager.h"
+#include "source/spirv_constant.h"
 #include "source/spirv_target_env.h"
 #include "source/spirv_target_env.h"
 #include "source/util/make_unique.h"
 #include "source/util/make_unique.h"
 #include "spirv-tools/libspirv.hpp"
 #include "spirv-tools/libspirv.hpp"
@@ -207,7 +208,7 @@ spv_result_t GenerateHeader(const MessageConsumer& consumer,
 
 
   header->magic_number = SpvMagicNumber;
   header->magic_number = SpvMagicNumber;
   header->version = version;
   header->version = version;
-  header->generator = 17u;
+  header->generator = SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_LINKER, 0);
   header->bound = max_id_bound;
   header->bound = max_id_bound;
   header->reserved = 0u;
   header->reserved = 0u;
 
 

+ 304 - 236
3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp

@@ -1,7 +1,7 @@
 // Copyright (c) 2017 The Khronos Group Inc.
 // Copyright (c) 2017 The Khronos Group Inc.
 // Copyright (c) 2017 Valve Corporation
 // Copyright (c) 2017 Valve Corporation
 // Copyright (c) 2017 LunarG Inc.
 // Copyright (c) 2017 LunarG Inc.
-// Copyright (c) 2018 Google LLC
+// Copyright (c) 2018-2021 Google LLC
 //
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@
 #include "source/cfa.h"
 #include "source/cfa.h"
 #include "source/latest_version_glsl_std_450_header.h"
 #include "source/latest_version_glsl_std_450_header.h"
 #include "source/opt/eliminate_dead_functions_util.h"
 #include "source/opt/eliminate_dead_functions_util.h"
+#include "source/opt/ir_builder.h"
 #include "source/opt/iterator.h"
 #include "source/opt/iterator.h"
 #include "source/opt/reflect.h"
 #include "source/opt/reflect.h"
 #include "source/spirv_constant.h"
 #include "source/spirv_constant.h"
@@ -38,6 +39,7 @@ const uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
 const uint32_t kLoopMergeContinueBlockIdInIdx = 1;
 const uint32_t kLoopMergeContinueBlockIdInIdx = 1;
 const uint32_t kCopyMemoryTargetAddrInIdx = 0;
 const uint32_t kCopyMemoryTargetAddrInIdx = 0;
 const uint32_t kCopyMemorySourceAddrInIdx = 1;
 const uint32_t kCopyMemorySourceAddrInIdx = 1;
+const uint32_t kLoadSourceAddrInIdx = 0;
 const uint32_t kDebugDeclareOperandVariableIndex = 5;
 const uint32_t kDebugDeclareOperandVariableIndex = 5;
 const uint32_t kGlobalVariableVariableIndex = 12;
 const uint32_t kGlobalVariableVariableIndex = 12;
 
 
@@ -95,16 +97,21 @@ bool AggressiveDCEPass::IsVarOfStorage(uint32_t varId, uint32_t storageClass) {
          storageClass;
          storageClass;
 }
 }
 
 
-bool AggressiveDCEPass::IsLocalVar(uint32_t varId) {
+bool AggressiveDCEPass::IsLocalVar(uint32_t varId, Function* func) {
   if (IsVarOfStorage(varId, SpvStorageClassFunction)) {
   if (IsVarOfStorage(varId, SpvStorageClassFunction)) {
     return true;
     return true;
   }
   }
-  if (!private_like_local_) {
+
+  if (!IsVarOfStorage(varId, SpvStorageClassPrivate) &&
+      !IsVarOfStorage(varId, SpvStorageClassWorkgroup)) {
     return false;
     return false;
   }
   }
 
 
-  return IsVarOfStorage(varId, SpvStorageClassPrivate) ||
-         IsVarOfStorage(varId, SpvStorageClassWorkgroup);
+  // For a variable in the Private or WorkGroup storage class, the variable will
+  // get a new instance for every call to an entry point.  If the entry point
+  // does not have a call, then no other function can read or write to that
+  // instance of the variable.
+  return IsEntryPointWithNoCalls(func);
 }
 }
 
 
 void AggressiveDCEPass::AddStores(Function* func, uint32_t ptrId) {
 void AggressiveDCEPass::AddStores(Function* func, uint32_t ptrId) {
@@ -160,14 +167,6 @@ bool AggressiveDCEPass::AllExtensionsSupported() const {
   return true;
   return true;
 }
 }
 
 
-bool AggressiveDCEPass::IsDead(Instruction* inst) {
-  if (IsLive(inst)) return false;
-  if ((inst->IsBranch() || inst->opcode() == SpvOpUnreachable) &&
-      context()->get_instr_block(inst)->GetMergeInst() == nullptr)
-    return false;
-  return true;
-}
-
 bool AggressiveDCEPass::IsTargetDead(Instruction* inst) {
 bool AggressiveDCEPass::IsTargetDead(Instruction* inst) {
   const uint32_t tId = inst->GetSingleWordInOperand(0);
   const uint32_t tId = inst->GetSingleWordInOperand(0);
   Instruction* tInst = get_def_use_mgr()->GetDef(tId);
   Instruction* tInst = get_def_use_mgr()->GetDef(tId);
@@ -184,12 +183,12 @@ bool AggressiveDCEPass::IsTargetDead(Instruction* inst) {
     });
     });
     return dead;
     return dead;
   }
   }
-  return IsDead(tInst);
+  return !IsLive(tInst);
 }
 }
 
 
 void AggressiveDCEPass::ProcessLoad(Function* func, uint32_t varId) {
 void AggressiveDCEPass::ProcessLoad(Function* func, uint32_t varId) {
   // Only process locals
   // Only process locals
-  if (!IsLocalVar(varId)) return;
+  if (!IsLocalVar(varId, func)) return;
   // Return if already processed
   // Return if already processed
   if (live_local_vars_.find(varId) != live_local_vars_.end()) return;
   if (live_local_vars_.find(varId) != live_local_vars_.end()) return;
   // Mark all stores to varId as live
   // Mark all stores to varId as live
@@ -265,170 +264,36 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist(
 }
 }
 
 
 bool AggressiveDCEPass::AggressiveDCE(Function* func) {
 bool AggressiveDCEPass::AggressiveDCE(Function* func) {
-  // Mark function parameters as live.
-  AddToWorklist(&func->DefInst());
-  MarkFunctionParameterAsLive(func);
-
-  std::list<BasicBlock*> structuredOrder;
-  cfg()->ComputeStructuredOrder(func, &*func->begin(), &structuredOrder);
-  bool modified = false;
+  std::list<BasicBlock*> structured_order;
+  cfg()->ComputeStructuredOrder(func, &*func->begin(), &structured_order);
   live_local_vars_.clear();
   live_local_vars_.clear();
-  InitializeWorkList(func, structuredOrder);
-
-  // Perform closure on live instruction set.
-  while (!worklist_.empty()) {
-    Instruction* liveInst = worklist_.front();
-    // Add all operand instructions of Debug[No]Lines
-    for (auto& lineInst : liveInst->dbg_line_insts()) {
-      if (lineInst.IsDebugLineInst()) {
-        lineInst.ForEachInId([this](const uint32_t* iid) {
-          AddToWorklist(get_def_use_mgr()->GetDef(*iid));
-        });
-      }
-    }
-    // Add all operand instructions if not already live
-    liveInst->ForEachInId([&liveInst, this](const uint32_t* iid) {
-      Instruction* inInst = get_def_use_mgr()->GetDef(*iid);
-      // Do not add label if an operand of a branch. This is not needed
-      // as part of live code discovery and can create false live code,
-      // for example, the branch to a header of a loop.
-      if (inInst->opcode() == SpvOpLabel && liveInst->IsBranch()) return;
-      AddToWorklist(inInst);
-    });
-    if (liveInst->type_id() != 0) {
-      AddToWorklist(get_def_use_mgr()->GetDef(liveInst->type_id()));
-    }
-    BasicBlock* basic_block = context()->get_instr_block(liveInst);
-    if (basic_block != nullptr) {
-      AddToWorklist(basic_block->GetLabelInst());
-    }
-
-    // If in a structured if or loop construct, add the controlling
-    // conditional branch and its merge.
-    BasicBlock* blk = context()->get_instr_block(liveInst);
-    Instruction* branchInst = GetHeaderBranch(blk);
-    if (branchInst != nullptr) {
-      AddToWorklist(branchInst);
-      Instruction* mergeInst = GetMergeInstruction(branchInst);
-      AddToWorklist(mergeInst);
-    }
-    // If the block is a header, add the next outermost controlling
-    // conditional branch and its merge.
-    Instruction* nextBranchInst = GetBranchForNextHeader(blk);
-    if (nextBranchInst != nullptr) {
-      AddToWorklist(nextBranchInst);
-      Instruction* mergeInst = GetMergeInstruction(nextBranchInst);
-      AddToWorklist(mergeInst);
-    }
-    // If local load, add all variable's stores if variable not already live
-    if (liveInst->opcode() == SpvOpLoad || liveInst->IsAtomicWithLoad()) {
-      uint32_t varId;
-      (void)GetPtr(liveInst, &varId);
-      if (varId != 0) {
-        ProcessLoad(func, varId);
-      }
-      // Process memory copies like loads
-    } else if (liveInst->opcode() == SpvOpCopyMemory ||
-               liveInst->opcode() == SpvOpCopyMemorySized) {
-      uint32_t varId;
-      (void)GetPtr(liveInst->GetSingleWordInOperand(kCopyMemorySourceAddrInIdx),
-                   &varId);
-      if (varId != 0) {
-        ProcessLoad(func, varId);
-      }
-      // If DebugDeclare, process as load of variable
-    } else if (liveInst->GetCommonDebugOpcode() ==
-               CommonDebugInfoDebugDeclare) {
-      uint32_t varId =
-          liveInst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
-      ProcessLoad(func, varId);
-      // If DebugValue with Deref, process as load of variable
-    } else if (liveInst->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) {
-      uint32_t varId = context()
-                           ->get_debug_info_mgr()
-                           ->GetVariableIdOfDebugValueUsedForDeclare(liveInst);
-      if (varId != 0) ProcessLoad(func, varId);
-      // If merge, add other branches that are part of its control structure
-    } else if (liveInst->opcode() == SpvOpLoopMerge ||
-               liveInst->opcode() == SpvOpSelectionMerge) {
-      AddBreaksAndContinuesToWorklist(liveInst);
-      // If function call, treat as if it loads from all pointer arguments
-    } else if (liveInst->opcode() == SpvOpFunctionCall) {
-      liveInst->ForEachInId([this, func](const uint32_t* iid) {
-        // Skip non-ptr args
-        if (!IsPtr(*iid)) return;
-        uint32_t varId;
-        (void)GetPtr(*iid, &varId);
-        ProcessLoad(func, varId);
-      });
-      // If function parameter, treat as if it's result id is loaded from
-    } else if (liveInst->opcode() == SpvOpFunctionParameter) {
-      ProcessLoad(func, liveInst->result_id());
-      // We treat an OpImageTexelPointer as a load of the pointer, and
-      // that value is manipulated to get the result.
-    } else if (liveInst->opcode() == SpvOpImageTexelPointer) {
-      uint32_t varId;
-      (void)GetPtr(liveInst, &varId);
-      if (varId != 0) {
-        ProcessLoad(func, varId);
-      }
-    }
-
-    // Add OpDecorateId instructions that apply to this instruction to the work
-    // list.  We use the decoration manager to look through the group
-    // decorations to get to the OpDecorate* instructions themselves.
-    auto decorations =
-        get_decoration_mgr()->GetDecorationsFor(liveInst->result_id(), false);
-    for (Instruction* dec : decorations) {
-      // We only care about OpDecorateId instructions because the are the only
-      // decorations that will reference an id that will have to be kept live
-      // because of that use.
-      if (dec->opcode() != SpvOpDecorateId) {
-        continue;
-      }
-      if (dec->GetSingleWordInOperand(1) ==
-          SpvDecorationHlslCounterBufferGOOGLE) {
-        // These decorations should not force the use id to be live.  It will be
-        // removed if either the target or the in operand are dead.
-        continue;
-      }
-      AddToWorklist(dec);
-    }
-
-    // Add DebugScope and DebugInlinedAt for |liveInst| to the work list.
-    if (liveInst->GetDebugScope().GetLexicalScope() != kNoDebugScope) {
-      auto* scope = get_def_use_mgr()->GetDef(
-          liveInst->GetDebugScope().GetLexicalScope());
-      AddToWorklist(scope);
-    }
-    if (liveInst->GetDebugInlinedAt() != kNoInlinedAt) {
-      auto* inlined_at =
-          get_def_use_mgr()->GetDef(liveInst->GetDebugInlinedAt());
-      AddToWorklist(inlined_at);
-    }
-    worklist_.pop();
-  }
+  InitializeWorkList(func, structured_order);
+  ProcessWorkList(func);
+  return KillDeadInstructions(func, structured_order);
+}
 
 
-  // Kill dead instructions and remember dead blocks
-  for (auto bi = structuredOrder.begin(); bi != structuredOrder.end();) {
-    uint32_t mergeBlockId = 0;
-    (*bi)->ForEachInst([this, &modified, &mergeBlockId](Instruction* inst) {
-      if (!IsDead(inst)) return;
+bool AggressiveDCEPass::KillDeadInstructions(
+    const Function* func, std::list<BasicBlock*>& structured_order) {
+  bool modified = false;
+  for (auto bi = structured_order.begin(); bi != structured_order.end();) {
+    uint32_t merge_block_id = 0;
+    (*bi)->ForEachInst([this, &modified, &merge_block_id](Instruction* inst) {
+      if (IsLive(inst)) return;
       if (inst->opcode() == SpvOpLabel) return;
       if (inst->opcode() == SpvOpLabel) return;
       // If dead instruction is selection merge, remember merge block
       // If dead instruction is selection merge, remember merge block
       // for new branch at end of block
       // for new branch at end of block
       if (inst->opcode() == SpvOpSelectionMerge ||
       if (inst->opcode() == SpvOpSelectionMerge ||
           inst->opcode() == SpvOpLoopMerge)
           inst->opcode() == SpvOpLoopMerge)
-        mergeBlockId = inst->GetSingleWordInOperand(0);
+        merge_block_id = inst->GetSingleWordInOperand(0);
       to_kill_.push_back(inst);
       to_kill_.push_back(inst);
       modified = true;
       modified = true;
     });
     });
     // If a structured if or loop was deleted, add a branch to its merge
     // If a structured if or loop was deleted, add a branch to its merge
     // block, and traverse to the merge block and continue processing there.
     // block, and traverse to the merge block and continue processing there.
     // We know the block still exists because the label is not deleted.
     // We know the block still exists because the label is not deleted.
-    if (mergeBlockId != 0) {
-      AddBranch(mergeBlockId, *bi);
-      for (++bi; (*bi)->id() != mergeBlockId; ++bi) {
+    if (merge_block_id != 0) {
+      AddBranch(merge_block_id, *bi);
+      for (++bi; (*bi)->id() != merge_block_id; ++bi) {
       }
       }
 
 
       auto merge_terminator = (*bi)->terminator();
       auto merge_terminator = (*bi)->terminator();
@@ -451,94 +316,250 @@ bool AggressiveDCEPass::AggressiveDCE(Function* func) {
         live_insts_.Set(merge_terminator->unique_id());
         live_insts_.Set(merge_terminator->unique_id());
       }
       }
     } else {
     } else {
+      Instruction* inst = (*bi)->terminator();
+      if (!IsLive(inst)) {
+        // If the terminator is not live, this block has no live instructions,
+        // and it will be unreachable.
+        AddUnreachable(*bi);
+      }
       ++bi;
       ++bi;
     }
     }
   }
   }
-
   return modified;
   return modified;
 }
 }
 
 
+void AggressiveDCEPass::ProcessWorkList(Function* func) {
+  while (!worklist_.empty()) {
+    Instruction* live_inst = worklist_.front();
+    worklist_.pop();
+    AddOperandsToWorkList(live_inst);
+    MarkBlockAsLive(live_inst);
+    MarkLoadedVariablesAsLive(func, live_inst);
+    AddDecorationsToWorkList(live_inst);
+    AddDebugInstructionsToWorkList(live_inst);
+  }
+}
+
+void AggressiveDCEPass::AddDebugInstructionsToWorkList(
+    const Instruction* inst) {
+  for (auto& line_inst : inst->dbg_line_insts()) {
+    if (line_inst.IsDebugLineInst()) {
+      AddOperandsToWorkList(&line_inst);
+    }
+  }
+
+  if (inst->GetDebugScope().GetLexicalScope() != kNoDebugScope) {
+    auto* scope =
+        get_def_use_mgr()->GetDef(inst->GetDebugScope().GetLexicalScope());
+    AddToWorklist(scope);
+  }
+  if (inst->GetDebugInlinedAt() != kNoInlinedAt) {
+    auto* inlined_at = get_def_use_mgr()->GetDef(inst->GetDebugInlinedAt());
+    AddToWorklist(inlined_at);
+  }
+}
+
+void AggressiveDCEPass::AddDecorationsToWorkList(const Instruction* inst) {
+  // Add OpDecorateId instructions that apply to this instruction to the work
+  // list.  We use the decoration manager to look through the group
+  // decorations to get to the OpDecorate* instructions themselves.
+  auto decorations =
+      get_decoration_mgr()->GetDecorationsFor(inst->result_id(), false);
+  for (Instruction* dec : decorations) {
+    // We only care about OpDecorateId instructions because the are the only
+    // decorations that will reference an id that will have to be kept live
+    // because of that use.
+    if (dec->opcode() != SpvOpDecorateId) {
+      continue;
+    }
+    if (dec->GetSingleWordInOperand(1) ==
+        SpvDecorationHlslCounterBufferGOOGLE) {
+      // These decorations should not force the use id to be live.  It will be
+      // removed if either the target or the in operand are dead.
+      continue;
+    }
+    AddToWorklist(dec);
+  }
+}
+
+void AggressiveDCEPass::MarkLoadedVariablesAsLive(Function* func,
+                                                  Instruction* inst) {
+  std::vector<uint32_t> live_variables = GetLoadedVariables(inst);
+  for (uint32_t var_id : live_variables) {
+    ProcessLoad(func, var_id);
+  }
+}
+
+std::vector<uint32_t> AggressiveDCEPass::GetLoadedVariables(Instruction* inst) {
+  if (inst->opcode() == SpvOpFunctionCall) {
+    return GetLoadedVariablesFromFunctionCall(inst);
+  }
+  uint32_t var_id = GetLoadedVariableFromNonFunctionCalls(inst);
+  if (var_id == 0) {
+    return {};
+  }
+  return {var_id};
+}
+
+uint32_t AggressiveDCEPass::GetLoadedVariableFromNonFunctionCalls(
+    Instruction* inst) {
+  std::vector<uint32_t> live_variables;
+  if (inst->IsAtomicWithLoad()) {
+    return GetVariableId(inst->GetSingleWordInOperand(kLoadSourceAddrInIdx));
+  }
+
+  switch (inst->opcode()) {
+    case SpvOpLoad:
+    case SpvOpImageTexelPointer:
+      return GetVariableId(inst->GetSingleWordInOperand(kLoadSourceAddrInIdx));
+    case SpvOpCopyMemory:
+    case SpvOpCopyMemorySized:
+      return GetVariableId(
+          inst->GetSingleWordInOperand(kCopyMemorySourceAddrInIdx));
+    default:
+      break;
+  }
+
+  switch (inst->GetCommonDebugOpcode()) {
+    case CommonDebugInfoDebugDeclare:
+      return inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
+    case CommonDebugInfoDebugValue: {
+      analysis::DebugInfoManager* debug_info_mgr =
+          context()->get_debug_info_mgr();
+      return debug_info_mgr->GetVariableIdOfDebugValueUsedForDeclare(inst);
+    }
+    default:
+      break;
+  }
+  return 0;
+}
+
+std::vector<uint32_t> AggressiveDCEPass::GetLoadedVariablesFromFunctionCall(
+    const Instruction* inst) {
+  assert(inst->opcode() == SpvOpFunctionCall);
+  std::vector<uint32_t> live_variables;
+  inst->ForEachInId([this, &live_variables](const uint32_t* operand_id) {
+    if (!IsPtr(*operand_id)) return;
+    uint32_t var_id = GetVariableId(*operand_id);
+    live_variables.push_back(var_id);
+  });
+  return live_variables;
+}
+
+uint32_t AggressiveDCEPass::GetVariableId(uint32_t ptr_id) {
+  assert(IsPtr(ptr_id) &&
+         "Cannot get the variable when input is not a pointer.");
+  uint32_t varId = 0;
+  (void)GetPtr(ptr_id, &varId);
+  return varId;
+}
+
+void AggressiveDCEPass::MarkBlockAsLive(Instruction* inst) {
+  BasicBlock* basic_block = context()->get_instr_block(inst);
+  if (basic_block == nullptr) {
+    return;
+  }
+
+  // If we intend to keep this instruction, we need the block label and
+  // block terminator to have a valid block for the instruction.
+  AddToWorklist(basic_block->GetLabelInst());
+
+  // We need to mark the successors blocks that follow as live.  If this is
+  // header of the merge construct, the construct may be folded, but we will
+  // definitely need the merge label.  If it is not a construct, the terminator
+  // must be live, and the successor blocks will be marked as live when
+  // processing the terminator.
+  uint32_t merge_id = basic_block->MergeBlockIdIfAny();
+  if (merge_id == 0) {
+    AddToWorklist(basic_block->terminator());
+  } else {
+    AddToWorklist(context()->get_def_use_mgr()->GetDef(merge_id));
+  }
+
+  // Mark the structured control flow constructs that contains this block as
+  // live.  If |inst| is an instruction in the loop header, then it is part of
+  // the loop, so the loop construct must be live.  We exclude the label because
+  // it does not matter how many times it is executed.  This could be extended
+  // to more instructions, but we will need it for now.
+  if (inst->opcode() != SpvOpLabel)
+    MarkLoopConstructAsLiveIfLoopHeader(basic_block);
+
+  Instruction* next_branch_inst = GetBranchForNextHeader(basic_block);
+  if (next_branch_inst != nullptr) {
+    AddToWorklist(next_branch_inst);
+    Instruction* mergeInst = GetMergeInstruction(next_branch_inst);
+    AddToWorklist(mergeInst);
+  }
+
+  if (inst->opcode() == SpvOpLoopMerge ||
+      inst->opcode() == SpvOpSelectionMerge) {
+    AddBreaksAndContinuesToWorklist(inst);
+  }
+}
+void AggressiveDCEPass::MarkLoopConstructAsLiveIfLoopHeader(
+    BasicBlock* basic_block) {
+  // If this is the header for a loop, then loop structure needs to keep as well
+  // because the loop header is also part of the loop.
+  Instruction* merge_inst = basic_block->GetLoopMergeInst();
+  if (merge_inst != nullptr) {
+    AddToWorklist(basic_block->terminator());
+    AddToWorklist(merge_inst);
+  }
+}
+
+void AggressiveDCEPass::AddOperandsToWorkList(const Instruction* inst) {
+  inst->ForEachInId([this](const uint32_t* iid) {
+    Instruction* inInst = get_def_use_mgr()->GetDef(*iid);
+    AddToWorklist(inInst);
+  });
+  if (inst->type_id() != 0) {
+    AddToWorklist(get_def_use_mgr()->GetDef(inst->type_id()));
+  }
+}
+
 void AggressiveDCEPass::InitializeWorkList(
 void AggressiveDCEPass::InitializeWorkList(
-    Function* func, std::list<BasicBlock*>& structuredOrder) {
+    Function* func, std::list<BasicBlock*>& structured_order) {
+  AddToWorklist(&func->DefInst());
+  MarkFunctionParameterAsLive(func);
+  MarkFirstBlockAsLive(func);
+
   // Add instructions with external side effects to the worklist. Also add
   // Add instructions with external side effects to the worklist. Also add
   // branches that are not attached to a structured construct.
   // branches that are not attached to a structured construct.
   // TODO(s-perron): The handling of branch seems to be adhoc.  This needs to be
   // TODO(s-perron): The handling of branch seems to be adhoc.  This needs to be
   // cleaned up.
   // cleaned up.
-  bool call_in_func = false;
-  bool func_is_entry_point = false;
-
-  // TODO(s-perron): We need to check if this is actually needed.  In cases
-  // where private variable can be treated as if they are function scope, the
-  // private-to-local pass should be able to change them to function scope.
-  std::vector<Instruction*> private_stores;
-
-  for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) {
-    for (auto ii = (*bi)->begin(); ii != (*bi)->end(); ++ii) {
+  for (auto& bi : structured_order) {
+    for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
       SpvOp op = ii->opcode();
       SpvOp op = ii->opcode();
+      if (ii->IsBranch()) {
+        continue;
+      }
       switch (op) {
       switch (op) {
         case SpvOpStore: {
         case SpvOpStore: {
-          uint32_t varId;
-          (void)GetPtr(&*ii, &varId);
-          // Mark stores as live if their variable is not function scope
-          // and is not private scope. Remember private stores for possible
-          // later inclusion.  We cannot call IsLocalVar at this point because
-          // private_like_local_ has not been set yet.
-          if (IsVarOfStorage(varId, SpvStorageClassPrivate) ||
-              IsVarOfStorage(varId, SpvStorageClassWorkgroup))
-            private_stores.push_back(&*ii);
-          else if (!IsVarOfStorage(varId, SpvStorageClassFunction))
-            AddToWorklist(&*ii);
+          uint32_t var_id = 0;
+          (void)GetPtr(&*ii, &var_id);
+          if (!IsLocalVar(var_id, func)) AddToWorklist(&*ii);
         } break;
         } break;
         case SpvOpCopyMemory:
         case SpvOpCopyMemory:
         case SpvOpCopyMemorySized: {
         case SpvOpCopyMemorySized: {
-          uint32_t varId;
-          (void)GetPtr(ii->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx),
-                       &varId);
-          if (IsVarOfStorage(varId, SpvStorageClassPrivate) ||
-              IsVarOfStorage(varId, SpvStorageClassWorkgroup))
-            private_stores.push_back(&*ii);
-          else if (!IsVarOfStorage(varId, SpvStorageClassFunction))
-            AddToWorklist(&*ii);
-        } break;
-        case SpvOpSwitch:
-        case SpvOpBranch:
-        case SpvOpBranchConditional:
-        case SpvOpUnreachable: {
-          bool branchRelatedToConstruct =
-              (GetMergeInstruction(&*ii) == nullptr &&
-               GetHeaderBlock(context()->get_instr_block(&*ii)) == nullptr);
-          if (branchRelatedToConstruct) {
-            AddToWorklist(&*ii);
-          }
+          uint32_t var_id = 0;
+          uint32_t target_addr_id =
+              ii->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx);
+          (void)GetPtr(target_addr_id, &var_id);
+          if (!IsLocalVar(var_id, func)) AddToWorklist(&*ii);
         } break;
         } break;
         case SpvOpLoopMerge:
         case SpvOpLoopMerge:
         case SpvOpSelectionMerge:
         case SpvOpSelectionMerge:
+        case SpvOpUnreachable:
           break;
           break;
         default: {
         default: {
           // Function calls, atomics, function params, function returns, etc.
           // Function calls, atomics, function params, function returns, etc.
           if (!ii->IsOpcodeSafeToDelete()) {
           if (!ii->IsOpcodeSafeToDelete()) {
             AddToWorklist(&*ii);
             AddToWorklist(&*ii);
           }
           }
-          // Remember function calls
-          if (op == SpvOpFunctionCall) call_in_func = true;
         } break;
         } break;
       }
       }
     }
     }
   }
   }
-  // See if current function is an entry point
-  for (auto& ei : get_module()->entry_points()) {
-    if (ei.GetSingleWordInOperand(kEntryPointFunctionIdInIdx) ==
-        func->result_id()) {
-      func_is_entry_point = true;
-      break;
-    }
-  }
-  // If the current function is an entry point and has no function calls,
-  // we can optimize private variables as locals
-  private_like_local_ = func_is_entry_point && !call_in_func;
-  // If privates are not like local, add their stores to worklist
-  if (!private_like_local_)
-    for (auto& ps : private_stores) AddToWorklist(ps);
 }
 }
 
 
 void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
 void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
@@ -596,16 +617,25 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
   }
   }
 
 
   // For each DebugInfo GlobalVariable keep all operands except the Variable.
   // For each DebugInfo GlobalVariable keep all operands except the Variable.
-  // Later, if the variable is dead, we will set the operand to DebugInfoNone.
+  // Later, if the variable is killed with KillInst(), we will set the operand
+  // to DebugInfoNone. Create and save DebugInfoNone now for this possible
+  // later use. This is slightly unoptimal, but it avoids generating it during
+  // instruction killing when the module is not consistent.
+  bool debug_global_seen = false;
   for (auto& dbg : get_module()->ext_inst_debuginfo()) {
   for (auto& dbg : get_module()->ext_inst_debuginfo()) {
     if (dbg.GetCommonDebugOpcode() != CommonDebugInfoDebugGlobalVariable)
     if (dbg.GetCommonDebugOpcode() != CommonDebugInfoDebugGlobalVariable)
       continue;
       continue;
+    debug_global_seen = true;
     dbg.ForEachInId([this](const uint32_t* iid) {
     dbg.ForEachInId([this](const uint32_t* iid) {
-      Instruction* inInst = get_def_use_mgr()->GetDef(*iid);
-      if (inInst->opcode() == SpvOpVariable) return;
-      AddToWorklist(inInst);
+      Instruction* in_inst = get_def_use_mgr()->GetDef(*iid);
+      if (in_inst->opcode() == SpvOpVariable) return;
+      AddToWorklist(in_inst);
     });
     });
   }
   }
+  if (debug_global_seen) {
+    auto dbg_none = context()->get_debug_info_mgr()->GetDebugInfoNone();
+    AddToWorklist(dbg_none);
+  }
 }
 }
 
 
 Pass::Status AggressiveDCEPass::ProcessImpl() {
 Pass::Status AggressiveDCEPass::ProcessImpl() {
@@ -743,7 +773,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
             uint32_t counter_buffer_id = annotation->GetSingleWordInOperand(2);
             uint32_t counter_buffer_id = annotation->GetSingleWordInOperand(2);
             Instruction* counter_buffer_inst =
             Instruction* counter_buffer_inst =
                 get_def_use_mgr()->GetDef(counter_buffer_id);
                 get_def_use_mgr()->GetDef(counter_buffer_id);
-            if (IsDead(counter_buffer_inst)) {
+            if (!IsLive(counter_buffer_inst)) {
               context()->KillInst(annotation);
               context()->KillInst(annotation);
               modified = true;
               modified = true;
             }
             }
@@ -758,7 +788,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
         for (uint32_t i = 1; i < annotation->NumOperands();) {
         for (uint32_t i = 1; i < annotation->NumOperands();) {
           Instruction* opInst =
           Instruction* opInst =
               get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i));
               get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i));
-          if (IsDead(opInst)) {
+          if (!IsLive(opInst)) {
             // Don't increment |i|.
             // Don't increment |i|.
             annotation->RemoveOperand(i);
             annotation->RemoveOperand(i);
             modified = true;
             modified = true;
@@ -785,7 +815,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
         for (uint32_t i = 1; i < annotation->NumOperands();) {
         for (uint32_t i = 1; i < annotation->NumOperands();) {
           Instruction* opInst =
           Instruction* opInst =
               get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i));
               get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i));
-          if (IsDead(opInst)) {
+          if (!IsLive(opInst)) {
             // Don't increment |i|.
             // Don't increment |i|.
             annotation->RemoveOperand(i + 1);
             annotation->RemoveOperand(i + 1);
             annotation->RemoveOperand(i);
             annotation->RemoveOperand(i);
@@ -819,13 +849,13 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
   }
   }
 
 
   for (auto& dbg : get_module()->ext_inst_debuginfo()) {
   for (auto& dbg : get_module()->ext_inst_debuginfo()) {
-    if (!IsDead(&dbg)) continue;
+    if (IsLive(&dbg)) continue;
     // Save GlobalVariable if its variable is live, otherwise null out variable
     // Save GlobalVariable if its variable is live, otherwise null out variable
     // index
     // index
     if (dbg.GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) {
     if (dbg.GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) {
       auto var_id = dbg.GetSingleWordOperand(kGlobalVariableVariableIndex);
       auto var_id = dbg.GetSingleWordOperand(kGlobalVariableVariableIndex);
       Instruction* var_inst = get_def_use_mgr()->GetDef(var_id);
       Instruction* var_inst = get_def_use_mgr()->GetDef(var_id);
-      if (!IsDead(var_inst)) continue;
+      if (IsLive(var_inst)) continue;
       context()->ForgetUses(&dbg);
       context()->ForgetUses(&dbg);
       dbg.SetOperand(
       dbg.SetOperand(
           kGlobalVariableVariableIndex,
           kGlobalVariableVariableIndex,
@@ -840,7 +870,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
   // Since ADCE is disabled for non-shaders, we don't check for export linkage
   // Since ADCE is disabled for non-shaders, we don't check for export linkage
   // attributes here.
   // attributes here.
   for (auto& val : get_module()->types_values()) {
   for (auto& val : get_module()->types_values()) {
-    if (IsDead(&val)) {
+    if (!IsLive(&val)) {
       // Save forwarded pointer if pointer is live since closure does not mark
       // Save forwarded pointer if pointer is live since closure does not mark
       // this live as it does not have a result id. This is a little too
       // this live as it does not have a result id. This is a little too
       // conservative since it is not known if the structure type that needed
       // conservative since it is not known if the structure type that needed
@@ -848,7 +878,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
       if (val.opcode() == SpvOpTypeForwardPointer) {
       if (val.opcode() == SpvOpTypeForwardPointer) {
         uint32_t ptr_ty_id = val.GetSingleWordInOperand(0);
         uint32_t ptr_ty_id = val.GetSingleWordInOperand(0);
         Instruction* ptr_ty_inst = get_def_use_mgr()->GetDef(ptr_ty_id);
         Instruction* ptr_ty_inst = get_def_use_mgr()->GetDef(ptr_ty_id);
-        if (!IsDead(ptr_ty_inst)) continue;
+        if (IsLive(ptr_ty_inst)) continue;
       }
       }
       to_kill_.push_back(&val);
       to_kill_.push_back(&val);
       modified = true;
       modified = true;
@@ -867,7 +897,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
         } else {
         } else {
           auto* var =
           auto* var =
               get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i));
               get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i));
-          if (!IsDead(var)) {
+          if (IsLive(var)) {
             new_operands.push_back(entry.GetInOperand(i));
             new_operands.push_back(entry.GetInOperand(i));
           }
           }
         }
         }
@@ -1018,5 +1048,43 @@ bool AggressiveDCEPass::BlockIsInConstruct(BasicBlock* header_block,
   return false;
   return false;
 }
 }
 
 
+bool AggressiveDCEPass::IsEntryPointWithNoCalls(Function* func) {
+  auto cached_result = entry_point_with_no_calls_cache_.find(func->result_id());
+  if (cached_result != entry_point_with_no_calls_cache_.end()) {
+    return cached_result->second;
+  }
+  bool result = IsEntryPoint(func) && !HasCall(func);
+  entry_point_with_no_calls_cache_[func->result_id()] = result;
+  return result;
+}
+
+bool AggressiveDCEPass::IsEntryPoint(Function* func) {
+  for (const Instruction& entry_point : get_module()->entry_points()) {
+    uint32_t entry_point_id =
+        entry_point.GetSingleWordInOperand(kEntryPointFunctionIdInIdx);
+    if (entry_point_id == func->result_id()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool AggressiveDCEPass::HasCall(Function* func) {
+  return !func->WhileEachInst(
+      [](Instruction* inst) { return inst->opcode() != SpvOpFunctionCall; });
+}
+
+void AggressiveDCEPass::MarkFirstBlockAsLive(Function* func) {
+  BasicBlock* first_block = &*func->begin();
+  MarkBlockAsLive(first_block->GetLabelInst());
+}
+
+void AggressiveDCEPass::AddUnreachable(BasicBlock*& block) {
+  InstructionBuilder builder(
+      context(), block,
+      IRContext::kAnalysisInstrToBlockMapping | IRContext::kAnalysisDefUse);
+  builder.AddUnreachable();
+}
+
 }  // namespace opt
 }  // namespace opt
 }  // namespace spvtools
 }  // namespace spvtools

+ 71 - 10
3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.h

@@ -67,19 +67,16 @@ class AggressiveDCEPass : public MemPass {
   // be 0 or the result of an instruction.
   // be 0 or the result of an instruction.
   bool IsVarOfStorage(uint32_t varId, uint32_t storageClass);
   bool IsVarOfStorage(uint32_t varId, uint32_t storageClass);
 
 
-  // Return true if |varId| is variable of function storage class or is
-  // private variable and privates can be optimized like locals (see
-  // privates_like_local_).
-  bool IsLocalVar(uint32_t varId);
+  // Return true if the instance of the variable |varId| can only be access in
+  // |func|.  For example, a function scope variable, or a private variable
+  // where |func| is an entry point with no function calls.
+  bool IsLocalVar(uint32_t varId, Function* func);
 
 
   // Return true if |inst| is marked live.
   // Return true if |inst| is marked live.
   bool IsLive(const Instruction* inst) const {
   bool IsLive(const Instruction* inst) const {
     return live_insts_.Get(inst->unique_id());
     return live_insts_.Get(inst->unique_id());
   }
   }
 
 
-  // Returns true if |inst| is dead.
-  bool IsDead(Instruction* inst);
-
   // Adds entry points, execution modes and workgroup size decorations to the
   // Adds entry points, execution modes and workgroup size decorations to the
   // worklist for processing with the first function.
   // worklist for processing with the first function.
   void InitializeModuleScopeLiveInstructions();
   void InitializeModuleScopeLiveInstructions();
@@ -139,7 +136,50 @@ class AggressiveDCEPass : public MemPass {
   // Adds instructions which must be kept because of they have side-effects
   // Adds instructions which must be kept because of they have side-effects
   // that ADCE cannot model to the work list.
   // that ADCE cannot model to the work list.
   void InitializeWorkList(Function* func,
   void InitializeWorkList(Function* func,
-                          std::list<BasicBlock*>& structuredOrder);
+                          std::list<BasicBlock*>& structured_order);
+
+  // Process each instruction in the work list by marking any instruction that
+  // that it depends on as live, and adding it to the work list.  The work list
+  // will be empty at the end.
+  void ProcessWorkList(Function* func);
+
+  // Kills any instructions in |func| that have not been marked as live.
+  bool KillDeadInstructions(const Function* func,
+                            std::list<BasicBlock*>& structured_order);
+
+  // Adds the instructions that define the operands of |inst| to the work list.
+  void AddOperandsToWorkList(const Instruction* inst);
+
+  // Marks all of the labels and branch that inst requires as live.
+  void MarkBlockAsLive(Instruction* inst);
+
+  // Marks any variables from which |inst| may require data as live.
+  void MarkLoadedVariablesAsLive(Function* func, Instruction* inst);
+
+  // Returns the id of the variable that |ptr_id| point to.  |ptr_id| must be a
+  // value whose type is a pointer.
+  uint32_t GetVariableId(uint32_t ptr_id);
+
+  // Returns all of the ids for the variables from which |inst| will load data.
+  std::vector<uint32_t> GetLoadedVariables(Instruction* inst);
+
+  // Returns all of the ids for the variables from which |inst| will load data.
+  // The opcode of |inst| must be  OpFunctionCall.
+  std::vector<uint32_t> GetLoadedVariablesFromFunctionCall(
+      const Instruction* inst);
+
+  // Returns the id of the variable from which |inst| will load data. |inst|
+  // must not be an OpFunctionCall.  Returns 0 if no data is read or the
+  // variable cannot be determined.  Note that in logical addressing mode the
+  // latter is not possible for function and private storage class because there
+  // cannot be variable pointers pointing to those storage classes.
+  uint32_t GetLoadedVariableFromNonFunctionCalls(Instruction* inst);
+
+  // Adds all decorations of |inst| to the work list.
+  void AddDecorationsToWorkList(const Instruction* inst);
+
+  // Adds all debug instruction associated with |inst| to the work list.
+  void AddDebugInstructionsToWorkList(const Instruction* inst);
 
 
   // Marks all of the OpFunctionParameter instructions in |func| as live.
   // Marks all of the OpFunctionParameter instructions in |func| as live.
   void MarkFunctionParameterAsLive(const Function* func);
   void MarkFunctionParameterAsLive(const Function* func);
@@ -164,8 +204,29 @@ class AggressiveDCEPass : public MemPass {
   // Returns true if |bb| is in the construct with header |header_block|.
   // Returns true if |bb| is in the construct with header |header_block|.
   bool BlockIsInConstruct(BasicBlock* header_block, BasicBlock* bb);
   bool BlockIsInConstruct(BasicBlock* header_block, BasicBlock* bb);
 
 
-  // True if current function is entry point and has no function calls.
-  bool private_like_local_;
+  // Returns true if |func| is an entry point that does not have any function
+  // calls.
+  bool IsEntryPointWithNoCalls(Function* func);
+
+  // Returns true if |func| is an entry point.
+  bool IsEntryPoint(Function* func);
+
+  // Returns true if |func| contains a function call.
+  bool HasCall(Function* func);
+
+  // Marks the first block, which is the entry block, in |func| as live.
+  void MarkFirstBlockAsLive(Function* func);
+
+  // Adds an OpUnreachable instruction at the end of |block|.
+  void AddUnreachable(BasicBlock*& block);
+
+  // Marks the OpLoopMerge and the terminator in |basic_block| as live if
+  // |basic_block| is a loop header.
+  void MarkLoopConstructAsLiveIfLoopHeader(BasicBlock* basic_block);
+
+  // The cached results for |IsEntryPointWithNoCalls|.  It maps the function's
+  // result id to the return value.
+  std::unordered_map<uint32_t, bool> entry_point_with_no_calls_cache_;
 
 
   // Live Instruction Worklist.  An instruction is added to this list
   // Live Instruction Worklist.  An instruction is added to this list
   // if it might have a side effect, either directly or indirectly.
   // if it might have a side effect, either directly or indirectly.

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

@@ -44,7 +44,7 @@ bool BlockMergePass::MergeBlocks(Function* func) {
 Pass::Status BlockMergePass::Process() {
 Pass::Status BlockMergePass::Process() {
   // Process all entry point functions.
   // Process all entry point functions.
   ProcessFunction pfn = [this](Function* fp) { return MergeBlocks(fp); };
   ProcessFunction pfn = [this](Function* fp) { return MergeBlocks(fp); };
-  bool modified = context()->ProcessEntryPointCallTree(pfn);
+  bool modified = context()->ProcessReachableCallTree(pfn);
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 }
 
 

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

@@ -171,12 +171,17 @@ void MergeWithSuccessor(IRContext* context, Function* func,
       // and OpBranchConditional.
       // and OpBranchConditional.
       auto terminator = bi->terminator();
       auto terminator = bi->terminator();
       auto& vec = terminator->dbg_line_insts();
       auto& vec = terminator->dbg_line_insts();
-      auto& new_vec = merge_inst->dbg_line_insts();
-      new_vec.insert(new_vec.end(), vec.begin(), vec.end());
-      terminator->ClearDbgLineInsts();
-      for (auto& l_inst : new_vec)
-        context->get_def_use_mgr()->AnalyzeInstDefUse(&l_inst);
-
+      if (vec.size() > 0) {
+        merge_inst->ClearDbgLineInsts();
+        auto& new_vec = merge_inst->dbg_line_insts();
+        new_vec.insert(new_vec.end(), vec.begin(), vec.end());
+        terminator->ClearDbgLineInsts();
+        for (auto& l_inst : new_vec)
+          context->get_def_use_mgr()->AnalyzeInstDefUse(&l_inst);
+      }
+      // Clear debug scope of terminator to avoid DebugScope
+      // emitted between terminator and merge.
+      terminator->SetDebugScope(DebugScope(kNoDebugScope, kNoInlinedAt));
       // Move the merge instruction to just before the terminator.
       // Move the merge instruction to just before the terminator.
       merge_inst->InsertBefore(terminator);
       merge_inst->InsertBefore(terminator);
     }
     }

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

@@ -291,6 +291,10 @@ bool CCPPass::ReplaceValues() {
 }
 }
 
 
 bool CCPPass::PropagateConstants(Function* fp) {
 bool CCPPass::PropagateConstants(Function* fp) {
+  if (fp->IsDeclaration()) {
+    return false;
+  }
+
   // Mark function parameters as varying.
   // Mark function parameters as varying.
   fp->ForEachParam([this](const Instruction* inst) {
   fp->ForEachParam([this](const Instruction* inst) {
     values_[inst->result_id()] = kVaryingSSAId;
     values_[inst->result_id()] = kVaryingSSAId;

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

@@ -34,6 +34,10 @@ Pass::Status CombineAccessChains::Process() {
 }
 }
 
 
 bool CombineAccessChains::ProcessFunction(Function& function) {
 bool CombineAccessChains::ProcessFunction(Function& function) {
+  if (function.IsDeclaration()) {
+    return false;
+  }
+
   bool modified = false;
   bool modified = false;
 
 
   cfg()->ForEachBlockInReversePostOrder(
   cfg()->ForEachBlockInReversePostOrder(

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

@@ -217,7 +217,8 @@ Instruction* ConstantManager::BuildInstructionAndAddToModule(
   auto* new_inst_ptr = new_inst.get();
   auto* new_inst_ptr = new_inst.get();
   *pos = pos->InsertBefore(std::move(new_inst));
   *pos = pos->InsertBefore(std::move(new_inst));
   ++(*pos);
   ++(*pos);
-  context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inst_ptr);
+  if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+    context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inst_ptr);
   MapConstantToInst(new_const, new_inst_ptr);
   MapConstantToInst(new_const, new_inst_ptr);
   return new_inst_ptr;
   return new_inst_ptr;
 }
 }

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

@@ -340,7 +340,7 @@ Pass::Status ConvertToHalfPass::ProcessImpl() {
   Pass::ProcessFunction pfn = [this](Function* fp) {
   Pass::ProcessFunction pfn = [this](Function* fp) {
     return ProcessFunction(fp);
     return ProcessFunction(fp);
   };
   };
-  bool modified = context()->ProcessEntryPointCallTree(pfn);
+  bool modified = context()->ProcessReachableCallTree(pfn);
   // If modified, make sure module has Float16 capability
   // If modified, make sure module has Float16 capability
   if (modified) context()->AddCapability(SpvCapabilityFloat16);
   if (modified) context()->AddCapability(SpvCapabilityFloat16);
   // Remove all RelaxedPrecision decorations from instructions and globals
   // Remove all RelaxedPrecision decorations from instructions and globals

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

@@ -40,6 +40,10 @@ bool IsDebugDeclareOrValue(Instruction* di) {
 Pass::Status CopyPropagateArrays::Process() {
 Pass::Status CopyPropagateArrays::Process() {
   bool modified = false;
   bool modified = false;
   for (Function& function : *get_module()) {
   for (Function& function : *get_module()) {
+    if (function.IsDeclaration()) {
+      continue;
+    }
+
     BasicBlock* entry_bb = &*function.begin();
     BasicBlock* entry_bb = &*function.begin();
 
 
     for (auto var_inst = entry_bb->begin(); var_inst->opcode() == SpvOpVariable;
     for (auto var_inst = entry_bb->begin(); var_inst->opcode() == SpvOpVariable;

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

@@ -346,6 +346,7 @@ bool DeadBranchElimPass::FixPhiNodesInLiveBlocks(
           if (operands.size() == 4) {
           if (operands.size() == 4) {
             // First input data operands is at index 2.
             // First input data operands is at index 2.
             uint32_t replId = operands[2u].words[0];
             uint32_t replId = operands[2u].words[0];
+            context()->KillNamesAndDecorates(inst->result_id());
             context()->ReplaceAllUsesWith(inst->result_id(), replId);
             context()->ReplaceAllUsesWith(inst->result_id(), replId);
             iter = context()->KillInst(&*inst);
             iter = context()->KillInst(&*inst);
           } else {
           } else {
@@ -419,6 +420,10 @@ bool DeadBranchElimPass::EraseDeadBlocks(
 }
 }
 
 
 bool DeadBranchElimPass::EliminateDeadBranches(Function* func) {
 bool DeadBranchElimPass::EliminateDeadBranches(Function* func) {
+  if (func->IsDeclaration()) {
+    return false;
+  }
+
   bool modified = false;
   bool modified = false;
   std::unordered_set<BasicBlock*> live_blocks;
   std::unordered_set<BasicBlock*> live_blocks;
   modified |= MarkLiveBlocks(func, &live_blocks);
   modified |= MarkLiveBlocks(func, &live_blocks);

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

@@ -256,7 +256,7 @@ Pass::Status DeadInsertElimPass::Process() {
   ProcessFunction pfn = [this](Function* fp) {
   ProcessFunction pfn = [this](Function* fp) {
     return EliminateDeadInserts(fp);
     return EliminateDeadInserts(fp);
   };
   };
-  bool modified = context()->ProcessEntryPointCallTree(pfn);
+  bool modified = context()->ProcessReachableCallTree(pfn);
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 }
 
 

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

@@ -146,6 +146,25 @@ void DebugInfoManager::RegisterDbgDeclare(uint32_t var_id,
   }
   }
 }
 }
 
 
+// Create new constant directly into global value area, bypassing the
+// Constant manager. This is used when the DefUse or Constant managers
+// are invalid and cannot be regenerated due to the module being in an
+// inconsistant state e.g. in the middle of significant modification
+// such as inlining. Invalidate Constant and DefUse managers if used.
+uint32_t AddNewConstInGlobals(IRContext* context, uint32_t const_value) {
+  uint32_t id = context->TakeNextId();
+  std::unique_ptr<Instruction> new_const(new Instruction(
+      context, SpvOpConstant, context->get_type_mgr()->GetUIntTypeId(), id,
+      {
+          {spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER,
+           {const_value}},
+      }));
+  context->module()->AddGlobalValue(std::move(new_const));
+  context->InvalidateAnalyses(IRContext::kAnalysisConstants);
+  context->InvalidateAnalyses(IRContext::kAnalysisDefUse);
+  return id;
+}
+
 uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
 uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
                                                 const DebugScope& scope) {
                                                 const DebugScope& scope) {
   uint32_t setId = GetDbgSetImportId();
   uint32_t setId = GetDbgSetImportId();
@@ -194,10 +213,18 @@ uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
     line_number = line->GetSingleWordOperand(kOpLineOperandLineIndex);
     line_number = line->GetSingleWordOperand(kOpLineOperandLineIndex);
 
 
     // If we need the line number as an ID, generate that constant now.
     // If we need the line number as an ID, generate that constant now.
+    // If Constant or DefUse managers are invalid, generate constant
+    // directly into the global value section of the module; do not
+    // use Constant manager which may attempt to invoke building of the
+    // DefUse manager which cannot be done during inlining. The extra
+    // constants that may be generated here is likely not significant
+    // and will likely be cleaned up in later passes.
     if (line_number_type == spv_operand_type_t::SPV_OPERAND_TYPE_ID) {
     if (line_number_type == spv_operand_type_t::SPV_OPERAND_TYPE_ID) {
-      uint32_t line_id =
-          context()->get_constant_mgr()->GetUIntConst(line_number);
-      line_number = line_id;
+      if (!context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse) ||
+          !context()->AreAnalysesValid(IRContext::Analysis::kAnalysisConstants))
+        line_number = AddNewConstInGlobals(context(), line_number);
+      else
+        line_number = context()->get_constant_mgr()->GetUIntConst(line_number);
     }
     }
   }
   }
 
 

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

@@ -490,6 +490,14 @@ void DecorationManager::ForEachDecoration(
   });
   });
 }
 }
 
 
+bool DecorationManager::HasDecoration(uint32_t id, uint32_t decoration) {
+  bool has_decoration = false;
+  ForEachDecoration(id, decoration, [&has_decoration](const Instruction&) {
+    has_decoration = true;
+  });
+  return has_decoration;
+}
+
 bool DecorationManager::FindDecoration(
 bool DecorationManager::FindDecoration(
     uint32_t id, uint32_t decoration,
     uint32_t id, uint32_t decoration,
     std::function<bool(const Instruction&)> f) {
     std::function<bool(const Instruction&)> f) {

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

@@ -90,6 +90,10 @@ class DecorationManager {
   bool AreDecorationsTheSame(const Instruction* inst1, const Instruction* inst2,
   bool AreDecorationsTheSame(const Instruction* inst1, const Instruction* inst2,
                              bool ignore_target) const;
                              bool ignore_target) const;
 
 
+  // Returns whether a decoration instruction for |id| with decoration
+  // |decoration| exists or not.
+  bool HasDecoration(uint32_t id, uint32_t decoration);
+
   // |f| is run on each decoration instruction for |id| with decoration
   // |f| is run on each decoration instruction for |id| with decoration
   // |decoration|. Processed are all decorations which target |id| either
   // |decoration|. Processed are all decorations which target |id| either
   // directly or indirectly by Decoration Groups.
   // directly or indirectly by Decoration Groups.

+ 13 - 106
3rdparty/spirv-tools/source/opt/desc_sroa.cpp

@@ -14,6 +14,7 @@
 
 
 #include "source/opt/desc_sroa.h"
 #include "source/opt/desc_sroa.h"
 
 
+#include "source/opt/desc_sroa_util.h"
 #include "source/util/string_utils.h"
 #include "source/util/string_utils.h"
 
 
 namespace spvtools {
 namespace spvtools {
@@ -25,7 +26,7 @@ Pass::Status DescriptorScalarReplacement::Process() {
   std::vector<Instruction*> vars_to_kill;
   std::vector<Instruction*> vars_to_kill;
 
 
   for (Instruction& var : context()->types_values()) {
   for (Instruction& var : context()->types_values()) {
-    if (IsCandidate(&var)) {
+    if (descsroautil::IsDescriptorArray(context(), &var)) {
       modified = true;
       modified = true;
       if (!ReplaceCandidate(&var)) {
       if (!ReplaceCandidate(&var)) {
         return Status::Failure;
         return Status::Failure;
@@ -41,72 +42,6 @@ Pass::Status DescriptorScalarReplacement::Process() {
   return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
   return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
 }
 }
 
 
-bool DescriptorScalarReplacement::IsCandidate(Instruction* var) {
-  if (var->opcode() != SpvOpVariable) {
-    return false;
-  }
-
-  uint32_t ptr_type_id = var->type_id();
-  Instruction* ptr_type_inst =
-      context()->get_def_use_mgr()->GetDef(ptr_type_id);
-  if (ptr_type_inst->opcode() != SpvOpTypePointer) {
-    return false;
-  }
-
-  uint32_t var_type_id = ptr_type_inst->GetSingleWordInOperand(1);
-  Instruction* var_type_inst =
-      context()->get_def_use_mgr()->GetDef(var_type_id);
-  if (var_type_inst->opcode() != SpvOpTypeArray &&
-      var_type_inst->opcode() != SpvOpTypeStruct) {
-    return false;
-  }
-
-  // All structures with descriptor assignments must be replaced by variables,
-  // one for each of their members - with the exceptions of buffers.
-  if (IsTypeOfStructuredBuffer(var_type_inst)) {
-    return false;
-  }
-
-  bool has_desc_set_decoration = false;
-  context()->get_decoration_mgr()->ForEachDecoration(
-      var->result_id(), SpvDecorationDescriptorSet,
-      [&has_desc_set_decoration](const Instruction&) {
-        has_desc_set_decoration = true;
-      });
-  if (!has_desc_set_decoration) {
-    return false;
-  }
-
-  bool has_binding_decoration = false;
-  context()->get_decoration_mgr()->ForEachDecoration(
-      var->result_id(), SpvDecorationBinding,
-      [&has_binding_decoration](const Instruction&) {
-        has_binding_decoration = true;
-      });
-  if (!has_binding_decoration) {
-    return false;
-  }
-
-  return true;
-}
-
-bool DescriptorScalarReplacement::IsTypeOfStructuredBuffer(
-    const Instruction* type) const {
-  if (type->opcode() != SpvOpTypeStruct) {
-    return false;
-  }
-
-  // All buffers have offset decorations for members of their structure types.
-  // This is how we distinguish it from a structure of descriptors.
-  bool has_offset_decoration = false;
-  context()->get_decoration_mgr()->ForEachDecoration(
-      type->result_id(), SpvDecorationOffset,
-      [&has_offset_decoration](const Instruction&) {
-        has_offset_decoration = true;
-      });
-  return has_offset_decoration;
-}
-
 bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) {
 bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) {
   std::vector<Instruction*> access_chain_work_list;
   std::vector<Instruction*> access_chain_work_list;
   std::vector<Instruction*> load_work_list;
   std::vector<Instruction*> load_work_list;
@@ -162,16 +97,15 @@ bool DescriptorScalarReplacement::ReplaceAccessChain(Instruction* var,
     return false;
     return false;
   }
   }
 
 
-  uint32_t idx_id = use->GetSingleWordInOperand(1);
-  const analysis::Constant* idx_const =
-      context()->get_constant_mgr()->FindDeclaredConstant(idx_id);
-  if (idx_const == nullptr) {
+  const analysis::Constant* const_index =
+      descsroautil::GetAccessChainIndexAsConst(context(), use);
+  if (const_index == nullptr) {
     context()->EmitErrorMessage("Variable cannot be replaced: invalid index",
     context()->EmitErrorMessage("Variable cannot be replaced: invalid index",
                                 use);
                                 use);
     return false;
     return false;
   }
   }
 
 
-  uint32_t idx = idx_const->GetU32();
+  uint32_t idx = const_index->GetU32();
   uint32_t replacement_var = GetReplacementVariable(var, idx);
   uint32_t replacement_var = GetReplacementVariable(var, idx);
 
 
   if (use->NumInOperands() == 2) {
   if (use->NumInOperands() == 2) {
@@ -208,39 +142,12 @@ uint32_t DescriptorScalarReplacement::GetReplacementVariable(Instruction* var,
                                                              uint32_t idx) {
                                                              uint32_t idx) {
   auto replacement_vars = replacement_variables_.find(var);
   auto replacement_vars = replacement_variables_.find(var);
   if (replacement_vars == replacement_variables_.end()) {
   if (replacement_vars == replacement_variables_.end()) {
-    uint32_t ptr_type_id = var->type_id();
-    Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id);
-    assert(ptr_type_inst->opcode() == SpvOpTypePointer &&
-           "Variable should be a pointer to an array or structure.");
-    uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1);
-    Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(pointee_type_id);
-    const bool is_array = pointee_type_inst->opcode() == SpvOpTypeArray;
-    const bool is_struct = pointee_type_inst->opcode() == SpvOpTypeStruct;
-    assert((is_array || is_struct) &&
-           "Variable should be a pointer to an array or structure.");
-
-    // For arrays, each array element should be replaced with a new replacement
-    // variable
-    if (is_array) {
-      uint32_t array_len_id = pointee_type_inst->GetSingleWordInOperand(1);
-      const analysis::Constant* array_len_const =
-          context()->get_constant_mgr()->FindDeclaredConstant(array_len_id);
-      assert(array_len_const != nullptr && "Array length must be a constant.");
-      uint32_t array_len = array_len_const->GetU32();
-
-      replacement_vars = replacement_variables_
-                             .insert({var, std::vector<uint32_t>(array_len, 0)})
-                             .first;
-    }
-    // For structures, each member should be replaced with a new replacement
-    // variable
-    if (is_struct) {
-      const uint32_t num_members = pointee_type_inst->NumInOperands();
-      replacement_vars =
-          replacement_variables_
-              .insert({var, std::vector<uint32_t>(num_members, 0)})
-              .first;
-    }
+    uint32_t number_of_elements =
+        descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var);
+    replacement_vars =
+        replacement_variables_
+            .insert({var, std::vector<uint32_t>(number_of_elements, 0)})
+            .first;
   }
   }
 
 
   if (replacement_vars->second[idx] == 0) {
   if (replacement_vars->second[idx] == 0) {
@@ -377,7 +284,7 @@ uint32_t DescriptorScalarReplacement::GetNumBindingsUsedByType(
   // The number of bindings consumed by a structure is the sum of the bindings
   // The number of bindings consumed by a structure is the sum of the bindings
   // used by its members.
   // used by its members.
   if (type_inst->opcode() == SpvOpTypeStruct &&
   if (type_inst->opcode() == SpvOpTypeStruct &&
-      !IsTypeOfStructuredBuffer(type_inst)) {
+      !descsroautil::IsTypeOfStructuredBuffer(context(), type_inst)) {
     uint32_t sum = 0;
     uint32_t sum = 0;
     for (uint32_t i = 0; i < type_inst->NumInOperands(); i++)
     for (uint32_t i = 0; i < type_inst->NumInOperands(); i++)
       sum += GetNumBindingsUsedByType(type_inst->GetSingleWordInOperand(i));
       sum += GetNumBindingsUsedByType(type_inst->GetSingleWordInOperand(i));

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

@@ -46,10 +46,6 @@ class DescriptorScalarReplacement : public Pass {
   }
   }
 
 
  private:
  private:
-  // Returns true if |var| is an OpVariable instruction that represents a
-  // descriptor array.  These are the variables that we want to replace.
-  bool IsCandidate(Instruction* var);
-
   // Replaces all references to |var| by new variables, one for each element of
   // Replaces all references to |var| by new variables, one for each element of
   // the array |var|.  The binding for the new variables corresponding to
   // the array |var|.  The binding for the new variables corresponding to
   // element i will be the binding of |var| plus i.  Returns true if successful.
   // element i will be the binding of |var| plus i.  Returns true if successful.
@@ -93,11 +89,6 @@ class DescriptorScalarReplacement : public Pass {
   // bindings used by its members.
   // bindings used by its members.
   uint32_t GetNumBindingsUsedByType(uint32_t type_id);
   uint32_t GetNumBindingsUsedByType(uint32_t type_id);
 
 
-  // Returns true if |type| is a type that could be used for a structured buffer
-  // as opposed to a type that would be used for a structure of resource
-  // descriptors.
-  bool IsTypeOfStructuredBuffer(const Instruction* type) const;
-
   // A map from an OpVariable instruction to the set of variables that will be
   // A map from an OpVariable instruction to the set of variables that will be
   // used to replace it. The entry |replacement_variables_[var][i]| is the id of
   // used to replace it. The entry |replacement_variables_[var][i]| is the id of
   // a variable that will be used in the place of the the ith element of the
   // a variable that will be used in the place of the the ith element of the

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

@@ -0,0 +1,117 @@
+// Copyright (c) 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/opt/desc_sroa_util.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+const uint32_t kOpAccessChainInOperandIndexes = 1;
+
+// Returns the length of array type |type|.
+uint32_t GetLengthOfArrayType(IRContext* context, Instruction* type) {
+  assert(type->opcode() == SpvOpTypeArray && "type must be array");
+  uint32_t length_id = type->GetSingleWordInOperand(1);
+  const analysis::Constant* length_const =
+      context->get_constant_mgr()->FindDeclaredConstant(length_id);
+  assert(length_const != nullptr);
+  return length_const->GetU32();
+}
+
+}  // namespace
+
+namespace descsroautil {
+
+bool IsDescriptorArray(IRContext* context, Instruction* var) {
+  if (var->opcode() != SpvOpVariable) {
+    return false;
+  }
+
+  uint32_t ptr_type_id = var->type_id();
+  Instruction* ptr_type_inst = context->get_def_use_mgr()->GetDef(ptr_type_id);
+  if (ptr_type_inst->opcode() != SpvOpTypePointer) {
+    return false;
+  }
+
+  uint32_t var_type_id = ptr_type_inst->GetSingleWordInOperand(1);
+  Instruction* var_type_inst = context->get_def_use_mgr()->GetDef(var_type_id);
+  if (var_type_inst->opcode() != SpvOpTypeArray &&
+      var_type_inst->opcode() != SpvOpTypeStruct) {
+    return false;
+  }
+
+  // All structures with descriptor assignments must be replaced by variables,
+  // one for each of their members - with the exceptions of buffers.
+  if (IsTypeOfStructuredBuffer(context, var_type_inst)) {
+    return false;
+  }
+
+  if (!context->get_decoration_mgr()->HasDecoration(
+          var->result_id(), SpvDecorationDescriptorSet)) {
+    return false;
+  }
+
+  return context->get_decoration_mgr()->HasDecoration(var->result_id(),
+                                                      SpvDecorationBinding);
+}
+
+bool IsTypeOfStructuredBuffer(IRContext* context, const Instruction* type) {
+  if (type->opcode() != SpvOpTypeStruct) {
+    return false;
+  }
+
+  // All buffers have offset decorations for members of their structure types.
+  // This is how we distinguish it from a structure of descriptors.
+  return context->get_decoration_mgr()->HasDecoration(type->result_id(),
+                                                      SpvDecorationOffset);
+}
+
+const analysis::Constant* GetAccessChainIndexAsConst(
+    IRContext* context, Instruction* access_chain) {
+  if (access_chain->NumInOperands() <= 1) {
+    return nullptr;
+  }
+  uint32_t idx_id = GetFirstIndexOfAccessChain(access_chain);
+  const analysis::Constant* idx_const =
+      context->get_constant_mgr()->FindDeclaredConstant(idx_id);
+  return idx_const;
+}
+
+uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain) {
+  assert(access_chain->NumInOperands() > 1 &&
+         "OpAccessChain does not have Indexes operand");
+  return access_chain->GetSingleWordInOperand(kOpAccessChainInOperandIndexes);
+}
+
+uint32_t GetNumberOfElementsForArrayOrStruct(IRContext* context,
+                                             Instruction* var) {
+  uint32_t ptr_type_id = var->type_id();
+  Instruction* ptr_type_inst = context->get_def_use_mgr()->GetDef(ptr_type_id);
+  assert(ptr_type_inst->opcode() == SpvOpTypePointer &&
+         "Variable should be a pointer to an array or structure.");
+  uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1);
+  Instruction* pointee_type_inst =
+      context->get_def_use_mgr()->GetDef(pointee_type_id);
+  if (pointee_type_inst->opcode() == SpvOpTypeArray) {
+    return GetLengthOfArrayType(context, pointee_type_inst);
+  }
+  assert(pointee_type_inst->opcode() == SpvOpTypeStruct &&
+         "Variable should be a pointer to an array or structure.");
+  return pointee_type_inst->NumInOperands();
+}
+
+}  // namespace descsroautil
+}  // namespace opt
+}  // namespace spvtools

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

@@ -0,0 +1,54 @@
+// Copyright (c) 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_OPT_DESC_SROA_UTIL_H_
+#define SOURCE_OPT_DESC_SROA_UTIL_H_
+
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace opt {
+
+// Provides functions for the descriptor array SROA.
+namespace descsroautil {
+
+// Returns true if |var| is an OpVariable instruction that represents a
+// descriptor array.
+bool IsDescriptorArray(IRContext* context, Instruction* var);
+
+// Returns true if |type| is a type that could be used for a structured buffer
+// as opposed to a type that would be used for a structure of resource
+// descriptors.
+bool IsTypeOfStructuredBuffer(IRContext* context, const Instruction* type);
+
+// Returns the first index of the OpAccessChain instruction |access_chain| as
+// a constant. Returns nullptr if it is not a constant.
+const analysis::Constant* GetAccessChainIndexAsConst(IRContext* context,
+                                                     Instruction* access_chain);
+
+// Returns the number of elements of an OpVariable instruction |var| whose type
+// must be a pointer to an array or a struct.
+uint32_t GetNumberOfElementsForArrayOrStruct(IRContext* context,
+                                             Instruction* var);
+
+// Returns the first Indexes operand id of the OpAccessChain or
+// OpInBoundsAccessChain instruction |access_chain|. The access chain must have
+// at least 1 index.
+uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain);
+
+}  // namespace descsroautil
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // SOURCE_OPT_DESC_SROA_UTIL_H_

+ 4 - 14
3rdparty/spirv-tools/source/opt/folding_rules.cpp

@@ -968,25 +968,17 @@ FoldingRule MergeDivMulArithmetic() {
 FoldingRule MergeDivNegateArithmetic() {
 FoldingRule MergeDivNegateArithmetic() {
   return [](IRContext* context, Instruction* inst,
   return [](IRContext* context, Instruction* inst,
             const std::vector<const analysis::Constant*>& constants) {
             const std::vector<const analysis::Constant*>& constants) {
-    assert(inst->opcode() == SpvOpFDiv || inst->opcode() == SpvOpSDiv);
+    assert(inst->opcode() == SpvOpFDiv);
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
     analysis::ConstantManager* const_mgr = context->get_constant_mgr();
-    const analysis::Type* type =
-        context->get_type_mgr()->GetType(inst->type_id());
-    bool uses_float = HasFloatingPoint(type);
-    if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false;
-
-    uint32_t width = ElementWidth(type);
-    if (width != 32 && width != 64) return false;
+    if (!inst->IsFloatingPointFoldingAllowed()) return false;
 
 
     const analysis::Constant* const_input1 = ConstInput(constants);
     const analysis::Constant* const_input1 = ConstInput(constants);
     if (!const_input1) return false;
     if (!const_input1) return false;
     Instruction* other_inst = NonConstInput(context, constants[0], inst);
     Instruction* other_inst = NonConstInput(context, constants[0], inst);
-    if (uses_float && !other_inst->IsFloatingPointFoldingAllowed())
-      return false;
+    if (!other_inst->IsFloatingPointFoldingAllowed()) return false;
 
 
     bool first_is_variable = constants[0] == nullptr;
     bool first_is_variable = constants[0] == nullptr;
-    if (other_inst->opcode() == SpvOpFNegate ||
-        other_inst->opcode() == SpvOpSNegate) {
+    if (other_inst->opcode() == SpvOpFNegate) {
       uint32_t neg_id = NegateConstant(const_mgr, const_input1);
       uint32_t neg_id = NegateConstant(const_mgr, const_input1);
 
 
       if (first_is_variable) {
       if (first_is_variable) {
@@ -2589,8 +2581,6 @@ void FoldingRules::AddFoldingRules() {
 
 
   rules_[SpvOpPhi].push_back(RedundantPhi());
   rules_[SpvOpPhi].push_back(RedundantPhi());
 
 
-  rules_[SpvOpSDiv].push_back(MergeDivNegateArithmetic());
-
   rules_[SpvOpSNegate].push_back(MergeNegateArithmetic());
   rules_[SpvOpSNegate].push_back(MergeNegateArithmetic());
   rules_[SpvOpSNegate].push_back(MergeNegateMulDivArithmetic());
   rules_[SpvOpSNegate].push_back(MergeNegateMulDivArithmetic());
   rules_[SpvOpSNegate].push_back(MergeNegateAddSubArithmetic());
   rules_[SpvOpSNegate].push_back(MergeNegateAddSubArithmetic());

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

@@ -177,6 +177,9 @@ class Function {
   // debuggers.
   // debuggers.
   void Dump() const;
   void Dump() const;
 
 
+  // Returns true is a function declaration and not a function definition.
+  bool IsDeclaration() { return begin() == end(); }
+
  private:
  private:
   // The OpFunction instruction that begins the definition of this function.
   // The OpFunction instruction that begins the definition of this function.
   std::unique_ptr<Instruction> def_inst_;
   std::unique_ptr<Instruction> def_inst_;

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

@@ -65,7 +65,7 @@ Pass::Status InlineExhaustivePass::ProcessImpl() {
     status = CombineStatus(status, InlineExhaustive(fp));
     status = CombineStatus(status, InlineExhaustive(fp));
     return false;
     return false;
   };
   };
-  context()->ProcessEntryPointCallTree(pfn);
+  context()->ProcessReachableCallTree(pfn);
   return status;
   return status;
 }
 }
 
 

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

@@ -105,7 +105,7 @@ Pass::Status InlineOpaquePass::ProcessImpl() {
     status = CombineStatus(status, InlineOpaque(fp));
     status = CombineStatus(status, InlineOpaque(fp));
     return false;
     return false;
   };
   };
-  context()->ProcessEntryPointCallTree(pfn);
+  context()->ProcessReachableCallTree(pfn);
   return status;
   return status;
 }
 }
 
 

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

@@ -142,7 +142,7 @@ inline IteratorRange<IteratorType> make_range(const IteratorType& begin,
 template <typename IteratorType>
 template <typename IteratorType>
 inline IteratorRange<IteratorType> make_range(IteratorType&& begin,
 inline IteratorRange<IteratorType> make_range(IteratorType&& begin,
                                               IteratorType&& end) {
                                               IteratorType&& end) {
-  return {std::move(begin), std::move(end)};
+  return {std::forward<IteratorType>(begin), std::forward<IteratorType>(end)};
 }
 }
 
 
 // Returns a (begin, end) iterator pair for the given container.
 // Returns a (begin, end) iterator pair for the given container.

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

@@ -223,7 +223,7 @@ Pass::Status LocalSingleBlockLoadStoreElimPass::ProcessImpl() {
     return LocalSingleBlockLoadStoreElim(fp);
     return LocalSingleBlockLoadStoreElim(fp);
   };
   };
 
 
-  bool modified = context()->ProcessEntryPointCallTree(pfn);
+  bool modified = context()->ProcessReachableCallTree(pfn);
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 }
 
 

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

@@ -81,7 +81,7 @@ Pass::Status LocalSingleStoreElimPass::ProcessImpl() {
   ProcessFunction pfn = [this](Function* fp) {
   ProcessFunction pfn = [this](Function* fp) {
     return LocalSingleStoreElim(fp);
     return LocalSingleStoreElim(fp);
   };
   };
-  bool modified = context()->ProcessEntryPointCallTree(pfn);
+  bool modified = context()->ProcessReachableCallTree(pfn);
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 }
 
 

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

@@ -1102,6 +1102,10 @@ void LoopUtils::Finalize() {
 Pass::Status LoopUnroller::Process() {
 Pass::Status LoopUnroller::Process() {
   bool changed = false;
   bool changed = false;
   for (Function& f : *context()->module()) {
   for (Function& f : *context()->module()) {
+    if (f.IsDeclaration()) {
+      continue;
+    }
+
     LoopDescriptor* LD = context()->GetLoopDescriptor(&f);
     LoopDescriptor* LD = context()->GetLoopDescriptor(&f);
     for (Loop& loop : *LD) {
     for (Loop& loop : *LD) {
       LoopUtils loop_utils{context(), &loop};
       LoopUtils loop_utils{context(), &loop};

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

@@ -86,8 +86,8 @@ bool MemPass::IsPtr(uint32_t ptrId) {
   }
   }
   const SpvOp op = ptrInst->opcode();
   const SpvOp op = ptrInst->opcode();
   if (op == SpvOpVariable || IsNonPtrAccessChain(op)) return true;
   if (op == SpvOpVariable || IsNonPtrAccessChain(op)) return true;
-  if (op != SpvOpFunctionParameter) return false;
   const uint32_t varTypeId = ptrInst->type_id();
   const uint32_t varTypeId = ptrInst->type_id();
+  if (varTypeId == 0) return false;
   const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId);
   const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId);
   return varTypeInst->opcode() == SpvOpTypePointer;
   return varTypeInst->opcode() == SpvOpTypePointer;
 }
 }

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

@@ -320,6 +320,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
     RegisterPass(CreateCombineAccessChainsPass());
     RegisterPass(CreateCombineAccessChainsPass());
   } else if (pass_name == "convert-local-access-chains") {
   } else if (pass_name == "convert-local-access-chains") {
     RegisterPass(CreateLocalAccessChainConvertPass());
     RegisterPass(CreateLocalAccessChainConvertPass());
+  } else if (pass_name == "replace-desc-array-access-using-var-index") {
+    RegisterPass(CreateReplaceDescArrayAccessUsingVarIndexPass());
   } else if (pass_name == "descriptor-scalar-replacement") {
   } else if (pass_name == "descriptor-scalar-replacement") {
     RegisterPass(CreateDescriptorScalarReplacementPass());
     RegisterPass(CreateDescriptorScalarReplacementPass());
   } else if (pass_name == "eliminate-dead-code-aggressive") {
   } else if (pass_name == "eliminate-dead-code-aggressive") {
@@ -958,6 +960,11 @@ Optimizer::PassToken CreateGraphicsRobustAccessPass() {
       MakeUnique<opt::GraphicsRobustAccessPass>());
       MakeUnique<opt::GraphicsRobustAccessPass>());
 }
 }
 
 
+Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass() {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::ReplaceDescArrayAccessUsingVarIndex>());
+}
+
 Optimizer::PassToken CreateDescriptorScalarReplacementPass() {
 Optimizer::PassToken CreateDescriptorScalarReplacementPass() {
   return MakeUnique<Optimizer::PassToken::Impl>(
   return MakeUnique<Optimizer::PassToken::Impl>(
       MakeUnique<opt::DescriptorScalarReplacement>());
       MakeUnique<opt::DescriptorScalarReplacement>());

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

@@ -66,6 +66,7 @@
 #include "source/opt/relax_float_ops_pass.h"
 #include "source/opt/relax_float_ops_pass.h"
 #include "source/opt/remove_duplicates_pass.h"
 #include "source/opt/remove_duplicates_pass.h"
 #include "source/opt/remove_unused_interface_variables_pass.h"
 #include "source/opt/remove_unused_interface_variables_pass.h"
+#include "source/opt/replace_desc_array_access_using_var_index.h"
 #include "source/opt/replace_invalid_opc.h"
 #include "source/opt/replace_invalid_opc.h"
 #include "source/opt/scalar_replacement_pass.h"
 #include "source/opt/scalar_replacement_pass.h"
 #include "source/opt/set_spec_constant_default_value_pass.h"
 #include "source/opt/set_spec_constant_default_value_pass.h"

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

@@ -24,6 +24,10 @@ Pass::Status RedundancyEliminationPass::Process() {
   ValueNumberTable vnTable(context());
   ValueNumberTable vnTable(context());
 
 
   for (auto& func : *get_module()) {
   for (auto& func : *get_module()) {
+    if (func.IsDeclaration()) {
+      continue;
+    }
+
     // Build the dominator tree for this function. It is how the code is
     // Build the dominator tree for this function. It is how the code is
     // traversed.
     // traversed.
     DominatorTree& dom_tree =
     DominatorTree& dom_tree =

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

@@ -51,7 +51,8 @@ inline bool IsTypeInst(SpvOp opcode) {
          opcode == SpvOpTypeCooperativeMatrixNV;
          opcode == SpvOpTypeCooperativeMatrixNV;
 }
 }
 inline bool IsConstantInst(SpvOp opcode) {
 inline bool IsConstantInst(SpvOp opcode) {
-  return opcode >= SpvOpConstantTrue && opcode <= SpvOpSpecConstantOp;
+  return (opcode >= SpvOpConstantTrue && opcode <= SpvOpSpecConstantOp) ||
+         opcode == SpvOpConstFunctionPointerINTEL;
 }
 }
 inline bool IsCompileTimeConstantInst(SpvOp opcode) {
 inline bool IsCompileTimeConstantInst(SpvOp opcode) {
   return opcode >= SpvOpConstantTrue && opcode <= SpvOpConstantNull;
   return opcode >= SpvOpConstantTrue && opcode <= SpvOpConstantNull;

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

@@ -76,7 +76,7 @@ Pass::Status RelaxFloatOpsPass::ProcessImpl() {
   Pass::ProcessFunction pfn = [this](Function* fp) {
   Pass::ProcessFunction pfn = [this](Function* fp) {
     return ProcessFunction(fp);
     return ProcessFunction(fp);
   };
   };
-  bool modified = context()->ProcessEntryPointCallTree(pfn);
+  bool modified = context()->ProcessReachableCallTree(pfn);
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 }
 
 

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

@@ -0,0 +1,423 @@
+// Copyright (c) 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/opt/replace_desc_array_access_using_var_index.h"
+
+#include "source/opt/desc_sroa_util.h"
+#include "source/opt/ir_builder.h"
+#include "source/util/string_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+const uint32_t kOpAccessChainInOperandIndexes = 1;
+const uint32_t kOpTypePointerInOperandType = 1;
+const uint32_t kOpTypeArrayInOperandType = 0;
+const uint32_t kOpTypeStructInOperandMember = 0;
+IRContext::Analysis kAnalysisDefUseAndInstrToBlockMapping =
+    IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping;
+
+uint32_t GetValueWithKeyExistenceCheck(
+    uint32_t key, const std::unordered_map<uint32_t, uint32_t>& map) {
+  auto itr = map.find(key);
+  assert(itr != map.end() && "Key does not exist");
+  return itr->second;
+}
+
+}  // namespace
+
+Pass::Status ReplaceDescArrayAccessUsingVarIndex::Process() {
+  Status status = Status::SuccessWithoutChange;
+  for (Instruction& var : context()->types_values()) {
+    if (descsroautil::IsDescriptorArray(context(), &var)) {
+      if (ReplaceVariableAccessesWithConstantElements(&var))
+        status = Status::SuccessWithChange;
+    }
+  }
+  return status;
+}
+
+bool ReplaceDescArrayAccessUsingVarIndex::
+    ReplaceVariableAccessesWithConstantElements(Instruction* var) const {
+  std::vector<Instruction*> work_list;
+  get_def_use_mgr()->ForEachUser(var, [&work_list](Instruction* use) {
+    switch (use->opcode()) {
+      case SpvOpAccessChain:
+      case SpvOpInBoundsAccessChain:
+        work_list.push_back(use);
+        break;
+      default:
+        break;
+    }
+  });
+
+  bool updated = false;
+  for (Instruction* access_chain : work_list) {
+    if (descsroautil::GetAccessChainIndexAsConst(context(), access_chain) ==
+        nullptr) {
+      ReplaceAccessChain(var, access_chain);
+      updated = true;
+    }
+  }
+  // Note that we do not consider OpLoad and OpCompositeExtract because
+  // OpCompositeExtract always has constant literals for indices.
+  return updated;
+}
+
+void ReplaceDescArrayAccessUsingVarIndex::ReplaceAccessChain(
+    Instruction* var, Instruction* access_chain) const {
+  uint32_t number_of_elements =
+      descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var);
+  assert(number_of_elements != 0 && "Number of element is 0");
+  if (number_of_elements == 1) {
+    UseConstIndexForAccessChain(access_chain, 0);
+    get_def_use_mgr()->AnalyzeInstUse(access_chain);
+    return;
+  }
+  ReplaceUsersOfAccessChain(access_chain, number_of_elements);
+}
+
+void ReplaceDescArrayAccessUsingVarIndex::ReplaceUsersOfAccessChain(
+    Instruction* access_chain, uint32_t number_of_elements) const {
+  std::vector<Instruction*> final_users;
+  CollectRecursiveUsersWithConcreteType(access_chain, &final_users);
+  for (auto* inst : final_users) {
+    std::deque<Instruction*> insts_to_be_cloned =
+        CollectRequiredImageInsts(inst);
+    ReplaceNonUniformAccessWithSwitchCase(
+        inst, access_chain, number_of_elements, insts_to_be_cloned);
+  }
+}
+
+void ReplaceDescArrayAccessUsingVarIndex::CollectRecursiveUsersWithConcreteType(
+    Instruction* access_chain, std::vector<Instruction*>* final_users) const {
+  std::queue<Instruction*> work_list;
+  work_list.push(access_chain);
+  while (!work_list.empty()) {
+    auto* inst_from_work_list = work_list.front();
+    work_list.pop();
+    get_def_use_mgr()->ForEachUser(
+        inst_from_work_list, [this, final_users, &work_list](Instruction* use) {
+          // TODO: Support Boolean type as well.
+          if (!use->HasResultId() || IsConcreteType(use->type_id())) {
+            final_users->push_back(use);
+          } else {
+            work_list.push(use);
+          }
+        });
+  }
+}
+
+std::deque<Instruction*>
+ReplaceDescArrayAccessUsingVarIndex::CollectRequiredImageInsts(
+    Instruction* user_of_image_insts) const {
+  std::unordered_set<uint32_t> seen_inst_ids;
+  std::queue<Instruction*> work_list;
+
+  auto decision_to_include_operand = [this, &seen_inst_ids,
+                                      &work_list](uint32_t* idp) {
+    if (!seen_inst_ids.insert(*idp).second) return;
+    Instruction* operand = get_def_use_mgr()->GetDef(*idp);
+    if (context()->get_instr_block(operand) != nullptr &&
+        HasImageOrImagePtrType(operand)) {
+      work_list.push(operand);
+    }
+  };
+
+  std::deque<Instruction*> required_image_insts;
+  required_image_insts.push_front(user_of_image_insts);
+  user_of_image_insts->ForEachInId(decision_to_include_operand);
+  while (!work_list.empty()) {
+    auto* inst_from_work_list = work_list.front();
+    work_list.pop();
+    required_image_insts.push_front(inst_from_work_list);
+    inst_from_work_list->ForEachInId(decision_to_include_operand);
+  }
+  return required_image_insts;
+}
+
+bool ReplaceDescArrayAccessUsingVarIndex::HasImageOrImagePtrType(
+    const Instruction* inst) const {
+  assert(inst != nullptr && inst->type_id() != 0 && "Invalid instruction");
+  return IsImageOrImagePtrType(get_def_use_mgr()->GetDef(inst->type_id()));
+}
+
+bool ReplaceDescArrayAccessUsingVarIndex::IsImageOrImagePtrType(
+    const Instruction* type_inst) const {
+  if (type_inst->opcode() == SpvOpTypeImage ||
+      type_inst->opcode() == SpvOpTypeSampler ||
+      type_inst->opcode() == SpvOpTypeSampledImage) {
+    return true;
+  }
+  if (type_inst->opcode() == SpvOpTypePointer) {
+    Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(
+        type_inst->GetSingleWordInOperand(kOpTypePointerInOperandType));
+    return IsImageOrImagePtrType(pointee_type_inst);
+  }
+  if (type_inst->opcode() == SpvOpTypeArray) {
+    Instruction* element_type_inst = get_def_use_mgr()->GetDef(
+        type_inst->GetSingleWordInOperand(kOpTypeArrayInOperandType));
+    return IsImageOrImagePtrType(element_type_inst);
+  }
+  if (type_inst->opcode() != SpvOpTypeStruct) return false;
+  for (uint32_t in_operand_idx = kOpTypeStructInOperandMember;
+       in_operand_idx < type_inst->NumInOperands(); ++in_operand_idx) {
+    Instruction* member_type_inst = get_def_use_mgr()->GetDef(
+        type_inst->GetSingleWordInOperand(kOpTypeStructInOperandMember));
+    if (IsImageOrImagePtrType(member_type_inst)) return true;
+  }
+  return false;
+}
+
+bool ReplaceDescArrayAccessUsingVarIndex::IsConcreteType(
+    uint32_t type_id) const {
+  Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
+  if (type_inst->opcode() == SpvOpTypeInt ||
+      type_inst->opcode() == SpvOpTypeFloat) {
+    return true;
+  }
+  if (type_inst->opcode() == SpvOpTypeVector ||
+      type_inst->opcode() == SpvOpTypeMatrix ||
+      type_inst->opcode() == SpvOpTypeArray) {
+    return IsConcreteType(type_inst->GetSingleWordInOperand(0));
+  }
+  if (type_inst->opcode() == SpvOpTypeStruct) {
+    for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) {
+      if (!IsConcreteType(type_inst->GetSingleWordInOperand(i))) return false;
+    }
+    return true;
+  }
+  return false;
+}
+
+BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateCaseBlock(
+    Instruction* access_chain, uint32_t element_index,
+    const std::deque<Instruction*>& insts_to_be_cloned,
+    uint32_t branch_target_id,
+    std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const {
+  auto* case_block = CreateNewBlock();
+  AddConstElementAccessToCaseBlock(case_block, access_chain, element_index,
+                                   old_ids_to_new_ids);
+  CloneInstsToBlock(case_block, access_chain, insts_to_be_cloned,
+                    old_ids_to_new_ids);
+  AddBranchToBlock(case_block, branch_target_id);
+  UseNewIdsInBlock(case_block, *old_ids_to_new_ids);
+  return case_block;
+}
+
+void ReplaceDescArrayAccessUsingVarIndex::CloneInstsToBlock(
+    BasicBlock* block, Instruction* inst_to_skip_cloning,
+    const std::deque<Instruction*>& insts_to_be_cloned,
+    std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const {
+  for (auto* inst_to_be_cloned : insts_to_be_cloned) {
+    if (inst_to_be_cloned == inst_to_skip_cloning) continue;
+    std::unique_ptr<Instruction> clone(inst_to_be_cloned->Clone(context()));
+    if (inst_to_be_cloned->HasResultId()) {
+      uint32_t new_id = context()->TakeNextId();
+      clone->SetResultId(new_id);
+      (*old_ids_to_new_ids)[inst_to_be_cloned->result_id()] = new_id;
+    }
+    get_def_use_mgr()->AnalyzeInstDefUse(clone.get());
+    context()->set_instr_block(clone.get(), block);
+    block->AddInstruction(std::move(clone));
+  }
+}
+
+void ReplaceDescArrayAccessUsingVarIndex::UseNewIdsInBlock(
+    BasicBlock* block,
+    const std::unordered_map<uint32_t, uint32_t>& old_ids_to_new_ids) const {
+  for (auto block_itr = block->begin(); block_itr != block->end();
+       ++block_itr) {
+    (&*block_itr)->ForEachInId([&old_ids_to_new_ids](uint32_t* idp) {
+      auto old_ids_to_new_ids_itr = old_ids_to_new_ids.find(*idp);
+      if (old_ids_to_new_ids_itr == old_ids_to_new_ids.end()) return;
+      *idp = old_ids_to_new_ids_itr->second;
+    });
+    get_def_use_mgr()->AnalyzeInstUse(&*block_itr);
+  }
+}
+
+void ReplaceDescArrayAccessUsingVarIndex::ReplaceNonUniformAccessWithSwitchCase(
+    Instruction* access_chain_final_user, Instruction* access_chain,
+    uint32_t number_of_elements,
+    const std::deque<Instruction*>& insts_to_be_cloned) const {
+  // Create merge block and add terminator
+  auto* block = context()->get_instr_block(access_chain_final_user);
+  auto* merge_block = SeparateInstructionsIntoNewBlock(
+      block, access_chain_final_user->NextNode());
+
+  auto* function = block->GetParent();
+
+  // Add case blocks
+  std::vector<uint32_t> phi_operands;
+  std::vector<uint32_t> case_block_ids;
+  for (uint32_t idx = 0; idx < number_of_elements; ++idx) {
+    std::unordered_map<uint32_t, uint32_t> old_ids_to_new_ids_for_cloned_insts;
+    std::unique_ptr<BasicBlock> case_block(CreateCaseBlock(
+        access_chain, idx, insts_to_be_cloned, merge_block->id(),
+        &old_ids_to_new_ids_for_cloned_insts));
+    case_block_ids.push_back(case_block->id());
+    function->InsertBasicBlockBefore(std::move(case_block), merge_block);
+
+    // Keep the operand for OpPhi
+    if (!access_chain_final_user->HasResultId()) continue;
+    uint32_t phi_operand =
+        GetValueWithKeyExistenceCheck(access_chain_final_user->result_id(),
+                                      old_ids_to_new_ids_for_cloned_insts);
+    phi_operands.push_back(phi_operand);
+  }
+
+  // Create default block
+  std::unique_ptr<BasicBlock> default_block(
+      CreateDefaultBlock(access_chain_final_user->HasResultId(), &phi_operands,
+                         merge_block->id()));
+  uint32_t default_block_id = default_block->id();
+  function->InsertBasicBlockBefore(std::move(default_block), merge_block);
+
+  // Create OpSwitch
+  uint32_t access_chain_index_var_id =
+      descsroautil::GetFirstIndexOfAccessChain(access_chain);
+  AddSwitchForAccessChain(block, access_chain_index_var_id, default_block_id,
+                          merge_block->id(), case_block_ids);
+
+  // Create phi instructions
+  if (!phi_operands.empty()) {
+    uint32_t phi_id = CreatePhiInstruction(merge_block, phi_operands,
+                                           case_block_ids, default_block_id);
+    context()->ReplaceAllUsesWith(access_chain_final_user->result_id(), phi_id);
+  }
+
+  // Replace OpPhi incoming block operand that uses |block| with |merge_block|
+  ReplacePhiIncomingBlock(block->id(), merge_block->id());
+}
+
+BasicBlock*
+ReplaceDescArrayAccessUsingVarIndex::SeparateInstructionsIntoNewBlock(
+    BasicBlock* block, Instruction* separation_begin_inst) const {
+  auto separation_begin = block->begin();
+  while (separation_begin != block->end() &&
+         &*separation_begin != separation_begin_inst) {
+    ++separation_begin;
+  }
+  return block->SplitBasicBlock(context(), context()->TakeNextId(),
+                                separation_begin);
+}
+
+BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateNewBlock() const {
+  auto* new_block = new BasicBlock(std::unique_ptr<Instruction>(
+      new Instruction(context(), SpvOpLabel, 0, context()->TakeNextId(), {})));
+  get_def_use_mgr()->AnalyzeInstDefUse(new_block->GetLabelInst());
+  context()->set_instr_block(new_block->GetLabelInst(), new_block);
+  return new_block;
+}
+
+void ReplaceDescArrayAccessUsingVarIndex::UseConstIndexForAccessChain(
+    Instruction* access_chain, uint32_t const_element_idx) const {
+  uint32_t const_element_idx_id =
+      context()->get_constant_mgr()->GetUIntConst(const_element_idx);
+  access_chain->SetInOperand(kOpAccessChainInOperandIndexes,
+                             {const_element_idx_id});
+}
+
+void ReplaceDescArrayAccessUsingVarIndex::AddConstElementAccessToCaseBlock(
+    BasicBlock* case_block, Instruction* access_chain,
+    uint32_t const_element_idx,
+    std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const {
+  std::unique_ptr<Instruction> access_clone(access_chain->Clone(context()));
+  UseConstIndexForAccessChain(access_clone.get(), const_element_idx);
+
+  uint32_t new_access_id = context()->TakeNextId();
+  (*old_ids_to_new_ids)[access_clone->result_id()] = new_access_id;
+  access_clone->SetResultId(new_access_id);
+  get_def_use_mgr()->AnalyzeInstDefUse(access_clone.get());
+
+  context()->set_instr_block(access_clone.get(), case_block);
+  case_block->AddInstruction(std::move(access_clone));
+}
+
+void ReplaceDescArrayAccessUsingVarIndex::AddBranchToBlock(
+    BasicBlock* parent_block, uint32_t branch_destination) const {
+  InstructionBuilder builder{context(), parent_block,
+                             kAnalysisDefUseAndInstrToBlockMapping};
+  builder.AddBranch(branch_destination);
+}
+
+BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateDefaultBlock(
+    bool null_const_for_phi_is_needed, std::vector<uint32_t>* phi_operands,
+    uint32_t merge_block_id) const {
+  auto* default_block = CreateNewBlock();
+  AddBranchToBlock(default_block, merge_block_id);
+  if (!null_const_for_phi_is_needed) return default_block;
+
+  // Create null value for OpPhi
+  Instruction* inst = context()->get_def_use_mgr()->GetDef((*phi_operands)[0]);
+  auto* null_const_inst = GetConstNull(inst->type_id());
+  phi_operands->push_back(null_const_inst->result_id());
+  return default_block;
+}
+
+Instruction* ReplaceDescArrayAccessUsingVarIndex::GetConstNull(
+    uint32_t type_id) const {
+  assert(type_id != 0 && "Result type is expected");
+  auto* type = context()->get_type_mgr()->GetType(type_id);
+  auto* null_const = context()->get_constant_mgr()->GetConstant(type, {});
+  return context()->get_constant_mgr()->GetDefiningInstruction(null_const);
+}
+
+void ReplaceDescArrayAccessUsingVarIndex::AddSwitchForAccessChain(
+    BasicBlock* parent_block, uint32_t access_chain_index_var_id,
+    uint32_t default_id, uint32_t merge_id,
+    const std::vector<uint32_t>& case_block_ids) const {
+  InstructionBuilder builder{context(), parent_block,
+                             kAnalysisDefUseAndInstrToBlockMapping};
+  std::vector<std::pair<Operand::OperandData, uint32_t>> cases;
+  for (uint32_t i = 0; i < static_cast<uint32_t>(case_block_ids.size()); ++i) {
+    cases.emplace_back(Operand::OperandData{i}, case_block_ids[i]);
+  }
+  builder.AddSwitch(access_chain_index_var_id, default_id, cases, merge_id);
+}
+
+uint32_t ReplaceDescArrayAccessUsingVarIndex::CreatePhiInstruction(
+    BasicBlock* parent_block, const std::vector<uint32_t>& phi_operands,
+    const std::vector<uint32_t>& case_block_ids,
+    uint32_t default_block_id) const {
+  std::vector<uint32_t> incomings;
+  assert(case_block_ids.size() + 1 == phi_operands.size() &&
+         "Number of Phi operands must be exactly 1 bigger than the one of case "
+         "blocks");
+  for (size_t i = 0; i < case_block_ids.size(); ++i) {
+    incomings.push_back(phi_operands[i]);
+    incomings.push_back(case_block_ids[i]);
+  }
+  incomings.push_back(phi_operands.back());
+  incomings.push_back(default_block_id);
+
+  InstructionBuilder builder{context(), &*parent_block->begin(),
+                             kAnalysisDefUseAndInstrToBlockMapping};
+  uint32_t phi_result_type_id =
+      context()->get_def_use_mgr()->GetDef(phi_operands[0])->type_id();
+  auto* phi = builder.AddPhi(phi_result_type_id, incomings);
+  return phi->result_id();
+}
+
+void ReplaceDescArrayAccessUsingVarIndex::ReplacePhiIncomingBlock(
+    uint32_t old_incoming_block_id, uint32_t new_incoming_block_id) const {
+  context()->ReplaceAllUsesWithPredicate(
+      old_incoming_block_id, new_incoming_block_id,
+      [](Instruction* use) { return use->opcode() == SpvOpPhi; });
+}
+
+}  // namespace opt
+}  // namespace spvtools

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

@@ -0,0 +1,204 @@
+// Copyright (c) 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_
+#define SOURCE_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_
+
+#include <cstdio>
+#include <memory>
+#include <queue>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "source/opt/function.h"
+#include "source/opt/pass.h"
+#include "source/opt/type_manager.h"
+
+namespace spvtools {
+namespace opt {
+
+// See optimizer.hpp for documentation.
+class ReplaceDescArrayAccessUsingVarIndex : public Pass {
+ public:
+  ReplaceDescArrayAccessUsingVarIndex() {}
+
+  const char* name() const override {
+    return "replace-desc-array-access-using-var-index";
+  }
+
+  Status Process() override;
+
+  IRContext::Analysis GetPreservedAnalyses() override {
+    return IRContext::kAnalysisDefUse |
+           IRContext::kAnalysisInstrToBlockMapping |
+           IRContext::kAnalysisConstants | IRContext::kAnalysisTypes;
+  }
+
+ private:
+  // Replaces all acceses to |var| using variable indices with constant
+  // elements of the array |var|. Creates switch-case statements to determine
+  // the value of the variable index for all the possible cases. Returns
+  // whether replacement is done or not.
+  bool ReplaceVariableAccessesWithConstantElements(Instruction* var) const;
+
+  // Replaces the OpAccessChain or OpInBoundsAccessChain instruction |use| that
+  // uses the descriptor variable |var| with the OpAccessChain or
+  // OpInBoundsAccessChain instruction with a constant Indexes operand.
+  void ReplaceAccessChain(Instruction* var, Instruction* use) const;
+
+  // Updates the first Indexes operand of the OpAccessChain or
+  // OpInBoundsAccessChain instruction |access_chain| to let it use a constant
+  // index |const_element_idx|.
+  void UseConstIndexForAccessChain(Instruction* access_chain,
+                                   uint32_t const_element_idx) const;
+
+  // Replaces users of the OpAccessChain or OpInBoundsAccessChain instruction
+  // |access_chain| that accesses an array descriptor variable using variable
+  // indices with constant elements. |number_of_elements| is the number
+  // of array elements.
+  void ReplaceUsersOfAccessChain(Instruction* access_chain,
+                                 uint32_t number_of_elements) const;
+
+  // Puts all the recursive users of |access_chain| with concrete result types
+  // or the ones without result it in |final_users|.
+  void CollectRecursiveUsersWithConcreteType(
+      Instruction* access_chain, std::vector<Instruction*>* final_users) const;
+
+  // Recursively collects the operands of |user_of_image_insts| (and operands
+  // of the operands) whose result types are images/samplers or pointers/array/
+  // struct of them and returns them.
+  std::deque<Instruction*> CollectRequiredImageInsts(
+      Instruction* user_of_image_insts) const;
+
+  // Returns whether result type of |inst| is an image/sampler/pointer of image
+  // or sampler or not.
+  bool HasImageOrImagePtrType(const Instruction* inst) const;
+
+  // Returns whether |type_inst| is an image/sampler or pointer/array/struct of
+  // image or sampler or not.
+  bool IsImageOrImagePtrType(const Instruction* type_inst) const;
+
+  // Returns whether the type with |type_id| is a concrete type or not.
+  bool IsConcreteType(uint32_t type_id) const;
+
+  // Replaces the non-uniform access to a descriptor variable
+  // |access_chain_final_user| with OpSwitch instruction and case blocks. Each
+  // case block will contain a clone of |access_chain| and clones of
+  // |non_uniform_accesses_to_clone| that are recursively used by
+  // |access_chain_final_user|. The clone of |access_chain| (or
+  // OpInBoundsAccessChain) will have a constant index for its first index. The
+  // OpSwitch instruction will have the cases for the variable index of
+  // |access_chain| from 0 to |number_of_elements| - 1.
+  void ReplaceNonUniformAccessWithSwitchCase(
+      Instruction* access_chain_final_user, Instruction* access_chain,
+      uint32_t number_of_elements,
+      const std::deque<Instruction*>& non_uniform_accesses_to_clone) const;
+
+  // Creates and returns a new basic block that contains all instructions of
+  // |block| after |separation_begin_inst|. The new basic block is added to the
+  // function in this method.
+  BasicBlock* SeparateInstructionsIntoNewBlock(
+      BasicBlock* block, Instruction* separation_begin_inst) const;
+
+  // Creates and returns a new block.
+  BasicBlock* CreateNewBlock() const;
+
+  // Returns the first operand id of the OpAccessChain or OpInBoundsAccessChain
+  // instruction |access_chain|.
+  uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain) const;
+
+  // Adds a clone of the OpAccessChain or OpInBoundsAccessChain instruction
+  // |access_chain| to |case_block|. The clone of |access_chain| will use
+  // |const_element_idx| for its first index. |old_ids_to_new_ids| keeps the
+  // mapping from the result id of |access_chain| to the result of its clone.
+  void AddConstElementAccessToCaseBlock(
+      BasicBlock* case_block, Instruction* access_chain,
+      uint32_t const_element_idx,
+      std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const;
+
+  // Clones all instructions in |insts_to_be_cloned| and put them to |block|.
+  // |old_ids_to_new_ids| keeps the mapping from the result id of each
+  // instruction of |insts_to_be_cloned| to the result of their clones.
+  void CloneInstsToBlock(
+      BasicBlock* block, Instruction* inst_to_skip_cloning,
+      const std::deque<Instruction*>& insts_to_be_cloned,
+      std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const;
+
+  // Adds OpBranch to |branch_destination| at the end of |parent_block|.
+  void AddBranchToBlock(BasicBlock* parent_block,
+                        uint32_t branch_destination) const;
+
+  // Replaces in-operands of all instructions in the basic block |block| using
+  // |old_ids_to_new_ids|. It conducts the replacement only if the in-operand
+  // id is a key of |old_ids_to_new_ids|.
+  void UseNewIdsInBlock(
+      BasicBlock* block,
+      const std::unordered_map<uint32_t, uint32_t>& old_ids_to_new_ids) const;
+
+  // Creates a case block for |element_index| case. It adds clones of
+  // |insts_to_be_cloned| and a clone of |access_chain| with |element_index| as
+  // its first index. The termination instruction of the created case block will
+  // be a branch to |branch_target_id|. Puts old ids to new ids map for the
+  // cloned instructions in |old_ids_to_new_ids|.
+  BasicBlock* CreateCaseBlock(
+      Instruction* access_chain, uint32_t element_index,
+      const std::deque<Instruction*>& insts_to_be_cloned,
+      uint32_t branch_target_id,
+      std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const;
+
+  // Creates a default block for switch-case statement that has only a single
+  // instruction OpBranch whose target is a basic block with |merge_block_id|.
+  // If |null_const_for_phi_is_needed| is true, gets or creates a default null
+  // constant value for a phi instruction whose operands are |phi_operands| and
+  // puts it in |phi_operands|.
+  BasicBlock* CreateDefaultBlock(bool null_const_for_phi_is_needed,
+                                 std::vector<uint32_t>* phi_operands,
+                                 uint32_t merge_block_id) const;
+
+  // Creates and adds an OpSwitch used for the selection of OpAccessChain whose
+  // first Indexes operand is |access_chain_index_var_id|. The OpSwitch will be
+  // added at the end of |parent_block|. It will jump to |default_id| for the
+  // default case and jumps to one of case blocks whoes ids are |case_block_ids|
+  // if |access_chain_index_var_id| matches the case number. |merge_id| is the
+  // merge block id.
+  void AddSwitchForAccessChain(
+      BasicBlock* parent_block, uint32_t access_chain_index_var_id,
+      uint32_t default_id, uint32_t merge_id,
+      const std::vector<uint32_t>& case_block_ids) const;
+
+  // Creates a phi instruction with |phi_operands| as values and
+  // |case_block_ids| and |default_block_id| as incoming blocks. The size of
+  // |phi_operands| must be exactly 1 larger than the size of |case_block_ids|.
+  // The last element of |phi_operands| will be used for |default_block_id|. It
+  // adds the phi instruction to the beginning of |parent_block|.
+  uint32_t CreatePhiInstruction(BasicBlock* parent_block,
+                                const std::vector<uint32_t>& phi_operands,
+                                const std::vector<uint32_t>& case_block_ids,
+                                uint32_t default_block_id) const;
+
+  // Replaces the incoming block operand of OpPhi instructions with
+  // |new_incoming_block_id| if the incoming block operand is
+  // |old_incoming_block_id|.
+  void ReplacePhiIncomingBlock(uint32_t old_incoming_block_id,
+                               uint32_t new_incoming_block_id) const;
+
+  // Create an OpConstantNull instruction whose result type id is |type_id|.
+  Instruction* GetConstNull(uint32_t type_id) const;
+};
+
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // SOURCE_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_

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

@@ -35,6 +35,10 @@ namespace opt {
 Pass::Status ScalarReplacementPass::Process() {
 Pass::Status ScalarReplacementPass::Process() {
   Status status = Status::SuccessWithoutChange;
   Status status = Status::SuccessWithoutChange;
   for (auto& f : *get_module()) {
   for (auto& f : *get_module()) {
+    if (f.IsDeclaration()) {
+      continue;
+    }
+
     Status functionStatus = ProcessFunction(&f);
     Status functionStatus = ProcessFunction(&f);
     if (functionStatus == Status::Failure)
     if (functionStatus == Status::Failure)
       return functionStatus;
       return functionStatus;

+ 29 - 4
3rdparty/spirv-tools/source/opt/set_spec_constant_default_value_pass.cpp

@@ -85,6 +85,10 @@ std::vector<uint32_t> ParseDefaultValueStr(const char* text,
 //   with 0x1, which represents a 'true'.
 //   with 0x1, which represents a 'true'.
 //   If all words in the bit pattern are zero, returns a bit pattern with 0x0,
 //   If all words in the bit pattern are zero, returns a bit pattern with 0x0,
 //   which represents a 'false'.
 //   which represents a 'false'.
+// For integer and floating point types narrower than 32 bits, the upper bits
+// in the input bit pattern are ignored.  Instead the upper bits are set
+// according to SPIR-V literal requirements: sign extend a signed integer, and
+// otherwise set the upper bits to zero.
 std::vector<uint32_t> ParseDefaultValueBitPattern(
 std::vector<uint32_t> ParseDefaultValueBitPattern(
     const std::vector<uint32_t>& input_bit_pattern,
     const std::vector<uint32_t>& input_bit_pattern,
     const analysis::Type* type) {
     const analysis::Type* type) {
@@ -98,12 +102,33 @@ std::vector<uint32_t> ParseDefaultValueBitPattern(
     }
     }
     return result;
     return result;
   } else if (const auto* IT = type->AsInteger()) {
   } else if (const auto* IT = type->AsInteger()) {
-    if (IT->width() == input_bit_pattern.size() * sizeof(uint32_t) * 8) {
-      return std::vector<uint32_t>(input_bit_pattern);
+    const auto width = IT->width();
+    assert(width > 0);
+    const auto adjusted_width = std::max(32u, width);
+    if (adjusted_width == input_bit_pattern.size() * sizeof(uint32_t) * 8) {
+      result = std::vector<uint32_t>(input_bit_pattern);
+      if (width < 32) {
+        const uint32_t high_active_bit = (1u << width) >> 1;
+        if (IT->IsSigned() && (high_active_bit & result[0])) {
+          // Sign extend.  This overwrites the sign bit again, but that's ok.
+          result[0] = result[0] | ~(high_active_bit - 1);
+        } else {
+          // Upper bits must be zero.
+          result[0] = result[0] & ((1u << width) - 1);
+        }
+      }
+      return result;
     }
     }
   } else if (const auto* FT = type->AsFloat()) {
   } else if (const auto* FT = type->AsFloat()) {
-    if (FT->width() == input_bit_pattern.size() * sizeof(uint32_t) * 8) {
-      return std::vector<uint32_t>(input_bit_pattern);
+    const auto width = FT->width();
+    const auto adjusted_width = std::max(32u, width);
+    if (adjusted_width == input_bit_pattern.size() * sizeof(uint32_t) * 8) {
+      result = std::vector<uint32_t>(input_bit_pattern);
+      if (width < 32) {
+        // Upper bits must be zero.
+        result[0] = result[0] & ((1u << width) - 1);
+      }
+      return result;
     }
     }
   }
   }
   result.clear();
   result.clear();

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

@@ -45,6 +45,10 @@ void SimplificationPass::AddNewOperands(
 }
 }
 
 
 bool SimplificationPass::SimplifyFunction(Function* function) {
 bool SimplificationPass::SimplifyFunction(Function* function) {
+  if (function->IsDeclaration()) {
+    return false;
+  }
+
   bool modified = false;
   bool modified = false;
   // Phase 1: Traverse all instructions in dominance order.
   // Phase 1: Traverse all instructions in dominance order.
   // The second phase will only be on the instructions whose inputs have changed
   // The second phase will only be on the instructions whose inputs have changed

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

@@ -753,6 +753,9 @@ Pass::Status SSARewriter::RewriteFunctionIntoSSA(Function* fp) {
 Pass::Status SSARewritePass::Process() {
 Pass::Status SSARewritePass::Process() {
   Status status = Status::SuccessWithoutChange;
   Status status = Status::SuccessWithoutChange;
   for (auto& fn : *get_module()) {
   for (auto& fn : *get_module()) {
+    if (fn.IsDeclaration()) {
+      continue;
+    }
     status =
     status =
         CombineStatus(status, SSARewriter(this).RewriteFunctionIntoSSA(&fn));
         CombineStatus(status, SSARewriter(this).RewriteFunctionIntoSSA(&fn));
     // Kill DebugDeclares for target variables.
     // Kill DebugDeclares for target variables.

+ 1 - 0
3rdparty/spirv-tools/source/spirv_constant.h

@@ -84,6 +84,7 @@ typedef enum spv_generator_t {
   SPV_GENERATOR_KHRONOS_LLVM_TRANSLATOR = 6,
   SPV_GENERATOR_KHRONOS_LLVM_TRANSLATOR = 6,
   SPV_GENERATOR_KHRONOS_ASSEMBLER = 7,
   SPV_GENERATOR_KHRONOS_ASSEMBLER = 7,
   SPV_GENERATOR_KHRONOS_GLSLANG = 8,
   SPV_GENERATOR_KHRONOS_GLSLANG = 8,
+  SPV_GENERATOR_KHRONOS_LINKER = 17,
   SPV_GENERATOR_NUM_ENTRIES,
   SPV_GENERATOR_NUM_ENTRIES,
   SPV_FORCE_16_BIT_ENUM(spv_generator_t)
   SPV_FORCE_16_BIT_ENUM(spv_generator_t)
 } spv_generator_t;
 } spv_generator_t;

+ 57 - 6
3rdparty/spirv-tools/source/spirv_target_env.cpp

@@ -62,7 +62,7 @@ const char* spvTargetEnvDescription(spv_target_env env) {
     case SPV_ENV_VULKAN_1_1:
     case SPV_ENV_VULKAN_1_1:
       return "SPIR-V 1.3 (under Vulkan 1.1 semantics)";
       return "SPIR-V 1.3 (under Vulkan 1.1 semantics)";
     case SPV_ENV_WEBGPU_0:
     case SPV_ENV_WEBGPU_0:
-      assert(false);
+      assert(false && "Deprecated target environment value.");
       break;
       break;
     case SPV_ENV_UNIVERSAL_1_4:
     case SPV_ENV_UNIVERSAL_1_4:
       return "SPIR-V 1.4";
       return "SPIR-V 1.4";
@@ -72,6 +72,9 @@ const char* spvTargetEnvDescription(spv_target_env env) {
       return "SPIR-V 1.5";
       return "SPIR-V 1.5";
     case SPV_ENV_VULKAN_1_2:
     case SPV_ENV_VULKAN_1_2:
       return "SPIR-V 1.5 (under Vulkan 1.2 semantics)";
       return "SPIR-V 1.5 (under Vulkan 1.2 semantics)";
+    case SPV_ENV_MAX:
+      assert(false && "Invalid target environment value.");
+      break;
   }
   }
   return "";
   return "";
 }
 }
@@ -102,7 +105,7 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) {
     case SPV_ENV_VULKAN_1_1:
     case SPV_ENV_VULKAN_1_1:
       return SPV_SPIRV_VERSION_WORD(1, 3);
       return SPV_SPIRV_VERSION_WORD(1, 3);
     case SPV_ENV_WEBGPU_0:
     case SPV_ENV_WEBGPU_0:
-      assert(false);
+      assert(false && "Deprecated target environment value.");
       break;
       break;
     case SPV_ENV_UNIVERSAL_1_4:
     case SPV_ENV_UNIVERSAL_1_4:
     case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
     case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
@@ -110,6 +113,9 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) {
     case SPV_ENV_UNIVERSAL_1_5:
     case SPV_ENV_UNIVERSAL_1_5:
     case SPV_ENV_VULKAN_1_2:
     case SPV_ENV_VULKAN_1_2:
       return SPV_SPIRV_VERSION_WORD(1, 5);
       return SPV_SPIRV_VERSION_WORD(1, 5);
+    case SPV_ENV_MAX:
+      assert(false && "Invalid target environment value.");
+      break;
   }
   }
   return SPV_SPIRV_VERSION_WORD(0, 0);
   return SPV_SPIRV_VERSION_WORD(0, 0);
 }
 }
@@ -212,7 +218,10 @@ bool spvIsVulkanEnv(spv_target_env env) {
     case SPV_ENV_VULKAN_1_2:
     case SPV_ENV_VULKAN_1_2:
       return true;
       return true;
     case SPV_ENV_WEBGPU_0:
     case SPV_ENV_WEBGPU_0:
-      assert(false);
+      assert(false && "Deprecated target environment value.");
+      break;
+    case SPV_ENV_MAX:
+      assert(false && "Invalid target environment value.");
       break;
       break;
   }
   }
   return false;
   return false;
@@ -246,7 +255,10 @@ bool spvIsOpenCLEnv(spv_target_env env) {
     case SPV_ENV_OPENCL_2_2:
     case SPV_ENV_OPENCL_2_2:
       return true;
       return true;
     case SPV_ENV_WEBGPU_0:
     case SPV_ENV_WEBGPU_0:
-      assert(false);
+      assert(false && "Deprecated target environment value.");
+      break;
+    case SPV_ENV_MAX:
+      assert(false && "Invalid target environment value.");
       break;
       break;
   }
   }
   return false;
   return false;
@@ -280,7 +292,43 @@ bool spvIsOpenGLEnv(spv_target_env env) {
     case SPV_ENV_OPENGL_4_5:
     case SPV_ENV_OPENGL_4_5:
       return true;
       return true;
     case SPV_ENV_WEBGPU_0:
     case SPV_ENV_WEBGPU_0:
-      assert(false);
+      assert(false && "Deprecated target environment value.");
+      break;
+    case SPV_ENV_MAX:
+      assert(false && "Invalid target environment value.");
+      break;
+  }
+  return false;
+}
+
+bool spvIsValidEnv(spv_target_env env) {
+  switch (env) {
+    case SPV_ENV_UNIVERSAL_1_0:
+    case SPV_ENV_VULKAN_1_0:
+    case SPV_ENV_UNIVERSAL_1_1:
+    case SPV_ENV_UNIVERSAL_1_2:
+    case SPV_ENV_UNIVERSAL_1_3:
+    case SPV_ENV_VULKAN_1_1:
+    case SPV_ENV_OPENCL_1_2:
+    case SPV_ENV_OPENCL_EMBEDDED_1_2:
+    case SPV_ENV_OPENCL_2_0:
+    case SPV_ENV_OPENCL_EMBEDDED_2_0:
+    case SPV_ENV_OPENCL_EMBEDDED_2_1:
+    case SPV_ENV_OPENCL_EMBEDDED_2_2:
+    case SPV_ENV_OPENCL_2_1:
+    case SPV_ENV_OPENCL_2_2:
+    case SPV_ENV_UNIVERSAL_1_4:
+    case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
+    case SPV_ENV_UNIVERSAL_1_5:
+    case SPV_ENV_VULKAN_1_2:
+    case SPV_ENV_OPENGL_4_0:
+    case SPV_ENV_OPENGL_4_1:
+    case SPV_ENV_OPENGL_4_2:
+    case SPV_ENV_OPENGL_4_3:
+    case SPV_ENV_OPENGL_4_5:
+      return true;
+    case SPV_ENV_WEBGPU_0:
+    case SPV_ENV_MAX:
       break;
       break;
   }
   }
   return false;
   return false;
@@ -320,7 +368,10 @@ std::string spvLogStringForEnv(spv_target_env env) {
       return "Universal";
       return "Universal";
     }
     }
     case SPV_ENV_WEBGPU_0:
     case SPV_ENV_WEBGPU_0:
-      assert(false);
+      assert(false && "Deprecated target environment value.");
+      break;
+    case SPV_ENV_MAX:
+      assert(false && "Invalid target environment value.");
       break;
       break;
   }
   }
   return "Unknown";
   return "Unknown";

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

@@ -28,6 +28,9 @@ bool spvIsOpenCLEnv(spv_target_env env);
 // Returns true if |env| is an OPENGL environment, false otherwise.
 // Returns true if |env| is an OPENGL environment, false otherwise.
 bool spvIsOpenGLEnv(spv_target_env env);
 bool spvIsOpenGLEnv(spv_target_env env);
 
 
+// Returns true if |env| is an implemented/valid environment, false otherwise.
+bool spvIsValidEnv(spv_target_env env);
+
 // Returns the version number for the given SPIR-V target environment.
 // Returns the version number for the given SPIR-V target environment.
 uint32_t spvVersionForTargetEnv(spv_target_env env);
 uint32_t spvVersionForTargetEnv(spv_target_env env);
 
 

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

@@ -659,9 +659,9 @@ spv_result_t ValidateStructuredSelections(
       // Mark the upcoming blocks as seen now, but only error out if this block
       // Mark the upcoming blocks as seen now, but only error out if this block
       // was missing a merge instruction and both labels hadn't been seen
       // was missing a merge instruction and both labels hadn't been seen
       // previously.
       // previously.
-      const bool both_unseen =
-          seen.insert(true_label).second && seen.insert(false_label).second;
-      if (!merge && both_unseen) {
+      const bool true_label_unseen = seen.insert(true_label).second;
+      const bool false_label_unseen = seen.insert(false_label).second;
+      if (!merge && true_label_unseen && false_label_unseen) {
         return _.diag(SPV_ERROR_INVALID_CFG, terminator)
         return _.diag(SPV_ERROR_INVALID_CFG, terminator)
                << "Selection must be structured";
                << "Selection must be structured";
       }
       }

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

@@ -140,6 +140,8 @@ bool isMissingOffsetInStruct(uint32_t struct_id, ValidationState_t& vstate) {
     for (auto& decoration : vstate.id_decorations(struct_id)) {
     for (auto& decoration : vstate.id_decorations(struct_id)) {
       if (SpvDecorationOffset == decoration.dec_type() &&
       if (SpvDecorationOffset == decoration.dec_type() &&
           Decoration::kInvalidMember != decoration.struct_member_index()) {
           Decoration::kInvalidMember != decoration.struct_member_index()) {
+        // Offset 0xffffffff is not valid so ignore it for simplicity's sake.
+        if (decoration.params()[0] == 0xffffffff) return true;
         hasOffset[decoration.struct_member_index()] = true;
         hasOffset[decoration.struct_member_index()] = true;
       }
       }
     }
     }
@@ -995,7 +997,9 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
 
 
       const bool phys_storage_buffer =
       const bool phys_storage_buffer =
           storageClass == SpvStorageClassPhysicalStorageBufferEXT;
           storageClass == SpvStorageClassPhysicalStorageBufferEXT;
-      const bool workgroup = storageClass == SpvStorageClassWorkgroup;
+      const bool workgroup =
+          storageClass == SpvStorageClassWorkgroup &&
+          vstate.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayoutKHR);
       if (uniform || push_constant || storage_buffer || phys_storage_buffer ||
       if (uniform || push_constant || storage_buffer || phys_storage_buffer ||
           workgroup) {
           workgroup) {
         const auto ptrInst = vstate.FindDef(words[1]);
         const auto ptrInst = vstate.FindDef(words[1]);

+ 22 - 10
3rdparty/spirv-tools/source/val/validate_image.cpp

@@ -66,6 +66,11 @@ bool CheckAllImageOperandsHandled() {
     case SpvImageOperandsVolatileTexelKHRMask:
     case SpvImageOperandsVolatileTexelKHRMask:
     case SpvImageOperandsSignExtendMask:
     case SpvImageOperandsSignExtendMask:
     case SpvImageOperandsZeroExtendMask:
     case SpvImageOperandsZeroExtendMask:
+    // TODO(jaebaek): Move this line properly after handling image offsets
+    //                operand. This line temporarily fixes CI failure that
+    //                blocks other PRs.
+    // https://github.com/KhronosGroup/SPIRV-Tools/issues/4565
+    case SpvImageOperandsOffsetsMask:
       return true;
       return true;
   }
   }
   return false;
   return false;
@@ -281,13 +286,14 @@ spv_result_t ValidateImageOperands(ValidationState_t& _,
   // the module to be invalid.
   // the module to be invalid.
   if (mask == 0) return SPV_SUCCESS;
   if (mask == 0) return SPV_SUCCESS;
 
 
-  if (spvtools::utils::CountSetBits(
-          mask & (SpvImageOperandsOffsetMask | SpvImageOperandsConstOffsetMask |
-                  SpvImageOperandsConstOffsetsMask)) > 1) {
+  if (spvtools::utils::CountSetBits(mask & (SpvImageOperandsOffsetMask |
+                                            SpvImageOperandsConstOffsetMask |
+                                            SpvImageOperandsConstOffsetsMask |
+                                            SpvImageOperandsOffsetsMask)) > 1) {
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
     return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << _.VkErrorID(4662)
            << _.VkErrorID(4662)
-           << "Image Operands Offset, ConstOffset, ConstOffsets cannot be used "
-           << "together";
+           << "Image Operands Offset, ConstOffset, ConstOffsets, Offsets "
+              "cannot be used together";
   }
   }
 
 
   const bool is_implicit_lod = IsImplicitLod(opcode);
   const bool is_implicit_lod = IsImplicitLod(opcode);
@@ -620,6 +626,10 @@ spv_result_t ValidateImageOperands(ValidationState_t& _,
     // setup.
     // setup.
   }
   }
 
 
+  if (mask & SpvImageOperandsOffsetsMask) {
+    // TODO: add validation
+  }
+
   return SPV_SUCCESS;
   return SPV_SUCCESS;
 }
 }
 
 
@@ -2058,11 +2068,13 @@ spv_result_t ImagePass(ValidationState_t& _, const Instruction* inst) {
                                       std::string* message) {
                                       std::string* message) {
           const auto* models = state.GetExecutionModels(entry_point->id());
           const auto* models = state.GetExecutionModels(entry_point->id());
           const auto* modes = state.GetExecutionModes(entry_point->id());
           const auto* modes = state.GetExecutionModes(entry_point->id());
-          if (models->find(SpvExecutionModelGLCompute) != models->end() &&
-              modes->find(SpvExecutionModeDerivativeGroupLinearNV) ==
-                  modes->end() &&
-              modes->find(SpvExecutionModeDerivativeGroupQuadsNV) ==
-                  modes->end()) {
+          if (models &&
+              models->find(SpvExecutionModelGLCompute) != models->end() &&
+              (!modes ||
+               (modes->find(SpvExecutionModeDerivativeGroupLinearNV) ==
+                    modes->end() &&
+                modes->find(SpvExecutionModeDerivativeGroupQuadsNV) ==
+                    modes->end()))) {
             if (message) {
             if (message) {
               *message =
               *message =
                   std::string(
                   std::string(

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

@@ -318,10 +318,9 @@ spv_result_t VersionCheck(ValidationState_t& _, const Instruction* inst) {
 
 
     if (module_version < min_version) {
     if (module_version < min_version) {
       return _.diag(SPV_ERROR_WRONG_VERSION, inst)
       return _.diag(SPV_ERROR_WRONG_VERSION, inst)
-             << spvOpcodeString(opcode) << " requires "
-             << spvTargetEnvDescription(
-                    static_cast<spv_target_env>(min_version))
-             << " at minimum.";
+             << spvOpcodeString(opcode) << " requires SPIR-V version "
+             << SPV_SPIRV_VERSION_MAJOR_PART(min_version) << "."
+             << SPV_SPIRV_VERSION_MINOR_PART(min_version) << " at minimum.";
     }
     }
   } else if (!_.HasAnyOfExtensions(exts)) {
   } else if (!_.HasAnyOfExtensions(exts)) {
     // Otherwise, we only error out when no enabling extensions are
     // Otherwise, we only error out when no enabling extensions are

+ 34 - 11
3rdparty/spirv-tools/source/val/validate_interfaces.cpp

@@ -199,6 +199,10 @@ uint32_t NumConsumedComponents(ValidationState_t& _, const Instruction* type) {
           NumConsumedComponents(_, _.FindDef(type->GetOperandAs<uint32_t>(1)));
           NumConsumedComponents(_, _.FindDef(type->GetOperandAs<uint32_t>(1)));
       num_components *= type->GetOperandAs<uint32_t>(2);
       num_components *= type->GetOperandAs<uint32_t>(2);
       break;
       break;
+    case SpvOpTypeArray:
+      // Skip the array.
+      return NumConsumedComponents(_,
+                                   _.FindDef(type->GetOperandAs<uint32_t>(1)));
     default:
     default:
       // This is an error that is validated elsewhere.
       // This is an error that is validated elsewhere.
       break;
       break;
@@ -430,17 +434,36 @@ spv_result_t GetLocationsForVariable(
         continue;
         continue;
       }
       }
 
 
-      uint32_t end = (location + num_locations) * 4;
-      if (num_components != 0) {
-        start += component;
-        end = location * 4 + component + num_components;
-      }
-      for (uint32_t l = start; l < end; ++l) {
-        if (!locations->insert(l).second) {
-          return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
-                 << "Entry-point has conflicting " << storage_class
-                 << " location assignment at location " << l / 4
-                 << ", component " << l % 4;
+      if (member->opcode() == SpvOpTypeArray && num_components >= 1 &&
+          num_components < 4) {
+        // When an array has an element that takes less than a location in
+        // size, calculate the used locations in a strided manner.
+        for (uint32_t l = location; l < num_locations + location; ++l) {
+          for (uint32_t c = component; c < component + num_components; ++c) {
+            uint32_t check = 4 * l + c;
+            if (!locations->insert(check).second) {
+              return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
+                     << "Entry-point has conflicting " << storage_class
+                     << " location assignment at location " << l
+                     << ", component " << c;
+            }
+          }
+        }
+      } else {
+        // TODO: There is a hole here is the member is an array of 3- or
+        // 4-element vectors of 64-bit types.
+        uint32_t end = (location + num_locations) * 4;
+        if (num_components != 0) {
+          start += component;
+          end = location * 4 + component + num_components;
+        }
+        for (uint32_t l = start; l < end; ++l) {
+          if (!locations->insert(l).second) {
+            return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
+                   << "Entry-point has conflicting " << storage_class
+                   << " location assignment at location " << l / 4
+                   << ", component " << l % 4;
+          }
         }
         }
       }
       }
     }
     }

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

@@ -235,11 +235,11 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) {
           }
           }
           if (!ok) {
           if (!ok) {
             return _.diag(SPV_ERROR_INVALID_DATA, inst)
             return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << _.VkErrorID(4683)
+                   << _.VkErrorID(6426)
                    << "In the Vulkan environment, GLCompute execution model "
                    << "In the Vulkan environment, GLCompute execution model "
-                      "entry points require either the LocalSize execution "
-                      "mode or an object decorated with WorkgroupSize must be "
-                      "specified.";
+                      "entry points require either the LocalSize or "
+                      "LocalSizeId execution mode or an object decorated with "
+                      "WorkgroupSize must be specified.";
           }
           }
         }
         }
         break;
         break;

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

@@ -1845,8 +1845,8 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
       return VUID_WRAP(VUID-StandaloneSpirv-Invariant-04677);
       return VUID_WRAP(VUID-StandaloneSpirv-Invariant-04677);
     case 4682:
     case 4682:
       return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04682);
       return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04682);
-    case 4683:
-      return VUID_WRAP(VUID-StandaloneSpirv-LocalSize-04683);
+    case 6426:
+      return VUID_WRAP(VUID-StandaloneSpirv-LocalSize-06426); // formally 04683
     case 4685:
     case 4685:
       return VUID_WRAP(VUID-StandaloneSpirv-OpGroupNonUniformBallotBitCount-04685);
       return VUID_WRAP(VUID-StandaloneSpirv-OpGroupNonUniformBallotBitCount-04685);
     case 4686:
     case 4686:

Some files were not shown because too many files changed in this diff