Bladeren bron

Updated spirv-tools.

Бранимир Караџић 5 jaren geleden
bovenliggende
commit
fd71b4060d
100 gewijzigde bestanden met toevoegingen van 2966 en 845 verwijderingen
  1. 36 2
      3rdparty/spirv-tools/CHANGES
  2. 1 1
      3rdparty/spirv-tools/include/generated/build-version.inc
  3. 6 1
      3rdparty/spirv-tools/include/generated/core.insts-unified1.inc
  4. 4 0
      3rdparty/spirv-tools/include/generated/enum_string_mapping.inc
  5. 2 0
      3rdparty/spirv-tools/include/generated/extension_enum.inc
  6. 4 1
      3rdparty/spirv-tools/include/generated/operand.kinds-unified1.inc
  7. 4 2
      3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp
  8. 16 0
      3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
  9. 42 10
      3rdparty/spirv-tools/source/fuzz/fact_manager.cpp
  10. 13 6
      3rdparty/spirv-tools/source/fuzz/fact_manager.h
  11. 4 0
      3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
  12. 18 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp
  13. 26 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_context.h
  14. 60 129
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp
  15. 28 32
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h
  16. 1 1
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp
  17. 1 1
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.cpp
  18. 1 1
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp
  19. 1 1
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp
  20. 4 7
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
  21. 4 7
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp
  22. 5 2
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
  23. 78 134
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.cpp
  24. 0 8
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.h
  25. 1 1
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.cpp
  26. 1 1
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp
  27. 1 1
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.cpp
  28. 4 2
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp
  29. 55 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp
  30. 39 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h
  31. 7 6
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp
  32. 14 2
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp
  33. 3 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
  34. 17 4
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp
  35. 6 2
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp
  36. 22 18
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp
  37. 11 2
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
  38. 2 3
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.cpp
  39. 3 3
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
  40. 14 2
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp
  41. 58 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
  42. 41 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h
  43. 83 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
  44. 41 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h
  45. 1 1
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp
  46. 115 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp
  47. 40 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h
  48. 268 113
      3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
  49. 80 26
      3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
  50. 101 0
      3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto
  51. 19 0
      3rdparty/spirv-tools/source/fuzz/transformation.cpp
  52. 12 10
      3rdparty/spirv-tools/source/fuzz/transformation_add_constant_boolean.cpp
  53. 4 1
      3rdparty/spirv-tools/source/fuzz/transformation_add_constant_boolean.h
  54. 9 2
      3rdparty/spirv-tools/source/fuzz/transformation_add_constant_composite.cpp
  55. 6 4
      3rdparty/spirv-tools/source/fuzz/transformation_add_constant_composite.h
  56. 10 2
      3rdparty/spirv-tools/source/fuzz/transformation_add_constant_scalar.cpp
  57. 3 1
      3rdparty/spirv-tools/source/fuzz/transformation_add_constant_scalar.h
  58. 6 5
      3rdparty/spirv-tools/source/fuzz/transformation_add_dead_block.cpp
  59. 12 7
      3rdparty/spirv-tools/source/fuzz/transformation_add_dead_break.cpp
  60. 2 1
      3rdparty/spirv-tools/source/fuzz/transformation_add_dead_break.h
  61. 13 7
      3rdparty/spirv-tools/source/fuzz/transformation_add_dead_continue.cpp
  62. 2 1
      3rdparty/spirv-tools/source/fuzz/transformation_add_dead_continue.h
  63. 7 4
      3rdparty/spirv-tools/source/fuzz/transformation_add_parameter.cpp
  64. 146 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.cpp
  65. 62 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.h
  66. 32 18
      3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.cpp
  67. 9 3
      3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.h
  68. 28 8
      3rdparty/spirv-tools/source/fuzz/transformation_composite_construct.cpp
  69. 23 11
      3rdparty/spirv-tools/source/fuzz/transformation_composite_extract.cpp
  70. 2 1
      3rdparty/spirv-tools/source/fuzz/transformation_composite_extract.h
  71. 5 1
      3rdparty/spirv-tools/source/fuzz/transformation_equation_instruction.cpp
  72. 34 50
      3rdparty/spirv-tools/source/fuzz/transformation_permute_function_parameters.cpp
  73. 18 6
      3rdparty/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp
  74. 2 2
      3rdparty/spirv-tools/source/fuzz/transformation_push_id_through_variable.h
  75. 67 48
      3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.cpp
  76. 14 3
      3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.h
  77. 127 0
      3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp
  78. 57 0
      3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.h
  79. 155 0
      3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.cpp
  80. 63 0
      3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.h
  81. 54 74
      3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp
  82. 306 0
      3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp
  83. 83 0
      3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.h
  84. 28 1
      3rdparty/spirv-tools/source/fuzz/transformation_vector_shuffle.cpp
  85. 3 2
      3rdparty/spirv-tools/source/fuzz/transformation_vector_shuffle.h
  86. 3 1
      3rdparty/spirv-tools/source/opcode.cpp
  87. 48 0
      3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp
  88. 1 0
      3rdparty/spirv-tools/source/opt/code_sink.cpp
  89. 5 5
      3rdparty/spirv-tools/source/opt/debug_info_manager.h
  90. 2 1
      3rdparty/spirv-tools/source/opt/dominator_tree.cpp
  91. 8 5
      3rdparty/spirv-tools/source/opt/inline_pass.cpp
  92. 3 2
      3rdparty/spirv-tools/source/opt/inline_pass.h
  93. 1 0
      3rdparty/spirv-tools/source/opt/local_access_chain_convert_pass.cpp
  94. 1 0
      3rdparty/spirv-tools/source/opt/local_single_block_elim_pass.cpp
  95. 1 0
      3rdparty/spirv-tools/source/opt/local_single_store_elim_pass.cpp
  96. 2 1
      3rdparty/spirv-tools/source/opt/loop_unroller.cpp
  97. 44 4
      3rdparty/spirv-tools/source/opt/merge_return_pass.cpp
  98. 2 1
      3rdparty/spirv-tools/source/opt/reflect.h
  99. 1 0
      3rdparty/spirv-tools/source/opt/replace_invalid_opc.cpp
  100. 34 19
      3rdparty/spirv-tools/source/opt/wrap_opkill.cpp

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

@@ -1,7 +1,41 @@
 Revision history for SPIRV-Tools
 
