Бранимир Караџић пре 5 година
родитељ
комит
ecd5959987
96 измењених фајлова са 3025 додато и 1875 уклоњено
  1. 3 3
      3rdparty/spirv-tools/CHANGES
  2. 1 1
      3rdparty/spirv-tools/include/generated/build-version.inc
  3. 28 0
      3rdparty/spirv-tools/include/generated/nonsemantic.clspvreflection.insts.inc
  4. 18 2
      3rdparty/spirv-tools/include/spirv-tools/libspirv.h
  5. 24 12
      3rdparty/spirv-tools/source/CMakeLists.txt
  6. 9 1
      3rdparty/spirv-tools/source/ext_inst.cpp
  7. 0 568
      3rdparty/spirv-tools/source/extinst.debuginfo.grammar.json
  8. 0 632
      3rdparty/spirv-tools/source/extinst.opencl.debuginfo.100.grammar.json
  9. 0 26
      3rdparty/spirv-tools/source/extinst.spv-amd-gcn-shader.grammar.json
  10. 0 41
      3rdparty/spirv-tools/source/extinst.spv-amd-shader-ballot.grammar.json
  11. 0 14
      3rdparty/spirv-tools/source/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json
  12. 0 95
      3rdparty/spirv-tools/source/extinst.spv-amd-shader-trinary-minmax.grammar.json
  13. 9 1
      3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
  14. 79 39
      3rdparty/spirv-tools/source/fuzz/fact_manager.cpp
  15. 4 0
      3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
  16. 8 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp
  17. 8 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_context.h
  18. 19 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp
  19. 11 2
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h
  20. 30 8
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp
  21. 12 3
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
  22. 29 3
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
  23. 10 1
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.h
  24. 4 4
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp
  25. 149 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp
  26. 51 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h
  27. 1 19
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
  28. 0 10
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h
  29. 29 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp
  30. 3 12
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp
  31. 105 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp
  32. 41 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h
  33. 1 1
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp
  34. 119 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
  35. 42 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
  36. 55 19
      3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto
  37. 14 11
      3rdparty/spirv-tools/source/fuzz/shrinker.cpp
  38. 9 0
      3rdparty/spirv-tools/source/fuzz/transformation.cpp
  39. 226 32
      3rdparty/spirv-tools/source/fuzz/transformation_access_chain.cpp
  40. 32 11
      3rdparty/spirv-tools/source/fuzz/transformation_access_chain.h
  41. 17 29
      3rdparty/spirv-tools/source/fuzz/transformation_add_function.cpp
  42. 0 7
      3rdparty/spirv-tools/source/fuzz/transformation_add_function.h
  43. 227 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_loop_preheader.cpp
  44. 57 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_loop_preheader.h
  45. 12 34
      3rdparty/spirv-tools/source/fuzz/transformation_outline_function.cpp
  46. 1 1
      3rdparty/spirv-tools/source/fuzz/transformation_outline_function.h
  47. 8 8
      3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.cpp
  48. 11 10
      3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.h
  49. 68 0
      3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp
  50. 15 0
      3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.h
  51. 206 10
      3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp
  52. 8 0
      3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h
  53. 192 0
      3rdparty/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp
  54. 76 0
      3rdparty/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.h
  55. 1 1
      3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp
  56. 13 10
      3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp
  57. 2 3
      3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.h
  58. 2 2
      3rdparty/spirv-tools/source/fuzz/transformation_set_loop_control.cpp
  59. 4 5
      3rdparty/spirv-tools/source/fuzz/transformation_split_block.cpp
  60. 1 1
      3rdparty/spirv-tools/source/link/CMakeLists.txt
  61. 1 1
      3rdparty/spirv-tools/source/name_mapper.cpp
  62. 26 5
      3rdparty/spirv-tools/source/operand.cpp
  63. 2 2
      3rdparty/spirv-tools/source/opt/CMakeLists.txt
  64. 2 2
      3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp
  65. 6 0
      3rdparty/spirv-tools/source/opt/constants.cpp
  66. 3 0
      3rdparty/spirv-tools/source/opt/constants.h
  67. 116 47
      3rdparty/spirv-tools/source/opt/debug_info_manager.cpp
  68. 23 5
      3rdparty/spirv-tools/source/opt/debug_info_manager.h
  69. 28 26
      3rdparty/spirv-tools/source/opt/dominator_tree.cpp
  70. 7 2
      3rdparty/spirv-tools/source/opt/instruction.h
  71. 4 4
      3rdparty/spirv-tools/source/opt/ir_builder.h
  72. 0 7
      3rdparty/spirv-tools/source/opt/ir_context.cpp
  73. 0 3
      3rdparty/spirv-tools/source/opt/ir_context.h
  74. 2 1
      3rdparty/spirv-tools/source/opt/local_single_block_elim_pass.cpp
  75. 2 2
      3rdparty/spirv-tools/source/opt/local_single_store_elim_pass.cpp
  76. 4 4
      3rdparty/spirv-tools/source/opt/loop_descriptor.cpp
  77. 19 17
      3rdparty/spirv-tools/source/opt/loop_descriptor.h
  78. 1 1
      3rdparty/spirv-tools/source/opt/loop_peeling.cpp
  79. 32 6
      3rdparty/spirv-tools/source/opt/loop_unroller.cpp
  80. 3 3
      3rdparty/spirv-tools/source/opt/loop_unswitch_pass.cpp
  81. 7 6
      3rdparty/spirv-tools/source/opt/merge_return_pass.cpp
  82. 7 7
      3rdparty/spirv-tools/source/opt/merge_return_pass.h
  83. 2 2
      3rdparty/spirv-tools/source/opt/optimizer.cpp
  84. 16 5
      3rdparty/spirv-tools/source/opt/private_to_local_pass.cpp
  85. 2 2
      3rdparty/spirv-tools/source/opt/private_to_local_pass.h
  86. 1 1
      3rdparty/spirv-tools/source/opt/register_pressure.cpp
  87. 86 0
      3rdparty/spirv-tools/source/opt/scalar_replacement_pass.cpp
  88. 15 0
      3rdparty/spirv-tools/source/opt/scalar_replacement_pass.h
  89. 9 5
      3rdparty/spirv-tools/source/opt/ssa_rewrite_pass.cpp
  90. 1 1
      3rdparty/spirv-tools/source/opt/wrap_opkill.cpp
  91. 2 2
      3rdparty/spirv-tools/source/reduce/CMakeLists.txt
  92. 2 1
      3rdparty/spirv-tools/source/reduce/operand_to_dominating_id_reduction_opportunity_finder.cpp
  93. 2 2
      3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.h
  94. 2 5
      3rdparty/spirv-tools/source/val/validate_capability.cpp
  95. 485 5
      3rdparty/spirv-tools/source/val/validate_extensions.cpp
  96. 1 1
      3rdparty/spirv-tools/source/val/validate_image.cpp

+ 3 - 3
3rdparty/spirv-tools/CHANGES

@@ -39,7 +39,7 @@ v2020.4 2020-07-22
 
 v2020.3 2020-05-27
  - General
