Przeglądaj źródła

Updated spirv-tools.

Бранимир Караџић 5 lat temu
rodzic
commit
c851c522f6
71 zmienionych plików z 1778 dodań i 578 usunięć
  1. 1 1
      3rdparty/spirv-tools/include/generated/build-version.inc
  2. 10 0
      3rdparty/spirv-tools/include/spirv-tools/instrument.hpp
  3. 6 0
      3rdparty/spirv-tools/include/spirv-tools/libspirv.h
  4. 5 0
      3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp
  5. 6 2
      3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
  6. 84 6
      3rdparty/spirv-tools/source/fuzz/fact_manager.cpp
  7. 9 1
      3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
  8. 22 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp
  9. 8 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_context.h
  10. 54 5
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp
  11. 24 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h
  12. 1 5
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp
  13. 2 5
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
  14. 2 7
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp
  15. 114 5
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
  16. 8 3
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.h
  17. 10 27
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp
  18. 0 4
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.h
  19. 126 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp
  20. 40 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.h
  21. 2 3
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
  22. 5 3
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp
  23. 119 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
  24. 63 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h
  25. 1 5
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.cpp
  26. 12 22
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
  27. 1 5
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp
  28. 1 6
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.cpp
  29. 1 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp
  30. 4 3
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp
  31. 1 5
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.cpp
  32. 25 39
      3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
  33. 17 32
      3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
  34. 1 0
      3rdparty/spirv-tools/source/fuzz/protobufs/spirvfuzz_protobufs.h
  35. 94 63
      3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto
  36. 15 1
      3rdparty/spirv-tools/source/fuzz/replayer.cpp
  37. 8 6
      3rdparty/spirv-tools/source/fuzz/replayer.h
  38. 2 0
      3rdparty/spirv-tools/source/fuzz/shrinker.cpp
  39. 3 3
      3rdparty/spirv-tools/source/fuzz/transformation.cpp
  40. 3 3
      3rdparty/spirv-tools/source/fuzz/transformation_access_chain.cpp
  41. 6 6
      3rdparty/spirv-tools/source/fuzz/transformation_add_dead_break.cpp
  42. 24 1
      3rdparty/spirv-tools/source/fuzz/transformation_add_parameter.cpp
  43. 6 1
      3rdparty/spirv-tools/source/fuzz/transformation_add_parameter.h
  44. 299 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.cpp
  45. 86 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.h
  46. 2 4
      3rdparty/spirv-tools/source/fuzz/transformation_context.cpp
  47. 0 115
      3rdparty/spirv-tools/source/fuzz/transformation_copy_object.cpp
  48. 0 77
      3rdparty/spirv-tools/source/fuzz/transformation_copy_object.h
  49. 103 6
      3rdparty/spirv-tools/source/fuzz/transformation_equation_instruction.cpp
  50. 3 5
      3rdparty/spirv-tools/source/fuzz/transformation_equation_instruction.h
  51. 36 0
      3rdparty/spirv-tools/source/fuzz/transformation_invert_comparison_operator.cpp
  52. 8 10
      3rdparty/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp
  53. 21 16
      3rdparty/spirv-tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp
  54. 180 0
      3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp
  55. 4 0
      3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h
  56. 7 8
      3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp
  57. 1 1
      3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.h
  58. 10 13
      3rdparty/spirv-tools/source/opt/debug_info_manager.cpp
  59. 16 3
      3rdparty/spirv-tools/source/opt/dominator_analysis.cpp
  60. 4 1
      3rdparty/spirv-tools/source/opt/instrument_pass.cpp
  61. 6 0
      3rdparty/spirv-tools/source/spirv_fuzzer_options.cpp
  62. 3 0
      3rdparty/spirv-tools/source/spirv_fuzzer_options.h
  63. 0 6
      3rdparty/spirv-tools/source/val/basic_block.cpp
  64. 0 3
      3rdparty/spirv-tools/source/val/basic_block.h
  65. 1 4
      3rdparty/spirv-tools/source/val/function.cpp
  66. 1 3
      3rdparty/spirv-tools/source/val/function.h
  67. 4 0
      3rdparty/spirv-tools/source/val/validate.cpp
  68. 3 0
      3rdparty/spirv-tools/source/val/validate.h
  69. 26 5
      3rdparty/spirv-tools/source/val/validate_cfg.cpp
  70. 8 16
      3rdparty/spirv-tools/source/val/validate_interfaces.cpp
  71. 0 4
      3rdparty/spirv-tools/source/val/validate_mode_setting.cpp

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

@@ -1 +1 @@
-"v2020.4-dev", "SPIRV-Tools v2020.4-dev 38bba44706260cfb807097c58ca926c64c3a13d2"
+"v2020.4-dev", "SPIRV-Tools v2020.4-dev 8d5c7eae3a89ee8b898c9771119abb8a3f977898"

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

@@ -110,6 +110,16 @@ static const int kInstRayTracingOutLaunchIdX = kInstCommonOutCnt;
 static const int kInstRayTracingOutLaunchIdY = kInstCommonOutCnt + 1;
 static const int kInstRayTracingOutLaunchIdY = kInstCommonOutCnt + 1;
 static const int kInstRayTracingOutLaunchIdZ = kInstCommonOutCnt + 2;
 static const int kInstRayTracingOutLaunchIdZ = kInstCommonOutCnt + 2;
 
 
+// Mesh Shader Output Record Offsets
+static const int kInstMeshOutGlobalInvocationIdX = kInstCommonOutCnt;
+static const int kInstMeshOutGlobalInvocationIdY = kInstCommonOutCnt + 1;
+static const int kInstMeshOutGlobalInvocationIdZ = kInstCommonOutCnt + 2;
+
+// Task Shader Output Record Offsets
+static const int kInstTaskOutGlobalInvocationIdX = kInstCommonOutCnt;
+static const int kInstTaskOutGlobalInvocationIdY = kInstCommonOutCnt + 1;
+static const int kInstTaskOutGlobalInvocationIdZ = kInstCommonOutCnt + 2;
+
 // Size of Common and Stage-specific Members
 // Size of Common and Stage-specific Members
 static const int kInstStageOutCnt = kInstCommonOutCnt + 3;
 static const int kInstStageOutCnt = kInstCommonOutCnt + 3;
 
 

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

