Prechádzať zdrojové kódy

Updated spirv-tools.

Бранимир Караџић 6 rokov pred
rodič
commit
70cbb974c4
91 zmenil súbory, kde vykonal 8316 pridanie a 1243 odobranie
  1. 0 4
      3rdparty/spirv-tools/BUILD.gn
  2. 37 6
      3rdparty/spirv-tools/CHANGES
  3. 1 1
      3rdparty/spirv-tools/include/generated/build-version.inc
  4. 22 0
      3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
  5. 5 3
      3rdparty/spirv-tools/source/fuzz/force_render_red.cpp
  6. 31 0
      3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
  7. 25 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp
  8. 32 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_context.h
  9. 99 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp
  10. 42 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h
  11. 61 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
  12. 39 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h
  13. 0 2
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_useful_constructs.cpp
  14. 73 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
  15. 39 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.h
  16. 121 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
  17. 15 12
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.h
  18. 76 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
  19. 39 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.h
  20. 17 6
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
  21. 328 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp
  22. 79 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.h
  23. 29 102
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp
  24. 15 10
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
  25. 48 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
  26. 12 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
  27. 28 56
      3rdparty/spirv-tools/source/fuzz/id_use_descriptor.cpp
  28. 12 13
      3rdparty/spirv-tools/source/fuzz/id_use_descriptor.h
  29. 70 0
      3rdparty/spirv-tools/source/fuzz/instruction_descriptor.cpp
  30. 40 0
      3rdparty/spirv-tools/source/fuzz/instruction_descriptor.h
  31. 120 17
      3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto
  32. 35 14
      3rdparty/spirv-tools/source/fuzz/transformation.cpp
  33. 110 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.cpp
  34. 58 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.h
  35. 310 0
      3rdparty/spirv-tools/source/fuzz/transformation_construct_composite.cpp
  36. 88 0
      3rdparty/spirv-tools/source/fuzz/transformation_construct_composite.h
  37. 3 47
      3rdparty/spirv-tools/source/fuzz/transformation_copy_object.cpp
  38. 2 10
      3rdparty/spirv-tools/source/fuzz/transformation_copy_object.h
  39. 2 2
      3rdparty/spirv-tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp
  40. 2 2
      3rdparty/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.cpp
  41. 94 10
      3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp
  42. 100 0
      3rdparty/spirv-tools/source/fuzz/transformation_set_function_control.cpp
  43. 58 0
      3rdparty/spirv-tools/source/fuzz/transformation_set_function_control.h
  44. 216 0
      3rdparty/spirv-tools/source/fuzz/transformation_set_loop_control.cpp
  45. 79 0
      3rdparty/spirv-tools/source/fuzz/transformation_set_loop_control.h
  46. 60 0
      3rdparty/spirv-tools/source/fuzz/transformation_set_selection_control.cpp
  47. 54 0
      3rdparty/spirv-tools/source/fuzz/transformation_set_selection_control.h
  48. 8 0
      3rdparty/spirv-tools/source/opt/ir_context.cpp
  49. 30 0
      3rdparty/spirv-tools/source/opt/type_manager.cpp
  50. 41 0
      3rdparty/spirv-tools/source/opt/types.cpp
  51. 33 0
      3rdparty/spirv-tools/source/opt/types.h
  52. 1 1
      3rdparty/spirv-tools/source/print.cpp
  53. 0 4
      3rdparty/spirv-tools/source/reduce/CMakeLists.txt
  54. 1 1
      3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp
  55. 18 2
      3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp
  56. 3 1
      3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h
  57. 115 91
      3rdparty/spirv-tools/source/reduce/reducer.cpp
  58. 22 5
      3rdparty/spirv-tools/source/reduce/reducer.h
  59. 17 0
      3rdparty/spirv-tools/source/reduce/reduction_util.cpp
  60. 5 0
      3rdparty/spirv-tools/source/reduce/reduction_util.h
  61. 0 45
      3rdparty/spirv-tools/source/reduce/remove_opname_instruction_reduction_opportunity_finder.cpp
  62. 0 45
      3rdparty/spirv-tools/source/reduce/remove_opname_instruction_reduction_opportunity_finder.h
  63. 0 49
      3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp
  64. 87 6
      3rdparty/spirv-tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp
  65. 3 1
      3rdparty/spirv-tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h
  66. 0 17
      3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp
  67. 0 5
      3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.h
  68. 66 2
      3rdparty/spirv-tools/source/val/validate_cfg.cpp
  69. 3 0
      3rdparty/spirv-tools/source/val/validate_misc.cpp
  70. 5 0
      3rdparty/spirv-tools/test/fuzz/CMakeLists.txt
  71. 11 9
      3rdparty/spirv-tools/test/fuzz/fuzzer_replayer_test.cpp
  72. 3 3
      3rdparty/spirv-tools/test/fuzz/fuzzer_shrinker_test.cpp
  73. 46 0
      3rdparty/spirv-tools/test/fuzz/transformation_add_dead_continue_test.cpp
  74. 194 0
      3rdparty/spirv-tools/test/fuzz/transformation_add_no_contraction_decoration_test.cpp
  75. 1248 0
      3rdparty/spirv-tools/test/fuzz/transformation_construct_composite_test.cpp
  76. 13 10
      3rdparty/spirv-tools/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp
  77. 31 30
      3rdparty/spirv-tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp
  78. 1092 74
      3rdparty/spirv-tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp
  79. 251 0
      3rdparty/spirv-tools/test/fuzz/transformation_set_function_control_test.cpp
  80. 968 0
      3rdparty/spirv-tools/test/fuzz/transformation_set_loop_control_test.cpp
  81. 219 0
      3rdparty/spirv-tools/test/fuzz/transformation_set_selection_control_test.cpp
  82. 3 5
      3rdparty/spirv-tools/test/opt/if_conversion_test.cpp
  83. 86 1
      3rdparty/spirv-tools/test/opt/ir_context_test.cpp
  84. 6 1
      3rdparty/spirv-tools/test/opt/type_manager_test.cpp
  85. 0 2
      3rdparty/spirv-tools/test/reduce/CMakeLists.txt
  86. 186 69
      3rdparty/spirv-tools/test/reduce/reducer_test.cpp
  87. 0 225
      3rdparty/spirv-tools/test/reduce/remove_opname_instruction_test.cpp
  88. 0 177
      3rdparty/spirv-tools/test/reduce/remove_relaxed_precision_decoration_test.cpp
  89. 322 43
      3rdparty/spirv-tools/test/reduce/remove_unreferenced_instruction_test.cpp
  90. 351 1
      3rdparty/spirv-tools/test/val/val_cfg_test.cpp
  91. 2 1
      3rdparty/spirv-tools/test/val/val_misc_test.cpp

+ 0 - 4
3rdparty/spirv-tools/BUILD.gn

@@ -734,10 +734,6 @@ static_library("spvtools_reduce") {
     "source/reduce/remove_function_reduction_opportunity_finder.h",
     "source/reduce/remove_function_reduction_opportunity_finder.h",
     "source/reduce/remove_instruction_reduction_opportunity.cpp",
     "source/reduce/remove_instruction_reduction_opportunity.cpp",
     "source/reduce/remove_instruction_reduction_opportunity.h",
     "source/reduce/remove_instruction_reduction_opportunity.h",
-    "source/reduce/remove_opname_instruction_reduction_opportunity_finder.cpp",
-    "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h",
-    "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp",
-    "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h",
     "source/reduce/remove_selection_reduction_opportunity.cpp",
     "source/reduce/remove_selection_reduction_opportunity.cpp",
     "source/reduce/remove_selection_reduction_opportunity.h",
     "source/reduce/remove_selection_reduction_opportunity.h",
     "source/reduce/remove_selection_reduction_opportunity_finder.cpp",
     "source/reduce/remove_selection_reduction_opportunity_finder.cpp",

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

@@ -1,7 +1,11 @@
 Revision history for SPIRV-Tools
 Revision history for SPIRV-Tools
 
 
-v2019.5-dev 2019-08-08
+v2019.5-dev 2019-10-09
  - General:
  - General:
+   - Export SPIRV-Tools targets on installation
+   - SPIRV-Tools support for SPIR-V 1.5 (#2865)
+   - Add WebGPU SPIR-V Assembler in JavaScript. (#2876)
+   - Add Bazel build configuration. (#2891)
  - Optimizer
  - Optimizer
    - Add descriptor array scalar replacement (#2742)
    - Add descriptor array scalar replacement (#2742)
    - Add pass to wrap OpKill in a function call (#2790)
    - Add pass to wrap OpKill in a function call (#2790)
@@ -9,17 +13,44 @@ v2019.5-dev 2019-08-08
    - Add pass to replace AMD shader ballot extension (#2811)
    - Add pass to replace AMD shader ballot extension (#2811)
    - Add pass to make Float32 operation relax precision (#2808)
    - Add pass to make Float32 operation relax precision (#2808)
    - Add pass to make relax precision operation Float16 (#2808)
    - Add pass to make relax precision operation Float16 (#2808)
+   - Add pass to replace uses of 3 AMD extensions (#2814)
+   - Fold Min, Max, and Clamp instructions. (#2836)
+   - Better handling of OpKill in continues (#2842,#2922,#2933)
+   - Enable OpTypeCooperativeMatrix specialization (#2927)
    Fixes:
    Fixes:
-   Instrument: Fix version 2 output record write for tess eval shaders. (#2782)
-   Instrument: Add support for Buffer Device Address extension (#2792)
-   Fix check for changed binary in API call. (#2798)
+   - Instrument: Fix version 2 output record write for tess eval shaders. (#2782)
+   - Instrument: Add support for Buffer Device Address extension (#2792)
+   - Fix check for changed binary in API call. (#2798)
+   - For WebGPU<->Vulkan optimization, set correct execution environment (#2834)
+   - Handle OpConstantNull in copy-prop-arrays. (#2870)
+   - Use OpReturn* in wrap-opkill (#2886)
  - Validator
  - Validator
+   - Add generic builtin validation of target (#2843)
+   - Extra resource interface validation (#2864)
+   - Adding valilidation checks for OpEntryPoint duplicate names and execution mode (#2862)
+   - Relaxed bitcast with pointers (#2878)
+   - Validate physical storage buffer restrictions (#2930)
+   - Add SPV_KHR_shader_clock validation (#2879)
    Fixes:
    Fixes:
-   Fix validation of constant matrices (#2794)
-   Update "remquor" validation
+   - Fix validation of constant matrices (#2794)
+   - Update "remquor" validation
+   - Only allow previously declared forward refs in structs (#2920)
  - Reduce
  - Reduce
    - Remove relaxed precision decorations (#2797)
    - Remove relaxed precision decorations (#2797)
+   - Reduce/fuzz: improve command line args (#2932)
+   - Improve remove unref instr pass (#2945)
    Fixes:
    Fixes:
+ - Fuzz
+   - Fix add-dead-break and add-dead-continue passes to respect dominance (#2838)
+   - Add fuzzer pass to copy objects (#2853)
+   - Add fuzzer pass to replace ids with synonyms (#2857)
+   - Allow validation during spirv-fuzz replay (#2873)
+   - Employ the "swarm testing" idea in spirv-fuzz (#2890)
+   - reduce/fuzz: improve command line args (#2932)
+   - option to convert shader into a form that renders red (#2934)
+   - Add fuzzer pass to change selection controls (#2944)
+   - add transformation and pass to construct composites (#2941)
+
 
 
 v2019.4 2019-08-08
 v2019.4 2019-08-08
  - General:
  - General:

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

@@ -1 +1 @@
-"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-102-gc18c9ff6"
+"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-120-g2276e597"

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

@@ -37,14 +37,20 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass.h
         fuzzer_pass.h
         fuzzer_pass_add_dead_breaks.h
         fuzzer_pass_add_dead_breaks.h
         fuzzer_pass_add_dead_continues.h
         fuzzer_pass_add_dead_continues.h
+        fuzzer_pass_add_no_contraction_decorations.h
         fuzzer_pass_add_useful_constructs.h
         fuzzer_pass_add_useful_constructs.h
+        fuzzer_pass_adjust_function_controls.h
+        fuzzer_pass_adjust_loop_controls.h
+        fuzzer_pass_adjust_selection_controls.h
         fuzzer_pass_apply_id_synonyms.h
         fuzzer_pass_apply_id_synonyms.h
+        fuzzer_pass_construct_composites.h
         fuzzer_pass_copy_objects.h
         fuzzer_pass_copy_objects.h
         fuzzer_pass_obfuscate_constants.h
         fuzzer_pass_obfuscate_constants.h
         fuzzer_pass_permute_blocks.h
         fuzzer_pass_permute_blocks.h
         fuzzer_pass_split_blocks.h
         fuzzer_pass_split_blocks.h
         fuzzer_util.h
         fuzzer_util.h
         id_use_descriptor.h
         id_use_descriptor.h
+        instruction_descriptor.h
         protobufs/spirvfuzz_protobufs.h
         protobufs/spirvfuzz_protobufs.h
         pseudo_random_generator.h
         pseudo_random_generator.h
         random_generator.h
         random_generator.h
@@ -55,15 +61,20 @@ if(SPIRV_BUILD_FUZZER)
         transformation_add_constant_scalar.h
         transformation_add_constant_scalar.h
         transformation_add_dead_break.h
         transformation_add_dead_break.h
         transformation_add_dead_continue.h
         transformation_add_dead_continue.h
+        transformation_add_no_contraction_decoration.h
         transformation_add_type_boolean.h
         transformation_add_type_boolean.h
         transformation_add_type_float.h
         transformation_add_type_float.h
         transformation_add_type_int.h
         transformation_add_type_int.h
         transformation_add_type_pointer.h
         transformation_add_type_pointer.h
+        transformation_construct_composite.h
         transformation_copy_object.h
         transformation_copy_object.h
         transformation_move_block_down.h
         transformation_move_block_down.h
         transformation_replace_boolean_constant_with_constant_binary.h
         transformation_replace_boolean_constant_with_constant_binary.h
         transformation_replace_constant_with_uniform.h
         transformation_replace_constant_with_uniform.h
         transformation_replace_id_with_synonym.h
         transformation_replace_id_with_synonym.h
+        transformation_set_function_control.h
+        transformation_set_loop_control.h
+        transformation_set_selection_control.h
         transformation_split_block.h
         transformation_split_block.h
         uniform_buffer_element_descriptor.h
         uniform_buffer_element_descriptor.h
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
@@ -76,14 +87,20 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass.cpp
         fuzzer_pass.cpp
         fuzzer_pass_add_dead_breaks.cpp
         fuzzer_pass_add_dead_breaks.cpp
         fuzzer_pass_add_dead_continues.cpp
         fuzzer_pass_add_dead_continues.cpp
+        fuzzer_pass_add_no_contraction_decorations.cpp
         fuzzer_pass_add_useful_constructs.cpp
         fuzzer_pass_add_useful_constructs.cpp
+        fuzzer_pass_adjust_function_controls.cpp
+        fuzzer_pass_adjust_loop_controls.cpp
+        fuzzer_pass_adjust_selection_controls.cpp
         fuzzer_pass_apply_id_synonyms.cpp
         fuzzer_pass_apply_id_synonyms.cpp
+        fuzzer_pass_construct_composites.cpp
         fuzzer_pass_copy_objects.cpp
         fuzzer_pass_copy_objects.cpp
         fuzzer_pass_obfuscate_constants.cpp
         fuzzer_pass_obfuscate_constants.cpp
         fuzzer_pass_permute_blocks.cpp
         fuzzer_pass_permute_blocks.cpp
         fuzzer_pass_split_blocks.cpp
         fuzzer_pass_split_blocks.cpp
         fuzzer_util.cpp
         fuzzer_util.cpp
         id_use_descriptor.cpp
         id_use_descriptor.cpp
+        instruction_descriptor.cpp
         pseudo_random_generator.cpp
         pseudo_random_generator.cpp
         random_generator.cpp
         random_generator.cpp
         replayer.cpp
         replayer.cpp
@@ -93,15 +110,20 @@ if(SPIRV_BUILD_FUZZER)
         transformation_add_constant_scalar.cpp
         transformation_add_constant_scalar.cpp
         transformation_add_dead_break.cpp
         transformation_add_dead_break.cpp
         transformation_add_dead_continue.cpp
         transformation_add_dead_continue.cpp
+        transformation_add_no_contraction_decoration.cpp
         transformation_add_type_boolean.cpp
         transformation_add_type_boolean.cpp
         transformation_add_type_float.cpp
         transformation_add_type_float.cpp
         transformation_add_type_int.cpp
         transformation_add_type_int.cpp
         transformation_add_type_pointer.cpp
         transformation_add_type_pointer.cpp
+        transformation_construct_composite.cpp
         transformation_copy_object.cpp
         transformation_copy_object.cpp
         transformation_move_block_down.cpp
         transformation_move_block_down.cpp
         transformation_replace_boolean_constant_with_constant_binary.cpp
         transformation_replace_boolean_constant_with_constant_binary.cpp
         transformation_replace_constant_with_uniform.cpp
         transformation_replace_constant_with_uniform.cpp
         transformation_replace_id_with_synonym.cpp
         transformation_replace_id_with_synonym.cpp
+        transformation_set_function_control.cpp
+        transformation_set_loop_control.cpp
+        transformation_set_selection_control.cpp
         transformation_split_block.cpp
         transformation_split_block.cpp
         uniform_buffer_element_descriptor.cpp
         uniform_buffer_element_descriptor.cpp
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc

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

@@ -15,6 +15,7 @@
 #include "source/fuzz/force_render_red.h"
 #include "source/fuzz/force_render_red.h"
 
 
 #include "source/fuzz/fact_manager.h"
 #include "source/fuzz/fact_manager.h"
+#include "source/fuzz/instruction_descriptor.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/transformation_replace_constant_with_uniform.h"
 #include "source/fuzz/transformation_replace_constant_with_uniform.h"
 #include "source/fuzz/uniform_buffer_element_descriptor.h"
 #include "source/fuzz/uniform_buffer_element_descriptor.h"
@@ -147,9 +148,10 @@ MakeConstantUniformReplacement(opt::IRContext* ir_context,
                                uint32_t greater_than_instruction,
                                uint32_t greater_than_instruction,
                                uint32_t in_operand_index) {
                                uint32_t in_operand_index) {
   return MakeUnique<TransformationReplaceConstantWithUniform>(
   return MakeUnique<TransformationReplaceConstantWithUniform>(
-      transformation::MakeIdUseDescriptor(constant_id, SpvOpFOrdGreaterThan,
-                                          in_operand_index,
-                                          greater_than_instruction, 0),
+      MakeIdUseDescriptor(constant_id,
+                          MakeInstructionDescriptor(greater_than_instruction,
+                                                    SpvOpFOrdGreaterThan, 0),
+                          in_operand_index),
       fact_manager.GetUniformDescriptorsForConstant(ir_context, constant_id)[0],
       fact_manager.GetUniformDescriptorsForConstant(ir_context, constant_id)[0],
       ir_context->TakeNextId(), ir_context->TakeNextId());
       ir_context->TakeNextId(), ir_context->TakeNextId());
 }
 }

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

@@ -22,8 +22,13 @@
 #include "source/fuzz/fuzzer_context.h"
 #include "source/fuzz/fuzzer_context.h"
 #include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
 #include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
 #include "source/fuzz/fuzzer_pass_add_dead_continues.h"
 #include "source/fuzz/fuzzer_pass_add_dead_continues.h"
+#include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
 #include "source/fuzz/fuzzer_pass_add_useful_constructs.h"
 #include "source/fuzz/fuzzer_pass_add_useful_constructs.h"
+#include "source/fuzz/fuzzer_pass_adjust_function_controls.h"
+#include "source/fuzz/fuzzer_pass_adjust_loop_controls.h"
+#include "source/fuzz/fuzzer_pass_adjust_selection_controls.h"
 #include "source/fuzz/fuzzer_pass_apply_id_synonyms.h"
 #include "source/fuzz/fuzzer_pass_apply_id_synonyms.h"
+#include "source/fuzz/fuzzer_pass_construct_composites.h"
 #include "source/fuzz/fuzzer_pass_copy_objects.h"
 #include "source/fuzz/fuzzer_pass_copy_objects.h"
 #include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
 #include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
 #include "source/fuzz/fuzzer_pass_permute_blocks.h"
 #include "source/fuzz/fuzzer_pass_permute_blocks.h"
@@ -138,6 +143,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
     MaybeAddPass<FuzzerPassApplyIdSynonyms>(&passes, ir_context.get(),
     MaybeAddPass<FuzzerPassApplyIdSynonyms>(&passes, ir_context.get(),
                                             &fact_manager, &fuzzer_context,
                                             &fact_manager, &fuzzer_context,
                                             transformation_sequence_out);
                                             transformation_sequence_out);
+    MaybeAddPass<FuzzerPassConstructComposites>(&passes, ir_context.get(),
+                                                &fact_manager, &fuzzer_context,
+                                                transformation_sequence_out);
     MaybeAddPass<FuzzerPassCopyObjects>(&passes, ir_context.get(),
     MaybeAddPass<FuzzerPassCopyObjects>(&passes, ir_context.get(),
                                         &fact_manager, &fuzzer_context,
                                         &fact_manager, &fuzzer_context,
                                         transformation_sequence_out);
                                         transformation_sequence_out);
@@ -162,6 +170,29 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
     passes[fuzzer_context.RandomIndex(passes)]->Apply();
     passes[fuzzer_context.RandomIndex(passes)]->Apply();
   }
   }
 
 
+  // Now apply some passes that it does not make sense to apply repeatedly,
+  // as they do not unlock other passes.
+  std::vector<std::unique_ptr<FuzzerPass>> final_passes;
+  MaybeAddPass<FuzzerPassAdjustFunctionControls>(&passes, ir_context.get(),
+                                                 &fact_manager, &fuzzer_context,
+                                                 transformation_sequence_out);
+  MaybeAddPass<FuzzerPassAdjustLoopControls>(&passes, ir_context.get(),
+                                             &fact_manager, &fuzzer_context,
+                                             transformation_sequence_out);
+  MaybeAddPass<FuzzerPassAdjustSelectionControls>(
+      &passes, ir_context.get(), &fact_manager, &fuzzer_context,
+      transformation_sequence_out);
+  for (auto& pass : final_passes) {
+    pass->Apply();
+  }
+
+  if (fuzzer_context.ChooseEven()) {
+    FuzzerPassAddNoContractionDecorations(ir_context.get(), &fact_manager,
+                                          &fuzzer_context,
+                                          transformation_sequence_out)
+        .Apply();
+  }
+
   // Encode the module as a binary.
   // Encode the module as a binary.
   ir_context->module()->ToBinary(binary_out, false);
   ir_context->module()->ToBinary(binary_out, false);
 
 

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

@@ -25,12 +25,25 @@ namespace {
 
 
 const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBreak = {5, 80};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBreak = {5, 80};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadContinue = {5, 80};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadContinue = {5, 80};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingNoContractionDecoration = {
+    5, 70};
+const std::pair<uint32_t, uint32_t> kChanceOfAdjustingFunctionControl = {20,
+                                                                         70};
+const std::pair<uint32_t, uint32_t> kChanceOfAdjustingLoopControl = {20, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfAdjustingSelectionControl = {20,
+                                                                          90};
 const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfConstructingComposite = {20, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
 const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
 
 
+// Default limits for various quantities that are chosen during fuzzing.
+// Keep them in alphabetical order.
+const uint32_t kDefaultMaxLoopControlPartialCount = 100;
+const uint32_t kDefaultMaxLoopControlPeelCount = 100;
+
 // Default functions for controlling how deep to go during recursive
 // Default functions for controlling how deep to go during recursive
 // generation/transformation. Keep them in alphabetical order.
 // generation/transformation. Keep them in alphabetical order.
 
 
@@ -53,6 +66,16 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
       ChooseBetweenMinAndMax(kChanceOfAddingDeadBreak);
       ChooseBetweenMinAndMax(kChanceOfAddingDeadBreak);
   chance_of_adding_dead_continue_ =
   chance_of_adding_dead_continue_ =
       ChooseBetweenMinAndMax(kChanceOfAddingDeadContinue);
       ChooseBetweenMinAndMax(kChanceOfAddingDeadContinue);
+  chance_of_adding_no_contraction_decoration_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingNoContractionDecoration);
+  chance_of_adjusting_function_control_ =
+      ChooseBetweenMinAndMax(kChanceOfAdjustingFunctionControl);
+  chance_of_adjusting_loop_control_ =
+      ChooseBetweenMinAndMax(kChanceOfAdjustingLoopControl);
+  chance_of_adjusting_selection_control_ =
+      ChooseBetweenMinAndMax(kChanceOfAdjustingSelectionControl);
+  chance_of_constructing_composite_ =
+      ChooseBetweenMinAndMax(kChanceOfConstructingComposite);
   chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject);
   chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject);
   chance_of_moving_block_down_ =
   chance_of_moving_block_down_ =
       ChooseBetweenMinAndMax(kChanceOfMovingBlockDown);
       ChooseBetweenMinAndMax(kChanceOfMovingBlockDown);
@@ -61,6 +84,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
   chance_of_replacing_id_with_synonym_ =
   chance_of_replacing_id_with_synonym_ =
       ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
       ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
   chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
   chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
+  max_loop_control_partial_count_ = kDefaultMaxLoopControlPartialCount;
+  max_loop_control_peel_count_ = kDefaultMaxLoopControlPeelCount;
 }
 }
 
 
 FuzzerContext::~FuzzerContext() = default;
 FuzzerContext::~FuzzerContext() = default;

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

@@ -62,6 +62,21 @@ class FuzzerContext {
   uint32_t GetChanceOfAddingDeadContinue() {
   uint32_t GetChanceOfAddingDeadContinue() {
     return chance_of_adding_dead_continue_;
     return chance_of_adding_dead_continue_;
   }
   }
+  uint32_t GetChanceOfAddingNoContractionDecoration() {
+    return chance_of_adding_no_contraction_decoration_;
+  }
+  uint32_t GetChanceOfAdjustingFunctionControl() {
+    return chance_of_adjusting_function_control_;
+  }
+  uint32_t GetChanceOfAdjustingLoopControl() {
+    return chance_of_adjusting_loop_control_;
+  }
+  uint32_t GetChanceOfAdjustingSelectionControl() {
+    return chance_of_adjusting_selection_control_;
+  }
+  uint32_t GetChanceOfConstructingComposite() {
+    return chance_of_constructing_composite_;
+  }
   uint32_t GetChanceOfCopyingObject() { return chance_of_copying_object_; }
   uint32_t GetChanceOfCopyingObject() { return chance_of_copying_object_; }
   uint32_t GetChanceOfMovingBlockDown() { return chance_of_moving_block_down_; }
   uint32_t GetChanceOfMovingBlockDown() { return chance_of_moving_block_down_; }
   uint32_t GetChanceOfObfuscatingConstant() {
   uint32_t GetChanceOfObfuscatingConstant() {
@@ -71,6 +86,12 @@ class FuzzerContext {
     return chance_of_replacing_id_with_synonym_;
     return chance_of_replacing_id_with_synonym_;
   }
   }
   uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
   uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
+  uint32_t GetRandomLoopControlPeelCount() {
+    return random_generator_->RandomUint32(max_loop_control_peel_count_);
+  }
+  uint32_t GetRandomLoopControlPartialCount() {
+    return random_generator_->RandomUint32(max_loop_control_partial_count_);
+  }
 
 
   // Functions to control how deeply to recurse.
   // Functions to control how deeply to recurse.
   // Keep them in alphabetical order.
   // Keep them in alphabetical order.
@@ -88,12 +109,23 @@ class FuzzerContext {
   // Keep them in alphabetical order.
   // Keep them in alphabetical order.
   uint32_t chance_of_adding_dead_break_;
   uint32_t chance_of_adding_dead_break_;
   uint32_t chance_of_adding_dead_continue_;
   uint32_t chance_of_adding_dead_continue_;
+  uint32_t chance_of_adding_no_contraction_decoration_;
+  uint32_t chance_of_adjusting_function_control_;
+  uint32_t chance_of_adjusting_loop_control_;
+  uint32_t chance_of_adjusting_selection_control_;
+  uint32_t chance_of_constructing_composite_;
   uint32_t chance_of_copying_object_;
   uint32_t chance_of_copying_object_;
   uint32_t chance_of_moving_block_down_;
   uint32_t chance_of_moving_block_down_;
   uint32_t chance_of_obfuscating_constant_;
   uint32_t chance_of_obfuscating_constant_;
   uint32_t chance_of_replacing_id_with_synonym_;
   uint32_t chance_of_replacing_id_with_synonym_;
   uint32_t chance_of_splitting_block_;
   uint32_t chance_of_splitting_block_;
 
 
+  // Limits associated with various quantities for which random values are
+  // chosen during fuzzing.
+  // Keep them in alphabetical order.
+  uint32_t max_loop_control_partial_count_;
+  uint32_t max_loop_control_peel_count_;
+
   // Functions to determine with what probability to go deeper when generating
   // Functions to determine with what probability to go deeper when generating
   // or mutating constructs recursively.
   // or mutating constructs recursively.
   const std::function<bool(uint32_t, RandomGenerator*)>&
   const std::function<bool(uint32_t, RandomGenerator*)>&

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

@@ -27,5 +27,104 @@ FuzzerPass::FuzzerPass(opt::IRContext* ir_context, FactManager* fact_manager,
 
 
 FuzzerPass::~FuzzerPass() = default;
 FuzzerPass::~FuzzerPass() = default;
 
 
+std::vector<opt::Instruction*> FuzzerPass::FindAvailableInstructions(
+    const opt::Function& function, opt::BasicBlock* block,
+    opt::BasicBlock::iterator inst_it,
+    std::function<bool(opt::IRContext*, opt::Instruction*)>
+        instruction_is_relevant) {
+  // TODO(afd) The following is (relatively) simple, but may end up being
+  //  prohibitively inefficient, as it walks the whole dominator tree for
+  //  every instruction that is considered.
+
+  std::vector<opt::Instruction*> result;
+  // Consider all global declarations
+  for (auto& global : GetIRContext()->module()->types_values()) {
+    if (instruction_is_relevant(GetIRContext(), &global)) {
+      result.push_back(&global);
+    }
+  }
+
+  // Consider all previous instructions in this block
+  for (auto prev_inst_it = block->begin(); prev_inst_it != inst_it;
+       ++prev_inst_it) {
+    if (instruction_is_relevant(GetIRContext(), &*prev_inst_it)) {
+      result.push_back(&*prev_inst_it);
+    }
+  }
+
+  // Walk the dominator tree to consider all instructions from dominating
+  // blocks
+  auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(&function);
+  for (auto next_dominator = dominator_analysis->ImmediateDominator(block);
+       next_dominator != nullptr;
+       next_dominator =
+           dominator_analysis->ImmediateDominator(next_dominator)) {
+    for (auto& dominating_inst : *next_dominator) {
+      if (instruction_is_relevant(GetIRContext(), &dominating_inst)) {
+        result.push_back(&dominating_inst);
+      }
+    }
+  }
+  return result;
+}
+
+void FuzzerPass::MaybeAddTransformationBeforeEachInstruction(
+    std::function<uint32_t(
+        const opt::Function& function, opt::BasicBlock* block,
+        opt::BasicBlock::iterator inst_it, uint32_t base, uint32_t offset)>
+        maybe_apply_transformation) {
+  // Consider every block in every function.
+  for (auto& function : *GetIRContext()->module()) {
+    for (auto& block : function) {
+      // We now consider every instruction in the block, randomly deciding
+      // whether to apply a transformation before it.
+
+      // In order for transformations to insert new instructions, they need to
+      // be able to identify the instruction to insert before.  We enable this
+      // by tracking a base instruction, which must generate a result id, and
+      // an offset (to allow us to identify instructions that do not generate
+      // result ids).
+
+      // The initial base instruction is the block label.
+      uint32_t base = block.id();
+      uint32_t offset = 0;
+      // Consider every instruction in the block.
+      for (auto inst_it = block.begin(); inst_it != block.end(); ++inst_it) {
+        if (inst_it->HasResultId()) {
+          // In the case that the instruction has a result id, we use the
+          // instruction as its own base, with zero offset.
+          base = inst_it->result_id();
+          offset = 0;
+        } else {
+          // The instruction does not have a result id, so we need to identify
+          // it via the latest instruction that did have a result id (base), and
+          // an incremented offset.
+          offset++;
+        }
+
+        // Invoke the provided function, which might apply a transformation.
+        // Its return value informs us of how many instructions it inserted.
+        // (This will be 0 if no transformation was applied.)
+        uint32_t num_instructions_inserted =
+            maybe_apply_transformation(function, &block, inst_it, base, offset);
+
+        if (!inst_it->HasResultId()) {
+          // We are tracking the current id-less instruction via an offset,
+          // |offset|, from a previous instruction, |base|, that has an id. We
+          // increment |offset| to reflect any newly-inserted instructions.
+          //
+          // An alternative would be to reset |base| to be an id generated by
+          // a newly-inserted instruction, but that would be more complex, and
+          // sticking to a |base| that already existed before this
+          // transformation was applied makes the applicability of future
+          // transformations less tightly coupled with the presence of the just-
+          // applied transformation.
+          offset += num_instructions_inserted;
+        }
+      }
+    }
+  }
+}
+
 }  // namespace fuzz
 }  // namespace fuzz
 }  // namespace spvtools
 }  // namespace spvtools

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

@@ -15,9 +15,13 @@
 #ifndef SOURCE_FUZZ_FUZZER_PASS_H_
 #ifndef SOURCE_FUZZ_FUZZER_PASS_H_
 #define SOURCE_FUZZ_FUZZER_PASS_H_
 #define SOURCE_FUZZ_FUZZER_PASS_H_
 
 
+#include <functional>
+#include <vector>
+
 #include "source/fuzz/fact_manager.h"
 #include "source/fuzz/fact_manager.h"
 #include "source/fuzz/fuzzer_context.h"
 #include "source/fuzz/fuzzer_context.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/opt/ir_context.h"
 
 
 namespace spvtools {
 namespace spvtools {
 namespace fuzz {
 namespace fuzz {
@@ -48,6 +52,44 @@ class FuzzerPass {
     return transformations_;
     return transformations_;
   }
   }
 
 
+  // Returns all instructions that are *available* at |inst_it|, which is
+  // required to be inside block |block| of function |function| - that is, all
+  // instructions at global scope and all instructions that strictly dominate
+  // |inst_it|.
+  //
+  // Filters said instructions to return only those that satisfy the
+  // |instruction_is_relevant| predicate.  This, for instance, could ignore all
+  // instructions that have a particular decoration.
+  std::vector<opt::Instruction*> FindAvailableInstructions(
+      const opt::Function& function, opt::BasicBlock* block,
+      opt::BasicBlock::iterator inst_it,
+      std::function<bool(opt::IRContext*, opt::Instruction*)>
+          instruction_is_relevant);
+
+  // A helper method that iterates through each instruction in each block, at
+  // all times tracking a base instruction and offset that allows that latest
+  // instruction to be located even if it has no result id.
+  //
+  // The code to manipulate the base and offset is a bit fiddly, and the point
+  // of this method is to avoiding having to duplicate it in multiple
+  // transformation passes.
+  //
+  // The function |maybe_apply_transformation| is invoked for each instruction
+  // |inst_it| in block |block| of function |function| that is encountered.  The
+  // |base| and |offset| parameters to the function object allow |inst_it| to be
+  // identified.
+  //
+  // The job of |maybe_apply_transformation| is to randomly decide whether to
+  // try to apply some transformation, and then - if selected - to attempt to
+  // apply it.  The function returns the number of instructions that were
+  // inserted before |inst_it|, so that |offset| can be updated.
+  //
+  void MaybeAddTransformationBeforeEachInstruction(
+      std::function<uint32_t(
+          const opt::Function& function, opt::BasicBlock* block,
+          opt::BasicBlock::iterator inst_it, uint32_t base, uint32_t offset)>
+          maybe_apply_transformation);
+
  private:
  private:
   opt::IRContext* ir_context_;
   opt::IRContext* ir_context_;
   FactManager* fact_manager_;
   FactManager* fact_manager_;

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

@@ -0,0 +1,61 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
+
+#include "source/fuzz/transformation_add_no_contraction_decoration.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddNoContractionDecorations::FuzzerPassAddNoContractionDecorations(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations){};
+
+FuzzerPassAddNoContractionDecorations::
+    ~FuzzerPassAddNoContractionDecorations() = default;
+
+void FuzzerPassAddNoContractionDecorations::Apply() {
+  // Consider every instruction in every block in every function.
+  for (auto& function : *GetIRContext()->module()) {
+    for (auto& block : function) {
+      for (auto& inst : block) {
+        // Restrict attention to arithmetic instructions (as defined in the
+        // SPIR-V specification).
+        if (TransformationAddNoContractionDecoration::IsArithmetic(
+                inst.opcode())) {
+          // Randomly choose whether to apply the NoContraction decoration to
+          // this arithmetic instruction.
+          if (GetFuzzerContext()->ChoosePercentage(
+                  GetFuzzerContext()
+                      ->GetChanceOfAddingNoContractionDecoration())) {
+            TransformationAddNoContractionDecoration transformation(
+                inst.result_id());
+            assert(transformation.IsApplicable(GetIRContext(),
+                                               *GetFactManager()) &&
+                   "Transformation should be applicable by construction.");
+            transformation.Apply(GetIRContext(), GetFactManager());
+            *GetTransformations()->add_transformation() =
+                transformation.ToMessage();
+          }
+        }
+      }
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

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

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

@@ -24,8 +24,6 @@
 namespace spvtools {
 namespace spvtools {
 namespace fuzz {
 namespace fuzz {
 
 
-using opt::IRContext;
-
 FuzzerPassAddUsefulConstructs::FuzzerPassAddUsefulConstructs(
 FuzzerPassAddUsefulConstructs::FuzzerPassAddUsefulConstructs(
     opt::IRContext* ir_context, FactManager* fact_manager,
     opt::IRContext* ir_context, FactManager* fact_manager,
     FuzzerContext* fuzzer_context,
     FuzzerContext* fuzzer_context,

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

@@ -0,0 +1,73 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_adjust_function_controls.h"
+
+#include "source/fuzz/transformation_set_function_control.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAdjustFunctionControls::FuzzerPassAdjustFunctionControls(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations){};
+
+FuzzerPassAdjustFunctionControls::~FuzzerPassAdjustFunctionControls() = default;
+
+void FuzzerPassAdjustFunctionControls::Apply() {
+  // Consider every function in the module.
+  for (auto& function : *GetIRContext()->module()) {
+    // Randomly decide whether to adjust this function's controls.
+    if (GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()->GetChanceOfAdjustingFunctionControl())) {
+      // Grab the function control mask for the function in its present form.
+      uint32_t existing_function_control_mask =
+          function.DefInst().GetSingleWordInOperand(0);
+
+      // For the new mask, we first randomly select one of three basic masks:
+      // None, Inline or DontInline.  These are always valid (and are mutually
+      // exclusive).
+      std::vector<uint32_t> basic_function_control_masks = {
+          SpvFunctionControlMaskNone, SpvFunctionControlInlineMask,
+          SpvFunctionControlDontInlineMask};
+      uint32_t new_function_control_mask =
+          basic_function_control_masks[GetFuzzerContext()->RandomIndex(
+              basic_function_control_masks)];
+
+      // We now consider the Pure and Const mask bits.  If these are already
+      // set on the function then it's OK to keep them, but also interesting
+      // to consider dropping them, so we decide randomly in each case.
+      for (auto mask_bit :
+           {SpvFunctionControlPureMask, SpvFunctionControlConstMask}) {
+        if ((existing_function_control_mask & mask_bit) &&
+            GetFuzzerContext()->ChooseEven()) {
+          new_function_control_mask |= mask_bit;
+        }
+      }
+
+      // Create and add a transformation.
+      TransformationSetFunctionControl transformation(
+          function.DefInst().result_id(), new_function_control_mask);
+      assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
+             "Transformation should be applicable by construction.");
+      transformation.Apply(GetIRContext(), GetFactManager());
+      *GetTransformations()->add_transformation() = transformation.ToMessage();
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

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

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

@@ -0,0 +1,121 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_adjust_loop_controls.h"
+
+#include "source/fuzz/transformation_set_loop_control.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAdjustLoopControls::FuzzerPassAdjustLoopControls(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations){};
+
+FuzzerPassAdjustLoopControls::~FuzzerPassAdjustLoopControls() = default;
+
+void FuzzerPassAdjustLoopControls::Apply() {
+  // Consider every merge instruction in the module (via looking through all
+  // functions and blocks).
+  for (auto& function : *GetIRContext()->module()) {
+    for (auto& block : function) {
+      if (auto merge_inst = block.GetMergeInst()) {
+        // Ignore the instruction if it is not a loop merge.
+        if (merge_inst->opcode() != SpvOpLoopMerge) {
+          continue;
+        }
+
+        // Decide randomly whether to adjust this loop merge.
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfAdjustingLoopControl())) {
+          continue;
+        }
+
+        uint32_t existing_mask = merge_inst->GetSingleWordOperand(
+            TransformationSetLoopControl::kLoopControlMaskInOperandIndex);
+
+        // First, set the new mask to one of None, Unroll or DontUnroll.
+        std::vector<uint32_t> basic_masks = {SpvLoopControlMaskNone,
+                                             SpvLoopControlUnrollMask,
+                                             SpvLoopControlDontUnrollMask};
+        uint32_t new_mask =
+            basic_masks[GetFuzzerContext()->RandomIndex(basic_masks)];
+
+        // For the loop controls that depend on guarantees about what the loop
+        // does, check which of these were present in the existing mask and
+        // randomly decide whether to keep them.  They are just hints, so
+        // removing them should not change the semantics of the module.
+        for (auto mask_bit :
+             {SpvLoopControlDependencyInfiniteMask,
+              SpvLoopControlDependencyLengthMask,
+              SpvLoopControlMinIterationsMask, SpvLoopControlMaxIterationsMask,
+              SpvLoopControlIterationMultipleMask}) {
+          if ((existing_mask & mask_bit) && GetFuzzerContext()->ChooseEven()) {
+            // The mask bits we are considering are not available in all SPIR-V
+            // versions.  However, we only include a mask bit if it was present
+            // in the original loop control mask, and we work under the
+            // assumption that we are transforming a valid module, thus we don't
+            // need to actually check whether the SPIR-V version being used
+            // supports these loop control mask bits.
+            new_mask |= mask_bit;
+          }
+        }
+
+        // We use 0 for peel count and partial count in the case that we choose
+        // not to set these controls.
+        uint32_t peel_count = 0;
+        uint32_t partial_count = 0;
+
+        // PeelCount and PartialCount are not compatible with DontUnroll, so
+        // we check whether DontUnroll is set.
+        if (!(new_mask & SpvLoopControlDontUnrollMask)) {
+          // If PeelCount is supported by this SPIR-V version, randomly choose
+          // whether to set it.  If it was set in the original mask and is not
+          // selected for setting here, that amounts to dropping it.
+          if (TransformationSetLoopControl::PeelCountIsSupported(
+                  GetIRContext()) &&
+              GetFuzzerContext()->ChooseEven()) {
+            new_mask |= SpvLoopControlPeelCountMask;
+            // The peel count is chosen randomly - if PeelCount was already set
+            // this will overwrite whatever peel count was previously used.
+            peel_count = GetFuzzerContext()->GetRandomLoopControlPeelCount();
+          }
+          // Similar, but for PartialCount.
+          if (TransformationSetLoopControl::PartialCountIsSupported(
+                  GetIRContext()) &&
+              GetFuzzerContext()->ChooseEven()) {
+            new_mask |= SpvLoopControlPartialCountMask;
+            partial_count =
+                GetFuzzerContext()->GetRandomLoopControlPartialCount();
+          }
+        }
+
+        // Apply the transformation and add it to the output transformation
+        // sequence.
+        TransformationSetLoopControl transformation(block.id(), new_mask,
+                                                    peel_count, partial_count);
+        assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
+               "Transformation should be applicable by construction.");
+        transformation.Apply(GetIRContext(), GetFactManager());
+        *GetTransformations()->add_transformation() =
+            transformation.ToMessage();
+      }
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 15 - 12
3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h → 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.h

@@ -12,25 +12,28 @@
 // See the License for the specific language governing permissions and
 // See the License for the specific language governing permissions and
 // limitations under the License.
 // limitations under the License.
 
 
-#ifndef SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_
-#define SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_
+#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_
 
 
-#include "source/reduce/reduction_opportunity_finder.h"
+#include "source/fuzz/fuzzer_pass.h"
 
 
 namespace spvtools {
 namespace spvtools {
-namespace reduce {
+namespace fuzz {
 
 
-// A finder for opportunities to remove relaxed precision decorations.
-class RemoveRelaxedPrecisionDecorationOpportunityFinder
-    : public ReductionOpportunityFinder {
+// A pass that adjusts the loop controls on OpLoopMerge instructions.
+class FuzzerPassAdjustLoopControls : public FuzzerPass {
  public:
  public:
-  std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
-      opt::IRContext* context) const override;
+  FuzzerPassAdjustLoopControls(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
 
 
-  std::string GetName() const override;
+  ~FuzzerPassAdjustLoopControls() override;
+
+  void Apply() override;
 };
 };
 
 
-}  // namespace reduce
+}  // namespace fuzz
 }  // namespace spvtools
 }  // namespace spvtools
 
 
-#endif  // SOURCE_REDUCE_REMOVE_RELAXED_PRECISION_OPPORTUNITY_FINDER_H_
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_

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

@@ -0,0 +1,76 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_adjust_selection_controls.h"
+
+#include "source/fuzz/transformation_set_selection_control.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAdjustSelectionControls::FuzzerPassAdjustSelectionControls(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations){};
+
+FuzzerPassAdjustSelectionControls::~FuzzerPassAdjustSelectionControls() =
+    default;
+
+void FuzzerPassAdjustSelectionControls::Apply() {
+  // Consider every merge instruction in the module (via looking through all
+  // functions and blocks).
+  for (auto& function : *GetIRContext()->module()) {
+    for (auto& block : function) {
+      if (auto merge_inst = block.GetMergeInst()) {
+        // Ignore the instruction if it is not a selection merge.
+        if (merge_inst->opcode() != SpvOpSelectionMerge) {
+          continue;
+        }
+
+        // Choose randomly whether to change the selection control for this
+        // instruction.
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfAdjustingSelectionControl())) {
+          continue;
+        }
+
+        // The choices to change the selection control to are the set of valid
+        // controls, minus the current control.
+        std::vector<uint32_t> choices;
+        for (auto control :
+             {SpvSelectionControlMaskNone, SpvSelectionControlFlattenMask,
+              SpvSelectionControlDontFlattenMask}) {
+          if (control == merge_inst->GetSingleWordOperand(1)) {
+            continue;
+          }
+          choices.push_back(control);
+        }
+
+        // Apply the transformation and add it to the output transformation
+        // sequence.
+        TransformationSetSelectionControl transformation(
+            block.id(), choices[GetFuzzerContext()->RandomIndex(choices)]);
+        assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
+               "Transformation should be applicable by construction.");
+        transformation.Apply(GetIRContext(), GetFactManager());
+        *GetTransformations()->add_transformation() =
+            transformation.ToMessage();
+      }
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

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

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

@@ -63,9 +63,6 @@ void FuzzerPassApplyIdSynonyms::Apply() {
                 GetFuzzerContext()->RandomIndex(synonyms_to_try);
                 GetFuzzerContext()->RandomIndex(synonyms_to_try);
             auto synonym_to_try = synonyms_to_try[synonym_index];
             auto synonym_to_try = synonyms_to_try[synonym_index];
             synonyms_to_try.erase(synonyms_to_try.begin() + synonym_index);
             synonyms_to_try.erase(synonyms_to_try.begin() + synonym_index);
-            assert(synonym_to_try->index().empty() &&
-                   "Right now we only support id == id synonyms; supporting "
-                   "e.g. id == index-into-vector will come later");
 
 
             if (!TransformationReplaceIdWithSynonym::
             if (!TransformationReplaceIdWithSynonym::
                     ReplacingUseWithSynonymIsOk(GetIRContext(), use_inst,
                     ReplacingUseWithSynonymIsOk(GetIRContext(), use_inst,
@@ -74,10 +71,24 @@ void FuzzerPassApplyIdSynonyms::Apply() {
               continue;
               continue;
             }
             }
 
 
+            // At present, we generate direct id synonyms (through
+            // OpCopyObject), which require no indices, and id synonyms that
+            // require a single index (through OpCompositeConstruct).
+            assert(synonym_to_try->index_size() <= 1);
+
+            // If an index is required, then we need to extract an element
+            // from a composite (e.g. through OpCompositeExtract), and this
+            // requires a fresh result id.
+            auto fresh_id_for_temporary =
+                synonym_to_try->index().empty()
+                    ? 0
+                    : GetFuzzerContext()->GetFreshId();
+
             TransformationReplaceIdWithSynonym replace_id_transformation(
             TransformationReplaceIdWithSynonym replace_id_transformation(
-                transformation::MakeIdUseDescriptorFromUse(
-                    GetIRContext(), use_inst, use_in_operand_index),
-                *synonym_to_try, 0);
+                MakeIdUseDescriptorFromUse(GetIRContext(), use_inst,
+                                           use_in_operand_index),
+                *synonym_to_try, fresh_id_for_temporary);
+
             // The transformation should be applicable by construction.
             // The transformation should be applicable by construction.
             assert(replace_id_transformation.IsApplicable(GetIRContext(),
             assert(replace_id_transformation.IsApplicable(GetIRContext(),
                                                           *GetFactManager()));
                                                           *GetFactManager()));

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

@@ -0,0 +1,328 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_construct_composites.h"
+
+#include <cmath>
+#include <memory>
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_construct_composite.h"
+#include "source/util/make_unique.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassConstructComposites::FuzzerPassConstructComposites(
+    opt::IRContext* ir_context, FactManager* fact_manager,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassConstructComposites::~FuzzerPassConstructComposites() = default;
+
+void FuzzerPassConstructComposites::Apply() {
+  // Gather up the ids of all composite types.
+  std::vector<uint32_t> composite_type_ids;
+  for (auto& inst : GetIRContext()->types_values()) {
+    if (fuzzerutil::IsCompositeType(
+            GetIRContext()->get_type_mgr()->GetType(inst.result_id()))) {
+      composite_type_ids.push_back(inst.result_id());
+    }
+  }
+
+  MaybeAddTransformationBeforeEachInstruction(
+      [this, &composite_type_ids](const opt::Function& function,
+                                  opt::BasicBlock* block,
+                                  opt::BasicBlock::iterator inst_it,
+                                  uint32_t base, uint32_t offset) -> uint32_t {
+        // Check whether it is legitimate to insert a composite construction
+        // before the instruction.
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
+                SpvOpCompositeConstruct, inst_it)) {
+          return 0;
+        }
+
+        // Randomly decide whether to try inserting an object copy here.
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfConstructingComposite())) {
+          return 0;
+        }
+
+        // For each instruction that is available at this program point (i.e. an
+        // instruction that is global or whose definition strictly dominates the
+        // 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)) {
+          RecordAvailableInstruction(instruction,
+                                     &type_id_to_available_instructions);
+        }
+
+        // At this point, |composite_type_ids| captures all the composite types
+        // we could try to create, while |type_id_to_available_instructions|
+        // captures all the available result ids we might use, organized by
+        // type.
+
+        // Now we try to find a composite that we can construct.  We might not
+        // manage, if there is a paucity of available ingredients in the module
+        // (e.g. if our only available composite was a boolean vector and we had
+        // no instructions generating boolean result types available).
+        //
+        // If we succeed, |chosen_composite_type| will end up being non-zero,
+        // and |constructor_arguments| will end up giving us result ids suitable
+        // for constructing a composite of that type.  Otherwise these variables
+        // will remain 0 and null respectively.
+        uint32_t chosen_composite_type = 0;
+        std::unique_ptr<std::vector<uint32_t>> constructor_arguments = nullptr;
+
+        // Initially, all composite type ids are available for us to try.  Keep
+        // trying until we run out of options.
+        auto composites_to_try_constructing = composite_type_ids;
+        while (!composites_to_try_constructing.empty()) {
+          // Remove a composite type from the composite types left for us to
+          // try.
+          auto index =
+              GetFuzzerContext()->RandomIndex(composites_to_try_constructing);
+          auto next_composite_to_try_constructing =
+              composites_to_try_constructing[index];
+          composites_to_try_constructing.erase(
+              composites_to_try_constructing.begin() + index);
+
+          // Now try to construct a composite of this type, using an appropriate
+          // helper method depending on the kind of composite type.
+          auto composite_type = GetIRContext()->get_type_mgr()->GetType(
+              next_composite_to_try_constructing);
+          if (auto array_type = composite_type->AsArray()) {
+            constructor_arguments = TryConstructingArrayComposite(
+                *array_type, type_id_to_available_instructions);
+          } else if (auto matrix_type = composite_type->AsMatrix()) {
+            constructor_arguments = TryConstructingMatrixComposite(
+                *matrix_type, type_id_to_available_instructions);
+          } else if (auto struct_type = composite_type->AsStruct()) {
+            constructor_arguments = TryConstructingStructComposite(
+                *struct_type, type_id_to_available_instructions);
+          } else {
+            auto vector_type = composite_type->AsVector();
+            assert(vector_type &&
+                   "The space of possible composite types should be covered by "
+                   "the above cases.");
+            constructor_arguments = TryConstructingVectorComposite(
+                *vector_type, type_id_to_available_instructions);
+          }
+          if (constructor_arguments != nullptr) {
+            // We succeeded!  Note the composite type we finally settled on, and
+            // exit from the loop.
+            chosen_composite_type = next_composite_to_try_constructing;
+            break;
+          }
+        }
+
+        if (!chosen_composite_type) {
+          // We did not manage to make a composite; return 0 to indicate that no
+          // instructions were added.
+          assert(constructor_arguments == nullptr);
+          return 0;
+        }
+        assert(constructor_arguments != nullptr);
+
+        // Make and apply a transformation.
+        TransformationConstructComposite transformation(
+            chosen_composite_type, *constructor_arguments, base, offset,
+            GetFuzzerContext()->GetFreshId());
+        assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
+               "This transformation should be applicable by construction.");
+        transformation.Apply(GetIRContext(), GetFactManager());
+        *GetTransformations()->add_transformation() =
+            transformation.ToMessage();
+        // Indicate that one instruction was added.
+        return 1;
+      });
+}
+
+void FuzzerPassConstructComposites::RecordAvailableInstruction(
+    opt::Instruction* inst,
+    TypeIdToInstructions* type_id_to_available_instructions) {
+  if (type_id_to_available_instructions->count(inst->type_id()) == 0) {
+    (*type_id_to_available_instructions)[inst->type_id()] = {};
+  }
+  type_id_to_available_instructions->at(inst->type_id()).push_back(inst);
+}
+
+std::unique_ptr<std::vector<uint32_t>>
+FuzzerPassConstructComposites::TryConstructingArrayComposite(
+    const opt::analysis::Array& array_type,
+    const TypeIdToInstructions& type_id_to_available_instructions) {
+  // TODO make these be true by construction
+  assert(array_type.length_info().words.size() == 2);
+  assert(array_type.length_info().words[0] ==
+         opt::analysis::Array::LengthInfo::kConstant);
+
+  auto result = MakeUnique<std::vector<uint32_t>>();
+  auto element_type_id =
+      GetIRContext()->get_type_mgr()->GetId(array_type.element_type());
+  auto available_instructions =
+      type_id_to_available_instructions.find(element_type_id);
+  if (available_instructions == type_id_to_available_instructions.cend()) {
+    // TODO comment infeasible
+    return nullptr;
+  }
+  for (uint32_t index = 0; index < array_type.length_info().words[1]; index++) {
+    result->push_back(available_instructions
+                          ->second[GetFuzzerContext()->RandomIndex(
+                              available_instructions->second)]
+                          ->result_id());
+  }
+  return result;
+}
+
+std::unique_ptr<std::vector<uint32_t>>
+FuzzerPassConstructComposites::TryConstructingMatrixComposite(
+    const opt::analysis::Matrix& matrix_type,
+    const TypeIdToInstructions& type_id_to_available_instructions) {
+  (void)(matrix_type);
+  (void)(type_id_to_available_instructions);
+  assert(false);
+  return nullptr;
+}
+
+std::unique_ptr<std::vector<uint32_t>>
+FuzzerPassConstructComposites::TryConstructingStructComposite(
+    const opt::analysis::Struct& struct_type,
+    const TypeIdToInstructions& type_id_to_available_instructions) {
+  auto result = MakeUnique<std::vector<uint32_t>>();
+  for (auto element_type : struct_type.element_types()) {
+    auto element_type_id = GetIRContext()->get_type_mgr()->GetId(element_type);
+    auto available_instructions =
+        type_id_to_available_instructions.find(element_type_id);
+    if (available_instructions == type_id_to_available_instructions.cend()) {
+      // TODO comment infeasible
+      return nullptr;
+    }
+    result->push_back(available_instructions
+                          ->second[GetFuzzerContext()->RandomIndex(
+                              available_instructions->second)]
+                          ->result_id());
+  }
+  return result;
+}
+
+std::unique_ptr<std::vector<uint32_t>>
+FuzzerPassConstructComposites::TryConstructingVectorComposite(
+    const opt::analysis::Vector& vector_type,
+    const TypeIdToInstructions& type_id_to_available_instructions) {
+  // Get details of the type underlying the vector, and the width of the vector,
+  // for convenience.
+  auto element_type = vector_type.element_type();
+  auto element_count = vector_type.element_count();
+
+  // Collect a mapping, from type id to width, for scalar/vector types that are
+  // smaller in width than |vector_type|, but that have the same underlying
+  // type.  For example, if |vector_type| is vec4, the mapping will be { float
+  // -> 1, vec2 -> 2, vec3 -> 3 }.  The mapping will have missing entries if
+  // some of these types do not exist.
+
+  // TODO comment why we have the list as well.
+  std::vector<uint32_t> smaller_vector_type_ids;
+  std::map<uint32_t, uint32_t> smaller_vector_type_id_to_width;
+  // Add the underlying type.  This id must exist, in order for |vector_type| to
+  // exist.
+  auto scalar_type_id = GetIRContext()->get_type_mgr()->GetId(element_type);
+  smaller_vector_type_ids.push_back(scalar_type_id);
+  smaller_vector_type_id_to_width[scalar_type_id] = 1;
+
+  // Now add every vector type with width at least 2, and less than the width of
+  // |vector_type|.
+  for (uint32_t width = 2; width < element_count; width++) {
+    opt::analysis::Vector smaller_vector_type(vector_type.element_type(),
+                                              width);
+    auto smaller_vector_type_id =
+        GetIRContext()->get_type_mgr()->GetId(&smaller_vector_type);
+    // TODO recap why it might be 0
+    if (smaller_vector_type_id) {
+      smaller_vector_type_ids.push_back(smaller_vector_type_id);
+      smaller_vector_type_id_to_width[smaller_vector_type_id] = width;
+    }
+  }
+
+  // Now we know the types that are available to us, we set about populating a
+  // vector of the right length.  We do this by deciding, with no order in mind,
+  // which instructions we will use to populate the vector, and subsequently
+  // randomly choosing an order.  This is to avoid biasing construction of
+  // vectors with smaller vectors to the left and scalars to the right.  That is
+  // a concern because, e.g. in the case of populating a vec4, if we populate
+  // the constructor instructions left-to-right, we can always choose a vec3 to
+  // construct the first three elements, but can only choose a vec3 to construct
+  // the last three elements if we chose a float to construct the first element
+  // (otherwise there will not be space left for a vec3).
+
+  uint32_t vector_slots_used = 0;
+  // The instructions we will use to construct the vector, in no particular
+  // order at this stage.
+  std::vector<opt::Instruction*> instructions_to_use;
+
+  while (vector_slots_used < vector_type.element_count()) {
+    std::vector<opt::Instruction*> instructions_to_choose_from;
+    for (auto& entry : smaller_vector_type_id_to_width) {
+      if (entry.second >
+          std::min(vector_type.element_count() - 1,
+                   vector_type.element_count() - vector_slots_used)) {
+        continue;
+      }
+      auto available_instructions =
+          type_id_to_available_instructions.find(entry.first);
+      if (available_instructions == type_id_to_available_instructions.cend()) {
+        continue;
+      }
+      instructions_to_choose_from.insert(instructions_to_choose_from.end(),
+                                         available_instructions->second.begin(),
+                                         available_instructions->second.end());
+    }
+    if (instructions_to_choose_from.empty()) {
+      // TODO comment - like fuzzed into a corner
+      return nullptr;
+    }
+    auto instruction_to_use =
+        instructions_to_choose_from[GetFuzzerContext()->RandomIndex(
+            instructions_to_choose_from)];
+    instructions_to_use.push_back(instruction_to_use);
+    auto chosen_type =
+        GetIRContext()->get_type_mgr()->GetType(instruction_to_use->type_id());
+    if (chosen_type->AsVector()) {
+      assert(chosen_type->AsVector()->element_type() == element_type);
+      assert(chosen_type->AsVector()->element_count() < element_count);
+      assert(chosen_type->AsVector()->element_count() <=
+             element_count - vector_slots_used);
+      vector_slots_used += chosen_type->AsVector()->element_count();
+    } else {
+      assert(chosen_type == element_type);
+      vector_slots_used += 1;
+    }
+  }
+  assert(vector_slots_used == vector_type.element_count());
+
+  auto result = MakeUnique<std::vector<uint32_t>>();
+  std::vector<uint32_t> operands;
+  while (!instructions_to_use.empty()) {
+    auto index = GetFuzzerContext()->RandomIndex(instructions_to_use);
+    result->push_back(instructions_to_use[index]->result_id());
+    instructions_to_use.erase(instructions_to_use.begin() + index);
+  }
+  assert(result->size() > 1);
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 79 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.h

@@ -0,0 +1,79 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_
+#define SOURCE_FUZZ_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+#include <map>
+#include <vector>
+
+namespace spvtools {
+namespace fuzz {
+
+// A fuzzer pass for constructing composite objects from smaller objects.
+class FuzzerPassConstructComposites : public FuzzerPass {
+ public:
+  FuzzerPassConstructComposites(
+      opt::IRContext* ir_context, FactManager* fact_manager,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassConstructComposites();
+
+  void Apply() override;
+
+ private:
+  // Used to map a type id to relevant instructions whose result type matches
+  // the type id.
+  typedef std::map<uint32_t, std::vector<opt::Instruction*>>
+      TypeIdToInstructions;
+
+  // Considers all instructions that are available at |inst| - instructions
+  // whose results could be packed into a composite - and updates
+  // |type_id_to_available_instructions| so that each such instruction is
+  // associated with its the id of its result type.
+  void RecordAvailableInstruction(
+      opt::Instruction* inst,
+      TypeIdToInstructions* type_id_to_available_instructions);
+
+  // Attempts to find suitable instruction result ids from the values of
+  // |type_id_to_available_instructions| that would allow a composite of type
+  // |array_type| to be constructed.  Returns said ids if they can be found.
+  // Returns |nullptr| otherwise.
+  std::unique_ptr<std::vector<uint32_t>> TryConstructingArrayComposite(
+      const opt::analysis::Array& array_type,
+      const TypeIdToInstructions& type_id_to_available_instructions);
+
+  // Similar to TryConstructingArrayComposite, but for matrices.
+  std::unique_ptr<std::vector<uint32_t>> TryConstructingMatrixComposite(
+      const opt::analysis::Matrix& matrix_type,
+      const TypeIdToInstructions& type_id_to_available_instructions);
+
+  // Similar to TryConstructingArrayComposite, but for structs.
+  std::unique_ptr<std::vector<uint32_t>> TryConstructingStructComposite(
+      const opt::analysis::Struct& struct_type,
+      const TypeIdToInstructions& type_id_to_available_instructions);
+
+  // Similar to TryConstructingArrayComposite, but for vectors.
+  std::unique_ptr<std::vector<uint32_t>> TryConstructingVectorComposite(
+      const opt::analysis::Vector& vector_type,
+      const TypeIdToInstructions& type_id_to_available_instructions);
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_

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

@@ -14,6 +14,7 @@
 
 
 #include "source/fuzz/fuzzer_pass_copy_objects.h"
 #include "source/fuzz/fuzzer_pass_copy_objects.h"
 
 
+#include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/transformation_copy_object.h"
 #include "source/fuzz/transformation_copy_object.h"
 
 
 namespace spvtools {
 namespace spvtools {
@@ -28,120 +29,46 @@ FuzzerPassCopyObjects::FuzzerPassCopyObjects(
 FuzzerPassCopyObjects::~FuzzerPassCopyObjects() = default;
 FuzzerPassCopyObjects::~FuzzerPassCopyObjects() = default;
 
 
 void FuzzerPassCopyObjects::Apply() {
 void FuzzerPassCopyObjects::Apply() {
-  // Consider every block in every function.
-  for (auto& function : *GetIRContext()->module()) {
-    for (auto& block : function) {
-      // We now consider every instruction in the block, randomly deciding
-      // whether to add an object copy before the instruction.
-
-      // In order to insert an object copy instruction, we need to be able to
-      // identify the instruction a copy should be inserted before.  We do this
-      // by tracking a base instruction, which must generate a result id, and an
-      // offset (to allow us to identify instructions that do not generate
-      // result ids).
-
-      // The initial base instruction is the block label.
-      uint32_t base = block.id();
-      uint32_t offset = 0;
-      // Consider every instruction in the block.
-      for (auto inst_it = block.begin(); inst_it != block.end(); ++inst_it) {
-        if (inst_it->HasResultId()) {
-          // In the case that the instruction has a result id, we use the
-          // instruction as its own base, with zero offset.
-          base = inst_it->result_id();
-          offset = 0;
-        } else {
-          // The instruction does not have a result id, so we need to identify
-          // it via the latest instruction that did have a result id (base), and
-          // an incremented offset.
-          offset++;
-        }
-
+  MaybeAddTransformationBeforeEachInstruction(
+      [this](const opt::Function& function, opt::BasicBlock* block,
+             opt::BasicBlock::iterator inst_it, uint32_t base,
+             uint32_t offset) -> uint32_t {
         // Check whether it is legitimate to insert a copy before this
         // Check whether it is legitimate to insert a copy before this
         // instruction.
         // instruction.
-        if (!TransformationCopyObject::CanInsertCopyBefore(inst_it)) {
-          continue;
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyObject,
+                                                          inst_it)) {
+          return 0;
         }
         }
 
 
         // Randomly decide whether to try inserting an object copy here.
         // Randomly decide whether to try inserting an object copy here.
         if (!GetFuzzerContext()->ChoosePercentage(
         if (!GetFuzzerContext()->ChoosePercentage(
                 GetFuzzerContext()->GetChanceOfCopyingObject())) {
                 GetFuzzerContext()->GetChanceOfCopyingObject())) {
-          continue;
+          return 0;
         }
         }
 
 
-        // Populate list of potential instructions that can be copied.
-        // TODO(afd) The following is (relatively) simple, but may end up being
-        //  prohibitively inefficient, as it walks the whole dominator tree for
-        //  every copy that is added.
-        std::vector<opt::Instruction*> copyable_instructions;
+        std::vector<opt::Instruction*> relevant_instructions =
+            FindAvailableInstructions(function, block, inst_it,
+                                      fuzzerutil::CanMakeSynonymOf);
 
 
-        // Consider all global declarations
-        for (auto& global : GetIRContext()->module()->types_values()) {
-          if (TransformationCopyObject::IsCopyable(GetIRContext(), &global)) {
-            copyable_instructions.push_back(&global);
-          }
-        }
-
-        // Consider all previous instructions in this block
-        for (auto prev_inst_it = block.begin(); prev_inst_it != inst_it;
-             ++prev_inst_it) {
-          if (TransformationCopyObject::IsCopyable(GetIRContext(),
-                                                   &*prev_inst_it)) {
-            copyable_instructions.push_back(&*prev_inst_it);
-          }
-        }
-
-        // Walk the dominator tree to consider all instructions from dominating
-        // blocks
-        auto dominator_analysis =
-            GetIRContext()->GetDominatorAnalysis(&function);
-        for (auto next_dominator =
-                 dominator_analysis->ImmediateDominator(&block);
-             next_dominator != nullptr;
-             next_dominator =
-                 dominator_analysis->ImmediateDominator(next_dominator)) {
-          for (auto& dominating_inst : *next_dominator) {
-            if (TransformationCopyObject::IsCopyable(GetIRContext(),
-                                                     &dominating_inst)) {
-              copyable_instructions.push_back(&dominating_inst);
-            }
-          }
-        }
-
-        // At this point, |copyable_instructions| contains all the instructions
+        // At this point, |relevant_instructions| contains all the instructions
         // we might think of copying.
         // we might think of copying.
-
-        if (!copyable_instructions.empty()) {
-          // Choose a copyable instruction at random, and create and apply an
-          // object copying transformation based on it.
-          uint32_t index =
-              GetFuzzerContext()->RandomIndex(copyable_instructions);
-          TransformationCopyObject transformation(
-              copyable_instructions[index]->result_id(), base, offset,
-              GetFuzzerContext()->GetFreshId());
-          assert(
-              transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
-              "This transformation should be applicable by construction.");
-          transformation.Apply(GetIRContext(), GetFactManager());
-          *GetTransformations()->add_transformation() =
-              transformation.ToMessage();
-
-          if (!inst_it->HasResultId()) {
-            // We have inserted a new instruction before the current
-            // instruction, and we are tracking the current id-less instruction
-            // via an offset (offset) from a previous instruction (base) that
-            // has an id. We increment |offset| to reflect the newly-inserted
-            // instruction.
-            //
-            // This is slightly preferable to the alternative of setting |base|
-            // to be the result id of the new instruction, since on replay we
-            // might end up eliminating this copy but keeping a subsequent copy.
-            offset++;
-          }
+        if (relevant_instructions.empty()) {
+          return 0;
         }
         }
-      }
-    }
-  }
+
+        // Choose a copyable instruction at random, and create and apply an
+        // object copying transformation based on it.
+        uint32_t index = GetFuzzerContext()->RandomIndex(relevant_instructions);
+        TransformationCopyObject transformation(
+            relevant_instructions[index]->result_id(), base, offset,
+            GetFuzzerContext()->GetFreshId());
+        assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
+               "This transformation should be applicable by construction.");
+        transformation.Apply(GetIRContext(), GetFactManager());
+        *GetTransformations()->add_transformation() =
+            transformation.ToMessage();
+        return 1;
+      });
 }
 }
 
 
 }  // namespace fuzz
 }  // namespace fuzz

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

@@ -16,6 +16,7 @@
 
 
 #include <cmath>
 #include <cmath>
 
 
+#include "source/fuzz/instruction_descriptor.h"
 #include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.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_constant_with_uniform.h"
 #include "source/opt/ir_context.h"
 #include "source/opt/ir_context.h"
@@ -102,10 +103,11 @@ void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaConstantPair(
     // We randomly decide, based on the current depth of obfuscation, whether
     // We randomly decide, based on the current depth of obfuscation, whether
     // to further obfuscate this operand.
     // to further obfuscate this operand.
     if (GetFuzzerContext()->GoDeeperInConstantObfuscation(depth)) {
     if (GetFuzzerContext()->GoDeeperInConstantObfuscation(depth)) {
-      auto in_operand_use = transformation::MakeIdUseDescriptor(
+      auto in_operand_use = MakeIdUseDescriptor(
           binary_operator_instruction->GetSingleWordInOperand(index),
           binary_operator_instruction->GetSingleWordInOperand(index),
-          binary_operator_instruction->opcode(), index,
-          binary_operator_instruction->result_id(), 0);
+          MakeInstructionDescriptor(binary_operator_instruction->result_id(),
+                                    binary_operator_instruction->opcode(), 0),
+          index);
       ObfuscateConstant(depth + 1, in_operand_use);
       ObfuscateConstant(depth + 1, in_operand_use);
     }
     }
   }
   }
@@ -366,14 +368,17 @@ void FuzzerPassObfuscateConstants::MaybeAddConstantIdUse(
       // it.
       // it.
       protobufs::IdUseDescriptor id_use_descriptor;
       protobufs::IdUseDescriptor id_use_descriptor;
       id_use_descriptor.set_id_of_interest(operand_id);
       id_use_descriptor.set_id_of_interest(operand_id);
-      id_use_descriptor.set_target_instruction_opcode(inst.opcode());
+      id_use_descriptor.mutable_enclosing_instruction()
+          ->set_target_instruction_opcode(inst.opcode());
+      id_use_descriptor.mutable_enclosing_instruction()
+          ->set_base_instruction_result_id(base_instruction_result_id);
+      id_use_descriptor.mutable_enclosing_instruction()
+          ->set_num_opcodes_to_ignore(
+              skipped_opcode_count.find(inst.opcode()) ==
+                      skipped_opcode_count.end()
+                  ? 0
+                  : skipped_opcode_count.at(inst.opcode()));
       id_use_descriptor.set_in_operand_index(in_operand_index);
       id_use_descriptor.set_in_operand_index(in_operand_index);
-      id_use_descriptor.set_base_instruction_result_id(
-          base_instruction_result_id);
-      id_use_descriptor.set_num_opcodes_to_ignore(
-          skipped_opcode_count.find(inst.opcode()) == skipped_opcode_count.end()
-              ? 0
-              : skipped_opcode_count.at(inst.opcode()));
       constant_uses->push_back(id_use_descriptor);
       constant_uses->push_back(id_use_descriptor);
     } break;
     } break;
     default:
     default:

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

@@ -216,6 +216,13 @@ bool NewEdgeRespectsUseDefDominance(opt::IRContext* context,
     return true;
     return true;
   }
   }
 
 
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2919): the
+  //  solution below to determining whether a new edge respects dominance
+  //  rules is incomplete.  Test
+  //  TransformationAddDeadContinueTest::DISABLED_Miscellaneous6 exposes the
+  //  problem.  In practice, this limitation does not bite too often, and the
+  //  worst it does is leads to SPIR-V that spirv-val rejects.
+
   // Let us assume that the module being manipulated is valid according to the
   // Let us assume that the module being manipulated is valid according to the
   // rules of the SPIR-V language.
   // rules of the SPIR-V language.
   //
   //
@@ -307,6 +314,47 @@ bool BlockIsReachableInItsFunction(opt::IRContext* context,
       ->Dominates(enclosing_function->entry().get(), bb);
       ->Dominates(enclosing_function->entry().get(), bb);
 }
 }
 
 
+bool CanInsertOpcodeBeforeInstruction(
+    SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block) {
+  if (instruction_in_block->PreviousNode() &&
+      (instruction_in_block->PreviousNode()->opcode() == SpvOpLoopMerge ||
+       instruction_in_block->PreviousNode()->opcode() == SpvOpSelectionMerge)) {
+    // We cannot insert directly after a merge instruction.
+    return false;
+  }
+  if (opcode != SpvOpVariable &&
+      instruction_in_block->opcode() == SpvOpVariable) {
+    // We cannot insert a non-OpVariable instruction directly before a
+    // variable; variables in a function must be contiguous in the entry block.
+    return false;
+  }
+  // We cannot insert a non-OpPhi instruction directly before an OpPhi, because
+  // OpPhi instructions need to be contiguous at the start of a block.
+  return opcode == SpvOpPhi || instruction_in_block->opcode() != SpvOpPhi;
+}
+
+bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst) {
+  if (!inst->HasResultId()) {
+    // We can only make a synonym of an instruction that generates an id.
+    return false;
+  }
+  if (!inst->type_id()) {
+    // We can only make a synonym of an instruction that has a type.
+    return false;
+  }
+  // We do not make synonyms of objects that have decorations: if the synonym is
+  // not decorated analogously, using the original object vs. its synonymous
+  // form may not be equivalent.
+  return ir_context->get_decoration_mgr()
+      ->GetDecorationsFor(inst->result_id(), true)
+      .empty();
+}
+
+bool IsCompositeType(const opt::analysis::Type* type) {
+  return type && (type->AsArray() || type->AsMatrix() || type->AsStruct() ||
+                  type->AsVector());
+}
+
 }  // namespace fuzzerutil
 }  // namespace fuzzerutil
 
 
 }  // namespace fuzz
 }  // namespace fuzz

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

@@ -87,6 +87,18 @@ bool NewEdgeRespectsUseDefDominance(opt::IRContext* context,
 bool BlockIsReachableInItsFunction(opt::IRContext* context,
 bool BlockIsReachableInItsFunction(opt::IRContext* context,
                                    opt::BasicBlock* bb);
                                    opt::BasicBlock* bb);
 
 
+// Determines whether it is OK to insert an instruction with opcode |opcode|
+// before |instruction_in_block|.
+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);
+
+// Determines whether the given type is a composite; that is: an array, matrix,
+// struct or vector.
+bool IsCompositeType(const opt::analysis::Type* type);
+
 }  // namespace fuzzerutil
 }  // namespace fuzzerutil
 
 
 }  // namespace fuzz
 }  // namespace fuzz

+ 28 - 56
3rdparty/spirv-tools/source/fuzz/id_use_descriptor.cpp

@@ -14,71 +14,41 @@
 
 
 #include "source/fuzz/id_use_descriptor.h"
 #include "source/fuzz/id_use_descriptor.h"
 
 
+#include "source/fuzz/instruction_descriptor.h"
+
 namespace spvtools {
 namespace spvtools {
 namespace fuzz {
 namespace fuzz {
 
 
-opt::Instruction* transformation::FindInstruction(
-    const protobufs::IdUseDescriptor& descriptor,
-    spvtools::opt::IRContext* context) {
-  for (auto& function : *context->module()) {
-    for (auto& block : function) {
-      bool found_base = block.id() == descriptor.base_instruction_result_id();
-      uint32_t num_ignored = 0;
-      for (auto& instruction : block) {
-        if (instruction.HasResultId() &&
-            instruction.result_id() ==
-                descriptor.base_instruction_result_id()) {
-          assert(!found_base &&
-                 "It should not be possible to find the base instruction "
-                 "multiple times.");
-          found_base = true;
-          assert(num_ignored == 0 &&
-                 "The skipped instruction count should only be incremented "
-                 "after the instruction base has been found.");
-        }
-        if (found_base &&
-            instruction.opcode() == descriptor.target_instruction_opcode()) {
-          if (num_ignored == descriptor.num_opcodes_to_ignore()) {
-            if (descriptor.in_operand_index() >= instruction.NumInOperands()) {
-              return nullptr;
-            }
-            auto in_operand =
-                instruction.GetInOperand(descriptor.in_operand_index());
-            if (in_operand.type != SPV_OPERAND_TYPE_ID) {
-              return nullptr;
-            }
-            if (in_operand.words[0] != descriptor.id_of_interest()) {
-              return nullptr;
-            }
-            return &instruction;
-          }
-          num_ignored++;
-        }
-      }
-      if (found_base) {
-        // We found the base instruction, but did not find the target
-        // instruction in the same block.
-        return nullptr;
-      }
-    }
+opt::Instruction* FindInstructionContainingUse(
+    const protobufs::IdUseDescriptor& id_use_descriptor,
+    opt::IRContext* context) {
+  auto result =
+      FindInstruction(id_use_descriptor.enclosing_instruction(), context);
+  if (!result) {
+    return nullptr;
+  }
+  if (id_use_descriptor.in_operand_index() >= result->NumInOperands()) {
+    return nullptr;
   }
   }
-  return nullptr;
+  if (result->GetSingleWordInOperand(id_use_descriptor.in_operand_index()) !=
+      id_use_descriptor.id_of_interest()) {
+    return nullptr;
+  }
+  return result;
 }
 }
 
 
-protobufs::IdUseDescriptor transformation::MakeIdUseDescriptor(
-    uint32_t id_of_interest, SpvOp target_instruction_opcode,
-    uint32_t in_operand_index, uint32_t base_instruction_result_id,
-    uint32_t num_opcodes_to_ignore) {
+protobufs::IdUseDescriptor MakeIdUseDescriptor(
+    uint32_t id_of_interest,
+    const protobufs::InstructionDescriptor& enclosing_instruction,
+    uint32_t in_operand_index) {
   protobufs::IdUseDescriptor result;
   protobufs::IdUseDescriptor result;
   result.set_id_of_interest(id_of_interest);
   result.set_id_of_interest(id_of_interest);
-  result.set_target_instruction_opcode(target_instruction_opcode);
+  *result.mutable_enclosing_instruction() = enclosing_instruction;
   result.set_in_operand_index(in_operand_index);
   result.set_in_operand_index(in_operand_index);
-  result.set_base_instruction_result_id(base_instruction_result_id);
-  result.set_num_opcodes_to_ignore(num_opcodes_to_ignore);
   return result;
   return result;
 }
 }
 
 
-protobufs::IdUseDescriptor transformation::MakeIdUseDescriptorFromUse(
+protobufs::IdUseDescriptor MakeIdUseDescriptorFromUse(
     opt::IRContext* context, opt::Instruction* inst,
     opt::IRContext* context, opt::Instruction* inst,
     uint32_t in_operand_index) {
     uint32_t in_operand_index) {
   auto in_operand = inst->GetInOperand(in_operand_index);
   auto in_operand = inst->GetInOperand(in_operand_index);
@@ -94,9 +64,11 @@ protobufs::IdUseDescriptor transformation::MakeIdUseDescriptorFromUse(
       num_opcodes_to_ignore = 0;
       num_opcodes_to_ignore = 0;
     }
     }
     if (&inst_in_block == inst) {
     if (&inst_in_block == inst) {
-      return MakeIdUseDescriptor(id_of_interest, inst->opcode(),
-                                 in_operand_index, base_instruction_result_id,
-                                 num_opcodes_to_ignore);
+      return MakeIdUseDescriptor(
+          id_of_interest,
+          MakeInstructionDescriptor(base_instruction_result_id, inst->opcode(),
+                                    num_opcodes_to_ignore),
+          in_operand_index);
     }
     }
     if (inst_in_block.opcode() == inst->opcode()) {
     if (inst_in_block.opcode() == inst->opcode()) {
       num_opcodes_to_ignore++;
       num_opcodes_to_ignore++;

+ 12 - 13
3rdparty/spirv-tools/source/fuzz/id_use_descriptor.h

@@ -12,28 +12,28 @@
 // See the License for the specific language governing permissions and
 // See the License for the specific language governing permissions and
 // limitations under the License.
 // limitations under the License.
 
 
-#ifndef SOURCE_FUZZ_ID_USE_LOCATOR_H_
-#define SOURCE_FUZZ_ID_USE_LOCATOR_H_
+#ifndef SOURCE_FUZZ_ID_USE_DESCRIPTOR_H_
+#define SOURCE_FUZZ_ID_USE_DESCRIPTOR_H_
 
 
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/opt/ir_context.h"
 #include "source/opt/ir_context.h"
 
 
 namespace spvtools {
 namespace spvtools {
 namespace fuzz {
 namespace fuzz {
-namespace transformation {
 
 
-// Looks for an instruction in |context| such that the id use represented by
-// |descriptor| is one of the operands to said instruction.  Returns |nullptr|
-// if no such instruction can be found.
-opt::Instruction* FindInstruction(const protobufs::IdUseDescriptor& descriptor,
-                                  opt::IRContext* context);
+// Looks for an instruction in |context| that contains a use
+// identified by |id_use_descriptor|.
+// Returns |nullptr| if no such instruction can be found.
+opt::Instruction* FindInstructionContainingUse(
+    const protobufs::IdUseDescriptor& id_use_descriptor,
+    opt::IRContext* context);
 
 
 // Creates an IdUseDescriptor protobuf message from the given components.
 // Creates an IdUseDescriptor protobuf message from the given components.
 // See the protobuf definition for details of what these components mean.
 // See the protobuf definition for details of what these components mean.
 protobufs::IdUseDescriptor MakeIdUseDescriptor(
 protobufs::IdUseDescriptor MakeIdUseDescriptor(
-    uint32_t id_of_interest, SpvOp target_instruction_opcode,
-    uint32_t in_operand_index, uint32_t base_instruction_result_id,
-    uint32_t num_opcodes_to_ignore);
+    uint32_t id_of_interest,
+    const protobufs::InstructionDescriptor& enclosing_instruction,
+    uint32_t in_operand_index);
 
 
 // Given an id use, represented by the instruction |inst| that uses the id, and
 // Given an id use, represented by the instruction |inst| that uses the id, and
 // the input operand index |in_operand_index| associated with the usage, returns
 // the input operand index |in_operand_index| associated with the usage, returns
@@ -41,8 +41,7 @@ protobufs::IdUseDescriptor MakeIdUseDescriptor(
 protobufs::IdUseDescriptor MakeIdUseDescriptorFromUse(
 protobufs::IdUseDescriptor MakeIdUseDescriptorFromUse(
     opt::IRContext* context, opt::Instruction* inst, uint32_t in_operand_index);
     opt::IRContext* context, opt::Instruction* inst, uint32_t in_operand_index);
 
 
-}  // namespace transformation
 }  // namespace fuzz
 }  // namespace fuzz
 }  // namespace spvtools
 }  // namespace spvtools
 
 
-#endif  // SOURCE_FUZZ_ID_USE_LOCATOR_H_
+#endif  // SOURCE_FUZZ_ID_USE_DESCRIPTOR_H_

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

@@ -0,0 +1,70 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+opt::Instruction* FindInstruction(
+    const protobufs::InstructionDescriptor& instruction_descriptor,
+    spvtools::opt::IRContext* context) {
+  for (auto& function : *context->module()) {
+    for (auto& block : function) {
+      bool found_base =
+          block.id() == instruction_descriptor.base_instruction_result_id();
+      uint32_t num_ignored = 0;
+      for (auto& instruction : block) {
+        if (instruction.HasResultId() &&
+            instruction.result_id() ==
+                instruction_descriptor.base_instruction_result_id()) {
+          assert(!found_base &&
+                 "It should not be possible to find the base instruction "
+                 "multiple times.");
+          found_base = true;
+          assert(num_ignored == 0 &&
+                 "The skipped instruction count should only be incremented "
+                 "after the instruction base has been found.");
+        }
+        if (found_base &&
+            instruction.opcode() ==
+                instruction_descriptor.target_instruction_opcode()) {
+          if (num_ignored == instruction_descriptor.num_opcodes_to_ignore()) {
+            return &instruction;
+          }
+          num_ignored++;
+        }
+      }
+      if (found_base) {
+        // We found the base instruction, but did not find the target
+        // instruction in the same block.
+        return nullptr;
+      }
+    }
+  }
+  return nullptr;
+}
+
+protobufs::InstructionDescriptor MakeInstructionDescriptor(
+    uint32_t base_instruction_result_id, SpvOp target_instruction_opcode,
+    uint32_t num_opcodes_to_ignore) {
+  protobufs::InstructionDescriptor result;
+  result.set_base_instruction_result_id(base_instruction_result_id);
+  result.set_target_instruction_opcode(target_instruction_opcode);
+  result.set_num_opcodes_to_ignore(num_opcodes_to_ignore);
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

@@ -0,0 +1,40 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_INSTRUCTION_DESCRIPTOR_H_
+#define SOURCE_FUZZ_INSTRUCTION_DESCRIPTOR_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Looks for an instruction in |context| corresponding to |descriptor|.
+// Returns |nullptr| if no such instruction can be found.
+opt::Instruction* FindInstruction(
+    const protobufs::InstructionDescriptor& instruction_descriptor,
+    opt::IRContext* context);
+
+// Creates an InstructionDescriptor protobuf message from the given
+// components.  See the protobuf definition for details of what these
+// components mean.
+protobufs::InstructionDescriptor MakeInstructionDescriptor(
+    uint32_t base_instruction_result_id, SpvOp target_instruction_opcode,
+    uint32_t num_opcodes_to_ignore);
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_INSTRUCTION_DESCRIPTOR_H_

+ 120 - 17
3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto

@@ -21,17 +21,37 @@ syntax = "proto3";
 
 
 package spvtools.fuzz.protobufs;
 package spvtools.fuzz.protobufs;
 
 
+message InstructionDescriptor {
+
+  // Describes an instruction in some block of a function with respect to a
+  // base instruction.
+
+  // The id of an instruction after which the instruction being described is
+  // believed to be located.  It might be the using instruction itself.
+  uint32 base_instruction_result_id = 1;
+
+  // The opcode for the instruction being described.
+  uint32 target_instruction_opcode = 2;
+
+  // The number of matching opcodes to skip over when searching from the base
+  // instruction to the instruction being described.
+  uint32 num_opcodes_to_ignore = 3;
+
+}
+
 message IdUseDescriptor {
 message IdUseDescriptor {
 
 
-  // Describes a use of an id as an input operand to an instruction in some block
-  // of a function.
+  // Describes a use of an id as an input operand to an instruction in some
+  // block of a function.
 
 
   // Example:
   // Example:
   //   - id_of_interest = 42
   //   - id_of_interest = 42
-  //   - target_instruction_opcode = OpStore
+  //   - enclosing_instruction = (
+  //         base_instruction_result_id = 50,
+  //         target_instruction_opcode = OpStore
+  //         num_opcodes_to_ignore = 7
+  //     )
   //   - in_operand_index = 1
   //   - in_operand_index = 1
-  //   - base_instruction_result_id = 50
-  //   - num_opcodes_to_ignore = 7
   // represents a use of id 42 as input operand 1 to an OpStore instruction,
   // represents a use of id 42 as input operand 1 to an OpStore instruction,
   // such that the OpStore instruction can be found in the same basic block as
   // such that the OpStore instruction can be found in the same basic block as
   // the instruction with result id 50, and in particular is the 8th OpStore
   // the instruction with result id 50, and in particular is the 8th OpStore
@@ -41,19 +61,10 @@ message IdUseDescriptor {
   // An id that we would like to be able to find a use of.
   // An id that we would like to be able to find a use of.
   uint32 id_of_interest = 1;
   uint32 id_of_interest = 1;
 
 
-  // The opcode for the instruction that uses the id.
-  uint32 target_instruction_opcode = 2;
-
   // The input operand index at which the use is expected.
   // The input operand index at which the use is expected.
-  uint32 in_operand_index = 3;
-
-  // The id of an instruction after which the instruction that contains the use
-  // is believed to occur; it might be the using instruction itself.
-  uint32 base_instruction_result_id = 4;
+  InstructionDescriptor enclosing_instruction = 2;
 
 
-  // The number of matching opcodes to skip over when searching for the using
-  // instruction from the base instruction.
-  uint32 num_opcodes_to_ignore = 5;
+  uint32 in_operand_index = 3;
 
 
 }
 }
 
 
@@ -167,12 +178,18 @@ message Transformation {
     TransformationAddTypeFloat add_type_float = 6;
     TransformationAddTypeFloat add_type_float = 6;
     TransformationAddTypeInt add_type_int = 7;
     TransformationAddTypeInt add_type_int = 7;
     TransformationAddDeadBreak add_dead_break = 8;
     TransformationAddDeadBreak add_dead_break = 8;
-    TransformationReplaceBooleanConstantWithConstantBinary replace_boolean_constant_with_constant_binary = 9;
+    TransformationReplaceBooleanConstantWithConstantBinary
+      replace_boolean_constant_with_constant_binary = 9;
     TransformationAddTypePointer add_type_pointer = 10;
     TransformationAddTypePointer add_type_pointer = 10;
     TransformationReplaceConstantWithUniform replace_constant_with_uniform = 11;
     TransformationReplaceConstantWithUniform replace_constant_with_uniform = 11;
     TransformationAddDeadContinue add_dead_continue = 12;
     TransformationAddDeadContinue add_dead_continue = 12;
     TransformationCopyObject copy_object = 13;
     TransformationCopyObject copy_object = 13;
     TransformationReplaceIdWithSynonym replace_id_with_synonym = 14;
     TransformationReplaceIdWithSynonym replace_id_with_synonym = 14;
+    TransformationSetSelectionControl set_selection_control = 15;
+    TransformationConstructComposite construct_composite = 16;
+    TransformationSetLoopControl set_loop_control = 17;
+    TransformationSetFunctionControl set_function_control = 18;
+    TransformationAddNoContractionDecoration add_no_contraction_decoration = 19;
     // Add additional option using the next available number.
     // Add additional option using the next available number.
   }
   }
 }
 }
@@ -245,6 +262,15 @@ message TransformationAddDeadContinue {
 
 
 }
 }
 
 
+message TransformationAddNoContractionDecoration {
+
+  // Applies OpDecorate NoContraction to the given result id
+
+  // Result id to be decorated
+  uint32 result_id = 1;
+
+}
+
 message TransformationAddTypeBoolean {
 message TransformationAddTypeBoolean {
 
 
   // Adds OpTypeBool to the module
   // Adds OpTypeBool to the module
@@ -317,6 +343,30 @@ message TransformationCopyObject {
 
 
 }
 }
 
 
+message TransformationConstructComposite {
+
+  // A transformation that introduces an OpCompositeConstruct instruction to
+  // make a composite object.
+
+  // Id of the type of the composite that is to be constructed
+  uint32 composite_type_id = 1;
+
+  // Ids of the objects that will form the components of the composite
+  repeated uint32 component = 2;
+
+  // The id of an instruction in a block
+  uint32 base_instruction_id = 3;
+
+  // An offset, such that OpCompositeConstruct instruction should be inserted
+  // right before the instruction |offset| instructions after
+  // |base_instruction_id|
+  uint32 offset = 4;
+
+  // A fresh id for the composite object
+  uint32 fresh_id = 5;
+
+}
+
 message TransformationMoveBlockDown {
 message TransformationMoveBlockDown {
 
 
   // A transformation that moves a basic block to be one position lower in
   // A transformation that moves a basic block to be one position lower in
@@ -384,6 +434,59 @@ message TransformationReplaceIdWithSynonym {
   uint32 fresh_id_for_temporary = 3;
   uint32 fresh_id_for_temporary = 3;
 }
 }
 
 
+message TransformationSetFunctionControl {
+
+  // A transformation that sets the function control operand of an OpFunction
+  // instruction.
+
+  // The result id of an OpFunction instruction
+  uint32 function_id = 1;
+
+  // The value to which the 'function control' operand should be set.
+  uint32 function_control = 2;
+
+}
+
+message TransformationSetLoopControl {
+
+  // A transformation that sets the loop control operand of an OpLoopMerge
+  // instruction.
+
+  // The id of a basic block that should contain OpLoopMerge
+  uint32 block_id = 1;
+
+  // The value to which the 'loop control' operand should be set.
+  // This must be a legal loop control mask.
+  uint32 loop_control = 2;
+
+  // Provides a peel count value for the loop.  Used if and only if the
+  // PeelCount bit is set.  Must be zero if the PeelCount bit is not set (can
+  // still be zero if this bit is set).
+  uint32 peel_count = 3;
+
+  // Provides a partial count value for the loop.  Used if and only if the
+  // PartialCount bit is set.  Must be zero if the PartialCount bit is not set
+  // (can still be zero if this bit is set).
+  uint32 partial_count = 4;
+
+}
+
+message TransformationSetSelectionControl {
+
+  // A transformation that sets the selection control operand of an
+  // OpSelectionMerge instruction.
+
+  // The id of a basic block that should contain OpSelectionMerge
+  uint32 block_id = 1;
+
+  // The value to which the 'selection control' operand should be set.
+  // Although technically 'selection control' is a literal mask that can be
+  // some combination of 'None', 'Flatten' and 'DontFlatten', the combination
+  // 'Flatten | DontFlatten' does not make sense and is not allowed here.
+  uint32 selection_control = 2;
+
+}
+
 message TransformationSplitBlock {
 message TransformationSplitBlock {
 
 
   // A transformation that splits a basic block into two basic blocks
   // A transformation that splits a basic block into two basic blocks

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

@@ -16,21 +16,26 @@
 
 
 #include <cassert>
 #include <cassert>
 
 
+#include "source/fuzz/transformation_add_constant_boolean.h"
+#include "source/fuzz/transformation_add_constant_scalar.h"
+#include "source/fuzz/transformation_add_dead_break.h"
+#include "source/fuzz/transformation_add_dead_continue.h"
+#include "source/fuzz/transformation_add_no_contraction_decoration.h"
+#include "source/fuzz/transformation_add_type_boolean.h"
+#include "source/fuzz/transformation_add_type_float.h"
+#include "source/fuzz/transformation_add_type_int.h"
+#include "source/fuzz/transformation_add_type_pointer.h"
+#include "source/fuzz/transformation_construct_composite.h"
+#include "source/fuzz/transformation_copy_object.h"
+#include "source/fuzz/transformation_move_block_down.h"
+#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
+#include "source/fuzz/transformation_replace_constant_with_uniform.h"
+#include "source/fuzz/transformation_replace_id_with_synonym.h"
+#include "source/fuzz/transformation_set_function_control.h"
+#include "source/fuzz/transformation_set_loop_control.h"
+#include "source/fuzz/transformation_set_selection_control.h"
+#include "source/fuzz/transformation_split_block.h"
 #include "source/util/make_unique.h"
 #include "source/util/make_unique.h"
-#include "transformation_add_constant_boolean.h"
-#include "transformation_add_constant_scalar.h"
-#include "transformation_add_dead_break.h"
-#include "transformation_add_dead_continue.h"
-#include "transformation_add_type_boolean.h"
-#include "transformation_add_type_float.h"
-#include "transformation_add_type_int.h"
-#include "transformation_add_type_pointer.h"
-#include "transformation_copy_object.h"
-#include "transformation_move_block_down.h"
-#include "transformation_replace_boolean_constant_with_constant_binary.h"
-#include "transformation_replace_constant_with_uniform.h"
-#include "transformation_replace_id_with_synonym.h"
-#include "transformation_split_block.h"
 
 
 namespace spvtools {
 namespace spvtools {
 namespace fuzz {
 namespace fuzz {
@@ -51,6 +56,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
     case protobufs::Transformation::TransformationCase::kAddDeadContinue:
     case protobufs::Transformation::TransformationCase::kAddDeadContinue:
       return MakeUnique<TransformationAddDeadContinue>(
       return MakeUnique<TransformationAddDeadContinue>(
           message.add_dead_continue());
           message.add_dead_continue());
+    case protobufs::Transformation::TransformationCase::
+        kAddNoContractionDecoration:
+      return MakeUnique<TransformationAddNoContractionDecoration>(
+          message.add_no_contraction_decoration());
     case protobufs::Transformation::TransformationCase::kAddTypeBoolean:
     case protobufs::Transformation::TransformationCase::kAddTypeBoolean:
       return MakeUnique<TransformationAddTypeBoolean>(
       return MakeUnique<TransformationAddTypeBoolean>(
           message.add_type_boolean());
           message.add_type_boolean());
@@ -61,6 +70,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
     case protobufs::Transformation::TransformationCase::kAddTypePointer:
     case protobufs::Transformation::TransformationCase::kAddTypePointer:
       return MakeUnique<TransformationAddTypePointer>(
       return MakeUnique<TransformationAddTypePointer>(
           message.add_type_pointer());
           message.add_type_pointer());
+    case protobufs::Transformation::TransformationCase::kConstructComposite:
+      return MakeUnique<TransformationConstructComposite>(
+          message.construct_composite());
     case protobufs::Transformation::TransformationCase::kCopyObject:
     case protobufs::Transformation::TransformationCase::kCopyObject:
       return MakeUnique<TransformationCopyObject>(message.copy_object());
       return MakeUnique<TransformationCopyObject>(message.copy_object());
     case protobufs::Transformation::TransformationCase::kMoveBlockDown:
     case protobufs::Transformation::TransformationCase::kMoveBlockDown:
@@ -76,6 +88,15 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
     case protobufs::Transformation::TransformationCase::kReplaceIdWithSynonym:
     case protobufs::Transformation::TransformationCase::kReplaceIdWithSynonym:
       return MakeUnique<TransformationReplaceIdWithSynonym>(
       return MakeUnique<TransformationReplaceIdWithSynonym>(
           message.replace_id_with_synonym());
           message.replace_id_with_synonym());
+    case protobufs::Transformation::TransformationCase::kSetFunctionControl:
+      return MakeUnique<TransformationSetFunctionControl>(
+          message.set_function_control());
+    case protobufs::Transformation::TransformationCase::kSetLoopControl:
+      return MakeUnique<TransformationSetLoopControl>(
+          message.set_loop_control());
+    case protobufs::Transformation::TransformationCase::kSetSelectionControl:
+      return MakeUnique<TransformationSetSelectionControl>(
+          message.set_selection_control());
     case protobufs::Transformation::TransformationCase::kSplitBlock:
     case protobufs::Transformation::TransformationCase::kSplitBlock:
       return MakeUnique<TransformationSplitBlock>(message.split_block());
       return MakeUnique<TransformationSplitBlock>(message.split_block());
     case protobufs::Transformation::TRANSFORMATION_NOT_SET:
     case protobufs::Transformation::TRANSFORMATION_NOT_SET:

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

@@ -0,0 +1,110 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_no_contraction_decoration.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddNoContractionDecoration::
+    TransformationAddNoContractionDecoration(
+        const spvtools::fuzz::protobufs::
+            TransformationAddNoContractionDecoration& message)
+    : message_(message) {}
+
+TransformationAddNoContractionDecoration::
+    TransformationAddNoContractionDecoration(uint32_t result_id) {
+  message_.set_result_id(result_id);
+}
+
+bool TransformationAddNoContractionDecoration::IsApplicable(
+    opt::IRContext* context,
+    const spvtools::fuzz::FactManager& /*unused*/) const {
+  // |message_.result_id| must be the id of an instruction.
+  auto instr = context->get_def_use_mgr()->GetDef(message_.result_id());
+  if (!instr) {
+    return false;
+  }
+  // The instruction must be arithmetic.
+  return IsArithmetic(instr->opcode());
+}
+
+void TransformationAddNoContractionDecoration::Apply(
+    opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+  // Add a NoContraction decoration targeting |message_.result_id|.
+  context->get_decoration_mgr()->AddDecoration(message_.result_id(),
+                                               SpvDecorationNoContraction);
+}
+
+protobufs::Transformation TransformationAddNoContractionDecoration::ToMessage()
+    const {
+  protobufs::Transformation result;
+  *result.mutable_add_no_contraction_decoration() = message_;
+  return result;
+}
+
+bool TransformationAddNoContractionDecoration::IsArithmetic(uint32_t opcode) {
+  switch (opcode) {
+    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 SpvOpAny:
+    case SpvOpAll:
+    case SpvOpIsNan:
+    case SpvOpIsInf:
+    case SpvOpIsFinite:
+    case SpvOpIsNormal:
+    case SpvOpSignBitSet:
+    case SpvOpLessOrGreater:
+    case SpvOpOrdered:
+    case SpvOpUnordered:
+    case SpvOpLogicalEqual:
+    case SpvOpLogicalNotEqual:
+    case SpvOpLogicalOr:
+    case SpvOpLogicalAnd:
+    case SpvOpLogicalNot:
+      return true;
+    default:
+      return false;
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

@@ -0,0 +1,58 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_NO_CONTRACTION_DECORATION_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_NO_CONTRACTION_DECORATION_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddNoContractionDecoration : public Transformation {
+ public:
+  explicit TransformationAddNoContractionDecoration(
+      const protobufs::TransformationAddNoContractionDecoration& message);
+
+  explicit TransformationAddNoContractionDecoration(uint32_t fresh_id);
+
+  // - |message_.result_id| must be the result id of an arithmetic instruction,
+  //   as defined by the SPIR-V specification.
+  // - It does not matter whether this instruction is already annotated with the
+  //   NoContraction decoration.
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Adds a decoration of the form:
+  //   'OpDecoration |message_.result_id| NoContraction'
+  // to the module.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Returns true if and only if |opcode| is the opcode of an arithmetic
+  // instruction, as defined by the SPIR-V specification.
+  static bool IsArithmetic(uint32_t opcode);
+
+ private:
+  protobufs::TransformationAddNoContractionDecoration message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_NO_CONTRACTION_DECORATION_H_

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

@@ -0,0 +1,310 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_construct_composite.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationConstructComposite::TransformationConstructComposite(
+    const protobufs::TransformationConstructComposite& message)
+    : message_(message) {}
+
+TransformationConstructComposite::TransformationConstructComposite(
+    uint32_t composite_type_id, std::vector<uint32_t> component,
+    uint32_t base_instruction_id, uint32_t offset, uint32_t fresh_id) {
+  message_.set_composite_type_id(composite_type_id);
+  for (auto a_component : component) {
+    message_.add_component(a_component);
+  }
+  message_.set_base_instruction_id(base_instruction_id);
+  message_.set_offset(offset);
+  message_.set_fresh_id(fresh_id);
+}
+
+bool TransformationConstructComposite::IsApplicable(
+    opt::IRContext* context, const FactManager& /*fact_manager*/) const {
+  if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+    // We require the id for the composite constructor to be unused.
+    return false;
+  }
+
+  auto base_instruction =
+      context->get_def_use_mgr()->GetDef(message_.base_instruction_id());
+  if (!base_instruction) {
+    // The given id to insert after is not defined.
+    return false;
+  }
+
+  auto destination_block = context->get_instr_block(base_instruction);
+  if (!destination_block) {
+    // The given id to insert after is not in a block.
+    return false;
+  }
+
+  auto insert_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset(
+      destination_block, base_instruction, message_.offset());
+
+  if (insert_before == destination_block->end()) {
+    // The offset was inappropriate.
+    return false;
+  }
+
+  auto composite_type =
+      context->get_type_mgr()->GetType(message_.composite_type_id());
+
+  if (!fuzzerutil::IsCompositeType(composite_type)) {
+    // The type must actually be a composite.
+    return false;
+  }
+
+  // If the type is an array, matrix, struct or vector, the components need to
+  // be suitable for constructing something of that type.
+  if (composite_type->AsArray() && !ComponentsForArrayConstructionAreOK(
+                                       context, *composite_type->AsArray())) {
+    return false;
+  }
+  if (composite_type->AsMatrix() && !ComponentsForMatrixConstructionAreOK(
+                                        context, *composite_type->AsMatrix())) {
+    return false;
+  }
+  if (composite_type->AsStruct() && !ComponentsForStructConstructionAreOK(
+                                        context, *composite_type->AsStruct())) {
+    return false;
+  }
+  if (composite_type->AsVector() && !ComponentsForVectorConstructionAreOK(
+                                        context, *composite_type->AsVector())) {
+    return false;
+  }
+
+  // Now check whether every component being used to initialize the composite is
+  // available at the desired program point.
+  for (auto& component : message_.component()) {
+    auto component_inst = context->get_def_use_mgr()->GetDef(component);
+    if (!context->get_instr_block(component)) {
+      // The component does not have a block; that means it is in global scope,
+      // which is OK. (Whether the component actually corresponds to an
+      // instruction is checked above when determining whether types are
+      // suitable.)
+      continue;
+    }
+    // Check whether the component is available.
+    if (insert_before->HasResultId() &&
+        insert_before->result_id() == component) {
+      // This constitutes trying to use an id right before it is defined.  The
+      // special case is needed due to an instruction always dominating itself.
+      return false;
+    }
+    if (!context
+             ->GetDominatorAnalysis(
+                 context->get_instr_block(&*insert_before)->GetParent())
+             ->Dominates(component_inst, &*insert_before)) {
+      // The instruction defining the component must dominate the instruction we
+      // wish to insert the composite before.
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void TransformationConstructComposite::Apply(opt::IRContext* context,
+                                             FactManager* fact_manager) const {
+  // Use the base and offset information from the transformation to determine
+  // where in the module a new instruction should be inserted.
+  auto base_instruction =
+      context->get_def_use_mgr()->GetDef(message_.base_instruction_id());
+  auto destination_block = context->get_instr_block(base_instruction);
+  auto insert_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset(
+      destination_block, base_instruction, message_.offset());
+
+  // Prepare the input operands for an OpCompositeConstruct instruction.
+  opt::Instruction::OperandList in_operands;
+  for (auto& component_id : message_.component()) {
+    in_operands.push_back({SPV_OPERAND_TYPE_ID, {component_id}});
+  }
+
+  // Insert an OpCompositeConstruct instruction.
+  insert_before.InsertBefore(MakeUnique<opt::Instruction>(
+      context, SpvOpCompositeConstruct, message_.composite_type_id(),
+      message_.fresh_id(), in_operands));
+
+  // Inform the fact manager that we now have new synonyms: every component of
+  // the composite is synonymous with the id used to construct that component.
+  auto composite_type =
+      context->get_type_mgr()->GetType(message_.composite_type_id());
+  uint32_t index = 0;
+  for (auto component : message_.component()) {
+    protobufs::Fact fact;
+    fact.mutable_id_synonym_fact()->set_id(component);
+    fact.mutable_id_synonym_fact()->mutable_data_descriptor()->set_object(
+        message_.fresh_id());
+    fact.mutable_id_synonym_fact()->mutable_data_descriptor()->add_index(index);
+    fact_manager->AddFact(fact, context);
+    if (composite_type->AsVector()) {
+      // The vector case is a bit fiddly, because one argument to a vector
+      // constructor can cover more than one element.
+      auto component_type = context->get_type_mgr()->GetType(
+          context->get_def_use_mgr()->GetDef(component)->type_id());
+      if (component_type->AsVector()) {
+        assert(component_type->AsVector()->element_type() ==
+               composite_type->AsVector()->element_type());
+        index += component_type->AsVector()->element_count();
+      } else {
+        assert(component_type == composite_type->AsVector()->element_type());
+        index++;
+      }
+    } else {
+      // The non-vector cases are all easy: the constructor has exactly the same
+      // number of arguments as the number of sub-components, so we can just
+      // increment the index.
+      index++;
+    }
+  }
+
+  fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+  context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+bool TransformationConstructComposite::ComponentsForArrayConstructionAreOK(
+    opt::IRContext* context, const opt::analysis::Array& array_type) const {
+  if (array_type.length_info().words[0] !=
+      opt::analysis::Array::LengthInfo::kConstant) {
+    // We only handle constant-sized arrays.
+    return false;
+  }
+  if (array_type.length_info().words.size() != 2) {
+    // We only handle the case where the array size can be captured in a single
+    // word.
+    return false;
+  }
+  // Get the array size.
+  auto array_size = array_type.length_info().words[1];
+  if (static_cast<uint32_t>(message_.component().size()) != array_size) {
+    // The number of components must match the array size.
+    return false;
+  }
+  // Check that each component is the result id of an instruction whose type is
+  // the array's element type.
+  for (auto component_id : message_.component()) {
+    auto inst = context->get_def_use_mgr()->GetDef(component_id);
+    if (inst == nullptr || !inst->type_id()) {
+      // The component does not correspond to an instruction with a result
+      // type.
+      return false;
+    }
+    auto component_type = context->get_type_mgr()->GetType(inst->type_id());
+    assert(component_type);
+    if (component_type != array_type.element_type()) {
+      // The component's type does not match the array's element type.
+      return false;
+    }
+  }
+  return true;
+}
+
+bool TransformationConstructComposite::ComponentsForMatrixConstructionAreOK(
+    opt::IRContext* context, const opt::analysis::Matrix& matrix_type) const {
+  if (static_cast<uint32_t>(message_.component().size()) !=
+      matrix_type.element_count()) {
+    // The number of components must match the number of columns of the matrix.
+    return false;
+  }
+  // Check that each component is the result id of an instruction whose type is
+  // the matrix's column type.
+  for (auto component_id : message_.component()) {
+    auto inst = context->get_def_use_mgr()->GetDef(component_id);
+    if (inst == nullptr || !inst->type_id()) {
+      // The component does not correspond to an instruction with a result
+      // type.
+      return false;
+    }
+    auto component_type = context->get_type_mgr()->GetType(inst->type_id());
+    assert(component_type);
+    if (component_type != matrix_type.element_type()) {
+      // The component's type does not match the matrix's column type.
+      return false;
+    }
+  }
+  return true;
+}
+
+bool TransformationConstructComposite::ComponentsForStructConstructionAreOK(
+    opt::IRContext* context, const opt::analysis::Struct& struct_type) const {
+  if (static_cast<uint32_t>(message_.component().size()) !=
+      struct_type.element_types().size()) {
+    // The number of components must match the number of fields of the struct.
+    return false;
+  }
+  // Check that each component is the result id of an instruction those type
+  // matches the associated field type.
+  for (uint32_t field_index = 0;
+       field_index < struct_type.element_types().size(); field_index++) {
+    auto inst =
+        context->get_def_use_mgr()->GetDef(message_.component()[field_index]);
+    if (inst == nullptr || !inst->type_id()) {
+      // The component does not correspond to an instruction with a result
+      // type.
+      return false;
+    }
+    auto component_type = context->get_type_mgr()->GetType(inst->type_id());
+    assert(component_type);
+    if (component_type != struct_type.element_types()[field_index]) {
+      // The component's type does not match the corresponding field type.
+      return false;
+    }
+  }
+  return true;
+}
+
+bool TransformationConstructComposite::ComponentsForVectorConstructionAreOK(
+    opt::IRContext* context, const opt::analysis::Vector& vector_type) const {
+  uint32_t base_element_count = 0;
+  auto element_type = vector_type.element_type();
+  for (auto& component_id : message_.component()) {
+    auto inst = context->get_def_use_mgr()->GetDef(component_id);
+    if (inst == nullptr || !inst->type_id()) {
+      // The component does not correspond to an instruction with a result
+      // type.
+      return false;
+    }
+    auto component_type = context->get_type_mgr()->GetType(inst->type_id());
+    assert(component_type);
+    if (component_type == element_type) {
+      base_element_count++;
+    } else if (component_type->AsVector() &&
+               component_type->AsVector()->element_type() == element_type) {
+      base_element_count += component_type->AsVector()->element_count();
+    } else {
+      // The component was not appropriate; e.g. no type corresponding to the
+      // given id was found, or the type that was found was not compatible
+      // with the vector being constructed.
+      return false;
+    }
+  }
+  // The number of components provided (when vector components are flattened
+  // out) needs to match the length of the vector being constructed.
+  return base_element_count == vector_type.element_count();
+}
+
+protobufs::Transformation TransformationConstructComposite::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_construct_composite() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 88 - 0
3rdparty/spirv-tools/source/fuzz/transformation_construct_composite.h

@@ -0,0 +1,88 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_CONSTRUCT_COMPOSITE_H_
+#define SOURCE_FUZZ_TRANSFORMATION_CONSTRUCT_COMPOSITE_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationConstructComposite : public Transformation {
+ public:
+  explicit TransformationConstructComposite(
+      const protobufs::TransformationConstructComposite& message);
+
+  TransformationConstructComposite(uint32_t composite_type_id,
+                                   std::vector<uint32_t> component,
+                                   uint32_t base_instruction_id,
+                                   uint32_t offset, uint32_t fresh_id);
+
+  // - |message_.fresh_id| must not be used by the module.
+  // - |message_.composite_type_id| must be the id of a composite type
+  // - The elements of |message_.component| must be result ids that are
+  //   suitable for constructing an element of the given composite type, in
+  //   order
+  // - The elements of |message_.component| must not be the target of any
+  //   decorations.
+  // - |message_.base_instruction_id| must be the result id of an instruction
+  //   'base' in some block 'blk'.
+  // - 'blk' must contain an instruction 'inst' located |message_.offset|
+  //   instructions after 'base' (if |message_.offset| = 0 then 'inst' =
+  //   'base').
+  // - It must be legal to insert an OpCompositeConstruct instruction directly
+  //   before 'inst'.
+  // - Each element of |message_.component| must be available directly before
+  //   'inst'.
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // Inserts a new OpCompositeConstruct instruction, with id
+  // |message_.fresh_id|, directly before the instruction identified by
+  // |message_.base_instruction_id| and |message_.offset|.  The instruction
+  // creates a composite of type |message_.composite_type_id| using the ids of
+  // |message_.component|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  // Helper to decide whether the components of the transformation are suitable
+  // for constructing an array of the given type.
+  bool ComponentsForArrayConstructionAreOK(
+      opt::IRContext* context, const opt::analysis::Array& array_type) const;
+
+  // Similar, but for matrices.
+  bool ComponentsForMatrixConstructionAreOK(
+      opt::IRContext* context, const opt::analysis::Matrix& matrix_type) const;
+
+  // Similar, but for structs.
+  bool ComponentsForStructConstructionAreOK(
+      opt::IRContext* context, const opt::analysis::Struct& struct_type) const;
+
+  // Similar, but for vectors.
+  bool ComponentsForVectorConstructionAreOK(
+      opt::IRContext* context, const opt::analysis::Vector& vector_type) const;
+
+  protobufs::TransformationConstructComposite message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_CONSTRUCT_COMPOSITE_H_

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

@@ -46,7 +46,7 @@ bool TransformationCopyObject::IsApplicable(
   if (!object_inst) {
   if (!object_inst) {
     return false;
     return false;
   }
   }
-  if (!IsCopyable(context, object_inst)) {
+  if (!fuzzerutil::CanMakeSynonymOf(context, object_inst)) {
     return false;
     return false;
   }
   }
 
 
@@ -71,7 +71,8 @@ bool TransformationCopyObject::IsApplicable(
     return false;
     return false;
   }
   }
 
 
-  if (!CanInsertCopyBefore(insert_before)) {
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyObject,
+                                                    insert_before)) {
     return false;
     return false;
   }
   }
 
 
@@ -91,13 +92,6 @@ bool TransformationCopyObject::IsApplicable(
 
 
 void TransformationCopyObject::Apply(opt::IRContext* context,
 void TransformationCopyObject::Apply(opt::IRContext* context,
                                      FactManager* fact_manager) const {
                                      FactManager* fact_manager) const {
-  // - A new instruction,
-  //     %|message_.fresh_id| = OpCopyObject %ty %|message_.object|
-  //   is added directly before the instruction at |message_.insert_after_id| +
-  //   |message_|.offset, where %ty is the type of |message_.object|.
-  // - The fact that |message_.fresh_id| and |message_.object| are synonyms
-  //   is added to the fact manager.
-  // The id of the object to be copied must exist
   auto object_inst = context->get_def_use_mgr()->GetDef(message_.object());
   auto object_inst = context->get_def_use_mgr()->GetDef(message_.object());
   assert(object_inst && "The object to be copied must exist.");
   assert(object_inst && "The object to be copied must exist.");
   auto base_instruction =
   auto base_instruction =
@@ -132,43 +126,5 @@ protobufs::Transformation TransformationCopyObject::ToMessage() const {
   return result;
   return result;
 }
 }
 
 
-bool TransformationCopyObject::IsCopyable(opt::IRContext* ir_context,
-                                          opt::Instruction* inst) {
-  if (!inst->HasResultId()) {
-    // We can only apply OpCopyObject to instructions that generate ids.
-    return false;
-  }
-  if (!inst->type_id()) {
-    // We can only apply OpCopyObject to instructions that have types.
-    return false;
-  }
-  // We do not copy objects that have decorations: if the copy is not
-  // decorated analogously, using the original object vs. its copy may not be
-  // equivalent.
-  // TODO(afd): it would be possible to make the copy but not add an id
-  // synonym.
-  return ir_context->get_decoration_mgr()
-      ->GetDecorationsFor(inst->result_id(), true)
-      .empty();
-}
-
-bool TransformationCopyObject::CanInsertCopyBefore(
-    const opt::BasicBlock::iterator& instruction_in_block) {
-  if (instruction_in_block->PreviousNode() &&
-      (instruction_in_block->PreviousNode()->opcode() == SpvOpLoopMerge ||
-       instruction_in_block->PreviousNode()->opcode() == SpvOpSelectionMerge)) {
-    // We cannot insert a copy directly after a merge instruction.
-    return false;
-  }
-  if (instruction_in_block->opcode() == SpvOpVariable) {
-    // We cannot insert a copy directly before a variable; variables in a
-    // function must be contiguous in the entry block.
-    return false;
-  }
-  // We cannot insert a copy directly before OpPhi, because OpPhi instructions
-  // need to be contiguous at the start of a block.
-  return instruction_in_block->opcode() != SpvOpPhi;
-}
-
 }  // namespace fuzz
 }  // namespace fuzz
 }  // namespace spvtools
 }  // namespace spvtools

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

@@ -37,14 +37,14 @@ class TransformationCopyObject : public Transformation {
   //   has a result type
   //   has a result type
   // - |message_.object| must not be the target of any decoration.
   // - |message_.object| must not be the target of any decoration.
   //   TODO(afd): consider copying decorations along with objects.
   //   TODO(afd): consider copying decorations along with objects.
-  // - |message_.insert_after_id| must be the result id of an instruction
+  // - |message_.base_instruction_id| must be the result id of an instruction
   //   'base' in some block 'blk'.
   //   'base' in some block 'blk'.
   // - 'blk' must contain an instruction 'inst' located |message_.offset|
   // - 'blk' must contain an instruction 'inst' located |message_.offset|
   //   instructions after 'base' (if |message_.offset| = 0 then 'inst' =
   //   instructions after 'base' (if |message_.offset| = 0 then 'inst' =
   //   'base').
   //   'base').
   // - It must be legal to insert an OpCopyObject instruction directly
   // - It must be legal to insert an OpCopyObject instruction directly
   //   before 'inst'.
   //   before 'inst'.
-  // - |message_object| must be available directly before 'inst'.
+  // - |message_.object| must be available directly before 'inst'.
   bool IsApplicable(opt::IRContext* context,
   bool IsApplicable(opt::IRContext* context,
                     const FactManager& fact_manager) const override;
                     const FactManager& fact_manager) const override;
 
 
@@ -58,14 +58,6 @@ class TransformationCopyObject : public Transformation {
 
 
   protobufs::Transformation ToMessage() const override;
   protobufs::Transformation ToMessage() const override;
 
 
-  // Determines whether it is OK to make a copy of |inst|.
-  static bool IsCopyable(opt::IRContext* ir_context, opt::Instruction* inst);
-
-  // Determines whether it is OK to insert a copy instruction before the given
-  // instruction.
-  static bool CanInsertCopyBefore(
-      const opt::BasicBlock::iterator& instruction_in_block);
-
  private:
  private:
   protobufs::TransformationCopyObject message_;
   protobufs::TransformationCopyObject message_;
 };
 };

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

@@ -238,7 +238,7 @@ bool TransformationReplaceBooleanConstantWithConstantBinary::IsApplicable(
 
 
   // The id use descriptor must identify some instruction
   // The id use descriptor must identify some instruction
   auto instruction =
   auto instruction =
-      transformation::FindInstruction(message_.id_use_descriptor(), context);
+      FindInstructionContainingUse(message_.id_use_descriptor(), context);
   if (instruction == nullptr) {
   if (instruction == nullptr) {
     return false;
     return false;
   }
   }
@@ -268,7 +268,7 @@ TransformationReplaceBooleanConstantWithConstantBinary::ApplyWithResult(
       message_.fresh_id_for_binary_operation(), operands);
       message_.fresh_id_for_binary_operation(), operands);
   opt::Instruction* result = binary_instruction.get();
   opt::Instruction* result = binary_instruction.get();
   auto instruction_containing_constant_use =
   auto instruction_containing_constant_use =
-      transformation::FindInstruction(message_.id_use_descriptor(), context);
+      FindInstructionContainingUse(message_.id_use_descriptor(), context);
 
 
   // We want to insert the new instruction before the instruction that contains
   // We want to insert the new instruction before the instruction that contains
   // the use of the boolean, but we need to go backwards one more instruction if
   // the use of the boolean, but we need to go backwards one more instruction if

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

@@ -149,7 +149,7 @@ bool TransformationReplaceConstantWithUniform::IsApplicable(
   // The id use descriptor must identify some instruction with respect to the
   // The id use descriptor must identify some instruction with respect to the
   // module.
   // module.
   auto instruction_using_constant =
   auto instruction_using_constant =
-      transformation::FindInstruction(message_.id_use_descriptor(), context);
+      FindInstructionContainingUse(message_.id_use_descriptor(), context);
   if (!instruction_using_constant) {
   if (!instruction_using_constant) {
     return false;
     return false;
   }
   }
@@ -188,7 +188,7 @@ void TransformationReplaceConstantWithUniform::Apply(
     spvtools::fuzz::FactManager* /*unused*/) const {
     spvtools::fuzz::FactManager* /*unused*/) const {
   // Get the instruction that contains the id use we wish to replace.
   // Get the instruction that contains the id use we wish to replace.
   auto instruction_containing_constant_use =
   auto instruction_containing_constant_use =
-      transformation::FindInstruction(message_.id_use_descriptor(), context);
+      FindInstructionContainingUse(message_.id_use_descriptor(), context);
   assert(instruction_containing_constant_use &&
   assert(instruction_containing_constant_use &&
          "Precondition requires that the id use can be found.");
          "Precondition requires that the id use can be found.");
   assert(instruction_containing_constant_use->GetSingleWordInOperand(
   assert(instruction_containing_constant_use->GetSingleWordInOperand(

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

@@ -20,6 +20,7 @@
 #include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/id_use_descriptor.h"
 #include "source/fuzz/id_use_descriptor.h"
 #include "source/opt/types.h"
 #include "source/opt/types.h"
+#include "source/util/make_unique.h"
 
 
 namespace spvtools {
 namespace spvtools {
 namespace fuzz {
 namespace fuzz {
@@ -33,9 +34,7 @@ TransformationReplaceIdWithSynonym::TransformationReplaceIdWithSynonym(
     protobufs::IdUseDescriptor id_use_descriptor,
     protobufs::IdUseDescriptor id_use_descriptor,
     protobufs::DataDescriptor data_descriptor,
     protobufs::DataDescriptor data_descriptor,
     uint32_t fresh_id_for_temporary) {
     uint32_t fresh_id_for_temporary) {
-  assert(fresh_id_for_temporary == 0 && data_descriptor.index().size() == 0 &&
-         "At present we do not support making an id that is synonymous with an "
-         "index into a composite.");
+  assert((fresh_id_for_temporary == 0) == (data_descriptor.index().empty()));
   *message_.mutable_id_use_descriptor() = std::move(id_use_descriptor);
   *message_.mutable_id_use_descriptor() = std::move(id_use_descriptor);
   *message_.mutable_data_descriptor() = std::move(data_descriptor);
   *message_.mutable_data_descriptor() = std::move(data_descriptor);
   message_.set_fresh_id_for_temporary(fresh_id_for_temporary);
   message_.set_fresh_id_for_temporary(fresh_id_for_temporary);
@@ -61,12 +60,15 @@ bool TransformationReplaceIdWithSynonym::IsApplicable(
     return false;
     return false;
   }
   }
 
 
+  // Does the id use descriptor in the transformation identify an instruction?
   auto use_instruction =
   auto use_instruction =
-      transformation::FindInstruction(message_.id_use_descriptor(), context);
+      FindInstructionContainingUse(message_.id_use_descriptor(), context);
   if (!use_instruction) {
   if (!use_instruction) {
     return false;
     return false;
   }
   }
 
 
+  // Is it legitimate to replace the use identified by the id use descriptor
+  // with a synonym?
   if (!ReplacingUseWithSynonymIsOk(
   if (!ReplacingUseWithSynonymIsOk(
           context, use_instruction,
           context, use_instruction,
           message_.id_use_descriptor().in_operand_index(),
           message_.id_use_descriptor().in_operand_index(),
@@ -74,8 +76,23 @@ bool TransformationReplaceIdWithSynonym::IsApplicable(
     return false;
     return false;
   }
   }
 
 
-  assert(message_.fresh_id_for_temporary() == 0);
-  assert(message_.data_descriptor().index().empty());
+  if (message_.fresh_id_for_temporary() == 0) {
+    if (!message_.data_descriptor().index().empty()) {
+      // If we have no id to use as a temporary variable, we should not have any
+      // indices to extract from.
+      return false;
+    }
+  } else {
+    if (!fuzzerutil::IsFreshId(context, message_.fresh_id_for_temporary())) {
+      // The id to be used as a temporary needs to be fresh.
+      return false;
+    }
+    if (message_.data_descriptor().index_size() != 1) {
+      // At present we support just a single index to allow extracting directly
+      // from a composite.
+      return false;
+    }
+  }
 
 
   return true;
   return true;
 }
 }
@@ -83,12 +100,79 @@ bool TransformationReplaceIdWithSynonym::IsApplicable(
 void TransformationReplaceIdWithSynonym::Apply(
 void TransformationReplaceIdWithSynonym::Apply(
     spvtools::opt::IRContext* context,
     spvtools::opt::IRContext* context,
     spvtools::fuzz::FactManager* /*unused*/) const {
     spvtools::fuzz::FactManager* /*unused*/) const {
-  assert(message_.data_descriptor().index().empty());
   auto instruction_to_change =
   auto instruction_to_change =
-      transformation::FindInstruction(message_.id_use_descriptor(), context);
+      FindInstructionContainingUse(message_.id_use_descriptor(), context);
+
+  // Ultimately we are going to replace the id use identified in the
+  // transformation with |replacement_id|, which will either be the synonym's
+  // id, or the id of a temporary used to extract the synonym from a composite.
+  uint32_t replacement_id;
+
+  if (message_.fresh_id_for_temporary()) {
+    // The transformation having a temporary variable means that we need to
+    // extract the synonym from a composite.
+
+    uint32_t type_id_of_id_to_be_replaced =
+        context->get_def_use_mgr()
+            ->GetDef(message_.id_use_descriptor().id_of_interest())
+            ->type_id();
+    opt::analysis::Type* type_of_id_to_be_replaced =
+        context->get_type_mgr()->GetType(type_id_of_id_to_be_replaced);
+    opt::analysis::Type* type_of_composite = context->get_type_mgr()->GetType(
+        context->get_def_use_mgr()
+            ->GetDef(message_.data_descriptor().object())
+            ->type_id());
+
+    // Intuitively we want to make an OpCompositeExtract instruction, to get the
+    // synonym out of the composite. But in the case of a vector, the synonym
+    // might involve multiple vector indices; e.g. the y and z components of a
+    // vec4 might be synonymous with a vec2, and in that case OpCompositeExtract
+    // doesn't give us what we want; we need to use OpVectorShuffle instead.
+    std::unique_ptr<opt::Instruction> new_instruction;
+    if (type_of_composite->AsVector() &&
+        type_of_composite->AsVector()->element_type() !=
+            type_of_id_to_be_replaced) {
+      // We need to extract a vector from inside a vector, so we will need to
+      // use OpVectorShuffle.
+
+      assert(type_of_id_to_be_replaced->AsVector());
+      assert(type_of_id_to_be_replaced->AsVector()->element_type() ==
+             type_of_composite->AsVector()->element_type());
+      opt::Instruction::OperandList shuffle_operands = {
+          {SPV_OPERAND_TYPE_ID, {message_.data_descriptor().object()}},
+          {SPV_OPERAND_TYPE_ID, {message_.data_descriptor().object()}}};
+      for (uint32_t i = 0;
+           i < type_of_id_to_be_replaced->AsVector()->element_count(); i++) {
+        shuffle_operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER,
+                                    {message_.data_descriptor().index(0) + i}});
+      }
+      new_instruction = MakeUnique<opt::Instruction>(
+          context, SpvOpVectorShuffle, type_id_of_id_to_be_replaced,
+          message_.fresh_id_for_temporary(), shuffle_operands);
+    } else {
+      // We are either extracting from a non-vector, or extracting a scalar from
+      // a vector, so we can use OpCompositeExtract.
+      opt::Instruction::OperandList extract_operands = {
+          {SPV_OPERAND_TYPE_ID, {message_.data_descriptor().object()}},
+          {SPV_OPERAND_TYPE_LITERAL_INTEGER,
+           {message_.data_descriptor().index(0)}}};
+      new_instruction = MakeUnique<opt::Instruction>(
+          context, SpvOpCompositeExtract, type_id_of_id_to_be_replaced,
+          message_.fresh_id_for_temporary(), extract_operands);
+    }
+    instruction_to_change->InsertBefore(std::move(new_instruction));
+
+    // The replacement id is the temporary variable we used to extract the
+    // synonym from a composite.
+    replacement_id = message_.fresh_id_for_temporary();
+    fuzzerutil::UpdateModuleIdBound(context, replacement_id);
+  } else {
+    // The replacement id is the synonym's id.
+    replacement_id = message_.data_descriptor().object();
+  }
+
   instruction_to_change->SetInOperand(
   instruction_to_change->SetInOperand(
-      message_.id_use_descriptor().in_operand_index(),
-      {message_.data_descriptor().object()});
+      message_.id_use_descriptor().in_operand_index(), {replacement_id});
   context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
   context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
 }
 }
 
 

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

@@ -0,0 +1,100 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_set_function_control.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationSetFunctionControl::TransformationSetFunctionControl(
+    const spvtools::fuzz::protobufs::TransformationSetFunctionControl& message)
+    : message_(message) {}
+
+TransformationSetFunctionControl::TransformationSetFunctionControl(
+    uint32_t function_id, uint32_t function_control) {
+  message_.set_function_id(function_id);
+  message_.set_function_control(function_control);
+}
+
+bool TransformationSetFunctionControl::IsApplicable(
+    opt::IRContext* context, const FactManager& /*unused*/) const {
+  opt::Instruction* function_def_instruction =
+      FindFunctionDefInstruction(context);
+  if (!function_def_instruction) {
+    // The given function id does not correspond to any function.
+    return false;
+  }
+  uint32_t existing_function_control_mask =
+      function_def_instruction->GetSingleWordInOperand(0);
+
+  // Check (via an assertion) that function control mask doesn't have any bad
+  // bits set.
+  uint32_t acceptable_function_control_bits =
+      SpvFunctionControlInlineMask | SpvFunctionControlDontInlineMask |
+      SpvFunctionControlPureMask | SpvFunctionControlConstMask;
+  // The following is to keep release-mode compilers happy as this variable is
+  // only used in an assertion.
+  (void)(acceptable_function_control_bits);
+  assert(!(message_.function_control() & ~acceptable_function_control_bits) &&
+         "Nonsensical loop control bits were found.");
+
+  // Check (via an assertion) that function control mask does not have both
+  // Inline and DontInline bits set.
+  assert(!((message_.function_control() & SpvFunctionControlInlineMask) &&
+           (message_.function_control() & SpvFunctionControlDontInlineMask)) &&
+         "It is not OK to set both the 'Inline' and 'DontInline' bits of a "
+         "function control mask");
+
+  // Check that Const and Pure are only present if they were present on the
+  // original function
+  for (auto mask_bit :
+       {SpvFunctionControlPureMask, SpvFunctionControlConstMask}) {
+    if ((message_.function_control() & mask_bit) &&
+        !(existing_function_control_mask & mask_bit)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void TransformationSetFunctionControl::Apply(opt::IRContext* context,
+                                             FactManager* /*unused*/) const {
+  opt::Instruction* function_def_instruction =
+      FindFunctionDefInstruction(context);
+  function_def_instruction->SetInOperand(0, {message_.function_control()});
+}
+
+protobufs::Transformation TransformationSetFunctionControl::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_set_function_control() = message_;
+  return result;
+}
+
+opt::Instruction* TransformationSetFunctionControl ::FindFunctionDefInstruction(
+    opt::IRContext* context) const {
+  // Look through all functions for a function whose defining instruction's
+  // result id matches |message_.function_id|, returning the defining
+  // instruction if found.
+  for (auto& function : *context->module()) {
+    if (function.DefInst().result_id() == message_.function_id()) {
+      return &function.DefInst();
+    }
+  }
+  // A nullptr result indicates that no match was found.
+  return nullptr;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

@@ -0,0 +1,58 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_SET_FUNCTION_CONTROL_H_
+#define SOURCE_FUZZ_TRANSFORMATION_SET_FUNCTION_CONTROL_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationSetFunctionControl : public Transformation {
+ public:
+  explicit TransformationSetFunctionControl(
+      const protobufs::TransformationSetFunctionControl& message);
+
+  TransformationSetFunctionControl(uint32_t function_id,
+                                   uint32_t function_control);
+
+  // - |message_.function_id| must be the result id of an OpFunction
+  //   instruction.
+  // - |message_.function_control| must be a function control mask that sets
+  //   at most one of 'Inline' or 'DontInline', and that may not contain 'Pure'
+  //   (respectively 'Const') unless the existing function control mask contains
+  //   'Pure' (respectively 'Const').
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // The function control operand of instruction |message_.function_id| is
+  // over-written with |message_.function_control|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  opt::Instruction* FindFunctionDefInstruction(opt::IRContext* context) const;
+
+  protobufs::TransformationSetFunctionControl message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_SET_FUNCTION_CONTROL_H_

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

@@ -0,0 +1,216 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_set_loop_control.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationSetLoopControl::TransformationSetLoopControl(
+    const spvtools::fuzz::protobufs::TransformationSetLoopControl& message)
+    : message_(message) {}
+
+TransformationSetLoopControl::TransformationSetLoopControl(
+    uint32_t block_id, uint32_t loop_control, uint32_t peel_count,
+    uint32_t partial_count) {
+  message_.set_block_id(block_id);
+  message_.set_loop_control(loop_control);
+  message_.set_peel_count(peel_count);
+  message_.set_partial_count(partial_count);
+}
+
+bool TransformationSetLoopControl::IsApplicable(
+    opt::IRContext* context, const FactManager& /*unused*/) const {
+  // |message_.block_id| must identify a block that ends with OpLoopMerge.
+  auto block = context->get_instr_block(message_.block_id());
+  if (!block) {
+    return false;
+  }
+  auto merge_inst = block->GetMergeInst();
+  if (!merge_inst || merge_inst->opcode() != SpvOpLoopMerge) {
+    return false;
+  }
+
+  // We sanity-check that the transformation does not try to set any meaningless
+  // bits of the loop control mask.
+  uint32_t all_loop_control_mask_bits_set =
+      SpvLoopControlUnrollMask | SpvLoopControlDontUnrollMask |
+      SpvLoopControlDependencyInfiniteMask |
+      SpvLoopControlDependencyLengthMask | SpvLoopControlMinIterationsMask |
+      SpvLoopControlMaxIterationsMask | SpvLoopControlIterationMultipleMask |
+      SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask;
+
+  // The variable is only used in an assertion; the following keeps release-mode
+  // compilers happy.
+  (void)(all_loop_control_mask_bits_set);
+
+  // No additional bits should be set.
+  assert(!(message_.loop_control() & ~all_loop_control_mask_bits_set));
+
+  // Grab the loop control mask currently associated with the OpLoopMerge
+  // instruction.
+  auto existing_loop_control_mask =
+      merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex);
+
+  // Check that there is no attempt to set one of the loop controls that
+  // requires guarantees to hold.
+  for (SpvLoopControlMask mask :
+       {SpvLoopControlDependencyInfiniteMask,
+        SpvLoopControlDependencyLengthMask, SpvLoopControlMinIterationsMask,
+        SpvLoopControlMaxIterationsMask, SpvLoopControlIterationMultipleMask}) {
+    // We have a problem if this loop control bit was not set in the original
+    // loop control mask but is set by the transformation.
+    if (LoopControlBitIsAddedByTransformation(mask,
+                                              existing_loop_control_mask)) {
+      return false;
+    }
+  }
+
+  if ((message_.loop_control() &
+       (SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask)) &&
+      !(PeelCountIsSupported(context) && PartialCountIsSupported(context))) {
+    // At least one of PeelCount or PartialCount is used, but the SPIR-V version
+    // in question does not support these loop controls.
+    return false;
+  }
+
+  if (message_.peel_count() > 0 &&
+      !(message_.loop_control() & SpvLoopControlPeelCountMask)) {
+    // Peel count provided, but peel count mask bit not set.
+    return false;
+  }
+
+  if (message_.partial_count() > 0 &&
+      !(message_.loop_control() & SpvLoopControlPartialCountMask)) {
+    // Partial count provided, but partial count mask bit not set.
+    return false;
+  }
+
+  // We must not set both 'don't unroll' and one of 'peel count' or 'partial
+  // count'.
+  return !((message_.loop_control() & SpvLoopControlDontUnrollMask) &&
+           (message_.loop_control() &
+            (SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask)));
+}
+
+void TransformationSetLoopControl::Apply(opt::IRContext* context,
+                                         FactManager* /*unused*/) const {
+  // Grab the loop merge instruction and its associated loop control mask.
+  auto merge_inst =
+      context->get_instr_block(message_.block_id())->GetMergeInst();
+  auto existing_loop_control_mask =
+      merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex);
+
+  // We are going to replace the OpLoopMerge's operands with this list.
+  opt::Instruction::OperandList new_operands;
+  // We add the existing merge block and continue target ids.
+  new_operands.push_back(merge_inst->GetInOperand(0));
+  new_operands.push_back(merge_inst->GetInOperand(1));
+  // We use the loop control mask from the transformation.
+  new_operands.push_back(
+      {SPV_OPERAND_TYPE_LOOP_CONTROL, {message_.loop_control()}});
+
+  // It remains to determine what literals to provide, in association with
+  // the new loop control mask.
+  //
+  // For the loop controls that require guarantees to hold about the number
+  // of loop iterations, we need to keep, from the original OpLoopMerge, any
+  // literals associated with loop control bits that are still set.
+
+  uint32_t literal_index = 0;  // Indexes into the literals from the original
+  // instruction.
+  for (SpvLoopControlMask mask :
+       {SpvLoopControlDependencyLengthMask, SpvLoopControlMinIterationsMask,
+        SpvLoopControlMaxIterationsMask, SpvLoopControlIterationMultipleMask}) {
+    // Check whether the bit was set in the original loop control mask.
+    if (existing_loop_control_mask & mask) {
+      // Check whether the bit is set in the new loop control mask.
+      if (message_.loop_control() & mask) {
+        // Add the associated literal to our sequence of replacement operands.
+        new_operands.push_back(
+            {SPV_OPERAND_TYPE_LITERAL_INTEGER,
+             {merge_inst->GetSingleWordInOperand(
+                 kLoopControlFirstLiteralInOperandIndex + literal_index)}});
+      }
+      // Increment our index into the original loop control mask's literals,
+      // whether or not the bit was set in the new mask.
+      literal_index++;
+    }
+  }
+
+  // If PeelCount is set in the new mask, |message_.peel_count| provides the
+  // associated peel count.
+  if (message_.loop_control() & SpvLoopControlPeelCountMask) {
+    new_operands.push_back(
+        {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.peel_count()}});
+  }
+
+  // Similar, but for PartialCount.
+  if (message_.loop_control() & SpvLoopControlPartialCountMask) {
+    new_operands.push_back(
+        {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.partial_count()}});
+  }
+
+  // Replace the input operands of the OpLoopMerge with the new operands we have
+  // accumulated.
+  merge_inst->SetInOperands(std::move(new_operands));
+}
+
+protobufs::Transformation TransformationSetLoopControl::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_set_loop_control() = message_;
+  return result;
+}
+
+bool TransformationSetLoopControl::LoopControlBitIsAddedByTransformation(
+    SpvLoopControlMask loop_control_single_bit_mask,
+    uint32_t existing_loop_control_mask) const {
+  return !(loop_control_single_bit_mask & existing_loop_control_mask) &&
+         (loop_control_single_bit_mask & message_.loop_control());
+}
+
+bool TransformationSetLoopControl::PartialCountIsSupported(
+    opt::IRContext* context) {
+  // TODO(afd): We capture the universal environments for which this loop
+  //  control is definitely not supported.  The check should be refined on
+  //  demand for other target environments.
+  switch (context->grammar().target_env()) {
+    case SPV_ENV_UNIVERSAL_1_0:
+    case SPV_ENV_UNIVERSAL_1_1:
+    case SPV_ENV_UNIVERSAL_1_2:
+    case SPV_ENV_UNIVERSAL_1_3:
+      return false;
+    default:
+      return true;
+  }
+}
+
+bool TransformationSetLoopControl::PeelCountIsSupported(
+    opt::IRContext* context) {
+  // TODO(afd): We capture the universal environments for which this loop
+  //  control is definitely not supported.  The check should be refined on
+  //  demand for other target environments.
+  switch (context->grammar().target_env()) {
+    case SPV_ENV_UNIVERSAL_1_0:
+    case SPV_ENV_UNIVERSAL_1_1:
+    case SPV_ENV_UNIVERSAL_1_2:
+    case SPV_ENV_UNIVERSAL_1_3:
+      return false;
+    default:
+      return true;
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 79 - 0
3rdparty/spirv-tools/source/fuzz/transformation_set_loop_control.h

@@ -0,0 +1,79 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_SET_LOOP_CONTROL_H_
+#define SOURCE_FUZZ_TRANSFORMATION_SET_LOOP_CONTROL_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationSetLoopControl : public Transformation {
+ public:
+  const static uint32_t kLoopControlMaskInOperandIndex = 2;
+  const static uint32_t kLoopControlFirstLiteralInOperandIndex = 3;
+
+  explicit TransformationSetLoopControl(
+      const protobufs::TransformationSetLoopControl& message);
+
+  TransformationSetLoopControl(uint32_t block_id, uint32_t loop_control,
+                               uint32_t peel_count, uint32_t partial_count);
+
+  // - |message_.block_id| must be a block containing an OpLoopMerge
+  //   instruction.
+  // - |message_.loop_control| must be a legal loop control mask that
+  //   only uses controls available in the SPIR-V version associated with
+  //   |context|, and must not add loop controls that are only valid in the
+  //   presence of guarantees about what the loop does (e.g. MinIterations).
+  // - |message_.peel_count| (respectively |message_.partial_count|) must be
+  //   zero PeelCount (respectively PartialCount) is set in
+  //   |message_.loop_control|.
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // - The loop control operand of the OpLoopMergeInstruction in
+  //   |message_.block_id| is overwritten with |message_.loop_control|.
+  // - The literals associated with the loop control are updated to reflect any
+  //   controls with associated literals that have been removed (e.g.
+  //   MinIterations), and any that have been added (PeelCount and/or
+  //   PartialCount).
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Does the version of SPIR-V being used support the PartialCount loop
+  // control?
+  static bool PartialCountIsSupported(opt::IRContext* context);
+
+  // Does the version of SPIR-V being used support the PeelCount loop control?
+  static bool PeelCountIsSupported(opt::IRContext* context);
+
+ private:
+  // Returns true if and only if |loop_single_bit_mask| is *not* set in
+  // |existing_loop_control| but *is* set in |message_.loop_control|.
+  bool LoopControlBitIsAddedByTransformation(
+      SpvLoopControlMask loop_control_single_bit_mask,
+      uint32_t existing_loop_control_mask) const;
+
+  protobufs::TransformationSetLoopControl message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_SET_LOOP_CONTROL_H_

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

@@ -0,0 +1,60 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_set_selection_control.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationSetSelectionControl::TransformationSetSelectionControl(
+    const spvtools::fuzz::protobufs::TransformationSetSelectionControl& message)
+    : message_(message) {}
+
+TransformationSetSelectionControl::TransformationSetSelectionControl(
+    uint32_t block_id, uint32_t selection_control) {
+  message_.set_block_id(block_id);
+  message_.set_selection_control(selection_control);
+}
+
+bool TransformationSetSelectionControl::IsApplicable(
+    opt::IRContext* context, const FactManager& /*unused*/) const {
+  assert((message_.selection_control() == SpvSelectionControlMaskNone ||
+          message_.selection_control() == SpvSelectionControlFlattenMask ||
+          message_.selection_control() == SpvSelectionControlDontFlattenMask) &&
+         "Selection control should never be set to something other than "
+         "'None', 'Flatten' or 'DontFlatten'");
+  if (auto block = context->get_instr_block(message_.block_id())) {
+    if (auto merge_inst = block->GetMergeInst()) {
+      return merge_inst->opcode() == SpvOpSelectionMerge;
+    }
+  }
+  // Either the block did not exit, or did not end with OpSelectionMerge.
+  return false;
+}
+
+void TransformationSetSelectionControl::Apply(opt::IRContext* context,
+                                              FactManager* /*unused*/) const {
+  context->get_instr_block(message_.block_id())
+      ->GetMergeInst()
+      ->SetInOperand(1, {message_.selection_control()});
+}
+
+protobufs::Transformation TransformationSetSelectionControl::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_set_selection_control() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 54 - 0
3rdparty/spirv-tools/source/fuzz/transformation_set_selection_control.h

@@ -0,0 +1,54 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_SET_SELECTION_CONTROL_H_
+#define SOURCE_FUZZ_TRANSFORMATION_SET_SELECTION_CONTROL_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationSetSelectionControl : public Transformation {
+ public:
+  explicit TransformationSetSelectionControl(
+      const protobufs::TransformationSetSelectionControl& message);
+
+  TransformationSetSelectionControl(uint32_t block_id,
+                                    uint32_t selection_control);
+
+  // - |message_.block_id| must be a block containing an OpSelectionMerge
+  //   instruction.
+  // - |message_.selection_control| must be one of None, Flatten or
+  //   DontFlatten.
+  bool IsApplicable(opt::IRContext* context,
+                    const FactManager& fact_manager) const override;
+
+  // - The selection control operand of the OpSelectionMergeInstruction in
+  //   |message_.block_id| is overwritten with |message_.selection_control|.
+  void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationSetSelectionControl message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_SET_SELECTION_CONTROL_H_

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

@@ -94,6 +94,14 @@ void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) {
   if (analyses_to_invalidate & kAnalysisTypes) {
   if (analyses_to_invalidate & kAnalysisTypes) {
     analyses_to_invalidate |= kAnalysisConstants;
     analyses_to_invalidate |= kAnalysisConstants;
   }
   }
+
+  // The dominator analysis hold the psuedo entry and exit nodes from the CFG.
+  // Also if the CFG change the dominators many changed as well, so the
+  // dominator analysis should be invalidated as well.
+  if (analyses_to_invalidate & kAnalysisCFG) {
+    analyses_to_invalidate |= kAnalysisDominatorAnalysis;
+  }
+
   if (analyses_to_invalidate & kAnalysisDefUse) {
   if (analyses_to_invalidate & kAnalysisDefUse) {
     def_use_mgr_.reset(nullptr);
     def_use_mgr_.reset(nullptr);
   }
   }

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

@@ -409,6 +409,22 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) {
                {static_cast<uint32_t>(
                {static_cast<uint32_t>(
                    type->AsForwardPointer()->storage_class())}}});
                    type->AsForwardPointer()->storage_class())}}});
       break;
       break;
+    case Type::kCooperativeMatrixNV: {
+      auto coop_mat = type->AsCooperativeMatrixNV();
+      uint32_t const component_type =
+          GetTypeInstruction(coop_mat->component_type());
+      if (component_type == 0) {
+        return 0;
+      }
+      typeInst = MakeUnique<Instruction>(
+          context(), SpvOpTypeCooperativeMatrixNV, 0, id,
+          std::initializer_list<Operand>{
+              {SPV_OPERAND_TYPE_ID, {component_type}},
+              {SPV_OPERAND_TYPE_SCOPE_ID, {coop_mat->scope_id()}},
+              {SPV_OPERAND_TYPE_ID, {coop_mat->rows_id()}},
+              {SPV_OPERAND_TYPE_ID, {coop_mat->columns_id()}}});
+      break;
+    }
     default:
     default:
       assert(false && "Unexpected type");
       assert(false && "Unexpected type");
       break;
       break;
@@ -604,6 +620,14 @@ Type* TypeManager::RebuildType(const Type& type) {
       }
       }
       break;
       break;
     }
     }
+    case Type::kCooperativeMatrixNV: {
+      const CooperativeMatrixNV* cm_type = type.AsCooperativeMatrixNV();
+      const Type* component_type = cm_type->component_type();
+      rebuilt_ty = MakeUnique<CooperativeMatrixNV>(
+          RebuildType(*component_type), cm_type->scope_id(), cm_type->rows_id(),
+          cm_type->columns_id());
+      break;
+    }
     default:
     default:
       assert(false && "Unhandled type");
       assert(false && "Unhandled type");
       return nullptr;
       return nullptr;
@@ -832,6 +856,12 @@ Type* TypeManager::RecordIfTypeDefinition(const Instruction& inst) {
     case SpvOpTypeAccelerationStructureNV:
     case SpvOpTypeAccelerationStructureNV:
       type = new AccelerationStructureNV();
       type = new AccelerationStructureNV();
       break;
       break;
+    case SpvOpTypeCooperativeMatrixNV:
+      type = new CooperativeMatrixNV(GetType(inst.GetSingleWordInOperand(0)),
+                                     inst.GetSingleWordInOperand(1),
+                                     inst.GetSingleWordInOperand(2),
+                                     inst.GetSingleWordInOperand(3));
+      break;
     default:
     default:
       SPIRV_UNIMPLEMENTED(consumer_, "unhandled type");
       SPIRV_UNIMPLEMENTED(consumer_, "unhandled type");
       break;
       break;

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

@@ -127,6 +127,7 @@ std::unique_ptr<Type> Type::Clone() const {
     DeclareKindCase(PipeStorage);
     DeclareKindCase(PipeStorage);
     DeclareKindCase(NamedBarrier);
     DeclareKindCase(NamedBarrier);
     DeclareKindCase(AccelerationStructureNV);
     DeclareKindCase(AccelerationStructureNV);
+    DeclareKindCase(CooperativeMatrixNV);
 #undef DeclareKindCase
 #undef DeclareKindCase
     default:
     default:
       assert(false && "Unhandled type");
       assert(false && "Unhandled type");
@@ -171,6 +172,7 @@ bool Type::operator==(const Type& other) const {
     DeclareKindCase(PipeStorage);
     DeclareKindCase(PipeStorage);
     DeclareKindCase(NamedBarrier);
     DeclareKindCase(NamedBarrier);
     DeclareKindCase(AccelerationStructureNV);
     DeclareKindCase(AccelerationStructureNV);
+    DeclareKindCase(CooperativeMatrixNV);
 #undef DeclareKindCase
 #undef DeclareKindCase
     default:
     default:
       assert(false && "Unhandled type");
       assert(false && "Unhandled type");
@@ -220,6 +222,7 @@ void Type::GetHashWords(std::vector<uint32_t>* words,
     DeclareKindCase(PipeStorage);
     DeclareKindCase(PipeStorage);
     DeclareKindCase(NamedBarrier);
     DeclareKindCase(NamedBarrier);
     DeclareKindCase(AccelerationStructureNV);
     DeclareKindCase(AccelerationStructureNV);
+    DeclareKindCase(CooperativeMatrixNV);
 #undef DeclareKindCase
 #undef DeclareKindCase
     default:
     default:
       assert(false && "Unhandled type");
       assert(false && "Unhandled type");
@@ -654,6 +657,44 @@ void ForwardPointer::GetExtraHashWords(
   if (pointer_) pointer_->GetHashWords(words, seen);
   if (pointer_) pointer_->GetHashWords(words, seen);
 }
 }
 
 
+CooperativeMatrixNV::CooperativeMatrixNV(const Type* type, const uint32_t scope,
+                                         const uint32_t rows,
+                                         const uint32_t columns)
+    : Type(kCooperativeMatrixNV),
+      component_type_(type),
+      scope_id_(scope),
+      rows_id_(rows),
+      columns_id_(columns) {
+  assert(type != nullptr);
+  assert(scope != 0);
+  assert(rows != 0);
+  assert(columns != 0);
+}
+
+std::string CooperativeMatrixNV::str() const {
+  std::ostringstream oss;
+  oss << "<" << component_type_->str() << ", " << scope_id_ << ", " << rows_id_
+      << ", " << columns_id_ << ">";
+  return oss.str();
+}
+
+void CooperativeMatrixNV::GetExtraHashWords(
+    std::vector<uint32_t>* words, std::unordered_set<const Type*>* pSet) const {
+  component_type_->GetHashWords(words, pSet);
+  words->push_back(scope_id_);
+  words->push_back(rows_id_);
+  words->push_back(columns_id_);
+}
+
+bool CooperativeMatrixNV::IsSameImpl(const Type* that,
+                                     IsSameCache* seen) const {
+  const CooperativeMatrixNV* mt = that->AsCooperativeMatrixNV();
+  if (!mt) return false;
+  return component_type_->IsSameImpl(mt->component_type_, seen) &&
+         scope_id_ == mt->scope_id_ && rows_id_ == mt->rows_id_ &&
+         columns_id_ == mt->columns_id_ && HasSameDecorations(that);
+}
+
 }  // namespace analysis
 }  // namespace analysis
 }  // namespace opt
 }  // namespace opt
 }  // namespace spvtools
 }  // namespace spvtools

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

@@ -58,6 +58,7 @@ class ForwardPointer;
 class PipeStorage;
 class PipeStorage;
 class NamedBarrier;
 class NamedBarrier;
 class AccelerationStructureNV;
 class AccelerationStructureNV;
+class CooperativeMatrixNV;
 
 
 // Abstract class for a SPIR-V type. It has a bunch of As<sublcass>() methods,
 // Abstract class for a SPIR-V type. It has a bunch of As<sublcass>() methods,
 // which is used as a way to probe the actual <subclass>.
 // which is used as a way to probe the actual <subclass>.
@@ -93,6 +94,7 @@ class Type {
     kPipeStorage,
     kPipeStorage,
     kNamedBarrier,
     kNamedBarrier,
     kAccelerationStructureNV,
     kAccelerationStructureNV,
+    kCooperativeMatrixNV
   };
   };
 
 
   Type(Kind k) : kind_(k) {}
   Type(Kind k) : kind_(k) {}
@@ -196,6 +198,7 @@ class Type {
   DeclareCastMethod(PipeStorage)
   DeclareCastMethod(PipeStorage)
   DeclareCastMethod(NamedBarrier)
   DeclareCastMethod(NamedBarrier)
   DeclareCastMethod(AccelerationStructureNV)
   DeclareCastMethod(AccelerationStructureNV)
+  DeclareCastMethod(CooperativeMatrixNV)
 #undef DeclareCastMethod
 #undef DeclareCastMethod
 
 
  protected:
  protected:
@@ -597,6 +600,36 @@ class ForwardPointer : public Type {
   const Pointer* pointer_;
   const Pointer* pointer_;
 };
 };
 
 
+class CooperativeMatrixNV : public Type {
+ public:
+  CooperativeMatrixNV(const Type* type, const uint32_t scope,
+                      const uint32_t rows, const uint32_t columns);
+  CooperativeMatrixNV(const CooperativeMatrixNV&) = default;
+
+  std::string str() const override;
+
+  CooperativeMatrixNV* AsCooperativeMatrixNV() override { return this; }
+  const CooperativeMatrixNV* AsCooperativeMatrixNV() const override {
+    return this;
+  }
+
+  void GetExtraHashWords(std::vector<uint32_t>*,
+                         std::unordered_set<const Type*>*) const override;
+
+  const Type* component_type() const { return component_type_; }
+  uint32_t scope_id() const { return scope_id_; }
+  uint32_t rows_id() const { return rows_id_; }
+  uint32_t columns_id() const { return columns_id_; }
+
+ private:
+  bool IsSameImpl(const Type* that, IsSameCache*) const override;
+
+  const Type* component_type_;
+  const uint32_t scope_id_;
+  const uint32_t rows_id_;
+  const uint32_t columns_id_;
+};
+
 #define DefineParameterlessType(type, name)                                    \
 #define DefineParameterlessType(type, name)                                    \
   class type : public Type {                                                   \
   class type : public Type {                                                   \
    public:                                                                     \
    public:                                                                     \

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

@@ -15,7 +15,7 @@
 #include "source/print.h"
 #include "source/print.h"
 
 
 #if defined(SPIRV_ANDROID) || defined(SPIRV_LINUX) || defined(SPIRV_MAC) || \
 #if defined(SPIRV_ANDROID) || defined(SPIRV_LINUX) || defined(SPIRV_MAC) || \
-    defined(SPIRV_FREEBSD)
+    defined(SPIRV_FREEBSD) || defined(SPIRV_EMSCRIPTEN)
 namespace spvtools {
 namespace spvtools {
 
 
 clr::reset::operator const char*() { return "\x1b[0m"; }
 clr::reset::operator const char*() { return "\x1b[0m"; }

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

@@ -29,8 +29,6 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
         remove_instruction_reduction_opportunity.h
         remove_instruction_reduction_opportunity.h
         remove_function_reduction_opportunity.h
         remove_function_reduction_opportunity.h
         remove_function_reduction_opportunity_finder.h
         remove_function_reduction_opportunity_finder.h
-        remove_opname_instruction_reduction_opportunity_finder.h
-        remove_relaxed_precision_decoration_opportunity_finder.h
         remove_selection_reduction_opportunity.h
         remove_selection_reduction_opportunity.h
         remove_selection_reduction_opportunity_finder.h
         remove_selection_reduction_opportunity_finder.h
         remove_unreferenced_instruction_reduction_opportunity_finder.h
         remove_unreferenced_instruction_reduction_opportunity_finder.h
@@ -57,11 +55,9 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
         remove_function_reduction_opportunity.cpp
         remove_function_reduction_opportunity.cpp
         remove_function_reduction_opportunity_finder.cpp
         remove_function_reduction_opportunity_finder.cpp
         remove_instruction_reduction_opportunity.cpp
         remove_instruction_reduction_opportunity.cpp
-        remove_relaxed_precision_decoration_opportunity_finder.cpp
         remove_selection_reduction_opportunity.cpp
         remove_selection_reduction_opportunity.cpp
         remove_selection_reduction_opportunity_finder.cpp
         remove_selection_reduction_opportunity_finder.cpp
         remove_unreferenced_instruction_reduction_opportunity_finder.cpp
         remove_unreferenced_instruction_reduction_opportunity_finder.cpp
-        remove_opname_instruction_reduction_opportunity_finder.cpp
         structured_loop_to_selection_reduction_opportunity.cpp
         structured_loop_to_selection_reduction_opportunity.cpp
         structured_loop_to_selection_reduction_opportunity_finder.cpp
         structured_loop_to_selection_reduction_opportunity_finder.cpp
         conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp
         conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp

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

@@ -73,7 +73,7 @@ ConditionalBranchToSimpleConditionalBranchOpportunityFinder::
         result.push_back(
         result.push_back(
             MakeUnique<
             MakeUnique<
                 ConditionalBranchToSimpleConditionalBranchReductionOpportunity>(
                 ConditionalBranchToSimpleConditionalBranchReductionOpportunity>(
-                block.terminator(), redirect_to_true));
+                context, block.terminator(), redirect_to_true));
       }
       }
     }
     }
   }
   }

+ 18 - 2
3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp

@@ -19,12 +19,15 @@
 namespace spvtools {
 namespace spvtools {
 namespace reduce {
 namespace reduce {
 
 
+using opt::IRContext;
 using opt::Instruction;
 using opt::Instruction;
 
 
 ConditionalBranchToSimpleConditionalBranchReductionOpportunity::
 ConditionalBranchToSimpleConditionalBranchReductionOpportunity::
     ConditionalBranchToSimpleConditionalBranchReductionOpportunity(
     ConditionalBranchToSimpleConditionalBranchReductionOpportunity(
-        Instruction* conditional_branch_instruction, bool redirect_to_true)
-    : conditional_branch_instruction_(conditional_branch_instruction),
+        IRContext* context, Instruction* conditional_branch_instruction,
+        bool redirect_to_true)
+    : context_(context),
+      conditional_branch_instruction_(conditional_branch_instruction),
       redirect_to_true_(redirect_to_true) {}
       redirect_to_true_(redirect_to_true) {}
 
 
 bool ConditionalBranchToSimpleConditionalBranchReductionOpportunity::
 bool ConditionalBranchToSimpleConditionalBranchReductionOpportunity::
@@ -43,11 +46,24 @@ void ConditionalBranchToSimpleConditionalBranchReductionOpportunity::Apply() {
   uint32_t operand_to_copy =
   uint32_t operand_to_copy =
       redirect_to_true_ ? kTrueBranchOperandIndex : kFalseBranchOperandIndex;
       redirect_to_true_ ? kTrueBranchOperandIndex : kFalseBranchOperandIndex;
 
 
+  auto old_successor_block_id =
+      conditional_branch_instruction_->GetSingleWordInOperand(
+          operand_to_modify);
+
   // Do the branch redirection.
   // Do the branch redirection.
   conditional_branch_instruction_->SetInOperand(
   conditional_branch_instruction_->SetInOperand(
       operand_to_modify,
       operand_to_modify,
       {conditional_branch_instruction_->GetSingleWordInOperand(
       {conditional_branch_instruction_->GetSingleWordInOperand(
           operand_to_copy)});
           operand_to_copy)});
+
+  // The old successor block may have phi instructions; these will need to
+  // respect the change in edges.
+  AdaptPhiInstructionsForRemovedEdge(
+      context_->get_instr_block(conditional_branch_instruction_)->id(),
+      context_->cfg()->block(old_successor_block_id));
+
+  // We have changed the CFG.
+  context_->InvalidateAnalysesExceptFor(IRContext::Analysis::kAnalysisNone);
 }
 }
 
 
 }  // namespace reduce
 }  // namespace reduce

+ 3 - 1
3rdparty/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h

@@ -31,7 +31,8 @@ class ConditionalBranchToSimpleConditionalBranchReductionOpportunity
   // to the true target; otherwise, the true target will be changed to also
   // to the true target; otherwise, the true target will be changed to also
   // point to the false target.
   // point to the false target.
   explicit ConditionalBranchToSimpleConditionalBranchReductionOpportunity(
   explicit ConditionalBranchToSimpleConditionalBranchReductionOpportunity(
-      opt::Instruction* conditional_branch_instruction, bool redirect_to_true);
+      opt::IRContext* context, opt::Instruction* conditional_branch_instruction,
+      bool redirect_to_true);
 
 
   bool PreconditionHolds() override;
   bool PreconditionHolds() override;
 
 
@@ -39,6 +40,7 @@ class ConditionalBranchToSimpleConditionalBranchReductionOpportunity
   void Apply() override;
   void Apply() override;
 
 
  private:
  private:
+  opt::IRContext* context_;
   opt::Instruction* conditional_branch_instruction_;
   opt::Instruction* conditional_branch_instruction_;
 
 
   // If true, the false target will be changed to point to the true target;
   // If true, the false target will be changed to point to the true target;

+ 115 - 91
3rdparty/spirv-tools/source/reduce/reducer.cpp

@@ -24,8 +24,6 @@
 #include "source/reduce/operand_to_undef_reduction_opportunity_finder.h"
 #include "source/reduce/operand_to_undef_reduction_opportunity_finder.h"
 #include "source/reduce/remove_block_reduction_opportunity_finder.h"
 #include "source/reduce/remove_block_reduction_opportunity_finder.h"
 #include "source/reduce/remove_function_reduction_opportunity_finder.h"
 #include "source/reduce/remove_function_reduction_opportunity_finder.h"
-#include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
-#include "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
 #include "source/reduce/remove_selection_reduction_opportunity_finder.h"
 #include "source/reduce/remove_selection_reduction_opportunity_finder.h"
 #include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
 #include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
 #include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h"
 #include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h"
@@ -35,41 +33,32 @@
 namespace spvtools {
 namespace spvtools {
 namespace reduce {
 namespace reduce {
 
 
-struct Reducer::Impl {
-  explicit Impl(spv_target_env env) : target_env(env) {}
-
-  bool ReachedStepLimit(uint32_t current_step,
-                        spv_const_reducer_options options);
-
-  const spv_target_env target_env;  // Target environment.
-  MessageConsumer consumer;         // Message consumer.
-  InterestingnessFunction interestingness_function;
-  std::vector<std::unique_ptr<ReductionPass>> passes;
-};
-
-Reducer::Reducer(spv_target_env env) : impl_(MakeUnique<Impl>(env)) {}
+Reducer::Reducer(spv_target_env target_env) : target_env_(target_env) {}
 
 
 Reducer::~Reducer() = default;
 Reducer::~Reducer() = default;
 
 
 void Reducer::SetMessageConsumer(MessageConsumer c) {
 void Reducer::SetMessageConsumer(MessageConsumer c) {
-  for (auto& pass : impl_->passes) {
+  for (auto& pass : passes_) {
     pass->SetMessageConsumer(c);
     pass->SetMessageConsumer(c);
   }
   }
-  impl_->consumer = std::move(c);
+  for (auto& pass : cleanup_passes_) {
+    pass->SetMessageConsumer(c);
+  }
+  consumer_ = std::move(c);
 }
 }
 
 
 void Reducer::SetInterestingnessFunction(
 void Reducer::SetInterestingnessFunction(
     Reducer::InterestingnessFunction interestingness_function) {
     Reducer::InterestingnessFunction interestingness_function) {
-  impl_->interestingness_function = std::move(interestingness_function);
+  interestingness_function_ = std::move(interestingness_function);
 }
 }
 
 
 Reducer::ReductionResultStatus Reducer::Run(
 Reducer::ReductionResultStatus Reducer::Run(
     std::vector<uint32_t>&& binary_in, std::vector<uint32_t>* binary_out,
     std::vector<uint32_t>&& binary_in, std::vector<uint32_t>* binary_out,
     spv_const_reducer_options options,
     spv_const_reducer_options options,
-    spv_validator_options validator_options) const {
+    spv_validator_options validator_options) {
   std::vector<uint32_t> current_binary(std::move(binary_in));
   std::vector<uint32_t> current_binary(std::move(binary_in));
 
 
-  spvtools::SpirvTools tools(impl_->target_env);
+  spvtools::SpirvTools tools(target_env_);
   assert(tools.IsValid() && "Failed to create SPIRV-Tools interface");
   assert(tools.IsValid() && "Failed to create SPIRV-Tools interface");
 
 
   // Keeps track of how many reduction attempts have been tried.  Reduction
   // Keeps track of how many reduction attempts have been tried.  Reduction
@@ -79,32 +68,108 @@ Reducer::ReductionResultStatus Reducer::Run(
   // Initial state should be valid.
   // Initial state should be valid.
   if (!tools.Validate(&current_binary[0], current_binary.size(),
   if (!tools.Validate(&current_binary[0], current_binary.size(),
                       validator_options)) {
                       validator_options)) {
-    impl_->consumer(SPV_MSG_INFO, nullptr, {},
-                    "Initial binary is invalid; stopping.");
+    consumer_(SPV_MSG_INFO, nullptr, {},
+              "Initial binary is invalid; stopping.");
     return Reducer::ReductionResultStatus::kInitialStateInvalid;
     return Reducer::ReductionResultStatus::kInitialStateInvalid;
   }
   }
 
 
   // Initial state should be interesting.
   // Initial state should be interesting.
-  if (!impl_->interestingness_function(current_binary, reductions_applied)) {
-    impl_->consumer(SPV_MSG_INFO, nullptr, {},
-                    "Initial state was not interesting; stopping.");
+  if (!interestingness_function_(current_binary, reductions_applied)) {
+    consumer_(SPV_MSG_INFO, nullptr, {},
+              "Initial state was not interesting; stopping.");
     return Reducer::ReductionResultStatus::kInitialStateNotInteresting;
     return Reducer::ReductionResultStatus::kInitialStateNotInteresting;
   }
   }
 
 
+  Reducer::ReductionResultStatus result =
+      RunPasses(&passes_, options, validator_options, tools, &current_binary,
+                &reductions_applied);
+
+  if (result == Reducer::ReductionResultStatus::kComplete) {
+    // Cleanup passes.
+    result = RunPasses(&cleanup_passes_, options, validator_options, tools,
+                       &current_binary, &reductions_applied);
+  }
+
+  if (result == Reducer::ReductionResultStatus::kComplete) {
+    consumer_(SPV_MSG_INFO, nullptr, {}, "No more to reduce; stopping.");
+  }
+
+  // Even if the reduction has failed by this point (e.g. due to producing an
+  // invalid binary), we still update the output binary for better debugging.
+  *binary_out = std::move(current_binary);
+
+  return result;
+}
+
+void Reducer::AddDefaultReductionPasses() {
+  AddReductionPass(
+      spvtools::MakeUnique<
+          RemoveUnreferencedInstructionReductionOpportunityFinder>(false));
+  AddReductionPass(
+      spvtools::MakeUnique<OperandToUndefReductionOpportunityFinder>());
+  AddReductionPass(
+      spvtools::MakeUnique<OperandToConstReductionOpportunityFinder>());
+  AddReductionPass(
+      spvtools::MakeUnique<OperandToDominatingIdReductionOpportunityFinder>());
+  AddReductionPass(spvtools::MakeUnique<
+                   StructuredLoopToSelectionReductionOpportunityFinder>());
+  AddReductionPass(
+      spvtools::MakeUnique<MergeBlocksReductionOpportunityFinder>());
+  AddReductionPass(
+      spvtools::MakeUnique<RemoveFunctionReductionOpportunityFinder>());
+  AddReductionPass(
+      spvtools::MakeUnique<RemoveBlockReductionOpportunityFinder>());
+  AddReductionPass(
+      spvtools::MakeUnique<RemoveSelectionReductionOpportunityFinder>());
+  AddReductionPass(
+      spvtools::MakeUnique<
+          ConditionalBranchToSimpleConditionalBranchOpportunityFinder>());
+  AddReductionPass(
+      spvtools::MakeUnique<SimpleConditionalBranchToBranchOpportunityFinder>());
+
+  // Cleanup passes.
+
+  AddCleanupReductionPass(
+      spvtools::MakeUnique<
+          RemoveUnreferencedInstructionReductionOpportunityFinder>(true));
+}
+
+void Reducer::AddReductionPass(
+    std::unique_ptr<ReductionOpportunityFinder>&& finder) {
+  passes_.push_back(
+      spvtools::MakeUnique<ReductionPass>(target_env_, std::move(finder)));
+}
+
+void Reducer::AddCleanupReductionPass(
+    std::unique_ptr<ReductionOpportunityFinder>&& finder) {
+  cleanup_passes_.push_back(
+      spvtools::MakeUnique<ReductionPass>(target_env_, std::move(finder)));
+}
+
+bool Reducer::ReachedStepLimit(uint32_t current_step,
+                               spv_const_reducer_options options) {
+  return current_step >= options->step_limit;
+}
+
+Reducer::ReductionResultStatus Reducer::RunPasses(
+    std::vector<std::unique_ptr<ReductionPass>>* passes,
+    spv_const_reducer_options options, spv_validator_options validator_options,
+    const SpirvTools& tools, std::vector<uint32_t>* current_binary,
+    uint32_t* const reductions_applied) {
   // Determines whether, on completing one round of reduction passes, it is
   // Determines whether, on completing one round of reduction passes, it is
   // worthwhile trying a further round.
   // worthwhile trying a further round.
   bool another_round_worthwhile = true;
   bool another_round_worthwhile = true;
 
 
   // Apply round after round of reduction passes until we hit the reduction
   // Apply round after round of reduction passes until we hit the reduction
   // step limit, or deem that another round is not going to be worthwhile.
   // step limit, or deem that another round is not going to be worthwhile.
-  while (!impl_->ReachedStepLimit(reductions_applied, options) &&
+  while (!ReachedStepLimit(*reductions_applied, options) &&
          another_round_worthwhile) {
          another_round_worthwhile) {
     // At the start of a round of reduction passes, assume another round will
     // At the start of a round of reduction passes, assume another round will
     // not be worthwhile unless we find evidence to the contrary.
     // not be worthwhile unless we find evidence to the contrary.
     another_round_worthwhile = false;
     another_round_worthwhile = false;
 
 
-    // Iterate through the available passes
-    for (auto& pass : impl_->passes) {
+    // Iterate through the available passes.
+    for (auto& pass : *passes) {
       // If this pass hasn't reached its minimum granularity then it's
       // If this pass hasn't reached its minimum granularity then it's
       // worth eventually doing another round of reductions, in order to
       // worth eventually doing another round of reductions, in order to
       // try this pass at a finer granularity.
       // try this pass at a finer granularity.
@@ -112,14 +177,14 @@ Reducer::ReductionResultStatus Reducer::Run(
 
 
       // Keep applying this pass at its current granularity until it stops
       // Keep applying this pass at its current granularity until it stops
       // working or we hit the reduction step limit.
       // working or we hit the reduction step limit.
-      impl_->consumer(SPV_MSG_INFO, nullptr, {},
-                      ("Trying pass " + pass->GetName() + ".").c_str());
+      consumer_(SPV_MSG_INFO, nullptr, {},
+                ("Trying pass " + pass->GetName() + ".").c_str());
       do {
       do {
-        auto maybe_result = pass->TryApplyReduction(current_binary);
+        auto maybe_result = pass->TryApplyReduction(*current_binary);
         if (maybe_result.empty()) {
         if (maybe_result.empty()) {
           // For this round, the pass has no more opportunities (chunks) to
           // For this round, the pass has no more opportunities (chunks) to
           // apply, so move on to the next pass.
           // apply, so move on to the next pass.
-          impl_->consumer(
+          consumer_(
               SPV_MSG_INFO, nullptr, {},
               SPV_MSG_INFO, nullptr, {},
               ("Pass " + pass->GetName() + " did not make a reduction step.")
               ("Pass " + pass->GetName() + " did not make a reduction step.")
                   .c_str());
                   .c_str());
@@ -127,91 +192,50 @@ Reducer::ReductionResultStatus Reducer::Run(
         }
         }
         bool interesting = false;
         bool interesting = false;
         std::stringstream stringstream;
         std::stringstream stringstream;
-        reductions_applied++;
+        (*reductions_applied)++;
         stringstream << "Pass " << pass->GetName() << " made reduction step "
         stringstream << "Pass " << pass->GetName() << " made reduction step "
-                     << reductions_applied << ".";
-        impl_->consumer(SPV_MSG_INFO, nullptr, {},
-                        (stringstream.str().c_str()));
+                     << *reductions_applied << ".";
+        consumer_(SPV_MSG_INFO, nullptr, {}, (stringstream.str().c_str()));
         if (!tools.Validate(&maybe_result[0], maybe_result.size(),
         if (!tools.Validate(&maybe_result[0], maybe_result.size(),
                             validator_options)) {
                             validator_options)) {
           // The reduction step went wrong and an invalid binary was produced.
           // The reduction step went wrong and an invalid binary was produced.
           // By design, this shouldn't happen; this is a safeguard to stop an
           // By design, this shouldn't happen; this is a safeguard to stop an
           // invalid binary from being regarded as interesting.
           // invalid binary from being regarded as interesting.
-          impl_->consumer(SPV_MSG_INFO, nullptr, {},
-                          "Reduction step produced an invalid binary.");
+          consumer_(SPV_MSG_INFO, nullptr, {},
+                    "Reduction step produced an invalid binary.");
           if (options->fail_on_validation_error) {
           if (options->fail_on_validation_error) {
+            // In this mode, we fail, so we update the current binary so it is
+            // output for debugging.
+            *current_binary = std::move(maybe_result);
             return Reducer::ReductionResultStatus::kStateInvalid;
             return Reducer::ReductionResultStatus::kStateInvalid;
           }
           }
-        } else if (impl_->interestingness_function(maybe_result,
-                                                   reductions_applied)) {
+        } else if (interestingness_function_(maybe_result,
+                                             *reductions_applied)) {
           // Success!  The binary produced by this reduction step is
           // Success!  The binary produced by this reduction step is
           // interesting, so make it the binary of interest henceforth, and
           // interesting, so make it the binary of interest henceforth, and
           // note that it's worth doing another round of reduction passes.
           // note that it's worth doing another round of reduction passes.
-          impl_->consumer(SPV_MSG_INFO, nullptr, {},
-                          "Reduction step succeeded.");
-          current_binary = std::move(maybe_result);
+          consumer_(SPV_MSG_INFO, nullptr, {}, "Reduction step succeeded.");
+          *current_binary = std::move(maybe_result);
           interesting = true;
           interesting = true;
           another_round_worthwhile = true;
           another_round_worthwhile = true;
         }
         }
         // We must call this before the next call to TryApplyReduction.
         // We must call this before the next call to TryApplyReduction.
         pass->NotifyInteresting(interesting);
         pass->NotifyInteresting(interesting);
         // Bail out if the reduction step limit has been reached.
         // Bail out if the reduction step limit has been reached.
-      } while (!impl_->ReachedStepLimit(reductions_applied, options));
+      } while (!ReachedStepLimit(*reductions_applied, options));
     }
     }
   }
   }
 
 
-  *binary_out = std::move(current_binary);
-
   // Report whether reduction completed, or bailed out early due to reaching
   // Report whether reduction completed, or bailed out early due to reaching
   // the step limit.
   // the step limit.
-  if (impl_->ReachedStepLimit(reductions_applied, options)) {
-    impl_->consumer(SPV_MSG_INFO, nullptr, {},
-                    "Reached reduction step limit; stopping.");
+  if (ReachedStepLimit(*reductions_applied, options)) {
+    consumer_(SPV_MSG_INFO, nullptr, {},
+              "Reached reduction step limit; stopping.");
     return Reducer::ReductionResultStatus::kReachedStepLimit;
     return Reducer::ReductionResultStatus::kReachedStepLimit;
   }
   }
-  impl_->consumer(SPV_MSG_INFO, nullptr, {}, "No more to reduce; stopping.");
-  return Reducer::ReductionResultStatus::kComplete;
-}
 
 
-void Reducer::AddDefaultReductionPasses() {
-  AddReductionPass(spvtools::MakeUnique<
-                   RemoveOpNameInstructionReductionOpportunityFinder>());
-  AddReductionPass(spvtools::MakeUnique<
-                   RemoveRelaxedPrecisionDecorationOpportunityFinder>());
-  AddReductionPass(
-      spvtools::MakeUnique<OperandToUndefReductionOpportunityFinder>());
-  AddReductionPass(
-      spvtools::MakeUnique<OperandToConstReductionOpportunityFinder>());
-  AddReductionPass(
-      spvtools::MakeUnique<OperandToDominatingIdReductionOpportunityFinder>());
-  AddReductionPass(spvtools::MakeUnique<
-                   RemoveUnreferencedInstructionReductionOpportunityFinder>());
-  AddReductionPass(spvtools::MakeUnique<
-                   StructuredLoopToSelectionReductionOpportunityFinder>());
-  AddReductionPass(
-      spvtools::MakeUnique<MergeBlocksReductionOpportunityFinder>());
-  AddReductionPass(
-      spvtools::MakeUnique<RemoveFunctionReductionOpportunityFinder>());
-  AddReductionPass(
-      spvtools::MakeUnique<RemoveBlockReductionOpportunityFinder>());
-  AddReductionPass(
-      spvtools::MakeUnique<RemoveSelectionReductionOpportunityFinder>());
-  AddReductionPass(
-      spvtools::MakeUnique<
-          ConditionalBranchToSimpleConditionalBranchOpportunityFinder>());
-  AddReductionPass(
-      spvtools::MakeUnique<SimpleConditionalBranchToBranchOpportunityFinder>());
-}
-
-void Reducer::AddReductionPass(
-    std::unique_ptr<ReductionOpportunityFinder>&& finder) {
-  impl_->passes.push_back(spvtools::MakeUnique<ReductionPass>(
-      impl_->target_env, std::move(finder)));
-}
-
-bool Reducer::Impl::ReachedStepLimit(uint32_t current_step,
-                                     spv_const_reducer_options options) {
-  return current_step >= options->step_limit;
+  // The passes completed successfully, although we may still run more passes.
+  return Reducer::ReductionResultStatus::kComplete;
 }
 }
 
 
 }  // namespace reduce
 }  // namespace reduce

+ 22 - 5
3rdparty/spirv-tools/source/reduce/reducer.h

@@ -50,7 +50,7 @@ class Reducer {
   using InterestingnessFunction =
   using InterestingnessFunction =
       std::function<bool(const std::vector<uint32_t>&, uint32_t)>;
       std::function<bool(const std::vector<uint32_t>&, uint32_t)>;
 
 
-  // Constructs an instance with the given target |env|, which is used to
+  // Constructs an instance with the given target |target_env|, which is used to
   // decode the binary to be reduced later.
   // decode the binary to be reduced later.
   //
   //
   // The constructed instance will have an empty message consumer, which just
   // The constructed instance will have an empty message consumer, which just
@@ -59,7 +59,7 @@ class Reducer {
   //
   //
   // The constructed instance also needs to have an interestingness function
   // The constructed instance also needs to have an interestingness function
   // set and some reduction passes added to it in order to be useful.
   // set and some reduction passes added to it in order to be useful.
-  explicit Reducer(spv_target_env env);
+  explicit Reducer(spv_target_env target_env);
 
 
   // Disables copy/move constructor/assignment operations.
   // Disables copy/move constructor/assignment operations.
   Reducer(const Reducer&) = delete;
   Reducer(const Reducer&) = delete;
@@ -86,17 +86,34 @@ class Reducer {
   // that will be iterated over.
   // that will be iterated over.
   void AddReductionPass(std::unique_ptr<ReductionOpportunityFinder>&& finder);
   void AddReductionPass(std::unique_ptr<ReductionOpportunityFinder>&& finder);
 
 
+  // Adds a cleanup reduction pass based on the given finder to the sequence of
+  // passes that will run after other passes.
+  void AddCleanupReductionPass(
+      std::unique_ptr<ReductionOpportunityFinder>&& finder);
+
   // Reduces the given SPIR-V module |binary_out|.
   // Reduces the given SPIR-V module |binary_out|.
   // The reduced binary ends up in |binary_out|.
   // The reduced binary ends up in |binary_out|.
   // A status is returned.
   // A status is returned.
   ReductionResultStatus Run(std::vector<uint32_t>&& binary_in,
   ReductionResultStatus Run(std::vector<uint32_t>&& binary_in,
                             std::vector<uint32_t>* binary_out,
                             std::vector<uint32_t>* binary_out,
                             spv_const_reducer_options options,
                             spv_const_reducer_options options,
-                            spv_validator_options validator_options) const;
+                            spv_validator_options validator_options);
 
 
  private:
  private:
-  struct Impl;                  // Opaque struct for holding internal data.
-  std::unique_ptr<Impl> impl_;  // Unique pointer to internal data.
+  static bool ReachedStepLimit(uint32_t current_step,
+                               spv_const_reducer_options options);
+
+  ReductionResultStatus RunPasses(
+      std::vector<std::unique_ptr<ReductionPass>>* passes,
+      spv_const_reducer_options options,
+      spv_validator_options validator_options, const SpirvTools& tools,
+      std::vector<uint32_t>* current_binary, uint32_t* reductions_applied);
+
+  const spv_target_env target_env_;
+  MessageConsumer consumer_;
+  InterestingnessFunction interestingness_function_;
+  std::vector<std::unique_ptr<ReductionPass>> passes_;
+  std::vector<std::unique_ptr<ReductionPass>> cleanup_passes_;
 };
 };
 
 
 }  // namespace reduce
 }  // namespace reduce

+ 17 - 0
3rdparty/spirv-tools/source/reduce/reduction_util.cpp

@@ -44,5 +44,22 @@ uint32_t FindOrCreateGlobalUndef(IRContext* context, uint32_t type_id) {
   return undef_id;
   return undef_id;
 }
 }
 
 
+void AdaptPhiInstructionsForRemovedEdge(uint32_t from_id,
+                                        opt::BasicBlock* to_block) {
+  to_block->ForEachPhiInst([&from_id](Instruction* phi_inst) {
+    Instruction::OperandList new_in_operands;
+    // Go through the OpPhi's input operands in (variable, parent) pairs.
+    for (uint32_t index = 0; index < phi_inst->NumInOperands(); index += 2) {
+      // Keep all pairs where the parent is not the block from which the edge
+      // is being removed.
+      if (phi_inst->GetInOperand(index + 1).words[0] != from_id) {
+        new_in_operands.push_back(phi_inst->GetInOperand(index));
+        new_in_operands.push_back(phi_inst->GetInOperand(index + 1));
+      }
+    }
+    phi_inst->SetInOperands(std::move(new_in_operands));
+  });
+}
+
 }  // namespace reduce
 }  // namespace reduce
 }  // namespace spvtools
 }  // namespace spvtools

+ 5 - 0
3rdparty/spirv-tools/source/reduce/reduction_util.h

@@ -30,6 +30,11 @@ extern const uint32_t kFalseBranchOperandIndex;
 // adding one if it does not exist.
 // adding one if it does not exist.
 uint32_t FindOrCreateGlobalUndef(opt::IRContext* context, uint32_t type_id);
 uint32_t FindOrCreateGlobalUndef(opt::IRContext* context, uint32_t type_id);
 
 
+// Removes any components of |to_block|'s phi instructions relating to
+// |from_id|.
+void AdaptPhiInstructionsForRemovedEdge(uint32_t from_id,
+                                        opt::BasicBlock* to_block);
+
 }  // namespace reduce
 }  // namespace reduce
 }  // namespace spvtools
 }  // namespace spvtools
 
 

+ 0 - 45
3rdparty/spirv-tools/source/reduce/remove_opname_instruction_reduction_opportunity_finder.cpp

@@ -1,45 +0,0 @@
-// Copyright (c) 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
-
-#include "source/opcode.h"
-#include "source/opt/instruction.h"
-#include "source/reduce/remove_instruction_reduction_opportunity.h"
-
-namespace spvtools {
-namespace reduce {
-
-using opt::IRContext;
-
-std::vector<std::unique_ptr<ReductionOpportunity>>
-RemoveOpNameInstructionReductionOpportunityFinder::GetAvailableOpportunities(
-    IRContext* context) const {
-  std::vector<std::unique_ptr<ReductionOpportunity>> result;
-
-  for (auto& inst : context->module()->debugs2()) {
-    if (inst.opcode() == SpvOpName || inst.opcode() == SpvOpMemberName) {
-      result.push_back(
-          MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
-    }
-  }
-  return result;
-}
-
-std::string RemoveOpNameInstructionReductionOpportunityFinder::GetName() const {
-  return "RemoveOpNameInstructionReductionOpportunityFinder";
-}
-
-}  // namespace reduce
-}  // namespace spvtools

+ 0 - 45
3rdparty/spirv-tools/source/reduce/remove_opname_instruction_reduction_opportunity_finder.h

@@ -1,45 +0,0 @@
-// Copyright (c) 2018 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef SOURCE_REDUCE_REMOVE_OPNAME_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_
-#define SOURCE_REDUCE_REMOVE_OPNAME_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_
-
-#include "source/reduce/reduction_opportunity_finder.h"
-
-namespace spvtools {
-namespace reduce {
-
-// A finder for opportunities to remove OpName instructions.  As well as making
-// the module smaller, removing an OpName instruction may create opportunities
-// for subsequently removing the instructions that create the ids to which the
-// OpName applies.
-class RemoveOpNameInstructionReductionOpportunityFinder
-    : public ReductionOpportunityFinder {
- public:
-  RemoveOpNameInstructionReductionOpportunityFinder() = default;
-
-  ~RemoveOpNameInstructionReductionOpportunityFinder() override = default;
-
-  std::string GetName() const final;
-
-  std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
-      opt::IRContext* context) const final;
-
- private:
-};
-
-}  // namespace reduce
-}  // namespace spvtools
-
-#endif  // SOURCE_REDUCE_REMOVE_OPNAME_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_

+ 0 - 49
3rdparty/spirv-tools/source/reduce/remove_relaxed_precision_decoration_opportunity_finder.cpp

@@ -1,49 +0,0 @@
-// Copyright (c) 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
-
-#include "source/reduce/remove_instruction_reduction_opportunity.h"
-
-namespace spvtools {
-namespace reduce {
-
-std::vector<std::unique_ptr<ReductionOpportunity>>
-RemoveRelaxedPrecisionDecorationOpportunityFinder::GetAvailableOpportunities(
-    opt::IRContext* context) const {
-  std::vector<std::unique_ptr<ReductionOpportunity>> result;
-
-  // Consider all annotation instructions
-  for (auto& inst : context->module()->annotations()) {
-    // We are interested in removing instructions of the form:
-    //   SpvOpDecorate %id RelaxedPrecision
-    // and
-    //   SpvOpMemberDecorate %id member RelaxedPrecision
-    if ((inst.opcode() == SpvOpDecorate &&
-         inst.GetSingleWordInOperand(1) == SpvDecorationRelaxedPrecision) ||
-        (inst.opcode() == SpvOpMemberDecorate &&
-         inst.GetSingleWordInOperand(2) == SpvDecorationRelaxedPrecision)) {
-      result.push_back(
-          MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
-    }
-  }
-  return result;
-}
-
-std::string RemoveRelaxedPrecisionDecorationOpportunityFinder::GetName() const {
-  return "RemoveRelaxedPrecisionDecorationOpportunityFinder";
-}
-
-}  // namespace reduce
-}  // namespace spvtools

+ 87 - 6
3rdparty/spirv-tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp

@@ -21,28 +21,109 @@
 namespace spvtools {
 namespace spvtools {
 namespace reduce {
 namespace reduce {
 
 
+RemoveUnreferencedInstructionReductionOpportunityFinder::
+    RemoveUnreferencedInstructionReductionOpportunityFinder(
+        bool remove_constants_and_undefs)
+    : remove_constants_and_undefs_(remove_constants_and_undefs) {}
+
 std::vector<std::unique_ptr<ReductionOpportunity>>
 std::vector<std::unique_ptr<ReductionOpportunity>>
 RemoveUnreferencedInstructionReductionOpportunityFinder::
 RemoveUnreferencedInstructionReductionOpportunityFinder::
     GetAvailableOpportunities(opt::IRContext* context) const {
     GetAvailableOpportunities(opt::IRContext* context) const {
   std::vector<std::unique_ptr<ReductionOpportunity>> result;
   std::vector<std::unique_ptr<ReductionOpportunity>> result;
 
 
+  for (auto& inst : context->module()->debugs1()) {
+    if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
+      continue;
+    }
+    result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
+  }
+
+  for (auto& inst : context->module()->debugs2()) {
+    if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
+      continue;
+    }
+    result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
+  }
+
+  for (auto& inst : context->module()->debugs3()) {
+    if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
+      continue;
+    }
+    result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
+  }
+
+  for (auto& inst : context->module()->types_values()) {
+    if (context->get_def_use_mgr()->NumUsers(&inst) > 0) {
+      continue;
+    }
+    if (!remove_constants_and_undefs_ &&
+        spvOpcodeIsConstantOrUndef(inst.opcode())) {
+      continue;
+    }
+    result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
+  }
+
+  for (auto& inst : context->module()->annotations()) {
+    if (context->get_def_use_mgr()->NumUsers(&inst) > 0) {
+      continue;
+    }
+
+    uint32_t decoration = SpvDecorationMax;
+    switch (inst.opcode()) {
+      case SpvOpDecorate:
+      case SpvOpDecorateId:
+      case SpvOpDecorateString:
+        decoration = inst.GetSingleWordInOperand(1u);
+        break;
+      case SpvOpMemberDecorate:
+      case SpvOpMemberDecorateString:
+        decoration = inst.GetSingleWordInOperand(2u);
+        break;
+      default:
+        break;
+    }
+
+    // We conservatively only remove specific decorations that we believe will
+    // not change the shader interface, will not make the shader invalid, will
+    // actually be found in practice, etc.
+
+    switch (decoration) {
+      case SpvDecorationRelaxedPrecision:
+      case SpvDecorationNoSignedWrap:
+      case SpvDecorationNoContraction:
+      case SpvDecorationNoUnsignedWrap:
+      case SpvDecorationUserSemantic:
+        break;
+      default:
+        // Give up.
+        continue;
+    }
+
+    result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
+  }
+
   for (auto& function : *context->module()) {
   for (auto& function : *context->module()) {
     for (auto& block : function) {
     for (auto& block : function) {
       for (auto& inst : block) {
       for (auto& inst : block) {
         if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
         if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
           continue;
           continue;
         }
         }
+        if (!remove_constants_and_undefs_ &&
+            spvOpcodeIsConstantOrUndef(inst.opcode())) {
+          continue;
+        }
         if (spvOpcodeIsBlockTerminator(inst.opcode()) ||
         if (spvOpcodeIsBlockTerminator(inst.opcode()) ||
             inst.opcode() == SpvOpSelectionMerge ||
             inst.opcode() == SpvOpSelectionMerge ||
             inst.opcode() == SpvOpLoopMerge) {
             inst.opcode() == SpvOpLoopMerge) {
-          // In this reduction pass we do not want to affect static control
-          // flow.
+          // In this reduction pass we do not want to affect static
+          // control flow.
           continue;
           continue;
         }
         }
-        // Given that we're in a block, we should only get here if the
-        // instruction is not directly related to control flow; i.e., it's
-        // some straightforward instruction with an unused result, like an
-        // arithmetic operation or function call.
+        // Given that we're in a block, we should only get here if
+        // the instruction is not directly related to control flow;
+        // i.e., it's some straightforward instruction with an
+        // unused result, like an arithmetic operation or function
+        // call.
         result.push_back(
         result.push_back(
             MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
             MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
       }
       }

+ 3 - 1
3rdparty/spirv-tools/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h

@@ -28,7 +28,8 @@ namespace reduce {
 class RemoveUnreferencedInstructionReductionOpportunityFinder
 class RemoveUnreferencedInstructionReductionOpportunityFinder
     : public ReductionOpportunityFinder {
     : public ReductionOpportunityFinder {
  public:
  public:
-  RemoveUnreferencedInstructionReductionOpportunityFinder() = default;
+  explicit RemoveUnreferencedInstructionReductionOpportunityFinder(
+      bool remove_constants_and_undefs);
 
 
   ~RemoveUnreferencedInstructionReductionOpportunityFinder() override = default;
   ~RemoveUnreferencedInstructionReductionOpportunityFinder() override = default;
 
 
@@ -38,6 +39,7 @@ class RemoveUnreferencedInstructionReductionOpportunityFinder
       opt::IRContext* context) const final;
       opt::IRContext* context) const final;
 
 
  private:
  private:
+  bool remove_constants_and_undefs_;
 };
 };
 
 
 }  // namespace reduce
 }  // namespace reduce

+ 0 - 17
3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp

@@ -167,23 +167,6 @@ void StructuredLoopToSelectionReductionOpportunity::RedirectEdge(
                                    context_->cfg()->block(new_target_id));
                                    context_->cfg()->block(new_target_id));
 }
 }
 
 
-void StructuredLoopToSelectionReductionOpportunity::
-    AdaptPhiInstructionsForRemovedEdge(uint32_t from_id, BasicBlock* to_block) {
-  to_block->ForEachPhiInst([&from_id](Instruction* phi_inst) {
-    Instruction::OperandList new_in_operands;
-    // Go through the OpPhi's input operands in (variable, parent) pairs.
-    for (uint32_t index = 0; index < phi_inst->NumInOperands(); index += 2) {
-      // Keep all pairs where the parent is not the block from which the edge
-      // is being removed.
-      if (phi_inst->GetInOperand(index + 1).words[0] != from_id) {
-        new_in_operands.push_back(phi_inst->GetInOperand(index));
-        new_in_operands.push_back(phi_inst->GetInOperand(index + 1));
-      }
-    }
-    phi_inst->SetInOperands(std::move(new_in_operands));
-  });
-}
-
 void StructuredLoopToSelectionReductionOpportunity::
 void StructuredLoopToSelectionReductionOpportunity::
     AdaptPhiInstructionsForAddedEdge(uint32_t from_id, BasicBlock* to_block) {
     AdaptPhiInstructionsForAddedEdge(uint32_t from_id, BasicBlock* to_block) {
   to_block->ForEachPhiInst([this, &from_id](Instruction* phi_inst) {
   to_block->ForEachPhiInst([this, &from_id](Instruction* phi_inst) {

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

@@ -62,11 +62,6 @@ class StructuredLoopToSelectionReductionOpportunity
   void RedirectEdge(uint32_t source_id, uint32_t original_target_id,
   void RedirectEdge(uint32_t source_id, uint32_t original_target_id,
                     uint32_t new_target_id);
                     uint32_t new_target_id);
 
 
-  // Removes any components of |to_block|'s phi instructions relating to
-  // |from_id|.
-  void AdaptPhiInstructionsForRemovedEdge(uint32_t from_id,
-                                          opt::BasicBlock* to_block);
-
   // Adds components to |to_block|'s phi instructions to account for a new
   // Adds components to |to_block|'s phi instructions to account for a new
   // incoming edge from |from_id|.
   // incoming edge from |from_id|.
   void AdaptPhiInstructionsForAddedEdge(uint32_t from_id,
   void AdaptPhiInstructionsForAddedEdge(uint32_t from_id,

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

@@ -590,9 +590,68 @@ spv_result_t StructuredSwitchChecks(ValidationState_t& _, Function* function,
   return SPV_SUCCESS;
   return SPV_SUCCESS;
 }
 }
 
 
+// Validates that all CFG divergences (i.e. conditional branch or switch) are
+// structured correctly. Either divergence is preceded by a merge instruction
+// or the divergence introduces at most one unseen label.
+spv_result_t ValidateStructuredSelections(
+    ValidationState_t& _, const std::vector<const BasicBlock*>& postorder) {
+  std::unordered_set<uint32_t> seen;
+  for (auto iter = postorder.rbegin(); iter != postorder.rend(); ++iter) {
+    const auto* block = *iter;
+    const auto* terminator = block->terminator();
+    if (!terminator) continue;
+    const auto index = terminator - &_.ordered_instructions()[0];
+    auto* merge = &_.ordered_instructions()[index - 1];
+    // Marks merges and continues as seen.
+    if (merge->opcode() == SpvOpSelectionMerge) {
+      seen.insert(merge->GetOperandAs<uint32_t>(0));
+    } else if (merge->opcode() == SpvOpLoopMerge) {
+      seen.insert(merge->GetOperandAs<uint32_t>(0));
+      seen.insert(merge->GetOperandAs<uint32_t>(1));
+    } else {
+      // Only track the pointer if it is a merge instruction.
+      merge = nullptr;
+    }
+
+    // Skip unreachable blocks.
+    if (!block->reachable()) continue;
+
+    if (terminator->opcode() == SpvOpBranchConditional) {
+      const auto true_label = terminator->GetOperandAs<uint32_t>(1);
+      const auto false_label = terminator->GetOperandAs<uint32_t>(2);
+      // Mark the upcoming blocks as seen now, but only error out if this block
+      // was missing a merge instruction and both labels hadn't been seen
+      // previously.
+      const bool both_unseen =
+          seen.insert(true_label).second && seen.insert(false_label).second;
+      if (!merge && both_unseen) {
+        return _.diag(SPV_ERROR_INVALID_CFG, terminator)
+               << "Selection must be structured";
+      }
+    } else if (terminator->opcode() == SpvOpSwitch) {
+      uint32_t count = 0;
+      // Mark the targets as seen now, but only error out if this block was
+      // missing a merge instruction and there were multiple unseen labels.
+      for (uint32_t i = 1; i < terminator->operands().size(); i += 2) {
+        const auto target = terminator->GetOperandAs<uint32_t>(i);
+        if (seen.insert(target).second) {
+          count++;
+        }
+      }
+      if (!merge && count > 1) {
+        return _.diag(SPV_ERROR_INVALID_CFG, terminator)
+               << "Selection must be structured";
+      }
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
 spv_result_t StructuredControlFlowChecks(
 spv_result_t StructuredControlFlowChecks(
     ValidationState_t& _, Function* function,
     ValidationState_t& _, Function* function,
-    const std::vector<std::pair<uint32_t, uint32_t>>& back_edges) {
+    const std::vector<std::pair<uint32_t, uint32_t>>& back_edges,
+    const std::vector<const BasicBlock*>& postorder) {
   /// Check all backedges target only loop headers and have exactly one
   /// Check all backedges target only loop headers and have exactly one
   /// back-edge branching to it
   /// back-edge branching to it
 
 
@@ -709,6 +768,10 @@ spv_result_t StructuredControlFlowChecks(
     }
     }
   }
   }
 
 
+  if (auto error = ValidateStructuredSelections(_, postorder)) {
+    return error;
+  }
+
   return SPV_SUCCESS;
   return SPV_SUCCESS;
 }
 }
 
 
@@ -931,7 +994,8 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) {
 
 
     /// Structured control flow checks are only required for shader capabilities
     /// Structured control flow checks are only required for shader capabilities
     if (_.HasCapability(SpvCapabilityShader)) {
     if (_.HasCapability(SpvCapabilityShader)) {
-      if (auto error = StructuredControlFlowChecks(_, &function, back_edges))
+      if (auto error =
+              StructuredControlFlowChecks(_, &function, back_edges, postorder))
         return error;
         return error;
     }
     }
   }
   }

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

@@ -42,10 +42,13 @@ spv_result_t ValidateUndef(ValidationState_t& _, const Instruction* inst) {
 
 
 spv_result_t ValidateShaderClock(ValidationState_t& _,
 spv_result_t ValidateShaderClock(ValidationState_t& _,
                                  const Instruction* inst) {
                                  const Instruction* inst) {
+// #2952: disabled until scope discussion is resolved.
+#if 0
   const uint32_t execution_scope = inst->word(3);
   const uint32_t execution_scope = inst->word(3);
   if (auto error = ValidateExecutionScope(_, inst, execution_scope)) {
   if (auto error = ValidateExecutionScope(_, inst, execution_scope)) {
     return error;
     return error;
   }
   }
+#endif
 
 
   // Result Type must be a 64 - bit unsigned integer type or
   // Result Type must be a 64 - bit unsigned integer type or
   // a vector of two - components of 32 -
   // a vector of two - components of 32 -

+ 5 - 0
3rdparty/spirv-tools/test/fuzz/CMakeLists.txt

@@ -24,15 +24,20 @@ if (${SPIRV_BUILD_FUZZER})
           transformation_add_constant_scalar_test.cpp
           transformation_add_constant_scalar_test.cpp
           transformation_add_dead_break_test.cpp
           transformation_add_dead_break_test.cpp
           transformation_add_dead_continue_test.cpp
           transformation_add_dead_continue_test.cpp
+          transformation_add_no_contraction_decoration_test.cpp
           transformation_add_type_boolean_test.cpp
           transformation_add_type_boolean_test.cpp
           transformation_add_type_float_test.cpp
           transformation_add_type_float_test.cpp
           transformation_add_type_int_test.cpp
           transformation_add_type_int_test.cpp
           transformation_add_type_pointer_test.cpp
           transformation_add_type_pointer_test.cpp
+          transformation_construct_composite_test.cpp
           transformation_copy_object_test.cpp
           transformation_copy_object_test.cpp
           transformation_move_block_down_test.cpp
           transformation_move_block_down_test.cpp
           transformation_replace_boolean_constant_with_constant_binary_test.cpp
           transformation_replace_boolean_constant_with_constant_binary_test.cpp
           transformation_replace_constant_with_uniform_test.cpp
           transformation_replace_constant_with_uniform_test.cpp
           transformation_replace_id_with_synonym_test.cpp
           transformation_replace_id_with_synonym_test.cpp
+          transformation_set_function_control_test.cpp
+          transformation_set_loop_control_test.cpp
+          transformation_set_selection_control_test.cpp
           transformation_split_block_test.cpp
           transformation_split_block_test.cpp
           uniform_buffer_element_descriptor_test.cpp)
           uniform_buffer_element_descriptor_test.cpp)
 
 

+ 11 - 9
3rdparty/spirv-tools/test/fuzz/fuzzer_replayer_test.cpp

@@ -21,6 +21,8 @@ namespace spvtools {
 namespace fuzz {
 namespace fuzz {
 namespace {
 namespace {
 
 
+const uint32_t kNumFuzzerRuns = 20;
+
 // Assembles the given |shader| text, and then runs the fuzzer |num_runs|
 // Assembles the given |shader| text, and then runs the fuzzer |num_runs|
 // times, using successive seeds starting from |initial_seed|.  Checks that
 // times, using successive seeds starting from |initial_seed|.  Checks that
 // the binary produced after each fuzzer run is valid, and that replaying
 // the binary produced after each fuzzer run is valid, and that replaying
@@ -29,7 +31,7 @@ namespace {
 void RunFuzzerAndReplayer(const std::string& shader,
 void RunFuzzerAndReplayer(const std::string& shader,
                           const protobufs::FactSequence& initial_facts,
                           const protobufs::FactSequence& initial_facts,
                           uint32_t initial_seed, uint32_t num_runs) {
                           uint32_t initial_seed, uint32_t num_runs) {
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
 
 
   std::vector<uint32_t> binary_in;
   std::vector<uint32_t> binary_in;
   SpirvTools t(env);
   SpirvTools t(env);
@@ -240,9 +242,9 @@ TEST(FuzzerReplayerTest, Miscellaneous1) {
                OpFunctionEnd
                OpFunctionEnd
   )";
   )";
 
 
-  // Do 5 fuzzer runs, starting from an initial seed of 0 (seed value chosen
+  // Do some fuzzer runs, starting from an initial seed of 0 (seed value chosen
   // arbitrarily).
   // arbitrarily).
-  RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 0, 5);
+  RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 0, kNumFuzzerRuns);
 }
 }
 
 
 TEST(FuzzerReplayerTest, Miscellaneous2) {
 TEST(FuzzerReplayerTest, Miscellaneous2) {
@@ -303,7 +305,7 @@ TEST(FuzzerReplayerTest, Miscellaneous2) {
                OpCapability Shader
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
           %1 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
                OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main" %16 %139
+               OpEntryPoint Fragment %4 "main" %16 %139 %25 %68
                OpExecutionMode %4 OriginUpperLeft
                OpExecutionMode %4 OriginUpperLeft
                OpSource ESSL 310
                OpSource ESSL 310
                OpName %4 "main"
                OpName %4 "main"
@@ -485,9 +487,9 @@ TEST(FuzzerReplayerTest, Miscellaneous2) {
                OpFunctionEnd
                OpFunctionEnd
   )";
   )";
 
 
-  // Do 5 fuzzer runs, starting from an initial seed of 10 (seed value chosen
+  // Do some fuzzer runs, starting from an initial seed of 10 (seed value chosen
   // arbitrarily).
   // arbitrarily).
-  RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 10, 5);
+  RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 10, kNumFuzzerRuns);
 }
 }
 
 
 TEST(FuzzerReplayerTest, Miscellaneous3) {
 TEST(FuzzerReplayerTest, Miscellaneous3) {
@@ -603,7 +605,7 @@ TEST(FuzzerReplayerTest, Miscellaneous3) {
                OpCapability Shader
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
           %1 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
                OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main" %68 %100
+               OpEntryPoint Fragment %4 "main" %68 %100 %24
                OpExecutionMode %4 OriginUpperLeft
                OpExecutionMode %4 OriginUpperLeft
                OpSource ESSL 310
                OpSource ESSL 310
                OpName %4 "main"
                OpName %4 "main"
@@ -970,9 +972,9 @@ TEST(FuzzerReplayerTest, Miscellaneous3) {
     *facts.mutable_fact()->Add() = temp;
     *facts.mutable_fact()->Add() = temp;
   }
   }
 
 
-  // Do 5 fuzzer runs, starting from an initial seed of 94 (seed value chosen
+  // Do some fuzzer runs, starting from an initial seed of 94 (seed value chosen
   // arbitrarily).
   // arbitrarily).
-  RunFuzzerAndReplayer(shader, facts, 94, 5);
+  RunFuzzerAndReplayer(shader, facts, 94, kNumFuzzerRuns);
 }
 }
 
 
 }  // namespace
 }  // namespace

+ 3 - 3
3rdparty/spirv-tools/test/fuzz/fuzzer_shrinker_test.cpp

@@ -154,7 +154,7 @@ void RunAndCheckShrinker(
 void RunFuzzerAndShrinker(const std::string& shader,
 void RunFuzzerAndShrinker(const std::string& shader,
                           const protobufs::FactSequence& initial_facts,
                           const protobufs::FactSequence& initial_facts,
                           uint32_t seed) {
                           uint32_t seed) {
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto env = SPV_ENV_UNIVERSAL_1_5;
 
 
   std::vector<uint32_t> binary_in;
   std::vector<uint32_t> binary_in;
   SpirvTools t(env);
   SpirvTools t(env);
@@ -434,7 +434,7 @@ TEST(FuzzerShrinkerTest, Miscellaneous2) {
                OpCapability Shader
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
           %1 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
                OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main" %16 %139
+               OpEntryPoint Fragment %4 "main" %16 %139 %25 %68
                OpExecutionMode %4 OriginUpperLeft
                OpExecutionMode %4 OriginUpperLeft
                OpSource ESSL 310
                OpSource ESSL 310
                OpName %4 "main"
                OpName %4 "main"
@@ -732,7 +732,7 @@ TEST(FuzzerShrinkerTest, Miscellaneous3) {
                OpCapability Shader
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
           %1 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
                OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main" %68 %100
+               OpEntryPoint Fragment %4 "main" %68 %100 %24
                OpExecutionMode %4 OriginUpperLeft
                OpExecutionMode %4 OriginUpperLeft
                OpSource ESSL 310
                OpSource ESSL 310
                OpName %4 "main"
                OpName %4 "main"

+ 46 - 0
3rdparty/spirv-tools/test/fuzz/transformation_add_dead_continue_test.cpp

@@ -1626,6 +1626,52 @@ TEST(TransformationAddDeadContinueTest, Miscellaneous5) {
   ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
 }
 }
 
 
+TEST(TransformationAddDeadContinueTest, DISABLED_Miscellaneous6) {
+  // A miscellaneous test that exposing a known bug in spirv-fuzz.
+
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2919): re-enable
+  //  this test as an when the known issue is fixed.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %9 = OpConstantTrue %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %10
+         %10 = OpLabel
+               OpLoopMerge %13 %12 None
+               OpBranchConditional %9 %13 %11
+         %11 = OpLabel
+         %20 = OpCopyObject %6 %9
+               OpBranch %12
+         %12 = OpLabel
+               OpBranchConditional %9 %10 %13
+         %13 = OpLabel
+         %21 = OpCopyObject %6 %20
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  auto bad_transformation = TransformationAddDeadContinue(10, true, {});
+
+  ASSERT_FALSE(bad_transformation.IsApplicable(context.get(), fact_manager));
+}
+
 }  // namespace
 }  // namespace
 }  // namespace fuzz
 }  // namespace fuzz
 }  // namespace spvtools
 }  // namespace spvtools

+ 194 - 0
3rdparty/spirv-tools/test/fuzz/transformation_add_no_contraction_decoration_test.cpp

@@ -0,0 +1,194 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_no_contraction_decoration.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddNoContractionDecorationTest, BasicScenarios) {
+  // This is a simple transformation and this test handles the main cases.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "x"
+               OpName %10 "y"
+               OpName %14 "i"
+               OpDecorate %32 NoContraction
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %11 = OpConstant %6 2
+         %12 = OpTypeInt 32 1
+         %13 = OpTypePointer Function %12
+         %15 = OpConstant %12 0
+         %22 = OpConstant %12 10
+         %23 = OpTypeBool
+         %31 = OpConstant %6 3.5999999
+         %38 = OpConstant %12 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %14 = OpVariable %13 Function
+               OpStore %8 %9
+               OpStore %10 %11
+               OpStore %14 %15
+               OpBranch %16
+         %16 = OpLabel
+               OpLoopMerge %18 %19 None
+               OpBranch %20
+         %20 = OpLabel
+         %21 = OpLoad %12 %14
+         %24 = OpSLessThan %23 %21 %22
+               OpBranchConditional %24 %17 %18
+         %17 = OpLabel
+         %25 = OpLoad %6 %10
+         %26 = OpLoad %6 %10
+         %27 = OpFMul %6 %25 %26
+         %28 = OpLoad %6 %8
+         %29 = OpFAdd %6 %28 %27
+               OpStore %8 %29
+         %30 = OpLoad %6 %10
+         %32 = OpFDiv %6 %30 %31
+               OpStore %10 %32
+         %33 = OpLoad %12 %14
+         %34 = OpConvertSToF %6 %33
+         %35 = OpLoad %6 %8
+         %36 = OpFAdd %6 %35 %34
+               OpStore %8 %36
+               OpBranch %19
+         %19 = OpLabel
+         %37 = OpLoad %12 %14
+         %39 = OpIAdd %12 %37 %38
+               OpStore %14 %39
+               OpBranch %16
+         %18 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  FactManager fact_manager;
+
+  // Invalid: 200 is not an id
+  ASSERT_FALSE(TransformationAddNoContractionDecoration(200).IsApplicable(
+      context.get(), fact_manager));
+  // Invalid: 17 is a block id
+  ASSERT_FALSE(TransformationAddNoContractionDecoration(17).IsApplicable(
+      context.get(), fact_manager));
+  // Invalid: 24 is not arithmetic
+  ASSERT_FALSE(TransformationAddNoContractionDecoration(24).IsApplicable(
+      context.get(), fact_manager));
+
+  // It is valid to add NoContraction to each of these ids (and it's fine to
+  // have duplicates of the decoration, in the case of 32).
+  for (uint32_t result_id : {32u, 32u, 27u, 29u, 39u}) {
+    TransformationAddNoContractionDecoration transformation(result_id);
+    ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+    transformation.Apply(context.get(), &fact_manager);
+    ASSERT_TRUE(IsValid(env, context.get()));
+  }
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "x"
+               OpName %10 "y"
+               OpName %14 "i"
+               OpDecorate %32 NoContraction
+               OpDecorate %32 NoContraction
+               OpDecorate %32 NoContraction
+               OpDecorate %27 NoContraction
+               OpDecorate %29 NoContraction
+               OpDecorate %39 NoContraction
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %11 = OpConstant %6 2
+         %12 = OpTypeInt 32 1
+         %13 = OpTypePointer Function %12
+         %15 = OpConstant %12 0
+         %22 = OpConstant %12 10
+         %23 = OpTypeBool
+         %31 = OpConstant %6 3.5999999
+         %38 = OpConstant %12 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %14 = OpVariable %13 Function
+               OpStore %8 %9
+               OpStore %10 %11
+               OpStore %14 %15
+               OpBranch %16
+         %16 = OpLabel
+               OpLoopMerge %18 %19 None
+               OpBranch %20
+         %20 = OpLabel
+         %21 = OpLoad %12 %14
+         %24 = OpSLessThan %23 %21 %22
+               OpBranchConditional %24 %17 %18
+         %17 = OpLabel
+         %25 = OpLoad %6 %10
+         %26 = OpLoad %6 %10
+         %27 = OpFMul %6 %25 %26
+         %28 = OpLoad %6 %8
+         %29 = OpFAdd %6 %28 %27
+               OpStore %8 %29
+         %30 = OpLoad %6 %10
+         %32 = OpFDiv %6 %30 %31
+               OpStore %10 %32
+         %33 = OpLoad %12 %14
+         %34 = OpConvertSToF %6 %33
+         %35 = OpLoad %6 %8
+         %36 = OpFAdd %6 %35 %34
+               OpStore %8 %36
+               OpBranch %19
+         %19 = OpLabel
+         %37 = OpLoad %12 %14
+         %39 = OpIAdd %12 %37 %38
+               OpStore %14 %39
+               OpBranch %16
+         %18 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools

+ 1248 - 0
3rdparty/spirv-tools/test/fuzz/transformation_construct_composite_test.cpp

@@ -0,0 +1,1248 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_construct_composite.h"
+#include "source/fuzz/data_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+bool SynonymFactHolds(const FactManager& fact_manager, uint32_t id,
+                      uint32_t synonym_base_id,
+                      std::vector<uint32_t>&& synonym_indices) {
+  if (fact_manager.GetIdsForWhichSynonymsAreKnown().count(id) == 0) {
+    return false;
+  }
+  auto synonyms = fact_manager.GetSynonymsForId(id);
+  auto temp = MakeDataDescriptor(synonym_base_id, std::move(synonym_indices));
+  return std::find_if(synonyms.begin(), synonyms.end(),
+                      [&temp](protobufs::DataDescriptor dd) -> bool {
+                        return DataDescriptorEquals()(&dd, &temp);
+                      }) != synonyms.end();
+}
+
+TEST(TransformationConstructCompositeTest, ConstructArrays) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %11 "floats"
+               OpName %22 "x"
+               OpName %39 "vecs"
+               OpName %49 "bools"
+               OpName %60 "many_uvec3s"
+               OpDecorate %60 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 0
+          %8 = OpConstant %7 2
+          %9 = OpTypeArray %6 %8
+         %10 = OpTypePointer Function %9
+         %12 = OpTypeInt 32 1
+         %13 = OpConstant %12 0
+         %14 = OpConstant %6 1
+         %15 = OpTypePointer Function %6
+         %17 = OpConstant %12 1
+         %18 = OpConstant %6 2
+         %20 = OpTypeVector %6 2
+         %21 = OpTypePointer Function %20
+         %32 = OpTypeBool
+         %36 = OpConstant %7 3
+         %37 = OpTypeArray %20 %36
+         %38 = OpTypePointer Private %37
+         %39 = OpVariable %38 Private
+         %40 = OpConstant %6 3
+         %41 = OpConstantComposite %20 %40 %40
+         %42 = OpTypePointer Private %20
+         %44 = OpConstant %12 2
+         %47 = OpTypeArray %32 %36
+         %48 = OpTypePointer Function %47
+         %50 = OpConstantTrue %32
+         %51 = OpTypePointer Function %32
+         %56 = OpTypeVector %7 3
+         %57 = OpTypeArray %56 %8
+         %58 = OpTypeArray %57 %8
+         %59 = OpTypePointer Function %58
+         %61 = OpConstant %7 4
+         %62 = OpConstantComposite %56 %61 %61 %61
+         %63 = OpTypePointer Function %56
+         %65 = OpConstant %7 5
+         %66 = OpConstantComposite %56 %65 %65 %65
+         %67 = OpConstant %7 6
+         %68 = OpConstantComposite %56 %67 %67 %67
+         %69 = OpConstantComposite %57 %66 %68
+        %100 = OpUndef %57
+         %70 = OpTypePointer Function %57
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+         %22 = OpVariable %21 Function
+         %49 = OpVariable %48 Function
+         %60 = OpVariable %59 Function
+         %16 = OpAccessChain %15 %11 %13
+               OpStore %16 %14
+         %19 = OpAccessChain %15 %11 %17
+               OpStore %19 %18
+         %23 = OpAccessChain %15 %11 %13
+         %24 = OpLoad %6 %23
+         %25 = OpAccessChain %15 %11 %17
+         %26 = OpLoad %6 %25
+         %27 = OpCompositeConstruct %20 %24 %26
+               OpStore %22 %27
+         %28 = OpAccessChain %15 %11 %13
+         %29 = OpLoad %6 %28
+         %30 = OpAccessChain %15 %11 %17
+         %31 = OpLoad %6 %30
+         %33 = OpFOrdGreaterThan %32 %29 %31
+               OpSelectionMerge %35 None
+               OpBranchConditional %33 %34 %35
+         %34 = OpLabel
+         %43 = OpAccessChain %42 %39 %17
+               OpStore %43 %41
+         %45 = OpLoad %20 %22
+         %46 = OpAccessChain %42 %39 %44
+               OpStore %46 %45
+               OpBranch %35
+         %35 = OpLabel
+         %52 = OpAccessChain %51 %49 %13
+               OpStore %52 %50
+         %53 = OpAccessChain %51 %49 %13
+         %54 = OpLoad %32 %53
+         %55 = OpAccessChain %51 %49 %17
+               OpStore %55 %54
+         %64 = OpAccessChain %63 %60 %13 %13
+               OpStore %64 %62
+         %71 = OpAccessChain %70 %60 %17
+               OpStore %71 %69
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // Make a vec2[3]
+  TransformationConstructComposite make_vec2_array_length_3(37, {41, 45, 27},
+                                                            46, 0, 200);
+  // Bad: there are too many components
+  TransformationConstructComposite make_vec2_array_length_3_bad(
+      37, {41, 45, 27, 27}, 46, 0, 200);
+  ASSERT_TRUE(
+      make_vec2_array_length_3.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      make_vec2_array_length_3_bad.IsApplicable(context.get(), fact_manager));
+  make_vec2_array_length_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 41, 200, {0}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 45, 200, {1}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 27, 200, {2}));
+
+  // Make a float[2]
+  TransformationConstructComposite make_float_array_length_2(9, {24, 40}, 71, 1,
+                                                             201);
+  // Bad: %41 does not have type float
+  TransformationConstructComposite make_float_array_length_2_bad(9, {41, 40},
+                                                                 71, 1, 201);
+  ASSERT_TRUE(
+      make_float_array_length_2.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      make_float_array_length_2_bad.IsApplicable(context.get(), fact_manager));
+  make_float_array_length_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 24, 201, {0}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 40, 201, {1}));
+
+  // Make a bool[3]
+  TransformationConstructComposite make_bool_array_length_3(47, {33, 50, 50},
+                                                            33, 1, 202);
+  // Bad: %54 is not available at the desired program point.
+  TransformationConstructComposite make_bool_array_length_3_bad(
+      47, {33, 54, 50}, 33, 1, 202);
+  ASSERT_TRUE(
+      make_bool_array_length_3.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      make_bool_array_length_3_bad.IsApplicable(context.get(), fact_manager));
+  make_bool_array_length_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 33, 202, {0}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 50, 202, {1}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 50, 202, {2}));
+
+  // make a uvec3[2][2]
+  TransformationConstructComposite make_uvec3_array_length_2_2(58, {69, 100},
+                                                               64, 1, 203);
+  // Bad: Offset 100 is too large.
+  TransformationConstructComposite make_uvec3_array_length_2_2_bad(
+      58, {33, 54}, 64, 100, 203);
+  ASSERT_TRUE(
+      make_uvec3_array_length_2_2.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_uvec3_array_length_2_2_bad.IsApplicable(context.get(),
+                                                            fact_manager));
+  make_uvec3_array_length_2_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 69, 203, {0}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 100, 203, {1}));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %11 "floats"
+               OpName %22 "x"
+               OpName %39 "vecs"
+               OpName %49 "bools"
+               OpName %60 "many_uvec3s"
+               OpDecorate %60 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeInt 32 0
+          %8 = OpConstant %7 2
+          %9 = OpTypeArray %6 %8
+         %10 = OpTypePointer Function %9
+         %12 = OpTypeInt 32 1
+         %13 = OpConstant %12 0
+         %14 = OpConstant %6 1
+         %15 = OpTypePointer Function %6
+         %17 = OpConstant %12 1
+         %18 = OpConstant %6 2
+         %20 = OpTypeVector %6 2
+         %21 = OpTypePointer Function %20
+         %32 = OpTypeBool
+         %36 = OpConstant %7 3
+         %37 = OpTypeArray %20 %36
+         %38 = OpTypePointer Private %37
+         %39 = OpVariable %38 Private
+         %40 = OpConstant %6 3
+         %41 = OpConstantComposite %20 %40 %40
+         %42 = OpTypePointer Private %20
+         %44 = OpConstant %12 2
+         %47 = OpTypeArray %32 %36
+         %48 = OpTypePointer Function %47
+         %50 = OpConstantTrue %32
+         %51 = OpTypePointer Function %32
+         %56 = OpTypeVector %7 3
+         %57 = OpTypeArray %56 %8
+         %58 = OpTypeArray %57 %8
+         %59 = OpTypePointer Function %58
+         %61 = OpConstant %7 4
+         %62 = OpConstantComposite %56 %61 %61 %61
+         %63 = OpTypePointer Function %56
+         %65 = OpConstant %7 5
+         %66 = OpConstantComposite %56 %65 %65 %65
+         %67 = OpConstant %7 6
+         %68 = OpConstantComposite %56 %67 %67 %67
+         %69 = OpConstantComposite %57 %66 %68
+        %100 = OpUndef %57
+         %70 = OpTypePointer Function %57
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+         %22 = OpVariable %21 Function
+         %49 = OpVariable %48 Function
+         %60 = OpVariable %59 Function
+         %16 = OpAccessChain %15 %11 %13
+               OpStore %16 %14
+         %19 = OpAccessChain %15 %11 %17
+               OpStore %19 %18
+         %23 = OpAccessChain %15 %11 %13
+         %24 = OpLoad %6 %23
+         %25 = OpAccessChain %15 %11 %17
+         %26 = OpLoad %6 %25
+         %27 = OpCompositeConstruct %20 %24 %26
+               OpStore %22 %27
+         %28 = OpAccessChain %15 %11 %13
+         %29 = OpLoad %6 %28
+         %30 = OpAccessChain %15 %11 %17
+         %31 = OpLoad %6 %30
+         %33 = OpFOrdGreaterThan %32 %29 %31
+        %202 = OpCompositeConstruct %47 %33 %50 %50
+               OpSelectionMerge %35 None
+               OpBranchConditional %33 %34 %35
+         %34 = OpLabel
+         %43 = OpAccessChain %42 %39 %17
+               OpStore %43 %41
+         %45 = OpLoad %20 %22
+        %200 = OpCompositeConstruct %37 %41 %45 %27
+         %46 = OpAccessChain %42 %39 %44
+               OpStore %46 %45
+               OpBranch %35
+         %35 = OpLabel
+         %52 = OpAccessChain %51 %49 %13
+               OpStore %52 %50
+         %53 = OpAccessChain %51 %49 %13
+         %54 = OpLoad %32 %53
+         %55 = OpAccessChain %51 %49 %17
+               OpStore %55 %54
+         %64 = OpAccessChain %63 %60 %13 %13
+        %203 = OpCompositeConstruct %58 %69 %100
+               OpStore %64 %62
+         %71 = OpAccessChain %70 %60 %17
+        %201 = OpCompositeConstruct %9 %24 %40
+               OpStore %71 %69
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationConstructCompositeTest, ConstructMatrices) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %9 "v1"
+               OpName %12 "v2"
+               OpName %14 "v3"
+               OpName %19 "v4"
+               OpName %26 "v5"
+               OpName %29 "v6"
+               OpName %34 "m34"
+               OpName %37 "m43"
+               OpName %43 "vecs"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 3
+          %8 = OpTypePointer Function %7
+         %10 = OpConstant %6 1
+         %11 = OpConstantComposite %7 %10 %10 %10
+         %17 = OpTypeVector %6 4
+         %18 = OpTypePointer Function %17
+         %21 = OpConstant %6 2
+         %32 = OpTypeMatrix %17 3
+         %33 = OpTypePointer Private %32
+         %34 = OpVariable %33 Private
+         %35 = OpTypeMatrix %7 4
+         %36 = OpTypePointer Private %35
+         %37 = OpVariable %36 Private
+         %38 = OpTypeVector %6 2
+         %39 = OpTypeInt 32 0
+         %40 = OpConstant %39 3
+         %41 = OpTypeArray %38 %40
+         %42 = OpTypePointer Private %41
+         %43 = OpVariable %42 Private
+        %100 = OpUndef %7
+        %101 = OpUndef %17
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %9 = OpVariable %8 Function
+         %12 = OpVariable %8 Function
+         %14 = OpVariable %8 Function
+         %19 = OpVariable %18 Function
+         %26 = OpVariable %18 Function
+         %29 = OpVariable %18 Function
+               OpStore %9 %11
+         %13 = OpLoad %7 %9
+               OpStore %12 %13
+         %15 = OpLoad %7 %12
+         %16 = OpVectorShuffle %7 %15 %15 2 1 0
+               OpStore %14 %16
+         %20 = OpLoad %7 %14
+         %22 = OpCompositeExtract %6 %20 0
+         %23 = OpCompositeExtract %6 %20 1
+         %24 = OpCompositeExtract %6 %20 2
+         %25 = OpCompositeConstruct %17 %22 %23 %24 %21
+               OpStore %19 %25
+         %27 = OpLoad %17 %19
+         %28 = OpVectorShuffle %17 %27 %27 3 2 1 0
+               OpStore %26 %28
+         %30 = OpLoad %7 %9
+         %31 = OpVectorShuffle %17 %30 %30 0 0 1 1
+               OpStore %29 %31
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // make a mat3x4
+  TransformationConstructComposite make_mat34(32, {25, 28, 31}, 31, 2, 200);
+  // Bad: %35 is mat4x3, not mat3x4.
+  TransformationConstructComposite make_mat34_bad(35, {25, 28, 31}, 31, 2, 200);
+  ASSERT_TRUE(make_mat34.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_mat34_bad.IsApplicable(context.get(), fact_manager));
+  make_mat34.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 25, 200, {0}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 28, 200, {1}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 31, 200, {2}));
+
+  // make a mat4x3
+  TransformationConstructComposite make_mat43(35, {11, 13, 16, 100}, 31, 1,
+                                              201);
+  // Bad: %25 does not match the matrix's column type.
+  TransformationConstructComposite make_mat43_bad(35, {25, 13, 16, 100}, 31, 1,
+                                                  201);
+  ASSERT_TRUE(make_mat43.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_mat43_bad.IsApplicable(context.get(), fact_manager));
+  make_mat43.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 11, 201, {0}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 13, 201, {1}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 16, 201, {2}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 100, 201, {3}));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %9 "v1"
+               OpName %12 "v2"
+               OpName %14 "v3"
+               OpName %19 "v4"
+               OpName %26 "v5"
+               OpName %29 "v6"
+               OpName %34 "m34"
+               OpName %37 "m43"
+               OpName %43 "vecs"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 3
+          %8 = OpTypePointer Function %7
+         %10 = OpConstant %6 1
+         %11 = OpConstantComposite %7 %10 %10 %10
+         %17 = OpTypeVector %6 4
+         %18 = OpTypePointer Function %17
+         %21 = OpConstant %6 2
+         %32 = OpTypeMatrix %17 3
+         %33 = OpTypePointer Private %32
+         %34 = OpVariable %33 Private
+         %35 = OpTypeMatrix %7 4
+         %36 = OpTypePointer Private %35
+         %37 = OpVariable %36 Private
+         %38 = OpTypeVector %6 2
+         %39 = OpTypeInt 32 0
+         %40 = OpConstant %39 3
+         %41 = OpTypeArray %38 %40
+         %42 = OpTypePointer Private %41
+         %43 = OpVariable %42 Private
+        %100 = OpUndef %7
+        %101 = OpUndef %17
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %9 = OpVariable %8 Function
+         %12 = OpVariable %8 Function
+         %14 = OpVariable %8 Function
+         %19 = OpVariable %18 Function
+         %26 = OpVariable %18 Function
+         %29 = OpVariable %18 Function
+               OpStore %9 %11
+         %13 = OpLoad %7 %9
+               OpStore %12 %13
+         %15 = OpLoad %7 %12
+         %16 = OpVectorShuffle %7 %15 %15 2 1 0
+               OpStore %14 %16
+         %20 = OpLoad %7 %14
+         %22 = OpCompositeExtract %6 %20 0
+         %23 = OpCompositeExtract %6 %20 1
+         %24 = OpCompositeExtract %6 %20 2
+         %25 = OpCompositeConstruct %17 %22 %23 %24 %21
+               OpStore %19 %25
+         %27 = OpLoad %17 %19
+         %28 = OpVectorShuffle %17 %27 %27 3 2 1 0
+               OpStore %26 %28
+         %30 = OpLoad %7 %9
+         %31 = OpVectorShuffle %17 %30 %30 0 0 1 1
+        %201 = OpCompositeConstruct %35 %11 %13 %16 %100
+               OpStore %29 %31
+        %200 = OpCompositeConstruct %32 %25 %28 %31
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationConstructCompositeTest, ConstructStructs) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %9 "Inner"
+               OpMemberName %9 0 "a"
+               OpMemberName %9 1 "b"
+               OpName %11 "i1"
+               OpName %22 "i2"
+               OpName %33 "Outer"
+               OpMemberName %33 0 "c"
+               OpMemberName %33 1 "d"
+               OpMemberName %33 2 "e"
+               OpName %35 "o"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypeStruct %7 %8
+         %10 = OpTypePointer Function %9
+         %12 = OpConstant %8 0
+         %13 = OpConstant %6 2
+         %14 = OpTypeInt 32 0
+         %15 = OpConstant %14 0
+         %16 = OpTypePointer Function %6
+         %18 = OpConstant %8 1
+         %19 = OpConstant %8 3
+         %20 = OpTypePointer Function %8
+         %23 = OpTypePointer Function %7
+         %31 = OpConstant %14 2
+         %32 = OpTypeArray %9 %31
+         %33 = OpTypeStruct %32 %9 %6
+         %34 = OpTypePointer Function %33
+         %36 = OpConstant %6 1
+         %37 = OpConstantComposite %7 %36 %13
+         %38 = OpConstant %8 2
+         %39 = OpConstantComposite %9 %37 %38
+         %40 = OpConstant %6 3
+         %41 = OpConstant %6 4
+         %42 = OpConstantComposite %7 %40 %41
+         %56 = OpConstant %6 5
+        %100 = OpUndef %9
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+         %22 = OpVariable %10 Function
+         %35 = OpVariable %34 Function
+         %17 = OpAccessChain %16 %11 %12 %15
+               OpStore %17 %13
+         %21 = OpAccessChain %20 %11 %18
+               OpStore %21 %19
+         %24 = OpAccessChain %23 %11 %12
+         %25 = OpLoad %7 %24
+         %26 = OpAccessChain %23 %22 %12
+               OpStore %26 %25
+         %27 = OpAccessChain %20 %11 %18
+         %28 = OpLoad %8 %27
+         %29 = OpIAdd %8 %28 %18
+         %30 = OpAccessChain %20 %22 %18
+               OpStore %30 %29
+         %43 = OpAccessChain %20 %11 %18
+         %44 = OpLoad %8 %43
+         %45 = OpCompositeConstruct %9 %42 %44
+         %46 = OpCompositeConstruct %32 %39 %45
+         %47 = OpLoad %9 %22
+         %48 = OpCompositeConstruct %33 %46 %47 %40
+               OpStore %35 %48
+         %49 = OpLoad %9 %11
+         %50 = OpAccessChain %10 %35 %12 %12
+               OpStore %50 %49
+         %51 = OpLoad %9 %22
+         %52 = OpAccessChain %10 %35 %12 %18
+               OpStore %52 %51
+         %53 = OpAccessChain %10 %35 %12 %12
+         %54 = OpLoad %9 %53
+         %55 = OpAccessChain %10 %35 %18
+               OpStore %55 %54
+         %57 = OpAccessChain %16 %35 %38
+               OpStore %57 %56
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // make an Inner
+  TransformationConstructComposite make_inner(9, {25, 19}, 57, 0, 200);
+  // Bad: Too few fields to make the struct.
+  TransformationConstructComposite make_inner_bad(9, {25}, 57, 0, 200);
+  ASSERT_TRUE(make_inner.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_inner_bad.IsApplicable(context.get(), fact_manager));
+  make_inner.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 25, 200, {0}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 19, 200, {1}));
+
+  // make an Outer
+  TransformationConstructComposite make_outer(33, {46, 200, 56}, 200, 1, 201);
+  // Bad: %200 is not available at the desired program point.
+  TransformationConstructComposite make_outer_bad(33, {46, 200, 56}, 200, 0,
+                                                  201);
+  ASSERT_TRUE(make_outer.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_outer_bad.IsApplicable(context.get(), fact_manager));
+  make_outer.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 46, 201, {0}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 200, 201, {1}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 56, 201, {2}));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %9 "Inner"
+               OpMemberName %9 0 "a"
+               OpMemberName %9 1 "b"
+               OpName %11 "i1"
+               OpName %22 "i2"
+               OpName %33 "Outer"
+               OpMemberName %33 0 "c"
+               OpMemberName %33 1 "d"
+               OpMemberName %33 2 "e"
+               OpName %35 "o"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 2
+          %8 = OpTypeInt 32 1
+          %9 = OpTypeStruct %7 %8
+         %10 = OpTypePointer Function %9
+         %12 = OpConstant %8 0
+         %13 = OpConstant %6 2
+         %14 = OpTypeInt 32 0
+         %15 = OpConstant %14 0
+         %16 = OpTypePointer Function %6
+         %18 = OpConstant %8 1
+         %19 = OpConstant %8 3
+         %20 = OpTypePointer Function %8
+         %23 = OpTypePointer Function %7
+         %31 = OpConstant %14 2
+         %32 = OpTypeArray %9 %31
+         %33 = OpTypeStruct %32 %9 %6
+         %34 = OpTypePointer Function %33
+         %36 = OpConstant %6 1
+         %37 = OpConstantComposite %7 %36 %13
+         %38 = OpConstant %8 2
+         %39 = OpConstantComposite %9 %37 %38
+         %40 = OpConstant %6 3
+         %41 = OpConstant %6 4
+         %42 = OpConstantComposite %7 %40 %41
+         %56 = OpConstant %6 5
+        %100 = OpUndef %9
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+         %22 = OpVariable %10 Function
+         %35 = OpVariable %34 Function
+         %17 = OpAccessChain %16 %11 %12 %15
+               OpStore %17 %13
+         %21 = OpAccessChain %20 %11 %18
+               OpStore %21 %19
+         %24 = OpAccessChain %23 %11 %12
+         %25 = OpLoad %7 %24
+         %26 = OpAccessChain %23 %22 %12
+               OpStore %26 %25
+         %27 = OpAccessChain %20 %11 %18
+         %28 = OpLoad %8 %27
+         %29 = OpIAdd %8 %28 %18
+         %30 = OpAccessChain %20 %22 %18
+               OpStore %30 %29
+         %43 = OpAccessChain %20 %11 %18
+         %44 = OpLoad %8 %43
+         %45 = OpCompositeConstruct %9 %42 %44
+         %46 = OpCompositeConstruct %32 %39 %45
+         %47 = OpLoad %9 %22
+         %48 = OpCompositeConstruct %33 %46 %47 %40
+               OpStore %35 %48
+         %49 = OpLoad %9 %11
+         %50 = OpAccessChain %10 %35 %12 %12
+               OpStore %50 %49
+         %51 = OpLoad %9 %22
+         %52 = OpAccessChain %10 %35 %12 %18
+               OpStore %52 %51
+         %53 = OpAccessChain %10 %35 %12 %12
+         %54 = OpLoad %9 %53
+         %55 = OpAccessChain %10 %35 %18
+               OpStore %55 %54
+        %200 = OpCompositeConstruct %9 %25 %19
+        %201 = OpCompositeConstruct %33 %46 %200 %56
+         %57 = OpAccessChain %16 %35 %38
+               OpStore %57 %56
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationConstructCompositeTest, ConstructVectors) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %9 "v2"
+               OpName %27 "v3"
+               OpName %46 "v4"
+               OpName %53 "iv2"
+               OpName %61 "uv3"
+               OpName %72 "bv4"
+               OpName %88 "uv2"
+               OpName %95 "bv3"
+               OpName %104 "bv2"
+               OpName %116 "iv3"
+               OpName %124 "iv4"
+               OpName %133 "uv4"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 2
+          %8 = OpTypePointer Function %7
+         %10 = OpConstant %6 1
+         %11 = OpConstant %6 2
+         %12 = OpConstantComposite %7 %10 %11
+         %13 = OpTypeInt 32 0
+         %14 = OpConstant %13 0
+         %15 = OpTypePointer Function %6
+         %18 = OpConstant %13 1
+         %21 = OpTypeBool
+         %25 = OpTypeVector %6 3
+         %26 = OpTypePointer Function %25
+         %33 = OpConstant %6 3
+         %34 = OpConstant %6 -0.756802499
+         %38 = OpConstant %13 2
+         %44 = OpTypeVector %6 4
+         %45 = OpTypePointer Function %44
+         %50 = OpTypeInt 32 1
+         %51 = OpTypeVector %50 2
+         %52 = OpTypePointer Function %51
+         %57 = OpTypePointer Function %50
+         %59 = OpTypeVector %13 3
+         %60 = OpTypePointer Function %59
+         %65 = OpConstant %13 3
+         %67 = OpTypePointer Function %13
+         %70 = OpTypeVector %21 4
+         %71 = OpTypePointer Function %70
+         %73 = OpConstantTrue %21
+         %74 = OpTypePointer Function %21
+         %86 = OpTypeVector %13 2
+         %87 = OpTypePointer Function %86
+         %93 = OpTypeVector %21 3
+         %94 = OpTypePointer Function %93
+        %102 = OpTypeVector %21 2
+        %103 = OpTypePointer Function %102
+        %111 = OpConstantFalse %21
+        %114 = OpTypeVector %50 3
+        %115 = OpTypePointer Function %114
+        %117 = OpConstant %50 3
+        %122 = OpTypeVector %50 4
+        %123 = OpTypePointer Function %122
+        %131 = OpTypeVector %13 4
+        %132 = OpTypePointer Function %131
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %9 = OpVariable %8 Function
+         %27 = OpVariable %26 Function
+         %46 = OpVariable %45 Function
+         %53 = OpVariable %52 Function
+         %61 = OpVariable %60 Function
+         %72 = OpVariable %71 Function
+         %88 = OpVariable %87 Function
+         %95 = OpVariable %94 Function
+        %104 = OpVariable %103 Function
+        %116 = OpVariable %115 Function
+        %124 = OpVariable %123 Function
+        %133 = OpVariable %132 Function
+               OpStore %9 %12
+         %16 = OpAccessChain %15 %9 %14
+         %17 = OpLoad %6 %16
+         %19 = OpAccessChain %15 %9 %18
+         %20 = OpLoad %6 %19
+         %22 = OpFOrdGreaterThan %21 %17 %20
+               OpSelectionMerge %24 None
+               OpBranchConditional %22 %23 %101
+         %23 = OpLabel
+         %28 = OpAccessChain %15 %9 %14
+         %29 = OpLoad %6 %28
+         %30 = OpAccessChain %15 %9 %18
+         %31 = OpLoad %6 %30
+         %32 = OpFAdd %6 %29 %31
+         %35 = OpCompositeConstruct %25 %32 %33 %34
+               OpStore %27 %35
+         %36 = OpAccessChain %15 %27 %14
+         %37 = OpLoad %6 %36
+         %39 = OpAccessChain %15 %27 %38
+         %40 = OpLoad %6 %39
+         %41 = OpFOrdLessThan %21 %37 %40
+               OpSelectionMerge %43 None
+               OpBranchConditional %41 %42 %69
+         %42 = OpLabel
+         %47 = OpAccessChain %15 %9 %18
+         %48 = OpLoad %6 %47
+         %49 = OpAccessChain %15 %46 %14
+               OpStore %49 %48
+         %54 = OpAccessChain %15 %27 %38
+         %55 = OpLoad %6 %54
+         %56 = OpConvertFToS %50 %55
+         %58 = OpAccessChain %57 %53 %14
+               OpStore %58 %56
+         %62 = OpAccessChain %15 %46 %14
+         %63 = OpLoad %6 %62
+         %64 = OpConvertFToU %13 %63
+         %66 = OpIAdd %13 %64 %65
+         %68 = OpAccessChain %67 %61 %14
+               OpStore %68 %66
+               OpBranch %43
+         %69 = OpLabel
+         %75 = OpAccessChain %74 %72 %14
+               OpStore %75 %73
+         %76 = OpAccessChain %74 %72 %14
+         %77 = OpLoad %21 %76
+         %78 = OpLogicalNot %21 %77
+         %79 = OpAccessChain %74 %72 %18
+               OpStore %79 %78
+         %80 = OpAccessChain %74 %72 %14
+         %81 = OpLoad %21 %80
+         %82 = OpAccessChain %74 %72 %18
+         %83 = OpLoad %21 %82
+         %84 = OpLogicalAnd %21 %81 %83
+         %85 = OpAccessChain %74 %72 %38
+               OpStore %85 %84
+         %89 = OpAccessChain %67 %88 %14
+         %90 = OpLoad %13 %89
+         %91 = OpINotEqual %21 %90 %14
+         %92 = OpAccessChain %74 %72 %65
+               OpStore %92 %91
+               OpBranch %43
+         %43 = OpLabel
+         %96 = OpLoad %70 %72
+         %97 = OpCompositeExtract %21 %96 0
+         %98 = OpCompositeExtract %21 %96 1
+         %99 = OpCompositeExtract %21 %96 2
+        %100 = OpCompositeConstruct %93 %97 %98 %99
+               OpStore %95 %100
+               OpBranch %24
+        %101 = OpLabel
+        %105 = OpAccessChain %67 %88 %14
+        %106 = OpLoad %13 %105
+        %107 = OpINotEqual %21 %106 %14
+        %108 = OpCompositeConstruct %102 %107 %107
+               OpStore %104 %108
+               OpBranch %24
+         %24 = OpLabel
+        %109 = OpAccessChain %74 %104 %18
+        %110 = OpLoad %21 %109
+        %112 = OpLogicalOr %21 %110 %111
+        %113 = OpAccessChain %74 %104 %14
+               OpStore %113 %112
+        %118 = OpAccessChain %57 %116 %14
+               OpStore %118 %117
+        %119 = OpAccessChain %57 %116 %14
+        %120 = OpLoad %50 %119
+        %121 = OpAccessChain %57 %53 %18
+               OpStore %121 %120
+        %125 = OpAccessChain %57 %116 %14
+        %126 = OpLoad %50 %125
+        %127 = OpAccessChain %57 %53 %18
+        %128 = OpLoad %50 %127
+        %129 = OpIAdd %50 %126 %128
+        %130 = OpAccessChain %57 %124 %65
+               OpStore %130 %129
+        %134 = OpAccessChain %57 %116 %14
+        %135 = OpLoad %50 %134
+        %136 = OpBitcast %13 %135
+        %137 = OpAccessChain %67 %133 %14
+               OpStore %137 %136
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  TransformationConstructComposite make_vec2(7, {17, 11}, 100, 1, 200);
+  // Bad: not enough data for a vec2
+  TransformationConstructComposite make_vec2_bad(7, {11}, 100, 1, 200);
+  ASSERT_TRUE(make_vec2.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_vec2_bad.IsApplicable(context.get(), fact_manager));
+  make_vec2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 17, 200, {0}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 11, 200, {1}));
+
+  TransformationConstructComposite make_vec3(25, {12, 32}, 35, 0, 201);
+  // Bad: too much data for a vec3
+  TransformationConstructComposite make_vec3_bad(25, {12, 32, 32}, 35, 0, 201);
+  ASSERT_TRUE(make_vec3.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_vec3_bad.IsApplicable(context.get(), fact_manager));
+  make_vec3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 12, 201, {0}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 32, 201, {2}));
+
+  TransformationConstructComposite make_vec4(44, {32, 32, 10, 11}, 75, 0, 202);
+  // Bad: id 48 is not available at the insertion points
+  TransformationConstructComposite make_vec4_bad(44, {48, 32, 10, 11}, 75, 0,
+                                                 202);
+  ASSERT_TRUE(make_vec4.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_vec4_bad.IsApplicable(context.get(), fact_manager));
+  make_vec4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 32, 202, {0}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 32, 202, {1}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 10, 202, {2}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 11, 202, {3}));
+
+  TransformationConstructComposite make_ivec2(51, {126, 120}, 128, 0, 203);
+  // Bad: if 128 is not available at the instruction that defines 128
+  TransformationConstructComposite make_ivec2_bad(51, {128, 120}, 128, 0, 203);
+  ASSERT_TRUE(make_ivec2.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_ivec2_bad.IsApplicable(context.get(), fact_manager));
+  make_ivec2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 126, 203, {0}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 120, 203, {1}));
+
+  TransformationConstructComposite make_ivec3(114, {56, 117, 56}, 66, 1, 204);
+  // Bad because 1300 is not an id
+  TransformationConstructComposite make_ivec3_bad(114, {56, 117, 1300}, 66, 1,
+                                                  204);
+  ASSERT_TRUE(make_ivec3.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_ivec3_bad.IsApplicable(context.get(), fact_manager));
+  make_ivec3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 56, 204, {0}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 117, 204, {1}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 56, 204, {2}));
+
+  TransformationConstructComposite make_ivec4(122, {56, 117, 117, 117}, 66, 0,
+                                              205);
+  // Bad because 86 is the wrong type.
+  TransformationConstructComposite make_ivec4_bad(86, {56, 117, 117, 117}, 66,
+                                                  0, 205);
+  ASSERT_TRUE(make_ivec4.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_ivec4_bad.IsApplicable(context.get(), fact_manager));
+  make_ivec4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 56, 205, {0}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 117, 205, {1}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 117, 205, {2}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 117, 205, {3}));
+
+  TransformationConstructComposite make_uvec2(86, {18, 38}, 133, 2, 206);
+  TransformationConstructComposite make_uvec2_bad(86, {18, 38}, 133, 200, 206);
+  ASSERT_TRUE(make_uvec2.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_uvec2_bad.IsApplicable(context.get(), fact_manager));
+  make_uvec2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 18, 206, {0}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 38, 206, {1}));
+
+  TransformationConstructComposite make_uvec3(59, {14, 18, 136}, 137, 2, 207);
+  // Bad because 1300 is not an id
+  TransformationConstructComposite make_uvec3_bad(59, {14, 18, 1300}, 137, 2,
+                                                  207);
+  ASSERT_TRUE(make_uvec3.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_uvec3_bad.IsApplicable(context.get(), fact_manager));
+  make_uvec3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 14, 207, {0}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 18, 207, {1}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 136, 207, {2}));
+
+  TransformationConstructComposite make_uvec4(131, {14, 18, 136, 136}, 137, 0,
+                                              208);
+  // Bad because 86 is the wrong type.
+  TransformationConstructComposite make_uvec4_bad(86, {14, 18, 136, 136}, 137,
+                                                  0, 208);
+  ASSERT_TRUE(make_uvec4.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_uvec4_bad.IsApplicable(context.get(), fact_manager));
+  make_uvec4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 14, 208, {0}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 18, 208, {1}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 136, 208, {2}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 136, 208, {3}));
+
+  TransformationConstructComposite make_bvec2(102,
+                                              {
+                                                  111,
+                                                  41,
+                                              },
+                                              75, 0, 209);
+  // Bad because 0 is not a valid base instruction id
+  TransformationConstructComposite make_bvec2_bad(102,
+                                                  {
+                                                      111,
+                                                      41,
+                                                  },
+                                                  0, 0, 209);
+  ASSERT_TRUE(make_bvec2.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_bvec2_bad.IsApplicable(context.get(), fact_manager));
+  make_bvec2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 111, 209, {0}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 41, 209, {1}));
+
+  TransformationConstructComposite make_bvec3(93, {108, 73}, 108, 1, 210);
+  // Bad because there are too many components for a bvec3
+  TransformationConstructComposite make_bvec3_bad(93, {108, 108}, 108, 1, 210);
+  ASSERT_TRUE(make_bvec3.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_bvec3_bad.IsApplicable(context.get(), fact_manager));
+  make_bvec3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 108, 210, {0}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 73, 210, {2}));
+
+  TransformationConstructComposite make_bvec4(70, {108, 108}, 108, 3, 211);
+  // Bad because 21 is a type, not a result id
+  TransformationConstructComposite make_bvec4_bad(70, {21, 108}, 108, 3, 211);
+  ASSERT_TRUE(make_bvec4.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(make_bvec4_bad.IsApplicable(context.get(), fact_manager));
+  make_bvec4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 108, 211, {0}));
+  ASSERT_TRUE(SynonymFactHolds(fact_manager, 108, 211, {2}));
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %9 "v2"
+               OpName %27 "v3"
+               OpName %46 "v4"
+               OpName %53 "iv2"
+               OpName %61 "uv3"
+               OpName %72 "bv4"
+               OpName %88 "uv2"
+               OpName %95 "bv3"
+               OpName %104 "bv2"
+               OpName %116 "iv3"
+               OpName %124 "iv4"
+               OpName %133 "uv4"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 2
+          %8 = OpTypePointer Function %7
+         %10 = OpConstant %6 1
+         %11 = OpConstant %6 2
+         %12 = OpConstantComposite %7 %10 %11
+         %13 = OpTypeInt 32 0
+         %14 = OpConstant %13 0
+         %15 = OpTypePointer Function %6
+         %18 = OpConstant %13 1
+         %21 = OpTypeBool
+         %25 = OpTypeVector %6 3
+         %26 = OpTypePointer Function %25
+         %33 = OpConstant %6 3
+         %34 = OpConstant %6 -0.756802499
+         %38 = OpConstant %13 2
+         %44 = OpTypeVector %6 4
+         %45 = OpTypePointer Function %44
+         %50 = OpTypeInt 32 1
+         %51 = OpTypeVector %50 2
+         %52 = OpTypePointer Function %51
+         %57 = OpTypePointer Function %50
+         %59 = OpTypeVector %13 3
+         %60 = OpTypePointer Function %59
+         %65 = OpConstant %13 3
+         %67 = OpTypePointer Function %13
+         %70 = OpTypeVector %21 4
+         %71 = OpTypePointer Function %70
+         %73 = OpConstantTrue %21
+         %74 = OpTypePointer Function %21
+         %86 = OpTypeVector %13 2
+         %87 = OpTypePointer Function %86
+         %93 = OpTypeVector %21 3
+         %94 = OpTypePointer Function %93
+        %102 = OpTypeVector %21 2
+        %103 = OpTypePointer Function %102
+        %111 = OpConstantFalse %21
+        %114 = OpTypeVector %50 3
+        %115 = OpTypePointer Function %114
+        %117 = OpConstant %50 3
+        %122 = OpTypeVector %50 4
+        %123 = OpTypePointer Function %122
+        %131 = OpTypeVector %13 4
+        %132 = OpTypePointer Function %131
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %9 = OpVariable %8 Function
+         %27 = OpVariable %26 Function
+         %46 = OpVariable %45 Function
+         %53 = OpVariable %52 Function
+         %61 = OpVariable %60 Function
+         %72 = OpVariable %71 Function
+         %88 = OpVariable %87 Function
+         %95 = OpVariable %94 Function
+        %104 = OpVariable %103 Function
+        %116 = OpVariable %115 Function
+        %124 = OpVariable %123 Function
+        %133 = OpVariable %132 Function
+               OpStore %9 %12
+        %206 = OpCompositeConstruct %86 %18 %38
+         %16 = OpAccessChain %15 %9 %14
+         %17 = OpLoad %6 %16
+         %19 = OpAccessChain %15 %9 %18
+         %20 = OpLoad %6 %19
+         %22 = OpFOrdGreaterThan %21 %17 %20
+               OpSelectionMerge %24 None
+               OpBranchConditional %22 %23 %101
+         %23 = OpLabel
+         %28 = OpAccessChain %15 %9 %14
+         %29 = OpLoad %6 %28
+         %30 = OpAccessChain %15 %9 %18
+         %31 = OpLoad %6 %30
+         %32 = OpFAdd %6 %29 %31
+        %201 = OpCompositeConstruct %25 %12 %32
+         %35 = OpCompositeConstruct %25 %32 %33 %34
+               OpStore %27 %35
+         %36 = OpAccessChain %15 %27 %14
+         %37 = OpLoad %6 %36
+         %39 = OpAccessChain %15 %27 %38
+         %40 = OpLoad %6 %39
+         %41 = OpFOrdLessThan %21 %37 %40
+               OpSelectionMerge %43 None
+               OpBranchConditional %41 %42 %69
+         %42 = OpLabel
+         %47 = OpAccessChain %15 %9 %18
+         %48 = OpLoad %6 %47
+         %49 = OpAccessChain %15 %46 %14
+               OpStore %49 %48
+         %54 = OpAccessChain %15 %27 %38
+         %55 = OpLoad %6 %54
+         %56 = OpConvertFToS %50 %55
+         %58 = OpAccessChain %57 %53 %14
+               OpStore %58 %56
+         %62 = OpAccessChain %15 %46 %14
+         %63 = OpLoad %6 %62
+         %64 = OpConvertFToU %13 %63
+        %205 = OpCompositeConstruct %122 %56 %117 %117 %117
+         %66 = OpIAdd %13 %64 %65
+        %204 = OpCompositeConstruct %114 %56 %117 %56
+         %68 = OpAccessChain %67 %61 %14
+               OpStore %68 %66
+               OpBranch %43
+         %69 = OpLabel
+        %202 = OpCompositeConstruct %44 %32 %32 %10 %11
+        %209 = OpCompositeConstruct %102 %111 %41
+         %75 = OpAccessChain %74 %72 %14
+               OpStore %75 %73
+         %76 = OpAccessChain %74 %72 %14
+         %77 = OpLoad %21 %76
+         %78 = OpLogicalNot %21 %77
+         %79 = OpAccessChain %74 %72 %18
+               OpStore %79 %78
+         %80 = OpAccessChain %74 %72 %14
+         %81 = OpLoad %21 %80
+         %82 = OpAccessChain %74 %72 %18
+         %83 = OpLoad %21 %82
+         %84 = OpLogicalAnd %21 %81 %83
+         %85 = OpAccessChain %74 %72 %38
+               OpStore %85 %84
+         %89 = OpAccessChain %67 %88 %14
+         %90 = OpLoad %13 %89
+         %91 = OpINotEqual %21 %90 %14
+         %92 = OpAccessChain %74 %72 %65
+               OpStore %92 %91
+               OpBranch %43
+         %43 = OpLabel
+         %96 = OpLoad %70 %72
+         %97 = OpCompositeExtract %21 %96 0
+         %98 = OpCompositeExtract %21 %96 1
+         %99 = OpCompositeExtract %21 %96 2
+        %100 = OpCompositeConstruct %93 %97 %98 %99
+        %200 = OpCompositeConstruct %7 %17 %11
+               OpStore %95 %100
+               OpBranch %24
+        %101 = OpLabel
+        %105 = OpAccessChain %67 %88 %14
+        %106 = OpLoad %13 %105
+        %107 = OpINotEqual %21 %106 %14
+        %108 = OpCompositeConstruct %102 %107 %107
+        %210 = OpCompositeConstruct %93 %108 %73
+               OpStore %104 %108
+        %211 = OpCompositeConstruct %70 %108 %108
+               OpBranch %24
+         %24 = OpLabel
+        %109 = OpAccessChain %74 %104 %18
+        %110 = OpLoad %21 %109
+        %112 = OpLogicalOr %21 %110 %111
+        %113 = OpAccessChain %74 %104 %14
+               OpStore %113 %112
+        %118 = OpAccessChain %57 %116 %14
+               OpStore %118 %117
+        %119 = OpAccessChain %57 %116 %14
+        %120 = OpLoad %50 %119
+        %121 = OpAccessChain %57 %53 %18
+               OpStore %121 %120
+        %125 = OpAccessChain %57 %116 %14
+        %126 = OpLoad %50 %125
+        %127 = OpAccessChain %57 %53 %18
+        %203 = OpCompositeConstruct %51 %126 %120
+        %128 = OpLoad %50 %127
+        %129 = OpIAdd %50 %126 %128
+        %130 = OpAccessChain %57 %124 %65
+               OpStore %130 %129
+        %134 = OpAccessChain %57 %116 %14
+        %135 = OpLoad %50 %134
+        %136 = OpBitcast %13 %135
+        %208 = OpCompositeConstruct %131 %14 %18 %136 %136
+        %137 = OpAccessChain %67 %133 %14
+               OpStore %137 %136
+        %207 = OpCompositeConstruct %59 %14 %18 %136
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools

+ 13 - 10
3rdparty/spirv-tools/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp

@@ -16,6 +16,7 @@
 
 
 #include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/id_use_descriptor.h"
 #include "source/fuzz/id_use_descriptor.h"
+#include "source/fuzz/instruction_descriptor.h"
 #include "test/fuzz/fuzz_test_util.h"
 #include "test/fuzz/fuzz_test_util.h"
 
 
 namespace spvtools {
 namespace spvtools {
@@ -164,12 +165,14 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,
   FactManager fact_manager;
   FactManager fact_manager;
 
 
   std::vector<protobufs::IdUseDescriptor> uses_of_true = {
   std::vector<protobufs::IdUseDescriptor> uses_of_true = {
-      transformation::MakeIdUseDescriptor(41, SpvOpStore, 1, 44, 12),
-      transformation::MakeIdUseDescriptor(41, SpvOpLogicalOr, 0, 46, 0)};
+      MakeIdUseDescriptor(41, MakeInstructionDescriptor(44, SpvOpStore, 12), 1),
+      MakeIdUseDescriptor(41, MakeInstructionDescriptor(46, SpvOpLogicalOr, 0),
+                          0)};
 
 
   std::vector<protobufs::IdUseDescriptor> uses_of_false = {
   std::vector<protobufs::IdUseDescriptor> uses_of_false = {
-      transformation::MakeIdUseDescriptor(43, SpvOpStore, 1, 44, 13),
-      transformation::MakeIdUseDescriptor(43, SpvOpLogicalAnd, 1, 48, 0)};
+      MakeIdUseDescriptor(43, MakeInstructionDescriptor(44, SpvOpStore, 13), 1),
+      MakeIdUseDescriptor(43, MakeInstructionDescriptor(48, SpvOpLogicalAnd, 0),
+                          1)};
 
 
   const uint32_t fresh_id = 100;
   const uint32_t fresh_id = 100;
 
 
@@ -529,10 +532,10 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,
 
 
   FactManager fact_manager;
   FactManager fact_manager;
 
 
-  auto use_of_true_in_if =
-      transformation::MakeIdUseDescriptor(13, SpvOpBranchConditional, 0, 10, 0);
-  auto use_of_false_in_while =
-      transformation::MakeIdUseDescriptor(21, SpvOpBranchConditional, 0, 16, 0);
+  auto use_of_true_in_if = MakeIdUseDescriptor(
+      13, MakeInstructionDescriptor(10, SpvOpBranchConditional, 0), 0);
+  auto use_of_false_in_while = MakeIdUseDescriptor(
+      21, MakeInstructionDescriptor(16, SpvOpBranchConditional, 0), 0);
 
 
   auto replacement_1 = TransformationReplaceBooleanConstantWithConstantBinary(
   auto replacement_1 = TransformationReplaceBooleanConstantWithConstantBinary(
       use_of_true_in_if, 9, 11, SpvOpSLessThan, 100);
       use_of_true_in_if, 9, 11, SpvOpSLessThan, 100);
@@ -641,8 +644,8 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, OpPhi) {
   FactManager fact_manager;
   FactManager fact_manager;
 
 
   auto replacement = TransformationReplaceBooleanConstantWithConstantBinary(
   auto replacement = TransformationReplaceBooleanConstantWithConstantBinary(
-      transformation::MakeIdUseDescriptor(9, SpvOpPhi, 0, 23, 0), 13, 15,
-      SpvOpSLessThan, 100);
+      MakeIdUseDescriptor(9, MakeInstructionDescriptor(23, SpvOpPhi, 0), 0), 13,
+      15, SpvOpSLessThan, 100);
 
 
   ASSERT_FALSE(replacement.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(replacement.IsApplicable(context.get(), fact_manager));
 }
 }

+ 31 - 30
3rdparty/spirv-tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp

@@ -13,6 +13,7 @@
 // limitations under the License.
 // limitations under the License.
 
 
 #include "source/fuzz/transformation_replace_constant_with_uniform.h"
 #include "source/fuzz/transformation_replace_constant_with_uniform.h"
+#include "source/fuzz/instruction_descriptor.h"
 #include "source/fuzz/uniform_buffer_element_descriptor.h"
 #include "source/fuzz/uniform_buffer_element_descriptor.h"
 #include "test/fuzz/fuzz_test_util.h"
 #include "test/fuzz/fuzz_test_util.h"
 
 
@@ -116,11 +117,11 @@ TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) {
 
 
   // The constant ids are 9, 11 and 14, for 1, 2 and 3 respectively.
   // The constant ids are 9, 11 and 14, for 1, 2 and 3 respectively.
   protobufs::IdUseDescriptor use_of_9_in_store =
   protobufs::IdUseDescriptor use_of_9_in_store =
-      transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0);
+      MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1);
   protobufs::IdUseDescriptor use_of_11_in_add =
   protobufs::IdUseDescriptor use_of_11_in_add =
-      transformation::MakeIdUseDescriptor(11, SpvOpIAdd, 1, 12, 0);
+      MakeIdUseDescriptor(11, MakeInstructionDescriptor(12, SpvOpIAdd, 0), 1);
   protobufs::IdUseDescriptor use_of_14_in_add =
   protobufs::IdUseDescriptor use_of_14_in_add =
-      transformation::MakeIdUseDescriptor(14, SpvOpIAdd, 0, 15, 0);
+      MakeIdUseDescriptor(14, MakeInstructionDescriptor(15, SpvOpIAdd, 0), 0);
 
 
   // These transformations work: they match the facts.
   // These transformations work: they match the facts.
   auto transformation_use_of_9_in_store =
   auto transformation_use_of_9_in_store =
@@ -167,7 +168,7 @@ TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) {
   // The following transformation does not apply because the id descriptor is
   // The following transformation does not apply because the id descriptor is
   // not sensible.
   // not sensible.
   protobufs::IdUseDescriptor nonsense_id_use_descriptor =
   protobufs::IdUseDescriptor nonsense_id_use_descriptor =
-      transformation::MakeIdUseDescriptor(9, SpvOpIAdd, 0, 15, 0);
+      MakeIdUseDescriptor(9, MakeInstructionDescriptor(15, SpvOpIAdd, 0), 0);
   ASSERT_FALSE(TransformationReplaceConstantWithUniform(
   ASSERT_FALSE(TransformationReplaceConstantWithUniform(
                    nonsense_id_use_descriptor, blockname_a, 101, 102)
                    nonsense_id_use_descriptor, blockname_a, 101, 102)
                    .IsApplicable(context.get(), fact_manager));
                    .IsApplicable(context.get(), fact_manager));
@@ -477,13 +478,13 @@ TEST(TransformationReplaceConstantWithUniformTest, NestedStruct) {
 
 
   // The constant ids are 13, 15, 17 and 20, for 1, 2, 3 and 4 respectively.
   // The constant ids are 13, 15, 17 and 20, for 1, 2, 3 and 4 respectively.
   protobufs::IdUseDescriptor use_of_13_in_store =
   protobufs::IdUseDescriptor use_of_13_in_store =
-      transformation::MakeIdUseDescriptor(13, SpvOpStore, 1, 21, 0);
+      MakeIdUseDescriptor(13, MakeInstructionDescriptor(21, SpvOpStore, 0), 1);
   protobufs::IdUseDescriptor use_of_15_in_add =
   protobufs::IdUseDescriptor use_of_15_in_add =
-      transformation::MakeIdUseDescriptor(15, SpvOpIAdd, 1, 16, 0);
+      MakeIdUseDescriptor(15, MakeInstructionDescriptor(16, SpvOpIAdd, 0), 1);
   protobufs::IdUseDescriptor use_of_17_in_add =
   protobufs::IdUseDescriptor use_of_17_in_add =
-      transformation::MakeIdUseDescriptor(17, SpvOpIAdd, 0, 19, 0);
+      MakeIdUseDescriptor(17, MakeInstructionDescriptor(19, SpvOpIAdd, 0), 0);
   protobufs::IdUseDescriptor use_of_20_in_store =
   protobufs::IdUseDescriptor use_of_20_in_store =
-      transformation::MakeIdUseDescriptor(20, SpvOpStore, 1, 19, 1);
+      MakeIdUseDescriptor(20, MakeInstructionDescriptor(19, SpvOpStore, 1), 1);
 
 
   // These transformations work: they match the facts.
   // These transformations work: they match the facts.
   auto transformation_use_of_13_in_store =
   auto transformation_use_of_13_in_store =
@@ -703,7 +704,7 @@ TEST(TransformationReplaceConstantWithUniformTest, NoUniformIntPointerPresent) {
 
 
   // The constant id is 9 for 0.
   // The constant id is 9 for 0.
   protobufs::IdUseDescriptor use_of_9_in_store =
   protobufs::IdUseDescriptor use_of_9_in_store =
-      transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0);
+      MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1);
 
 
   // This transformation is not available because no uniform pointer to integer
   // This transformation is not available because no uniform pointer to integer
   // type is present:
   // type is present:
@@ -778,7 +779,7 @@ TEST(TransformationReplaceConstantWithUniformTest, NoConstantPresentForIndex) {
 
 
   // The constant id is 9 for 9.
   // The constant id is 9 for 9.
   protobufs::IdUseDescriptor use_of_9_in_store =
   protobufs::IdUseDescriptor use_of_9_in_store =
-      transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0);
+      MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1);
 
 
   // This transformation is not available because no constant is present for the
   // This transformation is not available because no constant is present for the
   // index 1 required to index into the uniform buffer:
   // index 1 required to index into the uniform buffer:
@@ -852,7 +853,7 @@ TEST(TransformationReplaceConstantWithUniformTest,
 
 
   // The constant id is 9 for 3.0.
   // The constant id is 9 for 3.0.
   protobufs::IdUseDescriptor use_of_9_in_store =
   protobufs::IdUseDescriptor use_of_9_in_store =
-      transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0);
+      MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1);
 
 
   // This transformation is not available because no integer type is present to
   // This transformation is not available because no integer type is present to
   // allow a constant index to be expressed:
   // allow a constant index to be expressed:
@@ -937,9 +938,9 @@ TEST(TransformationReplaceConstantWithUniformTest,
 
 
   // The constant ids for 9 and 10 are 9 and 11 respectively
   // The constant ids for 9 and 10 are 9 and 11 respectively
   protobufs::IdUseDescriptor use_of_9_in_store =
   protobufs::IdUseDescriptor use_of_9_in_store =
-      transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 10, 0);
+      MakeIdUseDescriptor(9, MakeInstructionDescriptor(10, SpvOpStore, 0), 1);
   protobufs::IdUseDescriptor use_of_11_in_store =
   protobufs::IdUseDescriptor use_of_11_in_store =
-      transformation::MakeIdUseDescriptor(11, SpvOpStore, 1, 10, 1);
+      MakeIdUseDescriptor(11, MakeInstructionDescriptor(10, SpvOpStore, 1), 1);
 
 
   // These are right:
   // These are right:
   ASSERT_TRUE(TransformationReplaceConstantWithUniform(use_of_9_in_store,
   ASSERT_TRUE(TransformationReplaceConstantWithUniform(use_of_9_in_store,
@@ -1220,58 +1221,58 @@ TEST(TransformationReplaceConstantWithUniformTest, ComplexReplacements) {
   std::vector<TransformationReplaceConstantWithUniform> transformations;
   std::vector<TransformationReplaceConstantWithUniform> transformations;
 
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(18, SpvOpStore, 1, 20, 0),
+      MakeIdUseDescriptor(18, MakeInstructionDescriptor(20, SpvOpStore, 0), 1),
       uniform_f_a_4, 200, 201));
       uniform_f_a_4, 200, 201));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(22, SpvOpStore, 1, 23, 0),
+      MakeIdUseDescriptor(22, MakeInstructionDescriptor(23, SpvOpStore, 0), 1),
       uniform_f_a_3, 202, 203));
       uniform_f_a_3, 202, 203));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(25, SpvOpStore, 1, 26, 0),
+      MakeIdUseDescriptor(25, MakeInstructionDescriptor(26, SpvOpStore, 0), 1),
       uniform_f_a_2, 204, 205));
       uniform_f_a_2, 204, 205));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(28, SpvOpStore, 1, 29, 0),
+      MakeIdUseDescriptor(28, MakeInstructionDescriptor(29, SpvOpStore, 0), 1),
       uniform_f_a_1, 206, 207));
       uniform_f_a_1, 206, 207));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(31, SpvOpStore, 1, 32, 0),
+      MakeIdUseDescriptor(31, MakeInstructionDescriptor(32, SpvOpStore, 0), 1),
       uniform_f_a_0, 208, 209));
       uniform_f_a_0, 208, 209));
 
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(30, SpvOpStore, 1, 35, 0),
+      MakeIdUseDescriptor(30, MakeInstructionDescriptor(35, SpvOpStore, 0), 1),
       uniform_f_b_w, 210, 211));
       uniform_f_b_w, 210, 211));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(27, SpvOpStore, 1, 37, 0),
+      MakeIdUseDescriptor(27, MakeInstructionDescriptor(37, SpvOpStore, 0), 1),
       uniform_f_b_z, 212, 213));
       uniform_f_b_z, 212, 213));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(24, SpvOpStore, 1, 39, 0),
+      MakeIdUseDescriptor(24, MakeInstructionDescriptor(39, SpvOpStore, 0), 1),
       uniform_f_b_y, 214, 215));
       uniform_f_b_y, 214, 215));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(21, SpvOpStore, 1, 41, 0),
+      MakeIdUseDescriptor(21, MakeInstructionDescriptor(41, SpvOpStore, 0), 1),
       uniform_f_b_x, 216, 217));
       uniform_f_b_x, 216, 217));
 
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(44, SpvOpStore, 1, 45, 0),
+      MakeIdUseDescriptor(44, MakeInstructionDescriptor(45, SpvOpStore, 0), 1),
       uniform_f_c_z, 220, 221));
       uniform_f_c_z, 220, 221));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(46, SpvOpStore, 1, 47, 0),
+      MakeIdUseDescriptor(46, MakeInstructionDescriptor(47, SpvOpStore, 0), 1),
       uniform_f_c_y, 222, 223));
       uniform_f_c_y, 222, 223));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(48, SpvOpStore, 1, 49, 0),
+      MakeIdUseDescriptor(48, MakeInstructionDescriptor(49, SpvOpStore, 0), 1),
       uniform_f_c_x, 224, 225));
       uniform_f_c_x, 224, 225));
 
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(50, SpvOpStore, 1, 52, 0),
+      MakeIdUseDescriptor(50, MakeInstructionDescriptor(52, SpvOpStore, 0), 1),
       uniform_f_d, 226, 227));
       uniform_f_d, 226, 227));
 
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(53, SpvOpStore, 1, 54, 0),
+      MakeIdUseDescriptor(53, MakeInstructionDescriptor(54, SpvOpStore, 0), 1),
       uniform_h_x, 228, 229));
       uniform_h_x, 228, 229));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(55, SpvOpStore, 1, 56, 0),
+      MakeIdUseDescriptor(55, MakeInstructionDescriptor(56, SpvOpStore, 0), 1),
       uniform_h_y, 230, 231));
       uniform_h_y, 230, 231));
 
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(42, SpvOpStore, 1, 43, 0), uniform_g,
-      218, 219));
+      MakeIdUseDescriptor(42, MakeInstructionDescriptor(43, SpvOpStore, 0), 1),
+      uniform_g, 218, 219));
 
 
   for (auto& transformation : transformations) {
   for (auto& transformation : transformations) {
     ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
     ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));

+ 1092 - 74
3rdparty/spirv-tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp

@@ -15,6 +15,7 @@
 #include "source/fuzz/transformation_replace_id_with_synonym.h"
 #include "source/fuzz/transformation_replace_id_with_synonym.h"
 #include "source/fuzz/data_descriptor.h"
 #include "source/fuzz/data_descriptor.h"
 #include "source/fuzz/id_use_descriptor.h"
 #include "source/fuzz/id_use_descriptor.h"
+#include "source/fuzz/instruction_descriptor.h"
 #include "test/fuzz/fuzz_test_util.h"
 #include "test/fuzz/fuzz_test_util.h"
 
 
 namespace spvtools {
 namespace spvtools {
@@ -185,10 +186,14 @@ const std::string kComplexShader = R"(
                OpFunctionEnd
                OpFunctionEnd
 )";
 )";
 
 
-protobufs::Fact MakeFact(uint32_t id, uint32_t copy_id) {
+protobufs::Fact MakeSynonymFact(uint32_t id, uint32_t synonym_object,
+                                std::vector<uint32_t> indices = {}) {
   protobufs::FactIdSynonym id_synonym_fact;
   protobufs::FactIdSynonym id_synonym_fact;
   id_synonym_fact.set_id(id);
   id_synonym_fact.set_id(id);
-  id_synonym_fact.mutable_data_descriptor()->set_object(copy_id);
+  id_synonym_fact.mutable_data_descriptor()->set_object(synonym_object);
+  for (auto index : indices) {
+    id_synonym_fact.mutable_data_descriptor()->add_index(index);
+  }
   protobufs::Fact result;
   protobufs::Fact result;
   *result.mutable_id_synonym_fact() = id_synonym_fact;
   *result.mutable_id_synonym_fact() = id_synonym_fact;
   return result;
   return result;
@@ -196,17 +201,17 @@ protobufs::Fact MakeFact(uint32_t id, uint32_t copy_id) {
 
 
 // Equips the fact manager with synonym facts for the above shader.
 // Equips the fact manager with synonym facts for the above shader.
 void SetUpIdSynonyms(FactManager* fact_manager, opt::IRContext* context) {
 void SetUpIdSynonyms(FactManager* fact_manager, opt::IRContext* context) {
-  fact_manager->AddFact(MakeFact(15, 200), context);
-  fact_manager->AddFact(MakeFact(15, 201), context);
-  fact_manager->AddFact(MakeFact(15, 202), context);
-  fact_manager->AddFact(MakeFact(55, 203), context);
-  fact_manager->AddFact(MakeFact(54, 204), context);
-  fact_manager->AddFact(MakeFact(74, 205), context);
-  fact_manager->AddFact(MakeFact(78, 206), context);
-  fact_manager->AddFact(MakeFact(84, 207), context);
-  fact_manager->AddFact(MakeFact(33, 208), context);
-  fact_manager->AddFact(MakeFact(12, 209), context);
-  fact_manager->AddFact(MakeFact(19, 210), context);
+  fact_manager->AddFact(MakeSynonymFact(15, 200), context);
+  fact_manager->AddFact(MakeSynonymFact(15, 201), context);
+  fact_manager->AddFact(MakeSynonymFact(15, 202), context);
+  fact_manager->AddFact(MakeSynonymFact(55, 203), context);
+  fact_manager->AddFact(MakeSynonymFact(54, 204), context);
+  fact_manager->AddFact(MakeSynonymFact(74, 205), context);
+  fact_manager->AddFact(MakeSynonymFact(78, 206), context);
+  fact_manager->AddFact(MakeSynonymFact(84, 207), context);
+  fact_manager->AddFact(MakeSynonymFact(33, 208), context);
+  fact_manager->AddFact(MakeSynonymFact(12, 209), context);
+  fact_manager->AddFact(MakeSynonymFact(19, 210), context);
 }
 }
 
 
 TEST(TransformationReplaceIdWithSynonymTest, IllegalTransformations) {
 TEST(TransformationReplaceIdWithSynonymTest, IllegalTransformations) {
@@ -222,7 +227,7 @@ TEST(TransformationReplaceIdWithSynonymTest, IllegalTransformations) {
   // %202 cannot replace %15 as in-operand 0 of %300, since %202 does not
   // %202 cannot replace %15 as in-operand 0 of %300, since %202 does not
   // dominate %300.
   // dominate %300.
   auto synonym_does_not_dominate_use = TransformationReplaceIdWithSynonym(
   auto synonym_does_not_dominate_use = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(15, SpvOpIAdd, 0, 300, 0),
+      MakeIdUseDescriptor(15, MakeInstructionDescriptor(300, SpvOpIAdd, 0), 0),
       MakeDataDescriptor(202, {}), 0);
       MakeDataDescriptor(202, {}), 0);
   ASSERT_FALSE(
   ASSERT_FALSE(
       synonym_does_not_dominate_use.IsApplicable(context.get(), fact_manager));
       synonym_does_not_dominate_use.IsApplicable(context.get(), fact_manager));
@@ -231,28 +236,31 @@ TEST(TransformationReplaceIdWithSynonymTest, IllegalTransformations) {
   // incoming value for block %72, and %202 does not dominate %72.
   // incoming value for block %72, and %202 does not dominate %72.
   auto synonym_does_not_dominate_use_op_phi =
   auto synonym_does_not_dominate_use_op_phi =
       TransformationReplaceIdWithSynonym(
       TransformationReplaceIdWithSynonym(
-          transformation::MakeIdUseDescriptor(15, SpvOpPhi, 2, 301, 0),
+          MakeIdUseDescriptor(15, MakeInstructionDescriptor(301, SpvOpPhi, 0),
+                              2),
           MakeDataDescriptor(202, {}), 0);
           MakeDataDescriptor(202, {}), 0);
   ASSERT_FALSE(synonym_does_not_dominate_use_op_phi.IsApplicable(context.get(),
   ASSERT_FALSE(synonym_does_not_dominate_use_op_phi.IsApplicable(context.get(),
                                                                  fact_manager));
                                                                  fact_manager));
 
 
   // %200 is not a synonym for %84
   // %200 is not a synonym for %84
   auto id_in_use_is_not_synonymous = TransformationReplaceIdWithSynonym(
   auto id_in_use_is_not_synonymous = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(84, SpvOpSGreaterThan, 0, 67, 0),
+      MakeIdUseDescriptor(
+          84, MakeInstructionDescriptor(67, SpvOpSGreaterThan, 0), 0),
       MakeDataDescriptor(200, {}), 0);
       MakeDataDescriptor(200, {}), 0);
   ASSERT_FALSE(
   ASSERT_FALSE(
       id_in_use_is_not_synonymous.IsApplicable(context.get(), fact_manager));
       id_in_use_is_not_synonymous.IsApplicable(context.get(), fact_manager));
 
 
   // %86 is not a synonym for anything (and in particular not for %74)
   // %86 is not a synonym for anything (and in particular not for %74)
   auto id_has_no_synonyms = TransformationReplaceIdWithSynonym(
   auto id_has_no_synonyms = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(86, SpvOpPhi, 2, 84, 0),
+      MakeIdUseDescriptor(86, MakeInstructionDescriptor(84, SpvOpPhi, 0), 2),
       MakeDataDescriptor(74, {}), 0);
       MakeDataDescriptor(74, {}), 0);
   ASSERT_FALSE(id_has_no_synonyms.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(id_has_no_synonyms.IsApplicable(context.get(), fact_manager));
 
 
   // This would lead to %207 = 'OpCopyObject %type %207' if it were allowed
   // This would lead to %207 = 'OpCopyObject %type %207' if it were allowed
   auto synonym_use_is_in_synonym_definition =
   auto synonym_use_is_in_synonym_definition =
       TransformationReplaceIdWithSynonym(
       TransformationReplaceIdWithSynonym(
-          transformation::MakeIdUseDescriptor(84, SpvOpCopyObject, 0, 207, 0),
+          MakeIdUseDescriptor(
+              84, MakeInstructionDescriptor(207, SpvOpCopyObject, 0), 0),
           MakeDataDescriptor(207, {}), 0);
           MakeDataDescriptor(207, {}), 0);
   ASSERT_FALSE(synonym_use_is_in_synonym_definition.IsApplicable(context.get(),
   ASSERT_FALSE(synonym_use_is_in_synonym_definition.IsApplicable(context.get(),
                                                                  fact_manager));
                                                                  fact_manager));
@@ -260,14 +268,16 @@ TEST(TransformationReplaceIdWithSynonymTest, IllegalTransformations) {
   // The id use descriptor does not lead to a use (%84 is not used in the
   // The id use descriptor does not lead to a use (%84 is not used in the
   // definition of %207)
   // definition of %207)
   auto bad_id_use_descriptor = TransformationReplaceIdWithSynonym(
   auto bad_id_use_descriptor = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(84, SpvOpCopyObject, 0, 200, 0),
+      MakeIdUseDescriptor(
+          84, MakeInstructionDescriptor(200, SpvOpCopyObject, 0), 0),
       MakeDataDescriptor(207, {}), 0);
       MakeDataDescriptor(207, {}), 0);
   ASSERT_FALSE(bad_id_use_descriptor.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(bad_id_use_descriptor.IsApplicable(context.get(), fact_manager));
 
 
   // This replacement would lead to an access chain into a struct using a
   // This replacement would lead to an access chain into a struct using a
   // non-constant index.
   // non-constant index.
   auto bad_access_chain = TransformationReplaceIdWithSynonym(
   auto bad_access_chain = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(12, SpvOpAccessChain, 1, 14, 0),
+      MakeIdUseDescriptor(
+          12, MakeInstructionDescriptor(14, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(209, {}), 0);
       MakeDataDescriptor(209, {}), 0);
   ASSERT_FALSE(bad_access_chain.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(bad_access_chain.IsApplicable(context.get(), fact_manager));
 }
 }
@@ -283,7 +293,7 @@ TEST(TransformationReplaceIdWithSynonymTest, LegalTransformations) {
   SetUpIdSynonyms(&fact_manager, context.get());
   SetUpIdSynonyms(&fact_manager, context.get());
 
 
   auto global_constant_synonym = TransformationReplaceIdWithSynonym(
   auto global_constant_synonym = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(19, SpvOpStore, 1, 47, 0),
+      MakeIdUseDescriptor(19, MakeInstructionDescriptor(47, SpvOpStore, 0), 1),
       MakeDataDescriptor(210, {}), 0);
       MakeDataDescriptor(210, {}), 0);
   ASSERT_TRUE(
   ASSERT_TRUE(
       global_constant_synonym.IsApplicable(context.get(), fact_manager));
       global_constant_synonym.IsApplicable(context.get(), fact_manager));
@@ -291,7 +301,8 @@ TEST(TransformationReplaceIdWithSynonymTest, LegalTransformations) {
   ASSERT_TRUE(IsValid(env, context.get()));
   ASSERT_TRUE(IsValid(env, context.get()));
 
 
   auto replace_vector_access_chain_index = TransformationReplaceIdWithSynonym(
   auto replace_vector_access_chain_index = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(54, SpvOpAccessChain, 1, 55, 0),
+      MakeIdUseDescriptor(
+          54, MakeInstructionDescriptor(55, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(204, {}), 0);
       MakeDataDescriptor(204, {}), 0);
   ASSERT_TRUE(replace_vector_access_chain_index.IsApplicable(context.get(),
   ASSERT_TRUE(replace_vector_access_chain_index.IsApplicable(context.get(),
                                                              fact_manager));
                                                              fact_manager));
@@ -301,21 +312,22 @@ TEST(TransformationReplaceIdWithSynonymTest, LegalTransformations) {
   // This is an interesting case because it replaces something that is being
   // This is an interesting case because it replaces something that is being
   // copied with something that is already a synonym.
   // copied with something that is already a synonym.
   auto regular_replacement = TransformationReplaceIdWithSynonym(
   auto regular_replacement = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(15, SpvOpCopyObject, 0, 202, 0),
+      MakeIdUseDescriptor(
+          15, MakeInstructionDescriptor(202, SpvOpCopyObject, 0), 0),
       MakeDataDescriptor(201, {}), 0);
       MakeDataDescriptor(201, {}), 0);
   ASSERT_TRUE(regular_replacement.IsApplicable(context.get(), fact_manager));
   ASSERT_TRUE(regular_replacement.IsApplicable(context.get(), fact_manager));
   regular_replacement.Apply(context.get(), &fact_manager);
   regular_replacement.Apply(context.get(), &fact_manager);
   ASSERT_TRUE(IsValid(env, context.get()));
   ASSERT_TRUE(IsValid(env, context.get()));
 
 
   auto regular_replacement2 = TransformationReplaceIdWithSynonym(
   auto regular_replacement2 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(55, SpvOpStore, 0, 203, 0),
+      MakeIdUseDescriptor(55, MakeInstructionDescriptor(203, SpvOpStore, 0), 0),
       MakeDataDescriptor(203, {}), 0);
       MakeDataDescriptor(203, {}), 0);
   ASSERT_TRUE(regular_replacement2.IsApplicable(context.get(), fact_manager));
   ASSERT_TRUE(regular_replacement2.IsApplicable(context.get(), fact_manager));
   regular_replacement2.Apply(context.get(), &fact_manager);
   regular_replacement2.Apply(context.get(), &fact_manager);
   ASSERT_TRUE(IsValid(env, context.get()));
   ASSERT_TRUE(IsValid(env, context.get()));
 
 
   auto good_op_phi = TransformationReplaceIdWithSynonym(
   auto good_op_phi = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(74, SpvOpPhi, 2, 86, 0),
+      MakeIdUseDescriptor(74, MakeInstructionDescriptor(86, SpvOpPhi, 0), 2),
       MakeDataDescriptor(205, {}), 0);
       MakeDataDescriptor(205, {}), 0);
   ASSERT_TRUE(good_op_phi.IsApplicable(context.get(), fact_manager));
   ASSERT_TRUE(good_op_phi.IsApplicable(context.get(), fact_manager));
   good_op_phi.Apply(context.get(), &fact_manager);
   good_op_phi.Apply(context.get(), &fact_manager);
@@ -493,13 +505,13 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfVariables) {
 
 
   FactManager fact_manager;
   FactManager fact_manager;
 
 
-  fact_manager.AddFact(MakeFact(10, 100), context.get());
-  fact_manager.AddFact(MakeFact(8, 101), context.get());
+  fact_manager.AddFact(MakeSynonymFact(10, 100), context.get());
+  fact_manager.AddFact(MakeSynonymFact(8, 101), context.get());
 
 
   // Replace %10 with %100 in:
   // Replace %10 with %100 in:
   // %11 = OpLoad %6 %10
   // %11 = OpLoad %6 %10
   auto replacement1 = TransformationReplaceIdWithSynonym(
   auto replacement1 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(10, SpvOpLoad, 0, 11, 0),
+      MakeIdUseDescriptor(10, MakeInstructionDescriptor(11, SpvOpLoad, 0), 0),
       MakeDataDescriptor(100, {}), 0);
       MakeDataDescriptor(100, {}), 0);
   ASSERT_TRUE(replacement1.IsApplicable(context.get(), fact_manager));
   ASSERT_TRUE(replacement1.IsApplicable(context.get(), fact_manager));
   replacement1.Apply(context.get(), &fact_manager);
   replacement1.Apply(context.get(), &fact_manager);
@@ -508,7 +520,7 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfVariables) {
   // Replace %8 with %101 in:
   // Replace %8 with %101 in:
   // OpStore %8 %11
   // OpStore %8 %11
   auto replacement2 = TransformationReplaceIdWithSynonym(
   auto replacement2 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(8, SpvOpStore, 0, 11, 0),
+      MakeIdUseDescriptor(8, MakeInstructionDescriptor(11, SpvOpStore, 0), 0),
       MakeDataDescriptor(101, {}), 0);
       MakeDataDescriptor(101, {}), 0);
   ASSERT_TRUE(replacement2.IsApplicable(context.get(), fact_manager));
   ASSERT_TRUE(replacement2.IsApplicable(context.get(), fact_manager));
   replacement2.Apply(context.get(), &fact_manager);
   replacement2.Apply(context.get(), &fact_manager);
@@ -517,7 +529,7 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfVariables) {
   // Replace %8 with %101 in:
   // Replace %8 with %101 in:
   // %12 = OpLoad %6 %8
   // %12 = OpLoad %6 %8
   auto replacement3 = TransformationReplaceIdWithSynonym(
   auto replacement3 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(8, SpvOpLoad, 0, 12, 0),
+      MakeIdUseDescriptor(8, MakeInstructionDescriptor(12, SpvOpLoad, 0), 0),
       MakeDataDescriptor(101, {}), 0);
       MakeDataDescriptor(101, {}), 0);
   ASSERT_TRUE(replacement3.IsApplicable(context.get(), fact_manager));
   ASSERT_TRUE(replacement3.IsApplicable(context.get(), fact_manager));
   replacement3.Apply(context.get(), &fact_manager);
   replacement3.Apply(context.get(), &fact_manager);
@@ -526,7 +538,7 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfVariables) {
   // Replace %10 with %100 in:
   // Replace %10 with %100 in:
   // OpStore %10 %12
   // OpStore %10 %12
   auto replacement4 = TransformationReplaceIdWithSynonym(
   auto replacement4 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(10, SpvOpStore, 0, 12, 0),
+      MakeIdUseDescriptor(10, MakeInstructionDescriptor(12, SpvOpStore, 0), 0),
       MakeDataDescriptor(100, {}), 0);
       MakeDataDescriptor(100, {}), 0);
   ASSERT_TRUE(replacement4.IsApplicable(context.get(), fact_manager));
   ASSERT_TRUE(replacement4.IsApplicable(context.get(), fact_manager));
   replacement4.Apply(context.get(), &fact_manager);
   replacement4.Apply(context.get(), &fact_manager);
@@ -622,12 +634,13 @@ TEST(TransformationReplaceIdWithSynonymTest,
 
 
   FactManager fact_manager;
   FactManager fact_manager;
 
 
-  fact_manager.AddFact(MakeFact(14, 100), context.get());
+  fact_manager.AddFact(MakeSynonymFact(14, 100), context.get());
 
 
   // Replace %14 with %100 in:
   // Replace %14 with %100 in:
   // %16 = OpFunctionCall %2 %10 %14
   // %16 = OpFunctionCall %2 %10 %14
   auto replacement = TransformationReplaceIdWithSynonym(
   auto replacement = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(14, SpvOpFunctionCall, 1, 16, 0),
+      MakeIdUseDescriptor(
+          14, MakeInstructionDescriptor(16, SpvOpFunctionCall, 0), 1),
       MakeDataDescriptor(100, {}), 0);
       MakeDataDescriptor(100, {}), 0);
   ASSERT_FALSE(replacement.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(replacement.IsApplicable(context.get(), fact_manager));
 }
 }
@@ -785,19 +798,19 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
 
 
   // Add synonym facts corresponding to the OpCopyObject operations that have
   // Add synonym facts corresponding to the OpCopyObject operations that have
   // been applied to all constants in the module.
   // been applied to all constants in the module.
-  fact_manager.AddFact(MakeFact(16, 100), context.get());
-  fact_manager.AddFact(MakeFact(21, 101), context.get());
-  fact_manager.AddFact(MakeFact(17, 102), context.get());
-  fact_manager.AddFact(MakeFact(57, 103), context.get());
-  fact_manager.AddFact(MakeFact(18, 104), context.get());
-  fact_manager.AddFact(MakeFact(40, 105), context.get());
-  fact_manager.AddFact(MakeFact(32, 106), context.get());
-  fact_manager.AddFact(MakeFact(43, 107), context.get());
-  fact_manager.AddFact(MakeFact(55, 108), context.get());
-  fact_manager.AddFact(MakeFact(8, 109), context.get());
-  fact_manager.AddFact(MakeFact(47, 110), context.get());
-  fact_manager.AddFact(MakeFact(28, 111), context.get());
-  fact_manager.AddFact(MakeFact(45, 112), context.get());
+  fact_manager.AddFact(MakeSynonymFact(16, 100), context.get());
+  fact_manager.AddFact(MakeSynonymFact(21, 101), context.get());
+  fact_manager.AddFact(MakeSynonymFact(17, 102), context.get());
+  fact_manager.AddFact(MakeSynonymFact(57, 103), context.get());
+  fact_manager.AddFact(MakeSynonymFact(18, 104), context.get());
+  fact_manager.AddFact(MakeSynonymFact(40, 105), context.get());
+  fact_manager.AddFact(MakeSynonymFact(32, 106), context.get());
+  fact_manager.AddFact(MakeSynonymFact(43, 107), context.get());
+  fact_manager.AddFact(MakeSynonymFact(55, 108), context.get());
+  fact_manager.AddFact(MakeSynonymFact(8, 109), context.get());
+  fact_manager.AddFact(MakeSynonymFact(47, 110), context.get());
+  fact_manager.AddFact(MakeSynonymFact(28, 111), context.get());
+  fact_manager.AddFact(MakeSynonymFact(45, 112), context.get());
 
 
   // Replacements of the form %16 -> %100
   // Replacements of the form %16 -> %100
 
 
@@ -805,7 +818,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to d.*a*[2]
   // Corresponds to d.*a*[2]
   // The index %16 used for a cannot be replaced
   // The index %16 used for a cannot be replaced
   auto replacement1 = TransformationReplaceIdWithSynonym(
   auto replacement1 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 1, 20, 0),
+      MakeIdUseDescriptor(
+          16, MakeInstructionDescriptor(20, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(100, {}), 0);
       MakeDataDescriptor(100, {}), 0);
   ASSERT_FALSE(replacement1.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(replacement1.IsApplicable(context.get(), fact_manager));
 
 
@@ -813,7 +827,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to h.*f*
   // Corresponds to h.*f*
   // The index %16 used for f cannot be replaced
   // The index %16 used for f cannot be replaced
   auto replacement2 = TransformationReplaceIdWithSynonym(
   auto replacement2 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 1, 39, 0),
+      MakeIdUseDescriptor(
+          16, MakeInstructionDescriptor(39, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(100, {}), 0);
       MakeDataDescriptor(100, {}), 0);
   ASSERT_FALSE(replacement2.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(replacement2.IsApplicable(context.get(), fact_manager));
 
 
@@ -821,7 +836,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to h.g.*a*[1]
   // Corresponds to h.g.*a*[1]
   // The index %16 used for a cannot be replaced
   // The index %16 used for a cannot be replaced
   auto replacement3 = TransformationReplaceIdWithSynonym(
   auto replacement3 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 2, 41, 0),
+      MakeIdUseDescriptor(
+          16, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 2),
       MakeDataDescriptor(100, {}), 0);
       MakeDataDescriptor(100, {}), 0);
   ASSERT_FALSE(replacement3.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(replacement3.IsApplicable(context.get(), fact_manager));
 
 
@@ -829,7 +845,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to i[*0*].f
   // Corresponds to i[*0*].f
   // The index %16 used for 0 *can* be replaced
   // The index %16 used for 0 *can* be replaced
   auto replacement4 = TransformationReplaceIdWithSynonym(
   auto replacement4 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 1, 52, 0),
+      MakeIdUseDescriptor(
+          16, MakeInstructionDescriptor(52, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(100, {}), 0);
       MakeDataDescriptor(100, {}), 0);
   ASSERT_TRUE(replacement4.IsApplicable(context.get(), fact_manager));
   ASSERT_TRUE(replacement4.IsApplicable(context.get(), fact_manager));
   replacement4.Apply(context.get(), &fact_manager);
   replacement4.Apply(context.get(), &fact_manager);
@@ -839,7 +856,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to i[0].*f*
   // Corresponds to i[0].*f*
   // The index %16 used for f cannot be replaced
   // The index %16 used for f cannot be replaced
   auto replacement5 = TransformationReplaceIdWithSynonym(
   auto replacement5 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 2, 52, 0),
+      MakeIdUseDescriptor(
+          16, MakeInstructionDescriptor(52, SpvOpAccessChain, 0), 2),
       MakeDataDescriptor(100, {}), 0);
       MakeDataDescriptor(100, {}), 0);
   ASSERT_FALSE(replacement5.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(replacement5.IsApplicable(context.get(), fact_manager));
 
 
@@ -847,7 +865,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to i[1].g.*a*[0]
   // Corresponds to i[1].g.*a*[0]
   // The index %16 used for a cannot be replaced
   // The index %16 used for a cannot be replaced
   auto replacement6 = TransformationReplaceIdWithSynonym(
   auto replacement6 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 3, 53, 0),
+      MakeIdUseDescriptor(
+          16, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 3),
       MakeDataDescriptor(100, {}), 0);
       MakeDataDescriptor(100, {}), 0);
   ASSERT_FALSE(replacement6.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(replacement6.IsApplicable(context.get(), fact_manager));
 
 
@@ -855,7 +874,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to i[1].g.a[*0*]
   // Corresponds to i[1].g.a[*0*]
   // The index %16 used for 0 *can* be replaced
   // The index %16 used for 0 *can* be replaced
   auto replacement7 = TransformationReplaceIdWithSynonym(
   auto replacement7 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 4, 53, 0),
+      MakeIdUseDescriptor(
+          16, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 4),
       MakeDataDescriptor(100, {}), 0);
       MakeDataDescriptor(100, {}), 0);
   ASSERT_TRUE(replacement7.IsApplicable(context.get(), fact_manager));
   ASSERT_TRUE(replacement7.IsApplicable(context.get(), fact_manager));
   replacement7.Apply(context.get(), &fact_manager);
   replacement7.Apply(context.get(), &fact_manager);
@@ -867,7 +887,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to d.*b*[3]
   // Corresponds to d.*b*[3]
   // The index %24 used for b cannot be replaced
   // The index %24 used for b cannot be replaced
   auto replacement8 = TransformationReplaceIdWithSynonym(
   auto replacement8 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 24, 0),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(24, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(101, {}), 0);
       MakeDataDescriptor(101, {}), 0);
   ASSERT_FALSE(replacement8.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(replacement8.IsApplicable(context.get(), fact_manager));
 
 
@@ -875,7 +896,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to h.*g*.a[1]
   // Corresponds to h.*g*.a[1]
   // The index %24 used for g cannot be replaced
   // The index %24 used for g cannot be replaced
   auto replacement9 = TransformationReplaceIdWithSynonym(
   auto replacement9 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 41, 0),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(101, {}), 0);
       MakeDataDescriptor(101, {}), 0);
   ASSERT_FALSE(replacement9.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(replacement9.IsApplicable(context.get(), fact_manager));
 
 
@@ -883,7 +905,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to h.g.a[*1*]
   // Corresponds to h.g.a[*1*]
   // The index %24 used for 1 *can* be replaced
   // The index %24 used for 1 *can* be replaced
   auto replacement10 = TransformationReplaceIdWithSynonym(
   auto replacement10 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 3, 41, 0),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 3),
       MakeDataDescriptor(101, {}), 0);
       MakeDataDescriptor(101, {}), 0);
   ASSERT_TRUE(replacement10.IsApplicable(context.get(), fact_manager));
   ASSERT_TRUE(replacement10.IsApplicable(context.get(), fact_manager));
   replacement10.Apply(context.get(), &fact_manager);
   replacement10.Apply(context.get(), &fact_manager);
@@ -893,7 +916,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to h.*g*.b[0]
   // Corresponds to h.*g*.b[0]
   // The index %24 used for g cannot be replaced
   // The index %24 used for g cannot be replaced
   auto replacement11 = TransformationReplaceIdWithSynonym(
   auto replacement11 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 44, 0),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(101, {}), 0);
       MakeDataDescriptor(101, {}), 0);
   ASSERT_FALSE(replacement11.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(replacement11.IsApplicable(context.get(), fact_manager));
 
 
@@ -901,7 +925,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to h.g.*b*[0]
   // Corresponds to h.g.*b*[0]
   // The index %24 used for b cannot be replaced
   // The index %24 used for b cannot be replaced
   auto replacement12 = TransformationReplaceIdWithSynonym(
   auto replacement12 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 2, 44, 0),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 2),
       MakeDataDescriptor(101, {}), 0);
       MakeDataDescriptor(101, {}), 0);
   ASSERT_FALSE(replacement12.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(replacement12.IsApplicable(context.get(), fact_manager));
 
 
@@ -909,7 +934,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to h.*g*.c
   // Corresponds to h.*g*.c
   // The index %24 used for g cannot be replaced
   // The index %24 used for g cannot be replaced
   auto replacement13 = TransformationReplaceIdWithSynonym(
   auto replacement13 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 46, 0),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(101, {}), 0);
       MakeDataDescriptor(101, {}), 0);
   ASSERT_FALSE(replacement13.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(replacement13.IsApplicable(context.get(), fact_manager));
 
 
@@ -917,7 +943,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to i[*1*].g.a[0]
   // Corresponds to i[*1*].g.a[0]
   // The index %24 used for 1 *can* be replaced
   // The index %24 used for 1 *can* be replaced
   auto replacement14 = TransformationReplaceIdWithSynonym(
   auto replacement14 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 53, 0),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(101, {}), 0);
       MakeDataDescriptor(101, {}), 0);
   ASSERT_TRUE(replacement14.IsApplicable(context.get(), fact_manager));
   ASSERT_TRUE(replacement14.IsApplicable(context.get(), fact_manager));
   replacement14.Apply(context.get(), &fact_manager);
   replacement14.Apply(context.get(), &fact_manager);
@@ -927,7 +954,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to i[1].*g*.a[0]
   // Corresponds to i[1].*g*.a[0]
   // The index %24 used for g cannot be replaced
   // The index %24 used for g cannot be replaced
   auto replacement15 = TransformationReplaceIdWithSynonym(
   auto replacement15 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 2, 53, 0),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 2),
       MakeDataDescriptor(101, {}), 0);
       MakeDataDescriptor(101, {}), 0);
   ASSERT_FALSE(replacement15.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(replacement15.IsApplicable(context.get(), fact_manager));
 
 
@@ -935,7 +963,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to i[2].*g*.b[1]
   // Corresponds to i[2].*g*.b[1]
   // The index %24 used for g cannot be replaced
   // The index %24 used for g cannot be replaced
   auto replacement16 = TransformationReplaceIdWithSynonym(
   auto replacement16 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 2, 56, 0),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 2),
       MakeDataDescriptor(101, {}), 0);
       MakeDataDescriptor(101, {}), 0);
   ASSERT_FALSE(replacement16.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(replacement16.IsApplicable(context.get(), fact_manager));
 
 
@@ -943,7 +972,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to i[2].g.*b*[1]
   // Corresponds to i[2].g.*b*[1]
   // The index %24 used for b cannot be replaced
   // The index %24 used for b cannot be replaced
   auto replacement17 = TransformationReplaceIdWithSynonym(
   auto replacement17 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 3, 56, 0),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 3),
       MakeDataDescriptor(101, {}), 0);
       MakeDataDescriptor(101, {}), 0);
   ASSERT_FALSE(replacement17.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(replacement17.IsApplicable(context.get(), fact_manager));
 
 
@@ -951,7 +981,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to i[3].*g*.c
   // Corresponds to i[3].*g*.c
   // The index %24 used for g cannot be replaced
   // The index %24 used for g cannot be replaced
   auto replacement18 = TransformationReplaceIdWithSynonym(
   auto replacement18 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 2, 58, 0),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(58, SpvOpAccessChain, 0), 2),
       MakeDataDescriptor(101, {}), 0);
       MakeDataDescriptor(101, {}), 0);
   ASSERT_FALSE(replacement18.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(replacement18.IsApplicable(context.get(), fact_manager));
 
 
@@ -961,7 +992,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to d.a[*2*]
   // Corresponds to d.a[*2*]
   // The index %17 used for 2 *can* be replaced
   // The index %17 used for 2 *can* be replaced
   auto replacement19 = TransformationReplaceIdWithSynonym(
   auto replacement19 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 2, 20, 0),
+      MakeIdUseDescriptor(
+          17, MakeInstructionDescriptor(20, SpvOpAccessChain, 0), 2),
       MakeDataDescriptor(102, {}), 0);
       MakeDataDescriptor(102, {}), 0);
   ASSERT_TRUE(replacement19.IsApplicable(context.get(), fact_manager));
   ASSERT_TRUE(replacement19.IsApplicable(context.get(), fact_manager));
   replacement19.Apply(context.get(), &fact_manager);
   replacement19.Apply(context.get(), &fact_manager);
@@ -971,7 +1003,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to d.c
   // Corresponds to d.c
   // The index %17 used for c cannot be replaced
   // The index %17 used for c cannot be replaced
   auto replacement20 = TransformationReplaceIdWithSynonym(
   auto replacement20 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 1, 27, 0),
+      MakeIdUseDescriptor(
+          17, MakeInstructionDescriptor(27, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(102, {}), 0);
       MakeDataDescriptor(102, {}), 0);
   ASSERT_FALSE(replacement20.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(replacement20.IsApplicable(context.get(), fact_manager));
 
 
@@ -979,7 +1012,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to h.g.*c*
   // Corresponds to h.g.*c*
   // The index %17 used for c cannot be replaced
   // The index %17 used for c cannot be replaced
   auto replacement21 = TransformationReplaceIdWithSynonym(
   auto replacement21 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 2, 46, 0),
+      MakeIdUseDescriptor(
+          17, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 2),
       MakeDataDescriptor(102, {}), 0);
       MakeDataDescriptor(102, {}), 0);
   ASSERT_FALSE(replacement21.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(replacement21.IsApplicable(context.get(), fact_manager));
 
 
@@ -987,7 +1021,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to i[*2*].g.b[1]
   // Corresponds to i[*2*].g.b[1]
   // The index %17 used for 2 *can* be replaced
   // The index %17 used for 2 *can* be replaced
   auto replacement22 = TransformationReplaceIdWithSynonym(
   auto replacement22 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 1, 56, 0),
+      MakeIdUseDescriptor(
+          17, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(102, {}), 0);
       MakeDataDescriptor(102, {}), 0);
   ASSERT_TRUE(replacement22.IsApplicable(context.get(), fact_manager));
   ASSERT_TRUE(replacement22.IsApplicable(context.get(), fact_manager));
   replacement22.Apply(context.get(), &fact_manager);
   replacement22.Apply(context.get(), &fact_manager);
@@ -997,7 +1032,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to i[3].g.*c*
   // Corresponds to i[3].g.*c*
   // The index %17 used for c cannot be replaced
   // The index %17 used for c cannot be replaced
   auto replacement23 = TransformationReplaceIdWithSynonym(
   auto replacement23 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 3, 58, 0),
+      MakeIdUseDescriptor(
+          17, MakeInstructionDescriptor(58, SpvOpAccessChain, 0), 3),
       MakeDataDescriptor(102, {}), 0);
       MakeDataDescriptor(102, {}), 0);
   ASSERT_FALSE(replacement23.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(replacement23.IsApplicable(context.get(), fact_manager));
 
 
@@ -1007,7 +1043,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to i[*3*].g.c
   // Corresponds to i[*3*].g.c
   // The index %57 used for 3 *can* be replaced
   // The index %57 used for 3 *can* be replaced
   auto replacement24 = TransformationReplaceIdWithSynonym(
   auto replacement24 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(57, SpvOpAccessChain, 1, 58, 0),
+      MakeIdUseDescriptor(
+          57, MakeInstructionDescriptor(58, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(103, {}), 0);
       MakeDataDescriptor(103, {}), 0);
   ASSERT_TRUE(replacement24.IsApplicable(context.get(), fact_manager));
   ASSERT_TRUE(replacement24.IsApplicable(context.get(), fact_manager));
   replacement24.Apply(context.get(), &fact_manager);
   replacement24.Apply(context.get(), &fact_manager);
@@ -1019,7 +1056,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to e[*17*]
   // Corresponds to e[*17*]
   // The index %32 used for 17 *can* be replaced
   // The index %32 used for 17 *can* be replaced
   auto replacement25 = TransformationReplaceIdWithSynonym(
   auto replacement25 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(32, SpvOpAccessChain, 1, 34, 0),
+      MakeIdUseDescriptor(
+          32, MakeInstructionDescriptor(34, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(106, {}), 0);
       MakeDataDescriptor(106, {}), 0);
   ASSERT_TRUE(replacement25.IsApplicable(context.get(), fact_manager));
   ASSERT_TRUE(replacement25.IsApplicable(context.get(), fact_manager));
   replacement25.Apply(context.get(), &fact_manager);
   replacement25.Apply(context.get(), &fact_manager);
@@ -1031,7 +1069,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to h.g.b[*0*]
   // Corresponds to h.g.b[*0*]
   // The index %43 used for 0 *can* be replaced
   // The index %43 used for 0 *can* be replaced
   auto replacement26 = TransformationReplaceIdWithSynonym(
   auto replacement26 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(43, SpvOpAccessChain, 3, 44, 0),
+      MakeIdUseDescriptor(
+          43, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 3),
       MakeDataDescriptor(107, {}), 0);
       MakeDataDescriptor(107, {}), 0);
   ASSERT_TRUE(replacement26.IsApplicable(context.get(), fact_manager));
   ASSERT_TRUE(replacement26.IsApplicable(context.get(), fact_manager));
   replacement26.Apply(context.get(), &fact_manager);
   replacement26.Apply(context.get(), &fact_manager);
@@ -1043,7 +1082,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to i[2].g.b[*1*]
   // Corresponds to i[2].g.b[*1*]
   // The index %55 used for 1 *can* be replaced
   // The index %55 used for 1 *can* be replaced
   auto replacement27 = TransformationReplaceIdWithSynonym(
   auto replacement27 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(55, SpvOpAccessChain, 4, 56, 0),
+      MakeIdUseDescriptor(
+          55, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 4),
       MakeDataDescriptor(108, {}), 0);
       MakeDataDescriptor(108, {}), 0);
   ASSERT_TRUE(replacement27.IsApplicable(context.get(), fact_manager));
   ASSERT_TRUE(replacement27.IsApplicable(context.get(), fact_manager));
   replacement27.Apply(context.get(), &fact_manager);
   replacement27.Apply(context.get(), &fact_manager);
@@ -1055,7 +1095,8 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   // Corresponds to d.b[*3*]
   // Corresponds to d.b[*3*]
   // The index %8 used for 3 *can* be replaced
   // The index %8 used for 3 *can* be replaced
   auto replacement28 = TransformationReplaceIdWithSynonym(
   auto replacement28 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(8, SpvOpAccessChain, 2, 24, 0),
+      MakeIdUseDescriptor(8, MakeInstructionDescriptor(24, SpvOpAccessChain, 0),
+                          2),
       MakeDataDescriptor(109, {}), 0);
       MakeDataDescriptor(109, {}), 0);
   ASSERT_TRUE(replacement28.IsApplicable(context.get(), fact_manager));
   ASSERT_TRUE(replacement28.IsApplicable(context.get(), fact_manager));
   replacement28.Apply(context.get(), &fact_manager);
   replacement28.Apply(context.get(), &fact_manager);
@@ -1171,6 +1212,983 @@ TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
   ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
 }
 }
 
 
+TEST(TransformationReplaceIdWithSynonymTest, ArrayCompositeSynonyms) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %11 "A"
+               OpName %20 "B"
+               OpName %31 "g"
+               OpName %35 "h"
+               OpDecorate %11 RelaxedPrecision
+               OpDecorate %22 RelaxedPrecision
+               OpDecorate %27 RelaxedPrecision
+               OpDecorate %35 RelaxedPrecision
+               OpDecorate %36 RelaxedPrecision
+               OpDecorate %40 RelaxedPrecision
+               OpDecorate %41 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeInt 32 0
+          %8 = OpConstant %7 3
+          %9 = OpTypeArray %6 %8
+         %10 = OpTypePointer Function %9
+         %12 = OpConstant %6 0
+         %13 = OpConstant %6 3
+         %14 = OpTypePointer Function %6
+         %16 = OpTypeFloat 32
+         %17 = OpConstant %7 4
+         %18 = OpTypeArray %16 %17
+         %19 = OpTypePointer Function %18
+         %24 = OpTypePointer Function %16
+         %28 = OpConstant %16 42
+         %30 = OpConstant %6 2
+         %34 = OpConstant %6 1
+         %38 = OpConstant %6 42
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+         %20 = OpVariable %19 Function
+         %31 = OpVariable %24 Function
+         %35 = OpVariable %14 Function
+         %15 = OpAccessChain %14 %11 %12
+         %21 = OpAccessChain %14 %11 %12
+         %22 = OpLoad %6 %21
+        %100 = OpCompositeConstruct %9 %12 %13 %22
+               OpStore %15 %13
+         %23 = OpConvertSToF %16 %22
+         %25 = OpAccessChain %24 %20 %12
+               OpStore %25 %23
+         %26 = OpAccessChain %14 %11 %12
+         %27 = OpLoad %6 %26
+         %29 = OpAccessChain %24 %20 %27
+               OpStore %29 %28
+         %32 = OpLoad %16 %31
+        %101 = OpCompositeConstruct %18 %28 %23 %32 %23
+         %50 = OpCopyObject %16 %23
+         %51 = OpCopyObject %16 %23
+         %33 = OpAccessChain %24 %20 %30
+               OpStore %33 %28
+               OpStore %33 %32
+         %36 = OpLoad %6 %35
+         %37 = OpAccessChain %14 %11 %34
+               OpStore %37 %36
+         %39 = OpAccessChain %14 %11 %12
+         %40 = OpLoad %6 %39
+         %41 = OpIAdd %6 %38 %40
+         %42 = OpAccessChain %14 %11 %30
+               OpStore %42 %41
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  fact_manager.AddFact(MakeSynonymFact(12, 100, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(13, 100, {1}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(22, 100, {2}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(28, 101, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(23, 101, {1}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(32, 101, {2}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(23, 101, {3}), context.get());
+
+  // Replace %12 with %100[0] in '%25 = OpAccessChain %24 %20 %12'
+  auto good_replacement_1 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          12, MakeInstructionDescriptor(25, SpvOpAccessChain, 0), 1),
+      MakeDataDescriptor(100, {0}), 102);
+  // Bad: id already in use
+  auto bad_replacement_1 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          12, MakeInstructionDescriptor(25, SpvOpAccessChain, 0), 1),
+      MakeDataDescriptor(100, {0}), 25);
+  ASSERT_TRUE(good_replacement_1.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(bad_replacement_1.IsApplicable(context.get(), fact_manager));
+  good_replacement_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %13 with %100[1] in 'OpStore %15 %13'
+  auto good_replacement_2 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(13, MakeInstructionDescriptor(100, SpvOpStore, 0), 1),
+      MakeDataDescriptor(100, {1}), 103);
+  // Bad: too many indices
+  auto bad_replacement_2 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(13, MakeInstructionDescriptor(100, SpvOpStore, 0), 1),
+      MakeDataDescriptor(100, {1, 0}), 103);
+  ASSERT_TRUE(good_replacement_2.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(bad_replacement_2.IsApplicable(context.get(), fact_manager));
+  good_replacement_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %22 with %100[2] in '%23 = OpConvertSToF %16 %22'
+  auto good_replacement_3 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          22, MakeInstructionDescriptor(23, SpvOpConvertSToF, 0), 0),
+      MakeDataDescriptor(100, {2}), 104);
+  // Bad: wrong input operand index
+  auto bad_replacement_3 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          22, MakeInstructionDescriptor(23, SpvOpConvertSToF, 0), 1),
+      MakeDataDescriptor(100, {2}), 104);
+  ASSERT_TRUE(good_replacement_3.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(bad_replacement_3.IsApplicable(context.get(), fact_manager));
+  good_replacement_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %28 with %101[0] in 'OpStore %33 %28'
+  auto good_replacement_4 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(28, MakeInstructionDescriptor(33, SpvOpStore, 0), 1),
+      MakeDataDescriptor(101, {0}), 105);
+  // Bad: id use descriptor does not identify an appropriate instruction
+  auto bad_replacement_4 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(28, MakeInstructionDescriptor(33, SpvOpCopyObject, 0),
+                          1),
+      MakeDataDescriptor(101, {0}), 105);
+  ASSERT_TRUE(good_replacement_4.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(bad_replacement_4.IsApplicable(context.get(), fact_manager));
+  good_replacement_4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %23 with %101[1] in '%50 = OpCopyObject %16 %23'
+  auto good_replacement_5 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(23, MakeInstructionDescriptor(50, SpvOpCopyObject, 0),
+                          0),
+      MakeDataDescriptor(101, {1}), 106);
+  // Bad: wrong synonym fact being used
+  auto bad_replacement_5 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(23, MakeInstructionDescriptor(50, SpvOpCopyObject, 0),
+                          0),
+      MakeDataDescriptor(101, {0}), 106);
+  ASSERT_TRUE(good_replacement_5.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(bad_replacement_5.IsApplicable(context.get(), fact_manager));
+  good_replacement_5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %32 with %101[2] in 'OpStore %33 %32'
+  auto good_replacement_6 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(32, MakeInstructionDescriptor(33, SpvOpStore, 1), 1),
+      MakeDataDescriptor(101, {2}), 107);
+  // Bad: id 1001 does not exist
+  auto bad_replacement_6 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(32, MakeInstructionDescriptor(33, SpvOpStore, 1), 1),
+      MakeDataDescriptor(1001, {2}), 107);
+  ASSERT_TRUE(good_replacement_6.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(bad_replacement_6.IsApplicable(context.get(), fact_manager));
+  good_replacement_6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %23 with %101[3] in '%51 = OpCopyObject %16 %23'
+  auto good_replacement_7 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(23, MakeInstructionDescriptor(51, SpvOpCopyObject, 0),
+                          0),
+      MakeDataDescriptor(101, {3}), 108);
+  // Bad: id 0 is invalid
+  auto bad_replacement_7 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(0, MakeInstructionDescriptor(51, SpvOpCopyObject, 0),
+                          0),
+      MakeDataDescriptor(101, {3}), 108);
+  ASSERT_TRUE(good_replacement_7.IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(bad_replacement_7.IsApplicable(context.get(), fact_manager));
+  good_replacement_7.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  const std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %11 "A"
+               OpName %20 "B"
+               OpName %31 "g"
+               OpName %35 "h"
+               OpDecorate %11 RelaxedPrecision
+               OpDecorate %22 RelaxedPrecision
+               OpDecorate %27 RelaxedPrecision
+               OpDecorate %35 RelaxedPrecision
+               OpDecorate %36 RelaxedPrecision
+               OpDecorate %40 RelaxedPrecision
+               OpDecorate %41 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeInt 32 0
+          %8 = OpConstant %7 3
+          %9 = OpTypeArray %6 %8
+         %10 = OpTypePointer Function %9
+         %12 = OpConstant %6 0
+         %13 = OpConstant %6 3
+         %14 = OpTypePointer Function %6
+         %16 = OpTypeFloat 32
+         %17 = OpConstant %7 4
+         %18 = OpTypeArray %16 %17
+         %19 = OpTypePointer Function %18
+         %24 = OpTypePointer Function %16
+         %28 = OpConstant %16 42
+         %30 = OpConstant %6 2
+         %34 = OpConstant %6 1
+         %38 = OpConstant %6 42
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+         %20 = OpVariable %19 Function
+         %31 = OpVariable %24 Function
+         %35 = OpVariable %14 Function
+         %15 = OpAccessChain %14 %11 %12
+         %21 = OpAccessChain %14 %11 %12
+         %22 = OpLoad %6 %21
+        %100 = OpCompositeConstruct %9 %12 %13 %22
+        %103 = OpCompositeExtract %6 %100 1
+               OpStore %15 %103
+        %104 = OpCompositeExtract %6 %100 2
+         %23 = OpConvertSToF %16 %104
+        %102 = OpCompositeExtract %6 %100 0
+         %25 = OpAccessChain %24 %20 %102
+               OpStore %25 %23
+         %26 = OpAccessChain %14 %11 %12
+         %27 = OpLoad %6 %26
+         %29 = OpAccessChain %24 %20 %27
+               OpStore %29 %28
+         %32 = OpLoad %16 %31
+        %101 = OpCompositeConstruct %18 %28 %23 %32 %23
+        %106 = OpCompositeExtract %16 %101 1
+         %50 = OpCopyObject %16 %106
+        %108 = OpCompositeExtract %16 %101 3
+         %51 = OpCopyObject %16 %108
+         %33 = OpAccessChain %24 %20 %30
+        %105 = OpCompositeExtract %16 %101 0
+               OpStore %33 %105
+        %107 = OpCompositeExtract %16 %101 2
+               OpStore %33 %107
+         %36 = OpLoad %6 %35
+         %37 = OpAccessChain %14 %11 %34
+               OpStore %37 %36
+         %39 = OpAccessChain %14 %11 %12
+         %40 = OpLoad %6 %39
+         %41 = OpIAdd %6 %38 %40
+         %42 = OpAccessChain %14 %11 %30
+               OpStore %42 %41
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationReplaceIdWithSynonymTest, MatrixCompositeSynonyms) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %10 "m"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+         %50 = OpUndef %7
+          %8 = OpTypeMatrix %7 3
+          %9 = OpTypePointer Function %8
+         %11 = OpTypeInt 32 1
+         %12 = OpConstant %11 0
+         %13 = OpConstant %6 1
+         %14 = OpConstantComposite %7 %13 %13 %13 %13
+         %15 = OpTypePointer Function %7
+         %17 = OpConstant %11 1
+         %18 = OpConstant %6 2
+         %19 = OpConstantComposite %7 %18 %18 %18 %18
+         %21 = OpConstant %11 2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %10 = OpVariable %9 Function
+         %16 = OpAccessChain %15 %10 %12
+               OpStore %16 %14
+         %20 = OpAccessChain %15 %10 %17
+               OpStore %20 %19
+         %22 = OpAccessChain %15 %10 %12
+         %23 = OpLoad %7 %22
+         %24 = OpAccessChain %15 %10 %17
+         %25 = OpLoad %7 %24
+        %100 = OpCompositeConstruct %8 %23 %25 %50
+         %26 = OpFAdd %7 %23 %25
+         %27 = OpAccessChain %15 %10 %21
+               OpStore %27 %26
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  fact_manager.AddFact(MakeSynonymFact(23, 100, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(25, 100, {1}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(50, 100, {2}), context.get());
+
+  // Replace %23 with %100[0] in '%26 = OpFAdd %7 %23 %25'
+  auto replacement_1 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(23, MakeInstructionDescriptor(26, SpvOpFAdd, 0), 0),
+      MakeDataDescriptor(100, {0}), 101);
+  ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager));
+  replacement_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %25 with %100[1] in '%26 = OpFAdd %7 %23 %25'
+  auto replacement_2 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(25, MakeInstructionDescriptor(26, SpvOpFAdd, 0), 1),
+      MakeDataDescriptor(100, {1}), 102);
+  ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager));
+  replacement_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  const std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %10 "m"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypeVector %6 4
+         %50 = OpUndef %7
+          %8 = OpTypeMatrix %7 3
+          %9 = OpTypePointer Function %8
+         %11 = OpTypeInt 32 1
+         %12 = OpConstant %11 0
+         %13 = OpConstant %6 1
+         %14 = OpConstantComposite %7 %13 %13 %13 %13
+         %15 = OpTypePointer Function %7
+         %17 = OpConstant %11 1
+         %18 = OpConstant %6 2
+         %19 = OpConstantComposite %7 %18 %18 %18 %18
+         %21 = OpConstant %11 2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %10 = OpVariable %9 Function
+         %16 = OpAccessChain %15 %10 %12
+               OpStore %16 %14
+         %20 = OpAccessChain %15 %10 %17
+               OpStore %20 %19
+         %22 = OpAccessChain %15 %10 %12
+         %23 = OpLoad %7 %22
+         %24 = OpAccessChain %15 %10 %17
+         %25 = OpLoad %7 %24
+        %100 = OpCompositeConstruct %8 %23 %25 %50
+        %101 = OpCompositeExtract %7 %100 0
+        %102 = OpCompositeExtract %7 %100 1
+         %26 = OpFAdd %7 %101 %102
+         %27 = OpAccessChain %15 %10 %21
+               OpStore %27 %26
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationReplaceIdWithSynonymTest, StructCompositeSynonyms) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %9 "Inner"
+               OpMemberName %9 0 "a"
+               OpMemberName %9 1 "b"
+               OpName %11 "i1"
+               OpName %17 "i2"
+               OpName %31 "Point"
+               OpMemberName %31 0 "x"
+               OpMemberName %31 1 "y"
+               OpMemberName %31 2 "z"
+               OpName %32 "Outer"
+               OpMemberName %32 0 "c"
+               OpMemberName %32 1 "d"
+               OpName %34 "o1"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeFloat 32
+          %8 = OpTypeVector %7 2
+          %9 = OpTypeStruct %6 %8
+         %10 = OpTypePointer Function %9
+         %12 = OpConstant %6 1
+         %13 = OpConstant %7 2
+         %14 = OpConstant %7 3
+         %15 = OpConstantComposite %8 %13 %14
+         %16 = OpConstantComposite %9 %12 %15
+         %18 = OpConstant %6 0
+         %19 = OpTypePointer Function %6
+         %24 = OpTypePointer Function %8
+         %27 = OpConstant %7 4
+         %31 = OpTypeStruct %7 %7 %7
+         %32 = OpTypeStruct %9 %31
+         %33 = OpTypePointer Function %32
+         %36 = OpConstant %7 10
+         %37 = OpTypeInt 32 0
+         %38 = OpConstant %37 0
+         %39 = OpTypePointer Function %7
+         %42 = OpConstant %37 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+         %17 = OpVariable %10 Function
+         %34 = OpVariable %33 Function
+        %101 = OpCompositeConstruct %31 %27 %36 %27
+               OpStore %11 %16
+         %20 = OpAccessChain %19 %11 %18
+         %21 = OpLoad %6 %20
+         %22 = OpIAdd %6 %21 %12
+        %102 = OpCompositeConstruct %9 %22 %15
+         %23 = OpAccessChain %19 %17 %18
+               OpStore %23 %22
+         %25 = OpAccessChain %24 %17 %12
+         %26 = OpLoad %8 %25
+         %28 = OpCompositeConstruct %8 %27 %27
+         %29 = OpFAdd %8 %26 %28
+         %30 = OpAccessChain %24 %17 %12
+               OpStore %30 %29
+         %35 = OpLoad %9 %11
+         %40 = OpAccessChain %39 %11 %12 %38
+         %41 = OpLoad %7 %40
+         %43 = OpAccessChain %39 %11 %12 %42
+         %44 = OpLoad %7 %43
+         %45 = OpCompositeConstruct %31 %36 %41 %44
+        %100 = OpCompositeConstruct %32 %16 %45
+         %46 = OpCompositeConstruct %32 %35 %45
+               OpStore %34 %46
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  fact_manager.AddFact(MakeSynonymFact(16, 100, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(45, 100, {1}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(27, 101, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(36, 101, {1}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(27, 101, {2}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(22, 102, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(15, 102, {1}), context.get());
+
+  // Replace %45 with %100[1] in '%46 = OpCompositeConstruct %32 %35 %45'
+  auto replacement_1 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          45, MakeInstructionDescriptor(46, SpvOpCompositeConstruct, 0), 1),
+      MakeDataDescriptor(100, {1}), 201);
+  ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager));
+  replacement_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace second occurrence of %27 with %101[0] in '%28 =
+  // OpCompositeConstruct %8 %27 %27'
+  auto replacement_2 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          27, MakeInstructionDescriptor(28, SpvOpCompositeConstruct, 0), 1),
+      MakeDataDescriptor(101, {0}), 202);
+  ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager));
+  replacement_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %36 with %101[1] in '%45 = OpCompositeConstruct %31 %36 %41 %44'
+  auto replacement_3 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          36, MakeInstructionDescriptor(45, SpvOpCompositeConstruct, 0), 0),
+      MakeDataDescriptor(101, {1}), 203);
+  ASSERT_TRUE(replacement_3.IsApplicable(context.get(), fact_manager));
+  replacement_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace first occurrence of %27 with %101[2] in '%28 = OpCompositeConstruct
+  // %8 %27 %27'
+  auto replacement_4 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          27, MakeInstructionDescriptor(28, SpvOpCompositeConstruct, 0), 0),
+      MakeDataDescriptor(101, {2}), 204);
+  ASSERT_TRUE(replacement_4.IsApplicable(context.get(), fact_manager));
+  replacement_4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %22 with %102[0] in 'OpStore %23 %22'
+  auto replacement_5 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(22, MakeInstructionDescriptor(23, SpvOpStore, 0), 1),
+      MakeDataDescriptor(102, {0}), 205);
+  ASSERT_TRUE(replacement_5.IsApplicable(context.get(), fact_manager));
+  replacement_5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  const std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %9 "Inner"
+               OpMemberName %9 0 "a"
+               OpMemberName %9 1 "b"
+               OpName %11 "i1"
+               OpName %17 "i2"
+               OpName %31 "Point"
+               OpMemberName %31 0 "x"
+               OpMemberName %31 1 "y"
+               OpMemberName %31 2 "z"
+               OpName %32 "Outer"
+               OpMemberName %32 0 "c"
+               OpMemberName %32 1 "d"
+               OpName %34 "o1"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypeFloat 32
+          %8 = OpTypeVector %7 2
+          %9 = OpTypeStruct %6 %8
+         %10 = OpTypePointer Function %9
+         %12 = OpConstant %6 1
+         %13 = OpConstant %7 2
+         %14 = OpConstant %7 3
+         %15 = OpConstantComposite %8 %13 %14
+         %16 = OpConstantComposite %9 %12 %15
+         %18 = OpConstant %6 0
+         %19 = OpTypePointer Function %6
+         %24 = OpTypePointer Function %8
+         %27 = OpConstant %7 4
+         %31 = OpTypeStruct %7 %7 %7
+         %32 = OpTypeStruct %9 %31
+         %33 = OpTypePointer Function %32
+         %36 = OpConstant %7 10
+         %37 = OpTypeInt 32 0
+         %38 = OpConstant %37 0
+         %39 = OpTypePointer Function %7
+         %42 = OpConstant %37 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %11 = OpVariable %10 Function
+         %17 = OpVariable %10 Function
+         %34 = OpVariable %33 Function
+        %101 = OpCompositeConstruct %31 %27 %36 %27
+               OpStore %11 %16
+         %20 = OpAccessChain %19 %11 %18
+         %21 = OpLoad %6 %20
+         %22 = OpIAdd %6 %21 %12
+        %102 = OpCompositeConstruct %9 %22 %15
+         %23 = OpAccessChain %19 %17 %18
+        %205 = OpCompositeExtract %6 %102 0
+               OpStore %23 %205
+         %25 = OpAccessChain %24 %17 %12
+         %26 = OpLoad %8 %25
+        %202 = OpCompositeExtract %7 %101 0
+        %204 = OpCompositeExtract %7 %101 2
+         %28 = OpCompositeConstruct %8 %204 %202
+         %29 = OpFAdd %8 %26 %28
+         %30 = OpAccessChain %24 %17 %12
+               OpStore %30 %29
+         %35 = OpLoad %9 %11
+         %40 = OpAccessChain %39 %11 %12 %38
+         %41 = OpLoad %7 %40
+         %43 = OpAccessChain %39 %11 %12 %42
+         %44 = OpLoad %7 %43
+        %203 = OpCompositeExtract %7 %101 1
+         %45 = OpCompositeConstruct %31 %203 %41 %44
+        %100 = OpCompositeConstruct %32 %16 %45
+        %201 = OpCompositeExtract %31 %100 1
+         %46 = OpCompositeConstruct %32 %35 %201
+               OpStore %34 %46
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationReplaceIdWithSynonymTest, VectorCompositeSynonyms) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "f"
+               OpName %12 "v2"
+               OpName %18 "v3"
+               OpName %23 "v4"
+               OpName %32 "b"
+               OpName %36 "bv2"
+               OpName %41 "bv3"
+               OpName %50 "bv4"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 42
+         %10 = OpTypeVector %6 2
+         %11 = OpTypePointer Function %10
+         %16 = OpTypeVector %6 3
+         %17 = OpTypePointer Function %16
+         %21 = OpTypeVector %6 4
+         %22 = OpTypePointer Function %21
+         %30 = OpTypeBool
+         %31 = OpTypePointer Function %30
+         %33 = OpConstantFalse %30
+         %34 = OpTypeVector %30 2
+         %35 = OpTypePointer Function %34
+         %37 = OpConstantTrue %30
+         %38 = OpConstantComposite %34 %37 %37
+         %39 = OpTypeVector %30 3
+         %40 = OpTypePointer Function %39
+         %48 = OpTypeVector %30 4
+         %49 = OpTypePointer Function %48
+         %51 = OpTypeInt 32 0
+         %52 = OpConstant %51 2
+         %55 = OpConstant %6 0
+         %57 = OpConstant %51 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %12 = OpVariable %11 Function
+         %18 = OpVariable %17 Function
+         %23 = OpVariable %22 Function
+         %32 = OpVariable %31 Function
+         %36 = OpVariable %35 Function
+         %41 = OpVariable %40 Function
+         %50 = OpVariable %49 Function
+               OpStore %8 %9
+         %13 = OpLoad %6 %8
+         %14 = OpLoad %6 %8
+         %15 = OpCompositeConstruct %10 %13 %14
+               OpStore %12 %15
+         %19 = OpLoad %10 %12
+         %20 = OpVectorShuffle %16 %19 %19 0 0 1
+               OpStore %18 %20
+         %24 = OpLoad %16 %18
+         %25 = OpLoad %6 %8
+         %26 = OpCompositeExtract %6 %24 0
+         %27 = OpCompositeExtract %6 %24 1
+         %28 = OpCompositeExtract %6 %24 2
+         %29 = OpCompositeConstruct %21 %26 %27 %28 %25
+               OpStore %23 %29
+               OpStore %32 %33
+               OpStore %36 %38
+         %42 = OpLoad %30 %32
+         %43 = OpLoad %34 %36
+         %44 = OpVectorShuffle %34 %43 %43 0 0
+         %45 = OpCompositeExtract %30 %44 0
+         %46 = OpCompositeExtract %30 %44 1
+         %47 = OpCompositeConstruct %39 %42 %45 %46
+               OpStore %41 %47
+         %53 = OpAccessChain %7 %23 %52
+         %54 = OpLoad %6 %53
+
+        %100 = OpCompositeConstruct %21 %20 %54
+        %101 = OpCompositeConstruct %21 %15 %19
+        %102 = OpCompositeConstruct %16 %27 %15
+        %103 = OpCompositeConstruct %48 %33 %47
+        %104 = OpCompositeConstruct %34 %42 %45
+        %105 = OpCompositeConstruct %39 %38 %46
+
+         %86 = OpCopyObject %30 %33
+         %56 = OpFOrdNotEqual %30 %54 %55
+         %80 = OpCopyObject %16 %20
+         %58 = OpAccessChain %7 %18 %57
+         %59 = OpLoad %6 %58
+         %60 = OpFOrdNotEqual %30 %59 %55
+         %61 = OpLoad %34 %36
+         %62 = OpLogicalAnd %30 %45 %46
+         %63 = OpLogicalOr %30 %45 %46
+         %64 = OpCompositeConstruct %48 %56 %60 %62 %63
+               OpStore %12 %15
+         %81 = OpVectorShuffle %16 %19 %19 0 0 1
+         %82 = OpCompositeConstruct %21 %26 %27 %28 %25
+         %83 = OpCopyObject %10 %15
+         %84 = OpCopyObject %39 %47
+               OpStore %50 %64
+         %85 = OpCopyObject %30 %42
+               OpStore %36 %38
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+  fact_manager.AddFact(MakeSynonymFact(20, 100, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(54, 100, {3}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(15, 101, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(19, 101, {2}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(27, 102, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(15, 102, {1}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(33, 103, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(47, 103, {1}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(42, 104, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(45, 104, {1}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(38, 105, {0}), context.get());
+  fact_manager.AddFact(MakeSynonymFact(46, 105, {2}), context.get());
+
+  // Replace %20 with %100[0] in '%80 = OpCopyObject %16 %20'
+  auto replacement_1 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(20, MakeInstructionDescriptor(80, SpvOpCopyObject, 0),
+                          0),
+      MakeDataDescriptor(100, {0}), 200);
+  ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager));
+  replacement_1.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %54 with %100[3] in '%56 = OpFOrdNotEqual %30 %54 %55'
+  auto replacement_2 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          54, MakeInstructionDescriptor(56, SpvOpFOrdNotEqual, 0), 0),
+      MakeDataDescriptor(100, {3}), 201);
+  ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager));
+  replacement_2.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %15 with %101[0] in 'OpStore %12 %15'
+  auto replacement_3 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(15, MakeInstructionDescriptor(64, SpvOpStore, 0), 1),
+      MakeDataDescriptor(101, {0}), 202);
+  ASSERT_TRUE(replacement_3.IsApplicable(context.get(), fact_manager));
+  replacement_3.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %19 with %101[2] in '%81 = OpVectorShuffle %16 %19 %19 0 0 1'
+  auto replacement_4 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          19, MakeInstructionDescriptor(81, SpvOpVectorShuffle, 0), 0),
+      MakeDataDescriptor(101, {2}), 203);
+  ASSERT_TRUE(replacement_4.IsApplicable(context.get(), fact_manager));
+  replacement_4.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %27 with %102[0] in '%82 = OpCompositeConstruct %21 %26 %27 %28
+  // %25'
+  auto replacement_5 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(
+          27, MakeInstructionDescriptor(82, SpvOpCompositeConstruct, 0), 1),
+      MakeDataDescriptor(102, {0}), 204);
+  ASSERT_TRUE(replacement_5.IsApplicable(context.get(), fact_manager));
+  replacement_5.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %15 with %102[1] in '%83 = OpCopyObject %10 %15'
+  auto replacement_6 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(15, MakeInstructionDescriptor(83, SpvOpCopyObject, 0),
+                          0),
+      MakeDataDescriptor(102, {1}), 205);
+  ASSERT_TRUE(replacement_6.IsApplicable(context.get(), fact_manager));
+  replacement_6.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %33 with %103[0] in '%86 = OpCopyObject %30 %33'
+  auto replacement_7 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(33, MakeInstructionDescriptor(86, SpvOpCopyObject, 0),
+                          0),
+      MakeDataDescriptor(103, {0}), 206);
+  ASSERT_TRUE(replacement_7.IsApplicable(context.get(), fact_manager));
+  replacement_7.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %47 with %103[1] in '%84 = OpCopyObject %39 %47'
+  auto replacement_8 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(47, MakeInstructionDescriptor(84, SpvOpCopyObject, 0),
+                          0),
+      MakeDataDescriptor(103, {1}), 207);
+  ASSERT_TRUE(replacement_8.IsApplicable(context.get(), fact_manager));
+  replacement_8.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %42 with %104[0] in '%85 = OpCopyObject %30 %42'
+  auto replacement_9 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(42, MakeInstructionDescriptor(85, SpvOpCopyObject, 0),
+                          0),
+      MakeDataDescriptor(104, {0}), 208);
+  ASSERT_TRUE(replacement_9.IsApplicable(context.get(), fact_manager));
+  replacement_9.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %45 with %104[1] in '%63 = OpLogicalOr %30 %45 %46'
+  auto replacement_10 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(45, MakeInstructionDescriptor(63, SpvOpLogicalOr, 0),
+                          0),
+      MakeDataDescriptor(104, {1}), 209);
+  ASSERT_TRUE(replacement_10.IsApplicable(context.get(), fact_manager));
+  replacement_10.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %38 with %105[0] in 'OpStore %36 %38'
+  auto replacement_11 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(38, MakeInstructionDescriptor(85, SpvOpStore, 0), 1),
+      MakeDataDescriptor(105, {0}), 210);
+  ASSERT_TRUE(replacement_11.IsApplicable(context.get(), fact_manager));
+  replacement_11.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  // Replace %46 with %105[2] in '%62 = OpLogicalAnd %30 %45 %46'
+  auto replacement_12 = TransformationReplaceIdWithSynonym(
+      MakeIdUseDescriptor(46, MakeInstructionDescriptor(62, SpvOpLogicalAnd, 0),
+                          1),
+      MakeDataDescriptor(105, {2}), 211);
+  ASSERT_TRUE(replacement_12.IsApplicable(context.get(), fact_manager));
+  replacement_12.Apply(context.get(), &fact_manager);
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  const std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "f"
+               OpName %12 "v2"
+               OpName %18 "v3"
+               OpName %23 "v4"
+               OpName %32 "b"
+               OpName %36 "bv2"
+               OpName %41 "bv3"
+               OpName %50 "bv4"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeFloat 32
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 42
+         %10 = OpTypeVector %6 2
+         %11 = OpTypePointer Function %10
+         %16 = OpTypeVector %6 3
+         %17 = OpTypePointer Function %16
+         %21 = OpTypeVector %6 4
+         %22 = OpTypePointer Function %21
+         %30 = OpTypeBool
+         %31 = OpTypePointer Function %30
+         %33 = OpConstantFalse %30
+         %34 = OpTypeVector %30 2
+         %35 = OpTypePointer Function %34
+         %37 = OpConstantTrue %30
+         %38 = OpConstantComposite %34 %37 %37
+         %39 = OpTypeVector %30 3
+         %40 = OpTypePointer Function %39
+         %48 = OpTypeVector %30 4
+         %49 = OpTypePointer Function %48
+         %51 = OpTypeInt 32 0
+         %52 = OpConstant %51 2
+         %55 = OpConstant %6 0
+         %57 = OpConstant %51 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %12 = OpVariable %11 Function
+         %18 = OpVariable %17 Function
+         %23 = OpVariable %22 Function
+         %32 = OpVariable %31 Function
+         %36 = OpVariable %35 Function
+         %41 = OpVariable %40 Function
+         %50 = OpVariable %49 Function
+               OpStore %8 %9
+         %13 = OpLoad %6 %8
+         %14 = OpLoad %6 %8
+         %15 = OpCompositeConstruct %10 %13 %14
+               OpStore %12 %15
+         %19 = OpLoad %10 %12
+         %20 = OpVectorShuffle %16 %19 %19 0 0 1
+               OpStore %18 %20
+         %24 = OpLoad %16 %18
+         %25 = OpLoad %6 %8
+         %26 = OpCompositeExtract %6 %24 0
+         %27 = OpCompositeExtract %6 %24 1
+         %28 = OpCompositeExtract %6 %24 2
+         %29 = OpCompositeConstruct %21 %26 %27 %28 %25
+               OpStore %23 %29
+               OpStore %32 %33
+               OpStore %36 %38
+         %42 = OpLoad %30 %32
+         %43 = OpLoad %34 %36
+         %44 = OpVectorShuffle %34 %43 %43 0 0
+         %45 = OpCompositeExtract %30 %44 0
+         %46 = OpCompositeExtract %30 %44 1
+         %47 = OpCompositeConstruct %39 %42 %45 %46
+               OpStore %41 %47
+         %53 = OpAccessChain %7 %23 %52
+         %54 = OpLoad %6 %53
+
+        %100 = OpCompositeConstruct %21 %20 %54
+        %101 = OpCompositeConstruct %21 %15 %19
+        %102 = OpCompositeConstruct %16 %27 %15
+        %103 = OpCompositeConstruct %48 %33 %47
+        %104 = OpCompositeConstruct %34 %42 %45
+        %105 = OpCompositeConstruct %39 %38 %46
+
+        %206 = OpCompositeExtract %30 %103 0
+         %86 = OpCopyObject %30 %206
+        %201 = OpCompositeExtract %6 %100 3
+         %56 = OpFOrdNotEqual %30 %201 %55
+        %200 = OpVectorShuffle %16 %100 %100 0 1 2
+         %80 = OpCopyObject %16 %200
+         %58 = OpAccessChain %7 %18 %57
+         %59 = OpLoad %6 %58
+         %60 = OpFOrdNotEqual %30 %59 %55
+         %61 = OpLoad %34 %36
+        %211 = OpCompositeExtract %30 %105 2
+         %62 = OpLogicalAnd %30 %45 %211
+        %209 = OpCompositeExtract %30 %104 1
+         %63 = OpLogicalOr %30 %209 %46
+         %64 = OpCompositeConstruct %48 %56 %60 %62 %63
+        %202 = OpVectorShuffle %10 %101 %101 0 1
+               OpStore %12 %202
+        %203 = OpVectorShuffle %10 %101 %101 2 3
+         %81 = OpVectorShuffle %16 %203 %19 0 0 1
+        %204 = OpCompositeExtract %6 %102 0
+         %82 = OpCompositeConstruct %21 %26 %204 %28 %25
+        %205 = OpVectorShuffle %10 %102 %102 1 2
+         %83 = OpCopyObject %10 %205
+        %207 = OpVectorShuffle %39 %103 %103 1 2 3
+         %84 = OpCopyObject %39 %207
+               OpStore %50 %64
+        %208 = OpCompositeExtract %30 %104 0
+         %85 = OpCopyObject %30 %208
+        %210 = OpVectorShuffle %34 %105 %105 0 1
+               OpStore %36 %210
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
 }  // namespace
 }  // namespace
 }  // namespace fuzz
 }  // namespace fuzz
 }  // namespace spvtools
 }  // namespace spvtools

+ 251 - 0
3rdparty/spirv-tools/test/fuzz/transformation_set_function_control_test.cpp

@@ -0,0 +1,251 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_set_function_control.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationSetFunctionControlTest, VariousScenarios) {
+  // This is a simple transformation; this test captures the important things
+  // to check for.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %54
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %11 "foo(i1;i1;"
+               OpName %9 "a"
+               OpName %10 "b"
+               OpName %13 "bar("
+               OpName %17 "baz(i1;"
+               OpName %16 "x"
+               OpName %21 "boo(i1;i1;"
+               OpName %19 "a"
+               OpName %20 "b"
+               OpName %29 "g"
+               OpName %42 "param"
+               OpName %44 "param"
+               OpName %45 "param"
+               OpName %48 "param"
+               OpName %49 "param"
+               OpName %54 "color"
+               OpDecorate %54 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %6 %7 %7
+         %15 = OpTypeFunction %6 %7
+         %28 = OpTypePointer Private %6
+         %29 = OpVariable %28 Private
+         %30 = OpConstant %6 2
+         %31 = OpConstant %6 5
+         %51 = OpTypeFloat 32
+         %52 = OpTypeVector %51 4
+         %53 = OpTypePointer Output %52
+         %54 = OpVariable %53 Output
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+         %42 = OpVariable %7 Function
+         %44 = OpVariable %7 Function
+         %45 = OpVariable %7 Function
+         %48 = OpVariable %7 Function
+         %49 = OpVariable %7 Function
+         %41 = OpFunctionCall %2 %13
+               OpStore %42 %30
+         %43 = OpFunctionCall %6 %17 %42
+               OpStore %44 %31
+         %46 = OpLoad %6 %29
+               OpStore %45 %46
+         %47 = OpFunctionCall %6 %21 %44 %45
+               OpStore %48 %43
+               OpStore %49 %47
+         %50 = OpFunctionCall %6 %11 %48 %49
+               OpReturn
+               OpFunctionEnd
+         %11 = OpFunction %6 Const %8
+          %9 = OpFunctionParameter %7
+         %10 = OpFunctionParameter %7
+         %12 = OpLabel
+         %23 = OpLoad %6 %9
+         %24 = OpLoad %6 %10
+         %25 = OpIAdd %6 %23 %24
+               OpReturnValue %25
+               OpFunctionEnd
+         %13 = OpFunction %2 Inline %3
+         %14 = OpLabel
+               OpStore %29 %30
+               OpReturn
+               OpFunctionEnd
+         %17 = OpFunction %6 Pure|DontInline %15
+         %16 = OpFunctionParameter %7
+         %18 = OpLabel
+         %32 = OpLoad %6 %16
+         %33 = OpIAdd %6 %31 %32
+               OpReturnValue %33
+               OpFunctionEnd
+         %21 = OpFunction %6 DontInline %8
+         %19 = OpFunctionParameter %7
+         %20 = OpFunctionParameter %7
+         %22 = OpLabel
+         %36 = OpLoad %6 %19
+         %37 = OpLoad %6 %20
+         %38 = OpIMul %6 %36 %37
+               OpReturnValue %38
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+
+  // %36 is not a function
+  ASSERT_FALSE(TransformationSetFunctionControl(36, SpvFunctionControlMaskNone)
+                   .IsApplicable(context.get(), fact_manager));
+  // Cannot add the Pure function control to %4 as it did not already have it
+  ASSERT_FALSE(TransformationSetFunctionControl(4, SpvFunctionControlPureMask)
+                   .IsApplicable(context.get(), fact_manager));
+  // Cannot add the Const function control to %21 as it did not already
+  // have it
+  ASSERT_FALSE(TransformationSetFunctionControl(21, SpvFunctionControlConstMask)
+                   .IsApplicable(context.get(), fact_manager));
+
+  // Set to None, removing Const
+  TransformationSetFunctionControl transformation1(11,
+                                                   SpvFunctionControlMaskNone);
+  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
+  transformation1.Apply(context.get(), &fact_manager);
+
+  // Set to Inline; silly to do it on an entry point, but it is allowed
+  TransformationSetFunctionControl transformation2(
+      4, SpvFunctionControlInlineMask);
+  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
+  transformation2.Apply(context.get(), &fact_manager);
+
+  // Set to Pure, removing DontInline
+  TransformationSetFunctionControl transformation3(17,
+                                                   SpvFunctionControlPureMask);
+  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
+  transformation3.Apply(context.get(), &fact_manager);
+
+  // Change from Inline to DontInline
+  TransformationSetFunctionControl transformation4(
+      13, SpvFunctionControlDontInlineMask);
+  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
+  transformation4.Apply(context.get(), &fact_manager);
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %54
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %11 "foo(i1;i1;"
+               OpName %9 "a"
+               OpName %10 "b"
+               OpName %13 "bar("
+               OpName %17 "baz(i1;"
+               OpName %16 "x"
+               OpName %21 "boo(i1;i1;"
+               OpName %19 "a"
+               OpName %20 "b"
+               OpName %29 "g"
+               OpName %42 "param"
+               OpName %44 "param"
+               OpName %45 "param"
+               OpName %48 "param"
+               OpName %49 "param"
+               OpName %54 "color"
+               OpDecorate %54 Location 0
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %8 = OpTypeFunction %6 %7 %7
+         %15 = OpTypeFunction %6 %7
+         %28 = OpTypePointer Private %6
+         %29 = OpVariable %28 Private
+         %30 = OpConstant %6 2
+         %31 = OpConstant %6 5
+         %51 = OpTypeFloat 32
+         %52 = OpTypeVector %51 4
+         %53 = OpTypePointer Output %52
+         %54 = OpVariable %53 Output
+          %4 = OpFunction %2 Inline %3
+          %5 = OpLabel
+         %42 = OpVariable %7 Function
+         %44 = OpVariable %7 Function
+         %45 = OpVariable %7 Function
+         %48 = OpVariable %7 Function
+         %49 = OpVariable %7 Function
+         %41 = OpFunctionCall %2 %13
+               OpStore %42 %30
+         %43 = OpFunctionCall %6 %17 %42
+               OpStore %44 %31
+         %46 = OpLoad %6 %29
+               OpStore %45 %46
+         %47 = OpFunctionCall %6 %21 %44 %45
+               OpStore %48 %43
+               OpStore %49 %47
+         %50 = OpFunctionCall %6 %11 %48 %49
+               OpReturn
+               OpFunctionEnd
+         %11 = OpFunction %6 None %8
+          %9 = OpFunctionParameter %7
+         %10 = OpFunctionParameter %7
+         %12 = OpLabel
+         %23 = OpLoad %6 %9
+         %24 = OpLoad %6 %10
+         %25 = OpIAdd %6 %23 %24
+               OpReturnValue %25
+               OpFunctionEnd
+         %13 = OpFunction %2 DontInline %3
+         %14 = OpLabel
+               OpStore %29 %30
+               OpReturn
+               OpFunctionEnd
+         %17 = OpFunction %6 Pure %15
+         %16 = OpFunctionParameter %7
+         %18 = OpLabel
+         %32 = OpLoad %6 %16
+         %33 = OpIAdd %6 %31 %32
+               OpReturnValue %33
+               OpFunctionEnd
+         %21 = OpFunction %6 DontInline %8
+         %19 = OpFunctionParameter %7
+         %20 = OpFunctionParameter %7
+         %22 = OpLabel
+         %36 = OpLoad %6 %19
+         %37 = OpLoad %6 %20
+         %38 = OpIMul %6 %36 %37
+               OpReturnValue %38
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools

+ 968 - 0
3rdparty/spirv-tools/test/fuzz/transformation_set_loop_control_test.cpp

@@ -0,0 +1,968 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_set_loop_control.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationSetLoopControlTest, VariousScenarios) {
+  // This test features loops with various different controls, and goes through
+  // a number of acceptable and unacceptable transformations to those controls.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %16 = OpConstant %6 100
+         %17 = OpTypeBool
+         %20 = OpConstant %6 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %22 = OpVariable %7 Function
+         %32 = OpVariable %7 Function
+         %42 = OpVariable %7 Function
+         %52 = OpVariable %7 Function
+         %62 = OpVariable %7 Function
+         %72 = OpVariable %7 Function
+         %82 = OpVariable %7 Function
+         %92 = OpVariable %7 Function
+        %102 = OpVariable %7 Function
+        %112 = OpVariable %7 Function
+        %122 = OpVariable %7 Function
+               OpStore %8 %9
+               OpBranch %10
+         %10 = OpLabel
+        %132 = OpPhi %6 %9 %5 %21 %13
+               OpLoopMerge %12 %13 None
+               OpBranch %14
+         %14 = OpLabel
+         %18 = OpSLessThan %17 %132 %16
+               OpBranchConditional %18 %11 %12
+         %11 = OpLabel
+               OpBranch %13
+         %13 = OpLabel
+         %21 = OpIAdd %6 %132 %20
+               OpStore %8 %21
+               OpBranch %10
+         %12 = OpLabel
+               OpStore %22 %9
+               OpBranch %23
+         %23 = OpLabel
+        %133 = OpPhi %6 %9 %12 %31 %26
+               OpLoopMerge %25 %26 Unroll
+               OpBranch %27
+         %27 = OpLabel
+         %29 = OpSLessThan %17 %133 %16
+               OpBranchConditional %29 %24 %25
+         %24 = OpLabel
+               OpBranch %26
+         %26 = OpLabel
+         %31 = OpIAdd %6 %133 %20
+               OpStore %22 %31
+               OpBranch %23
+         %25 = OpLabel
+               OpStore %32 %9
+               OpBranch %33
+         %33 = OpLabel
+        %134 = OpPhi %6 %9 %25 %41 %36
+               OpLoopMerge %35 %36 DontUnroll
+               OpBranch %37
+         %37 = OpLabel
+         %39 = OpSLessThan %17 %134 %16
+               OpBranchConditional %39 %34 %35
+         %34 = OpLabel
+               OpBranch %36
+         %36 = OpLabel
+         %41 = OpIAdd %6 %134 %20
+               OpStore %32 %41
+               OpBranch %33
+         %35 = OpLabel
+               OpStore %42 %9
+               OpBranch %43
+         %43 = OpLabel
+        %135 = OpPhi %6 %9 %35 %51 %46
+               OpLoopMerge %45 %46 DependencyInfinite
+               OpBranch %47
+         %47 = OpLabel
+         %49 = OpSLessThan %17 %135 %16
+               OpBranchConditional %49 %44 %45
+         %44 = OpLabel
+               OpBranch %46
+         %46 = OpLabel
+         %51 = OpIAdd %6 %135 %20
+               OpStore %42 %51
+               OpBranch %43
+         %45 = OpLabel
+               OpStore %52 %9
+               OpBranch %53
+         %53 = OpLabel
+        %136 = OpPhi %6 %9 %45 %61 %56
+               OpLoopMerge %55 %56 DependencyLength 3
+               OpBranch %57
+         %57 = OpLabel
+         %59 = OpSLessThan %17 %136 %16
+               OpBranchConditional %59 %54 %55
+         %54 = OpLabel
+               OpBranch %56
+         %56 = OpLabel
+         %61 = OpIAdd %6 %136 %20
+               OpStore %52 %61
+               OpBranch %53
+         %55 = OpLabel
+               OpStore %62 %9
+               OpBranch %63
+         %63 = OpLabel
+        %137 = OpPhi %6 %9 %55 %71 %66
+               OpLoopMerge %65 %66 MinIterations 10
+               OpBranch %67
+         %67 = OpLabel
+         %69 = OpSLessThan %17 %137 %16
+               OpBranchConditional %69 %64 %65
+         %64 = OpLabel
+               OpBranch %66
+         %66 = OpLabel
+         %71 = OpIAdd %6 %137 %20
+               OpStore %62 %71
+               OpBranch %63
+         %65 = OpLabel
+               OpStore %72 %9
+               OpBranch %73
+         %73 = OpLabel
+        %138 = OpPhi %6 %9 %65 %81 %76
+               OpLoopMerge %75 %76 MaxIterations 50
+               OpBranch %77
+         %77 = OpLabel
+         %79 = OpSLessThan %17 %138 %16
+               OpBranchConditional %79 %74 %75
+         %74 = OpLabel
+               OpBranch %76
+         %76 = OpLabel
+         %81 = OpIAdd %6 %138 %20
+               OpStore %72 %81
+               OpBranch %73
+         %75 = OpLabel
+               OpStore %82 %9
+               OpBranch %83
+         %83 = OpLabel
+        %139 = OpPhi %6 %9 %75 %91 %86
+               OpLoopMerge %85 %86 IterationMultiple 4
+               OpBranch %87
+         %87 = OpLabel
+         %89 = OpSLessThan %17 %139 %16
+               OpBranchConditional %89 %84 %85
+         %84 = OpLabel
+               OpBranch %86
+         %86 = OpLabel
+         %91 = OpIAdd %6 %139 %20
+               OpStore %82 %91
+               OpBranch %83
+         %85 = OpLabel
+               OpStore %92 %9
+               OpBranch %93
+         %93 = OpLabel
+        %140 = OpPhi %6 %9 %85 %101 %96
+               OpLoopMerge %95 %96 PeelCount 2
+               OpBranch %97
+         %97 = OpLabel
+         %99 = OpSLessThan %17 %140 %16
+               OpBranchConditional %99 %94 %95
+         %94 = OpLabel
+               OpBranch %96
+         %96 = OpLabel
+        %101 = OpIAdd %6 %140 %20
+               OpStore %92 %101
+               OpBranch %93
+         %95 = OpLabel
+               OpStore %102 %9
+               OpBranch %103
+        %103 = OpLabel
+        %141 = OpPhi %6 %9 %95 %111 %106
+               OpLoopMerge %105 %106 PartialCount 3
+               OpBranch %107
+        %107 = OpLabel
+        %109 = OpSLessThan %17 %141 %16
+               OpBranchConditional %109 %104 %105
+        %104 = OpLabel
+               OpBranch %106
+        %106 = OpLabel
+        %111 = OpIAdd %6 %141 %20
+               OpStore %102 %111
+               OpBranch %103
+        %105 = OpLabel
+               OpStore %112 %9
+               OpBranch %113
+        %113 = OpLabel
+        %142 = OpPhi %6 %9 %105 %121 %116
+               OpLoopMerge %115 %116 Unroll|PeelCount|PartialCount 3 4
+               OpBranch %117
+        %117 = OpLabel
+        %119 = OpSLessThan %17 %142 %16
+               OpBranchConditional %119 %114 %115
+        %114 = OpLabel
+               OpBranch %116
+        %116 = OpLabel
+        %121 = OpIAdd %6 %142 %20
+               OpStore %112 %121
+               OpBranch %113
+        %115 = OpLabel
+               OpStore %122 %9
+               OpBranch %123
+        %123 = OpLabel
+        %143 = OpPhi %6 %9 %115 %131 %126
+               OpLoopMerge %125 %126 DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount 2 5 90 4 7 14
+               OpBranch %127
+        %127 = OpLabel
+        %129 = OpSLessThan %17 %143 %16
+               OpBranchConditional %129 %124 %125
+        %124 = OpLabel
+               OpBranch %126
+        %126 = OpLabel
+        %131 = OpIAdd %6 %143 %20
+               OpStore %122 %131
+               OpBranch %123
+        %125 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_4;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+  ASSERT_TRUE(IsValid(env, context.get()));
+
+  FactManager fact_manager;
+
+  // These are the loop headers together with the selection controls of their
+  // merge instructions:
+  //  %10 None
+  //  %23 Unroll
+  //  %33 DontUnroll
+  //  %43 DependencyInfinite
+  //  %53 DependencyLength 3
+  //  %63 MinIterations 10
+  //  %73 MaxIterations 50
+  //  %83 IterationMultiple 4
+  //  %93 PeelCount 2
+  // %103 PartialCount 3
+  // %113 Unroll|PeelCount|PartialCount 3 4
+  // %123
+  // DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount
+  // 2 5 90 4 7 14
+
+  ASSERT_TRUE(TransformationSetLoopControl(10, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(10, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(10, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   10, SpvLoopControlDependencyInfiniteMask, 0, 0)
+                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(10, SpvLoopControlDependencyLengthMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(10, SpvLoopControlMinIterationsMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(10, SpvLoopControlMaxIterationsMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   10, SpvLoopControlIterationMultipleMask, 0, 0)
+                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(10, SpvLoopControlPeelCountMask, 3, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(10, SpvLoopControlPeelCountMask, 3, 3)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(10, SpvLoopControlPartialCountMask, 0, 3)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(10, SpvLoopControlPartialCountMask, 3, 3)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  10,
+                  SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
+                  3, 3)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(10,
+                                           SpvLoopControlUnrollMask |
+                                               SpvLoopControlPeelCountMask |
+                                               SpvLoopControlPartialCountMask,
+                                           3, 3)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSetLoopControl(10,
+                                            SpvLoopControlDontUnrollMask |
+                                                SpvLoopControlPeelCountMask |
+                                                SpvLoopControlPartialCountMask,
+                                            3, 3)
+                   .IsApplicable(context.get(), fact_manager));
+
+  ASSERT_TRUE(TransformationSetLoopControl(23, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(23, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(23, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  23,
+                  SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
+                  3, 3)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(23, SpvLoopControlMaxIterationsMask, 2, 3)
+          .IsApplicable(context.get(), fact_manager));
+
+  ASSERT_TRUE(TransformationSetLoopControl(33, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(33, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(33, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(33, SpvLoopControlMinIterationsMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(
+          33, SpvLoopControlUnrollMask | SpvLoopControlPeelCountMask, 5, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSetLoopControl(33,
+                                            SpvLoopControlDontUnrollMask |
+                                                SpvLoopControlPartialCountMask,
+                                            0, 10)
+                   .IsApplicable(context.get(), fact_manager));
+
+  ASSERT_TRUE(TransformationSetLoopControl(43, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(43, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(43, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  43,
+                  SpvLoopControlMaskNone | SpvLoopControlDependencyInfiniteMask,
+                  0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(
+          43, SpvLoopControlUnrollMask | SpvLoopControlDependencyInfiniteMask,
+          0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(
+          43,
+          SpvLoopControlDontUnrollMask | SpvLoopControlDependencyInfiniteMask,
+          0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(43,
+                                   SpvLoopControlDependencyInfiniteMask |
+                                       SpvLoopControlDependencyLengthMask,
+                                   0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(
+          43, SpvLoopControlUnrollMask | SpvLoopControlPeelCountMask, 5, 0)
+          .IsApplicable(context.get(), fact_manager));
+
+  ASSERT_TRUE(TransformationSetLoopControl(53, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(53, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(53, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(53, SpvLoopControlMaxIterationsMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(
+          53, SpvLoopControlMaskNone | SpvLoopControlDependencyLengthMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(
+          53, SpvLoopControlUnrollMask | SpvLoopControlDependencyInfiniteMask,
+          0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(
+          53, SpvLoopControlDontUnrollMask | SpvLoopControlDependencyLengthMask,
+          0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(53,
+                                   SpvLoopControlDependencyInfiniteMask |
+                                       SpvLoopControlDependencyLengthMask,
+                                   0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(
+          53,
+          SpvLoopControlUnrollMask | SpvLoopControlDependencyLengthMask |
+              SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
+          5, 3)
+          .IsApplicable(context.get(), fact_manager));
+
+  ASSERT_TRUE(TransformationSetLoopControl(63, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(63, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(63, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(63,
+                                           SpvLoopControlUnrollMask |
+                                               SpvLoopControlMinIterationsMask |
+                                               SpvLoopControlPeelCountMask |
+                                               SpvLoopControlPartialCountMask,
+                                           5, 3)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(63,
+                                           SpvLoopControlUnrollMask |
+                                               SpvLoopControlMinIterationsMask |
+                                               SpvLoopControlPeelCountMask,
+                                           23, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   63,
+                   SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
+                       SpvLoopControlPeelCountMask,
+                   2, 23)
+                   .IsApplicable(context.get(), fact_manager));
+
+  ASSERT_TRUE(TransformationSetLoopControl(73, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(73, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(73, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   73,
+                   SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
+                       SpvLoopControlPeelCountMask |
+                       SpvLoopControlPartialCountMask,
+                   5, 3)
+                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(73,
+                                           SpvLoopControlUnrollMask |
+                                               SpvLoopControlMaxIterationsMask |
+                                               SpvLoopControlPeelCountMask,
+                                           23, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   73,
+                   SpvLoopControlUnrollMask | SpvLoopControlMaxIterationsMask |
+                       SpvLoopControlPeelCountMask,
+                   2, 23)
+                   .IsApplicable(context.get(), fact_manager));
+
+  ASSERT_TRUE(TransformationSetLoopControl(83, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(83, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(83, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   83,
+                   SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
+                       SpvLoopControlPeelCountMask |
+                       SpvLoopControlPartialCountMask,
+                   5, 3)
+                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(83,
+                                   SpvLoopControlUnrollMask |
+                                       SpvLoopControlIterationMultipleMask |
+                                       SpvLoopControlPeelCountMask,
+                                   23, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(83,
+                                   SpvLoopControlUnrollMask |
+                                       SpvLoopControlIterationMultipleMask |
+                                       SpvLoopControlPeelCountMask,
+                                   2, 23)
+          .IsApplicable(context.get(), fact_manager));
+
+  ASSERT_TRUE(TransformationSetLoopControl(93, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(93, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(93, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(93, SpvLoopControlPeelCountMask, 8, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(93, SpvLoopControlPeelCountMask, 8, 8)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(93, SpvLoopControlPartialCountMask, 0, 8)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(
+                  93,
+                  SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
+                  16, 8)
+                  .IsApplicable(context.get(), fact_manager));
+
+  ASSERT_TRUE(TransformationSetLoopControl(103, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(103, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(103, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(103, SpvLoopControlPartialCountMask, 0, 60)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSetLoopControl(103,
+                                            SpvLoopControlDontUnrollMask |
+                                                SpvLoopControlPartialCountMask,
+                                            0, 60)
+                   .IsApplicable(context.get(), fact_manager));
+
+  ASSERT_TRUE(TransformationSetLoopControl(113, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(113, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(113, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(113, SpvLoopControlPeelCountMask, 12, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(
+          113,
+          SpvLoopControlIterationMultipleMask | SpvLoopControlPeelCountMask, 12,
+          0)
+          .IsApplicable(context.get(), fact_manager));
+
+  ASSERT_TRUE(TransformationSetLoopControl(123, SpvLoopControlMaskNone, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(123, SpvLoopControlUnrollMask, 0, 0)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(123, SpvLoopControlDontUnrollMask, 0, 0)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(
+      TransformationSetLoopControl(
+          123,
+          SpvLoopControlMinIterationsMask | SpvLoopControlMaxIterationsMask |
+              SpvLoopControlIterationMultipleMask |
+              SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
+          7, 8)
+          .IsApplicable(context.get(), fact_manager));
+  ASSERT_TRUE(TransformationSetLoopControl(123,
+                                           SpvLoopControlUnrollMask |
+                                               SpvLoopControlMinIterationsMask |
+                                               SpvLoopControlMaxIterationsMask |
+                                               SpvLoopControlPartialCountMask,
+                                           0, 9)
+                  .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(TransformationSetLoopControl(
+                   123,
+                   SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
+                       SpvLoopControlMaxIterationsMask |
+                       SpvLoopControlPartialCountMask,
+                   7, 9)
+                   .IsApplicable(context.get(), fact_manager));
+  ASSERT_FALSE(
+      TransformationSetLoopControl(
+          123,
+          SpvLoopControlDontUnrollMask | SpvLoopControlMinIterationsMask |
+              SpvLoopControlMaxIterationsMask | SpvLoopControlPartialCountMask,
+          7, 9)
+          .IsApplicable(context.get(), fact_manager));
+
+  TransformationSetLoopControl(10,
+                               SpvLoopControlUnrollMask |
+                                   SpvLoopControlPeelCountMask |
+                                   SpvLoopControlPartialCountMask,
+                               3, 3)
+      .Apply(context.get(), &fact_manager);
+  TransformationSetLoopControl(23, SpvLoopControlDontUnrollMask, 0, 0)
+      .Apply(context.get(), &fact_manager);
+  TransformationSetLoopControl(33, SpvLoopControlUnrollMask, 0, 0)
+      .Apply(context.get(), &fact_manager);
+  TransformationSetLoopControl(
+      43, SpvLoopControlDontUnrollMask | SpvLoopControlDependencyInfiniteMask,
+      0, 0)
+      .Apply(context.get(), &fact_manager);
+  TransformationSetLoopControl(53, SpvLoopControlMaskNone, 0, 0)
+      .Apply(context.get(), &fact_manager);
+  TransformationSetLoopControl(63,
+                               SpvLoopControlUnrollMask |
+                                   SpvLoopControlMinIterationsMask |
+                                   SpvLoopControlPeelCountMask,
+                               23, 0)
+      .Apply(context.get(), &fact_manager);
+  TransformationSetLoopControl(73,
+                               SpvLoopControlUnrollMask |
+                                   SpvLoopControlMaxIterationsMask |
+                                   SpvLoopControlPeelCountMask,
+                               23, 0)
+      .Apply(context.get(), &fact_manager);
+  TransformationSetLoopControl(83, SpvLoopControlDontUnrollMask, 0, 0)
+      .Apply(context.get(), &fact_manager);
+  TransformationSetLoopControl(
+      93, SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 16, 8)
+      .Apply(context.get(), &fact_manager);
+  TransformationSetLoopControl(103, SpvLoopControlPartialCountMask, 0, 60)
+      .Apply(context.get(), &fact_manager);
+  TransformationSetLoopControl(113, SpvLoopControlPeelCountMask, 12, 0)
+      .Apply(context.get(), &fact_manager);
+  TransformationSetLoopControl(
+      123,
+      SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
+          SpvLoopControlMaxIterationsMask | SpvLoopControlPartialCountMask,
+      0, 9)
+      .Apply(context.get(), &fact_manager);
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %16 = OpConstant %6 100
+         %17 = OpTypeBool
+         %20 = OpConstant %6 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %22 = OpVariable %7 Function
+         %32 = OpVariable %7 Function
+         %42 = OpVariable %7 Function
+         %52 = OpVariable %7 Function
+         %62 = OpVariable %7 Function
+         %72 = OpVariable %7 Function
+         %82 = OpVariable %7 Function
+         %92 = OpVariable %7 Function
+        %102 = OpVariable %7 Function
+        %112 = OpVariable %7 Function
+        %122 = OpVariable %7 Function
+               OpStore %8 %9
+               OpBranch %10
+         %10 = OpLabel
+        %132 = OpPhi %6 %9 %5 %21 %13
+               OpLoopMerge %12 %13 Unroll|PeelCount|PartialCount 3 3
+               OpBranch %14
+         %14 = OpLabel
+         %18 = OpSLessThan %17 %132 %16
+               OpBranchConditional %18 %11 %12
+         %11 = OpLabel
+               OpBranch %13
+         %13 = OpLabel
+         %21 = OpIAdd %6 %132 %20
+               OpStore %8 %21
+               OpBranch %10
+         %12 = OpLabel
+               OpStore %22 %9
+               OpBranch %23
+         %23 = OpLabel
+        %133 = OpPhi %6 %9 %12 %31 %26
+               OpLoopMerge %25 %26 DontUnroll
+               OpBranch %27
+         %27 = OpLabel
+         %29 = OpSLessThan %17 %133 %16
+               OpBranchConditional %29 %24 %25
+         %24 = OpLabel
+               OpBranch %26
+         %26 = OpLabel
+         %31 = OpIAdd %6 %133 %20
+               OpStore %22 %31
+               OpBranch %23
+         %25 = OpLabel
+               OpStore %32 %9
+               OpBranch %33
+         %33 = OpLabel
+        %134 = OpPhi %6 %9 %25 %41 %36
+               OpLoopMerge %35 %36 Unroll
+               OpBranch %37
+         %37 = OpLabel
+         %39 = OpSLessThan %17 %134 %16
+               OpBranchConditional %39 %34 %35
+         %34 = OpLabel
+               OpBranch %36
+         %36 = OpLabel
+         %41 = OpIAdd %6 %134 %20
+               OpStore %32 %41
+               OpBranch %33
+         %35 = OpLabel
+               OpStore %42 %9
+               OpBranch %43
+         %43 = OpLabel
+        %135 = OpPhi %6 %9 %35 %51 %46
+               OpLoopMerge %45 %46 DontUnroll|DependencyInfinite
+               OpBranch %47
+         %47 = OpLabel
+         %49 = OpSLessThan %17 %135 %16
+               OpBranchConditional %49 %44 %45
+         %44 = OpLabel
+               OpBranch %46
+         %46 = OpLabel
+         %51 = OpIAdd %6 %135 %20
+               OpStore %42 %51
+               OpBranch %43
+         %45 = OpLabel
+               OpStore %52 %9
+               OpBranch %53
+         %53 = OpLabel
+        %136 = OpPhi %6 %9 %45 %61 %56
+               OpLoopMerge %55 %56 None
+               OpBranch %57
+         %57 = OpLabel
+         %59 = OpSLessThan %17 %136 %16
+               OpBranchConditional %59 %54 %55
+         %54 = OpLabel
+               OpBranch %56
+         %56 = OpLabel
+         %61 = OpIAdd %6 %136 %20
+               OpStore %52 %61
+               OpBranch %53
+         %55 = OpLabel
+               OpStore %62 %9
+               OpBranch %63
+         %63 = OpLabel
+        %137 = OpPhi %6 %9 %55 %71 %66
+               OpLoopMerge %65 %66 Unroll|MinIterations|PeelCount 10 23
+               OpBranch %67
+         %67 = OpLabel
+         %69 = OpSLessThan %17 %137 %16
+               OpBranchConditional %69 %64 %65
+         %64 = OpLabel
+               OpBranch %66
+         %66 = OpLabel
+         %71 = OpIAdd %6 %137 %20
+               OpStore %62 %71
+               OpBranch %63
+         %65 = OpLabel
+               OpStore %72 %9
+               OpBranch %73
+         %73 = OpLabel
+        %138 = OpPhi %6 %9 %65 %81 %76
+               OpLoopMerge %75 %76 Unroll|MaxIterations|PeelCount 50 23
+               OpBranch %77
+         %77 = OpLabel
+         %79 = OpSLessThan %17 %138 %16
+               OpBranchConditional %79 %74 %75
+         %74 = OpLabel
+               OpBranch %76
+         %76 = OpLabel
+         %81 = OpIAdd %6 %138 %20
+               OpStore %72 %81
+               OpBranch %73
+         %75 = OpLabel
+               OpStore %82 %9
+               OpBranch %83
+         %83 = OpLabel
+        %139 = OpPhi %6 %9 %75 %91 %86
+               OpLoopMerge %85 %86 DontUnroll
+               OpBranch %87
+         %87 = OpLabel
+         %89 = OpSLessThan %17 %139 %16
+               OpBranchConditional %89 %84 %85
+         %84 = OpLabel
+               OpBranch %86
+         %86 = OpLabel
+         %91 = OpIAdd %6 %139 %20
+               OpStore %82 %91
+               OpBranch %83
+         %85 = OpLabel
+               OpStore %92 %9
+               OpBranch %93
+         %93 = OpLabel
+        %140 = OpPhi %6 %9 %85 %101 %96
+               OpLoopMerge %95 %96 PeelCount|PartialCount 16 8
+               OpBranch %97
+         %97 = OpLabel
+         %99 = OpSLessThan %17 %140 %16
+               OpBranchConditional %99 %94 %95
+         %94 = OpLabel
+               OpBranch %96
+         %96 = OpLabel
+        %101 = OpIAdd %6 %140 %20
+               OpStore %92 %101
+               OpBranch %93
+         %95 = OpLabel
+               OpStore %102 %9
+               OpBranch %103
+        %103 = OpLabel
+        %141 = OpPhi %6 %9 %95 %111 %106
+               OpLoopMerge %105 %106 PartialCount 60
+               OpBranch %107
+        %107 = OpLabel
+        %109 = OpSLessThan %17 %141 %16
+               OpBranchConditional %109 %104 %105
+        %104 = OpLabel
+               OpBranch %106
+        %106 = OpLabel
+        %111 = OpIAdd %6 %141 %20
+               OpStore %102 %111
+               OpBranch %103
+        %105 = OpLabel
+               OpStore %112 %9
+               OpBranch %113
+        %113 = OpLabel
+        %142 = OpPhi %6 %9 %105 %121 %116
+               OpLoopMerge %115 %116 PeelCount 12
+               OpBranch %117
+        %117 = OpLabel
+        %119 = OpSLessThan %17 %142 %16
+               OpBranchConditional %119 %114 %115
+        %114 = OpLabel
+               OpBranch %116
+        %116 = OpLabel
+        %121 = OpIAdd %6 %142 %20
+               OpStore %112 %121
+               OpBranch %113
+        %115 = OpLabel
+               OpStore %122 %9
+               OpBranch %123
+        %123 = OpLabel
+        %143 = OpPhi %6 %9 %115 %131 %126
+               OpLoopMerge %125 %126 Unroll|MinIterations|MaxIterations|PartialCount 5 90 9
+               OpBranch %127
+        %127 = OpLabel
+        %129 = OpSLessThan %17 %143 %16
+               OpBranchConditional %129 %124 %125
+        %124 = OpLabel
+               OpBranch %126
+        %126 = OpLabel
+        %131 = OpIAdd %6 %143 %20
+               OpStore %122 %131
+               OpBranch %123
+        %125 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationSetLoopControlTest, CheckSPIRVVersionsRespected) {
+  // This test checks that we do not allow introducing PeelCount and
+  // PartialCount loop controls if the SPIR-V version being used does not
+  // support them.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "i"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %16 = OpConstant %6 10
+         %17 = OpTypeBool
+         %20 = OpConstant %6 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+               OpStore %8 %9
+               OpBranch %10
+         %10 = OpLabel
+               OpLoopMerge %12 %13 None
+               OpBranch %14
+         %14 = OpLabel
+         %15 = OpLoad %6 %8
+         %18 = OpSLessThan %17 %15 %16
+               OpBranchConditional %18 %11 %12
+         %11 = OpLabel
+               OpBranch %13
+         %13 = OpLabel
+         %19 = OpLoad %6 %8
+         %21 = OpIAdd %6 %19 %20
+               OpStore %8 %21
+               OpBranch %10
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto consumer = nullptr;
+  const auto context_1_0 =
+      BuildModule(SPV_ENV_UNIVERSAL_1_0, consumer, shader, kFuzzAssembleOption);
+  const auto context_1_1 =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, consumer, shader, kFuzzAssembleOption);
+  const auto context_1_2 =
+      BuildModule(SPV_ENV_UNIVERSAL_1_2, consumer, shader, kFuzzAssembleOption);
+  const auto context_1_3 =
+      BuildModule(SPV_ENV_UNIVERSAL_1_3, consumer, shader, kFuzzAssembleOption);
+  const auto context_1_4 =
+      BuildModule(SPV_ENV_UNIVERSAL_1_4, consumer, shader, kFuzzAssembleOption);
+  const auto context_1_5 =
+      BuildModule(SPV_ENV_UNIVERSAL_1_5, consumer, shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+
+  TransformationSetLoopControl set_peel_and_partial(
+      10, SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 4, 4);
+
+  // PeelCount and PartialCount were introduced in SPIRV 1.4, so are not valid
+  // in the context of older versions.
+  ASSERT_FALSE(
+      set_peel_and_partial.IsApplicable(context_1_0.get(), fact_manager));
+  ASSERT_FALSE(
+      set_peel_and_partial.IsApplicable(context_1_1.get(), fact_manager));
+  ASSERT_FALSE(
+      set_peel_and_partial.IsApplicable(context_1_2.get(), fact_manager));
+  ASSERT_FALSE(
+      set_peel_and_partial.IsApplicable(context_1_3.get(), fact_manager));
+
+  ASSERT_TRUE(
+      set_peel_and_partial.IsApplicable(context_1_4.get(), fact_manager));
+  ASSERT_TRUE(
+      set_peel_and_partial.IsApplicable(context_1_5.get(), fact_manager));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools

+ 219 - 0
3rdparty/spirv-tools/test/fuzz/transformation_set_selection_control_test.cpp

@@ -0,0 +1,219 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_set_selection_control.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationSetSelectionControlTest, VariousScenarios) {
+  // This is a simple transformation; this test captures the important things
+  // to check for.
+
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "i"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %16 = OpConstant %6 10
+         %17 = OpTypeBool
+         %20 = OpConstant %6 3
+         %25 = OpConstant %6 1
+         %28 = OpConstant %6 2
+         %38 = OpConstant %6 4
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+               OpStore %8 %9
+               OpBranch %10
+         %10 = OpLabel
+               OpLoopMerge %12 %13 None
+               OpBranch %14
+         %14 = OpLabel
+         %15 = OpLoad %6 %8
+         %18 = OpSLessThan %17 %15 %16
+               OpBranchConditional %18 %11 %12
+         %11 = OpLabel
+         %19 = OpLoad %6 %8
+         %21 = OpSGreaterThan %17 %19 %20
+               OpSelectionMerge %23 Flatten
+               OpBranchConditional %21 %22 %23
+         %22 = OpLabel
+         %24 = OpLoad %6 %8
+         %26 = OpIAdd %6 %24 %25
+               OpStore %8 %26
+               OpBranch %23
+         %23 = OpLabel
+         %27 = OpLoad %6 %8
+         %29 = OpSLessThan %17 %27 %28
+               OpSelectionMerge %31 DontFlatten
+               OpBranchConditional %29 %30 %31
+         %30 = OpLabel
+         %32 = OpLoad %6 %8
+         %33 = OpISub %6 %32 %25
+               OpStore %8 %33
+               OpBranch %31
+         %31 = OpLabel
+         %34 = OpLoad %6 %8
+               OpSelectionMerge %37 None
+               OpSwitch %34 %36 0 %35
+         %36 = OpLabel
+               OpBranch %37
+         %35 = OpLabel
+         %39 = OpLoad %6 %8
+         %40 = OpIAdd %6 %39 %38
+               OpStore %8 %40
+               OpBranch %36
+         %37 = OpLabel
+               OpBranch %13
+         %13 = OpLabel
+         %43 = OpLoad %6 %8
+         %44 = OpIAdd %6 %43 %25
+               OpStore %8 %44
+               OpBranch %10
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+  FactManager fact_manager;
+
+  // %44 is not a block
+  ASSERT_FALSE(
+      TransformationSetSelectionControl(44, SpvSelectionControlFlattenMask)
+          .IsApplicable(context.get(), fact_manager));
+  // %13 does not end with OpSelectionMerge
+  ASSERT_FALSE(
+      TransformationSetSelectionControl(13, SpvSelectionControlMaskNone)
+          .IsApplicable(context.get(), fact_manager));
+  // %10 ends in OpLoopMerge, not OpSelectionMerge
+  ASSERT_FALSE(
+      TransformationSetSelectionControl(10, SpvSelectionControlMaskNone)
+          .IsApplicable(context.get(), fact_manager));
+
+  TransformationSetSelectionControl transformation1(
+      11, SpvSelectionControlDontFlattenMask);
+  ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
+  transformation1.Apply(context.get(), &fact_manager);
+
+  TransformationSetSelectionControl transformation2(
+      23, SpvSelectionControlFlattenMask);
+  ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
+  transformation2.Apply(context.get(), &fact_manager);
+
+  TransformationSetSelectionControl transformation3(
+      31, SpvSelectionControlMaskNone);
+  ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
+  transformation3.Apply(context.get(), &fact_manager);
+
+  TransformationSetSelectionControl transformation4(
+      31, SpvSelectionControlFlattenMask);
+  ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
+  transformation4.Apply(context.get(), &fact_manager);
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "i"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 0
+         %16 = OpConstant %6 10
+         %17 = OpTypeBool
+         %20 = OpConstant %6 3
+         %25 = OpConstant %6 1
+         %28 = OpConstant %6 2
+         %38 = OpConstant %6 4
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+               OpStore %8 %9
+               OpBranch %10
+         %10 = OpLabel
+               OpLoopMerge %12 %13 None
+               OpBranch %14
+         %14 = OpLabel
+         %15 = OpLoad %6 %8
+         %18 = OpSLessThan %17 %15 %16
+               OpBranchConditional %18 %11 %12
+         %11 = OpLabel
+         %19 = OpLoad %6 %8
+         %21 = OpSGreaterThan %17 %19 %20
+               OpSelectionMerge %23 DontFlatten
+               OpBranchConditional %21 %22 %23
+         %22 = OpLabel
+         %24 = OpLoad %6 %8
+         %26 = OpIAdd %6 %24 %25
+               OpStore %8 %26
+               OpBranch %23
+         %23 = OpLabel
+         %27 = OpLoad %6 %8
+         %29 = OpSLessThan %17 %27 %28
+               OpSelectionMerge %31 Flatten
+               OpBranchConditional %29 %30 %31
+         %30 = OpLabel
+         %32 = OpLoad %6 %8
+         %33 = OpISub %6 %32 %25
+               OpStore %8 %33
+               OpBranch %31
+         %31 = OpLabel
+         %34 = OpLoad %6 %8
+               OpSelectionMerge %37 Flatten
+               OpSwitch %34 %36 0 %35
+         %36 = OpLabel
+               OpBranch %37
+         %35 = OpLabel
+         %39 = OpLoad %6 %8
+         %40 = OpIAdd %6 %39 %38
+               OpStore %8 %40
+               OpBranch %36
+         %37 = OpLabel
+               OpBranch %13
+         %13 = OpLabel
+         %43 = OpLoad %6 %8
+         %44 = OpIAdd %6 %43 %25
+               OpStore %8 %44
+               OpBranch %10
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools

+ 3 - 5
3rdparty/spirv-tools/test/opt/if_conversion_test.cpp

@@ -375,14 +375,12 @@ OpEntryPoint Vertex %1 "func" %2
 OpSelectionMerge %12 None
 OpSelectionMerge %12 None
 OpBranchConditional %true %13 %12
 OpBranchConditional %true %13 %12
 %13 = OpLabel
 %13 = OpLabel
-OpBranchConditional %true %14 %15
+OpBranchConditional %true %14 %12
 %14 = OpLabel
 %14 = OpLabel
 OpBranch %12
 OpBranch %12
-%15 = OpLabel
-OpBranch %12
 %12 = OpLabel
 %12 = OpLabel
-%16 = OpPhi %uint %uint_0 %11 %uint_0 %14 %uint_1 %15
-OpStore %2 %16
+%15 = OpPhi %uint %uint_0 %11 %uint_0 %13 %uint_1 %14
+OpStore %2 %15
 OpReturn
 OpReturn
 OpFunctionEnd
 OpFunctionEnd
 )";
 )";

+ 86 - 1
3rdparty/spirv-tools/test/opt/ir_context_test.cpp

@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // See the License for the specific language governing permissions and
 // limitations under the License.
 // limitations under the License.
 
 
+#include "source/opt/ir_context.h"
+
 #include <algorithm>
 #include <algorithm>
 #include <memory>
 #include <memory>
 #include <string>
 #include <string>
@@ -19,7 +21,6 @@
 
 
 #include "gmock/gmock.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "gtest/gtest.h"
-#include "source/opt/ir_context.h"
 #include "source/opt/pass.h"
 #include "source/opt/pass.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_fixture.h"
 #include "test/opt/pass_utils.h"
 #include "test/opt/pass_utils.h"
@@ -664,6 +665,90 @@ OpFunctionEnd)";
   EXPECT_EQ(next_id_bound, 0);
   EXPECT_EQ(next_id_bound, 0);
   EXPECT_EQ(current_bound, context->module()->id_bound());
   EXPECT_EQ(current_bound, context->module()->id_bound());
 }
 }
+
+TEST_F(IRContextTest, CfgAndDomAnalysis) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%1 = OpTypeVoid
+%2 = OpTypeFunction %1
+%3 = OpFunction %1 None %2
+%4 = OpLabel
+OpReturn
+OpFunctionEnd)";
+
+  std::unique_ptr<IRContext> ctx =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
+                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  // Building the dominator analysis should build the CFG.
+  ASSERT_TRUE(ctx->module()->begin() != ctx->module()->end());
+  ctx->GetDominatorAnalysis(&*ctx->module()->begin());
+
+  EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisCFG));
+  EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisDominatorAnalysis));
+
+  // Invalidating the CFG analysis should invalidate the dominator analysis.
+  ctx->InvalidateAnalyses(IRContext::kAnalysisCFG);
+  EXPECT_FALSE(ctx->AreAnalysesValid(IRContext::kAnalysisCFG));
+  EXPECT_FALSE(ctx->AreAnalysesValid(IRContext::kAnalysisDominatorAnalysis));
+}
+
+TEST_F(IRContextTest, AsanErrorTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+               OpName %8 "x"
+               OpName %10 "y"
+               OpDecorate %8 RelaxedPrecision
+               OpDecorate %10 RelaxedPrecision
+               OpDecorate %11 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+               OpStore %8 %9
+         %11 = OpLoad %6 %8
+	       OpBranch %20
+	 %20 = OpLabel
+	 %21 = OpPhi %6 %11 %5
+         OpStore %10 %21
+         OpReturn
+         OpFunctionEnd
+  )";
+
+  const auto env = SPV_ENV_UNIVERSAL_1_3;
+  const auto consumer = nullptr;
+  const auto context = BuildModule(
+      env, consumer, shader, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+
+  opt::Function* fun =
+      context->cfg()->block(5)->GetParent();  // Computes the CFG analysis
+  opt::DominatorAnalysis* dom = nullptr;
+  dom = context->GetDominatorAnalysis(fun);  // Computes the dominator analysis,
+                                             // which depends on the CFG
+                                             // analysis
+  context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisDominatorAnalysis);  // Invalidates the
+                                                              // CFG analysis
+  dom = context->GetDominatorAnalysis(
+      fun);  // Recompute the CFG analysis because the Dominator tree uses it.
+  auto bb = dom->ImmediateDominator(5);
+  std::cout
+      << bb->id();  // Make sure asan does not complain about use after free.
+}
+
 }  // namespace
 }  // namespace
 }  // namespace opt
 }  // namespace opt
 }  // namespace spvtools
 }  // namespace spvtools

+ 6 - 1
3rdparty/spirv-tools/test/opt/type_manager_test.cpp

@@ -156,7 +156,8 @@ std::vector<std::unique_ptr<Type>> GenerateAllTypes() {
   types.emplace_back(new ReserveId());
   types.emplace_back(new ReserveId());
   types.emplace_back(new Queue());
   types.emplace_back(new Queue());
 
 
-  // Pipe, Forward Pointer, PipeStorage, NamedBarrier, AccelerationStructureNV
+  // Pipe, Forward Pointer, PipeStorage, NamedBarrier, AccelerationStructureNV,
+  // CooperativeMatrixNV
   types.emplace_back(new Pipe(SpvAccessQualifierReadWrite));
   types.emplace_back(new Pipe(SpvAccessQualifierReadWrite));
   types.emplace_back(new Pipe(SpvAccessQualifierReadOnly));
   types.emplace_back(new Pipe(SpvAccessQualifierReadOnly));
   types.emplace_back(new ForwardPointer(1, SpvStorageClassInput));
   types.emplace_back(new ForwardPointer(1, SpvStorageClassInput));
@@ -165,6 +166,7 @@ std::vector<std::unique_ptr<Type>> GenerateAllTypes() {
   types.emplace_back(new PipeStorage());
   types.emplace_back(new PipeStorage());
   types.emplace_back(new NamedBarrier());
   types.emplace_back(new NamedBarrier());
   types.emplace_back(new AccelerationStructureNV());
   types.emplace_back(new AccelerationStructureNV());
+  types.emplace_back(new CooperativeMatrixNV(f32, 24, 24, 24));
 
 
   return types;
   return types;
 }
 }
@@ -214,6 +216,7 @@ TEST(TypeManager, TypeStrings) {
     %arr_spec_const_with_id = OpTypeArray %s32 %spec_const_with_id
     %arr_spec_const_with_id = OpTypeArray %s32 %spec_const_with_id
     %arr_long_constant = OpTypeArray %s32 %long_constant
     %arr_long_constant = OpTypeArray %s32 %long_constant
     %arr_spec_const_op = OpTypeArray %s32 %spec_const_op
     %arr_spec_const_op = OpTypeArray %s32 %spec_const_op
+    %cm   = OpTypeCooperativeMatrixNV %f64 %id4 %id4 %id4
   )";
   )";
 
 
   std::vector<std::pair<uint32_t, std::string>> type_id_strs = {
   std::vector<std::pair<uint32_t, std::string>> type_id_strs = {
@@ -251,6 +254,7 @@ TEST(TypeManager, TypeStrings) {
       {36, "[sint32, id(1), words(1,99,42)]"},
       {36, "[sint32, id(1), words(1,99,42)]"},
       {37, "[sint32, id(33), words(0,705032704,1)]"},
       {37, "[sint32, id(33), words(0,705032704,1)]"},
       {38, "[sint32, id(34), words(2,34)]"},
       {38, "[sint32, id(34), words(2,34)]"},
+      {39, "<float64, 6, 6, 6>"},
   };
   };
 
 
   std::unique_ptr<IRContext> context =
   std::unique_ptr<IRContext> context =
@@ -1060,6 +1064,7 @@ TEST(TypeManager, GetTypeInstructionAllTypes) {
 ; CHECK: OpTypePipeStorage
 ; CHECK: OpTypePipeStorage
 ; CHECK: OpTypeNamedBarrier
 ; CHECK: OpTypeNamedBarrier
 ; CHECK: OpTypeAccelerationStructureNV
 ; CHECK: OpTypeAccelerationStructureNV
+; CHECK: OpTypeCooperativeMatrixNV [[f32]] [[uint24]] [[uint24]] [[uint24]]
 OpCapability Shader
 OpCapability Shader
 OpCapability Int64
 OpCapability Int64
 OpCapability Linkage
 OpCapability Linkage

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

@@ -23,8 +23,6 @@ add_spvtools_unittest(TARGET reduce
         reducer_test.cpp
         reducer_test.cpp
         remove_block_test.cpp
         remove_block_test.cpp
         remove_function_test.cpp
         remove_function_test.cpp
-        remove_opname_instruction_test.cpp
-        remove_relaxed_precision_decoration_test.cpp
         remove_selection_test.cpp
         remove_selection_test.cpp
         remove_unreferenced_instruction_test.cpp
         remove_unreferenced_instruction_test.cpp
         structured_loop_to_selection_test.cpp
         structured_loop_to_selection_test.cpp

+ 186 - 69
3rdparty/spirv-tools/test/reduce/reducer_test.cpp

@@ -14,8 +14,8 @@
 
 
 #include "source/reduce/reducer.h"
 #include "source/reduce/reducer.h"
 
 
+#include "source/opt/build_module.h"
 #include "source/reduce/operand_to_const_reduction_opportunity_finder.h"
 #include "source/reduce/operand_to_const_reduction_opportunity_finder.h"
-#include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
 #include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
 #include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
 #include "test/reduce/reduce_test_util.h"
 #include "test/reduce/reduce_test_util.h"
 
 
@@ -23,6 +23,12 @@ namespace spvtools {
 namespace reduce {
 namespace reduce {
 namespace {
 namespace {
 
 
+using opt::BasicBlock;
+using opt::IRContext;
+
+const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3;
+const MessageConsumer kMessageConsumer = CLIMessageConsumer;
+
 // This changes its mind each time IsInteresting is invoked as to whether the
 // This changes its mind each time IsInteresting is invoked as to whether the
 // binary is interesting, until some limit is reached after which the binary is
 // binary is interesting, until some limit is reached after which the binary is
 // always deemed interesting.  This is useful to test that reduction passes
 // always deemed interesting.  This is useful to test that reduction passes
@@ -55,6 +61,8 @@ class PingPongInteresting {
 };
 };
 
 
 TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
 TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
+  // Check that ExprToConstant and RemoveUnreferenced work together; once some
+  // ID uses have been changed to constants, those IDs can be removed.
   std::string original = R"(
   std::string original = R"(
                OpCapability Shader
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
           %1 = OpExtInstImport "GLSL.std.450"
@@ -149,15 +157,6 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
                OpMemoryModel Logical GLSL450
                OpMemoryModel Logical GLSL450
                OpEntryPoint Fragment %4 "main" %60
                OpEntryPoint Fragment %4 "main" %60
                OpExecutionMode %4 OriginUpperLeft
                OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-               OpName %4 "main"
-               OpName %16 "buf2"
-               OpMemberName %16 0 "i"
-               OpName %18 ""
-               OpName %25 "buf1"
-               OpMemberName %25 0 "f"
-               OpName %27 ""
-               OpName %60 "_GLF_color"
                OpMemberDecorate %16 0 Offset 0
                OpMemberDecorate %16 0 Offset 0
                OpDecorate %16 Block
                OpDecorate %16 Block
                OpDecorate %18 DescriptorSet 0
                OpDecorate %18 DescriptorSet 0
@@ -174,14 +173,12 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
          %16 = OpTypeStruct %6
          %16 = OpTypeStruct %6
          %17 = OpTypePointer Uniform %16
          %17 = OpTypePointer Uniform %16
          %18 = OpVariable %17 Uniform
          %18 = OpVariable %17 Uniform
-         %19 = OpTypePointer Uniform %6
          %22 = OpTypeBool
          %22 = OpTypeBool
         %100 = OpConstantTrue %22
         %100 = OpConstantTrue %22
          %24 = OpTypeFloat 32
          %24 = OpTypeFloat 32
          %25 = OpTypeStruct %24
          %25 = OpTypeStruct %24
          %26 = OpTypePointer Uniform %25
          %26 = OpTypePointer Uniform %25
          %27 = OpVariable %26 Uniform
          %27 = OpVariable %26 Uniform
-         %28 = OpTypePointer Uniform %24
          %31 = OpConstant %24 2
          %31 = OpConstant %24 2
          %56 = OpConstant %6 1
          %56 = OpConstant %6 1
          %58 = OpTypeVector %24 4
          %58 = OpTypeVector %24 4
@@ -209,8 +206,7 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
                OpFunctionEnd
                OpFunctionEnd
   )";
   )";
 
 
-  spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
-  Reducer reducer(env);
+  Reducer reducer(kEnv);
   PingPongInteresting ping_pong_interesting(10);
   PingPongInteresting ping_pong_interesting(10);
   reducer.SetMessageConsumer(NopDiagnostic);
   reducer.SetMessageConsumer(NopDiagnostic);
   reducer.SetInterestingnessFunction(
   reducer.SetInterestingnessFunction(
@@ -218,12 +214,13 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
         return ping_pong_interesting.IsInteresting(binary);
         return ping_pong_interesting.IsInteresting(binary);
       });
       });
   reducer.AddReductionPass(
   reducer.AddReductionPass(
-      MakeUnique<OperandToConstReductionOpportunityFinder>());
+      MakeUnique<RemoveUnreferencedInstructionReductionOpportunityFinder>(
+          false));
   reducer.AddReductionPass(
   reducer.AddReductionPass(
-      MakeUnique<RemoveUnreferencedInstructionReductionOpportunityFinder>());
+      MakeUnique<OperandToConstReductionOpportunityFinder>());
 
 
   std::vector<uint32_t> binary_in;
   std::vector<uint32_t> binary_in;
-  SpirvTools t(env);
+  SpirvTools t(kEnv);
 
 
   ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption));
   ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption));
   std::vector<uint32_t> binary_out;
   std::vector<uint32_t> binary_out;
@@ -237,71 +234,169 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
 
 
   ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete);
   ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete);
 
 
-  CheckEqual(env, expected, binary_out);
+  CheckEqual(kEnv, expected, binary_out);
 }
 }
 
 
-TEST(ReducerTest, RemoveOpnameAndRemoveUnreferenced) {
-  const std::string original = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %2 "main"
-               OpExecutionMode %2 OriginUpperLeft
-               OpSource ESSL 310
-               OpName %2 "main"
-               OpName %3 "a"
-               OpName %4 "this-name-counts-as-usage-for-load-instruction"
-          %5 = OpTypeVoid
-          %6 = OpTypeFunction %5
-          %7 = OpTypeFloat 32
-          %8 = OpTypePointer Function %7
-          %9 = OpConstant %7 1
-          %2 = OpFunction %5 None %6
-         %10 = OpLabel
-          %3 = OpVariable %8 Function
-          %4 = OpLoad %7 %3
-               OpStore %3 %9
-               OpReturn
-               OpFunctionEnd
-  )";
+bool InterestingWhileOpcodeExists(const std::vector<uint32_t>& binary,
+                                  uint32_t opcode, uint32_t count, bool dump) {
+  if (dump) {
+    std::stringstream ss;
+    ss << "temp_" << count << ".spv";
+    DumpShader(binary, ss.str().c_str());
+  }
+
+  std::unique_ptr<IRContext> context =
+      BuildModule(kEnv, kMessageConsumer, binary.data(), binary.size());
+  assert(context);
+  bool interesting = false;
+  for (auto& function : *context->module()) {
+    context->cfg()->ForEachBlockInPostOrder(
+        &*function.begin(), [opcode, &interesting](BasicBlock* block) -> void {
+          for (auto& inst : *block) {
+            if (inst.opcode() == opcode) {
+              interesting = true;
+              break;
+            }
+          }
+        });
+    if (interesting) {
+      break;
+    }
+  }
+  return interesting;
+}
+
+bool InterestingWhileIMulReachable(const std::vector<uint32_t>& binary,
+                                   uint32_t count) {
+  return InterestingWhileOpcodeExists(binary, SpvOpIMul, count, false);
+}
+
+bool InterestingWhileSDivReachable(const std::vector<uint32_t>& binary,
+                                   uint32_t count) {
+  return InterestingWhileOpcodeExists(binary, SpvOpSDiv, count, false);
+}
 
 
-  const std::string expected = R"(
+// The shader below was derived from the following GLSL, and optimized.
+// #version 310 es
+// precision highp float;
+// layout(location = 0) out vec4 _GLF_color;
+// int foo() {
+//    int x = 1;
+//    int y;
+//    x = y / x;   // SDiv
+//    return x;
+// }
+// void main() {
+//    int c;
+//    while (bool(c)) {
+//        do {
+//            if (bool(c)) {
+//                if (bool(c)) {
+//                    ++c;
+//                } else {
+//                    _GLF_color.x = float(c*c);  // IMul
+//                }
+//                return;
+//            }
+//        } while(bool(foo()));
+//        return;
+//    }
+// }
+const std::string kShaderWithLoopsDivAndMul = R"(
                OpCapability Shader
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
           %1 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
                OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %2 "main"
-               OpExecutionMode %2 OriginUpperLeft
+               OpEntryPoint Fragment %4 "main" %49
+               OpExecutionMode %4 OriginUpperLeft
                OpSource ESSL 310
                OpSource ESSL 310
-          %5 = OpTypeVoid
-          %6 = OpTypeFunction %5
-          %7 = OpTypeFloat 32
-          %8 = OpTypePointer Function %7
-          %9 = OpConstant %7 1
-          %2 = OpFunction %5 None %6
-         %10 = OpLabel
+               OpName %4 "main"
+               OpName %49 "_GLF_color"
+               OpDecorate %49 Location 0
+               OpDecorate %52 RelaxedPrecision
+               OpDecorate %77 RelaxedPrecision
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+         %12 = OpConstant %6 1
+         %27 = OpTypeBool
+         %28 = OpTypeInt 32 0
+         %29 = OpConstant %28 0
+         %46 = OpTypeFloat 32
+         %47 = OpTypeVector %46 4
+         %48 = OpTypePointer Output %47
+         %49 = OpVariable %48 Output
+         %54 = OpTypePointer Output %46
+         %64 = OpConstantFalse %27
+         %67 = OpConstantTrue %27
+         %81 = OpUndef %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpBranch %61
+         %61 = OpLabel
+               OpLoopMerge %60 %63 None
+               OpBranch %20
+         %20 = OpLabel
+         %30 = OpINotEqual %27 %81 %29
+               OpLoopMerge %22 %23 None
+               OpBranchConditional %30 %21 %22
+         %21 = OpLabel
+               OpBranch %31
+         %31 = OpLabel
+               OpLoopMerge %33 %38 None
+               OpBranch %32
+         %32 = OpLabel
+               OpSelectionMerge %38 None
+               OpBranchConditional %30 %37 %38
+         %37 = OpLabel
+               OpSelectionMerge %42 None
+               OpBranchConditional %30 %41 %45
+         %41 = OpLabel
+               OpBranch %42
+         %45 = OpLabel
+         %52 = OpIMul %6 %81 %81
+         %53 = OpConvertSToF %46 %52
+         %55 = OpAccessChain %54 %49 %29
+               OpStore %55 %53
+               OpBranch %42
+         %42 = OpLabel
+               OpBranch %33
+         %38 = OpLabel
+         %77 = OpSDiv %6 %81 %12
+         %58 = OpINotEqual %27 %77 %29
+               OpBranchConditional %58 %31 %33
+         %33 = OpLabel
+         %86 = OpPhi %27 %67 %42 %64 %38
+               OpSelectionMerge %68 None
+               OpBranchConditional %86 %22 %68
+         %68 = OpLabel
+               OpBranch %22
+         %23 = OpLabel
+               OpBranch %20
+         %22 = OpLabel
+         %90 = OpPhi %27 %64 %20 %86 %33 %67 %68
+               OpSelectionMerge %70 None
+               OpBranchConditional %90 %60 %70
+         %70 = OpLabel
+               OpBranch %60
+         %63 = OpLabel
+               OpBranch %61
+         %60 = OpLabel
                OpReturn
                OpReturn
                OpFunctionEnd
                OpFunctionEnd
   )";
   )";
 
 
-  spv_target_env env = SPV_ENV_UNIVERSAL_1_3;
-  Reducer reducer(env);
-  // Make ping-pong interesting very quickly, as there are not many
-  // opportunities.
-  PingPongInteresting ping_pong_interesting(1);
-  reducer.SetMessageConsumer(NopDiagnostic);
-  reducer.SetInterestingnessFunction(
-      [&](const std::vector<uint32_t>& binary, uint32_t) -> bool {
-        return ping_pong_interesting.IsInteresting(binary);
-      });
-  reducer.AddReductionPass(
-      MakeUnique<RemoveOpNameInstructionReductionOpportunityFinder>());
-  reducer.AddReductionPass(
-      MakeUnique<RemoveUnreferencedInstructionReductionOpportunityFinder>());
+TEST(ReducerTest, ShaderReduceWhileMulReachable) {
+  Reducer reducer(kEnv);
+
+  reducer.SetInterestingnessFunction(InterestingWhileIMulReachable);
+  reducer.AddDefaultReductionPasses();
+  reducer.SetMessageConsumer(kMessageConsumer);
 
 
   std::vector<uint32_t> binary_in;
   std::vector<uint32_t> binary_in;
-  SpirvTools t(env);
+  SpirvTools t(kEnv);
 
 
-  ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption));
+  ASSERT_TRUE(
+      t.Assemble(kShaderWithLoopsDivAndMul, &binary_in, kReduceAssembleOption));
   std::vector<uint32_t> binary_out;
   std::vector<uint32_t> binary_out;
   spvtools::ReducerOptions reducer_options;
   spvtools::ReducerOptions reducer_options;
   reducer_options.set_step_limit(500);
   reducer_options.set_step_limit(500);
@@ -312,8 +407,30 @@ TEST(ReducerTest, RemoveOpnameAndRemoveUnreferenced) {
       std::move(binary_in), &binary_out, reducer_options, validator_options);
       std::move(binary_in), &binary_out, reducer_options, validator_options);
 
 
   ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete);
   ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete);
+}
+
+TEST(ReducerTest, ShaderReduceWhileDivReachable) {
+  Reducer reducer(kEnv);
+
+  reducer.SetInterestingnessFunction(InterestingWhileSDivReachable);
+  reducer.AddDefaultReductionPasses();
+  reducer.SetMessageConsumer(kMessageConsumer);
+
+  std::vector<uint32_t> binary_in;
+  SpirvTools t(kEnv);
+
+  ASSERT_TRUE(
+      t.Assemble(kShaderWithLoopsDivAndMul, &binary_in, kReduceAssembleOption));
+  std::vector<uint32_t> binary_out;
+  spvtools::ReducerOptions reducer_options;
+  reducer_options.set_step_limit(500);
+  reducer_options.set_fail_on_validation_error(true);
+  spvtools::ValidatorOptions validator_options;
 
 
-  CheckEqual(env, expected, binary_out);
+  Reducer::ReductionResultStatus status = reducer.Run(
+      std::move(binary_in), &binary_out, reducer_options, validator_options);
+
+  ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete);
 }
 }
 
 
 }  // namespace
 }  // namespace

+ 0 - 225
3rdparty/spirv-tools/test/reduce/remove_opname_instruction_test.cpp

@@ -1,225 +0,0 @@
-// Copyright (c) 2018 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/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
-
-#include "source/opt/build_module.h"
-#include "source/reduce/reduction_opportunity.h"
-#include "source/reduce/reduction_pass.h"
-#include "test/reduce/reduce_test_util.h"
-
-namespace spvtools {
-namespace reduce {
-namespace {
-
-TEST(RemoveOpnameInstructionReductionPassTest, NothingToRemove) {
-  const std::string source = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-  const auto consumer = nullptr;
-  const auto context =
-      BuildModule(env, consumer, source, kReduceAssembleOption);
-  const auto ops = RemoveOpNameInstructionReductionOpportunityFinder()
-                       .GetAvailableOpportunities(context.get());
-  ASSERT_EQ(0, ops.size());
-}
-
-TEST(RemoveOpnameInstructionReductionPassTest, RemoveSingleOpName) {
-  const std::string prologue = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-  )";
-
-  const std::string epilogue = R"(
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const std::string original = prologue + R"(
-               OpName %4 "main"
-  )" + epilogue;
-
-  const std::string expected = prologue + epilogue;
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-  const auto consumer = nullptr;
-  const auto context =
-      BuildModule(env, consumer, original, kReduceAssembleOption);
-  const auto ops = RemoveOpNameInstructionReductionOpportunityFinder()
-                       .GetAvailableOpportunities(context.get());
-  ASSERT_EQ(1, ops.size());
-  ASSERT_TRUE(ops[0]->PreconditionHolds());
-  ops[0]->TryToApply();
-
-  CheckEqual(env, expected, context.get());
-}
-
-TEST(RemoveOpnameInstructionReductionPassTest, TryApplyRemovesAllOpName) {
-  const std::string prologue = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-  )";
-
-  const std::string epilogue = R"(
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %6 = OpTypeFloat 32
-          %7 = OpTypePointer Function %6
-          %9 = OpConstant %6 1
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-          %8 = OpVariable %7 Function
-         %10 = OpVariable %7 Function
-         %11 = OpVariable %7 Function
-         %12 = OpVariable %7 Function
-               OpStore %8 %9
-               OpStore %10 %9
-               OpStore %11 %9
-               OpStore %12 %9
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const std::string original = prologue + R"(
-               OpName %4 "main"
-               OpName %8 "a"
-               OpName %10 "b"
-               OpName %11 "c"
-               OpName %12 "d"
-  )" + epilogue;
-
-  const std::string expected = prologue + epilogue;
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-
-  {
-    // Check the right number of opportunities is detected
-    const auto consumer = nullptr;
-    const auto context =
-        BuildModule(env, consumer, original, kReduceAssembleOption);
-    const auto ops = RemoveOpNameInstructionReductionOpportunityFinder()
-                         .GetAvailableOpportunities(context.get());
-    ASSERT_EQ(5, ops.size());
-  }
-
-  {
-    // The reduction should remove all OpName
-    std::vector<uint32_t> binary;
-    SpirvTools t(env);
-    ASSERT_TRUE(t.Assemble(original, &binary, kReduceAssembleOption));
-    auto reduced_binary =
-        ReductionPass(env,
-                      spvtools::MakeUnique<
-                          RemoveOpNameInstructionReductionOpportunityFinder>())
-            .TryApplyReduction(binary);
-    CheckEqual(env, expected, reduced_binary);
-  }
-}
-
-TEST(RemoveOpnameInstructionReductionPassTest,
-     TryApplyRemovesAllOpNameAndOpMemberName) {
-  const std::string prologue = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-  )";
-
-  const std::string epilogue = R"(
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %6 = OpTypeFloat 32
-          %7 = OpTypeInt 32 1
-          %8 = OpTypeVector %6 3
-          %9 = OpTypeStruct %6 %7 %8
-         %10 = OpTypePointer Function %9
-         %12 = OpConstant %7 0
-         %13 = OpConstant %6 1
-         %14 = OpTypePointer Function %6
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-         %11 = OpVariable %10 Function
-         %15 = OpAccessChain %14 %11 %12
-               OpStore %15 %13
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const std::string original = prologue + R"(
-               OpName %4 "main"
-               OpName %9 "S"
-               OpMemberName %9 0 "f"
-               OpMemberName %9 1 "i"
-               OpMemberName %9 2 "v"
-               OpName %11 "s"
-  )" + epilogue;
-
-  const std::string expected = prologue + epilogue;
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-
-  {
-    // Check the right number of opportunities is detected
-    const auto consumer = nullptr;
-    const auto context =
-        BuildModule(env, consumer, original, kReduceAssembleOption);
-    const auto ops = RemoveOpNameInstructionReductionOpportunityFinder()
-                         .GetAvailableOpportunities(context.get());
-    ASSERT_EQ(6, ops.size());
-  }
-
-  {
-    // The reduction should remove all OpName
-    std::vector<uint32_t> binary;
-    SpirvTools t(env);
-    ASSERT_TRUE(t.Assemble(original, &binary, kReduceAssembleOption));
-    auto reduced_binary =
-        ReductionPass(env,
-                      spvtools::MakeUnique<
-                          RemoveOpNameInstructionReductionOpportunityFinder>())
-            .TryApplyReduction(binary);
-    CheckEqual(env, expected, reduced_binary);
-  }
-}
-
-}  // namespace
-}  // namespace reduce
-}  // namespace spvtools

+ 0 - 177
3rdparty/spirv-tools/test/reduce/remove_relaxed_precision_decoration_test.cpp

@@ -1,177 +0,0 @@
-// Copyright (c) 2019 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "source/reduce/remove_relaxed_precision_decoration_opportunity_finder.h"
-
-#include "source/opt/build_module.h"
-#include "source/reduce/reduction_opportunity.h"
-#include "source/reduce/reduction_pass.h"
-#include "test/reduce/reduce_test_util.h"
-
-namespace spvtools {
-namespace reduce {
-namespace {
-
-TEST(RemoveRelaxedPrecisionDecorationTest, NothingToRemove) {
-  const std::string source = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-  const auto consumer = nullptr;
-  const auto context =
-      BuildModule(env, consumer, source, kReduceAssembleOption);
-  const auto ops = RemoveRelaxedPrecisionDecorationOpportunityFinder()
-                       .GetAvailableOpportunities(context.get());
-  ASSERT_EQ(0, ops.size());
-}
-
-TEST(RemoveRelaxedPrecisionDecorationTest, RemoveDecorations) {
-  const std::string source = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-               OpName %4 "main"
-               OpName %8 "f"
-               OpName %12 "i"
-               OpName %16 "v"
-               OpName %19 "S"
-               OpMemberName %19 0 "a"
-               OpMemberName %19 1 "b"
-               OpMemberName %19 2 "c"
-               OpName %21 "s"
-               OpDecorate %8 RelaxedPrecision
-               OpDecorate %12 RelaxedPrecision
-               OpDecorate %16 RelaxedPrecision
-               OpDecorate %17 RelaxedPrecision
-               OpDecorate %18 RelaxedPrecision
-               OpMemberDecorate %19 0 RelaxedPrecision
-               OpMemberDecorate %19 1 RelaxedPrecision
-               OpMemberDecorate %19 2 RelaxedPrecision
-               OpDecorate %22 RelaxedPrecision
-               OpDecorate %23 RelaxedPrecision
-               OpDecorate %24 RelaxedPrecision
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %6 = OpTypeFloat 32
-          %7 = OpTypePointer Function %6
-          %9 = OpConstant %6 2
-         %10 = OpTypeInt 32 1
-         %11 = OpTypePointer Function %10
-         %13 = OpConstant %10 22
-         %14 = OpTypeVector %6 2
-         %15 = OpTypePointer Function %14
-         %19 = OpTypeStruct %10 %6 %14
-         %20 = OpTypePointer Function %19
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-          %8 = OpVariable %7 Function
-         %12 = OpVariable %11 Function
-         %16 = OpVariable %15 Function
-         %21 = OpVariable %20 Function
-               OpStore %8 %9
-               OpStore %12 %13
-         %17 = OpLoad %6 %8
-         %18 = OpCompositeConstruct %14 %17 %17
-               OpStore %16 %18
-         %22 = OpLoad %10 %12
-         %23 = OpLoad %6 %8
-         %24 = OpLoad %14 %16
-         %25 = OpCompositeConstruct %19 %22 %23 %24
-               OpStore %21 %25
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-  const auto consumer = nullptr;
-  const auto context =
-      BuildModule(env, consumer, source, kReduceAssembleOption);
-  const auto ops = RemoveRelaxedPrecisionDecorationOpportunityFinder()
-                       .GetAvailableOpportunities(context.get());
-  ASSERT_EQ(11, ops.size());
-
-  for (auto& op : ops) {
-    ASSERT_TRUE(op->PreconditionHolds());
-    op->TryToApply();
-  }
-
-  const std::string expected = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %4 "main"
-               OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-               OpName %4 "main"
-               OpName %8 "f"
-               OpName %12 "i"
-               OpName %16 "v"
-               OpName %19 "S"
-               OpMemberName %19 0 "a"
-               OpMemberName %19 1 "b"
-               OpMemberName %19 2 "c"
-               OpName %21 "s"
-          %2 = OpTypeVoid
-          %3 = OpTypeFunction %2
-          %6 = OpTypeFloat 32
-          %7 = OpTypePointer Function %6
-          %9 = OpConstant %6 2
-         %10 = OpTypeInt 32 1
-         %11 = OpTypePointer Function %10
-         %13 = OpConstant %10 22
-         %14 = OpTypeVector %6 2
-         %15 = OpTypePointer Function %14
-         %19 = OpTypeStruct %10 %6 %14
-         %20 = OpTypePointer Function %19
-          %4 = OpFunction %2 None %3
-          %5 = OpLabel
-          %8 = OpVariable %7 Function
-         %12 = OpVariable %11 Function
-         %16 = OpVariable %15 Function
-         %21 = OpVariable %20 Function
-               OpStore %8 %9
-               OpStore %12 %13
-         %17 = OpLoad %6 %8
-         %18 = OpCompositeConstruct %14 %17 %17
-               OpStore %16 %18
-         %22 = OpLoad %10 %12
-         %23 = OpLoad %6 %8
-         %24 = OpLoad %14 %16
-         %25 = OpCompositeConstruct %19 %22 %23 %24
-               OpStore %21 %25
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CheckEqual(env, expected, context.get());
-}
-
-}  // namespace
-}  // namespace reduce
-}  // namespace spvtools

+ 322 - 43
3rdparty/spirv-tools/test/reduce/remove_unreferenced_instruction_test.cpp

@@ -23,19 +23,26 @@ namespace spvtools {
 namespace reduce {
 namespace reduce {
 namespace {
 namespace {
 
 
+const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3;
+
 TEST(RemoveUnreferencedInstructionReductionPassTest, RemoveStores) {
 TEST(RemoveUnreferencedInstructionReductionPassTest, RemoveStores) {
-  const std::string prologue = R"(
+  // A module with some unused instructions, including some unused OpStore
+  // instructions.
+
+  RemoveUnreferencedInstructionReductionOpportunityFinder finder(true);
+
+  const std::string original = R"(
                OpCapability Shader
                OpCapability Shader
           %1 = OpExtInstImport "GLSL.std.450"
           %1 = OpExtInstImport "GLSL.std.450"
                OpMemoryModel Logical GLSL450
                OpMemoryModel Logical GLSL450
                OpEntryPoint Fragment %4 "main"
                OpEntryPoint Fragment %4 "main"
                OpExecutionMode %4 OriginUpperLeft
                OpExecutionMode %4 OriginUpperLeft
-               OpSource ESSL 310
-               OpName %4 "main"
-               OpName %8 "a"
-               OpName %10 "b"
-               OpName %12 "c"
-               OpName %14 "d"
+               OpSource ESSL 310  ; 0
+               OpName %4 "main"   ; 1
+               OpName %8 "a"      ; 2
+               OpName %10 "b"     ; 3
+               OpName %12 "c"     ; 4
+               OpName %14 "d"     ; 5
           %2 = OpTypeVoid
           %2 = OpTypeVoid
           %3 = OpTypeFunction %2
           %3 = OpTypeFunction %2
           %6 = OpTypeInt 32 1
           %6 = OpTypeInt 32 1
@@ -49,51 +56,323 @@ TEST(RemoveUnreferencedInstructionReductionPassTest, RemoveStores) {
          %10 = OpVariable %7 Function
          %10 = OpVariable %7 Function
          %12 = OpVariable %7 Function
          %12 = OpVariable %7 Function
          %14 = OpVariable %7 Function
          %14 = OpVariable %7 Function
+               OpStore %8 %9           ; 6
+               OpStore %10 %11         ; 7
+               OpStore %12 %13         ; 8
+         %15 = OpLoad %6 %8
+               OpStore %14 %15         ; 9
+               OpReturn
+               OpFunctionEnd
+
   )";
   )";
 
 
-  const std::string epilogue = R"(
+  const MessageConsumer consumer = nullptr;
+  const auto context =
+      BuildModule(kEnv, consumer, original, kReduceAssembleOption);
+
+  CheckValid(kEnv, context.get());
+
+  auto ops = finder.GetAvailableOpportunities(context.get());
+
+  ASSERT_EQ(10, ops.size());
+
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+    CheckValid(kEnv, context.get());
+  }
+
+  const std::string step_2 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 10       ; 0
+         %11 = OpConstant %6 20       ; 1
+         %13 = OpConstant %6 30       ; 2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function ; 3
+         %12 = OpVariable %7 Function ; 4
+         %14 = OpVariable %7 Function ; 5
+         %15 = OpLoad %6 %8           ; 6
                OpReturn
                OpReturn
                OpFunctionEnd
                OpFunctionEnd
   )";
   )";
 
 
-  const std::string original = prologue + R"(
-               OpStore %8 %9
-               OpStore %10 %11
-               OpStore %12 %13
-         %15 = OpLoad %6 %8
-               OpStore %14 %15
-  )" + epilogue;
+  CheckEqual(kEnv, step_2, context.get());
 
 
-  const std::string expected_after_2 = prologue + R"(
-               OpStore %12 %13
-         %15 = OpLoad %6 %8
-               OpStore %14 %15
-  )" + epilogue;
+  ops = finder.GetAvailableOpportunities(context.get());
 
 
-  const std::string expected_after_4 = prologue + R"(
-         %15 = OpLoad %6 %8
-  )" + epilogue;
+  ASSERT_EQ(7, ops.size());
 
 
-  const auto env = SPV_ENV_UNIVERSAL_1_3;
-  const auto consumer = nullptr;
-  const auto context =
-      BuildModule(env, consumer, original, kReduceAssembleOption);
-  const auto ops = RemoveUnreferencedInstructionReductionOpportunityFinder()
-                       .GetAvailableOpportunities(context.get());
-  ASSERT_EQ(4, ops.size());
-  ASSERT_TRUE(ops[0]->PreconditionHolds());
-  ops[0]->TryToApply();
-  ASSERT_TRUE(ops[1]->PreconditionHolds());
-  ops[1]->TryToApply();
-
-  CheckEqual(env, expected_after_2, context.get());
-
-  ASSERT_TRUE(ops[2]->PreconditionHolds());
-  ops[2]->TryToApply();
-  ASSERT_TRUE(ops[3]->PreconditionHolds());
-  ops[3]->TryToApply();
-
-  CheckEqual(env, expected_after_4, context.get());
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+    CheckValid(kEnv, context.get());
+  }
+
+  const std::string step_3 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function   ; 0
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CheckEqual(kEnv, step_3, context.get());
+
+  ops = finder.GetAvailableOpportunities(context.get());
+
+  ASSERT_EQ(1, ops.size());
+
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+    CheckValid(kEnv, context.get());
+  }
+
+  const std::string step_4 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6  ; 0
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CheckEqual(kEnv, step_4, context.get());
+
+  ops = finder.GetAvailableOpportunities(context.get());
+
+  ASSERT_EQ(1, ops.size());
+
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+    CheckValid(kEnv, context.get());
+  }
+
+  const std::string step_5 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1        ; 0
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CheckEqual(kEnv, step_5, context.get());
+
+  ops = finder.GetAvailableOpportunities(context.get());
+
+  ASSERT_EQ(1, ops.size());
+
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+    CheckValid(kEnv, context.get());
+  }
+
+  const std::string step_6 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CheckEqual(kEnv, step_6, context.get());
+
+  ops = finder.GetAvailableOpportunities(context.get());
+
+  ASSERT_EQ(0, ops.size());
+}
+
+TEST(RemoveUnreferencedInstructionReductionPassTest, Referenced) {
+  // A module with some unused global variables, constants, and types. Some will
+  // not be removed initially because of the OpDecorate instructions.
+
+  RemoveUnreferencedInstructionReductionOpportunityFinder finder(true);
+
+  const std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310                 ; 1
+               OpName %4 "main"                  ; 2
+               OpName %12 "a"                    ; 3
+               OpDecorate %12 RelaxedPrecision   ; 4
+               OpDecorate %13 RelaxedPrecision   ; 5
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool
+          %7 = OpConstantTrue %6                 ; 6
+         %10 = OpTypeInt 32 1
+         %11 = OpTypePointer Private %10
+         %12 = OpVariable %11 Private
+         %13 = OpConstant %10 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
+
+  CheckValid(kEnv, context.get());
+
+  auto ops = finder.GetAvailableOpportunities(context.get());
+
+  ASSERT_EQ(6, ops.size());
+
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+    CheckValid(kEnv, context.get());
+  }
+
+  std::string after = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeBool                 ; 1
+         %10 = OpTypeInt 32 1
+         %11 = OpTypePointer Private %10
+         %12 = OpVariable %11 Private     ; 2
+         %13 = OpConstant %10 1           ; 3
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+    )";
+
+  CheckEqual(kEnv, after, context.get());
+
+  ops = finder.GetAvailableOpportunities(context.get());
+
+  ASSERT_EQ(3, ops.size());
+
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+    CheckValid(kEnv, context.get());
+  }
+
+  std::string after_2 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %10 = OpTypeInt 32 1
+         %11 = OpTypePointer Private %10   ; 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+    )";
+
+  CheckEqual(kEnv, after_2, context.get());
+
+  ops = finder.GetAvailableOpportunities(context.get());
+
+  ASSERT_EQ(1, ops.size());
+
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+    CheckValid(kEnv, context.get());
+  }
+
+  std::string after_3 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+         %10 = OpTypeInt 32 1          ; 1
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+    )";
+
+  CheckEqual(kEnv, after_3, context.get());
+
+  ops = finder.GetAvailableOpportunities(context.get());
+
+  ASSERT_EQ(1, ops.size());
+
+  for (auto& op : ops) {
+    ASSERT_TRUE(op->PreconditionHolds());
+    op->TryToApply();
+    CheckValid(kEnv, context.get());
+  }
+
+  std::string after_4 = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main"
+               OpExecutionMode %4 OriginUpperLeft
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+    )";
+
+  CheckEqual(kEnv, after_4, context.get());
+
+  ops = finder.GetAvailableOpportunities(context.get());
+
+  ASSERT_EQ(0, ops.size());
 }
 }
 
 
 }  // namespace
 }  // namespace

+ 351 - 1
3rdparty/spirv-tools/test/val/val_cfg_test.cpp

@@ -3026,6 +3026,7 @@ OpMemoryModel Logical GLSL450
 %undef = OpUndef %bool
 %undef = OpUndef %bool
 %func = OpFunction %void None %void_fn
 %func = OpFunction %void None %void_fn
 %entry = OpLabel
 %entry = OpLabel
+OpSelectionMerge %block None
 OpBranchConditional %undef %block %unreachable
 OpBranchConditional %undef %block %unreachable
 %block = OpLabel
 %block = OpLabel
 OpReturn
 OpReturn
@@ -3049,6 +3050,7 @@ OpMemoryModel Logical GLSL450
 %undef = OpUndef %int
 %undef = OpUndef %int
 %func = OpFunction %void None %void_fn
 %func = OpFunction %void None %void_fn
 %entry = OpLabel
 %entry = OpLabel
+OpSelectionMerge %block1 None
 OpSwitch %undef %block1 0 %unreachable 1 %block2
 OpSwitch %undef %block1 0 %unreachable 1 %block2
 %block1 = OpLabel
 %block1 = OpLabel
 OpReturn
 OpReturn
@@ -3748,7 +3750,355 @@ OpFunctionEnd
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 }
 
 
-/// TODO(umar): Nested CFG constructs
+TEST_F(ValidateCFG, MissingMergeConditionalBranchBad) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpBranchConditional %undef %then %else
+%then = OpLabel
+OpReturn
+%else = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
+}
+
+TEST_F(ValidateCFG, MissingMergeSwitchBad) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%undef = OpUndef %int
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpSwitch %undef %then 0 %else
+%then = OpLabel
+OpReturn
+%else = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
+}
+
+TEST_F(ValidateCFG, MissingMergeSwitchBad2) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%undef = OpUndef %int
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpSwitch %undef %then 0 %then 1 %then 2 %else
+%then = OpLabel
+OpReturn
+%else = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
+}
+
+TEST_F(ValidateCFG, MissingMergeOneBranchToMergeGood) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpSelectionMerge %b3 None
+OpBranchConditional %undef %b1 %b2
+%b1 = OpLabel
+OpBranchConditional %undef %b2 %b3
+%b2 = OpLabel
+OpBranch %b3
+%b3 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, MissingMergeSameTargetConditionalBranchGood) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpBranchConditional %undef %then %then
+%then = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, MissingMergeOneTargetSwitchGood) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%undef = OpUndef %int
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpSwitch %undef %then 0 %then 1 %then
+%then = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, MissingMergeOneUnseenTargetSwitchGood) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%undef_int = OpUndef %int
+%bool = OpTypeBool
+%undef_bool = OpUndef %bool
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpSelectionMerge %merge None
+OpBranchConditional %undef_bool %merge %b1
+%b1 = OpLabel
+OpSwitch %undef_int %b2 0 %b2 1 %merge 2 %b2
+%b2 = OpLabel
+OpBranch %merge
+%merge = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, MissingMergeLoopBreakGood) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpBranch %loop
+%loop = OpLabel
+OpLoopMerge %exit %continue None
+OpBranch %body
+%body = OpLabel
+OpBranchConditional %undef %body2 %exit
+%body2 = OpLabel
+OpBranch %continue
+%continue = OpLabel
+OpBranch %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, MissingMergeLoopContinueGood) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpBranch %loop
+%loop = OpLabel
+OpLoopMerge %exit %continue None
+OpBranch %body
+%body = OpLabel
+OpBranchConditional %undef %body2 %continue
+%body2 = OpLabel
+OpBranch %continue
+%continue = OpLabel
+OpBranch %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, MissingMergeSwitchBreakGood) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpSelectionMerge %merge None
+OpSwitch %int_0 %merge 1 %b1
+%b1 = OpLabel
+OpBranchConditional %undef %merge %b2
+%b2 = OpLabel
+OpBranch %merge
+%merge = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, MissingMergeSwitchFallThroughGood) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpSelectionMerge %merge None
+OpSwitch %int_0 %b1 1 %b2
+%b1 = OpLabel
+OpBranchConditional %undef %b3 %b2
+%b2 = OpLabel
+OpBranch %merge
+%b3 = OpLabel
+OpBranch %merge
+%merge = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, MissingMergeInALoopBad) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpBranch %loop
+%loop = OpLabel
+OpLoopMerge %exit %continue None
+OpBranch %body
+%body = OpLabel
+OpBranchConditional %undef %b1 %b2
+%b1 = OpLabel
+OpBranch %exit
+%b2 = OpLabel
+OpBranch %continue
+%continue = OpLabel
+OpBranch %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
+}
+
+TEST_F(ValidateCFG, MissingMergeCrissCrossBad) {
+  const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%bool = OpTypeBool
+%undef = OpUndef %bool
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpSelectionMerge %merge None
+OpBranchConditional %undef %b1 %b2
+%b1 = OpLabel
+OpBranchConditional %undef %b3 %b4
+%b2 = OpLabel
+OpBranchConditional %undef %b3 %b4
+%b3 = OpLabel
+OpBranch %merge
+%b4 = OpLabel
+OpBranch %merge
+%merge = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(text);
+  EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
+}
 
 
 }  // namespace
 }  // namespace
 }  // namespace val
 }  // namespace val

+ 2 - 1
3rdparty/spirv-tools/test/val/val_misc_test.cpp

@@ -144,7 +144,8 @@ OpFunctionEnd)";
   EXPECT_THAT(getDiagnosticString(), HasSubstr("vector of two components"));
   EXPECT_THAT(getDiagnosticString(), HasSubstr("vector of two components"));
 }
 }
 
 
-TEST_F(ValidateMisc, ShaderClockExecutionScope) {
+// #2952: disabled until scope discussion is resolved.
+TEST_F(ValidateMisc, DISABLED_ShaderClockExecutionScope) {
   const std::string spirv = ShaderClockSpriv + R"(
   const std::string spirv = ShaderClockSpriv + R"(
 %3 = OpTypeFunction %void
 %3 = OpTypeFunction %void
 %ulong = OpTypeInt 64 0
 %ulong = OpTypeInt 64 0