Browse Source

Updated spirv-tools.

Бранимир Караџић 5 years ago
parent
commit
71c7bf961d
100 changed files with 6462 additions and 556 deletions
  1. 1 0
      3rdparty/spirv-tools/include/generated/OpenCLDebugInfo100.h
  2. 1 1
      3rdparty/spirv-tools/include/generated/build-version.inc
  3. 1 0
      3rdparty/spirv-tools/include/generated/opencl.debuginfo.100.insts.inc
  4. 3 0
      3rdparty/spirv-tools/include/spirv-tools/instrument.hpp
  5. 4 0
      3rdparty/spirv-tools/include/spirv-tools/libspirv.h
  6. 12 0
      3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp
  7. 2 4
      3rdparty/spirv-tools/source/CMakeLists.txt
  8. 15 0
      3rdparty/spirv-tools/source/enum_set.h
  9. 13 0
      3rdparty/spirv-tools/source/extinst.opencl.debuginfo.100.grammar.json
  10. 40 0
      3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
  11. 81 0
      3rdparty/spirv-tools/source/fuzz/call_graph.cpp
  12. 62 0
      3rdparty/spirv-tools/source/fuzz/call_graph.h
  13. 24 13
      3rdparty/spirv-tools/source/fuzz/equivalence_relation.h
  14. 433 89
      3rdparty/spirv-tools/source/fuzz/fact_manager.cpp
  15. 24 14
      3rdparty/spirv-tools/source/fuzz/fact_manager.h
  16. 40 0
      3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
  17. 35 4
      3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp
  18. 84 3
      3rdparty/spirv-tools/source/fuzz/fuzzer_context.h
  19. 234 19
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp
  20. 97 12
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h
  21. 169 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp
  22. 41 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.h
  23. 35 8
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
  24. 37 6
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp
  25. 238 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
  26. 67 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.h
  27. 247 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.cpp
  28. 58 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.h
  29. 75 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.cpp
  30. 40 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.h
  31. 95 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_loads.cpp
  32. 38 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_loads.h
  33. 79 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.cpp
  34. 40 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.h
  35. 1 6
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
  36. 128 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_stores.cpp
  37. 40 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_stores.h
  38. 1 4
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
  39. 1 5
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
  40. 1 6
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
  41. 1 5
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
  42. 42 39
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
  43. 2 2
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp
  44. 7 11
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp
  45. 90 99
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp
  46. 22 7
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
  47. 81 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp
  48. 45 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.h
  49. 50 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp
  50. 41 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.h
  51. 54 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp
  52. 40 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h
  53. 179 33
      3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
  54. 72 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
  55. 226 26
      3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto
  56. 34 0
      3rdparty/spirv-tools/source/fuzz/transformation.cpp
  57. 215 0
      3rdparty/spirv-tools/source/fuzz/transformation_access_chain.cpp
  58. 80 0
      3rdparty/spirv-tools/source/fuzz/transformation_access_chain.h
  59. 1 1
      3rdparty/spirv-tools/source/fuzz/transformation_add_dead_continue.cpp
  60. 19 9
      3rdparty/spirv-tools/source/fuzz/transformation_add_function.cpp
  61. 17 19
      3rdparty/spirv-tools/source/fuzz/transformation_add_global_variable.cpp
  62. 4 1
      3rdparty/spirv-tools/source/fuzz/transformation_add_global_variable.h
  63. 98 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_local_variable.cpp
  64. 60 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_local_variable.h
  65. 1 1
      3rdparty/spirv-tools/source/fuzz/transformation_add_type_function.h
  66. 2 21
      3rdparty/spirv-tools/source/fuzz/transformation_composite_construct.cpp
  67. 8 14
      3rdparty/spirv-tools/source/fuzz/transformation_copy_object.cpp
  68. 4 1
      3rdparty/spirv-tools/source/fuzz/transformation_copy_object.h
  69. 186 0
      3rdparty/spirv-tools/source/fuzz/transformation_equation_instruction.cpp
  70. 76 0
      3rdparty/spirv-tools/source/fuzz/transformation_equation_instruction.h
  71. 185 0
      3rdparty/spirv-tools/source/fuzz/transformation_function_call.cpp
  72. 65 0
      3rdparty/spirv-tools/source/fuzz/transformation_function_call.h
  73. 103 0
      3rdparty/spirv-tools/source/fuzz/transformation_load.cpp
  74. 59 0
      3rdparty/spirv-tools/source/fuzz/transformation_load.h
  75. 3 3
      3rdparty/spirv-tools/source/fuzz/transformation_outline_function.cpp
  76. 184 0
      3rdparty/spirv-tools/source/fuzz/transformation_permute_function_parameters.cpp
  77. 61 0
      3rdparty/spirv-tools/source/fuzz/transformation_permute_function_parameters.h
  78. 16 5
      3rdparty/spirv-tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp
  79. 6 0
      3rdparty/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.cpp
  80. 3 27
      3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp
  81. 0 8
      3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.h
  82. 128 0
      3rdparty/spirv-tools/source/fuzz/transformation_store.cpp
  83. 63 0
      3rdparty/spirv-tools/source/fuzz/transformation_store.h
  84. 66 0
      3rdparty/spirv-tools/source/fuzz/transformation_swap_commutable_operands.cpp
  85. 51 0
      3rdparty/spirv-tools/source/fuzz/transformation_swap_commutable_operands.h
  86. 83 0
      3rdparty/spirv-tools/source/fuzz/transformation_toggle_access_chain_instruction.cpp
  87. 51 0
      3rdparty/spirv-tools/source/fuzz/transformation_toggle_access_chain_instruction.h
  88. 40 3
      3rdparty/spirv-tools/source/opcode.cpp
  89. 4 3
      3rdparty/spirv-tools/source/opcode.h
  90. 2 0
      3rdparty/spirv-tools/source/opt/CMakeLists.txt
  91. 10 0
      3rdparty/spirv-tools/source/opt/feature_manager.cpp
  92. 6 0
      3rdparty/spirv-tools/source/opt/feature_manager.h
  93. 266 0
      3rdparty/spirv-tools/source/opt/inst_debug_printf_pass.cpp
  94. 96 0
      3rdparty/spirv-tools/source/opt/inst_debug_printf_pass.h
  95. 39 14
      3rdparty/spirv-tools/source/opt/instrument_pass.cpp
  96. 16 6
      3rdparty/spirv-tools/source/opt/instrument_pass.h
  97. 8 0
      3rdparty/spirv-tools/source/opt/optimizer.cpp
  98. 1 0
      3rdparty/spirv-tools/source/opt/passes.h
  99. 2 2
      3rdparty/spirv-tools/source/val/validate.cpp
  100. 451 2
      3rdparty/spirv-tools/source/val/validate_extensions.cpp

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

@@ -67,6 +67,7 @@ enum OpenCLDebugInfo100Instructions {
     OpenCLDebugInfo100DebugExpression = 31,
     OpenCLDebugInfo100DebugMacroDef = 32,
     OpenCLDebugInfo100DebugMacroUndef = 33,
+    OpenCLDebugInfo100DebugImportedEntity = 34,
     OpenCLDebugInfo100DebugSource = 35,
     OpenCLDebugInfo100InstructionsMax = 0x7ffffff
 };

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

@@ -1 +1 @@
-"v2020.2-dev", "SPIRV-Tools v2020.2-dev b53f48f92c539b6f8561e36023bbf281d8dfeda8"
+"v2020.2-dev", "SPIRV-Tools v2020.2-dev 1584e578d04d193f9a242f79666f73f8cba06f10"

+ 1 - 0
3rdparty/spirv-tools/include/generated/opencl.debuginfo.100.insts.inc