@@ -677,6 +677,12 @@ SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableReplayValidation(
 SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetRandomSeed(
 SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetRandomSeed(
     spv_fuzzer_options options, uint32_t seed);
     spv_fuzzer_options options, uint32_t seed);
 
 
+// Sets the range of transformations that should be applied during replay: 0
+// means all transformations, +N means the first N transformations, -N means all
+// except the final N transformations.
+SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetReplayRange(
+    spv_fuzzer_options options, int32_t replay_range);
+
 // Sets the maximum number of steps that the shrinker should take before giving
 // Sets the maximum number of steps that the shrinker should take before giving
 // up.
 // up.
 SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit(
 SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit(

+ 5 - 0
3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp

@@ -227,6 +227,11 @@ class FuzzerOptions {
     spvFuzzerOptionsSetRandomSeed(options_, seed);
     spvFuzzerOptionsSetRandomSeed(options_, seed);
   }
   }
 
 
+  // See spvFuzzerOptionsSetReplayRange.
+  void set_replay_range(int32_t replay_range) {
+    spvFuzzerOptionsSetReplayRange(options_, replay_range);
+  }
+
   // See spvFuzzerOptionsSetShrinkerStepLimit.
   // See spvFuzzerOptionsSetShrinkerStepLimit.
   void set_shrinker_step_limit(uint32_t shrinker_step_limit) {
   void set_shrinker_step_limit(uint32_t shrinker_step_limit) {
     spvFuzzerOptionsSetShrinkerStepLimit(options_, shrinker_step_limit);
     spvFuzzerOptionsSetShrinkerStepLimit(options_, shrinker_step_limit);

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

@@ -47,6 +47,7 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_add_function_calls.h
         fuzzer_pass_add_function_calls.h
         fuzzer_pass_add_global_variables.h
         fuzzer_pass_add_global_variables.h
         fuzzer_pass_add_image_sample_unused_components.h
         fuzzer_pass_add_image_sample_unused_components.h
+        fuzzer_pass_add_synonyms.h
         fuzzer_pass_add_loads.h
         fuzzer_pass_add_loads.h
         fuzzer_pass_add_local_variables.h
         fuzzer_pass_add_local_variables.h
         fuzzer_pass_add_no_contraction_decorations.h
         fuzzer_pass_add_no_contraction_decorations.h
@@ -63,6 +64,7 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_copy_objects.h
         fuzzer_pass_copy_objects.h
         fuzzer_pass_donate_modules.h
         fuzzer_pass_donate_modules.h
         fuzzer_pass_invert_comparison_operators.h
         fuzzer_pass_invert_comparison_operators.h
+        fuzzer_pass_interchange_zero_like_constants.h
         fuzzer_pass_merge_blocks.h
         fuzzer_pass_merge_blocks.h
         fuzzer_pass_obfuscate_constants.h
         fuzzer_pass_obfuscate_constants.h
         fuzzer_pass_outline_functions.h
         fuzzer_pass_outline_functions.h
@@ -103,6 +105,7 @@ if(SPIRV_BUILD_FUZZER)
         transformation_add_no_contraction_decoration.h
         transformation_add_no_contraction_decoration.h
         transformation_add_parameter.h
         transformation_add_parameter.h
         transformation_add_spec_constant_op.h
         transformation_add_spec_constant_op.h
+        transformation_add_synonym.h
         transformation_add_type_array.h
         transformation_add_type_array.h
         transformation_add_type_boolean.h
         transformation_add_type_boolean.h
         transformation_add_type_float.h
         transformation_add_type_float.h
@@ -117,7 +120,6 @@ if(SPIRV_BUILD_FUZZER)
         transformation_composite_extract.h
         transformation_composite_extract.h
         transformation_compute_data_synonym_fact_closure.h
         transformation_compute_data_synonym_fact_closure.h
         transformation_context.h
         transformation_context.h
-        transformation_copy_object.h
         transformation_equation_instruction.h
         transformation_equation_instruction.h
         transformation_function_call.h
         transformation_function_call.h
         transformation_invert_comparison_operator.h
         transformation_invert_comparison_operator.h
@@ -164,6 +166,7 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_add_function_calls.cpp
         fuzzer_pass_add_function_calls.cpp
         fuzzer_pass_add_global_variables.cpp
         fuzzer_pass_add_global_variables.cpp
         fuzzer_pass_add_image_sample_unused_components.cpp
         fuzzer_pass_add_image_sample_unused_components.cpp
+        fuzzer_pass_add_synonyms.cpp
         fuzzer_pass_add_loads.cpp
         fuzzer_pass_add_loads.cpp
         fuzzer_pass_add_local_variables.cpp
         fuzzer_pass_add_local_variables.cpp
         fuzzer_pass_add_no_contraction_decorations.cpp
         fuzzer_pass_add_no_contraction_decorations.cpp
@@ -180,6 +183,7 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_copy_objects.cpp
         fuzzer_pass_copy_objects.cpp
         fuzzer_pass_donate_modules.cpp
         fuzzer_pass_donate_modules.cpp
         fuzzer_pass_invert_comparison_operators.cpp
         fuzzer_pass_invert_comparison_operators.cpp
+        fuzzer_pass_interchange_zero_like_constants.cpp
         fuzzer_pass_merge_blocks.cpp
         fuzzer_pass_merge_blocks.cpp
         fuzzer_pass_obfuscate_constants.cpp
         fuzzer_pass_obfuscate_constants.cpp
         fuzzer_pass_outline_functions.cpp
         fuzzer_pass_outline_functions.cpp
@@ -219,6 +223,7 @@ if(SPIRV_BUILD_FUZZER)
         transformation_add_no_contraction_decoration.cpp
         transformation_add_no_contraction_decoration.cpp
         transformation_add_parameter.cpp
         transformation_add_parameter.cpp
         transformation_add_spec_constant_op.cpp
         transformation_add_spec_constant_op.cpp
+        transformation_add_synonym.cpp
         transformation_add_type_array.cpp
         transformation_add_type_array.cpp
         transformation_add_type_boolean.cpp
         transformation_add_type_boolean.cpp
         transformation_add_type_float.cpp
         transformation_add_type_float.cpp
@@ -233,7 +238,6 @@ if(SPIRV_BUILD_FUZZER)
         transformation_composite_extract.cpp
         transformation_composite_extract.cpp
         transformation_compute_data_synonym_fact_closure.cpp
         transformation_compute_data_synonym_fact_closure.cpp
         transformation_context.cpp
         transformation_context.cpp
-        transformation_copy_object.cpp
         transformation_equation_instruction.cpp
         transformation_equation_instruction.cpp
         transformation_function_call.cpp
         transformation_function_call.cpp
         transformation_invert_comparison_operator.cpp
         transformation_invert_comparison_operator.cpp

+ 84 - 6
3rdparty/spirv-tools/source/fuzz/fact_manager.cpp

@@ -457,6 +457,18 @@ class FactManager::DataSynonymAndIdEquationFacts {
                                    const protobufs::DataDescriptor& dd2,
                                    const protobufs::DataDescriptor& dd2,
                                    opt::IRContext* context);
                                    opt::IRContext* context);
 
 
+  // Computes various corollary facts from the data descriptor |dd| if members
+  // of its equivalence class participate in equation facts with OpConvert*
+  // opcodes. The descriptor should be registered in the equivalence relation.
+  void ComputeConversionDataSynonymFacts(const protobufs::DataDescriptor& dd,
+                                         opt::IRContext* context);
+
+  // Recurses into sub-components of the data descriptors, if they are
+  // composites, to record that their components are pairwise-synonymous.
+  void ComputeCompositeDataSynonymFacts(const protobufs::DataDescriptor& dd1,
+                                        const protobufs::DataDescriptor& dd2,
+                                        opt::IRContext* context);
+
   // Records the fact that |dd1| and |dd2| are equivalent, and merges the sets
   // Records the fact that |dd1| and |dd2| are equivalent, and merges the sets
   // of equations that are known about them.
   // of equations that are known about them.
   void MakeEquivalent(const protobufs::DataDescriptor& dd1,
   void MakeEquivalent(const protobufs::DataDescriptor& dd1,
@@ -588,9 +600,13 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
   // Now try to work out corollaries implied by the new equation and existing
   // Now try to work out corollaries implied by the new equation and existing
   // facts.
   // facts.
   switch (opcode) {
   switch (opcode) {
+    case SpvOpConvertSToF:
+    case SpvOpConvertUToF:
+      ComputeConversionDataSynonymFacts(*rhs_dds[0], context);
+      break;
     case SpvOpIAdd: {
     case SpvOpIAdd: {
       // Equation form: "a = b + c"
       // Equation form: "a = b + c"
-      for (auto equation : GetEquations(rhs_dds[0])) {
+      for (const auto& equation : GetEquations(rhs_dds[0])) {
         if (equation.opcode == SpvOpISub) {
         if (equation.opcode == SpvOpISub) {
           // Equation form: "a = (d - e) + c"
           // Equation form: "a = (d - e) + c"
           if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[1])) {
           if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[1])) {
@@ -606,7 +622,7 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
           }
           }
         }
         }
       }
       }
-      for (auto equation : GetEquations(rhs_dds[1])) {
+      for (const auto& equation : GetEquations(rhs_dds[1])) {
         if (equation.opcode == SpvOpISub) {
         if (equation.opcode == SpvOpISub) {
           // Equation form: "a = b + (d - e)"
           // Equation form: "a = b + (d - e)"
           if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[0])) {
           if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[0])) {
@@ -620,7 +636,7 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
     }
     }
     case SpvOpISub: {
     case SpvOpISub: {
       // Equation form: "a = b - c"
       // Equation form: "a = b - c"
-      for (auto equation : GetEquations(rhs_dds[0])) {
+      for (const auto& equation : GetEquations(rhs_dds[0])) {
         if (equation.opcode == SpvOpIAdd) {
         if (equation.opcode == SpvOpIAdd) {
           // Equation form: "a = (d + e) - c"
           // Equation form: "a = (d + e) - c"
           if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) {
           if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) {
@@ -646,7 +662,7 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
         }
         }
       }
       }
 
 
-      for (auto equation : GetEquations(rhs_dds[1])) {
+      for (const auto& equation : GetEquations(rhs_dds[1])) {
         if (equation.opcode == SpvOpIAdd) {
         if (equation.opcode == SpvOpIAdd) {
           // Equation form: "a = b - (d + e)"
           // Equation form: "a = b - (d + e)"
           if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[0])) {
           if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[0])) {
@@ -676,7 +692,7 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
     case SpvOpLogicalNot:
     case SpvOpLogicalNot:
     case SpvOpSNegate: {
     case SpvOpSNegate: {
       // Equation form: "a = !b" or "a = -b"
       // Equation form: "a = !b" or "a = -b"
-      for (auto equation : GetEquations(rhs_dds[0])) {
+      for (const auto& equation : GetEquations(rhs_dds[0])) {
         if (equation.opcode == opcode) {
         if (equation.opcode == opcode) {
           // Equation form: "a = !!b" or "a = -(-b)"
           // Equation form: "a = !!b" or "a = -(-b)"
           // We can thus infer "a = b"
           // We can thus infer "a = b"
@@ -698,7 +714,69 @@ void FactManager::DataSynonymAndIdEquationFacts::AddDataSynonymFactRecursive(
   // Record that the data descriptors provided in the fact are equivalent.
   // Record that the data descriptors provided in the fact are equivalent.
   MakeEquivalent(dd1, dd2);
   MakeEquivalent(dd1, dd2);
 
 
-  // We now check whether this is a synonym about composite objects.  If it is,
+  // Compute various corollary facts.
+  ComputeConversionDataSynonymFacts(dd1, context);
+  ComputeCompositeDataSynonymFacts(dd1, dd2, context);
+}
+
+void FactManager::DataSynonymAndIdEquationFacts::
+    ComputeConversionDataSynonymFacts(const protobufs::DataDescriptor& dd,
+                                      opt::IRContext* context) {
+  assert(synonymous_.Exists(dd) &&
+         "|dd| should've been registered in the equivalence relation");
+
+  const auto* representative = synonymous_.Find(&dd);
+  assert(representative &&
+         "Representative can't be null for a registered descriptor");
+
+  const auto* type =
+      context->get_type_mgr()->GetType(fuzzerutil::WalkCompositeTypeIndices(
+          context, fuzzerutil::GetTypeId(context, representative->object()),
+          representative->index()));
+  assert(type && "Data descriptor has invalid type");
+
+  if ((type->AsVector() && type->AsVector()->element_type()->AsInteger()) ||
+      type->AsInteger()) {
+    // If there exist equation facts of the form |%a = opcode %representative|
+    // and |%b = opcode %representative| where |opcode| is either OpConvertSToF
+    // or OpConvertUToF, then |a| and |b| are synonymous.
+    std::vector<const protobufs::DataDescriptor*> convert_s_to_f_lhs;
+    std::vector<const protobufs::DataDescriptor*> convert_u_to_f_lhs;
+
+    for (const auto& fact : id_equations_) {
+      for (const auto& equation : fact.second) {
+        if (synonymous_.IsEquivalent(*equation.operands[0], *representative)) {
+          if (equation.opcode == SpvOpConvertSToF) {
+            convert_s_to_f_lhs.push_back(fact.first);
+          } else if (equation.opcode == SpvOpConvertUToF) {
+            convert_u_to_f_lhs.push_back(fact.first);
+          }
+        }
+      }
+    }
+
+    for (const auto& synonyms :
+         {std::move(convert_s_to_f_lhs), std::move(convert_u_to_f_lhs)}) {
+      for (const auto* synonym_a : synonyms) {
+        for (const auto* synonym_b : synonyms) {
+          if (!synonymous_.IsEquivalent(*synonym_a, *synonym_b) &&
+              DataDescriptorsAreWellFormedAndComparable(context, *synonym_a,
+                                                        *synonym_b)) {
+            // |synonym_a| and |synonym_b| have compatible types - they are
+            // synonymous.
+            AddDataSynonymFactRecursive(*synonym_a, *synonym_b, context);
+          }
+        }
+      }
+    }
+  }
+}
+
+void FactManager::DataSynonymAndIdEquationFacts::
+    ComputeCompositeDataSynonymFacts(const protobufs::DataDescriptor& dd1,
+                                     const protobufs::DataDescriptor& dd2,
+                                     opt::IRContext* context) {
+  // Check whether this is a synonym about composite objects.  If it is,
   // we can recursively add synonym facts about their associated sub-components.
   // we can recursively add synonym facts about their associated sub-components.
 
 
   // Get the type of the object referred to by the first data descriptor in the
   // Get the type of the object referred to by the first data descriptor in the

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

@@ -18,7 +18,6 @@
 #include <memory>
 #include <memory>
 #include <sstream>
 #include <sstream>
 
 
-#include "fuzzer_pass_adjust_memory_operands_masks.h"
 #include "source/fuzz/fact_manager.h"
 #include "source/fuzz/fact_manager.h"
 #include "source/fuzz/fuzzer_context.h"
 #include "source/fuzz/fuzzer_context.h"
 #include "source/fuzz/fuzzer_pass_add_access_chains.h"
 #include "source/fuzz/fuzzer_pass_add_access_chains.h"
@@ -36,15 +35,18 @@
 #include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
 #include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
 #include "source/fuzz/fuzzer_pass_add_parameters.h"
 #include "source/fuzz/fuzzer_pass_add_parameters.h"
 #include "source/fuzz/fuzzer_pass_add_stores.h"
 #include "source/fuzz/fuzzer_pass_add_stores.h"
+#include "source/fuzz/fuzzer_pass_add_synonyms.h"
 #include "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h"
 #include "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h"
 #include "source/fuzz/fuzzer_pass_adjust_branch_weights.h"
 #include "source/fuzz/fuzzer_pass_adjust_branch_weights.h"
 #include "source/fuzz/fuzzer_pass_adjust_function_controls.h"
 #include "source/fuzz/fuzzer_pass_adjust_function_controls.h"
 #include "source/fuzz/fuzzer_pass_adjust_loop_controls.h"
 #include "source/fuzz/fuzzer_pass_adjust_loop_controls.h"
+#include "source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h"
 #include "source/fuzz/fuzzer_pass_adjust_selection_controls.h"
 #include "source/fuzz/fuzzer_pass_adjust_selection_controls.h"
 #include "source/fuzz/fuzzer_pass_apply_id_synonyms.h"
 #include "source/fuzz/fuzzer_pass_apply_id_synonyms.h"
 #include "source/fuzz/fuzzer_pass_construct_composites.h"
 #include "source/fuzz/fuzzer_pass_construct_composites.h"
 #include "source/fuzz/fuzzer_pass_copy_objects.h"
 #include "source/fuzz/fuzzer_pass_copy_objects.h"
 #include "source/fuzz/fuzzer_pass_donate_modules.h"
 #include "source/fuzz/fuzzer_pass_donate_modules.h"
+#include "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h"
 #include "source/fuzz/fuzzer_pass_invert_comparison_operators.h"
 #include "source/fuzz/fuzzer_pass_invert_comparison_operators.h"
 #include "source/fuzz/fuzzer_pass_merge_blocks.h"
 #include "source/fuzz/fuzzer_pass_merge_blocks.h"
 #include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
 #include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
@@ -241,6 +243,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
     MaybeAddPass<FuzzerPassAddStores>(&passes, ir_context.get(),
     MaybeAddPass<FuzzerPassAddStores>(&passes, ir_context.get(),
                                       &transformation_context, &fuzzer_context,
                                       &transformation_context, &fuzzer_context,
                                       transformation_sequence_out);
                                       transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddSynonyms>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
     MaybeAddPass<FuzzerPassAddVectorShuffleInstructions>(
     MaybeAddPass<FuzzerPassAddVectorShuffleInstructions>(
         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out);
         transformation_sequence_out);
@@ -326,6 +331,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
   MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
   MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
       &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
       &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
       transformation_sequence_out);
       transformation_sequence_out);
+  MaybeAddPass<FuzzerPassInterchangeZeroLikeConstants>(
+      &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
+      transformation_sequence_out);
   MaybeAddPass<FuzzerPassPermutePhiOperands>(
   MaybeAddPass<FuzzerPassPermutePhiOperands>(
       &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
       &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
       transformation_sequence_out);
       transformation_sequence_out);

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

@@ -43,6 +43,7 @@ const std::pair<uint32_t, uint32_t> kChanceOfAddingNoContractionDecoration = {
     5, 70};
     5, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingParameters = {5, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingParameters = {5, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingStore = {5, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingStore = {5, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingSynonyms = {20, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorType = {20, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorType = {20, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorShuffle = {20, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorShuffle = {20, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfAdjustingBranchWeights = {20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfAdjustingBranchWeights = {20, 90};
@@ -63,6 +64,8 @@ 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> kChanceOfDonatingAdditionalModule = {5, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfGoingDeeperWhenMakingAccessChain =
 const std::pair<uint32_t, uint32_t> kChanceOfGoingDeeperWhenMakingAccessChain =
     {50, 95};
     {50, 95};
+const std::pair<uint32_t, uint32_t> kChanceOfInterchangingZeroLikeConstants = {
+    10, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfInvertingComparisonOperators = {
 const std::pair<uint32_t, uint32_t> kChanceOfInvertingComparisonOperators = {
     20, 50};
     20, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfMakingDonorLivesafe = {40, 60};
 const std::pair<uint32_t, uint32_t> kChanceOfMakingDonorLivesafe = {40, 60};
@@ -160,6 +163,7 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
       ChooseBetweenMinAndMax(kChanceOfAdjustingBranchWeights);
       ChooseBetweenMinAndMax(kChanceOfAdjustingBranchWeights);
   chance_of_adjusting_function_control_ =
   chance_of_adjusting_function_control_ =
       ChooseBetweenMinAndMax(kChanceOfAdjustingFunctionControl);
       ChooseBetweenMinAndMax(kChanceOfAdjustingFunctionControl);
+  chance_of_adding_synonyms_ = ChooseBetweenMinAndMax(kChanceOfAddingSynonyms);
   chance_of_adjusting_loop_control_ =
   chance_of_adjusting_loop_control_ =
       ChooseBetweenMinAndMax(kChanceOfAdjustingLoopControl);
       ChooseBetweenMinAndMax(kChanceOfAdjustingLoopControl);
   chance_of_adjusting_memory_operands_mask_ =
   chance_of_adjusting_memory_operands_mask_ =
@@ -179,6 +183,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
       ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule);
       ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule);
   chance_of_going_deeper_when_making_access_chain_ =
   chance_of_going_deeper_when_making_access_chain_ =
       ChooseBetweenMinAndMax(kChanceOfGoingDeeperWhenMakingAccessChain);
       ChooseBetweenMinAndMax(kChanceOfGoingDeeperWhenMakingAccessChain);
+  chance_of_interchanging_zero_like_constants_ =
+      ChooseBetweenMinAndMax(kChanceOfInterchangingZeroLikeConstants);
   chance_of_inverting_comparison_operators_ =
   chance_of_inverting_comparison_operators_ =
       ChooseBetweenMinAndMax(kChanceOfInvertingComparisonOperators);
       ChooseBetweenMinAndMax(kChanceOfInvertingComparisonOperators);
   chance_of_making_donor_livesafe_ =
   chance_of_making_donor_livesafe_ =
@@ -237,5 +243,21 @@ uint32_t FuzzerContext::ChooseBetweenMinAndMax(
          random_generator_->RandomUint32(min_max.second - min_max.first + 1);
          random_generator_->RandomUint32(min_max.second - min_max.first + 1);
 }
 }
 
 
+protobufs::TransformationAddSynonym::SynonymType
+FuzzerContext::GetRandomSynonymType() {
+  // value_count method is guaranteed to return a value greater than 0.
+  auto result_index = ChooseBetweenMinAndMax(
+      {0, static_cast<uint32_t>(
+              protobufs::TransformationAddSynonym::SynonymType_descriptor()
+                  ->value_count() -
+              1)});
+  auto result = protobufs::TransformationAddSynonym::SynonymType_descriptor()
+                    ->value(result_index)
+                    ->number();
+  assert(protobufs::TransformationAddSynonym::SynonymType_IsValid(result) &&
+         "|result| is not a value of SynonymType");
+  return static_cast<protobufs::TransformationAddSynonym::SynonymType>(result);
+}
+
 }  // namespace fuzz
 }  // namespace fuzz
 }  // namespace spvtools
 }  // namespace spvtools

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

@@ -18,6 +18,7 @@
 #include <functional>
 #include <functional>
 #include <utility>
 #include <utility>
 
 
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/random_generator.h"
 #include "source/fuzz/random_generator.h"
 #include "source/opt/function.h"
 #include "source/opt/function.h"
 
 
@@ -143,6 +144,7 @@ class FuzzerContext {
   }
   }
   uint32_t GetChanceOfAddingParameters() { return chance_of_adding_parameters; }
   uint32_t GetChanceOfAddingParameters() { return chance_of_adding_parameters; }
   uint32_t GetChanceOfAddingStore() { return chance_of_adding_store_; }
   uint32_t GetChanceOfAddingStore() { return chance_of_adding_store_; }
+  uint32_t GetChanceOfAddingSynonyms() { return chance_of_adding_synonyms_; }
   uint32_t GetChanceOfAddingVectorShuffle() {
   uint32_t GetChanceOfAddingVectorShuffle() {
     return chance_of_adding_vector_shuffle_;
     return chance_of_adding_vector_shuffle_;
   }
   }
@@ -181,6 +183,9 @@ class FuzzerContext {
   uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() {
   uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() {
     return chance_of_going_deeper_when_making_access_chain_;
     return chance_of_going_deeper_when_making_access_chain_;
   }
   }
+  uint32_t GetChanceOfInterchangingZeroLikeConstants() {
+    return chance_of_interchanging_zero_like_constants_;
+  }
   uint32_t GetChanceOfInvertingComparisonOperators() {
   uint32_t GetChanceOfInvertingComparisonOperators() {
     return chance_of_inverting_comparison_operators_;
     return chance_of_inverting_comparison_operators_;
   }
   }
@@ -274,6 +279,7 @@ class FuzzerContext {
     // Ensure that the array size is non-zero.
     // Ensure that the array size is non-zero.
     return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1;
     return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1;
   }
   }
+  protobufs::TransformationAddSynonym::SynonymType GetRandomSynonymType();
   uint32_t GetRandomUnusedComponentCountForImageSample(
   uint32_t GetRandomUnusedComponentCountForImageSample(
       uint32_t max_unused_component_count) {
       uint32_t max_unused_component_count) {
     // Ensure that the number of unused components is non-zero.
     // Ensure that the number of unused components is non-zero.
@@ -307,6 +313,7 @@ class FuzzerContext {
   uint32_t chance_of_adding_no_contraction_decoration_;
   uint32_t chance_of_adding_no_contraction_decoration_;
   uint32_t chance_of_adding_parameters;
   uint32_t chance_of_adding_parameters;
   uint32_t chance_of_adding_store_;
   uint32_t chance_of_adding_store_;
+  uint32_t chance_of_adding_synonyms_;
   uint32_t chance_of_adding_vector_shuffle_;
   uint32_t chance_of_adding_vector_shuffle_;
   uint32_t chance_of_adding_vector_type_;
   uint32_t chance_of_adding_vector_type_;
   uint32_t chance_of_adjusting_branch_weights_;
   uint32_t chance_of_adjusting_branch_weights_;
@@ -321,6 +328,7 @@ class FuzzerContext {
   uint32_t chance_of_copying_object_;
   uint32_t chance_of_copying_object_;
   uint32_t chance_of_donating_additional_module_;
   uint32_t chance_of_donating_additional_module_;
   uint32_t chance_of_going_deeper_when_making_access_chain_;
   uint32_t chance_of_going_deeper_when_making_access_chain_;
+  uint32_t chance_of_interchanging_zero_like_constants_;
   uint32_t chance_of_inverting_comparison_operators_;
   uint32_t chance_of_inverting_comparison_operators_;
   uint32_t chance_of_making_donor_livesafe_;
   uint32_t chance_of_making_donor_livesafe_;
   uint32_t chance_of_merging_blocks_;
   uint32_t chance_of_merging_blocks_;

+ 54 - 5
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp

@@ -20,6 +20,7 @@
 #include "source/fuzz/instruction_descriptor.h"
 #include "source/fuzz/instruction_descriptor.h"
 #include "source/fuzz/transformation_add_constant_boolean.h"
 #include "source/fuzz/transformation_add_constant_boolean.h"
 #include "source/fuzz/transformation_add_constant_composite.h"
 #include "source/fuzz/transformation_add_constant_composite.h"
+#include "source/fuzz/transformation_add_constant_null.h"
 #include "source/fuzz/transformation_add_constant_scalar.h"
 #include "source/fuzz/transformation_add_constant_scalar.h"
 #include "source/fuzz/transformation_add_global_undef.h"
 #include "source/fuzz/transformation_add_global_undef.h"
 #include "source/fuzz/transformation_add_type_boolean.h"
 #include "source/fuzz/transformation_add_type_boolean.h"
@@ -163,8 +164,9 @@ uint32_t FuzzerPass::FindOrCreateBoolType() {
 }
 }
 
 
 uint32_t FuzzerPass::FindOrCreateIntegerType(uint32_t width, bool is_signed) {
 uint32_t FuzzerPass::FindOrCreateIntegerType(uint32_t width, bool is_signed) {
-  if (auto existing_id =
-          fuzzerutil::MaybeGetIntegerType(GetIRContext(), width, is_signed)) {
+  opt::analysis::Integer int_type(width, is_signed);
+  auto existing_id = GetIRContext()->get_type_mgr()->GetId(&int_type);
+  if (existing_id) {
     return existing_id;
     return existing_id;
   }
   }
   auto result = GetFuzzerContext()->GetFreshId();
   auto result = GetFuzzerContext()->GetFreshId();
@@ -173,7 +175,9 @@ uint32_t FuzzerPass::FindOrCreateIntegerType(uint32_t width, bool is_signed) {
 }
 }
 
 
 uint32_t FuzzerPass::FindOrCreateFloatType(uint32_t width) {
 uint32_t FuzzerPass::FindOrCreateFloatType(uint32_t width) {
-  if (auto existing_id = fuzzerutil::MaybeGetFloatType(GetIRContext(), width)) {
+  opt::analysis::Float float_type(width);
+  auto existing_id = GetIRContext()->get_type_mgr()->GetId(&float_type);
+  if (existing_id) {
     return existing_id;
     return existing_id;
   }
   }
   auto result = GetFuzzerContext()->GetFreshId();
   auto result = GetFuzzerContext()->GetFreshId();
@@ -203,8 +207,14 @@ uint32_t FuzzerPass::FindOrCreateFunctionType(
 
 
 uint32_t FuzzerPass::FindOrCreateVectorType(uint32_t component_type_id,
 uint32_t FuzzerPass::FindOrCreateVectorType(uint32_t component_type_id,
                                             uint32_t component_count) {
                                             uint32_t component_count) {
-  if (auto existing_id = fuzzerutil::MaybeGetVectorType(
-          GetIRContext(), component_type_id, component_count)) {
+  assert(component_count >= 2 && component_count <= 4 &&
+         "Precondition: component count must be in range [2, 4].");
+  opt::analysis::Type* component_type =
+      GetIRContext()->get_type_mgr()->GetType(component_type_id);
+  assert(component_type && "Precondition: the component type must exist.");
+  opt::analysis::Vector vector_type(component_type, component_count);
+  auto existing_id = GetIRContext()->get_type_mgr()->GetId(&vector_type);
+  if (existing_id) {
     return existing_id;
     return existing_id;
   }
   }
   auto result = GetFuzzerContext()->GetFreshId();
   auto result = GetFuzzerContext()->GetFreshId();
@@ -344,6 +354,24 @@ uint32_t FuzzerPass::FindOrCreateConstant(const std::vector<uint32_t>& words,
   return 0;
   return 0;
 }
 }
 
 
+uint32_t FuzzerPass::FindOrCreateCompositeConstant(
+    const std::vector<uint32_t>& component_ids, uint32_t type_id) {
+  assert(type_id && "|type_id| can't be 0");
+  const auto* type_inst = GetIRContext()->get_def_use_mgr()->GetDef(type_id);
+  assert(type_inst && "|type_id| is invalid");
+
+  std::vector<const opt::analysis::Constant*> constants;
+  for (auto id : component_ids) {
+    assert(id && "Component's id can't be 0");
+    const auto* constant =
+        GetIRContext()->get_constant_mgr()->FindDeclaredConstant(id);
+    assert(constant && "Component's id is invalid");
+    constants.push_back(constant);
+  }
+
+  return FindOrCreateCompositeConstant(*type_inst, constants, component_ids);
+}
+
 uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
 uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
   for (auto& inst : GetIRContext()->types_values()) {
   for (auto& inst : GetIRContext()->types_values()) {
     if (inst.opcode() == SpvOpUndef && inst.type_id() == type_id) {
     if (inst.opcode() == SpvOpUndef && inst.type_id() == type_id) {
@@ -355,6 +383,27 @@ uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
   return result;
   return result;
 }
 }
 
 
+uint32_t FuzzerPass::FindOrCreateNullConstant(uint32_t type_id) {
+  // Find existing declaration
+  opt::analysis::NullConstant null_constant(
+      GetIRContext()->get_type_mgr()->GetType(type_id));
+  auto existing_constant =
+      GetIRContext()->get_constant_mgr()->FindConstant(&null_constant);
+
+  // Return if found
+  if (existing_constant) {
+    return GetIRContext()
+        ->get_constant_mgr()
+        ->GetDefiningInstruction(existing_constant)
+        ->result_id();
+  }
+
+  // Create new if not found
+  auto result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(TransformationAddConstantNull(result, type_id));
+  return result;
+}
+
 std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
 std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
 FuzzerPass::GetAvailableBasicTypesAndPointers(
 FuzzerPass::GetAvailableBasicTypesAndPointers(
     SpvStorageClass storage_class) const {
     SpvStorageClass storage_class) const {

+ 24 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h

@@ -103,6 +103,20 @@ class FuzzerPass {
     *GetTransformations()->add_transformation() = transformation.ToMessage();
     *GetTransformations()->add_transformation() = transformation.ToMessage();
   }
   }
 
 
+  // A generic helper for applying a transformation only if it is applicable.
+  // If it is applicable, the transformation is applied and then added to the
+  // sequence of applied transformations and the function returns true.
+  // Otherwise, the function returns false.
+  bool MaybeApplyTransformation(const Transformation& transformation) {
+    if (transformation.IsApplicable(GetIRContext(),
+                                    *GetTransformationContext())) {
+      transformation.Apply(GetIRContext(), GetTransformationContext());
+      *GetTransformations()->add_transformation() = transformation.ToMessage();
+      return true;
+    }
+    return false;
+  }
+
   // Returns the id of an OpTypeBool instruction.  If such an instruction does
   // Returns the id of an OpTypeBool instruction.  If such an instruction does
   // not exist, a transformation is applied to add it.
   // not exist, a transformation is applied to add it.
   uint32_t FindOrCreateBoolType();
   uint32_t FindOrCreateBoolType();
@@ -183,11 +197,21 @@ class FuzzerPass {
   uint32_t FindOrCreateConstant(const std::vector<uint32_t>& words,
   uint32_t FindOrCreateConstant(const std::vector<uint32_t>& words,
                                 uint32_t type_id);
                                 uint32_t type_id);
 
 
+  // Returns the id of an OpConstantComposite
+  uint32_t FindOrCreateCompositeConstant(
+      const std::vector<uint32_t>& component_ids, uint32_t type_id);
+
   // Returns the result id of an instruction of the form:
   // Returns the result id of an instruction of the form:
   //   %id = OpUndef %|type_id|
   //   %id = OpUndef %|type_id|
   // If no such instruction exists, a transformation is applied to add it.
   // If no such instruction exists, a transformation is applied to add it.
   uint32_t FindOrCreateGlobalUndef(uint32_t type_id);
   uint32_t FindOrCreateGlobalUndef(uint32_t type_id);
 
 
+  // Returns the id of an OpNullConstant instruction of type |type_id|. If
+  // that instruction doesn't exist, it is added through a transformation.
+  // |type_id| must be a valid result id of an OpType* instruction that exists
+  // in the module.
+  uint32_t FindOrCreateNullConstant(uint32_t type_id);
+
   // Define a *basic type* to be an integer, boolean or floating-point type,
   // Define a *basic type* to be an integer, boolean or floating-point type,
   // or a matrix, vector, struct or fixed-size array built from basic types.  In
   // or a matrix, vector, struct or fixed-size array built from basic types.  In
   // particular, a basic type cannot contain an opaque type (such as an image),
   // particular, a basic type cannot contain an opaque type (such as an image),

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

@@ -59,11 +59,7 @@ void FuzzerPassAddDeadBlocks::Apply() {
   }
   }
   // Apply all those transformations that are in fact applicable.
   // Apply all those transformations that are in fact applicable.
   for (auto& transformation : candidate_transformations) {
   for (auto& transformation : candidate_transformations) {
-    if (transformation.IsApplicable(GetIRContext(),
-                                    *GetTransformationContext())) {
-      transformation.Apply(GetIRContext(), GetTransformationContext());
-      *GetTransformations()->add_transformation() = transformation.ToMessage();
-    }
+    MaybeApplyTransformation(transformation);
   }
   }
 }
 }
 
 

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

@@ -114,12 +114,9 @@ void FuzzerPassAddDeadBreaks::Apply() {
     candidate_transformations.erase(candidate_transformations.begin() + index);
     candidate_transformations.erase(candidate_transformations.begin() + index);
     // Probabilistically decide whether to try to apply it vs. ignore it, in the
     // Probabilistically decide whether to try to apply it vs. ignore it, in the
     // case that it is applicable.
     // case that it is applicable.
-    if (transformation.IsApplicable(GetIRContext(),
-                                    *GetTransformationContext()) &&
-        GetFuzzerContext()->ChoosePercentage(
+    if (GetFuzzerContext()->ChoosePercentage(
             GetFuzzerContext()->GetChanceOfAddingDeadBreak())) {
             GetFuzzerContext()->GetChanceOfAddingDeadBreak())) {
-      transformation.Apply(GetIRContext(), GetTransformationContext());
-      *GetTransformations()->add_transformation() = transformation.ToMessage();
+      MaybeApplyTransformation(transformation);
     }
     }
   }
   }
 }
 }

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

@@ -80,14 +80,9 @@ void FuzzerPassAddDeadContinues::Apply() {
           block.id(), condition_value, std::move(phi_ids));
           block.id(), condition_value, std::move(phi_ids));
       // Probabilistically decide whether to apply the transformation in the
       // Probabilistically decide whether to apply the transformation in the
       // case that it is applicable.
       // case that it is applicable.
-      if (candidate_transformation.IsApplicable(GetIRContext(),
-                                                *GetTransformationContext()) &&
-          GetFuzzerContext()->ChoosePercentage(
+      if (GetFuzzerContext()->ChoosePercentage(
               GetFuzzerContext()->GetChanceOfAddingDeadContinue())) {
               GetFuzzerContext()->GetChanceOfAddingDeadContinue())) {
-        candidate_transformation.Apply(GetIRContext(),
-                                       GetTransformationContext());
-        *GetTransformations()->add_transformation() =
-            candidate_transformation.ToMessage();
+        MaybeApplyTransformation(candidate_transformation);
       }
       }
     }
     }
   }
   }

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

@@ -64,12 +64,106 @@ void FuzzerPassAddEquationInstructions::Apply() {
 
 
         // Try the opcodes for which we know how to make ids at random until
         // Try the opcodes for which we know how to make ids at random until
         // something works.
         // something works.
-        std::vector<SpvOp> candidate_opcodes = {SpvOpIAdd, SpvOpISub,
-                                                SpvOpLogicalNot, SpvOpSNegate};
+        std::vector<SpvOp> candidate_opcodes = {
+            SpvOpIAdd,        SpvOpISub,        SpvOpLogicalNot, SpvOpSNegate,
+            SpvOpConvertUToF, SpvOpConvertSToF, SpvOpBitcast};
         do {
         do {
           auto opcode =
           auto opcode =
               GetFuzzerContext()->RemoveAtRandomIndex(&candidate_opcodes);
               GetFuzzerContext()->RemoveAtRandomIndex(&candidate_opcodes);
           switch (opcode) {
           switch (opcode) {
+            case SpvOpConvertSToF:
+            case SpvOpConvertUToF: {
+              auto candidate_instructions =
+                  GetIntegerInstructions(available_instructions);
+
+              if (candidate_instructions.empty()) {
+                break;
+              }
+
+              const auto* operand =
+                  candidate_instructions[GetFuzzerContext()->RandomIndex(
+                      candidate_instructions)];
+
+              const auto* type =
+                  GetIRContext()->get_type_mgr()->GetType(operand->type_id());
+              assert(type && "Operand has invalid type");
+
+              // Make sure a result type exists in the module.
+              if (const auto* vector = type->AsVector()) {
+                FindOrCreateVectorType(
+                    FindOrCreateFloatType(
+                        vector->element_type()->AsInteger()->width()),
+                    vector->element_count());
+              } else {
+                FindOrCreateFloatType(type->AsInteger()->width());
+              }
+
+              ApplyTransformation(TransformationEquationInstruction(
+                  GetFuzzerContext()->GetFreshId(), opcode,
+                  {operand->result_id()}, instruction_descriptor));
+              return;
+            }
+            case SpvOpBitcast: {
+              std::vector<const opt::Instruction*> candidate_instructions;
+              for (const auto* inst : available_instructions) {
+                const auto* type =
+                    GetIRContext()->get_type_mgr()->GetType(inst->type_id());
+                assert(type && "Instruction has invalid type");
+                if ((type->AsVector() &&
+                     (type->AsVector()->element_type()->AsInteger() ||
+                      type->AsVector()->element_type()->AsFloat())) ||
+                    type->AsInteger() || type->AsFloat()) {
+                  // We support OpBitcast for only scalars or vectors of
+                  // numerical type.
+                  candidate_instructions.push_back(inst);
+                }
+              }
+
+              if (!candidate_instructions.empty()) {
+                const auto* operand_inst =
+                    candidate_instructions[GetFuzzerContext()->RandomIndex(
+                        candidate_instructions)];
+                const auto* operand_type =
+                    GetIRContext()->get_type_mgr()->GetType(
+                        operand_inst->type_id());
+                assert(operand_type && "Operand instruction has invalid type");
+
+                // Make sure a result type exists in the module.
+                //
+                // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3539):
+                //  The only constraint on the types of OpBitcast's parameters
+                //  is that they must have the same number of bits. Consider
+                //  improving the code below to support this in full.
+                if (const auto* vector = operand_type->AsVector()) {
+                  uint32_t element_type_id;
+                  if (const auto* int_type =
+                          vector->element_type()->AsInteger()) {
+                    element_type_id = FindOrCreateFloatType(int_type->width());
+                  } else {
+                    assert(vector->element_type()->AsFloat() &&
+                           "Vector must have numerical elements");
+                    element_type_id = FindOrCreateIntegerType(
+                        vector->element_type()->AsFloat()->width(),
+                        GetFuzzerContext()->ChooseEven());
+                  }
+
+                  FindOrCreateVectorType(element_type_id,
+                                         vector->element_count());
+                } else if (const auto* int_type = operand_type->AsInteger()) {
+                  FindOrCreateFloatType(int_type->width());
+                } else {
+                  assert(operand_type->AsFloat() &&
+                         "Operand is not a scalar of numerical type");
+                  FindOrCreateIntegerType(operand_type->AsFloat()->width(),
+                                          GetFuzzerContext()->ChooseEven());
+                }
+
+                ApplyTransformation(TransformationEquationInstruction(
+                    GetFuzzerContext()->GetFreshId(), opcode,
+                    {operand_inst->result_id()}, instruction_descriptor));
+                return;
+              }
+            } break;
             case SpvOpIAdd:
             case SpvOpIAdd:
             case SpvOpISub: {
             case SpvOpISub: {
               // Instructions of integer (scalar or vector) result type are
               // Instructions of integer (scalar or vector) result type are
@@ -181,6 +275,20 @@ FuzzerPassAddEquationInstructions::GetIntegerInstructions(
   return result;
   return result;
 }
 }
 
 
+std::vector<opt::Instruction*>
+FuzzerPassAddEquationInstructions::GetFloatInstructions(
+    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->AsFloat() ||
+        (type->AsVector() && type->AsVector()->element_type()->AsFloat())) {
+      result.push_back(inst);
+    }
+  }
+  return result;
+}
+
 std::vector<opt::Instruction*>
 std::vector<opt::Instruction*>
 FuzzerPassAddEquationInstructions::GetBooleanInstructions(
 FuzzerPassAddEquationInstructions::GetBooleanInstructions(
     const std::vector<opt::Instruction*>& instructions) const {
     const std::vector<opt::Instruction*>& instructions) const {
@@ -225,10 +333,11 @@ FuzzerPassAddEquationInstructions::RestrictToElementBitWidth(
     if (type->AsVector()) {
     if (type->AsVector()) {
       type = type->AsVector()->element_type();
       type = type->AsVector()->element_type();
     }
     }
-    assert(type->AsInteger() &&
+    assert((type->AsInteger() || type->AsFloat()) &&
            "Precondition: all input instructions must "
            "Precondition: all input instructions must "
-           "have integer scalar or vector type.");
-    if (type->AsInteger()->width() == bit_width) {
+           "have integer or float scalar or vector type.");
+    if ((type->AsInteger() && type->AsInteger()->width() == bit_width) ||
+        (type->AsFloat() && type->AsFloat()->width() == bit_width)) {
       result.push_back(inst);
       result.push_back(inst);
     }
     }
   }
   }

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

@@ -41,6 +41,11 @@ class FuzzerPassAddEquationInstructions : public FuzzerPass {
   std::vector<opt::Instruction*> GetIntegerInstructions(
   std::vector<opt::Instruction*> GetIntegerInstructions(
       const std::vector<opt::Instruction*>& instructions) const;
       const std::vector<opt::Instruction*>& instructions) const;
 
 
+  // Returns only instructions, that have either a scalar floating-point or a
+  // vector type.
+  std::vector<opt::Instruction*> GetFloatInstructions(
+      const std::vector<opt::Instruction*>& instructions) const;
+
   // Yields those instructions in |instructions| that have boolean scalar or
   // Yields those instructions in |instructions| that have boolean scalar or
   // vector result type.
   // vector result type.
   std::vector<opt::Instruction*> GetBooleanInstructions(
   std::vector<opt::Instruction*> GetBooleanInstructions(
@@ -53,9 +58,9 @@ class FuzzerPassAddEquationInstructions : public FuzzerPass {
       const std::vector<opt::Instruction*>& instructions,
       const std::vector<opt::Instruction*>& instructions,
       uint32_t vector_width) const;
       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|.
+  // Requires that |instructions| are integer or float scalars or vectors.
+  // Returns only those instructions for which the bit-width of the underlying
+  // integer or floating-point type is |bit_width|.
   std::vector<opt::Instruction*> RestrictToElementBitWidth(
   std::vector<opt::Instruction*> RestrictToElementBitWidth(
       const std::vector<opt::Instruction*>& instructions,
       const std::vector<opt::Instruction*>& instructions,
       uint32_t bit_width) const;
       uint32_t bit_width) const;

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

@@ -32,7 +32,16 @@ FuzzerPassAddParameters::FuzzerPassAddParameters(
 FuzzerPassAddParameters::~FuzzerPassAddParameters() = default;
 FuzzerPassAddParameters::~FuzzerPassAddParameters() = default;
 
 
 void FuzzerPassAddParameters::Apply() {
 void FuzzerPassAddParameters::Apply() {
-  const auto& type_candidates = ComputeTypeCandidates();
+  // Compute type candidates for the new parameter.
+  std::vector<uint32_t> type_candidates;
+  for (const auto& type_inst : GetIRContext()->module()->GetTypes()) {
+    const auto* type =
+        GetIRContext()->get_type_mgr()->GetType(type_inst->result_id());
+    assert(type && "Type instruction is not registered in the type manager");
+    if (TransformationAddParameter::IsParameterTypeSupported(*type)) {
+      type_candidates.push_back(type_inst->result_id());
+    }
+  }
 
 
   if (type_candidates.empty()) {
   if (type_candidates.empty()) {
     // The module contains no suitable types to use in new parameters.
     // The module contains no suitable types to use in new parameters.
@@ -71,32 +80,6 @@ void FuzzerPassAddParameters::Apply() {
   }
   }
 }
 }
 
 
-std::vector<uint32_t> FuzzerPassAddParameters::ComputeTypeCandidates() const {
-  std::vector<uint32_t> result;
-
-  for (const auto* type_inst : GetIRContext()->module()->GetTypes()) {
-    // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
-    //  the number of types we support here is limited by the number of types
-    //  supported by |FindOrCreateZeroConstant|.
-    switch (type_inst->opcode()) {
-      case SpvOpTypeBool:
-      case SpvOpTypeInt:
-      case SpvOpTypeFloat:
-      case SpvOpTypeArray:
-      case SpvOpTypeMatrix:
-      case SpvOpTypeVector:
-      case SpvOpTypeStruct: {
-        result.push_back(type_inst->result_id());
-      } break;
-      default:
-        // Ignore other types.
-        break;
-    }
-  }
-
-  return result;
-}
-
 uint32_t FuzzerPassAddParameters::GetNumberOfParameters(
 uint32_t FuzzerPassAddParameters::GetNumberOfParameters(
     const opt::Function& function) const {
     const opt::Function& function) const {
   const auto* type = GetIRContext()->get_type_mgr()->GetType(
   const auto* type = GetIRContext()->get_type_mgr()->GetType(

+ 0 - 4
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.h

@@ -37,10 +37,6 @@ class FuzzerPassAddParameters : public FuzzerPass {
   void Apply() override;
   void Apply() override;
 
 
  private:
  private:
-  // Uses types, defined in the module, to compute a vector of their ids, which
-  // will be used as type ids of new parameters.
-  std::vector<uint32_t> ComputeTypeCandidates() const;
-
   // Returns number of parameters of |function|.
   // Returns number of parameters of |function|.
   uint32_t GetNumberOfParameters(const opt::Function& function) const;
   uint32_t GetNumberOfParameters(const opt::Function& function) const;
 };
 };

+ 126 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp

@@ -0,0 +1,126 @@
+// 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 "source/fuzz/fuzzer_pass_add_synonyms.h"
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_add_synonym.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddSynonyms::FuzzerPassAddSynonyms(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassAddSynonyms::~FuzzerPassAddSynonyms() = default;
+
+void FuzzerPassAddSynonyms::Apply() {
+  ForEachInstructionWithInstructionDescriptor(
+      [this](opt::Function* function, opt::BasicBlock* block,
+             opt::BasicBlock::iterator inst_it,
+             const protobufs::InstructionDescriptor& instruction_descriptor) {
+        // Skip |inst_it| if we can't insert anything above it. OpIAdd is just
+        // a representative of some instruction that might be produced by the
+        // transformation.
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpIAdd, inst_it)) {
+          return;
+        }
+
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfAddingSynonyms())) {
+          return;
+        }
+
+        auto synonym_type = GetFuzzerContext()->GetRandomSynonymType();
+
+        // Select all instructions that can be used to create a synonym to.
+        auto available_instructions = FindAvailableInstructions(
+            function, block, inst_it,
+            [synonym_type](opt::IRContext* ir_context, opt::Instruction* inst) {
+              // Check that we can create a synonym to |inst| as described by
+              // the |synonym_type| and insert it before |inst_it|.
+              return TransformationAddSynonym::IsInstructionValid(
+                  ir_context, inst, synonym_type);
+            });
+
+        if (available_instructions.empty()) {
+          return;
+        }
+
+        const auto* existing_synonym =
+            available_instructions[GetFuzzerContext()->RandomIndex(
+                available_instructions)];
+
+        // Make sure the module contains all instructions required to apply the
+        // transformation.
+        switch (synonym_type) {
+          case protobufs::TransformationAddSynonym::ADD_ZERO:
+          case protobufs::TransformationAddSynonym::SUB_ZERO:
+          case protobufs::TransformationAddSynonym::LOGICAL_OR:
+            // Create a zero constant to be used as an operand of the synonymous
+            // instruction.
+            FindOrCreateZeroConstant(existing_synonym->type_id());
+            break;
+          case protobufs::TransformationAddSynonym::MUL_ONE:
+          case protobufs::TransformationAddSynonym::LOGICAL_AND: {
+            const auto* existing_synonym_type =
+                GetIRContext()->get_type_mgr()->GetType(
+                    existing_synonym->type_id());
+            assert(existing_synonym_type && "Instruction has invalid type");
+
+            if (const auto* vector = existing_synonym_type->AsVector()) {
+              auto element_type_id =
+                  GetIRContext()->get_type_mgr()->GetId(vector->element_type());
+              assert(element_type_id && "Vector's element type is invalid");
+
+              auto one_word = vector->element_type()->AsFloat()
+                                  ? fuzzerutil::FloatToWord(1)
+                                  : 1u;
+              FindOrCreateCompositeConstant(
+                  std::vector<uint32_t>(
+                      vector->element_count(),
+                      FindOrCreateConstant({one_word}, element_type_id)),
+                  existing_synonym->type_id());
+            } else {
+              FindOrCreateConstant(
+                  {existing_synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1)
+                                                    : 1u},
+                  existing_synonym->type_id());
+            }
+          } break;
+          default:
+            // This assertion will fail if some SynonymType is missing from the
+            // switch statement.
+            assert(
+                !TransformationAddSynonym::IsAdditionalConstantRequired(
+                    synonym_type) &&
+                "|synonym_type| requires an additional constant to be present "
+                "in the module");
+            break;
+        }
+
+        ApplyTransformation(TransformationAddSynonym(
+            existing_synonym->result_id(), synonym_type,
+            GetFuzzerContext()->GetFreshId(), instruction_descriptor));
+      });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

@@ -0,0 +1,40 @@
+// 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_ADD_SYNONYMS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_SYNONYMS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Sprinkles instructions through the module that produce ids, synonymous to
+// some other instructions.
+class FuzzerPassAddSynonyms : public FuzzerPass {
+ public:
+  FuzzerPassAddSynonyms(opt::IRContext* ir_context,
+                        TransformationContext* transformation_context,
+                        FuzzerContext* fuzzer_context,
+                        protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddSynonyms() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_SYNONYMS_H_

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

@@ -73,10 +73,9 @@ void FuzzerPassApplyIdSynonyms::Apply() {
         continue;
         continue;
       }
       }
       // |use_index| is the absolute index of the operand.  We require
       // |use_index| is the absolute index of the operand.  We require
-      // the index of the operand restricted to input operands only, so
-      // we subtract the number of non-input operands from |use_index|.
+      // the index of the operand restricted to input operands only.
       uint32_t use_in_operand_index =
       uint32_t use_in_operand_index =
-          use_index - use_inst->NumOperands() + use_inst->NumInOperands();
+          fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index);
       if (!TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
       if (!TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
               GetIRContext(), use_inst, use_in_operand_index)) {
               GetIRContext(), use_inst, use_in_operand_index)) {
         continue;
         continue;

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

@@ -15,7 +15,8 @@
 #include "source/fuzz/fuzzer_pass_copy_objects.h"
 #include "source/fuzz/fuzzer_pass_copy_objects.h"
 
 
 #include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/fuzzer_util.h"
-#include "source/fuzz/transformation_copy_object.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation_add_synonym.h"
 
 
 namespace spvtools {
 namespace spvtools {
 namespace fuzz {
 namespace fuzz {
@@ -65,11 +66,12 @@ void FuzzerPassCopyObjects::Apply() {
 
 
         // Choose a copyable instruction at random, and create and apply an
         // Choose a copyable instruction at random, and create and apply an
         // object copying transformation based on it.
         // object copying transformation based on it.
-        ApplyTransformation(TransformationCopyObject(
+        ApplyTransformation(TransformationAddSynonym(
             relevant_instructions[GetFuzzerContext()->RandomIndex(
             relevant_instructions[GetFuzzerContext()->RandomIndex(
                                       relevant_instructions)]
                                       relevant_instructions)]
                 ->result_id(),
                 ->result_id(),
-            instruction_descriptor, GetFuzzerContext()->GetFreshId()));
+            protobufs::TransformationAddSynonym::COPY_OBJECT,
+            GetFuzzerContext()->GetFreshId(), instruction_descriptor));
       });
       });
 }
 }
 
 

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

@@ -0,0 +1,119 @@
+// Copyright (c) 2020 Stefano Milizia
+// 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_interchange_zero_like_constants.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/id_use_descriptor.h"
+#include "source/fuzz/transformation_record_synonymous_constants.h"
+#include "source/fuzz/transformation_replace_id_with_synonym.h"
+
+namespace spvtools {
+namespace fuzz {
+FuzzerPassInterchangeZeroLikeConstants::FuzzerPassInterchangeZeroLikeConstants(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassInterchangeZeroLikeConstants::
+    ~FuzzerPassInterchangeZeroLikeConstants() = default;
+
+uint32_t FuzzerPassInterchangeZeroLikeConstants::FindOrCreateToggledConstant(
+    opt::Instruction* declaration) {
+  auto constant = GetIRContext()->get_constant_mgr()->FindDeclaredConstant(
+      declaration->result_id());
+
+  // This pass only toggles zero-like constants
+  if (!constant->IsZero()) {
+    return 0;
+  }
+
+  if (constant->AsScalarConstant()) {
+    return FindOrCreateNullConstant(declaration->type_id());
+  } else if (constant->AsNullConstant()) {
+    // Add declaration of equivalent scalar constant
+    auto kind = constant->type()->kind();
+    if (kind == opt::analysis::Type::kBool ||
+        kind == opt::analysis::Type::kInteger ||
+        kind == opt::analysis::Type::kFloat) {
+      return FindOrCreateZeroConstant(declaration->type_id());
+    }
+  }
+
+  return 0;
+}
+
+void FuzzerPassInterchangeZeroLikeConstants::MaybeAddUseToReplace(
+    opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
+    std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
+        uses_to_replace) {
+  // Only consider this use if it is in a block
+  if (!GetIRContext()->get_instr_block(use_inst)) {
+    return;
+  }
+
+  // Get the index of the operand restricted to input operands.
+  uint32_t in_operand_index =
+      fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index);
+  auto id_use_descriptor =
+      MakeIdUseDescriptorFromUse(GetIRContext(), use_inst, in_operand_index);
+  uses_to_replace->emplace_back(
+      std::make_pair(id_use_descriptor, replacement_id));
+}
+
+void FuzzerPassInterchangeZeroLikeConstants::Apply() {
+  // Make vector keeping track of all the uses we want to replace.
+  // This is a vector of pairs, where the first element is an id use descriptor
+  // identifying the use of a constant id and the second is the id that should
+  // be used to replace it.
+  std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>> uses_to_replace;
+
+  for (auto constant : GetIRContext()->GetConstants()) {
+    uint32_t constant_id = constant->result_id();
+    uint32_t toggled_id = FindOrCreateToggledConstant(constant);
+
+    if (!toggled_id) {
+      // Not a zero-like constant
+      continue;
+    }
+
+    // Record synonymous constants
+    ApplyTransformation(
+        TransformationRecordSynonymousConstants(constant_id, toggled_id));
+
+    // Find all the uses of the constant and, for each, probabilistically
+    // decide whether to replace it.
+    GetIRContext()->get_def_use_mgr()->ForEachUse(
+        constant_id,
+        [this, toggled_id, &uses_to_replace](opt::Instruction* use_inst,
+                                             uint32_t use_index) -> void {
+          if (GetFuzzerContext()->ChoosePercentage(
+                  GetFuzzerContext()
+                      ->GetChanceOfInterchangingZeroLikeConstants())) {
+            MaybeAddUseToReplace(use_inst, use_index, toggled_id,
+                                 &uses_to_replace);
+          }
+        });
+  }
+
+  // Replace the ids
+  for (auto use_to_replace : uses_to_replace) {
+    MaybeApplyTransformation(TransformationReplaceIdWithSynonym(
+        use_to_replace.first, use_to_replace.second));
+  }
+}
+}  // namespace fuzz
+}  // namespace spvtools

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

@@ -0,0 +1,63 @@
+// Copyright (c) 2020 Stefano Milizia
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_
+#define SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A pass that:
+// - Finds all the zero-like constant definitions in the module and adds the
+//   definitions of the corresponding synonym, recording the fact that they
+//   are synonymous. If the synonym is already in the module, it does not
+//   add a new one.
+// - For each use of a zero-like constant, decides whether to change it to the
+//   id of the toggled constant.
+class FuzzerPassInterchangeZeroLikeConstants : public FuzzerPass {
+ public:
+  FuzzerPassInterchangeZeroLikeConstants(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassInterchangeZeroLikeConstants() override;
+
+  void Apply() override;
+
+ private:
+  // Given the declaration of a zero-like constant, it finds or creates the
+  // corresponding toggled constant (a scalar constant of value 0 becomes a
+  // null constant of the same type and vice versa).
+  // Returns the id of the toggled instruction if the constant is zero-like,
+  // 0 otherwise.
+  uint32_t FindOrCreateToggledConstant(opt::Instruction* declaration);
+
+  // Given an id use (described by an instruction and an index) and an id with
+  // which the original one should be replaced, adds a pair (with the elements
+  // being the corresponding id use descriptor and the replacement id) to
+  // |uses_to_replace| if the use is in an instruction block, otherwise does
+  // nothing.
+  void MaybeAddUseToReplace(
+      opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
+      std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
+          uses_to_replace);
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+#endif  // SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_

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

@@ -56,11 +56,7 @@ void FuzzerPassMergeBlocks::Apply() {
     uint32_t index = GetFuzzerContext()->RandomIndex(potential_transformations);
     uint32_t index = GetFuzzerContext()->RandomIndex(potential_transformations);
     auto transformation = potential_transformations.at(index);
     auto transformation = potential_transformations.at(index);
     potential_transformations.erase(potential_transformations.begin() + index);
     potential_transformations.erase(potential_transformations.begin() + index);
-    if (transformation.IsApplicable(GetIRContext(),
-                                    *GetTransformationContext())) {
-      transformation.Apply(GetIRContext(), GetTransformationContext());
-      *GetTransformations()->add_transformation() = transformation.ToMessage();
-    }
+    MaybeApplyTransformation(transformation);
   }
   }
 }
 }
 
 

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

@@ -477,28 +477,18 @@ void FuzzerPassObfuscateConstants::Apply() {
           skipped_opcode_count.clear();
           skipped_opcode_count.clear();
         }
         }
 
 
-        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;
+        // The instruction must not be an OpVariable, the only id that an
+        // OpVariable uses is an initializer id, which has to remain
+        // constant.
+        if (inst.opcode() != SpvOpVariable) {
+          // 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);
+          }
         }
         }
 
 
         if (!inst.HasResultId()) {
         if (!inst.HasResultId()) {

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

@@ -89,11 +89,7 @@ void FuzzerPassOutlineFunctions::Apply() {
         /*new_callee_result_id*/ GetFuzzerContext()->GetFreshId(),
         /*new_callee_result_id*/ GetFuzzerContext()->GetFreshId(),
         /*input_id_to_fresh_id*/ std::move(input_id_to_fresh_id),
         /*input_id_to_fresh_id*/ std::move(input_id_to_fresh_id),
         /*output_id_to_fresh_id*/ std::move(output_id_to_fresh_id));
         /*output_id_to_fresh_id*/ std::move(output_id_to_fresh_id));
-    if (transformation.IsApplicable(GetIRContext(),
-                                    *GetTransformationContext())) {
-      transformation.Apply(GetIRContext(), GetTransformationContext());
-      *GetTransformations()->add_transformation() = transformation.ToMessage();
-    }
+    MaybeApplyTransformation(transformation);
   }
   }
 }
 }
 
 

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

@@ -67,12 +67,7 @@ void FuzzerPassPermuteBlocks::Apply() {
       // down indefinitely.
       // down indefinitely.
       while (true) {
       while (true) {
         TransformationMoveBlockDown transformation(*id);
         TransformationMoveBlockDown transformation(*id);
-        if (transformation.IsApplicable(GetIRContext(),
-                                        *GetTransformationContext())) {
-          transformation.Apply(GetIRContext(), GetTransformationContext());
-          *GetTransformations()->add_transformation() =
-              transformation.ToMessage();
-        } else {
+        if (!MaybeApplyTransformation(transformation)) {
           break;
           break;
         }
         }
       }
       }

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

@@ -45,6 +45,7 @@ void FuzzerPassReplaceLinearAlgebraInstructions::Apply() {
         instruction->opcode() != SpvOpMatrixTimesScalar &&
         instruction->opcode() != SpvOpMatrixTimesScalar &&
         instruction->opcode() != SpvOpVectorTimesMatrix &&
         instruction->opcode() != SpvOpVectorTimesMatrix &&
         instruction->opcode() != SpvOpMatrixTimesVector &&
         instruction->opcode() != SpvOpMatrixTimesVector &&
+        instruction->opcode() != SpvOpMatrixTimesMatrix &&
         instruction->opcode() != SpvOpDot) {
         instruction->opcode() != SpvOpDot) {
       return;
       return;
     }
     }

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

@@ -58,7 +58,7 @@ void FuzzerPassReplaceParameterWithGlobal::Apply() {
                                param->type_id());
                                param->type_id());
                        assert(param_type && "Parameter has invalid type");
                        assert(param_type && "Parameter has invalid type");
                        return TransformationReplaceParameterWithGlobal::
                        return TransformationReplaceParameterWithGlobal::
-                           CanReplaceFunctionParameterType(*param_type);
+                           IsParameterTypeSupported(*param_type);
                      })) {
                      })) {
       continue;
       continue;
     }
     }
@@ -71,8 +71,9 @@ void FuzzerPassReplaceParameterWithGlobal::Apply() {
       param_type =
       param_type =
           GetIRContext()->get_type_mgr()->GetType(replaced_param->type_id());
           GetIRContext()->get_type_mgr()->GetType(replaced_param->type_id());
       assert(param_type && "Parameter has invalid type");
       assert(param_type && "Parameter has invalid type");
-    } while (!TransformationReplaceParameterWithGlobal::
-                 CanReplaceFunctionParameterType(*param_type));
+    } while (
+        !TransformationReplaceParameterWithGlobal::IsParameterTypeSupported(
+            *param_type));
 
 
     assert(replaced_param && "Unable to find a parameter to replace");
     assert(replaced_param && "Unable to find a parameter to replace");
 
 

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

@@ -96,11 +96,7 @@ void FuzzerPassSplitBlocks::Apply() {
     // If the position we have chosen turns out to be a valid place to split
     // If the position we have chosen turns out to be a valid place to split
     // the block, we apply the split. Otherwise the block just doesn't get
     // the block, we apply the split. Otherwise the block just doesn't get
     // split.
     // split.
-    if (transformation.IsApplicable(GetIRContext(),
-                                    *GetTransformationContext())) {
-      transformation.Apply(GetIRContext(), GetTransformationContext());
-      *GetTransformations()->add_transformation() = transformation.ToMessage();
-    }
+    MaybeApplyTransformation(transformation);
   }
   }
 }
 }
 
 

+ 25 - 39
3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp

@@ -156,6 +156,25 @@ void AddUnreachableEdgeAndUpdateOpPhis(
   }
   }
 }
 }
 
 
+bool BlockIsBackEdge(opt::IRContext* context, uint32_t block_id,
+                     uint32_t loop_header_id) {
+  auto block = context->cfg()->block(block_id);
+  auto loop_header = context->cfg()->block(loop_header_id);
+
+  // |block| and |loop_header| must be defined, |loop_header| must be in fact
+  // loop header and |block| must branch to it.
+  if (!(block && loop_header && loop_header->IsLoopHeader() &&
+        block->IsSuccessor(loop_header))) {
+    return false;
+  }
+
+  // |block_id| must be reachable and be dominated by |loop_header|.
+  opt::DominatorAnalysis* dominator_analysis =
+      context->GetDominatorAnalysis(loop_header->GetParent());
+  return dominator_analysis->IsReachable(block_id) &&
+         dominator_analysis->Dominates(loop_header_id, block_id);
+}
+
 bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
 bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
                                     uint32_t maybe_loop_header_id) {
                                     uint32_t maybe_loop_header_id) {
   // We deem a block to be part of a loop's continue construct if the loop's
   // We deem a block to be part of a loop's continue construct if the loop's
@@ -533,6 +552,12 @@ uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
   return 0;
   return 0;
 }
 }
 
 
+uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst,
+                                        uint32_t absolute_index) {
+  // Subtract the number of non-input operands from the index
+  return absolute_index - inst.NumOperands() + inst.NumInOperands();
+}
+
 bool IsNullConstantSupported(const opt::analysis::Type& type) {
 bool IsNullConstantSupported(const opt::analysis::Type& type) {
   return type.AsBool() || type.AsInteger() || type.AsFloat() ||
   return type.AsBool() || type.AsInteger() || type.AsFloat() ||
          type.AsMatrix() || type.AsVector() || type.AsArray() ||
          type.AsMatrix() || type.AsVector() || type.AsArray() ||
@@ -1013,45 +1038,6 @@ void AddStructType(opt::IRContext* ir_context, uint32_t result_id,
   UpdateModuleIdBound(ir_context, result_id);
   UpdateModuleIdBound(ir_context, result_id);
 }
 }
 
 
-uint32_t FindOrCreateIntegerType(opt::IRContext* ir_context, uint32_t result_id,
-                                 uint32_t width, bool is_signed) {
-  if (auto existing_id = MaybeGetIntegerType(ir_context, width, is_signed)) {
-    return existing_id;
-  }
-  AddIntegerType(ir_context, result_id, width, is_signed);
-  return result_id;
-}
-
-uint32_t FindOrCreateFloatType(opt::IRContext* ir_context, uint32_t result_id,
-                               uint32_t width) {
-  if (auto existing_id = MaybeGetFloatType(ir_context, width)) {
-    return existing_id;
-  }
-  AddFloatType(ir_context, result_id, width);
-  return result_id;
-}
-
-uint32_t FindOrCreateVectorType(opt::IRContext* ir_context, uint32_t result_id,
-                                uint32_t component_type_id,
-                                uint32_t element_count) {
-  if (auto existing_id =
-          MaybeGetVectorType(ir_context, component_type_id, element_count)) {
-    return existing_id;
-  }
-  AddVectorType(ir_context, result_id, component_type_id, element_count);
-  return result_id;
-}
-
-uint32_t FindOrCreateStructType(
-    opt::IRContext* ir_context, uint32_t result_id,
-    const std::vector<uint32_t>& component_type_ids) {
-  if (auto existing_id = MaybeGetStructType(ir_context, component_type_ids)) {
-    return existing_id;
-  }
-  AddStructType(ir_context, result_id, component_type_ids);
-  return result_id;
-}
-
 }  // namespace fuzzerutil
 }  // namespace fuzzerutil
 
 
 }  // namespace fuzz
 }  // namespace fuzz

+ 17 - 32
3rdparty/spirv-tools/source/fuzz/fuzzer_util.h

@@ -64,6 +64,12 @@ void AddUnreachableEdgeAndUpdateOpPhis(
     bool condition_value,
     bool condition_value,
     const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids);
     const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids);
 
 
+// Returns true if and only if |loop_header_id| is a loop header and
+// |block_id| is a reachable block branching to and dominated by
+// |loop_header_id|.
+bool BlockIsBackEdge(opt::IRContext* context, uint32_t block_id,
+                     uint32_t loop_header_id);
+
 // Returns true if and only if |maybe_loop_header_id| is a loop header and
 // Returns true if and only if |maybe_loop_header_id| is a loop header and
 // |block_id| is in the continue construct of the associated loop.
 // |block_id| is in the continue construct of the associated loop.
 bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
 bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
@@ -207,6 +213,11 @@ SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context,
 uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
 uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
                              SpvStorageClass storage_class);
                              SpvStorageClass storage_class);
 
 