-   - Prevent Effcee install his things when build spirv-tools with testing enabled (#3256)
+   - Prevent Effcee from installing things when building spirv-tools with testing enabled (#3256)
    - Update acorn version (#3294)
    - If SPIRV-Headers is in our tree, include it as subproject (#3299)
    - allow cross compiling for Windows Store, UWP, etc. (#3330)
@@ -111,7 +111,7 @@ v2020.1 2020-02-03
  - Optimizer
    - Change default version for CreatInstBindlessCheckPass to 2 (#3096, #3119)
    - Better handling of OpLine on merge blocks (#3130)
-   - Use dummy switch instead of dummy loop in MergeReturn pass. (#3151)
+   - Use placeholder switch instead of placeholder loop in MergeReturn pass. (#3151)
    - Handle TimeAMD in AmdExtensionToKhrPass. (#3168)
  - Validator
    - Fix structured exit validation (#3141)
@@ -438,7 +438,7 @@ v2018.6 2018-11-07
  - Optimizer
    - Unrolling loops marked for unrolling in the legalization passes.
    - Improved the compile time of loop unrolling.
-   - Changee merge-return to create a dummy loop around the function.
+   - Changee merge-return to create a placeholder loop around the function.
    - Small improvement to merge-blocks to allow it to merge more often.
    - Enforce an upper bound for the ids, and add option to set it.
    - #1966: Report error if there are unreachable block before running merge return

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

@@ -1 +1 @@
-"v2020.5", "SPIRV-Tools v2020.5 aebdae4b9c5b3384041b88803e83fb244f5c8067"
+"v2020.5", "SPIRV-Tools v2020.5 276598ad50d33f1d1a56311520b17390a6bed635"

+ 28 - 0
3rdparty/spirv-tools/include/generated/nonsemantic.clspvreflection.insts.inc

@@ -0,0 +1,28 @@
+
+
+static const spv_ext_inst_desc_t nonsemantic_clspvreflection_entries[] = {
+  {"Kernel", 1, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"ArgumentInfo", 2, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
+  {"ArgumentStorageBuffer", 3, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
+  {"ArgumentUniform", 4, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
+  {"ArgumentPodStorageBuffer", 5, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
+  {"ArgumentPodUniform", 6, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
+  {"ArgumentPodPushConstant", 7, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
+  {"ArgumentSampledImage", 8, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
+  {"ArgumentStorageImage", 9, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
+  {"ArgumentSampler", 10, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
+  {"ArgumentWorkgroup", 11, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
+  {"SpecConstantWorkgroupSize", 12, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"SpecConstantGlobalOffset", 13, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"SpecConstantWorkDim", 14, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"PushConstantGlobalOffset", 15, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"PushConstantEnqueuedLocalSize", 16, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"PushConstantGlobalSize", 17, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"PushConstantRegionOffset", 18, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"PushConstantNumWorkgroups", 19, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"PushConstantRegionGroupOffset", 20, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"ConstantDataStorageBuffer", 21, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"ConstantDataUniform", 22, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"LiteralSampler", 23, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"PropertyRequiredWorkgroupSize", 24, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}}
+};

+ 18 - 2
3rdparty/spirv-tools/include/spirv-tools/libspirv.h

@@ -48,8 +48,8 @@ extern "C" {
 
 #define SPV_BIT(shift) (1 << (shift))
 
-#define SPV_FORCE_16_BIT_ENUM(name) _##name = 0x7fff
-#define SPV_FORCE_32_BIT_ENUM(name) _##name = 0x7fffffff
+#define SPV_FORCE_16_BIT_ENUM(name) SPV_FORCE_16BIT_##name = 0x7fff
+#define SPV_FORCE_32_BIT_ENUM(name) SPV_FORCE_32BIT_##name = 0x7fffffff
 
 // Enumerations
 
@@ -189,8 +189,17 @@ typedef enum spv_operand_type_t {
 //    Variable : expands to 0, 1 or many operands or pairs of operands.
 //               This is similar to * in regular expressions.
 
+// NOTE: These FIRST_* and LAST_* enum values are DEPRECATED.
+// The concept of "optional" and "variable" operand types are only intended
+// for use as an implementation detail of parsing SPIR-V, either in text or
+// binary form.  Instead of using enum ranges, use characteristic function
+// spvOperandIsConcrete.
+// The use of enum value ranges in a public API makes it difficult to insert
+// new values into a range without also breaking binary compatibility.
+//
 // Macros for defining bounds on optional and variable operand types.
 // Any variable operand type is also optional.
+// TODO(dneto): Remove SPV_OPERAND_TYPE_FIRST_* and SPV_OPERAND_TYPE_LAST_*
 #define FIRST_OPTIONAL(ENUM) ENUM, SPV_OPERAND_TYPE_FIRST_OPTIONAL_TYPE = ENUM
 #define FIRST_VARIABLE(ENUM) ENUM, SPV_OPERAND_TYPE_FIRST_VARIABLE_TYPE = ENUM
 #define LAST_VARIABLE(ENUM)                         \
@@ -258,6 +267,12 @@ typedef enum spv_operand_type_t {
   SPV_FORCE_32_BIT_ENUM(spv_operand_type_t)
 } spv_operand_type_t;
 
+// Returns true if the given type is concrete.
+bool spvOperandIsConcrete(spv_operand_type_t type);
+
+// Returns true if the given type is concrete and also a mask.
+bool spvOperandIsConcreteMask(spv_operand_type_t type);
+
 typedef enum spv_ext_inst_type_t {
   SPV_EXT_INST_TYPE_NONE = 0,
   SPV_EXT_INST_TYPE_GLSL_STD_450,
@@ -268,6 +283,7 @@ typedef enum spv_ext_inst_type_t {
   SPV_EXT_INST_TYPE_SPV_AMD_SHADER_BALLOT,
   SPV_EXT_INST_TYPE_DEBUGINFO,
   SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
+  SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION,
 
   // Multiple distinct extended instruction set types could return this
   // value, if they are prefixed with NonSemantic. and are otherwise

+ 24 - 12
3rdparty/spirv-tools/source/CMakeLists.txt

@@ -19,8 +19,8 @@ set(LANG_HEADER_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_lang
 
 # For now, assume the DebugInfo grammar file is in the current directory.
 # It might migrate to SPIRV-Headers.
-set(DEBUGINFO_GRAMMAR_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/extinst.debuginfo.grammar.json")
-set(CLDEBUGINFO100_GRAMMAR_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/extinst.opencl.debuginfo.100.grammar.json")
+set(DEBUGINFO_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.debuginfo.grammar.json")
+set(CLDEBUGINFO100_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json")
 
 # macro() definitions are used in the following because we need to append .inc
 # file paths into some global lists (*_CPP_DEPENDS). And those global lists are
@@ -112,7 +112,7 @@ endmacro(spvtools_opencl_tables)
 
 macro(spvtools_vendor_tables VENDOR_TABLE SHORT_NAME OPERAND_KIND_PREFIX)
   set(INSTS_FILE "${spirv-tools_BINARY_DIR}/${VENDOR_TABLE}.insts.inc")
-  set(GRAMMAR_FILE "${spirv-tools_SOURCE_DIR}/source/extinst.${VENDOR_TABLE}.grammar.json")
+  set(GRAMMAR_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.${VENDOR_TABLE}.grammar.json")
   add_custom_command(OUTPUT ${INSTS_FILE}
     COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT}
       --extinst-vendor-grammar=${GRAMMAR_FILE}
@@ -148,6 +148,7 @@ spvtools_vendor_tables("spv-amd-gcn-shader" "spv-amd-gs" "")
 spvtools_vendor_tables("spv-amd-shader-ballot" "spv-amd-sb" "")
 spvtools_vendor_tables("debuginfo" "debuginfo" "")
 spvtools_vendor_tables("opencl.debuginfo.100" "cldi100" "CLDEBUG100_")
+spvtools_vendor_tables("nonsemantic.clspvreflection" "clspvreflection" "")
 spvtools_extinst_lang_headers("DebugInfo" ${DEBUGINFO_GRAMMAR_JSON_FILE})
 spvtools_extinst_lang_headers("OpenCLDebugInfo100" ${CLDEBUGINFO100_GRAMMAR_JSON_FILE})
 
@@ -345,18 +346,21 @@ set_source_files_properties(
 
 spvtools_pch(SPIRV_SOURCES pch_source)
 
-add_library(${SPIRV_TOOLS} ${SPIRV_SOURCES})
-spvtools_default_compile_options(${SPIRV_TOOLS})
-target_include_directories(${SPIRV_TOOLS}
+add_library(${SPIRV_TOOLS}-static STATIC ${SPIRV_SOURCES})
+spvtools_default_compile_options(${SPIRV_TOOLS}-static)
+target_include_directories(${SPIRV_TOOLS}-static
   PUBLIC
     $<BUILD_INTERFACE:${spirv-tools_SOURCE_DIR}/include>
     $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
   PRIVATE ${spirv-tools_BINARY_DIR}
   PRIVATE ${SPIRV_HEADER_INCLUDE_DIR}
   )
-set_property(TARGET ${SPIRV_TOOLS} PROPERTY FOLDER "SPIRV-Tools libraries")
-spvtools_check_symbol_exports(${SPIRV_TOOLS})
-add_dependencies( ${SPIRV_TOOLS} core_tables enum_string_mapping extinst_tables )
+set_property(TARGET ${SPIRV_TOOLS}-static PROPERTY FOLDER "SPIRV-Tools libraries")
+spvtools_check_symbol_exports(${SPIRV_TOOLS}-static)
+add_dependencies(${SPIRV_TOOLS}-static core_tables enum_string_mapping extinst_tables)
+
+# The static target does not have the '-static' suffix.
+set_target_properties(${SPIRV_TOOLS}-static PROPERTIES OUTPUT_NAME "${SPIRV_TOOLS}")
 
 add_library(${SPIRV_TOOLS}-shared SHARED ${SPIRV_SOURCES})
 spvtools_default_compile_options(${SPIRV_TOOLS}-shared)
@@ -374,18 +378,26 @@ target_compile_definitions(${SPIRV_TOOLS}-shared
   PRIVATE SPIRV_TOOLS_IMPLEMENTATION
   PUBLIC SPIRV_TOOLS_SHAREDLIB
 )
-add_dependencies( ${SPIRV_TOOLS}-shared core_tables enum_string_mapping extinst_tables )
+add_dependencies(${SPIRV_TOOLS}-shared core_tables enum_string_mapping extinst_tables)
+
+# Create the "${SPIRV_TOOLS}" target as an alias to either "${SPIRV_TOOLS}-static"
+# or "${SPIRV_TOOLS}-shared" depending on the value of BUILD_SHARED_LIBS.
+if(BUILD_SHARED_LIBS)
+    add_library(${SPIRV_TOOLS} ALIAS ${SPIRV_TOOLS}-shared)
+else()
+    add_library(${SPIRV_TOOLS} ALIAS ${SPIRV_TOOLS}-static)
+endif()
 
 if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
   find_library(LIBRT rt)
   if(LIBRT)
-    target_link_libraries(${SPIRV_TOOLS} ${LIBRT})
+    target_link_libraries(${SPIRV_TOOLS}-static ${LIBRT})
     target_link_libraries(${SPIRV_TOOLS}-shared ${LIBRT})
   endif()
 endif()
 
 if(ENABLE_SPIRV_TOOLS_INSTALL)
-  install(TARGETS ${SPIRV_TOOLS} ${SPIRV_TOOLS}-shared EXPORT ${SPIRV_TOOLS}Targets
+  install(TARGETS ${SPIRV_TOOLS}-static ${SPIRV_TOOLS}-shared EXPORT ${SPIRV_TOOLS}Targets
     RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
     LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
     ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})

+ 9 - 1
3rdparty/spirv-tools/source/ext_inst.cpp

@@ -28,6 +28,7 @@
 
 #include "debuginfo.insts.inc"
 #include "glsl.std.450.insts.inc"
+#include "nonsemantic.clspvreflection.insts.inc"
 #include "opencl.debuginfo.100.insts.inc"
 #include "opencl.std.insts.inc"
 
@@ -54,6 +55,9 @@ static const spv_ext_inst_group_t kGroups_1_0[] = {
      debuginfo_entries},
     {SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
      ARRAY_SIZE(opencl_debuginfo_100_entries), opencl_debuginfo_100_entries},
+    {SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION,
+     ARRAY_SIZE(nonsemantic_clspvreflection_entries),
+     nonsemantic_clspvreflection_entries},
 };
 
 static const spv_ext_inst_table_t kTable_1_0 = {ARRAY_SIZE(kGroups_1_0),
@@ -123,6 +127,9 @@ spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) {
   if (!strcmp("OpenCL.DebugInfo.100", name)) {
     return SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100;
   }
+  if (!strncmp("NonSemantic.ClspvReflection.", name, 28)) {
+    return SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION;
+  }
   // ensure to add any known non-semantic extended instruction sets
   // above this point, and update spvExtInstIsNonSemantic()
   if (!strncmp("NonSemantic.", name, 12)) {
@@ -132,7 +139,8 @@ spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) {
 }
 
 bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type) {
-  if (type == SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN) {
+  if (type == SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN ||
+      type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) {
     return true;
   }
   return false;

+ 0 - 568
3rdparty/spirv-tools/source/extinst.debuginfo.grammar.json

@@ -1,568 +0,0 @@
-{
-  "copyright" : [
-    "Copyright (c) 2017 The Khronos Group Inc.",
-    "",
-    "Permission is hereby granted, free of charge, to any person obtaining a copy",
-    "of this software and/or associated documentation files (the \"Materials\"),",
-    "to deal in the Materials without restriction, including without limitation",
-    "the rights to use, copy, modify, merge, publish, distribute, sublicense,",
-    "and/or sell copies of the Materials, and to permit persons to whom the",
-    "Materials are furnished to do so, subject to the following conditions:",
-    "",
-    "The above copyright notice and this permission notice shall be included in",
-    "all copies or substantial portions of the Materials.",
-    "",
-    "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS",
-    "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND",
-    "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ",
-    "",
-    "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS",
-    "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
-    "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL",
-    "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
-    "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING",
-    "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS",
-    "IN THE MATERIALS."
-  ],
-  "version" : 100,
-  "revision" : 1,
-  "instructions" : [
-    {
-      "opname" : "DebugInfoNone",
-      "opcode" : 0
-    },
-    {
-      "opname" : "DebugCompilationUnit",
-      "opcode" : 1,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Version'" },
-        { "kind" : "LiteralInteger", "name" : "'DWARF Version'" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeBasic",
-      "opcode" : 2,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Size'" },
-        { "kind" : "DebugBaseTypeAttributeEncoding", "name" : "'Encoding'" }
-      ]
-    },
-    {
-      "opname" : "DebugTypePointer",
-      "opcode" : 3,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Base Type'" },
-        { "kind" : "StorageClass", "name" : "'Storage Class'" },
-        { "kind" : "DebugInfoFlags", "name" : "'Literal Flags'" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeQualifier",
-      "opcode" : 4,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Base Type'" },
-        { "kind" : "DebugTypeQualifier", "name" : "'Type Qualifier'" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeArray",
-      "opcode" : 5,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Base Type'" },
-        { "kind" : "IdRef", "name" : "'Component Counts'", "quantifier" : "*" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeVector",
-      "opcode" : 6,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Base Type'" },
-        { "kind" : "LiteralInteger", "name" : "'Component Count'" }
-      ]
-    },
-    {
-      "opname" : "DebugTypedef",
-      "opcode" : 7,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Base Type'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" },
-        { "kind" : "IdRef", "name" : "'Parent'" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeFunction",
-      "opcode" : 8,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Return Type'" },
-        { "kind" : "IdRef", "name" : "'Paramter Types'", "quantifier" : "*" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeEnum",
-      "opcode" : 9,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Underlying Type'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" },
-        { "kind" : "IdRef", "name" : "'Parent'" },
-        { "kind" : "IdRef", "name" : "'Size'" },
-        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
-        { "kind" : "PairIdRefIdRef", "name" : "'Value, Name, Value, Name, ...'", "quantifier" : "*" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeComposite",
-      "opcode" : 10,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "DebugCompositeType", "name" : "'Tag'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" },
-        { "kind" : "IdRef", "name" : "'Parent'" },
-        { "kind" : "IdRef", "name" : "'Size'" },
-        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
-        { "kind" : "IdRef", "name" : "'Members'", "quantifier" : "*" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeMember",
-      "opcode" : 11,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Type'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" },
-        { "kind" : "IdRef", "name" : "'Parent'" },
-        { "kind" : "IdRef", "name" : "'Offset'" },
-        { "kind" : "IdRef", "name" : "'Size'" },
-        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
-        { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeInheritance",
-      "opcode" : 12,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Child'" },
-        { "kind" : "IdRef", "name" : "'Parent'" },
-        { "kind" : "IdRef", "name" : "'Offset'" },
-        { "kind" : "IdRef", "name" : "'Size'" },
-        { "kind" : "DebugInfoFlags", "name" : "'Flags'" }
-      ]
-    },
-    {
-      "opname" : "DebugTypePtrToMember",
-      "opcode" : 13,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Member Type'" },
-        { "kind" : "IdRef", "name" : "'Parent'" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeTemplate",
-      "opcode" : 14,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Target'" },
-        { "kind" : "IdRef", "name" : "'Parameters'", "quantifier" : "*" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeTemplateParameter",
-      "opcode" : 15,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Actual Type'" },
-        { "kind" : "IdRef", "name" : "'Value'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeTemplateTemplateParameter",
-      "opcode" : 16,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Template Name'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeTemplateParameterPack",
-      "opcode" : 17,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" },
-        { "kind" : "IdRef", "name" : "'Template Parameters'", "quantifier" : "*" }
-      ]
-    },
-    {
-      "opname" : "DebugGlobalVariable",
-      "opcode" : 18,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Type'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" },
-        { "kind" : "IdRef", "name" : "'Parent'" },
-        { "kind" : "IdRef", "name" : "'Linkage Name'" },
-        { "kind" : "IdRef", "name" : "'Variable'" },
-        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
-        { "kind" : "IdRef", "name" : "'Static Member Declaration'", "quantifier" : "?" }
-      ]
-    },
-    {
-      "opname" : "DebugFunctionDeclaration",
-      "opcode" : 19,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Type'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" },
-        { "kind" : "IdRef", "name" : "'Parent'" },
-        { "kind" : "IdRef", "name" : "'Linkage Name'" },
-        { "kind" : "DebugInfoFlags", "name" : "'Flags'" }
-      ]
-    },
-    {
-      "opname" : "DebugFunction",
-      "opcode" : 20,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Type'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" },
-        { "kind" : "IdRef", "name" : "'Parent'" },
-        { "kind" : "IdRef", "name" : "'Linkage Name'" },
-        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
-        { "kind" : "LiteralInteger", "name" : "'Scope Line'" },
-        { "kind" : "IdRef", "name" : "'Function'" },
-        { "kind" : "IdRef", "name" : "'Declaration'", "quantifier" : "?" }
-      ]
-    },
-    {
-      "opname" : "DebugLexicalBlock",
-      "opcode" : 21,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" },
-        { "kind" : "IdRef", "name" : "'Parent'" },
-        { "kind" : "IdRef", "name" : "'Name'", "quantifier" : "?" }
-      ]
-    },
-    {
-      "opname" : "DebugLexicalBlockDiscriminator",
-      "opcode" : 22,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Scope'" },
-        { "kind" : "LiteralInteger", "name" : "'Discriminator'" },
-        { "kind" : "IdRef", "name" : "'Parent'" }
-      ]
-    },
-    {
-      "opname" : "DebugScope",
-      "opcode" : 23,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Scope'" },
-        { "kind" : "IdRef", "name" : "'Inlined At'", "quantifier" : "?" }
-      ]
-    },
-    {
-      "opname" : "DebugNoScope",
-      "opcode" : 24
-    },
-    {
-      "opname" : "DebugInlinedAt",
-      "opcode" : 25,
-      "operands" : [
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "IdRef", "name" : "'Scope'" },
-        { "kind" : "IdRef", "name" : "'Inlined'", "quantifier" : "?" }
-      ]
-    },
-    {
-      "opname" : "DebugLocalVariable",
-      "opcode" : 26,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Type'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" },
-        { "kind" : "IdRef", "name" : "'Parent'" },
-        { "kind" : "LiteralInteger", "name" : "'Arg Number'", "quantifier" : "?" }
-      ]
-    },
-    {
-      "opname" : "DebugInlinedVariable",
-      "opcode" : 27,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Variable'" },
-        { "kind" : "IdRef", "name" : "'Inlined'" }
-      ]
-    },
-    {
-      "opname" : "DebugDeclare",
-      "opcode" : 28,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Local Variable'" },
-        { "kind" : "IdRef", "name" : "'Variable'" },
-        { "kind" : "IdRef", "name" : "'Expression'" }
-      ]
-    },
-    {
-      "opname" : "DebugValue",
-      "opcode" : 29,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Value'" },
-        { "kind" : "IdRef", "name" : "'Expression'" },
-        { "kind" : "IdRef", "name" : "'Indexes'", "quantifier" : "*" }
-      ]
-    },
-    {
-      "opname" : "DebugOperation",
-      "opcode" : 30,
-      "operands" : [
-        { "kind" : "DebugOperation", "name" : "'OpCode'" },
-        { "kind" : "LiteralInteger", "name" : "'Operands ...'", "quantifier" : "*" }
-      ]
-    },
-    {
-      "opname" : "DebugExpression",
-      "opcode" : 31,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Operands ...'", "quantifier" : "*" }
-      ]
-    },
-    {
-      "opname" : "DebugMacroDef",
-      "opcode" : 32,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" }
-      ]
-    },
-    {
-      "opname" : "DebugMacroUndef",
-      "opcode" : 33,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "IdRef", "name" : "'Macro'" }
-      ]
-    }
-  ],
-  "operand_kinds" : [
-    {
-      "category" : "BitEnum",
-      "kind" : "DebugInfoFlags",
-      "enumerants" : [
-        {
-          "enumerant" : "FlagIsProtected",
-          "value" : "0x01"
-        },
-        {
-          "enumerant" : "FlagIsPrivate",
-          "value" : "0x02"
-        },
-        {
-          "enumerant" : "FlagIsPublic",
-          "value" : "0x03"
-        },
-        {
-          "enumerant" : "FlagIsLocal",
-          "value" : "0x04"
-        },
-        {
-          "enumerant" : "FlagIsDefinition",
-          "value" : "0x08"
-        },
-        {
-          "enumerant" : "FlagFwdDecl",
-          "value" : "0x10"
-        },
-        {
-          "enumerant" : "FlagArtificial",
-          "value" : "0x20"
-        },
-        {
-          "enumerant" : "FlagExplicit",
-          "value" : "0x40"
-        },
-        {
-          "enumerant" : "FlagPrototyped",
-          "value" : "0x80"
-        },
-        {
-          "enumerant" : "FlagObjectPointer",
-          "value" : "0x100"
-        },
-        {
-          "enumerant" : "FlagStaticMember",
-          "value" : "0x200"
-        },
-        {
-          "enumerant" : "FlagIndirectVariable",
-          "value" : "0x400"
-        },
-        {
-          "enumerant" : "FlagLValueReference",
-          "value" : "0x800"
-        },
-        {
-          "enumerant" : "FlagRValueReference",
-          "value" : "0x1000"
-        },
-        {
-          "enumerant" : "FlagIsOptimized",
-          "value" : "0x2000"
-        }
-      ]
-    },
-    {
-      "category" : "ValueEnum",
-      "kind" : "DebugBaseTypeAttributeEncoding",
-      "enumerants" : [
-        {
-          "enumerant" : "Unspecified",
-          "value" : "0"
-        },
-        {
-          "enumerant" : "Address",
-          "value" : "1"
-        },
-        {
-          "enumerant" : "Boolean",
-          "value" : "2"
-        },
-        {
-          "enumerant" : "Float",
-          "value" : "4"
-        },
-        {
-          "enumerant" : "Signed",
-          "value" : "5"
-        },
-        {
-          "enumerant" : "SignedChar",
-          "value" : "6"
-        },
-        {
-          "enumerant" : "Unsigned",
-          "value" : "7"
-        },
-        {
-          "enumerant" : "UnsignedChar",
-          "value" : "8"
-        }
-      ]
-    },
-    {
-      "category" : "ValueEnum",
-      "kind" : "DebugCompositeType",
-      "enumerants" : [
-        {
-          "enumerant" : "Class",
-          "value" : "0"
-        },
-        {
-          "enumerant" : "Structure",
-          "value" : "1"
-        },
-        {
-          "enumerant" : "Union",
-          "value" : "2"
-        }
-      ]
-    },
-    {
-      "category" : "ValueEnum",
-      "kind" : "DebugTypeQualifier",
-      "enumerants" : [
-        {
-          "enumerant" : "ConstType",
-          "value" : "0"
-        },
-        {
-          "enumerant" : "VolatileType",
-          "value" : "1"
-        },
-        {
-          "enumerant" : "RestrictType",
-          "value" : "2"
-        }
-      ]
-    },
-    {
-      "category" : "ValueEnum",
-      "kind" : "DebugOperation",
-      "enumerants" : [
-        {
-          "enumerant" : "Deref",
-          "value" : "0"
-        },
-        {
-          "enumerant" : "Plus",
-          "value" : "1"
-        },
-        {
-          "enumerant" : "Minus",
-          "value" : "2"
-        },
-        {
-          "enumerant" : "PlusUconst",
-          "value" : "3",
-          "parameters" : [
-             { "kind" : "LiteralInteger" }
-          ]
-        },
-        {
-          "enumerant" : "BitPiece",
-          "value" : "4",
-          "parameters" : [
-             { "kind" : "LiteralInteger" },
-             { "kind" : "LiteralInteger" }
-          ]
-        },
-        {
-          "enumerant" : "Swap",
-          "value" : "5"
-        },
-        {
-          "enumerant" : "Xderef",
-          "value" : "6"
-        },
-        {
-          "enumerant" : "StackValue",
-          "value" : "7"
-        },
-        {
-          "enumerant" : "Constu",
-          "value" : "8",
-          "parameters" : [
-             { "kind" : "LiteralInteger" }
-          ]
-        }
-      ]
-    }
-  ]
-}

+ 0 - 632
3rdparty/spirv-tools/source/extinst.opencl.debuginfo.100.grammar.json

@@ -1,632 +0,0 @@
-{
-  "copyright" : [
-    "Copyright (c) 2018 The Khronos Group Inc.",
-    "",
-    "Permission is hereby granted, free of charge, to any person obtaining a copy",
-    "of this software and/or associated documentation files (the \"Materials\"),",
-    "to deal in the Materials without restriction, including without limitation",
-    "the rights to use, copy, modify, merge, publish, distribute, sublicense,",
-    "and/or sell copies of the Materials, and to permit persons to whom the",
-    "Materials are furnished to do so, subject to the following conditions:",
-    "",
-    "The above copyright notice and this permission notice shall be included in",
-    "all copies or substantial portions of the Materials.",
-    "",
-    "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS",
-    "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND",
-    "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ",
-    "",
-    "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS",
-    "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
-    "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL",
-    "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
-    "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING",
-    "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS",
-    "IN THE MATERIALS."
-  ],
-  "version" : 200,
-  "revision" : 2,
-  "instructions" : [
-    {
-      "opname" : "DebugInfoNone",
-      "opcode" : 0
-    },
-    {
-      "opname" : "DebugCompilationUnit",
-      "opcode" : 1,
-      "operands" : [
-        { "kind" : "LiteralInteger", "name" : "'Version'" },
-        { "kind" : "LiteralInteger", "name" : "'DWARF Version'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "SourceLanguage", "name" : "'Language'" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeBasic",
-      "opcode" : 2,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Size'" },
-        { "kind" : "DebugBaseTypeAttributeEncoding", "name" : "'Encoding'" }
-      ]
-    },
-    {
-      "opname" : "DebugTypePointer",
-      "opcode" : 3,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Base Type'" },
-        { "kind" : "StorageClass", "name" : "'Storage Class'" },
-        { "kind" : "DebugInfoFlags", "name" : "'Flags'" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeQualifier",
-      "opcode" : 4,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Base Type'" },
-        { "kind" : "DebugTypeQualifier", "name" : "'Type Qualifier'" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeArray",
-      "opcode" : 5,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Base Type'" },
-        { "kind" : "IdRef", "name" : "'Component Counts'", "quantifier" : "*" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeVector",
-      "opcode" : 6,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Base Type'" },
-        { "kind" : "LiteralInteger", "name" : "'Component Count'" }
-      ]
-    },
-    {
-      "opname" : "DebugTypedef",
-      "opcode" : 7,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Base Type'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" },
-        { "kind" : "IdRef", "name" : "'Parent'" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeFunction",
-      "opcode" : 8,
-      "operands" : [
-        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
-        { "kind" : "IdRef", "name" : "'Return Type'" },
-        { "kind" : "IdRef", "name" : "'Parameter Types'", "quantifier" : "*" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeEnum",
-      "opcode" : 9,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Underlying Type'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" },
-        { "kind" : "IdRef", "name" : "'Parent'" },
-        { "kind" : "IdRef", "name" : "'Size'" },
-        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
-        { "kind" : "PairIdRefIdRef", "name" : "'Value, Name, Value, Name, ...'", "quantifier" : "*" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeComposite",
-      "opcode" : 10,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "DebugCompositeType", "name" : "'Tag'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" },
-        { "kind" : "IdRef", "name" : "'Parent'" },
-        { "kind" : "IdRef", "name" : "'Linkage Name'" },
-        { "kind" : "IdRef", "name" : "'Size'" },
-        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
-        { "kind" : "IdRef", "name" : "'Members'", "quantifier" : "*" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeMember",
-      "opcode" : 11,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Type'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" },
-        { "kind" : "IdRef", "name" : "'Parent'" },
-        { "kind" : "IdRef", "name" : "'Offset'" },
-        { "kind" : "IdRef", "name" : "'Size'" },
-        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
-        { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeInheritance",
-      "opcode" : 12,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Child'" },
-        { "kind" : "IdRef", "name" : "'Parent'" },
-        { "kind" : "IdRef", "name" : "'Offset'" },
-        { "kind" : "IdRef", "name" : "'Size'" },
-        { "kind" : "DebugInfoFlags", "name" : "'Flags'" }
-      ]
-    },
-    {
-      "opname" : "DebugTypePtrToMember",
-      "opcode" : 13,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Member Type'" },
-        { "kind" : "IdRef", "name" : "'Parent'" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeTemplate",
-      "opcode" : 14,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Target'" },
-        { "kind" : "IdRef", "name" : "'Parameters'", "quantifier" : "*" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeTemplateParameter",
-      "opcode" : 15,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Actual Type'" },
-        { "kind" : "IdRef", "name" : "'Value'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeTemplateTemplateParameter",
-      "opcode" : 16,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Template Name'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" }
-      ]
-    },
-    {
-      "opname" : "DebugTypeTemplateParameterPack",
-      "opcode" : 17,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" },
-        { "kind" : "IdRef", "name" : "'Template Parameters'", "quantifier" : "*" }
-      ]
-    },
-    {
-      "opname" : "DebugGlobalVariable",
-      "opcode" : 18,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Type'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" },
-        { "kind" : "IdRef", "name" : "'Parent'" },
-        { "kind" : "IdRef", "name" : "'Linkage Name'" },
-        { "kind" : "IdRef", "name" : "'Variable'" },
-        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
-        { "kind" : "IdRef", "name" : "'Static Member Declaration'", "quantifier" : "?" }
-      ]
-    },
-    {
-      "opname" : "DebugFunctionDeclaration",
-      "opcode" : 19,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Type'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" },
-        { "kind" : "IdRef", "name" : "'Parent'" },
-        { "kind" : "IdRef", "name" : "'Linkage Name'" },
-        { "kind" : "DebugInfoFlags", "name" : "'Flags'" }
-      ]
-    },
-    {
-      "opname" : "DebugFunction",
-      "opcode" : 20,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Type'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" },
-        { "kind" : "IdRef", "name" : "'Parent'" },
-        { "kind" : "IdRef", "name" : "'Linkage Name'" },
-        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
-        { "kind" : "LiteralInteger", "name" : "'Scope Line'" },
-        { "kind" : "IdRef", "name" : "'Function'" },
-        { "kind" : "IdRef", "name" : "'Declaration'", "quantifier" : "?" }
-      ]
-    },
-    {
-      "opname" : "DebugLexicalBlock",
-      "opcode" : 21,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" },
-        { "kind" : "IdRef", "name" : "'Parent'" },
-        { "kind" : "IdRef", "name" : "'Name'", "quantifier" : "?" }
-      ]
-    },
-    {
-      "opname" : "DebugLexicalBlockDiscriminator",
-      "opcode" : 22,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Discriminator'" },
-        { "kind" : "IdRef", "name" : "'Parent'" }
-      ]
-    },
-    {
-      "opname" : "DebugScope",
-      "opcode" : 23,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Scope'" },
-        { "kind" : "IdRef", "name" : "'Inlined At'", "quantifier" : "?" }
-      ]
-    },
-    {
-      "opname" : "DebugNoScope",
-      "opcode" : 24
-    },
-    {
-      "opname" : "DebugInlinedAt",
-      "opcode" : 25,
-      "operands" : [
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "IdRef", "name" : "'Scope'" },
-        { "kind" : "IdRef", "name" : "'Inlined'", "quantifier" : "?" }
-      ]
-    },
-    {
-      "opname" : "DebugLocalVariable",
-      "opcode" : 26,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Type'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" },
-        { "kind" : "IdRef", "name" : "'Parent'" },
-        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
-        { "kind" : "LiteralInteger", "name" : "'Arg Number'", "quantifier" : "?" }
-      ]
-    },
-    {
-      "opname" : "DebugInlinedVariable",
-      "opcode" : 27,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Variable'" },
-        { "kind" : "IdRef", "name" : "'Inlined'" }
-      ]
-    },
-    {
-      "opname" : "DebugDeclare",
-      "opcode" : 28,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Local Variable'" },
-        { "kind" : "IdRef", "name" : "'Variable'" },
-        { "kind" : "IdRef", "name" : "'Expression'" }
-      ]
-    },
-    {
-      "opname" : "DebugValue",
-      "opcode" : 29,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Local Variable'" },
-        { "kind" : "IdRef", "name" : "'Value'" },
-        { "kind" : "IdRef", "name" : "'Expression'" },
-        { "kind" : "IdRef", "name" : "'Indexes'", "quantifier" : "*" }
-      ]
-    },
-    {
-      "opname" : "DebugOperation",
-      "opcode" : 30,
-      "operands" : [
-        { "kind" : "DebugOperation", "name" : "'OpCode'" },
-        { "kind" : "LiteralInteger", "name" : "'Operands ...'", "quantifier" : "*" }
-      ]
-    },
-    {
-      "opname" : "DebugExpression",
-      "opcode" : 31,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Operands ...'", "quantifier" : "*" }
-      ]
-    },
-    {
-      "opname" : "DebugMacroDef",
-      "opcode" : 32,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" }
-      ]
-    },
-    {
-      "opname" : "DebugMacroUndef",
-      "opcode" : 33,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "IdRef", "name" : "'Macro'" }
-      ]
-    },
-    {
-      "opname" : "DebugImportedEntity",
-      "opcode" : 34,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'Name'" },
-        { "kind" : "DebugImportedEntity", "name" : "'Tag'" },
-        { "kind" : "IdRef", "name" : "'Source'" },
-        { "kind" : "IdRef", "name" : "'Entity'" },
-        { "kind" : "LiteralInteger", "name" : "'Line'" },
-        { "kind" : "LiteralInteger", "name" : "'Column'" },
-        { "kind" : "IdRef", "name" : "'Parent'" }
-      ]
-    },
-    {
-      "opname" : "DebugSource",
-      "opcode" : 35,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'File'" },
-        { "kind" : "IdRef", "name" : "'Text'", "quantifier" : "?" }
-      ]
-    }
-  ],
-  "operand_kinds" : [
-    {
-      "category" : "BitEnum",
-      "kind" : "DebugInfoFlags",
-      "enumerants" : [
-        {
-          "enumerant" : "FlagIsProtected",
-          "value" : "0x01"
-        },
-        {
-          "enumerant" : "FlagIsPrivate",
-          "value" : "0x02"
-        },
-        {
-          "enumerant" : "FlagIsPublic",
-          "value" : "0x03"
-        },
-        {
-          "enumerant" : "FlagIsLocal",
-          "value" : "0x04"
-        },
-        {
-          "enumerant" : "FlagIsDefinition",
-          "value" : "0x08"
-        },
-        {
-          "enumerant" : "FlagFwdDecl",
-          "value" : "0x10"
-        },
-        {
-          "enumerant" : "FlagArtificial",
-          "value" : "0x20"
-        },
-        {
-          "enumerant" : "FlagExplicit",
-          "value" : "0x40"
-        },
-        {
-          "enumerant" : "FlagPrototyped",
-          "value" : "0x80"
-        },
-        {
-          "enumerant" : "FlagObjectPointer",
-          "value" : "0x100"
-        },
-        {
-          "enumerant" : "FlagStaticMember",
-          "value" : "0x200"
-        },
-        {
-          "enumerant" : "FlagIndirectVariable",
-          "value" : "0x400"
-        },
-        {
-          "enumerant" : "FlagLValueReference",
-          "value" : "0x800"
-        },
-        {
-          "enumerant" : "FlagRValueReference",
-          "value" : "0x1000"
-        },
-        {
-          "enumerant" : "FlagIsOptimized",
-          "value" : "0x2000"
-        },
-        {
-          "enumerant" : "FlagIsEnumClass",
-          "value" : "0x4000"
-        },
-        {
-          "enumerant" : "FlagTypePassByValue",
-          "value" : "0x8000"
-        },
-        {
-          "enumerant" : "FlagTypePassByReference",
-          "value" : "0x10000"
-        }
-      ]
-    },
-    {
-      "category" : "ValueEnum",
-      "kind" : "DebugBaseTypeAttributeEncoding",
-      "enumerants" : [
-        {
-          "enumerant" : "Unspecified",
-          "value" : "0"
-        },
-        {
-          "enumerant" : "Address",
-          "value" : "1"
-        },
-        {
-          "enumerant" : "Boolean",
-          "value" : "2"
-        },
-        {
-          "enumerant" : "Float",
-          "value" : "3"
-        },
-        {
-          "enumerant" : "Signed",
-          "value" : "4"
-        },
-        {
-          "enumerant" : "SignedChar",
-          "value" : "5"
-        },
-        {
-          "enumerant" : "Unsigned",
-          "value" : "6"
-        },
-        {
-          "enumerant" : "UnsignedChar",
-          "value" : "7"
-        }
-      ]
-    },
-    {
-      "category" : "ValueEnum",
-      "kind" : "DebugCompositeType",
-      "enumerants" : [
-        {
-          "enumerant" : "Class",
-          "value" : "0"
-        },
-        {
-          "enumerant" : "Structure",
-          "value" : "1"
-        },
-        {
-          "enumerant" : "Union",
-          "value" : "2"
-        }
-      ]
-    },
-    {
-      "category" : "ValueEnum",
-      "kind" : "DebugTypeQualifier",
-      "enumerants" : [
-        {
-          "enumerant" : "ConstType",
-          "value" : "0"
-        },
-        {
-          "enumerant" : "VolatileType",
-          "value" : "1"
-        },
-        {
-          "enumerant" : "RestrictType",
-          "value" : "2"
-        },
-        {
-          "enumerant" : "AtomicType",
-          "value" : "3"
-        }
-      ]
-    },
-    {
-      "category" : "ValueEnum",
-      "kind" : "DebugOperation",
-      "enumerants" : [
-        {
-          "enumerant" : "Deref",
-          "value" : "0"
-        },
-        {
-          "enumerant" : "Plus",
-          "value" : "1"
-        },
-        {
-          "enumerant" : "Minus",
-          "value" : "2"
-        },
-        {
-          "enumerant" : "PlusUconst",
-          "value" : "3",
-          "parameters" : [
-             { "kind" : "LiteralInteger" }
-          ]
-        },
-        {
-          "enumerant" : "BitPiece",
-          "value" : "4",
-          "parameters" : [
-             { "kind" : "LiteralInteger" },
-             { "kind" : "LiteralInteger" }
-          ]
-        },
-        {
-          "enumerant" : "Swap",
-          "value" : "5"
-        },
-        {
-          "enumerant" : "Xderef",
-          "value" : "6"
-        },
-        {
-          "enumerant" : "StackValue",
-          "value" : "7"
-        },
-        {
-          "enumerant" : "Constu",
-          "value" : "8",
-          "parameters" : [
-             { "kind" : "LiteralInteger" }
-          ]
-        },
-        {
-          "enumerant" : "Fragment",
-          "value" : "9",
-          "parameters" : [
-             { "kind" : "LiteralInteger" },
-             { "kind" : "LiteralInteger" }
-          ]
-        }
-      ]
-    },
-    {
-      "category" : "ValueEnum",
-      "kind" : "DebugImportedEntity",
-      "enumerants" : [
-        {
-          "enumerant" : "ImportedModule",
-          "value" : "0"
-        },
-        {
-          "enumerant" : "ImportedDeclaration",
-          "value" : "1"
-	}
-      ]
-    }
-  ]
-}

+ 0 - 26
3rdparty/spirv-tools/source/extinst.spv-amd-gcn-shader.grammar.json

@@ -1,26 +0,0 @@
-{
-  "revision" : 2,
-  "instructions" : [
-    {
-      "opname" : "CubeFaceIndexAMD",
-      "opcode" : 1,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'P'" }
-      ],
-      "extensions" : [ "SPV_AMD_gcn_shader" ]
-    },
-    {
-      "opname" : "CubeFaceCoordAMD",
-      "opcode" : 2,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'P'" }
-      ],
-      "extensions" : [ "SPV_AMD_gcn_shader" ]
-    },
-    {
-      "opname" : "TimeAMD",
-      "opcode" : 3,
-      "extensions" : [ "SPV_AMD_gcn_shader" ]
-    }
-  ]
-}

+ 0 - 41
3rdparty/spirv-tools/source/extinst.spv-amd-shader-ballot.grammar.json

@@ -1,41 +0,0 @@
-{
-  "revision" : 5,
-  "instructions" : [
-    {
-      "opname" : "SwizzleInvocationsAMD",
-      "opcode" : 1,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'data'" },
-        { "kind" : "IdRef", "name" : "'offset'" }
-      ],
-      "extensions" : [ "SPV_AMD_shader_ballot" ]
-    },
-    {
-      "opname" : "SwizzleInvocationsMaskedAMD",
-      "opcode" : 2,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'data'" },
-        { "kind" : "IdRef", "name" : "'mask'" }
-      ],
-      "extensions" : [ "SPV_AMD_shader_ballot" ]
-    },
-    {
-      "opname" : "WriteInvocationAMD",
-      "opcode" : 3,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'inputValue'" },
-        { "kind" : "IdRef", "name" : "'writeValue'" },
-        { "kind" : "IdRef", "name" : "'invocationIndex'" }
-      ],
-      "extensions" : [ "SPV_AMD_shader_ballot" ]
-    },
-    {
-      "opname" : "MbcntAMD",
-      "opcode" : 4,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'mask'" }
-      ],
-      "extensions" : [ "SPV_AMD_shader_ballot" ]
-    }
-  ]
-}

+ 0 - 14
3rdparty/spirv-tools/source/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json

@@ -1,14 +0,0 @@
-{
-  "revision" : 4,
-  "instructions" : [
-    {
-      "opname" : "InterpolateAtVertexAMD",
-      "opcode" : 1,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'interpolant'" },
-        { "kind" : "IdRef", "name" : "'vertexIdx'" }
-      ],
-      "extensions" : [ "SPV_AMD_shader_explicit_vertex_parameter" ]
-    }
-  ]
-}

+ 0 - 95
3rdparty/spirv-tools/source/extinst.spv-amd-shader-trinary-minmax.grammar.json

@@ -1,95 +0,0 @@
-{
-  "revision" : 4,
-  "instructions" : [
-    {
-      "opname" : "FMin3AMD",
-      "opcode" : 1,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'x'" },
-        { "kind" : "IdRef", "name" : "'y'" },
-        { "kind" : "IdRef", "name" : "'z'" }
-      ],
-      "extensions" : [ "SPV_AMD_shader_trinary_minmax" ]
-    },
-    {
-      "opname" : "UMin3AMD",
-      "opcode" : 2,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'x'" },
-        { "kind" : "IdRef", "name" : "'y'" },
-        { "kind" : "IdRef", "name" : "'z'" }
-      ],
-      "extensions" : [ "SPV_AMD_shader_trinary_minmax" ]
-    },
-    {
-      "opname" : "SMin3AMD",
-      "opcode" : 3,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'x'" },
-        { "kind" : "IdRef", "name" : "'y'" },
-        { "kind" : "IdRef", "name" : "'z'" }
-      ],
-      "extensions" : [ "SPV_AMD_shader_trinary_minmax" ]
-    },
-    {
-      "opname" : "FMax3AMD",
-      "opcode" : 4,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'x'" },
-        { "kind" : "IdRef", "name" : "'y'" },
-        { "kind" : "IdRef", "name" : "'z'" }
-      ],
-      "extensions" : [ "SPV_AMD_shader_trinary_minmax" ]
-    },
-    {
-      "opname" : "UMax3AMD",
-      "opcode" : 5,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'x'" },
-        { "kind" : "IdRef", "name" : "'y'" },
-        { "kind" : "IdRef", "name" : "'z'" }
-      ],
-      "extensions" : [ "SPV_AMD_shader_trinary_minmax" ]
-    },
-    {
-      "opname" : "SMax3AMD",
-      "opcode" : 6,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'x'" },
-        { "kind" : "IdRef", "name" : "'y'" },
-        { "kind" : "IdRef", "name" : "'z'" }
-      ],
-      "extensions" : [ "SPV_AMD_shader_trinary_minmax" ]
-    },
-    {
-      "opname" : "FMid3AMD",
-      "opcode" : 7,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'x'" },
-        { "kind" : "IdRef", "name" : "'y'" },
-        { "kind" : "IdRef", "name" : "'z'" }
-      ],
-      "extensions" : [ "SPV_AMD_shader_trinary_minmax" ]
-    },
-    {
-      "opname" : "UMid3AMD",
-      "opcode" : 8,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'x'" },
-        { "kind" : "IdRef", "name" : "'y'" },
-        { "kind" : "IdRef", "name" : "'z'" }
-      ],
-      "extensions" : [ "SPV_AMD_shader_trinary_minmax" ]
-    },
-    {
-      "opname" : "SMid3AMD",
-      "opcode" : 9,
-      "operands" : [
-        { "kind" : "IdRef", "name" : "'x'" },
-        { "kind" : "IdRef", "name" : "'y'" },
-        { "kind" : "IdRef", "name" : "'z'" }
-      ],
-      "extensions" : [ "SPV_AMD_shader_trinary_minmax" ]
-    }
-  ]
-}

+ 9 - 1
3rdparty/spirv-tools/source/fuzz/CMakeLists.txt

@@ -65,6 +65,7 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_copy_objects.h
         fuzzer_pass_donate_modules.h
         fuzzer_pass_invert_comparison_operators.h
+        fuzzer_pass_interchange_signedness_of_integer_operands.h
         fuzzer_pass_interchange_zero_like_constants.h
         fuzzer_pass_merge_blocks.h
         fuzzer_pass_obfuscate_constants.h
@@ -76,6 +77,7 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_replace_copy_memories_with_loads_stores.h
         fuzzer_pass_replace_copy_objects_with_stores_loads.h
         fuzzer_pass_replace_linear_algebra_instructions.h
+        fuzzer_pass_replace_loads_stores_with_copy_memories.h
         fuzzer_pass_replace_parameter_with_global.h
         fuzzer_pass_replace_params_with_struct.h
         fuzzer_pass_split_blocks.h
@@ -106,6 +108,7 @@ if(SPIRV_BUILD_FUZZER)
         transformation_add_global_variable.h
         transformation_add_image_sample_unused_components.h
         transformation_add_local_variable.h
+        transformation_add_loop_preheader.h
         transformation_add_no_contraction_decoration.h
         transformation_add_parameter.h
         transformation_add_relaxed_decoration.h
@@ -142,6 +145,7 @@ if(SPIRV_BUILD_FUZZER)
         transformation_replace_copy_object_with_store_load.h
         transformation_replace_id_with_synonym.h
         transformation_replace_linear_algebra_instruction.h
+        transformation_replace_load_store_with_copy_memory.h
         transformation_replace_parameter_with_global.h
         transformation_replace_params_with_struct.h
         transformation_set_function_control.h
@@ -192,6 +196,7 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_copy_objects.cpp
         fuzzer_pass_donate_modules.cpp
         fuzzer_pass_invert_comparison_operators.cpp
+        fuzzer_pass_interchange_signedness_of_integer_operands.cpp
         fuzzer_pass_interchange_zero_like_constants.cpp
         fuzzer_pass_merge_blocks.cpp
         fuzzer_pass_obfuscate_constants.cpp
@@ -203,6 +208,7 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
         fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
         fuzzer_pass_replace_linear_algebra_instructions.cpp
+        fuzzer_pass_replace_loads_stores_with_copy_memories.cpp
         fuzzer_pass_replace_parameter_with_global.cpp
         fuzzer_pass_replace_params_with_struct.cpp
         fuzzer_pass_split_blocks.cpp
@@ -232,6 +238,7 @@ if(SPIRV_BUILD_FUZZER)
         transformation_add_global_variable.cpp
         transformation_add_image_sample_unused_components.cpp
         transformation_add_local_variable.cpp
+        transformation_add_loop_preheader.cpp
         transformation_add_no_contraction_decoration.cpp
         transformation_add_parameter.cpp
         transformation_add_relaxed_decoration.cpp
@@ -268,6 +275,7 @@ if(SPIRV_BUILD_FUZZER)
         transformation_replace_copy_object_with_store_load.cpp
         transformation_replace_id_with_synonym.cpp
         transformation_replace_linear_algebra_instruction.cpp
+        transformation_replace_load_store_with_copy_memory.cpp
         transformation_replace_parameter_with_global.cpp
         transformation_replace_params_with_struct.cpp
         transformation_set_function_control.cpp
@@ -315,7 +323,7 @@ if(SPIRV_BUILD_FUZZER)
 
   # The fuzzer reuses a lot of functionality from the SPIRV-Tools library.
   target_link_libraries(SPIRV-Tools-fuzz
-        PUBLIC ${SPIRV_TOOLS}
+        PUBLIC ${SPIRV_TOOLS}-static
         PUBLIC SPIRV-Tools-opt
         PUBLIC protobuf::libprotobuf)
 

+ 79 - 39
3rdparty/spirv-tools/source/fuzz/fact_manager.cpp

@@ -474,11 +474,19 @@ class FactManager::DataSynonymAndIdEquationFacts {
   void MakeEquivalent(const protobufs::DataDescriptor& dd1,
                       const protobufs::DataDescriptor& dd2);
 
+  // Registers a data descriptor in the equivalence relation if it hasn't been
+  // registered yet, and returns its representative.
+  const protobufs::DataDescriptor* RegisterDataDescriptor(
+      const protobufs::DataDescriptor& dd);
+
   // Returns true if and only if |dd1| and |dd2| are valid data descriptors
-  // whose associated data have the same type (modulo integer signedness).
-  bool DataDescriptorsAreWellFormedAndComparable(
+  // whose associated data have compatible types. Two types are compatible if:
+  // - they are the same
+  // - they both are numerical or vectors of numerical components with the same
+  //   number of components and the same bit count per component
+  static bool DataDescriptorsAreWellFormedAndComparable(
       opt::IRContext* context, const protobufs::DataDescriptor& dd1,
-      const protobufs::DataDescriptor& dd2) const;
+      const protobufs::DataDescriptor& dd2);
 
   OperationSet GetEquations(const protobufs::DataDescriptor* lhs) const;
 
@@ -530,9 +538,7 @@ void FactManager::DataSynonymAndIdEquationFacts::AddFact(
   protobufs::DataDescriptor lhs_dd = MakeDataDescriptor(fact.lhs_id(), {});
 
   // Register the LHS in the equivalence relation if needed.
-  if (!synonymous_.Exists(lhs_dd)) {
-    synonymous_.Register(lhs_dd);
-  }
+  RegisterDataDescriptor(lhs_dd);
 
   // Get equivalence class representatives for all ids used on the RHS of the
   // equation.
@@ -540,11 +546,8 @@ void FactManager::DataSynonymAndIdEquationFacts::AddFact(
   for (auto rhs_id : fact.rhs_id()) {
     // Register a data descriptor based on this id in the equivalence relation
     // if needed, and then record the equivalence class representative.
-    protobufs::DataDescriptor rhs_dd = MakeDataDescriptor(rhs_id, {});
-    if (!synonymous_.Exists(rhs_dd)) {
-      synonymous_.Register(rhs_dd);
-    }
-    rhs_dd_ptrs.push_back(synonymous_.Find(&rhs_dd));
+    rhs_dd_ptrs.push_back(
+        RegisterDataDescriptor(MakeDataDescriptor(rhs_id, {})));
   }
 
   // Now add the fact.
@@ -604,6 +607,14 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
     case SpvOpConvertUToF:
       ComputeConversionDataSynonymFacts(*rhs_dds[0], context);
       break;
+    case SpvOpBitcast: {
+      assert(DataDescriptorsAreWellFormedAndComparable(context, lhs_dd,
+                                                       *rhs_dds[0]) &&
+             "Operands of OpBitcast equation fact must have compatible types");
+      if (!synonymous_.IsEquivalent(lhs_dd, *rhs_dds[0])) {
+        AddDataSynonymFactRecursive(lhs_dd, *rhs_dds[0], context);
+      }
+    } break;
     case SpvOpIAdd: {
       // Equation form: "a = b + c"
       for (const auto& equation : GetEquations(rhs_dds[0])) {
@@ -713,9 +724,15 @@ void FactManager::DataSynonymAndIdEquationFacts::AddDataSynonymFactRecursive(
 
   // Record that the data descriptors provided in the fact are equivalent.
   MakeEquivalent(dd1, dd2);
+  assert(synonymous_.Find(&dd1) == synonymous_.Find(&dd2) &&
+         "|dd1| and |dd2| must have a single representative");
 
   // Compute various corollary facts.
+
+  // |dd1| and |dd2| belong to the same equivalence class so it doesn't matter
+  // which one we use here.
   ComputeConversionDataSynonymFacts(dd1, context);
+
   ComputeCompositeDataSynonymFacts(dd1, dd2, context);
 }
 
@@ -776,7 +793,7 @@ void FactManager::DataSynonymAndIdEquationFacts::
     ComputeCompositeDataSynonymFacts(const protobufs::DataDescriptor& dd1,
                                      const protobufs::DataDescriptor& dd2,
                                      opt::IRContext* context) {
-  // Check whether this is a synonym about composite objects.  If it is,
+  // Check whether this is a synonym about composite objects. If it is,
   // we can recursively add synonym facts about their associated sub-components.
 
   // Get the type of the object referred to by the first data descriptor in the
@@ -1132,11 +1149,8 @@ void FactManager::DataSynonymAndIdEquationFacts::MakeEquivalent(
     const protobufs::DataDescriptor& dd2) {
   // Register the data descriptors if they are not already known to the
   // equivalence relation.
-  for (const auto& dd : {dd1, dd2}) {
-    if (!synonymous_.Exists(dd)) {
-      synonymous_.Register(dd);
-    }
-  }
+  RegisterDataDescriptor(dd1);
+  RegisterDataDescriptor(dd2);
 
   if (synonymous_.IsEquivalent(dd1, dd2)) {
     // The data descriptors are already known to be equivalent, so there is
@@ -1203,10 +1217,17 @@ void FactManager::DataSynonymAndIdEquationFacts::MakeEquivalent(
   id_equations_.erase(no_longer_representative);
 }
 
+const protobufs::DataDescriptor*
+FactManager::DataSynonymAndIdEquationFacts::RegisterDataDescriptor(
+    const protobufs::DataDescriptor& dd) {
+  return synonymous_.Exists(dd) ? synonymous_.Find(&dd)
+                                : synonymous_.Register(dd);
+}
+
 bool FactManager::DataSynonymAndIdEquationFacts::
     DataDescriptorsAreWellFormedAndComparable(
         opt::IRContext* context, const protobufs::DataDescriptor& dd1,
-        const protobufs::DataDescriptor& dd2) const {
+        const protobufs::DataDescriptor& dd2) {
   auto end_type_id_1 = fuzzerutil::WalkCompositeTypeIndices(
       context, context->get_def_use_mgr()->GetDef(dd1.object())->type_id(),
       dd1.index());
@@ -1225,30 +1246,49 @@ bool FactManager::DataSynonymAndIdEquationFacts::
   // vectors that differ only in signedness.
 
   // Get both types.
-  const opt::analysis::Type* type_1 =
-      context->get_type_mgr()->GetType(end_type_id_1);
-  const opt::analysis::Type* type_2 =
-      context->get_type_mgr()->GetType(end_type_id_2);
-
-  // If the first type is a vector, check that the second type is a vector of
-  // the same width, and drill down to the vector element types.
-  if (type_1->AsVector()) {
-    if (!type_2->AsVector()) {
-      return false;
-    }
-    if (type_1->AsVector()->element_count() !=
-        type_2->AsVector()->element_count()) {
+  const auto* type_a = context->get_type_mgr()->GetType(end_type_id_1);
+  const auto* type_b = context->get_type_mgr()->GetType(end_type_id_2);
+  assert(type_a && type_b && "Data descriptors have invalid type(s)");
+
+  // If both types are numerical or vectors of numerical components, then they
+  // are compatible if they have the same number of components and the same bit
+  // count per component.
+
+  if (type_a->AsVector() && type_b->AsVector()) {
+    const auto* vector_a = type_a->AsVector();
+    const auto* vector_b = type_b->AsVector();
+
+    if (vector_a->element_count() != vector_b->element_count() ||
+        vector_a->element_type()->AsBool() ||
+        vector_b->element_type()->AsBool()) {
+      // The case where both vectors have boolean elements and the same number
+      // of components is handled by the direct equality check earlier.
+      // You can't have multiple identical boolean vector types.
       return false;
     }
-    type_1 = type_1->AsVector()->element_type();
-    type_2 = type_2->AsVector()->element_type();
+
+    type_a = vector_a->element_type();
+    type_b = vector_b->element_type();
   }
-  // Check that type_1 and type_2 are both integer types of the same bit-width
-  // (but with potentially different signedness).
-  auto integer_type_1 = type_1->AsInteger();
-  auto integer_type_2 = type_2->AsInteger();
-  return integer_type_1 && integer_type_2 &&
-         integer_type_1->width() == integer_type_2->width();
+
+  auto get_bit_count_for_numeric_type =
+      [](const opt::analysis::Type& type) -> uint32_t {
+    if (const auto* integer = type.AsInteger()) {
+      return integer->width();
+    } else if (const auto* floating = type.AsFloat()) {
+      return floating->width();
+    } else {
+      assert(false && "|type| must be a numerical type");
+      return 0;
+    }
+  };
+
+  // Checks that both |type_a| and |type_b| are either numerical or vectors of
+  // numerical components and have the same number of bits.
+  return (type_a->AsInteger() || type_a->AsFloat()) &&
+         (type_b->AsInteger() || type_b->AsFloat()) &&
+         (get_bit_count_for_numeric_type(*type_a) ==
+          get_bit_count_for_numeric_type(*type_b));
 }
 
 std::vector<const protobufs::DataDescriptor*>

+ 4 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp

@@ -46,6 +46,7 @@
 #include "source/fuzz/fuzzer_pass_construct_composites.h"
 #include "source/fuzz/fuzzer_pass_copy_objects.h"
 #include "source/fuzz/fuzzer_pass_donate_modules.h"
+#include "source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h"
 #include "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h"
 #include "source/fuzz/fuzzer_pass_invert_comparison_operators.h"
 #include "source/fuzz/fuzzer_pass_merge_blocks.h"
@@ -335,6 +336,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
   MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
       &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
       transformation_sequence_out);
+  MaybeAddPass<FuzzerPassInterchangeSignednessOfIntegerOperands>(
+      &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
+      transformation_sequence_out);
   MaybeAddPass<FuzzerPassInterchangeZeroLikeConstants>(
       &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
       transformation_sequence_out);

+ 8 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp

@@ -67,6 +67,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfGoingDeeperWhenMakingAccessChain =
     {50, 95};
 const std::pair<uint32_t, uint32_t> kChanceOfInterchangingZeroLikeConstants = {
     10, 90};
+const std::pair<uint32_t, uint32_t>
+    kChanceOfInterchangingSignednessOfIntegerOperands = {10, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfInvertingComparisonOperators = {
     20, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfMakingDonorLivesafe = {40, 60};
@@ -84,6 +86,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyObjectWithStoreLoad =
 const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
 const std::pair<uint32_t, uint32_t>
     kChanceOfReplacingLinearAlgebraInstructions = {10, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfReplacingLoadStoreWithCopyMemory =
+    {20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithGlobals = {
     30, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithStruct = {
@@ -195,6 +199,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
       ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule);
   chance_of_going_deeper_when_making_access_chain_ =
       ChooseBetweenMinAndMax(kChanceOfGoingDeeperWhenMakingAccessChain);
+  chance_of_interchanging_signedness_of_integer_operands_ =
+      ChooseBetweenMinAndMax(kChanceOfInterchangingSignednessOfIntegerOperands);
   chance_of_interchanging_zero_like_constants_ =
       ChooseBetweenMinAndMax(kChanceOfInterchangingZeroLikeConstants);
   chance_of_inverting_comparison_operators_ =
@@ -222,6 +228,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
       ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
   chance_of_replacing_linear_algebra_instructions_ =
       ChooseBetweenMinAndMax(kChanceOfReplacingLinearAlgebraInstructions);
+  chance_of_replacing_load_store_with_copy_memory_ =
+      ChooseBetweenMinAndMax(kChanceOfReplacingLoadStoreWithCopyMemory);
   chance_of_replacing_parameters_with_globals_ =
       ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithGlobals);
   chance_of_replacing_parameters_with_struct_ =

+ 8 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_context.h

@@ -186,6 +186,9 @@ class FuzzerContext {
   uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() {
     return chance_of_going_deeper_when_making_access_chain_;
   }
+  uint32_t GetChanceOfInterchangingSignednessOfIntegerOperands() {
+    return chance_of_interchanging_signedness_of_integer_operands_;
+  }
   uint32_t GetChanceOfInterchangingZeroLikeConstants() {
     return chance_of_interchanging_zero_like_constants_;
   }
@@ -224,6 +227,9 @@ class FuzzerContext {
   uint32_t GetChanceOfReplacingLinearAlgebraInstructions() {
     return chance_of_replacing_linear_algebra_instructions_;
   }
+  uint32_t GetChanceOfReplacingLoadStoreWithCopyMemory() {
+    return chance_of_replacing_load_store_with_copy_memory_;
+  }
   uint32_t GetChanceOfReplacingParametersWithGlobals() {
     return chance_of_replacing_parameters_with_globals_;
   }
@@ -350,6 +356,7 @@ class FuzzerContext {
   uint32_t chance_of_copying_object_;
   uint32_t chance_of_donating_additional_module_;
   uint32_t chance_of_going_deeper_when_making_access_chain_;
+  uint32_t chance_of_interchanging_signedness_of_integer_operands_;
   uint32_t chance_of_interchanging_zero_like_constants_;
   uint32_t chance_of_inverting_comparison_operators_;
   uint32_t chance_of_making_donor_livesafe_;
@@ -364,6 +371,7 @@ class FuzzerContext {
   uint32_t chance_of_replacing_copyobject_with_store_load_;
   uint32_t chance_of_replacing_id_with_synonym_;
   uint32_t chance_of_replacing_linear_algebra_instructions_;
+  uint32_t chance_of_replacing_load_store_with_copy_memory_;
   uint32_t chance_of_replacing_parameters_with_globals_;
   uint32_t chance_of_replacing_parameters_with_struct_;
   uint32_t chance_of_splitting_block_;

+ 19 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp

@@ -17,6 +17,7 @@
 #include <set>
 
 #include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/id_use_descriptor.h"
 #include "source/fuzz/instruction_descriptor.h"
 #include "source/fuzz/transformation_add_constant_boolean.h"
 #include "source/fuzz/transformation_add_constant_composite.h"
@@ -514,5 +515,23 @@ uint32_t FuzzerPass::FindOrCreateZeroConstant(
   }
 }
 
+void FuzzerPass::MaybeAddUseToReplace(
+    opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
+    std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
+        uses_to_replace) {
+  // Only consider this use if it is in a block
+  if (!GetIRContext()->get_instr_block(use_inst)) {
+    return;
+  }
+
+  // Get the index of the operand restricted to input operands.
+  uint32_t in_operand_index =
+      fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index);
+  auto id_use_descriptor =
+      MakeIdUseDescriptorFromUse(GetIRContext(), use_inst, in_operand_index);
+  uses_to_replace->emplace_back(
+      std::make_pair(id_use_descriptor, replacement_id));
+}
+
 }  // namespace fuzz
 }  // namespace spvtools

+ 11 - 2
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h

@@ -175,8 +175,7 @@ class FuzzerPass {
   // with |words| as its value.  If either the required integer type or the
   // constant do not exist, transformations are applied to add them.
   // The returned id either participates in IdIsIrrelevant fact or not,
-  // depending
-  // on the |is_irrelevant| parameter.
+  // depending on the |is_irrelevant| parameter.
   uint32_t FindOrCreateIntegerConstant(const std::vector<uint32_t>& words,
                                        uint32_t width, bool is_signed,
                                        bool is_irrelevant);
@@ -274,6 +273,16 @@ class FuzzerPass {
   uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id,
                                     bool is_irrelevant);
 
+  // Adds a pair (id_use_descriptor, |replacement_id|) to the vector
+  // |uses_to_replace|, where id_use_descriptor is the id use descriptor
+  // representing the usage of an id in the |use_inst| instruction, at operand
+  // index |use_index|, only if the instruction is in a basic block.
+  // If the instruction is not in a basic block, it does nothing.
+  void MaybeAddUseToReplace(
+      opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
+      std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
+          uses_to_replace);
+
  private:
   opt::IRContext* ir_context_;
   TransformationContext* transformation_context_;

+ 30 - 8
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp

@@ -92,6 +92,11 @@ void FuzzerPassAddAccessChains::Apply() {
             relevant_pointer_instructions[GetFuzzerContext()->RandomIndex(
                 relevant_pointer_instructions)];
         std::vector<uint32_t> index_ids;
+
+        // Each index accessing a non-struct composite will be clamped, thus
+        // needing a pair of fresh ids
+        std::vector<std::pair<uint32_t, uint32_t>> fresh_ids_for_clamping;
+
         auto pointer_type = GetIRContext()->get_def_use_mgr()->GetDef(
             chosen_pointer->type_id());
         uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
@@ -132,20 +137,37 @@ void FuzzerPassAddAccessChains::Apply() {
             break;
           }
 
-          // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3179) We
-          //  could allow non-constant indices when looking up non-structs,
-          //  using clamping to ensure they are in-bounds.
           uint32_t index_value =
               GetFuzzerContext()->GetRandomIndexForAccessChain(bound);
-          index_ids.push_back(FindOrCreateIntegerConstant(
-              {index_value}, 32, GetFuzzerContext()->ChooseEven(), false));
+
           switch (subobject_type->opcode()) {
             case SpvOpTypeArray:
             case SpvOpTypeMatrix:
-            case SpvOpTypeVector:
+            case SpvOpTypeVector: {
+              // The index will be clamped
+
+              bool is_signed = GetFuzzerContext()->ChooseEven();
+
+              // Make the constant ready for clamping. We need:
+              // - an OpTypeBool to be present in the module
+              // - an OpConstant with the same type as the index and value
+              //   the maximum value for an index
+              // - a new pair of fresh ids for the clamping instructions
+              FindOrCreateBoolType();
+              FindOrCreateIntegerConstant({bound - 1}, 32, is_signed, false);
+              std::pair<uint32_t, uint32_t> fresh_pair_of_ids = {
+                  GetFuzzerContext()->GetFreshId(),
+                  GetFuzzerContext()->GetFreshId()};
+              fresh_ids_for_clamping.emplace_back(fresh_pair_of_ids);
+
+              index_ids.push_back(FindOrCreateIntegerConstant(
+                  {index_value}, 32, is_signed, false));
               subobject_type_id = subobject_type->GetSingleWordInOperand(0);
-              break;
+
+            } break;
             case SpvOpTypeStruct:
+              index_ids.push_back(FindOrCreateIntegerConstant(
+                  {index_value}, 32, GetFuzzerContext()->ChooseEven(), false));
               subobject_type_id =
                   subobject_type->GetSingleWordInOperand(index_value);
               break;
@@ -162,7 +184,7 @@ void FuzzerPassAddAccessChains::Apply() {
         // Apply the transformation to add an access chain.
         ApplyTransformation(TransformationAccessChain(
             GetFuzzerContext()->GetFreshId(), chosen_pointer->result_id(),
-            index_ids, instruction_descriptor));
+            index_ids, instruction_descriptor, fresh_ids_for_clamping));
       });
 }
 

+ 12 - 3
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp

@@ -93,10 +93,15 @@ void FuzzerPassAddEquationInstructions::Apply() {
 
               // Make sure a result type exists in the module.
               if (const auto* vector = type->AsVector()) {
+                // We store element count in a separate variable since the
+                // call FindOrCreate* functions below might invalidate
+                // |vector| pointer.
+                const auto element_count = vector->element_count();
+
                 FindOrCreateVectorType(
                     FindOrCreateFloatType(
                         vector->element_type()->AsInteger()->width()),
-                    vector->element_count());
+                    element_count);
               } else {
                 FindOrCreateFloatType(type->AsInteger()->width());
               }
@@ -138,6 +143,11 @@ void FuzzerPassAddEquationInstructions::Apply() {
                 //  is that they must have the same number of bits. Consider
                 //  improving the code below to support this in full.
                 if (const auto* vector = operand_type->AsVector()) {
+                  // We store element count in a separate variable since the
+                  // call FindOrCreate* functions below might invalidate
+                  // |vector| pointer.
+                  const auto element_count = vector->element_count();
+
                   uint32_t element_type_id;
                   if (const auto* int_type =
                           vector->element_type()->AsInteger()) {
@@ -150,8 +160,7 @@ void FuzzerPassAddEquationInstructions::Apply() {
                         GetFuzzerContext()->ChooseEven());
                   }
 
-                  FindOrCreateVectorType(element_type_id,
-                                         vector->element_count());
+                  FindOrCreateVectorType(element_type_id, element_count);
                 } else if (const auto* int_type = operand_type->AsInteger()) {
                   FindOrCreateFloatType(int_type->width());
                 } else {

+ 29 - 3
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp

@@ -60,7 +60,7 @@ void FuzzerPassApplyIdSynonyms::Apply() {
           }
         });
 
-    for (auto& use : uses) {
+    for (const auto& use : uses) {
       auto use_inst = use.first;
       auto use_index = use.second;
       auto block_containing_use = GetIRContext()->get_instr_block(use_inst);
@@ -82,7 +82,7 @@ void FuzzerPassApplyIdSynonyms::Apply() {
       }
 
       std::vector<const protobufs::DataDescriptor*> synonyms_to_try;
-      for (auto& data_descriptor :
+      for (const auto* data_descriptor :
            GetTransformationContext()->GetFactManager()->GetSynonymsForId(
                id_with_known_synonyms)) {
         protobufs::DataDescriptor descriptor_for_this_id =
@@ -91,7 +91,12 @@ void FuzzerPassApplyIdSynonyms::Apply() {
           // Exclude the fact that the id is synonymous with itself.
           continue;
         }
-        synonyms_to_try.push_back(data_descriptor);
+
+        if (DataDescriptorsHaveCompatibleTypes(
+                use_inst->opcode(), use_in_operand_index,
+                descriptor_for_this_id, *data_descriptor)) {
+          synonyms_to_try.push_back(data_descriptor);
+        }
       }
       while (!synonyms_to_try.empty()) {
         auto synonym_to_try =
@@ -162,5 +167,26 @@ void FuzzerPassApplyIdSynonyms::Apply() {
   }
 }
 
+bool FuzzerPassApplyIdSynonyms::DataDescriptorsHaveCompatibleTypes(
+    SpvOp opcode, uint32_t use_in_operand_index,
+    const protobufs::DataDescriptor& dd1,
+    const protobufs::DataDescriptor& dd2) {
+  auto base_object_type_id_1 =
+      fuzzerutil::GetTypeId(GetIRContext(), dd1.object());
+  auto base_object_type_id_2 =
+      fuzzerutil::GetTypeId(GetIRContext(), dd2.object());
+  assert(base_object_type_id_1 && base_object_type_id_2 &&
+         "Data descriptors are invalid");
+
+  auto type_id_1 = fuzzerutil::WalkCompositeTypeIndices(
+      GetIRContext(), base_object_type_id_1, dd1.index());
+  auto type_id_2 = fuzzerutil::WalkCompositeTypeIndices(
+      GetIRContext(), base_object_type_id_2, dd2.index());
+  assert(type_id_1 && type_id_2 && "Data descriptors have invalid types");
+
+  return TransformationReplaceIdWithSynonym::TypesAreCompatible(
+      GetIRContext(), opcode, use_in_operand_index, type_id_1, type_id_2);
+}
+
 }  // namespace fuzz
 }  // namespace spvtools

+ 10 - 1
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.h

@@ -16,7 +16,6 @@
 #define SOURCE_FUZZ_FUZZER_PASS_APPLY_ID_SYNONYMS_
 
 #include "source/fuzz/fuzzer_pass.h"
-
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
@@ -34,6 +33,16 @@ class FuzzerPassApplyIdSynonyms : public FuzzerPass {
   ~FuzzerPassApplyIdSynonyms() override;
 
   void Apply() override;
+
+ private:
+  // Returns true if uses of |dd1| can be replaced with |dd2| and vice-versa
+  // with respect to the type. Concretely, returns true if |dd1| and |dd2| have
+  // the same type or both |dd1| and |dd2| are either a numerical or a vector
+  // type of integral components with possibly different signedness.
+  bool DataDescriptorsHaveCompatibleTypes(SpvOp opcode,
+                                          uint32_t use_in_operand_index,
+                                          const protobufs::DataDescriptor& dd1,
+                                          const protobufs::DataDescriptor& dd2);
 };
 
 }  // namespace fuzz

+ 4 - 4
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp

@@ -1100,11 +1100,11 @@ void FuzzerPassDonateModules::AddLivesafeFunction(
                      "A runtime array type in the donor should have been "
                      "replaced by a fixed-sized array in the recipient.");
               // The size of this fixed-size array is a suitable bound.
-              bound = TransformationAddFunction::GetBoundForCompositeIndex(
-                  GetIRContext(), *fixed_size_array_type);
+              bound = fuzzerutil::GetBoundForCompositeIndex(
+                  *fixed_size_array_type, GetIRContext());
             } else {
-              bound = TransformationAddFunction::GetBoundForCompositeIndex(
-                  donor_ir_context, *should_be_composite_type);
+              bound = fuzzerutil::GetBoundForCompositeIndex(
+                  *should_be_composite_type, donor_ir_context);
             }
             const uint32_t index_id = inst.GetSingleWordInOperand(index);
             auto index_inst =

+ 149 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp

@@ -0,0 +1,149 @@
+// Copyright (c) 2020 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 "fuzzer_pass_interchange_signedness_of_integer_operands.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/id_use_descriptor.h"
+#include "source/fuzz/transformation_record_synonymous_constants.h"
+#include "source/fuzz/transformation_replace_id_with_synonym.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassInterchangeSignednessOfIntegerOperands::
+    FuzzerPassInterchangeSignednessOfIntegerOperands(
+        opt::IRContext* ir_context,
+        TransformationContext* transformation_context,
+        FuzzerContext* fuzzer_context,
+        protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassInterchangeSignednessOfIntegerOperands::
+    ~FuzzerPassInterchangeSignednessOfIntegerOperands() = default;
+
+void FuzzerPassInterchangeSignednessOfIntegerOperands::Apply() {
+  // Make vector keeping track of all the uses we want to replace.
+  // This is a vector of pairs, where the first element is an id use descriptor
+  // identifying the use of a constant id and the second is the id that should
+  // be used to replace it.
+  std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>> uses_to_replace;
+
+  for (auto constant : GetIRContext()->GetConstants()) {
+    uint32_t constant_id = constant->result_id();
+
+    // We want to record the synonymity of an integer constant with another
+    // constant with opposite signedness, and this can only be done if they are
+    // not irrelevant.
+    if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
+            constant_id)) {
+      continue;
+    }
+
+    uint32_t toggled_id =
+        FindOrCreateToggledIntegerConstant(constant->result_id());
+    if (!toggled_id) {
+      // Not an integer constant
+      continue;
+    }
+
+    assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
+               toggled_id) &&
+           "FindOrCreateToggledConstant can't produce an irrelevant id");
+
+    // Record synonymous constants
+    ApplyTransformation(
+        TransformationRecordSynonymousConstants(constant_id, toggled_id));
+
+    // Find all the uses of the constant and, for each, probabilistically
+    // decide whether to replace it.
+    GetIRContext()->get_def_use_mgr()->ForEachUse(
+        constant_id,
+        [this, toggled_id, &uses_to_replace](opt::Instruction* use_inst,
+                                             uint32_t use_index) -> void {
+          if (GetFuzzerContext()->ChoosePercentage(
+                  GetFuzzerContext()
+                      ->GetChanceOfInterchangingSignednessOfIntegerOperands())) {
+            MaybeAddUseToReplace(use_inst, use_index, toggled_id,
+                                 &uses_to_replace);
+          }
+        });
+  }
+
+  // Replace the ids if it is allowed.
+  for (auto use_to_replace : uses_to_replace) {
+    MaybeApplyTransformation(TransformationReplaceIdWithSynonym(
+        use_to_replace.first, use_to_replace.second));
+  }
+}
+
+uint32_t FuzzerPassInterchangeSignednessOfIntegerOperands::
+    FindOrCreateToggledIntegerConstant(uint32_t id) {
+  auto constant = GetIRContext()->get_constant_mgr()->FindDeclaredConstant(id);
+
+  // This pass only toggles integer constants.
+  if (!constant->AsIntConstant() &&
+      (!constant->AsVectorConstant() ||
+       !constant->AsVectorConstant()->component_type()->AsInteger())) {
+    return 0;
+  }
+
+  if (auto integer = constant->AsIntConstant()) {
+    auto type = integer->type()->AsInteger();
+
+    // Find or create and return the toggled constant.
+    return FindOrCreateIntegerConstant(std::vector<uint32_t>(integer->words()),
+                                       type->width(), !type->IsSigned(), false);
+  }
+
+  // The constant is an integer vector.
+
+  // Find the component type.
+  auto component_type =
+      constant->AsVectorConstant()->component_type()->AsInteger();
+
+  // Find or create the toggled component type.
+  uint32_t toggled_component_type = FindOrCreateIntegerType(
+      component_type->width(), !component_type->IsSigned());
+
+  // Get the information about the toggled components. We need to extract this
+  // information now because the analyses might be invalidated, which would make
+  // the constant and component_type variables invalid.
+  std::vector<std::vector<uint32_t>> component_words;
+
+  for (auto component : constant->AsVectorConstant()->GetComponents()) {
+    component_words.push_back(component->AsIntConstant()->words());
+  }
+  uint32_t width = component_type->width();
+  bool is_signed = !component_type->IsSigned();
+
+  std::vector<uint32_t> toggled_components;
+
+  // Find or create the toggled components.
+  for (auto words : component_words) {
+    toggled_components.push_back(
+        FindOrCreateIntegerConstant(words, width, is_signed, false));
+  }
+
+  // Find or create the required toggled vector type.
+  uint32_t toggled_type = FindOrCreateVectorType(
+      toggled_component_type, (uint32_t)toggled_components.size());
+
+  // Find or create and return the toggled vector constant.
+  return FindOrCreateCompositeConstant(toggled_components, toggled_type, false);
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 51 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h

@@ -0,0 +1,51 @@
+// Copyright (c) 2020 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_FUZZ_FUZZER_PASS_INTERCHANGE_SIGNEDNESS_OF_INTEGER_OPERANDS_
+#define SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_SIGNEDNESS_OF_INTEGER_OPERANDS_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A pass that:
+// - Finds all the integer constant (scalar and vector) definitions in the
+//   module and adds the definitions of the integer with the same data words but
+//   opposite signedness. If the synonym is already in the module, it does not
+//   add a new one.
+// - For each use of an integer constant where its signedness does not matter,
+// decides whether to change it to the id of the toggled constant.
+class FuzzerPassInterchangeSignednessOfIntegerOperands : public FuzzerPass {
+ public:
+  FuzzerPassInterchangeSignednessOfIntegerOperands(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassInterchangeSignednessOfIntegerOperands() override;
+
+  void Apply() override;
+
+ private:
+  // Given the id of an integer constant (scalar or vector), it finds or creates
+  // the corresponding toggled constant (the integer with the same data words
+  // but opposite signedness). Returns the id of the toggled instruction if the
+  // constant is an integer scalar or vector, 0 otherwise.
+  uint32_t FindOrCreateToggledIntegerConstant(uint32_t id);
+};
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_SIGNEDNESS_OF_INTEGER_OPERANDS_

+ 1 - 19
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp

@@ -57,24 +57,6 @@ uint32_t FuzzerPassInterchangeZeroLikeConstants::FindOrCreateToggledConstant(
   return 0;
 }
 
-void FuzzerPassInterchangeZeroLikeConstants::MaybeAddUseToReplace(
-    opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
-    std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
-        uses_to_replace) {
-  // Only consider this use if it is in a block
-  if (!GetIRContext()->get_instr_block(use_inst)) {
-    return;
-  }
-
-  // Get the index of the operand restricted to input operands.
-  uint32_t in_operand_index =
-      fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index);
-  auto id_use_descriptor =
-      MakeIdUseDescriptorFromUse(GetIRContext(), use_inst, in_operand_index);
-  uses_to_replace->emplace_back(
-      std::make_pair(id_use_descriptor, replacement_id));
-}
-
 void FuzzerPassInterchangeZeroLikeConstants::Apply() {
   // Make vector keeping track of all the uses we want to replace.
   // This is a vector of pairs, where the first element is an id use descriptor
@@ -118,7 +100,7 @@ void FuzzerPassInterchangeZeroLikeConstants::Apply() {
         });
   }
 
-  // Replace the ids
+  // Replace the ids if it is allowed.
   for (auto use_to_replace : uses_to_replace) {
     MaybeApplyTransformation(TransformationReplaceIdWithSynonym(
         use_to_replace.first, use_to_replace.second));

+ 0 - 10
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h

@@ -46,16 +46,6 @@ class FuzzerPassInterchangeZeroLikeConstants : public FuzzerPass {
   // Returns the id of the toggled instruction if the constant is zero-like,
   // 0 otherwise.
   uint32_t FindOrCreateToggledConstant(opt::Instruction* declaration);
-
-  // Given an id use (described by an instruction and an index) and an id with
-  // which the original one should be replaced, adds a pair (with the elements
-  // being the corresponding id use descriptor and the replacement id) to
-  // |uses_to_replace| if the use is in an instruction block, otherwise does
-  // nothing.
-  void MaybeAddUseToReplace(
-      opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
-      std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
-          uses_to_replace);
 };
 
 }  // namespace fuzz

+ 29 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp

@@ -17,7 +17,9 @@
 #include <vector>
 
 #include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
 #include "source/fuzz/transformation_outline_function.h"
+#include "source/fuzz/transformation_split_block.h"
 
 namespace spvtools {
 namespace fuzz {
@@ -46,6 +48,33 @@ void FuzzerPassOutlineFunctions::Apply() {
       blocks.push_back(&block);
     }
     auto entry_block = blocks[GetFuzzerContext()->RandomIndex(blocks)];
+
+    // If the entry block starts with OpPhi, try to split it.
+    if (entry_block->begin()->opcode() == SpvOpPhi) {
+      // Find the first non-OpPhi instruction.
+      opt::Instruction* non_phi_inst = nullptr;
+      for (auto& instruction : *entry_block) {
+        if (instruction.opcode() != SpvOpPhi) {
+          non_phi_inst = &instruction;
+          break;
+        }
+      }
+
+      assert(non_phi_inst && "|non_phi_inst| must've been initialized");
+
+      // If the split was not applicable, the transformation will not work.
+      uint32_t new_block_id = GetFuzzerContext()->GetFreshId();
+      if (!MaybeApplyTransformation(TransformationSplitBlock(
+              MakeInstructionDescriptor(non_phi_inst->result_id(),
+                                        non_phi_inst->opcode(), 0),
+              new_block_id))) {
+        return;
+      }
+
+      // The new entry block is the newly-created block.
+      entry_block = &*function->FindBlock(new_block_id);
+    }
+
     auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(function);
     auto postdominator_analysis =
         GetIRContext()->GetPostDominatorAnalysis(function);

+ 3 - 12
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp

@@ -34,19 +34,10 @@ FuzzerPassReplaceLinearAlgebraInstructions::
     ~FuzzerPassReplaceLinearAlgebraInstructions() = default;
 
 void FuzzerPassReplaceLinearAlgebraInstructions::Apply() {
-  // For each instruction, checks whether it is a supported linear algebra
-  // instruction. In this case, the transformation is randomly applied.
+  // For each instruction, checks whether it is a linear algebra instruction. In
+  // this case, the transformation is randomly applied.
   GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
-    // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
-    // Right now we only support certain operations. When this issue is
-    // addressed the following conditional can use the function
-    // |spvOpcodeIsLinearAlgebra|.
-    if (instruction->opcode() != SpvOpVectorTimesScalar &&
-        instruction->opcode() != SpvOpMatrixTimesScalar &&
-        instruction->opcode() != SpvOpVectorTimesMatrix &&
-        instruction->opcode() != SpvOpMatrixTimesVector &&
-        instruction->opcode() != SpvOpMatrixTimesMatrix &&
-        instruction->opcode() != SpvOpDot) {
+    if (!spvOpcodeIsLinearAlgebra(instruction->opcode())) {
       return;
     }
 

+ 105 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp

@@ -0,0 +1,105 @@
+// Copyright (c) 2020 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/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_replace_load_store_with_copy_memory.h"
+#include "source/opt/instruction.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassReplaceLoadsStoresWithCopyMemories::
+    FuzzerPassReplaceLoadsStoresWithCopyMemories(
+        opt::IRContext* ir_context,
+        TransformationContext* transformation_context,
+        FuzzerContext* fuzzer_context,
+        protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassReplaceLoadsStoresWithCopyMemories::
+    ~FuzzerPassReplaceLoadsStoresWithCopyMemories() = default;
+
+void FuzzerPassReplaceLoadsStoresWithCopyMemories::Apply() {
+  // We look for matching pairs of instructions OpLoad and
+  // OpStore within the same block. Potential instructions OpLoad to be matched
+  // are stored in a hash map. If we encounter instructions that write to memory
+  // or instructions of memory barriers that could operate on variables within
+  // unsafe storage classes we need to erase the hash map to avoid unsafe
+  // operations.
+
+  // A vector of matching OpLoad and OpStore instructions.
+  std::vector<std::pair<opt::Instruction*, opt::Instruction*>>
+      op_load_store_pairs;
+
+  for (auto& function : *GetIRContext()->module()) {
+    for (auto& block : function) {
+      // A hash map storing potential OpLoad instructions.
+      std::unordered_map<uint32_t, opt::Instruction*> current_op_loads;
+      for (auto& instruction : block) {
+        // Add a potential OpLoad instruction.
+        if (instruction.opcode() == SpvOpLoad) {
+          current_op_loads[instruction.result_id()] = &instruction;
+        } else if (instruction.opcode() == SpvOpStore) {
+          if (current_op_loads.find(instruction.GetSingleWordOperand(1)) !=
+              current_op_loads.end()) {
+            // We have found the matching OpLoad instruction to the current
+            // OpStore instruction.
+            op_load_store_pairs.push_back(std::make_pair(
+                current_op_loads[instruction.GetSingleWordOperand(1)],
+                &instruction));
+          }
+        }
+        if (TransformationReplaceLoadStoreWithCopyMemory::IsMemoryWritingOpCode(
+                instruction.opcode())) {
+          current_op_loads.clear();
+        } else if (TransformationReplaceLoadStoreWithCopyMemory::
+                       IsMemoryBarrierOpCode(instruction.opcode())) {
+          for (auto it = current_op_loads.begin();
+               it != current_op_loads.end();) {
+            // Get the storage class.
+            opt::Instruction* source_id =
+                GetIRContext()->get_def_use_mgr()->GetDef(
+                    it->second->GetSingleWordOperand(2));
+            SpvStorageClass storage_class =
+                fuzzerutil::GetStorageClassFromPointerType(
+                    GetIRContext(), source_id->type_id());
+            if (!TransformationReplaceLoadStoreWithCopyMemory::
+                    IsStorageClassSafeAcrossMemoryBarriers(storage_class)) {
+              it = current_op_loads.erase(it);
+            } else {
+              it++;
+            }
+          }
+        }
+      }
+    }
+  }
+  for (auto instr_pair : op_load_store_pairs) {
+    // Randomly decide to apply the transformation for the
+    // potential pairs.
+    if (!GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()
+                ->GetChanceOfReplacingLoadStoreWithCopyMemory())) {
+      ApplyTransformation(TransformationReplaceLoadStoreWithCopyMemory(
+          MakeInstructionDescriptor(GetIRContext(), instr_pair.first),
+          MakeInstructionDescriptor(GetIRContext(), instr_pair.second)));
+    }
+  }
+}  // namespace fuzz
+}  // namespace fuzz
+}  // namespace spvtools

+ 41 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h

@@ -0,0 +1,41 @@
+// Copyright (c) 2020 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 SPIRV_TOOLS_FUZZER_PASS_REPLACE_LOADS_STORES_WITH_COPY_MEMORIES_H
+#define SPIRV_TOOLS_FUZZER_PASS_REPLACE_LOADS_STORES_WITH_COPY_MEMORIES_H
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A fuzzer pass that takes pairs of instruction descriptors to OpLoad and
+// OpStore that have the same intermediate value and in each pair replaces the
+// OpStore with an equivalent OpCopyMemory.
+class FuzzerPassReplaceLoadsStoresWithCopyMemories : public FuzzerPass {
+ public:
+  FuzzerPassReplaceLoadsStoresWithCopyMemories(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassReplaceLoadsStoresWithCopyMemories() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SPIRV_TOOLS_FUZZER_PASS_REPLACE_LOADS_STORES_WITH_COPY_MEMORIES_H

+ 1 - 1
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp

@@ -98,7 +98,7 @@ void FuzzerPassReplaceParamsWithStruct::Apply() {
       parameter_id.push_back(params[index]->result_id());
     }
 
-    std::unordered_map<uint32_t, uint32_t> caller_id_to_fresh_id;
+    std::map<uint32_t, uint32_t> caller_id_to_fresh_id;
     for (const auto* inst :
          fuzzerutil::GetCallers(GetIRContext(), function.result_id())) {
       caller_id_to_fresh_id[inst->result_id()] =

+ 119 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp

@@ -373,6 +373,28 @@ uint32_t GetArraySize(const opt::Instruction& array_type_instruction,
   return array_length_constant->GetU32();
 }
 
+uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst,
+                                   opt::IRContext* ir_context) {
+  switch (composite_type_inst.opcode()) {
+    case SpvOpTypeArray:
+      return fuzzerutil::GetArraySize(composite_type_inst, ir_context);
+    case SpvOpTypeMatrix:
+    case SpvOpTypeVector:
+      return composite_type_inst.GetSingleWordInOperand(1);
+    case SpvOpTypeStruct: {
+      return fuzzerutil::GetNumberOfStructMembers(composite_type_inst);
+    }
+    case SpvOpTypeRuntimeArray:
+      assert(false &&
+             "GetBoundForCompositeIndex should not be invoked with an "
+             "OpTypeRuntimeArray, which does not have a static bound.");
+      return 0;
+    default:
+      assert(false && "Unknown composite type.");
+      return 0;
+  }
+}
+
 bool IsValid(opt::IRContext* context, spv_validator_options validator_options) {
   std::vector<uint32_t> binary;
   context->module()->ToBinary(&binary, false);
@@ -735,6 +757,20 @@ std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
   return result;
 }
 
+void RemoveParameter(opt::IRContext* ir_context, uint32_t parameter_id) {
+  auto* function = GetFunctionFromParameterId(ir_context, parameter_id);
+  assert(function && "|parameter_id| is invalid");
+  assert(!FunctionIsEntryPoint(ir_context, function->result_id()) &&
+         "Can't remove parameter from an entry point function");
+
+  function->RemoveParameter(parameter_id);
+
+  // We've just removed parameters from the function and cleared their memory.
+  // Make sure analyses have no dangling pointers.
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
+}
+
 std::vector<opt::Instruction*> GetCallers(opt::IRContext* ir_context,
                                           uint32_t function_id) {
   assert(FindFunction(ir_context, function_id) &&
@@ -1104,6 +1140,32 @@ uint32_t MaybeGetIntegerConstant(
   return 0;
 }
 
+uint32_t MaybeGetIntegerConstantFromValueAndType(opt::IRContext* ir_context,
+                                                 uint32_t value,
+                                                 uint32_t int_type_id) {
+  auto int_type_inst = ir_context->get_def_use_mgr()->GetDef(int_type_id);
+
+  assert(int_type_inst && "The given type id must exist.");
+
+  auto int_type = ir_context->get_type_mgr()
+                      ->GetType(int_type_inst->result_id())
+                      ->AsInteger();
+
+  assert(int_type && int_type->width() == 32 &&
+         "The given type id must correspond to an 32-bit integer type.");
+
+  opt::analysis::IntConstant constant(int_type, {value});
+
+  // Check that the constant exists in the module.
+  if (!ir_context->get_constant_mgr()->FindConstant(&constant)) {
+    return 0;
+  }
+
+  return ir_context->get_constant_mgr()
+      ->GetDefiningInstruction(&constant)
+      ->result_id();
+}
+
 uint32_t MaybeGetFloatConstant(
     opt::IRContext* ir_context,
     const TransformationContext& transformation_context,
@@ -1193,6 +1255,63 @@ void AddStructType(opt::IRContext* ir_context, uint32_t result_id,
   UpdateModuleIdBound(ir_context, result_id);
 }
 
+bool TypesAreEqualUpToSign(opt::IRContext* ir_context, uint32_t type1_id,
+                           uint32_t type2_id) {
+  if (type1_id == type2_id) {
+    return true;
+  }
+
+  auto type1 = ir_context->get_type_mgr()->GetType(type1_id);
+  auto type2 = ir_context->get_type_mgr()->GetType(type2_id);
+
+  // Integer scalar types must have the same width
+  if (type1->AsInteger() && type2->AsInteger()) {
+    return type1->AsInteger()->width() == type2->AsInteger()->width();
+  }
+
+  // Integer vector types must have the same number of components and their
+  // component types must be integers with the same width.
+  if (type1->AsVector() && type2->AsVector()) {
+    auto component_type1 = type1->AsVector()->element_type()->AsInteger();
+    auto component_type2 = type2->AsVector()->element_type()->AsInteger();
+
+    // Only check the component count and width if they are integer.
+    if (component_type1 && component_type2) {
+      return type1->AsVector()->element_count() ==
+                 type2->AsVector()->element_count() &&
+             component_type1->width() == component_type2->width();
+    }
+  }
+
+  // In all other cases, the types cannot be considered equal.
+  return false;
+}
+
+std::map<uint32_t, uint32_t> RepeatedUInt32PairToMap(
+    const google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>& data) {
+  std::map<uint32_t, uint32_t> result;
+
+  for (const auto& entry : data) {
+    result[entry.first()] = entry.second();
+  }
+
+  return result;
+}
+
+google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>
+MapToRepeatedUInt32Pair(const std::map<uint32_t, uint32_t>& data) {
+  google::protobuf::RepeatedPtrField<protobufs::UInt32Pair> result;
+
+  for (const auto& entry : data) {
+    protobufs::UInt32Pair pair;
+    pair.set_first(entry.first);
+    pair.set_second(entry.second);
+    *result.Add() = std::move(pair);
+  }
+
+  return result;
+}
+
 }  // namespace fuzzerutil
 
 }  // namespace fuzz

+ 42 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_util.h

@@ -15,6 +15,7 @@
 #ifndef SOURCE_FUZZ_FUZZER_UTIL_H_
 #define SOURCE_FUZZ_FUZZER_UTIL_H_
 
+#include <map>
 #include <vector>
 
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
@@ -141,6 +142,13 @@ uint32_t GetNumberOfStructMembers(
 uint32_t GetArraySize(const opt::Instruction& array_type_instruction,
                       opt::IRContext* context);
 
+// Returns the bound for indexing into a composite of type
+// |composite_type_inst|, i.e. the number of fields of a struct, the size of an
+// array, the number of components of a vector, or the number of columns of a
+// matrix. |composite_type_inst| must be the type of a composite.
+uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst,
+                                   opt::IRContext* ir_context);
+
 // Returns true if and only if |context| is valid, according to the validator
 // instantiated with |validator_options|.
 bool IsValid(opt::IRContext* context, spv_validator_options validator_options);
@@ -281,6 +289,15 @@ bool IsPermutationOfRange(const std::vector<uint32_t>& arr, uint32_t lo,
 std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
                                              uint32_t function_id);
 
+// Removes an OpFunctionParameter instruction with result id |parameter_id|
+// from the its function. Parameter's function must not be an entry-point
+// function. The function must have a parameter with result id |parameter_id|.
+//
+// Prefer using this function to opt::Function::RemoveParameter since
+// this function also guarantees that |ir_context| has no invalid pointers
+// to the removed parameter.
+void RemoveParameter(opt::IRContext* ir_context, uint32_t parameter_id);
+
 // Returns all OpFunctionCall instructions that call a function with result id
 // |function_id|.
 std::vector<opt::Instruction*> GetCallers(opt::IRContext* ir_context,
@@ -394,6 +411,14 @@ uint32_t MaybeGetIntegerConstant(
     const std::vector<uint32_t>& words, uint32_t width, bool is_signed,
     bool is_irrelevant);
 
+// Returns the id of a 32-bit integer constant in the module with type
+// |int_type_id| and value |value|, or 0 if no such constant exists in the
+// module. |int_type_id| must exist in the module and it must correspond to a
+// 32-bit integer type.
+uint32_t MaybeGetIntegerConstantFromValueAndType(opt::IRContext* ir_context,
+                                                 uint32_t value,
+                                                 uint32_t int_type_id);
+
 // Returns the result id of an OpConstant instruction of floating-point type.
 // Returns 0 if no such instruction or type is present in the module.
 // The returned id either participates in IdIsIrrelevant fact or not, depending
@@ -441,6 +466,23 @@ inline uint32_t FloatToWord(float value) {
   return result;
 }
 
+// Returns true if any of the following is true:
+// - |type1_id| and |type2_id| are the same id
+// - |type1_id| and |type2_id| refer to integer scalar or vector types, only
+//   differing by their signedness.
+bool TypesAreEqualUpToSign(opt::IRContext* ir_context, uint32_t type1_id,
+                           uint32_t type2_id);
+
+// Converts repeated field of UInt32Pair to a map. If two or more equal values
+// of |UInt32Pair::first()| are available in |data|, the last value of
+// |UInt32Pair::second()| is used.
+std::map<uint32_t, uint32_t> RepeatedUInt32PairToMap(
+    const google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>& data);
+
+// Converts a map into a repeated field of UInt32Pair.
+google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>
+MapToRepeatedUInt32Pair(const std::map<uint32_t, uint32_t>& data);
+
 }  // namespace fuzzerutil
 
 }  // namespace fuzz

+ 55 - 19
3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto

@@ -406,6 +406,8 @@ message Transformation {
     TransformationReplaceParamsWithStruct replace_params_with_struct = 59;
     TransformationReplaceCopyObjectWithStoreLoad replace_copy_object_with_store_load = 60;
     TransformationReplaceCopyMemoryWithLoadStore replace_copy_memory_with_load_store = 61;
+    TransformationReplaceLoadStoreWithCopyMemory replace_load_store_with_copy_memory = 62;
+    TransformationAddLoopPreheader add_loop_preheader = 63;
     // Add additional option using the next available number.
   }
 }
@@ -416,6 +418,13 @@ message TransformationAccessChain {
 
   // Adds an access chain instruction based on a given pointer and indices.
 
+  // When accessing a struct, the corresponding indices must be 32-bit integer constants.
+  // For any other composite, the indices can be any 32-bit integer, and the transformation
+  // adds two instructions for each such index to clamp it to the bound, as follows:
+  //
+  // %t1 = OpULessThanEqual %bool %index %bound_minus_one
+  // %t2 = OpSelect %int_type %t1 %index %bound_minus_one
+
   // Result id for the access chain
   uint32 fresh_id = 1;
 
@@ -429,6 +438,10 @@ message TransformationAccessChain {
   // OpAccessChain instruction should be inserted
   InstructionDescriptor instruction_to_insert_before = 4;
 
+  // Additional fresh ids, required to clamp index variables. A pair is needed
+  // for each access to a non-struct composite.
+  repeated UInt32Pair fresh_ids_for_clamping = 5;
+
 }
 
 message TransformationAddConstantBoolean {
@@ -690,6 +703,34 @@ message TransformationAddLocalVariable {
 
 }
 
+message TransformationAddLoopPreheader {
+
+  // A transformation that adds a loop preheader block before the given loop header.
+
+  // The id of the loop header block
+  uint32 loop_header_block = 1;
+
+  // A fresh id for the preheader block
+  uint32 fresh_id = 2;
+
+  // Fresh ids for splitting the OpPhi instructions in the header.
+  // A new OpPhi instruction in the preheader is needed for each OpPhi instruction in the header,
+  // if the header has more than one predecessor outside of the loop.
+  // This allows turning instructions of the form:
+  //
+  //   %loop_header_block = OpLabel
+  //                 %id1 = OpPhi %type %val1 %pred1_id %val2 %pred2_id %val3 %backedge_block_id
+  //
+  // into:
+  //            %fresh_id = OpLabel
+  //             %phi_id1 = OpPhi %type %val1 %pred1_id %val2 %pred2_id
+  //                        OpBranch %header_id
+  //   %loop_header_block = OpLabel
+  //                 %id1 = OpPhi %type %phi_id1 %fresh_id %val3 %backedge_block_id
+  repeated uint32 phi_id = 3;
+
+}
+
 message TransformationAddNoContractionDecoration {
 
   // Applies OpDecorate NoContraction to the given result id
@@ -1331,24 +1372,22 @@ message TransformationReplaceLinearAlgebraInstruction {
   repeated uint32 fresh_ids = 1;
 
   // A descriptor for a linear algebra instruction.
-  // This transformation is only applicable if the described instruction has one of the following opcodes.
-  // Supported:
-  //   OpVectorTimesScalar
-  //   OpMatrixTimesScalar
-  //   OpVectorTimesMatrix
-  //   OpMatrixTimesVector
-  //   OpMatrixTimesMatrix
-  //   OpDot
-  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
-  // Right now we only support certain operations. When this issue is addressed
-  // the supporting comments can be removed.
-  // To be supported in the future:
-  //   OpTranspose
-  //   OpOuterProduct
   InstructionDescriptor instruction_descriptor = 2;
 
 }
 
+message TransformationReplaceLoadStoreWithCopyMemory {
+  // A transformation that takes a pair of instruction descriptors
+  // to OpLoad and OpStore that have the same intermediate value
+  // and replaces the OpStore with an equivalent OpCopyMemory.
+
+  // The instruction descriptor to OpLoad
+  InstructionDescriptor load_instruction_descriptor = 1;
+
+  // The instruction descriptor to OpStore
+  InstructionDescriptor store_instruction_descriptor = 2;
+}
+
 message TransformationReplaceParamsWithStruct {
 
   // Replaces parameters of the function with a struct containing
@@ -1365,12 +1404,9 @@ message TransformationReplaceParamsWithStruct {
   uint32 fresh_parameter_id = 3;
 
   // Fresh ids for struct objects containing values of replaced parameters.
-  // This map contains a fresh id for at least every result id of a relevant
+  // This field contains a fresh id for at least every result id of a relevant
   // OpFunctionCall instruction.
-  //
-  // While maps are not fully deterministic, the way this map is used does not
-  // exhibit nondeterminism. Change to repeated Uint32Pair if this changes.
-  map<uint32, uint32> caller_id_to_fresh_composite_id = 4;
+  repeated UInt32Pair caller_id_to_fresh_composite_id = 4;
 
 }
 

+ 14 - 11
3rdparty/spirv-tools/source/fuzz/shrinker.cpp

@@ -121,11 +121,13 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
   // succeeds, (b) get the binary that results from running these
   // transformations, and (c) get the subsequence of the initial transformations
   // that actually apply (in principle this could be a strict subsequence).
-  if (Replayer(impl_->target_env, impl_->validate_during_replay,
-               impl_->validator_options)
-          .Run(binary_in, initial_facts, transformation_sequence_in,
-               transformation_sequence_in.transformation_size(),
-               &current_best_binary, &current_best_transformations) !=
+  Replayer replayer(impl_->target_env, impl_->validate_during_replay,
+                    impl_->validator_options);
+  replayer.SetMessageConsumer(impl_->consumer);
+  if (replayer.Run(binary_in, initial_facts, transformation_sequence_in,
+                   static_cast<uint32_t>(
+                       transformation_sequence_in.transformation_size()),
+                   &current_best_binary, &current_best_transformations) !=
       Replayer::ReplayerResultStatus::kComplete) {
     return ShrinkerResultStatus::kReplayFailed;
   }
@@ -185,7 +187,8 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
       // Remove a chunk of transformations according to the current index and
       // chunk size.
       auto transformations_with_chunk_removed =
-          RemoveChunk(current_best_transformations, chunk_index, chunk_size);
+          RemoveChunk(current_best_transformations,
+                      static_cast<uint32_t>(chunk_index), chunk_size);
 
       // Replay the smaller sequence of transformations to get a next binary and
       // transformation sequence. Note that the transformations arising from
@@ -194,11 +197,11 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
       // transformations inapplicable.
       std::vector<uint32_t> next_binary;
       protobufs::TransformationSequence next_transformation_sequence;
-      if (Replayer(impl_->target_env, impl_->validate_during_replay,
-                   impl_->validator_options)
-              .Run(binary_in, initial_facts, transformations_with_chunk_removed,
-                   transformations_with_chunk_removed.transformation_size(),
-                   &next_binary, &next_transformation_sequence) !=
+      if (replayer.Run(
+              binary_in, initial_facts, transformations_with_chunk_removed,
+              static_cast<uint32_t>(
+                  transformations_with_chunk_removed.transformation_size()),
+              &next_binary, &next_transformation_sequence) !=
           Replayer::ReplayerResultStatus::kComplete) {
         // Replay should not fail; if it does, we need to abort shrinking.
         return ShrinkerResultStatus::kReplayFailed;

+ 9 - 0
3rdparty/spirv-tools/source/fuzz/transformation.cpp

@@ -31,6 +31,7 @@
 #include "source/fuzz/transformation_add_global_variable.h"
 #include "source/fuzz/transformation_add_image_sample_unused_components.h"
 #include "source/fuzz/transformation_add_local_variable.h"
+#include "source/fuzz/transformation_add_loop_preheader.h"
 #include "source/fuzz/transformation_add_no_contraction_decoration.h"
 #include "source/fuzz/transformation_add_parameter.h"
 #include "source/fuzz/transformation_add_relaxed_decoration.h"
@@ -66,6 +67,7 @@
 #include "source/fuzz/transformation_replace_copy_object_with_store_load.h"
 #include "source/fuzz/transformation_replace_id_with_synonym.h"
 #include "source/fuzz/transformation_replace_linear_algebra_instruction.h"
+#include "source/fuzz/transformation_replace_load_store_with_copy_memory.h"
 #include "source/fuzz/transformation_replace_parameter_with_global.h"
 #include "source/fuzz/transformation_replace_params_with_struct.h"
 #include "source/fuzz/transformation_set_function_control.h"
@@ -126,6 +128,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
     case protobufs::Transformation::TransformationCase::kAddLocalVariable:
       return MakeUnique<TransformationAddLocalVariable>(
           message.add_local_variable());
+    case protobufs::Transformation::TransformationCase::kAddLoopPreheader:
+      return MakeUnique<TransformationAddLoopPreheader>(
+          message.add_loop_preheader());
     case protobufs::Transformation::TransformationCase::
         kAddNoContractionDecoration:
       return MakeUnique<TransformationAddNoContractionDecoration>(
@@ -233,6 +238,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
         kReplaceLinearAlgebraInstruction:
       return MakeUnique<TransformationReplaceLinearAlgebraInstruction>(
           message.replace_linear_algebra_instruction());
+    case protobufs::Transformation::TransformationCase::
+        kReplaceLoadStoreWithCopyMemory:
+      return MakeUnique<TransformationReplaceLoadStoreWithCopyMemory>(
+          message.replace_load_store_with_copy_memory());
     case protobufs::Transformation::TransformationCase::
         kReplaceParamsWithStruct:
       return MakeUnique<TransformationReplaceParamsWithStruct>(

+ 226 - 32
3rdparty/spirv-tools/source/fuzz/transformation_access_chain.cpp

@@ -29,7 +29,8 @@ TransformationAccessChain::TransformationAccessChain(
 TransformationAccessChain::TransformationAccessChain(
     uint32_t fresh_id, uint32_t pointer_id,
     const std::vector<uint32_t>& index_id,
-    const protobufs::InstructionDescriptor& instruction_to_insert_before) {
+    const protobufs::InstructionDescriptor& instruction_to_insert_before,
+    const std::vector<std::pair<uint32_t, uint32_t>>& fresh_ids_for_clamping) {
   message_.set_fresh_id(fresh_id);
   message_.set_pointer_id(pointer_id);
   for (auto id : index_id) {
@@ -37,12 +38,22 @@ TransformationAccessChain::TransformationAccessChain(
   }
   *message_.mutable_instruction_to_insert_before() =
       instruction_to_insert_before;
+  for (auto clamping_ids_pair : fresh_ids_for_clamping) {
+    protobufs::UInt32Pair pair;
+    pair.set_first(clamping_ids_pair.first);
+    pair.set_second(clamping_ids_pair.second);
+    *message_.add_fresh_ids_for_clamping() = pair;
+  }
 }
 
 bool TransformationAccessChain::IsApplicable(
     opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
-  // The result id must be fresh
-  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
+  // Keep track of the fresh ids used to make sure that they are distinct.
+  std::set<uint32_t> fresh_ids_used;
+
+  // The result id must be fresh.
+  if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+          message_.fresh_id(), ir_context, &fresh_ids_used)) {
     return false;
   }
   // The pointer id must exist and have a type.
@@ -50,7 +61,7 @@ bool TransformationAccessChain::IsApplicable(
   if (!pointer || !pointer->type_id()) {
     return false;
   }
-  // The type must indeed be a pointer
+  // The type must indeed be a pointer.
   auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id());
   if (pointer_type->opcode() != SpvOpTypePointer) {
     return false;
@@ -96,23 +107,86 @@ bool TransformationAccessChain::IsApplicable(
   // Start from the base type of the pointer.
   uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
 
+  int id_pairs_used = 0;
+
   // Consider the given index ids in turn.
   for (auto index_id : message_.index_id()) {
-    // Try to get the integer value associated with this index is.  The first
-    // component of the result will be false if the id did not correspond to an
-    // integer.  Otherwise, the integer with which the id is associated is the
-    // second component.
-    std::pair<bool, uint32_t> maybe_index_value =
-        GetIndexValue(ir_context, index_id);
-    if (!maybe_index_value.first) {
-      // There was no integer: this index is no good.
-      return false;
+    // The index value will correspond to the value of the index if the object
+    // is a struct, otherwise the value 0 will be used.
+    uint32_t index_value;
+
+    // Check whether the object is a struct.
+    if (ir_context->get_def_use_mgr()->GetDef(subobject_type_id)->opcode() ==
+        SpvOpTypeStruct) {
+      // It is a struct: we need to retrieve the integer value.
+
+      bool successful;
+      std::tie(successful, index_value) =
+          GetIndexValue(ir_context, index_id, subobject_type_id);
+
+      if (!successful) {
+        return false;
+      }
+    } else {
+      // It is not a struct: the index will need clamping.
+
+      if (message_.fresh_ids_for_clamping().size() <= id_pairs_used) {
+        // We don't have enough ids
+        return false;
+      }
+
+      // Get two new ids to use and update the amount used.
+      protobufs::UInt32Pair fresh_ids =
+          message_.fresh_ids_for_clamping()[id_pairs_used++];
+
+      // Valid ids need to have been given
+      if (fresh_ids.first() == 0 || fresh_ids.second() == 0) {
+        return false;
+      }
+
+      // Check that the ids are actually fresh and not already used by this
+      // transformation.
+      if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+              fresh_ids.first(), ir_context, &fresh_ids_used) ||
+          !CheckIdIsFreshAndNotUsedByThisTransformation(
+              fresh_ids.second(), ir_context, &fresh_ids_used)) {
+        return false;
+      }
+
+      if (!ValidIndexToComposite(ir_context, index_id, subobject_type_id)) {
+        return false;
+      }
+
+      // Perform the clamping using the fresh ids at our disposal.
+      auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
+
+      uint32_t bound = fuzzerutil::GetBoundForCompositeIndex(
+          *ir_context->get_def_use_mgr()->GetDef(subobject_type_id),
+          ir_context);
+
+      // The module must have an integer constant of value bound-1 of the same
+      // type as the index.
+      if (!fuzzerutil::MaybeGetIntegerConstantFromValueAndType(
+              ir_context, bound - 1, index_instruction->type_id())) {
+        return false;
+      }
+
+      // The module must have the definition of bool type to make a comparison.
+      if (!fuzzerutil::MaybeGetBoolType(ir_context)) {
+        return false;
+      }
+
+      // The index is not necessarily a constant, so we may not know its value.
+      // We can use index 0 because the components of a non-struct composite
+      // all have the same type, and index 0 is always in bounds.
+      index_value = 0;
     }
+
     // Try to walk down the type using this index.  This will yield 0 if the
     // type is not a composite or the index is out of bounds, and the id of
     // the next type otherwise.
     subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
-        ir_context, subobject_type_id, maybe_index_value.second);
+        ir_context, subobject_type_id, index_value);
     if (!subobject_type_id) {
       // Either the type was not a composite (so that too many indices were
       // provided), or the index was out of bounds.
@@ -152,25 +226,105 @@ void TransformationAccessChain::Apply(
       ir_context->get_def_use_mgr()->GetDef(message_.pointer_id())->type_id());
   uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
 
+  uint32_t id_pairs_used = 0;
+
   // Go through the index ids in turn.
   for (auto index_id : message_.index_id()) {
-    // Add the index id to the operands.
-    operands.push_back({SPV_OPERAND_TYPE_ID, {index_id}});
-    // Get the integer value associated with the index id.
-    uint32_t index_value = GetIndexValue(ir_context, index_id).second;
+    uint32_t index_value;
+
+    // Actual id to be used in the instruction: the original id
+    // or the clamped one.
+    uint32_t new_index_id;
+
+    // Check whether the object is a struct.
+    if (ir_context->get_def_use_mgr()->GetDef(subobject_type_id)->opcode() ==
+        SpvOpTypeStruct) {
+      // It is a struct: we need to retrieve the integer value.
+
+      index_value =
+          GetIndexValue(ir_context, index_id, subobject_type_id).second;
+
+      new_index_id = index_id;
+
+    } else {
+      // It is not a struct: the index will need clamping.
+
+      // Get two new ids to use and update the amount used.
+      protobufs::UInt32Pair fresh_ids =
+          message_.fresh_ids_for_clamping()[id_pairs_used++];
+
+      // Perform the clamping using the fresh ids at our disposal.
+      // The module will not be changed if |add_clamping_instructions| is not
+      // set.
+      auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
+
+      uint32_t bound = fuzzerutil::GetBoundForCompositeIndex(
+          *ir_context->get_def_use_mgr()->GetDef(subobject_type_id),
+          ir_context);
+
+      auto bound_minus_one_id =
+          fuzzerutil::MaybeGetIntegerConstantFromValueAndType(
+              ir_context, bound - 1, index_instruction->type_id());
+
+      assert(bound_minus_one_id &&
+             "A constant of value bound - 1 and the same type as the index "
+             "must exist as a precondition.");
+
+      uint32_t bool_type_id = fuzzerutil::MaybeGetBoolType(ir_context);
+
+      assert(bool_type_id &&
+             "An OpTypeBool instruction must exist as a precondition.");
+
+      auto int_type_inst =
+          ir_context->get_def_use_mgr()->GetDef(index_instruction->type_id());
+
+      // Clamp the integer and add the corresponding instructions in the module
+      // if |add_clamping_instructions| is set.
+      auto instruction_to_insert_before =
+          FindInstruction(message_.instruction_to_insert_before(), ir_context);
+
+      // Compare the index with the bound via an instruction of the form:
+      //   %fresh_ids.first = OpULessThanEqual %bool %int_id %bound_minus_one.
+      fuzzerutil::UpdateModuleIdBound(ir_context, fresh_ids.first());
+      instruction_to_insert_before->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpULessThanEqual, bool_type_id, fresh_ids.first(),
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {index_instruction->result_id()}},
+               {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
+
+      // Select the index if in-bounds, otherwise one less than the bound:
+      //   %fresh_ids.second = OpSelect %int_type %fresh_ids.first %int_id
+      //                           %bound_minus_one
+      fuzzerutil::UpdateModuleIdBound(ir_context, fresh_ids.second());
+      instruction_to_insert_before->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpSelect, int_type_inst->result_id(),
+          fresh_ids.second(),
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {fresh_ids.first()}},
+               {SPV_OPERAND_TYPE_ID, {index_instruction->result_id()}},
+               {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
+
+      new_index_id = fresh_ids.second();
+
+      index_value = 0;
+    }
+
+    // Add the correct index id to the operands.
+    operands.push_back({SPV_OPERAND_TYPE_ID, {new_index_id}});
+
     // Walk to the next type in the composite object using this index.
     subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
         ir_context, subobject_type_id, index_value);
   }
-  // The access chain's result type is a pointer to the composite component that
-  // was reached after following all indices.  The storage class is that of the
-  // original pointer.
+  // The access chain's result type is a pointer to the composite component
+  // that was reached after following all indices.  The storage class is that
+  // of the original pointer.
   uint32_t result_type = fuzzerutil::MaybeGetPointerType(
       ir_context, subobject_type_id,
       static_cast<SpvStorageClass>(pointer_type->GetSingleWordInOperand(0)));
 
-  // Add the access chain instruction to the module, and update the module's id
-  // bound.
+  // Add the access chain instruction to the module, and update the module's
+  // id bound.
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
   FindInstruction(message_.instruction_to_insert_before(), ir_context)
       ->InsertBefore(MakeUnique<opt::Instruction>(
@@ -180,8 +334,8 @@ void TransformationAccessChain::Apply(
   // Conservatively invalidate all analyses.
   ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 
-  // If the base pointer's pointee value was irrelevant, the same is true of the
-  // pointee value of the result of this access chain.
+  // If the base pointer's pointee value was irrelevant, the same is true of
+  // the pointee value of the result of this access chain.
   if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant(
           message_.pointer_id())) {
     transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
@@ -196,21 +350,61 @@ protobufs::Transformation TransformationAccessChain::ToMessage() const {
 }
 
 std::pair<bool, uint32_t> TransformationAccessChain::GetIndexValue(
-    opt::IRContext* ir_context, uint32_t index_id) const {
+    opt::IRContext* ir_context, uint32_t index_id,
+    uint32_t object_type_id) const {
+  if (!ValidIndexToComposite(ir_context, index_id, object_type_id)) {
+    return {false, 0};
+  }
   auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
-  if (!index_instruction || !spvOpcodeIsConstant(index_instruction->opcode())) {
-    // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3179) We could
-    //  allow non-constant indices when looking up non-structs, using clamping
-    //  to ensure they are in-bounds.
+
+  uint32_t bound = fuzzerutil::GetBoundForCompositeIndex(
+      *ir_context->get_def_use_mgr()->GetDef(object_type_id), ir_context);
+
+  // The index must be a constant
+  if (!spvOpcodeIsConstant(index_instruction->opcode())) {
+    return {false, 0};
+  }
+
+  // The index must be in bounds.
+  uint32_t value = index_instruction->GetSingleWordInOperand(0);
+
+  if (value >= bound) {
     return {false, 0};
   }
+
+  return {true, value};
+}
+
+bool TransformationAccessChain::ValidIndexToComposite(
+    opt::IRContext* ir_context, uint32_t index_id, uint32_t object_type_id) {
+  auto object_type_def = ir_context->get_def_use_mgr()->GetDef(object_type_id);
+  // The object being indexed must be a composite.
+  if (!spvOpcodeIsComposite(object_type_def->opcode())) {
+    return false;
+  }
+
+  // Get the defining instruction of the index.
+  auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
+  if (!index_instruction) {
+    return false;
+  }
+
+  // The index type must be 32-bit integer.
   auto index_type =
       ir_context->get_def_use_mgr()->GetDef(index_instruction->type_id());
   if (index_type->opcode() != SpvOpTypeInt ||
       index_type->GetSingleWordInOperand(0) != 32) {
-    return {false, 0};
+    return false;
+  }
+
+  // If the object being traversed is a struct, the id must correspond to an
+  // in-bound constant.
+  if (object_type_def->opcode() == SpvOpTypeStruct) {
+    if (!spvOpcodeIsConstant(index_instruction->opcode())) {
+      return false;
+    }
   }
-  return {true, index_instruction->GetSingleWordInOperand(0)};
+  return true;
 }
 
 }  // namespace fuzz

+ 32 - 11
3rdparty/spirv-tools/source/fuzz/transformation_access_chain.h

@@ -33,20 +33,28 @@ class TransformationAccessChain : public Transformation {
   TransformationAccessChain(
       uint32_t fresh_id, uint32_t pointer_id,
       const std::vector<uint32_t>& index_id,
-      const protobufs::InstructionDescriptor& instruction_to_insert_before);
+      const protobufs::InstructionDescriptor& instruction_to_insert_before,
+      const std::vector<std::pair<uint32_t, uint32_t>>& fresh_ids_for_clamping =
+          {});
 
-  // - |message_.fresh_id| must be fresh
+  // - |message_.fresh_id| must be fresh.
   // - |message_.instruction_to_insert_before| must identify an instruction
-  //   before which it is legitimate to insert an OpAccessChain instruction
+  //   before which it is legitimate to insert an OpAccessChain instruction.
   // - |message_.pointer_id| must be a result id with pointer type that is
   //   available (according to dominance rules) at the insertion point.
-  // - The pointer must not be OpConstantNull or OpUndef
-  // - |message_.index_id| must be a sequence of ids of 32-bit integer constants
+  // - The pointer must not be OpConstantNull or OpUndef.
+  // - |message_.index_id| must be a sequence of ids of 32-bit integers
   //   such that it is possible to walk the pointee type of
-  //   |message_.pointer_id| using these indices, remaining in-bounds.
+  //   |message_.pointer_id| using these indices.
+  // - All indices used to access a struct must be OpConstant.
+  // - The indices used to index non-struct composites will be clamped to be
+  //   in bound. Enough fresh ids must be given in
+  //   |message_.fresh_id_for_clamping| to perform clamping (2 for
+  //   each index accessing a non-struct). This requires the bool type and
+  //   a constant of value (bound - 1) to be declared in the module.
   // - If type t is the final type reached by walking these indices, the module
   //   must include an instruction "OpTypePointer SC %t" where SC is the storage
-  //   class associated with |message_.pointer_id|
+  //   class associated with |message_.pointer_id|.
   bool IsApplicable(
       opt::IRContext* ir_context,
       const TransformationContext& transformation_context) const override;
@@ -58,6 +66,9 @@ class TransformationAccessChain : public Transformation {
   // the indices in |message_.index_id|, and with the same storage class as
   // |message_.pointer_id|.
   //
+  // For each of the indices traversing non-struct composites, two clamping
+  // instructions are added using ids in |message_.fresh_id_for_clamping|.
+  //
   // If the fact manager in |transformation_context| reports that
   // |message_.pointer_id| has an irrelevant pointee value, then the fact that
   // |message_.fresh_id| (the result of the access chain) also has an irrelevant
@@ -68,11 +79,21 @@ class TransformationAccessChain : public Transformation {
   protobufs::Transformation ToMessage() const override;
 
  private:
-  // Returns {false, 0} if |index_id| does not correspond to a 32-bit integer
-  // constant.  Otherwise, returns {true, value}, where value is the value of
-  // the 32-bit integer constant to which |index_id| corresponds.
+  // Returns {false, 0} in each of the following cases:
+  // - |index_id| does not correspond to a 32-bit integer constant
+  // - the object being indexed is not a composite type
+  // - the constant at |index_id| is out of bounds.
+  // Otherwise, returns {true, value}, where value is the value of the constant
+  // at |index_id|.
   std::pair<bool, uint32_t> GetIndexValue(opt::IRContext* ir_context,
-                                          uint32_t index_id) const;
+                                          uint32_t index_id,
+                                          uint32_t object_type_id) const;
+
+  // Returns true if |index_id| corresponds, in the given context, to a 32-bit
+  // integer which can be used to index an object of the type specified by
+  // |object_type_id|. Returns false otherwise.
+  static bool ValidIndexToComposite(opt::IRContext* ir_context,
+                                    uint32_t index_id, uint32_t object_type_id);
 
   protobufs::TransformationAccessChain message_;
 };

+ 17 - 29
3rdparty/spirv-tools/source/fuzz/transformation_add_function.cpp

@@ -488,7 +488,20 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
       // move on from this loop.
       continue;
     }
-    auto back_edge_block = ir_context->cfg()->block(back_edge_block_id);
+
+    // If the loop's merge block is unreachable, then there are no constraints
+    // on where the merge block appears in relation to the blocks of the loop.
+    // This means we need to be careful when adding a branch from the back-edge
+    // block to the merge block: the branch might make the loop merge reachable,
+    // and it might then be dominated by the loop header and possibly by other
+    // blocks in the loop. Since a block needs to appear before those blocks it
+    // strictly dominates, this could make the module invalid. To avoid this
+    // problem we bail out in the case where the loop header does not dominate
+    // the loop merge.
+    if (!ir_context->GetDominatorAnalysis(added_function)
+             ->Dominates(loop_header->id(), loop_header->MergeBlockId())) {
+      return false;
+    }
 
     // Go through the sequence of loop limiter infos and find the one
     // corresponding to this loop.
@@ -560,6 +573,7 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
     // %t4 = OpLogicalOr %bool %c %t3
     //       OpBranchConditional %t4 %loop_merge %loop_header
 
+    auto back_edge_block = ir_context->cfg()->block(back_edge_block_id);
     auto back_edge_block_terminator = back_edge_block->terminator();
     bool compare_using_greater_than_equal;
     if (back_edge_block_terminator->opcode() == SpvOpBranch) {
@@ -675,10 +689,6 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
       }
 
       // Add the new edge, by changing OpBranch to OpBranchConditional.
-      // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3162): This
-      //  could be a problem if the merge block was originally unreachable: it
-      //  might now be dominated by other blocks that it appears earlier than in
-      //  the module.
       back_edge_block_terminator->SetOpcode(SpvOpBranchConditional);
       back_edge_block_terminator->SetInOperands(opt::Instruction::OperandList(
           {{SPV_OPERAND_TYPE_ID, {loop_limiter_info.compare_id()}},
@@ -794,8 +804,8 @@ bool TransformationAddFunction::TryToClampAccessChainIndices(
 
     // Get the bound for the composite being indexed into; e.g. the number of
     // columns of matrix or the size of an array.
-    uint32_t bound =
-        GetBoundForCompositeIndex(ir_context, *should_be_composite_type);
+    uint32_t bound = fuzzerutil::GetBoundForCompositeIndex(
+        *should_be_composite_type, ir_context);
 
     // Get the instruction associated with the index and figure out its integer
     // type.
@@ -873,28 +883,6 @@ bool TransformationAddFunction::TryToClampAccessChainIndices(
   return true;
 }
 
-uint32_t TransformationAddFunction::GetBoundForCompositeIndex(
-    opt::IRContext* ir_context, const opt::Instruction& composite_type_inst) {
-  switch (composite_type_inst.opcode()) {
-    case SpvOpTypeArray:
-      return fuzzerutil::GetArraySize(composite_type_inst, ir_context);
-    case SpvOpTypeMatrix:
-    case SpvOpTypeVector:
-      return composite_type_inst.GetSingleWordInOperand(1);
-    case SpvOpTypeStruct: {
-      return fuzzerutil::GetNumberOfStructMembers(composite_type_inst);
-    }
-    case SpvOpTypeRuntimeArray:
-      assert(false &&
-             "GetBoundForCompositeIndex should not be invoked with an "
-             "OpTypeRuntimeArray, which does not have a static bound.");
-      return 0;
-    default:
-      assert(false && "Unknown composite type.");
-      return 0;
-  }
-}
-
 opt::Instruction* TransformationAddFunction::FollowCompositeIndex(
     opt::IRContext* ir_context, const opt::Instruction& composite_type_inst,
     uint32_t index_id) {

+ 0 - 7
3rdparty/spirv-tools/source/fuzz/transformation_add_function.h

@@ -58,13 +58,6 @@ class TransformationAddFunction : public Transformation {
 
   protobufs::Transformation ToMessage() const override;
 
-  // Helper method that returns the bound for indexing into a composite of type
-  // |composite_type_inst|, i.e. the number of fields of a struct, the size of
-  // an array, the number of components of a vector, or the number of columns of
-  // a matrix.
-  static uint32_t GetBoundForCompositeIndex(
-      opt::IRContext* ir_context, const opt::Instruction& composite_type_inst);
-
   // Helper method that, given composite type |composite_type_inst|, returns the
   // type of the sub-object at index |index_id|, which is required to be in-
   // bounds.

+ 227 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_loop_preheader.cpp

@@ -0,0 +1,227 @@
+// Copyright (c) 2020 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 "transformation_add_loop_preheader.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/opt/instruction.h"
+
+namespace spvtools {
+namespace fuzz {
+TransformationAddLoopPreheader::TransformationAddLoopPreheader(
+    const protobufs::TransformationAddLoopPreheader& message)
+    : message_(message) {}
+
+TransformationAddLoopPreheader::TransformationAddLoopPreheader(
+    uint32_t loop_header_block, uint32_t fresh_id,
+    std::vector<uint32_t> phi_id) {
+  message_.set_loop_header_block(loop_header_block);
+  message_.set_fresh_id(fresh_id);
+  for (auto id : phi_id) {
+    message_.add_phi_id(id);
+  }
+}
+
+bool TransformationAddLoopPreheader::IsApplicable(
+    opt::IRContext* ir_context,
+    const TransformationContext& /* unused */) const {
+  // |message_.loop_header_block()| must be the id of a loop header block.
+  opt::BasicBlock* loop_header_block =
+      fuzzerutil::MaybeFindBlock(ir_context, message_.loop_header_block());
+  if (!loop_header_block || !loop_header_block->IsLoopHeader()) {
+    return false;
+  }
+
+  // The id for the preheader must actually be fresh.
+  std::set<uint32_t> used_ids;
+  if (!CheckIdIsFreshAndNotUsedByThisTransformation(message_.fresh_id(),
+                                                    ir_context, &used_ids)) {
+    return false;
+  }
+
+  size_t num_predecessors =
+      ir_context->cfg()->preds(message_.loop_header_block()).size();
+
+  // The block must have at least 2 predecessors (the back-edge block and
+  // another predecessor outside of the loop)
+  if (num_predecessors < 2) {
+    return false;
+  }
+
+  // If the block only has one predecessor outside of the loop (and thus 2 in
+  // total), then no additional fresh ids are necessary.
+  if (num_predecessors == 2) {
+    return true;
+  }
+
+  // Count the number of OpPhi instructions.
+  int32_t num_phi_insts = 0;
+  loop_header_block->ForEachPhiInst(
+      [&num_phi_insts](opt::Instruction* /* unused */) { num_phi_insts++; });
+
+  // There must be enough fresh ids for the OpPhi instructions.
+  if (num_phi_insts > message_.phi_id_size()) {
+    return false;
+  }
+
+  // Check that the needed ids are fresh and distinct.
+  for (int32_t i = 0; i < num_phi_insts; i++) {
+    if (!CheckIdIsFreshAndNotUsedByThisTransformation(message_.phi_id(i),
+                                                      ir_context, &used_ids)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void TransformationAddLoopPreheader::Apply(
+    opt::IRContext* ir_context,
+    TransformationContext* /* transformation_context */) const {
+  // Find the loop header.
+  opt::BasicBlock* loop_header =
+      fuzzerutil::MaybeFindBlock(ir_context, message_.loop_header_block());
+
+  auto dominator_analysis =
+      ir_context->GetDominatorAnalysis(loop_header->GetParent());
+
+  uint32_t back_edge_block_id = 0;
+
+  // Update the branching instructions of the out-of-loop predecessors of the
+  // header. Set |back_edge_block_id| to be the id of the back-edge block.
+  ir_context->get_def_use_mgr()->ForEachUse(
+      loop_header->id(),
+      [this, &ir_context, &dominator_analysis, &loop_header,
+       &back_edge_block_id](opt::Instruction* use_inst, uint32_t use_index) {
+
+        if (dominator_analysis->Dominates(loop_header->GetLabelInst(),
+                                          use_inst)) {
+          // If |use_inst| is a branch instruction dominated by the header, the
+          // block containing it is the back-edge block.
+          if (use_inst->IsBranch()) {
+            assert(back_edge_block_id == 0 &&
+                   "There should only be one back-edge block");
+            back_edge_block_id = ir_context->get_instr_block(use_inst)->id();
+          }
+          // References to the header inside the loop should not be updated
+          return;
+        }
+
+        // If |use_inst| is not a branch or merge instruction, it should not be
+        // changed.
+        if (!use_inst->IsBranch() &&
+            use_inst->opcode() != SpvOpSelectionMerge &&
+            use_inst->opcode() != SpvOpLoopMerge) {
+          return;
+        }
+
+        // Update the reference.
+        use_inst->SetOperand(use_index, {message_.fresh_id()});
+      });
+
+  assert(back_edge_block_id && "The back-edge block should have been found");
+
+  // Make a new block for the preheader.
+  std::unique_ptr<opt::BasicBlock> preheader = MakeUnique<opt::BasicBlock>(
+      std::unique_ptr<opt::Instruction>(new opt::Instruction(
+          ir_context, SpvOpLabel, 0, message_.fresh_id(), {})));
+
+  uint32_t phi_ids_used = 0;
+
+  // Update the OpPhi instructions and, if there is more than one out-of-loop
+  // predecessor, add necessary OpPhi instructions so the preheader.
+  loop_header->ForEachPhiInst([this, &ir_context, &preheader,
+                               &back_edge_block_id,
+                               &phi_ids_used](opt::Instruction* phi_inst) {
+
+    // The loop header must have at least 2 incoming edges (the back edge, and
+    // at least one from outside the loop).
+    assert(phi_inst->NumInOperands() >= 4);
+
+    if (phi_inst->NumInOperands() == 4) {
+      // There is just one out-of-loop predecessor, so no additional
+      // instructions in the preheader are necessary. The reference to the
+      // original out-of-loop predecessor needs to be updated so that it refers
+      // to the preheader.
+      uint32_t index_of_out_of_loop_pred_id =
+          phi_inst->GetInOperand(1).words[0] == back_edge_block_id ? 3 : 1;
+      phi_inst->SetInOperand(index_of_out_of_loop_pred_id, {preheader->id()});
+    } else {
+      // There is more than one out-of-loop predecessor, so an OpPhi instruction
+      // needs to be added to the preheader, and its value will depend on all
+      // the current out-of-loop predecessors of the header.
+
+      // Get the operand list and the value corresponding to the back-edge
+      // block.
+      std::vector<opt::Operand> preheader_in_operands;
+      uint32_t back_edge_val = 0;
+
+      for (uint32_t i = 0; i < phi_inst->NumInOperands(); i += 2) {
+        // Only add operands if they don't refer to the back-edge block.
+        if (phi_inst->GetInOperand(i + 1).words[0] == back_edge_block_id) {
+          back_edge_val = phi_inst->GetInOperand(i).words[0];
+        } else {
+          preheader_in_operands.push_back(std::move(phi_inst->GetInOperand(i)));
+          preheader_in_operands.push_back(
+              std::move(phi_inst->GetInOperand(i + 1)));
+        }
+      }
+
+      // Add the new instruction to the preheader.
+      uint32_t fresh_phi_id = message_.phi_id(phi_ids_used++);
+
+      // Update id bound.
+      fuzzerutil::UpdateModuleIdBound(ir_context, fresh_phi_id);
+
+      preheader->AddInstruction(std::unique_ptr<opt::Instruction>(
+          new opt::Instruction(ir_context, SpvOpPhi, phi_inst->type_id(),
+                               fresh_phi_id, preheader_in_operands)));
+
+      // Update the OpPhi instruction in the header so that it refers to the
+      // back edge block and the preheader as the predecessors, and it uses the
+      // newly-defined OpPhi in the preheader for the corresponding value.
+      phi_inst->SetInOperands(
+          {{SPV_OPERAND_TYPE_RESULT_ID, {fresh_phi_id}},
+           {SPV_OPERAND_TYPE_RESULT_ID, {preheader->id()}},
+           {SPV_OPERAND_TYPE_RESULT_ID, {back_edge_val}},
+           {SPV_OPERAND_TYPE_RESULT_ID, {back_edge_block_id}}});
+    }
+  });
+
+  // Update id bound.
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+
+  // Add an unconditional branch from the preheader to the header.
+  preheader->AddInstruction(std::unique_ptr<opt::Instruction>(
+      new opt::Instruction(ir_context, SpvOpBranch, 0, 0,
+                           std::initializer_list<opt::Operand>{opt::Operand(
+                               spv_operand_type_t::SPV_OPERAND_TYPE_RESULT_ID,
+                               {loop_header->id()})})));
+
+  // Insert the preheader in the module.
+  loop_header->GetParent()->InsertBasicBlockBefore(std::move(preheader),
+                                                   loop_header);
+
+  // Invalidate analyses because the structure of the program changed.
+  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddLoopPreheader::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_loop_preheader() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 57 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_loop_preheader.h

@@ -0,0 +1,57 @@
+// Copyright (c) 2020 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_FUZZ_TRANSFORMATION_ADD_LOOP_PREHEADER_H
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_LOOP_PREHEADER_H
+
+#include "source/fuzz/transformation.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddLoopPreheader : public Transformation {
+ public:
+  explicit TransformationAddLoopPreheader(
+      const protobufs::TransformationAddLoopPreheader& message);
+
+  TransformationAddLoopPreheader(uint32_t loop_header_block, uint32_t fresh_id,
+                                 std::vector<uint32_t> phi_id);
+
+  // - |message_.loop_header_block| must be the id of a loop header block in
+  //   the given module.
+  // - |message_.fresh_id| must be an available id.
+  // - |message_.phi_ids| must be a list of available ids.
+  //   It can be empty if the loop header only has one predecessor outside of
+  //   the loop. Otherwise, it must contain at least as many ids as OpPhi
+  //   instructions in the loop header block.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Adds a preheader block as the unique out-of-loop predecessor of the given
+  // loop header block. All of the existing out-of-loop predecessors of the
+  // header are changed so that they branch to the preheader instead.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAddLoopPreheader message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_LOOP_PREHEADER_H

+ 12 - 34
3rdparty/spirv-tools/source/fuzz/transformation_outline_function.cpp

@@ -21,20 +21,6 @@
 namespace spvtools {
 namespace fuzz {
 
-namespace {
-
-std::map<uint32_t, uint32_t> PairSequenceToMap(
-    const google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>&
-        pair_sequence) {
-  std::map<uint32_t, uint32_t> result;
-  for (auto& pair : pair_sequence) {
-    result[pair.first()] = pair.second();
-  }
-  return result;
-}
-
-}  // namespace
-
 TransformationOutlineFunction::TransformationOutlineFunction(
     const spvtools::fuzz::protobufs::TransformationOutlineFunction& message)
     : message_(message) {}
@@ -55,18 +41,10 @@ TransformationOutlineFunction::TransformationOutlineFunction(
   message_.set_new_function_region_entry_block(new_function_region_entry_block);
   message_.set_new_caller_result_id(new_caller_result_id);
   message_.set_new_callee_result_id(new_callee_result_id);
-  for (auto& entry : input_id_to_fresh_id) {
-    protobufs::UInt32Pair pair;
-    pair.set_first(entry.first);
-    pair.set_second(entry.second);
-    *message_.add_input_id_to_fresh_id() = pair;
-  }
-  for (auto& entry : output_id_to_fresh_id) {
-    protobufs::UInt32Pair pair;
-    pair.set_first(entry.first);
-    pair.set_second(entry.second);
-    *message_.add_output_id_to_fresh_id() = pair;
-  }
+  *message_.mutable_input_id_to_fresh_id() =
+      fuzzerutil::MapToRepeatedUInt32Pair(input_id_to_fresh_id);
+  *message_.mutable_output_id_to_fresh_id() =
+      fuzzerutil::MapToRepeatedUInt32Pair(output_id_to_fresh_id);
 }
 
 bool TransformationOutlineFunction::IsApplicable(
@@ -252,8 +230,8 @@ bool TransformationOutlineFunction::IsApplicable(
 
   // For each region input id, i.e. every id defined outside the region but
   // used inside the region, ...
-  std::map<uint32_t, uint32_t> input_id_to_fresh_id_map =
-      PairSequenceToMap(message_.input_id_to_fresh_id());
+  auto input_id_to_fresh_id_map =
+      fuzzerutil::RepeatedUInt32PairToMap(message_.input_id_to_fresh_id());
   for (auto id : GetRegionInputIds(ir_context, region_set, exit_block)) {
     // There needs to be a corresponding fresh id to be used as a function
     // parameter.
@@ -280,8 +258,8 @@ bool TransformationOutlineFunction::IsApplicable(
 
   // For each region output id -- i.e. every id defined inside the region but
   // used outside the region, ...
-  std::map<uint32_t, uint32_t> output_id_to_fresh_id_map =
-      PairSequenceToMap(message_.output_id_to_fresh_id());
+  auto output_id_to_fresh_id_map =
+      fuzzerutil::RepeatedUInt32PairToMap(message_.output_id_to_fresh_id());
   for (auto id : GetRegionOutputIds(ir_context, region_set, exit_block)) {
     if (
         // ... there needs to be a corresponding fresh id that can hold the
@@ -323,10 +301,10 @@ void TransformationOutlineFunction::Apply(
       GetRegionOutputIds(ir_context, region_blocks, original_region_exit_block);
 
   // Maps from input and output ids to fresh ids.
-  std::map<uint32_t, uint32_t> input_id_to_fresh_id_map =
-      PairSequenceToMap(message_.input_id_to_fresh_id());
-  std::map<uint32_t, uint32_t> output_id_to_fresh_id_map =
-      PairSequenceToMap(message_.output_id_to_fresh_id());
+  auto input_id_to_fresh_id_map =
+      fuzzerutil::RepeatedUInt32PairToMap(message_.input_id_to_fresh_id());
+  auto output_id_to_fresh_id_map =
+      fuzzerutil::RepeatedUInt32PairToMap(message_.output_id_to_fresh_id());
 
   UpdateModuleIdBoundForFreshIds(ir_context, input_id_to_fresh_id_map,
                                  output_id_to_fresh_id_map);

+ 1 - 1
3rdparty/spirv-tools/source/fuzz/transformation_outline_function.h

@@ -73,7 +73,7 @@ class TransformationOutlineFunction : public Transformation {
   // - Unless the type required for the new function is already known,
   //   |message_.new_function_type_id| is used as the type id for a new function
   //   type, and the new function uses this type.
-  // - The new function starts with a dummy block with id
+  // - The new function starts with a placeholder block with id
   //   |message_.new_function_first_block|, which jumps straight to a successor
   //   block, to avoid violating rules on what the first block in a function may
   //   look like.

+ 8 - 8
3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.cpp

@@ -15,6 +15,8 @@
 
 #include "transformation_record_synonymous_constants.h"
 
+#include "source/fuzz/fuzzer_util.h"
+
 namespace spvtools {
 namespace fuzz {
 
@@ -76,19 +78,17 @@ bool TransformationRecordSynonymousConstants::AreEquivalentConstants(
     return false;
   }
 
-  // The type ids must be the same
-  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3536): Somehow
-  // relax this for integers (so that unsigned integer and signed integer are
-  // considered the same type)
-  if (def_1->type_id() != def_2->type_id()) {
-    return false;
-  }
-
   auto constant1 = ir_context->get_constant_mgr()->GetConstantFromInst(def_1);
   auto constant2 = ir_context->get_constant_mgr()->GetConstantFromInst(def_2);
 
   assert(constant1 && constant2 && "The ids must refer to constants.");
 
+  // The types must be compatible.
+  if (!fuzzerutil::TypesAreEqualUpToSign(ir_context, def_1->type_id(),
+                                         def_2->type_id())) {
+    return false;
+  }
+
   // If either constant is null, the other is equivalent iff it is zero-like
   if (constant1->AsNullConstant()) {
     return constant2->IsZero();

+ 11 - 10
3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.h

@@ -32,16 +32,17 @@ class TransformationRecordSynonymousConstants : public Transformation {
   // - |message_.constant_id| and |message_.synonym_id| are distinct ids
   //   of constants
   // - |message_.constant_id| and |message_.synonym_id| refer to constants
-  //   that are equal or equivalent.
-  //   TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3536): Signed and
-  //   unsigned integers are currently considered non-equivalent
-  //   Two integers with the same width and value are equal, even if one is
-  //   signed and the other is not.
-  //   Constants are equivalent:
-  //   - if both of them represent zero-like values of the same type
-  //   - if they are composite constants with the same type and their
-  //     components are pairwise equivalent.
-  // - |constant1_id| and |constant2_id| may not be irrelevant.
+  //   that are equivalent.
+  // Constants are equivalent if at least one of the following holds:
+  // - they are equal (i.e. they have the same type ids and equal values)
+  // - both of them represent zero-like values of compatible types
+  // - they are composite constants with compatible types and their
+  //   components are pairwise equivalent
+  // Two types are compatible if at least one of the following holds:
+  // - they have the same id
+  // - they are integer scalar types with the same width
+  // - they are integer vectors and their components have the same width
+  //   (this is always the case if the components are equivalent)
   bool IsApplicable(
       opt::IRContext* ir_context,
       const TransformationContext& transformation_context) const override;

+ 68 - 0
3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp

@@ -57,6 +57,21 @@ bool TransformationReplaceIdWithSynonym::IsApplicable(
     return false;
   }
 
+  uint32_t type_id_of_interest =
+      ir_context->get_def_use_mgr()->GetDef(id_of_interest)->type_id();
+  uint32_t type_id_synonym = ir_context->get_def_use_mgr()
+                                 ->GetDef(message_.synonymous_id())
+                                 ->type_id();
+
+  // If the id of interest and the synonym are scalar or vector integer
+  // constants with different signedness, their use can only be swapped if the
+  // instruction is agnostic to the signedness of the operand.
+  if (!TypesAreCompatible(ir_context, use_instruction->opcode(),
+                          message_.id_use_descriptor().in_operand_index(),
+                          type_id_of_interest, type_id_synonym)) {
+    return false;
+  }
+
   // Is the use suitable for being replaced in principle?
   if (!UseCanBeReplacedWithSynonym(
           ir_context, use_instruction,
@@ -182,5 +197,58 @@ bool TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
   return true;
 }
 
+// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3582): Add all
+//  opcodes that are agnostic to signedness of operands to function.
+//  This is not exhaustive yet.
+bool TransformationReplaceIdWithSynonym::IsAgnosticToSignednessOfOperand(
+    SpvOp opcode, uint32_t use_in_operand_index) {
+  switch (opcode) {
+    case SpvOpSNegate:
+    case SpvOpNot:
+    case SpvOpIAdd:
+    case SpvOpISub:
+    case SpvOpIMul:
+    case SpvOpSDiv:
+    case SpvOpSRem:
+    case SpvOpSMod:
+    case SpvOpShiftRightLogical:
+    case SpvOpShiftRightArithmetic:
+    case SpvOpShiftLeftLogical:
+    case SpvOpBitwiseOr:
+    case SpvOpBitwiseXor:
+    case SpvOpBitwiseAnd:
+    case SpvOpIEqual:
+    case SpvOpINotEqual:
+    case SpvOpULessThan:
+    case SpvOpSLessThan:
+    case SpvOpUGreaterThan:
+    case SpvOpSGreaterThan:
+    case SpvOpULessThanEqual:
+    case SpvOpSLessThanEqual:
+    case SpvOpUGreaterThanEqual:
+    case SpvOpSGreaterThanEqual:
+      return true;
+    case SpvOpAccessChain:
+      // The signedness of indices does not matter.
+      return use_in_operand_index > 0;
+    default:
+      // Conservatively assume that the id cannot be swapped in other
+      // instructions.
+      return false;
+  }
+}
+
+bool TransformationReplaceIdWithSynonym::TypesAreCompatible(
+    opt::IRContext* ir_context, SpvOp opcode, uint32_t use_in_operand_index,
+    uint32_t type_id_1, uint32_t type_id_2) {
+  assert(ir_context->get_type_mgr()->GetType(type_id_1) &&
+         ir_context->get_type_mgr()->GetType(type_id_2) &&
+         "Type ids are invalid");
+
+  return type_id_1 == type_id_2 ||
+         (IsAgnosticToSignednessOfOperand(opcode, use_in_operand_index) &&
+          fuzzerutil::TypesAreEqualUpToSign(ir_context, type_id_1, type_id_2));
+}
+
 }  // namespace fuzz
 }  // namespace spvtools

+ 15 - 0
3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.h

@@ -64,7 +64,22 @@ class TransformationReplaceIdWithSynonym : public Transformation {
                                           opt::Instruction* use_instruction,
                                           uint32_t use_in_operand_index);
 
+  // Returns true if |type_id_1| and |type_id_2| represent compatible types
+  // given the context of the instruction with |opcode| (i.e. we can replace
+  // an operand of |opcode| of the first type with an id of the second type
+  // and vice-versa).
+  static bool TypesAreCompatible(opt::IRContext* ir_context, SpvOp opcode,
+                                 uint32_t use_in_operand_index,
+                                 uint32_t type_id_1, uint32_t type_id_2);
+
  private:
+  // Returns true if the instruction with opcode |opcode| does not change its
+  // behaviour depending on the signedness of the operand at
+  // |use_in_operand_index|.
+  // Assumes that the operand must be the id of an integer scalar or vector.
+  static bool IsAgnosticToSignednessOfOperand(SpvOp opcode,
+                                              uint32_t use_in_operand_index);
+
   protobufs::TransformationReplaceIdWithSynonym message_;
 };
 

+ 206 - 10
3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp

@@ -41,16 +41,8 @@ bool TransformationReplaceLinearAlgebraInstruction::IsApplicable(
   auto instruction =
       FindInstruction(message_.instruction_descriptor(), ir_context);
 
-  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
-  // Right now we only support certain operations. When this issue is addressed
-  // the following conditional can use the function |spvOpcodeIsLinearAlgebra|.
-  // It must be a supported linear algebra instruction.
-  if (instruction->opcode() != SpvOpVectorTimesScalar &&
-      instruction->opcode() != SpvOpMatrixTimesScalar &&
-      instruction->opcode() != SpvOpVectorTimesMatrix &&
-      instruction->opcode() != SpvOpMatrixTimesVector &&
-      instruction->opcode() != SpvOpMatrixTimesMatrix &&
-      instruction->opcode() != SpvOpDot) {
+  // It must be a linear algebra instruction.
+  if (!spvOpcodeIsLinearAlgebra(instruction->opcode())) {
     return false;
   }
 
@@ -77,6 +69,9 @@ void TransformationReplaceLinearAlgebraInstruction::Apply(
       FindInstruction(message_.instruction_descriptor(), ir_context);
 
   switch (linear_algebra_instruction->opcode()) {
+    case SpvOpTranspose:
+      ReplaceOpTranspose(ir_context, linear_algebra_instruction);
+      break;
     case SpvOpVectorTimesScalar:
       ReplaceOpVectorTimesScalar(ir_context, linear_algebra_instruction);
       break;
@@ -92,6 +87,9 @@ void TransformationReplaceLinearAlgebraInstruction::Apply(
     case SpvOpMatrixTimesMatrix:
       ReplaceOpMatrixTimesMatrix(ir_context, linear_algebra_instruction);
       break;
+    case SpvOpOuterProduct:
+      ReplaceOpOuterProduct(ir_context, linear_algebra_instruction);
+      break;
     case SpvOpDot:
       ReplaceOpDot(ir_context, linear_algebra_instruction);
       break;
@@ -115,6 +113,24 @@ uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount(
   // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
   // Right now we only support certain operations.
   switch (instruction->opcode()) {
+    case SpvOpTranspose: {
+      // For each matrix row, |2 * matrix_column_count| OpCompositeExtract and 1
+      // OpCompositeConstruct will be inserted.
+      auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
+          instruction->GetSingleWordInOperand(0));
+      uint32_t matrix_column_count =
+          ir_context->get_type_mgr()
+              ->GetType(matrix_instruction->type_id())
+              ->AsMatrix()
+              ->element_count();
+      uint32_t matrix_row_count = ir_context->get_type_mgr()
+                                      ->GetType(matrix_instruction->type_id())
+                                      ->AsMatrix()
+                                      ->element_type()
+                                      ->AsVector()
+                                      ->element_count();
+      return matrix_row_count * (2 * matrix_column_count + 1);
+    }
     case SpvOpVectorTimesScalar:
       // For each vector component, 1 OpCompositeExtract and 1 OpFMul will be
       // inserted.
@@ -213,6 +229,26 @@ uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount(
       return matrix_2_column_count *
              (2 + matrix_1_row_count * (5 * matrix_1_column_count - 1));
     }
+    case SpvOpOuterProduct: {
+      // For each |vector_2| component, |vector_1_component_count + 1|
+      // OpCompositeExtract, |vector_1_component_count| OpFMul and 1
+      // OpCompositeConstruct instructions will be inserted.
+      auto vector_1_instruction = ir_context->get_def_use_mgr()->GetDef(
+          instruction->GetSingleWordInOperand(0));
+      auto vector_2_instruction = ir_context->get_def_use_mgr()->GetDef(
+          instruction->GetSingleWordInOperand(1));
+      uint32_t vector_1_component_count =
+          ir_context->get_type_mgr()
+              ->GetType(vector_1_instruction->type_id())
+              ->AsVector()
+              ->element_count();
+      uint32_t vector_2_component_count =
+          ir_context->get_type_mgr()
+              ->GetType(vector_2_instruction->type_id())
+              ->AsVector()
+              ->element_count();
+      return 2 * vector_2_component_count * (vector_1_component_count + 1);
+    }
     case SpvOpDot:
       // For each pair of vector components, 2 OpCompositeExtract and 1 OpFMul
       // will be inserted. The first two OpFMul instructions will result the
@@ -233,6 +269,80 @@ uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount(
   }
 }
 
+void TransformationReplaceLinearAlgebraInstruction::ReplaceOpTranspose(
+    opt::IRContext* ir_context,
+    opt::Instruction* linear_algebra_instruction) const {
+  // Gets OpTranspose instruction information.
+  auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
+      linear_algebra_instruction->GetSingleWordInOperand(0));
+  uint32_t matrix_column_count = ir_context->get_type_mgr()
+                                     ->GetType(matrix_instruction->type_id())
+                                     ->AsMatrix()
+                                     ->element_count();
+  auto matrix_column_type = ir_context->get_type_mgr()
+                                ->GetType(matrix_instruction->type_id())
+                                ->AsMatrix()
+                                ->element_type();
+  auto matrix_column_component_type =
+      matrix_column_type->AsVector()->element_type();
+  uint32_t matrix_row_count = matrix_column_type->AsVector()->element_count();
+  auto resulting_matrix_column_type =
+      ir_context->get_type_mgr()
+          ->GetType(linear_algebra_instruction->type_id())
+          ->AsMatrix()
+          ->element_type();
+
+  uint32_t fresh_id_index = 0;
+  std::vector<uint32_t> result_column_ids(matrix_row_count);
+  for (uint32_t i = 0; i < matrix_row_count; i++) {
+    std::vector<uint32_t> column_component_ids(matrix_column_count);
+    for (uint32_t j = 0; j < matrix_column_count; j++) {
+      // Extracts the matrix column.
+      uint32_t matrix_column_id = message_.fresh_ids(fresh_id_index++);
+      linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpCompositeExtract,
+          ir_context->get_type_mgr()->GetId(matrix_column_type),
+          matrix_column_id,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}},
+               {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
+
+      // Extracts the matrix column component.
+      column_component_ids[j] = message_.fresh_ids(fresh_id_index++);
+      linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpCompositeExtract,
+          ir_context->get_type_mgr()->GetId(matrix_column_component_type),
+          column_component_ids[j],
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {matrix_column_id}},
+               {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+    }
+
+    // Inserts the resulting matrix column.
+    opt::Instruction::OperandList in_operands;
+    for (auto& column_component_id : column_component_ids) {
+      in_operands.push_back({SPV_OPERAND_TYPE_ID, {column_component_id}});
+    }
+    result_column_ids[i] = message_.fresh_ids(fresh_id_index++);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpCompositeConstruct,
+        ir_context->get_type_mgr()->GetId(resulting_matrix_column_type),
+        result_column_ids[i], opt::Instruction::OperandList(in_operands)));
+  }
+
+  // The OpTranspose instruction is changed to an OpCompositeConstruct
+  // instruction.
+  linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
+  linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]});
+  for (uint32_t i = 1; i < result_column_ids.size(); i++) {
+    linear_algebra_instruction->AddOperand(
+        {SPV_OPERAND_TYPE_ID, {result_column_ids[i]}});
+  }
+
+  fuzzerutil::UpdateModuleIdBound(
+      ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
+}
+
 void TransformationReplaceLinearAlgebraInstruction::ReplaceOpVectorTimesScalar(
     opt::IRContext* ir_context,
     opt::Instruction* linear_algebra_instruction) const {
@@ -740,6 +850,92 @@ void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesMatrix(
       ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
 }
 
+void TransformationReplaceLinearAlgebraInstruction::ReplaceOpOuterProduct(
+    opt::IRContext* ir_context,
+    opt::Instruction* linear_algebra_instruction) const {
+  // Gets vector 1 information.
+  auto vector_1_instruction = ir_context->get_def_use_mgr()->GetDef(
+      linear_algebra_instruction->GetSingleWordInOperand(0));
+  uint32_t vector_1_component_count =
+      ir_context->get_type_mgr()
+          ->GetType(vector_1_instruction->type_id())
+          ->AsVector()
+          ->element_count();
+  auto vector_1_component_type = ir_context->get_type_mgr()
+                                     ->GetType(vector_1_instruction->type_id())
+                                     ->AsVector()
+                                     ->element_type();
+
+  // Gets vector 2 information.
+  auto vector_2_instruction = ir_context->get_def_use_mgr()->GetDef(
+      linear_algebra_instruction->GetSingleWordInOperand(1));
+  uint32_t vector_2_component_count =
+      ir_context->get_type_mgr()
+          ->GetType(vector_2_instruction->type_id())
+          ->AsVector()
+          ->element_count();
+
+  uint32_t fresh_id_index = 0;
+  std::vector<uint32_t> result_column_ids(vector_2_component_count);
+  for (uint32_t i = 0; i < vector_2_component_count; i++) {
+    // Extracts |vector_2| component.
+    uint32_t vector_2_component_id = message_.fresh_ids(fresh_id_index++);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpCompositeExtract,
+        ir_context->get_type_mgr()->GetId(vector_1_component_type),
+        vector_2_component_id,
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {vector_2_instruction->result_id()}},
+             {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+
+    std::vector<uint32_t> column_component_ids(vector_1_component_count);
+    for (uint32_t j = 0; j < vector_1_component_count; j++) {
+      // Extracts |vector_1| component.
+      uint32_t vector_1_component_id = message_.fresh_ids(fresh_id_index++);
+      linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpCompositeExtract,
+          ir_context->get_type_mgr()->GetId(vector_1_component_type),
+          vector_1_component_id,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {vector_1_instruction->result_id()}},
+               {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
+
+      // Multiplies |vector_1| and |vector_2| components.
+      column_component_ids[j] = message_.fresh_ids(fresh_id_index++);
+      linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpFMul,
+          ir_context->get_type_mgr()->GetId(vector_1_component_type),
+          column_component_ids[j],
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {vector_2_component_id}},
+               {SPV_OPERAND_TYPE_ID, {vector_1_component_id}}})));
+    }
+
+    // Inserts the resulting matrix column.
+    opt::Instruction::OperandList in_operands;
+    for (auto& column_component_id : column_component_ids) {
+      in_operands.push_back({SPV_OPERAND_TYPE_ID, {column_component_id}});
+    }
+    result_column_ids[i] = message_.fresh_ids(fresh_id_index++);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpCompositeConstruct, vector_1_instruction->type_id(),
+        result_column_ids[i], in_operands));
+  }
+
+  // The OpOuterProduct instruction is changed to an OpCompositeConstruct
+  // instruction.
+  linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
+  linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]});
+  linear_algebra_instruction->SetInOperand(1, {result_column_ids[1]});
+  for (uint32_t i = 2; i < result_column_ids.size(); i++) {
+    linear_algebra_instruction->AddOperand(
+        {SPV_OPERAND_TYPE_ID, {result_column_ids[i]}});
+  }
+
+  fuzzerutil::UpdateModuleIdBound(
+      ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
+}
+
 void TransformationReplaceLinearAlgebraInstruction::ReplaceOpDot(
     opt::IRContext* ir_context,
     opt::Instruction* linear_algebra_instruction) const {

+ 8 - 0
3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h

@@ -52,6 +52,10 @@ class TransformationReplaceLinearAlgebraInstruction : public Transformation {
  private:
   protobufs::TransformationReplaceLinearAlgebraInstruction message_;
 
+  // Replaces an OpTranspose instruction.
+  void ReplaceOpTranspose(opt::IRContext* ir_context,
+                          opt::Instruction* instruction) const;
+
   // Replaces an OpVectorTimesScalar instruction.
   void ReplaceOpVectorTimesScalar(opt::IRContext* ir_context,
                                   opt::Instruction* instruction) const;
@@ -72,6 +76,10 @@ class TransformationReplaceLinearAlgebraInstruction : public Transformation {
   void ReplaceOpMatrixTimesMatrix(opt::IRContext* ir_context,
                                   opt::Instruction* instruction) const;
 
+  // Replaces an OpOuterProduct instruction.
+  void ReplaceOpOuterProduct(opt::IRContext* ir_context,
+                             opt::Instruction* instruction) const;
+
   // Replaces an OpDot instruction.
   void ReplaceOpDot(opt::IRContext* ir_context,
                     opt::Instruction* instruction) const;

+ 192 - 0
3rdparty/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp

@@ -0,0 +1,192 @@
+// Copyright (c) 2020 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 "transformation_replace_load_store_with_copy_memory.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/opcode.h"
+
+namespace spvtools {
+namespace fuzz {
+
+namespace {
+const uint32_t kOpStoreOperandIndexTargetVariable = 0;
+const uint32_t kOpStoreOperandIndexIntermediateIdToWrite = 1;
+const uint32_t kOpLoadOperandIndexSourceVariable = 2;
+}  // namespace
+
+TransformationReplaceLoadStoreWithCopyMemory::
+    TransformationReplaceLoadStoreWithCopyMemory(
+        const spvtools::fuzz::protobufs::
+            TransformationReplaceLoadStoreWithCopyMemory& message)
+    : message_(message) {}
+
+TransformationReplaceLoadStoreWithCopyMemory::
+    TransformationReplaceLoadStoreWithCopyMemory(
+        const protobufs::InstructionDescriptor& load_instruction_descriptor,
+        const protobufs::InstructionDescriptor& store_instruction_descriptor) {
+  *message_.mutable_load_instruction_descriptor() = load_instruction_descriptor;
+  *message_.mutable_store_instruction_descriptor() =
+      store_instruction_descriptor;
+}
+bool TransformationReplaceLoadStoreWithCopyMemory::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  // This transformation is only applicable to the pair of OpLoad and OpStore
+  // instructions.
+  if (message_.load_instruction_descriptor().target_instruction_opcode() !=
+      SpvOpLoad) {
+    return false;
+  }
+  if (message_.store_instruction_descriptor().target_instruction_opcode() !=
+      SpvOpStore) {
+    return false;
+  }
+
+  // The OpLoad instruction must be defined.
+  auto load_instruction =
+      FindInstruction(message_.load_instruction_descriptor(), ir_context);
+  if (!load_instruction) {
+    return false;
+  }
+
+  // The OpStore instruction must be defined.
+  auto store_instruction =
+      FindInstruction(message_.store_instruction_descriptor(), ir_context);
+  if (!store_instruction) {
+    return false;
+  }
+
+  // Intermediate values of the OpLoad and the OpStore must match.
+  if (load_instruction->result_id() !=
+      store_instruction->GetSingleWordOperand(
+          kOpStoreOperandIndexIntermediateIdToWrite)) {
+    return false;
+  }
+
+  // Get storage class of the variable pointed by the source operand in OpLoad.
+  opt::Instruction* source_id = ir_context->get_def_use_mgr()->GetDef(
+      load_instruction->GetSingleWordOperand(2));
+  SpvStorageClass storage_class = fuzzerutil::GetStorageClassFromPointerType(
+      ir_context, source_id->type_id());
+
+  // Iterate over all instructions between |load_instruction| and
+  // |store_instruction|.
+  for (auto it = load_instruction; it != store_instruction;
+       it = it->NextNode()) {
+    //|load_instruction| and |store_instruction| are not in the same block.
+    if (it == nullptr) {
+      return false;
+    }
+
+    // We need to make sure that the value pointed to by the source of the
+    // OpLoad hasn't changed by the time we see the matching OpStore
+    // instruction.
+    if (IsMemoryWritingOpCode(it->opcode())) {
+      return false;
+    } else if (IsMemoryBarrierOpCode(it->opcode()) &&
+               !IsStorageClassSafeAcrossMemoryBarriers(storage_class)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+void TransformationReplaceLoadStoreWithCopyMemory::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  // OpLoad and OpStore instructions must be defined.
+  auto load_instruction =
+      FindInstruction(message_.load_instruction_descriptor(), ir_context);
+  assert(load_instruction && load_instruction->opcode() == SpvOpLoad &&
+         "The required OpLoad instruction must be defined.");
+  auto store_instruction =
+      FindInstruction(message_.store_instruction_descriptor(), ir_context);
+  assert(store_instruction && store_instruction->opcode() == SpvOpStore &&
+         "The required OpStore instruction must be defined.");
+
+  // Intermediate values of the OpLoad and the OpStore must match.
+  assert(load_instruction->result_id() ==
+             store_instruction->GetSingleWordOperand(
+                 kOpStoreOperandIndexIntermediateIdToWrite) &&
+         "OpLoad and OpStore must refer to the same value.");
+
+  // Get the ids of the source operand of the OpLoad and the target operand of
+  // the OpStore.
+  uint32_t source_variable_id =
+      load_instruction->GetSingleWordOperand(kOpLoadOperandIndexSourceVariable);
+  uint32_t target_variable_id = store_instruction->GetSingleWordOperand(
+      kOpStoreOperandIndexTargetVariable);
+
+  // Insert the OpCopyMemory instruction before the OpStore instruction.
+  store_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpCopyMemory, 0, 0,
+      opt::Instruction::OperandList(
+          {{SPV_OPERAND_TYPE_ID, {target_variable_id}},
+           {SPV_OPERAND_TYPE_ID, {source_variable_id}}})));
+
+  // Remove the OpStore instruction.
+  ir_context->KillInst(store_instruction);
+
+  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+bool TransformationReplaceLoadStoreWithCopyMemory::IsMemoryWritingOpCode(
+    SpvOp op_code) {
+  if (spvOpcodeIsAtomicOp(op_code)) {
+    return op_code != SpvOpAtomicLoad;
+  }
+  switch (op_code) {
+    case SpvOpStore:
+    case SpvOpCopyMemory:
+    case SpvOpCopyMemorySized:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool TransformationReplaceLoadStoreWithCopyMemory::IsMemoryBarrierOpCode(
+    SpvOp op_code) {
+  switch (op_code) {
+    case SpvOpMemoryBarrier:
+    case SpvOpMemoryNamedBarrier:
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool TransformationReplaceLoadStoreWithCopyMemory::
+    IsStorageClassSafeAcrossMemoryBarriers(SpvStorageClass storage_class) {
+  switch (storage_class) {
+    case SpvStorageClassUniformConstant:
+    case SpvStorageClassInput:
+    case SpvStorageClassUniform:
+    case SpvStorageClassPrivate:
+    case SpvStorageClassFunction:
+      return true;
+    default:
+      return false;
+  }
+}
+
+protobufs::Transformation
+TransformationReplaceLoadStoreWithCopyMemory::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_replace_load_store_with_copy_memory() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 76 - 0
3rdparty/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.h

@@ -0,0 +1,76 @@
+// Copyright (c) 2020 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 SPIRV_TOOLS_TRANSFORMATION_REPLACE_LOAD_STORE_WITH_COPY_MEMORY_H
+#define SPIRV_TOOLS_TRANSFORMATION_REPLACE_LOAD_STORE_WITH_COPY_MEMORY_H
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationReplaceLoadStoreWithCopyMemory : public Transformation {
+ public:
+  explicit TransformationReplaceLoadStoreWithCopyMemory(
+      const protobufs::TransformationReplaceLoadStoreWithCopyMemory& message);
+
+  TransformationReplaceLoadStoreWithCopyMemory(
+      const protobufs::InstructionDescriptor& load_instruction_descriptor,
+      const protobufs::InstructionDescriptor& store_instruction_descriptor);
+
+  // - |message_.load_instruction_descriptor| must identify an OpLoad
+  //   instruction.
+  // - |message_.store_instruction_descriptor| must identify an OpStore
+  //   instruction.
+  // - The OpStore must write the intermediate value loaded by the OpLoad.
+  // - The OpLoad and the OpStore must not have certain instruction in between
+  //   (checked by IsMemoryWritingOpCode(), IsMemoryBarrierOpCode(),
+  //   IsStorageClassSafeAcrossMemoryBarriers()).
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Takes a pair of instruction descriptors to OpLoad and OpStore that have the
+  // same intermediate value and replaces the OpStore with an equivalent
+  // OpCopyMemory.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  // Checks if the instruction that has an |op_code| might write to
+  // the source operand of the OpLoad instruction.
+  static bool IsMemoryWritingOpCode(SpvOp op_code);
+
+  // Checks if the instruction that has an |op_code| is a memory barrier that
+  // could interfere with the source operand of the OpLoad instruction
+  static bool IsMemoryBarrierOpCode(SpvOp op_code);
+
+  // Checks if the |storage_class| of the source operand of the OpLoad
+  // instruction implies that this variable cannot change (due to other threads)
+  // across memory barriers.
+  static bool IsStorageClassSafeAcrossMemoryBarriers(
+      SpvStorageClass storage_class);
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationReplaceLoadStoreWithCopyMemory message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SPIRV_TOOLS_TRANSFORMATION_REPLACE_LOAD_STORE_WITH_COPY_MEMORY_H

+ 1 - 1
3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp

@@ -161,7 +161,7 @@ void TransformationReplaceParameterWithGlobal::Apply(
   }
 
   // Remove the parameter from the function.
-  function->RemoveParameter(message_.parameter_id());
+  fuzzerutil::RemoveParameter(ir_context, message_.parameter_id());
 
   // Update function's type.
   {

+ 13 - 10
3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp

@@ -28,8 +28,7 @@ TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct(
 TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct(
     const std::vector<uint32_t>& parameter_id, uint32_t fresh_function_type_id,
     uint32_t fresh_parameter_id,
-    const std::unordered_map<uint32_t, uint32_t>&
-        caller_id_to_fresh_composite_id) {
+    const std::map<uint32_t, uint32_t>& caller_id_to_fresh_composite_id) {
   message_.set_fresh_function_type_id(fresh_function_type_id);
   message_.set_fresh_parameter_id(fresh_parameter_id);
 
@@ -37,9 +36,8 @@ TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct(
     message_.add_parameter_id(id);
   }
 
-  message_.mutable_caller_id_to_fresh_composite_id()->insert(
-      caller_id_to_fresh_composite_id.begin(),
-      caller_id_to_fresh_composite_id.end());
+  *message_.mutable_caller_id_to_fresh_composite_id() =
+      fuzzerutil::MapToRepeatedUInt32Pair(caller_id_to_fresh_composite_id);
 }
 
 bool TransformationReplaceParamsWithStruct::IsApplicable(
@@ -103,13 +101,15 @@ bool TransformationReplaceParamsWithStruct::IsApplicable(
     return false;
   }
 
+  auto caller_id_to_fresh_composite_id = fuzzerutil::RepeatedUInt32PairToMap(
+      message_.caller_id_to_fresh_composite_id());
+
   // Check that |callee_id_to_fresh_composite_id| is valid.
   for (const auto* inst :
        fuzzerutil::GetCallers(ir_context, function->result_id())) {
     // Check that the callee is present in the map. It's ok if the map contains
     // more ids that there are callees (those ids will not be used).
-    if (!message_.caller_id_to_fresh_composite_id().contains(
-            inst->result_id())) {
+    if (!caller_id_to_fresh_composite_id.count(inst->result_id())) {
       return false;
     }
   }
@@ -118,7 +118,7 @@ bool TransformationReplaceParamsWithStruct::IsApplicable(
   std::vector<uint32_t> fresh_ids = {message_.fresh_function_type_id(),
                                      message_.fresh_parameter_id()};
 
-  for (const auto& entry : message_.caller_id_to_fresh_composite_id()) {
+  for (const auto& entry : caller_id_to_fresh_composite_id) {
     fresh_ids.push_back(entry.second);
   }
 
@@ -167,6 +167,9 @@ void TransformationReplaceParamsWithStruct::Apply(
     }
   }
 
+  auto caller_id_to_fresh_composite_id = fuzzerutil::RepeatedUInt32PairToMap(
+      message_.caller_id_to_fresh_composite_id());
+
   // Update all function calls.
   for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) {
     // Create a list of operands for the OpCompositeConstruct instruction.
@@ -189,7 +192,7 @@ void TransformationReplaceParamsWithStruct::Apply(
 
     // Insert OpCompositeConstruct before the function call.
     auto fresh_composite_id =
-        message_.caller_id_to_fresh_composite_id().at(inst->result_id());
+        caller_id_to_fresh_composite_id.at(inst->result_id());
     inst->InsertBefore(MakeUnique<opt::Instruction>(
         ir_context, SpvOpCompositeConstruct, struct_type_id, fresh_composite_id,
         std::move(composite_components)));
@@ -227,7 +230,7 @@ void TransformationReplaceParamsWithStruct::Apply(
             {SPV_OPERAND_TYPE_ID, {message_.fresh_parameter_id()}},
             {SPV_OPERAND_TYPE_LITERAL_INTEGER, {static_cast<uint32_t>(i)}}}));
 
-    function->RemoveParameter(param_inst->result_id());
+    fuzzerutil::RemoveParameter(ir_context, param_inst->result_id());
   }
 
   // Update function's type.

+ 2 - 3
3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.h

@@ -15,7 +15,7 @@
 #ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_
 #define SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_
 
-#include <unordered_map>
+#include <map>
 
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/transformation.h"
@@ -33,8 +33,7 @@ class TransformationReplaceParamsWithStruct : public Transformation {
   TransformationReplaceParamsWithStruct(
       const std::vector<uint32_t>& parameter_id,
       uint32_t fresh_function_type_id, uint32_t fresh_parameter_id,
-      const std::unordered_map<uint32_t, uint32_t>&
-          caller_id_to_fresh_composite_id);
+      const std::map<uint32_t, uint32_t>& caller_id_to_fresh_composite_id);
 
   // - Each element of |parameter_id| is a valid result id of some
   //   OpFunctionParameter instruction. All parameter ids must correspond to

+ 2 - 2
3rdparty/spirv-tools/source/fuzz/transformation_set_loop_control.cpp

@@ -42,8 +42,8 @@ bool TransformationSetLoopControl::IsApplicable(
     return false;
   }
 
-  // We sanity-check that the transformation does not try to set any meaningless
-  // bits of the loop control mask.
+  // We assert that the transformation does not try to set any meaningless bits
+  // of the loop control mask.
   uint32_t all_loop_control_mask_bits_set =
       SpvLoopControlUnrollMask | SpvLoopControlDontUnrollMask |
       SpvLoopControlDependencyInfiniteMask |

+ 4 - 5
3rdparty/spirv-tools/source/fuzz/transformation_split_block.cpp

@@ -135,11 +135,10 @@ void TransformationSplitBlock::Apply(
   // predecessor operand so that the block they used to be inside is now the
   // predecessor.
   new_bb->ForEachPhiInst([block_to_split](opt::Instruction* phi_inst) {
-    // The following assertion is a sanity check.  It is guaranteed to hold
-    // if IsApplicable holds.
-    assert(phi_inst->NumInOperands() == 2 &&
-           "We can only split a block before an OpPhi if block has exactly "
-           "one predecessor.");
+    assert(
+        phi_inst->NumInOperands() == 2 &&
+        "Precondition: a block can only be split before an OpPhi if the block"
+        "has exactly one predecessor.");
     phi_inst->SetInOperand(1, {block_to_split->id()});
   });
 

+ 1 - 1
3rdparty/spirv-tools/source/link/CMakeLists.txt

@@ -11,7 +11,7 @@
 # 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.
-add_library(SPIRV-Tools-link
+add_library(SPIRV-Tools-link STATIC
   linker.cpp
 )
 

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

@@ -324,7 +324,7 @@ std::string FriendlyNameMapper::NameForEnumOperand(spv_operand_type_t type,
   if (SPV_SUCCESS == grammar_.lookupOperand(type, word, &desc)) {
     return desc->name;
   } else {
-    // Invalid input.  Just give something sane.
+    // Invalid input.  Just give something.
     return std::string("StorageClass") + to_string(word);
   }
 }

+ 26 - 5
3rdparty/spirv-tools/source/operand.cpp

@@ -265,7 +265,6 @@ const char* spvOperandTypeStr(spv_operand_type_t type) {
     case SPV_OPERAND_TYPE_NONE:
       return "NONE";
     default:
-      assert(0 && "Unhandled operand type!");
       break;
   }
   return "unknown";
@@ -371,13 +370,35 @@ bool spvOperandIsConcreteMask(spv_operand_type_t type) {
 }
 
 bool spvOperandIsOptional(spv_operand_type_t type) {
-  return SPV_OPERAND_TYPE_FIRST_OPTIONAL_TYPE <= type &&
-         type <= SPV_OPERAND_TYPE_LAST_OPTIONAL_TYPE;
+  switch (type) {
+    case SPV_OPERAND_TYPE_OPTIONAL_ID:
+    case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
+    case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
+    case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER:
+    case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER:
+    case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER:
+    case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING:
+    case SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER:
+    case SPV_OPERAND_TYPE_OPTIONAL_CIV:
+      return true;
+    default:
+      break;
+  }
+  // Any variable operand is also optional.
+  return spvOperandIsVariable(type);
 }
 
 bool spvOperandIsVariable(spv_operand_type_t type) {
-  return SPV_OPERAND_TYPE_FIRST_VARIABLE_TYPE <= type &&
-         type <= SPV_OPERAND_TYPE_LAST_VARIABLE_TYPE;
+  switch (type) {
+    case SPV_OPERAND_TYPE_VARIABLE_ID:
+    case SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER:
+    case SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER_ID:
+    case SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER:
+      return true;
+    default:
+      break;
+  }
+  return false;
 }
 
 bool spvExpandOperandSequenceOnce(spv_operand_type_t type,

+ 2 - 2
3rdparty/spirv-tools/source/opt/CMakeLists.txt

@@ -233,7 +233,7 @@ endif()
 
 spvtools_pch(SPIRV_TOOLS_OPT_SOURCES pch_source_opt)
 
-add_library(SPIRV-Tools-opt ${SPIRV_TOOLS_OPT_SOURCES})
+add_library(SPIRV-Tools-opt STATIC ${SPIRV_TOOLS_OPT_SOURCES})
 
 spvtools_default_compile_options(SPIRV-Tools-opt)
 target_include_directories(SPIRV-Tools-opt
@@ -245,7 +245,7 @@ target_include_directories(SPIRV-Tools-opt
 )
 # We need the assembling and disassembling functionalities in the main library.
 target_link_libraries(SPIRV-Tools-opt
-  PUBLIC ${SPIRV_TOOLS})
+  PUBLIC ${SPIRV_TOOLS}-static)
 
 set_property(TARGET SPIRV-Tools-opt PROPERTY FOLDER "SPIRV-Tools libraries")
 spvtools_check_symbol_exports(SPIRV-Tools-opt)

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

@@ -696,8 +696,8 @@ Pass::Status AggressiveDCEPass::ProcessImpl() {
   // been marked, it is safe to remove dead global values.
   modified |= ProcessGlobalValues();
 
-  // Sanity check.
-  assert(to_kill_.size() == 0 || modified);
+  assert((to_kill_.empty() || modified) &&
+         "A dead instruction was identified, but no change recorded.");
 
   // Kill all dead instructions.
   for (auto inst : to_kill_) {

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

@@ -396,6 +396,12 @@ uint32_t ConstantManager::GetFloatConst(float val) {
   return GetDefiningInstruction(c)->result_id();
 }
 
+uint32_t ConstantManager::GetSIntConst(int32_t val) {
+  Type* sint_type = context()->get_type_mgr()->GetSIntType();
+  const Constant* c = GetConstant(sint_type, {static_cast<uint32_t>(val)});
+  return GetDefiningInstruction(c)->result_id();
+}
+
 std::vector<const analysis::Constant*> Constant::GetVectorComponents(
     analysis::ConstantManager* const_mgr) const {
   std::vector<const analysis::Constant*> components;

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

@@ -630,6 +630,9 @@ class ConstantManager {
   // Returns the id of a 32-bit floating point constant with value |val|.
   uint32_t GetFloatConst(float val);
 
+  // Returns the id of a 32-bit signed integer constant with value |val|.
+  uint32_t GetSIntConst(int32_t val);
+
  private:
   // Creates a Constant instance with the given type and a vector of constant
   // defining words. Returns a unique pointer to the created Constant instance

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

@@ -33,10 +33,13 @@ static const uint32_t kDebugDeclareOperandLocalVariableIndex = 4;
 static const uint32_t kDebugDeclareOperandVariableIndex = 5;
 static const uint32_t kDebugValueOperandLocalVariableIndex = 4;
 static const uint32_t kDebugValueOperandExpressionIndex = 6;
+static const uint32_t kDebugValueOperandIndexesIndex = 7;
 static const uint32_t kDebugOperationOperandOperationIndex = 4;
 static const uint32_t kOpVariableOperandStorageClassIndex = 2;
 static const uint32_t kDebugLocalVariableOperandParentIndex = 9;
-static const uint32_t kDebugOperationOperandOpCodeIndex = 4;
+static const uint32_t kExtInstInstructionInIdx = 1;
+static const uint32_t kDebugGlobalVariableOperandFlagsIndex = 12;
+static const uint32_t kDebugLocalVariableOperandFlagsIndex = 10;
 
 namespace spvtools {
 namespace opt {
@@ -369,7 +372,7 @@ Instruction* DebugInfoManager::CloneDebugInlinedAt(uint32_t clone_inlined_at_id,
       std::move(new_inlined_at));
 }
 
-bool DebugInfoManager::IsDebugDeclared(uint32_t variable_id) {
+bool DebugInfoManager::IsVariableDebugDeclared(uint32_t variable_id) {
   auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
   return dbg_decl_itr != var_id_to_dbg_decl_.end();
 }
@@ -446,9 +449,47 @@ bool DebugInfoManager::IsDeclareVisibleToInstr(Instruction* dbg_declare,
   return IsAncestorOfScope(instr_scope_id, decl_scope_id);
 }
 
-void DebugInfoManager::AddDebugValue(Instruction* scope_and_line,
-                                     uint32_t variable_id, uint32_t value_id,
-                                     Instruction* insert_pos) {
+Instruction* DebugInfoManager::AddDebugValueWithIndex(
+    uint32_t dbg_local_var_id, uint32_t value_id, uint32_t expr_id,
+    uint32_t index_id, Instruction* insert_before) {
+  uint32_t result_id = context()->TakeNextId();
+  if (!result_id) return nullptr;
+  std::unique_ptr<Instruction> new_dbg_value(new Instruction(
+      context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
+      result_id,
+      {
+          {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+           {context()
+                ->get_feature_mgr()
+                ->GetExtInstImportId_OpenCL100DebugInfo()}},
+          {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+           {static_cast<uint32_t>(OpenCLDebugInfo100DebugValue)}},
+          {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {dbg_local_var_id}},
+          {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {value_id}},
+          {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+           {expr_id == 0 ? GetEmptyDebugExpression()->result_id() : expr_id}},
+      }));
+  if (index_id) {
+    new_dbg_value->AddOperand(
+        {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {index_id}});
+  }
+
+  Instruction* added_dbg_value =
+      insert_before->InsertBefore(std::move(new_dbg_value));
+  AnalyzeDebugInst(added_dbg_value);
+  if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+    context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_value);
+  if (context()->AreAnalysesValid(
+          IRContext::Analysis::kAnalysisInstrToBlockMapping)) {
+    auto insert_blk = context()->get_instr_block(insert_before);
+    context()->set_instr_block(added_dbg_value, insert_blk);
+  }
+  return added_dbg_value;
+}
+
+void DebugInfoManager::AddDebugValueIfVarDeclIsVisible(
+    Instruction* scope_and_line, uint32_t variable_id, uint32_t value_id,
+    Instruction* insert_pos) {
   auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
   if (dbg_decl_itr == var_id_to_dbg_decl_.end()) return;
 
@@ -456,36 +497,6 @@ void DebugInfoManager::AddDebugValue(Instruction* scope_and_line,
   for (auto* dbg_decl_or_val : dbg_decl_itr->second) {
     if (!IsDeclareVisibleToInstr(dbg_decl_or_val, instr_scope_id)) continue;
 
-    uint32_t result_id = context()->TakeNextId();
-    std::unique_ptr<Instruction> new_dbg_value(new Instruction(
-        context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
-        result_id,
-        {
-            {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
-             {context()
-                  ->get_feature_mgr()
-                  ->GetExtInstImportId_OpenCL100DebugInfo()}},
-            {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
-             {static_cast<uint32_t>(OpenCLDebugInfo100DebugValue)}},
-            {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
-             {dbg_decl_or_val->GetSingleWordOperand(
-                 kDebugValueOperandLocalVariableIndex)}},
-            {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {value_id}},
-            {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
-             {GetEmptyDebugExpression()->result_id()}},
-        }));
-
-    if (dbg_decl_or_val->NumOperands() >
-        kDebugValueOperandExpressionIndex + 1) {
-      assert(dbg_decl_or_val->GetOpenCL100DebugOpcode() ==
-             OpenCLDebugInfo100DebugValue);
-      for (uint32_t i = kDebugValueOperandExpressionIndex + 1;
-           i < dbg_decl_or_val->NumOperands(); ++i) {
-        new_dbg_value->AddOperand({spv_operand_type_t::SPV_OPERAND_TYPE_ID,
-                                   {dbg_decl_or_val->GetSingleWordOperand(i)}});
-      }
-    }
-
     // Avoid inserting the new DebugValue between OpPhi or OpVariable
     // instructions.
     Instruction* insert_before = insert_pos->NextNode();
@@ -494,17 +505,18 @@ void DebugInfoManager::AddDebugValue(Instruction* scope_and_line,
       insert_before = insert_before->NextNode();
     }
 
-    Instruction* added_dbg_value =
-        insert_before->InsertBefore(std::move(new_dbg_value));
-    added_dbg_value->UpdateDebugInfo(scope_and_line);
-    AnalyzeDebugInst(added_dbg_value);
-    if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
-      context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_value);
-    if (context()->AreAnalysesValid(
-            IRContext::Analysis::kAnalysisInstrToBlockMapping)) {
-      auto insert_blk = context()->get_instr_block(insert_before);
-      context()->set_instr_block(added_dbg_value, insert_blk);
+    uint32_t index_id = 0;
+    if (dbg_decl_or_val->NumOperands() > kDebugValueOperandIndexesIndex) {
+      index_id =
+          dbg_decl_or_val->GetSingleWordOperand(kDebugValueOperandIndexesIndex);
     }
+
+    Instruction* added_dbg_value =
+        AddDebugValueWithIndex(dbg_decl_or_val->GetSingleWordOperand(
+                                   kDebugValueOperandLocalVariableIndex),
+                               value_id, 0, index_id, insert_before);
+    assert(added_dbg_value != nullptr);
+    added_dbg_value->UpdateDebugInfoFrom(scope_and_line);
   }
 }
 
@@ -542,6 +554,12 @@ uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare(
   return 0;
 }
 
+bool DebugInfoManager::IsDebugDeclare(Instruction* instr) {
+  if (!instr->IsOpenCL100DebugInstr()) return false;
+  return instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare ||
+         GetVariableIdOfDebugValueUsedForDeclare(instr) != 0;
+}
+
 void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
   if (!dbg_inst->IsOpenCL100DebugInstr()) return;
 
@@ -556,7 +574,7 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
 
   if (deref_operation_ == nullptr &&
       dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugOperation &&
-      dbg_inst->GetSingleWordOperand(kDebugOperationOperandOpCodeIndex) ==
+      dbg_inst->GetSingleWordOperand(kDebugOperationOperandOperationIndex) ==
           OpenCLDebugInfo100Deref) {
     deref_operation_ = dbg_inst;
   }
@@ -581,6 +599,56 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
   }
 }
 
+void DebugInfoManager::ConvertDebugGlobalToLocalVariable(
+    Instruction* dbg_global_var, Instruction* local_var) {
+  if (dbg_global_var->GetOpenCL100DebugOpcode() !=
+      OpenCLDebugInfo100DebugGlobalVariable) {
+    return;
+  }
+  assert(local_var->opcode() == SpvOpVariable ||
+         local_var->opcode() == SpvOpFunctionParameter);
+
+  // Convert |dbg_global_var| to DebugLocalVariable
+  dbg_global_var->SetInOperand(kExtInstInstructionInIdx,
+                               {OpenCLDebugInfo100DebugLocalVariable});
+  auto flags = dbg_global_var->GetSingleWordOperand(
+      kDebugGlobalVariableOperandFlagsIndex);
+  for (uint32_t i = dbg_global_var->NumInOperands() - 1;
+       i >= kDebugLocalVariableOperandFlagsIndex; --i) {
+    dbg_global_var->RemoveOperand(i);
+  }
+  dbg_global_var->SetOperand(kDebugLocalVariableOperandFlagsIndex, {flags});
+  context()->ForgetUses(dbg_global_var);
+  context()->AnalyzeUses(dbg_global_var);
+
+  // Create a DebugDeclare
+  std::unique_ptr<Instruction> new_dbg_decl(new Instruction(
+      context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
+      context()->TakeNextId(),
+      {
+          {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+           {context()
+                ->get_feature_mgr()
+                ->GetExtInstImportId_OpenCL100DebugInfo()}},
+          {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+           {static_cast<uint32_t>(OpenCLDebugInfo100DebugDeclare)}},
+          {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+           {dbg_global_var->result_id()}},
+          {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {local_var->result_id()}},
+          {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+           {GetEmptyDebugExpression()->result_id()}},
+      }));
+  auto* added_dbg_decl =
+      local_var->NextNode()->InsertBefore(std::move(new_dbg_decl));
+  if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+    context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_decl);
+  if (context()->AreAnalysesValid(
+          IRContext::Analysis::kAnalysisInstrToBlockMapping)) {
+    auto insert_blk = context()->get_instr_block(local_var);
+    context()->set_instr_block(added_dbg_decl, insert_blk);
+  }
+}
+
 void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
   deref_operation_ = nullptr;
   debug_info_none_inst_ = nullptr;
@@ -638,7 +706,8 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
           dbg_instr_itr->GetOpenCL100DebugOpcode() ==
               OpenCLDebugInfo100DebugOperation &&
           dbg_instr_itr->GetSingleWordOperand(
-              kDebugOperationOperandOpCodeIndex) == OpenCLDebugInfo100Deref) {
+              kDebugOperationOperandOperationIndex) ==
+              OpenCLDebugInfo100Deref) {
         deref_operation_ = &*dbg_instr_itr;
         break;
       }

+ 23 - 5
3rdparty/spirv-tools/source/opt/debug_info_manager.h

@@ -133,17 +133,27 @@ class DebugInfoManager {
   uint32_t BuildDebugInlinedAtChain(uint32_t callee_inlined_at,
                                     DebugInlinedAtContext* inlined_at_ctx);
 
-  // Return true if |variable_id| has DebugDeclare or DebugVal.
-  bool IsDebugDeclared(uint32_t variable_id);
+  // Returns true if there is a debug declaration instruction whose
+  // 'Local Variable' operand is |variable_id|.
+  bool IsVariableDebugDeclared(uint32_t variable_id);
 
-  // Kill all DebugDeclares for |variable_id|
+  // Kills all debug declaration instructions with Deref whose 'Local Variable'
+  // operand is |variable_id|.
   void KillDebugDeclares(uint32_t variable_id);
 
   // Generates a DebugValue instruction with value |value_id| for every local
   // variable that is in the scope of |scope_and_line| and whose memory is
   // |variable_id| and inserts it after the instruction |insert_pos|.
-  void AddDebugValue(Instruction* scope_and_line, uint32_t variable_id,
-                     uint32_t value_id, Instruction* insert_pos);
+  void AddDebugValueIfVarDeclIsVisible(Instruction* scope_and_line,
+                                       uint32_t variable_id, uint32_t value_id,
+                                       Instruction* insert_pos);
+
+  // Generates a DebugValue instruction with |dbg_local_var_id|, |value_id|,
+  // |expr_id|, |index_id| operands and inserts it before |insert_before|.
+  Instruction* AddDebugValueWithIndex(uint32_t dbg_local_var_id,
+                                      uint32_t value_id, uint32_t expr_id,
+                                      uint32_t index_id,
+                                      Instruction* insert_before);
 
   // Erases |instr| from data structures of this class.
   void ClearDebugInfo(Instruction* instr);
@@ -153,6 +163,14 @@ class DebugInfoManager {
   // Function storage class. Otherwise, returns 0.
   uint32_t GetVariableIdOfDebugValueUsedForDeclare(Instruction* inst);
 
+  // Converts DebugGlobalVariable |dbg_global_var| to a DebugLocalVariable and
+  // creates a DebugDeclare mapping the new DebugLocalVariable to |local_var|.
+  void ConvertDebugGlobalToLocalVariable(Instruction* dbg_global_var,
+                                         Instruction* local_var);
+
+  // Returns true if |instr| is a debug declaration instruction.
+  bool IsDebugDeclare(Instruction* instr);
+
  private:
   IRContext* context() { return context_; }
 

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

@@ -22,8 +22,8 @@
 
 // Calculates the dominator or postdominator tree for a given function.
 // 1 - Compute the successors and predecessors for each BasicBlock. We add a
-// dummy node for the start node or for postdominators the exit. This node will
-// point to all entry or all exit nodes.
+// placeholder node for the start node or for postdominators the exit. This node
+// will point to all entry or all exit nodes.
 // 2 - Using the CFA::DepthFirstTraversal get a depth first postordered list of
 // all BasicBlocks. Using the successors (or for postdominator, predecessors)
 // calculated in step 1 to traverse the tree.
@@ -107,8 +107,9 @@ class BasicBlockSuccessorHelper {
 
  public:
   // For compliance with the dominance tree computation, entry nodes are
-  // connected to a single dummy node.
-  BasicBlockSuccessorHelper(Function& func, const BasicBlock* dummy_start_node,
+  // connected to a single placeholder node.
+  BasicBlockSuccessorHelper(Function& func,
+                            const BasicBlock* placeholder_start_node,
                             bool post);
 
   // CFA::CalculateDominators requires std::vector<BasicBlock*>.
@@ -139,23 +140,24 @@ class BasicBlockSuccessorHelper {
   // Build the successors and predecessors map for each basic blocks |f|.
   // If |invert_graph_| is true, all edges are reversed (successors becomes
   // predecessors and vice versa).
-  // For convenience, the start of the graph is |dummy_start_node|.
+  // For convenience, the start of the graph is |placeholder_start_node|.
   // The dominator tree construction requires a unique entry node, which cannot
-  // be guaranteed for the postdominator graph. The |dummy_start_node| BB is
-  // here to gather all entry nodes.
-  void CreateSuccessorMap(Function& f, const BasicBlock* dummy_start_node);
+  // be guaranteed for the postdominator graph. The |placeholder_start_node| BB
+  // is here to gather all entry nodes.
+  void CreateSuccessorMap(Function& f,
+                          const BasicBlock* placeholder_start_node);
 };
 
 template <typename BBType>
 BasicBlockSuccessorHelper<BBType>::BasicBlockSuccessorHelper(
-    Function& func, const BasicBlock* dummy_start_node, bool invert)
+    Function& func, const BasicBlock* placeholder_start_node, bool invert)
     : invert_graph_(invert) {
-  CreateSuccessorMap(func, dummy_start_node);
+  CreateSuccessorMap(func, placeholder_start_node);
 }
 
 template <typename BBType>
 void BasicBlockSuccessorHelper<BBType>::CreateSuccessorMap(
-    Function& f, const BasicBlock* dummy_start_node) {
+    Function& f, const BasicBlock* placeholder_start_node) {
   std::map<uint32_t, BasicBlock*> id_to_BB_map;
   auto GetSuccessorBasicBlock = [&f, &id_to_BB_map](uint32_t successor_id) {
     BasicBlock*& Succ = id_to_BB_map[successor_id];
@@ -173,11 +175,10 @@ void BasicBlockSuccessorHelper<BBType>::CreateSuccessorMap(
   if (invert_graph_) {
     // For the post dominator tree, we see the inverted graph.
     // successors_ in the inverted graph are the predecessors in the CFG.
-    // The tree construction requires 1 entry point, so we add a dummy node
-    // that is connected to all function exiting basic blocks.
-    // An exiting basic block is a block with an OpKill, OpUnreachable,
-    // OpReturn, OpReturnValue, or OpTerminateInvocation  as terminator
-    // instruction.
+    // The tree construction requires 1 entry point, so we add a placeholder
+    // node that is connected to all function exiting basic blocks. An exiting
+    // basic block is a block with an OpKill, OpUnreachable, OpReturn,
+    // OpReturnValue, or OpTerminateInvocation  as terminator instruction.
     for (BasicBlock& bb : f) {
       if (bb.hasSuccessor()) {
         BasicBlockListTy& pred_list = predecessors_[&bb];
@@ -192,14 +193,15 @@ void BasicBlockSuccessorHelper<BBType>::CreateSuccessorMap(
               pred_list.push_back(succ);
             });
       } else {
-        successors_[dummy_start_node].push_back(&bb);
-        predecessors_[&bb].push_back(const_cast<BasicBlock*>(dummy_start_node));
+        successors_[placeholder_start_node].push_back(&bb);
+        predecessors_[&bb].push_back(
+            const_cast<BasicBlock*>(placeholder_start_node));
       }
     }
   } else {
-    successors_[dummy_start_node].push_back(f.entry().get());
+    successors_[placeholder_start_node].push_back(f.entry().get());
     predecessors_[f.entry().get()].push_back(
-        const_cast<BasicBlock*>(dummy_start_node));
+        const_cast<BasicBlock*>(placeholder_start_node));
     for (BasicBlock& bb : f) {
       BasicBlockListTy& succ_list = successors_[&bb];
 
@@ -288,7 +290,7 @@ DominatorTreeNode* DominatorTree::GetOrInsertNode(BasicBlock* bb) {
 }
 
 void DominatorTree::GetDominatorEdges(
-    const Function* f, const BasicBlock* dummy_start_node,
+    const Function* f, const BasicBlock* placeholder_start_node,
     std::vector<std::pair<BasicBlock*, BasicBlock*>>* edges) {
   // Each time the depth first traversal calls the postorder callback
   // std::function we push that node into the postorder vector to create our
@@ -302,7 +304,7 @@ void DominatorTree::GetDominatorEdges(
   // BB are derived from F, so we need to const cast it at some point
   // no modification is made on F.
   BasicBlockSuccessorHelper<BasicBlock> helper{
-      *const_cast<Function*>(f), dummy_start_node, postdominator_};
+      *const_cast<Function*>(f), placeholder_start_node, postdominator_};
 
   // The successor function tells DepthFirstTraversal how to move to successive
   // nodes by providing an interface to get a list of successor nodes from any
@@ -316,7 +318,7 @@ void DominatorTree::GetDominatorEdges(
   // If we're building a post dominator tree we traverse the tree in reverse
   // using the predecessor function in place of the successor function and vice
   // versa.
-  DepthFirstSearchPostOrder(dummy_start_node, successor_functor,
+  DepthFirstSearchPostOrder(placeholder_start_node, successor_functor,
                             postorder_function);
   *edges = CFA<BasicBlock>::CalculateDominators(postorder, predecessor_functor);
 }
@@ -329,12 +331,12 @@ void DominatorTree::InitializeTree(const CFG& cfg, const Function* f) {
     return;
   }
 
-  const BasicBlock* dummy_start_node =
+  const BasicBlock* placeholder_start_node =
       postdominator_ ? cfg.pseudo_exit_block() : cfg.pseudo_entry_block();
 
   // Get the immediate dominator for each node.
   std::vector<std::pair<BasicBlock*, BasicBlock*>> edges;
-  GetDominatorEdges(f, dummy_start_node, &edges);
+  GetDominatorEdges(f, placeholder_start_node, &edges);
 
   // Transform the vector<pair> into the tree structure which we can use to
   // efficiently query dominance.
@@ -380,7 +382,7 @@ void DominatorTree::DumpTreeAsDot(std::ostream& out_stream) const {
     }
 
     // Print the arrow from the parent to this node. Entry nodes will not have
-    // parents so draw them as children from the dummy node.
+    // parents so draw them as children from the placeholder node.
     if (node->parent_) {
       out_stream << node->parent_->bb_->id() << " -> " << node->bb_->id()
                  << ";\n";

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

@@ -246,6 +246,11 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
   // Clear line-related debug instructions attached to this instruction.
   void clear_dbg_line_insts() { dbg_line_insts_.clear(); }
 
+  // Set line-related debug instructions.
+  void set_dbg_line_insts(const std::vector<Instruction>& lines) {
+    dbg_line_insts_ = lines;
+  }
+
   // Same semantics as in the base class except the list the InstructionList
   // containing |pos| will now assume ownership of |this|.
   // inline void MoveBefore(Instruction* pos);
@@ -301,7 +306,7 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
     return dbg_scope_.GetInlinedAt();
   }
   // Updates OpLine and DebugScope based on the information of |from|.
-  inline void UpdateDebugInfo(const Instruction* from);
+  inline void UpdateDebugInfoFrom(const Instruction* from);
   // Remove the |index|-th operand
   void RemoveOperand(uint32_t index) {
     operands_.erase(operands_.begin() + index);
@@ -667,7 +672,7 @@ inline void Instruction::UpdateDebugInlinedAt(uint32_t new_inlined_at) {
   }
 }
 
-inline void Instruction::UpdateDebugInfo(const Instruction* from) {
+inline void Instruction::UpdateDebugInfoFrom(const Instruction* from) {
   if (from == nullptr) return;
   clear_dbg_line_insts();
   if (!from->dbg_line_insts().empty())

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

@@ -601,15 +601,15 @@ class InstructionBuilder {
     return preserved_analyses_ & analysis;
   }
 
-  // Updates the def/use manager if the user requested it. If he did not request
-  // an update, this function does nothing.
+  // Updates the def/use manager if the user requested it. If an update was not
+  // requested, this function does nothing.
   inline void UpdateDefUseMgr(Instruction* insn) {
     if (IsAnalysisUpdateRequested(IRContext::kAnalysisDefUse))
       GetContext()->get_def_use_mgr()->AnalyzeInstDefUse(insn);
   }
 
-  // Updates the instruction to block analysis if the user requested it. If he
-  // did not request an update, this function does nothing.
+  // Updates the instruction to block analysis if the user requested it. If
+  // an update was not requested, this function does nothing.
   inline void UpdateInstrToBlockMapping(Instruction* insn) {
     if (IsAnalysisUpdateRequested(IRContext::kAnalysisInstrToBlockMapping) &&
         parent_)

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

@@ -222,13 +222,6 @@ bool IRContext::KillDef(uint32_t id) {
   return false;
 }
 
-void IRContext::KillDebugDeclareInsts(Function* fn) {
-  fn->ForEachInst([this](Instruction* inst) {
-    if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare)
-      KillInst(inst);
-  });
-}
-
 bool IRContext::ReplaceAllUsesWith(uint32_t before, uint32_t after) {
   return ReplaceAllUsesWithPredicate(
       before, after, [](Instruction*, uint32_t) { return true; });

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

@@ -403,9 +403,6 @@ class IRContext {
   // instruction exists.
   Instruction* KillInst(Instruction* inst);
 
-  // Deletes DebugDeclare instructions in the given function |fn|.
-  void KillDebugDeclareInsts(Function* fn);
-
   // Returns true if all of the given analyses are valid.
   bool AreAnalysesValid(Analysis set) { return (set & valid_analyses_) == set; }
 

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

@@ -83,7 +83,8 @@ bool LocalSingleBlockLoadStoreElimPass::LocalSingleBlockLoadStoreElim(
             auto prev_store = var2store_.find(varId);
             if (prev_store != var2store_.end() &&
                 instructions_to_save.count(prev_store->second) == 0 &&
-                !context()->get_debug_info_mgr()->IsDebugDeclared(varId)) {
+                !context()->get_debug_info_mgr()->IsVariableDebugDeclared(
+                    varId)) {
               instructions_to_kill.push_back(prev_store->second);
               modified = true;
             }

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

@@ -142,12 +142,12 @@ bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) {
   // the DebugDeclare.
   uint32_t var_id = var_inst->result_id();
   if (all_rewritten &&
-      context()->get_debug_info_mgr()->IsDebugDeclared(var_id)) {
+      context()->get_debug_info_mgr()->IsVariableDebugDeclared(var_id)) {
     const analysis::Type* var_type =
         context()->get_type_mgr()->GetType(var_inst->type_id());
     const analysis::Type* store_type = var_type->AsPointer()->pointee_type();
     if (!(store_type->AsStruct() || store_type->AsArray())) {
-      context()->get_debug_info_mgr()->AddDebugValue(
+      context()->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(
           store_inst, var_id, store_inst->GetSingleWordInOperand(1),
           store_inst);
       context()->get_debug_info_mgr()->KillDebugDeclares(var_id);

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

@@ -511,7 +511,7 @@ void Loop::ComputeLoopStructuredOrder(
 }
 
 LoopDescriptor::LoopDescriptor(IRContext* context, const Function* f)
-    : loops_(), dummy_top_loop_(nullptr) {
+    : loops_(), placeholder_top_loop_(nullptr) {
   PopulateList(context, f);
 }
 
@@ -592,7 +592,7 @@ void LoopDescriptor::PopulateList(IRContext* context, const Function* f) {
     }
   }
   for (Loop* loop : loops_) {
-    if (!loop->HasParent()) dummy_top_loop_.nested_loops_.push_back(loop);
+    if (!loop->HasParent()) placeholder_top_loop_.nested_loops_.push_back(loop);
   }
 }
 
@@ -986,7 +986,7 @@ void LoopDescriptor::ClearLoops() {
 // Adds a new loop nest to the descriptor set.
 Loop* LoopDescriptor::AddLoopNest(std::unique_ptr<Loop> new_loop) {
   Loop* loop = new_loop.release();
-  if (!loop->HasParent()) dummy_top_loop_.nested_loops_.push_back(loop);
+  if (!loop->HasParent()) placeholder_top_loop_.nested_loops_.push_back(loop);
   // Iterate from inner to outer most loop, adding basic block to loop mapping
   // as we go.
   for (Loop& current_loop :
@@ -1000,7 +1000,7 @@ Loop* LoopDescriptor::AddLoopNest(std::unique_ptr<Loop> new_loop) {
 }
 
 void LoopDescriptor::RemoveLoop(Loop* loop) {
-  Loop* parent = loop->GetParent() ? loop->GetParent() : &dummy_top_loop_;
+  Loop* parent = loop->GetParent() ? loop->GetParent() : &placeholder_top_loop_;
   parent->nested_loops_.erase(std::find(parent->nested_loops_.begin(),
                                         parent->nested_loops_.end(), loop));
   std::for_each(

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

@@ -406,8 +406,8 @@ class Loop {
   // the iterators.
   bool loop_is_marked_for_removal_;
 
-  // This is only to allow LoopDescriptor::dummy_top_loop_ to add top level
-  // loops as child.
+  // This is only to allow LoopDescriptor::placeholder_top_loop_ to add top
+  // level loops as child.
   friend class LoopDescriptor;
   friend class LoopUtils;
 };
@@ -430,14 +430,14 @@ class LoopDescriptor {
   // Disable copy constructor, to avoid double-free on destruction.
   LoopDescriptor(const LoopDescriptor&) = delete;
   // Move constructor.
-  LoopDescriptor(LoopDescriptor&& other) : dummy_top_loop_(nullptr) {
+  LoopDescriptor(LoopDescriptor&& other) : placeholder_top_loop_(nullptr) {
     // We need to take ownership of the Loop objects in the other
     // LoopDescriptor, to avoid double-free.
     loops_ = std::move(other.loops_);
     other.loops_.clear();
     basic_block_to_loop_ = std::move(other.basic_block_to_loop_);
     other.basic_block_to_loop_.clear();
-    dummy_top_loop_ = std::move(other.dummy_top_loop_);
+    placeholder_top_loop_ = std::move(other.placeholder_top_loop_);
   }
 
   // Destructor
@@ -470,25 +470,27 @@ class LoopDescriptor {
 
   // Iterators for post order depth first traversal of the loops.
   // Inner most loops will be visited first.
-  inline iterator begin() { return iterator::begin(&dummy_top_loop_); }
-  inline iterator end() { return iterator::end(&dummy_top_loop_); }
+  inline iterator begin() { return iterator::begin(&placeholder_top_loop_); }
+  inline iterator end() { return iterator::end(&placeholder_top_loop_); }
   inline const_iterator begin() const { return cbegin(); }
   inline const_iterator end() const { return cend(); }
   inline const_iterator cbegin() const {
-    return const_iterator::begin(&dummy_top_loop_);
+    return const_iterator::begin(&placeholder_top_loop_);
   }
   inline const_iterator cend() const {
-    return const_iterator::end(&dummy_top_loop_);
+    return const_iterator::end(&placeholder_top_loop_);
   }
 
   // Iterators for pre-order depth first traversal of the loops.
   // Inner most loops will be visited first.
-  inline pre_iterator pre_begin() { return ++pre_iterator(&dummy_top_loop_); }
+  inline pre_iterator pre_begin() {
+    return ++pre_iterator(&placeholder_top_loop_);
+  }
   inline pre_iterator pre_end() { return pre_iterator(); }
   inline const_pre_iterator pre_begin() const { return pre_cbegin(); }
   inline const_pre_iterator pre_end() const { return pre_cend(); }
   inline const_pre_iterator pre_cbegin() const {
-    return ++const_pre_iterator(&dummy_top_loop_);
+    return ++const_pre_iterator(&placeholder_top_loop_);
   }
   inline const_pre_iterator pre_cend() const { return const_pre_iterator(); }
 
@@ -524,14 +526,14 @@ class LoopDescriptor {
   void RemoveLoop(Loop* loop);
 
   void SetAsTopLoop(Loop* loop) {
-    assert(std::find(dummy_top_loop_.begin(), dummy_top_loop_.end(), loop) ==
-               dummy_top_loop_.end() &&
+    assert(std::find(placeholder_top_loop_.begin(), placeholder_top_loop_.end(),
+                     loop) == placeholder_top_loop_.end() &&
            "already registered");
-    dummy_top_loop_.nested_loops_.push_back(loop);
+    placeholder_top_loop_.nested_loops_.push_back(loop);
   }
 
-  Loop* GetDummyRootLoop() { return &dummy_top_loop_; }
-  const Loop* GetDummyRootLoop() const { return &dummy_top_loop_; }
+  Loop* GetPlaceholderRootLoop() { return &placeholder_top_loop_; }
+  const Loop* GetPlaceholderRootLoop() const { return &placeholder_top_loop_; }
 
  private:
   // TODO(dneto): This should be a vector of unique_ptr.  But VisualStudio 2013
@@ -558,8 +560,8 @@ class LoopDescriptor {
   // objects.
   LoopContainerType loops_;
 
-  // Dummy root: this "loop" is only there to help iterators creation.
-  Loop dummy_top_loop_;
+  // Placeholder root: this "loop" is only there to help iterators creation.
+  Loop placeholder_top_loop_;
 
   std::unordered_map<uint32_t, Loop*> basic_block_to_loop_;
 

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

@@ -1063,7 +1063,7 @@ LoopPeelingPass::LoopPeelingInfo::HandleInequality(CmpOperator cmp_op,
   }
 
   uint32_t cast_iteration = 0;
-  // sanity check: can we fit |iteration| in a uint32_t ?
+  // coherence check: can we fit |iteration| in a uint32_t ?
   if (static_cast<uint64_t>(iteration) < std::numeric_limits<uint32_t>::max()) {
     cast_iteration = static_cast<uint32_t>(iteration);
   }

+ 32 - 6
3rdparty/spirv-tools/source/opt/loop_unroller.cpp

@@ -286,6 +286,9 @@ class LoopUnrollerUtilsImpl {
   // to be the actual value of the phi at that point.
   void LinkLastPhisToStart(Loop* loop) const;
 
+  // Kill all debug declaration instructions from |bb|.
+  void KillDebugDeclares(BasicBlock* bb);
+
   // A pointer to the IRContext. Used to add/remove instructions and for usedef
   // chains.
   IRContext* context_;
@@ -598,6 +601,20 @@ void LoopUnrollerUtilsImpl::FullyUnroll(Loop* loop) {
       IRContext::Analysis::kAnalysisDefUse);
 }
 
+void LoopUnrollerUtilsImpl::KillDebugDeclares(BasicBlock* bb) {
+  // We cannot kill an instruction inside BasicBlock::ForEachInst()
+  // because it will generate dangling pointers. We use |to_be_killed|
+  // to kill them after the loop.
+  std::vector<Instruction*> to_be_killed;
+
+  bb->ForEachInst([&to_be_killed, this](Instruction* inst) {
+    if (context_->get_debug_info_mgr()->IsDebugDeclare(inst)) {
+      to_be_killed.push_back(inst);
+    }
+  });
+  for (auto* inst : to_be_killed) context_->KillInst(inst);
+}
+
 // Copy a given basic block, give it a new result_id, and store the new block
 // and the id mapping in the state. |preserve_instructions| is used to determine
 // whether or not this function should edit instructions other than the
@@ -608,6 +625,9 @@ void LoopUnrollerUtilsImpl::CopyBasicBlock(Loop* loop, const BasicBlock* itr,
   BasicBlock* basic_block = itr->Clone(context_);
   basic_block->SetParent(itr->GetParent());
 
+  // We do not want to duplicate DebugDeclare.
+  KillDebugDeclares(basic_block);
+
   // Assign each result a new unique ID and keep a mapping of the old ids to
   // the new ones.
   AssignNewResultIds(basic_block);
@@ -674,21 +694,21 @@ void LoopUnrollerUtilsImpl::CopyBody(Loop* loop, bool eliminate_conditions) {
   std::vector<Instruction*> inductions;
   loop->GetInductionVariables(inductions);
   for (size_t index = 0; index < inductions.size(); ++index) {
-    Instruction* master_copy = inductions[index];
+    Instruction* primary_copy = inductions[index];
 
-    assert(master_copy->result_id() != 0);
+    assert(primary_copy->result_id() != 0);
     Instruction* induction_clone =
-        state_.ids_to_new_inst[state_.new_inst[master_copy->result_id()]];
+        state_.ids_to_new_inst[state_.new_inst[primary_copy->result_id()]];
 
     state_.new_phis_.push_back(induction_clone);
     assert(induction_clone->result_id() != 0);
 
     if (!state_.previous_phis_.empty()) {
-      state_.new_inst[master_copy->result_id()] = GetPhiDefID(
+      state_.new_inst[primary_copy->result_id()] = GetPhiDefID(
           state_.previous_phis_[index], state_.previous_latch_block_->id());
     } else {
       // Do not replace the first phi block ids.
-      state_.new_inst[master_copy->result_id()] = master_copy->result_id();
+      state_.new_inst[primary_copy->result_id()] = primary_copy->result_id();
     }
   }
 
@@ -729,13 +749,19 @@ void LoopUnrollerUtilsImpl::FoldConditionBlock(BasicBlock* condition_block,
   Instruction& old_branch = *condition_block->tail();
   uint32_t new_target = old_branch.GetSingleWordOperand(operand_label);
 
+  DebugScope scope = old_branch.GetDebugScope();
+  const std::vector<Instruction> lines = old_branch.dbg_line_insts();
+
   context_->KillInst(&old_branch);
   // Add the new unconditional branch to the merge block.
   InstructionBuilder builder(
       context_, condition_block,
       IRContext::Analysis::kAnalysisDefUse |
           IRContext::Analysis::kAnalysisInstrToBlockMapping);
-  builder.AddBranch(new_target);
+  Instruction* new_branch = builder.AddBranch(new_target);
+
+  new_branch->set_dbg_line_insts(lines);
+  new_branch->SetDebugScope(scope);
 }
 
 void LoopUnrollerUtilsImpl::CloseUnrolledLoop(Loop* loop) {

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

@@ -594,9 +594,9 @@ bool LoopUnswitchPass::ProcessFunction(Function* f) {
   bool loop_changed = true;
   while (loop_changed) {
     loop_changed = false;
-    for (Loop& loop :
-         make_range(++TreeDFIterator<Loop>(loop_descriptor.GetDummyRootLoop()),
-                    TreeDFIterator<Loop>())) {
+    for (Loop& loop : make_range(
+             ++TreeDFIterator<Loop>(loop_descriptor.GetPlaceholderRootLoop()),
+             TreeDFIterator<Loop>())) {
       if (processed_loop.count(&loop)) continue;
       processed_loop.insert(&loop);
 

+ 7 - 6
3rdparty/spirv-tools/source/opt/merge_return_pass.cpp

@@ -111,7 +111,7 @@ bool MergeReturnPass::ProcessStructured(
   }
 
   RecordImmediateDominators(function);
-  AddDummySwitchAroundFunction();
+  AddSingleCaseSwitchAroundFunction();
 
   std::list<BasicBlock*> order;
   cfg()->ComputeStructuredOrder(function, &*function->begin(), &order);
@@ -223,7 +223,8 @@ void MergeReturnPass::ProcessStructuredBlock(BasicBlock* block) {
 
   if (tail_opcode == SpvOpReturn || tail_opcode == SpvOpReturnValue ||
       tail_opcode == SpvOpUnreachable) {
-    assert(CurrentState().InBreakable() && "Should be in the dummy construct.");
+    assert(CurrentState().InBreakable() &&
+           "Should be in the placeholder construct.");
     BranchToBlock(block, CurrentState().BreakMergeId());
     return_blocks_.insert(block->id());
   }
@@ -408,7 +409,7 @@ bool MergeReturnPass::PredicateBlocks(
     if (!predicated->insert(block).second) break;
     // Skip structured subgraphs.
     assert(state->InBreakable() &&
-           "Should be in the dummy construct at the very least.");
+           "Should be in the placeholder construct at the very least.");
     Instruction* break_merge_inst = state->BreakMergeInst();
     uint32_t merge_block_id = break_merge_inst->GetSingleWordInOperand(0);
     while (state->BreakMergeId() == merge_block_id) {
@@ -768,7 +769,7 @@ void MergeReturnPass::InsertAfterElement(BasicBlock* element,
   list->insert(pos, new_element);
 }
 
-void MergeReturnPass::AddDummySwitchAroundFunction() {
+void MergeReturnPass::AddSingleCaseSwitchAroundFunction() {
   CreateReturnBlock();
   CreateReturn(final_return_block_);
 
@@ -776,7 +777,7 @@ void MergeReturnPass::AddDummySwitchAroundFunction() {
     cfg()->RegisterBlock(final_return_block_);
   }
 
-  CreateDummySwitch(final_return_block_);
+  CreateSingleCaseSwitch(final_return_block_);
 }
 
 BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) {
@@ -811,7 +812,7 @@ BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) {
   return new_block;
 }
 
-void MergeReturnPass::CreateDummySwitch(BasicBlock* merge_target) {
+void MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) {
   // Insert the switch before any code is run.  We have to split the entry
   // block to make sure the OpVariable instructions remain in the entry block.
   BasicBlock* start_block = &*function_->begin();

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

@@ -48,13 +48,13 @@ namespace opt {
  * is the final return. This block should branch to the new return block (its
  * direct successor). If the current block is within structured control flow,
  * the branch destination should be the innermost construct's merge.  This
- * merge will always exist because a dummy switch is added around the
+ * merge will always exist because a single case switch is added around the
  * entire function. If the merge block produces any live values it will need to
  * be predicated. While the merge is nested in structured control flow, the
  * predication path should branch to the merge block of the inner-most loop
  * (or switch if no loop) it is contained in. Once structured control flow has
- * been exited, it will be at the merge of the dummy switch, which will simply
- * return.
+ * been exited, it will be at the merge of the single case switch, which will
+ * simply return.
  *
  * In the final return block, the return value should be loaded and returned.
  * Memory promotion passes should be able to promote the newly introduced
@@ -73,7 +73,7 @@ namespace opt {
  *         ||
  *         \/
  *
- *          0 (dummy switch header)
+ *          0 (single case switch header)
  *          |
  *          1 (loop header)
  *         / \
@@ -83,7 +83,7 @@ namespace opt {
  *        / \
  *        |  3 (original code in 3)
  *        \ /
- *   (ret) 4 (dummy switch merge)
+ *   (ret) 4 (single case switch merge)
  *
  * In the above (simple) example, the return originally in |2| is passed through
  * the loop merge. That merge is predicated such that the old body of the block
@@ -277,7 +277,7 @@ class MergeReturnPass : public MemPass {
   // current function where the switch and case value are both zero and the
   // default is the merge block. Returns after the switch is executed. Sets
   // |final_return_block_|.
-  void AddDummySwitchAroundFunction();
+  void AddSingleCaseSwitchAroundFunction();
 
   // Creates a new basic block that branches to |header_label_id|.  Returns the
   // new basic block.  The block will be the second last basic block in the
@@ -286,7 +286,7 @@ class MergeReturnPass : public MemPass {
 
   // Creates a one case switch around the executable code of the function with
   // |merge_target| as the merge node.
-  void CreateDummySwitch(BasicBlock* merge_target);
+  void CreateSingleCaseSwitch(BasicBlock* merge_target);
 
   // Returns true if |function| has an unreachable block that is not a continue
   // target that simply branches back to the header, or a merge block containing

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

@@ -579,8 +579,8 @@ bool Optimizer::Run(const uint32_t* original_binary,
 
 #ifndef NDEBUG
   // We do not keep the result id of DebugScope in struct DebugScope.
-  // Instead, we assign random ids for them, which results in sanity
-  // check failures. We want to skip the sanity check when the module
+  // Instead, we assign random ids for them, which results in coherence
+  // check failures. We want to skip the coherence check when the module
   // contains DebugScope instructions.
   if (status == opt::Pass::Status::SuccessWithoutChange &&
       !context->module()->ContainsDebugScope()) {

+ 16 - 5
3rdparty/spirv-tools/source/opt/private_to_local_pass.cpp

@@ -138,7 +138,7 @@ bool PrivateToLocalPass::MoveVariable(Instruction* variable,
   function->begin()->begin()->InsertBefore(move(var));
 
   // Update uses where the type may have changed.
-  return UpdateUses(variable->result_id());
+  return UpdateUses(variable);
 }
 
 uint32_t PrivateToLocalPass::GetNewType(uint32_t old_type_id) {
@@ -157,6 +157,10 @@ uint32_t PrivateToLocalPass::GetNewType(uint32_t old_type_id) {
 bool PrivateToLocalPass::IsValidUse(const Instruction* inst) const {
   // The cases in this switch have to match the cases in |UpdateUse|.
   // If we don't know how to update it, it is not valid.
+  if (inst->GetOpenCL100DebugOpcode() ==
+      OpenCLDebugInfo100DebugGlobalVariable) {
+    return true;
+  }
   switch (inst->opcode()) {
     case SpvOpLoad:
     case SpvOpStore:
@@ -175,10 +179,16 @@ bool PrivateToLocalPass::IsValidUse(const Instruction* inst) const {
   }
 }
 
-bool PrivateToLocalPass::UpdateUse(Instruction* inst) {
+bool PrivateToLocalPass::UpdateUse(Instruction* inst, Instruction* user) {
   // The cases in this switch have to match the cases in |IsValidUse|.  If we
   // don't think it is valid, the optimization will not view the variable as a
   // candidate, and therefore the use will not be updated.
+  if (inst->GetOpenCL100DebugOpcode() ==
+      OpenCLDebugInfo100DebugGlobalVariable) {
+    context()->get_debug_info_mgr()->ConvertDebugGlobalToLocalVariable(inst,
+                                                                       user);
+    return true;
+  }
   switch (inst->opcode()) {
     case SpvOpLoad:
     case SpvOpStore:
@@ -196,7 +206,7 @@ bool PrivateToLocalPass::UpdateUse(Instruction* inst) {
       context()->AnalyzeUses(inst);
 
       // Update uses where the type may have changed.
-      if (!UpdateUses(inst->result_id())) {
+      if (!UpdateUses(inst)) {
         return false;
       }
     } break;
@@ -211,13 +221,14 @@ bool PrivateToLocalPass::UpdateUse(Instruction* inst) {
   return true;
 }
 
-bool PrivateToLocalPass::UpdateUses(uint32_t id) {
+bool PrivateToLocalPass::UpdateUses(Instruction* inst) {
+  uint32_t id = inst->result_id();
   std::vector<Instruction*> uses;
   context()->get_def_use_mgr()->ForEachUser(
       id, [&uses](Instruction* use) { uses.push_back(use); });
 
   for (Instruction* use : uses) {
-    if (!UpdateUse(use)) {
+    if (!UpdateUse(use, inst)) {
       return false;
     }
   }

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

@@ -63,8 +63,8 @@ class PrivateToLocalPass : public Pass {
 
   // Updates |inst|, and any instruction dependent on |inst|, to reflect the
   // change of the base pointer now pointing to the function storage class.
-  bool UpdateUse(Instruction* inst);
-  bool UpdateUses(uint32_t id);
+  bool UpdateUse(Instruction* inst, Instruction* user);
+  bool UpdateUses(Instruction* inst);
 };
 
 }  // namespace opt

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

@@ -163,7 +163,7 @@ class ComputeRegisterLiveness {
 
   // Propagates the register liveness information of each loop iterators.
   void DoLoopLivenessUnification() {
-    for (const Loop* loop : *loop_desc_.GetDummyRootLoop()) {
+    for (const Loop* loop : *loop_desc_.GetPlaceholderRootLoop()) {
       DoLoopLivenessUnification(*loop);
     }
   }

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

@@ -25,6 +25,10 @@
 #include "source/opt/types.h"
 #include "source/util/make_unique.h"
 
+static const uint32_t kDebugDeclareOperandLocalVariableIndex = 4;
+static const uint32_t kDebugValueOperandValueIndex = 5;
+static const uint32_t kDebugValueOperandExpressionIndex = 6;
+
 namespace spvtools {
 namespace opt {
 
@@ -80,6 +84,20 @@ Pass::Status ScalarReplacementPass::ReplaceVariable(
   std::vector<Instruction*> dead;
   bool replaced_all_uses = get_def_use_mgr()->WhileEachUser(
       inst, [this, &replacements, &dead](Instruction* user) {
+        if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) {
+          if (ReplaceWholeDebugDeclare(user, replacements)) {
+            dead.push_back(user);
+            return true;
+          }
+          return false;
+        }
+        if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) {
+          if (ReplaceWholeDebugValue(user, replacements)) {
+            dead.push_back(user);
+            return true;
+          }
+          return false;
+        }
         if (!IsAnnotationInst(user->opcode())) {
           switch (user->opcode()) {
             case SpvOpLoad:
@@ -144,6 +162,58 @@ Pass::Status ScalarReplacementPass::ReplaceVariable(
   return Status::SuccessWithChange;
 }
 
+bool ScalarReplacementPass::ReplaceWholeDebugDeclare(
+    Instruction* dbg_decl, const std::vector<Instruction*>& replacements) {
+  // Insert Deref operation to the front of the operation list of |dbg_decl|.
+  Instruction* dbg_expr = context()->get_def_use_mgr()->GetDef(
+      dbg_decl->GetSingleWordOperand(kDebugValueOperandExpressionIndex));
+  auto* deref_expr =
+      context()->get_debug_info_mgr()->DerefDebugExpression(dbg_expr);
+
+  // Add DebugValue instruction with Indexes operand and Deref operation.
+  int32_t idx = 0;
+  for (const auto* var : replacements) {
+    uint32_t dbg_local_variable =
+        dbg_decl->GetSingleWordOperand(kDebugDeclareOperandLocalVariableIndex);
+    uint32_t index_id = context()->get_constant_mgr()->GetSIntConst(idx);
+
+    Instruction* added_dbg_value =
+        context()->get_debug_info_mgr()->AddDebugValueWithIndex(
+            dbg_local_variable,
+            /*value_id=*/var->result_id(), /*expr_id=*/deref_expr->result_id(),
+            index_id, /*insert_before=*/var->NextNode());
+    if (added_dbg_value == nullptr) return false;
+    added_dbg_value->UpdateDebugInfoFrom(dbg_decl);
+    ++idx;
+  }
+  return true;
+}
+
+bool ScalarReplacementPass::ReplaceWholeDebugValue(
+    Instruction* dbg_value, const std::vector<Instruction*>& replacements) {
+  int32_t idx = 0;
+  BasicBlock* block = context()->get_instr_block(dbg_value);
+  for (auto var : replacements) {
+    // Clone the DebugValue.
+    std::unique_ptr<Instruction> new_dbg_value(dbg_value->Clone(context()));
+    uint32_t new_id = TakeNextId();
+    if (new_id == 0) return false;
+    new_dbg_value->SetResultId(new_id);
+    // Update 'Value' operand to the |replacements|.
+    new_dbg_value->SetOperand(kDebugValueOperandValueIndex, {var->result_id()});
+    // Append 'Indexes' operand.
+    new_dbg_value->AddOperand(
+        {SPV_OPERAND_TYPE_ID,
+         {context()->get_constant_mgr()->GetSIntConst(idx)}});
+    // Insert the new DebugValue to the basic block.
+    auto* added_instr = dbg_value->InsertBefore(std::move(new_dbg_value));
+    get_def_use_mgr()->AnalyzeInstDefUse(added_instr);
+    context()->set_instr_block(added_instr, block);
+    ++idx;
+  }
+  return true;
+}
+
 bool ScalarReplacementPass::ReplaceWholeLoad(
     Instruction* load, const std::vector<Instruction*>& replacements) {
   // Replaces the load of the entire composite with a load from each replacement
@@ -177,6 +247,7 @@ bool ScalarReplacementPass::ReplaceWholeLoad(
     where = where.InsertBefore(std::move(newLoad));
     get_def_use_mgr()->AnalyzeInstDefUse(&*where);
     context()->set_instr_block(&*where, block);
+    where->UpdateDebugInfoFrom(load);
     loads.push_back(&*where);
   }
 
@@ -195,6 +266,7 @@ bool ScalarReplacementPass::ReplaceWholeLoad(
   }
   where = where.InsertBefore(std::move(compositeConstruct));
   get_def_use_mgr()->AnalyzeInstDefUse(&*where);
+  where->UpdateDebugInfoFrom(load);
   context()->set_instr_block(&*where, block);
   context()->ReplaceAllUsesWith(load->result_id(), compositeId);
   return true;
@@ -226,6 +298,7 @@ bool ScalarReplacementPass::ReplaceWholeStore(
             {SPV_OPERAND_TYPE_ID, {storeInput}},
             {SPV_OPERAND_TYPE_LITERAL_INTEGER, {elementIndex++}}}));
     auto iter = where.InsertBefore(std::move(extract));
+    iter->UpdateDebugInfoFrom(store);
     get_def_use_mgr()->AnalyzeInstDefUse(&*iter);
     context()->set_instr_block(&*iter, block);
 
@@ -242,6 +315,7 @@ bool ScalarReplacementPass::ReplaceWholeStore(
       newStore->AddOperand(std::move(copy));
     }
     iter = where.InsertBefore(std::move(newStore));
+    iter->UpdateDebugInfoFrom(store);
     get_def_use_mgr()->AnalyzeInstDefUse(&*iter);
     context()->set_instr_block(&*iter, block);
   }
@@ -281,6 +355,7 @@ bool ScalarReplacementPass::ReplaceAccessChain(
         Operand copy(chain->GetInOperand(i));
         replacementChain->AddOperand(std::move(copy));
       }
+      replacementChain->UpdateDebugInfoFrom(chain);
       auto iter = chainIter.InsertBefore(std::move(replacementChain));
       get_def_use_mgr()->AnalyzeInstDefUse(&*iter);
       context()->set_instr_block(&*iter, context()->get_instr_block(chain));
@@ -427,6 +502,9 @@ void ScalarReplacementPass::CreateVariable(
     }
   }
 
+  // Update the OpenCL.DebugInfo.100 debug information.
+  inst->UpdateDebugInfoFrom(varInst);
+
   replacements->push_back(inst);
 }
 
@@ -711,6 +789,14 @@ bool ScalarReplacementPass::CheckUses(const Instruction* inst,
   get_def_use_mgr()->ForEachUse(inst, [this, max_legal_index, stats, &ok](
                                           const Instruction* user,
                                           uint32_t index) {
+    if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare ||
+        user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) {
+      // TODO: include num_partial_accesses if it uses Fragment operation or
+      // DebugValue has Indexes operand.
+      stats->num_full_accesses++;
+      return;
+    }
+
     // Annotations are check as a group separately.
     if (!IsAnnotationInst(user->opcode())) {
       switch (user->opcode()) {

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

@@ -199,6 +199,21 @@ class ScalarReplacementPass : public Pass {
   bool ReplaceWholeStore(Instruction* store,
                          const std::vector<Instruction*>& replacements);
 
+  // Replaces the DebugDeclare to the entire composite.
+  //
+  // Generates a DebugValue with Deref operation for each element in the
+  // scalarized variable from the original DebugDeclare.  Returns true if
+  // successful.
+  bool ReplaceWholeDebugDeclare(Instruction* dbg_decl,
+                                const std::vector<Instruction*>& replacements);
+
+  // Replaces the DebugValue to the entire composite.
+  //
+  // Generates a DebugValue for each element in the scalarized variable from
+  // the original DebugValue.  Returns true if successful.
+  bool ReplaceWholeDebugValue(Instruction* dbg_value,
+                              const std::vector<Instruction*>& replacements);
+
   // Replaces an access chain to the composite variable with either a direct use
   // of the appropriate replacement variable or another access chain with the
   // replacement variable as the base and one fewer indexes. Returns true if

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

@@ -307,8 +307,8 @@ void SSARewriter::ProcessStore(Instruction* inst, BasicBlock* bb) {
   }
   if (pass_->IsTargetVar(var_id)) {
     WriteVariable(var_id, bb, val_id);
-    pass_->context()->get_debug_info_mgr()->AddDebugValue(inst, var_id, val_id,
-                                                          inst);
+    pass_->context()->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(
+        inst, var_id, val_id, inst);
 
 #if SSA_REWRITE_DEBUGGING_LEVEL > 1
     std::cerr << "\tFound store '%" << var_id << " = %" << val_id << "': "
@@ -491,7 +491,7 @@ bool SSARewriter::ApplyReplacements() {
 
     // Add DebugValue for the new OpPhi instruction.
     insert_it->SetDebugScope(local_var->GetDebugScope());
-    pass_->context()->get_debug_info_mgr()->AddDebugValue(
+    pass_->context()->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(
         &*insert_it, phi_candidate->var_id(), phi_candidate->result_id(),
         &*insert_it);
 
@@ -615,8 +615,6 @@ Pass::Status SSARewriter::RewriteFunctionIntoSSA(Function* fp) {
             << fp->PrettyPrint(0) << "\n";
 #endif
 
-  if (modified) pass_->context()->KillDebugDeclareInsts(fp);
-
   return modified ? Pass::Status::SuccessWithChange
                   : Pass::Status::SuccessWithoutChange;
 }
@@ -626,6 +624,12 @@ Pass::Status SSARewritePass::Process() {
   for (auto& fn : *get_module()) {
     status =
         CombineStatus(status, SSARewriter(this).RewriteFunctionIntoSSA(&fn));
+    if (status == Status::SuccessWithChange) {
+      // Kill DebugDeclares for target variables.
+      for (auto var_id : seen_target_vars_) {
+        context()->get_debug_info_mgr()->KillDebugDeclares(var_id);
+      }
+    }
     if (status == Status::Failure) {
       break;
     }

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

@@ -71,7 +71,7 @@ bool WrapOpKill::ReplaceWithFunctionCall(Instruction* inst) {
   if (call_inst == nullptr) {
     return false;
   }
-  call_inst->UpdateDebugInfo(inst);
+  call_inst->UpdateDebugInfoFrom(inst);
 
   Instruction* return_inst = nullptr;
   uint32_t return_type_id = GetOwningFunctionsReturnType(inst);

+ 2 - 2
3rdparty/spirv-tools/source/reduce/CMakeLists.txt

@@ -77,7 +77,7 @@ endif()
 
 spvtools_pch(SPIRV_TOOLS_REDUCE_SOURCES pch_source_reduce)
 
-add_library(SPIRV-Tools-reduce ${SPIRV_TOOLS_REDUCE_SOURCES})
+add_library(SPIRV-Tools-reduce STATIC ${SPIRV_TOOLS_REDUCE_SOURCES})
 
 spvtools_default_compile_options(SPIRV-Tools-reduce)
 target_include_directories(SPIRV-Tools-reduce
@@ -89,7 +89,7 @@ target_include_directories(SPIRV-Tools-reduce
 )
 # The reducer reuses a lot of functionality from the SPIRV-Tools library.
 target_link_libraries(SPIRV-Tools-reduce
-  PUBLIC ${SPIRV_TOOLS}
+  PUBLIC ${SPIRV_TOOLS}-static
   PUBLIC SPIRV-Tools-opt)
 
 set_property(TARGET SPIRV-Tools-reduce PROPERTY FOLDER "SPIRV-Tools libraries")

+ 2 - 1
3rdparty/spirv-tools/source/reduce/operand_to_dominating_id_reduction_opportunity_finder.cpp

@@ -91,7 +91,8 @@ void OperandToDominatingIdReductionOpportunityFinder::
             // constant.  It is thus not relevant to this pass.
             continue;
           }
-          // Sanity check that we don't get here if the argument is a constant.
+          // Coherence check: we should not get here if the argument is a
+          // constant.
           assert(!context->get_constant_mgr()->GetConstantFromInst(def));
           if (def->type_id() != candidate_dominator->type_id()) {
             // The types need to match.

+ 2 - 2
3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.h

@@ -72,8 +72,8 @@ class StructuredLoopToSelectionReductionOpportunity
   void ChangeLoopToSelection();
 
   // Fixes any scenarios where, due to CFG changes, ids have uses not dominated
-  // by their definitions, by changing such uses to uses of OpUndef or of dummy
-  // variables.
+  // by their definitions, by changing such uses to uses of OpUndef or of
+  // placeholder variables.
   void FixNonDominatedIdUses();
 
   // Returns true if and only if at least one of the following holds:

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

@@ -14,8 +14,6 @@
 
 // Validates OpCapability instruction.
 
-#include "source/val/validate.h"
-
 #include <cassert>
 #include <string>
 #include <unordered_set>
@@ -23,6 +21,7 @@
 #include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
@@ -166,7 +165,6 @@ bool IsSupportGuaranteedOpenCL_1_2(uint32_t capability, bool embedded_profile) {
   switch (capability) {
     case SpvCapabilityAddresses:
     case SpvCapabilityFloat16Buffer:
-    case SpvCapabilityGroups:
     case SpvCapabilityInt16:
     case SpvCapabilityInt8:
     case SpvCapabilityKernel:
@@ -175,8 +173,6 @@ bool IsSupportGuaranteedOpenCL_1_2(uint32_t capability, bool embedded_profile) {
       return true;
     case SpvCapabilityInt64:
       return !embedded_profile;
-    case SpvCapabilityPipes:
-      return embedded_profile;
   }
   return false;
 }
@@ -187,6 +183,7 @@ bool IsSupportGuaranteedOpenCL_2_0(uint32_t capability, bool embedded_profile) {
   switch (capability) {
     case SpvCapabilityDeviceEnqueue:
     case SpvCapabilityGenericPointer:
+    case SpvCapabilityGroups:
     case SpvCapabilityPipes:
       return true;
   }

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

@@ -13,11 +13,13 @@
 // limitations under the License.
 
 // Validates correctness of extension SPIR-V instructions.
-
+#include <cstdlib>
 #include <sstream>
 #include <string>
 #include <vector>
 
+#include "spirv/unified1/NonSemanticClspvReflection.h"
+
 #include "OpenCLDebugInfo100.h"
 #include "source/diagnostic.h"
 #include "source/enum_string_mapping.h"
@@ -180,6 +182,460 @@ spv_result_t ValidateOperandDebugType(
          << " is not a valid debug type";
 }
 
+bool IsUint32Constant(ValidationState_t& _, uint32_t id) {
+  auto inst = _.FindDef(id);
+  if (!inst || inst->opcode() != SpvOpConstant) {
+    return false;
+  }
+
+  auto type = _.FindDef(inst->type_id());
+  if (!type || type->opcode() != SpvOpTypeInt) {
+    return false;
+  }
+
+  if (type->GetOperandAs<uint32_t>(1) != 32) {
+    return false;
+  }
+
+  if (type->GetOperandAs<uint32_t>(2) != 0) {
+    return false;
+  }
+
+  return true;
+}
+
+spv_result_t ValidateClspvReflectionKernel(ValidationState_t& _,
+                                           const Instruction* inst) {
+  const auto kernel_id = inst->GetOperandAs<uint32_t>(4);
+  const auto kernel = _.FindDef(kernel_id);
+  if (kernel->opcode() != SpvOpFunction) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Kernel does not reference a function";
+  }
+
+  bool found_kernel = false;
+  for (auto entry_point : _.entry_points()) {
+    if (entry_point == kernel_id) {
+      found_kernel = true;
+      break;
+    }
+  }
+  if (!found_kernel) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Kernel does not reference an entry-point";
+  }
+
+  const auto* exec_models = _.GetExecutionModels(kernel_id);
+  if (!exec_models || exec_models->empty()) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Kernel does not reference an entry-point";
+  }
+  for (auto exec_model : *exec_models) {
+    if (exec_model != SpvExecutionModelGLCompute) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "Kernel must refer only to GLCompute entry-points";
+    }
+  }
+
+  auto name = _.FindDef(inst->GetOperandAs<uint32_t>(5));
+  if (!name || name->opcode() != SpvOpString) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst) << "Name must be an OpString";
+  }
+
+  const std::string name_str = reinterpret_cast<const char*>(
+      name->words().data() + name->operands()[1].offset);
+  bool found = false;
+  for (auto& desc : _.entry_point_descriptions(kernel_id)) {
+    if (name_str == desc.name) {
+      found = true;
+      break;
+    }
+  }
+  if (!found) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Name must match an entry-point for Kernel";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateClspvReflectionArgumentInfo(ValidationState_t& _,
+                                                 const Instruction* inst) {
+  const auto num_operands = inst->operands().size();
+  if (_.GetIdOpcode(inst->GetOperandAs<uint32_t>(4)) != SpvOpString) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst) << "Name must be an OpString";
+  }
+  if (num_operands > 5) {
+    if (_.GetIdOpcode(inst->GetOperandAs<uint32_t>(5)) != SpvOpString) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "TypeName must be an OpString";
+    }
+  }
+  if (num_operands > 6) {
+    if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "AddressQualifier must be a 32-bit unsigned integer "
+                "OpConstant";
+    }
+  }
+  if (num_operands > 7) {
+    if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "AccessQualifier must be a 32-bit unsigned integer "
+                "OpConstant";
+    }
+  }
+  if (num_operands > 8) {
+    if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(8))) {
+      return _.diag(SPV_ERROR_INVALID_ID, inst)
+             << "TypeQualifier must be a 32-bit unsigned integer "
+                "OpConstant";
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateKernelDecl(ValidationState_t& _, const Instruction* inst) {
+  const auto decl_id = inst->GetOperandAs<uint32_t>(4);
+  const auto decl = _.FindDef(decl_id);
+  if (!decl || decl->opcode() != SpvOpExtInst) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Kernel must be a Kernel extended instruction";
+  }
+
+  if (decl->GetOperandAs<uint32_t>(2) != inst->GetOperandAs<uint32_t>(2)) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Kernel must be from the same extended instruction import";
+  }
+
+  const auto ext_inst =
+      decl->GetOperandAs<NonSemanticClspvReflectionInstructions>(3);
+  if (ext_inst != NonSemanticClspvReflectionKernel) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Kernel must be a Kernel extended instruction";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateArgInfo(ValidationState_t& _, const Instruction* inst,
+                             uint32_t info_index) {
+  auto info = _.FindDef(inst->GetOperandAs<uint32_t>(info_index));
+  if (!info || info->opcode() != SpvOpExtInst) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "ArgInfo must be an ArgumentInfo extended instruction";
+  }
+
+  if (info->GetOperandAs<uint32_t>(2) != inst->GetOperandAs<uint32_t>(2)) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "ArgInfo must be from the same extended instruction import";
+  }
+
+  auto ext_inst = info->GetOperandAs<NonSemanticClspvReflectionInstructions>(3);
+  if (ext_inst != NonSemanticClspvReflectionArgumentInfo) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "ArgInfo must be an ArgumentInfo extended instruction";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateClspvReflectionArgumentBuffer(ValidationState_t& _,
+                                                   const Instruction* inst) {
+  const auto num_operands = inst->operands().size();
+  if (auto error = ValidateKernelDecl(_, inst)) {
+    return error;
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Ordinal must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "DescriptorSet must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Binding must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (num_operands == 9) {
+    if (auto error = ValidateArgInfo(_, inst, 8)) {
+      return error;
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateClspvReflectionArgumentPodBuffer(ValidationState_t& _,
+                                                      const Instruction* inst) {
+  const auto num_operands = inst->operands().size();
+  if (auto error = ValidateKernelDecl(_, inst)) {
+    return error;
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Ordinal must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "DescriptorSet must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Binding must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(8))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Offset must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(9))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Size must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (num_operands == 11) {
+    if (auto error = ValidateArgInfo(_, inst, 10)) {
+      return error;
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateClspvReflectionArgumentPodPushConstant(
+    ValidationState_t& _, const Instruction* inst) {
+  const auto num_operands = inst->operands().size();
+  if (auto error = ValidateKernelDecl(_, inst)) {
+    return error;
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Ordinal must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Offset must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Size must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (num_operands == 9) {
+    if (auto error = ValidateArgInfo(_, inst, 8)) {
+      return error;
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateClspvReflectionArgumentWorkgroup(ValidationState_t& _,
+                                                      const Instruction* inst) {
+  const auto num_operands = inst->operands().size();
+  if (auto error = ValidateKernelDecl(_, inst)) {
+    return error;
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Ordinal must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "SpecId must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "ElemSize must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (num_operands == 9) {
+    if (auto error = ValidateArgInfo(_, inst, 8)) {
+      return error;
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateClspvReflectionSpecConstantTriple(
+    ValidationState_t& _, const Instruction* inst) {
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "X must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Y must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Z must be a 32-bit unsigned integer OpConstant";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateClspvReflectionSpecConstantWorkDim(
+    ValidationState_t& _, const Instruction* inst) {
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Dim must be a 32-bit unsigned integer OpConstant";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateClspvReflectionPushConstant(ValidationState_t& _,
+                                                 const Instruction* inst) {
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Offset must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Size must be a 32-bit unsigned integer OpConstant";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateClspvReflectionConstantData(ValidationState_t& _,
+                                                 const Instruction* inst) {
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "DescriptorSet must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Binding must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (_.GetIdOpcode(inst->GetOperandAs<uint32_t>(6)) != SpvOpString) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst) << "Data must be an OpString";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateClspvReflectionSampler(ValidationState_t& _,
+                                            const Instruction* inst) {
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "DescriptorSet must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Binding must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Mask must be a 32-bit unsigned integer OpConstant";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateClspvReflectionPropertyRequiredWorkgroupSize(
+    ValidationState_t& _, const Instruction* inst) {
+  if (auto error = ValidateKernelDecl(_, inst)) {
+    return error;
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "X must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Y must be a 32-bit unsigned integer OpConstant";
+  }
+
+  if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Z must be a 32-bit unsigned integer OpConstant";
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateClspvReflectionInstruction(ValidationState_t& _,
+                                                const Instruction* inst,
+                                                uint32_t /*version*/) {
+  if (!_.IsVoidType(inst->type_id())) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "Return Type must be OpTypeVoid";
+  }
+
+  auto ext_inst = inst->GetOperandAs<NonSemanticClspvReflectionInstructions>(3);
+  switch (ext_inst) {
+    case NonSemanticClspvReflectionKernel:
+      return ValidateClspvReflectionKernel(_, inst);
+    case NonSemanticClspvReflectionArgumentInfo:
+      return ValidateClspvReflectionArgumentInfo(_, inst);
+    case NonSemanticClspvReflectionArgumentStorageBuffer:
+    case NonSemanticClspvReflectionArgumentUniform:
+    case NonSemanticClspvReflectionArgumentSampledImage:
+    case NonSemanticClspvReflectionArgumentStorageImage:
+    case NonSemanticClspvReflectionArgumentSampler:
+      return ValidateClspvReflectionArgumentBuffer(_, inst);
+    case NonSemanticClspvReflectionArgumentPodStorageBuffer:
+    case NonSemanticClspvReflectionArgumentPodUniform:
+      return ValidateClspvReflectionArgumentPodBuffer(_, inst);
+    case NonSemanticClspvReflectionArgumentPodPushConstant:
+      return ValidateClspvReflectionArgumentPodPushConstant(_, inst);
+    case NonSemanticClspvReflectionArgumentWorkgroup:
+      return ValidateClspvReflectionArgumentWorkgroup(_, inst);
+    case NonSemanticClspvReflectionSpecConstantWorkgroupSize:
+    case NonSemanticClspvReflectionSpecConstantGlobalOffset:
+      return ValidateClspvReflectionSpecConstantTriple(_, inst);
+    case NonSemanticClspvReflectionSpecConstantWorkDim:
+      return ValidateClspvReflectionSpecConstantWorkDim(_, inst);
+    case NonSemanticClspvReflectionPushConstantGlobalOffset:
+    case NonSemanticClspvReflectionPushConstantEnqueuedLocalSize:
+    case NonSemanticClspvReflectionPushConstantGlobalSize:
+    case NonSemanticClspvReflectionPushConstantRegionOffset:
+    case NonSemanticClspvReflectionPushConstantNumWorkgroups:
+    case NonSemanticClspvReflectionPushConstantRegionGroupOffset:
+      return ValidateClspvReflectionPushConstant(_, inst);
+    case NonSemanticClspvReflectionConstantDataStorageBuffer:
+    case NonSemanticClspvReflectionConstantDataUniform:
+      return ValidateClspvReflectionConstantData(_, inst);
+    case NonSemanticClspvReflectionLiteralSampler:
+      return ValidateClspvReflectionSampler(_, inst);
+    case NonSemanticClspvReflectionPropertyRequiredWorkgroupSize:
+      return ValidateClspvReflectionPropertyRequiredWorkgroupSize(_, inst);
+    default:
+      break;
+  }
+
+  return SPV_SUCCESS;
+}
+
 }  // anonymous namespace
 
 spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) {
@@ -2441,10 +2897,10 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
         CHECK_DEBUG_OPERAND("Local Variable",
                             OpenCLDebugInfo100DebugLocalVariable, 5);
 
-        // TODO: We must discuss DebugDeclare.Variable of OpenCL.100.DebugInfo.
-        // Currently, it says "Variable must be an id of OpVariable instruction
-        // which defines the local variable.", but we want to allow
-        // OpFunctionParameter as well.
+        // TODO: We must discuss DebugDeclare.Variable of
+        // OpenCL.100.DebugInfo. Currently, it says "Variable must be an id of
+        // OpVariable instruction which defines the local variable.", but we
+        // want to allow OpFunctionParameter as well.
         auto* operand = _.FindDef(inst->word(6));
         if (operand->opcode() != SpvOpVariable &&
             operand->opcode() != SpvOpFunctionParameter) {
@@ -2484,6 +2940,30 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
         assert(0);
         break;
     }
+  } else if (ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) {
+    auto import_inst = _.FindDef(inst->GetOperandAs<uint32_t>(2));
+    const std::string name(reinterpret_cast<const char*>(
+        import_inst->words().data() + import_inst->operands()[1].offset));
+    const std::string reflection = "NonSemantic.ClspvReflection.";
+    char* end_ptr;
+    auto version_string = name.substr(reflection.size());
+    if (version_string.empty()) {
+      return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
+             << "Missing NonSemantic.ClspvReflection import version";
+    }
+    uint32_t version = static_cast<uint32_t>(
+        std::strtoul(version_string.c_str(), &end_ptr, 10));
+    if (end_ptr && *end_ptr != '\0') {
+      return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
+             << "NonSemantic.ClspvReflection import does not encode the "
+                "version correctly";
+    }
+    if (version == 0 || version > NonSemanticClspvReflectionRevision) {
+      return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
+             << "Unknown NonSemantic.ClspvReflection import version";
+    }
+
+    return ValidateClspvReflectionInstruction(_, inst, version);
   }
 
   return SPV_SUCCESS;

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

@@ -33,7 +33,7 @@ namespace {
 // Performs compile time check that all SpvImageOperandsXXX cases are handled in
 // this module. If SpvImageOperandsXXX list changes, this function will fail the
 // build.
-// For all other purposes this is a dummy function.
+// For all other purposes this is a placeholder function.
 bool CheckAllImageOperandsHandled() {
   SpvImageOperandsMask enum_val = SpvImageOperandsBiasMask;