-v2020.4-dev 2020-05-27
- - Start v2020.4-dev
+v2020.5 2020-07-22
+ - Start SPIRV-Tools v2020.5
+
+v2020.4 2020-07-22
+ - General
+   - Changed variable names to be more descriptive (#3433)
+   - Add support to GPU-AV instrumentation for Task and Mesh shaders (#3512)
+   - Permit Simple and GLSL450 memory model in WEBGPU_0 (#3463)
+   - Support SPV_KHR_terminate_invocation (#3568)
+ - Optimizer
+   - Preserving debug information in optimizations
+     (#3389,#3420,#3425,#3356,#3459,#3444,#3492,#3451,#3497i,#3498,#3542)
+   - Eliminate branches with condition of OpConstantNull (#3438)
+   - Use structured order to unroll loops. (#3443)
+   - Updated desc_sroa to support flattening structures (#3448)
+   - Support OpCompositeExtract pattern in desc_sroa (#3456)
+   - Fix ADCE pass bug for mulitple entries (#3470)
+     - Sink pointer instructions in merge return (#3569)
+ - Validator
+   - Validate location assignments (#3308)
+     - Fix reachability in the validator (#3541)
+ - Reduce
+ - Fuzz
+   - Add support for OpSpecConstant* (#3373)
+   - Add replace linear algebra instruction transformation (#3402)
+   - Implement vector shuffle fuzzer pass (#3412)
+   - Swap operands in OpBranchConditional (#3423)
+   - Permute OpPhi instruction operands (#3421)
+   - Add FuzzerPassAddCopyMemoryInstructions (#3391)
+   - TransformationInvertComparisonOperator (#3475)
+   - Add variables with workgroup storage class (#3485)
+   - Add image sample unused components transformation (#3439)
+   - TransformationReplaceParameterWithGlobal (#3434)
+     - Support adding dead break from back-edge block (#3519)
+       - Fuzzer pass to interchange zero-like constants (#3524)
+ - Linker
 
 v2020.3 2020-05-27
  - General

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

@@ -1 +1 @@
-"v2020.4-dev", "SPIRV-Tools v2020.4-dev 8d5c7eae3a89ee8b898c9771119abb8a3f977898"
+"v2020.5", "SPIRV-Tools v2020.5 aebdae4b9c5b3384041b88803e83fb244f5c8067"

+ 6 - 1
3rdparty/spirv-tools/include/generated/core.insts-unified1.inc

@@ -2,6 +2,7 @@ static const SpvCapability pygen_variable_caps_Addresses[] = {SpvCapabilityAddre
 static const SpvCapability pygen_variable_caps_AddressesPhysicalStorageBufferAddresses[] = {SpvCapabilityAddresses, SpvCapabilityPhysicalStorageBufferAddresses};
 static const SpvCapability pygen_variable_caps_AddressesVariablePointersVariablePointersStorageBuffer[] = {SpvCapabilityAddresses, SpvCapabilityVariablePointers, SpvCapabilityVariablePointersStorageBuffer};
 static const SpvCapability pygen_variable_caps_AddressesVariablePointersVariablePointersStorageBufferPhysicalStorageBufferAddresses[] = {SpvCapabilityAddresses, SpvCapabilityVariablePointers, SpvCapabilityVariablePointersStorageBuffer, SpvCapabilityPhysicalStorageBufferAddresses};
+static const SpvCapability pygen_variable_caps_AtomicFloat32AddEXTAtomicFloat64AddEXT[] = {SpvCapabilityAtomicFloat32AddEXT, SpvCapabilityAtomicFloat64AddEXT};
 static const SpvCapability pygen_variable_caps_BlockingPipesINTEL[] = {SpvCapabilityBlockingPipesINTEL};
 static const SpvCapability pygen_variable_caps_CooperativeMatrixNV[] = {SpvCapabilityCooperativeMatrixNV};
 static const SpvCapability pygen_variable_caps_DemoteToHelperInvocationEXT[] = {SpvCapabilityDemoteToHelperInvocationEXT};
@@ -55,6 +56,7 @@ static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_ballot[] = {
 static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_fragment_mask[] = {spvtools::Extension::kSPV_AMD_shader_fragment_mask};
 static const spvtools::Extension pygen_variable_exts_SPV_EXT_demote_to_helper_invocation[] = {spvtools::Extension::kSPV_EXT_demote_to_helper_invocation};
 static const spvtools::Extension pygen_variable_exts_SPV_EXT_fragment_shader_interlock[] = {spvtools::Extension::kSPV_EXT_fragment_shader_interlock};
+static const spvtools::Extension pygen_variable_exts_SPV_EXT_shader_atomic_float_add[] = {spvtools::Extension::kSPV_EXT_shader_atomic_float_add};
 static const spvtools::Extension pygen_variable_exts_SPV_GOOGLE_decorate_stringSPV_GOOGLE_hlsl_functionality1[] = {spvtools::Extension::kSPV_GOOGLE_decorate_string, spvtools::Extension::kSPV_GOOGLE_hlsl_functionality1};
 static const spvtools::Extension pygen_variable_exts_SPV_GOOGLE_hlsl_functionality1[] = {spvtools::Extension::kSPV_GOOGLE_hlsl_functionality1};
 static const spvtools::Extension pygen_variable_exts_SPV_INTEL_blocking_pipes[] = {spvtools::Extension::kSPV_INTEL_blocking_pipes};
@@ -65,6 +67,7 @@ static const spvtools::Extension pygen_variable_exts_SPV_KHR_ray_query[] = {spvt
 static const spvtools::Extension pygen_variable_exts_SPV_KHR_shader_ballot[] = {spvtools::Extension::kSPV_KHR_shader_ballot};
 static const spvtools::Extension pygen_variable_exts_SPV_KHR_shader_clock[] = {spvtools::Extension::kSPV_KHR_shader_clock};
 static const spvtools::Extension pygen_variable_exts_SPV_KHR_subgroup_vote[] = {spvtools::Extension::kSPV_KHR_subgroup_vote};
+static const spvtools::Extension pygen_variable_exts_SPV_KHR_terminate_invocation[] = {spvtools::Extension::kSPV_KHR_terminate_invocation};
 static const spvtools::Extension pygen_variable_exts_SPV_NV_cooperative_matrix[] = {spvtools::Extension::kSPV_NV_cooperative_matrix};
 static const spvtools::Extension pygen_variable_exts_SPV_NV_mesh_shader[] = {spvtools::Extension::kSPV_NV_mesh_shader};
 static const spvtools::Extension pygen_variable_exts_SPV_NV_ray_tracingSPV_KHR_ray_tracing[] = {spvtools::Extension::kSPV_NV_ray_tracing, spvtools::Extension::kSPV_KHR_ray_tracing};
@@ -417,6 +420,7 @@ static const spv_opcode_desc_t kOpcodeTableEntries[] = {
   {"PtrEqual", SpvOpPtrEqual, 0, nullptr, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu},
   {"PtrNotEqual", SpvOpPtrNotEqual, 0, nullptr, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu},
   {"PtrDiff", SpvOpPtrDiff, 3, pygen_variable_caps_AddressesVariablePointersVariablePointersStorageBuffer, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, SPV_SPIRV_VERSION_WORD(1,4), 0xffffffffu},
+  {"TerminateInvocation", SpvOpTerminateInvocation, 1, pygen_variable_caps_Shader, 0, {}, 0, 0, 1, pygen_variable_exts_SPV_KHR_terminate_invocation, 0xffffffffu, 0xffffffffu},
   {"SubgroupBallotKHR", SpvOpSubgroupBallotKHR, 1, pygen_variable_caps_SubgroupBallotKHR, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_shader_ballot, 0xffffffffu, 0xffffffffu},
   {"SubgroupFirstInvocationKHR", SpvOpSubgroupFirstInvocationKHR, 1, pygen_variable_caps_SubgroupBallotKHR, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_shader_ballot, 0xffffffffu, 0xffffffffu},
   {"SubgroupAllKHR", SpvOpSubgroupAllKHR, 1, pygen_variable_caps_SubgroupVoteKHR, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_subgroup_vote, 0xffffffffu, 0xffffffffu},
@@ -633,5 +637,6 @@ static const spv_opcode_desc_t kOpcodeTableEntries[] = {
   {"RayQueryGetWorldRayDirectionKHR", SpvOpRayQueryGetWorldRayDirectionKHR, 1, pygen_variable_caps_RayQueryProvisionalKHR, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_ray_query, 0xffffffffu, 0xffffffffu},
   {"RayQueryGetWorldRayOriginKHR", SpvOpRayQueryGetWorldRayOriginKHR, 1, pygen_variable_caps_RayQueryProvisionalKHR, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_ray_query, 0xffffffffu, 0xffffffffu},
   {"RayQueryGetIntersectionObjectToWorldKHR", SpvOpRayQueryGetIntersectionObjectToWorldKHR, 1, pygen_variable_caps_RayQueryProvisionalKHR, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_ray_query, 0xffffffffu, 0xffffffffu},
-  {"RayQueryGetIntersectionWorldToObjectKHR", SpvOpRayQueryGetIntersectionWorldToObjectKHR, 1, pygen_variable_caps_RayQueryProvisionalKHR, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_ray_query, 0xffffffffu, 0xffffffffu}
+  {"RayQueryGetIntersectionWorldToObjectKHR", SpvOpRayQueryGetIntersectionWorldToObjectKHR, 1, pygen_variable_caps_RayQueryProvisionalKHR, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_KHR_ray_query, 0xffffffffu, 0xffffffffu},
+  {"AtomicFAddEXT", SpvOpAtomicFAddEXT, 2, pygen_variable_caps_AtomicFloat32AddEXTAtomicFloat64AddEXT, 6, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_SCOPE_ID, SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_EXT_shader_atomic_float_add, 0xffffffffu, 0xffffffffu}
 };

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


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

@@ -14,6 +14,7 @@ kSPV_EXT_fragment_fully_covered,
 kSPV_EXT_fragment_invocation_density,
 kSPV_EXT_fragment_shader_interlock,
 kSPV_EXT_physical_storage_buffer,
+kSPV_EXT_shader_atomic_float_add,
 kSPV_EXT_shader_stencil_export,
 kSPV_EXT_shader_viewport_index_layer,
 kSPV_GOOGLE_decorate_string,
@@ -47,6 +48,7 @@ kSPV_KHR_shader_clock,
 kSPV_KHR_shader_draw_parameters,
 kSPV_KHR_storage_buffer_storage_class,
 kSPV_KHR_subgroup_vote,
+kSPV_KHR_terminate_invocation,
 kSPV_KHR_variable_pointers,
 kSPV_KHR_vulkan_memory_model,
 kSPV_NVX_multiview_per_view_attributes,

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

@@ -108,6 +108,7 @@ static const spvtools::Extension pygen_variable_exts_SPV_EXT_fragment_fully_cove
 static const spvtools::Extension pygen_variable_exts_SPV_EXT_fragment_invocation_densitySPV_NV_shading_rate[] = {spvtools::Extension::kSPV_EXT_fragment_invocation_density, spvtools::Extension::kSPV_NV_shading_rate};
 static const spvtools::Extension pygen_variable_exts_SPV_EXT_fragment_shader_interlock[] = {spvtools::Extension::kSPV_EXT_fragment_shader_interlock};
 static const spvtools::Extension pygen_variable_exts_SPV_EXT_physical_storage_bufferSPV_KHR_physical_storage_buffer[] = {spvtools::Extension::kSPV_EXT_physical_storage_buffer, spvtools::Extension::kSPV_KHR_physical_storage_buffer};
+static const spvtools::Extension pygen_variable_exts_SPV_EXT_shader_atomic_float_add[] = {spvtools::Extension::kSPV_EXT_shader_atomic_float_add};
 static const spvtools::Extension pygen_variable_exts_SPV_EXT_shader_stencil_export[] = {spvtools::Extension::kSPV_EXT_shader_stencil_export};
 static const spvtools::Extension pygen_variable_exts_SPV_EXT_shader_viewport_index_layerSPV_NV_viewport_array2[] = {spvtools::Extension::kSPV_EXT_shader_viewport_index_layer, spvtools::Extension::kSPV_NV_viewport_array2};
 static const spvtools::Extension pygen_variable_exts_SPV_GOOGLE_hlsl_functionality1[] = {spvtools::Extension::kSPV_GOOGLE_hlsl_functionality1};
@@ -962,7 +963,9 @@ static const spv_operand_desc_t pygen_variable_CapabilityEntries[] = {
   {"KernelAttributesINTEL", 5892, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_kernel_attributes, {}, 0xffffffffu, 0xffffffffu},
   {"FPGAKernelAttributesINTEL", 5897, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_kernel_attributes, {}, 0xffffffffu, 0xffffffffu},
   {"BlockingPipesINTEL", 5945, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_blocking_pipes, {}, 0xffffffffu, 0xffffffffu},
-  {"FPGARegINTEL", 5948, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_fpga_reg, {}, 0xffffffffu, 0xffffffffu}
+  {"FPGARegINTEL", 5948, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_fpga_reg, {}, 0xffffffffu, 0xffffffffu},
+  {"AtomicFloat32AddEXT", 6033, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_EXT_shader_atomic_float_add, {}, 0xffffffffu, 0xffffffffu},
+  {"AtomicFloat64AddEXT", 6034, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_EXT_shader_atomic_float_add, {}, 0xffffffffu, 0xffffffffu}
 };
 
 static const spv_operand_desc_t pygen_variable_RayQueryIntersectionEntries[] = {

+ 4 - 2
3rdparty/spirv-tools/include/spirv-tools/optimizer.hpp

@@ -876,8 +876,10 @@ Optimizer::PassToken CreateGraphicsRobustAccessPass();
 // for the first index.
 Optimizer::PassToken CreateDescriptorScalarReplacementPass();
 
-// Create a pass to replace all OpKill instruction with a function call to a
-// function that has a single OpKill.  This allows more code to be inlined.
+// Create a pass to replace each OpKill instruction with a function call to a
+// function that has a single OpKill.  Also replace each OpTerminateInvocation
+// instruction  with a function call to a function that has a single
+// OpTerminateInvocation.  This allows more code to be inlined.
 Optimizer::PassToken CreateWrapOpKillPass();
 
 // Replaces the extensions VK_AMD_shader_ballot,VK_AMD_gcn_shader, and

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

@@ -52,6 +52,7 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_add_local_variables.h
         fuzzer_pass_add_no_contraction_decorations.h
         fuzzer_pass_add_parameters.h
+        fuzzer_pass_add_relaxed_decorations.h
         fuzzer_pass_add_stores.h
         fuzzer_pass_add_vector_shuffle_instructions.h
         fuzzer_pass_adjust_branch_weights.h
@@ -72,8 +73,11 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_permute_function_parameters.h
         fuzzer_pass_permute_phi_operands.h
         fuzzer_pass_push_ids_through_variables.h
+        fuzzer_pass_replace_copy_memories_with_loads_stores.h
+        fuzzer_pass_replace_copy_objects_with_stores_loads.h
         fuzzer_pass_replace_linear_algebra_instructions.h
         fuzzer_pass_replace_parameter_with_global.h
+        fuzzer_pass_replace_params_with_struct.h
         fuzzer_pass_split_blocks.h
         fuzzer_pass_swap_commutable_operands.h
         fuzzer_pass_swap_conditional_branch_operands.h
@@ -104,6 +108,7 @@ if(SPIRV_BUILD_FUZZER)
         transformation_add_local_variable.h
         transformation_add_no_contraction_decoration.h
         transformation_add_parameter.h
+        transformation_add_relaxed_decoration.h
         transformation_add_spec_constant_op.h
         transformation_add_synonym.h
         transformation_add_type_array.h
@@ -133,9 +138,12 @@ if(SPIRV_BUILD_FUZZER)
         transformation_record_synonymous_constants.h
         transformation_replace_boolean_constant_with_constant_binary.h
         transformation_replace_constant_with_uniform.h
+        transformation_replace_copy_memory_with_load_store.h
+        transformation_replace_copy_object_with_store_load.h
         transformation_replace_id_with_synonym.h
         transformation_replace_linear_algebra_instruction.h
         transformation_replace_parameter_with_global.h
+        transformation_replace_params_with_struct.h
         transformation_set_function_control.h
         transformation_set_loop_control.h
         transformation_set_memory_operands_mask.h
@@ -171,6 +179,7 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_add_local_variables.cpp
         fuzzer_pass_add_no_contraction_decorations.cpp
         fuzzer_pass_add_parameters.cpp
+        fuzzer_pass_add_relaxed_decorations.cpp
         fuzzer_pass_add_stores.cpp
         fuzzer_pass_add_vector_shuffle_instructions.cpp
         fuzzer_pass_adjust_branch_weights.cpp
@@ -191,8 +200,11 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_permute_function_parameters.cpp
         fuzzer_pass_permute_phi_operands.cpp
         fuzzer_pass_push_ids_through_variables.cpp
+        fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
+        fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
         fuzzer_pass_replace_linear_algebra_instructions.cpp
         fuzzer_pass_replace_parameter_with_global.cpp
+        fuzzer_pass_replace_params_with_struct.cpp
         fuzzer_pass_split_blocks.cpp
         fuzzer_pass_swap_commutable_operands.cpp
         fuzzer_pass_swap_conditional_branch_operands.cpp
@@ -222,6 +234,7 @@ if(SPIRV_BUILD_FUZZER)
         transformation_add_local_variable.cpp
         transformation_add_no_contraction_decoration.cpp
         transformation_add_parameter.cpp
+        transformation_add_relaxed_decoration.cpp
         transformation_add_spec_constant_op.cpp
         transformation_add_synonym.cpp
         transformation_add_type_array.cpp
@@ -251,9 +264,12 @@ if(SPIRV_BUILD_FUZZER)
         transformation_record_synonymous_constants.cpp
         transformation_replace_boolean_constant_with_constant_binary.cpp
         transformation_replace_constant_with_uniform.cpp
+        transformation_replace_copy_memory_with_load_store.cpp
+        transformation_replace_copy_object_with_store_load.cpp
         transformation_replace_id_with_synonym.cpp
         transformation_replace_linear_algebra_instruction.cpp
         transformation_replace_parameter_with_global.cpp
+        transformation_replace_params_with_struct.cpp
         transformation_set_function_control.cpp
         transformation_set_loop_control.cpp
         transformation_set_memory_operands_mask.cpp

+ 42 - 10
3rdparty/spirv-tools/source/fuzz/fact_manager.cpp

@@ -1343,32 +1343,49 @@ bool FactManager::LivesafeFunctionFacts::FunctionIsLivesafe(
 //==============================
 
 //==============================
-// Irrelevant pointee value facts
+// Irrelevant value facts
 
 // The purpose of this class is to group the fields and data used to represent
-// facts about pointers whose pointee values are irrelevant.
-class FactManager::IrrelevantPointeeValueFacts {
+// facts about various irrelevant values in the module.
+class FactManager::IrrelevantValueFacts {
  public:
   // See method in FactManager which delegates to this method.
   void AddFact(const protobufs::FactPointeeValueIsIrrelevant& fact);
 
+  // See method in FactManager which delegates to this method.
+  void AddFact(const protobufs::FactIdIsIrrelevant& fact);
+
   // See method in FactManager which delegates to this method.
   bool PointeeValueIsIrrelevant(uint32_t pointer_id) const;
 
+  // See method in FactManager which delegates to this method.
+  bool IdIsIrrelevant(uint32_t pointer_id) const;
+
  private:
-  std::set<uint32_t> pointers_to_irrelevant_pointees_ids_;
+  std::unordered_set<uint32_t> pointers_to_irrelevant_pointees_ids_;
+  std::unordered_set<uint32_t> irrelevant_ids_;
 };
 
-void FactManager::IrrelevantPointeeValueFacts::AddFact(
+void FactManager::IrrelevantValueFacts::AddFact(
     const protobufs::FactPointeeValueIsIrrelevant& fact) {
   pointers_to_irrelevant_pointees_ids_.insert(fact.pointer_id());
 }
 
-bool FactManager::IrrelevantPointeeValueFacts::PointeeValueIsIrrelevant(
+void FactManager::IrrelevantValueFacts::AddFact(
+    const protobufs::FactIdIsIrrelevant& fact) {
+  irrelevant_ids_.insert(fact.result_id());
+}
+
+bool FactManager::IrrelevantValueFacts::PointeeValueIsIrrelevant(
     uint32_t pointer_id) const {
   return pointers_to_irrelevant_pointees_ids_.count(pointer_id) != 0;
 }
 
+bool FactManager::IrrelevantValueFacts::IdIsIrrelevant(
+    uint32_t pointer_id) const {
+  return irrelevant_ids_.count(pointer_id) != 0;
+}
+
 // End of arbitrarily-valued variable facts
 //==============================
 
@@ -1378,8 +1395,7 @@ FactManager::FactManager()
           MakeUnique<DataSynonymAndIdEquationFacts>()),
       dead_block_facts_(MakeUnique<DeadBlockFacts>()),
       livesafe_function_facts_(MakeUnique<LivesafeFunctionFacts>()),
-      irrelevant_pointee_value_facts_(
-          MakeUnique<IrrelevantPointeeValueFacts>()) {}
+      irrelevant_value_facts_(MakeUnique<IrrelevantValueFacts>()) {}
 
 FactManager::~FactManager() = default;
 
@@ -1420,6 +1436,8 @@ bool FactManager::AddFact(const fuzz::protobufs::Fact& fact,
 void FactManager::AddFactDataSynonym(const protobufs::DataDescriptor& data1,
                                      const protobufs::DataDescriptor& data2,
                                      opt::IRContext* context) {
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3550):
+  //  assert that neither |data1| nor |data2| are irrelevant.
   protobufs::FactDataSynonym fact;
   *fact.mutable_data1() = data1;
   *fact.mutable_data2() = data2;
@@ -1500,18 +1518,32 @@ void FactManager::AddFactFunctionIsLivesafe(uint32_t function_id) {
 }
 
 bool FactManager::PointeeValueIsIrrelevant(uint32_t pointer_id) const {
-  return irrelevant_pointee_value_facts_->PointeeValueIsIrrelevant(pointer_id);
+  return irrelevant_value_facts_->PointeeValueIsIrrelevant(pointer_id);
+}
+
+bool FactManager::IdIsIrrelevant(uint32_t result_id) const {
+  return irrelevant_value_facts_->IdIsIrrelevant(result_id);
 }
 
 void FactManager::AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id) {
   protobufs::FactPointeeValueIsIrrelevant fact;
   fact.set_pointer_id(pointer_id);
-  irrelevant_pointee_value_facts_->AddFact(fact);
+  irrelevant_value_facts_->AddFact(fact);
+}
+
+void FactManager::AddFactIdIsIrrelevant(uint32_t result_id) {
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3550):
+  //  assert that |result_id| is not a part of any DataSynonym fact.
+  protobufs::FactIdIsIrrelevant fact;
+  fact.set_result_id(result_id);
+  irrelevant_value_facts_->AddFact(fact);
 }
 
 void FactManager::AddFactIdEquation(uint32_t lhs_id, SpvOp opcode,
                                     const std::vector<uint32_t>& rhs_id,
                                     opt::IRContext* context) {
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3550):
+  //  assert that elements of |rhs_id| and |lhs_id| are not irrelevant.
   protobufs::FactIdEquation fact;
   fact.set_lhs_id(lhs_id);
   fact.set_opcode(opcode);

+ 13 - 6
3rdparty/spirv-tools/source/fuzz/fact_manager.h

@@ -68,6 +68,10 @@ class FactManager {
   // is irrelevant: it does not affect the observable behaviour of the module.
   void AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id);
 
+  // Records a fact that the |result_id| is irrelevant (i.e. it doesn't affect
+  // the semantics of the module)
+  void AddFactIdIsIrrelevant(uint32_t result_id);
+
   // Records the fact that |lhs_id| is defined by the equation:
   //
   //   |lhs_id| = |opcode| |rhs_id[0]| ... |rhs_id[N-1]|
@@ -181,13 +185,16 @@ class FactManager {
   //==============================
 
   //==============================
-  // Querying facts about pointers with irrelevant pointee values
+  // Querying facts about irrelevant values
 
   // Returns true if and ony if the value of the pointee associated with
   // |pointer_id| is irrelevant.
   bool PointeeValueIsIrrelevant(uint32_t pointer_id) const;
 
-  // End of irrelevant pointee value facts
+  // Returns true iff there exists a fact that the |result_id| is irrelevant.
+  bool IdIsIrrelevant(uint32_t result_id) const;
+
+  // End of irrelevant value facts
   //==============================
 
  private:
@@ -213,10 +220,10 @@ class FactManager {
   std::unique_ptr<LivesafeFunctionFacts>
       livesafe_function_facts_;  // Unique pointer to internal data.
 
-  class IrrelevantPointeeValueFacts;  // Opaque class for management of
-  // facts about pointers whose pointee values do not matter.
-  std::unique_ptr<IrrelevantPointeeValueFacts>
-      irrelevant_pointee_value_facts_;  // Unique pointer to internal data.
+  class IrrelevantValueFacts;  // Opaque class for management of
+  // facts about various irrelevant values in the module.
+  std::unique_ptr<IrrelevantValueFacts>
+      irrelevant_value_facts_;  // Unique pointer to internal data.
 };
 
 }  // namespace fuzz

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

@@ -57,6 +57,7 @@
 #include "source/fuzz/fuzzer_pass_push_ids_through_variables.h"
 #include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h"
 #include "source/fuzz/fuzzer_pass_replace_parameter_with_global.h"
+#include "source/fuzz/fuzzer_pass_replace_params_with_struct.h"
 #include "source/fuzz/fuzzer_pass_split_blocks.h"
 #include "source/fuzz/fuzzer_pass_swap_commutable_operands.h"
 #include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h"
@@ -288,6 +289,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
     MaybeAddPass<FuzzerPassReplaceLinearAlgebraInstructions>(
         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out);
+    MaybeAddPass<FuzzerPassReplaceParamsWithStruct>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
     MaybeAddPass<FuzzerPassSplitBlocks>(
         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out);

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

@@ -42,6 +42,7 @@ const std::pair<uint32_t, uint32_t> kChanceOfAddingMatrixType = {20, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingNoContractionDecoration = {
     5, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingParameters = {5, 70};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingRelaxedDecoration = {20, 90};
 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};
@@ -76,11 +77,17 @@ const std::pair<uint32_t, uint32_t> kChanceOfOutliningFunction = {10, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfPermutingParameters = {30, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfPermutingPhiOperands = {30, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfPushingIdThroughVariable = {5, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyMemoryWithLoadStore =
+    {20, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyObjectWithStoreLoad =
+    {20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
 const std::pair<uint32_t, uint32_t>
     kChanceOfReplacingLinearAlgebraInstructions = {10, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithGlobals = {
     30, 70};
+const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithStruct = {
+    20, 40};
 const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
 const std::pair<uint32_t, uint32_t> kChanceOfSwappingConditionalBranchOperands =
     {10, 70};
@@ -98,6 +105,7 @@ const uint32_t kDefaultMaxNewArraySizeLimit = 100;
 //  think whether there is a better limit on the maximum number of parameters.
 const uint32_t kDefaultMaxNumberOfFunctionParameters = 128;
 const uint32_t kDefaultMaxNumberOfNewParameters = 15;
+const uint32_t kGetDefaultMaxNumberOfParametersReplacedWithStruct = 5;
 
 // Default functions for controlling how deep to go during recursive
 // generation/transformation. Keep them in alphabetical order.
@@ -123,6 +131,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
       max_new_array_size_limit_(kDefaultMaxNewArraySizeLimit),
       max_number_of_function_parameters_(kDefaultMaxNumberOfFunctionParameters),
       max_number_of_new_parameters_(kDefaultMaxNumberOfNewParameters),
+      max_number_of_parameters_replaced_with_struct_(
+          kGetDefaultMaxNumberOfParametersReplacedWithStruct),
       go_deeper_in_constant_obfuscation_(
           kDefaultGoDeeperInConstantObfuscation) {
   chance_of_adding_access_chain_ =
@@ -154,6 +164,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
       ChooseBetweenMinAndMax(kChanceOfAddingNoContractionDecoration);
   chance_of_adding_parameters =
       ChooseBetweenMinAndMax(kChanceOfAddingParameters);
+  chance_of_adding_relaxed_decoration_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingRelaxedDecoration);
   chance_of_adding_store_ = ChooseBetweenMinAndMax(kChanceOfAddingStore);
   chance_of_adding_vector_shuffle_ =
       ChooseBetweenMinAndMax(kChanceOfAddingVectorShuffle);
@@ -202,12 +214,18 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
       ChooseBetweenMinAndMax(kChanceOfPermutingPhiOperands);
   chance_of_pushing_id_through_variable_ =
       ChooseBetweenMinAndMax(kChanceOfPushingIdThroughVariable);
+  chance_of_replacing_copy_memory_with_load_store_ =
+      ChooseBetweenMinAndMax(kChanceOfReplacingCopyMemoryWithLoadStore);
+  chance_of_replacing_copyobject_with_store_load_ =
+      ChooseBetweenMinAndMax(kChanceOfReplacingCopyObjectWithStoreLoad);
   chance_of_replacing_id_with_synonym_ =
       ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
   chance_of_replacing_linear_algebra_instructions_ =
       ChooseBetweenMinAndMax(kChanceOfReplacingLinearAlgebraInstructions);
   chance_of_replacing_parameters_with_globals_ =
       ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithGlobals);
+  chance_of_replacing_parameters_with_struct_ =
+      ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithStruct);
   chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
   chance_of_swapping_conditional_branch_operands_ =
       ChooseBetweenMinAndMax(kChanceOfSwappingConditionalBranchOperands);

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

@@ -143,6 +143,9 @@ class FuzzerContext {
     return chance_of_adding_no_contraction_decoration_;
   }
   uint32_t GetChanceOfAddingParameters() { return chance_of_adding_parameters; }
+  uint32_t GetChanceOfAddingRelaxedDecoration() {
+    return chance_of_adding_relaxed_decoration_;
+  }
   uint32_t GetChanceOfAddingStore() { return chance_of_adding_store_; }
   uint32_t GetChanceOfAddingSynonyms() { return chance_of_adding_synonyms_; }
   uint32_t GetChanceOfAddingVectorShuffle() {
@@ -209,6 +212,12 @@ class FuzzerContext {
   uint32_t GetChanceOfPushingIdThroughVariable() {
     return chance_of_pushing_id_through_variable_;
   }
+  uint32_t GetChanceOfReplacingCopyMemoryWithLoadStore() {
+    return chance_of_replacing_copy_memory_with_load_store_;
+  }
+  uint32_t GetChanceOfReplacingCopyObjectWithStoreLoad() {
+    return chance_of_replacing_copyobject_with_store_load_;
+  }
   uint32_t GetChanceOfReplacingIdWithSynonym() {
     return chance_of_replacing_id_with_synonym_;
   }
@@ -218,6 +227,9 @@ class FuzzerContext {
   uint32_t GetChanceOfReplacingParametersWithGlobals() {
     return chance_of_replacing_parameters_with_globals_;
   }
+  uint32_t GetChanceOfReplacingParametersWithStruct() {
+    return chance_of_replacing_parameters_with_struct_;
+  }
   uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
   uint32_t GetChanceOfSwappingConditionalBranchOperands() {
     return chance_of_swapping_conditional_branch_operands_;
@@ -234,6 +246,9 @@ class FuzzerContext {
   uint32_t GetMaximumNumberOfFunctionParameters() {
     return max_number_of_function_parameters_;
   }
+  uint32_t GetMaximumNumberOfParametersReplacedWithStruct() {
+    return max_number_of_parameters_replaced_with_struct_;
+  }
   std::pair<uint32_t, uint32_t> GetRandomBranchWeights() {
     std::pair<uint32_t, uint32_t> branch_weights = {0, 0};
 
@@ -275,6 +290,12 @@ class FuzzerContext {
         {1, std::min(max_number_of_new_parameters_,
                      GetMaximumNumberOfFunctionParameters() - num_of_params)});
   }
+  uint32_t GetRandomNumberOfParametersReplacedWithStruct(uint32_t num_params) {
+    assert(num_params != 0 && "A function must have parameters to replace");
+    return ChooseBetweenMinAndMax(
+        {1, std::min(num_params,
+                     GetMaximumNumberOfParametersReplacedWithStruct())});
+  }
   uint32_t GetRandomSizeForNewArray() {
     // Ensure that the array size is non-zero.
     return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1;
@@ -312,6 +333,7 @@ class FuzzerContext {
   uint32_t chance_of_adding_matrix_type_;
   uint32_t chance_of_adding_no_contraction_decoration_;
   uint32_t chance_of_adding_parameters;
+  uint32_t chance_of_adding_relaxed_decoration_;
   uint32_t chance_of_adding_store_;
   uint32_t chance_of_adding_synonyms_;
   uint32_t chance_of_adding_vector_shuffle_;
@@ -338,9 +360,12 @@ class FuzzerContext {
   uint32_t chance_of_permuting_parameters_;
   uint32_t chance_of_permuting_phi_operands_;
   uint32_t chance_of_pushing_id_through_variable_;
+  uint32_t chance_of_replacing_copy_memory_with_load_store_;
+  uint32_t chance_of_replacing_copyobject_with_store_load_;
   uint32_t chance_of_replacing_id_with_synonym_;
   uint32_t chance_of_replacing_linear_algebra_instructions_;
   uint32_t chance_of_replacing_parameters_with_globals_;
+  uint32_t chance_of_replacing_parameters_with_struct_;
   uint32_t chance_of_splitting_block_;
   uint32_t chance_of_swapping_conditional_branch_operands_;
   uint32_t chance_of_toggling_access_chain_instruction_;
@@ -355,6 +380,7 @@ class FuzzerContext {
   uint32_t max_new_array_size_limit_;
   uint32_t max_number_of_function_parameters_;
   uint32_t max_number_of_new_parameters_;
+  uint32_t max_number_of_parameters_replaced_with_struct_;
 
   // Functions to determine with what probability to go deeper when generating
   // or mutating constructs recursively.

+ 60 - 129
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp

@@ -153,9 +153,7 @@ void FuzzerPass::ForEachInstructionWithInstructionDescriptor(
 }
 
 uint32_t FuzzerPass::FindOrCreateBoolType() {
-  opt::analysis::Bool bool_type;
-  auto existing_id = GetIRContext()->get_type_mgr()->GetId(&bool_type);
-  if (existing_id) {
+  if (auto existing_id = fuzzerutil::MaybeGetBoolType(GetIRContext())) {
     return existing_id;
   }
   auto result = GetFuzzerContext()->GetFreshId();
@@ -277,62 +275,52 @@ uint32_t FuzzerPass::FindOrCreatePointerToIntegerType(
 }
 
 uint32_t FuzzerPass::FindOrCreateIntegerConstant(
-    const std::vector<uint32_t>& words, uint32_t width, bool is_signed) {
+    const std::vector<uint32_t>& words, uint32_t width, bool is_signed,
+    bool is_irrelevant) {
   auto int_type_id = FindOrCreateIntegerType(width, is_signed);
-  opt::analysis::IntConstant int_constant(
-      GetIRContext()->get_type_mgr()->GetType(int_type_id)->AsInteger(), words);
-  auto existing_constant =
-      GetIRContext()->get_constant_mgr()->FindConstant(&int_constant);
-  if (existing_constant) {
-    return GetIRContext()
-        ->get_constant_mgr()
-        ->GetDefiningInstruction(existing_constant)
-        ->result_id();
+  if (auto constant_id = fuzzerutil::MaybeGetScalarConstant(
+          GetIRContext(), *GetTransformationContext(), words, int_type_id,
+          is_irrelevant)) {
+    return constant_id;
   }
   auto result = GetFuzzerContext()->GetFreshId();
-  ApplyTransformation(
-      TransformationAddConstantScalar(result, int_type_id, words));
+  ApplyTransformation(TransformationAddConstantScalar(result, int_type_id,
+                                                      words, is_irrelevant));
   return result;
 }
 
 uint32_t FuzzerPass::FindOrCreateFloatConstant(
-    const std::vector<uint32_t>& words, uint32_t width) {
+    const std::vector<uint32_t>& words, uint32_t width, bool is_irrelevant) {
   auto float_type_id = FindOrCreateFloatType(width);
   opt::analysis::FloatConstant float_constant(
       GetIRContext()->get_type_mgr()->GetType(float_type_id)->AsFloat(), words);
-  auto existing_constant =
-      GetIRContext()->get_constant_mgr()->FindConstant(&float_constant);
-  if (existing_constant) {
-    return GetIRContext()
-        ->get_constant_mgr()
-        ->GetDefiningInstruction(existing_constant)
-        ->result_id();
+  if (auto constant_id = fuzzerutil::MaybeGetScalarConstant(
+          GetIRContext(), *GetTransformationContext(), words, float_type_id,
+          is_irrelevant)) {
+    return constant_id;
   }
   auto result = GetFuzzerContext()->GetFreshId();
-  ApplyTransformation(
-      TransformationAddConstantScalar(result, float_type_id, words));
+  ApplyTransformation(TransformationAddConstantScalar(result, float_type_id,
+                                                      words, is_irrelevant));
   return result;
 }
 
-uint32_t FuzzerPass::FindOrCreateBoolConstant(bool value) {
+uint32_t FuzzerPass::FindOrCreateBoolConstant(bool value, bool is_irrelevant) {
   auto bool_type_id = FindOrCreateBoolType();
-  opt::analysis::BoolConstant bool_constant(
-      GetIRContext()->get_type_mgr()->GetType(bool_type_id)->AsBool(), value);
-  auto existing_constant =
-      GetIRContext()->get_constant_mgr()->FindConstant(&bool_constant);
-  if (existing_constant) {
-    return GetIRContext()
-        ->get_constant_mgr()
-        ->GetDefiningInstruction(existing_constant)
-        ->result_id();
+  if (auto constant_id = fuzzerutil::MaybeGetScalarConstant(
+          GetIRContext(), *GetTransformationContext(), {value ? 1u : 0u},
+          bool_type_id, is_irrelevant)) {
+    return constant_id;
   }
   auto result = GetFuzzerContext()->GetFreshId();
-  ApplyTransformation(TransformationAddConstantBoolean(result, value));
+  ApplyTransformation(
+      TransformationAddConstantBoolean(result, value, is_irrelevant));
   return result;
 }
 
 uint32_t FuzzerPass::FindOrCreateConstant(const std::vector<uint32_t>& words,
-                                          uint32_t type_id) {
+                                          uint32_t type_id,
+                                          bool is_irrelevant) {
   assert(type_id && "Constant's type id can't be 0.");
 
   const auto* type = GetIRContext()->get_type_mgr()->GetType(type_id);
@@ -340,12 +328,12 @@ uint32_t FuzzerPass::FindOrCreateConstant(const std::vector<uint32_t>& words,
 
   if (type->AsBool()) {
     assert(words.size() == 1);
-    return FindOrCreateBoolConstant(words[0]);
+    return FindOrCreateBoolConstant(words[0], is_irrelevant);
   } else if (const auto* integer = type->AsInteger()) {
     return FindOrCreateIntegerConstant(words, integer->width(),
-                                       integer->IsSigned());
+                                       integer->IsSigned(), is_irrelevant);
   } else if (const auto* floating = type->AsFloat()) {
-    return FindOrCreateFloatConstant(words, floating->width());
+    return FindOrCreateFloatConstant(words, floating->width(), is_irrelevant);
   }
 
   // This assertion will fail in debug build but not in release build
@@ -355,21 +343,17 @@ uint32_t FuzzerPass::FindOrCreateConstant(const std::vector<uint32_t>& words,
 }
 
 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);
+    const std::vector<uint32_t>& component_ids, uint32_t type_id,
+    bool is_irrelevant) {
+  if (auto existing_constant = fuzzerutil::MaybeGetCompositeConstant(
+          GetIRContext(), *GetTransformationContext(), component_ids, type_id,
+          is_irrelevant)) {
+    return existing_constant;
   }
-
-  return FindOrCreateCompositeConstant(*type_inst, constants, component_ids);
+  uint32_t result = GetFuzzerContext()->GetFreshId();
+  ApplyTransformation(TransformationAddConstantComposite(
+      result, type_id, component_ids, is_irrelevant));
+  return result;
 }
 
 uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
@@ -474,51 +458,55 @@ FuzzerPass::GetAvailableBasicTypesAndPointers(
 }
 
 uint32_t FuzzerPass::FindOrCreateZeroConstant(
-    uint32_t scalar_or_composite_type_id) {
+    uint32_t scalar_or_composite_type_id, bool is_irrelevant) {
   auto type_instruction =
       GetIRContext()->get_def_use_mgr()->GetDef(scalar_or_composite_type_id);
   assert(type_instruction && "The type instruction must exist.");
   switch (type_instruction->opcode()) {
     case SpvOpTypeBool:
-      return FindOrCreateBoolConstant(false);
+      return FindOrCreateBoolConstant(false, is_irrelevant);
     case SpvOpTypeFloat: {
       auto width = type_instruction->GetSingleWordInOperand(0);
       auto num_words = (width + 32 - 1) / 32;
       return FindOrCreateFloatConstant(std::vector<uint32_t>(num_words, 0),
-                                       width);
+                                       width, is_irrelevant);
     }
     case SpvOpTypeInt: {
       auto width = type_instruction->GetSingleWordInOperand(0);
       auto num_words = (width + 32 - 1) / 32;
       return FindOrCreateIntegerConstant(
           std::vector<uint32_t>(num_words, 0), width,
-          type_instruction->GetSingleWordInOperand(1));
+          type_instruction->GetSingleWordInOperand(1), is_irrelevant);
     }
     case SpvOpTypeArray: {
-      return GetZeroConstantForHomogeneousComposite(
-          *type_instruction, type_instruction->GetSingleWordInOperand(0),
-          fuzzerutil::GetArraySize(*type_instruction, GetIRContext()));
+      auto component_type_id = type_instruction->GetSingleWordInOperand(0);
+      auto num_components =
+          fuzzerutil::GetArraySize(*type_instruction, GetIRContext());
+      return FindOrCreateCompositeConstant(
+          std::vector<uint32_t>(
+              num_components,
+              FindOrCreateZeroConstant(component_type_id, is_irrelevant)),
+          scalar_or_composite_type_id, is_irrelevant);
     }
     case SpvOpTypeMatrix:
     case SpvOpTypeVector: {
-      return GetZeroConstantForHomogeneousComposite(
-          *type_instruction, type_instruction->GetSingleWordInOperand(0),
-          type_instruction->GetSingleWordInOperand(1));
+      auto component_type_id = type_instruction->GetSingleWordInOperand(0);
+      auto num_components = type_instruction->GetSingleWordInOperand(1);
+      return FindOrCreateCompositeConstant(
+          std::vector<uint32_t>(
+              num_components,
+              FindOrCreateZeroConstant(component_type_id, is_irrelevant)),
+          scalar_or_composite_type_id, is_irrelevant);
     }
     case SpvOpTypeStruct: {
-      std::vector<const opt::analysis::Constant*> field_zero_constants;
       std::vector<uint32_t> field_zero_ids;
       for (uint32_t index = 0; index < type_instruction->NumInOperands();
            index++) {
-        uint32_t field_constant_id = FindOrCreateZeroConstant(
-            type_instruction->GetSingleWordInOperand(index));
-        field_zero_ids.push_back(field_constant_id);
-        field_zero_constants.push_back(
-            GetIRContext()->get_constant_mgr()->FindDeclaredConstant(
-                field_constant_id));
+        field_zero_ids.push_back(FindOrCreateZeroConstant(
+            type_instruction->GetSingleWordInOperand(index), is_irrelevant));
       }
       return FindOrCreateCompositeConstant(
-          *type_instruction, field_zero_constants, field_zero_ids);
+          field_zero_ids, scalar_or_composite_type_id, is_irrelevant);
     }
     default:
       assert(false && "Unknown type.");
@@ -526,62 +514,5 @@ uint32_t FuzzerPass::FindOrCreateZeroConstant(
   }
 }
 
-uint32_t FuzzerPass::FindOrCreateCompositeConstant(
-    const opt::Instruction& composite_type_instruction,
-    const std::vector<const opt::analysis::Constant*>& constants,
-    const std::vector<uint32_t>& constant_ids) {
-  assert(constants.size() == constant_ids.size() &&
-         "Precondition: |constants| and |constant_ids| must be in "
-         "correspondence.");
-
-  opt::analysis::Type* composite_type = GetIRContext()->get_type_mgr()->GetType(
-      composite_type_instruction.result_id());
-  std::unique_ptr<opt::analysis::Constant> composite_constant;
-  if (composite_type->AsArray()) {
-    composite_constant = MakeUnique<opt::analysis::ArrayConstant>(
-        composite_type->AsArray(), constants);
-  } else if (composite_type->AsMatrix()) {
-    composite_constant = MakeUnique<opt::analysis::MatrixConstant>(
-        composite_type->AsMatrix(), constants);
-  } else if (composite_type->AsStruct()) {
-    composite_constant = MakeUnique<opt::analysis::StructConstant>(
-        composite_type->AsStruct(), constants);
-  } else if (composite_type->AsVector()) {
-    composite_constant = MakeUnique<opt::analysis::VectorConstant>(
-        composite_type->AsVector(), constants);
-  } else {
-    assert(false &&
-           "Precondition: |composite_type| must declare a composite type.");
-    return 0;
-  }
-
-  uint32_t existing_constant =
-      GetIRContext()->get_constant_mgr()->FindDeclaredConstant(
-          composite_constant.get(), composite_type_instruction.result_id());
-  if (existing_constant) {
-    return existing_constant;
-  }
-  uint32_t result = GetFuzzerContext()->GetFreshId();
-  ApplyTransformation(TransformationAddConstantComposite(
-      result, composite_type_instruction.result_id(), constant_ids));
-  return result;
-}
-
-uint32_t FuzzerPass::GetZeroConstantForHomogeneousComposite(
-    const opt::Instruction& composite_type_instruction,
-    uint32_t component_type_id, uint32_t num_components) {
-  std::vector<const opt::analysis::Constant*> zero_constants;
-  std::vector<uint32_t> zero_ids;
-  uint32_t zero_component = FindOrCreateZeroConstant(component_type_id);
-  const opt::analysis::Constant* registered_zero_component =
-      GetIRContext()->get_constant_mgr()->FindDeclaredConstant(zero_component);
-  for (uint32_t i = 0; i < num_components; i++) {
-    zero_constants.push_back(registered_zero_component);
-    zero_ids.push_back(zero_component);
-  }
-  return FindOrCreateCompositeConstant(composite_type_instruction,
-                                       zero_constants, zero_ids);
-}
-
 }  // namespace fuzz
 }  // namespace spvtools

+ 28 - 32
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h

@@ -174,32 +174,48 @@ class FuzzerPass {
   // width and signedness specified by |width| and |is_signed|, respectively,
   // with |words| as its value.  If either the required integer type or the
   // constant do not exist, transformations are applied to add them.
+  // The returned id either participates in IdIsIrrelevant fact or not,
+  // depending
+  // on the |is_irrelevant| parameter.
   uint32_t FindOrCreateIntegerConstant(const std::vector<uint32_t>& words,
-                                       uint32_t width, bool is_signed);
+                                       uint32_t width, bool is_signed,
+                                       bool is_irrelevant);
 
   // Returns the id of an OpConstant instruction, with a floating-point
   // type of width specified by |width|, with |words| as its value.  If either
   // the required floating-point type or the constant do not exist,
-  // transformations are applied to add them.
+  // transformations are applied to add them. The returned id either
+  // participates in IdIsIrrelevant fact or not, depending on the
+  // |is_irrelevant| parameter.
   uint32_t FindOrCreateFloatConstant(const std::vector<uint32_t>& words,
-                                     uint32_t width);
+                                     uint32_t width, bool is_irrelevant);
 
   // Returns the id of an OpConstantTrue or OpConstantFalse instruction,
   // according to |value|.  If either the required instruction or the bool
   // type do not exist, transformations are applied to add them.
-  uint32_t FindOrCreateBoolConstant(bool value);
+  // The returned id either participates in IdIsIrrelevant fact or not,
+  // depending on the |is_irrelevant| parameter.
+  uint32_t FindOrCreateBoolConstant(bool value, bool is_irrelevant);
 
   // Returns the id of an OpConstant instruction of type with |type_id|
   // that consists of |words|. If that instruction doesn't exist,
   // transformations are applied to add it. |type_id| must be a valid
   // result id of either scalar or boolean OpType* instruction that exists
-  // in the module.
+  // in the module. The returned id either participates in IdIsIrrelevant fact
+  // or not, depending on the |is_irrelevant| parameter.
   uint32_t FindOrCreateConstant(const std::vector<uint32_t>& words,
-                                uint32_t type_id);
+                                uint32_t type_id, bool is_irrelevant);
 
-  // Returns the id of an OpConstantComposite
+  // Returns the id of an OpConstantComposite instruction of type with |type_id|
+  // that consists of |component_ids|. If that instruction doesn't exist,
+  // transformations are applied to add it. |type_id| must be a valid
+  // result id of an OpType* instruction that represents a composite type
+  // (i.e. a vector, matrix, struct or array).
+  // The returned id either participates in IdIsIrrelevant fact or not,
+  // depending on the |is_irrelevant| parameter.
   uint32_t FindOrCreateCompositeConstant(
-      const std::vector<uint32_t>& component_ids, uint32_t type_id);
+      const std::vector<uint32_t>& component_ids, uint32_t type_id,
+      bool is_irrelevant);
 
   // Returns the result id of an instruction of the form:
   //   %id = OpUndef %|type_id|
@@ -232,7 +248,8 @@ class FuzzerPass {
   // some scalar or composite type, returns the result id of an instruction
   // defining a constant of the given type that is zero or false at everywhere.
   // If such an instruction does not yet exist, transformations are applied to
-  // add it.
+  // add it. The returned id either participates in IdIsIrrelevant fact or not,
+  // depending on the |is_irrelevant| parameter.
   //
   // Examples:
   // --------------+-------------------------------
@@ -254,31 +271,10 @@ class FuzzerPass {
   //     uint2 u;  |
   //   }           |
   // --------------+-------------------------------
-  uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id);
+  uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id,
+                                    bool is_irrelevant);
 
  private:
-  // Array, matrix and vector are *homogeneous* composite types in the sense
-  // that every component of one of these types has the same type.  Given a
-  // homogeneous composite type instruction, |composite_type_instruction|,
-  // returns the id of a composite constant instruction for which every element
-  // is zero/false.  If such an instruction does not yet exist, transformations
-  // are applied to add it.
-  uint32_t GetZeroConstantForHomogeneousComposite(
-      const opt::Instruction& composite_type_instruction,
-      uint32_t component_type_id, uint32_t num_components);
-
-  // Helper to find an existing composite constant instruction of the given
-  // composite type with the given constant components, or to apply
-  // transformations to create such an instruction if it does not yet exist.
-  // Parameter |composite_type_instruction| must be a composite type
-  // instruction.  The parameters |constants| and |constant_ids| must have the
-  // same size, and it must be the case that for each i, |constant_ids[i]| is
-  // the result id of an instruction that defines |constants[i]|.
-  uint32_t FindOrCreateCompositeConstant(
-      const opt::Instruction& composite_type_instruction,
-      const std::vector<const opt::analysis::Constant*>& constants,
-      const std::vector<uint32_t>& constant_ids);
-
   opt::IRContext* ir_context_;
   TransformationContext* transformation_context_;
   FuzzerContext* fuzzer_context_;

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

@@ -138,7 +138,7 @@ void FuzzerPassAddAccessChains::Apply() {
           uint32_t index_value =
               GetFuzzerContext()->GetRandomIndexForAccessChain(bound);
           index_ids.push_back(FindOrCreateIntegerConstant(
-              {index_value}, 32, GetFuzzerContext()->ChooseEven()));
+              {index_value}, 32, GetFuzzerContext()->ChooseEven(), false));
           switch (subobject_type->opcode()) {
             case SpvOpTypeArray:
             case SpvOpTypeMatrix:

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

@@ -97,7 +97,7 @@ void FuzzerPassAddCompositeTypes::AddNewArrayType() {
   ApplyTransformation(TransformationAddTypeArray(
       GetFuzzerContext()->GetFreshId(), ChooseScalarOrCompositeType(),
       FindOrCreateIntegerConstant(
-          {GetFuzzerContext()->GetRandomSizeForNewArray()}, 32, false)));
+          {GetFuzzerContext()->GetRandomSizeForNewArray()}, 32, false, false)));
 }
 
 void FuzzerPassAddCompositeTypes::AddNewStructType() {

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

@@ -74,7 +74,7 @@ void FuzzerPassAddCopyMemory::Apply() {
         ApplyTransformation(TransformationAddCopyMemory(
             instruction_descriptor, GetFuzzerContext()->GetFreshId(),
             inst->result_id(), storage_class,
-            FindOrCreateZeroConstant(pointee_type_id)));
+            FindOrCreateZeroConstant(pointee_type_id, false)));
       });
 }
 

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

@@ -45,7 +45,7 @@ void FuzzerPassAddDeadBlocks::Apply() {
       // Make sure the module contains a boolean constant equal to
       // |condition_value|.
       bool condition_value = GetFuzzerContext()->ChooseEven();
-      FindOrCreateBoolConstant(condition_value);
+      FindOrCreateBoolConstant(condition_value, false);
 
       // We speculatively create a transformation, and then apply it (below) if
       // it turns out to be applicable.  This avoids duplicating the logic for

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

@@ -68,19 +68,16 @@ void FuzzerPassAddDeadBreaks::Apply() {
           merge_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) {
             // Add an additional operand for OpPhi instruction.
             //
-            // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
-            // If we have a way to communicate to the fact manager
-            // that a specific id use is irrelevant and could be replaced with
-            // something else, we should add such a fact about the zero
-            // provided as an OpPhi operand
-            phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id()));
+            // We mark the constant as irrelevant so that we can replace it with
+            // a more interesting value later.
+            phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id(), true));
           });
         }
 
         // Make sure the module has a required boolean constant to be used in
         // OpBranchConditional instruction.
         auto break_condition = GetFuzzerContext()->ChooseEven();
-        FindOrCreateBoolConstant(break_condition);
+        FindOrCreateBoolConstant(break_condition, false);
 
         auto candidate_transformation = TransformationAddDeadBreak(
             block.id(), merge_block->id(), break_condition, std::move(phi_ids));

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

@@ -59,19 +59,16 @@ void FuzzerPassAddDeadContinues::Apply() {
         continue_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) {
           // Add an additional operand for OpPhi instruction.
           //
-          // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
-          // If we have a way to communicate to the fact manager
-          // that a specific id use is irrelevant and could be replaced with
-          // something else, we should add such a fact about the zero
-          // provided as an OpPhi operand
-          phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id()));
+          // We mark the constant as irrelevant so that we can replace it with a
+          // more interesting value later.
+          phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id(), true));
         });
       }
 
       // Make sure the module contains a boolean constant equal to
       // |condition_value|.
       bool condition_value = GetFuzzerContext()->ChooseEven();
-      FindOrCreateBoolConstant(condition_value);
+      FindOrCreateBoolConstant(condition_value, false);
 
       // Make a transformation to add a dead continue from this node; if the
       // node turns out to be inappropriate (e.g. by not being in a loop) the

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

@@ -57,9 +57,12 @@ void FuzzerPassAddEquationInstructions::Apply() {
         std::vector<opt::Instruction*> available_instructions =
             FindAvailableInstructions(
                 function, block, inst_it,
-                [](opt::IRContext*, opt::Instruction* instruction) -> bool {
+                [this](opt::IRContext*, opt::Instruction* instruction) -> bool {
                   return instruction->result_id() && instruction->type_id() &&
-                         instruction->opcode() != SpvOpUndef;
+                         instruction->opcode() != SpvOpUndef &&
+                         !GetTransformationContext()
+                              ->GetFactManager()
+                              ->IdIsIrrelevant(instruction->result_id());
                 });
 
         // Try the opcodes for which we know how to make ids at random until

+ 78 - 134
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.cpp

@@ -106,148 +106,92 @@ void FuzzerPassAddFunctionCalls::Apply() {
       });
 }
 
-std::map<uint32_t, std::vector<opt::Instruction*>>
-FuzzerPassAddFunctionCalls::GetAvailableInstructionsSuitableForActualParameters(
-    opt::Function* function, opt::BasicBlock* block,
-    const opt::BasicBlock::iterator& inst_it) {
-  // Find all instructions in scope that could potentially be used as actual
-  // parameters.  Weed out unsuitable pointer arguments immediately.
-  std::vector<opt::Instruction*> potentially_suitable_instructions =
-      FindAvailableInstructions(
-          function, block, inst_it,
-          [this, block](opt::IRContext* context,
-                        opt::Instruction* inst) -> bool {
-            if (!inst->HasResultId() || !inst->type_id()) {
-              // An instruction needs a result id and type in order
-              // to be suitable as an actual parameter.
-              return false;
-            }
-            if (context->get_def_use_mgr()->GetDef(inst->type_id())->opcode() ==
-                SpvOpTypePointer) {
-              switch (inst->opcode()) {
-                case SpvOpFunctionParameter:
-                case SpvOpVariable:
-                  // Function parameters and variables are the only
-                  // kinds of pointer that can be used as actual
-                  // parameters.
-                  break;
-                default:
-                  return false;
-              }
-              if (!GetTransformationContext()->GetFactManager()->BlockIsDead(
-                      block->id()) &&
-                  !GetTransformationContext()
-                       ->GetFactManager()
-                       ->PointeeValueIsIrrelevant(inst->result_id())) {
-                // We can only pass a pointer as an actual parameter
-                // if the pointee value for the pointer is irrelevant,
-                // or if the block from which we would make the
-                // function call is dead.
-                return false;
-              }
-            }
-            return true;
-          });
-
-  // Group all the instructions that are potentially viable as function actual
-  // parameters by their result types.
-  std::map<uint32_t, std::vector<opt::Instruction*>> result;
-  for (auto inst : potentially_suitable_instructions) {
-    if (result.count(inst->type_id()) == 0) {
-      // This is the first instruction of this type we have seen, so populate
-      // the map with an entry.
-      result.insert({inst->type_id(), {}});
-    }
-    // Add the instruction to the sequence of instructions already associated
-    // with this type.
-    result.at(inst->type_id()).push_back(inst);
-  }
-  return result;
-}
-
 std::vector<uint32_t> FuzzerPassAddFunctionCalls::ChooseFunctionCallArguments(
     const opt::Function& callee, opt::Function* caller_function,
     opt::BasicBlock* caller_block,
     const opt::BasicBlock::iterator& caller_inst_it) {
-  auto type_to_available_instructions =
-      GetAvailableInstructionsSuitableForActualParameters(
-          caller_function, caller_block, caller_inst_it);
-
-  opt::Instruction* function_type = GetIRContext()->get_def_use_mgr()->GetDef(
-      callee.DefInst().GetSingleWordInOperand(1));
-  assert(function_type->opcode() == SpvOpTypeFunction &&
-         "The function type does not have the expected opcode.");
+  auto available_pointers = FindAvailableInstructions(
+      caller_function, caller_block, caller_inst_it,
+      [this, caller_block](opt::IRContext* /*unused*/, opt::Instruction* inst) {
+        if (inst->opcode() != SpvOpVariable ||
+            inst->opcode() != SpvOpFunctionParameter) {
+          // Function parameters and variables are the only
+          // kinds of pointer that can be used as actual
+          // parameters.
+          return false;
+        }
+
+        return GetTransformationContext()->GetFactManager()->BlockIsDead(
+                   caller_block->id()) ||
+               GetTransformationContext()
+                   ->GetFactManager()
+                   ->PointeeValueIsIrrelevant(inst->result_id());
+      });
+
+  std::unordered_map<uint32_t, std::vector<uint32_t>> type_id_to_result_id;
+  for (const auto* inst : available_pointers) {
+    type_id_to_result_id[inst->type_id()].push_back(inst->result_id());
+  }
+
   std::vector<uint32_t> result;
-  for (uint32_t arg_index = 1; arg_index < function_type->NumInOperands();
-       arg_index++) {
-    auto arg_type_id =
-        GetIRContext()
-            ->get_def_use_mgr()
-            ->GetDef(function_type->GetSingleWordInOperand(arg_index))
-            ->result_id();
-    if (type_to_available_instructions.count(arg_type_id)) {
-      std::vector<opt::Instruction*>& candidate_arguments =
-          type_to_available_instructions.at(arg_type_id);
-      // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177) The value
-      //  selected here is arbitrary.  We should consider adding this
-      //  information as a fact so that the passed parameter could be
-      //  transformed/changed.
-      result.push_back(candidate_arguments[GetFuzzerContext()->RandomIndex(
-                                               candidate_arguments)]
-                           ->result_id());
+  for (const auto* param :
+       fuzzerutil::GetParameters(GetIRContext(), callee.result_id())) {
+    const auto* param_type =
+        GetIRContext()->get_type_mgr()->GetType(param->type_id());
+    assert(param_type && "Parameter has invalid type");
+
+    if (!param_type->AsPointer()) {
+      // We mark the constant as irrelevant so that we can replace it with a
+      // more interesting value later.
+      result.push_back(FindOrCreateZeroConstant(param->type_id(), true));
+      continue;
+    }
+
+    if (type_id_to_result_id.count(param->type_id())) {
+      // Use an existing pointer if there are any.
+      const auto& candidates = type_id_to_result_id[param->type_id()];
+      result.push_back(candidates[GetFuzzerContext()->RandomIndex(candidates)]);
+      continue;
+    }
+
+    // Make a new variable, at function or global scope depending on the storage
+    // class of the pointer.
+
+    // Get a fresh id for the new variable.
+    uint32_t fresh_variable_id = GetFuzzerContext()->GetFreshId();
+
+    // The id of this variable is what we pass as the parameter to
+    // the call.
+    result.push_back(fresh_variable_id);
+    type_id_to_result_id[param->type_id()].push_back(fresh_variable_id);
+
+    // Now bring the variable into existence.
+    auto storage_class = param_type->AsPointer()->storage_class();
+    auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType(
+        GetIRContext(), param->type_id());
+    if (storage_class == SpvStorageClassFunction) {
+      // Add a new zero-initialized local variable to the current
+      // function, noting that its pointee value is irrelevant.
+      ApplyTransformation(TransformationAddLocalVariable(
+          fresh_variable_id, param->type_id(), caller_function->result_id(),
+          FindOrCreateZeroConstant(pointee_type_id, false), true));
     } else {
-      // We don't have a suitable id in scope to pass, so we must make
-      // something up.
-      auto type_instruction =
-          GetIRContext()->get_def_use_mgr()->GetDef(arg_type_id);
-
-      if (type_instruction->opcode() == SpvOpTypePointer) {
-        // In the case of a pointer, we make a new variable, at function
-        // or global scope depending on the storage class of the
-        // pointer.
-
-        // Get a fresh id for the new variable.
-        uint32_t fresh_variable_id = GetFuzzerContext()->GetFreshId();
-
-        // The id of this variable is what we pass as the parameter to
-        // the call.
-        result.push_back(fresh_variable_id);
-
-        // Now bring the variable into existence.
-        auto storage_class = static_cast<SpvStorageClass>(
-            type_instruction->GetSingleWordInOperand(0));
-        if (storage_class == SpvStorageClassFunction) {
-          // Add a new zero-initialized local variable to the current
-          // function, noting that its pointee value is irrelevant.
-          ApplyTransformation(TransformationAddLocalVariable(
-              fresh_variable_id, arg_type_id, caller_function->result_id(),
-              FindOrCreateZeroConstant(
-                  type_instruction->GetSingleWordInOperand(1)),
-              true));
-        } else {
-          assert((storage_class == SpvStorageClassPrivate ||
-                  storage_class == SpvStorageClassWorkgroup) &&
-                 "Only Function, Private and Workgroup storage classes are "
-                 "supported at present.");
-          // Add a new global variable to the module, zero-initializing it if
-          // it has Private storage class, and noting that its pointee value is
-          // irrelevant.
-          ApplyTransformation(TransformationAddGlobalVariable(
-              fresh_variable_id, arg_type_id, storage_class,
-              storage_class == SpvStorageClassPrivate
-                  ? FindOrCreateZeroConstant(
-                        type_instruction->GetSingleWordInOperand(1))
-                  : 0,
-              true));
-        }
-      } else {
-        // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177): We use
-        //  constant zero for the parameter, but could consider adding a fact
-        //  to allow further passes to obfuscate it.
-        result.push_back(FindOrCreateZeroConstant(arg_type_id));
-      }
+      assert((storage_class == SpvStorageClassPrivate ||
+              storage_class == SpvStorageClassWorkgroup) &&
+             "Only Function, Private and Workgroup storage classes are "
+             "supported at present.");
+      // Add a new global variable to the module, zero-initializing it if
+      // it has Private storage class, and noting that its pointee value is
+      // irrelevant.
+      ApplyTransformation(TransformationAddGlobalVariable(
+          fresh_variable_id, param->type_id(), storage_class,
+          storage_class == SpvStorageClassPrivate
+              ? FindOrCreateZeroConstant(pointee_type_id, false)
+              : 0,
+          true));
     }
   }
+
   return result;
 }
 

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

@@ -34,14 +34,6 @@ class FuzzerPassAddFunctionCalls : public FuzzerPass {
   void Apply() override;
 
  private:
-  // Identify all instructions available at |instr_it|, in block |block| of
-  // |function|, that are potentially suitable as function call actual
-  // parameters.  The results are grouped by type.
-  std::map<uint32_t, std::vector<opt::Instruction*>>
-  GetAvailableInstructionsSuitableForActualParameters(
-      opt::Function* function, opt::BasicBlock* block,
-      const opt::BasicBlock::iterator& inst_it);
-
   // Randomly chooses suitable arguments to invoke |callee| right before
   // instruction |caller_inst_it| of block |caller_block| in |caller_function|,
   // based on both existing available instructions and the addition of new

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

@@ -85,7 +85,7 @@ void FuzzerPassAddGlobalVariables::Apply() {
         GetFuzzerContext()->GetFreshId(), pointer_type_id,
         variable_storage_class,
         variable_storage_class == SpvStorageClassPrivate
-            ? FindOrCreateZeroConstant(basic_type)
+            ? FindOrCreateZeroConstant(basic_type, false)
             : 0,
         true));
   }

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

@@ -184,7 +184,7 @@ void FuzzerPassAddImageSampleUnusedComponents::Apply() {
          // FindOrCreateZeroConstant
          // %20 = OpConstant %4 0
          // %21 = OpConstantComposite %5 %20 %20
-         FindOrCreateZeroConstant(zero_constant_type_id)},
+         FindOrCreateZeroConstant(zero_constant_type_id, false)},
         MakeInstructionDescriptor(GetIRContext(), instruction),
         coordinate_with_unused_components_id));
 

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

@@ -71,7 +71,7 @@ void FuzzerPassAddLocalVariables::Apply() {
       }
       ApplyTransformation(TransformationAddLocalVariable(
           GetFuzzerContext()->GetFreshId(), pointer_type, function.result_id(),
-          FindOrCreateZeroConstant(basic_type), true));
+          FindOrCreateZeroConstant(basic_type, false), true));
     }
   }
 }

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

@@ -72,9 +72,11 @@ void FuzzerPassAddParameters::Apply() {
     for (uint32_t i = 0; i < num_new_parameters; ++i) {
       ApplyTransformation(TransformationAddParameter(
           function.result_id(), GetFuzzerContext()->GetFreshId(),
+          // We mark the constant as irrelevant so that we can replace it with a
+          // more interesting value later.
           FindOrCreateZeroConstant(
-              type_candidates[GetFuzzerContext()->RandomIndex(
-                  type_candidates)]),
+              type_candidates[GetFuzzerContext()->RandomIndex(type_candidates)],
+              true),
           GetFuzzerContext()->GetFreshId()));
     }
   }

+ 55 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp

@@ -0,0 +1,55 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_add_relaxed_decorations.h"
+
+#include "source/fuzz/transformation_add_relaxed_decoration.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddRelaxedDecorations::FuzzerPassAddRelaxedDecorations(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassAddRelaxedDecorations::~FuzzerPassAddRelaxedDecorations() = default;
+
+void FuzzerPassAddRelaxedDecorations::Apply() {
+  // Consider every instruction in every block in every function.
+  for (auto& function : *GetIRContext()->module()) {
+    for (auto& block : function) {
+      for (auto& inst : block) {
+        // Randomly choose whether to apply the RelaxedPrecision decoration
+        // to this instruction.
+        if (GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfAddingRelaxedDecoration())) {
+          TransformationAddRelaxedDecoration transformation(inst.result_id());
+          // Restrict attention to numeric instructions (returning 32-bit
+          // floats or ints according to SPIR-V documentation) in dead blocks.
+          if (transformation.IsApplicable(GetIRContext(),
+                                          *GetTransformationContext())) {
+            transformation.Apply(GetIRContext(), GetTransformationContext());
+            *GetTransformations()->add_transformation() =
+                transformation.ToMessage();
+          }
+        }
+      }
+    }
+  }
+}
+}  // namespace fuzz
+}  // namespace spvtools

+ 39 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h

@@ -0,0 +1,39 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SPIRV_TOOLS_FUZZER_PASS_ADD_RELAXED_DECORATIONS_H
+#define SPIRV_TOOLS_FUZZER_PASS_ADD_RELAXED_DECORATIONS_H
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A pass that applies the Relaxed decoration to numeric instructions.
+class FuzzerPassAddRelaxedDecorations : public FuzzerPass {
+ public:
+  FuzzerPassAddRelaxedDecorations(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddRelaxedDecorations() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SPIRV_TOOLS_FUZZER_PASS_ADD_RELAXED_DECORATIONS_H

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

@@ -53,11 +53,12 @@ void FuzzerPassAddSynonyms::Apply() {
         // 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) {
+            [synonym_type, this](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);
+                  ir_context, *GetTransformationContext(), inst, synonym_type);
             });
 
         if (available_instructions.empty()) {
@@ -76,7 +77,7 @@ void FuzzerPassAddSynonyms::Apply() {
           case protobufs::TransformationAddSynonym::LOGICAL_OR:
             // Create a zero constant to be used as an operand of the synonymous
             // instruction.
-            FindOrCreateZeroConstant(existing_synonym->type_id());
+            FindOrCreateZeroConstant(existing_synonym->type_id(), false);
             break;
           case protobufs::TransformationAddSynonym::MUL_ONE:
           case protobufs::TransformationAddSynonym::LOGICAL_AND: {
@@ -96,13 +97,13 @@ void FuzzerPassAddSynonyms::Apply() {
               FindOrCreateCompositeConstant(
                   std::vector<uint32_t>(
                       vector->element_count(),
-                      FindOrCreateConstant({one_word}, element_type_id)),
-                  existing_synonym->type_id());
+                      FindOrCreateConstant({one_word}, element_type_id, false)),
+                  existing_synonym->type_id(), false);
             } else {
               FindOrCreateConstant(
                   {existing_synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1)
                                                     : 1u},
-                  existing_synonym->type_id());
+                  existing_synonym->type_id(), false);
             }
           } break;
           default:

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

@@ -60,10 +60,10 @@ void FuzzerPassAddVectorShuffleInstructions::Apply() {
         std::vector<opt::Instruction*> vector_instructions =
             FindAvailableInstructions(
                 function, block, instruction_iterator,
-                [instruction_descriptor](
+                [this, instruction_descriptor](
                     opt::IRContext* ir_context,
                     opt::Instruction* instruction) -> bool {
-                  if (!instruction->type_id()) {
+                  if (!instruction->result_id() || !instruction->type_id()) {
                     return false;
                   }
 
@@ -73,6 +73,18 @@ void FuzzerPassAddVectorShuffleInstructions::Apply() {
                     return false;
                   }
 
+                  if (!GetTransformationContext()
+                           ->GetFactManager()
+                           ->IdIsIrrelevant(instruction->result_id()) &&
+                      !fuzzerutil::CanMakeSynonymOf(ir_context,
+                                                    *GetTransformationContext(),
+                                                    instruction)) {
+                    // If the id is irrelevant, we can use it since it will not
+                    // participate in DataSynonym fact. Otherwise, we should be
+                    // able to produce a synonym out of the id.
+                    return false;
+                  }
+
                   return fuzzerutil::IdIsAvailableBeforeInstruction(
                       ir_context,
                       FindInstruction(instruction_descriptor, ir_context),

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

@@ -142,6 +142,9 @@ void FuzzerPassApplyIdSynonyms::Apply() {
                                                : parent_block->terminator();
           }
 
+          assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
+                     synonym_to_try->object()) &&
+                 "Irrelevant ids can't participate in DataSynonym facts");
           ApplyTransformation(TransformationCompositeExtract(
               MakeInstructionDescriptor(GetIRContext(),
                                         instruction_to_insert_before),

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

@@ -14,12 +14,10 @@
 
 #include "source/fuzz/fuzzer_pass_construct_composites.h"
 
-#include <cmath>
 #include <memory>
 
 #include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/transformation_composite_construct.h"
-#include "source/util/make_unique.h"
 
 namespace spvtools {
 namespace fuzz {
@@ -67,8 +65,23 @@ void FuzzerPassConstructComposites::Apply() {
         // program point) and suitable for making a synonym of, associate it
         // with the id of its result type.
         TypeIdToInstructions type_id_to_available_instructions;
-        for (auto instruction : FindAvailableInstructions(
-                 function, block, inst_it, fuzzerutil::CanMakeSynonymOf)) {
+        auto available_instructions = FindAvailableInstructions(
+            function, block, inst_it,
+            [this](opt::IRContext* ir_context, opt::Instruction* inst) {
+              if (!inst->result_id() || !inst->type_id()) {
+                return false;
+              }
+
+              // If the id is irrelevant, we can use it since it will not
+              // participate in DataSynonym fact. Otherwise, we should be able
+              // to produce a synonym out of the id.
+              return GetTransformationContext()
+                         ->GetFactManager()
+                         ->IdIsIrrelevant(inst->result_id()) ||
+                     fuzzerutil::CanMakeSynonymOf(
+                         ir_context, *GetTransformationContext(), inst);
+            });
+        for (auto instruction : available_instructions) {
           RecordAvailableInstruction(instruction,
                                      &type_id_to_available_instructions);
         }

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

@@ -55,8 +55,12 @@ void FuzzerPassCopyObjects::Apply() {
         }
 
         std::vector<opt::Instruction*> relevant_instructions =
-            FindAvailableInstructions(function, block, inst_it,
-                                      fuzzerutil::CanMakeSynonymOf);
+            FindAvailableInstructions(
+                function, block, inst_it,
+                [this](opt::IRContext* ir_context, opt::Instruction* inst) {
+                  return fuzzerutil::CanMakeSynonymOf(
+                      ir_context, *GetTransformationContext(), inst);
+                });
 
         // At this point, |relevant_instructions| contains all the instructions
         // we might think of copying.

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

@@ -328,7 +328,8 @@ void FuzzerPassDonateModules::HandleTypeOrValue(
       ApplyTransformation(TransformationAddTypeArray(
           new_result_id, original_id_to_donated_id->at(component_type_id),
           FindOrCreateIntegerConstant(
-              {GetFuzzerContext()->GetRandomSizeForNewArray()}, 32, false)));
+              {GetFuzzerContext()->GetRandomSizeForNewArray()}, 32, false,
+              false)));
     } break;
     case SpvOpTypeStruct: {
       // Similar to SpvOpTypeArray.
@@ -446,7 +447,7 @@ void FuzzerPassDonateModules::HandleTypeOrValue(
       auto value = type_or_value.opcode() == SpvOpConstantTrue ||
                    type_or_value.opcode() == SpvOpSpecConstantTrue;
       ApplyTransformation(
-          TransformationAddConstantBoolean(new_result_id, value));
+          TransformationAddConstantBoolean(new_result_id, value, false));
     } break;
     case SpvOpSpecConstant:
     case SpvOpConstant: {
@@ -459,7 +460,7 @@ void FuzzerPassDonateModules::HandleTypeOrValue(
       });
       ApplyTransformation(TransformationAddConstantScalar(
           new_result_id, original_id_to_donated_id->at(type_or_value.type_id()),
-          data_words));
+          data_words, false));
     } break;
     case SpvOpSpecConstantComposite:
     case SpvOpConstantComposite: {
@@ -482,7 +483,7 @@ void FuzzerPassDonateModules::HandleTypeOrValue(
       });
       ApplyTransformation(TransformationAddConstantComposite(
           new_result_id, original_id_to_donated_id->at(type_or_value.type_id()),
-          constituent_ids));
+          constituent_ids, false));
     } break;
     case SpvOpConstantNull: {
       if (!original_id_to_donated_id->count(type_or_value.type_id())) {
@@ -544,7 +545,8 @@ void FuzzerPassDonateModules::HandleTypeOrValue(
                              ? 0
                              : FindOrCreateZeroConstant(
                                    fuzzerutil::GetPointeeTypeIdFromPointerType(
-                                       GetIRContext(), remapped_pointer_type));
+                                       GetIRContext(), remapped_pointer_type),
+                                   false);
       } else {
         // The variable already had an initializer; use its remapped id.
         initializer_id = original_id_to_donated_id->at(
@@ -919,11 +921,10 @@ void FuzzerPassDonateModules::HandleDifficultInstruction(
 
   // We find or add a zero constant to the receiving module for the type in
   // question, and add an OpCopyObject instruction that copies this zero.
-  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
-  //  Using this particular constant is arbitrary, so if we have a
-  //  mechanism for noting that an id use is arbitrary and could be
-  //  fuzzed we should use it here.
-  auto zero_constant = FindOrCreateZeroConstant(remapped_type_id);
+  //
+  // We mark the constant as irrelevant so that we can replace it with a
+  // more interesting value later.
+  auto zero_constant = FindOrCreateZeroConstant(remapped_type_id, true);
   donated_instructions->push_back(MakeInstructionMessage(
       SpvOpCopyObject, remapped_type_id,
       original_id_to_donated_id->at(instruction.result_id()),
@@ -980,9 +981,11 @@ void FuzzerPassDonateModules::PrepareInstructionForDonation(
     // This is an uninitialized local variable.  Initialize it to zero.
     input_operands.push_back(
         {SPV_OPERAND_TYPE_ID,
-         {FindOrCreateZeroConstant(fuzzerutil::GetPointeeTypeIdFromPointerType(
-             GetIRContext(),
-             original_id_to_donated_id->at(instruction.type_id())))}});
+         {FindOrCreateZeroConstant(
+             fuzzerutil::GetPointeeTypeIdFromPointerType(
+                 GetIRContext(),
+                 original_id_to_donated_id->at(instruction.type_id())),
+             false)}});
   }
 
   if (instruction.result_id() &&
@@ -1013,9 +1016,9 @@ void FuzzerPassDonateModules::AddLivesafeFunction(
   FindOrCreateBoolType();  // Needed for comparisons
   FindOrCreatePointerToIntegerType(
       32, false, SpvStorageClassFunction);  // Needed for adding loop limiters
-  FindOrCreateIntegerConstant({0}, 32,
+  FindOrCreateIntegerConstant({0}, 32, false,
                               false);  // Needed for initializing loop limiters
-  FindOrCreateIntegerConstant({1}, 32,
+  FindOrCreateIntegerConstant({1}, 32, false,
                               false);  // Needed for incrementing loop limiters
 
   // Get a fresh id for the variable that will be used as a loop limiter.
@@ -1023,7 +1026,7 @@ void FuzzerPassDonateModules::AddLivesafeFunction(
   // Choose a random loop limit, and add the required constant to the
   // module if not already there.
   const uint32_t loop_limit = FindOrCreateIntegerConstant(
-      {GetFuzzerContext()->GetRandomLoopLimit()}, 32, false);
+      {GetFuzzerContext()->GetRandomLoopLimit()}, 32, false, false);
 
   // Consider every loop header in the function to donate, and create a
   // structure capturing the ids to be used for manipulating the loop
@@ -1118,7 +1121,7 @@ void FuzzerPassDonateModules::AddLivesafeFunction(
               // whose value is one less than the bound, to compare
               // against and to use as the clamped value.
               FindOrCreateIntegerConstant({bound - 1}, 32,
-                                          index_int_type->IsSigned());
+                                          index_int_type->IsSigned(), false);
             }
             should_be_composite_type =
                 TransformationAddFunction::FollowCompositeIndex(
@@ -1147,7 +1150,8 @@ void FuzzerPassDonateModules::AddLivesafeFunction(
     assert(function_return_type_inst->opcode() != SpvOpTypePointer &&
            "Function return type must not be a pointer.");
     kill_unreachable_return_value_id = FindOrCreateZeroConstant(
-        original_id_to_donated_id.at(function_return_type_inst->result_id()));
+        original_id_to_donated_id.at(function_return_type_inst->result_id()),
+        false);
   }
   // Add the function in a livesafe manner.
   ApplyTransformation(TransformationAddFunction(

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

@@ -14,6 +14,7 @@
 // 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"
@@ -49,7 +50,7 @@ uint32_t FuzzerPassInterchangeZeroLikeConstants::FindOrCreateToggledConstant(
     if (kind == opt::analysis::Type::kBool ||
         kind == opt::analysis::Type::kInteger ||
         kind == opt::analysis::Type::kFloat) {
-      return FindOrCreateZeroConstant(declaration->type_id());
+      return FindOrCreateZeroConstant(declaration->type_id(), false);
     }
   }
 
@@ -83,13 +84,21 @@ void FuzzerPassInterchangeZeroLikeConstants::Apply() {
 
   for (auto constant : GetIRContext()->GetConstants()) {
     uint32_t constant_id = constant->result_id();
-    uint32_t toggled_id = FindOrCreateToggledConstant(constant);
+    if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
+            constant_id)) {
+      continue;
+    }
 
+    uint32_t toggled_id = FindOrCreateToggledConstant(constant);
     if (!toggled_id) {
       // Not a zero-like constant
       continue;
     }
 
+    assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
+               toggled_id) &&
+           "FindOrCreateToggledConstant can't produce an irrelevant id");
+
     // Record synonymous constants
     ApplyTransformation(
         TransformationRecordSynonymousConstants(constant_id, toggled_id));

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

@@ -53,9 +53,8 @@ void FuzzerPassMergeBlocks::Apply() {
   }
 
   while (!potential_transformations.empty()) {
-    uint32_t index = GetFuzzerContext()->RandomIndex(potential_transformations);
-    auto transformation = potential_transformations.at(index);
-    potential_transformations.erase(potential_transformations.begin() + index);
+    auto transformation =
+        GetFuzzerContext()->RemoveAtRandomIndex(&potential_transformations);
     MaybeApplyTransformation(transformation);
   }
 }

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

@@ -311,9 +311,9 @@ void FuzzerPassObfuscateConstants::ObfuscateBoolConstant(
   } while (constant_index_1 == constant_index_2);
 
   auto constant_id_1 = FindOrCreateConstant(
-      available_constant_words[constant_index_1], chosen_type_id);
+      available_constant_words[constant_index_1], chosen_type_id, false);
   auto constant_id_2 = FindOrCreateConstant(
-      available_constant_words[constant_index_2], chosen_type_id);
+      available_constant_words[constant_index_2], chosen_type_id, false);
 
   assert(constant_id_1 != 0 && constant_id_2 != 0 &&
          "We should not find an available constant with an id of 0.");
@@ -361,7 +361,7 @@ void FuzzerPassObfuscateConstants::ObfuscateScalarConstant(
   // Make sure the module has OpConstant instructions for each index used to
   // access a uniform.
   for (auto index : uniform_descriptor.index()) {
-    FindOrCreateIntegerConstant({index}, 32, true);
+    FindOrCreateIntegerConstant({index}, 32, true, false);
   }
 
   // Make sure the module has OpTypePointer that points to the element type of

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

@@ -80,7 +80,7 @@ void FuzzerPassPushIdsThroughVariables::Apply() {
         std::vector<opt::Instruction*> value_instructions =
             FindAvailableInstructions(
                 function, block, instruction_iterator,
-                [basic_type_id, instruction_descriptor](
+                [this, basic_type_id, instruction_descriptor](
                     opt::IRContext* ir_context,
                     opt::Instruction* instruction) -> bool {
                   if (!instruction->result_id() || !instruction->type_id()) {
@@ -91,6 +91,18 @@ void FuzzerPassPushIdsThroughVariables::Apply() {
                     return false;
                   }
 
+                  // If the id is irrelevant, we can use it since it will not
+                  // participate in DataSynonym fact. Otherwise, we should be
+                  // able to produce a synonym out of the id.
+                  if (!GetTransformationContext()
+                           ->GetFactManager()
+                           ->IdIsIrrelevant(instruction->result_id()) &&
+                      !fuzzerutil::CanMakeSynonymOf(ir_context,
+                                                    *GetTransformationContext(),
+                                                    instruction)) {
+                    return false;
+                  }
+
                   return fuzzerutil::IdIsAvailableBeforeInstruction(
                       ir_context,
                       FindInstruction(instruction_descriptor, ir_context),
@@ -126,7 +138,7 @@ void FuzzerPassPushIdsThroughVariables::Apply() {
         // Create a constant to initialize the variable from. This might update
         // module's id bound so it must be done before any fresh ids are
         // computed.
-        auto initializer_id = FindOrCreateZeroConstant(basic_type_id);
+        auto initializer_id = FindOrCreateZeroConstant(basic_type_id, false);
 
         // Applies the push id through variable transformation.
         ApplyTransformation(TransformationPushIdThroughVariable(

+ 58 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp

@@ -0,0 +1,58 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassReplaceCopyMemoriesWithLoadsStores::
+    FuzzerPassReplaceCopyMemoriesWithLoadsStores(
+        opt::IRContext* ir_context,
+        TransformationContext* transformation_context,
+        FuzzerContext* fuzzer_context,
+        protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassReplaceCopyMemoriesWithLoadsStores::
+    ~FuzzerPassReplaceCopyMemoriesWithLoadsStores() = default;
+
+void FuzzerPassReplaceCopyMemoriesWithLoadsStores::Apply() {
+  GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
+    // Randomly decide whether to replace the OpCopyMemory.
+    if (!GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()
+                ->GetChanceOfReplacingCopyMemoryWithLoadStore())) {
+      return;
+    }
+
+    // The instruction must be OpCopyMemory.
+    if (instruction->opcode() != SpvOpCopyMemory) {
+      return;
+    }
+
+    // Apply the transformation replacing OpCopyMemory with OpLoad and OpStore.
+    ApplyTransformation(TransformationReplaceCopyMemoryWithLoadStore(
+        GetFuzzerContext()->GetFreshId(),
+        MakeInstructionDescriptor(GetIRContext(), instruction)));
+  });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

@@ -0,0 +1,41 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H
+#define SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Replaces instructions OpCopyMemory with loading the source variable to
+// an intermediate value and storing this value into the target variable of
+// the original OpCopyMemory instruction.
+class FuzzerPassReplaceCopyMemoriesWithLoadsStores : public FuzzerPass {
+ public:
+  FuzzerPassReplaceCopyMemoriesWithLoadsStores(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassReplaceCopyMemoriesWithLoadsStores() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H

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

@@ -0,0 +1,83 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_replace_copy_object_with_store_load.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassReplaceCopyObjectsWithStoresLoads::
+    FuzzerPassReplaceCopyObjectsWithStoresLoads(
+        opt::IRContext* ir_context,
+        TransformationContext* transformation_context,
+        FuzzerContext* fuzzer_context,
+        protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassReplaceCopyObjectsWithStoresLoads::
+    ~FuzzerPassReplaceCopyObjectsWithStoresLoads() = default;
+
+void FuzzerPassReplaceCopyObjectsWithStoresLoads::Apply() {
+  GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
+    // Randomly decide whether to replace OpCopyObject.
+    if (!GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()
+                ->GetChanceOfReplacingCopyObjectWithStoreLoad())) {
+      return;
+    }
+    // The instruction must be OpCopyObject.
+    if (instruction->opcode() != SpvOpCopyObject) {
+      return;
+    }
+    // The opcode of the type_id instruction cannot be a OpTypePointer,
+    // because we cannot define a pointer to pointer.
+    if (GetIRContext()
+            ->get_def_use_mgr()
+            ->GetDef(instruction->type_id())
+            ->opcode() == SpvOpTypePointer) {
+      return;
+    }
+    // It must be valid to insert OpStore and OpLoad instructions
+    // before the instruction OpCopyObject.
+    if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore,
+                                                      instruction) ||
+        !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, instruction)) {
+      return;
+    }
+
+    // Randomly decides whether a global or local variable will be added.
+    auto variable_storage_class = GetFuzzerContext()->ChooseEven()
+                                      ? SpvStorageClassPrivate
+                                      : SpvStorageClassFunction;
+
+    // Find or create a constant to initialize the variable from.
+    auto variable_initializer_id =
+        FindOrCreateZeroConstant(instruction->type_id(), false);
+
+    // Make sure that pointer type is defined.
+    FindOrCreatePointerType(instruction->type_id(), variable_storage_class);
+    // Apply the transformation replacing OpCopyObject with Store and Load.
+    ApplyTransformation(TransformationReplaceCopyObjectWithStoreLoad(
+        instruction->result_id(), GetFuzzerContext()->GetFreshId(),
+        variable_storage_class, variable_initializer_id));
+  });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

@@ -0,0 +1,41 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_OBJECTS_WITH_STORES_LOADS_H
+#define SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_OBJECTS_WITH_STORES_LOADS_H
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Replaces instructions OpCopyObject with storing into a new variable
+// and immediately loading this variable to |result_id| of the
+// original OpCopyObject instruction.
+class FuzzerPassReplaceCopyObjectsWithStoresLoads : public FuzzerPass {
+ public:
+  FuzzerPassReplaceCopyObjectsWithStoresLoads(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassReplaceCopyObjectsWithStoresLoads() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SPIRV_TOOLS_FUZZER_PASS_REPLACE_COPY_OBJECTS_WITH_STORES_LOADS_H

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

@@ -81,7 +81,7 @@ void FuzzerPassReplaceParameterWithGlobal::Apply() {
     FindOrCreatePointerType(replaced_param->type_id(), SpvStorageClassPrivate);
 
     // Make sure initializer for the global variable exists in the module.
-    FindOrCreateZeroConstant(replaced_param->type_id());
+    FindOrCreateZeroConstant(replaced_param->type_id(), false);
 
     ApplyTransformation(TransformationReplaceParameterWithGlobal(
         GetFuzzerContext()->GetFreshId(), replaced_param->result_id(),

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

@@ -0,0 +1,115 @@
+// 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_replace_params_with_struct.h"
+
+#include <numeric>
+#include <vector>
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_replace_params_with_struct.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassReplaceParamsWithStruct::FuzzerPassReplaceParamsWithStruct(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassReplaceParamsWithStruct::~FuzzerPassReplaceParamsWithStruct() =
+    default;
+
+void FuzzerPassReplaceParamsWithStruct::Apply() {
+  for (const auto& function : *GetIRContext()->module()) {
+    auto params =
+        fuzzerutil::GetParameters(GetIRContext(), function.result_id());
+
+    if (params.empty() || fuzzerutil::FunctionIsEntryPoint(
+                              GetIRContext(), function.result_id())) {
+      continue;
+    }
+
+    if (!GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()->GetChanceOfReplacingParametersWithStruct())) {
+      continue;
+    }
+
+    std::vector<uint32_t> parameter_index(params.size());
+    std::iota(parameter_index.begin(), parameter_index.end(), 0);
+
+    // Remove the indices of unsupported parameters.
+    auto new_end = std::remove_if(
+        parameter_index.begin(), parameter_index.end(),
+        [this, &params](uint32_t index) {
+          const auto* type =
+              GetIRContext()->get_type_mgr()->GetType(params[index]->type_id());
+          assert(type && "Parameter has invalid type");
+          return !TransformationReplaceParamsWithStruct::
+              IsParameterTypeSupported(*type);
+        });
+
+    // std::remove_if changes the vector so that removed elements are placed at
+    // the end (i.e. [new_end, parameter_index.end()) is a range of removed
+    // elements). However, the size of the vector is not changed so we need to
+    // change that explicitly by popping those elements from the vector.
+    parameter_index.erase(new_end, parameter_index.end());
+
+    if (parameter_index.empty()) {
+      continue;
+    }
+
+    // Select |num_replaced_params| parameters at random. We shuffle the vector
+    // of indices for randomization and shrink it to select first
+    // |num_replaced_params| parameters.
+    auto num_replaced_params = std::min<size_t>(
+        parameter_index.size(),
+        GetFuzzerContext()->GetRandomNumberOfParametersReplacedWithStruct(
+            static_cast<uint32_t>(params.size())));
+
+    GetFuzzerContext()->Shuffle(&parameter_index);
+    parameter_index.resize(num_replaced_params);
+
+    // Make sure OpTypeStruct exists in the module.
+    std::vector<uint32_t> component_type_ids;
+    for (auto index : parameter_index) {
+      component_type_ids.push_back(params[index]->type_id());
+    }
+
+    FindOrCreateStructType(component_type_ids);
+
+    // Map parameters' indices to parameters' ids.
+    std::vector<uint32_t> parameter_id;
+    for (auto index : parameter_index) {
+      parameter_id.push_back(params[index]->result_id());
+    }
+
+    std::unordered_map<uint32_t, uint32_t> caller_id_to_fresh_id;
+    for (const auto* inst :
+         fuzzerutil::GetCallers(GetIRContext(), function.result_id())) {
+      caller_id_to_fresh_id[inst->result_id()] =
+          GetFuzzerContext()->GetFreshId();
+    }
+
+    ApplyTransformation(TransformationReplaceParamsWithStruct(
+        parameter_id, GetFuzzerContext()->GetFreshId(),
+        GetFuzzerContext()->GetFreshId(), caller_id_to_fresh_id));
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 40 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.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_REPLACE_PARAMS_WITH_STRUCT_H_
+#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMS_WITH_STRUCT_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Iterates over all functions in the module and randomly decides for each one
+// whether to replace a subset of its parameters with a struct value.
+class FuzzerPassReplaceParamsWithStruct : public FuzzerPass {
+ public:
+  FuzzerPassReplaceParamsWithStruct(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassReplaceParamsWithStruct() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMS_WITH_STRUCT_H_

+ 268 - 113
3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp

@@ -23,6 +23,25 @@ namespace spvtools {
 namespace fuzz {
 
 namespace fuzzerutil {
+namespace {
+
+uint32_t MaybeGetOpConstant(opt::IRContext* ir_context,
+                            const TransformationContext& transformation_context,
+                            const std::vector<uint32_t>& words,
+                            uint32_t type_id, bool is_irrelevant) {
+  for (const auto& inst : ir_context->types_values()) {
+    if (inst.opcode() == SpvOpConstant && inst.type_id() == type_id &&
+        inst.GetInOperand(0).words == words &&
+        transformation_context.GetFactManager()->IdIsIrrelevant(
+            inst.result_id()) == is_irrelevant) {
+      return inst.result_id();
+    }
+  }
+
+  return 0;
+}
+
+}  // namespace
 
 bool IsFreshId(opt::IRContext* context, uint32_t id) {
   return !context->get_def_use_mgr()->GetDef(id);
@@ -114,7 +133,7 @@ bool PhiIdsOkForNewEdge(
 
 void AddUnreachableEdgeAndUpdateOpPhis(
     opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
-    bool condition_value,
+    uint32_t bool_id,
     const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids) {
   assert(PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) &&
          "Precondition on phi_ids is not satisfied");
@@ -122,10 +141,13 @@ void AddUnreachableEdgeAndUpdateOpPhis(
          "Precondition on terminator of bb_from is not satisfied");
 
   // Get the id of the boolean constant to be used as the condition.
-  uint32_t bool_id = MaybeGetBoolConstant(context, condition_value);
-  assert(
-      bool_id &&
-      "Precondition that condition value must be available is not satisfied");
+  auto condition_inst = context->get_def_use_mgr()->GetDef(bool_id);
+  assert(condition_inst &&
+         (condition_inst->opcode() == SpvOpConstantTrue ||
+          condition_inst->opcode() == SpvOpConstantFalse) &&
+         "|bool_id| is invalid");
+
+  auto condition_value = condition_inst->opcode() == SpvOpConstantTrue;
 
   const bool from_to_edge_already_exists = bb_from->IsSuccessor(bb_to);
   auto successor = bb_from->terminator()->GetSingleWordInOperand(0);
@@ -226,7 +248,9 @@ bool CanInsertOpcodeBeforeInstruction(
   return opcode == SpvOpPhi || instruction_in_block->opcode() != SpvOpPhi;
 }
 
-bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst) {
+bool CanMakeSynonymOf(opt::IRContext* ir_context,
+                      const TransformationContext& transformation_context,
+                      opt::Instruction* inst) {
   if (inst->opcode() == SpvOpSampledImage) {
     // The SPIR-V data rules say that only very specific instructions may
     // may consume the result id of an OpSampledImage, and this excludes the
@@ -237,6 +261,11 @@ bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst) {
     // We can only make a synonym of an instruction that generates an id.
     return false;
   }
+  if (transformation_context.GetFactManager()->IdIsIrrelevant(
+          inst->result_id())) {
+    // An irrelevant id can't be a synonym of anything.
+    return false;
+  }
   if (!inst->type_id()) {
     // We can only make a synonym of an instruction that has a type.
     return false;
@@ -706,6 +735,101 @@ std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
   return result;
 }
 
+std::vector<opt::Instruction*> GetCallers(opt::IRContext* ir_context,
+                                          uint32_t function_id) {
+  assert(FindFunction(ir_context, function_id) &&
+         "|function_id| is not a result id of a function");
+
+  std::vector<opt::Instruction*> result;
+  ir_context->get_def_use_mgr()->ForEachUser(
+      function_id, [&result, function_id](opt::Instruction* inst) {
+        if (inst->opcode() == SpvOpFunctionCall &&
+            inst->GetSingleWordInOperand(0) == function_id) {
+          result.push_back(inst);
+        }
+      });
+
+  return result;
+}
+
+opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context,
+                                          uint32_t param_id) {
+  auto* param_inst = ir_context->get_def_use_mgr()->GetDef(param_id);
+  assert(param_inst && "Parameter id is invalid");
+
+  for (auto& function : *ir_context->module()) {
+    if (InstructionIsFunctionParameter(param_inst, &function)) {
+      return &function;
+    }
+  }
+
+  return nullptr;
+}
+
+uint32_t UpdateFunctionType(opt::IRContext* ir_context, uint32_t function_id,
+                            uint32_t new_function_type_result_id,
+                            uint32_t return_type_id,
+                            const std::vector<uint32_t>& parameter_type_ids) {
+  // Check some initial constraints.
+  assert(ir_context->get_type_mgr()->GetType(return_type_id) &&
+         "Return type is invalid");
+  for (auto id : parameter_type_ids) {
+    const auto* type = ir_context->get_type_mgr()->GetType(id);
+    (void)type;  // Make compilers happy in release mode.
+    // Parameters can't be OpTypeVoid.
+    assert(type && !type->AsVoid() && "Parameter has invalid type");
+  }
+
+  auto* function = FindFunction(ir_context, function_id);
+  assert(function && "|function_id| is invalid");
+
+  auto* old_function_type = GetFunctionType(ir_context, function);
+  assert(old_function_type && "Function has invalid type");
+
+  std::vector<uint32_t> operand_ids = {return_type_id};
+  operand_ids.insert(operand_ids.end(), parameter_type_ids.begin(),
+                     parameter_type_ids.end());
+
+  if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1 &&
+      FindFunctionType(ir_context, operand_ids) == 0) {
+    // We can change |old_function_type| only if it's used once in the module
+    // and we are certain we won't create a duplicate as a result of the change.
+
+    // Update |old_function_type| in-place.
+    opt::Instruction::OperandList operands;
+    for (auto id : operand_ids) {
+      operands.push_back({SPV_OPERAND_TYPE_ID, {id}});
+    }
+
+    old_function_type->SetInOperands(std::move(operands));
+
+    // |operands| may depend on result ids defined below the |old_function_type|
+    // in the module.
+    old_function_type->RemoveFromList();
+    ir_context->AddType(std::unique_ptr<opt::Instruction>(old_function_type));
+    return old_function_type->result_id();
+  } else {
+    // We can't modify the |old_function_type| so we have to either use an
+    // existing one or create a new one.
+    auto type_id = FindOrCreateFunctionType(
+        ir_context, new_function_type_result_id, operand_ids);
+
+    if (type_id != old_function_type->result_id()) {
+      function->DefInst().SetInOperand(1, {type_id});
+
+      // DefUseManager hasn't been updated yet, so if the following condition is
+      // true, then |old_function_type| will have no users when this function
+      // returns. We might as well remove it.
+      if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1) {
+        old_function_type->RemoveFromList();
+        delete old_function_type;
+      }
+    }
+
+    return type_id;
+  }
+}
+
 void AddFunctionType(opt::IRContext* ir_context, uint32_t result_id,
                      const std::vector<uint32_t>& type_ids) {
   assert(result_id != 0 && "Result id can't be 0");
@@ -755,6 +879,11 @@ uint32_t MaybeGetFloatType(opt::IRContext* ir_context, uint32_t width) {
   return ir_context->get_type_mgr()->GetId(&type);
 }
 
+uint32_t MaybeGetBoolType(opt::IRContext* ir_context) {
+  opt::analysis::Bool type;
+  return ir_context->get_type_mgr()->GetId(&type);
+}
+
 uint32_t MaybeGetVectorType(opt::IRContext* ir_context,
                             uint32_t component_type_id,
                             uint32_t element_count) {
@@ -786,15 +915,18 @@ uint32_t MaybeGetStructType(opt::IRContext* ir_context,
   return ir_context->get_type_mgr()->GetId(&type);
 }
 
-uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context,
-                              uint32_t scalar_or_composite_type_id) {
+uint32_t MaybeGetZeroConstant(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context,
+    uint32_t scalar_or_composite_type_id, bool is_irrelevant) {
   const auto* type =
       ir_context->get_type_mgr()->GetType(scalar_or_composite_type_id);
   assert(type && "|scalar_or_composite_type_id| is invalid");
 
   switch (type->kind()) {
     case opt::analysis::Type::kBool:
-      return MaybeGetBoolConstant(ir_context, false);
+      return MaybeGetBoolConstant(ir_context, transformation_context, false,
+                                  is_irrelevant);
     case opt::analysis::Type::kFloat:
     case opt::analysis::Type::kInteger: {
       std::vector<uint32_t> words = {0};
@@ -803,8 +935,8 @@ uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context,
         words.push_back(0);
       }
 
-      return MaybeGetScalarConstant(ir_context, words,
-                                    scalar_or_composite_type_id);
+      return MaybeGetScalarConstant(ir_context, transformation_context, words,
+                                    scalar_or_composite_type_id, is_irrelevant);
     }
     case opt::analysis::Type::kStruct: {
       std::vector<uint32_t> component_ids;
@@ -813,7 +945,16 @@ uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context,
             ir_context->get_type_mgr()->GetId(component_type);
         assert(component_type_id && "Component type is invalid");
 
-        auto component_id = MaybeGetZeroConstant(ir_context, component_type_id);
+        auto component_id =
+            MaybeGetZeroConstant(ir_context, transformation_context,
+                                 component_type_id, is_irrelevant);
+        if (component_id == 0 && is_irrelevant) {
+          // Irrelevant constants can use either relevant or irrelevant
+          // constituents.
+          component_id = MaybeGetZeroConstant(
+              ir_context, transformation_context, component_type_id, false);
+        }
+
         if (component_id == 0) {
           return 0;
         }
@@ -821,8 +962,9 @@ uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context,
         component_ids.push_back(component_id);
       }
 
-      return MaybeGetCompositeConstant(ir_context, component_ids,
-                                       scalar_or_composite_type_id);
+      return MaybeGetCompositeConstant(
+          ir_context, transformation_context, component_ids,
+          scalar_or_composite_type_id, is_irrelevant);
     }
     case opt::analysis::Type::kMatrix:
     case opt::analysis::Type::kVector: {
@@ -833,39 +975,56 @@ uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context,
           ir_context->get_type_mgr()->GetId(component_type);
       assert(component_type_id && "Component type is invalid");
 
-      if (auto component_id =
-              MaybeGetZeroConstant(ir_context, component_type_id)) {
-        auto component_count = type->AsVector()
-                                   ? type->AsVector()->element_count()
-                                   : type->AsMatrix()->element_count();
-        return MaybeGetCompositeConstant(
-            ir_context, std::vector<uint32_t>(component_count, component_id),
-            scalar_or_composite_type_id);
+      auto component_id = MaybeGetZeroConstant(
+          ir_context, transformation_context, component_type_id, is_irrelevant);
+
+      if (component_id == 0 && is_irrelevant) {
+        // Irrelevant constants can use either relevant or irrelevant
+        // constituents.
+        component_id = MaybeGetZeroConstant(ir_context, transformation_context,
+                                            component_type_id, false);
       }
 
-      return 0;
+      if (component_id == 0) {
+        return 0;
+      }
+
+      auto component_count = type->AsVector()
+                                 ? type->AsVector()->element_count()
+                                 : type->AsMatrix()->element_count();
+      return MaybeGetCompositeConstant(
+          ir_context, transformation_context,
+          std::vector<uint32_t>(component_count, component_id),
+          scalar_or_composite_type_id, is_irrelevant);
     }
     case opt::analysis::Type::kArray: {
       auto component_type_id =
           ir_context->get_type_mgr()->GetId(type->AsArray()->element_type());
       assert(component_type_id && "Component type is invalid");
 
-      if (auto component_id =
-              MaybeGetZeroConstant(ir_context, component_type_id)) {
-        auto type_id = ir_context->get_type_mgr()->GetId(type);
-        assert(type_id && "|type| is invalid");
+      auto component_id = MaybeGetZeroConstant(
+          ir_context, transformation_context, component_type_id, is_irrelevant);
 
-        const auto* type_inst = ir_context->get_def_use_mgr()->GetDef(type_id);
-        assert(type_inst && "Array's type id is invalid");
+      if (component_id == 0 && is_irrelevant) {
+        component_id = MaybeGetZeroConstant(ir_context, transformation_context,
+                                            component_type_id, false);
+      }
 
-        return MaybeGetCompositeConstant(
-            ir_context,
-            std::vector<uint32_t>(GetArraySize(*type_inst, ir_context),
-                                  component_id),
-            scalar_or_composite_type_id);
+      if (component_id == 0) {
+        return 0;
       }
 
-      return 0;
+      auto type_id = ir_context->get_type_mgr()->GetId(type);
+      assert(type_id && "|type| is invalid");
+
+      const auto* type_inst = ir_context->get_def_use_mgr()->GetDef(type_id);
+      assert(type_inst && "Array's type id is invalid");
+
+      return MaybeGetCompositeConstant(
+          ir_context, transformation_context,
+          std::vector<uint32_t>(GetArraySize(*type_inst, ir_context),
+                                component_id),
+          scalar_or_composite_type_id, is_irrelevant);
     }
     default:
       assert(false && "Type is not supported");
@@ -873,110 +1032,106 @@ uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context,
   }
 }
 
-uint32_t MaybeGetScalarConstant(opt::IRContext* ir_context,
-                                const std::vector<uint32_t>& words,
-                                uint32_t scalar_type_id) {
+uint32_t MaybeGetScalarConstant(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context,
+    const std::vector<uint32_t>& words, uint32_t scalar_type_id,
+    bool is_irrelevant) {
   const auto* type = ir_context->get_type_mgr()->GetType(scalar_type_id);
   assert(type && "|scalar_type_id| is invalid");
 
   if (const auto* int_type = type->AsInteger()) {
-    return MaybeGetIntegerConstant(ir_context, words, int_type->width(),
-                                   int_type->IsSigned());
+    return MaybeGetIntegerConstant(ir_context, transformation_context, words,
+                                   int_type->width(), int_type->IsSigned(),
+                                   is_irrelevant);
   } else if (const auto* float_type = type->AsFloat()) {
-    return MaybeGetFloatConstant(ir_context, words, float_type->width());
+    return MaybeGetFloatConstant(ir_context, transformation_context, words,
+                                 float_type->width(), is_irrelevant);
   } else {
     assert(type->AsBool() && words.size() == 1 &&
            "|scalar_type_id| doesn't represent a scalar type");
-    return MaybeGetBoolConstant(ir_context, words[0]);
+    return MaybeGetBoolConstant(ir_context, transformation_context, words[0],
+                                is_irrelevant);
   }
 }
 
-uint32_t MaybeGetCompositeConstant(opt::IRContext* ir_context,
-                                   const std::vector<uint32_t>& component_ids,
-                                   uint32_t composite_type_id) {
-  std::vector<const opt::analysis::Constant*> constants;
-  for (auto id : component_ids) {
-    const auto* component_constant =
-        ir_context->get_constant_mgr()->FindDeclaredConstant(id);
-    assert(component_constant && "|id| is invalid");
-
-    constants.push_back(component_constant);
-  }
-
+uint32_t MaybeGetCompositeConstant(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context,
+    const std::vector<uint32_t>& component_ids, uint32_t composite_type_id,
+    bool is_irrelevant) {
   const auto* type = ir_context->get_type_mgr()->GetType(composite_type_id);
-  assert(type && "|composite_type_id| is invalid");
+  (void)type;  // Make compilers happy in release mode.
+  assert(type &&
+         (type->AsArray() || type->AsStruct() || type->AsVector() ||
+          type->AsMatrix()) &&
+         "|composite_type_id| is invalid");
+
+  for (const auto& inst : ir_context->types_values()) {
+    if (inst.opcode() == SpvOpConstantComposite &&
+        inst.type_id() == composite_type_id &&
+        transformation_context.GetFactManager()->IdIsIrrelevant(
+            inst.result_id()) == is_irrelevant &&
+        inst.NumInOperands() == component_ids.size()) {
+      bool is_match = true;
+
+      for (uint32_t i = 0; i < inst.NumInOperands(); ++i) {
+        if (inst.GetSingleWordInOperand(i) != component_ids[i]) {
+          is_match = false;
+          break;
+        }
+      }
 
-  std::unique_ptr<opt::analysis::Constant> composite_constant;
-  switch (type->kind()) {
-    case opt::analysis::Type::kStruct:
-      composite_constant = MakeUnique<opt::analysis::StructConstant>(
-          type->AsStruct(), std::move(constants));
-      break;
-    case opt::analysis::Type::kVector:
-      composite_constant = MakeUnique<opt::analysis::VectorConstant>(
-          type->AsVector(), std::move(constants));
-      break;
-    case opt::analysis::Type::kMatrix:
-      composite_constant = MakeUnique<opt::analysis::MatrixConstant>(
-          type->AsMatrix(), std::move(constants));
-      break;
-    case opt::analysis::Type::kArray:
-      composite_constant = MakeUnique<opt::analysis::ArrayConstant>(
-          type->AsArray(), std::move(constants));
-      break;
-    default:
-      assert(false &&
-             "|composite_type_id| is not a result id of a composite type");
-      return 0;
+      if (is_match) {
+        return inst.result_id();
+      }
+    }
   }
 
-  return ir_context->get_constant_mgr()->FindDeclaredConstant(
-      composite_constant.get(), composite_type_id);
+  return 0;
 }
 
-uint32_t MaybeGetIntegerConstant(opt::IRContext* ir_context,
-                                 const std::vector<uint32_t>& words,
-                                 uint32_t width, bool is_signed) {
-  auto type_id = MaybeGetIntegerType(ir_context, width, is_signed);
-  if (!type_id) {
-    return 0;
+uint32_t MaybeGetIntegerConstant(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context,
+    const std::vector<uint32_t>& words, uint32_t width, bool is_signed,
+    bool is_irrelevant) {
+  if (auto type_id = MaybeGetIntegerType(ir_context, width, is_signed)) {
+    return MaybeGetOpConstant(ir_context, transformation_context, words,
+                              type_id, is_irrelevant);
   }
 
-  const auto* type = ir_context->get_type_mgr()->GetType(type_id);
-  assert(type && "|type_id| is invalid");
-
-  opt::analysis::IntConstant constant(type->AsInteger(), words);
-  return ir_context->get_constant_mgr()->FindDeclaredConstant(&constant,
-                                                              type_id);
+  return 0;
 }
 
-uint32_t MaybeGetFloatConstant(opt::IRContext* ir_context,
-                               const std::vector<uint32_t>& words,
-                               uint32_t width) {
-  auto type_id = MaybeGetFloatType(ir_context, width);
-  if (!type_id) {
-    return 0;
+uint32_t MaybeGetFloatConstant(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context,
+    const std::vector<uint32_t>& words, uint32_t width, bool is_irrelevant) {
+  if (auto type_id = MaybeGetFloatType(ir_context, width)) {
+    return MaybeGetOpConstant(ir_context, transformation_context, words,
+                              type_id, is_irrelevant);
   }
 
-  const auto* type = ir_context->get_type_mgr()->GetType(type_id);
-  assert(type && "|type_id| is invalid");
-
-  opt::analysis::FloatConstant constant(type->AsFloat(), words);
-  return ir_context->get_constant_mgr()->FindDeclaredConstant(&constant,
-                                                              type_id);
+  return 0;
 }
 
-uint32_t MaybeGetBoolConstant(opt::IRContext* context, bool value) {
-  opt::analysis::Bool bool_type;
-  auto registered_bool_type =
-      context->get_type_mgr()->GetRegisteredType(&bool_type);
-  if (!registered_bool_type) {
-    return 0;
+uint32_t MaybeGetBoolConstant(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context, bool value,
+    bool is_irrelevant) {
+  if (auto type_id = MaybeGetBoolType(ir_context)) {
+    for (const auto& inst : ir_context->types_values()) {
+      if (inst.opcode() == (value ? SpvOpConstantTrue : SpvOpConstantFalse) &&
+          inst.type_id() == type_id &&
+          transformation_context.GetFactManager()->IdIsIrrelevant(
+              inst.result_id()) == is_irrelevant) {
+        return inst.result_id();
+      }
+    }
   }
-  opt::analysis::BoolConstant bool_constant(registered_bool_type->AsBool(),
-                                            value);
-  return context->get_constant_mgr()->FindDeclaredConstant(
-      &bool_constant, context->get_type_mgr()->GetId(&bool_type));
+
+  return 0;
 }
 
 void AddIntegerType(opt::IRContext* ir_context, uint32_t result_id,

+ 80 - 26
3rdparty/spirv-tools/source/fuzz/fuzzer_util.h

@@ -53,15 +53,17 @@ bool PhiIdsOkForNewEdge(
     opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
     const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids);
 
-// Requires that a boolean constant with value |condition_value| is available,
-// that PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) holds, and that
-// bb_from ends with "OpBranch %some_block".  Turns OpBranch into
-// "OpBranchConditional |condition_value| ...", such that control will branch
-// to %some_block, with |bb_to| being the unreachable alternative.  Updates
-// OpPhi instructions in |bb_to| using |phi_ids| so that the new edge is valid.
+// Requires that |bool_id| is a valid result id of either OpConstantTrue or
+// OpConstantFalse, that PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids)
+// holds, and that bb_from ends with "OpBranch %some_block".  Turns OpBranch
+// into "OpBranchConditional |condition_value| ...", such that control will
+// branch to %some_block, with |bb_to| being the unreachable alternative.
+// Updates OpPhi instructions in |bb_to| using |phi_ids| so that the new edge is
+// valid. |condition_value| above is equal to |true| if |bool_id| is a result id
+// of an OpConstantTrue instruction.
 void AddUnreachableEdgeAndUpdateOpPhis(
     opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
-    bool condition_value,
+    uint32_t bool_id,
     const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids);
 
 // Returns true if and only if |loop_header_id| is a loop header and
@@ -91,7 +93,11 @@ bool CanInsertOpcodeBeforeInstruction(
     SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block);
 
 // Determines whether it is OK to make a synonym of |inst|.
-bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst);
+// |transformation_context| is used to verify that the result id of |inst|
+// does not participate in IdIsIrrelevant fact.
+bool CanMakeSynonymOf(opt::IRContext* ir_context,
+                      const TransformationContext& transformation_context,
+                      opt::Instruction* inst);
 
 // Determines whether the given type is a composite; that is: an array, matrix,
 // struct or vector.
@@ -275,6 +281,29 @@ bool IsPermutationOfRange(const std::vector<uint32_t>& arr, uint32_t lo,
 std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
                                              uint32_t function_id);
 
+// Returns all OpFunctionCall instructions that call a function with result id
+// |function_id|.
+std::vector<opt::Instruction*> GetCallers(opt::IRContext* ir_context,
+                                          uint32_t function_id);
+
+// Returns a function that contains OpFunctionParameter instruction with result
+// id |param_id|. Returns nullptr if the module has no such function.
+opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context,
+                                          uint32_t param_id);
+
+// Changes the type of function |function_id| so that its return type is
+// |return_type_id| and its parameters' types are |parameter_type_ids|. If a
+// suitable function type already exists in the module, it is used, otherwise
+// |new_function_type_result_id| is used as the result id of a suitable new
+// function type instruction. If the old type of the function doesn't have any
+// more users, it is removed from the module. Returns the result id of the
+// OpTypeFunction instruction that is used as a type of the function with
+// |function_id|.
+uint32_t UpdateFunctionType(opt::IRContext* ir_context, uint32_t function_id,
+                            uint32_t new_function_type_result_id,
+                            uint32_t return_type_id,
+                            const std::vector<uint32_t>& parameter_type_ids);
+
 // Creates new OpTypeFunction instruction in the module. |type_ids| may not be
 // empty. It may not contain result ids of OpTypeFunction instructions.
 // |type_ids[i]| may not be a result id of OpTypeVoid instruction for |i >= 1|.
@@ -302,6 +331,10 @@ uint32_t MaybeGetIntegerType(opt::IRContext* ir_context, uint32_t width,
 // otherwise.
 uint32_t MaybeGetFloatType(opt::IRContext* ir_context, uint32_t width);
 
+// Returns a result id of an OpTypeBool instruction if present. Returns 0
+// otherwise.
+uint32_t MaybeGetBoolType(opt::IRContext* ir_context);
+
 // Returns a result id of an OpTypeVector instruction if present. Returns 0
 // otherwise. |component_type_id| must be a valid result id of an OpTypeInt,
 // OpTypeFloat or OpTypeBool instruction in the module. |element_count| must be
@@ -323,39 +356,60 @@ uint32_t MaybeGetStructType(opt::IRContext* ir_context,
 //   Every component of the composite constant is looked up by calling this
 //   function with the type id of that component.
 // Returns 0 if no such instruction is present in the module.
-uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context,
-                              uint32_t scalar_or_composite_type_id);
+// The returned id either participates in IdIsIrrelevant fact or not, depending
+// on the |is_irrelevant| parameter.
+uint32_t MaybeGetZeroConstant(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context,
+    uint32_t scalar_or_composite_type_id, bool is_irrelevant);
 
 // Returns the result id of an OpConstant instruction. |scalar_type_id| must be
 // a result id of a scalar type (i.e. int, float or bool). Returns 0 if no such
-// instruction is present in the module.
-uint32_t MaybeGetScalarConstant(opt::IRContext* ir_context,
-                                const std::vector<uint32_t>& words,
-                                uint32_t scalar_type_id);
+// instruction is present in the module. The returned id either participates in
+// IdIsIrrelevant fact or not, depending on the |is_irrelevant| parameter.
+uint32_t MaybeGetScalarConstant(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context,
+    const std::vector<uint32_t>& words, uint32_t scalar_type_id,
+    bool is_irrelevant);
 
 // Returns the result id of an OpConstantComposite instruction.
 // |composite_type_id| must be a result id of a composite type (i.e. vector,
 // matrix, struct or array). Returns 0 if no such instruction is present in the
-// module.
-uint32_t MaybeGetCompositeConstant(opt::IRContext* ir_context,
-                                   const std::vector<uint32_t>& component_ids,
-                                   uint32_t composite_type_id);
+// module. The returned id either participates in IdIsIrrelevant fact or not,
+// depending on the |is_irrelevant| parameter.
+uint32_t MaybeGetCompositeConstant(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context,
+    const std::vector<uint32_t>& component_ids, uint32_t composite_type_id,
+    bool is_irrelevant);
 
 // Returns the result id of an OpConstant instruction of integral type.
 // Returns 0 if no such instruction or type is present in the module.
-uint32_t MaybeGetIntegerConstant(opt::IRContext* ir_context,
-                                 const std::vector<uint32_t>& words,
-                                 uint32_t width, bool is_signed);
+// The returned id either participates in IdIsIrrelevant fact or not, depending
+// on the |is_irrelevant| parameter.
+uint32_t MaybeGetIntegerConstant(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context,
+    const std::vector<uint32_t>& words, uint32_t width, bool is_signed,
+    bool is_irrelevant);
 
 // Returns the result id of an OpConstant instruction of floating-point type.
 // Returns 0 if no such instruction or type is present in the module.
-uint32_t MaybeGetFloatConstant(opt::IRContext* ir_context,
-                               const std::vector<uint32_t>& words,
-                               uint32_t width);
+// The returned id either participates in IdIsIrrelevant fact or not, depending
+// on the |is_irrelevant| parameter.
+uint32_t MaybeGetFloatConstant(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context,
+    const std::vector<uint32_t>& words, uint32_t width, bool is_irrelevant);
 
 // Returns the id of a boolean constant with value |value| if it exists in the
-// module, or 0 otherwise.
-uint32_t MaybeGetBoolConstant(opt::IRContext* context, bool value);
+// module, or 0 otherwise. The returned id either participates in IdIsIrrelevant
+// fact or not, depending on the |is_irrelevant| parameter.
+uint32_t MaybeGetBoolConstant(
+    opt::IRContext* context,
+    const TransformationContext& transformation_context, bool value,
+    bool is_irrelevant);
 
 // Creates a new OpTypeInt instruction in the module. Updates module's id bound
 // to accommodate for |result_id|.

+ 101 - 0
3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto

@@ -170,6 +170,7 @@ message Fact {
     FactFunctionIsLivesafe function_is_livesafe_fact = 4;
     FactPointeeValueIsIrrelevant pointee_value_is_irrelevant_fact = 5;
     FactIdEquation id_equation_fact = 6;
+    FactIdIsIrrelevant id_is_irrelevant = 7;
   }
 }
 
@@ -182,6 +183,7 @@ message FactBlockIsDead {
   // can be made to this block.
 
   uint32 block_id = 1;
+
 }
 
 message FactConstantUniform {
@@ -222,6 +224,7 @@ message FactFunctionIsLivesafe {
   // functions.
 
   uint32 function_id = 1;
+
 }
 
 message FactIdEquation {
@@ -249,6 +252,18 @@ message FactIdEquation {
 
 }
 
+message FactIdIsIrrelevant {
+
+  // Records a fact that |result_id| is irrelevant (i.e. it's usage doesn't
+  // change the semantics of the module). This implies that a use of this id
+  // can later be replaced with some other id of the same type, or the
+  // definition of |result_id| can be changed so that it yields a different value.
+
+  // An irrelevant id.
+  uint32 result_id = 1;
+
+}
+
 message FactPointeeValueIsIrrelevant {
 
   // Records the fact that value of the data pointed to by a pointer id does
@@ -258,6 +273,7 @@ message FactPointeeValueIsIrrelevant {
 
   // A result id of pointer type
   uint32 pointer_id = 1;
+
 }
 
 message AccessChainClampingInfo {
@@ -386,6 +402,10 @@ message Transformation {
     TransformationReplaceParameterWithGlobal replace_parameter_with_global = 55;
     TransformationRecordSynonymousConstants record_synonymous_constants = 56;
     TransformationAddSynonym add_synonym = 57;
+    TransformationAddRelaxedDecoration add_relaxed_decoration = 58;
+    TransformationReplaceParamsWithStruct replace_params_with_struct = 59;
+    TransformationReplaceCopyObjectWithStoreLoad replace_copy_object_with_store_load = 60;
+    TransformationReplaceCopyMemoryWithLoadStore replace_copy_memory_with_load_store = 61;
     // Add additional option using the next available number.
   }
 }
@@ -415,15 +435,21 @@ message TransformationAddConstantBoolean {
 
   // Supports adding the constants true and false to a module, which may be
   // necessary in order to enable other transformations if they are not present.
+  // Also, creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is true.
 
   uint32 fresh_id = 1;
   bool is_true = 2;
 
+  // If the constant should be marked as irrelevant.
+  bool is_irrelevant = 3;
+
 }
 
 message TransformationAddConstantComposite {
 
   // Adds a constant of the given composite type to the module.
+  // Also, creates an IdIsIrrelevant fact about |fresh_id| if
+  // |is_irrelevant| is true.
 
   // Fresh id for the composite
   uint32 fresh_id = 1;
@@ -434,6 +460,9 @@ message TransformationAddConstantComposite {
   // Constituent ids for the composite
   repeated uint32 constituent_id = 3;
 
+  // If the constant should be marked as irrelevant.
+  bool is_irrelevant = 4;
+
 }
 
 message TransformationAddConstantNull {
@@ -451,6 +480,8 @@ message TransformationAddConstantNull {
 message TransformationAddConstantScalar {
 
   // Adds a constant of the given scalar type.
+  // Also, creates an IdIsIrrelevant fact about
+  // |fresh_id| if |is_irrelevant| is true.
 
   // Id for the constant
   uint32 fresh_id = 1;
@@ -461,6 +492,9 @@ message TransformationAddConstantScalar {
   // Value of the constant
   repeated uint32 word = 3;
 
+  // If the constant should be marked as irrelevant.
+  bool is_irrelevant = 4;
+
 }
 
 message TransformationAddCopyMemory {
@@ -687,6 +721,15 @@ message TransformationAddParameter {
 
 }
 
+message TransformationAddRelaxedDecoration {
+
+  // Applies OpDecorate RelaxedPrecision to the given result id
+
+  // Result id to be decorated
+  uint32 result_id = 1;
+
+}
+
 message TransformationAddSpecConstantOp {
 
   // Adds OpSpecConstantOp into the module.
@@ -1233,6 +1276,39 @@ message TransformationReplaceConstantWithUniform {
 
 }
 
+message TransformationReplaceCopyMemoryWithLoadStore {
+
+   // A transformation that replaces instructions OpCopyMemory with loading
+   // the source variable to an intermediate value and storing this value into the
+   // target variable of the original OpCopyMemory instruction.
+
+   // The intermediate value.
+   uint32 fresh_id = 1;
+
+   // The instruction descriptor to OpCopyMemory. It is necessary, because
+   // OpCopyMemory doesn't have a result id.
+   InstructionDescriptor copy_memory_instruction_descriptor = 2;
+}
+
+message TransformationReplaceCopyObjectWithStoreLoad {
+
+  // A transformation that replaces instruction OpCopyObject with
+  // storing into a new variable and immediately loading from this
+  // variable to |result_id| of the original OpCopyObject instruction.
+
+  // The result id of initial OpCopyObject instruction
+  uint32 copy_object_result_id = 1;
+
+  // A fresh id for the variable to be stored to.
+  uint32 fresh_variable_id = 2;
+
+  // The variable storage class (Function or Private).
+  uint32 variable_storage_class = 3;
+
+  // Constant to initialize the variable with.
+  uint32 variable_initializer_id = 4;
+}
+
 message TransformationReplaceIdWithSynonym {
 
   // Replaces a use of an id with an id that is known to be synonymous, e.g.
@@ -1273,6 +1349,31 @@ message TransformationReplaceLinearAlgebraInstruction {
 
 }
 
+message TransformationReplaceParamsWithStruct {
+
+  // Replaces parameters of the function with a struct containing
+  // values of those parameters.
+
+  // Result ids of parameters to replace.
+  repeated uint32 parameter_id = 1;
+
+  // Fresh id for a new function type. This might be unused if the required type
+  // already exists in the module or if we can change the old type.
+  uint32 fresh_function_type_id = 2;
+
+  // Fresh id for a new struct function parameter to be used as a replacement.
+  uint32 fresh_parameter_id = 3;
+
+  // Fresh ids for struct objects containing values of replaced parameters.
+  // This map contains a fresh id for at least every result id of a relevant
+  // OpFunctionCall instruction.
+  //
+  // While maps are not fully deterministic, the way this map is used does not
+  // exhibit nondeterminism. Change to repeated Uint32Pair if this changes.
+  map<uint32, uint32> caller_id_to_fresh_composite_id = 4;
+
+}
+
 message TransformationSetFunctionControl {
 
   // A transformation that sets the function control operand of an OpFunction

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

@@ -33,6 +33,7 @@
 #include "source/fuzz/transformation_add_local_variable.h"
 #include "source/fuzz/transformation_add_no_contraction_decoration.h"
 #include "source/fuzz/transformation_add_parameter.h"
+#include "source/fuzz/transformation_add_relaxed_decoration.h"
 #include "source/fuzz/transformation_add_spec_constant_op.h"
 #include "source/fuzz/transformation_add_synonym.h"
 #include "source/fuzz/transformation_add_type_array.h"
@@ -61,9 +62,12 @@
 #include "source/fuzz/transformation_record_synonymous_constants.h"
 #include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
 #include "source/fuzz/transformation_replace_constant_with_uniform.h"
+#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h"
+#include "source/fuzz/transformation_replace_copy_object_with_store_load.h"
 #include "source/fuzz/transformation_replace_id_with_synonym.h"
 #include "source/fuzz/transformation_replace_linear_algebra_instruction.h"
 #include "source/fuzz/transformation_replace_parameter_with_global.h"
+#include "source/fuzz/transformation_replace_params_with_struct.h"
 #include "source/fuzz/transformation_set_function_control.h"
 #include "source/fuzz/transformation_set_loop_control.h"
 #include "source/fuzz/transformation_set_memory_operands_mask.h"
@@ -128,6 +132,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
           message.add_no_contraction_decoration());
     case protobufs::Transformation::TransformationCase::kAddParameter:
       return MakeUnique<TransformationAddParameter>(message.add_parameter());
+    case protobufs::Transformation::TransformationCase::kAddRelaxedDecoration:
+      return MakeUnique<TransformationAddRelaxedDecoration>(
+          message.add_relaxed_decoration());
     case protobufs::Transformation::TransformationCase::kAddSpecConstantOp:
       return MakeUnique<TransformationAddSpecConstantOp>(
           message.add_spec_constant_op());
@@ -211,6 +218,14 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
         kReplaceConstantWithUniform:
       return MakeUnique<TransformationReplaceConstantWithUniform>(
           message.replace_constant_with_uniform());
+    case protobufs::Transformation::TransformationCase::
+        kReplaceCopyMemoryWithLoadStore:
+      return MakeUnique<TransformationReplaceCopyMemoryWithLoadStore>(
+          message.replace_copy_memory_with_load_store());
+    case protobufs::Transformation::TransformationCase::
+        kReplaceCopyObjectWithStoreLoad:
+      return MakeUnique<TransformationReplaceCopyObjectWithStoreLoad>(
+          message.replace_copy_object_with_store_load());
     case protobufs::Transformation::TransformationCase::kReplaceIdWithSynonym:
       return MakeUnique<TransformationReplaceIdWithSynonym>(
           message.replace_id_with_synonym());
@@ -218,6 +233,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
         kReplaceLinearAlgebraInstruction:
       return MakeUnique<TransformationReplaceLinearAlgebraInstruction>(
           message.replace_linear_algebra_instruction());
+    case protobufs::Transformation::TransformationCase::
+        kReplaceParamsWithStruct:
+      return MakeUnique<TransformationReplaceParamsWithStruct>(
+          message.replace_params_with_struct());
     case protobufs::Transformation::TransformationCase::kSetFunctionControl:
       return MakeUnique<TransformationSetFunctionControl>(
           message.set_function_control());

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

@@ -25,34 +25,36 @@ TransformationAddConstantBoolean::TransformationAddConstantBoolean(
     : message_(message) {}
 
 TransformationAddConstantBoolean::TransformationAddConstantBoolean(
-    uint32_t fresh_id, bool is_true) {
+    uint32_t fresh_id, bool is_true, bool is_irrelevant) {
   message_.set_fresh_id(fresh_id);
   message_.set_is_true(is_true);
+  message_.set_is_irrelevant(is_irrelevant);
 }
 
 bool TransformationAddConstantBoolean::IsApplicable(
     opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
-  opt::analysis::Bool bool_type;
-  if (!ir_context->get_type_mgr()->GetId(&bool_type)) {
-    // No OpTypeBool is present.
-    return false;
-  }
-  return fuzzerutil::IsFreshId(ir_context, message_.fresh_id());
+  return fuzzerutil::MaybeGetBoolType(ir_context) != 0 &&
+         fuzzerutil::IsFreshId(ir_context, message_.fresh_id());
 }
 
 void TransformationAddConstantBoolean::Apply(
-    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
-  opt::analysis::Bool bool_type;
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
   // Add the boolean constant to the module, ensuring the module's id bound is
   // high enough.
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
   ir_context->module()->AddGlobalValue(
       message_.is_true() ? SpvOpConstantTrue : SpvOpConstantFalse,
-      message_.fresh_id(), ir_context->get_type_mgr()->GetId(&bool_type));
+      message_.fresh_id(), fuzzerutil::MaybeGetBoolType(ir_context));
   // We have added an instruction to the module, so need to be careful about the
   // validity of existing analyses.
   ir_context->InvalidateAnalysesExceptFor(
       opt::IRContext::Analysis::kAnalysisNone);
+
+  if (message_.is_irrelevant()) {
+    transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
+        message_.fresh_id());
+  }
 }
 
 protobufs::Transformation TransformationAddConstantBoolean::ToMessage() const {

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

@@ -28,7 +28,8 @@ class TransformationAddConstantBoolean : public Transformation {
   explicit TransformationAddConstantBoolean(
       const protobufs::TransformationAddConstantBoolean& message);
 
-  TransformationAddConstantBoolean(uint32_t fresh_id, bool is_true);
+  TransformationAddConstantBoolean(uint32_t fresh_id, bool is_true,
+                                   bool is_irrelevant);
 
   // - |message_.fresh_id| must not be used by the module.
   // - The module must already contain OpTypeBool.
@@ -38,6 +39,8 @@ class TransformationAddConstantBoolean : public Transformation {
 
   // - Adds OpConstantTrue (OpConstantFalse) to the module with id
   //   |message_.fresh_id| if |message_.is_true| holds (does not hold).
+  // - Also, creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant|
+  //   is true.
   void Apply(opt::IRContext* ir_context,
              TransformationContext* transformation_context) const override;
 

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

@@ -28,9 +28,10 @@ TransformationAddConstantComposite::TransformationAddConstantComposite(
 
 TransformationAddConstantComposite::TransformationAddConstantComposite(
     uint32_t fresh_id, uint32_t type_id,
-    const std::vector<uint32_t>& constituent_ids) {
+    const std::vector<uint32_t>& constituent_ids, bool is_irrelevant) {
   message_.set_fresh_id(fresh_id);
   message_.set_type_id(type_id);
+  message_.set_is_irrelevant(is_irrelevant);
   for (auto constituent_id : constituent_ids) {
     message_.add_constituent_id(constituent_id);
   }
@@ -104,7 +105,8 @@ bool TransformationAddConstantComposite::IsApplicable(
 }
 
 void TransformationAddConstantComposite::Apply(
-    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
   opt::Instruction::OperandList in_operands;
   for (auto constituent_id : message_.constituent_id()) {
     in_operands.push_back({SPV_OPERAND_TYPE_ID, {constituent_id}});
@@ -117,6 +119,11 @@ void TransformationAddConstantComposite::Apply(
   // validity of existing analyses.
   ir_context->InvalidateAnalysesExceptFor(
       opt::IRContext::Analysis::kAnalysisNone);
+
+  if (message_.is_irrelevant()) {
+    transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
+        message_.fresh_id());
+  }
 }
 
 protobufs::Transformation TransformationAddConstantComposite::ToMessage()

+ 6 - 4
3rdparty/spirv-tools/source/fuzz/transformation_add_constant_composite.h

@@ -32,7 +32,7 @@ class TransformationAddConstantComposite : public Transformation {
 
   TransformationAddConstantComposite(
       uint32_t fresh_id, uint32_t type_id,
-      const std::vector<uint32_t>& constituent_ids);
+      const std::vector<uint32_t>& constituent_ids, bool is_irrelevant);
 
   // - |message_.fresh_id| must be a fresh id
   // - |message_.type_id| must be the id of a composite type
@@ -42,9 +42,11 @@ class TransformationAddConstantComposite : public Transformation {
       opt::IRContext* ir_context,
       const TransformationContext& transformation_context) const override;
 
-  // Adds an OpConstantComposite instruction defining a constant of type
-  // |message_.type_id|, using |message_.constituent_id| as constituents, with
-  // result id |message_.fresh_id|.
+  // - Adds an OpConstantComposite instruction defining a constant of type
+  //   |message_.type_id|, using |message_.constituent_id| as constituents, with
+  //   result id |message_.fresh_id|.
+  // - Creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is
+  //   true.
   void Apply(opt::IRContext* ir_context,
              TransformationContext* transformation_context) const override;
 

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

@@ -24,9 +24,11 @@ TransformationAddConstantScalar::TransformationAddConstantScalar(
     : message_(message) {}
 
 TransformationAddConstantScalar::TransformationAddConstantScalar(
-    uint32_t fresh_id, uint32_t type_id, std::vector<uint32_t> words) {
+    uint32_t fresh_id, uint32_t type_id, const std::vector<uint32_t>& words,
+    bool is_irrelevant) {
   message_.set_fresh_id(fresh_id);
   message_.set_type_id(type_id);
+  message_.set_is_irrelevant(is_irrelevant);
   for (auto word : words) {
     message_.add_word(word);
   }
@@ -60,7 +62,8 @@ bool TransformationAddConstantScalar::IsApplicable(
 }
 
 void TransformationAddConstantScalar::Apply(
-    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
   opt::Instruction::OperandList operand_list;
   for (auto word : message_.word()) {
     operand_list.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {word}});
@@ -75,6 +78,11 @@ void TransformationAddConstantScalar::Apply(
   // validity of existing analyses.
   ir_context->InvalidateAnalysesExceptFor(
       opt::IRContext::Analysis::kAnalysisNone);
+
+  if (message_.is_irrelevant()) {
+    transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
+        message_.fresh_id());
+  }
 }
 
 protobufs::Transformation TransformationAddConstantScalar::ToMessage() const {

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

@@ -31,7 +31,8 @@ class TransformationAddConstantScalar : public Transformation {
       const protobufs::TransformationAddConstantScalar& message);
 
   TransformationAddConstantScalar(uint32_t fresh_id, uint32_t type_id,
-                                  std::vector<uint32_t> words);
+                                  const std::vector<uint32_t>& words,
+                                  bool is_irrelevant);
 
   // - |message_.fresh_id| must not be used by the module
   // - |message_.type_id| must be the id of a floating-point or integer type
@@ -42,6 +43,7 @@ class TransformationAddConstantScalar : public Transformation {
       const TransformationContext& transformation_context) const override;
 
   // Adds a new OpConstant instruction with the given type and words.
+  // Creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is true.
   void Apply(opt::IRContext* ir_context,
              TransformationContext* transformation_context) const override;
 

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

@@ -32,7 +32,8 @@ TransformationAddDeadBlock::TransformationAddDeadBlock(uint32_t fresh_id,
 }
 
 bool TransformationAddDeadBlock::IsApplicable(
-    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   // The new block's id must be fresh.
   if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
@@ -40,8 +41,8 @@ bool TransformationAddDeadBlock::IsApplicable(
 
   // First, we check that a constant with the same value as
   // |message_.condition_value| is present.
-  if (!fuzzerutil::MaybeGetBoolConstant(ir_context,
-                                        message_.condition_value())) {
+  if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
+                                        message_.condition_value(), false)) {
     // The required constant is not present, so the transformation cannot be
     // applied.
     return false;
@@ -92,8 +93,8 @@ void TransformationAddDeadBlock::Apply(
       existing_block->terminator()->GetSingleWordInOperand(0);
 
   // Get the id of the boolean value that will be used as the branch condition.
-  auto bool_id =
-      fuzzerutil::MaybeGetBoolConstant(ir_context, message_.condition_value());
+  auto bool_id = fuzzerutil::MaybeGetBoolConstant(
+      ir_context, *transformation_context, message_.condition_value(), false);
 
   // Make a new block that unconditionally branches to the original successor
   // block.

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

@@ -112,8 +112,9 @@ bool TransformationAddDeadBreak::IsApplicable(
     const TransformationContext& transformation_context) const {
   // First, we check that a constant with the same value as
   // |message_.break_condition_value| is present.
-  if (!fuzzerutil::MaybeGetBoolConstant(ir_context,
-                                        message_.break_condition_value())) {
+  if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
+                                        message_.break_condition_value(),
+                                        false)) {
     // The required constant is not present, so the transformation cannot be
     // applied.
     return false;
@@ -179,14 +180,15 @@ bool TransformationAddDeadBreak::IsApplicable(
   // the validator is complete with respect to checking structured control flow
   // rules.
   auto cloned_context = fuzzerutil::CloneIRContext(ir_context);
-  ApplyImpl(cloned_context.get());
+  ApplyImpl(cloned_context.get(), transformation_context);
   return fuzzerutil::IsValid(cloned_context.get(),
                              transformation_context.GetValidatorOptions());
 }
 
 void TransformationAddDeadBreak::Apply(
-    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
-  ApplyImpl(ir_context);
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
+  ApplyImpl(ir_context, *transformation_context);
   // Invalidate all analyses
   ir_context->InvalidateAnalysesExceptFor(
       opt::IRContext::Analysis::kAnalysisNone);
@@ -199,11 +201,14 @@ protobufs::Transformation TransformationAddDeadBreak::ToMessage() const {
 }
 
 void TransformationAddDeadBreak::ApplyImpl(
-    spvtools::opt::IRContext* ir_context) const {
+    spvtools::opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
       ir_context, ir_context->cfg()->block(message_.from_block()),
       ir_context->cfg()->block(message_.to_block()),
-      message_.break_condition_value(), message_.phi_id());
+      fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
+                                       message_.break_condition_value(), false),
+      message_.phi_id());
 }
 
 }  // namespace fuzz

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

@@ -75,7 +75,8 @@ class TransformationAddDeadBreak : public Transformation {
   // module.  This is only invoked by 'IsApplicable' after certain basic
   // applicability checks have been made, ensuring that the invocation of this
   // method is legal.
-  void ApplyImpl(opt::IRContext* ir_context) const;
+  void ApplyImpl(opt::IRContext* ir_context,
+                 const TransformationContext& transformation_context) const;
 
   protobufs::TransformationAddDeadBreak message_;
 };

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

@@ -38,8 +38,9 @@ bool TransformationAddDeadContinue::IsApplicable(
     const TransformationContext& transformation_context) const {
   // First, we check that a constant with the same value as
   // |message_.continue_condition_value| is present.
-  if (!fuzzerutil::MaybeGetBoolConstant(ir_context,
-                                        message_.continue_condition_value())) {
+  if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
+                                        message_.continue_condition_value(),
+                                        false)) {
     // The required constant is not present, so the transformation cannot be
     // applied.
     return false;
@@ -119,14 +120,15 @@ bool TransformationAddDeadContinue::IsApplicable(
   // the validator is complete with respect to checking structured control flow
   // rules.
   auto cloned_context = fuzzerutil::CloneIRContext(ir_context);
-  ApplyImpl(cloned_context.get());
+  ApplyImpl(cloned_context.get(), transformation_context);
   return fuzzerutil::IsValid(cloned_context.get(),
                              transformation_context.GetValidatorOptions());
 }
 
 void TransformationAddDeadContinue::Apply(
-    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
-  ApplyImpl(ir_context);
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
+  ApplyImpl(ir_context, *transformation_context);
   // Invalidate all analyses
   ir_context->InvalidateAnalysesExceptFor(
       opt::IRContext::Analysis::kAnalysisNone);
@@ -139,7 +141,8 @@ protobufs::Transformation TransformationAddDeadContinue::ToMessage() const {
 }
 
 void TransformationAddDeadContinue::ApplyImpl(
-    spvtools::opt::IRContext* ir_context) const {
+    spvtools::opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   auto bb_from = ir_context->cfg()->block(message_.from_block());
   auto continue_block =
       bb_from->IsLoopHeader()
@@ -149,7 +152,10 @@ void TransformationAddDeadContinue::ApplyImpl(
   assert(continue_block && "message_.from_block must be in a loop.");
   fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
       ir_context, bb_from, ir_context->cfg()->block(continue_block),
-      message_.continue_condition_value(), message_.phi_id());
+      fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
+                                       message_.continue_condition_value(),
+                                       false),
+      message_.phi_id());
 }
 
 }  // namespace fuzz

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

@@ -72,7 +72,8 @@ class TransformationAddDeadContinue : public Transformation {
   // module.  This is only invoked by 'IsApplicable' after certain basic
   // applicability checks have been made, ensuring that the invocation of this
   // method is legal.
-  void ApplyImpl(opt::IRContext* ir_context) const;
+  void ApplyImpl(opt::IRContext* ir_context,
+                 const TransformationContext& transformation_context) const;
 
   protobufs::TransformationAddDeadContinue message_;
 };

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

@@ -66,7 +66,8 @@ bool TransformationAddParameter::IsApplicable(
 }
 
 void TransformationAddParameter::Apply(
-    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
   // Find the function that will be transformed
   auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id());
   assert(function && "Can't find the function");
@@ -85,6 +86,11 @@ void TransformationAddParameter::Apply(
   // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
   //  Add an PointeeValueIsIrrelevant fact if the parameter is a pointer.
 
+  // Mark new parameter as irrelevant so that we can replace its use with some
+  // other id.
+  transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
+      message_.parameter_fresh_id());
+
   // Fix all OpFunctionCall instructions.
   ir_context->get_def_use_mgr()->ForEachUser(
       &function->DefInst(), [this](opt::Instruction* call) {
@@ -93,9 +99,6 @@ void TransformationAddParameter::Apply(
           return;
         }
 
-        // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
-        //  it would be good to mark this usage of |id| as irrelevant, so that
-        //  we can perform some interesting transformations on it later.
         call->AddOperand({SPV_OPERAND_TYPE_ID, {message_.initializer_id()}});
       });
 

+ 146 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.cpp

@@ -0,0 +1,146 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_relaxed_decoration.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddRelaxedDecoration::TransformationAddRelaxedDecoration(
+    const spvtools::fuzz::protobufs::TransformationAddRelaxedDecoration&
+        message)
+    : message_(message) {}
+
+TransformationAddRelaxedDecoration::TransformationAddRelaxedDecoration(
+    uint32_t result_id) {
+  message_.set_result_id(result_id);
+}
+
+bool TransformationAddRelaxedDecoration::IsApplicable(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
+  // |message_.result_id| must be the id of an instruction.
+  auto instr = ir_context->get_def_use_mgr()->GetDef(message_.result_id());
+  if (!instr) {
+    return false;
+  }
+  opt::BasicBlock* cur_block = ir_context->get_instr_block(instr);
+  // The instruction must have a block.
+  if (cur_block == nullptr) {
+    return false;
+  }
+  // |cur_block| must be a dead block.
+  if (!(transformation_context.GetFactManager()->BlockIsDead(
+          cur_block->id()))) {
+    return false;
+  }
+  // The instruction must be numeric.
+  return IsNumeric(instr->opcode());
+}
+
+void TransformationAddRelaxedDecoration::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  // Add a RelaxedPrecision decoration targeting |message_.result_id|.
+  ir_context->get_decoration_mgr()->AddDecoration(
+      message_.result_id(), SpvDecorationRelaxedPrecision);
+}
+
+protobufs::Transformation TransformationAddRelaxedDecoration::ToMessage()
+    const {
+  protobufs::Transformation result;
+  *result.mutable_add_relaxed_decoration() = message_;
+  return result;
+}
+
+bool TransformationAddRelaxedDecoration::IsNumeric(uint32_t opcode) {
+  switch (opcode) {
+    case SpvOpConvertFToU:
+    case SpvOpConvertFToS:
+    case SpvOpConvertSToF:
+    case SpvOpConvertUToF:
+    case SpvOpUConvert:
+    case SpvOpSConvert:
+    case SpvOpFConvert:
+    case SpvOpConvertPtrToU:
+    case SpvOpSatConvertSToU:
+    case SpvOpSatConvertUToS:
+    case SpvOpVectorExtractDynamic:
+    case SpvOpVectorInsertDynamic:
+    case SpvOpVectorShuffle:
+    case SpvOpTranspose:
+    case SpvOpSNegate:
+    case SpvOpFNegate:
+    case SpvOpIAdd:
+    case SpvOpFAdd:
+    case SpvOpISub:
+    case SpvOpFSub:
+    case SpvOpIMul:
+    case SpvOpFMul:
+    case SpvOpUDiv:
+    case SpvOpSDiv:
+    case SpvOpFDiv:
+    case SpvOpUMod:
+    case SpvOpSRem:
+    case SpvOpSMod:
+    case SpvOpFRem:
+    case SpvOpFMod:
+    case SpvOpVectorTimesScalar:
+    case SpvOpMatrixTimesScalar:
+    case SpvOpVectorTimesMatrix:
+    case SpvOpMatrixTimesVector:
+    case SpvOpMatrixTimesMatrix:
+    case SpvOpOuterProduct:
+    case SpvOpDot:
+    case SpvOpIAddCarry:
+    case SpvOpISubBorrow:
+    case SpvOpUMulExtended:
+    case SpvOpSMulExtended:
+    case SpvOpShiftRightLogical:
+    case SpvOpShiftRightArithmetic:
+    case SpvOpShiftLeftLogical:
+    case SpvOpBitwiseOr:
+    case SpvOpBitwiseXor:
+    case SpvOpBitwiseAnd:
+    case SpvOpNot:
+    case SpvOpBitFieldInsert:
+    case SpvOpBitFieldSExtract:
+    case SpvOpBitFieldUExtract:
+    case SpvOpBitReverse:
+    case SpvOpBitCount:
+    case SpvOpAtomicLoad:
+    case SpvOpAtomicStore:
+    case SpvOpAtomicExchange:
+    case SpvOpAtomicCompareExchange:
+    case SpvOpAtomicCompareExchangeWeak:
+    case SpvOpAtomicIIncrement:
+    case SpvOpAtomicIDecrement:
+    case SpvOpAtomicIAdd:
+    case SpvOpAtomicISub:
+    case SpvOpAtomicSMin:
+    case SpvOpAtomicUMin:
+    case SpvOpAtomicSMax:
+    case SpvOpAtomicUMax:
+    case SpvOpAtomicAnd:
+    case SpvOpAtomicOr:
+    case SpvOpAtomicXor:
+      return true;
+    default:
+      return false;
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

@@ -0,0 +1,62 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SPIRV_TOOLS_TRANSFORMATION_ADD_RELAXED_DECORATION_H
+#define SPIRV_TOOLS_TRANSFORMATION_ADD_RELAXED_DECORATION_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 TransformationAddRelaxedDecoration : public Transformation {
+ public:
+  explicit TransformationAddRelaxedDecoration(
+      const protobufs::TransformationAddRelaxedDecoration& message);
+
+  explicit TransformationAddRelaxedDecoration(uint32_t fresh_id);
+
+  // - |message_.result_id| must be the result id of an instruction, which is
+  //   located in a dead block and Relaxed decoration can be applied.
+  // - It does not matter whether this instruction is already annotated with the
+  //   Relaxed decoration.
+  bool IsApplicable(
+
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Adds a decoration of the form:
+  //   'OpDecoration |message_.result_id| RelaxedPrecision'
+  // to the module.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Returns true if and only if |opcode| is the opcode of an instruction
+  // that operates on 32-bit integers and 32-bit floats
+  // as defined by the SPIR-V specification.
+  static bool IsNumeric(uint32_t opcode);
+
+ private:
+  protobufs::TransformationAddRelaxedDecoration message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SPIRV_TOOLS_TRANSFORMATION_ADD_RELAXED_DECORATION_H

+ 32 - 18
3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.cpp

@@ -38,7 +38,8 @@ TransformationAddSynonym::TransformationAddSynonym(
 }
 
 bool TransformationAddSynonym::IsApplicable(
-    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   assert(protobufs::TransformationAddSynonym::SynonymType_IsValid(
              message_.synonym_type()) &&
          "Synonym type is invalid");
@@ -55,7 +56,8 @@ bool TransformationAddSynonym::IsApplicable(
   }
 
   // Check that we can apply |synonym_type| to |result_id|.
-  if (!IsInstructionValid(ir_context, synonym, message_.synonym_type())) {
+  if (!IsInstructionValid(ir_context, transformation_context, synonym,
+                          message_.synonym_type())) {
     return false;
   }
 
@@ -76,7 +78,7 @@ bool TransformationAddSynonym::IsApplicable(
 
   // A constant instruction must be present in the module if required.
   if (IsAdditionalConstantRequired(message_.synonym_type()) &&
-      MaybeGetConstantId(ir_context) == 0) {
+      MaybeGetConstantId(ir_context, transformation_context) == 0) {
     return false;
   }
 
@@ -90,7 +92,8 @@ void TransformationAddSynonym::Apply(
     TransformationContext* transformation_context) const {
   // Add a synonymous instruction.
   FindInstruction(message_.insert_before(), ir_context)
-      ->InsertBefore(MakeSynonymousInstruction(ir_context));
+      ->InsertBefore(
+          MakeSynonymousInstruction(ir_context, *transformation_context));
 
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.synonym_fresh_id());
 
@@ -122,7 +125,8 @@ protobufs::Transformation TransformationAddSynonym::ToMessage() const {
 }
 
 bool TransformationAddSynonym::IsInstructionValid(
-    opt::IRContext* ir_context, opt::Instruction* inst,
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context, opt::Instruction* inst,
     protobufs::TransformationAddSynonym::SynonymType synonym_type) {
   // Instruction must have a result id, type id. We skip OpUndef and
   // OpConstantNull.
@@ -131,8 +135,10 @@ bool TransformationAddSynonym::IsInstructionValid(
     return false;
   }
 
-  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
-  //  We can't create a synonym of an irrelevant id.
+  if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, inst)) {
+    return false;
+  }
+
   switch (synonym_type) {
     case protobufs::TransformationAddSynonym::ADD_ZERO:
     case protobufs::TransformationAddSynonym::SUB_ZERO:
@@ -149,7 +155,9 @@ bool TransformationAddSynonym::IsInstructionValid(
       return type->AsInteger() || type->AsFloat();
     }
     case protobufs::TransformationAddSynonym::COPY_OBJECT:
-      return fuzzerutil::CanMakeSynonymOf(ir_context, inst);
+      // All checks for OpCopyObject are handled by
+      // fuzzerutil::CanMakeSynonymOf.
+      return true;
     case protobufs::TransformationAddSynonym::LOGICAL_AND:
     case protobufs::TransformationAddSynonym::LOGICAL_OR: {
       // The instruction must be either a scalar or a vector of booleans.
@@ -166,7 +174,8 @@ bool TransformationAddSynonym::IsInstructionValid(
 
 std::unique_ptr<opt::Instruction>
 TransformationAddSynonym::MakeSynonymousInstruction(
-    opt::IRContext* ir_context) const {
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   auto synonym_type_id =
       fuzzerutil::GetTypeId(ir_context, message_.result_id());
   assert(synonym_type_id && "Synonym has invalid type id");
@@ -206,7 +215,8 @@ TransformationAddSynonym::MakeSynonymousInstruction(
           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)}}});
+              {SPV_OPERAND_TYPE_ID,
+               {MaybeGetConstantId(ir_context, transformation_context)}}});
     }
     case protobufs::TransformationAddSynonym::COPY_OBJECT:
       return MakeUnique<opt::Instruction>(
@@ -224,7 +234,8 @@ TransformationAddSynonym::MakeSynonymousInstruction(
           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)}}});
+              {SPV_OPERAND_TYPE_ID,
+               {MaybeGetConstantId(ir_context, transformation_context)}}});
     }
     default:
       assert(false && "Unhandled synonym type");
@@ -233,7 +244,8 @@ TransformationAddSynonym::MakeSynonymousInstruction(
 }
 
 uint32_t TransformationAddSynonym::MaybeGetConstantId(
-    opt::IRContext* ir_context) const {
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   assert(IsAdditionalConstantRequired(message_.synonym_type()) &&
          "Synonym type doesn't require an additional constant");
 
@@ -245,7 +257,8 @@ uint32_t TransformationAddSynonym::MaybeGetConstantId(
     case protobufs::TransformationAddSynonym::ADD_ZERO:
     case protobufs::TransformationAddSynonym::SUB_ZERO:
     case protobufs::TransformationAddSynonym::LOGICAL_OR:
-      return fuzzerutil::MaybeGetZeroConstant(ir_context, synonym_type_id);
+      return fuzzerutil::MaybeGetZeroConstant(
+          ir_context, transformation_context, synonym_type_id, false);
     case protobufs::TransformationAddSynonym::MUL_ONE:
     case protobufs::TransformationAddSynonym::LOGICAL_AND: {
       auto synonym_type = ir_context->get_type_mgr()->GetType(synonym_type_id);
@@ -259,19 +272,20 @@ uint32_t TransformationAddSynonym::MaybeGetConstantId(
         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)) {
+                ir_context, transformation_context, {one_word}, element_type_id,
+                false)) {
           return fuzzerutil::MaybeGetCompositeConstant(
-              ir_context,
+              ir_context, transformation_context,
               std::vector<uint32_t>(vector->element_count(), scalar_one_id),
-              synonym_type_id);
+              synonym_type_id, false);
         }
 
         return 0;
       } else {
         return fuzzerutil::MaybeGetScalarConstant(
-            ir_context,
+            ir_context, transformation_context,
             {synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u},
-            synonym_type_id);
+            synonym_type_id, false);
       }
     }
     default:

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

@@ -35,6 +35,7 @@ class TransformationAddSynonym : public Transformation {
       const protobufs::InstructionDescriptor& insert_before);
 
   // - |result_id| must be a valid result id of some instruction in the module.
+  // - |result_id| may not be an irrelevant id.
   // - |synonym_type| is a type of the synonymous instruction that will be
   //   created.
   // - |synonym_fresh_id| is a fresh id.
@@ -57,7 +58,9 @@ class TransformationAddSynonym : public Transformation {
   // 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,
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context,
+      opt::Instruction* inst,
       protobufs::TransformationAddSynonym::SynonymType synonym_type);
 
   // Returns true if |synonym_type| requires an additional constant instruction
@@ -68,14 +71,17 @@ class TransformationAddSynonym : public Transformation {
  private:
   // Returns a new instruction which is synonymous to |message_.result_id|.
   std::unique_ptr<opt::Instruction> MakeSynonymousInstruction(
-      opt::IRContext* ir_context) const;
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_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;
+  uint32_t MaybeGetConstantId(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const;
 
   protobufs::TransformationAddSynonym message_;
 };

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

@@ -40,7 +40,8 @@ TransformationCompositeConstruct::TransformationCompositeConstruct(
 }
 
 bool TransformationCompositeConstruct::IsApplicable(
-    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     // We require the id for the composite constructor to be unused.
     return false;
@@ -87,7 +88,20 @@ bool TransformationCompositeConstruct::IsApplicable(
 
   // Now check whether every component being used to initialize the composite is
   // available at the desired program point.
-  for (auto& component : message_.component()) {
+  for (auto component : message_.component()) {
+    auto* inst = ir_context->get_def_use_mgr()->GetDef(component);
+    if (!inst) {
+      return false;
+    }
+
+    // We should be able to create a synonym of |component| if it's not
+    // irrelevant.
+    if (!transformation_context.GetFactManager()->IdIsIrrelevant(component) &&
+        !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
+                                      inst)) {
+      return false;
+    }
+
     if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
                                                     component)) {
       return false;
@@ -144,17 +158,23 @@ void TransformationCompositeConstruct::Apply(
       for (uint32_t subvector_index = 0;
            subvector_index < component_type->AsVector()->element_count();
            subvector_index++) {
-        transformation_context->GetFactManager()->AddFactDataSynonym(
-            MakeDataDescriptor(component, {subvector_index}),
-            MakeDataDescriptor(message_.fresh_id(), {index}), ir_context);
+        if (!transformation_context->GetFactManager()->IdIsIrrelevant(
+                component)) {
+          transformation_context->GetFactManager()->AddFactDataSynonym(
+              MakeDataDescriptor(component, {subvector_index}),
+              MakeDataDescriptor(message_.fresh_id(), {index}), ir_context);
+        }
         index++;
       }
     } else {
       // The other cases are simple: the component is made directly synonymous
       // with the element of the composite being constructed.
-      transformation_context->GetFactManager()->AddFactDataSynonym(
-          MakeDataDescriptor(component, {}),
-          MakeDataDescriptor(message_.fresh_id(), {index}), ir_context);
+      if (!transformation_context->GetFactManager()->IdIsIrrelevant(
+              component)) {
+        transformation_context->GetFactManager()->AddFactDataSynonym(
+            MakeDataDescriptor(component, {}),
+            MakeDataDescriptor(message_.fresh_id(), {index}), ir_context);
+      }
       index++;
     }
   }

+ 23 - 11
3rdparty/spirv-tools/source/fuzz/transformation_composite_extract.cpp

@@ -40,7 +40,8 @@ TransformationCompositeExtract::TransformationCompositeExtract(
 }
 
 bool TransformationCompositeExtract::IsApplicable(
-    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
   }
@@ -54,6 +55,14 @@ bool TransformationCompositeExtract::IsApplicable(
   if (!composite_instruction) {
     return false;
   }
+  if (!transformation_context.GetFactManager()->IdIsIrrelevant(
+          message_.composite_id()) &&
+      !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
+                                    composite_instruction)) {
+    // |composite_id| will participate in DataSynonym facts. Thus, it can't be
+    // an irrelevant id.
+    return false;
+  }
   if (auto block = ir_context->get_instr_block(composite_instruction)) {
     if (composite_instruction == instruction_to_insert_before ||
         !ir_context->GetDominatorAnalysis(block->GetParent())
@@ -105,17 +114,20 @@ void TransformationCompositeExtract::Apply(
 
   // Add the fact that the id storing the extracted element is synonymous with
   // the index into the structure.
-  std::vector<uint32_t> indices;
-  for (auto an_index : message_.index()) {
-    indices.push_back(an_index);
+  if (!transformation_context->GetFactManager()->IdIsIrrelevant(
+          message_.composite_id())) {
+    std::vector<uint32_t> indices;
+    for (auto an_index : message_.index()) {
+      indices.push_back(an_index);
+    }
+    protobufs::DataDescriptor data_descriptor_for_extracted_element =
+        MakeDataDescriptor(message_.composite_id(), std::move(indices));
+    protobufs::DataDescriptor data_descriptor_for_result_id =
+        MakeDataDescriptor(message_.fresh_id(), {});
+    transformation_context->GetFactManager()->AddFactDataSynonym(
+        data_descriptor_for_extracted_element, data_descriptor_for_result_id,
+        ir_context);
   }
-  protobufs::DataDescriptor data_descriptor_for_extracted_element =
-      MakeDataDescriptor(message_.composite_id(), std::move(indices));
-  protobufs::DataDescriptor data_descriptor_for_result_id =
-      MakeDataDescriptor(message_.fresh_id(), {});
-  transformation_context->GetFactManager()->AddFactDataSynonym(
-      data_descriptor_for_extracted_element, data_descriptor_for_result_id,
-      ir_context);
 }
 
 protobufs::Transformation TransformationCompositeExtract::ToMessage() const {

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

@@ -48,7 +48,8 @@ class TransformationCompositeExtract : public Transformation {
   // Adds an OpCompositeConstruct instruction before the instruction identified
   // by |message_.instruction_to_insert_before|, that extracts from
   // |message_.composite_id| via indices |message_.index| into
-  // |message_.fresh_id|.  Generates a data synonym fact relating
+  // |message_.fresh_id|. If |composite_id| is not an irrelevant id,
+  // generates a data synonym fact relating
   // |message_.fresh_id| to the extracted element.
   void Apply(opt::IRContext* ir_context,
              TransformationContext* transformation_context) const override;

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

@@ -37,7 +37,8 @@ TransformationEquationInstruction::TransformationEquationInstruction(
 }
 
 bool TransformationEquationInstruction::IsApplicable(
-    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   // The result id must be fresh.
   if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
@@ -59,6 +60,9 @@ bool TransformationEquationInstruction::IsApplicable(
     if (inst->opcode() == SpvOpUndef) {
       return false;
     }
+    if (transformation_context.GetFactManager()->IdIsIrrelevant(id)) {
+      return false;
+    }
     if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
                                                     id)) {
       return false;

+ 34 - 50
3rdparty/spirv-tools/source/fuzz/transformation_permute_function_parameters.cpp

@@ -87,38 +87,6 @@ void TransformationPermuteFunctionParameters::Apply(
   auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id());
   assert(function && "Can't find the function");
 
-  auto* old_function_type_inst =
-      fuzzerutil::GetFunctionType(ir_context, function);
-  assert(old_function_type_inst && "Function must have a valid type");
-
-  // Change function's type
-  if (ir_context->get_def_use_mgr()->NumUsers(old_function_type_inst) == 1) {
-    // If only the current function uses |old_function_type_inst| - change it
-    // in-place.
-    opt::Instruction::OperandList permuted_operands = {
-        std::move(old_function_type_inst->GetInOperand(0))};
-    for (auto index : message_.permutation()) {
-      // +1 since the first operand to OpTypeFunction is a return type.
-      permuted_operands.push_back(
-          std::move(old_function_type_inst->GetInOperand(index + 1)));
-    }
-
-    old_function_type_inst->SetInOperands(std::move(permuted_operands));
-  } else {
-    // Either use an existing type or create a new one.
-    std::vector<uint32_t> type_ids = {
-        old_function_type_inst->GetSingleWordInOperand(0)};
-    for (auto index : message_.permutation()) {
-      // +1 since the first operand to OpTypeFunction is a return type.
-      type_ids.push_back(
-          old_function_type_inst->GetSingleWordInOperand(index + 1));
-    }
-
-    function->DefInst().SetInOperand(
-        1, {fuzzerutil::FindOrCreateFunctionType(
-               ir_context, message_.function_type_fresh_id(), type_ids)});
-  }
-
   // Adjust OpFunctionParameter instructions
 
   // Collect ids and types from OpFunctionParameter instructions
@@ -146,24 +114,40 @@ void TransformationPermuteFunctionParameters::Apply(
       });
 
   // Fix all OpFunctionCall instructions
-  ir_context->get_def_use_mgr()->ForEachUser(
-      &function->DefInst(), [this](opt::Instruction* call) {
-        if (call->opcode() != SpvOpFunctionCall ||
-            call->GetSingleWordInOperand(0) != message_.function_id()) {
-          return;
-        }
-
-        opt::Instruction::OperandList call_operands = {
-            call->GetInOperand(0)  // Function id
-        };
-
-        for (auto index : message_.permutation()) {
-          // Take function id into account
-          call_operands.push_back(call->GetInOperand(index + 1));
-        }
-
-        call->SetInOperands(std::move(call_operands));
-      });
+  for (auto* call : fuzzerutil::GetCallers(ir_context, function->result_id())) {
+    opt::Instruction::OperandList call_operands = {
+        call->GetInOperand(0)  // Function id
+    };
+
+    for (auto index : message_.permutation()) {
+      // Take function id into account
+      call_operands.push_back(call->GetInOperand(index + 1));
+    }
+
+    call->SetInOperands(std::move(call_operands));
+  }
+
+  // Update function type.
+  {
+    // We use a separate scope here since |old_function_type_inst| might become
+    // a dangling pointer after the call to the fuzzerutil::UpdateFunctionType.
+
+    auto* old_function_type_inst =
+        fuzzerutil::GetFunctionType(ir_context, function);
+    assert(old_function_type_inst && "Function must have a valid type");
+
+    std::vector<uint32_t> parameter_type_ids;
+    for (auto index : message_.permutation()) {
+      // +1 since the first operand to OpTypeFunction is a return type.
+      parameter_type_ids.push_back(
+          old_function_type_inst->GetSingleWordInOperand(index + 1));
+    }
+
+    // Change function's type.
+    fuzzerutil::UpdateFunctionType(
+        ir_context, function->result_id(), message_.function_type_fresh_id(),
+        old_function_type_inst->GetSingleWordInOperand(0), parameter_type_ids);
+  }
 
   // Make sure our changes are analyzed
   ir_context->InvalidateAnalysesExceptFor(

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

@@ -38,7 +38,8 @@ TransformationPushIdThroughVariable::TransformationPushIdThroughVariable(
 }
 
 bool TransformationPushIdThroughVariable::IsApplicable(
-    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   // |message_.value_synonym_id| and |message_.variable_id| must be fresh.
   if (!fuzzerutil::IsFreshId(ir_context, message_.value_synonym_id()) ||
       !fuzzerutil::IsFreshId(ir_context, message_.variable_id())) {
@@ -73,6 +74,14 @@ bool TransformationPushIdThroughVariable::IsApplicable(
     return false;
   }
 
+  // We should be able to create a synonym of |value_id| if it's not irrelevant.
+  if (!transformation_context.GetFactManager()->IdIsIrrelevant(
+          message_.value_id()) &&
+      !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
+                                    value_instruction)) {
+    return false;
+  }
+
   // A pointer type instruction pointing to the value type must be defined.
   auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
       ir_context, value_instruction->type_id(),
@@ -144,11 +153,14 @@ void TransformationPushIdThroughVariable::Apply(
 
   ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 
-  // Adds the fact that |message_.value_synonym_id|
-  // and |message_.value_id| are synonymous.
-  transformation_context->GetFactManager()->AddFactDataSynonym(
-      MakeDataDescriptor(message_.value_synonym_id(), {}),
-      MakeDataDescriptor(message_.value_id(), {}), ir_context);
+  if (!transformation_context->GetFactManager()->IdIsIrrelevant(
+          message_.value_id())) {
+    // Adds the fact that |message_.value_synonym_id|
+    // and |message_.value_id| are synonymous.
+    transformation_context->GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(message_.value_synonym_id(), {}),
+        MakeDataDescriptor(message_.value_id(), {}), ir_context);
+  }
 }
 
 protobufs::Transformation TransformationPushIdThroughVariable::ToMessage()

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

@@ -51,8 +51,8 @@ class TransformationPushIdThroughVariable : public Transformation {
       const TransformationContext& transformation_context) const override;
 
   // Stores |value_id| to |variable_id|, loads |variable_id| to
-  // |value_synonym_id| and adds the fact that |value_synonym_id| and |value_id|
-  // are synonymous.
+  // |value_synonym_id|. Adds the fact that |value_synonym_id| and |value_id|
+  // are synonymous if |value_id| is not irrelevant.
   void Apply(opt::IRContext* ir_context,
              TransformationContext* transformation_context) const override;
 

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

@@ -18,12 +18,6 @@
 namespace spvtools {
 namespace fuzz {
 
-namespace {
-bool IsScalarZeroConstant(const opt::analysis::Constant* constant) {
-  return constant->AsScalarConstant() && constant->IsZero();
-}
-}  // namespace
-
 TransformationRecordSynonymousConstants::
     TransformationRecordSynonymousConstants(
         const protobufs::TransformationRecordSynonymousConstants& message)
@@ -38,62 +32,30 @@ TransformationRecordSynonymousConstants::
 
 bool TransformationRecordSynonymousConstants::IsApplicable(
     opt::IRContext* ir_context,
-    const TransformationContext& /* unused */) const {
+    const TransformationContext& transformation_context) const {
   // The ids must be different
   if (message_.constant1_id() == message_.constant2_id()) {
     return false;
   }
 
-  auto constant1 = ir_context->get_constant_mgr()->FindDeclaredConstant(
-      message_.constant1_id());
-  auto constant2 = ir_context->get_constant_mgr()->FindDeclaredConstant(
-      message_.constant2_id());
-
-  // The constants must exist
-  if (constant1 == nullptr || constant2 == nullptr) {
+  if (transformation_context.GetFactManager()->IdIsIrrelevant(
+          message_.constant1_id()) ||
+      transformation_context.GetFactManager()->IdIsIrrelevant(
+          message_.constant2_id())) {
     return false;
   }
 
-  // If the constants are equal, then they are equivalent
-  if (constant1 == constant2) {
-    return true;
-  }
-
-  // If the constants are two integers (signed or unsigned), they are equal
-  // if they have the same width and the same data words.
-  if (constant1->AsIntConstant() && constant2->AsIntConstant() &&
-      constant1->type()->AsInteger()->width() ==
-          constant2->type()->AsInteger()->width() &&
-      constant1->AsIntConstant()->words() ==
-          constant2->AsIntConstant()->words()) {
-    return true;
-  }
-
-  // The types must be the same
-  if (!constant1->type()->IsSame(constant2->type())) {
-    return false;
-  }
-
-  // The constants are equivalent if one is null and the other is a static
-  // constant with value 0.
-  return (constant1->AsNullConstant() && IsScalarZeroConstant(constant2)) ||
-         (IsScalarZeroConstant(constant1) && constant2->AsNullConstant());
+  return AreEquivalentConstants(ir_context, message_.constant1_id(),
+                                message_.constant2_id());
 }
 
 void TransformationRecordSynonymousConstants::Apply(
     opt::IRContext* ir_context,
     TransformationContext* transformation_context) const {
-  protobufs::FactDataSynonym fact_data_synonym;
-  // Define the two equivalent data descriptors (just containing the ids)
-  *fact_data_synonym.mutable_data1() =
-      MakeDataDescriptor(message_.constant1_id(), {});
-  *fact_data_synonym.mutable_data2() =
-      MakeDataDescriptor(message_.constant2_id(), {});
-  protobufs::Fact fact;
-  *fact.mutable_data_synonym_fact() = fact_data_synonym;
-
   // Add the fact to the fact manager
-  transformation_context->GetFactManager()->AddFact(fact, ir_context);
+  transformation_context->GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(message_.constant1_id(), {}),
+      MakeDataDescriptor(message_.constant2_id(), {}), ir_context);
 }
 
 protobufs::Transformation TransformationRecordSynonymousConstants::ToMessage()
@@ -103,5 +65,62 @@ protobufs::Transformation TransformationRecordSynonymousConstants::ToMessage()
   return result;
 }
 
+bool TransformationRecordSynonymousConstants::AreEquivalentConstants(
+    opt::IRContext* ir_context, uint32_t constant_id1, uint32_t constant_id2) {
+  const auto* def_1 = ir_context->get_def_use_mgr()->GetDef(constant_id1);
+  const auto* def_2 = ir_context->get_def_use_mgr()->GetDef(constant_id2);
+
+  // Check that the definitions exist
+  if (!def_1 || !def_2) {
+    // We don't use an assertion since otherwise the shrinker fails.
+    return false;
+  }
+
+  // The type ids must be the same
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3536): Somehow
+  // relax this for integers (so that unsigned integer and signed integer are
+  // considered the same type)
+  if (def_1->type_id() != def_2->type_id()) {
+    return false;
+  }
+
+  auto constant1 = ir_context->get_constant_mgr()->GetConstantFromInst(def_1);
+  auto constant2 = ir_context->get_constant_mgr()->GetConstantFromInst(def_2);
+
+  assert(constant1 && constant2 && "The ids must refer to constants.");
+
+  // If either constant is null, the other is equivalent iff it is zero-like
+  if (constant1->AsNullConstant()) {
+    return constant2->IsZero();
+  }
+
+  if (constant2->AsNullConstant()) {
+    return constant1->IsZero();
+  }
+
+  // If the constants are scalar, they are equal iff their words are the same
+  if (auto scalar1 = constant1->AsScalarConstant()) {
+    return scalar1->words() == constant2->AsScalarConstant()->words();
+  }
+
+  // The only remaining possibility is that the constants are composite
+  assert(constant1->AsCompositeConstant() &&
+         "Equivalence of constants can only be checked with scalar, composite "
+         "or null constants.");
+
+  // Since the types match, we already know that the number of components is
+  // the same. We check that the input operands of the definitions are all
+  // constants and that they are pairwise equivalent.
+  for (uint32_t i = 0; i < def_1->NumInOperands(); i++) {
+    if (!AreEquivalentConstants(ir_context, def_1->GetSingleWordInOperand(i),
+                                def_2->GetSingleWordInOperand(i))) {
+      return false;
+    }
+  }
+
+  // If we get here, all the components are equivalent
+  return true;
+}
+
 }  // namespace fuzz
 }  // namespace spvtools

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

@@ -33,11 +33,15 @@ class TransformationRecordSynonymousConstants : public Transformation {
   //   of constants
   // - |message_.constant_id| and |message_.synonym_id| refer to constants
   //   that are equal or equivalent.
+  //   TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3536): Signed and
+  //   unsigned integers are currently considered non-equivalent
   //   Two integers with the same width and value are equal, even if one is
   //   signed and the other is not.
-  //   Constants are equivalent if both of them represent zero-like scalar
-  //   values of the same type (for example OpConstant of type int and value
-  //   0 and OpConstantNull of type int).
+  //   Constants are equivalent:
+  //   - if both of them represent zero-like values of the same type
+  //   - if they are composite constants with the same type and their
+  //     components are pairwise equivalent.
+  // - |constant1_id| and |constant2_id| may not be irrelevant.
   bool IsApplicable(
       opt::IRContext* ir_context,
       const TransformationContext& transformation_context) const override;
@@ -51,6 +55,13 @@ class TransformationRecordSynonymousConstants : public Transformation {
 
  private:
   protobufs::TransformationRecordSynonymousConstants message_;
+
+  // Returns true if the two given constants are equivalent
+  // (the description of IsApplicable specifies the conditions they must satisfy
+  // to be considered equivalent)
+  static bool AreEquivalentConstants(opt::IRContext* ir_context,
+                                     uint32_t constant_id1,
+                                     uint32_t constant_id2);
 };
 
 }  // namespace fuzz

+ 127 - 0
3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp

@@ -0,0 +1,127 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationReplaceCopyMemoryWithLoadStore::
+    TransformationReplaceCopyMemoryWithLoadStore(
+        const spvtools::fuzz::protobufs::
+            TransformationReplaceCopyMemoryWithLoadStore& message)
+    : message_(message) {}
+
+TransformationReplaceCopyMemoryWithLoadStore::
+    TransformationReplaceCopyMemoryWithLoadStore(
+        uint32_t fresh_id, const protobufs::InstructionDescriptor&
+                               copy_memory_instruction_descriptor) {
+  message_.set_fresh_id(fresh_id);
+  *message_.mutable_copy_memory_instruction_descriptor() =
+      copy_memory_instruction_descriptor;
+}
+
+bool TransformationReplaceCopyMemoryWithLoadStore::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  // |message_.fresh_id| must be fresh.
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
+    return false;
+  }
+  // The instruction to be replaced must be defined and have opcode
+  // OpCopyMemory.
+  auto copy_memory_instruction = FindInstruction(
+      message_.copy_memory_instruction_descriptor(), ir_context);
+  if (!copy_memory_instruction ||
+      copy_memory_instruction->opcode() != SpvOpCopyMemory) {
+    return false;
+  }
+  return true;
+}
+
+void TransformationReplaceCopyMemoryWithLoadStore::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  auto copy_memory_instruction = FindInstruction(
+      message_.copy_memory_instruction_descriptor(), ir_context);
+  // |copy_memory_instruction| must be defined.
+  assert(copy_memory_instruction &&
+         copy_memory_instruction->opcode() == SpvOpCopyMemory &&
+         "The required OpCopyMemory instruction must be defined.");
+
+  // Coherence check: Both operands must be pointers.
+
+  // Get types of ids used as a source and target of |copy_memory_instruction|.
+  auto target = ir_context->get_def_use_mgr()->GetDef(
+      copy_memory_instruction->GetSingleWordInOperand(0));
+  auto source = ir_context->get_def_use_mgr()->GetDef(
+      copy_memory_instruction->GetSingleWordInOperand(1));
+  auto target_type_opcode =
+      ir_context->get_def_use_mgr()->GetDef(target->type_id())->opcode();
+  auto source_type_opcode =
+      ir_context->get_def_use_mgr()->GetDef(source->type_id())->opcode();
+
+  // Keep release-mode compilers happy. (No unused variables.)
+  (void)target;
+  (void)source;
+  (void)target_type_opcode;
+  (void)source_type_opcode;
+
+  assert(target_type_opcode == SpvOpTypePointer &&
+         source_type_opcode == SpvOpTypePointer &&
+         "Operands must be of type OpTypePointer");
+
+  // Coherence check: |source| and |target| must point to the same type.
+  uint32_t target_pointee_type = fuzzerutil::GetPointeeTypeIdFromPointerType(
+      ir_context, target->type_id());
+  uint32_t source_pointee_type = fuzzerutil::GetPointeeTypeIdFromPointerType(
+      ir_context, source->type_id());
+
+  // Keep release-mode compilers happy. (No unused variables.)
+  (void)target_pointee_type;
+  (void)source_pointee_type;
+
+  assert(target_pointee_type == source_pointee_type &&
+         "Operands must have the same type to which they point to.");
+
+  // First, insert the OpStore instruction before the OpCopyMemory instruction
+  // and then insert the OpLoad instruction before the OpStore instruction.
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+  FindInstruction(message_.copy_memory_instruction_descriptor(), ir_context)
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpStore, 0, 0,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {target->result_id()}},
+               {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}})))
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpLoad, target_pointee_type, message_.fresh_id(),
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {source->result_id()}}})));
+
+  // Remove the OpCopyMemory instruction.
+  ir_context->KillInst(copy_memory_instruction);
+
+  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation
+TransformationReplaceCopyMemoryWithLoadStore::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_replace_copy_memory_with_load_store() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

@@ -0,0 +1,57 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_H
+#define SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_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 TransformationReplaceCopyMemoryWithLoadStore : public Transformation {
+ public:
+  explicit TransformationReplaceCopyMemoryWithLoadStore(
+      const protobufs::TransformationReplaceCopyMemoryWithLoadStore& message);
+
+  TransformationReplaceCopyMemoryWithLoadStore(
+      uint32_t fresh_id, const protobufs::InstructionDescriptor&
+                             copy_memory_instruction_descriptor);
+
+  // - |message_.fresh_id| must be fresh.
+  // - |message_.copy_memory_instruction_descriptor| must refer to an
+  //   OpCopyMemory instruction.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Replaces instruction OpCopyMemory with loading the source variable to an
+  // intermediate value and storing this value into the target variable of the
+  // original OpCopyMemory instruction.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationReplaceCopyMemoryWithLoadStore message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_H

+ 155 - 0
3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.cpp

@@ -0,0 +1,155 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_replace_copy_object_with_store_load.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationReplaceCopyObjectWithStoreLoad::
+    TransformationReplaceCopyObjectWithStoreLoad(
+        const spvtools::fuzz::protobufs::
+            TransformationReplaceCopyObjectWithStoreLoad& message)
+    : message_(message) {}
+
+TransformationReplaceCopyObjectWithStoreLoad::
+    TransformationReplaceCopyObjectWithStoreLoad(
+        uint32_t copy_object_result_id, uint32_t fresh_variable_id,
+        uint32_t variable_storage_class, uint32_t variable_initializer_id) {
+  message_.set_copy_object_result_id(copy_object_result_id);
+  message_.set_fresh_variable_id(fresh_variable_id);
+  message_.set_variable_storage_class(variable_storage_class);
+  message_.set_variable_initializer_id(variable_initializer_id);
+}
+
+bool TransformationReplaceCopyObjectWithStoreLoad::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  // |message_.fresh_variable_id| must be fresh.
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_variable_id())) {
+    return false;
+  }
+  auto copy_object_instruction =
+      ir_context->get_def_use_mgr()->GetDef(message_.copy_object_result_id());
+
+  // This must be a defined OpCopyObject instruction.
+  if (!copy_object_instruction ||
+      copy_object_instruction->opcode() != SpvOpCopyObject) {
+    return false;
+  }
+
+  // The opcode of the type_id instruction cannot be a OpTypePointer,
+  // because we cannot define a pointer to pointer.
+  if (ir_context->get_def_use_mgr()
+          ->GetDef(copy_object_instruction->type_id())
+          ->opcode() == SpvOpTypePointer) {
+    return false;
+  }
+
+  // It must be valid to insert the OpStore and OpLoad instruction before it.
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore,
+                                                    copy_object_instruction) ||
+      !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad,
+                                                    copy_object_instruction)) {
+    return false;
+  }
+
+  // A pointer type instruction pointing to the value type must be defined.
+  auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
+      ir_context, copy_object_instruction->type_id(),
+      static_cast<SpvStorageClass>(message_.variable_storage_class()));
+  if (!pointer_type_id) {
+    return false;
+  }
+
+  // Check that initializer is valid.
+  const auto* constant_inst =
+      ir_context->get_def_use_mgr()->GetDef(message_.variable_initializer_id());
+  if (!constant_inst || !spvOpcodeIsConstant(constant_inst->opcode()) ||
+      copy_object_instruction->type_id() != constant_inst->type_id()) {
+    return false;
+  }
+  // |message_.variable_storage_class| must be Private or Function.
+  return message_.variable_storage_class() == SpvStorageClassPrivate ||
+         message_.variable_storage_class() == SpvStorageClassFunction;
+}
+
+void TransformationReplaceCopyObjectWithStoreLoad::Apply(
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
+  auto copy_object_instruction =
+      ir_context->get_def_use_mgr()->GetDef(message_.copy_object_result_id());
+  // |copy_object_instruction| must be defined.
+  assert(copy_object_instruction &&
+         copy_object_instruction->opcode() == SpvOpCopyObject &&
+         "The required OpCopyObject instruction must be defined.");
+  // Get id used as a source by the OpCopyObject instruction.
+  uint32_t src_operand = copy_object_instruction->GetSingleWordOperand(2);
+  // A pointer type instruction pointing to the value type must be defined.
+  auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
+      ir_context, copy_object_instruction->type_id(),
+      static_cast<SpvStorageClass>(message_.variable_storage_class()));
+  assert(pointer_type_id && "The required pointer type must be available.");
+
+  // Adds a global or local variable (according to the storage class).
+  if (message_.variable_storage_class() == SpvStorageClassPrivate) {
+    fuzzerutil::AddGlobalVariable(ir_context, message_.fresh_variable_id(),
+                                  pointer_type_id, SpvStorageClassPrivate,
+                                  message_.variable_initializer_id());
+  } else {
+    auto function_id = ir_context->get_instr_block(copy_object_instruction)
+                           ->GetParent()
+                           ->result_id();
+    fuzzerutil::AddLocalVariable(ir_context, message_.fresh_variable_id(),
+                                 pointer_type_id, function_id,
+                                 message_.variable_initializer_id());
+  }
+
+  // First, insert the OpLoad instruction before the OpCopyObject instruction
+  // and then insert the OpStore instruction before the OpLoad instruction.
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_variable_id());
+  copy_object_instruction
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpLoad, copy_object_instruction->type_id(),
+          message_.copy_object_result_id(),
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}}})))
+      ->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpStore, 0, 0,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}},
+               {SPV_OPERAND_TYPE_ID, {src_operand}}})));
+  // Remove the CopyObject instruction.
+  ir_context->KillInst(copy_object_instruction);
+
+  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+
+  // Adds the fact that |message_.copy_object_result_id|
+  // and src_operand (id used by OpCopyObject) are synonymous.
+  transformation_context->GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(message_.copy_object_result_id(), {}),
+      MakeDataDescriptor(src_operand, {}), ir_context);
+}
+
+protobufs::Transformation
+TransformationReplaceCopyObjectWithStoreLoad::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_replace_copy_object_with_store_load() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

@@ -0,0 +1,63 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_OBJECT_WITH_STORE_LOAD_H
+#define SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_OBJECT_WITH_STORE_LOAD_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 TransformationReplaceCopyObjectWithStoreLoad : public Transformation {
+ public:
+  explicit TransformationReplaceCopyObjectWithStoreLoad(
+      const protobufs::TransformationReplaceCopyObjectWithStoreLoad& message);
+
+  TransformationReplaceCopyObjectWithStoreLoad(
+      uint32_t copy_object_result_id, uint32_t fresh_variable_id,
+      uint32_t variable_storage_class, uint32_t variable_initializer_id);
+
+  // - |message_.copy_object_result_id| must be a result id of an OpCopyObject
+  //   instruction.
+  // - |message_.fresh_variable_id| must be a fresh id given to variable used by
+  //   OpStore.
+  // - |message_.variable_storage_class| must be either StorageClassPrivate or
+  //   StorageClassFunction.
+  // - |message_.initializer_id| must be a result id of some constant in the
+  //   module. Its type must be equal to the pointee type of the variable that
+  //   will be created.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Replaces instruction OpCopyObject with storing into a new variable and
+  // immediately loading from this variable to |result_id| of the original
+  // OpCopyObject instruction.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationReplaceCopyObjectWithStoreLoad message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SPIRV_TOOLS_TRANSFORMATION_REPLACE_COPY_OBJECT_WITH_STORE_LOAD_H

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

@@ -20,23 +20,6 @@
 
 namespace spvtools {
 namespace fuzz {
-namespace {
-
-opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context,
-                                          uint32_t param_id) {
-  auto* param_inst = ir_context->get_def_use_mgr()->GetDef(param_id);
-  assert(param_inst && "Parameter id is invalid");
-
-  for (auto& function : *ir_context->module()) {
-    if (fuzzerutil::InstructionIsFunctionParameter(param_inst, &function)) {
-      return &function;
-    }
-  }
-
-  return nullptr;
-}
-
-}  // namespace
 
 TransformationReplaceParameterWithGlobal::
     TransformationReplaceParameterWithGlobal(
@@ -53,7 +36,8 @@ TransformationReplaceParameterWithGlobal::
 }
 
 bool TransformationReplaceParameterWithGlobal::IsApplicable(
-    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   // Check that |parameter_id| is valid.
   const auto* param_inst =
       ir_context->get_def_use_mgr()->GetDef(message_.parameter_id());
@@ -62,8 +46,8 @@ bool TransformationReplaceParameterWithGlobal::IsApplicable(
   }
 
   // Check that function exists and is not an entry point.
-  const auto* function =
-      GetFunctionFromParameterId(ir_context, message_.parameter_id());
+  const auto* function = fuzzerutil::GetFunctionFromParameterId(
+      ir_context, message_.parameter_id());
   if (!function ||
       fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
     return false;
@@ -81,8 +65,8 @@ bool TransformationReplaceParameterWithGlobal::IsApplicable(
   }
 
   // Check that initializer for the global variable exists in the module.
-  if (fuzzerutil::MaybeGetZeroConstant(ir_context, param_inst->type_id()) ==
-      0) {
+  if (fuzzerutil::MaybeGetZeroConstant(ir_context, transformation_context,
+                                       param_inst->type_id(), false) == 0) {
     return false;
   }
 
@@ -100,25 +84,31 @@ bool TransformationReplaceParameterWithGlobal::IsApplicable(
 }
 
 void TransformationReplaceParameterWithGlobal::Apply(
-    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
   const auto* param_inst =
       ir_context->get_def_use_mgr()->GetDef(message_.parameter_id());
   assert(param_inst && "Parameter must exist");
 
   // Create global variable to store parameter's value.
-  //
-  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
-  //  Mark the global variable's pointee as irrelevant if replaced parameter is
-  //  irrelevant.
   fuzzerutil::AddGlobalVariable(
       ir_context, message_.global_variable_fresh_id(),
       fuzzerutil::MaybeGetPointerType(ir_context, param_inst->type_id(),
                                       SpvStorageClassPrivate),
       SpvStorageClassPrivate,
-      fuzzerutil::MaybeGetZeroConstant(ir_context, param_inst->type_id()));
+      fuzzerutil::MaybeGetZeroConstant(ir_context, *transformation_context,
+                                       param_inst->type_id(), false));
+
+  // Mark the global variable's pointee as irrelevant if replaced parameter is
+  // irrelevant.
+  if (transformation_context->GetFactManager()->IdIsIrrelevant(
+          message_.parameter_id())) {
+    transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
+        message_.global_variable_fresh_id());
+  }
 
-  auto* function =
-      GetFunctionFromParameterId(ir_context, message_.parameter_id());
+  auto* function = fuzzerutil::GetFunctionFromParameterId(
+      ir_context, message_.parameter_id());
   assert(function && "Function must exist");
 
   // Insert an OpLoad instruction right after OpVariable instructions.
@@ -152,57 +142,47 @@ void TransformationReplaceParameterWithGlobal::Apply(
          "Parameter must exist in the function");
 
   // Update all relevant OpFunctionCall instructions.
-  ir_context->get_def_use_mgr()->ForEachUser(
-      function->result_id(),
-      [ir_context, parameter_index, this](opt::Instruction* inst) {
-        if (inst->opcode() != SpvOpFunctionCall) {
-          return;
-        }
-
-        assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, inst) &&
-               "Can't insert OpStore right before the function call");
-
-        // Insert an OpStore before the OpFunctionCall. +1 since the first
-        // operand of OpFunctionCall is an id of the function.
-        inst->InsertBefore(MakeUnique<opt::Instruction>(
-            ir_context, SpvOpStore, 0, 0,
-            opt::Instruction::OperandList{
-                {SPV_OPERAND_TYPE_ID, {message_.global_variable_fresh_id()}},
-                {SPV_OPERAND_TYPE_ID,
-                 {inst->GetSingleWordInOperand(parameter_index + 1)}}}));
-
-        // +1 since the first operand of OpFunctionCall is an id of the
-        // function.
-        inst->RemoveInOperand(parameter_index + 1);
-      });
+  for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) {
+    assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, inst) &&
+           "Can't insert OpStore right before the function call");
+
+    // Insert an OpStore before the OpFunctionCall. +1 since the first
+    // operand of OpFunctionCall is an id of the function.
+    inst->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpStore, 0, 0,
+        opt::Instruction::OperandList{
+            {SPV_OPERAND_TYPE_ID, {message_.global_variable_fresh_id()}},
+            {SPV_OPERAND_TYPE_ID,
+             {inst->GetSingleWordInOperand(parameter_index + 1)}}}));
+
+    // +1 since the first operand of OpFunctionCall is an id of the
+    // function.
+    inst->RemoveInOperand(parameter_index + 1);
+  }
 
   // Remove the parameter from the function.
   function->RemoveParameter(message_.parameter_id());
 
   // Update function's type.
-  auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function);
-  assert(old_function_type && "Function has invalid type");
-
-  // Preemptively add function's return type id.
-  std::vector<uint32_t> type_ids = {
-      old_function_type->GetSingleWordInOperand(0)};
-
-  // +1 and -1 since the first operand is the return type id.
-  for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) {
-    if (i - 1 != parameter_index) {
-      type_ids.push_back(old_function_type->GetSingleWordInOperand(i));
+  {
+    // We use a separate scope here since |old_function_type| might become a
+    // dangling pointer after the call to the fuzzerutil::UpdateFunctionType.
+
+    auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function);
+    assert(old_function_type && "Function has invalid type");
+
+    // +1 and -1 since the first operand is the return type id.
+    std::vector<uint32_t> parameter_type_ids;
+    for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) {
+      if (i - 1 != parameter_index) {
+        parameter_type_ids.push_back(
+            old_function_type->GetSingleWordInOperand(i));
+      }
     }
-  }
 
-  if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1) {
-    // Change the old type in place. +1 since the first operand is the result
-    // type id of the function.
-    old_function_type->RemoveInOperand(parameter_index + 1);
-  } else {
-    // Find an existing or create a new function type.
-    function->DefInst().SetInOperand(
-        1, {fuzzerutil::FindOrCreateFunctionType(
-               ir_context, message_.function_type_fresh_id(), type_ids)});
+    fuzzerutil::UpdateFunctionType(
+        ir_context, function->result_id(), message_.function_type_fresh_id(),
+        old_function_type->GetSingleWordInOperand(0), parameter_type_ids);
   }
 
   // Make sure our changes are analyzed

+ 306 - 0
3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp

@@ -0,0 +1,306 @@
+// 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_replace_params_with_struct.h"
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct(
+    const protobufs::TransformationReplaceParamsWithStruct& message)
+    : message_(message) {}
+
+TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct(
+    const std::vector<uint32_t>& parameter_id, uint32_t fresh_function_type_id,
+    uint32_t fresh_parameter_id,
+    const std::unordered_map<uint32_t, uint32_t>&
+        caller_id_to_fresh_composite_id) {
+  message_.set_fresh_function_type_id(fresh_function_type_id);
+  message_.set_fresh_parameter_id(fresh_parameter_id);
+
+  for (auto id : parameter_id) {
+    message_.add_parameter_id(id);
+  }
+
+  message_.mutable_caller_id_to_fresh_composite_id()->insert(
+      caller_id_to_fresh_composite_id.begin(),
+      caller_id_to_fresh_composite_id.end());
+}
+
+bool TransformationReplaceParamsWithStruct::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  std::vector<uint32_t> parameter_id(message_.parameter_id().begin(),
+                                     message_.parameter_id().end());
+
+  // Check that |parameter_id| is neither empty nor it has duplicates.
+  if (parameter_id.empty() || fuzzerutil::HasDuplicates(parameter_id)) {
+    return false;
+  }
+
+  // All ids must correspond to valid parameters of the same function.
+  // The function can't be an entry-point function.
+
+  // fuzzerutil::GetFunctionFromParameterId requires a valid id.
+  if (!ir_context->get_def_use_mgr()->GetDef(parameter_id[0])) {
+    return false;
+  }
+
+  const auto* function =
+      fuzzerutil::GetFunctionFromParameterId(ir_context, parameter_id[0]);
+  if (!function ||
+      fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
+    return false;
+  }
+
+  // Compute all ids of the function's parameters.
+  std::unordered_set<uint32_t> all_parameter_ids;
+  for (const auto* param :
+       fuzzerutil::GetParameters(ir_context, function->result_id())) {
+    all_parameter_ids.insert(param->result_id());
+  }
+
+  // Check that all elements in |parameter_id| are valid.
+  for (auto id : parameter_id) {
+    // fuzzerutil::GetFunctionFromParameterId requires a valid id.
+    if (!ir_context->get_def_use_mgr()->GetDef(id)) {
+      return false;
+    }
+
+    // Check that |id| is a result id of one of the |function|'s parameters.
+    if (!all_parameter_ids.count(id)) {
+      return false;
+    }
+
+    // Check that the parameter with result id |id| has supported type.
+    const auto* type = ir_context->get_type_mgr()->GetType(
+        fuzzerutil::GetTypeId(ir_context, id));
+    assert(type && "Parameter has invalid type");
+    if (!IsParameterTypeSupported(*type)) {
+      return false;
+    }
+  }
+
+  // We already know that the function has at least |parameter_id.size()|
+  // parameters.
+
+  // Check that a relevant OpTypeStruct exists in the module.
+  if (!MaybeGetRequiredStructType(ir_context)) {
+    return false;
+  }
+
+  // Check that |callee_id_to_fresh_composite_id| is valid.
+  for (const auto* inst :
+       fuzzerutil::GetCallers(ir_context, function->result_id())) {
+    // Check that the callee is present in the map. It's ok if the map contains
+    // more ids that there are callees (those ids will not be used).
+    if (!message_.caller_id_to_fresh_composite_id().contains(
+            inst->result_id())) {
+      return false;
+    }
+  }
+
+  // Check that all fresh ids are unique and fresh.
+  std::vector<uint32_t> fresh_ids = {message_.fresh_function_type_id(),
+                                     message_.fresh_parameter_id()};
+
+  for (const auto& entry : message_.caller_id_to_fresh_composite_id()) {
+    fresh_ids.push_back(entry.second);
+  }
+
+  return !fuzzerutil::HasDuplicates(fresh_ids) &&
+         std::all_of(fresh_ids.begin(), fresh_ids.end(),
+                     [ir_context](uint32_t id) {
+                       return fuzzerutil::IsFreshId(ir_context, id);
+                     });
+}
+
+void TransformationReplaceParamsWithStruct::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  auto* function = fuzzerutil::GetFunctionFromParameterId(
+      ir_context, message_.parameter_id(0));
+  assert(function &&
+         "All parameters' ids should've been checked in the IsApplicable");
+
+  // Get a type id of the OpTypeStruct used as a type id of the new parameter.
+  auto struct_type_id = MaybeGetRequiredStructType(ir_context);
+  assert(struct_type_id &&
+         "IsApplicable should've guaranteed that this value isn't equal to 0");
+
+  // Add new parameter to the function.
+  function->AddParameter(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpFunctionParameter, struct_type_id,
+      message_.fresh_parameter_id(), opt::Instruction::OperandList()));
+
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_parameter_id());
+
+  // Compute indices of replaced parameters. This will be used to adjust
+  // OpFunctionCall instructions and create OpCompositeConstruct instructions at
+  // every call site.
+  std::vector<uint32_t> indices_of_replaced_params;
+  {
+    // We want to destroy |params| after the loop because it will contain
+    // dangling pointers when we remove parameters from the function.
+    auto params = fuzzerutil::GetParameters(ir_context, function->result_id());
+    for (auto id : message_.parameter_id()) {
+      auto it = std::find_if(params.begin(), params.end(),
+                             [id](const opt::Instruction* param) {
+                               return param->result_id() == id;
+                             });
+      assert(it != params.end() && "Parameter's id is invalid");
+      indices_of_replaced_params.push_back(
+          static_cast<uint32_t>(it - params.begin()));
+    }
+  }
+
+  // Update all function calls.
+  for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) {
+    // Create a list of operands for the OpCompositeConstruct instruction.
+    opt::Instruction::OperandList composite_components;
+    for (auto index : indices_of_replaced_params) {
+      // +1 since the first in operand to OpFunctionCall is the result id of
+      // the function.
+      composite_components.emplace_back(
+          std::move(inst->GetInOperand(index + 1)));
+    }
+
+    // Remove arguments from the function call. We do it in a separate loop
+    // and in reverse order to make sure we have removed correct operands.
+    for (auto it = indices_of_replaced_params.rbegin();
+         it != indices_of_replaced_params.rend(); ++it) {
+      // +1 since the first in operand to OpFunctionCall is the result id of
+      // the function.
+      inst->RemoveInOperand(*it + 1);
+    }
+
+    // Insert OpCompositeConstruct before the function call.
+    auto fresh_composite_id =
+        message_.caller_id_to_fresh_composite_id().at(inst->result_id());
+    inst->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpCompositeConstruct, struct_type_id, fresh_composite_id,
+        std::move(composite_components)));
+
+    // Add a new operand to the OpFunctionCall instruction.
+    inst->AddOperand({SPV_OPERAND_TYPE_ID, {fresh_composite_id}});
+    fuzzerutil::UpdateModuleIdBound(ir_context, fresh_composite_id);
+  }
+
+  // Insert OpCompositeExtract instructions into the entry point block of the
+  // function and remove replaced parameters.
+  for (int i = 0; i < message_.parameter_id_size(); ++i) {
+    const auto* param_inst =
+        ir_context->get_def_use_mgr()->GetDef(message_.parameter_id(i));
+    assert(param_inst && "Parameter id is invalid");
+
+    // Skip all OpVariable instructions.
+    auto iter = function->begin()->begin();
+    while (iter != function->begin()->end() &&
+           !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract,
+                                                         iter)) {
+      ++iter;
+    }
+
+    assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract,
+                                                        iter) &&
+           "Can't extract parameter's value from the structure");
+
+    // Insert OpCompositeExtract instructions to unpack parameters' values from
+    // the struct type.
+    iter.InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpCompositeExtract, param_inst->type_id(),
+        param_inst->result_id(),
+        opt::Instruction::OperandList{
+            {SPV_OPERAND_TYPE_ID, {message_.fresh_parameter_id()}},
+            {SPV_OPERAND_TYPE_LITERAL_INTEGER, {static_cast<uint32_t>(i)}}}));
+
+    function->RemoveParameter(param_inst->result_id());
+  }
+
+  // Update function's type.
+  {
+    // We use a separate scope here since |old_function_type| might become a
+    // dangling pointer after the call to the fuzzerutil::UpdateFunctionType.
+
+    auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function);
+    assert(old_function_type && "Function has invalid type");
+
+    // +1 since the first in operand to OpTypeFunction is the result type id
+    // of the function.
+    std::vector<uint32_t> parameter_type_ids;
+    for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) {
+      if (std::find(indices_of_replaced_params.begin(),
+                    indices_of_replaced_params.end(),
+                    i - 1) == indices_of_replaced_params.end()) {
+        parameter_type_ids.push_back(
+            old_function_type->GetSingleWordInOperand(i));
+      }
+    }
+
+    parameter_type_ids.push_back(struct_type_id);
+
+    fuzzerutil::UpdateFunctionType(
+        ir_context, function->result_id(), message_.fresh_function_type_id(),
+        old_function_type->GetSingleWordInOperand(0), parameter_type_ids);
+  }
+
+  // Make sure our changes are analyzed
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationReplaceParamsWithStruct::ToMessage()
+    const {
+  protobufs::Transformation result;
+  *result.mutable_replace_params_with_struct() = message_;
+  return result;
+}
+
+bool TransformationReplaceParamsWithStruct::IsParameterTypeSupported(
+    const opt::analysis::Type& param_type) {
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
+  //  Consider adding support for more types of parameters.
+  switch (param_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::kVector:
+    case opt::analysis::Type::kMatrix:
+      return true;
+    case opt::analysis::Type::kStruct:
+      return std::all_of(param_type.AsStruct()->element_types().begin(),
+                         param_type.AsStruct()->element_types().end(),
+                         [](const opt::analysis::Type* type) {
+                           return IsParameterTypeSupported(*type);
+                         });
+    default:
+      return false;
+  }
+}
+
+uint32_t TransformationReplaceParamsWithStruct::MaybeGetRequiredStructType(
+    opt::IRContext* ir_context) const {
+  std::vector<uint32_t> component_type_ids;
+  for (auto id : message_.parameter_id()) {
+    component_type_ids.push_back(fuzzerutil::GetTypeId(ir_context, id));
+  }
+
+  return fuzzerutil::MaybeGetStructType(ir_context, component_type_ids);
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

@@ -0,0 +1,83 @@
+// 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_REPLACE_PARAMS_WITH_STRUCT_H_
+#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_
+
+#include <unordered_map>
+
+#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 TransformationReplaceParamsWithStruct : public Transformation {
+ public:
+  explicit TransformationReplaceParamsWithStruct(
+      const protobufs::TransformationReplaceParamsWithStruct& message);
+
+  TransformationReplaceParamsWithStruct(
+      const std::vector<uint32_t>& parameter_id,
+      uint32_t fresh_function_type_id, uint32_t fresh_parameter_id,
+      const std::unordered_map<uint32_t, uint32_t>&
+          caller_id_to_fresh_composite_id);
+
+  // - Each element of |parameter_id| is a valid result id of some
+  //   OpFunctionParameter instruction. All parameter ids must correspond to
+  //   parameters of the same function. That function may not be an entry-point
+  //   function.
+  // - Types of all parameters must be supported by this transformation (see
+  //   IsParameterTypeSupported method).
+  // - |parameter_id| may not be empty or contain duplicates.
+  // - There must exist an OpTypeStruct instruction containing types of all
+  //   replaced parameters. Type of the i'th component of the struct is equal
+  //   to the type of the instruction with result id |parameter_id[i]|.
+  // - |caller_id_to_fresh_composite_id| should contain a key for at least every
+  //   result id of an OpFunctionCall instruction that calls the function.
+  // - |fresh_function_type_id|, |fresh_parameter_id|,
+  //   |caller_id_to_fresh_composite_id| are all fresh and unique ids.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // - Creates a new function parameter with result id |fresh_parameter_id|.
+  //   Parameter's type is OpTypeStruct with each components type equal to the
+  //   type of the replaced parameter.
+  // - OpCompositeConstruct with result id from |fresh_composite_id| is inserted
+  //   before each OpFunctionCall instruction.
+  // - OpCompositeExtract with result id equal to the result id of the replaced
+  //   parameter is created in the function.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Returns true if parameter's type is supported by this transformation.
+  static bool IsParameterTypeSupported(const opt::analysis::Type& param_type);
+
+ private:
+  // Returns a result id of the OpTypeStruct instruction required by this
+  // transformation (see docs on the IsApplicable method to learn more).
+  uint32_t MaybeGetRequiredStructType(opt::IRContext* ir_context) const;
+
+  protobufs::TransformationReplaceParamsWithStruct message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_

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

@@ -39,7 +39,8 @@ TransformationVectorShuffle::TransformationVectorShuffle(
 }
 
 bool TransformationVectorShuffle::IsApplicable(
-    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
   // The fresh id must not already be in use.
   if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
     return false;
@@ -56,12 +57,26 @@ bool TransformationVectorShuffle::IsApplicable(
   if (!vector1_instruction || !vector1_instruction->type_id()) {
     return false;
   }
+  // We should be able to create a synonym of |vector1| if it's not irrelevant.
+  if (!transformation_context.GetFactManager()->IdIsIrrelevant(
+          message_.vector1()) &&
+      !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
+                                    vector1_instruction)) {
+    return false;
+  }
   // The second vector must be an instruction with a type id
   auto vector2_instruction =
       ir_context->get_def_use_mgr()->GetDef(message_.vector2());
   if (!vector2_instruction || !vector2_instruction->type_id()) {
     return false;
   }
+  // We should be able to create a synonym of |vector2| if it's not irrelevant.
+  if (!transformation_context.GetFactManager()->IdIsIrrelevant(
+          message_.vector2()) &&
+      !fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
+                                    vector2_instruction)) {
+    return false;
+  }
   auto vector1_type =
       ir_context->get_type_mgr()->GetType(vector1_instruction->type_id());
   // The first vector instruction's type must actually be a vector type.
@@ -161,9 +176,21 @@ void TransformationVectorShuffle::Apply(
     // |component| refers.
     if (component <
         GetVectorType(ir_context, message_.vector1())->element_count()) {
+      // Irrelevant id cannot participate in DataSynonym facts.
+      if (transformation_context->GetFactManager()->IdIsIrrelevant(
+              message_.vector1())) {
+        continue;
+      }
+
       descriptor_for_source_component =
           MakeDataDescriptor(message_.vector1(), {component});
     } else {
+      // Irrelevant id cannot participate in DataSynonym facts.
+      if (transformation_context->GetFactManager()->IdIsIrrelevant(
+              message_.vector2())) {
+        continue;
+      }
+
       auto index_into_vector_2 =
           component -
           GetVectorType(ir_context, message_.vector1())->element_count();

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

@@ -19,7 +19,6 @@
 #include "source/fuzz/transformation.h"
 #include "source/fuzz/transformation_context.h"
 #include "source/opt/ir_context.h"
-
 #include "source/opt/types.h"
 
 namespace spvtools {
@@ -59,7 +58,9 @@ class TransformationVectorShuffle : public Transformation {
   // from which it came (with undefined components being ignored).  If the
   // result vector is a contiguous sub-range of one of the input vectors, a
   // fact is added to record that |message_.fresh_id| is synonymous with this
-  // sub-range.
+  // sub-range. DataSynonym facts are added only for non-irrelevant vectors
+  // (e.g. if |vector1| is irrelevant but |vector2| is not, synonyms will be
+  // created for |vector1| but not |vector2|).
   void Apply(opt::IRContext* ir_context,
              TransformationContext* transformation_context) const override;
 

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

@@ -413,6 +413,7 @@ bool spvOpcodeIsAtomicWithLoad(const SpvOp opcode) {
     case SpvOpAtomicIIncrement:
     case SpvOpAtomicIDecrement:
     case SpvOpAtomicIAdd:
+    case SpvOpAtomicFAddEXT:
     case SpvOpAtomicISub:
     case SpvOpAtomicSMin:
     case SpvOpAtomicUMin:
@@ -445,7 +446,7 @@ bool spvOpcodeIsReturn(SpvOp opcode) {
 
 bool spvOpcodeIsReturnOrAbort(SpvOp opcode) {
   return spvOpcodeIsReturn(opcode) || opcode == SpvOpKill ||
-         opcode == SpvOpUnreachable;
+         opcode == SpvOpUnreachable || opcode == SpvOpTerminateInvocation;
 }
 
 bool spvOpcodeIsBlockTerminator(SpvOp opcode) {
@@ -700,6 +701,7 @@ std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode) {
     case SpvOpAtomicIIncrement:
     case SpvOpAtomicIDecrement:
     case SpvOpAtomicIAdd:
+    case SpvOpAtomicFAddEXT:
     case SpvOpAtomicISub:
     case SpvOpAtomicSMin:
     case SpvOpAtomicUMin:

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

@@ -38,6 +38,8 @@ const uint32_t kLoopMergeMergeBlockIdInIdx = 0;
 const uint32_t kLoopMergeContinueBlockIdInIdx = 1;
 const uint32_t kCopyMemoryTargetAddrInIdx = 0;
 const uint32_t kCopyMemorySourceAddrInIdx = 1;
+const uint32_t kDebugDeclareOperandVariableIndex = 5;
+const uint32_t kGlobalVariableVariableIndex = 12;
 
 // Sorting functor to present annotation instructions in an easy-to-process
 // order. The functor orders by opcode first and falls back on unique id
@@ -470,6 +472,19 @@ bool AggressiveDCEPass::AggressiveDCE(Function* func) {
       if (varId != 0) {
         ProcessLoad(func, varId);
       }
+      // If DebugDeclare, process as load of variable
+    } else if (liveInst->GetOpenCL100DebugOpcode() ==
+               OpenCLDebugInfo100DebugDeclare) {
+      uint32_t varId =
+          liveInst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
+      ProcessLoad(func, varId);
+      // If DebugValue with Deref, process as load of variable
+    } else if (liveInst->GetOpenCL100DebugOpcode() ==
+               OpenCLDebugInfo100DebugValue) {
+      uint32_t varId = context()
+                           ->get_debug_info_mgr()
+                           ->GetVariableIdOfDebugValueUsedForDeclare(liveInst);
+      if (varId != 0) ProcessLoad(func, varId);
       // If merge, add other branches that are part of its control structure
     } else if (liveInst->opcode() == SpvOpLoopMerge ||
                liveInst->opcode() == SpvOpSelectionMerge) {
@@ -621,6 +636,18 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
       }
     }
   }
+
+  // For each DebugInfo GlobalVariable keep all operands except the Variable.
+  // Later, if the variable is dead, we will set the operand to DebugInfoNone.
+  for (auto& dbg : get_module()->ext_inst_debuginfo()) {
+    if (dbg.GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugGlobalVariable)
+      continue;
+    dbg.ForEachInId([this](const uint32_t* iid) {
+      Instruction* inInst = get_def_use_mgr()->GetDef(*iid);
+      if (inInst->opcode() == SpvOpVariable) return;
+      AddToWorklist(inInst);
+    });
+  }
 }
 
 Pass::Status AggressiveDCEPass::ProcessImpl() {
@@ -840,6 +867,26 @@ bool AggressiveDCEPass::ProcessGlobalValues() {
     }
   }
 
+  for (auto& dbg : get_module()->ext_inst_debuginfo()) {
+    if (!IsDead(&dbg)) continue;
+    // Save GlobalVariable if its variable is live, otherwise null out variable
+    // index
+    if (dbg.GetOpenCL100DebugOpcode() ==
+        OpenCLDebugInfo100DebugGlobalVariable) {
+      auto var_id = dbg.GetSingleWordOperand(kGlobalVariableVariableIndex);
+      Instruction* var_inst = get_def_use_mgr()->GetDef(var_id);
+      if (!IsDead(var_inst)) continue;
+      context()->ForgetUses(&dbg);
+      dbg.SetOperand(
+          kGlobalVariableVariableIndex,
+          {context()->get_debug_info_mgr()->GetDebugInfoNone()->result_id()});
+      context()->AnalyzeUses(&dbg);
+      continue;
+    }
+    to_kill_.push_back(&dbg);
+    modified = true;
+  }
+
   // Since ADCE is disabled for non-shaders, we don't check for export linkage
   // attributes here.
   for (auto& val : get_module()->types_values()) {
@@ -939,6 +986,7 @@ void AggressiveDCEPass::InitExtensions() {
       "SPV_KHR_ray_tracing",
       "SPV_EXT_fragment_invocation_density",
       "SPV_EXT_physical_storage_buffer",
+      "SPV_KHR_terminate_invocation",
   });
 }
 

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

@@ -214,6 +214,7 @@ bool CodeSinkingPass::HasUniformMemorySync() {
       case SpvOpAtomicIIncrement:
       case SpvOpAtomicIDecrement:
       case SpvOpAtomicIAdd:
+      case SpvOpAtomicFAddEXT:
       case SpvOpAtomicISub:
       case SpvOpAtomicSMin:
       case SpvOpAtomicUMin:

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

@@ -148,6 +148,11 @@ class DebugInfoManager {
   // Erases |instr| from data structures of this class.
   void ClearDebugInfo(Instruction* instr);
 
+  // Returns the id of Value operand if |inst| is DebugValue who has Deref
+  // operation and its Value operand is a result id of OpVariable with
+  // Function storage class. Otherwise, returns 0.
+  uint32_t GetVariableIdOfDebugValueUsedForDeclare(Instruction* inst);
+
  private:
   IRContext* context() { return context_; }
 
@@ -178,11 +183,6 @@ class DebugInfoManager {
   // Returns a DebugExpression instruction without Operation operands.
   Instruction* GetEmptyDebugExpression();
 
-  // Returns the id of Value operand if |inst| is DebugValue who has Deref
-  // operation and its Value operand is a result id of OpVariable with
-  // Function storage class. Otherwise, returns 0.
-  uint32_t GetVariableIdOfDebugValueUsedForDeclare(Instruction* inst);
-
   // Returns true if a scope |ancestor| is |scope| or an ancestor scope
   // of |scope|.
   bool IsAncestorOfScope(uint32_t scope, uint32_t ancestor);

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

@@ -176,7 +176,8 @@ void BasicBlockSuccessorHelper<BBType>::CreateSuccessorMap(
     // The tree construction requires 1 entry point, so we add a dummy node
     // that is connected to all function exiting basic blocks.
     // An exiting basic block is a block with an OpKill, OpUnreachable,
-    // OpReturn or OpReturnValue as terminator instruction.
+    // OpReturn, OpReturnValue, or OpTerminateInvocation  as terminator
+    // instruction.
     for (BasicBlock& bb : f) {
       if (bb.hasSuccessor()) {
         BasicBlockListTy& pred_list = predecessors_[&bb];

+ 8 - 5
3rdparty/spirv-tools/source/opt/inline_pass.cpp

@@ -384,7 +384,8 @@ std::unique_ptr<BasicBlock> InlinePass::InlineReturn(
   for (auto callee_block_itr = calleeFn->begin();
        callee_block_itr != calleeFn->end(); ++callee_block_itr) {
     if (callee_block_itr->tail()->opcode() == SpvOpUnreachable ||
-        callee_block_itr->tail()->opcode() == SpvOpKill) {
+        callee_block_itr->tail()->opcode() == SpvOpKill ||
+        callee_block_itr->tail()->opcode() == SpvOpTerminateInvocation) {
       returnLabelId = context()->TakeNextId();
       break;
     }
@@ -738,16 +739,18 @@ bool InlinePass::IsInlinableFunction(Function* func) {
   bool func_is_called_from_continue =
       funcs_called_from_continue_.count(func->result_id()) != 0;
 
-  if (func_is_called_from_continue && ContainsKill(func)) {
+  if (func_is_called_from_continue && ContainsKillOrTerminateInvocation(func)) {
     return false;
   }
 
   return true;
 }
 
-bool InlinePass::ContainsKill(Function* func) const {
-  return !func->WhileEachInst(
-      [](Instruction* inst) { return inst->opcode() != SpvOpKill; });
+bool InlinePass::ContainsKillOrTerminateInvocation(Function* func) const {
+  return !func->WhileEachInst([](Instruction* inst) {
+    const auto opcode = inst->opcode();
+    return (opcode != SpvOpKill) && (opcode != SpvOpTerminateInvocation);
+  });
 }
 
 void InlinePass::InitializeInline() {

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

@@ -139,8 +139,9 @@ class InlinePass : public Pass {
   // Return true if |func| is a function that can be inlined.
   bool IsInlinableFunction(Function* func);
 
-  // Returns true if |func| contains an OpKill instruction.
-  bool ContainsKill(Function* func) const;
+  // Returns true if |func| contains an OpKill or OpTerminateInvocation
+  // instruction.
+  bool ContainsKillOrTerminateInvocation(Function* func) const;
 
   // Update phis in succeeding blocks to point to new last block
   void UpdateSucceedingPhis(

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

@@ -382,6 +382,7 @@ void LocalAccessChainConvertPass::InitExtensions() {
       "SPV_KHR_ray_tracing",
       "SPV_KHR_ray_query",
       "SPV_EXT_fragment_invocation_density",
+      "SPV_KHR_terminate_invocation",
   });
 }
 

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

@@ -267,6 +267,7 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() {
       "SPV_KHR_ray_query",
       "SPV_EXT_fragment_invocation_density",
       "SPV_EXT_physical_storage_buffer",
+      "SPV_KHR_terminate_invocation",
   });
 }
 

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

@@ -121,6 +121,7 @@ void LocalSingleStoreElimPass::InitExtensionAllowList() {
       "SPV_KHR_ray_query",
       "SPV_EXT_fragment_invocation_density",
       "SPV_EXT_physical_storage_buffer",
+      "SPV_KHR_terminate_invocation",
   });
 }
 bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) {

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

@@ -997,7 +997,8 @@ bool LoopUtils::CanPerformUnroll() {
     const BasicBlock* block = context_->cfg()->block(label_id);
     if (block->ctail()->opcode() == SpvOp::SpvOpKill ||
         block->ctail()->opcode() == SpvOp::SpvOpReturn ||
-        block->ctail()->opcode() == SpvOp::SpvOpReturnValue) {
+        block->ctail()->opcode() == SpvOp::SpvOpReturnValue ||
+        block->ctail()->opcode() == SpvOp::SpvOpTerminateInvocation) {
       return false;
     }
   }

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

@@ -299,9 +299,6 @@ void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
 
     // There is at least one values that needs to be replaced.
     // First create the OpPhi instruction.
-    InstructionBuilder builder(
-        context(), &*merge_block->begin(),
-        IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
     uint32_t undef_id = Type2Undef(inst.type_id());
     std::vector<uint32_t> phi_operands;
     const std::set<uint32_t>& new_edges = new_edges_[merge_block];
@@ -318,7 +315,50 @@ void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
       phi_operands.push_back(pred_id);
     }
 
-    Instruction* new_phi = builder.AddPhi(inst.type_id(), phi_operands);
+    Instruction* new_phi = nullptr;
+    // If the instruction is a pointer and variable pointers are not an option,
+    // then we have to regenerate the instruction instead of creating an OpPhi
+    // instruction.  If not, the Spir-V will be invalid.
+    Instruction* inst_type = get_def_use_mgr()->GetDef(inst.type_id());
+    bool regenerateInstruction = false;
+    if (inst_type->opcode() == SpvOpTypePointer) {
+      if (!context()->get_feature_mgr()->HasCapability(
+              SpvCapabilityVariablePointers)) {
+        regenerateInstruction = true;
+      }
+
+      uint32_t storage_class = inst_type->GetSingleWordInOperand(0);
+      if (storage_class != SpvStorageClassWorkgroup &&
+          storage_class != SpvStorageClassStorageBuffer) {
+        regenerateInstruction = true;
+      }
+    }
+
+    if (regenerateInstruction) {
+      std::unique_ptr<Instruction> regen_inst(inst.Clone(context()));
+      uint32_t new_id = TakeNextId();
+      regen_inst->SetResultId(new_id);
+      Instruction* insert_pos = &*merge_block->begin();
+      while (insert_pos->opcode() == SpvOpPhi) {
+        insert_pos = insert_pos->NextNode();
+      }
+      new_phi = insert_pos->InsertBefore(std::move(regen_inst));
+      get_def_use_mgr()->AnalyzeInstDefUse(new_phi);
+      context()->set_instr_block(new_phi, merge_block);
+
+      new_phi->ForEachInId([dom_tree, merge_block, this](uint32_t* use_id) {
+        Instruction* use = get_def_use_mgr()->GetDef(*use_id);
+        BasicBlock* use_bb = context()->get_instr_block(use);
+        if (use_bb != nullptr && !dom_tree->Dominates(use_bb, merge_block)) {
+          CreatePhiNodesForInst(merge_block, *use);
+        }
+      });
+    } else {
+      InstructionBuilder builder(
+          context(), &*merge_block->begin(),
+          IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+      new_phi = builder.AddPhi(inst.type_id(), phi_operands);
+    }
     uint32_t result_of_phi = new_phi->result_id();
 
     // Update all of the users to use the result of the new OpPhi.

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

@@ -60,7 +60,8 @@ inline bool IsSpecConstantInst(SpvOp opcode) {
   return opcode >= SpvOpSpecConstantTrue && opcode <= SpvOpSpecConstantOp;
 }
 inline bool IsTerminatorInst(SpvOp opcode) {
-  return opcode >= SpvOpBranch && opcode <= SpvOpUnreachable;
+  return (opcode >= SpvOpBranch && opcode <= SpvOpUnreachable) ||
+         (opcode == SpvOpTerminateInvocation);
 }
 
 }  // namespace opt

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

@@ -141,6 +141,7 @@ bool ReplaceInvalidOpcodePass::IsFragmentShaderOnlyInstruction(
       // TODO: Teach |ReplaceInstruction| to handle block terminators.  Then
       // uncomment the OpKill case.
       // case SpvOpKill:
+      // case SpvOpTerminateInstruction:
       return true;
     default:
       return false;

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

@@ -27,7 +27,8 @@ Pass::Status WrapOpKill::Process() {
   for (uint32_t func_id : func_to_process) {
     Function* func = context()->GetFunction(func_id);
     bool successful = func->WhileEachInst([this, &modified](Instruction* inst) {
-      if (inst->opcode() == SpvOpKill) {
+      const auto opcode = inst->opcode();
+      if ((opcode == SpvOpKill) || (opcode == SpvOpTerminateInvocation)) {
         modified = true;
         if (!ReplaceWithFunctionCall(inst)) {
           return false;
@@ -46,16 +47,22 @@ Pass::Status WrapOpKill::Process() {
            "The function should only be generated if something was modified.");
     context()->AddFunction(std::move(opkill_function_));
   }
+  if (opterminateinvocation_function_ != nullptr) {
+    assert(modified &&
+           "The function should only be generated if something was modified.");
+    context()->AddFunction(std::move(opterminateinvocation_function_));
+  }
   return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
 }
 
 bool WrapOpKill::ReplaceWithFunctionCall(Instruction* inst) {
-  assert(inst->opcode() == SpvOpKill &&
-         "|inst| must be an OpKill instruction.");
+  assert((inst->opcode() == SpvOpKill ||
+          inst->opcode() == SpvOpTerminateInvocation) &&
+         "|inst| must be an OpKill or OpTerminateInvocation instruction.");
   InstructionBuilder ir_builder(
       context(), inst,
       IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
-  uint32_t func_id = GetOpKillFuncId();
+  uint32_t func_id = GetKillingFuncId(inst->opcode());
   if (func_id == 0) {
     return false;
   }
@@ -108,13 +115,20 @@ uint32_t WrapOpKill::GetVoidFunctionTypeId() {
   return type_mgr->GetTypeInstruction(&func_type);
 }
 
-uint32_t WrapOpKill::GetOpKillFuncId() {
-  if (opkill_function_ != nullptr) {
-    return opkill_function_->result_id();
+uint32_t WrapOpKill::GetKillingFuncId(SpvOp opcode) {
+  //  Parameterize by opcode
+  assert(opcode == SpvOpKill || opcode == SpvOpTerminateInvocation);
+
+  std::unique_ptr<Function>* const killing_func =
+      (opcode == SpvOpKill) ? &opkill_function_
+                            : &opterminateinvocation_function_;
+
+  if (*killing_func != nullptr) {
+    return (*killing_func)->result_id();
   }
 
-  uint32_t opkill_func_id = TakeNextId();
-  if (opkill_func_id == 0) {
+  uint32_t killing_func_id = TakeNextId();
+  if (killing_func_id == 0) {
     return 0;
   }
 
@@ -125,15 +139,15 @@ uint32_t WrapOpKill::GetOpKillFuncId() {
 
   // Generate the function start instruction
   std::unique_ptr<Instruction> func_start(new Instruction(
-      context(), SpvOpFunction, void_type_id, opkill_func_id, {}));
+      context(), SpvOpFunction, void_type_id, killing_func_id, {}));
   func_start->AddOperand({SPV_OPERAND_TYPE_FUNCTION_CONTROL, {0}});
   func_start->AddOperand({SPV_OPERAND_TYPE_ID, {GetVoidFunctionTypeId()}});
-  opkill_function_.reset(new Function(std::move(func_start)));
+  (*killing_func).reset(new Function(std::move(func_start)));
 
   // Generate the function end instruction
   std::unique_ptr<Instruction> func_end(
       new Instruction(context(), SpvOpFunctionEnd, 0, 0, {}));
-  opkill_function_->SetFunctionEnd(std::move(func_end));
+  (*killing_func)->SetFunctionEnd(std::move(func_end));
 
   // Create the one basic block for the function.
   uint32_t lab_id = TakeNextId();
@@ -146,21 +160,22 @@ uint32_t WrapOpKill::GetOpKillFuncId() {
 
   // Add the OpKill to the basic block
   std::unique_ptr<Instruction> kill_inst(
-      new Instruction(context(), SpvOpKill, 0, 0, {}));
+      new Instruction(context(), opcode, 0, 0, {}));
   bb->AddInstruction(std::move(kill_inst));
 
   // Add the bb to the function
-  bb->SetParent(opkill_function_.get());
-  opkill_function_->AddBasicBlock(std::move(bb));
+  bb->SetParent((*killing_func).get());
+  (*killing_func)->AddBasicBlock(std::move(bb));
 
   // Add the function to the module.
   if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) {
-    opkill_function_->ForEachInst(
-        [this](Instruction* inst) { context()->AnalyzeDefUse(inst); });
+    (*killing_func)->ForEachInst([this](Instruction* inst) {
+      context()->AnalyzeDefUse(inst);
+    });
   }
 
   if (context()->AreAnalysesValid(IRContext::kAnalysisInstrToBlockMapping)) {
-    for (BasicBlock& basic_block : *opkill_function_) {
+    for (BasicBlock& basic_block : *(*killing_func)) {
       context()->set_instr_block(basic_block.GetLabelInst(), &basic_block);
       for (Instruction& inst : basic_block) {
         context()->set_instr_block(&inst, &basic_block);
@@ -168,7 +183,7 @@ uint32_t WrapOpKill::GetOpKillFuncId() {
     }
   }
 
-  return opkill_function_->result_id();
+  return (*killing_func)->result_id();
 }
 
 uint32_t WrapOpKill::GetOwningFunctionsReturnType(Instruction* inst) {

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