+// Given an instruction |inst| and an operand absolute index |absolute_index|,
+// returns the index of the operand restricted to the input operands.
+uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst,
+                                        uint32_t absolute_index);
+
 // Returns true if and only if |type| is one of the types for which it is legal
 // Returns true if and only if |type| is one of the types for which it is legal
 // to have an OpConstantNull value.
 // to have an OpConstantNull value.
 bool IsNullConstantSupported(const opt::analysis::Type& type);
 bool IsNullConstantSupported(const opt::analysis::Type& type);
@@ -369,38 +380,12 @@ void AddVectorType(opt::IRContext* ir_context, uint32_t result_id,
 void AddStructType(opt::IRContext* ir_context, uint32_t result_id,
 void AddStructType(opt::IRContext* ir_context, uint32_t result_id,
                    const std::vector<uint32_t>& component_type_ids);
                    const std::vector<uint32_t>& component_type_ids);
 
 
-// Returns a result id of an OpTypeInt instruction in the module. Creates a new
-// instruction with |result_id|, if no required OpTypeInt is present in the
-// module, and returns |result_id|. Updates module's id bound to accommodate for
-// |result_id|.
-uint32_t FindOrCreateIntegerType(opt::IRContext* ir_context, uint32_t result_id,
-                                 uint32_t width, bool is_signed);
-
-// Returns a result id of an OpTypeFloat instruction in the module. Creates a
-// new instruction with |result_id|, if no required OpTypeFloat is present in
-// the module, and returns |result_id|. Updates module's id bound
-// to accommodate for |result_id|.
-uint32_t FindOrCreateFloatType(opt::IRContext* ir_context, uint32_t result_id,
-                               uint32_t width);
-
-// Returns a result id of an OpTypeVector instruction in the module. Creates a
-// new instruction with |result_id|, if no required OpTypeVector is present in
-// the module, and returns |result_id|. |component_type_id| must be a valid
-// result id of an OpTypeInt, OpTypeFloat or OpTypeBool instruction in the
-// module. |element_count| must be in the range [2, 4]. Updates module's id
-// bound to accommodate for |result_id|.
-uint32_t FindOrCreateVectorType(opt::IRContext* ir_context, uint32_t result_id,
-                                uint32_t component_type_id,
-                                uint32_t element_count);
-
-// Returns a result id of an OpTypeStruct instruction in the module. Creates a
-// new instruction with |result_id|, if no required OpTypeStruct is present in
-// the module, and returns |result_id|. Updates module's id bound
-// to accommodate for |result_id|. |component_type_ids| may not contain a result
-// id of an OpTypeFunction.
-uint32_t FindOrCreateStructType(
-    opt::IRContext* ir_context, uint32_t result_id,
-    const std::vector<uint32_t>& component_type_ids);
+// Returns a bit pattern that represents a floating-point |value|.
+inline uint32_t FloatToWord(float value) {
+  uint32_t result;
+  memcpy(&result, &value, sizeof(uint32_t));
+  return result;
+}
 
 
 }  // namespace fuzzerutil
 }  // namespace fuzzerutil
 
 