@@ -35,5 +35,6 @@ static const spv_ext_inst_desc_t opencl_debuginfo_100_entries[] = {
   {"DebugExpression", 31, 0, nullptr, {SPV_OPERAND_TYPE_VARIABLE_ID, SPV_OPERAND_TYPE_NONE}},
   {"DebugMacroDef", 32, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
   {"DebugMacroUndef", 33, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
+  {"DebugImportedEntity", 34, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_DEBUG_IMPORTED_ENTITY, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
   {"DebugSource", 35, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}}
 };

+ 3 - 0
3rdparty/spirv-tools/include/spirv-tools/instrument.hpp

@@ -208,6 +208,9 @@ static const int kDebugInputBindingBindless = 1;
 // The binding for the input buffer read by InstBuffAddrCheckPass.
 static const int kDebugInputBindingBuffAddr = 2;
 
+// This is the output buffer written by InstDebugPrintfPass.
+static const int kDebugOutputPrintfStream = 3;
+
 // Bindless Validation Input Buffer Format
 //
 // An input buffer for bindless validation consists of a single array of

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

@@ -232,6 +232,7 @@ typedef enum spv_operand_type_t {
   SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE,                // DebugInfo Sec 3.4
   SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER,                // DebugInfo Sec 3.5
   SPV_OPERAND_TYPE_DEBUG_OPERATION,                     // DebugInfo Sec 3.6
+  SPV_OPERAND_TYPE_DEBUG_IMPORTED_ENTITY,
 
   // The following are concrete enum types from the OpenCL.DebugInfo.100
   // extended instruction set.
@@ -766,6 +767,9 @@ SPIRV_TOOLS_EXPORT void spvDiagnosticDestroy(spv_diagnostic diagnostic);
 SPIRV_TOOLS_EXPORT spv_result_t
 spvDiagnosticPrint(const spv_diagnostic diagnostic);
 
+// Gets the name of an instruction, without the "Op" prefix.
+SPIRV_TOOLS_EXPORT const char* spvOpcodeString(const uint32_t opcode);
+
 // The binary parser interface.
 
 // A pointer to a function that accepts a parsed SPIR-V header.

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

@@ -791,6 +791,18 @@ Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
                                                  uint32_t shader_id,
                                                  uint32_t version = 2);
 
+// Create a pass to instrument OpDebugPrintf instructions.
+// This pass replaces all OpDebugPrintf instructions with instructions to write
+// a record containing the string id and the all specified values into a special
+// printf output buffer (if space allows). This pass is designed to support
+// the printf validation in the Vulkan validation layers.
+//
+// The instrumentation will write buffers in debug descriptor set |desc_set|.
+// It will write |shader_id| in each output record to identify the shader
+// module which generated the record.
+Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set,
+                                               uint32_t shader_id);
+
 // Create a pass to upgrade to the VulkanKHR memory model.
 // This pass upgrades the Logical GLSL450 memory model to Logical VulkanKHR.
 // Additionally, it modifies memory, image, atomic and barrier operations to

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

@@ -126,13 +126,11 @@ macro(spvtools_vendor_tables VENDOR_TABLE SHORT_NAME OPERAND_KIND_PREFIX)
 endmacro(spvtools_vendor_tables)
 
 macro(spvtools_extinst_lang_headers NAME GRAMMAR_FILE)
-  set(OUTBASE ${spirv-tools_BINARY_DIR}/${NAME})
-  set(OUT_H ${OUTBASE}.h)
+  set(OUT_H ${spirv-tools_BINARY_DIR}/${NAME}.h)
   add_custom_command(OUTPUT ${OUT_H}
     COMMAND ${PYTHON_EXECUTABLE} ${LANG_HEADER_PROCESSING_SCRIPT}
-      --extinst-name=${NAME}
       --extinst-grammar=${GRAMMAR_FILE}
-      --extinst-output-base=${OUTBASE}
+      --extinst-output-path=${OUT_H}
     DEPENDS ${LANG_HEADER_PROCESSING_SCRIPT} ${GRAMMAR_FILE}
     COMMENT "Generate language specific header for ${NAME}.")
   add_custom_target(spirv-tools-header-${NAME} DEPENDS ${OUT_H})

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

@@ -93,6 +93,10 @@ class EnumSet {
   // enum value is already in the set.
   void Add(EnumType c) { AddWord(ToWord(c)); }
 
+  // Removes the given enum value from the set.  This has no effect if the
+  // enum value is not in the set.
+  void Remove(EnumType c) { RemoveWord(ToWord(c)); }
+
   // Returns true if this enum value is in the set.
   bool Contains(EnumType c) const { return ContainsWord(ToWord(c)); }
 
@@ -141,6 +145,17 @@ class EnumSet {
     }
   }
 
+  // Removes the given enum value (as a 32-bit word) from the set.  This has no
+  // effect if the enum value is not in the set.
+  void RemoveWord(uint32_t word) {
+    if (auto new_bits = AsMask(word)) {
+      mask_ &= ~new_bits;
+    } else {
+      auto itr = Overflow().find(word);
+      if (itr != Overflow().end()) Overflow().erase(itr);
+    }
+  }
+
   // Returns true if the enum represented as a 32-bit word is in the set.
   bool ContainsWord(uint32_t word) const {
     // We shouldn't call Overflow() since this is a const method.

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

@@ -375,6 +375,19 @@
         { "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,

+ 40 - 0
3rdparty/spirv-tools/source/fuzz/CMakeLists.txt

@@ -29,6 +29,7 @@ if(SPIRV_BUILD_FUZZER)
   )
 
   set(SPIRV_TOOLS_FUZZ_SOURCES
+        call_graph.h
         data_descriptor.h
         equivalence_relation.h
         fact_manager.h
@@ -36,11 +37,18 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer.h
         fuzzer_context.h
         fuzzer_pass.h
+        fuzzer_pass_add_access_chains.h
         fuzzer_pass_add_composite_types.h
         fuzzer_pass_add_dead_blocks.h
         fuzzer_pass_add_dead_breaks.h
         fuzzer_pass_add_dead_continues.h
+        fuzzer_pass_add_equation_instructions.h
+        fuzzer_pass_add_function_calls.h
+        fuzzer_pass_add_global_variables.h
+        fuzzer_pass_add_loads.h
+        fuzzer_pass_add_local_variables.h
         fuzzer_pass_add_no_contraction_decorations.h
+        fuzzer_pass_add_stores.h
         fuzzer_pass_add_useful_constructs.h
         fuzzer_pass_adjust_function_controls.h
         fuzzer_pass_adjust_loop_controls.h
@@ -54,7 +62,10 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_obfuscate_constants.h
         fuzzer_pass_outline_functions.h
         fuzzer_pass_permute_blocks.h
+        fuzzer_pass_permute_function_parameters.h
         fuzzer_pass_split_blocks.h
+        fuzzer_pass_swap_commutable_operands.h
+        fuzzer_pass_toggle_access_chain_instruction.h
         fuzzer_util.h
         id_use_descriptor.h
         instruction_descriptor.h
@@ -65,6 +76,7 @@ if(SPIRV_BUILD_FUZZER)
         replayer.h
         shrinker.h
         transformation.h
+        transformation_access_chain.h
         transformation_add_constant_boolean.h
         transformation_add_constant_composite.h
         transformation_add_constant_scalar.h
@@ -74,6 +86,7 @@ if(SPIRV_BUILD_FUZZER)
         transformation_add_function.h
         transformation_add_global_undef.h
         transformation_add_global_variable.h
+        transformation_add_local_variable.h
         transformation_add_no_contraction_decoration.h
         transformation_add_type_array.h
         transformation_add_type_boolean.h
@@ -87,9 +100,13 @@ if(SPIRV_BUILD_FUZZER)
         transformation_composite_construct.h
         transformation_composite_extract.h
         transformation_copy_object.h
+        transformation_equation_instruction.h
+        transformation_function_call.h
+        transformation_load.h
         transformation_merge_blocks.h
         transformation_move_block_down.h
         transformation_outline_function.h
+        transformation_permute_function_parameters.h
         transformation_replace_boolean_constant_with_constant_binary.h
         transformation_replace_constant_with_uniform.h
         transformation_replace_id_with_synonym.h
@@ -98,21 +115,32 @@ if(SPIRV_BUILD_FUZZER)
         transformation_set_memory_operands_mask.h
         transformation_set_selection_control.h
         transformation_split_block.h
+        transformation_store.h
+        transformation_swap_commutable_operands.h
+        transformation_toggle_access_chain_instruction.h
         transformation_vector_shuffle.h
         uniform_buffer_element_descriptor.h
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
 
+        call_graph.cpp
         data_descriptor.cpp
         fact_manager.cpp
         force_render_red.cpp
         fuzzer.cpp
         fuzzer_context.cpp
         fuzzer_pass.cpp
+        fuzzer_pass_add_access_chains.cpp
         fuzzer_pass_add_composite_types.cpp
         fuzzer_pass_add_dead_blocks.cpp
         fuzzer_pass_add_dead_breaks.cpp
         fuzzer_pass_add_dead_continues.cpp
+        fuzzer_pass_add_equation_instructions.cpp
+        fuzzer_pass_add_function_calls.cpp
+        fuzzer_pass_add_global_variables.cpp
+        fuzzer_pass_add_loads.cpp
+        fuzzer_pass_add_local_variables.cpp
         fuzzer_pass_add_no_contraction_decorations.cpp
+        fuzzer_pass_add_stores.cpp
         fuzzer_pass_add_useful_constructs.cpp
         fuzzer_pass_adjust_function_controls.cpp
         fuzzer_pass_adjust_loop_controls.cpp
@@ -126,7 +154,10 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_obfuscate_constants.cpp
         fuzzer_pass_outline_functions.cpp
         fuzzer_pass_permute_blocks.cpp
+        fuzzer_pass_permute_function_parameters.cpp
         fuzzer_pass_split_blocks.cpp
+        fuzzer_pass_swap_commutable_operands.cpp
+        fuzzer_pass_toggle_access_chain_instruction.cpp
         fuzzer_util.cpp
         id_use_descriptor.cpp
         instruction_descriptor.cpp
@@ -136,6 +167,7 @@ if(SPIRV_BUILD_FUZZER)
         replayer.cpp
         shrinker.cpp
         transformation.cpp
+        transformation_access_chain.cpp
         transformation_add_constant_boolean.cpp
         transformation_add_constant_composite.cpp
         transformation_add_constant_scalar.cpp
@@ -145,6 +177,7 @@ if(SPIRV_BUILD_FUZZER)
         transformation_add_function.cpp
         transformation_add_global_undef.cpp
         transformation_add_global_variable.cpp
+        transformation_add_local_variable.cpp
         transformation_add_no_contraction_decoration.cpp
         transformation_add_type_array.cpp
         transformation_add_type_boolean.cpp
@@ -158,9 +191,13 @@ if(SPIRV_BUILD_FUZZER)
         transformation_composite_construct.cpp
         transformation_composite_extract.cpp
         transformation_copy_object.cpp
+        transformation_equation_instruction.cpp
+        transformation_function_call.cpp
+        transformation_load.cpp
         transformation_merge_blocks.cpp
         transformation_move_block_down.cpp
         transformation_outline_function.cpp
+        transformation_permute_function_parameters.cpp
         transformation_replace_boolean_constant_with_constant_binary.cpp
         transformation_replace_constant_with_uniform.cpp
         transformation_replace_id_with_synonym.cpp
@@ -169,6 +206,9 @@ if(SPIRV_BUILD_FUZZER)
         transformation_set_memory_operands_mask.cpp
         transformation_set_selection_control.cpp
         transformation_split_block.cpp
+        transformation_store.cpp
+        transformation_swap_commutable_operands.cpp
+        transformation_toggle_access_chain_instruction.cpp
         transformation_vector_shuffle.cpp
         uniform_buffer_element_descriptor.cpp
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc

+ 81 - 0
3rdparty/spirv-tools/source/fuzz/call_graph.cpp

@@ -0,0 +1,81 @@
+// 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/call_graph.h"
+
+#include <queue>
+
+namespace spvtools {
+namespace fuzz {
+
+CallGraph::CallGraph(opt::IRContext* context) {
+  // Initialize function in-degree and call graph edges to 0 and empty.
+  for (auto& function : *context->module()) {
+    function_in_degree_[function.result_id()] = 0;
+    call_graph_edges_[function.result_id()] = std::set<uint32_t>();
+  }
+
+  // Consider every function.
+  for (auto& function : *context->module()) {
+    // Avoid considering the same callee of this function multiple times by
+    // recording known callees.
+    std::set<uint32_t> known_callees;
+    // Consider every function call instruction in every block.
+    for (auto& block : function) {
+      for (auto& instruction : block) {
+        if (instruction.opcode() != SpvOpFunctionCall) {
+          continue;
+        }
+        // Get the id of the function being called.
+        uint32_t callee = instruction.GetSingleWordInOperand(0);
+        if (known_callees.count(callee)) {
+          // We have already considered a call to this function - ignore it.
+          continue;
+        }
+        // Increase the callee's in-degree and add an edge to the call graph.
+        function_in_degree_[callee]++;
+        call_graph_edges_[function.result_id()].insert(callee);
+        // Mark the callee as 'known'.
+        known_callees.insert(callee);
+      }
+    }
+  }
+}
+
+void CallGraph::PushDirectCallees(uint32_t function_id,
+                                  std::queue<uint32_t>* queue) const {
+  for (auto callee : GetDirectCallees(function_id)) {
+    queue->push(callee);
+  }
+}
+
+std::set<uint32_t> CallGraph::GetIndirectCallees(uint32_t function_id) const {
+  std::set<uint32_t> result;
+  std::queue<uint32_t> queue;
+  PushDirectCallees(function_id, &queue);
+
+  while (!queue.empty()) {
+    auto next = queue.front();
+    queue.pop();
+    if (result.count(next)) {
+      continue;
+    }
+    result.insert(next);
+    PushDirectCallees(next, &queue);
+  }
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 62 - 0
3rdparty/spirv-tools/source/fuzz/call_graph.h

@@ -0,0 +1,62 @@
+// 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_CALL_GRAPH_H_
+#define SOURCE_FUZZ_CALL_GRAPH_H_
+
+#include <map>
+#include <set>
+
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Represents the acyclic call graph of a SPIR-V module.
+class CallGraph {
+ public:
+  // Creates a call graph corresponding to the given SPIR-V module.
+  explicit CallGraph(opt::IRContext* context);
+
+  // Returns a mapping from each function to its number of distinct callers.
+  const std::map<uint32_t, uint32_t>& GetFunctionInDegree() const {
+    return function_in_degree_;
+  }
+
+  // Returns the ids of the functions that |function_id| directly invokes.
+  const std::set<uint32_t>& GetDirectCallees(uint32_t function_id) const {
+    return call_graph_edges_.at(function_id);
+  }
+
+  // Returns the ids of the functions that |function_id| directly or indirectly
+  // invokes.
+  std::set<uint32_t> GetIndirectCallees(uint32_t function_id) const;
+
+ private:
+  // Pushes the direct callees of |function_id| on to |queue|.
+  void PushDirectCallees(uint32_t function_id,
+                         std::queue<uint32_t>* queue) const;
+
+  // Maps each function id to the ids of its immediate callees.
+  std::map<uint32_t, std::set<uint32_t>> call_graph_edges_;
+
+  // For each function id, stores the number of distinct functions that call
+  // the function.
+  std::map<uint32_t, uint32_t> function_in_degree_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_CALL_GRAPH_H_

+ 24 - 13
3rdparty/spirv-tools/source/fuzz/equivalence_relation.h

@@ -75,18 +75,8 @@ class EquivalenceRelation {
     // Register each value if necessary.
     for (auto value : {value1, value2}) {
       if (!Exists(value)) {
-        // Register the value in the equivalence relation.  This relies on
-        // T having a copy constructor.
-        auto unique_pointer_to_value = MakeUnique<T>(value);
-        auto pointer_to_value = unique_pointer_to_value.get();
-        owned_values_.push_back(std::move(unique_pointer_to_value));
-        value_set_.insert(pointer_to_value);
-
-        // Initially say that the value is its own parent and that it has no
-        // children.
-        assert(pointer_to_value && "Representatives should never be null.");
-        parent_[pointer_to_value] = pointer_to_value;
-        children_[pointer_to_value] = std::vector<const T*>();
+        // Register the value in the equivalence relation.
+        Register(value);
       }
     }
 
@@ -112,6 +102,27 @@ class EquivalenceRelation {
     }
   }
 
+  // Requires that |value| is not known to the equivalence relation. Registers
+  // it in its own equivalence class and returns a pointer to the equivalence
+  // class representative.
+  const T* Register(T& value) {
+    assert(!Exists(value));
+
+    // This relies on T having a copy constructor.
+    auto unique_pointer_to_value = MakeUnique<T>(value);
+    auto pointer_to_value = unique_pointer_to_value.get();
+    owned_values_.push_back(std::move(unique_pointer_to_value));
+    value_set_.insert(pointer_to_value);
+
+    // Initially say that the value is its own parent and that it has no
+    // children.
+    assert(pointer_to_value && "Representatives should never be null.");
+    parent_[pointer_to_value] = pointer_to_value;
+    children_[pointer_to_value] = std::vector<const T*>();
+
+    return pointer_to_value;
+  }
+
   // Returns exactly one representative per equivalence class.
   std::vector<const T*> GetEquivalenceClassRepresentatives() const {
     std::vector<const T*> result;
@@ -168,7 +179,6 @@ class EquivalenceRelation {
     return value_set_.find(&value) != value_set_.end();
   }
 
- private:
   // Returns the representative of the equivalence class of |value|, which must
   // already be known to the equivalence relation.  This is the 'Find' operation
   // in a classic union-find data structure.
@@ -207,6 +217,7 @@ class EquivalenceRelation {
     return result;
   }
 
+ private:
   // Maps every value to a parent.  The representative of an equivalence class
   // is its own parent.  A value's representative can be found by walking its
   // chain of ancestors.

+ 433 - 89
3rdparty/spirv-tools/source/fuzz/fact_manager.cpp

@@ -28,24 +28,13 @@ namespace fuzz {
 
 namespace {
 
-std::string ToString(const protobufs::Fact& fact) {
-  assert(fact.fact_case() == protobufs::Fact::kConstantUniformFact &&
-         "Right now this is the only fact we know how to stringify.");
+std::string ToString(const protobufs::FactConstantUniform& fact) {
   std::stringstream stream;
-  stream << "("
-         << fact.constant_uniform_fact()
-                .uniform_buffer_element_descriptor()
-                .descriptor_set()
-         << ", "
-         << fact.constant_uniform_fact()
-                .uniform_buffer_element_descriptor()
-                .binding()
-         << ")[";
+  stream << "(" << fact.uniform_buffer_element_descriptor().descriptor_set()
+         << ", " << fact.uniform_buffer_element_descriptor().binding() << ")[";
 
   bool first = true;
-  for (auto index : fact.constant_uniform_fact()
-                        .uniform_buffer_element_descriptor()
-                        .index()) {
+  for (auto index : fact.uniform_buffer_element_descriptor().index()) {
     if (first) {
       first = false;
     } else {
@@ -57,7 +46,7 @@ std::string ToString(const protobufs::Fact& fact) {
   stream << "] == [";
 
   first = true;
-  for (auto constant_word : fact.constant_uniform_fact().constant_word()) {
+  for (auto constant_word : fact.constant_word()) {
     if (first) {
       first = false;
     } else {
@@ -70,6 +59,36 @@ std::string ToString(const protobufs::Fact& fact) {
   return stream.str();
 }
 
+std::string ToString(const protobufs::FactDataSynonym& fact) {
+  std::stringstream stream;
+  stream << fact.data1() << " = " << fact.data2();
+  return stream.str();
+}
+
+std::string ToString(const protobufs::FactIdEquation& fact) {
+  std::stringstream stream;
+  stream << fact.lhs_id();
+  stream << " " << static_cast<SpvOp>(fact.opcode());
+  for (auto rhs_id : fact.rhs_id()) {
+    stream << " " << rhs_id;
+  }
+  return stream.str();
+}
+
+std::string ToString(const protobufs::Fact& fact) {
+  switch (fact.fact_case()) {
+    case protobufs::Fact::kConstantUniformFact:
+      return ToString(fact.constant_uniform_fact());
+    case protobufs::Fact::kDataSynonymFact:
+      return ToString(fact.data_synonym_fact());
+    case protobufs::Fact::kIdEquationFact:
+      return ToString(fact.id_equation_fact());
+    default:
+      assert(false && "Stringification not supported for this fact.");
+      return "";
+  }
+}
+
 }  // namespace
 
 //=======================
@@ -331,15 +350,70 @@ FactManager::ConstantUniformFacts::GetConstantUniformFactsAndTypes() const {
 //==============================
 
 //==============================
-// Data synonym facts
+// Data synonym and id equation facts
+
+// This helper struct represents the right hand side of an equation as an
+// operator applied to a number of data descriptor operands.
+struct Operation {
+  SpvOp opcode;
+  std::vector<const protobufs::DataDescriptor*> operands;
+};
+
+// Hashing for operations, to allow deterministic unordered sets.
+struct OperationHash {
+  size_t operator()(const Operation& operation) const {
+    std::u32string hash;
+    hash.push_back(operation.opcode);
+    for (auto operand : operation.operands) {
+      hash.push_back(static_cast<uint32_t>(DataDescriptorHash()(operand)));
+    }
+    return std::hash<std::u32string>()(hash);
+  }
+};
+
+// Equality for operations, to allow deterministic unordered sets.
+struct OperationEquals {
+  bool operator()(const Operation& first, const Operation& second) const {
+    // Equal operations require...
+    //
+    // Equal opcodes.
+    if (first.opcode != second.opcode) {
+      return false;
+    }
+    // Matching operand counds.
+    if (first.operands.size() != second.operands.size()) {
+      return false;
+    }
+    // Equal operands.
+    for (uint32_t i = 0; i < first.operands.size(); i++) {
+      if (!DataDescriptorEquals()(first.operands[i], second.operands[i])) {
+        return false;
+      }
+    }
+    return true;
+  }
+};
+
+// A helper, for debugging, to represent an operation as a string.
+std::string ToString(const Operation& operation) {
+  std::stringstream stream;
+  stream << operation.opcode;
+  for (auto operand : operation.operands) {
+    stream << " " << *operand;
+  }
+  return stream.str();
+}
 
 // The purpose of this class is to group the fields and data used to represent
-// facts about data synonyms.
-class FactManager::DataSynonymFacts {
+// facts about data synonyms and id equations.
+class FactManager::DataSynonymAndIdEquationFacts {
  public:
   // See method in FactManager which delegates to this method.
   void AddFact(const protobufs::FactDataSynonym& fact, opt::IRContext* context);
 
+  // See method in FactManager which delegates to this method.
+  void AddFact(const protobufs::FactIdEquation& fact, opt::IRContext* context);
+
   // See method in FactManager which delegates to this method.
   std::vector<const protobufs::DataDescriptor*> GetSynonymsForDataDescriptor(
       const protobufs::DataDescriptor& data_descriptor,
@@ -355,11 +429,12 @@ class FactManager::DataSynonymFacts {
                     opt::IRContext* context) const;
 
  private:
-  // Adds |fact| to the set of managed facts, and recurses into sub-components
-  // of the data descriptors referenced in |fact|, if they are composites, to
+  // Adds the synonym |dd1| = |dd2| to the set of managed facts, and recurses
+  // into sub-components of the data descriptors, if they are composites, to
   // record that their components are pairwise-synonymous.
-  void AddFactRecursive(const protobufs::FactDataSynonym& fact,
-                        opt::IRContext* context);
+  void AddDataSynonymFactRecursive(const protobufs::DataDescriptor& dd1,
+                                   const protobufs::DataDescriptor& dd2,
+                                   opt::IRContext* context);
 
   // Inspects all known facts and adds corollary facts; e.g. if we know that
   // a.x == b.x and a.y == b.y, where a and b have vec2 type, we can record
@@ -371,11 +446,21 @@ class FactManager::DataSynonymFacts {
   void ComputeClosureOfFacts(opt::IRContext* context) const;
 
   // Returns true if and only if |dd1| and |dd2| are valid data descriptors
-  // whose associated data have the same type.
+  // whose associated data have the same type (modulo integer signedness).
   bool DataDescriptorsAreWellFormedAndComparable(
       opt::IRContext* context, const protobufs::DataDescriptor& dd1,
       const protobufs::DataDescriptor& dd2) const;
 
+  // Requires that |lhs_dd| and every element of |rhs_dds| is present in the
+  // |synonymous_| equivalence relation and is its own representative.  Records
+  // the fact that the equation "|lhs_dd| |opcode| |rhs_dds|" holds, and adds
+  // any corollaries, in the form of data synonym or equation facts, that
+  // follow from this and other known facts.
+  void AddEquationFactRecursive(
+      const protobufs::DataDescriptor& lhs_dd, SpvOp opcode,
+      const std::vector<const protobufs::DataDescriptor*>& rhs_dds,
+      opt::IRContext* context);
+
   // The data descriptors that are known to be synonymous with one another are
   // captured by this equivalence relation.
   //
@@ -396,31 +481,243 @@ class FactManager::DataSynonymFacts {
   // whether a new fact has been added since the last time such a computation
   // was performed.
   //
-  // It is mutable so faciliate having const methods, that provide answers to
+  // It is mutable to facilitate having const methods, that provide answers to
   // questions about data synonym facts, triggering closure computation on
   // demand.
-  mutable bool closure_computation_required = false;
+  mutable bool closure_computation_required_ = false;
+
+  // Represents a set of equations on data descriptors as a map indexed by
+  // left-hand-side, mapping a left-hand-side to a set of operations, each of
+  // which (together with the left-hand-side) defines an equation.
+  //
+  // All data descriptors occurring in equations are required to be present in
+  // the |synonymous_| equivalence relation, and to be their own representatives
+  // in that relation.
+  std::unordered_map<
+      const protobufs::DataDescriptor*,
+      std::unordered_set<Operation, OperationHash, OperationEquals>>
+      id_equations_;
 };
 
-void FactManager::DataSynonymFacts::AddFact(
+void FactManager::DataSynonymAndIdEquationFacts::AddFact(
     const protobufs::FactDataSynonym& fact, opt::IRContext* context) {
   // Add the fact, including all facts relating sub-components of the data
   // descriptors that are involved.
-  AddFactRecursive(fact, context);
+  AddDataSynonymFactRecursive(fact.data1(), fact.data2(), context);
 }
 
-void FactManager::DataSynonymFacts::AddFactRecursive(
-    const protobufs::FactDataSynonym& fact, opt::IRContext* context) {
-  assert(DataDescriptorsAreWellFormedAndComparable(context, fact.data1(),
-                                                   fact.data2()));
+void FactManager::DataSynonymAndIdEquationFacts::AddFact(
+    const protobufs::FactIdEquation& fact, opt::IRContext* context) {
+  protobufs::DataDescriptor lhs_dd = MakeDataDescriptor(fact.lhs_id(), {});
+
+  // Register the LHS in the equivalence relation if needed, and get a pointer
+  // to its representative.
+  if (!synonymous_.Exists(lhs_dd)) {
+    synonymous_.Register(lhs_dd);
+  }
+  const protobufs::DataDescriptor* lhs_dd_ptr = synonymous_.Find(&lhs_dd);
+
+  // Get equivalence class representatives for all ids used on the RHS of the
+  // equation.
+  std::vector<const protobufs::DataDescriptor*> rhs_dd_ptrs;
+  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));
+  }
+  // We now have the equation in a form where it refers exclusively to
+  // equivalence class representatives.  Add it to our set of facts and work
+  // out any follow-on facts.
+  AddEquationFactRecursive(*lhs_dd_ptr, static_cast<SpvOp>(fact.opcode()),
+                           rhs_dd_ptrs, context);
+}
+
+void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
+    const protobufs::DataDescriptor& lhs_dd, SpvOp opcode,
+    const std::vector<const protobufs::DataDescriptor*>& rhs_dds,
+    opt::IRContext* context) {
+  // Precondition: all data descriptors referenced in this equation must be
+  // equivalence class representatives - i.e. the equation must be in canonical
+  // form.
+  assert(synonymous_.Exists(lhs_dd));
+  assert(synonymous_.Find(&lhs_dd) == &lhs_dd);
+  for (auto rhs_dd : rhs_dds) {
+    (void)(rhs_dd);  // Keep compilers happy in release mode.
+    assert(synonymous_.Exists(*rhs_dd));
+    assert(synonymous_.Find(rhs_dd) == rhs_dd);
+  }
+
+  if (id_equations_.count(&lhs_dd) == 0) {
+    // We have not seen an equation with this LHS before, so associate the LHS
+    // with an initially empty set.
+    id_equations_.insert(
+        {&lhs_dd,
+         std::unordered_set<Operation, OperationHash, OperationEquals>()});
+  }
+
+  {
+    auto existing_equations = id_equations_.find(&lhs_dd);
+    assert(existing_equations != id_equations_.end() &&
+           "A set of operations should be present, even if empty.");
+
+    Operation new_operation = {opcode, rhs_dds};
+    if (existing_equations->second.count(new_operation)) {
+      // This equation is known, so there is nothing further to be done.
+      return;
+    }
+    // Add the equation to the set of known equations.
+    existing_equations->second.insert(new_operation);
+  }
+
+  // Now try to work out corollaries implied by the new equation and existing
+  // facts.
+  switch (opcode) {
+    case SpvOpIAdd: {
+      // Equation form: "a = b + c"
+      {
+        auto existing_first_operand_equations = id_equations_.find(rhs_dds[0]);
+        if (existing_first_operand_equations != id_equations_.end()) {
+          for (auto equation : existing_first_operand_equations->second) {
+            if (equation.opcode == SpvOpISub) {
+              // Equation form: "a = (d - e) + c"
+              if (equation.operands[1] == rhs_dds[1]) {
+                // Equation form: "a = (d - c) + c"
+                // We can thus infer "a = d"
+                AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0],
+                                            context);
+              }
+              if (equation.operands[0] == rhs_dds[1]) {
+                // Equation form: "a = (c - e) + c"
+                // We can thus infer "a = -e"
+                AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
+                                         {equation.operands[1]}, context);
+              }
+            }
+          }
+        }
+      }
+      {
+        auto existing_second_operand_equations = id_equations_.find(rhs_dds[1]);
+        if (existing_second_operand_equations != id_equations_.end()) {
+          for (auto equation : existing_second_operand_equations->second) {
+            if (equation.opcode == SpvOpISub) {
+              // Equation form: "a = b + (d - e)"
+              if (equation.operands[1] == rhs_dds[0]) {
+                // Equation form: "a = b + (d - b)"
+                // We can thus infer "a = d"
+                AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0],
+                                            context);
+              }
+            }
+          }
+        }
+      }
+      break;
+    }
+    case SpvOpISub: {
+      // Equation form: "a = b - c"
+      {
+        auto existing_first_operand_equations = id_equations_.find(rhs_dds[0]);
+        if (existing_first_operand_equations != id_equations_.end()) {
+          for (auto equation : existing_first_operand_equations->second) {
+            if (equation.opcode == SpvOpIAdd) {
+              // Equation form: "a = (d + e) - c"
+              if (equation.operands[0] == rhs_dds[1]) {
+                // Equation form: "a = (c + e) - c"
+                // We can thus infer "a = e"
+                AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1],
+                                            context);
+              }
+              if (equation.operands[1] == rhs_dds[1]) {
+                // Equation form: "a = (d + c) - c"
+                // We can thus infer "a = d"
+                AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0],
+                                            context);
+              }
+            }
+
+            if (equation.opcode == SpvOpISub) {
+              // Equation form: "a = (d - e) - c"
+              if (equation.operands[0] == rhs_dds[1]) {
+                // Equation form: "a = (c - e) - c"
+                // We can thus infer "a = -e"
+                AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
+                                         {equation.operands[1]}, context);
+              }
+            }
+          }
+        }
+      }
+
+      {
+        auto existing_second_operand_equations = id_equations_.find(rhs_dds[1]);
+        if (existing_second_operand_equations != id_equations_.end()) {
+          for (auto equation : existing_second_operand_equations->second) {
+            if (equation.opcode == SpvOpIAdd) {
+              // Equation form: "a = b - (d + e)"
+              if (equation.operands[0] == rhs_dds[0]) {
+                // Equation form: "a = b - (b + e)"
+                // We can thus infer "a = -e"
+                AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
+                                         {equation.operands[1]}, context);
+              }
+              if (equation.operands[1] == rhs_dds[0]) {
+                // Equation form: "a = b - (d + b)"
+                // We can thus infer "a = -d"
+                AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
+                                         {equation.operands[0]}, context);
+              }
+            }
+            if (equation.opcode == SpvOpISub) {
+              // Equation form: "a = b - (d - e)"
+              if (equation.operands[0] == rhs_dds[0]) {
+                // Equation form: "a = b - (b - e)"
+                // We can thus infer "a = e"
+                AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1],
+                                            context);
+              }
+            }
+          }
+        }
+      }
+      break;
+    }
+    case SpvOpLogicalNot:
+    case SpvOpSNegate: {
+      // Equation form: "a = !b" or "a = -b"
+      auto existing_equations = id_equations_.find(rhs_dds[0]);
+      if (existing_equations != id_equations_.end()) {
+        for (auto equation : existing_equations->second) {
+          if (equation.opcode == opcode) {
+            // Equation form: "a = !!b" or "a = -(-b)"
+            // We can thus infer "a = b"
+            AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
+          }
+        }
+      }
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+void FactManager::DataSynonymAndIdEquationFacts::AddDataSynonymFactRecursive(
+    const protobufs::DataDescriptor& dd1, const protobufs::DataDescriptor& dd2,
+    opt::IRContext* context) {
+  assert(DataDescriptorsAreWellFormedAndComparable(context, dd1, dd2));
 
   // Record that the data descriptors provided in the fact are equivalent.
-  synonymous_.MakeEquivalent(fact.data1(), fact.data2());
+  synonymous_.MakeEquivalent(dd1, dd2);
   // As we have updated the equivalence relation, we might be able to deduce
   // more facts by performing a closure computation, so we record that such a
   // computation is required; it will be performed next time a method answering
   // a data synonym fact-related question is invoked.
-  closure_computation_required = true;
+  closure_computation_required_ = true;
 
   // We now check whether this is a synonym about composite objects.  If it is,
   // we can recursively add synonym facts about their associated sub-components.
@@ -428,9 +725,8 @@ void FactManager::DataSynonymFacts::AddFactRecursive(
   // Get the type of the object referred to by the first data descriptor in the
   // synonym fact.
   uint32_t type_id = fuzzerutil::WalkCompositeTypeIndices(
-      context,
-      context->get_def_use_mgr()->GetDef(fact.data1().object())->type_id(),
-      fact.data1().index());
+      context, context->get_def_use_mgr()->GetDef(dd1.object())->type_id(),
+      dd1.index());
   auto type = context->get_type_mgr()->GetType(type_id);
   auto type_instruction = context->get_def_use_mgr()->GetDef(type_id);
   assert(type != nullptr &&
@@ -460,21 +756,19 @@ void FactManager::DataSynonymFacts::AddFactRecursive(
   //   obj_1[a_1, ..., a_m, i] == obj_2[b_1, ..., b_n, i]
   for (uint32_t i = 0; i < num_composite_elements; i++) {
     std::vector<uint32_t> extended_indices1 =
-        fuzzerutil::RepeatedFieldToVector(fact.data1().index());
+        fuzzerutil::RepeatedFieldToVector(dd1.index());
     extended_indices1.push_back(i);
     std::vector<uint32_t> extended_indices2 =
-        fuzzerutil::RepeatedFieldToVector(fact.data2().index());
+        fuzzerutil::RepeatedFieldToVector(dd2.index());
     extended_indices2.push_back(i);
-    protobufs::FactDataSynonym extended_data_synonym_fact;
-    *extended_data_synonym_fact.mutable_data1() =
-        MakeDataDescriptor(fact.data1().object(), std::move(extended_indices1));
-    *extended_data_synonym_fact.mutable_data2() =
-        MakeDataDescriptor(fact.data2().object(), std::move(extended_indices2));
-    AddFactRecursive(extended_data_synonym_fact, context);
+    AddDataSynonymFactRecursive(
+        MakeDataDescriptor(dd1.object(), std::move(extended_indices1)),
+        MakeDataDescriptor(dd2.object(), std::move(extended_indices2)),
+        context);
   }
 }
 
-void FactManager::DataSynonymFacts::ComputeClosureOfFacts(
+void FactManager::DataSynonymAndIdEquationFacts::ComputeClosureOfFacts(
     opt::IRContext* context) const {
   // Suppose that obj_1[a_1, ..., a_m] and obj_2[b_1, ..., b_n] are distinct
   // data descriptors that describe objects of the same composite type, and that
@@ -551,10 +845,10 @@ void FactManager::DataSynonymFacts::ComputeClosureOfFacts(
 
   // We keep looking for new facts until we perform a complete pass over the
   // equivalence relation without finding any new facts.
-  while (closure_computation_required) {
+  while (closure_computation_required_) {
     // We have not found any new facts yet during this pass; we set this to
     // 'true' if we do find a new fact.
-    closure_computation_required = false;
+    closure_computation_required_ = false;
 
     // Consider each class in the equivalence relation.
     for (auto representative :
@@ -738,7 +1032,7 @@ void FactManager::DataSynonymFacts::ComputeClosureOfFacts(
             synonymous_.MakeEquivalent(dd1_prefix, dd2_prefix);
             // As we have added a new synonym fact, we might benefit from doing
             // another pass over the equivalence relation.
-            closure_computation_required = true;
+            closure_computation_required_ = true;
             // Now that we know this pair of data descriptors are synonymous,
             // there is no point recording how close they are to being
             // synonymous.
@@ -750,20 +1044,56 @@ void FactManager::DataSynonymFacts::ComputeClosureOfFacts(
   }
 }
 
-bool FactManager::DataSynonymFacts::DataDescriptorsAreWellFormedAndComparable(
-    opt::IRContext* context, const protobufs::DataDescriptor& dd1,
-    const protobufs::DataDescriptor& dd2) const {
-  auto end_type_1 = fuzzerutil::WalkCompositeTypeIndices(
+bool FactManager::DataSynonymAndIdEquationFacts::
+    DataDescriptorsAreWellFormedAndComparable(
+        opt::IRContext* context, const protobufs::DataDescriptor& dd1,
+        const protobufs::DataDescriptor& dd2) const {
+  auto end_type_id_1 = fuzzerutil::WalkCompositeTypeIndices(
       context, context->get_def_use_mgr()->GetDef(dd1.object())->type_id(),
       dd1.index());
-  auto end_type_2 = fuzzerutil::WalkCompositeTypeIndices(
+  auto end_type_id_2 = fuzzerutil::WalkCompositeTypeIndices(
       context, context->get_def_use_mgr()->GetDef(dd2.object())->type_id(),
       dd2.index());
-  return end_type_1 && end_type_1 == end_type_2;
+  // The end types of the data descriptors must exist.
+  if (end_type_id_1 == 0 || end_type_id_2 == 0) {
+    return false;
+  }
+  // If the end types are the same, the data descriptors are comparable.
+  if (end_type_id_1 == end_type_id_2) {
+    return true;
+  }
+  // Otherwise they are only comparable if they are integer scalars or integer
+  // 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()) {
+      return false;
+    }
+    type_1 = type_1->AsVector()->element_type();
+    type_2 = type_2->AsVector()->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();
 }
 
 std::vector<const protobufs::DataDescriptor*>
-FactManager::DataSynonymFacts::GetSynonymsForDataDescriptor(
+FactManager::DataSynonymAndIdEquationFacts::GetSynonymsForDataDescriptor(
     const protobufs::DataDescriptor& data_descriptor,
     opt::IRContext* context) const {
   ComputeClosureOfFacts(context);
@@ -774,7 +1104,7 @@ FactManager::DataSynonymFacts::GetSynonymsForDataDescriptor(
 }
 
 std::vector<uint32_t>
-FactManager::DataSynonymFacts ::GetIdsForWhichSynonymsAreKnown(
+FactManager::DataSynonymAndIdEquationFacts ::GetIdsForWhichSynonymsAreKnown(
     opt::IRContext* context) const {
   ComputeClosureOfFacts(context);
   std::vector<uint32_t> result;
@@ -786,12 +1116,12 @@ FactManager::DataSynonymFacts ::GetIdsForWhichSynonymsAreKnown(
   return result;
 }
 
-bool FactManager::DataSynonymFacts::IsSynonymous(
+bool FactManager::DataSynonymAndIdEquationFacts::IsSynonymous(
     const protobufs::DataDescriptor& data_descriptor1,
     const protobufs::DataDescriptor& data_descriptor2,
     opt::IRContext* context) const {
-  const_cast<FactManager::DataSynonymFacts*>(this)->ComputeClosureOfFacts(
-      context);
+  const_cast<FactManager::DataSynonymAndIdEquationFacts*>(this)
+      ->ComputeClosureOfFacts(context);
   return synonymous_.Exists(data_descriptor1) &&
          synonymous_.Exists(data_descriptor2) &&
          synonymous_.IsEquivalent(data_descriptor1, data_descriptor2);
@@ -860,30 +1190,30 @@ bool FactManager::LivesafeFunctionFacts::FunctionIsLivesafe(
 //==============================
 
 //==============================
-// Arbitrarily-valued variable facts
+// Irrelevant pointee value facts
 
 // The purpose of this class is to group the fields and data used to represent
-// facts about livesafe functions.
-class FactManager::ArbitrarilyValuedVaribleFacts {
+// facts about pointers whose pointee values are irrelevant.
+class FactManager::IrrelevantPointeeValueFacts {
  public:
   // See method in FactManager which delegates to this method.
-  void AddFact(const protobufs::FactValueOfVariableIsArbitrary& fact);
+  void AddFact(const protobufs::FactPointeeValueIsIrrelevant& fact);
 
   // See method in FactManager which delegates to this method.
-  bool VariableValueIsArbitrary(uint32_t variable_id) const;
+  bool PointeeValueIsIrrelevant(uint32_t pointer_id) const;
 
  private:
-  std::set<uint32_t> arbitrary_valued_varible_ids_;
+  std::set<uint32_t> pointers_to_irrelevant_pointees_ids_;
 };
 
-void FactManager::ArbitrarilyValuedVaribleFacts::AddFact(
-    const protobufs::FactValueOfVariableIsArbitrary& fact) {
-  arbitrary_valued_varible_ids_.insert(fact.variable_id());
+void FactManager::IrrelevantPointeeValueFacts::AddFact(
+    const protobufs::FactPointeeValueIsIrrelevant& fact) {
+  pointers_to_irrelevant_pointees_ids_.insert(fact.pointer_id());
 }
 
-bool FactManager::ArbitrarilyValuedVaribleFacts::VariableValueIsArbitrary(
-    uint32_t variable_id) const {
-  return arbitrary_valued_varible_ids_.count(variable_id) != 0;
+bool FactManager::IrrelevantPointeeValueFacts::PointeeValueIsIrrelevant(
+    uint32_t pointer_id) const {
+  return pointers_to_irrelevant_pointees_ids_.count(pointer_id) != 0;
 }
 
 // End of arbitrarily-valued variable facts
@@ -891,11 +1221,12 @@ bool FactManager::ArbitrarilyValuedVaribleFacts::VariableValueIsArbitrary(
 
 FactManager::FactManager()
     : uniform_constant_facts_(MakeUnique<ConstantUniformFacts>()),
-      data_synonym_facts_(MakeUnique<DataSynonymFacts>()),
+      data_synonym_and_id_equation_facts_(
+          MakeUnique<DataSynonymAndIdEquationFacts>()),
       dead_block_facts_(MakeUnique<DeadBlockFacts>()),
       livesafe_function_facts_(MakeUnique<LivesafeFunctionFacts>()),
-      arbitrarily_valued_variable_facts_(
-          MakeUnique<ArbitrarilyValuedVaribleFacts>()) {}
+      irrelevant_pointee_value_facts_(
+          MakeUnique<IrrelevantPointeeValueFacts>()) {}
 
 FactManager::~FactManager() = default;
 
@@ -918,7 +1249,8 @@ bool FactManager::AddFact(const fuzz::protobufs::Fact& fact,
       return uniform_constant_facts_->AddFact(fact.constant_uniform_fact(),
                                               context);
     case protobufs::Fact::kDataSynonymFact:
-      data_synonym_facts_->AddFact(fact.data_synonym_fact(), context);
+      data_synonym_and_id_equation_facts_->AddFact(fact.data_synonym_fact(),
+                                                   context);
       return true;
     case protobufs::Fact::kBlockIsDeadFact:
       dead_block_facts_->AddFact(fact.block_is_dead_fact());
@@ -938,7 +1270,7 @@ void FactManager::AddFactDataSynonym(const protobufs::DataDescriptor& data1,
   protobufs::FactDataSynonym fact;
   *fact.mutable_data1() = data1;
   *fact.mutable_data2() = data2;
-  data_synonym_facts_->AddFact(fact, context);
+  data_synonym_and_id_equation_facts_->AddFact(fact, context);
 }
 
 std::vector<uint32_t> FactManager::GetConstantsAvailableFromUniformsForType(
@@ -973,15 +1305,16 @@ FactManager::GetConstantUniformFactsAndTypes() const {
 
 std::vector<uint32_t> FactManager::GetIdsForWhichSynonymsAreKnown(
     opt::IRContext* context) const {
-  return data_synonym_facts_->GetIdsForWhichSynonymsAreKnown(context);
+  return data_synonym_and_id_equation_facts_->GetIdsForWhichSynonymsAreKnown(
+      context);
 }
 
 std::vector<const protobufs::DataDescriptor*>
 FactManager::GetSynonymsForDataDescriptor(
     const protobufs::DataDescriptor& data_descriptor,
     opt::IRContext* context) const {
-  return data_synonym_facts_->GetSynonymsForDataDescriptor(data_descriptor,
-                                                           context);
+  return data_synonym_and_id_equation_facts_->GetSynonymsForDataDescriptor(
+      data_descriptor, context);
 }
 
 std::vector<const protobufs::DataDescriptor*> FactManager::GetSynonymsForId(
@@ -993,8 +1326,8 @@ bool FactManager::IsSynonymous(
     const protobufs::DataDescriptor& data_descriptor1,
     const protobufs::DataDescriptor& data_descriptor2,
     opt::IRContext* context) const {
-  return data_synonym_facts_->IsSynonymous(data_descriptor1, data_descriptor2,
-                                           context);
+  return data_synonym_and_id_equation_facts_->IsSynonymous(
+      data_descriptor1, data_descriptor2, context);
 }
 
 bool FactManager::BlockIsDead(uint32_t block_id) const {
@@ -1017,15 +1350,26 @@ void FactManager::AddFactFunctionIsLivesafe(uint32_t function_id) {
   livesafe_function_facts_->AddFact(fact);
 }
 
-bool FactManager::VariableValueIsArbitrary(uint32_t variable_id) const {
-  return arbitrarily_valued_variable_facts_->VariableValueIsArbitrary(
-      variable_id);
+bool FactManager::PointeeValueIsIrrelevant(uint32_t pointer_id) const {
+  return irrelevant_pointee_value_facts_->PointeeValueIsIrrelevant(pointer_id);
 }
 
-void FactManager::AddFactValueOfVariableIsArbitrary(uint32_t variable_id) {
-  protobufs::FactValueOfVariableIsArbitrary fact;
-  fact.set_variable_id(variable_id);
-  arbitrarily_valued_variable_facts_->AddFact(fact);
+void FactManager::AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id) {
+  protobufs::FactPointeeValueIsIrrelevant fact;
+  fact.set_pointer_id(pointer_id);
+  irrelevant_pointee_value_facts_->AddFact(fact);
+}
+
+void FactManager::AddFactIdEquation(uint32_t lhs_id, SpvOp opcode,
+                                    const std::vector<uint32_t>& rhs_id,
+                                    opt::IRContext* context) {
+  protobufs::FactIdEquation fact;
+  fact.set_lhs_id(lhs_id);
+  fact.set_opcode(opcode);
+  for (auto an_rhs_id : rhs_id) {
+    fact.add_rhs_id(an_rhs_id);
+  }
+  data_synonym_and_id_equation_facts_->AddFact(fact, context);
 }
 
 }  // namespace fuzz

+ 24 - 14
3rdparty/spirv-tools/source/fuzz/fact_manager.h

@@ -64,9 +64,17 @@ class FactManager {
   // Records the fact that |function_id| is livesafe.
   void AddFactFunctionIsLivesafe(uint32_t function_id);
 
-  // Records the fact that |variable_id| has an arbitrary value and can thus be
-  // stored to without affecting the module's behaviour.
-  void AddFactValueOfVariableIsArbitrary(uint32_t variable_id);
+  // Records the fact that the value of the pointee associated with |pointer_id|
+  // is irrelevant: it does not affect the observable behaviour of the module.
+  void AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id);
+
+  // Records the fact that |lhs_id| is defined by the equation:
+  //
+  //   |lhs_id| = |opcode| |rhs_id[0]| ... |rhs_id[N-1]|
+  //
+  void AddFactIdEquation(uint32_t lhs_id, SpvOp opcode,
+                         const std::vector<uint32_t>& rhs_id,
+                         opt::IRContext* context);
 
   // The fact manager is responsible for managing a few distinct categories of
   // facts. In principle there could be different fact managers for each kind
@@ -161,12 +169,13 @@ class FactManager {
   //==============================
 
   //==============================
-  // Querying facts about arbitrarily-valued variables
+  // Querying facts about pointers with irrelevant pointee values
 
-  // Returns true if and ony if |variable_id| is arbitrarily-valued.
-  bool VariableValueIsArbitrary(uint32_t variable_id) const;
+  // Returns true if and ony if the value of the pointee associated with
+  // |pointer_id| is irrelevant.
+  bool PointeeValueIsIrrelevant(uint32_t pointer_id) const;
 
-  // End of arbitrarily-valued variable facts
+  // End of irrelevant pointee value facts
   //==============================
 
  private:
@@ -178,9 +187,10 @@ class FactManager {
   std::unique_ptr<ConstantUniformFacts>
       uniform_constant_facts_;  // Unique pointer to internal data.
 
-  class DataSynonymFacts;  // Opaque class for management of data synonym facts.
-  std::unique_ptr<DataSynonymFacts>
-      data_synonym_facts_;  // Unique pointer to internal data.
+  class DataSynonymAndIdEquationFacts;  // Opaque class for management of data
+                                        // synonym and id equation facts.
+  std::unique_ptr<DataSynonymAndIdEquationFacts>
+      data_synonym_and_id_equation_facts_;  // Unique pointer to internal data.
 
   class DeadBlockFacts;  // Opaque class for management of dead block facts.
   std::unique_ptr<DeadBlockFacts>
@@ -191,10 +201,10 @@ class FactManager {
   std::unique_ptr<LivesafeFunctionFacts>
       livesafe_function_facts_;  // Unique pointer to internal data.
 
-  class ArbitrarilyValuedVaribleFacts;  // Opaque class for management of
-  // facts about variables whose values should be expected to be arbitrary.
-  std::unique_ptr<ArbitrarilyValuedVaribleFacts>
-      arbitrarily_valued_variable_facts_;  // Unique pointer to internal data.
+  class IrrelevantPointeeValueFacts;  // Opaque class for management of
+  // facts about pointers whose pointee values do not matter.
+  std::unique_ptr<IrrelevantPointeeValueFacts>
+      irrelevant_pointee_value_facts_;  // Unique pointer to internal data.
 };
 
 }  // namespace fuzz

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

@@ -21,11 +21,18 @@
 #include "fuzzer_pass_adjust_memory_operands_masks.h"
 #include "source/fuzz/fact_manager.h"
 #include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_pass_add_access_chains.h"
 #include "source/fuzz/fuzzer_pass_add_composite_types.h"
 #include "source/fuzz/fuzzer_pass_add_dead_blocks.h"
 #include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
 #include "source/fuzz/fuzzer_pass_add_dead_continues.h"
+#include "source/fuzz/fuzzer_pass_add_equation_instructions.h"
+#include "source/fuzz/fuzzer_pass_add_function_calls.h"
+#include "source/fuzz/fuzzer_pass_add_global_variables.h"
+#include "source/fuzz/fuzzer_pass_add_loads.h"
+#include "source/fuzz/fuzzer_pass_add_local_variables.h"
 #include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
+#include "source/fuzz/fuzzer_pass_add_stores.h"
 #include "source/fuzz/fuzzer_pass_add_useful_constructs.h"
 #include "source/fuzz/fuzzer_pass_adjust_function_controls.h"
 #include "source/fuzz/fuzzer_pass_adjust_loop_controls.h"
@@ -38,7 +45,10 @@
 #include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
 #include "source/fuzz/fuzzer_pass_outline_functions.h"
 #include "source/fuzz/fuzzer_pass_permute_blocks.h"
+#include "source/fuzz/fuzzer_pass_permute_function_parameters.h"
 #include "source/fuzz/fuzzer_pass_split_blocks.h"
+#include "source/fuzz/fuzzer_pass_swap_commutable_operands.h"
+#include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/pseudo_random_generator.h"
 #include "source/opt/build_module.h"
@@ -179,6 +189,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
   // Apply some semantics-preserving passes.
   std::vector<std::unique_ptr<FuzzerPass>> passes;
   while (passes.empty()) {
+    MaybeAddPass<FuzzerPassAddAccessChains>(&passes, ir_context.get(),
+                                            &fact_manager, &fuzzer_context,
+                                            transformation_sequence_out);
     MaybeAddPass<FuzzerPassAddCompositeTypes>(&passes, ir_context.get(),
                                               &fact_manager, &fuzzer_context,
                                               transformation_sequence_out);
@@ -191,6 +204,24 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
     MaybeAddPass<FuzzerPassAddDeadContinues>(&passes, ir_context.get(),
                                              &fact_manager, &fuzzer_context,
                                              transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddEquationInstructions>(
+        &passes, ir_context.get(), &fact_manager, &fuzzer_context,
+        transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddFunctionCalls>(&passes, ir_context.get(),
+                                             &fact_manager, &fuzzer_context,
+                                             transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddGlobalVariables>(&passes, ir_context.get(),
+                                               &fact_manager, &fuzzer_context,
+                                               transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddLoads>(&passes, ir_context.get(), &fact_manager,
+                                     &fuzzer_context,
+                                     transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddLocalVariables>(&passes, ir_context.get(),
+                                              &fact_manager, &fuzzer_context,
+                                              transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddStores>(&passes, ir_context.get(), &fact_manager,
+                                      &fuzzer_context,
+                                      transformation_sequence_out);
     MaybeAddPass<FuzzerPassApplyIdSynonyms>(&passes, ir_context.get(),
                                             &fact_manager, &fuzzer_context,
                                             transformation_sequence_out);
@@ -215,6 +246,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
     MaybeAddPass<FuzzerPassPermuteBlocks>(&passes, ir_context.get(),
                                           &fact_manager, &fuzzer_context,
                                           transformation_sequence_out);
+    MaybeAddPass<FuzzerPassPermuteFunctionParameters>(
+        &passes, ir_context.get(), &fact_manager, &fuzzer_context,
+        transformation_sequence_out);
     MaybeAddPass<FuzzerPassSplitBlocks>(&passes, ir_context.get(),
                                         &fact_manager, &fuzzer_context,
                                         transformation_sequence_out);
@@ -252,6 +286,12 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
   MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
       &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
       transformation_sequence_out);
+  MaybeAddPass<FuzzerPassSwapCommutableOperands>(
+      &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
+      transformation_sequence_out);
+  MaybeAddPass<FuzzerPassToggleAccessChainInstruction>(
+      &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
+      transformation_sequence_out);
   for (auto& pass : final_passes) {
     if (!impl_->ApplyPassAndCheckValidity(pass.get(), *ir_context, tools)) {
       return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;

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

@@ -23,15 +23,22 @@ namespace {
 // Default <minimum, maximum> pairs of probabilities for applying various
 // transformations. All values are percentages. Keep them in alphabetical order.
 
+const std::pair<uint32_t, uint32_t> kChanceOfAddingAccessChain = {5, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingAnotherStructField = {20,
                                                                          90};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingArrayOrStructType = {20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBlock = {20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBreak = {5, 80};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadContinue = {5, 80};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingEquationInstruction = {5,
+                                                                          90};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingGlobalVariable = {20, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingLoad = {5, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingLocalVariable = {20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingMatrixType = {20, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingNoContractionDecoration = {
     5, 70};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingStore = {5, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorType = {20, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfAdjustingFunctionControl = {20,
                                                                          70};
@@ -40,18 +47,24 @@ const std::pair<uint32_t, uint32_t> kChanceOfAdjustingMemoryOperandsMask = {20,
                                                                             90};
 const std::pair<uint32_t, uint32_t> kChanceOfAdjustingSelectionControl = {20,
                                                                           90};
+const std::pair<uint32_t, uint32_t> kChanceOfCallingFunction = {1, 10};
 const std::pair<uint32_t, uint32_t> kChanceOfChoosingStructTypeVsArrayType = {
     20, 80};
 const std::pair<uint32_t, uint32_t> kChanceOfConstructingComposite = {20, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfDonatingAdditionalModule = {5, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfGoingDeeperWhenMakingAccessChain =
+    {50, 95};
 const std::pair<uint32_t, uint32_t> kChanceOfMakingDonorLivesafe = {40, 60};
 const std::pair<uint32_t, uint32_t> kChanceOfMergingBlocks = {20, 95};
 const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfOutliningFunction = {10, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfPermutingParameters = {30, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
+const std::pair<uint32_t, uint32_t> kChanceOfTogglingAccessChainInstruction = {
+    20, 90};
 
 // Default limits for various quantities that are chosen during fuzzing.
 // Keep them in alphabetical order.
@@ -76,8 +89,14 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
                              uint32_t min_fresh_id)
     : random_generator_(random_generator),
       next_fresh_id_(min_fresh_id),
+      max_loop_control_partial_count_(kDefaultMaxLoopControlPartialCount),
+      max_loop_control_peel_count_(kDefaultMaxLoopControlPeelCount),
+      max_loop_limit_(kDefaultMaxLoopLimit),
+      max_new_array_size_limit_(kDefaultMaxNewArraySizeLimit),
       go_deeper_in_constant_obfuscation_(
           kDefaultGoDeeperInConstantObfuscation) {
+  chance_of_adding_access_chain_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingAccessChain);
   chance_of_adding_another_struct_field_ =
       ChooseBetweenMinAndMax(kChanceOfAddingAnotherStructField);
   chance_of_adding_array_or_struct_type_ =
@@ -88,10 +107,18 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
       ChooseBetweenMinAndMax(kChanceOfAddingDeadBreak);
   chance_of_adding_dead_continue_ =
       ChooseBetweenMinAndMax(kChanceOfAddingDeadContinue);
+  chance_of_adding_equation_instruction_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingEquationInstruction);
+  chance_of_adding_global_variable_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingGlobalVariable);
+  chance_of_adding_load_ = ChooseBetweenMinAndMax(kChanceOfAddingLoad);
+  chance_of_adding_local_variable_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingLocalVariable);
   chance_of_adding_matrix_type_ =
       ChooseBetweenMinAndMax(kChanceOfAddingMatrixType);
   chance_of_adding_no_contraction_decoration_ =
       ChooseBetweenMinAndMax(kChanceOfAddingNoContractionDecoration);
+  chance_of_adding_store_ = ChooseBetweenMinAndMax(kChanceOfAddingStore);
   chance_of_adding_vector_type_ =
       ChooseBetweenMinAndMax(kChanceOfAddingVectorType);
   chance_of_adjusting_function_control_ =
@@ -102,6 +129,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
       ChooseBetweenMinAndMax(kChanceOfAdjustingMemoryOperandsMask);
   chance_of_adjusting_selection_control_ =
       ChooseBetweenMinAndMax(kChanceOfAdjustingSelectionControl);
+  chance_of_calling_function_ =
+      ChooseBetweenMinAndMax(kChanceOfCallingFunction);
   chance_of_choosing_struct_type_vs_array_type_ =
       ChooseBetweenMinAndMax(kChanceOfChoosingStructTypeVsArrayType);
   chance_of_constructing_composite_ =
@@ -109,6 +138,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
   chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject);
   chance_of_donating_additional_module_ =
       ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule);
+  chance_of_going_deeper_when_making_access_chain_ =
+      ChooseBetweenMinAndMax(kChanceOfGoingDeeperWhenMakingAccessChain);
   chance_of_making_donor_livesafe_ =
       ChooseBetweenMinAndMax(kChanceOfMakingDonorLivesafe);
   chance_of_merging_blocks_ = ChooseBetweenMinAndMax(kChanceOfMergingBlocks);
@@ -118,13 +149,13 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
       ChooseBetweenMinAndMax(kChanceOfObfuscatingConstant);
   chance_of_outlining_function_ =
       ChooseBetweenMinAndMax(kChanceOfOutliningFunction);
+  chance_of_permuting_parameters_ =
+      ChooseBetweenMinAndMax(kChanceOfPermutingParameters);
   chance_of_replacing_id_with_synonym_ =
       ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
   chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
-  max_loop_control_partial_count_ = kDefaultMaxLoopControlPartialCount;
-  max_loop_control_peel_count_ = kDefaultMaxLoopControlPeelCount;
-  max_loop_limit_ = kDefaultMaxLoopLimit;
-  max_new_array_size_limit_ = kDefaultMaxNewArraySizeLimit;
+  chance_of_toggling_access_chain_instruction_ =
+      ChooseBetweenMinAndMax(kChanceOfTogglingAccessChainInstruction);
 }
 
 FuzzerContext::~FuzzerContext() = default;

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

@@ -46,18 +46,65 @@ class FuzzerContext {
   // method, and which must be non-empty.  Typically 'HasSizeMethod' will be an
   // std::vector.
   template <typename HasSizeMethod>
-  uint32_t RandomIndex(const HasSizeMethod& sequence) {
+  uint32_t RandomIndex(const HasSizeMethod& sequence) const {
     assert(sequence.size() > 0);
     return random_generator_->RandomUint32(
         static_cast<uint32_t>(sequence.size()));
   }
 
+  // Selects a random index into |sequence|, removes the element at that index
+  // and returns it.
+  template <typename T>
+  T RemoveAtRandomIndex(std::vector<T>* sequence) const {
+    uint32_t index = RandomIndex(*sequence);
+    T result = sequence->at(index);
+    sequence->erase(sequence->begin() + index);
+    return result;
+  }
+
+  // Randomly shuffles a |sequence| between |lo| and |hi| indices inclusively.
+  // |lo| and |hi| must be valid indices to the |sequence|
+  template <typename T>
+  void Shuffle(std::vector<T>* sequence, size_t lo, size_t hi) const {
+    auto& array = *sequence;
+
+    if (array.empty()) {
+      return;
+    }
+
+    assert(lo <= hi && hi < array.size() && "lo and/or hi indices are invalid");
+
+    // i > lo to account for potential infinite loop when lo == 0
+    for (size_t i = hi; i > lo; --i) {
+      auto index =
+          random_generator_->RandomUint32(static_cast<uint32_t>(i - lo + 1));
+
+      if (lo + index != i) {
+        // Introduce std::swap to the scope but don't use it
+        // directly since there might be a better overload
+        using std::swap;
+        swap(array[lo + index], array[i]);
+      }
+    }
+  }
+
+  // Ramdomly shuffles a |sequence|
+  template <typename T>
+  void Shuffle(std::vector<T>* sequence) const {
+    if (!sequence->empty()) {
+      Shuffle(sequence, 0, sequence->size() - 1);
+    }
+  }
+
   // Yields an id that is guaranteed not to be used in the module being fuzzed,
   // or to have been issued before.
   uint32_t GetFreshId();
 
   // Probabilities associated with applying various transformations.
   // Keep them in alphabetical order.
+  uint32_t GetChanceOfAddingAccessChain() {
+    return chance_of_adding_access_chain_;
+  }
   uint32_t GetChanceOfAddingAnotherStructField() {
     return chance_of_adding_another_struct_field_;
   }
@@ -69,12 +116,23 @@ class FuzzerContext {
   uint32_t GetChanceOfAddingDeadContinue() {
     return chance_of_adding_dead_continue_;
   }
+  uint32_t GetChanceOfAddingEquationInstruction() {
+    return chance_of_adding_equation_instruction_;
+  }
+  uint32_t GetChanceOfAddingGlobalVariable() {
+    return chance_of_adding_global_variable_;
+  }
+  uint32_t GetChanceOfAddingLoad() { return chance_of_adding_load_; }
+  uint32_t GetChanceOfAddingLocalVariable() {
+    return chance_of_adding_local_variable_;
+  }
   uint32_t GetChanceOfAddingMatrixType() {
     return chance_of_adding_matrix_type_;
   }
   uint32_t GetChanceOfAddingNoContractionDecoration() {
     return chance_of_adding_no_contraction_decoration_;
   }
+  uint32_t GetChanceOfAddingStore() { return chance_of_adding_store_; }
   uint32_t GetChanceOfAddingVectorType() {
     return chance_of_adding_vector_type_;
   }
@@ -90,6 +148,7 @@ class FuzzerContext {
   uint32_t GetChanceOfAdjustingSelectionControl() {
     return chance_of_adjusting_selection_control_;
   }
+  uint32_t GetChanceOfCallingFunction() { return chance_of_calling_function_; }
   uint32_t GetChanceOfChoosingStructTypeVsArrayType() {
     return chance_of_choosing_struct_type_vs_array_type_;
   }
@@ -100,6 +159,9 @@ class FuzzerContext {
   uint32_t GetChanceOfDonatingAdditionalModule() {
     return chance_of_donating_additional_module_;
   }
+  uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() {
+    return chance_of_going_deeper_when_making_access_chain_;
+  }
   uint32_t ChanceOfMakingDonorLivesafe() {
     return chance_of_making_donor_livesafe_;
   }
@@ -111,10 +173,16 @@ class FuzzerContext {
   uint32_t GetChanceOfOutliningFunction() {
     return chance_of_outlining_function_;
   }
+  uint32_t GetChanceOfPermutingParameters() {
+    return chance_of_permuting_parameters_;
+  }
   uint32_t GetChanceOfReplacingIdWithSynonym() {
     return chance_of_replacing_id_with_synonym_;
   }
   uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
+  uint32_t GetChanceOfTogglingAccessChainInstruction() {
+    return chance_of_toggling_access_chain_instruction_;
+  }
   uint32_t GetRandomLoopControlPeelCount() {
     return random_generator_->RandomUint32(max_loop_control_peel_count_);
   }
@@ -129,8 +197,11 @@ class FuzzerContext {
     return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1;
   }
 
-  // Functions to control how deeply to recurse.
-  // Keep them in alphabetical order.
+  // Other functions to control transformations. Keep them in alphabetical
+  // order.
+  uint32_t GetRandomIndexForAccessChain(uint32_t composite_size_bound) {
+    return random_generator_->RandomUint32(composite_size_bound);
+  }
   bool GoDeeperInConstantObfuscation(uint32_t depth) {
     return go_deeper_in_constant_obfuscation_(depth, random_generator_);
   }
@@ -143,29 +214,39 @@ class FuzzerContext {
 
   // Probabilities associated with applying various transformations.
   // Keep them in alphabetical order.
+  uint32_t chance_of_adding_access_chain_;
   uint32_t chance_of_adding_another_struct_field_;
   uint32_t chance_of_adding_array_or_struct_type_;
   uint32_t chance_of_adding_dead_block_;
   uint32_t chance_of_adding_dead_break_;
   uint32_t chance_of_adding_dead_continue_;
+  uint32_t chance_of_adding_equation_instruction_;
+  uint32_t chance_of_adding_global_variable_;
+  uint32_t chance_of_adding_load_;
+  uint32_t chance_of_adding_local_variable_;
   uint32_t chance_of_adding_matrix_type_;
   uint32_t chance_of_adding_no_contraction_decoration_;
+  uint32_t chance_of_adding_store_;
   uint32_t chance_of_adding_vector_type_;
   uint32_t chance_of_adjusting_function_control_;
   uint32_t chance_of_adjusting_loop_control_;
   uint32_t chance_of_adjusting_memory_operands_mask_;
   uint32_t chance_of_adjusting_selection_control_;
+  uint32_t chance_of_calling_function_;
   uint32_t chance_of_choosing_struct_type_vs_array_type_;
   uint32_t chance_of_constructing_composite_;
   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_making_donor_livesafe_;
   uint32_t chance_of_merging_blocks_;
   uint32_t chance_of_moving_block_down_;
   uint32_t chance_of_obfuscating_constant_;
   uint32_t chance_of_outlining_function_;
+  uint32_t chance_of_permuting_parameters_;
   uint32_t chance_of_replacing_id_with_synonym_;
   uint32_t chance_of_splitting_block_;
+  uint32_t chance_of_toggling_access_chain_instruction_;
 
   // Limits associated with various quantities for which random values are
   // chosen during fuzzing.

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

@@ -14,11 +14,15 @@
 
 #include "source/fuzz/fuzzer_pass.h"
 
+#include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_add_constant_boolean.h"
+#include "source/fuzz/transformation_add_constant_composite.h"
 #include "source/fuzz/transformation_add_constant_scalar.h"
 #include "source/fuzz/transformation_add_global_undef.h"
 #include "source/fuzz/transformation_add_type_boolean.h"
 #include "source/fuzz/transformation_add_type_float.h"
+#include "source/fuzz/transformation_add_type_function.h"
 #include "source/fuzz/transformation_add_type_int.h"
 #include "source/fuzz/transformation_add_type_matrix.h"
 #include "source/fuzz/transformation_add_type_pointer.h"
@@ -38,10 +42,10 @@ FuzzerPass::FuzzerPass(opt::IRContext* ir_context, FactManager* fact_manager,
 FuzzerPass::~FuzzerPass() = default;
 
 std::vector<opt::Instruction*> FuzzerPass::FindAvailableInstructions(
-    const opt::Function& function, opt::BasicBlock* block,
-    opt::BasicBlock::iterator inst_it,
+    opt::Function* function, opt::BasicBlock* block,
+    const opt::BasicBlock::iterator& inst_it,
     std::function<bool(opt::IRContext*, opt::Instruction*)>
-        instruction_is_relevant) {
+        instruction_is_relevant) const {
   // TODO(afd) The following is (relatively) simple, but may end up being
   //  prohibitively inefficient, as it walks the whole dominator tree for
   //  every instruction that is considered.
@@ -54,6 +58,14 @@ std::vector<opt::Instruction*> FuzzerPass::FindAvailableInstructions(
     }
   }
 
+  // Consider all function parameters
+  function->ForEachParam(
+      [this, &instruction_is_relevant, &result](opt::Instruction* param) {
+        if (instruction_is_relevant(GetIRContext(), param)) {
+          result.push_back(param);
+        }
+      });
+
   // Consider all previous instructions in this block
   for (auto prev_inst_it = block->begin(); prev_inst_it != inst_it;
        ++prev_inst_it) {
@@ -64,7 +76,7 @@ std::vector<opt::Instruction*> FuzzerPass::FindAvailableInstructions(
 
   // Walk the dominator tree to consider all instructions from dominating
   // blocks
-  auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(&function);
+  auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(function);
   for (auto next_dominator = dominator_analysis->ImmediateDominator(block);
        next_dominator != nullptr;
        next_dominator =
@@ -78,12 +90,12 @@ std::vector<opt::Instruction*> FuzzerPass::FindAvailableInstructions(
   return result;
 }
 
-void FuzzerPass::MaybeAddTransformationBeforeEachInstruction(
+void FuzzerPass::ForEachInstructionWithInstructionDescriptor(
     std::function<
-        void(const opt::Function& function, opt::BasicBlock* block,
+        void(opt::Function* function, opt::BasicBlock* block,
              opt::BasicBlock::iterator inst_it,
              const protobufs::InstructionDescriptor& instruction_descriptor)>
-        maybe_apply_transformation) {
+        action) {
   // Consider every block in every function.
   for (auto& function : *GetIRContext()->module()) {
     for (auto& block : function) {
@@ -121,11 +133,10 @@ void FuzzerPass::MaybeAddTransformationBeforeEachInstruction(
         const SpvOp opcode = inst_it->opcode();
 
         // Invoke the provided function, which might apply a transformation.
-        maybe_apply_transformation(
-            function, &block, inst_it,
-            MakeInstructionDescriptor(
-                base, opcode,
-                skip_count.count(opcode) ? skip_count.at(opcode) : 0));
+        action(&function, &block, inst_it,
+               MakeInstructionDescriptor(
+                   base, opcode,
+                   skip_count.count(opcode) ? skip_count.at(opcode) : 0));
 
         if (!inst_it->HasResultId()) {
           skip_count[opcode] =
@@ -169,6 +180,26 @@ uint32_t FuzzerPass::FindOrCreate32BitFloatType() {
   return result;
 }
 
+uint32_t FuzzerPass::FindOrCreateFunctionType(
+    uint32_t return_type_id, const std::vector<uint32_t>& argument_id) {
+  // FindFunctionType has a sigle argument for OpTypeFunction operands
+  // so we will have to copy them all in this vector
+  std::vector<uint32_t> type_ids(argument_id.size() + 1);
+  type_ids[0] = return_type_id;
+  std::copy(argument_id.begin(), argument_id.end(), type_ids.begin() + 1);
+
+  // Check if type exists
+  auto existing_id = fuzzerutil::FindFunctionType(GetIRContext(), type_ids);
+  if (existing_id) {
+    return existing_id;
+  }
+
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(
+      TransformationAddTypeFunction(result, return_type_id, argument_id));
+  return result;
+}
+
 uint32_t FuzzerPass::FindOrCreateVectorType(uint32_t component_type_id,
                                             uint32_t component_count) {
   assert(component_count >= 2 && component_count <= 4 &&
@@ -208,21 +239,27 @@ uint32_t FuzzerPass::FindOrCreateMatrixType(uint32_t column_count,
   return result;
 }
 
-uint32_t FuzzerPass::FindOrCreatePointerTo32BitIntegerType(
-    bool is_signed, SpvStorageClass storage_class) {
-  auto uint32_type_id = FindOrCreate32BitIntegerType(is_signed);
-  opt::analysis::Pointer pointer_type(
-      GetIRContext()->get_type_mgr()->GetType(uint32_type_id), storage_class);
-  auto existing_id = GetIRContext()->get_type_mgr()->GetId(&pointer_type);
+uint32_t FuzzerPass::FindOrCreatePointerType(uint32_t base_type_id,
+                                             SpvStorageClass storage_class) {
+  // We do not use the type manager here, due to problems related to isomorphic
+  // but distinct structs not being regarded as different.
+  auto existing_id = fuzzerutil::MaybeGetPointerType(
+      GetIRContext(), base_type_id, storage_class);
   if (existing_id) {
     return existing_id;
   }
   auto result = GetFuzzerContext()->GetFreshId();
   ApplyTransformation(
-      TransformationAddTypePointer(result, storage_class, uint32_type_id));
+      TransformationAddTypePointer(result, storage_class, base_type_id));
   return result;
 }
 
+uint32_t FuzzerPass::FindOrCreatePointerTo32BitIntegerType(
+    bool is_signed, SpvStorageClass storage_class) {
+  return FindOrCreatePointerType(FindOrCreate32BitIntegerType(is_signed),
+                                 storage_class);
+}
+
 uint32_t FuzzerPass::FindOrCreate32BitIntegerConstant(uint32_t word,
                                                       bool is_signed) {
   auto uint32_type_id = FindOrCreate32BitIntegerType(is_signed);
@@ -243,6 +280,42 @@ uint32_t FuzzerPass::FindOrCreate32BitIntegerConstant(uint32_t word,
   return result;
 }
 
+uint32_t FuzzerPass::FindOrCreate32BitFloatConstant(uint32_t word) {
+  auto float_type_id = FindOrCreate32BitFloatType();
+  opt::analysis::FloatConstant float_constant(
+      GetIRContext()->get_type_mgr()->GetType(float_type_id)->AsFloat(),
+      {word});
+  auto existing_constant =
+      GetIRContext()->get_constant_mgr()->FindConstant(&float_constant);
+  if (existing_constant) {
+    return GetIRContext()
+        ->get_constant_mgr()
+        ->GetDefiningInstruction(existing_constant)
+        ->result_id();
+  }
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(
+      TransformationAddConstantScalar(result, float_type_id, {word}));
+  return result;
+}
+
+uint32_t FuzzerPass::FindOrCreateBoolConstant(bool value) {
+  auto bool_type_id = FindOrCreateBoolType();
+  opt::analysis::BoolConstant bool_constant(
+      GetIRContext()->get_type_mgr()->GetType(bool_type_id)->AsBool(), value);
+  auto existing_constant =
+      GetIRContext()->get_constant_mgr()->FindConstant(&bool_constant);
+  if (existing_constant) {
+    return GetIRContext()
+        ->get_constant_mgr()
+        ->GetDefiningInstruction(existing_constant)
+        ->result_id();
+  }
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(TransformationAddConstantBoolean(result, value));
+  return result;
+}
+
 uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
   for (auto& inst : GetIRContext()->types_values()) {
     if (inst.opcode() == SpvOpUndef && inst.type_id() == type_id) {
@@ -254,5 +327,147 @@ uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
   return result;
 }
 
+std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
+FuzzerPass::GetAvailableBaseTypesAndPointers(
+    SpvStorageClass storage_class) const {
+  // Records all of the base types available in the module.
+  std::vector<uint32_t> base_types;
+
+  // For each base type, records all the associated pointer types that target
+  // that base type and that have |storage_class| as their storage class.
+  std::map<uint32_t, std::vector<uint32_t>> base_type_to_pointers;
+
+  for (auto& inst : GetIRContext()->types_values()) {
+    switch (inst.opcode()) {
+      case SpvOpTypeArray:
+      case SpvOpTypeBool:
+      case SpvOpTypeFloat:
+      case SpvOpTypeInt:
+      case SpvOpTypeMatrix:
+      case SpvOpTypeStruct:
+      case SpvOpTypeVector:
+        // These types are suitable as pointer base types.  Record the type,
+        // and the fact that we cannot yet have seen any pointers that use this
+        // as its base type.
+        base_types.push_back(inst.result_id());
+        base_type_to_pointers.insert({inst.result_id(), {}});
+        break;
+      case SpvOpTypePointer:
+        if (inst.GetSingleWordInOperand(0) == storage_class) {
+          // The pointer has the desired storage class, so we are interested in
+          // it.  Associate it with its base type.
+          base_type_to_pointers.at(inst.GetSingleWordInOperand(1))
+              .push_back(inst.result_id());
+        }
+        break;
+      default:
+        break;
+    }
+  }
+  return {base_types, base_type_to_pointers};
+}
+
+uint32_t FuzzerPass::FindOrCreateZeroConstant(
+    uint32_t scalar_or_composite_type_id) {
+  auto type_instruction =
+      GetIRContext()->get_def_use_mgr()->GetDef(scalar_or_composite_type_id);
+  assert(type_instruction && "The type instruction must exist.");
+  switch (type_instruction->opcode()) {
+    case SpvOpTypeBool:
+      return FindOrCreateBoolConstant(false);
+    case SpvOpTypeFloat:
+      return FindOrCreate32BitFloatConstant(0);
+    case SpvOpTypeInt:
+      return FindOrCreate32BitIntegerConstant(
+          0, type_instruction->GetSingleWordInOperand(1) != 0);
+    case SpvOpTypeArray: {
+      return GetZeroConstantForHomogeneousComposite(
+          *type_instruction, type_instruction->GetSingleWordInOperand(0),
+          fuzzerutil::GetArraySize(*type_instruction, GetIRContext()));
+    }
+    case SpvOpTypeMatrix:
+    case SpvOpTypeVector: {
+      return GetZeroConstantForHomogeneousComposite(
+          *type_instruction, type_instruction->GetSingleWordInOperand(0),
+          type_instruction->GetSingleWordInOperand(1));
+    }
+    case SpvOpTypeStruct: {
+      std::vector<const opt::analysis::Constant*> field_zero_constants;
+      std::vector<uint32_t> field_zero_ids;
+      for (uint32_t index = 0; index < type_instruction->NumInOperands();
+           index++) {
+        uint32_t field_constant_id = FindOrCreateZeroConstant(
+            type_instruction->GetSingleWordInOperand(index));
+        field_zero_ids.push_back(field_constant_id);
+        field_zero_constants.push_back(
+            GetIRContext()->get_constant_mgr()->FindDeclaredConstant(
+                field_constant_id));
+      }
+      return FindOrCreateCompositeConstant(
+          *type_instruction, field_zero_constants, field_zero_ids);
+    }
+    default:
+      assert(false && "Unknown type.");
+      return 0;
+  }
+}
+
+uint32_t FuzzerPass::FindOrCreateCompositeConstant(
+    const opt::Instruction& composite_type_instruction,
+    const std::vector<const opt::analysis::Constant*>& constants,
+    const std::vector<uint32_t>& constant_ids) {
+  assert(constants.size() == constant_ids.size() &&
+         "Precondition: |constants| and |constant_ids| must be in "
+         "correspondence.");
+
+  opt::analysis::Type* composite_type = GetIRContext()->get_type_mgr()->GetType(
+      composite_type_instruction.result_id());
+  std::unique_ptr<opt::analysis::Constant> composite_constant;
+  if (composite_type->AsArray()) {
+    composite_constant = MakeUnique<opt::analysis::ArrayConstant>(
+        composite_type->AsArray(), constants);
+  } else if (composite_type->AsMatrix()) {
+    composite_constant = MakeUnique<opt::analysis::MatrixConstant>(
+        composite_type->AsMatrix(), constants);
+  } else if (composite_type->AsStruct()) {
+    composite_constant = MakeUnique<opt::analysis::StructConstant>(
+        composite_type->AsStruct(), constants);
+  } else if (composite_type->AsVector()) {
+    composite_constant = MakeUnique<opt::analysis::VectorConstant>(
+        composite_type->AsVector(), constants);
+  } else {
+    assert(false &&
+           "Precondition: |composite_type| must declare a composite type.");
+    return 0;
+  }
+
+  uint32_t existing_constant =
+      GetIRContext()->get_constant_mgr()->FindDeclaredConstant(
+          composite_constant.get(), composite_type_instruction.result_id());
+  if (existing_constant) {
+    return existing_constant;
+  }
+  uint32_t result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(TransformationAddConstantComposite(
+      result, composite_type_instruction.result_id(), constant_ids));
+  return result;
+}
+
+uint32_t FuzzerPass::GetZeroConstantForHomogeneousComposite(
+    const opt::Instruction& composite_type_instruction,
+    uint32_t component_type_id, uint32_t num_components) {
+  std::vector<const opt::analysis::Constant*> zero_constants;
+  std::vector<uint32_t> zero_ids;
+  uint32_t zero_component = FindOrCreateZeroConstant(component_type_id);
+  const opt::analysis::Constant* registered_zero_component =
+      GetIRContext()->get_constant_mgr()->FindDeclaredConstant(zero_component);
+  for (uint32_t i = 0; i < num_components; i++) {
+    zero_constants.push_back(registered_zero_component);
+    zero_ids.push_back(zero_component);
+  }
+  return FindOrCreateCompositeConstant(composite_type_instruction,
+                                       zero_constants, zero_ids);
+}
+
 }  // namespace fuzz
 }  // namespace spvtools

+ 97 - 12
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h

@@ -61,33 +61,33 @@ class FuzzerPass {
   // |instruction_is_relevant| predicate.  This, for instance, could ignore all
   // instructions that have a particular decoration.
   std::vector<opt::Instruction*> FindAvailableInstructions(
-      const opt::Function& function, opt::BasicBlock* block,
-      opt::BasicBlock::iterator inst_it,
+      opt::Function* function, opt::BasicBlock* block,
+      const opt::BasicBlock::iterator& inst_it,
       std::function<bool(opt::IRContext*, opt::Instruction*)>
-          instruction_is_relevant);
+          instruction_is_relevant) const;
 
   // A helper method that iterates through each instruction in each block, at
   // all times tracking an instruction descriptor that allows the latest
   // instruction to be located even if it has no result id.
   //
-  // The code to manipulate the instruction descriptor is a bit fiddly, and the
+  // The code to manipulate the instruction descriptor is a bit fiddly.  The
   // point of this method is to avoiding having to duplicate it in multiple
   // transformation passes.
   //
-  // The function |maybe_apply_transformation| is invoked for each instruction
-  // |inst_it| in block |block| of function |function| that is encountered.  The
+  // The function |action| is invoked for each instruction |inst_it| in block
+  // |block| of function |function| that is encountered.  The
   // |instruction_descriptor| parameter to the function object allows |inst_it|
   // to be identified.
   //
-  // The job of |maybe_apply_transformation| is to randomly decide whether to
-  // try to apply some transformation, and then - if selected - to attempt to
-  // apply it.
-  void MaybeAddTransformationBeforeEachInstruction(
+  // In most intended use cases, the job of |action| is to randomly decide
+  // whether to try to apply some transformation, and then - if selected - to
+  // attempt to apply it.
+  void ForEachInstructionWithInstructionDescriptor(
       std::function<
-          void(const opt::Function& function, opt::BasicBlock* block,
+          void(opt::Function* function, opt::BasicBlock* block,
                opt::BasicBlock::iterator inst_it,
                const protobufs::InstructionDescriptor& instruction_descriptor)>
-          maybe_apply_transformation);
+          action);
 
   // A generic helper for applying a transformation that should be applicable
   // by construction, and adding it to the sequence of applied transformations.
@@ -112,6 +112,12 @@ class FuzzerPass {
   // instruction does not exist, a transformation is applied to add it.
   uint32_t FindOrCreate32BitFloatType();
 
+  // Returns the id of an OpTypeFunction %<return_type_id> %<...argument_id>
+  // instruction. If such an instruction doesn't exist, a transformation
+  // is applied to create a new one.
+  uint32_t FindOrCreateFunctionType(uint32_t return_type_id,
+                                    const std::vector<uint32_t>& argument_id);
+
   // Returns the id of an OpTypeVector instruction, with |component_type_id|
   // (which must already exist) as its base type, and |component_count|
   // elements (which must be in the range [2, 4]).  If such an instruction does
@@ -125,6 +131,12 @@ class FuzzerPass {
   // type itself do not exist, transformations are applied to add them.
   uint32_t FindOrCreateMatrixType(uint32_t column_count, uint32_t row_count);
 
+  // Returns the id of a pointer type with base type |base_type_id| (which must
+  // already exist) and storage class |storage_class|.  A transformation is
+  // applied to add the pointer if it does not already exist.
+  uint32_t FindOrCreatePointerType(uint32_t base_type_id,
+                                   SpvStorageClass storage_class);
+
   // Returns the id of an OpTypePointer instruction, with a 32-bit integer base
   // type of signedness specified by |is_signed|.  If the pointer type or
   // required integer base type do not exist, transformations are applied to add
@@ -138,12 +150,85 @@ class FuzzerPass {
   // applied to add them.
   uint32_t FindOrCreate32BitIntegerConstant(uint32_t word, bool is_signed);
 
+  // Returns the id of an OpConstant instruction, with 32-bit floating-point
+  // type, with |word| as its value.  If either the required floating-point type
+  // or the constant do not exist, transformations are applied to add them.
+  uint32_t FindOrCreate32BitFloatConstant(uint32_t word);
+
+  // Returns the id of an OpConstantTrue or OpConstantFalse instruction,
+  // according to |value|.  If either the required instruction or the bool
+  // type do not exist, transformations are applied to add them.
+  uint32_t FindOrCreateBoolConstant(bool value);
+
   // Returns the result id of an instruction of the form:
   //   %id = OpUndef %|type_id|
   // If no such instruction exists, a transformation is applied to add it.
   uint32_t FindOrCreateGlobalUndef(uint32_t type_id);
 
+  // Yields a pair, (base_type_ids, base_type_ids_to_pointers), such that:
+  // - base_type_ids captures every scalar or composite type declared in the
+  //   module (i.e., all int, bool, float, vector, matrix, struct and array
+  //   types
+  // - base_type_ids_to_pointers maps every such base type to the sequence
+  //   of all pointer types that have storage class |storage_class| and the
+  //   given base type as their pointee type.  The sequence may be empty for
+  //   some base types if no pointers to those types are defined for the given
+  //   storage class, and the sequence will have multiple elements if there are
+  //   repeated pointer declarations for the same base type and storage class.
+  std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
+  GetAvailableBaseTypesAndPointers(SpvStorageClass storage_class) const;
+
+  // Given a type id, |scalar_or_composite_type_id|, which must correspond to
+  // some scalar or composite type, returns the result id of an instruction
+  // defining a constant of the given type that is zero or false at everywhere.
+  // If such an instruction does not yet exist, transformations are applied to
+  // add it.
+  //
+  // Examples:
+  // --------------+-------------------------------
+  //   TYPE        | RESULT is id corresponding to
+  // --------------+-------------------------------
+  //   bool        | false
+  // --------------+-------------------------------
+  //   bvec4       | (false, false, false, false)
+  // --------------+-------------------------------
+  //   float       | 0.0
+  // --------------+-------------------------------
+  //   vec2        | (0.0, 0.0)
+  // --------------+-------------------------------
+  //   int[3]      | [0, 0, 0]
+  // --------------+-------------------------------
+  //   struct S {  |
+  //     int i;    | S(0, false, (0u, 0u))
+  //     bool b;   |
+  //     uint2 u;  |
+  //   }           |
+  // --------------+-------------------------------
+  uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id);
+
  private:
+  // Array, matrix and vector are *homogeneous* composite types in the sense
+  // that every component of one of these types has the same type.  Given a
+  // homogeneous composite type instruction, |composite_type_instruction|,
+  // returns the id of a composite constant instruction for which every element
+  // is zero/false.  If such an instruction does not yet exist, transformations
+  // are applied to add it.
+  uint32_t GetZeroConstantForHomogeneousComposite(
+      const opt::Instruction& composite_type_instruction,
+      uint32_t component_type_id, uint32_t num_components);
+
+  // Helper to find an existing composite constant instruction of the given
+  // composite type with the given constant components, or to apply
+  // transformations to create such an instruction if it does not yet exist.
+  // Parameter |composite_type_instruction| must be a composite type
+  // instruction.  The parameters |constants| and |constant_ids| must have the
+  // same size, and it must be the case that for each i, |constant_ids[i]| is
+  // the result id of an instruction that defines |constants[i]|.
+  uint32_t FindOrCreateCompositeConstant(
+      const opt::Instruction& composite_type_instruction,
+      const std::vector<const opt::analysis::Constant*>& constants,
+      const std::vector<uint32_t>& constant_ids);
+
   opt::IRContext* ir_context_;
   FactManager* fact_manager_;
   FuzzerContext* fuzzer_context_;

+ 169 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp

@@ -0,0 +1,169 @@
+// 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_add_access_chains.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_access_chain.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddAccessChains::FuzzerPassAddAccessChains(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAddAccessChains::~FuzzerPassAddAccessChains() = default;
+
+void FuzzerPassAddAccessChains::Apply() {
+  ForEachInstructionWithInstructionDescriptor(
+      [this](opt::Function* function, opt::BasicBlock* block,
+             opt::BasicBlock::iterator inst_it,
+             const protobufs::InstructionDescriptor& instruction_descriptor)
+          -> void {
+        assert(inst_it->opcode() ==
+                   instruction_descriptor.target_instruction_opcode() &&
+               "The opcode of the instruction we might insert before must be "
+               "the same as the opcode in the descriptor for the instruction");
+
+        // Check whether it is legitimate to insert an access chain
+        // instruction before this instruction.
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpAccessChain,
+                                                          inst_it)) {
+          return;
+        }
+
+        // Randomly decide whether to try inserting a load here.
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfAddingAccessChain())) {
+          return;
+        }
+
+        // Get all of the pointers that are currently in scope, excluding
+        // explicitly null and undefined pointers.
+        std::vector<opt::Instruction*> relevant_pointer_instructions =
+            FindAvailableInstructions(
+                function, block, inst_it,
+                [](opt::IRContext* context,
+                   opt::Instruction* instruction) -> bool {
+                  if (!instruction->result_id() || !instruction->type_id()) {
+                    // A pointer needs both a result and type id.
+                    return false;
+                  }
+                  switch (instruction->opcode()) {
+                    case SpvOpConstantNull:
+                    case SpvOpUndef:
+                      // Do not allow making an access chain from a null or
+                      // undefined pointer.  (We can eliminate these cases
+                      // before actually checking that the instruction is a
+                      // pointer.)
+                      return false;
+                    default:
+                      break;
+                  }
+                  // If the instruction has pointer type, we can legitimately
+                  // make an access chain from it.
+                  return context->get_def_use_mgr()
+                             ->GetDef(instruction->type_id())
+                             ->opcode() == SpvOpTypePointer;
+                });
+
+        // At this point, |relevant_instructions| contains all the pointers
+        // we might think of making an access chain from.
+        if (relevant_pointer_instructions.empty()) {
+          return;
+        }
+
+        auto chosen_pointer =
+            relevant_pointer_instructions[GetFuzzerContext()->RandomIndex(
+                relevant_pointer_instructions)];
+        std::vector<uint32_t> index_ids;
+        auto pointer_type = GetIRContext()->get_def_use_mgr()->GetDef(
+            chosen_pointer->type_id());
+        uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
+        while (true) {
+          auto subobject_type =
+              GetIRContext()->get_def_use_mgr()->GetDef(subobject_type_id);
+          if (!spvOpcodeIsComposite(subobject_type->opcode())) {
+            break;
+          }
+          if (!GetFuzzerContext()->ChoosePercentage(
+                  GetFuzzerContext()
+                      ->GetChanceOfGoingDeeperWhenMakingAccessChain())) {
+            break;
+          }
+          uint32_t bound;
+          switch (subobject_type->opcode()) {
+            case SpvOpTypeArray:
+              bound = fuzzerutil::GetArraySize(*subobject_type, GetIRContext());
+              break;
+            case SpvOpTypeMatrix:
+            case SpvOpTypeVector:
+              bound = subobject_type->GetSingleWordInOperand(1);
+              break;
+            case SpvOpTypeStruct:
+              bound = fuzzerutil::GetNumberOfStructMembers(*subobject_type);
+              break;
+            default:
+              assert(false && "Not a composite type opcode.");
+              // Set the bound to a value in order to keep release compilers
+              // happy.
+              bound = 0;
+              break;
+          }
+          if (bound == 0) {
+            // It is possible for a composite type to legitimately have zero
+            // sub-components, at least in the case of a struct, which
+            // can have no fields.
+            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(FindOrCreate32BitIntegerConstant(
+              index_value, GetFuzzerContext()->ChooseEven()));
+          switch (subobject_type->opcode()) {
+            case SpvOpTypeArray:
+            case SpvOpTypeMatrix:
+            case SpvOpTypeVector:
+              subobject_type_id = subobject_type->GetSingleWordInOperand(0);
+              break;
+            case SpvOpTypeStruct:
+              subobject_type_id =
+                  subobject_type->GetSingleWordInOperand(index_value);
+              break;
+            default:
+              assert(false && "Not a composite type opcode.");
+          }
+        }
+        // The transformation we are about to create will only apply if a
+        // pointer suitable for the access chain's result type exists, so we
+        // create one if it does not.
+        FindOrCreatePointerType(subobject_type_id,
+                                static_cast<SpvStorageClass>(
+                                    pointer_type->GetSingleWordInOperand(0)));
+        // Apply the transformation to add an access chain.
+        ApplyTransformation(TransformationAccessChain(
+            GetFuzzerContext()->GetFreshId(), chosen_pointer->result_id(),
+            index_ids, instruction_descriptor));
+      });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 41 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.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 SOURCE_FUZZ_FUZZER_PASS_ADD_ACCESS_CHAINS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_ACCESS_CHAINS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Fuzzer pass that randomly adds access chains based on pointers available in
+// the module.  Other passes can use these access chains, e.g. by loading from
+// them.
+class FuzzerPassAddAccessChains : public FuzzerPass {
+ public:
+  FuzzerPassAddAccessChains(opt::IRContext* ir_context,
+                            FactManager* fact_manager,
+                            FuzzerContext* fuzzer_context,
+                            protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddAccessChains();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_ACCESS_CHAINS_H_

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

@@ -13,7 +13,7 @@
 // limitations under the License.
 
 #include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
-
+#include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/transformation_add_dead_break.h"
 #include "source/opt/ir_context.h"
 
@@ -34,11 +34,16 @@ void FuzzerPassAddDeadBreaks::Apply() {
   // We consider each function separately.
   for (auto& function : *GetIRContext()->module()) {
     // For a given function, we find all the merge blocks in that function.
-    std::vector<uint32_t> merge_block_ids;
+    std::vector<opt::BasicBlock*> merge_blocks;
     for (auto& block : function) {
       auto maybe_merge_id = block.MergeBlockIdIfAny();
       if (maybe_merge_id) {
-        merge_block_ids.push_back(maybe_merge_id);
+        auto merge_block =
+            fuzzerutil::MaybeFindBlock(GetIRContext(), maybe_merge_id);
+
+        assert(merge_block && "Merge block can't be null");
+
+        merge_blocks.push_back(merge_block);
       }
     }
     // We rather aggressively consider the possibility of adding a break from
@@ -46,12 +51,34 @@ void FuzzerPassAddDeadBreaks::Apply() {
     // inapplicable as they would be illegal.  That's OK - we later discard the
     // ones that turn out to be no good.
     for (auto& block : function) {
-      for (auto merge_block_id : merge_block_ids) {
-        // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2856): right
-        //  now we completely ignore OpPhi instructions at merge blocks.  This
-        //  will lead to interesting opportunities being missed.
+      for (auto* merge_block : merge_blocks) {
+        // Populate this vector with ids that are available at the branch point
+        // of this basic block. We will use these ids to update OpPhi
+        // instructions later.
+        std::vector<uint32_t> phi_ids;
+
+        // Determine how we need to adjust OpPhi instructions' operands
+        // for this transformation to be valid.
+        //
+        // If |block| has a branch to |merge_block|, the latter must have all of
+        // its OpPhi instructions set up correctly - we don't need to adjust
+        // anything.
+        if (!block.IsSuccessor(merge_block)) {
+          merge_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) {
+            // Add an additional operand for OpPhi instruction.
+            //
+            // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
+            // If we have a way to communicate to the fact manager
+            // that a specific id use is irrelevant and could be replaced with
+            // something else, we should add such a fact about the zero
+            // provided as an OpPhi operand
+            phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id()));
+          });
+        }
+
         auto candidate_transformation = TransformationAddDeadBreak(
-            block.id(), merge_block_id, GetFuzzerContext()->ChooseEven(), {});
+            block.id(), merge_block->id(), GetFuzzerContext()->ChooseEven(),
+            std::move(phi_ids));
         if (candidate_transformation.IsApplicable(GetIRContext(),
                                                   *GetFactManager())) {
           // Only consider a transformation as a candidate if it is applicable.

+ 37 - 6
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp

@@ -13,7 +13,7 @@
 // limitations under the License.
 
 #include "source/fuzz/fuzzer_pass_add_dead_continues.h"
-
+#include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/transformation_add_dead_continue.h"
 #include "source/opt/ir_context.h"
 
@@ -32,15 +32,46 @@ void FuzzerPassAddDeadContinues::Apply() {
   // Consider every block in every function.
   for (auto& function : *GetIRContext()->module()) {
     for (auto& block : function) {
+      // Get the label id of the continue target of the innermost loop.
+      auto continue_block_id =
+          block.IsLoopHeader()
+              ? block.ContinueBlockId()
+              : GetIRContext()->GetStructuredCFGAnalysis()->LoopContinueBlock(
+                    block.id());
+
+      // This transformation is not applicable if current block is not inside a
+      // loop.
+      if (continue_block_id == 0) {
+        continue;
+      }
+
+      auto* continue_block =
+          fuzzerutil::MaybeFindBlock(GetIRContext(), continue_block_id);
+      assert(continue_block && "Continue block is null");
+
+      // Analyze return type of each OpPhi instruction in the continue target
+      // and provide an id for the transformation if needed.
+      std::vector<uint32_t> phi_ids;
+      // Check whether current block has an edge to the continue target.
+      // If this is the case, we don't need to do anything.
+      if (!block.IsSuccessor(continue_block)) {
+        continue_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) {
+          // Add an additional operand for OpPhi instruction.
+          //
+          // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
+          // If we have a way to communicate to the fact manager
+          // that a specific id use is irrelevant and could be replaced with
+          // something else, we should add such a fact about the zero
+          // provided as an OpPhi operand
+          phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id()));
+        });
+      }
+
       // Make a transformation to add a dead continue from this node; if the
       // node turns out to be inappropriate (e.g. by not being in a loop) the
       // precondition for the transformation will fail and it will be ignored.
-      //
-      // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2856): right
-      //  now we completely ignore OpPhi instructions at continue targets.
-      //  This will lead to interesting opportunities being missed.
       auto candidate_transformation = TransformationAddDeadContinue(
-          block.id(), GetFuzzerContext()->ChooseEven(), {});
+          block.id(), GetFuzzerContext()->ChooseEven(), std::move(phi_ids));
       // Probabilistically decide whether to apply the transformation in the
       // case that it is applicable.
       if (candidate_transformation.IsApplicable(GetIRContext(),

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

@@ -0,0 +1,238 @@
+// 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_add_equation_instructions.h"
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_equation_instruction.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddEquationInstructions::FuzzerPassAddEquationInstructions(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAddEquationInstructions::~FuzzerPassAddEquationInstructions() =
+    default;
+
+void FuzzerPassAddEquationInstructions::Apply() {
+  ForEachInstructionWithInstructionDescriptor(
+      [this](opt::Function* function, opt::BasicBlock* block,
+             opt::BasicBlock::iterator inst_it,
+             const protobufs::InstructionDescriptor& instruction_descriptor) {
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfAddingEquationInstruction())) {
+          return;
+        }
+
+        // Check that it is OK to add an equation instruction before the given
+        // instruction in principle - e.g. check that this does not lead to
+        // inserting before an OpVariable or OpPhi instruction.  We use OpIAdd
+        // as an example opcode for this check, to be representative of *some*
+        // opcode that defines an equation, even though we may choose a
+        // different opcode below.
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpIAdd, inst_it)) {
+          return;
+        }
+
+        // Get all available instructions with result ids and types that are not
+        // OpUndef.
+        std::vector<opt::Instruction*> available_instructions =
+            FindAvailableInstructions(
+                function, block, inst_it,
+                [](opt::IRContext*, opt::Instruction* instruction) -> bool {
+                  return instruction->result_id() && instruction->type_id() &&
+                         instruction->opcode() != SpvOpUndef;
+                });
+
+        // Try the opcodes for which we know how to make ids at random until
+        // something works.
+        std::vector<SpvOp> candidate_opcodes = {SpvOpIAdd, SpvOpISub,
+                                                SpvOpLogicalNot, SpvOpSNegate};
+        do {
+          auto opcode =
+              GetFuzzerContext()->RemoveAtRandomIndex(&candidate_opcodes);
+          switch (opcode) {
+            case SpvOpIAdd:
+            case SpvOpISub: {
+              // Instructions of integer (scalar or vector) result type are
+              // suitable for these opcodes.
+              auto integer_instructions =
+                  GetIntegerInstructions(available_instructions);
+              if (!integer_instructions.empty()) {
+                // There is at least one such instruction, so pick one at random
+                // for the LHS of an equation.
+                auto lhs = integer_instructions.at(
+                    GetFuzzerContext()->RandomIndex(integer_instructions));
+
+                // For the RHS, we can use any instruction with an integer
+                // scalar/vector result type of the same number of components
+                // and the same bit-width for the underlying integer type.
+
+                // Work out the element count and bit-width.
+                auto lhs_type =
+                    GetIRContext()->get_type_mgr()->GetType(lhs->type_id());
+                uint32_t lhs_element_count;
+                uint32_t lhs_bit_width;
+                if (lhs_type->AsVector()) {
+                  lhs_element_count = lhs_type->AsVector()->element_count();
+                  lhs_bit_width = lhs_type->AsVector()
+                                      ->element_type()
+                                      ->AsInteger()
+                                      ->width();
+                } else {
+                  lhs_element_count = 1;
+                  lhs_bit_width = lhs_type->AsInteger()->width();
+                }
+
+                // Get all the instructions that match on element count and
+                // bit-width.
+                auto candidate_rhs_instructions = RestrictToElementBitWidth(
+                    RestrictToVectorWidth(integer_instructions,
+                                          lhs_element_count),
+                    lhs_bit_width);
+
+                // Choose a RHS instruction at random; there is guaranteed to
+                // be at least one choice as the LHS will be available.
+                auto rhs = candidate_rhs_instructions.at(
+                    GetFuzzerContext()->RandomIndex(
+                        candidate_rhs_instructions));
+
+                // Add the equation instruction.
+                ApplyTransformation(TransformationEquationInstruction(
+                    GetFuzzerContext()->GetFreshId(), opcode,
+                    {lhs->result_id(), rhs->result_id()},
+                    instruction_descriptor));
+                return;
+              }
+              break;
+            }
+            case SpvOpLogicalNot: {
+              // Choose any available instruction of boolean scalar/vector
+              // result type and equate its negation with a fresh id.
+              auto boolean_instructions =
+                  GetBooleanInstructions(available_instructions);
+              if (!boolean_instructions.empty()) {
+                ApplyTransformation(TransformationEquationInstruction(
+                    GetFuzzerContext()->GetFreshId(), opcode,
+                    {boolean_instructions
+                         .at(GetFuzzerContext()->RandomIndex(
+                             boolean_instructions))
+                         ->result_id()},
+                    instruction_descriptor));
+                return;
+              }
+              break;
+            }
+            case SpvOpSNegate: {
+              // Similar to OpLogicalNot, but for signed integer negation.
+              auto integer_instructions =
+                  GetIntegerInstructions(available_instructions);
+              if (!integer_instructions.empty()) {
+                ApplyTransformation(TransformationEquationInstruction(
+                    GetFuzzerContext()->GetFreshId(), opcode,
+                    {integer_instructions
+                         .at(GetFuzzerContext()->RandomIndex(
+                             integer_instructions))
+                         ->result_id()},
+                    instruction_descriptor));
+                return;
+              }
+              break;
+            }
+            default:
+              assert(false && "Unexpected opcode.");
+              break;
+          }
+        } while (!candidate_opcodes.empty());
+        // Reaching here means that we did not manage to apply any
+        // transformation at this point of the module.
+      });
+}
+
+std::vector<opt::Instruction*>
+FuzzerPassAddEquationInstructions::GetIntegerInstructions(
+    const std::vector<opt::Instruction*>& instructions) const {
+  std::vector<opt::Instruction*> result;
+  for (auto& inst : instructions) {
+    auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id());
+    if (type->AsInteger() ||
+        (type->AsVector() && type->AsVector()->element_type()->AsInteger())) {
+      result.push_back(inst);
+    }
+  }
+  return result;
+}
+
+std::vector<opt::Instruction*>
+FuzzerPassAddEquationInstructions::GetBooleanInstructions(
+    const std::vector<opt::Instruction*>& instructions) const {
+  std::vector<opt::Instruction*> result;
+  for (auto& inst : instructions) {
+    auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id());
+    if (type->AsBool() ||
+        (type->AsVector() && type->AsVector()->element_type()->AsBool())) {
+      result.push_back(inst);
+    }
+  }
+  return result;
+}
+
+std::vector<opt::Instruction*>
+FuzzerPassAddEquationInstructions::RestrictToVectorWidth(
+    const std::vector<opt::Instruction*>& instructions,
+    uint32_t vector_width) const {
+  std::vector<opt::Instruction*> result;
+  for (auto& inst : instructions) {
+    auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id());
+    // Get the vector width of |inst|, which is 1 if |inst| is a scalar and is
+    // otherwise derived from its vector type.
+    uint32_t other_vector_width =
+        type->AsVector() ? type->AsVector()->element_count() : 1;
+    // Keep |inst| if the vector widths match.
+    if (vector_width == other_vector_width) {
+      result.push_back(inst);
+    }
+  }
+  return result;
+}
+
+std::vector<opt::Instruction*>
+FuzzerPassAddEquationInstructions::RestrictToElementBitWidth(
+    const std::vector<opt::Instruction*>& instructions,
+    uint32_t bit_width) const {
+  std::vector<opt::Instruction*> result;
+  for (auto& inst : instructions) {
+    const opt::analysis::Type* type =
+        GetIRContext()->get_type_mgr()->GetType(inst->type_id());
+    if (type->AsVector()) {
+      type = type->AsVector()->element_type();
+    }
+    assert(type->AsInteger() &&
+           "Precondition: all input instructions must "
+           "have integer scalar or vector type.");
+    if (type->AsInteger()->width() == bit_width) {
+      result.push_back(inst);
+    }
+  }
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 67 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.h

@@ -0,0 +1,67 @@
+// 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_ADD_EQUATION_INSTRUCTIONS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_EQUATION_INSTRUCTIONS_H_
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Fuzzer pass that sprinkles instructions through the module that define
+// equations using various arithmetic and logical operators.
+class FuzzerPassAddEquationInstructions : public FuzzerPass {
+ public:
+  FuzzerPassAddEquationInstructions(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddEquationInstructions();
+
+  void Apply() override;
+
+ private:
+  // Yields those instructions in |instructions| that have integer scalar or
+  // vector result type.
+  std::vector<opt::Instruction*> GetIntegerInstructions(
+      const std::vector<opt::Instruction*>& instructions) const;
+
+  // Yields those instructions in |instructions| that have boolean scalar or
+  // vector result type.
+  std::vector<opt::Instruction*> GetBooleanInstructions(
+      const std::vector<opt::Instruction*>& instructions) const;
+
+  // Requires that |instructions| are scalars or vectors of some type.  Returns
+  // only those instructions whose width is |width|. If |width| is 1 this means
+  // the scalars.
+  std::vector<opt::Instruction*> RestrictToVectorWidth(
+      const std::vector<opt::Instruction*>& instructions,
+      uint32_t vector_width) const;
+
+  // Requires that |instructions| are integer scalars or vectors.  Returns only
+  // those instructions for which the bit-width of the underlying integer type
+  // is |bit_width|.
+  std::vector<opt::Instruction*> RestrictToElementBitWidth(
+      const std::vector<opt::Instruction*>& instructions,
+      uint32_t bit_width) const;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_EQUATION_INSTRUCTIONS_H_

+ 247 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.cpp

@@ -0,0 +1,247 @@
+// 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_add_function_calls.h"
+
+#include "source/fuzz/call_graph.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_add_global_variable.h"
+#include "source/fuzz/transformation_add_local_variable.h"
+#include "source/fuzz/transformation_function_call.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddFunctionCalls::FuzzerPassAddFunctionCalls(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAddFunctionCalls::~FuzzerPassAddFunctionCalls() = default;
+
+void FuzzerPassAddFunctionCalls::Apply() {
+  ForEachInstructionWithInstructionDescriptor(
+      [this](opt::Function* function, opt::BasicBlock* block,
+             opt::BasicBlock::iterator inst_it,
+             const protobufs::InstructionDescriptor& instruction_descriptor)
+          -> void {
+        // Check whether it is legitimate to insert a function call before the
+        // instruction.
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpFunctionCall,
+                                                          inst_it)) {
+          return;
+        }
+
+        // Randomly decide whether to try inserting a function call here.
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfCallingFunction())) {
+          return;
+        }
+
+        // Compute the module's call graph - we don't cache it since it may
+        // change each time we apply a transformation.  If this proves to be
+        // a bottleneck the call graph data structure could be made updatable.
+        CallGraph call_graph(GetIRContext());
+
+        // Gather all the non-entry point functions different from this
+        // function.  It is important to ignore entry points as a function
+        // cannot be an entry point and the target of an OpFunctionCall
+        // instruction.  We ignore this function to avoid direct recursion.
+        std::vector<opt::Function*> candidate_functions;
+        for (auto& other_function : *GetIRContext()->module()) {
+          if (&other_function != function &&
+              !fuzzerutil::FunctionIsEntryPoint(GetIRContext(),
+                                                other_function.result_id())) {
+            candidate_functions.push_back(&other_function);
+          }
+        }
+
+        // Choose a function to call, at random, by considering candidate
+        // functions until a suitable one is found.
+        opt::Function* chosen_function = nullptr;
+        while (!candidate_functions.empty()) {
+          opt::Function* candidate_function =
+              GetFuzzerContext()->RemoveAtRandomIndex(&candidate_functions);
+          if (!GetFactManager()->BlockIsDead(block->id()) &&
+              !GetFactManager()->FunctionIsLivesafe(
+                  candidate_function->result_id())) {
+            // Unless in a dead block, only livesafe functions can be invoked
+            continue;
+          }
+          if (call_graph.GetIndirectCallees(candidate_function->result_id())
+                  .count(function->result_id())) {
+            // Calling this function could lead to indirect recursion
+            continue;
+          }
+          chosen_function = candidate_function;
+          break;
+        }
+
+        if (!chosen_function) {
+          // No suitable function was found to call.  (This can happen, for
+          // instance, if the current function is the only function in the
+          // module.)
+          return;
+        }
+
+        ApplyTransformation(TransformationFunctionCall(
+            GetFuzzerContext()->GetFreshId(), chosen_function->result_id(),
+            ChooseFunctionCallArguments(*chosen_function, function, block,
+                                        inst_it),
+            instruction_descriptor));
+      });
+}
+
+std::map<uint32_t, std::vector<opt::Instruction*>>
+FuzzerPassAddFunctionCalls::GetAvailableInstructionsSuitableForActualParameters(
+    opt::Function* function, opt::BasicBlock* block,
+    const opt::BasicBlock::iterator& inst_it) {
+  // Find all instructions in scope that could potentially be used as actual
+  // parameters.  Weed out unsuitable pointer arguments immediately.
+  std::vector<opt::Instruction*> potentially_suitable_instructions =
+      FindAvailableInstructions(
+          function, block, inst_it,
+          [this, block](opt::IRContext* context,
+                        opt::Instruction* inst) -> bool {
+            if (!inst->HasResultId() || !inst->type_id()) {
+              // An instruction needs a result id and type in order
+              // to be suitable as an actual parameter.
+              return false;
+            }
+            if (context->get_def_use_mgr()->GetDef(inst->type_id())->opcode() ==
+                SpvOpTypePointer) {
+              switch (inst->opcode()) {
+                case SpvOpFunctionParameter:
+                case SpvOpVariable:
+                  // Function parameters and variables are the only
+                  // kinds of pointer that can be used as actual
+                  // parameters.
+                  break;
+                default:
+                  return false;
+              }
+              if (!GetFactManager()->BlockIsDead(block->id()) &&
+                  !GetFactManager()->PointeeValueIsIrrelevant(
+                      inst->result_id())) {
+                // We can only pass a pointer as an actual parameter
+                // if the pointee value for the pointer is irrelevant,
+                // or if the block from which we would make the
+                // function call is dead.
+                return false;
+              }
+            }
+            return true;
+          });
+
+  // Group all the instructions that are potentially viable as function actual
+  // parameters by their result types.
+  std::map<uint32_t, std::vector<opt::Instruction*>> result;
+  for (auto inst : potentially_suitable_instructions) {
+    if (result.count(inst->type_id()) == 0) {
+      // This is the first instruction of this type we have seen, so populate
+      // the map with an entry.
+      result.insert({inst->type_id(), {}});
+    }
+    // Add the instruction to the sequence of instructions already associated
+    // with this type.
+    result.at(inst->type_id()).push_back(inst);
+  }
+  return result;
+}
+
+std::vector<uint32_t> FuzzerPassAddFunctionCalls::ChooseFunctionCallArguments(
+    const opt::Function& callee, opt::Function* caller_function,
+    opt::BasicBlock* caller_block,
+    const opt::BasicBlock::iterator& caller_inst_it) {
+  auto type_to_available_instructions =
+      GetAvailableInstructionsSuitableForActualParameters(
+          caller_function, caller_block, caller_inst_it);
+
+  opt::Instruction* function_type = GetIRContext()->get_def_use_mgr()->GetDef(
+      callee.DefInst().GetSingleWordInOperand(1));
+  assert(function_type->opcode() == SpvOpTypeFunction &&
+         "The function type does not have the expected opcode.");
+  std::vector<uint32_t> result;
+  for (uint32_t arg_index = 1; arg_index < function_type->NumInOperands();
+       arg_index++) {
+    auto arg_type_id =
+        GetIRContext()
+            ->get_def_use_mgr()
+            ->GetDef(function_type->GetSingleWordInOperand(arg_index))
+            ->result_id();
+    if (type_to_available_instructions.count(arg_type_id)) {
+      std::vector<opt::Instruction*>& candidate_arguments =
+          type_to_available_instructions.at(arg_type_id);
+      // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177) The value
+      //  selected here is arbitrary.  We should consider adding this
+      //  information as a fact so that the passed parameter could be
+      //  transformed/changed.
+      result.push_back(candidate_arguments[GetFuzzerContext()->RandomIndex(
+                                               candidate_arguments)]
+                           ->result_id());
+    } else {
+      // We don't have a suitable id in scope to pass, so we must make
+      // something up.
+      auto type_instruction =
+          GetIRContext()->get_def_use_mgr()->GetDef(arg_type_id);
+
+      if (type_instruction->opcode() == SpvOpTypePointer) {
+        // In the case of a pointer, we make a new variable, at function
+        // or global scope depending on the storage class of the
+        // pointer.
+
+        // Get a fresh id for the new variable.
+        uint32_t fresh_variable_id = GetFuzzerContext()->GetFreshId();
+
+        // The id of this variable is what we pass as the parameter to
+        // the call.
+        result.push_back(fresh_variable_id);
+
+        // Now bring the variable into existence.
+        if (type_instruction->GetSingleWordInOperand(0) ==
+            SpvStorageClassFunction) {
+          // Add a new zero-initialized local variable to the current
+          // function, noting that its pointee value is irrelevant.
+          ApplyTransformation(TransformationAddLocalVariable(
+              fresh_variable_id, arg_type_id, caller_function->result_id(),
+              FindOrCreateZeroConstant(
+                  type_instruction->GetSingleWordInOperand(1)),
+              true));
+        } else {
+          assert(type_instruction->GetSingleWordInOperand(0) ==
+                     SpvStorageClassPrivate &&
+                 "Only Function and Private storage classes are "
+                 "supported at present.");
+          // Add a new zero-initialized global variable to the module,
+          // noting that its pointee value is irrelevant.
+          ApplyTransformation(TransformationAddGlobalVariable(
+              fresh_variable_id, arg_type_id,
+              FindOrCreateZeroConstant(
+                  type_instruction->GetSingleWordInOperand(1)),
+              true));
+        }
+      } else {
+        // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177): We use
+        //  constant zero for the parameter, but could consider adding a fact
+        //  to allow further passes to obfuscate it.
+        result.push_back(FindOrCreateZeroConstant(arg_type_id));
+      }
+    }
+  }
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 58 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.h

@@ -0,0 +1,58 @@
+// 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_ADD_FUNCTION_CALLS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_FUNCTION_CALLS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Fuzzer pass that adds calls at random to (a) livesafe functions, from
+// anywhere, and (b) any functions, from dead blocks.
+class FuzzerPassAddFunctionCalls : public FuzzerPass {
+ public:
+  FuzzerPassAddFunctionCalls(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddFunctionCalls();
+
+  void Apply() override;
+
+ private:
+  // Identify all instructions available at |instr_it|, in block |block| of
+  // |function|, that are potentially suitable as function call actual
+  // parameters.  The results are grouped by type.
+  std::map<uint32_t, std::vector<opt::Instruction*>>
+  GetAvailableInstructionsSuitableForActualParameters(
+      opt::Function* function, opt::BasicBlock* block,
+      const opt::BasicBlock::iterator& inst_it);
+
+  // Randomly chooses suitable arguments to invoke |callee| right before
+  // instruction |caller_inst_it| of block |caller_block| in |caller_function|,
+  // based on both existing available instructions and the addition of new
+  // instructions to the module.
+  std::vector<uint32_t> ChooseFunctionCallArguments(
+      const opt::Function& callee, opt::Function* caller_function,
+      opt::BasicBlock* caller_block,
+      const opt::BasicBlock::iterator& caller_inst_it);
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_FUNCTION_CALLS_H_

+ 75 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.cpp

@@ -0,0 +1,75 @@
+// 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_add_global_variables.h"
+
+#include "source/fuzz/transformation_add_global_variable.h"
+#include "source/fuzz/transformation_add_type_pointer.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddGlobalVariables::FuzzerPassAddGlobalVariables(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAddGlobalVariables::~FuzzerPassAddGlobalVariables() = default;
+
+void FuzzerPassAddGlobalVariables::Apply() {
+  auto base_type_ids_and_pointers =
+      GetAvailableBaseTypesAndPointers(SpvStorageClassPrivate);
+
+  // These are the base types that are available to this fuzzer pass.
+  auto& base_types = base_type_ids_and_pointers.first;
+
+  // These are the pointers to those base types that are *initially* available
+  // to the fuzzer pass.  The fuzzer pass might add pointer types in cases where
+  // none are available for a given base type.
+  auto& base_type_to_pointers = base_type_ids_and_pointers.second;
+
+  // Probabilistically keep adding global variables.
+  while (GetFuzzerContext()->ChoosePercentage(
+      GetFuzzerContext()->GetChanceOfAddingGlobalVariable())) {
+    // Choose a random base type; the new variable's type will be a pointer to
+    // this base type.
+    uint32_t base_type =
+        base_types[GetFuzzerContext()->RandomIndex(base_types)];
+    uint32_t pointer_type_id;
+    std::vector<uint32_t>& available_pointers_to_base_type =
+        base_type_to_pointers.at(base_type);
+    // Determine whether there is at least one pointer to this base type.
+    if (available_pointers_to_base_type.empty()) {
+      // There is not.  Make one, to use here, and add it to the available
+      // pointers for the base type so that future variables can potentially
+      // use it.
+      pointer_type_id = GetFuzzerContext()->GetFreshId();
+      available_pointers_to_base_type.push_back(pointer_type_id);
+      ApplyTransformation(TransformationAddTypePointer(
+          pointer_type_id, SpvStorageClassPrivate, base_type));
+    } else {
+      // There is - grab one.
+      pointer_type_id =
+          available_pointers_to_base_type[GetFuzzerContext()->RandomIndex(
+              available_pointers_to_base_type)];
+    }
+    ApplyTransformation(TransformationAddGlobalVariable(
+        GetFuzzerContext()->GetFreshId(), pointer_type_id,
+        FindOrCreateZeroConstant(base_type), true));
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 40 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.h

@@ -0,0 +1,40 @@
+// 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_ADD_GLOBAL_VARIABLES_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_GLOBAL_VARIABLES_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Fuzzer pass that randomly adds global variables, with Private storage class,
+// to the module.
+class FuzzerPassAddGlobalVariables : public FuzzerPass {
+ public:
+  FuzzerPassAddGlobalVariables(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddGlobalVariables();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_GLOBAL_VARIABLES_H_

+ 95 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_loads.cpp

@@ -0,0 +1,95 @@
+// 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_add_loads.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_load.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddLoads::FuzzerPassAddLoads(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAddLoads::~FuzzerPassAddLoads() = default;
+
+void FuzzerPassAddLoads::Apply() {
+  ForEachInstructionWithInstructionDescriptor(
+      [this](opt::Function* function, opt::BasicBlock* block,
+             opt::BasicBlock::iterator inst_it,
+             const protobufs::InstructionDescriptor& instruction_descriptor)
+          -> void {
+        assert(inst_it->opcode() ==
+                   instruction_descriptor.target_instruction_opcode() &&
+               "The opcode of the instruction we might insert before must be "
+               "the same as the opcode in the descriptor for the instruction");
+
+        // Check whether it is legitimate to insert a load before this
+        // instruction.
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, inst_it)) {
+          return;
+        }
+
+        // Randomly decide whether to try inserting a load here.
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfAddingLoad())) {
+          return;
+        }
+
+        std::vector<opt::Instruction*> relevant_instructions =
+            FindAvailableInstructions(
+                function, block, inst_it,
+                [](opt::IRContext* context,
+                   opt::Instruction* instruction) -> bool {
+                  if (!instruction->result_id() || !instruction->type_id()) {
+                    return false;
+                  }
+                  switch (instruction->result_id()) {
+                    case SpvOpConstantNull:
+                    case SpvOpUndef:
+                      // Do not allow loading from a null or undefined pointer;
+                      // this might be OK if the block is dead, but for now we
+                      // conservatively avoid it.
+                      return false;
+                    default:
+                      break;
+                  }
+                  return context->get_def_use_mgr()
+                             ->GetDef(instruction->type_id())
+                             ->opcode() == SpvOpTypePointer;
+                });
+
+        // At this point, |relevant_instructions| contains all the pointers
+        // we might think of loading from.
+        if (relevant_instructions.empty()) {
+          return;
+        }
+
+        // Choose a pointer at random, and create and apply a loading
+        // transformation based on it.
+        ApplyTransformation(TransformationLoad(
+            GetFuzzerContext()->GetFreshId(),
+            relevant_instructions[GetFuzzerContext()->RandomIndex(
+                                      relevant_instructions)]
+                ->result_id(),
+            instruction_descriptor));
+      });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 38 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_loads.h

@@ -0,0 +1,38 @@
+// 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_ADD_LOADS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_LOADS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Fuzzer pass that adds stores, at random, from pointers in the module.
+class FuzzerPassAddLoads : public FuzzerPass {
+ public:
+  FuzzerPassAddLoads(opt::IRContext* ir_context, FactManager* fact_manager,
+                     FuzzerContext* fuzzer_context,
+                     protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddLoads();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_LOADS_H_

+ 79 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.cpp

@@ -0,0 +1,79 @@
+// 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_add_local_variables.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_add_local_variable.h"
+#include "source/fuzz/transformation_add_type_pointer.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddLocalVariables::FuzzerPassAddLocalVariables(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAddLocalVariables::~FuzzerPassAddLocalVariables() = default;
+
+void FuzzerPassAddLocalVariables::Apply() {
+  auto base_type_ids_and_pointers =
+      GetAvailableBaseTypesAndPointers(SpvStorageClassFunction);
+
+  // These are the base types that are available to this fuzzer pass.
+  auto& base_types = base_type_ids_and_pointers.first;
+
+  // These are the pointers to those base types that are *initially* available
+  // to the fuzzer pass.  The fuzzer pass might add pointer types in cases where
+  // none are available for a given base type.
+  auto& base_type_to_pointers = base_type_ids_and_pointers.second;
+
+  // Consider every function in the module.
+  for (auto& function : *GetIRContext()->module()) {
+    // Probabilistically keep adding random variables to this function.
+    while (GetFuzzerContext()->ChoosePercentage(
+        GetFuzzerContext()->GetChanceOfAddingLocalVariable())) {
+      // Choose a random base type; the new variable's type will be a pointer to
+      // this base type.
+      uint32_t base_type =
+          base_types[GetFuzzerContext()->RandomIndex(base_types)];
+      uint32_t pointer_type;
+      std::vector<uint32_t>& available_pointers_to_base_type =
+          base_type_to_pointers.at(base_type);
+      // Determine whether there is at least one pointer to this base type.
+      if (available_pointers_to_base_type.empty()) {
+        // There is not.  Make one, to use here, and add it to the available
+        // pointers for the base type so that future variables can potentially
+        // use it.
+        pointer_type = GetFuzzerContext()->GetFreshId();
+        ApplyTransformation(TransformationAddTypePointer(
+            pointer_type, SpvStorageClassFunction, base_type));
+        available_pointers_to_base_type.push_back(pointer_type);
+      } else {
+        // There is - grab one.
+        pointer_type =
+            available_pointers_to_base_type[GetFuzzerContext()->RandomIndex(
+                available_pointers_to_base_type)];
+      }
+      ApplyTransformation(TransformationAddLocalVariable(
+          GetFuzzerContext()->GetFreshId(), pointer_type, function.result_id(),
+          FindOrCreateZeroConstant(base_type), true));
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 40 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.h

@@ -0,0 +1,40 @@
+// 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_ADD_LOCAL_VARIABLES_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_LOCAL_VARIABLES_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Fuzzer pass that randomly adds local variables, with Function storage class,
+// to the module.
+class FuzzerPassAddLocalVariables : public FuzzerPass {
+ public:
+  FuzzerPassAddLocalVariables(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddLocalVariables();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_LOCAL_VARIABLES_H_

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

@@ -44,12 +44,7 @@ void FuzzerPassAddNoContractionDecorations::Apply() {
                       ->GetChanceOfAddingNoContractionDecoration())) {
             TransformationAddNoContractionDecoration transformation(
                 inst.result_id());
-            assert(transformation.IsApplicable(GetIRContext(),
-                                               *GetFactManager()) &&
-                   "Transformation should be applicable by construction.");
-            transformation.Apply(GetIRContext(), GetFactManager());
-            *GetTransformations()->add_transformation() =
-                transformation.ToMessage();
+            ApplyTransformation(transformation);
           }
         }
       }

+ 128 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_stores.cpp

@@ -0,0 +1,128 @@
+// 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_add_stores.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_store.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddStores::FuzzerPassAddStores(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAddStores::~FuzzerPassAddStores() = default;
+
+void FuzzerPassAddStores::Apply() {
+  ForEachInstructionWithInstructionDescriptor(
+      [this](opt::Function* function, opt::BasicBlock* block,
+             opt::BasicBlock::iterator inst_it,
+             const protobufs::InstructionDescriptor& instruction_descriptor)
+          -> void {
+        assert(inst_it->opcode() ==
+                   instruction_descriptor.target_instruction_opcode() &&
+               "The opcode of the instruction we might insert before must be "
+               "the same as the opcode in the descriptor for the instruction");
+
+        // Check whether it is legitimate to insert a store before this
+        // instruction.
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore,
+                                                          inst_it)) {
+          return;
+        }
+
+        // Randomly decide whether to try inserting a store here.
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfAddingStore())) {
+          return;
+        }
+
+        // Look for pointers we might consider storing to.
+        std::vector<opt::Instruction*> relevant_pointers =
+            FindAvailableInstructions(
+                function, block, inst_it,
+                [this, block](opt::IRContext* context,
+                              opt::Instruction* instruction) -> bool {
+                  if (!instruction->result_id() || !instruction->type_id()) {
+                    return false;
+                  }
+                  auto type_inst = context->get_def_use_mgr()->GetDef(
+                      instruction->type_id());
+                  if (type_inst->opcode() != SpvOpTypePointer) {
+                    // Not a pointer.
+                    return false;
+                  }
+                  if (type_inst->GetSingleWordInOperand(0) ==
+                      SpvStorageClassInput) {
+                    // Read-only: cannot store to it.
+                    return false;
+                  }
+                  switch (instruction->result_id()) {
+                    case SpvOpConstantNull:
+                    case SpvOpUndef:
+                      // Do not allow storing to a null or undefined pointer;
+                      // this might be OK if the block is dead, but for now we
+                      // conservatively avoid it.
+                      return false;
+                    default:
+                      break;
+                  }
+                  return GetFactManager()->BlockIsDead(block->id()) ||
+                         GetFactManager()->PointeeValueIsIrrelevant(
+                             instruction->result_id());
+                });
+
+        // At this point, |relevant_pointers| contains all the pointers we might
+        // think of storing to.
+        if (relevant_pointers.empty()) {
+          return;
+        }
+
+        auto pointer = relevant_pointers[GetFuzzerContext()->RandomIndex(
+            relevant_pointers)];
+
+        std::vector<opt::Instruction*> relevant_values =
+            FindAvailableInstructions(
+                function, block, inst_it,
+                [pointer](opt::IRContext* context,
+                          opt::Instruction* instruction) -> bool {
+                  if (!instruction->result_id() || !instruction->type_id()) {
+                    return false;
+                  }
+                  return instruction->type_id() ==
+                         context->get_def_use_mgr()
+                             ->GetDef(pointer->type_id())
+                             ->GetSingleWordInOperand(1);
+                });
+
+        if (relevant_values.empty()) {
+          return;
+        }
+
+        // Choose a value at random, and create and apply a storing
+        // transformation based on it and the pointer.
+        ApplyTransformation(TransformationStore(
+            pointer->result_id(),
+            relevant_values[GetFuzzerContext()->RandomIndex(relevant_values)]
+                ->result_id(),
+            instruction_descriptor));
+      });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 40 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_stores.h

@@ -0,0 +1,40 @@
+// 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_ADD_STORES_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_STORES_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Fuzzer pass that adds stores, at random, through pointers in the module,
+// either (a) from dead blocks, or (b) through pointers whose pointee values
+// are known not to affect the module's overall behaviour.
+class FuzzerPassAddStores : public FuzzerPass {
+ public:
+  FuzzerPassAddStores(opt::IRContext* ir_context, FactManager* fact_manager,
+                      FuzzerContext* fuzzer_context,
+                      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddStores();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_STORES_H_

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

@@ -61,10 +61,7 @@ void FuzzerPassAdjustFunctionControls::Apply() {
       // Create and add a transformation.
       TransformationSetFunctionControl transformation(
           function.DefInst().result_id(), new_function_control_mask);
-      assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
-             "Transformation should be applicable by construction.");
-      transformation.Apply(GetIRContext(), GetFactManager());
-      *GetTransformations()->add_transformation() = transformation.ToMessage();
+      ApplyTransformation(transformation);
     }
   }
 }

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

@@ -107,11 +107,7 @@ void FuzzerPassAdjustLoopControls::Apply() {
         // sequence.
         TransformationSetLoopControl transformation(block.id(), new_mask,
                                                     peel_count, partial_count);
-        assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
-               "Transformation should be applicable by construction.");
-        transformation.Apply(GetIRContext(), GetFactManager());
-        *GetTransformations()->add_transformation() =
-            transformation.ToMessage();
+        ApplyTransformation(transformation);
       }
     }
   }

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

@@ -97,12 +97,7 @@ void FuzzerPassAdjustMemoryOperandsMasks::Apply() {
 
           TransformationSetMemoryOperandsMask transformation(
               MakeInstructionDescriptor(block, inst_it), new_mask, mask_index);
-          assert(
-              transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
-              "Transformation should be applicable by construction.");
-          transformation.Apply(GetIRContext(), GetFactManager());
-          *GetTransformations()->add_transformation() =
-              transformation.ToMessage();
+          ApplyTransformation(transformation);
         }
       }
     }

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

@@ -62,11 +62,7 @@ void FuzzerPassAdjustSelectionControls::Apply() {
         // sequence.
         TransformationSetSelectionControl transformation(
             block.id(), choices[GetFuzzerContext()->RandomIndex(choices)]);
-        assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
-               "Transformation should be applicable by construction.");
-        transformation.Apply(GetIRContext(), GetFactManager());
-        *GetTransformations()->add_transformation() =
-            transformation.ToMessage();
+        ApplyTransformation(transformation);
       }
     }
   }

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

@@ -81,62 +81,65 @@ void FuzzerPassApplyIdSynonyms::Apply() {
         synonyms_to_try.push_back(data_descriptor);
       }
       while (!synonyms_to_try.empty()) {
-        auto synonym_index = GetFuzzerContext()->RandomIndex(synonyms_to_try);
-        auto synonym_to_try = synonyms_to_try[synonym_index];
-        synonyms_to_try.erase(synonyms_to_try.begin() + synonym_index);
+        auto synonym_to_try =
+            GetFuzzerContext()->RemoveAtRandomIndex(&synonyms_to_try);
 
+        // If the synonym's |index_size| is zero, the synonym represents an id.
+        // Otherwise it represents some element of a composite structure, in
+        // which case we need to be able to add an extract instruction to get
+        // that element out.
         if (synonym_to_try->index_size() > 0 &&
-            use_inst->opcode() == SpvOpPhi) {
-          // We are trying to replace an operand to an OpPhi.  This means
-          // we cannot use a composite synonym, because that requires
-          // extracting a component from a composite and we cannot insert
-          // an extract instruction before an OpPhi.
-          //
-          // TODO(afd): We could consider inserting the extract instruction
-          //  into the relevant parent block of the OpPhi.
+            !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract,
+                                                          use_inst) &&
+            use_inst->opcode() != SpvOpPhi) {
+          // We cannot insert an extract before this instruction, so this
+          // synonym is no good.
           continue;
         }
 
-        if (!TransformationReplaceIdWithSynonym::IdsIsAvailableAtUse(
-                GetIRContext(), use_inst, use_in_operand_index,
-                synonym_to_try->object())) {
+        if (!fuzzerutil::IdIsAvailableAtUse(GetIRContext(), use_inst,
+                                            use_in_operand_index,
+                                            synonym_to_try->object())) {
           continue;
         }
 
-        // We either replace the use with an id known to be synonymous, or
-        // an id that will hold the result of extracting a synonym from a
-        // composite.
+        // We either replace the use with an id known to be synonymous (when
+        // the synonym's |index_size| is 0), or an id that will hold the result
+        // of extracting a synonym from a composite (when the synonym's
+        // |index_size| is > 0).
         uint32_t id_with_which_to_replace_use;
         if (synonym_to_try->index_size() == 0) {
           id_with_which_to_replace_use = synonym_to_try->object();
         } else {
           id_with_which_to_replace_use = GetFuzzerContext()->GetFreshId();
-          protobufs::InstructionDescriptor instruction_to_insert_before =
-              MakeInstructionDescriptor(GetIRContext(), use_inst);
-          TransformationCompositeExtract composite_extract_transformation(
-              instruction_to_insert_before, id_with_which_to_replace_use,
-              synonym_to_try->object(),
-              fuzzerutil::RepeatedFieldToVector(synonym_to_try->index()));
-          assert(composite_extract_transformation.IsApplicable(
-                     GetIRContext(), *GetFactManager()) &&
-                 "Transformation should be applicable by construction.");
-          composite_extract_transformation.Apply(GetIRContext(),
-                                                 GetFactManager());
-          *GetTransformations()->add_transformation() =
-              composite_extract_transformation.ToMessage();
+          opt::Instruction* instruction_to_insert_before = nullptr;
+
+          if (use_inst->opcode() != SpvOpPhi) {
+            instruction_to_insert_before = use_inst;
+          } else {
+            auto parent_block_id =
+                use_inst->GetSingleWordInOperand(use_in_operand_index + 1);
+            auto parent_block_instruction =
+                GetIRContext()->get_def_use_mgr()->GetDef(parent_block_id);
+            auto parent_block =
+                GetIRContext()->get_instr_block(parent_block_instruction);
+
+            instruction_to_insert_before = parent_block->GetMergeInst()
+                                               ? parent_block->GetMergeInst()
+                                               : parent_block->terminator();
+          }
+
+          ApplyTransformation(TransformationCompositeExtract(
+              MakeInstructionDescriptor(GetIRContext(),
+                                        instruction_to_insert_before),
+              id_with_which_to_replace_use, synonym_to_try->object(),
+              fuzzerutil::RepeatedFieldToVector(synonym_to_try->index())));
         }
 
-        TransformationReplaceIdWithSynonym replace_id_transformation(
+        ApplyTransformation(TransformationReplaceIdWithSynonym(
             MakeIdUseDescriptorFromUse(GetIRContext(), use_inst,
                                        use_in_operand_index),
-            id_with_which_to_replace_use);
-
-        // The transformation should be applicable by construction.
-        assert(replace_id_transformation.IsApplicable(GetIRContext(),
-                                                      *GetFactManager()));
-        replace_id_transformation.Apply(GetIRContext(), GetFactManager());
-        *GetTransformations()->add_transformation() =
-            replace_id_transformation.ToMessage();
+            id_with_which_to_replace_use));
         break;
       }
     }

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

@@ -42,9 +42,9 @@ void FuzzerPassConstructComposites::Apply() {
     }
   }
 
-  MaybeAddTransformationBeforeEachInstruction(
+  ForEachInstructionWithInstructionDescriptor(
       [this, &composite_type_ids](
-          const opt::Function& function, opt::BasicBlock* block,
+          opt::Function* function, opt::BasicBlock* block,
           opt::BasicBlock::iterator inst_it,
           const protobufs::InstructionDescriptor& instruction_descriptor)
           -> void {

+ 7 - 11
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp

@@ -29,8 +29,8 @@ FuzzerPassCopyObjects::FuzzerPassCopyObjects(
 FuzzerPassCopyObjects::~FuzzerPassCopyObjects() = default;
 
 void FuzzerPassCopyObjects::Apply() {
-  MaybeAddTransformationBeforeEachInstruction(
-      [this](const opt::Function& function, opt::BasicBlock* block,
+  ForEachInstructionWithInstructionDescriptor(
+      [this](opt::Function* function, opt::BasicBlock* block,
              opt::BasicBlock::iterator inst_it,
              const protobufs::InstructionDescriptor& instruction_descriptor)
           -> void {
@@ -64,15 +64,11 @@ void FuzzerPassCopyObjects::Apply() {
 
         // Choose a copyable instruction at random, and create and apply an
         // object copying transformation based on it.
-        uint32_t index = GetFuzzerContext()->RandomIndex(relevant_instructions);
-        TransformationCopyObject transformation(
-            relevant_instructions[index]->result_id(), instruction_descriptor,
-            GetFuzzerContext()->GetFreshId());
-        assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
-               "This transformation should be applicable by construction.");
-        transformation.Apply(GetIRContext(), GetFactManager());
-        *GetTransformations()->add_transformation() =
-            transformation.ToMessage();
+        ApplyTransformation(TransformationCopyObject(
+            relevant_instructions[GetFuzzerContext()->RandomIndex(
+                                      relevant_instructions)]
+                ->result_id(),
+            instruction_descriptor, GetFuzzerContext()->GetFreshId()));
       });
 }
 

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

@@ -18,6 +18,7 @@
 #include <queue>
 #include <set>
 
+#include "source/fuzz/call_graph.h"
 #include "source/fuzz/instruction_message.h"
 #include "source/fuzz/transformation_add_constant_boolean.h"
 #include "source/fuzz/transformation_add_constant_composite.h"
@@ -61,6 +62,8 @@ void FuzzerPassDonateModules::Apply() {
     std::unique_ptr<opt::IRContext> donor_ir_context = donor_suppliers_.at(
         GetFuzzerContext()->RandomIndex(donor_suppliers_))();
     assert(donor_ir_context != nullptr && "Supplying of donor failed");
+    assert(fuzzerutil::IsValid(donor_ir_context.get()) &&
+           "The donor module must be valid");
     // Donate the supplied module.
     //
     // Randomly decide whether to make the module livesafe (see
@@ -397,19 +400,27 @@ void FuzzerPassDonateModules::HandleTypesAndValues(
         // storage class global variable, using remapped versions of the result
         // type and initializer ids for the global variable in the donor.
         //
-        // We regard the added variable as having an arbitrary value.  This
+        // We regard the added variable as having an irrelevant value.  This
         // means that future passes can add stores to the variable in any
         // way they wish, and pass them as pointer parameters to functions
         // without worrying about whether their data might get modified.
         new_result_id = GetFuzzerContext()->GetFreshId();
+        uint32_t remapped_pointer_type =
+            original_id_to_donated_id->at(type_or_value.type_id());
+        uint32_t initializer_id;
+        if (type_or_value.NumInOperands() == 1) {
+          // The variable did not have an initializer; initialize it to zero.
+          // This is to limit problems associated with uninitialized data.
+          initializer_id = FindOrCreateZeroConstant(
+              fuzzerutil::GetPointeeTypeIdFromPointerType(
+                  GetIRContext(), remapped_pointer_type));
+        } else {
+          // The variable already had an initializer; use its remapped id.
+          initializer_id = original_id_to_donated_id->at(
+              type_or_value.GetSingleWordInOperand(1));
+        }
         ApplyTransformation(TransformationAddGlobalVariable(
-            new_result_id,
-            original_id_to_donated_id->at(type_or_value.type_id()),
-            type_or_value.NumInOperands() == 1
-                ? 0
-                : original_id_to_donated_id->at(
-                      type_or_value.GetSingleWordInOperand(1)),
-            true));
+            new_result_id, remapped_pointer_type, initializer_id, true));
       } break;
       case SpvOpUndef: {
         // It is fine to have multiple Undef instructions of the same type, so
@@ -473,53 +484,65 @@ void FuzzerPassDonateModules::HandleFunctions(
     });
 
     // Consider every instruction of the donor function.
-    function_to_donate->ForEachInst(
-        [&donated_instructions,
-         &original_id_to_donated_id](const opt::Instruction* instruction) {
-          // Get the instruction's input operands into donation-ready form,
-          // remapping any id uses in the process.
-          opt::Instruction::OperandList input_operands;
-
-          // Consider each input operand in turn.
-          for (uint32_t in_operand_index = 0;
-               in_operand_index < instruction->NumInOperands();
-               in_operand_index++) {
-            std::vector<uint32_t> operand_data;
-            const opt::Operand& in_operand =
-                instruction->GetInOperand(in_operand_index);
-            switch (in_operand.type) {
-              case SPV_OPERAND_TYPE_ID:
-              case SPV_OPERAND_TYPE_TYPE_ID:
-              case SPV_OPERAND_TYPE_RESULT_ID:
-              case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
-              case SPV_OPERAND_TYPE_SCOPE_ID:
-                // This is an id operand - it consists of a single word of data,
-                // which needs to be remapped so that it is replaced with the
-                // donated form of the id.
-                operand_data.push_back(
-                    original_id_to_donated_id->at(in_operand.words[0]));
-                break;
-              default:
-                // For non-id operands, we just add each of the data words.
-                for (auto word : in_operand.words) {
-                  operand_data.push_back(word);
-                }
-                break;
+    function_to_donate->ForEachInst([this, &donated_instructions,
+                                     &original_id_to_donated_id](
+                                        const opt::Instruction* instruction) {
+      // Get the instruction's input operands into donation-ready form,
+      // remapping any id uses in the process.
+      opt::Instruction::OperandList input_operands;
+
+      // Consider each input operand in turn.
+      for (uint32_t in_operand_index = 0;
+           in_operand_index < instruction->NumInOperands();
+           in_operand_index++) {
+        std::vector<uint32_t> operand_data;
+        const opt::Operand& in_operand =
+            instruction->GetInOperand(in_operand_index);
+        switch (in_operand.type) {
+          case SPV_OPERAND_TYPE_ID:
+          case SPV_OPERAND_TYPE_TYPE_ID:
+          case SPV_OPERAND_TYPE_RESULT_ID:
+          case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
+          case SPV_OPERAND_TYPE_SCOPE_ID:
+            // This is an id operand - it consists of a single word of data,
+            // which needs to be remapped so that it is replaced with the
+            // donated form of the id.
+            operand_data.push_back(
+                original_id_to_donated_id->at(in_operand.words[0]));
+            break;
+          default:
+            // For non-id operands, we just add each of the data words.
+            for (auto word : in_operand.words) {
+              operand_data.push_back(word);
             }
-            input_operands.push_back({in_operand.type, operand_data});
-          }
-          // Remap the result type and result id (if present) of the
-          // instruction, and turn it into a protobuf message.
-          donated_instructions.push_back(MakeInstructionMessage(
-              instruction->opcode(),
-              instruction->type_id()
-                  ? original_id_to_donated_id->at(instruction->type_id())
-                  : 0,
-              instruction->result_id()
-                  ? original_id_to_donated_id->at(instruction->result_id())
-                  : 0,
-              input_operands));
-        });
+            break;
+        }
+        input_operands.push_back({in_operand.type, operand_data});
+      }
+
+      if (instruction->opcode() == SpvOpVariable &&
+          instruction->NumInOperands() == 1) {
+        // This is an uninitialized local variable.  Initialize it to zero.
+        input_operands.push_back(
+            {SPV_OPERAND_TYPE_ID,
+             {FindOrCreateZeroConstant(
+                 fuzzerutil::GetPointeeTypeIdFromPointerType(
+                     GetIRContext(),
+                     original_id_to_donated_id->at(instruction->type_id())))}});
+      }
+
+      // Remap the result type and result id (if present) of the
+      // instruction, and turn it into a protobuf message.
+      donated_instructions.push_back(MakeInstructionMessage(
+          instruction->opcode(),
+          instruction->type_id()
+              ? original_id_to_donated_id->at(instruction->type_id())
+              : 0,
+          instruction->result_id()
+              ? original_id_to_donated_id->at(instruction->result_id())
+              : 0,
+          input_operands));
+    });
 
     if (make_livesafe) {
       // Various types and constants must be in place for a function to be made
@@ -647,9 +670,12 @@ void FuzzerPassDonateModules::HandleFunctions(
         // The return type is void, so we don't need a return value.
         kill_unreachable_return_value_id = 0;
       } else {
-        // We do need a return value; we use OpUndef.
+        // We do need a return value; we use zero.
+        assert(function_return_type_inst->opcode() != SpvOpTypePointer &&
+               "Function return type must not be a pointer.");
         kill_unreachable_return_value_id =
-            FindOrCreateGlobalUndef(function_return_type_inst->type_id());
+            FindOrCreateZeroConstant(original_id_to_donated_id->at(
+                function_return_type_inst->result_id()));
       }
       // Add the function in a livesafe manner.
       ApplyTransformation(TransformationAddFunction(
@@ -666,53 +692,18 @@ void FuzzerPassDonateModules::HandleFunctions(
 std::vector<uint32_t>
 FuzzerPassDonateModules::GetFunctionsInCallGraphTopologicalOrder(
     opt::IRContext* context) {
-  // This is an implementation of Kahn’s algorithm for topological sorting.
-
-  // For each function id, stores the number of distinct functions that call
-  // the function.
-  std::map<uint32_t, uint32_t> function_in_degree;
-
-  // We first build a call graph for the module, and compute the in-degree for
-  // each function in the process.
-  // TODO(afd): If there is functionality elsewhere in the SPIR-V tools
-  //  framework to construct call graphs it could be nice to re-use it here.
-  std::map<uint32_t, std::set<uint32_t>> call_graph_edges;
+  CallGraph call_graph(context);
 
-  // Initialize function in-degree and call graph edges to 0 and empty.
-  for (auto& function : *context->module()) {
-    function_in_degree[function.result_id()] = 0;
-    call_graph_edges[function.result_id()] = std::set<uint32_t>();
-  }
-
-  // Consider every function.
-  for (auto& function : *context->module()) {
-    // Avoid considering the same callee of this function multiple times by
-    // recording known callees.
-    std::set<uint32_t> known_callees;
-    // Consider every function call instruction in every block.
-    for (auto& block : function) {
-      for (auto& instruction : block) {
-        if (instruction.opcode() != SpvOpFunctionCall) {
-          continue;
-        }
-        // Get the id of the function being called.
-        uint32_t callee = instruction.GetSingleWordInOperand(0);
-        if (known_callees.count(callee)) {
-          // We have already considered a call to this function - ignore it.
-          continue;
-        }
-        // Increase the callee's in-degree and add an edge to the call graph.
-        function_in_degree[callee]++;
-        call_graph_edges[function.result_id()].insert(callee);
-        // Mark the callee as 'known'.
-        known_callees.insert(callee);
-      }
-    }
-  }
+  // This is an implementation of Kahn’s algorithm for topological sorting.
 
   // This is the sorted order of function ids that we will eventually return.
   std::vector<uint32_t> result;
 
+  // Get a copy of the initial in-degrees of all functions.  The algorithm
+  // involves decrementing these values, hence why we work on a copy.
+  std::map<uint32_t, uint32_t> function_in_degree =
+      call_graph.GetFunctionInDegree();
+
   // Populate a queue with all those function ids with in-degree zero.
   std::queue<uint32_t> queue;
   for (auto& entry : function_in_degree) {
@@ -728,7 +719,7 @@ FuzzerPassDonateModules::GetFunctionsInCallGraphTopologicalOrder(
     auto next = queue.front();
     queue.pop();
     result.push_back(next);
-    for (auto successor : call_graph_edges.at(next)) {
+    for (auto successor : call_graph.GetDirectCallees(next)) {
       assert(function_in_degree.at(successor) > 0 &&
              "The in-degree cannot be zero if the function is a successor.");
       function_in_degree[successor] = function_in_degree.at(successor) - 1;

+ 22 - 7
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp

@@ -416,13 +416,28 @@ void FuzzerPassObfuscateConstants::Apply() {
           skipped_opcode_count.clear();
         }
 
-        // Consider each operand of the instruction, and add a constant id use
-        // for the operand if relevant.
-        for (uint32_t in_operand_index = 0;
-             in_operand_index < inst.NumInOperands(); in_operand_index++) {
-          MaybeAddConstantIdUse(inst, in_operand_index,
-                                base_instruction_result_id,
-                                skipped_opcode_count, &constant_uses);
+        switch (inst.opcode()) {
+          case SpvOpPhi:
+            // The instruction must not be an OpPhi, as we cannot insert
+            // instructions before an OpPhi.
+            // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902):
+            //  there is scope for being less conservative.
+            break;
+          case SpvOpVariable:
+            // The instruction must not be an OpVariable, the only id that an
+            // OpVariable uses is an initializer id, which has to remain
+            // constant.
+            break;
+          default:
+            // Consider each operand of the instruction, and add a constant id
+            // use for the operand if relevant.
+            for (uint32_t in_operand_index = 0;
+                 in_operand_index < inst.NumInOperands(); in_operand_index++) {
+              MaybeAddConstantIdUse(inst, in_operand_index,
+                                    base_instruction_result_id,
+                                    skipped_opcode_count, &constant_uses);
+            }
+            break;
         }
 
         if (!inst.HasResultId()) {

+ 81 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp

@@ -0,0 +1,81 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// 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 <numeric>
+#include <vector>
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_pass_permute_function_parameters.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_permute_function_parameters.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassPermuteFunctionParameters::FuzzerPassPermuteFunctionParameters(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassPermuteFunctionParameters::~FuzzerPassPermuteFunctionParameters() =
+    default;
+
+void FuzzerPassPermuteFunctionParameters::Apply() {
+  for (const auto& function : *GetIRContext()->module()) {
+    uint32_t function_id = function.result_id();
+
+    // Skip the function if it is an entry point
+    if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(), function_id)) {
+      continue;
+    }
+
+    if (!GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()->GetChanceOfPermutingParameters())) {
+      continue;
+    }
+
+    // Compute permutation for parameters
+    auto* function_type =
+        fuzzerutil::GetFunctionType(GetIRContext(), &function);
+    assert(function_type && "Function type is null");
+
+    // Don't take return type into account
+    uint32_t arg_size = function_type->NumInOperands() - 1;
+
+    // Create a vector, fill it with [0, n-1] values and shuffle it
+    std::vector<uint32_t> permutation(arg_size);
+    std::iota(permutation.begin(), permutation.end(), 0);
+    GetFuzzerContext()->Shuffle(&permutation);
+
+    // Create a new OpFunctionType instruction with permuted arguments
+    // if needed
+    auto result_type_id = function_type->GetSingleWordInOperand(0);
+    std::vector<uint32_t> argument_ids;
+
+    for (auto index : permutation) {
+      // +1 to take function's return type into account
+      argument_ids.push_back(function_type->GetSingleWordInOperand(index + 1));
+    }
+
+    // Apply our transformation
+    ApplyTransformation(TransformationPermuteFunctionParameters(
+        function_id, FindOrCreateFunctionType(result_type_id, argument_ids),
+        permutation));
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 45 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.h

@@ -0,0 +1,45 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// 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_PERMUTE_FUNCTION_PARAMETERS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_PARAMETERS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Fuzzer pass that, given a non-entry-point function taking n parameters
+// and a permutation of the set [0, n - 1]:
+//   1. Introduces a new function type that is the same as the original
+//      function's type but with the order of arguments permuted
+//      (only add this if it doesn't already exist)
+//   2. Changes the type of the function to this type
+//   3. Adjusts all calls to the function so that their arguments are permuted
+class FuzzerPassPermuteFunctionParameters : public FuzzerPass {
+ public:
+  FuzzerPassPermuteFunctionParameters(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassPermuteFunctionParameters();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_PARAMETERS_H_

+ 50 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp

@@ -0,0 +1,50 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_swap_commutable_operands.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_swap_commutable_operands.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassSwapCommutableOperands::FuzzerPassSwapCommutableOperands(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassSwapCommutableOperands::~FuzzerPassSwapCommutableOperands() = default;
+
+void FuzzerPassSwapCommutableOperands::Apply() {
+  auto context = GetIRContext();
+  // Iterates over the module's instructions and checks whether it is
+  // commutative. In this case, the transformation is probabilistically applied.
+  context->module()->ForEachInst(
+      [this, context](opt::Instruction* instruction) {
+        if (spvOpcodeIsCommutativeBinaryOperator(instruction->opcode()) &&
+            GetFuzzerContext()->ChooseEven()) {
+          auto instructionDescriptor =
+              MakeInstructionDescriptor(context, instruction);
+          auto transformation =
+              TransformationSwapCommutableOperands(instructionDescriptor);
+          ApplyTransformation(transformation);
+        }
+      });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

@@ -0,0 +1,41 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_SWAP_COMMUTABLE_OPERANDS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_SWAP_COMMUTABLE_OPERANDS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// This fuzzer pass searches for all commutative instructions in the module,
+// probabilistically choosing which of these instructions will have its input
+// operands swapped.
+class FuzzerPassSwapCommutableOperands : public FuzzerPass {
+ public:
+  FuzzerPassSwapCommutableOperands(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassSwapCommutableOperands();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_SWAP_COMMUTABLE_OPERANDS_H_

+ 54 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp

@@ -0,0 +1,54 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_toggle_access_chain_instruction.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_toggle_access_chain_instruction.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassToggleAccessChainInstruction::FuzzerPassToggleAccessChainInstruction(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassToggleAccessChainInstruction::
+    ~FuzzerPassToggleAccessChainInstruction() = default;
+
+void FuzzerPassToggleAccessChainInstruction::Apply() {
+  auto context = GetIRContext();
+  // Iterates over the module's instructions and checks whether it is
+  // OpAccessChain or OpInBoundsAccessChain. In this case, the transformation is
+  // probabilistically applied.
+  context->module()->ForEachInst([this,
+                                  context](opt::Instruction* instruction) {
+    SpvOp opcode = instruction->opcode();
+    if ((opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain) &&
+        GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()->GetChanceOfTogglingAccessChainInstruction())) {
+      auto instructionDescriptor =
+          MakeInstructionDescriptor(context, instruction);
+      auto transformation =
+          TransformationToggleAccessChainInstruction(instructionDescriptor);
+      ApplyTransformation(transformation);
+    }
+  });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 40 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h

@@ -0,0 +1,40 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
+#define SOURCE_FUZZ_FUZZER_PASS_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// This fuzzer pass searches for all access chain instructions in the module,
+// probabilistically choosing which of these instructions will be toggled.
+class FuzzerPassToggleAccessChainInstruction : public FuzzerPass {
+ public:
+  FuzzerPassToggleAccessChainInstruction(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassToggleAccessChainInstruction();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_

+ 179 - 33
3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp

@@ -262,44 +262,48 @@ std::vector<uint32_t> RepeatedFieldToVector(
   return result;
 }
 
+uint32_t WalkOneCompositeTypeIndex(opt::IRContext* context,
+                                   uint32_t base_object_type_id,
+                                   uint32_t index) {
+  auto should_be_composite_type =
+      context->get_def_use_mgr()->GetDef(base_object_type_id);
+  assert(should_be_composite_type && "The type should exist.");
+  switch (should_be_composite_type->opcode()) {
+    case SpvOpTypeArray: {
+      auto array_length = GetArraySize(*should_be_composite_type, context);
+      if (array_length == 0 || index >= array_length) {
+        return 0;
+      }
+      return should_be_composite_type->GetSingleWordInOperand(0);
+    }
+    case SpvOpTypeMatrix:
+    case SpvOpTypeVector: {
+      auto count = should_be_composite_type->GetSingleWordInOperand(1);
+      if (index >= count) {
+        return 0;
+      }
+      return should_be_composite_type->GetSingleWordInOperand(0);
+    }
+    case SpvOpTypeStruct: {
+      if (index >= GetNumberOfStructMembers(*should_be_composite_type)) {
+        return 0;
+      }
+      return should_be_composite_type->GetSingleWordInOperand(index);
+    }
+    default:
+      return 0;
+  }
+}
+
 uint32_t WalkCompositeTypeIndices(
     opt::IRContext* context, uint32_t base_object_type_id,
     const google::protobuf::RepeatedField<google::protobuf::uint32>& indices) {
   uint32_t sub_object_type_id = base_object_type_id;
   for (auto index : indices) {
-    auto should_be_composite_type =
-        context->get_def_use_mgr()->GetDef(sub_object_type_id);
-    assert(should_be_composite_type && "The type should exist.");
-    switch (should_be_composite_type->opcode()) {
-      case SpvOpTypeArray: {
-        auto array_length = GetArraySize(*should_be_composite_type, context);
-        if (array_length == 0 || index >= array_length) {
-          return 0;
-        }
-        sub_object_type_id =
-            should_be_composite_type->GetSingleWordInOperand(0);
-        break;
-      }
-      case SpvOpTypeMatrix:
-      case SpvOpTypeVector: {
-        auto count = should_be_composite_type->GetSingleWordInOperand(1);
-        if (index >= count) {
-          return 0;
-        }
-        sub_object_type_id =
-            should_be_composite_type->GetSingleWordInOperand(0);
-        break;
-      }
-      case SpvOpTypeStruct: {
-        if (index >= GetNumberOfStructMembers(*should_be_composite_type)) {
-          return 0;
-        }
-        sub_object_type_id =
-            should_be_composite_type->GetSingleWordInOperand(index);
-        break;
-      }
-      default:
-        return 0;
+    sub_object_type_id =
+        WalkOneCompositeTypeIndex(context, sub_object_type_id, index);
+    if (!sub_object_type_id) {
+      return 0;
     }
   }
   return sub_object_type_id;
@@ -391,6 +395,148 @@ uint32_t FindFunctionType(opt::IRContext* ir_context,
   return 0;
 }
 
+opt::Instruction* GetFunctionType(opt::IRContext* context,
+                                  const opt::Function* function) {
+  uint32_t type_id = function->DefInst().GetSingleWordInOperand(1);
+  return context->get_def_use_mgr()->GetDef(type_id);
+}
+
+opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id) {
+  for (auto& function : *ir_context->module()) {
+    if (function.result_id() == function_id) {
+      return &function;
+    }
+  }
+  return nullptr;
+}
+
+bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id) {
+  for (auto& entry_point : context->module()->entry_points()) {
+    if (entry_point.GetSingleWordInOperand(1) == function_id) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool IdIsAvailableAtUse(opt::IRContext* context,
+                        opt::Instruction* use_instruction,
+                        uint32_t use_input_operand_index, uint32_t id) {
+  auto defining_instruction = context->get_def_use_mgr()->GetDef(id);
+  auto enclosing_function =
+      context->get_instr_block(use_instruction)->GetParent();
+  // If the id a function parameter, it needs to be associated with the
+  // function containing the use.
+  if (defining_instruction->opcode() == SpvOpFunctionParameter) {
+    return InstructionIsFunctionParameter(defining_instruction,
+                                          enclosing_function);
+  }
+  if (!context->get_instr_block(id)) {
+    // The id must be at global scope.
+    return true;
+  }
+  if (defining_instruction == use_instruction) {
+    // It is not OK for a definition to use itself.
+    return false;
+  }
+  auto dominator_analysis = context->GetDominatorAnalysis(enclosing_function);
+  if (use_instruction->opcode() == SpvOpPhi) {
+    // In the case where the use is an operand to OpPhi, it is actually the
+    // *parent* block associated with the operand that must be dominated by
+    // the synonym.
+    auto parent_block =
+        use_instruction->GetSingleWordInOperand(use_input_operand_index + 1);
+    return dominator_analysis->Dominates(
+        context->get_instr_block(defining_instruction)->id(), parent_block);
+  }
+  return dominator_analysis->Dominates(defining_instruction, use_instruction);
+}
+
+bool IdIsAvailableBeforeInstruction(opt::IRContext* context,
+                                    opt::Instruction* instruction,
+                                    uint32_t id) {
+  auto defining_instruction = context->get_def_use_mgr()->GetDef(id);
+  auto enclosing_function = context->get_instr_block(instruction)->GetParent();
+  // If the id a function parameter, it needs to be associated with the
+  // function containing the instruction.
+  if (defining_instruction->opcode() == SpvOpFunctionParameter) {
+    return InstructionIsFunctionParameter(defining_instruction,
+                                          enclosing_function);
+  }
+  if (!context->get_instr_block(id)) {
+    // The id is at global scope.
+    return true;
+  }
+  if (defining_instruction == instruction) {
+    // The instruction is not available right before its own definition.
+    return false;
+  }
+  return context->GetDominatorAnalysis(enclosing_function)
+      ->Dominates(defining_instruction, instruction);
+}
+
+bool InstructionIsFunctionParameter(opt::Instruction* instruction,
+                                    opt::Function* function) {
+  if (instruction->opcode() != SpvOpFunctionParameter) {
+    return false;
+  }
+  bool found_parameter = false;
+  function->ForEachParam(
+      [instruction, &found_parameter](opt::Instruction* param) {
+        if (param == instruction) {
+          found_parameter = true;
+        }
+      });
+  return found_parameter;
+}
+
+uint32_t GetTypeId(opt::IRContext* context, uint32_t result_id) {
+  return context->get_def_use_mgr()->GetDef(result_id)->type_id();
+}
+
+uint32_t GetPointeeTypeIdFromPointerType(opt::Instruction* pointer_type_inst) {
+  assert(pointer_type_inst && pointer_type_inst->opcode() == SpvOpTypePointer &&
+         "Precondition: |pointer_type_inst| must be OpTypePointer.");
+  return pointer_type_inst->GetSingleWordInOperand(1);
+}
+
+uint32_t GetPointeeTypeIdFromPointerType(opt::IRContext* context,
+                                         uint32_t pointer_type_id) {
+  return GetPointeeTypeIdFromPointerType(
+      context->get_def_use_mgr()->GetDef(pointer_type_id));
+}
+
+SpvStorageClass GetStorageClassFromPointerType(
+    opt::Instruction* pointer_type_inst) {
+  assert(pointer_type_inst && pointer_type_inst->opcode() == SpvOpTypePointer &&
+         "Precondition: |pointer_type_inst| must be OpTypePointer.");
+  return static_cast<SpvStorageClass>(
+      pointer_type_inst->GetSingleWordInOperand(0));
+}
+
+SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context,
+                                               uint32_t pointer_type_id) {
+  return GetStorageClassFromPointerType(
+      context->get_def_use_mgr()->GetDef(pointer_type_id));
+}
+
+uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
+                             SpvStorageClass storage_class) {
+  for (auto& inst : context->types_values()) {
+    switch (inst.opcode()) {
+      case SpvOpTypePointer:
+        if (inst.GetSingleWordInOperand(0) == storage_class &&
+            inst.GetSingleWordInOperand(1) == pointee_type_id) {
+          return inst.result_id();
+        }
+        break;
+      default:
+        break;
+    }
+  }
+  return 0;
+}
+
 }  // namespace fuzzerutil
 
 }  // namespace fuzz

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

@@ -98,6 +98,21 @@ bool IsCompositeType(const opt::analysis::Type* type);
 std::vector<uint32_t> RepeatedFieldToVector(
     const google::protobuf::RepeatedField<uint32_t>& repeated_field);
 
+// Given a type id, |base_object_type_id|, returns 0 if the type is not a
+// composite type or if |index| is too large to be used as an index into the
+// composite.  Otherwise returns the type id of the type associated with the
+// composite's index.
+//
+// Example: if |base_object_type_id| is 10, and we have:
+//
+// %10 = OpTypeStruct %3 %4 %5
+//
+// then 3 will be returned if |index| is 0, 5 if |index| is 2, and 0 if index
+// is 3 or larger.
+uint32_t WalkOneCompositeTypeIndex(opt::IRContext* context,
+                                   uint32_t base_object_type_id,
+                                   uint32_t index);
+
 // Given a type id, |base_object_type_id|, checks that the given sequence of
 // |indices| is suitable for indexing into this type.  Returns the id of the
 // type of the final sub-object reached via the indices if they are valid, and
@@ -137,6 +152,63 @@ bool IsMergeOrContinue(opt::IRContext* ir_context, uint32_t block_id);
 uint32_t FindFunctionType(opt::IRContext* ir_context,
                           const std::vector<uint32_t>& type_ids);
 
+// Returns a type instruction (OpTypeFunction) for |function|.
+// Returns |nullptr| if type is not found.
+opt::Instruction* GetFunctionType(opt::IRContext* context,
+                                  const opt::Function* function);
+
+// Returns the function with result id |function_id|, or |nullptr| if no such
+// function exists.
+opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id);
+
+// Returns |true| if one of entry points has function id |function_id|.
+bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id);
+
+// Checks whether |id| is available (according to dominance rules) at the use
+// point defined by input operand |use_input_operand_index| of
+// |use_instruction|.
+bool IdIsAvailableAtUse(opt::IRContext* context,
+                        opt::Instruction* use_instruction,
+                        uint32_t use_input_operand_index, uint32_t id);
+
+// Checks whether |id| is available (according to dominance rules) at the
+// program point directly before |instruction|.
+bool IdIsAvailableBeforeInstruction(opt::IRContext* context,
+                                    opt::Instruction* instruction, uint32_t id);
+
+// Returns true if and only if |instruction| is an OpFunctionParameter
+// associated with |function|.
+bool InstructionIsFunctionParameter(opt::Instruction* instruction,
+                                    opt::Function* function);
+
+// Returns the type id of the instruction defined by |result_id|, or 0 if there
+// is no such result id.
+uint32_t GetTypeId(opt::IRContext* context, uint32_t result_id);
+
+// Given |pointer_type_inst|, which must be an OpTypePointer instruction,
+// returns the id of the associated pointee type.
+uint32_t GetPointeeTypeIdFromPointerType(opt::Instruction* pointer_type_inst);
+
+// Given |pointer_type_id|, which must be the id of a pointer type, returns the
+// id of the associated pointee type.
+uint32_t GetPointeeTypeIdFromPointerType(opt::IRContext* context,
+                                         uint32_t pointer_type_id);
+
+// Given |pointer_type_inst|, which must be an OpTypePointer instruction,
+// returns the associated storage class.
+SpvStorageClass GetStorageClassFromPointerType(
+    opt::Instruction* pointer_type_inst);
+
+// Given |pointer_type_id|, which must be the id of a pointer type, returns the
+// associated storage class.
+SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context,
+                                               uint32_t pointer_type_id);
+
+// Returns the id of a pointer with pointee type |pointee_type_id| and storage
+// class |storage_class|, if it exists, and 0 otherwise.
+uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
+                             SpvStorageClass storage_class);
+
 }  // namespace fuzzerutil
 
 }  // namespace fuzz

+ 226 - 26
3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto

@@ -168,12 +168,22 @@ message Fact {
     FactDataSynonym data_synonym_fact = 2;
     FactBlockIsDead block_is_dead_fact = 3;
     FactFunctionIsLivesafe function_is_livesafe_fact = 4;
-    FactValueOfVariableIsArbitrary value_of_variable_is_arbitrary = 5;
+    FactPointeeValueIsIrrelevant pointee_value_is_irrelevant_fact = 5;
+    FactIdEquation id_equation_fact = 6;
   }
 }
 
 // Keep fact message types in alphabetical order:
 
+message FactBlockIsDead {
+
+  // Records the fact that a block is guaranteed to be dynamically unreachable.
+  // This is useful because it informs the fuzzer that rather arbitrary changes
+  // can be made to this block.
+
+  uint32 block_id = 1;
+}
+
 message FactConstantUniform {
 
   // Records the fact that a uniform buffer element is guaranteed to be equal
@@ -203,15 +213,6 @@ message FactDataSynonym {
 
 }
 
-message FactBlockIsDead {
-
-  // Records the fact that a block is guaranteed to be dynamically unreachable.
-  // This is useful because it informs the fuzzer that rather arbitrary changes
-  // can be made to this block.
-
-  uint32 block_id = 1;
-}
-
 message FactFunctionIsLivesafe {
 
   // Records the fact that a function is guaranteed to be "livesafe", meaning
@@ -223,17 +224,40 @@ message FactFunctionIsLivesafe {
   uint32 function_id = 1;
 }
 
-message FactValueOfVariableIsArbitrary {
+message FactIdEquation {
 
-  // Records the fact that the value stored in the variable or function
-  // parameter with the given id can be arbitrary: the module's observable
-  // behaviour does not depend on it.  This means that arbitrary stores can be
-  // made to the variable, and that nothing can be guaranteed about values
-  // loaded from the variable.
+  // Records the fact that the equation:
+  //
+  // lhs_id = opcode rhs_id[0] rhs_id[1] ... rhs_id[N-1]
+  //
+  // holds; e.g. that the equation:
+  //
+  // %12 = OpIAdd %13 %14
+  //
+  // holds in the case where lhs_id is 12, rhs_id is [13, 14], and the opcode is
+  // OpIAdd.
+
+  // The left-hand-side of the equation.
+  uint32 lhs_id = 1;
+
+  // A SPIR-V opcode, from a restricted set of instructions for which equation
+  // facts make sense.
+  uint32 opcode = 2;
+
+  // The operands to the right-hand-side of the equation.
+  repeated uint32 rhs_id = 3;
 
-  // The result id of an OpVariable instruction, or an OpFunctionParameter
-  // instruction with pointer type
-  uint32 variable_id = 1;
+}
+
+message FactPointeeValueIsIrrelevant {
+
+  // Records the fact that value of the data pointed to by a pointer id does
+  // not influence the observable behaviour of the module.  This means that
+  // arbitrary stores can be made through the pointer, and that nothing can be
+  // guaranteed about the values that are loaded via the pointer.
+
+  // A result id of pointer type
+  uint32 pointer_id = 1;
 }
 
 message AccessChainClampingInfo {
@@ -339,12 +363,40 @@ message Transformation {
     TransformationAddGlobalUndef add_global_undef = 32;
     TransformationAddFunction add_function = 33;
     TransformationAddDeadBlock add_dead_block = 34;
+    TransformationAddLocalVariable add_local_variable = 35;
+    TransformationLoad load = 36;
+    TransformationStore store = 37;
+    TransformationFunctionCall function_call = 38;
+    TransformationAccessChain access_chain = 39;
+    TransformationEquationInstruction equation_instruction = 40;
+    TransformationSwapCommutableOperands swap_commutable_operands = 41;
+    TransformationPermuteFunctionParameters permute_function_parameters = 42;
+    TransformationToggleAccessChainInstruction toggle_access_chain_instruction = 43;
     // Add additional option using the next available number.
   }
 }
 
 // Keep transformation message types in alphabetical order:
 
+message TransformationAccessChain {
+
+  // Adds an access chain instruction based on a given pointer and indices.
+
+  // Result id for the access chain
+  uint32 fresh_id = 1;
+
+  // The pointer from which the access chain starts
+  uint32 pointer_id = 2;
+
+  // Zero or more access chain indices
+  repeated uint32 index_id = 3;
+
+  // A descriptor for an instruction in a block before which the new
+  // OpAccessChain instruction should be inserted
+  InstructionDescriptor instruction_to_insert_before = 4;
+
+}
+
 message TransformationAddConstantBoolean {
 
   // Supports adding the constants true and false to a module, which may be
@@ -504,15 +556,38 @@ message TransformationAddGlobalVariable {
   // The type of the global variable
   uint32 type_id = 2;
 
-  // Optional initializer; 0 if there is no initializer
+  // Initial value of the variable
   uint32 initializer_id = 3;
 
-  // True if and only if the value of the variable should be regarded, in
-  // general, as totally unknown and subject to change (even if, due to an
-  // initializer, the original value is known).  This is the case for variables
-  // added when a module is donated, for example, and means that stores to such
-  // variables can be performed in an arbitrary fashion.
-  bool value_is_arbitrary = 4;
+  // True if and only if the behaviour of the module should not depend on the
+  // value of the variable, in which case stores to the variable can be
+  // performed in an arbitrary fashion.
+  bool value_is_irrelevant = 4;
+
+}
+
+message TransformationAddLocalVariable {
+
+  // Adds a local variable of the given type (which must be a pointer with
+  // Function storage class) to the given function, initialized to the given
+  // id.
+
+  // Fresh id for the local variable
+  uint32 fresh_id = 1;
+
+  // The type of the local variable
+  uint32 type_id = 2;
+
+  // The id of the function to which the local variable should be added
+  uint32 function_id = 3;
+
+  // Initial value of the variable
+  uint32 initializer_id = 4;
+
+  // True if and only if the behaviour of the module should not depend on the
+  // value of the variable, in which case stores to the variable can be
+  // performed in an arbitrary fashion.
+  bool value_is_irrelevant = 5;
 
 }
 
@@ -707,6 +782,67 @@ message TransformationCopyObject {
 
 }
 
+message TransformationEquationInstruction {
+
+  // A transformation that adds an instruction to the module that defines an
+  // equation between its result id and input operand ids, such that the
+  // equation is guaranteed to hold at any program point where all ids involved
+  // are available (i.e. at any program point dominated by the instruction).
+
+  // The result id of the new instruction
+  uint32 fresh_id = 1;
+
+  // The instruction's opcode
+  uint32 opcode = 2;
+
+  // The input operands to the instruction
+  repeated uint32 in_operand_id = 3;
+
+  // A descriptor for an instruction in a block before which the new
+  // instruction should be inserted
+  InstructionDescriptor instruction_to_insert_before = 4;
+
+}
+
+message TransformationFunctionCall {
+
+  // A transformation that introduces an OpFunctionCall instruction.  The call
+  // must not make the module's call graph cyclic.  Beyond that, if the call
+  // is in a dead block it can be to any function with arbitrary suitably-typed
+  // arguments; otherwise it must be to a livesafe function, with injected
+  // variables as pointer arguments and arbitrary non-pointer arguments.
+
+  // A fresh id for the result of the call
+  uint32 fresh_id = 1;
+
+  // Id of the function to be called
+  uint32 callee_id = 2;
+
+  // Ids for arguments to the function
+  repeated uint32 argument_id = 3;
+
+  // A descriptor for an instruction in a block before which the new
+  // OpFunctionCall instruction should be inserted
+  InstructionDescriptor instruction_to_insert_before = 4;
+
+}
+
+message TransformationLoad {
+
+  // Transformation that adds an OpLoad instruction from a pointer into an id.
+
+  // The result of the load instruction
+  uint32 fresh_id = 1;
+
+  // The pointer to be loaded from
+  uint32 pointer_id = 2;
+
+  // A descriptor for an instruction in a block before which the new OpLoad
+  // instruction should be inserted
+  InstructionDescriptor instruction_to_insert_before = 3;
+
+}
+
 message TransformationMergeBlocks {
 
   // A transformation that merges a block with its predecessor.
@@ -773,6 +909,36 @@ message TransformationOutlineFunction {
 
 }
 
+message TransformationPermuteFunctionParameters {
+
+  // A transformation that, given a non-entry-point function taking n
+  // parameters and a permutation of the set [0, n-1]:
+  //   - Introduces a new function type that is the same as the original
+  //     function's type but with the order of arguments permuted
+  //     (only if it doesn't already exist)
+  //   - Changes the type of the function to this type
+  //   - Adjusts all calls to the function so that their arguments are permuted
+
+  // Function, whose parameters will be permuted
+  uint32 function_id = 1;
+
+  // |new_type_id| is a result id of a valid OpTypeFunction instruction.
+  // New type is valid if:
+  //   - it has the same number of operands as the old one
+  //   - function's result type is the same as the old one
+  //   - function's arguments are permuted according to |permutation| vector
+  uint32 new_type_id = 2;
+
+  // An array of size |n|, where |n| is a number of arguments to a function
+  // with |function_id|. For each i: 0 <= permutation[i] < n.
+  //
+  // i-th element of this array contains a position for an i-th
+  // function's argument (i.e. i-th argument will be permutation[i]-th
+  // after running this transformation)
+  repeated uint32 permutation = 3;
+
+}
+
 message TransformationReplaceBooleanConstantWithConstantBinary {
 
   // A transformation to capture replacing a use of a boolean constant with
@@ -919,6 +1085,40 @@ message TransformationSplitBlock {
 
 }
 
+message TransformationStore {
+
+  // Transformation that adds an OpStore instruction of an id to a pointer.
+
+  // The pointer to be stored to
+  uint32 pointer_id = 1;
+
+  // The value to be stored
+  uint32 value_id = 2;
+
+  // A descriptor for an instruction in a block before which the new OpStore
+  // instruction should be inserted
+  InstructionDescriptor instruction_to_insert_before = 3;
+
+}
+
+message TransformationSwapCommutableOperands {
+
+  // A transformation that swaps the operands of a commutative instruction.
+
+  // A descriptor for a commutative instruction
+  InstructionDescriptor instruction_descriptor = 1;
+
+}
+
+message TransformationToggleAccessChainInstruction {
+
+  // A transformation that toggles an access chain instruction.
+
+  // A descriptor for an access chain instruction
+  InstructionDescriptor instruction_descriptor = 1;
+
+}
+
 message TransformationVectorShuffle {
 
   // A transformation that adds a vector shuffle instruction.

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

@@ -17,6 +17,7 @@
 #include <cassert>
 
 #include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_access_chain.h"
 #include "source/fuzz/transformation_add_constant_boolean.h"
 #include "source/fuzz/transformation_add_constant_composite.h"
 #include "source/fuzz/transformation_add_constant_scalar.h"
@@ -26,6 +27,7 @@
 #include "source/fuzz/transformation_add_function.h"
 #include "source/fuzz/transformation_add_global_undef.h"
 #include "source/fuzz/transformation_add_global_variable.h"
+#include "source/fuzz/transformation_add_local_variable.h"
 #include "source/fuzz/transformation_add_no_contraction_decoration.h"
 #include "source/fuzz/transformation_add_type_array.h"
 #include "source/fuzz/transformation_add_type_boolean.h"
@@ -39,9 +41,13 @@
 #include "source/fuzz/transformation_composite_construct.h"
 #include "source/fuzz/transformation_composite_extract.h"
 #include "source/fuzz/transformation_copy_object.h"
+#include "source/fuzz/transformation_equation_instruction.h"
+#include "source/fuzz/transformation_function_call.h"
+#include "source/fuzz/transformation_load.h"
 #include "source/fuzz/transformation_merge_blocks.h"
 #include "source/fuzz/transformation_move_block_down.h"
 #include "source/fuzz/transformation_outline_function.h"
+#include "source/fuzz/transformation_permute_function_parameters.h"
 #include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
 #include "source/fuzz/transformation_replace_constant_with_uniform.h"
 #include "source/fuzz/transformation_replace_id_with_synonym.h"
@@ -50,6 +56,9 @@
 #include "source/fuzz/transformation_set_memory_operands_mask.h"
 #include "source/fuzz/transformation_set_selection_control.h"
 #include "source/fuzz/transformation_split_block.h"
+#include "source/fuzz/transformation_store.h"
+#include "source/fuzz/transformation_swap_commutable_operands.h"
+#include "source/fuzz/transformation_toggle_access_chain_instruction.h"
 #include "source/fuzz/transformation_vector_shuffle.h"
 #include "source/util/make_unique.h"
 
@@ -61,6 +70,8 @@ Transformation::~Transformation() = default;
 std::unique_ptr<Transformation> Transformation::FromMessage(
     const protobufs::Transformation& message) {
   switch (message.transformation_case()) {
+    case protobufs::Transformation::TransformationCase::kAccessChain:
+      return MakeUnique<TransformationAccessChain>(message.access_chain());
     case protobufs::Transformation::TransformationCase::kAddConstantBoolean:
       return MakeUnique<TransformationAddConstantBoolean>(
           message.add_constant_boolean());
@@ -85,6 +96,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
     case protobufs::Transformation::TransformationCase::kAddGlobalVariable:
       return MakeUnique<TransformationAddGlobalVariable>(
           message.add_global_variable());
+    case protobufs::Transformation::TransformationCase::kAddLocalVariable:
+      return MakeUnique<TransformationAddLocalVariable>(
+          message.add_local_variable());
     case protobufs::Transformation::TransformationCase::
         kAddNoContractionDecoration:
       return MakeUnique<TransformationAddNoContractionDecoration>(
@@ -118,6 +132,13 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
           message.composite_extract());
     case protobufs::Transformation::TransformationCase::kCopyObject:
       return MakeUnique<TransformationCopyObject>(message.copy_object());
+    case protobufs::Transformation::TransformationCase::kEquationInstruction:
+      return MakeUnique<TransformationEquationInstruction>(
+          message.equation_instruction());
+    case protobufs::Transformation::TransformationCase::kFunctionCall:
+      return MakeUnique<TransformationFunctionCall>(message.function_call());
+    case protobufs::Transformation::TransformationCase::kLoad:
+      return MakeUnique<TransformationLoad>(message.load());
     case protobufs::Transformation::TransformationCase::kMergeBlocks:
       return MakeUnique<TransformationMergeBlocks>(message.merge_blocks());
     case protobufs::Transformation::TransformationCase::kMoveBlockDown:
@@ -125,6 +146,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
     case protobufs::Transformation::TransformationCase::kOutlineFunction:
       return MakeUnique<TransformationOutlineFunction>(
           message.outline_function());
+    case protobufs::Transformation::TransformationCase::
+        kPermuteFunctionParameters:
+      return MakeUnique<TransformationPermuteFunctionParameters>(
+          message.permute_function_parameters());
     case protobufs::Transformation::TransformationCase::
         kReplaceBooleanConstantWithConstantBinary:
       return MakeUnique<TransformationReplaceBooleanConstantWithConstantBinary>(
@@ -150,6 +175,15 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
           message.set_selection_control());
     case protobufs::Transformation::TransformationCase::kSplitBlock:
       return MakeUnique<TransformationSplitBlock>(message.split_block());
+    case protobufs::Transformation::TransformationCase::kStore:
+      return MakeUnique<TransformationStore>(message.store());
+    case protobufs::Transformation::TransformationCase::kSwapCommutableOperands:
+      return MakeUnique<TransformationSwapCommutableOperands>(
+          message.swap_commutable_operands());
+    case protobufs::Transformation::TransformationCase::
+        kToggleAccessChainInstruction:
+      return MakeUnique<TransformationToggleAccessChainInstruction>(
+          message.toggle_access_chain_instruction());
     case protobufs::Transformation::TransformationCase::kVectorShuffle:
       return MakeUnique<TransformationVectorShuffle>(message.vector_shuffle());
     case protobufs::Transformation::TRANSFORMATION_NOT_SET:

+ 215 - 0
3rdparty/spirv-tools/source/fuzz/transformation_access_chain.cpp

@@ -0,0 +1,215 @@
+// 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/transformation_access_chain.h"
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAccessChain::TransformationAccessChain(
+    const spvtools::fuzz::protobufs::TransformationAccessChain& message)
+    : message_(message) {}
+
+TransformationAccessChain::TransformationAccessChain(
+    uint32_t fresh_id, uint32_t pointer_id,
+    const std::vector<uint32_t>& index_id,
+    const protobufs::InstructionDescriptor& instruction_to_insert_before) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_pointer_id(pointer_id);
+  for (auto id : index_id) {
+    message_.add_index_id(id);
+  }
+  *message_.mutable_instruction_to_insert_before() =
+      instruction_to_insert_before;
+}
+
+bool TransformationAccessChain::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // The result id must be fresh
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  // The pointer id must exist and have a type.
+  auto pointer = context->get_def_use_mgr()->GetDef(message_.pointer_id());
+  if (!pointer || !pointer->type_id()) {
+    return false;
+  }
+  // The type must indeed be a pointer
+  auto pointer_type = context->get_def_use_mgr()->GetDef(pointer->type_id());
+  if (pointer_type->opcode() != SpvOpTypePointer) {
+    return false;
+  }
+
+  // The described instruction to insert before must exist and be a suitable
+  // point where an OpAccessChain instruction could be inserted.
+  auto instruction_to_insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), context);
+  if (!instruction_to_insert_before) {
+    return false;
+  }
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
+          SpvOpAccessChain, instruction_to_insert_before)) {
+    return false;
+  }
+
+  // Do not allow making an access chain from a null or undefined pointer, as
+  // we do not want to allow accessing such pointers.  This might be acceptable
+  // in dead blocks, but we conservatively avoid it.
+  switch (pointer->opcode()) {
+    case SpvOpConstantNull:
+    case SpvOpUndef:
+      // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3185): When
+      //  fuzzing for real we would like an 'assert(false)' here.  But we also
+      //  want to be able to write negative unit tests.
+      return false;
+    default:
+      break;
+  }
+
+  // The pointer on which the access chain is to be based needs to be available
+  // (according to dominance rules) at the insertion point.
+  if (!fuzzerutil::IdIsAvailableBeforeInstruction(
+          context, instruction_to_insert_before, message_.pointer_id())) {
+    return false;
+  }
+
+  // We now need to use the given indices to walk the type structure of the
+  // base type of the pointer, making sure that (a) the indices correspond to
+  // integers, and (b) these integer values are in-bounds.
+
+  // Start from the base type of the pointer.
+  uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
+
+  // 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(context, index_id);
+    if (!maybe_index_value.first) {
+      // There was no integer: this index is no good.
+      return false;
+    }
+    // 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(
+        context, subobject_type_id, maybe_index_value.second);
+    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.
+      return false;
+    }
+  }
+  // At this point, |subobject_type_id| is the type of the value targeted by
+  // the new access chain.  The result type of the access chain should be a
+  // pointer to this type, with the same storage class as for the original
+  // pointer.  Such a pointer type needs to exist in the module.
+  //
+  // We do not use the type manager to look up this type, due to problems
+  // associated with pointers to isomorphic structs being regarded as the same.
+  return fuzzerutil::MaybeGetPointerType(
+             context, subobject_type_id,
+             static_cast<SpvStorageClass>(
+                 pointer_type->GetSingleWordInOperand(0))) != 0;
+}
+
+void TransformationAccessChain::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+  // The operands to the access chain are the pointer followed by the indices.
+  // The result type of the access chain is determined by where the indices
+  // lead.  We thus push the pointer to a sequence of operands, and then follow
+  // the indices, pushing each to the operand list and tracking the type
+  // obtained by following it.  Ultimately this yields the type of the
+  // component reached by following all the indices, and the result type is
+  // a pointer to this component type.
+  opt::Instruction::OperandList operands;
+
+  // Add the pointer id itself.
+  operands.push_back({SPV_OPERAND_TYPE_ID, {message_.pointer_id()}});
+
+  // Start walking the indices, starting with the pointer's base type.
+  auto pointer_type = context->get_def_use_mgr()->GetDef(
+      context->get_def_use_mgr()->GetDef(message_.pointer_id())->type_id());
+  uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
+
+  // 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(context, index_id).second;
+    // Walk to the next type in the composite object using this index.
+    subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
+        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.
+  uint32_t result_type = fuzzerutil::MaybeGetPointerType(
+      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.
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  FindInstruction(message_.instruction_to_insert_before(), context)
+      ->InsertBefore(
+          MakeUnique<opt::Instruction>(context, SpvOpAccessChain, result_type,
+                                       message_.fresh_id(), operands));
+
+  // Conservatively invalidate all analyses.
+  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 (fact_manager->PointeeValueIsIrrelevant(message_.pointer_id())) {
+    fact_manager->AddFactValueOfPointeeIsIrrelevant(message_.fresh_id());
+  }
+}
+
+protobufs::Transformation TransformationAccessChain::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_access_chain() = message_;
+  return result;
+}
+
+std::pair<bool, uint32_t> TransformationAccessChain::GetIndexValue(
+    opt::IRContext* context, uint32_t index_id) const {
+  auto index_instruction = 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.
+    return {false, 0};
+  }
+  auto index_type =
+      context->get_def_use_mgr()->GetDef(index_instruction->type_id());
+  if (index_type->opcode() != SpvOpTypeInt ||
+      index_type->GetSingleWordInOperand(0) != 32) {
+    return {false, 0};
+  }
+  return {true, index_instruction->GetSingleWordInOperand(0)};
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 80 - 0
3rdparty/spirv-tools/source/fuzz/transformation_access_chain.h

@@ -0,0 +1,80 @@
+// 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_ACCESS_CHAIN_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ACCESS_CHAIN_H_
+
+#include <utility>
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAccessChain : public Transformation {
+ public:
+  explicit TransformationAccessChain(
+      const protobufs::TransformationAccessChain& message);
+
+  TransformationAccessChain(
+      uint32_t fresh_id, uint32_t pointer_id,
+      const std::vector<uint32_t>& index_id,
+      const protobufs::InstructionDescriptor& instruction_to_insert_before);
+
+  // - |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
+  // - |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
+  //   such that it is possible to walk the pointee type of
+  //   |message_.pointer_id| using these indices, remaining in-bounds.
+  // - 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|
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an instruction of the form:
+  //   |message_.fresh_id| = OpAccessChain %ptr |message_.index_id|
+  // where %ptr is the result if of an instruction declaring a pointer to the
+  // type reached by walking the pointee type of |message_.pointer_id| using
+  // the indices in |message_.index_id|, and with the same storage class as
+  // |message_.pointer_id|.
+  //
+  // If |fact_manager| 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 pointee value is also recorded.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  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.
+  std::pair<bool, uint32_t> GetIndexValue(opt::IRContext* context,
+                                          uint32_t index_id) const;
+
+  protobufs::TransformationAccessChain message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ACCESS_CHAIN_H_

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

@@ -112,7 +112,7 @@ bool TransformationAddDeadContinue::IsApplicable(
   // clone, and check whether the transformed clone is valid.
   //
   // In principle some of the above checks could be removed, with more reliance
-  // being places on the validator.  This should be revisited if we are sure
+  // being placed on the validator.  This should be revisited if we are sure
   // the validator is complete with respect to checking structured control flow
   // rules.
   auto cloned_context = fuzzerutil::CloneIRContext(context);

+ 19 - 9
3rdparty/spirv-tools/source/fuzz/transformation_add_function.cpp

@@ -132,17 +132,27 @@ bool TransformationAddFunction::IsApplicable(
     return false;
   }
 
+  // Check whether the cloned module is still valid after adding the function.
+  // If it is not, the transformation is not applicable.
+  if (!fuzzerutil::IsValid(cloned_module.get())) {
+    return false;
+  }
+
   if (message_.is_livesafe()) {
-    // We make the cloned module livesafe.
     if (!TryToMakeFunctionLivesafe(cloned_module.get(), fact_manager)) {
       return false;
     }
+    // After making the function livesafe, we check validity of the module
+    // again.  This is because the turning of OpKill, OpUnreachable and OpReturn
+    // instructions into branches changes control flow graph reachability, which
+    // has the potential to make the module invalid when it was otherwise valid.
+    // It is simpler to rely on the validator to guard against this than to
+    // consider all scenarios when making a function livesafe.
+    if (!fuzzerutil::IsValid(cloned_module.get())) {
+      return false;
+    }
   }
-
-  // Having managed to add the new function to the cloned module, and
-  // potentially also made it livesafe, we ascertain whether the cloned module
-  // is still valid.  If it is, the transformation is applicable.
-  return fuzzerutil::IsValid(cloned_module.get());
+  return true;
 }
 
 void TransformationAddFunction::Apply(
@@ -155,7 +165,7 @@ void TransformationAddFunction::Apply(
                     // that |success| is not used).
 
   // Record the fact that all pointer parameters and variables declared in the
-  // function should be regarded as having arbitrary values.  This allows other
+  // function should be regarded as having irrelevant values.  This allows other
   // passes to store arbitrarily to such variables, and to pass them freely as
   // parameters to other functions knowing that it is OK if they get
   // over-written.
@@ -165,12 +175,12 @@ void TransformationAddFunction::Apply(
         if (context->get_def_use_mgr()
                 ->GetDef(instruction.result_type_id())
                 ->opcode() == SpvOpTypePointer) {
-          fact_manager->AddFactValueOfVariableIsArbitrary(
+          fact_manager->AddFactValueOfPointeeIsIrrelevant(
               instruction.result_id());
         }
         break;
       case SpvOpVariable:
-        fact_manager->AddFactValueOfVariableIsArbitrary(
+        fact_manager->AddFactValueOfPointeeIsIrrelevant(
             instruction.result_id());
         break;
       default:

+ 17 - 19
3rdparty/spirv-tools/source/fuzz/transformation_add_global_variable.cpp

@@ -25,11 +25,11 @@ TransformationAddGlobalVariable::TransformationAddGlobalVariable(
 
 TransformationAddGlobalVariable::TransformationAddGlobalVariable(
     uint32_t fresh_id, uint32_t type_id, uint32_t initializer_id,
-    bool value_is_arbitrary) {
+    bool value_is_irrelevant) {
   message_.set_fresh_id(fresh_id);
   message_.set_type_id(type_id);
   message_.set_initializer_id(initializer_id);
-  message_.set_value_is_arbitrary(value_is_arbitrary);
+  message_.set_value_is_irrelevant(value_is_irrelevant);
 }
 
 bool TransformationAddGlobalVariable::IsApplicable(
@@ -53,21 +53,19 @@ bool TransformationAddGlobalVariable::IsApplicable(
   if (pointer_type->storage_class() != SpvStorageClassPrivate) {
     return false;
   }
-  if (message_.initializer_id()) {
-    // The initializer id must be the id of a constant.  Check this with the
-    // constant manager.
-    auto constant_id = context->get_constant_mgr()->GetConstantsFromIds(
-        {message_.initializer_id()});
-    if (constant_id.empty()) {
-      return false;
-    }
-    assert(constant_id.size() == 1 &&
-           "We asked for the constant associated with a single id; we should "
-           "get a single constant.");
-    // The type of the constant must match the pointee type of the pointer.
-    if (pointer_type->pointee_type() != constant_id[0]->type()) {
-      return false;
-    }
+  // The initializer id must be the id of a constant.  Check this with the
+  // constant manager.
+  auto constant_id = context->get_constant_mgr()->GetConstantsFromIds(
+      {message_.initializer_id()});
+  if (constant_id.empty()) {
+    return false;
+  }
+  assert(constant_id.size() == 1 &&
+         "We asked for the constant associated with a single id; we should "
+         "get a single constant.");
+  // The type of the constant must match the pointee type of the pointer.
+  if (pointer_type->pointee_type() != constant_id[0]->type()) {
+    return false;
   }
   return true;
 }
@@ -101,8 +99,8 @@ void TransformationAddGlobalVariable::Apply(
     }
   }
 
-  if (message_.value_is_arbitrary()) {
-    fact_manager->AddFactValueOfVariableIsArbitrary(message_.fresh_id());
+  if (message_.value_is_irrelevant()) {
+    fact_manager->AddFactValueOfPointeeIsIrrelevant(message_.fresh_id());
   }
 
   // We have added an instruction to the module, so need to be careful about the

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

@@ -30,7 +30,7 @@ class TransformationAddGlobalVariable : public Transformation {
 
   TransformationAddGlobalVariable(uint32_t fresh_id, uint32_t type_id,
                                   uint32_t initializer_id,
-                                  bool value_is_arbitrary);
+                                  bool value_is_irrelevant);
 
   // - |message_.fresh_id| must be fresh
   // - |message_.type_id| must be the id of a pointer type with Private storage
@@ -44,6 +44,9 @@ class TransformationAddGlobalVariable : public Transformation {
   // |message_.type_id| and either no initializer or |message_.initializer_id|
   // as an initializer, depending on whether |message_.initializer_id| is 0.
   // The global variable has result id |message_.fresh_id|.
+  //
+  // If |message_.value_is_irrelevant| holds, adds a corresponding fact to
+  // |fact_manager|.
   void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
 
   protobufs::Transformation ToMessage() const override;

+ 98 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_local_variable.cpp

@@ -0,0 +1,98 @@
+// 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/transformation_add_local_variable.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddLocalVariable::TransformationAddLocalVariable(
+    const spvtools::fuzz::protobufs::TransformationAddLocalVariable& message)
+    : message_(message) {}
+
+TransformationAddLocalVariable::TransformationAddLocalVariable(
+    uint32_t fresh_id, uint32_t type_id, uint32_t function_id,
+    uint32_t initializer_id, bool value_is_irrelevant) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_type_id(type_id);
+  message_.set_function_id(function_id);
+  message_.set_initializer_id(initializer_id);
+  message_.set_value_is_irrelevant(value_is_irrelevant);
+}
+
+bool TransformationAddLocalVariable::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // The provided id must be fresh.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  // The pointer type id must indeed correspond to a pointer, and it must have
+  // function storage class.
+  auto type_instruction =
+      context->get_def_use_mgr()->GetDef(message_.type_id());
+  if (!type_instruction || type_instruction->opcode() != SpvOpTypePointer ||
+      type_instruction->GetSingleWordInOperand(0) != SpvStorageClassFunction) {
+    return false;
+  }
+  // The initializer must...
+  auto initializer_instruction =
+      context->get_def_use_mgr()->GetDef(message_.initializer_id());
+  // ... exist, ...
+  if (!initializer_instruction) {
+    return false;
+  }
+  // ... be a constant, ...
+  if (!spvOpcodeIsConstant(initializer_instruction->opcode())) {
+    return false;
+  }
+  // ... and have the same type as the pointee type.
+  if (initializer_instruction->type_id() !=
+      type_instruction->GetSingleWordInOperand(1)) {
+    return false;
+  }
+  // The function to which the local variable is to be added must exist.
+  return fuzzerutil::FindFunction(context, message_.function_id());
+}
+
+void TransformationAddLocalVariable::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  fuzzerutil::FindFunction(context, message_.function_id())
+      ->begin()
+      ->begin()
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          context, SpvOpVariable, message_.type_id(), message_.fresh_id(),
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_STORAGE_CLASS,
+                {
+
+                    SpvStorageClassFunction}},
+               {SPV_OPERAND_TYPE_ID, {message_.initializer_id()}}})));
+  if (message_.value_is_irrelevant()) {
+    fact_manager->AddFactValueOfPointeeIsIrrelevant(message_.fresh_id());
+  }
+  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddLocalVariable::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_local_variable() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 60 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_local_variable.h

@@ -0,0 +1,60 @@
+// 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_LOCAL_VARIABLE_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_LOCAL_VARIABLE_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddLocalVariable : public Transformation {
+ public:
+  explicit TransformationAddLocalVariable(
+      const protobufs::TransformationAddLocalVariable& message);
+
+  TransformationAddLocalVariable(uint32_t fresh_id, uint32_t type_id,
+                                 uint32_t function_id, uint32_t initializer_id,
+                                 bool value_is_irrelevant);
+
+  // - |message_.fresh_id| must not be used by the module
+  // - |message_.type_id| must be the id of a pointer type with Function
+  //   storage class
+  // - |message_.initializer_id| must be the id of a constant with the same
+  //   type as the pointer's pointee type
+  // - |message_.function_id| must be the id of a function
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an instruction to the start of |message_.function_id|, of the form:
+  //   |message_.fresh_id| = OpVariable |message_.type_id| Function
+  //                         |message_.initializer_id|
+  // If |message_.value_is_irrelevant| holds, adds a corresponding fact to
+  // |fact_manager|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAddLocalVariable message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_LOCAL_VARIABLE_H_

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

@@ -37,7 +37,7 @@ class TransformationAddTypeFunction : public Transformation {
   // - |message_.return_type_id| and each element of |message_.argument_type_id|
   //   must be the ids of non-function types
   // - The module must not contain an OpTypeFunction instruction defining a
-  //   function type with the signature provided by teh given return and
+  //   function type with the signature provided by the given return and
   //   argument types
   bool IsApplicable(opt::IRContext* context,
                     const FactManager& fact_manager) const override;

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

@@ -84,27 +84,8 @@ bool TransformationCompositeConstruct::IsApplicable(
   // Now check whether every component being used to initialize the composite is
   // available at the desired program point.
   for (auto& component : message_.component()) {
-    auto component_inst = context->get_def_use_mgr()->GetDef(component);
-    if (!context->get_instr_block(component)) {
-      // The component does not have a block; that means it is in global scope,
-      // which is OK. (Whether the component actually corresponds to an
-      // instruction is checked above when determining whether types are
-      // suitable.)
-      continue;
-    }
-    // Check whether the component is available.
-    if (insert_before->HasResultId() &&
-        insert_before->result_id() == component) {
-      // This constitutes trying to use an id right before it is defined.  The
-      // special case is needed due to an instruction always dominating itself.
-      return false;
-    }
-    if (!context
-             ->GetDominatorAnalysis(
-                 context->get_instr_block(&*insert_before)->GetParent())
-             ->Dominates(component_inst, &*insert_before)) {
-      // The instruction defining the component must dominate the instruction we
-      // wish to insert the composite before.
+    if (!fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before,
+                                                    component)) {
       return false;
     }
   }

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

@@ -64,20 +64,10 @@ bool TransformationCopyObject::IsApplicable(
     return false;
   }
 
-  // |message_object| must be available at the point where we want to add the
-  // copy. It is available if it is at global scope (in which case it has no
-  // block), or if it dominates the point of insertion but is different from the
-  // point of insertion.
-  //
-  // The reason why the object needs to be different from the insertion point is
-  // that the copy will be added *before* this point, and we do not want to
-  // insert it before the object's defining instruction.
-  return !context->get_instr_block(object_inst) ||
-         (object_inst != &*insert_before &&
-          context
-              ->GetDominatorAnalysis(
-                  context->get_instr_block(insert_before)->GetParent())
-              ->Dominates(object_inst, &*insert_before));
+  // |message_object| must be available directly before the point where we want
+  // to add the copy.
+  return fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before,
+                                                    message_.object());
 }
 
 void TransformationCopyObject::Apply(opt::IRContext* context,
@@ -105,6 +95,10 @@ void TransformationCopyObject::Apply(opt::IRContext* context,
   fact_manager->AddFactDataSynonym(MakeDataDescriptor(message_.object(), {}),
                                    MakeDataDescriptor(message_.fresh_id(), {}),
                                    context);
+
+  if (fact_manager->PointeeValueIsIrrelevant(message_.object())) {
+    fact_manager->AddFactValueOfPointeeIsIrrelevant(message_.fresh_id());
+  }
 }
 
 protobufs::Transformation TransformationCopyObject::ToMessage() const {

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

@@ -57,7 +57,10 @@ class TransformationCopyObject : public Transformation {
   //   is added directly before the instruction at |message_.insert_after_id| +
   //   |message_|.offset, where %ty is the type of |message_.object|.
   // - The fact that |message_.fresh_id| and |message_.object| are synonyms
-  //   is added to the fact manager.
+  //   is added to |fact_manager|.
+  // - If |message_.object| is a pointer whose pointee value is known to be
+  //   irrelevant, the analogous fact is added to |fact_manager| about
+  //   |message_.fresh_id|.
   void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
 
   protobufs::Transformation ToMessage() const override;

+ 186 - 0
3rdparty/spirv-tools/source/fuzz/transformation_equation_instruction.cpp

@@ -0,0 +1,186 @@
+// 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/transformation_equation_instruction.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationEquationInstruction::TransformationEquationInstruction(
+    const spvtools::fuzz::protobufs::TransformationEquationInstruction& message)
+    : message_(message) {}
+
+TransformationEquationInstruction::TransformationEquationInstruction(
+    uint32_t fresh_id, SpvOp opcode, const std::vector<uint32_t>& in_operand_id,
+    const protobufs::InstructionDescriptor& instruction_to_insert_before) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_opcode(opcode);
+  for (auto id : in_operand_id) {
+    message_.add_in_operand_id(id);
+  }
+  *message_.mutable_instruction_to_insert_before() =
+      instruction_to_insert_before;
+}
+
+bool TransformationEquationInstruction::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // The result id must be fresh.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+  // The instruction to insert before must exist.
+  auto insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), context);
+  if (!insert_before) {
+    return false;
+  }
+  // The input ids must all exist, not be OpUndef, and be available before this
+  // instruction.
+  for (auto id : message_.in_operand_id()) {
+    auto inst = context->get_def_use_mgr()->GetDef(id);
+    if (!inst) {
+      return false;
+    }
+    if (inst->opcode() == SpvOpUndef) {
+      return false;
+    }
+    if (!fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before,
+                                                    id)) {
+      return false;
+    }
+  }
+
+  return MaybeGetResultType(context) != 0;
+}
+
+void TransformationEquationInstruction::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+
+  opt::Instruction::OperandList in_operands;
+  std::vector<uint32_t> rhs_id;
+  for (auto id : message_.in_operand_id()) {
+    in_operands.push_back({SPV_OPERAND_TYPE_ID, {id}});
+    rhs_id.push_back(id);
+  }
+
+  FindInstruction(message_.instruction_to_insert_before(), context)
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          context, static_cast<SpvOp>(message_.opcode()),
+          MaybeGetResultType(context), message_.fresh_id(), in_operands));
+
+  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+
+  fact_manager->AddFactIdEquation(message_.fresh_id(),
+                                  static_cast<SpvOp>(message_.opcode()), rhs_id,
+                                  context);
+}
+
+protobufs::Transformation TransformationEquationInstruction::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_equation_instruction() = message_;
+  return result;
+}
+
+uint32_t TransformationEquationInstruction::MaybeGetResultType(
+    opt::IRContext* context) const {
+  switch (static_cast<SpvOp>(message_.opcode())) {
+    case SpvOpIAdd:
+    case SpvOpISub: {
+      if (message_.in_operand_id().size() != 2) {
+        return 0;
+      }
+      uint32_t first_operand_width = 0;
+      uint32_t first_operand_type_id = 0;
+      for (uint32_t index = 0; index < 2; index++) {
+        auto operand_inst =
+            context->get_def_use_mgr()->GetDef(message_.in_operand_id(index));
+        if (!operand_inst || !operand_inst->type_id()) {
+          return 0;
+        }
+        auto operand_type =
+            context->get_type_mgr()->GetType(operand_inst->type_id());
+        if (!(operand_type->AsInteger() ||
+              (operand_type->AsVector() &&
+               operand_type->AsVector()->element_type()->AsInteger()))) {
+          return 0;
+        }
+        uint32_t operand_width =
+            operand_type->AsInteger()
+                ? 1
+                : operand_type->AsVector()->element_count();
+        if (index == 0) {
+          first_operand_width = operand_width;
+          first_operand_type_id = operand_inst->type_id();
+        } else {
+          assert(first_operand_width != 0 &&
+                 "The first operand should have been processed.");
+          if (operand_width != first_operand_width) {
+            return 0;
+          }
+        }
+      }
+      assert(first_operand_type_id != 0 &&
+             "A type must have been found for the first operand.");
+      return first_operand_type_id;
+    }
+    case SpvOpLogicalNot: {
+      if (message_.in_operand_id().size() != 1) {
+        return 0;
+      }
+      auto operand_inst =
+          context->get_def_use_mgr()->GetDef(message_.in_operand_id(0));
+      if (!operand_inst || !operand_inst->type_id()) {
+        return 0;
+      }
+      auto operand_type =
+          context->get_type_mgr()->GetType(operand_inst->type_id());
+      if (!(operand_type->AsBool() ||
+            (operand_type->AsVector() &&
+             operand_type->AsVector()->element_type()->AsBool()))) {
+        return 0;
+      }
+      return operand_inst->type_id();
+    }
+    case SpvOpSNegate: {
+      if (message_.in_operand_id().size() != 1) {
+        return 0;
+      }
+      auto operand_inst =
+          context->get_def_use_mgr()->GetDef(message_.in_operand_id(0));
+      if (!operand_inst || !operand_inst->type_id()) {
+        return 0;
+      }
+      auto operand_type =
+          context->get_type_mgr()->GetType(operand_inst->type_id());
+      if (!(operand_type->AsInteger() ||
+            (operand_type->AsVector() &&
+             operand_type->AsVector()->element_type()->AsInteger()))) {
+        return 0;
+      }
+      return operand_inst->type_id();
+    }
+
+    default:
+      assert(false && "Inappropriate opcode for equation instruction.");
+      return 0;
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 76 - 0
3rdparty/spirv-tools/source/fuzz/transformation_equation_instruction.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 SOURCE_FUZZ_TRANSFORMATION_EQUATION_INSTRUCTION_H_
+#define SOURCE_FUZZ_TRANSFORMATION_EQUATION_INSTRUCTION_H_
+
+#include <vector>
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationEquationInstruction : public Transformation {
+ public:
+  explicit TransformationEquationInstruction(
+      const protobufs::TransformationEquationInstruction& message);
+
+  TransformationEquationInstruction(
+      uint32_t fresh_id, SpvOp opcode,
+      const std::vector<uint32_t>& in_operand_id,
+      const protobufs::InstructionDescriptor& instruction_to_insert_before);
+
+  // - |message_.fresh_id| must be fresh.
+  // - |message_.instruction_to_insert_before| must identify an instruction
+  //   before which an equation instruction can legitimately be inserted.
+  // - Each id in |message_.in_operand_id| must exist, not be an OpUndef, and
+  //   be available before |message_.instruction_to_insert_before|.
+  // - |message_.opcode| must be an opcode for which we know how to handle
+  //   equations, the types of the ids in |message_.in_operand_id| must be
+  //   suitable for use with this opcode, and the module must contain an
+  //   appropriate result type id.
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an instruction to the module, right before
+  // |message_.instruction_to_insert_before|, of the form:
+  //
+  // |message_.fresh_id| = |message_.opcode| %type |message_.in_operand_ids|
+  //
+  // where %type is a type id that already exists in the module and that is
+  // compatible with the opcode and input operands.
+  //
+  // The fact manager is also updated to inform it of this equation fact.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  // A helper that, in one fell swoop, checks that |message_.opcode| and the ids
+  // in |message_.in_operand_id| are compatible, and that the module contains
+  // an appropriate result type id.  If all is well, the result type id is
+  // returned.  Otherwise, 0 is returned.
+  uint32_t MaybeGetResultType(opt::IRContext* context) const;
+
+  protobufs::TransformationEquationInstruction message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_EQUATION_INSTRUCTION_H_

+ 185 - 0
3rdparty/spirv-tools/source/fuzz/transformation_function_call.cpp

@@ -0,0 +1,185 @@
+// 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/transformation_function_call.h"
+
+#include "source/fuzz/call_graph.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationFunctionCall::TransformationFunctionCall(
+    const spvtools::fuzz::protobufs::TransformationFunctionCall& message)
+    : message_(message) {}
+
+TransformationFunctionCall::TransformationFunctionCall(
+    uint32_t fresh_id, uint32_t callee_id,
+    const std::vector<uint32_t>& argument_id,
+    const protobufs::InstructionDescriptor& instruction_to_insert_before) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_callee_id(callee_id);
+  for (auto argument : argument_id) {
+    message_.add_argument_id(argument);
+  }
+  *message_.mutable_instruction_to_insert_before() =
+      instruction_to_insert_before;
+}
+
+bool TransformationFunctionCall::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& fact_manager) const {
+  // The result id must be fresh
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+
+  // The function must exist
+  auto callee_inst = context->get_def_use_mgr()->GetDef(message_.callee_id());
+  if (!callee_inst || callee_inst->opcode() != SpvOpFunction) {
+    return false;
+  }
+
+  // The function must not be an entry point
+  if (fuzzerutil::FunctionIsEntryPoint(context, message_.callee_id())) {
+    return false;
+  }
+
+  auto callee_type_inst = context->get_def_use_mgr()->GetDef(
+      callee_inst->GetSingleWordInOperand(1));
+  assert(callee_type_inst->opcode() == SpvOpTypeFunction &&
+         "Bad function type.");
+
+  // The number of expected function arguments must match the number of given
+  // arguments.  The number of expected arguments is one less than the function
+  // type's number of input operands, as one operand is for the return type.
+  if (callee_type_inst->NumInOperands() - 1 !=
+      static_cast<uint32_t>(message_.argument_id().size())) {
+    return false;
+  }
+
+  // The instruction descriptor must refer to a position where it is valid to
+  // insert the call
+  auto insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), context);
+  if (!insert_before) {
+    return false;
+  }
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpFunctionCall,
+                                                    insert_before)) {
+    return false;
+  }
+
+  auto block = context->get_instr_block(insert_before);
+  auto enclosing_function = block->GetParent();
+
+  // If the block is not dead, the function must be livesafe
+  bool block_is_dead = fact_manager.BlockIsDead(block->id());
+  if (!block_is_dead &&
+      !fact_manager.FunctionIsLivesafe(message_.callee_id())) {
+    return false;
+  }
+
+  // The ids must all match and have the right types and satisfy rules on
+  // pointers.  If the block is not dead, pointers must be arbitrary.
+  for (uint32_t arg_index = 0;
+       arg_index < static_cast<uint32_t>(message_.argument_id().size());
+       arg_index++) {
+    opt::Instruction* arg_inst =
+        context->get_def_use_mgr()->GetDef(message_.argument_id(arg_index));
+    if (!arg_inst) {
+      // The given argument does not correspond to an instruction.
+      return false;
+    }
+    if (!arg_inst->type_id()) {
+      // The given argument does not have a type; it is thus not suitable.
+    }
+    if (arg_inst->type_id() !=
+        callee_type_inst->GetSingleWordInOperand(arg_index + 1)) {
+      // Argument type mismatch.
+      return false;
+    }
+    opt::Instruction* arg_type_inst =
+        context->get_def_use_mgr()->GetDef(arg_inst->type_id());
+    if (arg_type_inst->opcode() == SpvOpTypePointer) {
+      switch (arg_inst->opcode()) {
+        case SpvOpFunctionParameter:
+        case SpvOpVariable:
+          // These are OK
+          break;
+        default:
+          // Other pointer ids cannot be passed as parameters
+          return false;
+      }
+      if (!block_is_dead &&
+          !fact_manager.PointeeValueIsIrrelevant(arg_inst->result_id())) {
+        // This is not a dead block, so pointer parameters passed to the called
+        // function might really have their contents modified. We thus require
+        // such pointers to be to arbitrary-valued variables, which this is not.
+        return false;
+      }
+    }
+
+    // The argument id needs to be available (according to dominance rules) at
+    // the point where the call will occur.
+    if (!fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before,
+                                                    arg_inst->result_id())) {
+      return false;
+    }
+  }
+
+  // Introducing the call must not lead to recursion.
+  if (message_.callee_id() == enclosing_function->result_id()) {
+    // This would be direct recursion.
+    return false;
+  }
+  // Ensure the call would not lead to indirect recursion.
+  return !CallGraph(context)
+              .GetIndirectCallees(message_.callee_id())
+              .count(block->GetParent()->result_id());
+}
+
+void TransformationFunctionCall::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  // Update the module's bound to reflect the fresh id for the result of the
+  // function call.
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  // Get the return type of the function being called.
+  uint32_t return_type =
+      context->get_def_use_mgr()->GetDef(message_.callee_id())->type_id();
+  // Populate the operands to the call instruction, with the function id and the
+  // arguments.
+  opt::Instruction::OperandList operands;
+  operands.push_back({SPV_OPERAND_TYPE_ID, {message_.callee_id()}});
+  for (auto arg : message_.argument_id()) {
+    operands.push_back({SPV_OPERAND_TYPE_ID, {arg}});
+  }
+  // Insert the function call before the instruction specified in the message.
+  FindInstruction(message_.instruction_to_insert_before(), context)
+      ->InsertBefore(
+          MakeUnique<opt::Instruction>(context, SpvOpFunctionCall, return_type,
+                                       message_.fresh_id(), operands));
+  // Invalidate all analyses since we have changed the module.
+  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationFunctionCall::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_function_call() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 65 - 0
3rdparty/spirv-tools/source/fuzz/transformation_function_call.h

@@ -0,0 +1,65 @@
+// 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_FUNCTION_CALL_H_
+#define SOURCE_FUZZ_TRANSFORMATION_FUNCTION_CALL_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationFunctionCall : public Transformation {
+ public:
+  explicit TransformationFunctionCall(
+      const protobufs::TransformationFunctionCall& message);
+
+  TransformationFunctionCall(
+      uint32_t fresh_id, uint32_t callee_id,
+      const std::vector<uint32_t>& argument_id,
+      const protobufs::InstructionDescriptor& instruction_to_insert_before);
+
+  // - |message_.fresh_id| must be fresh
+  // - |message_.instruction_to_insert_before| must identify an instruction
+  //   before which an OpFunctionCall can be legitimately inserted
+  // - |message_.function_id| must be the id of a function, and calling the
+  //   function before the identified instruction must not introduce recursion
+  // - |message_.arg_id| must provide suitable arguments for the function call
+  //   (they must have the right types and be available according to dominance
+  //   rules)
+  // - If the insertion point is not in a dead block then |message_function_id|
+  //   must refer to a livesafe function, and every pointer argument in
+  //   |message_.arg_id| must refer to an arbitrary-valued variable
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an instruction of the form:
+  //   |fresh_id| = OpFunctionCall %type |callee_id| |arg_id...|
+  // before |instruction_to_insert_before|, where %type is the return type of
+  // |callee_id|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationFunctionCall message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_FUNCTION_CALL_H_

+ 103 - 0
3rdparty/spirv-tools/source/fuzz/transformation_load.cpp

@@ -0,0 +1,103 @@
+// 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/transformation_load.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationLoad::TransformationLoad(
+    const spvtools::fuzz::protobufs::TransformationLoad& message)
+    : message_(message) {}
+
+TransformationLoad::TransformationLoad(
+    uint32_t fresh_id, uint32_t pointer_id,
+    const protobufs::InstructionDescriptor& instruction_to_insert_before) {
+  message_.set_fresh_id(fresh_id);
+  message_.set_pointer_id(pointer_id);
+  *message_.mutable_instruction_to_insert_before() =
+      instruction_to_insert_before;
+}
+
+bool TransformationLoad::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // The result id must be fresh.
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    return false;
+  }
+
+  // The pointer must exist and have a type.
+  auto pointer = context->get_def_use_mgr()->GetDef(message_.pointer_id());
+  if (!pointer || !pointer->type_id()) {
+    return false;
+  }
+  // The type must indeed be a pointer type.
+  auto pointer_type = context->get_def_use_mgr()->GetDef(pointer->type_id());
+  assert(pointer_type && "Type id must be defined.");
+  if (pointer_type->opcode() != SpvOpTypePointer) {
+    return false;
+  }
+  // We do not want to allow loading from null or undefined pointers, as it is
+  // not clear how punishing the consequences of doing so are from a semantics
+  // point of view.
+  switch (pointer->opcode()) {
+    case SpvOpConstantNull:
+    case SpvOpUndef:
+      return false;
+    default:
+      break;
+  }
+
+  // Determine which instruction we should be inserting before.
+  auto insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), context);
+  // It must exist, ...
+  if (!insert_before) {
+    return false;
+  }
+  // ... and it must be legitimate to insert a store before it.
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, insert_before)) {
+    return false;
+  }
+
+  // The pointer needs to be available at the insertion point.
+  return fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before,
+                                                    message_.pointer_id());
+}
+
+void TransformationLoad::Apply(opt::IRContext* context,
+                               spvtools::fuzz::FactManager* /*unused*/) const {
+  uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType(
+      context, fuzzerutil::GetTypeId(context, message_.pointer_id()));
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  FindInstruction(message_.instruction_to_insert_before(), context)
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          context, SpvOpLoad, result_type, message_.fresh_id(),
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}})));
+  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationLoad::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_load() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 59 - 0
3rdparty/spirv-tools/source/fuzz/transformation_load.h

@@ -0,0 +1,59 @@
+// 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_LOAD_H_
+#define SOURCE_FUZZ_TRANSFORMATION_LOAD_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationLoad : public Transformation {
+ public:
+  explicit TransformationLoad(const protobufs::TransformationLoad& message);
+
+  TransformationLoad(
+      uint32_t fresh_id, uint32_t pointer_id,
+      const protobufs::InstructionDescriptor& instruction_to_insert_before);
+
+  // - |message_.fresh_id| must be fresh
+  // - |message_.pointer_id| must be the id of a pointer
+  // - The pointer must not be OpConstantNull or OpUndef
+  // - |message_.instruction_to_insert_before| must identify an instruction
+  //   before which it is valid to insert an OpLoad, and where
+  //   |message_.pointer_id| is available (according to dominance rules)
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an instruction of the form:
+  //   |message_.fresh_id| = OpLoad %type |message_.pointer_id|
+  // before the instruction identified by
+  // |message_.instruction_to_insert_before|, where %type is the pointer's
+  // pointee type.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationLoad message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_LOAD_H_

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

@@ -617,10 +617,10 @@ TransformationOutlineFunction::PrepareFunctionPrototype(
         context, SpvOpFunctionParameter,
         context->get_def_use_mgr()->GetDef(id)->type_id(),
         input_id_to_fresh_id_map.at(id), opt::Instruction::OperandList()));
-    // If the input id is an arbitrary-valued variable, the same should be true
+    // If the input id is an irrelevant-valued variable, the same should be true
     // of the corresponding parameter.
-    if (fact_manager->VariableValueIsArbitrary(id)) {
-      fact_manager->AddFactValueOfVariableIsArbitrary(
+    if (fact_manager->PointeeValueIsIrrelevant(id)) {
+      fact_manager->AddFactValueOfPointeeIsIrrelevant(
           input_id_to_fresh_id_map.at(id));
     }
   }

+ 184 - 0
3rdparty/spirv-tools/source/fuzz/transformation_permute_function_parameters.cpp

@@ -0,0 +1,184 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// 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 <unordered_set>
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_permute_function_parameters.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationPermuteFunctionParameters::
+    TransformationPermuteFunctionParameters(
+        const spvtools::fuzz::protobufs::
+            TransformationPermuteFunctionParameters& message)
+    : message_(message) {}
+
+TransformationPermuteFunctionParameters::
+    TransformationPermuteFunctionParameters(
+        uint32_t function_id, uint32_t new_type_id,
+        const std::vector<uint32_t>& permutation) {
+  message_.set_function_id(function_id);
+  message_.set_new_type_id(new_type_id);
+
+  for (auto index : permutation) {
+    message_.add_permutation(index);
+  }
+}
+
+bool TransformationPermuteFunctionParameters::IsApplicable(
+    opt::IRContext* context, const FactManager& /*unused*/) const {
+  // Check that function exists
+  const auto* function =
+      fuzzerutil::FindFunction(context, message_.function_id());
+  if (!function || function->DefInst().opcode() != SpvOpFunction ||
+      fuzzerutil::FunctionIsEntryPoint(context, function->result_id())) {
+    return false;
+  }
+
+  // Check that permutation has valid indices
+  const auto* function_type = fuzzerutil::GetFunctionType(context, function);
+  assert(function_type && "Function type is null");
+
+  const auto& permutation = message_.permutation();
+
+  // Don't take return type into account
+  auto arg_size = function_type->NumInOperands() - 1;
+
+  // |permutation| vector should be equal to the number of arguments
+  if (static_cast<uint32_t>(permutation.size()) != arg_size) {
+    return false;
+  }
+
+  // Check that all indices are valid
+  // and unique integers from the [0, n-1] set
+  std::unordered_set<uint32_t> unique_indices;
+  for (auto index : permutation) {
+    // We don't compare |index| with 0 since it's an unsigned integer
+    if (index >= arg_size) {
+      return false;
+    }
+
+    unique_indices.insert(index);
+  }
+
+  // Check that permutation doesn't have duplicated values
+  assert(unique_indices.size() == arg_size && "Permutation has duplicates");
+
+  // Check that new function's type is valid:
+  //   - Has the same number of operands
+  //   - Has the same result type as the old one
+  //   - Order of arguments is permuted
+  auto new_type_id = message_.new_type_id();
+  const auto* new_type = context->get_def_use_mgr()->GetDef(new_type_id);
+
+  if (!new_type || new_type->opcode() != SpvOpTypeFunction ||
+      new_type->NumInOperands() != function_type->NumInOperands()) {
+    return false;
+  }
+
+  // Check that both instructions have the same result type
+  if (new_type->GetSingleWordInOperand(0) !=
+      function_type->GetSingleWordInOperand(0)) {
+    return false;
+  }
+
+  // Check that new function type has its arguments permuted
+  for (int i = 0, n = static_cast<int>(permutation.size()); i < n; ++i) {
+    // +1 to take return type into account
+    if (new_type->GetSingleWordInOperand(i + 1) !=
+        function_type->GetSingleWordInOperand(permutation[i] + 1)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void TransformationPermuteFunctionParameters::Apply(
+    opt::IRContext* context, FactManager* /*unused*/) const {
+  // Retrieve all data from the message
+  uint32_t function_id = message_.function_id();
+  uint32_t new_type_id = message_.new_type_id();
+  const auto& permutation = message_.permutation();
+
+  // Find the function that will be transformed
+  auto* function = fuzzerutil::FindFunction(context, function_id);
+  assert(function && "Can't find the function");
+
+  // Change function's type
+  function->DefInst().SetInOperand(1, {new_type_id});
+
+  // Adjust OpFunctionParameter instructions
+
+  // Collect ids and types from OpFunctionParameter instructions
+  std::vector<uint32_t> param_id, param_type;
+  function->ForEachParam(
+      [&param_id, &param_type](const opt::Instruction* param) {
+        param_id.push_back(param->result_id());
+        param_type.push_back(param->type_id());
+      });
+
+  // Permute parameters' ids and types
+  std::vector<uint32_t> permuted_param_id, permuted_param_type;
+  for (auto index : permutation) {
+    permuted_param_id.push_back(param_id[index]);
+    permuted_param_type.push_back(param_type[index]);
+  }
+
+  // Set OpFunctionParameter instructions to point to new parameters
+  size_t i = 0;
+  function->ForEachParam(
+      [&i, &permuted_param_id, &permuted_param_type](opt::Instruction* param) {
+        param->SetResultType(permuted_param_type[i]);
+        param->SetResultId(permuted_param_id[i]);
+        ++i;
+      });
+
+  // Fix all OpFunctionCall instructions
+  context->get_def_use_mgr()->ForEachUser(
+      &function->DefInst(),
+      [function_id, &permutation](opt::Instruction* call) {
+        if (call->opcode() != SpvOpFunctionCall ||
+            call->GetSingleWordInOperand(0) != function_id) {
+          return;
+        }
+
+        opt::Instruction::OperandList call_operands = {
+            call->GetInOperand(0)  // Function id
+        };
+
+        for (auto index : permutation) {
+          // Take function id into account
+          call_operands.push_back(call->GetInOperand(index + 1));
+        }
+
+        call->SetInOperands(std::move(call_operands));
+      });
+
+  // Make sure our changes are analyzed
+  context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationPermuteFunctionParameters::ToMessage()
+    const {
+  protobufs::Transformation result;
+  *result.mutable_permute_function_parameters() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 61 - 0
3rdparty/spirv-tools/source/fuzz/transformation_permute_function_parameters.h

@@ -0,0 +1,61 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// 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_PERMUTE_FUNCTION_PARAMETERS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_PERMUTE_FUNCTION_PARAMETERS_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationPermuteFunctionParameters : public Transformation {
+ public:
+  explicit TransformationPermuteFunctionParameters(
+      const protobufs::TransformationPermuteFunctionParameters& message);
+
+  TransformationPermuteFunctionParameters(
+      uint32_t function_id, uint32_t new_type_id,
+      const std::vector<uint32_t>& permutation);
+
+  // - |function_id| is a valid non-entry-point OpFunction instruction
+  // - |new_type_id| is a result id of a valid OpTypeFunction instruction.
+  //   New type is valid if:
+  //     - it has the same number of operands as the old one
+  //     - function's result type is the same as the old one
+  //     - function's arguments are permuted according to |permutation| vector
+  // - |permutation| is a set of [0..(n - 1)], where n is a number of arguments
+  //   to the function
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // - OpFunction instruction with |result_id == function_id| is changed.
+  //   Its arguments are permuted according to the |permutation| vector
+  // - Changed function gets a new type specified by |type_id|
+  // - Calls to the function are adjusted accordingly
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationPermuteFunctionParameters message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_PERMUTE_FUNCTION_PARAMETERS_H_

+ 16 - 5
3rdparty/spirv-tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp

@@ -243,11 +243,22 @@ bool TransformationReplaceBooleanConstantWithConstantBinary::IsApplicable(
     return false;
   }
 
-  // The instruction must not be an OpPhi, as we cannot insert a binary
-  // operator instruction before an OpPhi.
-  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902): there is
-  //  scope for being less conservative.
-  return instruction->opcode() != SpvOpPhi;
+  switch (instruction->opcode()) {
+    case SpvOpPhi:
+      // The instruction must not be an OpPhi, as we cannot insert a binary
+      // operator instruction before an OpPhi.
+      // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902): there is
+      //  scope for being less conservative.
+      return false;
+    case SpvOpVariable:
+      // The instruction must not be an OpVariable, because (a) we cannot insert
+      // a binary operator before an OpVariable, but in any case (b) the
+      // constant we would be replacing is the initializer constant of the
+      // OpVariable, and this cannot be the result of a binary operation.
+      return false;
+    default:
+      return true;
+  }
 }
 
 void TransformationReplaceBooleanConstantWithConstantBinary::Apply(

+ 6 - 0
3rdparty/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.cpp

@@ -154,6 +154,12 @@ bool TransformationReplaceConstantWithUniform::IsApplicable(
     return false;
   }
 
+  // The use must not be a variable initializer; these are required to be
+  // constants, so it would be illegal to replace one with a uniform access.
+  if (instruction_using_constant->opcode() == SpvOpVariable) {
+    return false;
+  }
+
   // The module needs to have a uniform pointer type suitable for indexing into
   // the uniform variable, i.e. matching the type of the constant we wish to
   // replace with a uniform.

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

@@ -65,9 +65,9 @@ bool TransformationReplaceIdWithSynonym::IsApplicable(
 
   // The transformation is applicable if the synonymous id is available at the
   // use point.
-  return IdsIsAvailableAtUse(context, use_instruction,
-                             message_.id_use_descriptor().in_operand_index(),
-                             message_.synonymous_id());
+  return fuzzerutil::IdIsAvailableAtUse(
+      context, use_instruction, message_.id_use_descriptor().in_operand_index(),
+      message_.synonymous_id());
 }
 
 void TransformationReplaceIdWithSynonym::Apply(
@@ -88,30 +88,6 @@ protobufs::Transformation TransformationReplaceIdWithSynonym::ToMessage()
   return result;
 }
 
-bool TransformationReplaceIdWithSynonym::IdsIsAvailableAtUse(
-    opt::IRContext* context, opt::Instruction* use_instruction,
-    uint32_t use_input_operand_index, uint32_t id) {
-  if (!context->get_instr_block(id)) {
-    return true;
-  }
-  auto defining_instruction = context->get_def_use_mgr()->GetDef(id);
-  if (defining_instruction == use_instruction) {
-    return false;
-  }
-  auto dominator_analysis = context->GetDominatorAnalysis(
-      context->get_instr_block(use_instruction)->GetParent());
-  if (use_instruction->opcode() == SpvOpPhi) {
-    // In the case where the use is an operand to OpPhi, it is actually the
-    // *parent* block associated with the operand that must be dominated by
-    // the synonym.
-    auto parent_block =
-        use_instruction->GetSingleWordInOperand(use_input_operand_index + 1);
-    return dominator_analysis->Dominates(
-        context->get_instr_block(defining_instruction)->id(), parent_block);
-  }
-  return dominator_analysis->Dominates(defining_instruction, use_instruction);
-}
-
 bool TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
     opt::IRContext* context, opt::Instruction* use_instruction,
     uint32_t use_in_operand_index) {

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

@@ -51,14 +51,6 @@ class TransformationReplaceIdWithSynonym : public Transformation {
 
   protobufs::Transformation ToMessage() const override;
 
-  // Checks whether the |id| is available (according to dominance rules) at the
-  // use point defined by input operand |use_input_operand_index| of
-  // |use_instruction|.
-  static bool IdsIsAvailableAtUse(opt::IRContext* context,
-                                  opt::Instruction* use_instruction,
-                                  uint32_t use_input_operand_index,
-                                  uint32_t id);
-
   // Checks whether various conditions hold related to the acceptability of
   // replacing the id use at |use_in_operand_index| of |use_instruction| with
   // a synonym.  In particular, this checks that:

+ 128 - 0
3rdparty/spirv-tools/source/fuzz/transformation_store.cpp

@@ -0,0 +1,128 @@
+// 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/transformation_store.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationStore::TransformationStore(
+    const spvtools::fuzz::protobufs::TransformationStore& message)
+    : message_(message) {}
+
+TransformationStore::TransformationStore(
+    uint32_t pointer_id, uint32_t value_id,
+    const protobufs::InstructionDescriptor& instruction_to_insert_before) {
+  message_.set_pointer_id(pointer_id);
+  message_.set_value_id(value_id);
+  *message_.mutable_instruction_to_insert_before() =
+      instruction_to_insert_before;
+}
+
+bool TransformationStore::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& fact_manager) const {
+  // The pointer must exist and have a type.
+  auto pointer = context->get_def_use_mgr()->GetDef(message_.pointer_id());
+  if (!pointer || !pointer->type_id()) {
+    return false;
+  }
+
+  // The pointer type must indeed be a pointer.
+  auto pointer_type = context->get_def_use_mgr()->GetDef(pointer->type_id());
+  assert(pointer_type && "Type id must be defined.");
+  if (pointer_type->opcode() != SpvOpTypePointer) {
+    return false;
+  }
+
+  // The pointer must not be read only.
+  if (pointer_type->GetSingleWordInOperand(0) == SpvStorageClassInput) {
+    return false;
+  }
+
+  // We do not want to allow storing to null or undefined pointers.
+  switch (pointer->opcode()) {
+    case SpvOpConstantNull:
+    case SpvOpUndef:
+      return false;
+    default:
+      break;
+  }
+
+  // Determine which instruction we should be inserting before.
+  auto insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), context);
+  // It must exist, ...
+  if (!insert_before) {
+    return false;
+  }
+  // ... and it must be legitimate to insert a store before it.
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore,
+                                                    insert_before)) {
+    return false;
+  }
+
+  // The block we are inserting into needs to be dead, or else the pointee type
+  // of the pointer we are storing to needs to be irrelevant (otherwise the
+  // store could impact on the observable behaviour of the module).
+  if (!fact_manager.BlockIsDead(
+          context->get_instr_block(insert_before)->id()) &&
+      !fact_manager.PointeeValueIsIrrelevant(message_.pointer_id())) {
+    return false;
+  }
+
+  // The value being stored needs to exist and have a type.
+  auto value = context->get_def_use_mgr()->GetDef(message_.value_id());
+  if (!value || !value->type_id()) {
+    return false;
+  }
+
+  // The type of the value must match the pointee type.
+  if (pointer_type->GetSingleWordInOperand(1) != value->type_id()) {
+    return false;
+  }
+
+  // The pointer needs to be available at the insertion point.
+  if (!fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before,
+                                                  message_.pointer_id())) {
+    return false;
+  }
+
+  // The value needs to be available at the insertion point.
+  return fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before,
+                                                    message_.value_id());
+}
+
+void TransformationStore::Apply(opt::IRContext* context,
+                                spvtools::fuzz::FactManager* /*unused*/) const {
+  FindInstruction(message_.instruction_to_insert_before(), context)
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          context, SpvOpStore, 0, 0,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
+               {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})));
+  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationStore::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_store() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 63 - 0
3rdparty/spirv-tools/source/fuzz/transformation_store.h

@@ -0,0 +1,63 @@
+// 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_STORE_H_
+#define SOURCE_FUZZ_TRANSFORMATION_STORE_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationStore : public Transformation {
+ public:
+  explicit TransformationStore(const protobufs::TransformationStore& message);
+
+  TransformationStore(
+      uint32_t pointer_id, uint32_t value_id,
+      const protobufs::InstructionDescriptor& instruction_to_insert_before);
+
+  // - |message_.pointer_id| must be the id of a pointer
+  // - The pointer type must not have read-only storage class
+  // - The pointer must not be OpConstantNull or OpUndef
+  // - |message_.value_id| must be an instruction result id that has the same
+  //   type as the pointee type of |message_.pointer_id|
+  // - |message_.instruction_to_insert_before| must identify an instruction
+  //   before which it is valid to insert an OpStore, and where both
+  //   |message_.pointer_id| and |message_.value_id| are available (according
+  //   to dominance rules)
+  // - Either the insertion point must be in a dead block, or it must be known
+  //   that the pointee value of |message_.pointer_id| is irrelevant
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds an instruction of the form:
+  //   OpStore |pointer_id| |value_id|
+  // before the instruction identified by
+  // |message_.instruction_to_insert_before|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationStore message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_STORE_H_

+ 66 - 0
3rdparty/spirv-tools/source/fuzz/transformation_swap_commutable_operands.cpp

@@ -0,0 +1,66 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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/transformation_swap_commutable_operands.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationSwapCommutableOperands::TransformationSwapCommutableOperands(
+    const spvtools::fuzz::protobufs::TransformationSwapCommutableOperands&
+        message)
+    : message_(message) {}
+
+TransformationSwapCommutableOperands::TransformationSwapCommutableOperands(
+    const protobufs::InstructionDescriptor& instruction_descriptor) {
+  *message_.mutable_instruction_descriptor() = instruction_descriptor;
+}
+
+bool TransformationSwapCommutableOperands::IsApplicable(
+    opt::IRContext* context, const spvtools::fuzz::FactManager& /*unused*/
+    ) const {
+  auto instruction =
+      FindInstruction(message_.instruction_descriptor(), context);
+  if (instruction == nullptr) return false;
+
+  SpvOp opcode = static_cast<SpvOp>(
+      message_.instruction_descriptor().target_instruction_opcode());
+  assert(instruction->opcode() == opcode &&
+         "The located instruction must have the same opcode as in the "
+         "descriptor.");
+  return spvOpcodeIsCommutativeBinaryOperator(opcode);
+}
+
+void TransformationSwapCommutableOperands::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/
+    ) const {
+  auto instruction =
+      FindInstruction(message_.instruction_descriptor(), context);
+  // By design, the instructions defined to be commutative have exactly two
+  // input parameters.
+  std::swap(instruction->GetInOperand(0), instruction->GetInOperand(1));
+}
+
+protobufs::Transformation TransformationSwapCommutableOperands::ToMessage()
+    const {
+  protobufs::Transformation result;
+  *result.mutable_swap_commutable_operands() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

@@ -0,0 +1,51 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_SWAP_COMMUTABLE_OPERANDS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_SWAP_COMMUTABLE_OPERANDS_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationSwapCommutableOperands : public Transformation {
+ public:
+  explicit TransformationSwapCommutableOperands(
+      const protobufs::TransformationSwapCommutableOperands& message);
+
+  TransformationSwapCommutableOperands(
+      const protobufs::InstructionDescriptor& instruction_descriptor);
+
+  // - |message_.instruction_descriptor| must identify an existing
+  // commutative instruction
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Swaps the commutable operands.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationSwapCommutableOperands message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_SWAP_COMMUTABLE_OPERANDS_H_

+ 83 - 0
3rdparty/spirv-tools/source/fuzz/transformation_toggle_access_chain_instruction.cpp

@@ -0,0 +1,83 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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/transformation_toggle_access_chain_instruction.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationToggleAccessChainInstruction::
+    TransformationToggleAccessChainInstruction(
+        const spvtools::fuzz::protobufs::
+            TransformationToggleAccessChainInstruction& message)
+    : message_(message) {}
+
+TransformationToggleAccessChainInstruction::
+    TransformationToggleAccessChainInstruction(
+        const protobufs::InstructionDescriptor& instruction_descriptor) {
+  *message_.mutable_instruction_descriptor() = instruction_descriptor;
+}
+
+bool TransformationToggleAccessChainInstruction::IsApplicable(
+    opt::IRContext* context, const spvtools::fuzz::FactManager& /*unused*/
+    ) const {
+  auto instruction =
+      FindInstruction(message_.instruction_descriptor(), context);
+  if (instruction == nullptr) {
+    return false;
+  }
+
+  SpvOp opcode = static_cast<SpvOp>(
+      message_.instruction_descriptor().target_instruction_opcode());
+
+  assert(instruction->opcode() == opcode &&
+         "The located instruction must have the same opcode as in the "
+         "descriptor.");
+
+  if (opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain) {
+    return true;
+  }
+
+  return false;
+}
+
+void TransformationToggleAccessChainInstruction::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/
+    ) const {
+  auto instruction =
+      FindInstruction(message_.instruction_descriptor(), context);
+  SpvOp opcode = instruction->opcode();
+
+  if (opcode == SpvOpAccessChain) {
+    instruction->SetOpcode(SpvOpInBoundsAccessChain);
+  } else {
+    assert(opcode == SpvOpInBoundsAccessChain &&
+           "The located instruction must be an OpInBoundsAccessChain "
+           "instruction.");
+    instruction->SetOpcode(SpvOpAccessChain);
+  }
+}
+
+protobufs::Transformation
+TransformationToggleAccessChainInstruction::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_toggle_access_chain_instruction() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

@@ -0,0 +1,51 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
+#define SOURCE_FUZZ_TRANSFORMATION_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationToggleAccessChainInstruction : public Transformation {
+ public:
+  explicit TransformationToggleAccessChainInstruction(
+      const protobufs::TransformationToggleAccessChainInstruction& message);
+
+  TransformationToggleAccessChainInstruction(
+      const protobufs::InstructionDescriptor& instruction_descriptor);
+
+  // - |message_.instruction_descriptor| must identify an existing
+  //   access chain instruction
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Toggles the access chain instruction.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationToggleAccessChainInstruction message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_

+ 40 - 3
3rdparty/spirv-tools/source/opcode.cpp

@@ -181,11 +181,15 @@ void spvInstructionCopy(const uint32_t* words, const SpvOp opcode,
   }
 }
 
-const char* spvOpcodeString(const SpvOp opcode) {
+const char* spvOpcodeString(const uint32_t opcode) {
   const auto beg = kOpcodeTableEntries;
   const auto end = kOpcodeTableEntries + ARRAY_SIZE(kOpcodeTableEntries);
-  spv_opcode_desc_t needle = {"",    opcode, 0, nullptr, 0,   {},
-                              false, false,  0, nullptr, ~0u, ~0u};
+  spv_opcode_desc_t needle = {"",    static_cast<SpvOp>(opcode),
+                              0,     nullptr,
+                              0,     {},
+                              false, false,
+                              0,     nullptr,
+                              ~0u,   ~0u};
   auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) {
     return lhs.opcode < rhs.opcode;
   };
@@ -608,6 +612,39 @@ bool spvOpcodeIsDebug(SpvOp opcode) {
   }
 }
 
+bool spvOpcodeIsCommutativeBinaryOperator(SpvOp opcode) {
+  switch (opcode) {
+    case SpvOpPtrEqual:
+    case SpvOpPtrNotEqual:
+    case SpvOpIAdd:
+    case SpvOpFAdd:
+    case SpvOpIMul:
+    case SpvOpFMul:
+    case SpvOpDot:
+    case SpvOpIAddCarry:
+    case SpvOpUMulExtended:
+    case SpvOpSMulExtended:
+    case SpvOpBitwiseOr:
+    case SpvOpBitwiseXor:
+    case SpvOpBitwiseAnd:
+    case SpvOpOrdered:
+    case SpvOpUnordered:
+    case SpvOpLogicalEqual:
+    case SpvOpLogicalNotEqual:
+    case SpvOpLogicalOr:
+    case SpvOpLogicalAnd:
+    case SpvOpIEqual:
+    case SpvOpINotEqual:
+    case SpvOpFOrdEqual:
+    case SpvOpFUnordEqual:
+    case SpvOpFOrdNotEqual:
+    case SpvOpFUnordNotEqual:
+      return true;
+    default:
+      return false;
+  }
+}
+
 std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode) {
   switch (opcode) {
     case SpvOpMemoryBarrier:

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

@@ -56,9 +56,6 @@ void spvInstructionCopy(const uint32_t* words, const SpvOp opcode,
                         const uint16_t word_count,
                         const spv_endianness_t endian, spv_instruction_t* inst);
 
-// Gets the name of an instruction, without the "Op" prefix.
-const char* spvOpcodeString(const SpvOp opcode);
-
 // Determine if the given opcode is a scalar type. Returns zero if false,
 // non-zero otherwise.
 int32_t spvOpcodeIsScalarType(const SpvOp opcode);
@@ -133,6 +130,10 @@ bool spvOpcodeIsScalarizable(SpvOp opcode);
 // Returns true if the given opcode is a debug instruction.
 bool spvOpcodeIsDebug(SpvOp opcode);
 
+// Returns true for opcodes that are binary operators,
+// where the order of the operands is irrelevant.
+bool spvOpcodeIsCommutativeBinaryOperator(SpvOp opcode);
+
 // Returns a vector containing the indices of the memory semantics <id>
 // operands for |opcode|.
 std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode);

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

@@ -58,6 +58,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
   inline_pass.h
   inst_bindless_check_pass.h
   inst_buff_addr_check_pass.h
+  inst_debug_printf_pass.h
   instruction.h
   instruction_list.h
   instrument_pass.h
@@ -164,6 +165,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
   inline_pass.cpp
   inst_bindless_check_pass.cpp
   inst_buff_addr_check_pass.cpp
+  inst_debug_printf_pass.cpp
   instruction.cpp
   instruction_list.cpp
   instrument_pass.cpp

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

@@ -47,6 +47,11 @@ void FeatureManager::AddExtension(Instruction* ext) {
   }
 }
 
+void FeatureManager::RemoveExtension(Extension ext) {
+  if (!extensions_.Contains(ext)) return;
+  extensions_.Remove(ext);
+}
+
 void FeatureManager::AddCapability(SpvCapability cap) {
   if (capabilities_.Contains(cap)) return;
 
@@ -60,6 +65,11 @@ void FeatureManager::AddCapability(SpvCapability cap) {
   }
 }
 
+void FeatureManager::RemoveCapability(SpvCapability cap) {
+  if (!capabilities_.Contains(cap)) return;
+  capabilities_.Remove(cap);
+}
+
 void FeatureManager::AddCapabilities(Module* module) {
   for (Instruction& inst : module->capabilities()) {
     AddCapability(static_cast<SpvCapability>(inst.GetSingleWordInOperand(0)));

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

@@ -30,11 +30,17 @@ class FeatureManager {
   // Returns true if |ext| is an enabled extension in the module.
   bool HasExtension(Extension ext) const { return extensions_.Contains(ext); }
 
+  // Removes the given |extension| from the current FeatureManager.
+  void RemoveExtension(Extension extension);
+
   // Returns true if |cap| is an enabled capability in the module.
   bool HasCapability(SpvCapability cap) const {
     return capabilities_.Contains(cap);
   }
 
+  // Removes the given |capability| from the current FeatureManager.
+  void RemoveCapability(SpvCapability capability);
+
   // Analyzes |module| and records enabled extensions and capabilities.
   void Analyze(Module* module);
 

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

@@ -0,0 +1,266 @@
+// Copyright (c) 2020 The Khronos Group Inc.
+// Copyright (c) 2020 Valve Corporation
+// Copyright (c) 2020 LunarG Inc.
+//
+// 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 "inst_debug_printf_pass.h"
+
+#include "spirv/unified1/NonSemanticDebugPrintf.h"
+
+namespace spvtools {
+namespace opt {
+
+void InstDebugPrintfPass::GenOutputValues(Instruction* val_inst,
+                                          std::vector<uint32_t>* val_ids,
+                                          InstructionBuilder* builder) {
+  uint32_t val_ty_id = val_inst->type_id();
+  analysis::TypeManager* type_mgr = context()->get_type_mgr();
+  analysis::Type* val_ty = type_mgr->GetType(val_ty_id);
+  switch (val_ty->kind()) {
+    case analysis::Type::kVector: {
+      analysis::Vector* v_ty = val_ty->AsVector();
+      const analysis::Type* c_ty = v_ty->element_type();
+      uint32_t c_ty_id = type_mgr->GetId(c_ty);
+      for (uint32_t c = 0; c < v_ty->element_count(); ++c) {
+        Instruction* c_inst = builder->AddIdLiteralOp(
+            c_ty_id, SpvOpCompositeExtract, val_inst->result_id(), c);
+        GenOutputValues(c_inst, val_ids, builder);
+      }
+      return;
+    }
+    case analysis::Type::kBool: {
+      // Select between uint32 zero or one
+      uint32_t zero_id = builder->GetUintConstantId(0);
+      uint32_t one_id = builder->GetUintConstantId(1);
+      Instruction* sel_inst = builder->AddTernaryOp(
+          GetUintId(), SpvOpSelect, val_inst->result_id(), one_id, zero_id);
+      val_ids->push_back(sel_inst->result_id());
+      return;
+    }
+    case analysis::Type::kFloat: {
+      analysis::Float* f_ty = val_ty->AsFloat();
+      switch (f_ty->width()) {
+        case 16: {
+          // Convert float16 to float32 and recurse
+          Instruction* f32_inst = builder->AddUnaryOp(
+              GetFloatId(), SpvOpFConvert, val_inst->result_id());
+          GenOutputValues(f32_inst, val_ids, builder);
+          return;
+        }
+        case 64: {
+          // Bitcast float64 to uint64 and recurse
+          Instruction* ui64_inst = builder->AddUnaryOp(
+              GetUint64Id(), SpvOpBitcast, val_inst->result_id());
+          GenOutputValues(ui64_inst, val_ids, builder);
+          return;
+        }
+        case 32: {
+          // Bitcase float32 to uint32
+          Instruction* bc_inst = builder->AddUnaryOp(GetUintId(), SpvOpBitcast,
+                                                     val_inst->result_id());
+          val_ids->push_back(bc_inst->result_id());
+          return;
+        }
+        default:
+          assert(false && "unsupported float width");
+          return;
+      }
+    }
+    case analysis::Type::kInteger: {
+      analysis::Integer* i_ty = val_ty->AsInteger();
+      switch (i_ty->width()) {
+        case 64: {
+          Instruction* ui64_inst = val_inst;
+          if (i_ty->IsSigned()) {
+            // Bitcast sint64 to uint64
+            ui64_inst = builder->AddUnaryOp(GetUint64Id(), SpvOpBitcast,
+                                            val_inst->result_id());
+          }
+          // Break uint64 into 2x uint32
+          Instruction* lo_ui64_inst = builder->AddUnaryOp(
+              GetUintId(), SpvOpUConvert, ui64_inst->result_id());
+          Instruction* rshift_ui64_inst = builder->AddBinaryOp(
+              GetUint64Id(), SpvOpShiftRightLogical, ui64_inst->result_id(),
+              builder->GetUintConstantId(32));
+          Instruction* hi_ui64_inst = builder->AddUnaryOp(
+              GetUintId(), SpvOpUConvert, rshift_ui64_inst->result_id());
+          val_ids->push_back(lo_ui64_inst->result_id());
+          val_ids->push_back(hi_ui64_inst->result_id());
+          return;
+        }
+        case 8: {
+          Instruction* ui8_inst = val_inst;
+          if (i_ty->IsSigned()) {
+            // Bitcast sint8 to uint8
+            ui8_inst = builder->AddUnaryOp(GetUint8Id(), SpvOpBitcast,
+                                           val_inst->result_id());
+          }
+          // Convert uint8 to uint32
+          Instruction* ui32_inst = builder->AddUnaryOp(
+              GetUintId(), SpvOpUConvert, ui8_inst->result_id());
+          val_ids->push_back(ui32_inst->result_id());
+          return;
+        }
+        case 32: {
+          Instruction* ui32_inst = val_inst;
+          if (i_ty->IsSigned()) {
+            // Bitcast sint32 to uint32
+            ui32_inst = builder->AddUnaryOp(GetUintId(), SpvOpBitcast,
+                                            val_inst->result_id());
+          }
+          // uint32 needs no further processing
+          val_ids->push_back(ui32_inst->result_id());
+          return;
+        }
+        default:
+          // TODO(greg-lunarg): Support non-32-bit int
+          assert(false && "unsupported int width");
+          return;
+      }
+    }
+    default:
+      assert(false && "unsupported type");
+      return;
+  }
+}
+
+void InstDebugPrintfPass::GenOutputCode(
+    Instruction* printf_inst, uint32_t stage_idx,
+    std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
+  BasicBlock* back_blk_ptr = &*new_blocks->back();
+  InstructionBuilder builder(
+      context(), back_blk_ptr,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  // Gen debug printf record validation-specific values. The format string
+  // will have its id written. Vectors will need to be broken down into
+  // component values. float16 will need to be converted to float32. Pointer
+  // and uint64 will need to be converted to two uint32 values. float32 will
+  // need to be bitcast to uint32. int32 will need to be bitcast to uint32.
+  std::vector<uint32_t> val_ids;
+  bool is_first_operand = false;
+  printf_inst->ForEachInId(
+      [&is_first_operand, &val_ids, &builder, this](const uint32_t* iid) {
+        // skip set operand
+        if (!is_first_operand) {
+          is_first_operand = true;
+          return;
+        }
+        Instruction* opnd_inst = get_def_use_mgr()->GetDef(*iid);
+        if (opnd_inst->opcode() == SpvOpString) {
+          uint32_t string_id_id = builder.GetUintConstantId(*iid);
+          val_ids.push_back(string_id_id);
+        } else {
+          GenOutputValues(opnd_inst, &val_ids, &builder);
+        }
+      });
+  GenDebugStreamWrite(uid2offset_[printf_inst->unique_id()], stage_idx, val_ids,
+                      &builder);
+  context()->KillInst(printf_inst);
+}
+
+void InstDebugPrintfPass::GenDebugPrintfCode(
+    BasicBlock::iterator ref_inst_itr,
+    UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
+    std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
+  // If not DebugPrintf OpExtInst, return.
+  Instruction* printf_inst = &*ref_inst_itr;
+  if (printf_inst->opcode() != SpvOpExtInst) return;
+  if (printf_inst->GetSingleWordInOperand(0) != ext_inst_printf_id_) return;
+  if (printf_inst->GetSingleWordInOperand(1) !=
+      NonSemanticDebugPrintfDebugPrintf)
+    return;
+  // Initialize DefUse manager before dismantling module
+  (void)get_def_use_mgr();
+  // Move original block's preceding instructions into first new block
+  std::unique_ptr<BasicBlock> new_blk_ptr;
+  MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr);
+  new_blocks->push_back(std::move(new_blk_ptr));
+  // Generate instructions to output printf args to printf buffer
+  GenOutputCode(printf_inst, stage_idx, new_blocks);
+  // Caller expects at least two blocks with last block containing remaining
+  // code, so end block after instrumentation, create remainder block, and
+  // branch to it
+  uint32_t rem_blk_id = TakeNextId();
+  std::unique_ptr<Instruction> rem_label(NewLabel(rem_blk_id));
+  BasicBlock* back_blk_ptr = &*new_blocks->back();
+  InstructionBuilder builder(
+      context(), back_blk_ptr,
+      IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+  (void)builder.AddBranch(rem_blk_id);
+  // Gen remainder block
+  new_blk_ptr.reset(new BasicBlock(std::move(rem_label)));
+  builder.SetInsertPoint(&*new_blk_ptr);
+  // Move original block's remaining code into remainder block and add
+  // to new blocks
+  MovePostludeCode(ref_block_itr, &*new_blk_ptr);
+  new_blocks->push_back(std::move(new_blk_ptr));
+}
+
+void InstDebugPrintfPass::InitializeInstDebugPrintf() {
+  // Initialize base class
+  InitializeInstrument();
+}
+
+Pass::Status InstDebugPrintfPass::ProcessImpl() {
+  // Perform printf instrumentation on each entry point function in module
+  InstProcessFunction pfn =
+      [this](BasicBlock::iterator ref_inst_itr,
+             UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
+             std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
+        return GenDebugPrintfCode(ref_inst_itr, ref_block_itr, stage_idx,
+                                  new_blocks);
+      };
+  (void)InstProcessEntryPointCallTree(pfn);
+  // Remove DebugPrintf OpExtInstImport instruction
+  Instruction* ext_inst_import_inst =
+      get_def_use_mgr()->GetDef(ext_inst_printf_id_);
+  context()->KillInst(ext_inst_import_inst);
+  // If no remaining non-semantic instruction sets, remove non-semantic debug
+  // info extension from module and feature manager
+  bool non_sem_set_seen = false;
+  for (auto c_itr = context()->module()->ext_inst_import_begin();
+       c_itr != context()->module()->ext_inst_import_end(); ++c_itr) {
+    const char* set_name =
+        reinterpret_cast<const char*>(&c_itr->GetInOperand(0).words[0]);
+    const char* non_sem_str = "NonSemantic.";
+    if (!strncmp(set_name, non_sem_str, strlen(non_sem_str))) {
+      non_sem_set_seen = true;
+      break;
+    }
+  }
+  if (!non_sem_set_seen) {
+    for (auto c_itr = context()->module()->extension_begin();
+         c_itr != context()->module()->extension_end(); ++c_itr) {
+      const char* ext_name =
+          reinterpret_cast<const char*>(&c_itr->GetInOperand(0).words[0]);
+      if (!strcmp(ext_name, "SPV_KHR_non_semantic_info")) {
+        context()->KillInst(&*c_itr);
+        break;
+      }
+    }
+    context()->get_feature_mgr()->RemoveExtension(kSPV_KHR_non_semantic_info);
+  }
+  return Status::SuccessWithChange;
+}
+
+Pass::Status InstDebugPrintfPass::Process() {
+  ext_inst_printf_id_ =
+      get_module()->GetExtInstImportId("NonSemantic.DebugPrintf");
+  if (ext_inst_printf_id_ == 0) return Status::SuccessWithoutChange;
+  InitializeInstDebugPrintf();
+  return ProcessImpl();
+}
+
+}  // namespace opt
+}  // namespace spvtools

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

@@ -0,0 +1,96 @@
+// Copyright (c) 2020 The Khronos Group Inc.
+// Copyright (c) 2020 Valve Corporation
+// Copyright (c) 2020 LunarG Inc.
+//
+// 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 LIBSPIRV_OPT_INST_DEBUG_PRINTF_PASS_H_
+#define LIBSPIRV_OPT_INST_DEBUG_PRINTF_PASS_H_
+
+#include "instrument_pass.h"
+
+namespace spvtools {
+namespace opt {
+
+// This class/pass is designed to support the debug printf GPU-assisted layer
+// of https://github.com/KhronosGroup/Vulkan-ValidationLayers. Its internal and
+// external design may change as the layer evolves.
+class InstDebugPrintfPass : public InstrumentPass {
+ public:
+  // For test harness only
+  InstDebugPrintfPass()
+      : InstrumentPass(7, 23, kInstValidationIdDebugPrintf, 2) {}
+  // For all other interfaces
+  InstDebugPrintfPass(uint32_t desc_set, uint32_t shader_id)
+      : InstrumentPass(desc_set, shader_id, kInstValidationIdDebugPrintf, 2) {}
+
+  ~InstDebugPrintfPass() override = default;
+
+  // See optimizer.hpp for pass user documentation.
+  Status Process() override;
+
+  const char* name() const override { return "inst-printf-pass"; }
+
+ private:
+  // Generate instructions for OpDebugPrintf.
+  //
+  // If |ref_inst_itr| is an OpDebugPrintf, return in |new_blocks| the result
+  // of replacing it with buffer write instructions within its block at
+  // |ref_block_itr|.  The instructions write a record to the printf
+  // output buffer stream including |function_idx, instruction_idx, stage_idx|
+  // and removes the OpDebugPrintf. The block at |ref_block_itr| can just be
+  // replaced with the block in |new_blocks|. Besides the buffer writes, this
+  // block will comprise all instructions preceding and following
+  // |ref_inst_itr|.
+  //
+  // This function is designed to be passed to
+  // InstrumentPass::InstProcessEntryPointCallTree(), which applies the
+  // function to each instruction in a module and replaces the instruction
+  // if warranted.
+  //
+  // This instrumentation function utilizes GenDebugStreamWrite() to write its
+  // error records. The validation-specific part of the error record will
+  // consist of a uint32 which is the id of the format string plus a sequence
+  // of uint32s representing the values of the remaining operands of the
+  // DebugPrintf.
+  void GenDebugPrintfCode(BasicBlock::iterator ref_inst_itr,
+                          UptrVectorIterator<BasicBlock> ref_block_itr,
+                          uint32_t stage_idx,
+                          std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
+
+  // Generate a sequence of uint32 instructions in |builder| (if necessary)
+  // representing the value of |val_inst|, which must be a buffer pointer, a
+  // uint64, or a scalar or vector of type uint32, float32 or float16. Append
+  // the ids of all values to the end of |val_ids|.
+  void GenOutputValues(Instruction* val_inst, std::vector<uint32_t>* val_ids,
+                       InstructionBuilder* builder);
+
+  // Generate instructions to write a record containing the operands of
+  // |printf_inst| arguments to printf buffer, adding new code to the end of
+  // the last block in |new_blocks|. Kill OpDebugPrintf instruction.
+  void GenOutputCode(Instruction* printf_inst, uint32_t stage_idx,
+                     std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
+
+  // Initialize state for instrumenting bindless checking
+  void InitializeInstDebugPrintf();
+
+  // Apply GenDebugPrintfCode to every instruction in module.
+  Pass::Status ProcessImpl();
+
+  uint32_t ext_inst_printf_id_;
+};
+
+}  // namespace opt
+}  // namespace spvtools
+
+#endif  // LIBSPIRV_OPT_INST_DEBUG_PRINTF_PASS_H_

+ 39 - 14
3rdparty/spirv-tools/source/opt/instrument_pass.cpp

@@ -380,6 +380,8 @@ uint32_t InstrumentPass::GetOutputBufferBinding() {
       return kDebugOutputBindingStream;
     case kInstValidationIdBuffAddr:
       return kDebugOutputBindingStream;
+    case kInstValidationIdDebugPrintf:
+      return kDebugOutputPrintfStream;
     default:
       assert(false && "unexpected validation id");
   }
@@ -529,6 +531,16 @@ uint32_t InstrumentPass::GetInputBufferId() {
   return input_buffer_id_;
 }
 
+uint32_t InstrumentPass::GetFloatId() {
+  if (float_id_ == 0) {
+    analysis::TypeManager* type_mgr = context()->get_type_mgr();
+    analysis::Float float_ty(32);
+    analysis::Type* reg_float_ty = type_mgr->GetRegisteredType(&float_ty);
+    float_id_ = type_mgr->GetTypeInstruction(reg_float_ty);
+  }
+  return float_id_;
+}
+
 uint32_t InstrumentPass::GetVec4FloatId() {
   if (v4float_id_ == 0) {
     analysis::TypeManager* type_mgr = context()->get_type_mgr();
@@ -561,6 +573,16 @@ uint32_t InstrumentPass::GetUint64Id() {
   return uint64_id_;
 }
 
+uint32_t InstrumentPass::GetUint8Id() {
+  if (uint8_id_ == 0) {
+    analysis::TypeManager* type_mgr = context()->get_type_mgr();
+    analysis::Integer uint8_ty(8, false);
+    analysis::Type* reg_uint8_ty = type_mgr->GetRegisteredType(&uint8_ty);
+    uint8_id_ = type_mgr->GetTypeInstruction(reg_uint8_ty);
+  }
+  return uint8_id_;
+}
+
 uint32_t InstrumentPass::GetVecUintId(uint32_t len) {
   analysis::TypeManager* type_mgr = context()->get_type_mgr();
   analysis::Integer uint_ty(32, false);
@@ -606,21 +628,22 @@ uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx,
   // Total param count is common params plus validation-specific
   // params
   uint32_t param_cnt = kInstCommonParamCnt + val_spec_param_cnt;
-  if (output_func_id_ == 0) {
+  if (param2output_func_id_[param_cnt] == 0) {
     // Create function
-    output_func_id_ = TakeNextId();
+    param2output_func_id_[param_cnt] = TakeNextId();
     analysis::TypeManager* type_mgr = context()->get_type_mgr();
     std::vector<const analysis::Type*> param_types;
     for (uint32_t c = 0; c < param_cnt; ++c)
       param_types.push_back(type_mgr->GetType(GetUintId()));
     analysis::Function func_ty(type_mgr->GetType(GetVoidId()), param_types);
     analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty);
-    std::unique_ptr<Instruction> func_inst(new Instruction(
-        get_module()->context(), SpvOpFunction, GetVoidId(), output_func_id_,
-        {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
-          {SpvFunctionControlMaskNone}},
-         {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
-          {type_mgr->GetTypeInstruction(reg_func_ty)}}}));
+    std::unique_ptr<Instruction> func_inst(
+        new Instruction(get_module()->context(), SpvOpFunction, GetVoidId(),
+                        param2output_func_id_[param_cnt],
+                        {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
+                          {SpvFunctionControlMaskNone}},
+                         {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+                          {type_mgr->GetTypeInstruction(reg_func_ty)}}}));
     get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst);
     std::unique_ptr<Function> output_func =
         MakeUnique<Function>(std::move(func_inst));
@@ -709,10 +732,8 @@ uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx,
     get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst);
     output_func->SetFunctionEnd(std::move(func_end_inst));
     context()->AddFunction(std::move(output_func));
-    output_func_param_cnt_ = param_cnt;
   }
-  assert(param_cnt == output_func_param_cnt_ && "bad arg count");
-  return output_func_id_;
+  return param2output_func_id_[param_cnt];
 }
 
 uint32_t InstrumentPass::GetDirectReadFunctionId(uint32_t param_cnt) {
@@ -848,7 +869,7 @@ bool InstrumentPass::InstProcessCallTreeFromRoots(InstProcessFunction& pfn,
   std::unordered_set<uint32_t> done;
   // Don't process input and output functions
   for (auto& ifn : param2input_func_id_) done.insert(ifn.second);
-  if (output_func_id_ != 0) done.insert(output_func_id_);
+  for (auto& ofn : param2output_func_id_) done.insert(ofn.second);
   // Process all functions from roots
   while (!roots->empty()) {
     const uint32_t fi = roots->front();
@@ -926,12 +947,12 @@ void InstrumentPass::InitializeInstrument() {
   output_buffer_id_ = 0;
   output_buffer_ptr_id_ = 0;
   input_buffer_ptr_id_ = 0;
-  output_func_id_ = 0;
-  output_func_param_cnt_ = 0;
   input_buffer_id_ = 0;
+  float_id_ = 0;
   v4float_id_ = 0;
   uint_id_ = 0;
   uint64_id_ = 0;
+  uint8_id_ = 0;
   v4uint_id_ = 0;
   v3uint_id_ = 0;
   bool_id_ = 0;
@@ -944,6 +965,10 @@ void InstrumentPass::InitializeInstrument() {
   id2function_.clear();
   id2block_.clear();
 
+  // clear maps
+  param2input_func_id_.clear();
+  param2output_func_id_.clear();
+
   // Initialize function and block maps.
   for (auto& fn : *get_module()) {
     id2function_[fn.result_id()] = &fn;

+ 16 - 6
3rdparty/spirv-tools/source/opt/instrument_pass.h

@@ -61,6 +61,7 @@ namespace opt {
 // its output buffers.
 static const uint32_t kInstValidationIdBindless = 0;
 static const uint32_t kInstValidationIdBuffAddr = 1;
+static const uint32_t kInstValidationIdDebugPrintf = 2;
 
 class InstrumentPass : public Pass {
   using cbb_ptr = const BasicBlock*;
@@ -227,9 +228,12 @@ class InstrumentPass : public Pass {
   // Return id for 32-bit unsigned type
   uint32_t GetUintId();
 
-  // Return id for 32-bit unsigned type
+  // Return id for 64-bit unsigned type
   uint32_t GetUint64Id();
 
+  // Return id for 8-bit unsigned type
+  uint32_t GetUint8Id();
+
   // Return id for 32-bit unsigned type
   uint32_t GetBoolId();
 
@@ -267,6 +271,9 @@ class InstrumentPass : public Pass {
   // Return id for debug input buffer
   uint32_t GetInputBufferId();
 
+  // Return id for 32-bit float type
+  uint32_t GetFloatId();
+
   // Return id for v4float type
   uint32_t GetVec4FloatId();
 
@@ -383,17 +390,17 @@ class InstrumentPass : public Pass {
   uint32_t input_buffer_ptr_id_;
 
   // id for debug output function
-  uint32_t output_func_id_;
+  std::unordered_map<uint32_t, uint32_t> param2output_func_id_;
 
   // ids for debug input functions
   std::unordered_map<uint32_t, uint32_t> param2input_func_id_;
 
-  // param count for output function
-  uint32_t output_func_param_cnt_;
-
   // id for input buffer variable
   uint32_t input_buffer_id_;
 
+  // id for 32-bit float type
+  uint32_t float_id_;
+
   // id for v4float type
   uint32_t v4float_id_;
 
@@ -406,9 +413,12 @@ class InstrumentPass : public Pass {
   // id for 32-bit unsigned type
   uint32_t uint_id_;
 
-  // id for 32-bit unsigned type
+  // id for 64-bit unsigned type
   uint32_t uint64_id_;
 
+  // id for 8-bit unsigned type
+  uint32_t uint8_id_;
+
   // id for bool type
   uint32_t bool_id_;
 

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

@@ -425,6 +425,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
     RegisterPass(CreateConvertRelaxedToHalfPass());
   } else if (pass_name == "relax-float-ops") {
     RegisterPass(CreateRelaxFloatOpsPass());
+  } else if (pass_name == "inst-debug-printf") {
+    RegisterPass(CreateInstDebugPrintfPass(7, 23));
   } else if (pass_name == "simplify-instructions") {
     RegisterPass(CreateSimplificationPass());
   } else if (pass_name == "ssa-rewrite") {
@@ -886,6 +888,12 @@ Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set,
                                              input_init_enable, version));
 }
 
+Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set,
+                                               uint32_t shader_id) {
+  return MakeUnique<Optimizer::PassToken::Impl>(
+      MakeUnique<opt::InstDebugPrintfPass>(desc_set, shader_id));
+}
+
 Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
                                                  uint32_t shader_id,
                                                  uint32_t version) {

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

@@ -46,6 +46,7 @@
 #include "source/opt/inline_opaque_pass.h"
 #include "source/opt/inst_bindless_check_pass.h"
 #include "source/opt/inst_buff_addr_check_pass.h"
+#include "source/opt/inst_debug_printf_pass.h"
 #include "source/opt/legalize_vector_shuffle_pass.h"
 #include "source/opt/licm_pass.h"
 #include "source/opt/local_access_chain_convert_pass.h"

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

@@ -142,7 +142,7 @@ spv_result_t ValidateEntryPointNameUnique(ValidationState_t& _,
   for (const auto other_id : _.entry_points()) {
     if (other_id == id) continue;
     const auto other_id_names = CalculateNamesForEntryPoint(_, other_id);
-    for (const auto other_id_name : other_id_names) {
+    for (const auto& other_id_name : other_id_names) {
       if (names.find(other_id_name) != names.end()) {
         return _.diag(SPV_ERROR_INVALID_BINARY, _.FindDef(id))
                << "Entry point name \"" << other_id_name
@@ -431,7 +431,7 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
   if (auto error = ValidateBuiltIns(*vstate)) return error;
   // These checks must be performed after individual opcode checks because
   // those checks register the limitation checked here.
-  for (const auto inst : vstate->ordered_instructions()) {
+  for (const auto& inst : vstate->ordered_instructions()) {
     if (auto error = ValidateExecutionLimitations(*vstate, &inst)) return error;
     if (auto error = ValidateSmallTypeUses(*vstate, &inst)) return error;
   }

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

@@ -14,12 +14,11 @@
 
 // Validates correctness of extension SPIR-V instructions.
 
-#include "source/val/validate.h"
-
 #include <sstream>
 #include <string>
 #include <vector>
 
+#include "OpenCLDebugInfo100.h"
 #include "source/diagnostic.h"
 #include "source/enum_string_mapping.h"
 #include "source/extensions.h"
@@ -28,6 +27,7 @@
 #include "source/opcode.h"
 #include "source/spirv_target_env.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
@@ -42,6 +42,144 @@ uint32_t GetSizeTBitWidth(const ValidationState_t& _) {
   return 0;
 }
 
+// Check that the operand of a debug info instruction |inst| at |word_index|
+// is a result id of an instruction with |expected_opcode|.
+spv_result_t ValidateOperandForDebugInfo(
+    ValidationState_t& _, const std::string& operand_name,
+    SpvOp expected_opcode, const Instruction* inst, uint32_t word_index,
+    const std::function<std::string()>& ext_inst_name) {
+  auto* operand = _.FindDef(inst->word(word_index));
+  if (operand->opcode() != expected_opcode) {
+    spv_opcode_desc desc = nullptr;
+    if (_.grammar().lookupOpcode(expected_opcode, &desc) != SPV_SUCCESS ||
+        !desc) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << ext_inst_name() << ": "
+             << "expected operand " << operand_name << " is invalid";
+    }
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << ext_inst_name() << ": "
+           << "expected operand " << operand_name << " must be a result id of "
+           << "Op" << desc->name;
+  }
+  return SPV_SUCCESS;
+}
+
+#define CHECK_OPERAND(NAME, opcode, index)                                  \
+  do {                                                                      \
+    auto result = ValidateOperandForDebugInfo(_, NAME, opcode, inst, index, \
+                                              ext_inst_name);               \
+    if (result != SPV_SUCCESS) return result;                               \
+  } while (0)
+
+// True if the operand of a debug info instruction |inst| at |word_index|
+// satisifies |expectation| that is given as a function. Otherwise,
+// returns false.
+bool DoesDebugInfoOperandMatchExpectation(
+    const ValidationState_t& _,
+    const std::function<bool(OpenCLDebugInfo100Instructions)>& expectation,
+    const Instruction* inst, uint32_t word_index) {
+  auto* debug_inst = _.FindDef(inst->word(word_index));
+  if (debug_inst->opcode() != SpvOpExtInst ||
+      debug_inst->ext_inst_type() != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
+      !expectation(OpenCLDebugInfo100Instructions(debug_inst->word(4)))) {
+    return false;
+  }
+  return true;
+}
+
+// Check that the operand of a debug info instruction |inst| at |word_index|
+// is a result id of an debug info instruction whose debug instruction type
+// is |expected_debug_inst|.
+spv_result_t ValidateDebugInfoOperand(
+    ValidationState_t& _, const std::string& debug_inst_name,
+    OpenCLDebugInfo100Instructions expected_debug_inst, const Instruction* inst,
+    uint32_t word_index, const std::function<std::string()>& ext_inst_name) {
+  std::function<bool(OpenCLDebugInfo100Instructions)> expectation =
+      [expected_debug_inst](OpenCLDebugInfo100Instructions dbg_inst) {
+        return dbg_inst == expected_debug_inst;
+      };
+  if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index))
+    return SPV_SUCCESS;
+
+  spv_ext_inst_desc desc = nullptr;
+  _.grammar().lookupExtInst(SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
+                            expected_debug_inst, &desc);
+  if (_.grammar().lookupExtInst(SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
+                                expected_debug_inst, &desc) != SPV_SUCCESS ||
+      !desc) {
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
+           << ext_inst_name() << ": "
+           << "expected operand " << debug_inst_name << " is invalid";
+  }
+  return _.diag(SPV_ERROR_INVALID_DATA, inst)
+         << ext_inst_name() << ": "
+         << "expected operand " << debug_inst_name << " must be a result id of "
+         << desc->name;
+}
+
+#define CHECK_DEBUG_OPERAND(NAME, debug_opcode, index)                         \
+  do {                                                                         \
+    auto result = ValidateDebugInfoOperand(_, NAME, debug_opcode, inst, index, \
+                                           ext_inst_name);                     \
+    if (result != SPV_SUCCESS) return result;                                  \
+  } while (0)
+
+// Check that the operand of a debug info instruction |inst| at |word_index|
+// is a result id of an debug info instruction with DebugTypeBasic.
+spv_result_t ValidateOperandBaseType(
+    ValidationState_t& _, const Instruction* inst, uint32_t word_index,
+    const std::function<std::string()>& ext_inst_name) {
+  return ValidateDebugInfoOperand(_, "Base Type",
+                                  OpenCLDebugInfo100DebugTypeBasic, inst,
+                                  word_index, ext_inst_name);
+}
+
+// Check that the operand of a debug info instruction |inst| at |word_index|
+// is a result id of a debug lexical scope instruction which is one of
+// DebugCompilationUnit, DebugFunction, DebugLexicalBlock, or
+// DebugTypeComposite.
+spv_result_t ValidateOperandLexicalScope(
+    ValidationState_t& _, const std::string& debug_inst_name,
+    const Instruction* inst, uint32_t word_index,
+    const std::function<std::string()>& ext_inst_name) {
+  std::function<bool(OpenCLDebugInfo100Instructions)> expectation =
+      [](OpenCLDebugInfo100Instructions dbg_inst) {
+        return dbg_inst == OpenCLDebugInfo100DebugCompilationUnit ||
+               dbg_inst == OpenCLDebugInfo100DebugFunction ||
+               dbg_inst == OpenCLDebugInfo100DebugLexicalBlock ||
+               dbg_inst == OpenCLDebugInfo100DebugTypeComposite;
+      };
+  if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index))
+    return SPV_SUCCESS;
+
+  return _.diag(SPV_ERROR_INVALID_DATA, inst)
+         << ext_inst_name() << ": "
+         << "expected operand " << debug_inst_name
+         << " must be a result id of a lexical scope";
+}
+
+// Check that the operand of a debug info instruction |inst| at |word_index|
+// is a result id of a debug type instruction (See DebugTypeXXX in
+// "4.3. Type instructions" section of OpenCL.DebugInfo.100 spec.
+spv_result_t ValidateOperandDebugType(
+    ValidationState_t& _, const std::string& debug_inst_name,
+    const Instruction* inst, uint32_t word_index,
+    const std::function<std::string()>& ext_inst_name) {
+  std::function<bool(OpenCLDebugInfo100Instructions)> expectation =
+      [](OpenCLDebugInfo100Instructions dbg_inst) {
+        return OpenCLDebugInfo100DebugTypeBasic <= dbg_inst &&
+               dbg_inst <= OpenCLDebugInfo100DebugTypePtrToMember;
+      };
+  if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index))
+    return SPV_SUCCESS;
+
+  return _.diag(SPV_ERROR_INVALID_DATA, inst)
+         << ext_inst_name() << ": "
+         << "expected operand " << debug_inst_name
+         << " is not a valid debug type";
+}
+
 }  // anonymous namespace
 
 spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) {
@@ -2028,6 +2166,317 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
         break;
       }
     }
+  } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
+    if (!_.IsVoidType(result_type)) {
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
+             << ext_inst_name() << ": "
+             << "expected result type must be a result id of "
+             << "OpTypeVoid";
+    }
+
+    auto num_words = inst->words().size();
+
+    const OpenCLDebugInfo100Instructions ext_inst_key =
+        OpenCLDebugInfo100Instructions(ext_inst_index);
+    switch (ext_inst_key) {
+      case OpenCLDebugInfo100DebugInfoNone:
+      case OpenCLDebugInfo100DebugNoScope:
+      case OpenCLDebugInfo100DebugOperation:
+        // The binary parser validates the opcode for DebugInfoNone,
+        // DebugNoScope, DebugOperation, and the literal values don't need
+        // further checks.
+        break;
+      case OpenCLDebugInfo100DebugCompilationUnit: {
+        CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+        break;
+      }
+      case OpenCLDebugInfo100DebugSource: {
+        CHECK_OPERAND("File", SpvOpString, 5);
+        if (num_words == 7) CHECK_OPERAND("Text", SpvOpString, 6);
+        break;
+      }
+      case OpenCLDebugInfo100DebugTypeBasic: {
+        CHECK_OPERAND("Name", SpvOpString, 5);
+        CHECK_OPERAND("Size", SpvOpConstant, 6);
+        // "Encoding" param is already validated by the binary parsing stage.
+        break;
+      }
+      case OpenCLDebugInfo100DebugTypePointer:
+      case OpenCLDebugInfo100DebugTypeQualifier: {
+        auto validate_base_type =
+            ValidateOperandBaseType(_, inst, 5, ext_inst_name);
+        if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+        break;
+      }
+      case OpenCLDebugInfo100DebugTypeVector: {
+        auto validate_base_type =
+            ValidateOperandBaseType(_, inst, 5, ext_inst_name);
+        if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+
+        uint32_t component_count = inst->word(6);
+        if (!component_count || component_count > 4) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << ext_inst_name() << ": Component Count must be positive "
+                 << "integer less than or equal to 4";
+        }
+        break;
+      }
+      case OpenCLDebugInfo100DebugTypeArray: {
+        auto validate_base_type =
+            ValidateOperandDebugType(_, "Base Type", inst, 5, ext_inst_name);
+        if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+        for (uint32_t i = 6; i < num_words; ++i) {
+          CHECK_OPERAND("Component Count", SpvOpConstant, i);
+          auto* component_count = _.FindDef(inst->word(i));
+          if (!_.IsIntScalarType(component_count->type_id()) ||
+              !component_count->word(3)) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << ext_inst_name() << ": Component Count must be positive "
+                   << "integer";
+          }
+        }
+        break;
+      }
+      case OpenCLDebugInfo100DebugTypedef: {
+        CHECK_OPERAND("Name", SpvOpString, 5);
+        auto validate_base_type =
+            ValidateOperandBaseType(_, inst, 6, ext_inst_name);
+        if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+        CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        break;
+      }
+      case OpenCLDebugInfo100DebugTypeFunction: {
+        auto* return_type = _.FindDef(inst->word(6));
+        if (return_type->opcode() != SpvOpTypeVoid) {
+          auto validate_return = ValidateOperandDebugType(
+              _, "Return Type", inst, 6, ext_inst_name);
+          if (validate_return != SPV_SUCCESS) return validate_return;
+        }
+        for (uint32_t word_index = 7; word_index < num_words; ++word_index) {
+          auto validate_param = ValidateOperandDebugType(
+              _, "Parameter Types", inst, word_index, ext_inst_name);
+          if (validate_param != SPV_SUCCESS) return validate_param;
+        }
+        break;
+      }
+      case OpenCLDebugInfo100DebugTypeEnum: {
+        CHECK_OPERAND("Name", SpvOpString, 5);
+        if (!DoesDebugInfoOperandMatchExpectation(
+                _,
+                [](OpenCLDebugInfo100Instructions dbg_inst) {
+                  return dbg_inst == OpenCLDebugInfo100DebugInfoNone;
+                },
+                inst, 6)) {
+          auto validate_underlying_type = ValidateOperandDebugType(
+              _, "Underlying Types", inst, 6, ext_inst_name);
+          if (validate_underlying_type != SPV_SUCCESS)
+            return validate_underlying_type;
+        }
+        CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        CHECK_OPERAND("Size", SpvOpConstant, 11);
+        auto* size = _.FindDef(inst->word(11));
+        if (!_.IsIntScalarType(size->type_id()) || !size->word(3)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << ext_inst_name() << ": expected operand Size is a "
+                 << "positive integer";
+        }
+        for (uint32_t word_index = 13; word_index + 1 < num_words;
+             word_index += 2) {
+          CHECK_OPERAND("Value", SpvOpConstant, word_index);
+          CHECK_OPERAND("Name", SpvOpString, word_index + 1);
+        }
+        break;
+      }
+      case OpenCLDebugInfo100DebugTypeComposite: {
+        CHECK_OPERAND("Name", SpvOpString, 5);
+        CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        CHECK_OPERAND("Linkage Name", SpvOpString, 11);
+        CHECK_OPERAND("Size", SpvOpConstant, 12);
+        for (uint32_t word_index = 14; word_index < num_words; ++word_index) {
+          if (!DoesDebugInfoOperandMatchExpectation(
+                  _,
+                  [](OpenCLDebugInfo100Instructions dbg_inst) {
+                    return dbg_inst == OpenCLDebugInfo100DebugTypeMember ||
+                           dbg_inst == OpenCLDebugInfo100DebugFunction ||
+                           dbg_inst == OpenCLDebugInfo100DebugTypeInheritance;
+                  },
+                  inst, word_index)) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << ext_inst_name() << ": "
+                   << "expected operand Members "
+                   << "must be DebugTypeMember, DebugFunction, or "
+                      "DebugTypeInheritance";
+          }
+        }
+        break;
+      }
+      case OpenCLDebugInfo100DebugTypeMember: {
+        CHECK_OPERAND("Name", SpvOpString, 5);
+        auto validate_type =
+            ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name);
+        if (validate_type != SPV_SUCCESS) return validate_type;
+        CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+        CHECK_DEBUG_OPERAND("Parent", OpenCLDebugInfo100DebugTypeComposite, 10);
+        CHECK_OPERAND("Offset", SpvOpConstant, 11);
+        CHECK_OPERAND("Size", SpvOpConstant, 12);
+        if (num_words == 15) CHECK_OPERAND("Value", SpvOpConstant, 14);
+        break;
+      }
+      case OpenCLDebugInfo100DebugTypeInheritance: {
+        CHECK_DEBUG_OPERAND("Child", OpenCLDebugInfo100DebugTypeComposite, 5);
+        auto* debug_inst = _.FindDef(inst->word(5));
+        auto composite_type =
+            OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6));
+        if (composite_type != OpenCLDebugInfo100Class &&
+            composite_type != OpenCLDebugInfo100Structure) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << ext_inst_name() << ": "
+                 << "expected operand Child must be class or struct debug type";
+        }
+        CHECK_DEBUG_OPERAND("Parent", OpenCLDebugInfo100DebugTypeComposite, 6);
+        debug_inst = _.FindDef(inst->word(6));
+        composite_type =
+            OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6));
+        if (composite_type != OpenCLDebugInfo100Class &&
+            composite_type != OpenCLDebugInfo100Structure) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << ext_inst_name() << ": "
+                 << "expected operand Parent must be class or struct debug "
+                    "type";
+        }
+        CHECK_OPERAND("Offset", SpvOpConstant, 7);
+        CHECK_OPERAND("Size", SpvOpConstant, 8);
+        break;
+      }
+      case OpenCLDebugInfo100DebugFunction: {
+        CHECK_OPERAND("Name", SpvOpString, 5);
+        auto validate_type =
+            ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name);
+        if (validate_type != SPV_SUCCESS) return validate_type;
+        CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        CHECK_OPERAND("Linkage Name", SpvOpString, 11);
+        // TODO: The current OpenCL.100.DebugInfo spec says "Function
+        // is an OpFunction which is described by this instruction.".
+        // However, the function definition can be opted-out e.g.,
+        // inlining. We assume that Function operand can be a
+        // DebugInfoNone, but we must discuss it and update the spec.
+        if (!DoesDebugInfoOperandMatchExpectation(
+                _,
+                [](OpenCLDebugInfo100Instructions dbg_inst) {
+                  return dbg_inst == OpenCLDebugInfo100DebugInfoNone;
+                },
+                inst, 14)) {
+          CHECK_OPERAND("Function", SpvOpFunction, 14);
+        }
+        if (num_words == 16) {
+          CHECK_DEBUG_OPERAND("Declaration",
+                              OpenCLDebugInfo100DebugFunctionDeclaration, 15);
+        }
+        break;
+      }
+      case OpenCLDebugInfo100DebugFunctionDeclaration: {
+        CHECK_OPERAND("Name", SpvOpString, 5);
+        auto validate_type =
+            ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name);
+        if (validate_type != SPV_SUCCESS) return validate_type;
+        CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        CHECK_OPERAND("Linkage Name", SpvOpString, 11);
+        break;
+      }
+      case OpenCLDebugInfo100DebugLexicalBlock: {
+        CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 5);
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 8, ext_inst_name);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        if (num_words == 10) CHECK_OPERAND("Name", SpvOpString, 9);
+        break;
+      }
+      case OpenCLDebugInfo100DebugScope: {
+        // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/533): We are
+        // still in spec discussion about what must be "Scope" operand of
+        // DebugScope. Update this code if the conclusion is different.
+        auto validate_scope =
+            ValidateOperandLexicalScope(_, "Scope", inst, 5, ext_inst_name);
+        if (validate_scope != SPV_SUCCESS) return validate_scope;
+        if (num_words == 7) {
+          CHECK_DEBUG_OPERAND("Inlined At", OpenCLDebugInfo100DebugInlinedAt,
+                              6);
+        }
+        break;
+      }
+      case OpenCLDebugInfo100DebugLocalVariable: {
+        CHECK_OPERAND("Name", SpvOpString, 5);
+        auto validate_type =
+            ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name);
+        if (validate_type != SPV_SUCCESS) return validate_type;
+        CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+        auto validate_parent =
+            ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+        if (validate_parent != SPV_SUCCESS) return validate_parent;
+        break;
+      }
+      case OpenCLDebugInfo100DebugDeclare: {
+        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.
+        auto* operand = _.FindDef(inst->word(6));
+        if (operand->opcode() != SpvOpVariable &&
+            operand->opcode() != SpvOpFunctionParameter) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << ext_inst_name() << ": "
+                 << "expected operand Variable must be a result id of "
+                    "OpVariable or OpFunctionParameter";
+        }
+
+        CHECK_DEBUG_OPERAND("Expression", OpenCLDebugInfo100DebugExpression, 7);
+        break;
+      }
+      case OpenCLDebugInfo100DebugExpression: {
+        for (uint32_t word_index = 5; word_index < num_words; ++word_index) {
+          CHECK_DEBUG_OPERAND("Operation", OpenCLDebugInfo100DebugOperation,
+                              word_index);
+        }
+        break;
+      }
+
+      // TODO: Add validation rules for remaining cases as well.
+      case OpenCLDebugInfo100DebugTypePtrToMember:
+      case OpenCLDebugInfo100DebugTypeTemplate:
+      case OpenCLDebugInfo100DebugTypeTemplateParameter:
+      case OpenCLDebugInfo100DebugTypeTemplateTemplateParameter:
+      case OpenCLDebugInfo100DebugTypeTemplateParameterPack:
+      case OpenCLDebugInfo100DebugGlobalVariable:
+      case OpenCLDebugInfo100DebugLexicalBlockDiscriminator:
+      case OpenCLDebugInfo100DebugInlinedAt:
+      case OpenCLDebugInfo100DebugInlinedVariable:
+      case OpenCLDebugInfo100DebugValue:
+      case OpenCLDebugInfo100DebugMacroDef:
+      case OpenCLDebugInfo100DebugMacroUndef:
+      case OpenCLDebugInfo100DebugImportedEntity:
+        break;
+      case OpenCLDebugInfo100InstructionsMax:
+        assert(0);
+        break;
+    }
   }
 
   return SPV_SUCCESS;

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