+ 1 - 0
3rdparty/spirv-tools/source/fuzz/protobufs/spirvfuzz_protobufs.h

@@ -39,6 +39,7 @@
 // where warnings are ignored.
 // where warnings are ignored.
 
 
 #include "google/protobuf/util/json_util.h"
 #include "google/protobuf/util/json_util.h"
+#include "google/protobuf/util/message_differencer.h"
 #include "source/fuzz/protobufs/spvtoolsfuzz.pb.h"
 #include "source/fuzz/protobufs/spvtoolsfuzz.pb.h"
 
 
 #if defined(__clang__)
 #if defined(__clang__)

+ 94 - 63
3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto

@@ -341,51 +341,51 @@ message Transformation {
     TransformationAddTypePointer add_type_pointer = 10;
     TransformationAddTypePointer add_type_pointer = 10;
     TransformationReplaceConstantWithUniform replace_constant_with_uniform = 11;
     TransformationReplaceConstantWithUniform replace_constant_with_uniform = 11;
     TransformationAddDeadContinue add_dead_continue = 12;
     TransformationAddDeadContinue add_dead_continue = 12;
-    TransformationCopyObject copy_object = 13;
-    TransformationReplaceIdWithSynonym replace_id_with_synonym = 14;
-    TransformationSetSelectionControl set_selection_control = 15;
-    TransformationCompositeConstruct composite_construct = 16;
-    TransformationSetLoopControl set_loop_control = 17;
-    TransformationSetFunctionControl set_function_control = 18;
-    TransformationAddNoContractionDecoration add_no_contraction_decoration = 19;
-    TransformationSetMemoryOperandsMask set_memory_operands_mask = 20;
-    TransformationCompositeExtract composite_extract = 21;
-    TransformationVectorShuffle vector_shuffle = 22;
-    TransformationOutlineFunction outline_function = 23;
-    TransformationMergeBlocks merge_blocks = 24;
-    TransformationAddTypeVector add_type_vector = 25;
-    TransformationAddTypeArray add_type_array = 26;
-    TransformationAddTypeMatrix add_type_matrix = 27;
-    TransformationAddTypeStruct add_type_struct = 28;
-    TransformationAddTypeFunction add_type_function = 29;
-    TransformationAddConstantComposite add_constant_composite = 30;
-    TransformationAddGlobalVariable add_global_variable = 31;
-    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;
-    TransformationAddConstantNull add_constant_null = 44;
-    TransformationComputeDataSynonymFactClosure compute_data_synonym_fact_closure = 45;
-    TransformationAdjustBranchWeights adjust_branch_weights = 46;
-    TransformationPushIdThroughVariable push_id_through_variable = 47;
-    TransformationAddSpecConstantOp add_spec_constant_op = 48;
-    TransformationReplaceLinearAlgebraInstruction replace_linear_algebra_instruction = 49;
-    TransformationSwapConditionalBranchOperands swap_conditional_branch_operands = 50;
-    TransformationPermutePhiOperands permute_phi_operands = 51;
-    TransformationAddParameter add_parameter = 52;
-    TransformationAddCopyMemory add_copy_memory = 53;
-    TransformationInvertComparisonOperator invert_comparison_operator = 54;
-    TransformationAddImageSampleUnusedComponents add_image_sample_unused_components = 55;
-    TransformationReplaceParameterWithGlobal replace_parameter_with_global = 56;
-    TransformationRecordSynonymousConstants record_synonymous_constants = 57;
+    TransformationReplaceIdWithSynonym replace_id_with_synonym = 13;
+    TransformationSetSelectionControl set_selection_control = 14;
+    TransformationCompositeConstruct composite_construct = 15;
+    TransformationSetLoopControl set_loop_control = 16;
+    TransformationSetFunctionControl set_function_control = 17;
+    TransformationAddNoContractionDecoration add_no_contraction_decoration = 18;
+    TransformationSetMemoryOperandsMask set_memory_operands_mask = 19;
+    TransformationCompositeExtract composite_extract = 20;
+    TransformationVectorShuffle vector_shuffle = 21;
+    TransformationOutlineFunction outline_function = 22;
+    TransformationMergeBlocks merge_blocks = 23;
+    TransformationAddTypeVector add_type_vector = 24;
+    TransformationAddTypeArray add_type_array = 25;
+    TransformationAddTypeMatrix add_type_matrix = 26;
+    TransformationAddTypeStruct add_type_struct = 27;
+    TransformationAddTypeFunction add_type_function = 28;
+    TransformationAddConstantComposite add_constant_composite = 29;
+    TransformationAddGlobalVariable add_global_variable = 30;
+    TransformationAddGlobalUndef add_global_undef = 31;
+    TransformationAddFunction add_function = 32;
+    TransformationAddDeadBlock add_dead_block = 33;
+    TransformationAddLocalVariable add_local_variable = 34;
+    TransformationLoad load = 35;
+    TransformationStore store = 36;
+    TransformationFunctionCall function_call = 37;
+    TransformationAccessChain access_chain = 38;
+    TransformationEquationInstruction equation_instruction = 39;
+    TransformationSwapCommutableOperands swap_commutable_operands = 40;
+    TransformationPermuteFunctionParameters permute_function_parameters = 41;
+    TransformationToggleAccessChainInstruction toggle_access_chain_instruction = 42;
+    TransformationAddConstantNull add_constant_null = 43;
+    TransformationComputeDataSynonymFactClosure compute_data_synonym_fact_closure = 44;
+    TransformationAdjustBranchWeights adjust_branch_weights = 45;
+    TransformationPushIdThroughVariable push_id_through_variable = 46;
+    TransformationAddSpecConstantOp add_spec_constant_op = 47;
+    TransformationReplaceLinearAlgebraInstruction replace_linear_algebra_instruction = 48;
+    TransformationSwapConditionalBranchOperands swap_conditional_branch_operands = 49;
+    TransformationPermutePhiOperands permute_phi_operands = 50;
+    TransformationAddParameter add_parameter = 51;
+    TransformationAddCopyMemory add_copy_memory = 52;
+    TransformationInvertComparisonOperator invert_comparison_operator = 53;
+    TransformationAddImageSampleUnusedComponents add_image_sample_unused_components = 54;
+    TransformationReplaceParameterWithGlobal replace_parameter_with_global = 55;
+    TransformationRecordSynonymousConstants record_synonymous_constants = 56;
+    TransformationAddSynonym add_synonym = 57;
     // Add additional option using the next available number.
     // Add additional option using the next available number.
   }
   }
 }
 }
@@ -705,6 +705,54 @@ message TransformationAddSpecConstantOp {
 
 
 }
 }
 
 
+message TransformationAddSynonym {
+
+  // Adds a |synonymous_instruction| before |insert_before| instruction with
+  // and creates a fact that |result_id| and the result id of |synonymous_instruction|
+  // are synonymous.
+
+  // Result id of the first synonym.
+  uint32 result_id = 1;
+
+  // Type of the synonym to apply. Some types might produce instructions
+  // with commutative operands. Such types do not specify the order of the
+  // operands since we have a special transformation to swap commutable operands.
+  //
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3499):
+  //  Consider adding more types here.
+  enum SynonymType {
+    // New synonym is derived by adding zero to the |result_id|.
+    ADD_ZERO = 0;
+
+    // New synonym is derived by subtracting zero from the |result_id|.
+    SUB_ZERO = 1;
+
+    // New synonym is derived by multiplying |result_id| by one.
+    MUL_ONE = 2;
+
+    // New synonym is derived by applying OpCopyObject instruction to |result_id|.
+    COPY_OBJECT = 3;
+
+    // New synonym is derived by applying OpLogicalOr to |result_id| with the second
+    // operand being 'false'.
+    LOGICAL_OR = 4;
+
+    // New synonym is derived by applying OpLogicalAnd to |result_id| with the second
+    // operand being 'true'.
+    LOGICAL_AND = 5;
+  }
+
+  // Type of the synonym to create. See SynonymType for more details.
+  SynonymType synonym_type = 2;
+
+  // Fresh result id for a created synonym.
+  uint32 synonym_fresh_id = 3;
+
+  // An instruction to insert a new synonym before.
+  InstructionDescriptor insert_before = 4;
+
+}
+
 message TransformationAddTypeArray {
 message TransformationAddTypeArray {
 
 
   // Adds an array type of the given element type and size to the module
   // Adds an array type of the given element type and size to the module
@@ -896,23 +944,6 @@ message TransformationComputeDataSynonymFactClosure {
 
 
 }
 }
 
 
-message TransformationCopyObject {
-
-  // A transformation that introduces an OpCopyObject instruction to make a
-  // copy of an object.
-
-  // Id of the object to be copied
-  uint32 object = 1;
-
-  // A descriptor for an instruction in a block before which the new
-  // OpCopyObject instruction should be inserted
-  InstructionDescriptor instruction_to_insert_before = 2;
-
-  // A fresh id for the copied object
-  uint32 fresh_id = 3;
-
-}
-
 message TransformationEquationInstruction {
 message TransformationEquationInstruction {
 
 
   // A transformation that adds an instruction to the module that defines an
   // A transformation that adds an instruction to the module that defines an
@@ -1230,13 +1261,13 @@ message TransformationReplaceLinearAlgebraInstruction {
   //   OpMatrixTimesScalar
   //   OpMatrixTimesScalar
   //   OpVectorTimesMatrix
   //   OpVectorTimesMatrix
   //   OpMatrixTimesVector
   //   OpMatrixTimesVector
+  //   OpMatrixTimesMatrix
   //   OpDot
   //   OpDot
   // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
   // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
   // Right now we only support certain operations. When this issue is addressed
   // Right now we only support certain operations. When this issue is addressed
   // the supporting comments can be removed.
   // the supporting comments can be removed.
   // To be supported in the future:
   // To be supported in the future:
   //   OpTranspose
   //   OpTranspose
-  //   OpMatrixTimesMatrix
   //   OpOuterProduct
   //   OpOuterProduct
   InstructionDescriptor instruction_descriptor = 2;
   InstructionDescriptor instruction_descriptor = 2;
 
 

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

@@ -65,12 +65,20 @@ Replayer::ReplayerResultStatus Replayer::Run(
     const std::vector<uint32_t>& binary_in,
     const std::vector<uint32_t>& binary_in,
     const protobufs::FactSequence& initial_facts,
     const protobufs::FactSequence& initial_facts,
     const protobufs::TransformationSequence& transformation_sequence_in,
     const protobufs::TransformationSequence& transformation_sequence_in,
-    std::vector<uint32_t>* binary_out,
+    uint32_t num_transformations_to_apply, std::vector<uint32_t>* binary_out,
     protobufs::TransformationSequence* transformation_sequence_out) const {
     protobufs::TransformationSequence* transformation_sequence_out) const {
   // Check compatibility between the library version being linked with and the
   // Check compatibility between the library version being linked with and the
   // header files being used.
   // header files being used.
   GOOGLE_PROTOBUF_VERIFY_VERSION;
   GOOGLE_PROTOBUF_VERIFY_VERSION;
 
 
+  if (num_transformations_to_apply >
+      static_cast<uint32_t>(transformation_sequence_in.transformation_size())) {
+    impl_->consumer(SPV_MSG_ERROR, nullptr, {},
+                    "The number of transformations to be replayed must not "
+                    "exceed the size of the transformation sequence.");
+    return Replayer::ReplayerResultStatus::kTooManyTransformationsRequested;
+  }
+
   spvtools::SpirvTools tools(impl_->target_env);
   spvtools::SpirvTools tools(impl_->target_env);
   if (!tools.IsValid()) {
   if (!tools.IsValid()) {
     impl_->consumer(SPV_MSG_ERROR, nullptr, {},
     impl_->consumer(SPV_MSG_ERROR, nullptr, {},
@@ -104,7 +112,13 @@ Replayer::ReplayerResultStatus Replayer::Run(
                                                impl_->validator_options);
                                                impl_->validator_options);
 
 
   // Consider the transformation proto messages in turn.
   // Consider the transformation proto messages in turn.
+  uint32_t counter = 0;
   for (auto& message : transformation_sequence_in.transformation()) {
   for (auto& message : transformation_sequence_in.transformation()) {
+    if (counter >= num_transformations_to_apply) {
+      break;
+    }
+    counter++;
+
     auto transformation = Transformation::FromMessage(message);
     auto transformation = Transformation::FromMessage(message);
 
 
     // Check whether the transformation can be applied.
     // Check whether the transformation can be applied.

+ 8 - 6
3rdparty/spirv-tools/source/fuzz/replayer.h

@@ -34,6 +34,7 @@ class Replayer {
     kFailedToCreateSpirvToolsInterface,
     kFailedToCreateSpirvToolsInterface,
     kInitialBinaryInvalid,
     kInitialBinaryInvalid,
     kReplayValidationFailure,
     kReplayValidationFailure,
+    kTooManyTransformationsRequested,
   };
   };
 
 
   // Constructs a replayer from the given target environment.
   // Constructs a replayer from the given target environment.
@@ -52,16 +53,17 @@ class Replayer {
   // invoked once for each message communicated from the library.
   // invoked once for each message communicated from the library.
   void SetMessageConsumer(MessageConsumer consumer);
   void SetMessageConsumer(MessageConsumer consumer);
 
 
-  // Transforms |binary_in| to |binary_out| by attempting to apply the
-  // transformations from |transformation_sequence_in|.  Initial facts about the
-  // input binary and the context in which it will execute are provided via
-  // |initial_facts|.  The transformations that were successfully applied are
-  // returned via |transformation_sequence_out|.
+  // Transforms |binary_in| to |binary_out| by attempting to apply the first
+  // |num_transformations_to_apply| transformations from
+  // |transformation_sequence_in|.  Initial facts about the input binary and the
+  // context in which it will execute are provided via |initial_facts|.  The
+  // transformations that were successfully applied are returned via
+  // |transformation_sequence_out|.
   ReplayerResultStatus Run(
   ReplayerResultStatus Run(
       const std::vector<uint32_t>& binary_in,
       const std::vector<uint32_t>& binary_in,
       const protobufs::FactSequence& initial_facts,
       const protobufs::FactSequence& initial_facts,
       const protobufs::TransformationSequence& transformation_sequence_in,
       const protobufs::TransformationSequence& transformation_sequence_in,
-      std::vector<uint32_t>* binary_out,
+      uint32_t num_transformations_to_apply, std::vector<uint32_t>* binary_out,
       protobufs::TransformationSequence* transformation_sequence_out) const;
       protobufs::TransformationSequence* transformation_sequence_out) const;
 
 
  private:
  private:

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

@@ -124,6 +124,7 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
   if (Replayer(impl_->target_env, impl_->validate_during_replay,
   if (Replayer(impl_->target_env, impl_->validate_during_replay,
                impl_->validator_options)
                impl_->validator_options)
           .Run(binary_in, initial_facts, transformation_sequence_in,
           .Run(binary_in, initial_facts, transformation_sequence_in,
+               transformation_sequence_in.transformation_size(),
                &current_best_binary, &current_best_transformations) !=
                &current_best_binary, &current_best_transformations) !=
       Replayer::ReplayerResultStatus::kComplete) {
       Replayer::ReplayerResultStatus::kComplete) {
     return ShrinkerResultStatus::kReplayFailed;
     return ShrinkerResultStatus::kReplayFailed;
@@ -196,6 +197,7 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
       if (Replayer(impl_->target_env, impl_->validate_during_replay,
       if (Replayer(impl_->target_env, impl_->validate_during_replay,
                    impl_->validator_options)
                    impl_->validator_options)
               .Run(binary_in, initial_facts, transformations_with_chunk_removed,
               .Run(binary_in, initial_facts, transformations_with_chunk_removed,
+                   transformations_with_chunk_removed.transformation_size(),
                    &next_binary, &next_transformation_sequence) !=
                    &next_binary, &next_transformation_sequence) !=
           Replayer::ReplayerResultStatus::kComplete) {
           Replayer::ReplayerResultStatus::kComplete) {
         // Replay should not fail; if it does, we need to abort shrinking.
         // Replay should not fail; if it does, we need to abort shrinking.

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

@@ -34,6 +34,7 @@
 #include "source/fuzz/transformation_add_no_contraction_decoration.h"
 #include "source/fuzz/transformation_add_no_contraction_decoration.h"
 #include "source/fuzz/transformation_add_parameter.h"
 #include "source/fuzz/transformation_add_parameter.h"
 #include "source/fuzz/transformation_add_spec_constant_op.h"
 #include "source/fuzz/transformation_add_spec_constant_op.h"
+#include "source/fuzz/transformation_add_synonym.h"
 #include "source/fuzz/transformation_add_type_array.h"
 #include "source/fuzz/transformation_add_type_array.h"
 #include "source/fuzz/transformation_add_type_boolean.h"
 #include "source/fuzz/transformation_add_type_boolean.h"
 #include "source/fuzz/transformation_add_type_float.h"
 #include "source/fuzz/transformation_add_type_float.h"
@@ -47,7 +48,6 @@
 #include "source/fuzz/transformation_composite_construct.h"
 #include "source/fuzz/transformation_composite_construct.h"
 #include "source/fuzz/transformation_composite_extract.h"
 #include "source/fuzz/transformation_composite_extract.h"
 #include "source/fuzz/transformation_compute_data_synonym_fact_closure.h"
 #include "source/fuzz/transformation_compute_data_synonym_fact_closure.h"
-#include "source/fuzz/transformation_copy_object.h"
 #include "source/fuzz/transformation_equation_instruction.h"
 #include "source/fuzz/transformation_equation_instruction.h"
 #include "source/fuzz/transformation_function_call.h"
 #include "source/fuzz/transformation_function_call.h"
 #include "source/fuzz/transformation_invert_comparison_operator.h"
 #include "source/fuzz/transformation_invert_comparison_operator.h"
@@ -131,6 +131,8 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
     case protobufs::Transformation::TransformationCase::kAddSpecConstantOp:
     case protobufs::Transformation::TransformationCase::kAddSpecConstantOp:
       return MakeUnique<TransformationAddSpecConstantOp>(
       return MakeUnique<TransformationAddSpecConstantOp>(
           message.add_spec_constant_op());
           message.add_spec_constant_op());
+    case protobufs::Transformation::TransformationCase::kAddSynonym:
+      return MakeUnique<TransformationAddSynonym>(message.add_synonym());
     case protobufs::Transformation::TransformationCase::kAddTypeArray:
     case protobufs::Transformation::TransformationCase::kAddTypeArray:
       return MakeUnique<TransformationAddTypeArray>(message.add_type_array());
       return MakeUnique<TransformationAddTypeArray>(message.add_type_array());
     case protobufs::Transformation::TransformationCase::kAddTypeBoolean:
     case protobufs::Transformation::TransformationCase::kAddTypeBoolean:
@@ -165,8 +167,6 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
         kComputeDataSynonymFactClosure:
         kComputeDataSynonymFactClosure:
       return MakeUnique<TransformationComputeDataSynonymFactClosure>(
       return MakeUnique<TransformationComputeDataSynonymFactClosure>(
           message.compute_data_synonym_fact_closure());
           message.compute_data_synonym_fact_closure());
-    case protobufs::Transformation::TransformationCase::kCopyObject:
-      return MakeUnique<TransformationCopyObject>(message.copy_object());
     case protobufs::Transformation::TransformationCase::kEquationInstruction:
     case protobufs::Transformation::TransformationCase::kEquationInstruction:
       return MakeUnique<TransformationEquationInstruction>(
       return MakeUnique<TransformationEquationInstruction>(
           message.equation_instruction());
           message.equation_instruction());

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

@@ -74,9 +74,9 @@ bool TransformationAccessChain::IsApplicable(
   switch (pointer->opcode()) {
   switch (pointer->opcode()) {
     case SpvOpConstantNull:
     case SpvOpConstantNull:
     case SpvOpUndef:
     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.
+      assert(
+          false &&
+          "Access chains should not be created from null/undefined pointers");
       return false;
       return false;
     default:
     default:
       break;
       break;

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

@@ -85,12 +85,10 @@ bool TransformationAddDeadBreak::AddingBreakRespectsStructuredControlFlow(
     // loop as part of the loop, but it is not legal to jump from a loop's
     // loop as part of the loop, but it is not legal to jump from a loop's
     // continue construct to the loop's merge (except from the back-edge block),
     // continue construct to the loop's merge (except from the back-edge block),
     // so we need to check for this case.
     // so we need to check for this case.
-    //
-    // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2577): We do not
-    //  currently allow a dead break from a back edge block, but we could and
-    //  ultimately should.
     return !fuzzerutil::BlockIsInLoopContinueConstruct(
     return !fuzzerutil::BlockIsInLoopContinueConstruct(
-        ir_context, message_.from_block(), containing_construct);
+               ir_context, message_.from_block(), containing_construct) ||
+           fuzzerutil::BlockIsBackEdge(ir_context, message_.from_block(),
+                                       containing_construct);
   }
   }
 
 
   // Case (3) holds if and only if |to_block| is the merge block for this
   // Case (3) holds if and only if |to_block| is the merge block for this
@@ -102,7 +100,9 @@ bool TransformationAddDeadBreak::AddingBreakRespectsStructuredControlFlow(
       message_.to_block() ==
       message_.to_block() ==
           ir_context->cfg()->block(containing_loop_header)->MergeBlockId()) {
           ir_context->cfg()->block(containing_loop_header)->MergeBlockId()) {
     return !fuzzerutil::BlockIsInLoopContinueConstruct(
     return !fuzzerutil::BlockIsInLoopContinueConstruct(
-        ir_context, message_.from_block(), containing_loop_header);
+               ir_context, message_.from_block(), containing_loop_header) ||
+           fuzzerutil::BlockIsBackEdge(ir_context, message_.from_block(),
+                                       containing_loop_header);
   }
   }
   return false;
   return false;
 }
 }

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

@@ -56,7 +56,7 @@ bool TransformationAddParameter::IsApplicable(
   const auto* initializer_type =
   const auto* initializer_type =
       ir_context->get_type_mgr()->GetType(initializer_inst->type_id());
       ir_context->get_type_mgr()->GetType(initializer_inst->type_id());
 
 
-  if (!initializer_type || initializer_type->AsVoid()) {
+  if (!initializer_type || !IsParameterTypeSupported(*initializer_type)) {
     return false;
     return false;
   }
   }
 
 
@@ -139,5 +139,28 @@ protobufs::Transformation TransformationAddParameter::ToMessage() const {
   return result;
   return result;
 }
 }
 
 
+bool TransformationAddParameter::IsParameterTypeSupported(
+    const opt::analysis::Type& type) {
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
+  //  Think about other type instructions we can add here.
+  switch (type.kind()) {
+    case opt::analysis::Type::kBool:
+    case opt::analysis::Type::kInteger:
+    case opt::analysis::Type::kFloat:
+    case opt::analysis::Type::kArray:
+    case opt::analysis::Type::kMatrix:
+    case opt::analysis::Type::kVector:
+      return true;
+    case opt::analysis::Type::kStruct:
+      return std::all_of(type.AsStruct()->element_types().begin(),
+                         type.AsStruct()->element_types().end(),
+                         [](const opt::analysis::Type* element_type) {
+                           return IsParameterTypeSupported(*element_type);
+                         });
+    default:
+      return false;
+  }
+}
+
 }  // namespace fuzz
 }  // namespace fuzz
 }  // namespace spvtools
 }  // namespace spvtools

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

@@ -35,7 +35,8 @@ class TransformationAddParameter : public Transformation {
   // - |function_id| must be a valid result id of some non-entry-point function
   // - |function_id| must be a valid result id of some non-entry-point function
   //   in the module.
   //   in the module.
   // - |initializer_id| must be a valid result id of some instruction in the
   // - |initializer_id| must be a valid result id of some instruction in the
-  //   module. Instruction's type may not be OpTypeVoid.
+  //   module. Instruction's type must be supported by this transformation
+  //   as specified by IsParameterTypeSupported function.
   // - |parameter_fresh_id| and |function_type_fresh_id| are fresh ids and are
   // - |parameter_fresh_id| and |function_type_fresh_id| are fresh ids and are
   //   not equal.
   //   not equal.
   bool IsApplicable(
   bool IsApplicable(
@@ -52,6 +53,10 @@ class TransformationAddParameter : public Transformation {
 
 
   protobufs::Transformation ToMessage() const override;
   protobufs::Transformation ToMessage() const override;
 
 
+  // Returns true if the type of the parameter is supported by this
+  // transformation.
+  static bool IsParameterTypeSupported(const opt::analysis::Type& type);
+
  private:
  private:
   protobufs::TransformationAddParameter message_;
   protobufs::TransformationAddParameter message_;
 };
 };

+ 299 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.cpp

@@ -0,0 +1,299 @@
+// 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 "source/fuzz/transformation_add_synonym.h"
+
+#include <utility>
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddSynonym::TransformationAddSynonym(
+    protobufs::TransformationAddSynonym message)
+    : message_(std::move(message)) {}
+
+TransformationAddSynonym::TransformationAddSynonym(
+    uint32_t result_id,
+    protobufs::TransformationAddSynonym::SynonymType synonym_type,
+    uint32_t synonym_fresh_id,
+    const protobufs::InstructionDescriptor& insert_before) {
+  message_.set_result_id(result_id);
+  message_.set_synonym_type(synonym_type);
+  message_.set_synonym_fresh_id(synonym_fresh_id);
+  *message_.mutable_insert_before() = insert_before;
+}
+
+bool TransformationAddSynonym::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  assert(protobufs::TransformationAddSynonym::SynonymType_IsValid(
+             message_.synonym_type()) &&
+         "Synonym type is invalid");
+
+  // |synonym_fresh_id| must be fresh.
+  if (!fuzzerutil::IsFreshId(ir_context, message_.synonym_fresh_id())) {
+    return false;
+  }
+
+  // Check that |message_.result_id| is valid.
+  auto* synonym = ir_context->get_def_use_mgr()->GetDef(message_.result_id());
+  if (!synonym) {
+    return false;
+  }
+
+  // Check that we can apply |synonym_type| to |result_id|.
+  if (!IsInstructionValid(ir_context, synonym, message_.synonym_type())) {
+    return false;
+  }
+
+  // Check that |insert_before| is valid.
+  auto* insert_before_inst =
+      FindInstruction(message_.insert_before(), ir_context);
+  if (!insert_before_inst) {
+    return false;
+  }
+
+  // Check that we can insert |message._synonymous_instruction| before
+  // |message_.insert_before| instruction. We use OpIAdd to represent some
+  // instruction that can produce a synonym.
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpIAdd,
+                                                    insert_before_inst)) {
+    return false;
+  }
+
+  // A constant instruction must be present in the module if required.
+  if (IsAdditionalConstantRequired(message_.synonym_type()) &&
+      MaybeGetConstantId(ir_context) == 0) {
+    return false;
+  }
+
+  // Domination rules must be satisfied.
+  return fuzzerutil::IdIsAvailableBeforeInstruction(
+      ir_context, insert_before_inst, message_.result_id());
+}
+
+void TransformationAddSynonym::Apply(
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
+  // Add a synonymous instruction.
+  FindInstruction(message_.insert_before(), ir_context)
+      ->InsertBefore(MakeSynonymousInstruction(ir_context));
+
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.synonym_fresh_id());
+
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
+
+  // Propagate PointeeValueIsIrrelevant fact.
+  const auto* new_synonym_type = ir_context->get_type_mgr()->GetType(
+      fuzzerutil::GetTypeId(ir_context, message_.synonym_fresh_id()));
+  assert(new_synonym_type && "New synonym should have a valid type");
+
+  if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant(
+          message_.result_id()) &&
+      new_synonym_type->AsPointer()) {
+    transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+        message_.synonym_fresh_id());
+  }
+
+  // Mark two ids as synonymous.
+  transformation_context->GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(message_.result_id(), {}),
+      MakeDataDescriptor(message_.synonym_fresh_id(), {}), ir_context);
+}
+
+protobufs::Transformation TransformationAddSynonym::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_synonym() = message_;
+  return result;
+}
+
+bool TransformationAddSynonym::IsInstructionValid(
+    opt::IRContext* ir_context, opt::Instruction* inst,
+    protobufs::TransformationAddSynonym::SynonymType synonym_type) {
+  // Instruction must have a result id, type id. We skip OpUndef and
+  // OpConstantNull.
+  if (!inst || !inst->result_id() || !inst->type_id() ||
+      inst->opcode() == SpvOpUndef || inst->opcode() == SpvOpConstantNull) {
+    return false;
+  }
+
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
+  //  We can't create a synonym of an irrelevant id.
+  switch (synonym_type) {
+    case protobufs::TransformationAddSynonym::ADD_ZERO:
+    case protobufs::TransformationAddSynonym::SUB_ZERO:
+    case protobufs::TransformationAddSynonym::MUL_ONE: {
+      // The instruction must be either scalar or vector of integers or floats.
+      const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
+      assert(type && "Instruction's result id is invalid");
+
+      if (const auto* vector = type->AsVector()) {
+        return vector->element_type()->AsInteger() ||
+               vector->element_type()->AsFloat();
+      }
+
+      return type->AsInteger() || type->AsFloat();
+    }
+    case protobufs::TransformationAddSynonym::COPY_OBJECT:
+      return fuzzerutil::CanMakeSynonymOf(ir_context, inst);
+    case protobufs::TransformationAddSynonym::LOGICAL_AND:
+    case protobufs::TransformationAddSynonym::LOGICAL_OR: {
+      // The instruction must be either a scalar or a vector of booleans.
+      const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
+      assert(type && "Instruction's result id is invalid");
+      return (type->AsVector() && type->AsVector()->element_type()->AsBool()) ||
+             type->AsBool();
+    }
+    default:
+      assert(false && "Synonym type is not supported");
+      return false;
+  }
+}
+
+std::unique_ptr<opt::Instruction>
+TransformationAddSynonym::MakeSynonymousInstruction(
+    opt::IRContext* ir_context) const {
+  auto synonym_type_id =
+      fuzzerutil::GetTypeId(ir_context, message_.result_id());
+  assert(synonym_type_id && "Synonym has invalid type id");
+
+  switch (message_.synonym_type()) {
+    case protobufs::TransformationAddSynonym::SUB_ZERO:
+    case protobufs::TransformationAddSynonym::MUL_ONE:
+    case protobufs::TransformationAddSynonym::ADD_ZERO: {
+      const auto* synonym_type =
+          ir_context->get_type_mgr()->GetType(synonym_type_id);
+      assert(synonym_type && "Synonym has invalid type");
+
+      // Compute instruction's opcode based on the type of the operand.
+      // We have already checked that the operand is either a scalar or a vector
+      // of either integers or floats.
+      auto is_integral =
+          (synonym_type->AsVector() &&
+           synonym_type->AsVector()->element_type()->AsInteger()) ||
+          synonym_type->AsInteger();
+      auto opcode = SpvOpNop;
+      switch (message_.synonym_type()) {
+        case protobufs::TransformationAddSynonym::SUB_ZERO:
+          opcode = is_integral ? SpvOpISub : SpvOpFSub;
+          break;
+        case protobufs::TransformationAddSynonym::MUL_ONE:
+          opcode = is_integral ? SpvOpIMul : SpvOpFMul;
+          break;
+        case protobufs::TransformationAddSynonym::ADD_ZERO:
+          opcode = is_integral ? SpvOpIAdd : SpvOpFAdd;
+          break;
+        default:
+          assert(false && "Unreachable");
+          break;
+      }
+
+      return MakeUnique<opt::Instruction>(
+          ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(),
+          opt::Instruction::OperandList{
+              {SPV_OPERAND_TYPE_ID, {message_.result_id()}},
+              {SPV_OPERAND_TYPE_ID, {MaybeGetConstantId(ir_context)}}});
+    }
+    case protobufs::TransformationAddSynonym::COPY_OBJECT:
+      return MakeUnique<opt::Instruction>(
+          ir_context, SpvOpCopyObject, synonym_type_id,
+          message_.synonym_fresh_id(),
+          opt::Instruction::OperandList{
+              {SPV_OPERAND_TYPE_ID, {message_.result_id()}}});
+    case protobufs::TransformationAddSynonym::LOGICAL_OR:
+    case protobufs::TransformationAddSynonym::LOGICAL_AND: {
+      auto opcode = message_.synonym_type() ==
+                            protobufs::TransformationAddSynonym::LOGICAL_OR
+                        ? SpvOpLogicalOr
+                        : SpvOpLogicalAnd;
+      return MakeUnique<opt::Instruction>(
+          ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(),
+          opt::Instruction::OperandList{
+              {SPV_OPERAND_TYPE_ID, {message_.result_id()}},
+              {SPV_OPERAND_TYPE_ID, {MaybeGetConstantId(ir_context)}}});
+    }
+    default:
+      assert(false && "Unhandled synonym type");
+      return nullptr;
+  }
+}
+
+uint32_t TransformationAddSynonym::MaybeGetConstantId(
+    opt::IRContext* ir_context) const {
+  assert(IsAdditionalConstantRequired(message_.synonym_type()) &&
+         "Synonym type doesn't require an additional constant");
+
+  auto synonym_type_id =
+      fuzzerutil::GetTypeId(ir_context, message_.result_id());
+  assert(synonym_type_id && "Synonym has invalid type id");
+
+  switch (message_.synonym_type()) {
+    case protobufs::TransformationAddSynonym::ADD_ZERO:
+    case protobufs::TransformationAddSynonym::SUB_ZERO:
+    case protobufs::TransformationAddSynonym::LOGICAL_OR:
+      return fuzzerutil::MaybeGetZeroConstant(ir_context, synonym_type_id);
+    case protobufs::TransformationAddSynonym::MUL_ONE:
+    case protobufs::TransformationAddSynonym::LOGICAL_AND: {
+      auto synonym_type = ir_context->get_type_mgr()->GetType(synonym_type_id);
+      assert(synonym_type && "Synonym has invalid type");
+
+      if (const auto* vector = synonym_type->AsVector()) {
+        auto element_type_id =
+            ir_context->get_type_mgr()->GetId(vector->element_type());
+        assert(element_type_id && "Vector's element type is invalid");
+
+        auto one_word =
+            vector->element_type()->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u;
+        if (auto scalar_one_id = fuzzerutil::MaybeGetScalarConstant(
+                ir_context, {one_word}, element_type_id)) {
+          return fuzzerutil::MaybeGetCompositeConstant(
+              ir_context,
+              std::vector<uint32_t>(vector->element_count(), scalar_one_id),
+              synonym_type_id);
+        }
+
+        return 0;
+      } else {
+        return fuzzerutil::MaybeGetScalarConstant(
+            ir_context,
+            {synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u},
+            synonym_type_id);
+      }
+    }
+    default:
+      // The assertion at the beginning of the function will fail in the debug
+      // mode.
+      return 0;
+  }
+}
+
+bool TransformationAddSynonym::IsAdditionalConstantRequired(
+    protobufs::TransformationAddSynonym::SynonymType synonym_type) {
+  switch (synonym_type) {
+    case protobufs::TransformationAddSynonym::ADD_ZERO:
+    case protobufs::TransformationAddSynonym::SUB_ZERO:
+    case protobufs::TransformationAddSynonym::LOGICAL_OR:
+    case protobufs::TransformationAddSynonym::MUL_ONE:
+    case protobufs::TransformationAddSynonym::LOGICAL_AND:
+      return true;
+    default:
+      return false;
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 86 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.h

@@ -0,0 +1,86 @@
+// 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_ADD_SYNONYM_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_SYNONYM_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddSynonym : public Transformation {
+ public:
+  explicit TransformationAddSynonym(
+      protobufs::TransformationAddSynonym message);
+
+  TransformationAddSynonym(
+      uint32_t result_id,
+      protobufs::TransformationAddSynonym::SynonymType synonym_type,
+      uint32_t synonym_fresh_id,
+      const protobufs::InstructionDescriptor& insert_before);
+
+  // - |result_id| must be a valid result id of some instruction in the module.
+  // - |synonym_type| is a type of the synonymous instruction that will be
+  //   created.
+  // - |synonym_fresh_id| is a fresh id.
+  // - |insert_before| must be a valid instruction descriptor and we must be
+  //   able to insert a new synonymous instruction before |insert_before|.
+  // - |result_id| must be available before |insert_before|.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Creates a new synonymous instruction according to the |synonym_type| with
+  // result id |synonym_fresh_id|.
+  // Inserts that instruction before |insert_before| and creates a fact
+  // that the |synonym_fresh_id| and the |result_id| are synonymous.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Returns true if we can create a synonym of |inst| according to the
+  // |synonym_type|.
+  static bool IsInstructionValid(
+      opt::IRContext* ir_context, opt::Instruction* inst,
+      protobufs::TransformationAddSynonym::SynonymType synonym_type);
+
+  // Returns true if |synonym_type| requires an additional constant instruction
+  // to be present in the module.
+  static bool IsAdditionalConstantRequired(
+      protobufs::TransformationAddSynonym::SynonymType synonym_type);
+
+ private:
+  // Returns a new instruction which is synonymous to |message_.result_id|.
+  std::unique_ptr<opt::Instruction> MakeSynonymousInstruction(
+      opt::IRContext* ir_context) const;
+
+  // Returns a result id of a constant instruction that is required to be
+  // present in some synonym types (e.g. returns a result id of a zero constant
+  // for ADD_ZERO synonym type). Returns 0 if no such instruction is present in
+  // the module. This method should only be called when
+  // IsAdditionalConstantRequired returns true.
+  uint32_t MaybeGetConstantId(opt::IRContext* ir_context) const;
+
+  protobufs::TransformationAddSynonym message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_SYNONYM_H_

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

@@ -18,10 +18,8 @@ namespace spvtools {
 namespace fuzz {
 namespace fuzz {
 
 
 TransformationContext::TransformationContext(
 TransformationContext::TransformationContext(
-    FactManager* transformation_context,
-    spv_validator_options validator_options)
-    : fact_manager_(transformation_context),
-      validator_options_(validator_options) {}
+    FactManager* fact_manager, spv_validator_options validator_options)
+    : fact_manager_(fact_manager), validator_options_(validator_options) {}
 
 
 TransformationContext::~TransformationContext() = default;
 TransformationContext::~TransformationContext() = default;
 
 

+ 0 - 115
3rdparty/spirv-tools/source/fuzz/transformation_copy_object.cpp

@@ -1,115 +0,0 @@
-// Copyright (c) 2019 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_copy_object.h"
-
-#include "source/fuzz/data_descriptor.h"
-#include "source/fuzz/fuzzer_util.h"
-#include "source/fuzz/instruction_descriptor.h"
-#include "source/opt/instruction.h"
-#include "source/util/make_unique.h"
-
-namespace spvtools {
-namespace fuzz {
-
-TransformationCopyObject::TransformationCopyObject(
-    const protobufs::TransformationCopyObject& message)
-    : message_(message) {}
-
-TransformationCopyObject::TransformationCopyObject(
-    uint32_t object,
-    const protobufs::InstructionDescriptor& instruction_to_insert_before,
-    uint32_t fresh_id) {
-  message_.set_object(object);
-  *message_.mutable_instruction_to_insert_before() =
-      instruction_to_insert_before;
-  message_.set_fresh_id(fresh_id);
-}
-
-bool TransformationCopyObject::IsApplicable(
-    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
-  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
-    // We require the id for the object copy to be unused.
-    return false;
-  }
-  // The id of the object to be copied must exist
-  auto object_inst = ir_context->get_def_use_mgr()->GetDef(message_.object());
-  if (!object_inst) {
-    return false;
-  }
-  if (!fuzzerutil::CanMakeSynonymOf(ir_context, object_inst)) {
-    return false;
-  }
-
-  auto insert_before =
-      FindInstruction(message_.instruction_to_insert_before(), ir_context);
-  if (!insert_before) {
-    // The instruction before which the copy should be inserted was not found.
-    return false;
-  }
-
-  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyObject,
-                                                    insert_before)) {
-    return false;
-  }
-
-  // |message_object| must be available directly before the point where we want
-  // to add the copy.
-  return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
-                                                    message_.object());
-}
-
-void TransformationCopyObject::Apply(
-    opt::IRContext* ir_context,
-    TransformationContext* transformation_context) const {
-  auto object_inst = ir_context->get_def_use_mgr()->GetDef(message_.object());
-  assert(object_inst && "The object to be copied must exist.");
-  auto insert_before_inst =
-      FindInstruction(message_.instruction_to_insert_before(), ir_context);
-  auto destination_block = ir_context->get_instr_block(insert_before_inst);
-  assert(destination_block && "The base instruction must be in a block.");
-  auto insert_before = fuzzerutil::GetIteratorForInstruction(
-      destination_block, insert_before_inst);
-  assert(insert_before != destination_block->end() &&
-         "There must be an instruction before which the copy can be inserted.");
-
-  opt::Instruction::OperandList operands = {
-      {SPV_OPERAND_TYPE_ID, {message_.object()}}};
-  insert_before->InsertBefore(MakeUnique<opt::Instruction>(
-      ir_context, SpvOp::SpvOpCopyObject, object_inst->type_id(),
-      message_.fresh_id(), operands));
-
-  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
-
-  transformation_context->GetFactManager()->AddFactDataSynonym(
-      MakeDataDescriptor(message_.object(), {}),
-      MakeDataDescriptor(message_.fresh_id(), {}), ir_context);
-
-  if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant(
-          message_.object())) {
-    transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
-        message_.fresh_id());
-  }
-}
-
-protobufs::Transformation TransformationCopyObject::ToMessage() const {
-  protobufs::Transformation result;
-  *result.mutable_copy_object() = message_;
-  return result;
-}
-
-}  // namespace fuzz
-}  // namespace spvtools

+ 0 - 77
3rdparty/spirv-tools/source/fuzz/transformation_copy_object.h

@@ -1,77 +0,0 @@
-// Copyright (c) 2019 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_COPY_OBJECT_H_
-#define SOURCE_FUZZ_TRANSFORMATION_COPY_OBJECT_H_
-
-#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
-#include "source/fuzz/transformation.h"
-#include "source/fuzz/transformation_context.h"
-#include "source/opt/ir_context.h"
-
-namespace spvtools {
-namespace fuzz {
-
-class TransformationCopyObject : public Transformation {
- public:
-  explicit TransformationCopyObject(
-      const protobufs::TransformationCopyObject& message);
-
-  TransformationCopyObject(
-      uint32_t object,
-      const protobufs::InstructionDescriptor& instruction_to_insert_before,
-      uint32_t fresh_id);
-
-  // - |message_.fresh_id| must not be used by the module.
-  // - |message_.object| must be a result id that is a legitimate operand for
-  //   OpCopyObject.  In particular, it must be the id of an instruction that
-  //   has a result type
-  // - |message_.object| must not be the target of any decoration.
-  //   TODO(afd): consider copying decorations along with objects.
-  // - |message_.base_instruction_id| must be the result id of an instruction
-  //   'base' in some block 'blk'.
-  // - 'blk' must contain an instruction 'inst' located |message_.offset|
-  //   instructions after 'base' (if |message_.offset| = 0 then 'inst' =
-  //   'base').
-  // - It must be legal to insert an OpCopyObject instruction directly
-  //   before 'inst'.
-  // - |message_.object| must be available directly before 'inst'.
-  // - |message_.object| must not be a null pointer or undefined pointer (so as
-  //   to make it legal to load from copied pointers).
-  bool IsApplicable(
-      opt::IRContext* ir_context,
-      const TransformationContext& transformation_context) const override;
-
-  // - A new instruction,
-  //     %|message_.fresh_id| = OpCopyObject %ty %|message_.object|
-  //   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 in |transformation_context|.
-  // - If |message_.object| is a pointer whose pointee value is known to be
-  //   irrelevant, the analogous fact is added to the fact manager in
-  //   |transformation_context| about |message_.fresh_id|.
-  void Apply(opt::IRContext* ir_context,
-             TransformationContext* transformation_context) const override;
-
-  protobufs::Transformation ToMessage() const override;
-
- private:
-  protobufs::TransformationCopyObject message_;
-};
-
-}  // namespace fuzz
-}  // namespace spvtools
-
-#endif  // SOURCE_FUZZ_TRANSFORMATION_COPY_OBJECT_H_

+ 103 - 6
3rdparty/spirv-tools/source/fuzz/transformation_equation_instruction.cpp

@@ -42,6 +42,7 @@ bool TransformationEquationInstruction::IsApplicable(
   if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
   if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
     return false;
   }
   }
+
   // The instruction to insert before must exist.
   // The instruction to insert before must exist.
   auto insert_before =
   auto insert_before =
       FindInstruction(message_.instruction_to_insert_before(), ir_context);
       FindInstruction(message_.instruction_to_insert_before(), ir_context);
@@ -64,7 +65,7 @@ bool TransformationEquationInstruction::IsApplicable(
     }
     }
   }
   }
 
 
-  return MaybeGetResultType(ir_context) != 0;
+  return MaybeGetResultTypeId(ir_context) != 0;
 }
 }
 
 
 void TransformationEquationInstruction::Apply(
 void TransformationEquationInstruction::Apply(
@@ -82,7 +83,8 @@ void TransformationEquationInstruction::Apply(
   FindInstruction(message_.instruction_to_insert_before(), ir_context)
   FindInstruction(message_.instruction_to_insert_before(), ir_context)
       ->InsertBefore(MakeUnique<opt::Instruction>(
       ->InsertBefore(MakeUnique<opt::Instruction>(
           ir_context, static_cast<SpvOp>(message_.opcode()),
           ir_context, static_cast<SpvOp>(message_.opcode()),
-          MaybeGetResultType(ir_context), message_.fresh_id(), in_operands));
+          MaybeGetResultTypeId(ir_context), message_.fresh_id(),
+          std::move(in_operands)));
 
 
   ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
   ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 
 
@@ -97,12 +99,108 @@ protobufs::Transformation TransformationEquationInstruction::ToMessage() const {
   return result;
   return result;
 }
 }
 
 
-uint32_t TransformationEquationInstruction::MaybeGetResultType(
+uint32_t TransformationEquationInstruction::MaybeGetResultTypeId(
     opt::IRContext* ir_context) const {
     opt::IRContext* ir_context) const {
-  switch (static_cast<SpvOp>(message_.opcode())) {
+  auto opcode = static_cast<SpvOp>(message_.opcode());
+  switch (opcode) {
+    case SpvOpConvertUToF:
+    case SpvOpConvertSToF: {
+      if (message_.in_operand_id_size() != 1) {
+        return 0;
+      }
+
+      const auto* type = ir_context->get_type_mgr()->GetType(
+          fuzzerutil::GetTypeId(ir_context, message_.in_operand_id(0)));
+      if (!type) {
+        return 0;
+      }
+
+      if (const auto* vector = type->AsVector()) {
+        if (!vector->element_type()->AsInteger()) {
+          return 0;
+        }
+
+        if (auto element_type_id = fuzzerutil::MaybeGetFloatType(
+                ir_context, vector->element_type()->AsInteger()->width())) {
+          return fuzzerutil::MaybeGetVectorType(ir_context, element_type_id,
+                                                vector->element_count());
+        }
+
+        return 0;
+      } else {
+        if (!type->AsInteger()) {
+          return 0;
+        }
+
+        return fuzzerutil::MaybeGetFloatType(ir_context,
+                                             type->AsInteger()->width());
+      }
+    }
+    case SpvOpBitcast: {
+      if (message_.in_operand_id_size() != 1) {
+        return 0;
+      }
+
+      const auto* operand_inst =
+          ir_context->get_def_use_mgr()->GetDef(message_.in_operand_id(0));
+      if (!operand_inst) {
+        return 0;
+      }
+
+      const auto* operand_type =
+          ir_context->get_type_mgr()->GetType(operand_inst->type_id());
+      if (!operand_type) {
+        return 0;
+      }
+
+      // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3539):
+      //  The only constraint on the types of OpBitcast's parameters is that
+      //  they must have the same number of bits. Consider improving the code
+      //  below to support this in full.
+      if (const auto* vector = operand_type->AsVector()) {
+        uint32_t component_type_id;
+        if (const auto* int_type = vector->element_type()->AsInteger()) {
+          component_type_id =
+              fuzzerutil::MaybeGetFloatType(ir_context, int_type->width());
+        } else if (const auto* float_type = vector->element_type()->AsFloat()) {
+          component_type_id = fuzzerutil::MaybeGetIntegerType(
+              ir_context, float_type->width(), true);
+          if (component_type_id == 0 ||
+              fuzzerutil::MaybeGetVectorType(ir_context, component_type_id,
+                                             vector->element_count()) == 0) {
+            component_type_id = fuzzerutil::MaybeGetIntegerType(
+                ir_context, float_type->width(), false);
+          }
+        } else {
+          assert(false && "Only vectors of numerical components are supported");
+          return 0;
+        }
+
+        if (component_type_id == 0) {
+          return 0;
+        }
+
+        return fuzzerutil::MaybeGetVectorType(ir_context, component_type_id,
+                                              vector->element_count());
+      } else if (const auto* int_type = operand_type->AsInteger()) {
+        return fuzzerutil::MaybeGetFloatType(ir_context, int_type->width());
+      } else if (const auto* float_type = operand_type->AsFloat()) {
+        if (auto existing_id = fuzzerutil::MaybeGetIntegerType(
+                ir_context, float_type->width(), true)) {
+          return existing_id;
+        }
+
+        return fuzzerutil::MaybeGetIntegerType(ir_context, float_type->width(),
+                                               false);
+      } else {
+        assert(false &&
+               "Operand is not a scalar or a vector of numerical type");
+        return 0;
+      }
+    }
     case SpvOpIAdd:
     case SpvOpIAdd:
     case SpvOpISub: {
     case SpvOpISub: {
-      if (message_.in_operand_id().size() != 2) {
+      if (message_.in_operand_id_size() != 2) {
         return 0;
         return 0;
       }
       }
       uint32_t first_operand_width = 0;
       uint32_t first_operand_width = 0;
@@ -175,7 +273,6 @@ uint32_t TransformationEquationInstruction::MaybeGetResultType(
       }
       }
       return operand_inst->type_id();
       return operand_inst->type_id();
     }
     }
-
     default:
     default:
       assert(false && "Inappropriate opcode for equation instruction.");
       assert(false && "Inappropriate opcode for equation instruction.");
       return 0;
       return 0;

+ 3 - 5
3rdparty/spirv-tools/source/fuzz/transformation_equation_instruction.h

@@ -63,11 +63,9 @@ class TransformationEquationInstruction : public Transformation {
   protobufs::Transformation ToMessage() const override;
   protobufs::Transformation ToMessage() const override;
 
 
  private:
  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* ir_context) const;
+  // Returns type id for the equation instruction. Returns 0 if result type does
+  // not exist.
+  uint32_t MaybeGetResultTypeId(opt::IRContext* ir_context) const;
 
 
   protobufs::TransformationEquationInstruction message_;
   protobufs::TransformationEquationInstruction message_;
 };
 };

+ 36 - 0
3rdparty/spirv-tools/source/fuzz/transformation_invert_comparison_operator.cpp

@@ -94,6 +94,18 @@ bool TransformationInvertComparisonOperator::IsInversionSupported(
     case SpvOpULessThanEqual:
     case SpvOpULessThanEqual:
     case SpvOpIEqual:
     case SpvOpIEqual:
     case SpvOpINotEqual:
     case SpvOpINotEqual:
+    case SpvOpFOrdEqual:
+    case SpvOpFUnordEqual:
+    case SpvOpFOrdNotEqual:
+    case SpvOpFUnordNotEqual:
+    case SpvOpFOrdLessThan:
+    case SpvOpFUnordLessThan:
+    case SpvOpFOrdLessThanEqual:
+    case SpvOpFUnordLessThanEqual:
+    case SpvOpFOrdGreaterThan:
+    case SpvOpFUnordGreaterThan:
+    case SpvOpFOrdGreaterThanEqual:
+    case SpvOpFUnordGreaterThanEqual:
       return true;
       return true;
     default:
     default:
       return false;
       return false;
@@ -124,6 +136,30 @@ SpvOp TransformationInvertComparisonOperator::InvertOpcode(SpvOp opcode) {
       return SpvOpINotEqual;
       return SpvOpINotEqual;
     case SpvOpINotEqual:
     case SpvOpINotEqual:
       return SpvOpIEqual;
       return SpvOpIEqual;
+    case SpvOpFOrdEqual:
+      return SpvOpFUnordNotEqual;
+    case SpvOpFUnordEqual:
+      return SpvOpFOrdNotEqual;
+    case SpvOpFOrdNotEqual:
+      return SpvOpFUnordEqual;
+    case SpvOpFUnordNotEqual:
+      return SpvOpFOrdEqual;
+    case SpvOpFOrdLessThan:
+      return SpvOpFUnordGreaterThanEqual;
+    case SpvOpFUnordLessThan:
+      return SpvOpFOrdGreaterThanEqual;
+    case SpvOpFOrdLessThanEqual:
+      return SpvOpFUnordGreaterThan;
+    case SpvOpFUnordLessThanEqual:
+      return SpvOpFOrdGreaterThan;
+    case SpvOpFOrdGreaterThan:
+      return SpvOpFUnordLessThanEqual;
+    case SpvOpFUnordGreaterThan:
+      return SpvOpFOrdLessThanEqual;
+    case SpvOpFOrdGreaterThanEqual:
+      return SpvOpFUnordLessThan;
+    case SpvOpFUnordGreaterThanEqual:
+      return SpvOpFOrdLessThan;
     default:
     default:
       // The program will fail in the debug mode because of the assertion
       // The program will fail in the debug mode because of the assertion
       // at the beginning of the function.
       // at the beginning of the function.

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

@@ -127,22 +127,20 @@ void TransformationPushIdThroughVariable::Apply(
                                  message_.initializer_id());
                                  message_.initializer_id());
   }
   }
 
 
-  // Stores value id to variable id.
-  FindInstruction(message_.instruction_descriptor(), ir_context)
-      ->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpStore, 0, 0,
-          opt::Instruction::OperandList(
-              {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}},
-               {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})));
-
-  // Loads variable id to value synonym id.
+  // First, insert the OpLoad instruction before |instruction_descriptor| and
+  // then insert the OpStore instruction before the OpLoad instruction.
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.value_synonym_id());
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.value_synonym_id());
   FindInstruction(message_.instruction_descriptor(), ir_context)
   FindInstruction(message_.instruction_descriptor(), ir_context)
       ->InsertBefore(MakeUnique<opt::Instruction>(
       ->InsertBefore(MakeUnique<opt::Instruction>(
           ir_context, SpvOpLoad, value_instruction->type_id(),
           ir_context, SpvOpLoad, value_instruction->type_id(),
           message_.value_synonym_id(),
           message_.value_synonym_id(),
           opt::Instruction::OperandList(
           opt::Instruction::OperandList(
-              {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}}})));
+              {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}}})))
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpStore, 0, 0,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}},
+               {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})));
 
 
   ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
   ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 
 

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

@@ -243,22 +243,15 @@ bool TransformationReplaceBooleanConstantWithConstantBinary::IsApplicable(
     return false;
     return false;
   }
   }
 
 
-  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;
+  // 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.
+  if (instruction->opcode() == SpvOpVariable) {
+    return false;
   }
   }
+
+  return true;
 }
 }
 
 
 void TransformationReplaceBooleanConstantWithConstantBinary::Apply(
 void TransformationReplaceBooleanConstantWithConstantBinary::Apply(
@@ -281,11 +274,22 @@ TransformationReplaceBooleanConstantWithConstantBinary::ApplyWithResult(
   opt::Instruction* result = binary_instruction.get();
   opt::Instruction* result = binary_instruction.get();
   auto instruction_containing_constant_use =
   auto instruction_containing_constant_use =
       FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
       FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
+  auto instruction_before_which_to_insert = instruction_containing_constant_use;
+
+  // If |instruction_before_which_to_insert| is an OpPhi instruction,
+  // then |binary_instruction| will be inserted into the parent block associated
+  // with the OpPhi variable operand.
+  if (instruction_containing_constant_use->opcode() == SpvOpPhi) {
+    instruction_before_which_to_insert =
+        ir_context->cfg()
+            ->block(instruction_containing_constant_use->GetSingleWordInOperand(
+                message_.id_use_descriptor().in_operand_index() + 1))
+            ->terminator();
+  }
 
 
   // We want to insert the new instruction before the instruction that contains
   // We want to insert the new instruction before the instruction that contains
   // the use of the boolean, but we need to go backwards one more instruction if
   // the use of the boolean, but we need to go backwards one more instruction if
   // the using instruction is preceded by a merge instruction.
   // the using instruction is preceded by a merge instruction.
-  auto instruction_before_which_to_insert = instruction_containing_constant_use;
   {
   {
     opt::Instruction* previous_node =
     opt::Instruction* previous_node =
         instruction_before_which_to_insert->PreviousNode();
         instruction_before_which_to_insert->PreviousNode();
@@ -294,6 +298,7 @@ TransformationReplaceBooleanConstantWithConstantBinary::ApplyWithResult(
       instruction_before_which_to_insert = previous_node;
       instruction_before_which_to_insert = previous_node;
     }
     }
   }
   }
+
   instruction_before_which_to_insert->InsertBefore(
   instruction_before_which_to_insert->InsertBefore(
       std::move(binary_instruction));
       std::move(binary_instruction));
   instruction_containing_constant_use->SetInOperand(
   instruction_containing_constant_use->SetInOperand(

+ 180 - 0
3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp

@@ -49,6 +49,7 @@ bool TransformationReplaceLinearAlgebraInstruction::IsApplicable(
       instruction->opcode() != SpvOpMatrixTimesScalar &&
       instruction->opcode() != SpvOpMatrixTimesScalar &&
       instruction->opcode() != SpvOpVectorTimesMatrix &&
       instruction->opcode() != SpvOpVectorTimesMatrix &&
       instruction->opcode() != SpvOpMatrixTimesVector &&
       instruction->opcode() != SpvOpMatrixTimesVector &&
+      instruction->opcode() != SpvOpMatrixTimesMatrix &&
       instruction->opcode() != SpvOpDot) {
       instruction->opcode() != SpvOpDot) {
     return false;
     return false;
   }
   }
@@ -88,6 +89,9 @@ void TransformationReplaceLinearAlgebraInstruction::Apply(
     case SpvOpMatrixTimesVector:
     case SpvOpMatrixTimesVector:
       ReplaceOpMatrixTimesVector(ir_context, linear_algebra_instruction);
       ReplaceOpMatrixTimesVector(ir_context, linear_algebra_instruction);
       break;
       break;
+    case SpvOpMatrixTimesMatrix:
+      ReplaceOpMatrixTimesMatrix(ir_context, linear_algebra_instruction);
+      break;
     case SpvOpDot:
     case SpvOpDot:
       ReplaceOpDot(ir_context, linear_algebra_instruction);
       ReplaceOpDot(ir_context, linear_algebra_instruction);
       break;
       break;
@@ -178,6 +182,37 @@ uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount(
       return 3 * matrix_column_count * matrix_row_count +
       return 3 * matrix_column_count * matrix_row_count +
              2 * matrix_column_count - matrix_row_count;
              2 * matrix_column_count - matrix_row_count;
     }
     }
+    case SpvOpMatrixTimesMatrix: {
+      // For each matrix 2 column, 1 OpCompositeExtract, 1 OpCompositeConstruct,
+      // |3 * matrix_1_row_count * matrix_1_column_count| OpCompositeExtract,
+      // |matrix_1_row_count * matrix_1_column_count| OpFMul,
+      // |matrix_1_row_count * (matrix_1_column_count - 1)| OpFAdd instructions
+      // will be inserted.
+      auto matrix_1_instruction = ir_context->get_def_use_mgr()->GetDef(
+          instruction->GetSingleWordInOperand(0));
+      uint32_t matrix_1_column_count =
+          ir_context->get_type_mgr()
+              ->GetType(matrix_1_instruction->type_id())
+              ->AsMatrix()
+              ->element_count();
+      uint32_t matrix_1_row_count =
+          ir_context->get_type_mgr()
+              ->GetType(matrix_1_instruction->type_id())
+              ->AsMatrix()
+              ->element_type()
+              ->AsVector()
+              ->element_count();
+
+      auto matrix_2_instruction = ir_context->get_def_use_mgr()->GetDef(
+          instruction->GetSingleWordInOperand(1));
+      uint32_t matrix_2_column_count =
+          ir_context->get_type_mgr()
+              ->GetType(matrix_2_instruction->type_id())
+              ->AsMatrix()
+              ->element_count();
+      return matrix_2_column_count *
+             (2 + matrix_1_row_count * (5 * matrix_1_column_count - 1));
+    }
     case SpvOpDot:
     case SpvOpDot:
       // For each pair of vector components, 2 OpCompositeExtract and 1 OpFMul
       // For each pair of vector components, 2 OpCompositeExtract and 1 OpFMul
       // will be inserted. The first two OpFMul instructions will result the
       // will be inserted. The first two OpFMul instructions will result the
@@ -560,6 +595,151 @@ void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesVector(
       ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
       ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
 }
 }
 
 
+void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesMatrix(
+    opt::IRContext* ir_context,
+    opt::Instruction* linear_algebra_instruction) const {
+  // Gets matrix 1 information.
+  auto matrix_1_instruction = ir_context->get_def_use_mgr()->GetDef(
+      linear_algebra_instruction->GetSingleWordInOperand(0));
+  uint32_t matrix_1_column_count =
+      ir_context->get_type_mgr()
+          ->GetType(matrix_1_instruction->type_id())
+          ->AsMatrix()
+          ->element_count();
+  auto matrix_1_column_type = ir_context->get_type_mgr()
+                                  ->GetType(matrix_1_instruction->type_id())
+                                  ->AsMatrix()
+                                  ->element_type();
+  auto matrix_1_column_component_type =
+      matrix_1_column_type->AsVector()->element_type();
+  uint32_t matrix_1_row_count =
+      matrix_1_column_type->AsVector()->element_count();
+
+  // Gets matrix 2 information.
+  auto matrix_2_instruction = ir_context->get_def_use_mgr()->GetDef(
+      linear_algebra_instruction->GetSingleWordInOperand(1));
+  uint32_t matrix_2_column_count =
+      ir_context->get_type_mgr()
+          ->GetType(matrix_2_instruction->type_id())
+          ->AsMatrix()
+          ->element_count();
+  auto matrix_2_column_type = ir_context->get_type_mgr()
+                                  ->GetType(matrix_2_instruction->type_id())
+                                  ->AsMatrix()
+                                  ->element_type();
+
+  uint32_t fresh_id_index = 0;
+  std::vector<uint32_t> result_column_ids(matrix_2_column_count);
+  for (uint32_t i = 0; i < matrix_2_column_count; i++) {
+    // Extracts matrix 2 column.
+    uint32_t matrix_2_column_id = message_.fresh_ids(fresh_id_index++);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpCompositeExtract,
+        ir_context->get_type_mgr()->GetId(matrix_2_column_type),
+        matrix_2_column_id,
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {matrix_2_instruction->result_id()}},
+             {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+
+    std::vector<uint32_t> column_component_ids(matrix_1_row_count);
+    for (uint32_t j = 0; j < matrix_1_row_count; j++) {
+      std::vector<uint32_t> float_multiplication_ids(matrix_1_column_count);
+      for (uint32_t k = 0; k < matrix_1_column_count; k++) {
+        // Extracts matrix 1 column.
+        uint32_t matrix_1_column_id = message_.fresh_ids(fresh_id_index++);
+        linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+            ir_context, SpvOpCompositeExtract,
+            ir_context->get_type_mgr()->GetId(matrix_1_column_type),
+            matrix_1_column_id,
+            opt::Instruction::OperandList(
+                {{SPV_OPERAND_TYPE_ID, {matrix_1_instruction->result_id()}},
+                 {SPV_OPERAND_TYPE_LITERAL_INTEGER, {k}}})));
+
+        // Extracts matrix 1 column component.
+        uint32_t matrix_1_column_component_id =
+            message_.fresh_ids(fresh_id_index++);
+        linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+            ir_context, SpvOpCompositeExtract,
+            ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
+            matrix_1_column_component_id,
+            opt::Instruction::OperandList(
+                {{SPV_OPERAND_TYPE_ID, {matrix_1_column_id}},
+                 {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
+
+        // Extracts matrix 2 column component.
+        uint32_t matrix_2_column_component_id =
+            message_.fresh_ids(fresh_id_index++);
+        linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+            ir_context, SpvOpCompositeExtract,
+            ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
+            matrix_2_column_component_id,
+            opt::Instruction::OperandList(
+                {{SPV_OPERAND_TYPE_ID, {matrix_2_column_id}},
+                 {SPV_OPERAND_TYPE_LITERAL_INTEGER, {k}}})));
+
+        // Multiplies corresponding matrix 1 and matrix 2 column components.
+        float_multiplication_ids[k] = message_.fresh_ids(fresh_id_index++);
+        linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+            ir_context, SpvOpFMul,
+            ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
+            float_multiplication_ids[k],
+            opt::Instruction::OperandList(
+                {{SPV_OPERAND_TYPE_ID, {matrix_1_column_component_id}},
+                 {SPV_OPERAND_TYPE_ID, {matrix_2_column_component_id}}})));
+      }
+
+      // Adds the multiplication results.
+      std::vector<uint32_t> float_add_ids;
+      uint32_t float_add_id = message_.fresh_ids(fresh_id_index++);
+      float_add_ids.push_back(float_add_id);
+      linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpFAdd,
+          ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
+          float_add_id,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}},
+               {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}})));
+      for (uint32_t k = 2; k < float_multiplication_ids.size(); k++) {
+        float_add_id = message_.fresh_ids(fresh_id_index++);
+        float_add_ids.push_back(float_add_id);
+        linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+            ir_context, SpvOpFAdd,
+            ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
+            float_add_id,
+            opt::Instruction::OperandList(
+                {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[k]}},
+                 {SPV_OPERAND_TYPE_ID, {float_add_ids[k - 2]}}})));
+      }
+
+      column_component_ids[j] = float_add_ids.back();
+    }
+
+    // Inserts the resulting matrix column.
+    opt::Instruction::OperandList in_operands;
+    for (auto& column_component_id : column_component_ids) {
+      in_operands.push_back({SPV_OPERAND_TYPE_ID, {column_component_id}});
+    }
+    result_column_ids[i] = message_.fresh_ids(fresh_id_index++);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpCompositeConstruct,
+        ir_context->get_type_mgr()->GetId(matrix_1_column_type),
+        result_column_ids[i], opt::Instruction::OperandList(in_operands)));
+  }
+
+  // The OpMatrixTimesMatrix instruction is changed to an OpCompositeConstruct
+  // instruction.
+  linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
+  linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]});
+  linear_algebra_instruction->SetInOperand(1, {result_column_ids[1]});
+  for (uint32_t i = 2; i < result_column_ids.size(); i++) {
+    linear_algebra_instruction->AddOperand(
+        {SPV_OPERAND_TYPE_ID, {result_column_ids[i]}});
+  }
+
+  fuzzerutil::UpdateModuleIdBound(
+      ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
+}
+
 void TransformationReplaceLinearAlgebraInstruction::ReplaceOpDot(
 void TransformationReplaceLinearAlgebraInstruction::ReplaceOpDot(
     opt::IRContext* ir_context,
     opt::IRContext* ir_context,
     opt::Instruction* linear_algebra_instruction) const {
     opt::Instruction* linear_algebra_instruction) const {

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

@@ -68,6 +68,10 @@ class TransformationReplaceLinearAlgebraInstruction : public Transformation {
   void ReplaceOpMatrixTimesVector(opt::IRContext* ir_context,
   void ReplaceOpMatrixTimesVector(opt::IRContext* ir_context,
                                   opt::Instruction* instruction) const;
                                   opt::Instruction* instruction) const;
 
 
+  // Replaces an OpMatrixTimesMatrix instruction.
+  void ReplaceOpMatrixTimesMatrix(opt::IRContext* ir_context,
+                                  opt::Instruction* instruction) const;
+
   // Replaces an OpDot instruction.
   // Replaces an OpDot instruction.
   void ReplaceOpDot(opt::IRContext* ir_context,
   void ReplaceOpDot(opt::IRContext* ir_context,
                     opt::Instruction* instruction) const;
                     opt::Instruction* instruction) const;

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

@@ -76,7 +76,7 @@ bool TransformationReplaceParameterWithGlobal::IsApplicable(
   const auto* param_type =
   const auto* param_type =
       ir_context->get_type_mgr()->GetType(param_inst->type_id());
       ir_context->get_type_mgr()->GetType(param_inst->type_id());
   assert(param_type && "Parameter has invalid type");
   assert(param_type && "Parameter has invalid type");
-  if (!CanReplaceFunctionParameterType(*param_type)) {
+  if (!IsParameterTypeSupported(*param_type)) {
     return false;
     return false;
   }
   }
 
 
@@ -217,7 +217,7 @@ protobufs::Transformation TransformationReplaceParameterWithGlobal::ToMessage()
   return result;
   return result;
 }
 }
 
 
-bool TransformationReplaceParameterWithGlobal::CanReplaceFunctionParameterType(
+bool TransformationReplaceParameterWithGlobal::IsParameterTypeSupported(
     const opt::analysis::Type& type) {
     const opt::analysis::Type& type) {
   // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
   // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
   //  Think about other type instructions we can add here.
   //  Think about other type instructions we can add here.
@@ -230,12 +230,11 @@ bool TransformationReplaceParameterWithGlobal::CanReplaceFunctionParameterType(
     case opt::analysis::Type::kVector:
     case opt::analysis::Type::kVector:
       return true;
       return true;
     case opt::analysis::Type::kStruct:
     case opt::analysis::Type::kStruct:
-      return std::all_of(
-          type.AsStruct()->element_types().begin(),
-          type.AsStruct()->element_types().end(),
-          [](const opt::analysis::Type* element_type) {
-            return CanReplaceFunctionParameterType(*element_type);
-          });
+      return std::all_of(type.AsStruct()->element_types().begin(),
+                         type.AsStruct()->element_types().end(),
+                         [](const opt::analysis::Type* element_type) {
+                           return IsParameterTypeSupported(*element_type);
+                         });
     default:
     default:
       return false;
       return false;
   }
   }

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

@@ -55,7 +55,7 @@ class TransformationReplaceParameterWithGlobal : public Transformation {
 
 
   // Returns true if the type of the parameter is supported by this
   // Returns true if the type of the parameter is supported by this
   // transformation.
   // transformation.
-  static bool CanReplaceFunctionParameterType(const opt::analysis::Type& type);
+  static bool IsParameterTypeSupported(const opt::analysis::Type& type);
 
 
  private:
  private:
   protobufs::TransformationReplaceParameterWithGlobal message_;
   protobufs::TransformationReplaceParameterWithGlobal message_;

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

@@ -375,20 +375,17 @@ bool DebugInfoManager::IsDebugDeclared(uint32_t variable_id) {
 }
 }
 
 
 void DebugInfoManager::KillDebugDeclares(uint32_t variable_id) {
 void DebugInfoManager::KillDebugDeclares(uint32_t variable_id) {
-  bool done = false;
-  while (!done) {
-    Instruction* kill_inst = nullptr;
-    auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
-    if (dbg_decl_itr != var_id_to_dbg_decl_.end()) {
-      for (auto dbg_decl : dbg_decl_itr->second) {
-        kill_inst = dbg_decl;
-        break;
-      }
+  auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
+  if (dbg_decl_itr != var_id_to_dbg_decl_.end()) {
+    // We intentionally copy the list of DebugDeclare instructions because
+    // context()->KillInst(dbg_decl) will update |var_id_to_dbg_decl_|. If we
+    // directly use |dbg_decl_itr->second|, it accesses a dangling pointer.
+    auto copy_dbg_decls = dbg_decl_itr->second;
+
+    for (auto* dbg_decl : copy_dbg_decls) {
+      context()->KillInst(dbg_decl);
     }
     }
-    if (kill_inst)
-      context()->KillInst(kill_inst);
-    else
-      done = true;
+    var_id_to_dbg_decl_.erase(dbg_decl_itr);
   }
   }
 }
 }
 
 

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

@@ -55,12 +55,25 @@ bool DominatorAnalysisBase::Dominates(Instruction* a, Instruction* b) const {
     return tree_.Dominates(bb_a, bb_b);
     return tree_.Dominates(bb_a, bb_b);
   }
   }
 
 
-  Instruction* current_inst = a;
-  while ((current_inst = current_inst->NextNode())) {
-    if (current_inst == b) {
+  const Instruction* current = a;
+  const Instruction* other = b;
+
+  if (tree_.IsPostDominator()) {
+    std::swap(current, other);
+  }
+
+  // We handle OpLabel instructions explicitly since they are not stored in the
+  // instruction list.
+  if (current->opcode() == SpvOpLabel) {
+    return true;
+  }
+
+  while ((current = current->NextNode())) {
+    if (current == other) {
       return true;
       return true;
     }
     }
   }
   }
+
   return false;
   return false;
 }
 }
 
 

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

@@ -174,7 +174,9 @@ void InstrumentPass::GenStageStreamWriteCode(uint32_t stage_idx,
           context()->GetBuiltinInputVarId(SpvBuiltInInstanceIndex),
           context()->GetBuiltinInputVarId(SpvBuiltInInstanceIndex),
           kInstVertOutInstanceIndex, base_offset_id, builder);
           kInstVertOutInstanceIndex, base_offset_id, builder);
     } break;
     } break;
-    case SpvExecutionModelGLCompute: {
+    case SpvExecutionModelGLCompute:
+    case SpvExecutionModelTaskNV:
+    case SpvExecutionModelMeshNV: {
       // Load and store GlobalInvocationId.
       // Load and store GlobalInvocationId.
       uint32_t load_id = GenVarLoad(
       uint32_t load_id = GenVarLoad(
           context()->GetBuiltinInputVarId(SpvBuiltInGlobalInvocationId),
           context()->GetBuiltinInputVarId(SpvBuiltInGlobalInvocationId),
@@ -914,6 +916,7 @@ bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) {
       stage != SpvExecutionModelGLCompute &&
       stage != SpvExecutionModelGLCompute &&
       stage != SpvExecutionModelTessellationControl &&
       stage != SpvExecutionModelTessellationControl &&
       stage != SpvExecutionModelTessellationEvaluation &&
       stage != SpvExecutionModelTessellationEvaluation &&
+      stage != SpvExecutionModelTaskNV && stage != SpvExecutionModelMeshNV &&
       stage != SpvExecutionModelRayGenerationNV &&
       stage != SpvExecutionModelRayGenerationNV &&
       stage != SpvExecutionModelIntersectionNV &&
       stage != SpvExecutionModelIntersectionNV &&
       stage != SpvExecutionModelAnyHitNV &&
       stage != SpvExecutionModelAnyHitNV &&

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

@@ -22,6 +22,7 @@ const uint32_t kDefaultStepLimit = 250;
 spv_fuzzer_options_t::spv_fuzzer_options_t()
 spv_fuzzer_options_t::spv_fuzzer_options_t()
     : has_random_seed(false),
     : has_random_seed(false),
       random_seed(0),
       random_seed(0),
+      replay_range(0),
       replay_validation_enabled(false),
       replay_validation_enabled(false),
       shrinker_step_limit(kDefaultStepLimit),
       shrinker_step_limit(kDefaultStepLimit),
       fuzzer_pass_validation_enabled(false) {}
       fuzzer_pass_validation_enabled(false) {}
@@ -45,6 +46,11 @@ SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetRandomSeed(
   options->random_seed = seed;
   options->random_seed = seed;
 }
 }
 
 
+SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetReplayRange(
+    spv_fuzzer_options options, int32_t replay_range) {
+  options->replay_range = replay_range;
+}
+
 SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit(
 SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit(
     spv_fuzzer_options options, uint32_t shrinker_step_limit) {
     spv_fuzzer_options options, uint32_t shrinker_step_limit) {
   options->shrinker_step_limit = shrinker_step_limit;
   options->shrinker_step_limit = shrinker_step_limit;

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

@@ -29,6 +29,9 @@ struct spv_fuzzer_options_t {
   bool has_random_seed;
   bool has_random_seed;
   uint32_t random_seed;
   uint32_t random_seed;
 
 
+  // See spvFuzzerOptionsSetReplayRange.
+  int32_t replay_range;
+
   // See spvFuzzerOptionsEnableReplayValidation.
   // See spvFuzzerOptionsEnableReplayValidation.
   bool replay_validation_enabled;
   bool replay_validation_enabled;
 
 

+ 0 - 6
3rdparty/spirv-tools/source/val/basic_block.cpp

@@ -58,15 +58,9 @@ void BasicBlock::RegisterSuccessors(
   for (auto& block : next_blocks) {
   for (auto& block : next_blocks) {
     block->predecessors_.push_back(this);
     block->predecessors_.push_back(this);
     successors_.push_back(block);
     successors_.push_back(block);
-    if (block->reachable_ == false) block->set_reachable(reachable_);
   }
   }
 }
 }
 
 
-void BasicBlock::RegisterBranchInstruction(SpvOp branch_instruction) {
-  if (branch_instruction == SpvOpUnreachable) reachable_ = false;
-  return;
-}
-
 bool BasicBlock::dominates(const BasicBlock& other) const {
 bool BasicBlock::dominates(const BasicBlock& other) const {
   return (this == &other) ||
   return (this == &other) ||
          !(other.dom_end() ==
          !(other.dom_end() ==

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

@@ -106,9 +106,6 @@ class BasicBlock {
   /// Returns the immedate post dominator of this basic block
   /// Returns the immedate post dominator of this basic block
   const BasicBlock* immediate_post_dominator() const;
   const BasicBlock* immediate_post_dominator() const;
 
 
-  /// Ends the block without a successor
-  void RegisterBranchInstruction(SpvOp branch_instruction);
-
   /// Returns the label instruction for the block, or nullptr if not set.
   /// Returns the label instruction for the block, or nullptr if not set.
   const Instruction* label() const { return label_; }
   const Instruction* label() const { return label_; }
 
 

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

@@ -130,7 +130,6 @@ spv_result_t Function::RegisterBlock(uint32_t block_id, bool is_definition) {
     undefined_blocks_.erase(block_id);
     undefined_blocks_.erase(block_id);
     current_block_ = &inserted_block->second;
     current_block_ = &inserted_block->second;
     ordered_blocks_.push_back(current_block_);
     ordered_blocks_.push_back(current_block_);
-    if (IsFirstBlock(block_id)) current_block_->set_reachable(true);
   } else if (success) {  // Block doesn't exsist but this is not a definition
   } else if (success) {  // Block doesn't exsist but this is not a definition
     undefined_blocks_.insert(block_id);
     undefined_blocks_.insert(block_id);
   }
   }
@@ -138,8 +137,7 @@ spv_result_t Function::RegisterBlock(uint32_t block_id, bool is_definition) {
   return SPV_SUCCESS;
   return SPV_SUCCESS;
 }
 }
 
 
-void Function::RegisterBlockEnd(std::vector<uint32_t> next_list,
-                                SpvOp branch_instruction) {
+void Function::RegisterBlockEnd(std::vector<uint32_t> next_list) {
   assert(
   assert(
       current_block_ &&
       current_block_ &&
       "RegisterBlockEnd can only be called when parsing a binary in a block");
       "RegisterBlockEnd can only be called when parsing a binary in a block");
@@ -174,7 +172,6 @@ void Function::RegisterBlockEnd(std::vector<uint32_t> next_list,
     }
     }
   }
   }
 
 
-  current_block_->RegisterBranchInstruction(branch_instruction);
   current_block_->RegisterSuccessors(next_blocks);
   current_block_->RegisterSuccessors(next_blocks);
   current_block_ = nullptr;
   current_block_ = nullptr;
   return;
   return;

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

@@ -97,9 +97,7 @@ class Function {
   /// Registers the end of the block
   /// Registers the end of the block
   ///
   ///
   /// @param[in] successors_list A list of ids to the block's successors
   /// @param[in] successors_list A list of ids to the block's successors
-  /// @param[in] branch_instruction the branch instruction that ended the block
-  void RegisterBlockEnd(std::vector<uint32_t> successors_list,
-                        SpvOp branch_instruction);
+  void RegisterBlockEnd(std::vector<uint32_t> successors_list);
 
 
   /// Registers the end of the function.  This is idempotent.
   /// Registers the end of the function.  This is idempotent.
   void RegisterFunctionEnd();
   void RegisterFunctionEnd();

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

@@ -368,6 +368,10 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
   // Catch undefined forward references before performing further checks.
   // Catch undefined forward references before performing further checks.
   if (auto error = ValidateForwardDecls(*vstate)) return error;
   if (auto error = ValidateForwardDecls(*vstate)) return error;
 
 
+  // Calculate reachability after all the blocks are parsed, but early that it
+  // can be relied on in subsequent pases.
+  ReachabilityPass(*vstate);
+
   // ID usage needs be handled in its own iteration of the instructions,
   // ID usage needs be handled in its own iteration of the instructions,
   // between the two others. It depends on the first loop to have been
   // between the two others. It depends on the first loop to have been
   // finished, so that all instructions have been registered. And the following
   // finished, so that all instructions have been registered. And the following

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

@@ -197,6 +197,9 @@ spv_result_t FunctionPass(ValidationState_t& _, const Instruction* inst);
 /// Validates correctness of miscellaneous instructions.
 /// Validates correctness of miscellaneous instructions.
 spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst);
 spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst);
 
 
+/// Calculates the reachability of basic blocks.
+void ReachabilityPass(ValidationState_t& _);
+
 /// Validates execution limitations.
 /// Validates execution limitations.
 ///
 ///
 /// Verifies execution models are allowed for all functionality they contain.
 /// Verifies execution models are allowed for all functionality they contain.

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

@@ -1062,7 +1062,7 @@ spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) {
       uint32_t target = inst->GetOperandAs<uint32_t>(0);
       uint32_t target = inst->GetOperandAs<uint32_t>(0);
       CFG_ASSERT(FirstBlockAssert, target);
       CFG_ASSERT(FirstBlockAssert, target);
 
 
-      _.current_function().RegisterBlockEnd({target}, opcode);
+      _.current_function().RegisterBlockEnd({target});
     } break;
     } break;
     case SpvOpBranchConditional: {
     case SpvOpBranchConditional: {
       uint32_t tlabel = inst->GetOperandAs<uint32_t>(1);
       uint32_t tlabel = inst->GetOperandAs<uint32_t>(1);
@@ -1070,7 +1070,7 @@ spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) {
       CFG_ASSERT(FirstBlockAssert, tlabel);
       CFG_ASSERT(FirstBlockAssert, tlabel);
       CFG_ASSERT(FirstBlockAssert, flabel);
       CFG_ASSERT(FirstBlockAssert, flabel);
 
 
-      _.current_function().RegisterBlockEnd({tlabel, flabel}, opcode);
+      _.current_function().RegisterBlockEnd({tlabel, flabel});
     } break;
     } break;
 
 
     case SpvOpSwitch: {
     case SpvOpSwitch: {
@@ -1080,7 +1080,7 @@ spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) {
         CFG_ASSERT(FirstBlockAssert, target);
         CFG_ASSERT(FirstBlockAssert, target);
         cases.push_back(target);
         cases.push_back(target);
       }
       }
-      _.current_function().RegisterBlockEnd({cases}, opcode);
+      _.current_function().RegisterBlockEnd({cases});
     } break;
     } break;
     case SpvOpReturn: {
     case SpvOpReturn: {
       const uint32_t return_type = _.current_function().GetResultTypeId();
       const uint32_t return_type = _.current_function().GetResultTypeId();
@@ -1090,13 +1090,13 @@ spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) {
         return _.diag(SPV_ERROR_INVALID_CFG, inst)
         return _.diag(SPV_ERROR_INVALID_CFG, inst)
                << "OpReturn can only be called from a function with void "
                << "OpReturn can only be called from a function with void "
                << "return type.";
                << "return type.";
-      _.current_function().RegisterBlockEnd(std::vector<uint32_t>(), opcode);
+      _.current_function().RegisterBlockEnd(std::vector<uint32_t>());
       break;
       break;
     }
     }
     case SpvOpKill:
     case SpvOpKill:
     case SpvOpReturnValue:
     case SpvOpReturnValue:
     case SpvOpUnreachable:
     case SpvOpUnreachable:
-      _.current_function().RegisterBlockEnd(std::vector<uint32_t>(), opcode);
+      _.current_function().RegisterBlockEnd(std::vector<uint32_t>());
       if (opcode == SpvOpKill) {
       if (opcode == SpvOpKill) {
         _.current_function().RegisterExecutionModelLimitation(
         _.current_function().RegisterExecutionModelLimitation(
             SpvExecutionModelFragment,
             SpvExecutionModelFragment,
@@ -1109,6 +1109,27 @@ spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) {
   return SPV_SUCCESS;
   return SPV_SUCCESS;
 }
 }
 
 
+void ReachabilityPass(ValidationState_t& _) {
+  for (auto& f : _.functions()) {
+    std::vector<BasicBlock*> stack;
+    auto entry = f.first_block();
+    // Skip function declarations.
+    if (entry) stack.push_back(entry);
+
+    while (!stack.empty()) {
+      auto block = stack.back();
+      stack.pop_back();
+
+      if (block->reachable()) continue;
+
+      block->set_reachable(true);
+      for (auto succ : *block->successors()) {
+        stack.push_back(succ);
+      }
+    }
+  }
+}
+
 spv_result_t ControlFlowPass(ValidationState_t& _, const Instruction* inst) {
 spv_result_t ControlFlowPass(ValidationState_t& _, const Instruction* inst) {
   switch (inst->opcode()) {
   switch (inst->opcode()) {
     case SpvOpPhi:
     case SpvOpPhi:

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

@@ -208,8 +208,8 @@ uint32_t NumConsumedComponents(ValidationState_t& _, const Instruction* type) {
 // 4 * location + component.
 // 4 * location + component.
 spv_result_t GetLocationsForVariable(
 spv_result_t GetLocationsForVariable(
     ValidationState_t& _, const Instruction* entry_point,
     ValidationState_t& _, const Instruction* entry_point,
-    const Instruction* variable, std::vector<bool>* locations,
-    std::vector<bool>* output_index1_locations) {
+    const Instruction* variable, std::unordered_set<uint32_t>* locations,
+    std::unordered_set<uint32_t>* output_index1_locations) {
   const bool is_fragment = entry_point->GetOperandAs<SpvExecutionModel>(0) ==
   const bool is_fragment = entry_point->GetOperandAs<SpvExecutionModel>(0) ==
                            SpvExecutionModelFragment;
                            SpvExecutionModelFragment;
   const bool is_output =
   const bool is_output =
@@ -356,17 +356,13 @@ spv_result_t GetLocationsForVariable(
       auto locs = locations;
       auto locs = locations;
       if (has_index && index == 1) locs = output_index1_locations;
       if (has_index && index == 1) locs = output_index1_locations;
 
 
-      if (end > locs->size()) {
-        locs->resize(end, false);
-      }
       for (uint32_t i = start; i < end; ++i) {
       for (uint32_t i = start; i < end; ++i) {
-        if (locs->at(i)) {
+        if (!locs->insert(i).second) {
           return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
           return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
                  << "Entry-point has conflicting " << storage_class
                  << "Entry-point has conflicting " << storage_class
                  << " location assignment at location " << i / 4
                  << " location assignment at location " << i / 4
                  << ", component " << i % 4;
                  << ", component " << i % 4;
         }
         }
-        (*locs)[i] = true;
       }
       }
     }
     }
   } else {
   } else {
@@ -425,17 +421,13 @@ spv_result_t GetLocationsForVariable(
         start += component;
         start += component;
         end = location * 4 + component + num_components;
         end = location * 4 + component + num_components;
       }
       }
-      if (end > locations->size()) {
-        locations->resize(end, false);
-      }
       for (uint32_t l = start; l < end; ++l) {
       for (uint32_t l = start; l < end; ++l) {
-        if (locations->at(l)) {
+        if (!locations->insert(l).second) {
           return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
           return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
                  << "Entry-point has conflicting " << storage_class
                  << "Entry-point has conflicting " << storage_class
                  << " location assignment at location " << l / 4
                  << " location assignment at location " << l / 4
                  << ", component " << l % 4;
                  << ", component " << l % 4;
         }
         }
-        (*locations)[l] = true;
       }
       }
     }
     }
   }
   }
@@ -445,10 +437,10 @@ spv_result_t GetLocationsForVariable(
 
 
 spv_result_t ValidateLocations(ValidationState_t& _,
 spv_result_t ValidateLocations(ValidationState_t& _,
                                const Instruction* entry_point) {
                                const Instruction* entry_point) {
-  // Reserve space for 16 locations with 4 components each.
-  std::vector<bool> input_locations(16 * 4, false);
-  std::vector<bool> output_locations_index0(16 * 4, false);
-  std::vector<bool> output_locations_index1(16 * 4, false);
+  // Locations are stored as a combined location and component values.
+  std::unordered_set<uint32_t> input_locations;
+  std::unordered_set<uint32_t> output_locations_index0;
+  std::unordered_set<uint32_t> output_locations_index1;
   for (uint32_t i = 3; i < entry_point->operands().size(); ++i) {
   for (uint32_t i = 3; i < entry_point->operands().size(); ++i) {
     auto interface_id = entry_point->GetOperandAs<uint32_t>(i);
     auto interface_id = entry_point->GetOperandAs<uint32_t>(i);
     auto interface_var = _.FindDef(interface_id);
     auto interface_var = _.FindDef(interface_id);

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

@@ -501,10 +501,6 @@ spv_result_t ValidateMemoryModel(ValidationState_t& _,
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
       return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Addressing model must be Logical for WebGPU environment.";
              << "Addressing model must be Logical for WebGPU environment.";
     }
     }
-    if (_.memory_model() != SpvMemoryModelVulkanKHR) {
-      return _.diag(SPV_ERROR_INVALID_DATA, inst)
-             << "Memory model must be VulkanKHR for WebGPU environment.";
-    }
   }
   }
 
 
   if (spvIsOpenCLEnv(_.context()->target_env)) {
   if (spvIsOpenCLEnv(_.context()->target_env)) {