Browse Source

Updated spirv-tools.

Бранимир Караџић 5 years ago
parent
commit
f16ab74475
74 changed files with 3122 additions and 324 deletions
  1. 1 1
      3rdparty/spirv-tools/include/generated/build-version.inc
  2. 1 1
      3rdparty/spirv-tools/source/CMakeLists.txt
  3. 19 1
      3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
  4. 16 0
      3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
  5. 18 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp
  6. 20 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_context.h
  7. 62 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp
  8. 10 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h
  9. 69 16
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
  10. 8 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.h
  11. 66 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp
  12. 43 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.h
  13. 7 7
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp
  14. 116 17
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp
  15. 13 3
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.h
  16. 7 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp
  17. 7 1
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
  18. 71 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp
  19. 40 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h
  20. 98 28
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp
  21. 25 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.h
  22. 64 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.cpp
  23. 40 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.h
  24. 64 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp
  25. 40 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.h
  26. 76 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp
  27. 42 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h
  28. 20 16
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp
  29. 39 9
      3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
  30. 10 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
  31. 1 1
      3rdparty/spirv-tools/source/fuzz/id_use_descriptor.cpp
  32. 66 0
      3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto
  33. 21 3
      3rdparty/spirv-tools/source/fuzz/transformation.cpp
  34. 19 11
      3rdparty/spirv-tools/source/fuzz/transformation_add_function.cpp
  35. 6 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_function.h
  36. 24 39
      3rdparty/spirv-tools/source/fuzz/transformation_add_parameter.cpp
  37. 22 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_type_float.cpp
  38. 28 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_type_int.cpp
  39. 111 0
      3rdparty/spirv-tools/source/fuzz/transformation_make_vector_operation_dynamic.cpp
  40. 63 0
      3rdparty/spirv-tools/source/fuzz/transformation_make_vector_operation_dynamic.h
  41. 219 0
      3rdparty/spirv-tools/source/fuzz/transformation_move_instruction_down.cpp
  42. 62 0
      3rdparty/spirv-tools/source/fuzz/transformation_move_instruction_down.h
  43. 402 0
      3rdparty/spirv-tools/source/fuzz/transformation_propagate_instruction_up.cpp
  44. 89 0
      3rdparty/spirv-tools/source/fuzz/transformation_propagate_instruction_up.h
  45. 232 0
      3rdparty/spirv-tools/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp
  46. 69 0
      3rdparty/spirv-tools/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h
  47. 48 3
      3rdparty/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.cpp
  48. 5 0
      3rdparty/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.h
  49. 0 8
      3rdparty/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.cpp
  50. 1 1
      3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp
  51. 2 10
      3rdparty/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp
  52. 38 23
      3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp
  53. 6 0
      3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.h
  54. 12 0
      3rdparty/spirv-tools/source/opcode.cpp
  55. 3 0
      3rdparty/spirv-tools/source/opcode.h
  56. 1 1
      3rdparty/spirv-tools/source/opt/CMakeLists.txt
  57. 3 8
      3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp
  58. 0 3
      3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.h
  59. 1 0
      3rdparty/spirv-tools/source/opt/dead_insert_elim_pass.cpp
  60. 28 2
      3rdparty/spirv-tools/source/opt/eliminate_dead_functions_util.cpp
  61. 1 1
      3rdparty/spirv-tools/source/opt/folding_rules.cpp
  62. 44 10
      3rdparty/spirv-tools/source/opt/function.cpp
  63. 25 8
      3rdparty/spirv-tools/source/opt/function.h
  64. 25 12
      3rdparty/spirv-tools/source/opt/instruction.cpp
  65. 23 20
      3rdparty/spirv-tools/source/opt/instruction.h
  66. 24 0
      3rdparty/spirv-tools/source/opt/ir_context.cpp
  67. 3 0
      3rdparty/spirv-tools/source/opt/ir_context.h
  68. 12 3
      3rdparty/spirv-tools/source/opt/ir_loader.cpp
  69. 25 2
      3rdparty/spirv-tools/source/opt/local_access_chain_convert_pass.cpp
  70. 13 17
      3rdparty/spirv-tools/source/opt/loop_descriptor.cpp
  71. 7 3
      3rdparty/spirv-tools/source/opt/module.cpp
  72. 1 1
      3rdparty/spirv-tools/source/reduce/CMakeLists.txt
  73. 212 34
      3rdparty/spirv-tools/source/val/validate_extensions.cpp
  74. 13 0
      3rdparty/spirv-tools/source/val/validate_interfaces.cpp

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

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

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

@@ -414,7 +414,7 @@ if(ENABLE_SPIRV_TOOLS_INSTALL)
   install(FILES ${CMAKE_BINARY_DIR}/${SPIRV_TOOLS}Config.cmake DESTINATION ${PACKAGE_DIR})
 endif(ENABLE_SPIRV_TOOLS_INSTALL)
 
-if(MSVC)
+if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")))
   # Enable parallel builds across four cores for this lib
   add_definitions(/MP4)
 endif()

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

@@ -50,6 +50,7 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_add_synonyms.h
         fuzzer_pass_add_loads.h
         fuzzer_pass_add_local_variables.h
+        fuzzer_pass_add_loop_preheaders.h
         fuzzer_pass_add_no_contraction_decorations.h
         fuzzer_pass_add_parameters.h
         fuzzer_pass_add_relaxed_decorations.h
@@ -67,13 +68,17 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_invert_comparison_operators.h
         fuzzer_pass_interchange_signedness_of_integer_operands.h
         fuzzer_pass_interchange_zero_like_constants.h
+        fuzzer_pass_make_vector_operations_dynamic.h
         fuzzer_pass_merge_blocks.h
         fuzzer_pass_obfuscate_constants.h
         fuzzer_pass_outline_functions.h
         fuzzer_pass_permute_blocks.h
         fuzzer_pass_permute_function_parameters.h
+        fuzzer_pass_permute_instructions.h
         fuzzer_pass_permute_phi_operands.h
+        fuzzer_pass_propagate_instructions_up.h
         fuzzer_pass_push_ids_through_variables.h
+        fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h
         fuzzer_pass_replace_copy_memories_with_loads_stores.h
         fuzzer_pass_replace_copy_objects_with_stores_loads.h
         fuzzer_pass_replace_linear_algebra_instructions.h
@@ -132,13 +137,17 @@ if(SPIRV_BUILD_FUZZER)
         transformation_function_call.h
         transformation_invert_comparison_operator.h
         transformation_load.h
+        transformation_make_vector_operation_dynamic.h
         transformation_merge_blocks.h
         transformation_move_block_down.h
+        transformation_move_instruction_down.h
         transformation_outline_function.h
         transformation_permute_function_parameters.h
         transformation_permute_phi_operands.h
+        transformation_propagate_instruction_up.h
         transformation_push_id_through_variable.h
         transformation_record_synonymous_constants.h
+        transformation_replace_add_sub_mul_with_carrying_extended.h
         transformation_replace_boolean_constant_with_constant_binary.h
         transformation_replace_constant_with_uniform.h
         transformation_replace_copy_memory_with_load_store.h
@@ -181,6 +190,7 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_add_synonyms.cpp
         fuzzer_pass_add_loads.cpp
         fuzzer_pass_add_local_variables.cpp
+        fuzzer_pass_add_loop_preheaders.cpp
         fuzzer_pass_add_no_contraction_decorations.cpp
         fuzzer_pass_add_parameters.cpp
         fuzzer_pass_add_relaxed_decorations.cpp
@@ -198,13 +208,17 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_invert_comparison_operators.cpp
         fuzzer_pass_interchange_signedness_of_integer_operands.cpp
         fuzzer_pass_interchange_zero_like_constants.cpp
+        fuzzer_pass_make_vector_operations_dynamic.cpp
         fuzzer_pass_merge_blocks.cpp
         fuzzer_pass_obfuscate_constants.cpp
         fuzzer_pass_outline_functions.cpp
         fuzzer_pass_permute_blocks.cpp
         fuzzer_pass_permute_function_parameters.cpp
+        fuzzer_pass_permute_instructions.cpp
         fuzzer_pass_permute_phi_operands.cpp
+        fuzzer_pass_propagate_instructions_up.cpp
         fuzzer_pass_push_ids_through_variables.cpp
+        fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp
         fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
         fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
         fuzzer_pass_replace_linear_algebra_instructions.cpp
@@ -262,13 +276,17 @@ if(SPIRV_BUILD_FUZZER)
         transformation_function_call.cpp
         transformation_invert_comparison_operator.cpp
         transformation_load.cpp
+        transformation_make_vector_operation_dynamic.cpp
         transformation_merge_blocks.cpp
         transformation_move_block_down.cpp
+        transformation_move_instruction_down.cpp
         transformation_outline_function.cpp
         transformation_permute_function_parameters.cpp
         transformation_permute_phi_operands.cpp
+        transformation_propagate_instruction_up.cpp
         transformation_push_id_through_variable.cpp
         transformation_record_synonymous_constants.cpp
+        transformation_replace_add_sub_mul_with_carrying_extended.cpp
         transformation_replace_boolean_constant_with_constant_binary.cpp
         transformation_replace_constant_with_uniform.cpp
         transformation_replace_copy_memory_with_load_store.cpp
@@ -292,7 +310,7 @@ if(SPIRV_BUILD_FUZZER)
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc
         )
 
-  if(MSVC)
+  if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")))
     # Enable parallel builds across four cores for this lib
     add_definitions(/MP4)
   endif()

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

@@ -32,6 +32,7 @@
 #include "source/fuzz/fuzzer_pass_add_image_sample_unused_components.h"
 #include "source/fuzz/fuzzer_pass_add_loads.h"
 #include "source/fuzz/fuzzer_pass_add_local_variables.h"
+#include "source/fuzz/fuzzer_pass_add_loop_preheaders.h"
 #include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
 #include "source/fuzz/fuzzer_pass_add_parameters.h"
 #include "source/fuzz/fuzzer_pass_add_stores.h"
@@ -49,12 +50,15 @@
 #include "source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h"
 #include "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h"
 #include "source/fuzz/fuzzer_pass_invert_comparison_operators.h"
+#include "source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h"
 #include "source/fuzz/fuzzer_pass_merge_blocks.h"
 #include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
 #include "source/fuzz/fuzzer_pass_outline_functions.h"
 #include "source/fuzz/fuzzer_pass_permute_blocks.h"
 #include "source/fuzz/fuzzer_pass_permute_function_parameters.h"
+#include "source/fuzz/fuzzer_pass_permute_instructions.h"
 #include "source/fuzz/fuzzer_pass_permute_phi_operands.h"
+#include "source/fuzz/fuzzer_pass_propagate_instructions_up.h"
 #include "source/fuzz/fuzzer_pass_push_ids_through_variables.h"
 #include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h"
 #include "source/fuzz/fuzzer_pass_replace_parameter_with_global.h"
@@ -239,6 +243,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
     MaybeAddPass<FuzzerPassAddLocalVariables>(
         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddLoopPreheaders>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
     MaybeAddPass<FuzzerPassAddParameters>(
         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out);
@@ -266,6 +273,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
     MaybeAddPass<FuzzerPassInvertComparisonOperators>(
         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out);
+    MaybeAddPass<FuzzerPassMakeVectorOperationsDynamic>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
     MaybeAddPass<FuzzerPassMergeBlocks>(
         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out);
@@ -281,6 +291,12 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
     MaybeAddPass<FuzzerPassPermuteFunctionParameters>(
         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out);
+    MaybeAddPass<FuzzerPassPermuteInstructions>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
+    MaybeAddPass<FuzzerPassPropagateInstructionsUp>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
     MaybeAddPass<FuzzerPassPushIdsThroughVariables>(
         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out);

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

@@ -38,6 +38,7 @@ const std::pair<uint32_t, uint32_t> kChanceOfAddingImageSampleUnusedComponents =
     {20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingLoad = {5, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingLocalVariable = {20, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingLoopPreheader = {20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingMatrixType = {20, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingNoContractionDecoration = {
     5, 70};
@@ -72,13 +73,20 @@ const std::pair<uint32_t, uint32_t>
 const std::pair<uint32_t, uint32_t> kChanceOfInvertingComparisonOperators = {
     20, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfMakingDonorLivesafe = {40, 60};
+const std::pair<uint32_t, uint32_t> kChanceOfMakingVectorOperationDynamic = {
+    20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfMergingBlocks = {20, 95};
 const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfOutliningFunction = {10, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfPermutingInstructions = {20, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfPermutingParameters = {30, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfPermutingPhiOperands = {30, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfPropagatingInstructionsUp = {20,
+                                                                          70};
 const std::pair<uint32_t, uint32_t> kChanceOfPushingIdThroughVariable = {5, 50};
+const std::pair<uint32_t, uint32_t>
+    kChanceOfReplacingAddSubMulWithCarryingExtended = {20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyMemoryWithLoadStore =
     {20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyObjectWithStoreLoad =
@@ -158,6 +166,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
   chance_of_adding_global_variable_ =
       ChooseBetweenMinAndMax(kChanceOfAddingGlobalVariable);
   chance_of_adding_load_ = ChooseBetweenMinAndMax(kChanceOfAddingLoad);
+  chance_of_adding_loop_preheader_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingLoopPreheader);
   chance_of_adding_image_sample_unused_components_ =
       ChooseBetweenMinAndMax(kChanceOfAddingImageSampleUnusedComponents);
   chance_of_adding_local_variable_ =
@@ -207,6 +217,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
       ChooseBetweenMinAndMax(kChanceOfInvertingComparisonOperators);
   chance_of_making_donor_livesafe_ =
       ChooseBetweenMinAndMax(kChanceOfMakingDonorLivesafe);
+  chance_of_making_vector_operation_dynamic_ =
+      ChooseBetweenMinAndMax(kChanceOfMakingVectorOperationDynamic);
   chance_of_merging_blocks_ = ChooseBetweenMinAndMax(kChanceOfMergingBlocks);
   chance_of_moving_block_down_ =
       ChooseBetweenMinAndMax(kChanceOfMovingBlockDown);
@@ -214,12 +226,18 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
       ChooseBetweenMinAndMax(kChanceOfObfuscatingConstant);
   chance_of_outlining_function_ =
       ChooseBetweenMinAndMax(kChanceOfOutliningFunction);
+  chance_of_permuting_instructions_ =
+      ChooseBetweenMinAndMax(kChanceOfPermutingInstructions);
   chance_of_permuting_parameters_ =
       ChooseBetweenMinAndMax(kChanceOfPermutingParameters);
   chance_of_permuting_phi_operands_ =
       ChooseBetweenMinAndMax(kChanceOfPermutingPhiOperands);
+  chance_of_propagating_instructions_up_ =
+      ChooseBetweenMinAndMax(kChanceOfPropagatingInstructionsUp);
   chance_of_pushing_id_through_variable_ =
       ChooseBetweenMinAndMax(kChanceOfPushingIdThroughVariable);
+  chance_of_replacing_add_sub_mul_with_carrying_extended_ =
+      ChooseBetweenMinAndMax(kChanceOfReplacingAddSubMulWithCarryingExtended);
   chance_of_replacing_copy_memory_with_load_store_ =
       ChooseBetweenMinAndMax(kChanceOfReplacingCopyMemoryWithLoadStore);
   chance_of_replacing_copyobject_with_store_load_ =

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

@@ -136,6 +136,9 @@ class FuzzerContext {
   uint32_t GetChanceOfAddingLocalVariable() {
     return chance_of_adding_local_variable_;
   }
+  uint32_t GetChanceOfAddingLoopPreheader() {
+    return chance_of_adding_loop_preheader_;
+  }
   uint32_t GetChanceOfAddingMatrixType() {
     return chance_of_adding_matrix_type_;
   }
@@ -198,6 +201,9 @@ class FuzzerContext {
   uint32_t ChanceOfMakingDonorLivesafe() {
     return chance_of_making_donor_livesafe_;
   }
+  uint32_t GetChanceOfMakingVectorOperationDynamic() {
+    return chance_of_making_vector_operation_dynamic_;
+  }
   uint32_t GetChanceOfMergingBlocks() { return chance_of_merging_blocks_; }
   uint32_t GetChanceOfMovingBlockDown() { return chance_of_moving_block_down_; }
   uint32_t GetChanceOfObfuscatingConstant() {
@@ -206,15 +212,24 @@ class FuzzerContext {
   uint32_t GetChanceOfOutliningFunction() {
     return chance_of_outlining_function_;
   }
+  uint32_t GetChanceOfPermutingInstructions() {
+    return chance_of_permuting_instructions_;
+  }
   uint32_t GetChanceOfPermutingParameters() {
     return chance_of_permuting_parameters_;
   }
   uint32_t GetChanceOfPermutingPhiOperands() {
     return chance_of_permuting_phi_operands_;
   }
+  uint32_t GetChanceOfPropagatingInstructionsUp() {
+    return chance_of_propagating_instructions_up_;
+  }
   uint32_t GetChanceOfPushingIdThroughVariable() {
     return chance_of_pushing_id_through_variable_;
   }
+  uint32_t GetChanceOfReplacingAddSubMulWithCarryingExtended() {
+    return chance_of_replacing_add_sub_mul_with_carrying_extended_;
+  }
   uint32_t GetChanceOfReplacingCopyMemoryWithLoadStore() {
     return chance_of_replacing_copy_memory_with_load_store_;
   }
@@ -336,6 +351,7 @@ class FuzzerContext {
   uint32_t chance_of_adding_image_sample_unused_components_;
   uint32_t chance_of_adding_load_;
   uint32_t chance_of_adding_local_variable_;
+  uint32_t chance_of_adding_loop_preheader_;
   uint32_t chance_of_adding_matrix_type_;
   uint32_t chance_of_adding_no_contraction_decoration_;
   uint32_t chance_of_adding_parameters;
@@ -360,13 +376,17 @@ class FuzzerContext {
   uint32_t chance_of_interchanging_zero_like_constants_;
   uint32_t chance_of_inverting_comparison_operators_;
   uint32_t chance_of_making_donor_livesafe_;
+  uint32_t chance_of_making_vector_operation_dynamic_;
   uint32_t chance_of_merging_blocks_;
   uint32_t chance_of_moving_block_down_;
   uint32_t chance_of_obfuscating_constant_;
   uint32_t chance_of_outlining_function_;
+  uint32_t chance_of_permuting_instructions_;
   uint32_t chance_of_permuting_parameters_;
   uint32_t chance_of_permuting_phi_operands_;
+  uint32_t chance_of_propagating_instructions_up_;
   uint32_t chance_of_pushing_id_through_variable_;
+  uint32_t chance_of_replacing_add_sub_mul_with_carrying_extended_;
   uint32_t chance_of_replacing_copy_memory_with_load_store_;
   uint32_t chance_of_replacing_copyobject_with_store_load_;
   uint32_t chance_of_replacing_id_with_synonym_;

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

@@ -24,6 +24,7 @@
 #include "source/fuzz/transformation_add_constant_null.h"
 #include "source/fuzz/transformation_add_constant_scalar.h"
 #include "source/fuzz/transformation_add_global_undef.h"
+#include "source/fuzz/transformation_add_loop_preheader.h"
 #include "source/fuzz/transformation_add_type_boolean.h"
 #include "source/fuzz/transformation_add_type_float.h"
 #include "source/fuzz/transformation_add_type_function.h"
@@ -533,5 +534,66 @@ void FuzzerPass::MaybeAddUseToReplace(
       std::make_pair(id_use_descriptor, replacement_id));
 }
 
+opt::BasicBlock* FuzzerPass::GetOrCreateSimpleLoopPreheader(
+    uint32_t header_id) {
+  auto header_block = fuzzerutil::MaybeFindBlock(GetIRContext(), header_id);
+
+  assert(header_block && header_block->IsLoopHeader() &&
+         "|header_id| should be the label id of a loop header");
+
+  auto predecessors = GetIRContext()->cfg()->preds(header_id);
+
+  assert(predecessors.size() >= 2 &&
+         "The block |header_id| should be reachable.");
+
+  auto function = header_block->GetParent();
+
+  if (predecessors.size() == 2) {
+    // The header has a single out-of-loop predecessor, which could be a
+    // preheader.
+
+    opt::BasicBlock* maybe_preheader;
+
+    if (GetIRContext()->GetDominatorAnalysis(function)->Dominates(
+            header_id, predecessors[0])) {
+      // The first predecessor is the back-edge block, because the header
+      // dominates it, so the second one is out of the loop.
+      maybe_preheader = &*function->FindBlock(predecessors[1]);
+    } else {
+      // The first predecessor is out of the loop.
+      maybe_preheader = &*function->FindBlock(predecessors[0]);
+    }
+
+    // |maybe_preheader| is a preheader if it branches unconditionally to
+    // the header. We also require it not to be a loop header.
+    if (maybe_preheader->terminator()->opcode() == SpvOpBranch &&
+        !maybe_preheader->IsLoopHeader()) {
+      return maybe_preheader;
+    }
+  }
+
+  // We need to add a preheader.
+
+  // Get a fresh id for the preheader.
+  uint32_t preheader_id = GetFuzzerContext()->GetFreshId();
+
+  // Get a fresh id for each OpPhi instruction, if there is more than one
+  // out-of-loop predecessor.
+  std::vector<uint32_t> phi_ids;
+  if (predecessors.size() > 2) {
+    header_block->ForEachPhiInst(
+        [this, &phi_ids](opt::Instruction* /* unused */) {
+          phi_ids.push_back(GetFuzzerContext()->GetFreshId());
+        });
+  }
+
+  // Add the preheader.
+  ApplyTransformation(
+      TransformationAddLoopPreheader(header_id, preheader_id, phi_ids));
+
+  // Make the newly-created preheader the new entry block.
+  return &*function->FindBlock(preheader_id);
+}
+
 }  // namespace fuzz
 }  // namespace spvtools

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

@@ -283,6 +283,16 @@ class FuzzerPass {
       std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
           uses_to_replace);
 
+  // Returns the preheader of the loop with header |header_id|, which satisfies
+  // all of the following conditions:
+  // - It is the only out-of-loop predecessor of the header
+  // - It unconditionally branches to the header
+  // - It is not a loop header itself
+  // If such preheader does not exist, a new one is added and returned.
+  // Requires |header_id| to be the label id of a loop header block that is
+  // reachable in the CFG (and thus has at least 2 predecessors).
+  opt::BasicBlock* GetOrCreateSimpleLoopPreheader(uint32_t header_id);
+
  private:
   opt::IRContext* ir_context_;
   TransformationContext* transformation_context_;

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

@@ -21,6 +21,26 @@
 
 namespace spvtools {
 namespace fuzz {
+namespace {
+
+bool IsBitWidthSupported(opt::IRContext* ir_context, uint32_t bit_width) {
+  switch (bit_width) {
+    case 32:
+      return true;
+    case 64:
+      return ir_context->get_feature_mgr()->HasCapability(
+                 SpvCapabilityFloat64) &&
+             ir_context->get_feature_mgr()->HasCapability(SpvCapabilityInt64);
+    case 16:
+      return ir_context->get_feature_mgr()->HasCapability(
+                 SpvCapabilityFloat16) &&
+             ir_context->get_feature_mgr()->HasCapability(SpvCapabilityInt16);
+    default:
+      return false;
+  }
+}
+
+}  // namespace
 
 FuzzerPassAddEquationInstructions::FuzzerPassAddEquationInstructions(
     opt::IRContext* ir_context, TransformationContext* transformation_context,
@@ -76,8 +96,22 @@ void FuzzerPassAddEquationInstructions::Apply() {
           switch (opcode) {
             case SpvOpConvertSToF:
             case SpvOpConvertUToF: {
-              auto candidate_instructions =
-                  GetIntegerInstructions(available_instructions);
+              std::vector<const opt::Instruction*> candidate_instructions;
+              for (const auto* inst :
+                   GetIntegerInstructions(available_instructions)) {
+                const auto* type =
+                    GetIRContext()->get_type_mgr()->GetType(inst->type_id());
+                assert(type && "|inst| has invalid type");
+
+                if (const auto* vector_type = type->AsVector()) {
+                  type = vector_type->element_type();
+                }
+
+                if (IsBitWidthSupported(GetIRContext(),
+                                        type->AsInteger()->width())) {
+                  candidate_instructions.push_back(inst);
+                }
+              }
 
               if (candidate_instructions.empty()) {
                 break;
@@ -112,20 +146,8 @@ void FuzzerPassAddEquationInstructions::Apply() {
               return;
             }
             case SpvOpBitcast: {
-              std::vector<const opt::Instruction*> candidate_instructions;
-              for (const auto* inst : available_instructions) {
-                const auto* type =
-                    GetIRContext()->get_type_mgr()->GetType(inst->type_id());
-                assert(type && "Instruction has invalid type");
-                if ((type->AsVector() &&
-                     (type->AsVector()->element_type()->AsInteger() ||
-                      type->AsVector()->element_type()->AsFloat())) ||
-                    type->AsInteger() || type->AsFloat()) {
-                  // We support OpBitcast for only scalars or vectors of
-                  // numerical type.
-                  candidate_instructions.push_back(inst);
-                }
-              }
+              const auto candidate_instructions =
+                  GetNumericalInstructions(available_instructions);
 
               if (!candidate_instructions.empty()) {
                 const auto* operand_inst =
@@ -356,5 +378,36 @@ FuzzerPassAddEquationInstructions::RestrictToElementBitWidth(
   return result;
 }
 
+std::vector<opt::Instruction*>
+FuzzerPassAddEquationInstructions::GetNumericalInstructions(
+    const std::vector<opt::Instruction*>& instructions) const {
+  std::vector<opt::Instruction*> result;
+
+  for (auto* inst : instructions) {
+    const auto* type = GetIRContext()->get_type_mgr()->GetType(inst->type_id());
+    assert(type && "Instruction has invalid type");
+
+    if (const auto* vector_type = type->AsVector()) {
+      type = vector_type->element_type();
+    }
+
+    if (!type->AsInteger() && !type->AsFloat()) {
+      // Only numerical scalars or vectors of numerical components are
+      // supported.
+      continue;
+    }
+
+    if (!IsBitWidthSupported(GetIRContext(), type->AsInteger()
+                                                 ? type->AsInteger()->width()
+                                                 : type->AsFloat()->width())) {
+      continue;
+    }
+
+    result.push_back(inst);
+  }
+
+  return result;
+}
+
 }  // namespace fuzz
 }  // namespace spvtools

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

@@ -51,6 +51,14 @@ class FuzzerPassAddEquationInstructions : public FuzzerPass {
   std::vector<opt::Instruction*> GetBooleanInstructions(
       const std::vector<opt::Instruction*>& instructions) const;
 
+  // Yields those instructions in |instructions| that have a scalar numerical or
+  // a vector of numerical components type. Only 16, 32 and 64-bit numericals
+  // are supported if both OpTypeInt and OpTypeFloat instructions can be created
+  // with the specified width (e.g. for 16-bit types both Float16 and Int16
+  // capabilities must be present).
+  std::vector<opt::Instruction*> GetNumericalInstructions(
+      const std::vector<opt::Instruction*>& instructions) const;
+
   // Requires that |instructions| are scalars or vectors of some type.  Returns
   // only those instructions whose width is |width|. If |width| is 1 this means
   // the scalars.

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

@@ -0,0 +1,66 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_add_loop_preheaders.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_add_loop_preheader.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddLoopPreheaders::FuzzerPassAddLoopPreheaders(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassAddLoopPreheaders::~FuzzerPassAddLoopPreheaders() = default;
+
+void FuzzerPassAddLoopPreheaders::Apply() {
+  for (auto& function : *GetIRContext()->module()) {
+    // Keep track of all the loop headers we want to add a preheader to.
+    std::vector<uint32_t> loop_header_ids_to_consider;
+    for (auto& block : function) {
+      // We only care about loop headers.
+      if (!block.IsLoopHeader()) {
+        continue;
+      }
+
+      // Randomly decide whether to consider this header.
+      if (!GetFuzzerContext()->ChoosePercentage(
+              GetFuzzerContext()->GetChanceOfAddingLoopPreheader())) {
+        continue;
+      }
+
+      // We exclude loop headers with just one predecessor (the back-edge block)
+      // because they are unreachable.
+      if (GetIRContext()->cfg()->preds(block.id()).size() < 2) {
+        continue;
+      }
+
+      loop_header_ids_to_consider.push_back(block.id());
+    }
+
+    for (uint32_t header_id : loop_header_ids_to_consider) {
+      // If not already present, add a preheader which is not also a loop
+      // header.
+      GetOrCreateSimpleLoopPreheader(header_id);
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 43 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.h

@@ -0,0 +1,43 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_LOOP_PREHEADERS_H
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_LOOP_PREHEADERS_H
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A fuzzer pass that randomly adds simple loop preheaders to loops that do not
+// have one. A simple loop preheader is a block that:
+// - is the only out-of-loop predecessor of the header
+// - branches unconditionally to the header
+// - is not a loop header itself
+class FuzzerPassAddLoopPreheaders : public FuzzerPass {
+ public:
+  FuzzerPassAddLoopPreheaders(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddLoopPreheaders();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_LOOP_PREHEADERS_H

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

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

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

@@ -84,6 +84,16 @@ void FuzzerPassDonateModules::Apply() {
 
 void FuzzerPassDonateModules::DonateSingleModule(
     opt::IRContext* donor_ir_context, bool make_livesafe) {
+  // Check that the donated module has capabilities, supported by the recipient
+  // module.
+  for (const auto& capability_inst : donor_ir_context->capabilities()) {
+    auto capability =
+        static_cast<SpvCapability>(capability_inst.GetSingleWordInOperand(0));
+    if (!GetIRContext()->get_feature_mgr()->HasCapability(capability)) {
+      return;
+    }
+  }
+
   // The ids used by the donor module may very well clash with ids defined in
   // the recipient module.  Furthermore, some instructions defined in the donor
   // module will be equivalent to instructions defined in the recipient module,
@@ -646,12 +656,13 @@ void FuzzerPassDonateModules::HandleFunctions(
           }
         });
 
-    if (make_livesafe) {
-      // Make the function livesafe and then add it.
-      AddLivesafeFunction(*function_to_donate, donor_ir_context,
-                          *original_id_to_donated_id, donated_instructions);
-    } else {
-      // Add the function in a non-livesafe manner.
+    // If |make_livesafe| is true, try to add the function in a livesafe manner.
+    // Otherwise (if |make_lifesafe| is false or an attempt to make the function
+    // livesafe has failed), add the function in a non-livesafe manner.
+    if (!make_livesafe ||
+        !MaybeAddLivesafeFunction(*function_to_donate, donor_ir_context,
+                                  *original_id_to_donated_id,
+                                  donated_instructions)) {
       ApplyTransformation(TransformationAddFunction(donated_instructions));
     }
   }
@@ -773,6 +784,7 @@ bool FuzzerPassDonateModules::IsBasicType(
     const opt::Instruction& instruction) const {
   switch (instruction.opcode()) {
     case SpvOpTypeArray:
+    case SpvOpTypeBool:
     case SpvOpTypeFloat:
     case SpvOpTypeInt:
     case SpvOpTypeMatrix:
@@ -1007,7 +1019,96 @@ void FuzzerPassDonateModules::PrepareInstructionForDonation(
       input_operands));
 }
 
-void FuzzerPassDonateModules::AddLivesafeFunction(
+bool FuzzerPassDonateModules::CreateLoopLimiterInfo(
+    opt::IRContext* donor_ir_context, const opt::BasicBlock& loop_header,
+    const std::map<uint32_t, uint32_t>& original_id_to_donated_id,
+    protobufs::LoopLimiterInfo* out) {
+  assert(loop_header.IsLoopHeader() && "|loop_header| is not a loop header");
+
+  // Grab the loop header's id, mapped to its donated value.
+  out->set_loop_header_id(original_id_to_donated_id.at(loop_header.id()));
+
+  // Get fresh ids that will be used to load the loop limiter, increment
+  // it, compare it with the loop limit, and an id for a new block that
+  // will contain the loop's original terminator.
+  out->set_load_id(GetFuzzerContext()->GetFreshId());
+  out->set_increment_id(GetFuzzerContext()->GetFreshId());
+  out->set_compare_id(GetFuzzerContext()->GetFreshId());
+  out->set_logical_op_id(GetFuzzerContext()->GetFreshId());
+
+  // We are creating a branch from the back-edge block to the merge block. Thus,
+  // if merge block has any OpPhi instructions, we might need to adjust
+  // them.
+
+  // Note that the loop might have an unreachable back-edge block. This means
+  // that the loop can't iterate, so we don't need to adjust anything.
+  const auto back_edge_block_id = TransformationAddFunction::GetBackEdgeBlockId(
+      donor_ir_context, loop_header.id());
+  if (!back_edge_block_id) {
+    return true;
+  }
+
+  auto* back_edge_block = donor_ir_context->cfg()->block(back_edge_block_id);
+  assert(back_edge_block && "|back_edge_block_id| is invalid");
+
+  const auto* merge_block =
+      donor_ir_context->cfg()->block(loop_header.MergeBlockId());
+  assert(merge_block && "Loop header has invalid merge block id");
+
+  // We don't need to adjust anything if there is already a branch from
+  // the back-edge block to the merge block.
+  if (back_edge_block->IsSuccessor(merge_block)) {
+    return true;
+  }
+
+  // Adjust OpPhi instructions in the |merge_block|.
+  for (const auto& inst : *merge_block) {
+    if (inst.opcode() != SpvOpPhi) {
+      break;
+    }
+
+    // There is no simple way to ensure that a chosen operand for the OpPhi
+    // instruction will never cause any problems (e.g. if we choose an
+    // integer id, it might have a zero value when we branch from the back
+    // edge block. This might cause a division by 0 later in the function.).
+    // Thus, we ignore possible problems and proceed as follows:
+    // - if any of the existing OpPhi operands dominates the back-edge
+    //   block - use it
+    // - if OpPhi has a basic type (see IsBasicType method) - create
+    //   a zero constant
+    // - otherwise, we can't add a livesafe function.
+    uint32_t suitable_operand_id = 0;
+    for (uint32_t i = 0; i < inst.NumInOperands(); i += 2) {
+      auto dependency_inst_id = inst.GetSingleWordInOperand(i);
+
+      if (fuzzerutil::IdIsAvailableBeforeInstruction(
+              donor_ir_context, back_edge_block->terminator(),
+              dependency_inst_id)) {
+        suitable_operand_id = original_id_to_donated_id.at(dependency_inst_id);
+        break;
+      }
+    }
+
+    if (suitable_operand_id == 0 &&
+        IsBasicType(
+            *donor_ir_context->get_def_use_mgr()->GetDef(inst.type_id()))) {
+      // We mark this constant as irrelevant so that we can replace it
+      // with more interesting value later.
+      suitable_operand_id = FindOrCreateZeroConstant(
+          original_id_to_donated_id.at(inst.type_id()), true);
+    }
+
+    if (suitable_operand_id == 0) {
+      return false;
+    }
+
+    out->add_phi_id(suitable_operand_id);
+  }
+
+  return true;
+}
+
+bool FuzzerPassDonateModules::MaybeAddLivesafeFunction(
     const opt::Function& function_to_donate, opt::IRContext* donor_ir_context,
     const std::map<uint32_t, uint32_t>& original_id_to_donated_id,
     const std::vector<protobufs::Instruction>& donated_instructions) {
@@ -1035,16 +1136,13 @@ void FuzzerPassDonateModules::AddLivesafeFunction(
   for (auto& block : function_to_donate) {
     if (block.IsLoopHeader()) {
       protobufs::LoopLimiterInfo loop_limiter;
-      // Grab the loop header's id, mapped to its donated value.
-      loop_limiter.set_loop_header_id(original_id_to_donated_id.at(block.id()));
-      // Get fresh ids that will be used to load the loop limiter, increment
-      // it, compare it with the loop limit, and an id for a new block that
-      // will contain the loop's original terminator.
-      loop_limiter.set_load_id(GetFuzzerContext()->GetFreshId());
-      loop_limiter.set_increment_id(GetFuzzerContext()->GetFreshId());
-      loop_limiter.set_compare_id(GetFuzzerContext()->GetFreshId());
-      loop_limiter.set_logical_op_id(GetFuzzerContext()->GetFreshId());
-      loop_limiters.emplace_back(loop_limiter);
+
+      if (!CreateLoopLimiterInfo(donor_ir_context, block,
+                                 original_id_to_donated_id, &loop_limiter)) {
+        return false;
+      }
+
+      loop_limiters.emplace_back(std::move(loop_limiter));
     }
   }
 
@@ -1157,6 +1255,7 @@ void FuzzerPassDonateModules::AddLivesafeFunction(
   ApplyTransformation(TransformationAddFunction(
       donated_instructions, loop_limiter_variable_id, loop_limit, loop_limiters,
       kill_unreachable_return_value_id, access_chain_clamping_info));
+  return true;
 }
 
 }  // namespace fuzz

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

@@ -128,14 +128,24 @@ class FuzzerPassDonateModules : public FuzzerPass {
       std::map<uint32_t, uint32_t>* original_id_to_donated_id,
       std::vector<protobufs::Instruction>* donated_instructions);
 
+  // Tries to create a protobufs::LoopLimiterInfo given a loop header basic
+  // block. Returns true if successful and outputs loop limiter into the |out|
+  // variable. Otherwise, returns false. |out| contains an undefined value when
+  // this function returns false.
+  bool CreateLoopLimiterInfo(
+      opt::IRContext* donor_ir_context, const opt::BasicBlock& loop_header,
+      const std::map<uint32_t, uint32_t>& original_id_to_donated_id,
+      protobufs::LoopLimiterInfo* out);
+
   // Requires that |donated_instructions| represents a prepared version of the
   // instructions of |function_to_donate| (which comes from |donor_ir_context|)
   // ready for donation, and |original_id_to_donated_id| maps ids from
   // |donor_ir_context| to their corresponding ids in the recipient module.
   //
-  // Adds a livesafe version of the function, based on |donated_instructions|,
-  // to the recipient module.
-  void AddLivesafeFunction(
+  // Attempts to add a livesafe version of the function, based on
+  // |donated_instructions|, to the recipient module. Returns true if the
+  // donation was successful, false otherwise.
+  bool MaybeAddLivesafeFunction(
       const opt::Function& function_to_donate, opt::IRContext* donor_ir_context,
       const std::map<uint32_t, uint32_t>& original_id_to_donated_id,
       const std::vector<protobufs::Instruction>& donated_instructions);

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

@@ -91,6 +91,13 @@ void FuzzerPassInterchangeSignednessOfIntegerOperands::Apply() {
 
 uint32_t FuzzerPassInterchangeSignednessOfIntegerOperands::
     FindOrCreateToggledIntegerConstant(uint32_t id) {
+  // |id| must not be a specialization constant because we do not know the value
+  // of specialization constants.
+  if (opt::IsSpecConstantInst(
+          GetIRContext()->get_def_use_mgr()->GetDef(id)->opcode())) {
+    return 0;
+  }
+
   auto constant = GetIRContext()->get_constant_mgr()->FindDeclaredConstant(id);
 
   // This pass only toggles integer constants.

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

@@ -34,6 +34,12 @@ FuzzerPassInterchangeZeroLikeConstants::
 
 uint32_t FuzzerPassInterchangeZeroLikeConstants::FindOrCreateToggledConstant(
     opt::Instruction* declaration) {
+  // |declaration| must not be a specialization constant because we do not know
+  // the value of specialization constants.
+  if (opt::IsSpecConstantInst(declaration->opcode())) {
+    return 0;
+  }
+
   auto constant = GetIRContext()->get_constant_mgr()->FindDeclaredConstant(
       declaration->result_id());
 
@@ -107,4 +113,4 @@ void FuzzerPassInterchangeZeroLikeConstants::Apply() {
   }
 }
 }  // namespace fuzz
-}  // namespace spvtools
+}  // namespace spvtools

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

@@ -0,0 +1,71 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_make_vector_operation_dynamic.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassMakeVectorOperationsDynamic::FuzzerPassMakeVectorOperationsDynamic(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassMakeVectorOperationsDynamic::
+    ~FuzzerPassMakeVectorOperationsDynamic() = default;
+
+void FuzzerPassMakeVectorOperationsDynamic::Apply() {
+  for (auto& function : *GetIRContext()->module()) {
+    for (auto& block : function) {
+      for (auto& instruction : block) {
+        // Randomly decide whether to try applying the transformation.
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()
+                    ->GetChanceOfMakingVectorOperationDynamic())) {
+          continue;
+        }
+
+        // |instruction| must be a vector operation.
+        if (!TransformationMakeVectorOperationDynamic::IsVectorOperation(
+                GetIRContext(), &instruction)) {
+          continue;
+        }
+
+        // Make sure |instruction| has only one indexing operand.
+        assert(instruction.NumInOperands() ==
+                   (instruction.opcode() == SpvOpCompositeExtract ? 2 : 3) &&
+               "FuzzerPassMakeVectorOperationsDynamic: the composite "
+               "instruction must have "
+               "only one indexing operand.");
+
+        // Applies the make vector operation dynamic transformation.
+        ApplyTransformation(TransformationMakeVectorOperationDynamic(
+            instruction.result_id(),
+            FindOrCreateIntegerConstant(
+                {instruction.GetSingleWordInOperand(
+                    instruction.opcode() == SpvOpCompositeExtract ? 1 : 2)},
+                32, GetFuzzerContext()->ChooseEven(), false)));
+      }
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

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

+ 98 - 28
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp

@@ -47,32 +47,13 @@ void FuzzerPassOutlineFunctions::Apply() {
     for (auto& block : *function) {
       blocks.push_back(&block);
     }
-    auto entry_block = blocks[GetFuzzerContext()->RandomIndex(blocks)];
-
-    // If the entry block starts with OpPhi, try to split it.
-    if (entry_block->begin()->opcode() == SpvOpPhi) {
-      // Find the first non-OpPhi instruction.
-      opt::Instruction* non_phi_inst = nullptr;
-      for (auto& instruction : *entry_block) {
-        if (instruction.opcode() != SpvOpPhi) {
-          non_phi_inst = &instruction;
-          break;
-        }
-      }
-
-      assert(non_phi_inst && "|non_phi_inst| must've been initialized");
-
-      // If the split was not applicable, the transformation will not work.
-      uint32_t new_block_id = GetFuzzerContext()->GetFreshId();
-      if (!MaybeApplyTransformation(TransformationSplitBlock(
-              MakeInstructionDescriptor(non_phi_inst->result_id(),
-                                        non_phi_inst->opcode(), 0),
-              new_block_id))) {
-        return;
-      }
+    auto entry_block = MaybeGetEntryBlockSuitableForOutlining(
+        blocks[GetFuzzerContext()->RandomIndex(blocks)]);
 
-      // The new entry block is the newly-created block.
-      entry_block = &*function->FindBlock(new_block_id);
+    if (!entry_block) {
+      // The chosen block is not suitable to be the entry block of a region that
+      // will be outlined.
+      continue;
     }
 
     auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(function);
@@ -83,16 +64,26 @@ void FuzzerPassOutlineFunctions::Apply() {
          postdominates_entry_block != nullptr;
          postdominates_entry_block = postdominator_analysis->ImmediateDominator(
              postdominates_entry_block)) {
+      // Consider the block if it is dominated by the entry block, ignore it if
+      // it is a continue target.
       if (dominator_analysis->Dominates(entry_block,
-                                        postdominates_entry_block)) {
+                                        postdominates_entry_block) &&
+          !GetIRContext()->GetStructuredCFGAnalysis()->IsContinueBlock(
+              postdominates_entry_block->id())) {
         candidate_exit_blocks.push_back(postdominates_entry_block);
       }
     }
     if (candidate_exit_blocks.empty()) {
       continue;
     }
-    auto exit_block = candidate_exit_blocks[GetFuzzerContext()->RandomIndex(
-        candidate_exit_blocks)];
+    auto exit_block = MaybeGetExitBlockSuitableForOutlining(
+        candidate_exit_blocks[GetFuzzerContext()->RandomIndex(
+            candidate_exit_blocks)]);
+
+    if (!exit_block) {
+      // The block chosen is not suitable
+      continue;
+    }
 
     auto region_blocks = TransformationOutlineFunction::GetRegionBlocks(
         GetIRContext(), entry_block, exit_block);
@@ -122,5 +113,84 @@ void FuzzerPassOutlineFunctions::Apply() {
   }
 }
 
+opt::BasicBlock*
+FuzzerPassOutlineFunctions::MaybeGetEntryBlockSuitableForOutlining(
+    opt::BasicBlock* entry_block) {
+  // If the entry block is a loop header, we need to get or create its
+  // preheader and make it the entry block, if possible.
+  if (entry_block->IsLoopHeader()) {
+    auto predecessors =
+        GetIRContext()->cfg()->preds(entry_block->GetLabel()->result_id());
+
+    if (predecessors.size() < 2) {
+      // The header only has one predecessor (the back-edge block) and thus
+      // it is unreachable. The block cannot be adjusted to be suitable for
+      // outlining.
+      return nullptr;
+    }
+
+    // Get or create a suitable preheader and make it become the entry block.
+    entry_block =
+        GetOrCreateSimpleLoopPreheader(entry_block->GetLabel()->result_id());
+  }
+
+  assert(!entry_block->IsLoopHeader() &&
+         "The entry block cannot be a loop header at this point.");
+
+  // If the entry block starts with OpPhi or OpVariable, try to split it.
+  if (entry_block->begin()->opcode() == SpvOpPhi ||
+      entry_block->begin()->opcode() == SpvOpVariable) {
+    // Find the first non-OpPhi and non-OpVariable instruction.
+    auto non_phi_or_var_inst = &*entry_block->begin();
+    while (non_phi_or_var_inst->opcode() == SpvOpPhi ||
+           non_phi_or_var_inst->opcode() == SpvOpVariable) {
+      non_phi_or_var_inst = non_phi_or_var_inst->NextNode();
+    }
+
+    // Split the block.
+    uint32_t new_block_id = GetFuzzerContext()->GetFreshId();
+    ApplyTransformation(TransformationSplitBlock(
+        MakeInstructionDescriptor(GetIRContext(), non_phi_or_var_inst),
+        new_block_id));
+
+    // The new entry block is the newly-created block.
+    entry_block = &*entry_block->GetParent()->FindBlock(new_block_id);
+  }
+
+  return entry_block;
+}
+
+opt::BasicBlock*
+FuzzerPassOutlineFunctions::MaybeGetExitBlockSuitableForOutlining(
+    opt::BasicBlock* exit_block) {
+  // The exit block must not be a continue target.
+  assert(!GetIRContext()->GetStructuredCFGAnalysis()->IsContinueBlock(
+             exit_block->id()) &&
+         "A candidate exit block cannot be a continue target.");
+
+  // If the exit block is a merge block, try to split it and return the second
+  // block in the pair as the exit block.
+  if (GetIRContext()->GetStructuredCFGAnalysis()->IsMergeBlock(
+          exit_block->id())) {
+    uint32_t new_block_id = GetFuzzerContext()->GetFreshId();
+
+    // Find the first non-OpPhi instruction, after which to split.
+    auto split_before = &*exit_block->begin();
+    while (split_before->opcode() == SpvOpPhi) {
+      split_before = split_before->NextNode();
+    }
+
+    if (!MaybeApplyTransformation(TransformationSplitBlock(
+            MakeInstructionDescriptor(GetIRContext(), split_before),
+            new_block_id))) {
+      return nullptr;
+    }
+
+    return &*exit_block->GetParent()->FindBlock(new_block_id);
+  }
+
+  return exit_block;
+}
+
 }  // namespace fuzz
 }  // namespace spvtools

+ 25 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.h

@@ -32,6 +32,31 @@ class FuzzerPassOutlineFunctions : public FuzzerPass {
   ~FuzzerPassOutlineFunctions();
 
   void Apply() override;
+
+  // Returns a block suitable to be an entry block for a region that can be
+  // outlined, i.e. a block that is not a loop header and that does not start
+  // with OpPhi or OpVariable. In particular, it returns:
+  // - |entry_block| if it is suitable
+  // - otherwise, a block found by:
+  //   - looking for or creating a new preheader, if |entry_block| is a loop
+  //     header
+  //   - splitting the candidate entry block, if it starts with OpPhi or
+  //     OpVariable.
+  // Returns nullptr if a suitable block cannot be found following the
+  // instructions above.
+  opt::BasicBlock* MaybeGetEntryBlockSuitableForOutlining(
+      opt::BasicBlock* entry_block);
+
+  // Returns:
+  // - |exit_block| if it is not a merge block
+  // - the second block obtained by splitting |exit_block|, if |exit_block| is a
+  //   merge block.
+  // Assumes that |exit_block| is not a continue target.
+  // The block returned by this function should be suitable to be the exit block
+  // of a region that can be outlined.
+  // Returns nullptr if |exit_block| is a merge block and it cannot be split.
+  opt::BasicBlock* MaybeGetExitBlockSuitableForOutlining(
+      opt::BasicBlock* exit_block);
 };
 
 }  // namespace fuzz

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

@@ -0,0 +1,64 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_permute_instructions.h"
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_move_instruction_down.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassPermuteInstructions::FuzzerPassPermuteInstructions(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassPermuteInstructions::~FuzzerPassPermuteInstructions() = default;
+
+void FuzzerPassPermuteInstructions::Apply() {
+  // We are iterating over all instructions in all basic blocks.
+  for (auto& function : *GetIRContext()->module()) {
+    for (auto& block : function) {
+      // We need to collect all instructions in the block into a separate vector
+      // since application of the transformation below might invalidate
+      // iterators.
+      std::vector<opt::Instruction*> instructions;
+      for (auto& instruction : block) {
+        instructions.push_back(&instruction);
+      }
+
+      // We consider all instructions in reverse to increase the possible number
+      // of applied transformations.
+      for (auto it = instructions.rbegin(); it != instructions.rend(); ++it) {
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfPermutingInstructions())) {
+          continue;
+        }
+
+        while (MaybeApplyTransformation(TransformationMoveInstructionDown(
+            MakeInstructionDescriptor(GetIRContext(), *it)))) {
+          // Apply the transformation as many times as possible.
+        }
+      }
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

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

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

@@ -0,0 +1,64 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_propagate_instructions_up.h"
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_propagate_instruction_up.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassPropagateInstructionsUp::FuzzerPassPropagateInstructionsUp(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassPropagateInstructionsUp::~FuzzerPassPropagateInstructionsUp() =
+    default;
+
+void FuzzerPassPropagateInstructionsUp::Apply() {
+  for (const auto& function : *GetIRContext()->module()) {
+    for (const auto& block : function) {
+      if (!GetFuzzerContext()->ChoosePercentage(
+              GetFuzzerContext()->GetChanceOfPropagatingInstructionsUp())) {
+        continue;
+      }
+
+      if (TransformationPropagateInstructionUp::IsApplicableToBlock(
+              GetIRContext(), block.id())) {
+        std::map<uint32_t, uint32_t> fresh_ids;
+        for (auto id : GetIRContext()->cfg()->preds(block.id())) {
+          auto& fresh_id = fresh_ids[id];
+
+          if (!fresh_id) {
+            // Create a fresh id if it hasn't been created yet. |fresh_id| will
+            // be default-initialized to 0 in this case.
+            fresh_id = GetFuzzerContext()->GetFreshId();
+          }
+        }
+
+        ApplyTransformation(
+            TransformationPropagateInstructionUp(block.id(), fresh_ids));
+      }
+    }
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

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

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

@@ -0,0 +1,76 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h"
+
+namespace spvtools {
+namespace fuzz {
+
+namespace {
+const uint32_t kArithmeticInstructionIndexLeftInOperand = 0;
+}  // namespace
+
+FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::
+    FuzzerPassReplaceAddsSubsMulsWithCarryingExtended(
+        opt::IRContext* ir_context,
+        TransformationContext* transformation_context,
+        FuzzerContext* fuzzer_context,
+        protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::
+    ~FuzzerPassReplaceAddsSubsMulsWithCarryingExtended() = default;
+
+void FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::Apply() {
+  for (auto& function : *GetIRContext()->module()) {
+    for (auto& block : function) {
+      for (auto& instruction : block) {
+        // Randomly decide whether to apply the transformation.
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()
+                    ->GetChanceOfReplacingAddSubMulWithCarryingExtended())) {
+          continue;
+        }
+
+        // Check if the transformation can be applied to this instruction.
+        if (!TransformationReplaceAddSubMulWithCarryingExtended::
+                IsInstructionSuitable(GetIRContext(), instruction)) {
+          continue;
+        }
+
+        // Get the operand type id. We know that both operands have the same
+        // type.
+        uint32_t operand_type_id =
+            GetIRContext()
+                ->get_def_use_mgr()
+                ->GetDef(instruction.GetSingleWordInOperand(
+                    kArithmeticInstructionIndexLeftInOperand))
+                ->type_id();
+
+        // Ensure the required struct type exists. The struct type is based on
+        // the operand type.
+        FindOrCreateStructType({operand_type_id, operand_type_id});
+
+        ApplyTransformation(TransformationReplaceAddSubMulWithCarryingExtended(
+            GetFuzzerContext()->GetFreshId(), instruction.result_id()));
+      }
+    }
+  }
+}
+}  // namespace fuzz
+}  // namespace spvtools

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

@@ -0,0 +1,42 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SPIRV_TOOLS_FUZZER_PASS_REPLACE_ADDS_SUBS_MULS_WITH_CARRYING_EXTENDED_H
+#define SPIRV_TOOLS_FUZZER_PASS_REPLACE_ADDS_SUBS_MULS_WITH_CARRYING_EXTENDED_H
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A fuzzer pass that replaces instructions OpIAdd, OpISub, OpIMul with pairs of
+// instructions. The first one (OpIAddCarry, OpISubBorrow, OpUMulExtended,
+// OpSMulExtended) computes the result into a struct. The second one extracts
+// the appropriate component from the struct to yield the original result.
+class FuzzerPassReplaceAddsSubsMulsWithCarryingExtended : public FuzzerPass {
+ public:
+  FuzzerPassReplaceAddsSubsMulsWithCarryingExtended(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassReplaceAddsSubsMulsWithCarryingExtended() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SPIRV_TOOLS_FUZZER_PASS_REPLACE_ADDS_SUBS_MULS_WITH_CARRYING_EXTENDED_H

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

@@ -36,23 +36,27 @@ FuzzerPassReplaceLinearAlgebraInstructions::
 void FuzzerPassReplaceLinearAlgebraInstructions::Apply() {
   // For each instruction, checks whether it is a linear algebra instruction. In
   // this case, the transformation is randomly applied.
-  GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
-    if (!spvOpcodeIsLinearAlgebra(instruction->opcode())) {
-      return;
+  for (auto& function : *GetIRContext()->module()) {
+    for (auto& block : function) {
+      for (auto& instruction : block) {
+        if (!spvOpcodeIsLinearAlgebra(instruction.opcode())) {
+          continue;
+        }
+
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()
+                    ->GetChanceOfReplacingLinearAlgebraInstructions())) {
+          continue;
+        }
+
+        ApplyTransformation(TransformationReplaceLinearAlgebraInstruction(
+            GetFuzzerContext()->GetFreshIds(
+                TransformationReplaceLinearAlgebraInstruction::
+                    GetRequiredFreshIdCount(GetIRContext(), &instruction)),
+            MakeInstructionDescriptor(GetIRContext(), &instruction)));
+      }
     }
-
-    if (!GetFuzzerContext()->ChoosePercentage(
-            GetFuzzerContext()
-                ->GetChanceOfReplacingLinearAlgebraInstructions())) {
-      return;
-    }
-
-    ApplyTransformation(TransformationReplaceLinearAlgebraInstruction(
-        GetFuzzerContext()->GetFreshIds(
-            TransformationReplaceLinearAlgebraInstruction::
-                GetRequiredFreshIdCount(GetIRContext(), instruction)),
-        MakeInstructionDescriptor(GetIRContext(), instruction)));
-  });
+  }
 }
 
 }  // namespace fuzz

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

@@ -826,6 +826,12 @@ uint32_t UpdateFunctionType(opt::IRContext* ir_context, uint32_t function_id,
   operand_ids.insert(operand_ids.end(), parameter_type_ids.begin(),
                      parameter_type_ids.end());
 
+  // A trivial case - we change nothing.
+  if (FindFunctionType(ir_context, operand_ids) ==
+      old_function_type->result_id()) {
+    return old_function_type->result_id();
+  }
+
   if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1 &&
       FindFunctionType(ir_context, operand_ids) == 0) {
     // We can change |old_function_type| only if it's used once in the module
@@ -849,17 +855,16 @@ uint32_t UpdateFunctionType(opt::IRContext* ir_context, uint32_t function_id,
     // existing one or create a new one.
     auto type_id = FindOrCreateFunctionType(
         ir_context, new_function_type_result_id, operand_ids);
+    assert(type_id != old_function_type->result_id() &&
+           "We should've handled this case above");
 
-    if (type_id != old_function_type->result_id()) {
-      function->DefInst().SetInOperand(1, {type_id});
+    function->DefInst().SetInOperand(1, {type_id});
 
-      // DefUseManager hasn't been updated yet, so if the following condition is
-      // true, then |old_function_type| will have no users when this function
-      // returns. We might as well remove it.
-      if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1) {
-        old_function_type->RemoveFromList();
-        delete old_function_type;
-      }
+    // DefUseManager hasn't been updated yet, so if the following condition is
+    // true, then |old_function_type| will have no users when this function
+    // returns. We might as well remove it.
+    if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1) {
+      ir_context->KillInst(old_function_type);
     }
 
     return type_id;
@@ -1312,6 +1317,31 @@ MapToRepeatedUInt32Pair(const std::map<uint32_t, uint32_t>& data) {
   return result;
 }
 
+opt::Instruction* GetLastInsertBeforeInstruction(opt::IRContext* ir_context,
+                                                 uint32_t block_id,
+                                                 SpvOp opcode) {
+  // CFG::block uses std::map::at which throws an exception when |block_id| is
+  // invalid. The error message is unhelpful, though. Thus, we test that
+  // |block_id| is valid here.
+  const auto* label_inst = ir_context->get_def_use_mgr()->GetDef(block_id);
+  (void)label_inst;  // Make compilers happy in release mode.
+  assert(label_inst && label_inst->opcode() == SpvOpLabel &&
+         "|block_id| is invalid");
+
+  auto* block = ir_context->cfg()->block(block_id);
+  auto it = block->rbegin();
+  assert(it != block->rend() && "Basic block can't be empty");
+
+  if (block->GetMergeInst()) {
+    ++it;
+    assert(it != block->rend() &&
+           "|block| must have at least two instructions:"
+           "terminator and a merge instruction");
+  }
+
+  return CanInsertOpcodeBeforeInstruction(opcode, &*it) ? &*it : nullptr;
+}
+
 }  // namespace fuzzerutil
 
 }  // namespace fuzz

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

@@ -316,6 +316,10 @@ opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context,
 // more users, it is removed from the module. Returns the result id of the
 // OpTypeFunction instruction that is used as a type of the function with
 // |function_id|.
+//
+// CAUTION: When the old type of the function is removed from the module, its
+//          memory is deallocated. Be sure not to use any pointers to the old
+//          type when this function returns.
 uint32_t UpdateFunctionType(opt::IRContext* ir_context, uint32_t function_id,
                             uint32_t new_function_type_result_id,
                             uint32_t return_type_id,
@@ -483,6 +487,12 @@ std::map<uint32_t, uint32_t> RepeatedUInt32PairToMap(
 google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>
 MapToRepeatedUInt32Pair(const std::map<uint32_t, uint32_t>& data);
 
+// Returns the last instruction in |block_id| before which an instruction with
+// opcode |opcode| can be inserted, or nullptr if there is no such instruction.
+opt::Instruction* GetLastInsertBeforeInstruction(opt::IRContext* ir_context,
+                                                 uint32_t block_id,
+                                                 SpvOp opcode);
+
 }  // namespace fuzzerutil
 
 }  // namespace fuzz

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

@@ -52,7 +52,7 @@ protobufs::IdUseDescriptor MakeIdUseDescriptorFromUse(
     opt::IRContext* context, opt::Instruction* inst,
     uint32_t in_operand_index) {
   const auto& in_operand = inst->GetInOperand(in_operand_index);
-  assert(in_operand.type == SPV_OPERAND_TYPE_ID);
+  assert(spvIsInIdType(in_operand.type));
   return MakeIdUseDescriptor(in_operand.words[0],
                              MakeInstructionDescriptor(context, inst),
                              in_operand_index);

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

@@ -408,6 +408,10 @@ message Transformation {
     TransformationReplaceCopyMemoryWithLoadStore replace_copy_memory_with_load_store = 61;
     TransformationReplaceLoadStoreWithCopyMemory replace_load_store_with_copy_memory = 62;
     TransformationAddLoopPreheader add_loop_preheader = 63;
+    TransformationMoveInstructionDown move_instruction_down = 64;
+    TransformationMakeVectorOperationDynamic make_vector_operation_dynamic = 65;
+    TransformationReplaceAddSubMulWithCarryingExtended replace_add_sub_mul_with_carrying_extended = 66;
+    TransformationPropagateInstructionUp propagate_instruction_up = 67;
     // Add additional option using the next available number.
   }
 }
@@ -1105,6 +1109,21 @@ message TransformationLoad {
 
 }
 
+message TransformationMakeVectorOperationDynamic {
+
+  // A transformation that replaces the OpCompositeExtract and OpCompositeInsert
+  // instructions with the OpVectorExtractDynamic and OpVectorInsertDynamic instructions.
+
+  // The composite instruction result id.
+  uint32 instruction_result_id = 1;
+
+  // The OpCompositeExtract/Insert instructions accept integer literals as indices to the composite object.
+  // However, the OpVectorInsert/ExtractDynamic instructions require its single index to be an integer instruction.
+  // This is the result id of the integer instruction.
+  uint32 constant_index_id = 2;
+
+}
+
 message TransformationMergeBlocks {
 
   // A transformation that merges a block with its predecessor.
@@ -1124,6 +1143,15 @@ message TransformationMoveBlockDown {
   uint32 block_id = 1;
 }
 
+message TransformationMoveInstructionDown {
+
+  // Swaps |instruction| with the next instruction in the block.
+
+  // The instruction to move down.
+  InstructionDescriptor instruction = 1;
+
+}
+
 message TransformationOutlineFunction {
 
   // A transformation that outlines a single-entry single-exit region of a
@@ -1212,6 +1240,26 @@ message TransformationPermutePhiOperands {
 
 }
 
+message TransformationPropagateInstructionUp {
+
+  // Propagates an instruction in the block into the block's predecessors.
+  // Concretely, this transformation clones some particular instruction from
+  // the |block_id| into every block's predecessor and replaces the original
+  // instruction with OpPhi. Take a look at the transformation class to learn
+  // more about how we choose what instruction to propagate.
+
+  // Id of the block to propagate an instruction from.
+  uint32 block_id = 1;
+
+  // A map from the id of some predecessor of the |block_id| to the fresh id.
+  // The map contains a fresh id for at least every predecessor of the |block_id|.
+  // The instruction is propagated by creating a number of clones - one clone for
+  // each predecessor. Fresh ids from this field are used as result ids of cloned
+  // instructions.
+  repeated UInt32Pair predecessor_id_to_fresh_id = 2;
+
+}
+
 message TransformationPushIdThroughVariable {
 
   // A transformation that makes |value_synonym_id| and |value_id| to be
@@ -1259,6 +1307,24 @@ message TransformationRecordSynonymousConstants {
 
 }
 
+message TransformationReplaceAddSubMulWithCarryingExtended {
+
+   // Replaces OpIAdd with OpIAddCarry, OpISub with OpISubBorrow, OpIMul
+   // with OpUMulExtended or OpSMulExtended (depending on the signedness
+   // of the operands) and stores the result into a |struct_fresh_id|.
+   // In the original instruction the result type id and the type ids of
+   // the operands must be the same. Then the transformation extracts
+   // the first element of the result into the original |result_id|.
+   // This value is the same as the result of the original instruction.
+
+   // The fresh id of the intermediate result.
+   uint32 struct_fresh_id = 1;
+
+   // The result id of the original instruction.
+   uint32 result_id = 2;
+
+}
+
 message TransformationReplaceParameterWithGlobal {
 
   // Removes parameter with result id |parameter_id| from its function

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

@@ -54,13 +54,17 @@
 #include "source/fuzz/transformation_function_call.h"
 #include "source/fuzz/transformation_invert_comparison_operator.h"
 #include "source/fuzz/transformation_load.h"
+#include "source/fuzz/transformation_make_vector_operation_dynamic.h"
 #include "source/fuzz/transformation_merge_blocks.h"
 #include "source/fuzz/transformation_move_block_down.h"
+#include "source/fuzz/transformation_move_instruction_down.h"
 #include "source/fuzz/transformation_outline_function.h"
 #include "source/fuzz/transformation_permute_function_parameters.h"
 #include "source/fuzz/transformation_permute_phi_operands.h"
+#include "source/fuzz/transformation_propagate_instruction_up.h"
 #include "source/fuzz/transformation_push_id_through_variable.h"
 #include "source/fuzz/transformation_record_synonymous_constants.h"
+#include "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h"
 #include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
 #include "source/fuzz/transformation_replace_constant_with_uniform.h"
 #include "source/fuzz/transformation_replace_copy_memory_with_load_store.h"
@@ -190,10 +194,17 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
           message.invert_comparison_operator());
     case protobufs::Transformation::TransformationCase::kLoad:
       return MakeUnique<TransformationLoad>(message.load());
+    case protobufs::Transformation::TransformationCase::
+        kMakeVectorOperationDynamic:
+      return MakeUnique<TransformationMakeVectorOperationDynamic>(
+          message.make_vector_operation_dynamic());
     case protobufs::Transformation::TransformationCase::kMergeBlocks:
       return MakeUnique<TransformationMergeBlocks>(message.merge_blocks());
     case protobufs::Transformation::TransformationCase::kMoveBlockDown:
       return MakeUnique<TransformationMoveBlockDown>(message.move_block_down());
+    case protobufs::Transformation::TransformationCase::kMoveInstructionDown:
+      return MakeUnique<TransformationMoveInstructionDown>(
+          message.move_instruction_down());
     case protobufs::Transformation::TransformationCase::kOutlineFunction:
       return MakeUnique<TransformationOutlineFunction>(
           message.outline_function());
@@ -204,6 +215,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
     case protobufs::Transformation::TransformationCase::kPermutePhiOperands:
       return MakeUnique<TransformationPermutePhiOperands>(
           message.permute_phi_operands());
+    case protobufs::Transformation::TransformationCase::kPropagateInstructionUp:
+      return MakeUnique<TransformationPropagateInstructionUp>(
+          message.propagate_instruction_up());
     case protobufs::Transformation::TransformationCase::kPushIdThroughVariable:
       return MakeUnique<TransformationPushIdThroughVariable>(
           message.push_id_through_variable());
@@ -212,9 +226,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
       return MakeUnique<TransformationRecordSynonymousConstants>(
           message.record_synonymous_constants());
     case protobufs::Transformation::TransformationCase::
-        kReplaceParameterWithGlobal:
-      return MakeUnique<TransformationReplaceParameterWithGlobal>(
-          message.replace_parameter_with_global());
+        kReplaceAddSubMulWithCarryingExtended:
+      return MakeUnique<TransformationReplaceAddSubMulWithCarryingExtended>(
+          message.replace_add_sub_mul_with_carrying_extended());
     case protobufs::Transformation::TransformationCase::
         kReplaceBooleanConstantWithConstantBinary:
       return MakeUnique<TransformationReplaceBooleanConstantWithConstantBinary>(
@@ -242,6 +256,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
         kReplaceLoadStoreWithCopyMemory:
       return MakeUnique<TransformationReplaceLoadStoreWithCopyMemory>(
           message.replace_load_store_with_copy_memory());
+    case protobufs::Transformation::TransformationCase::
+        kReplaceParameterWithGlobal:
+      return MakeUnique<TransformationReplaceParameterWithGlobal>(
+          message.replace_parameter_with_global());
     case protobufs::Transformation::TransformationCase::
         kReplaceParamsWithStruct:
       return MakeUnique<TransformationReplaceParamsWithStruct>(

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

@@ -363,6 +363,22 @@ bool TransformationAddFunction::TryToMakeFunctionLivesafe(
   return true;
 }
 
+uint32_t TransformationAddFunction::GetBackEdgeBlockId(
+    opt::IRContext* ir_context, uint32_t loop_header_block_id) {
+  const auto* loop_header_block =
+      ir_context->cfg()->block(loop_header_block_id);
+  assert(loop_header_block && "|loop_header_block_id| is invalid");
+
+  for (auto pred : ir_context->cfg()->preds(loop_header_block_id)) {
+    if (ir_context->GetDominatorAnalysis(loop_header_block->GetParent())
+            ->Dominates(loop_header_block_id, pred)) {
+      return pred;
+    }
+  }
+
+  return 0;
+}
+
 bool TransformationAddFunction::TryToAddLoopLimiters(
     opt::IRContext* ir_context, opt::Function* added_function) const {
   // Collect up all the loop headers so that we can subsequently add loop
@@ -474,14 +490,8 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
   for (auto loop_header : loop_headers) {
     // Look for the loop's back-edge block.  This is a predecessor of the loop
     // header that is dominated by the loop header.
-    uint32_t back_edge_block_id = 0;
-    for (auto pred : ir_context->cfg()->preds(loop_header->id())) {
-      if (ir_context->GetDominatorAnalysis(added_function)
-              ->Dominates(loop_header->id(), pred)) {
-        back_edge_block_id = pred;
-        break;
-      }
-    }
+    const auto back_edge_block_id =
+        GetBackEdgeBlockId(ir_context, loop_header->id());
     if (!back_edge_block_id) {
       // The loop's back-edge block must be unreachable.  This means that the
       // loop cannot iterate, so there is no need to make it lifesafe; we can
@@ -692,9 +702,7 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
       back_edge_block_terminator->SetOpcode(SpvOpBranchConditional);
       back_edge_block_terminator->SetInOperands(opt::Instruction::OperandList(
           {{SPV_OPERAND_TYPE_ID, {loop_limiter_info.compare_id()}},
-           {SPV_OPERAND_TYPE_ID, {loop_header->MergeBlockId()}
-
-           },
+           {SPV_OPERAND_TYPE_ID, {loop_header->MergeBlockId()}},
            {SPV_OPERAND_TYPE_ID, {loop_header->id()}}}));
     }
 

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

@@ -65,6 +65,12 @@ class TransformationAddFunction : public Transformation {
       opt::IRContext* ir_context, const opt::Instruction& composite_type_inst,
       uint32_t index_id);
 
+  // Returns id of the back-edge block, given the corresponding
+  // |loop_header_block_id|. |loop_header_block_id| must be the id of a loop
+  // header block. Returns 0 if the loop has no back-edge block.
+  static uint32_t GetBackEdgeBlockId(opt::IRContext* ir_context,
+                                     uint32_t loop_header_block_id);
+
  private:
   // Attempts to create a function from the series of instructions in
   // |message_.instruction| and add it to |ir_context|.

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

@@ -14,8 +14,6 @@
 
 #include "source/fuzz/transformation_add_parameter.h"
 
-#include <source/spirv_constant.h>
-
 #include "source/fuzz/fuzzer_util.h"
 
 namespace spvtools {
@@ -72,13 +70,13 @@ void TransformationAddParameter::Apply(
   auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id());
   assert(function && "Can't find the function");
 
-  auto parameter_type_id =
+  const auto new_parameter_type_id =
       fuzzerutil::GetTypeId(ir_context, message_.initializer_id());
-  assert(parameter_type_id != 0 && "Initializer has invalid type");
+  assert(new_parameter_type_id != 0 && "Initializer has invalid type");
 
   // Add new parameters to the function.
   function->AddParameter(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpFunctionParameter, parameter_type_id,
+      ir_context, SpvOpFunctionParameter, new_parameter_type_id,
       message_.parameter_fresh_id(), opt::Instruction::OperandList()));
 
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.parameter_fresh_id());
@@ -92,43 +90,30 @@ void TransformationAddParameter::Apply(
       message_.parameter_fresh_id());
 
   // Fix all OpFunctionCall instructions.
-  ir_context->get_def_use_mgr()->ForEachUser(
-      &function->DefInst(), [this](opt::Instruction* call) {
-        if (call->opcode() != SpvOpFunctionCall ||
-            call->GetSingleWordInOperand(0) != message_.function_id()) {
-          return;
-        }
-
-        call->AddOperand({SPV_OPERAND_TYPE_ID, {message_.initializer_id()}});
-      });
-
-  auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function);
-  assert(old_function_type && "Function must have a valid type");
-
-  if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1) {
-    // Adjust existing function type if it is used only by this function.
-    old_function_type->AddOperand({SPV_OPERAND_TYPE_ID, {parameter_type_id}});
-
-    // We must make sure that all dependencies of |old_function_type| are
-    // evaluated before |old_function_type| (i.e. the domination rules are not
-    // broken). Thus, we move |old_function_type| to the end of the list of all
-    // types in the module.
-    old_function_type->RemoveFromList();
-    ir_context->AddType(std::unique_ptr<opt::Instruction>(old_function_type));
-  } else {
-    // Otherwise, either create a new type or use an existing one.
-    std::vector<uint32_t> type_ids;
-    type_ids.reserve(old_function_type->NumInOperands() + 1);
-
-    for (uint32_t i = 0, n = old_function_type->NumInOperands(); i < n; ++i) {
-      type_ids.push_back(old_function_type->GetSingleWordInOperand(i));
+  for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) {
+    inst->AddOperand({SPV_OPERAND_TYPE_ID, {message_.initializer_id()}});
+  }
+
+  // Update function's type.
+  {
+    // We use a separate scope here since |old_function_type| might become a
+    // dangling pointer after the call to the fuzzerutil::UpdateFunctionType.
+
+    const auto* old_function_type =
+        fuzzerutil::GetFunctionType(ir_context, function);
+    assert(old_function_type && "Function must have a valid type");
+
+    std::vector<uint32_t> parameter_type_ids;
+    for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) {
+      parameter_type_ids.push_back(
+          old_function_type->GetSingleWordInOperand(i));
     }
 
-    type_ids.push_back(parameter_type_id);
+    parameter_type_ids.push_back(new_parameter_type_id);
 
-    function->DefInst().SetInOperand(
-        1, {fuzzerutil::FindOrCreateFunctionType(
-               ir_context, message_.function_type_fresh_id(), type_ids)});
+    fuzzerutil::UpdateFunctionType(
+        ir_context, function->result_id(), message_.function_type_fresh_id(),
+        old_function_type->GetSingleWordInOperand(0), parameter_type_ids);
   }
 
   // Make sure our changes are analyzed.

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

@@ -36,6 +36,28 @@ bool TransformationAddTypeFloat::IsApplicable(
     return false;
   }
 
+  // Checks float type width capabilities.
+  switch (message_.width()) {
+    case 16:
+      // The Float16 capability must be present.
+      if (!ir_context->get_feature_mgr()->HasCapability(SpvCapabilityFloat16)) {
+        return false;
+      }
+      break;
+    case 32:
+      // No capabilities needed.
+      break;
+    case 64:
+      // The Float64 capability must be present.
+      if (!ir_context->get_feature_mgr()->HasCapability(SpvCapabilityFloat64)) {
+        return false;
+      }
+      break;
+    default:
+      assert(false && "Unexpected float type width");
+      return false;
+  }
+
   // Applicable if there is no float type with this width already declared in
   // the module.
   return fuzzerutil::MaybeGetFloatType(ir_context, message_.width()) == 0;

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

@@ -38,6 +38,34 @@ bool TransformationAddTypeInt::IsApplicable(
     return false;
   }
 
+  // Checks integer type width capabilities.
+  switch (message_.width()) {
+    case 8:
+      // The Int8 capability must be present.
+      if (!ir_context->get_feature_mgr()->HasCapability(SpvCapabilityInt8)) {
+        return false;
+      }
+      break;
+    case 16:
+      // The Int16 capability must be present.
+      if (!ir_context->get_feature_mgr()->HasCapability(SpvCapabilityInt16)) {
+        return false;
+      }
+      break;
+    case 32:
+      // No capabilities needed.
+      break;
+    case 64:
+      // The Int64 capability must be present.
+      if (!ir_context->get_feature_mgr()->HasCapability(SpvCapabilityInt64)) {
+        return false;
+      }
+      break;
+    default:
+      assert(false && "Unexpected integer type width");
+      return false;
+  }
+
   // Applicable if there is no int type with this width and signedness already
   // declared in the module.
   return fuzzerutil::MaybeGetIntegerType(ir_context, message_.width(),

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

@@ -0,0 +1,111 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_make_vector_operation_dynamic.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationMakeVectorOperationDynamic::
+    TransformationMakeVectorOperationDynamic(
+        const spvtools::fuzz::protobufs::
+            TransformationMakeVectorOperationDynamic& message)
+    : message_(message) {}
+
+TransformationMakeVectorOperationDynamic::
+    TransformationMakeVectorOperationDynamic(uint32_t instruction_result_id,
+                                             uint32_t constant_index_id) {
+  message_.set_instruction_result_id(instruction_result_id);
+  message_.set_constant_index_id(constant_index_id);
+}
+
+bool TransformationMakeVectorOperationDynamic::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  // |instruction| must be a vector operation.
+  auto instruction =
+      ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id());
+  if (!IsVectorOperation(ir_context, instruction)) {
+    return false;
+  }
+
+  // |constant_index_instruction| must be defined as an integer instruction.
+  auto constant_index_instruction =
+      ir_context->get_def_use_mgr()->GetDef(message_.constant_index_id());
+  if (!constant_index_instruction || !constant_index_instruction->type_id() ||
+      !ir_context->get_type_mgr()
+           ->GetType(constant_index_instruction->type_id())
+           ->AsInteger()) {
+    return false;
+  }
+
+  return true;
+}
+
+void TransformationMakeVectorOperationDynamic::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  auto instruction =
+      ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id());
+
+  // The OpVectorInsertDynamic instruction has the vector and component operands
+  // in reverse order in relation to the OpCompositeInsert corresponding
+  // operands.
+  if (instruction->opcode() == SpvOpCompositeInsert) {
+    std::swap(instruction->GetInOperand(0), instruction->GetInOperand(1));
+  }
+
+  // Sets the literal operand to the equivalent constant.
+  instruction->SetInOperand(
+      instruction->opcode() == SpvOpCompositeExtract ? 1 : 2,
+      {message_.constant_index_id()});
+
+  // Sets the |instruction| opcode to the corresponding vector dynamic opcode.
+  instruction->SetOpcode(instruction->opcode() == SpvOpCompositeExtract
+                             ? SpvOpVectorExtractDynamic
+                             : SpvOpVectorInsertDynamic);
+}
+
+protobufs::Transformation TransformationMakeVectorOperationDynamic::ToMessage()
+    const {
+  protobufs::Transformation result;
+  *result.mutable_make_vector_operation_dynamic() = message_;
+  return result;
+}
+
+bool TransformationMakeVectorOperationDynamic::IsVectorOperation(
+    opt::IRContext* ir_context, opt::Instruction* instruction) {
+  // |instruction| must be defined and must be an OpCompositeExtract/Insert
+  // instruction.
+  if (!instruction || (instruction->opcode() != SpvOpCompositeExtract &&
+                       instruction->opcode() != SpvOpCompositeInsert)) {
+    return false;
+  }
+
+  // The composite must be a vector.
+  auto composite_instruction =
+      ir_context->get_def_use_mgr()->GetDef(instruction->GetSingleWordInOperand(
+          instruction->opcode() == SpvOpCompositeExtract ? 0 : 1));
+  if (!ir_context->get_type_mgr()
+           ->GetType(composite_instruction->type_id())
+           ->AsVector()) {
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

@@ -0,0 +1,63 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_MAKE_VECTOR_OPERATION_DYNAMIC_H_
+#define SOURCE_FUZZ_TRANSFORMATION_MAKE_VECTOR_OPERATION_DYNAMIC_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationMakeVectorOperationDynamic : public Transformation {
+ public:
+  explicit TransformationMakeVectorOperationDynamic(
+      const protobufs::TransformationMakeVectorOperationDynamic& message);
+
+  TransformationMakeVectorOperationDynamic(uint32_t instruction_result_id,
+                                           uint32_t constant_index_id);
+
+  // - |message_.instruction_result_id| must be the result id of an
+  // OpCompositeExtract/Insert instruction such that the composite operand is a
+  // vector.
+  // - |message_.constant_index_id| must be the result id of an integer
+  // instruction such that its value equals the indexing literal of the
+  // OpCompositeExtract/Insert instruction.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Replaces the OpCompositeExtract and OpCompositeInsert instructions with the
+  // OpVectorExtractDynamic and OpVectorInsertDynamic instructions.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Checks |instruction| is defined, is an OpCompositeExtract/Insert
+  // instruction and the composite operand is a vector.
+  static bool IsVectorOperation(opt::IRContext* ir_context,
+                                opt::Instruction* instruction);
+
+ private:
+  protobufs::TransformationMakeVectorOperationDynamic message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_MAKE_VECTOR_OPERATION_DYNAMIC_H_

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

@@ -0,0 +1,219 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_move_instruction_down.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationMoveInstructionDown::TransformationMoveInstructionDown(
+    const protobufs::TransformationMoveInstructionDown& message)
+    : message_(message) {}
+
+TransformationMoveInstructionDown::TransformationMoveInstructionDown(
+    const protobufs::InstructionDescriptor& instruction) {
+  *message_.mutable_instruction() = instruction;
+}
+
+bool TransformationMoveInstructionDown::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  // |instruction| must be valid.
+  auto* inst = FindInstruction(message_.instruction(), ir_context);
+  if (!inst) {
+    return false;
+  }
+
+  // Instruction's opcode must be supported by this transformation.
+  if (!IsOpcodeSupported(inst->opcode())) {
+    return false;
+  }
+
+  auto* inst_block = ir_context->get_instr_block(inst);
+  assert(inst_block &&
+         "Global instructions and function parameters are not supported");
+
+  auto inst_it = fuzzerutil::GetIteratorForInstruction(inst_block, inst);
+  assert(inst_it != inst_block->end() &&
+         "Can't get an iterator for the instruction");
+
+  // |instruction| can't be the last instruction in the block.
+  auto successor_it = ++inst_it;
+  if (successor_it == inst_block->end()) {
+    return false;
+  }
+
+  // Check that we can insert |instruction| after |inst_it|.
+  auto successors_successor_it = ++inst_it;
+  if (successors_successor_it == inst_block->end() ||
+      !fuzzerutil::CanInsertOpcodeBeforeInstruction(inst->opcode(),
+                                                    successors_successor_it)) {
+    return false;
+  }
+
+  // Check that |instruction|'s successor doesn't depend on the |instruction|.
+  if (inst->result_id()) {
+    for (uint32_t i = 0; i < successor_it->NumInOperands(); ++i) {
+      const auto& operand = successor_it->GetInOperand(i);
+      if (operand.type == SPV_OPERAND_TYPE_ID &&
+          operand.words[0] == inst->result_id()) {
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+void TransformationMoveInstructionDown::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  auto* inst = FindInstruction(message_.instruction(), ir_context);
+  assert(inst &&
+         "The instruction should've been validated in the IsApplicable");
+
+  auto inst_it = fuzzerutil::GetIteratorForInstruction(
+      ir_context->get_instr_block(inst), inst);
+
+  // Move the instruction down in the block.
+  inst->InsertAfter(&*++inst_it);
+
+  ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationMoveInstructionDown::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_move_instruction_down() = message_;
+  return result;
+}
+
+bool TransformationMoveInstructionDown::IsOpcodeSupported(SpvOp opcode) {
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3605):
+  //  We only support "simple" instructions that work don't with memory.
+  //  We should extend this so that we support the ones that modify the memory
+  //  too.
+  switch (opcode) {
+    case SpvOpNop:
+    case SpvOpUndef:
+    case SpvOpAccessChain:
+    case SpvOpInBoundsAccessChain:
+    case SpvOpArrayLength:
+    case SpvOpVectorExtractDynamic:
+    case SpvOpVectorInsertDynamic:
+    case SpvOpVectorShuffle:
+    case SpvOpCompositeConstruct:
+    case SpvOpCompositeExtract:
+    case SpvOpCompositeInsert:
+    case SpvOpCopyObject:
+    case SpvOpTranspose:
+    case SpvOpConvertFToU:
+    case SpvOpConvertFToS:
+    case SpvOpConvertSToF:
+    case SpvOpConvertUToF:
+    case SpvOpUConvert:
+    case SpvOpSConvert:
+    case SpvOpFConvert:
+    case SpvOpQuantizeToF16:
+    case SpvOpSatConvertSToU:
+    case SpvOpSatConvertUToS:
+    case SpvOpBitcast:
+    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:
+    case SpvOpSelect:
+    case SpvOpIEqual:
+    case SpvOpINotEqual:
+    case SpvOpUGreaterThan:
+    case SpvOpSGreaterThan:
+    case SpvOpUGreaterThanEqual:
+    case SpvOpSGreaterThanEqual:
+    case SpvOpULessThan:
+    case SpvOpSLessThan:
+    case SpvOpULessThanEqual:
+    case SpvOpSLessThanEqual:
+    case SpvOpFOrdEqual:
+    case SpvOpFUnordEqual:
+    case SpvOpFOrdNotEqual:
+    case SpvOpFUnordNotEqual:
+    case SpvOpFOrdLessThan:
+    case SpvOpFUnordLessThan:
+    case SpvOpFOrdGreaterThan:
+    case SpvOpFUnordGreaterThan:
+    case SpvOpFOrdLessThanEqual:
+    case SpvOpFUnordLessThanEqual:
+    case SpvOpFOrdGreaterThanEqual:
+    case SpvOpFUnordGreaterThanEqual:
+    case SpvOpShiftRightLogical:
+    case SpvOpShiftRightArithmetic:
+    case SpvOpShiftLeftLogical:
+    case SpvOpBitwiseOr:
+    case SpvOpBitwiseXor:
+    case SpvOpBitwiseAnd:
+    case SpvOpNot:
+    case SpvOpBitFieldInsert:
+    case SpvOpBitFieldSExtract:
+    case SpvOpBitFieldUExtract:
+    case SpvOpBitReverse:
+    case SpvOpBitCount:
+    case SpvOpCopyLogical:
+    case SpvOpPtrEqual:
+    case SpvOpPtrNotEqual:
+      return true;
+    default:
+      return false;
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

@@ -0,0 +1,62 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_MOVE_INSTRUCTION_DOWN_H_
+#define SOURCE_FUZZ_TRANSFORMATION_MOVE_INSTRUCTION_DOWN_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationMoveInstructionDown : public Transformation {
+ public:
+  explicit TransformationMoveInstructionDown(
+      const protobufs::TransformationMoveInstructionDown& message);
+
+  explicit TransformationMoveInstructionDown(
+      const protobufs::InstructionDescriptor& instruction);
+
+  // - |instruction| should be a descriptor of a valid instruction in the module
+  // - |instruction|'s opcode should be supported by this transformation
+  // - neither |instruction| nor its successor may be the last instruction in
+  //   the block
+  // - |instruction|'s successor may not be dependent on the |instruction|
+  // - it should be possible to insert |instruction|'s opcode after its
+  //   successor
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Swaps |instruction| with its successor.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  // Returns true if the |opcode| is supported by this transformation (i.e.
+  // we can move an instruction with this opcode).
+  static bool IsOpcodeSupported(SpvOp opcode);
+
+  protobufs::TransformationMoveInstructionDown message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_MOVE_INSTRUCTION_DOWN_H_

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

@@ -0,0 +1,402 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_propagate_instruction_up.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+uint32_t GetResultIdFromLabelId(const opt::Instruction& phi_inst,
+                                uint32_t label_id) {
+  assert(phi_inst.opcode() == SpvOpPhi && "|phi_inst| is not an OpPhi");
+
+  for (uint32_t i = 1; i < phi_inst.NumInOperands(); i += 2) {
+    if (phi_inst.GetSingleWordInOperand(i) == label_id) {
+      return phi_inst.GetSingleWordInOperand(i - 1);
+    }
+  }
+
+  return 0;
+}
+
+bool ContainsPointers(const opt::analysis::Type& type) {
+  switch (type.kind()) {
+    case opt::analysis::Type::kPointer:
+      return true;
+    case opt::analysis::Type::kStruct:
+      return std::any_of(type.AsStruct()->element_types().begin(),
+                         type.AsStruct()->element_types().end(),
+                         [](const opt::analysis::Type* element_type) {
+                           return ContainsPointers(*element_type);
+                         });
+    default:
+      return false;
+  }
+}
+
+bool HasValidDependencies(opt::IRContext* ir_context, opt::Instruction* inst) {
+  const auto* inst_block = ir_context->get_instr_block(inst);
+  assert(inst_block &&
+         "This function shouldn't be applied to global instructions or function"
+         "parameters");
+
+  for (uint32_t i = 0; i < inst->NumInOperands(); ++i) {
+    const auto& operand = inst->GetInOperand(i);
+    if (operand.type != SPV_OPERAND_TYPE_ID) {
+      // Consider only <id> operands.
+      continue;
+    }
+
+    auto* dependency = ir_context->get_def_use_mgr()->GetDef(operand.words[0]);
+    assert(dependency && "Operand has invalid id");
+
+    if (ir_context->get_instr_block(dependency) == inst_block &&
+        dependency->opcode() != SpvOpPhi) {
+      // |dependency| is "valid" if it's an OpPhi from the same basic block or
+      // an instruction from a different basic block.
+      return false;
+    }
+  }
+
+  return true;
+}
+
+}  // namespace
+
+TransformationPropagateInstructionUp::TransformationPropagateInstructionUp(
+    const protobufs::TransformationPropagateInstructionUp& message)
+    : message_(message) {}
+
+TransformationPropagateInstructionUp::TransformationPropagateInstructionUp(
+    uint32_t block_id,
+    const std::map<uint32_t, uint32_t>& predecessor_id_to_fresh_id) {
+  message_.set_block_id(block_id);
+  *message_.mutable_predecessor_id_to_fresh_id() =
+      fuzzerutil::MapToRepeatedUInt32Pair(predecessor_id_to_fresh_id);
+}
+
+bool TransformationPropagateInstructionUp::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  // Check that we can apply this transformation to the |block_id|.
+  if (!IsApplicableToBlock(ir_context, message_.block_id())) {
+    return false;
+  }
+
+  const auto predecessor_id_to_fresh_id = fuzzerutil::RepeatedUInt32PairToMap(
+      message_.predecessor_id_to_fresh_id());
+  for (auto id : ir_context->cfg()->preds(message_.block_id())) {
+    // Each predecessor must have a fresh id in the |predecessor_id_to_fresh_id|
+    // map.
+    if (!predecessor_id_to_fresh_id.count(id)) {
+      return false;
+    }
+  }
+
+  std::vector<uint32_t> maybe_fresh_ids;
+  maybe_fresh_ids.reserve(predecessor_id_to_fresh_id.size());
+  for (const auto& entry : predecessor_id_to_fresh_id) {
+    maybe_fresh_ids.push_back(entry.second);
+  }
+
+  // All ids must be unique and fresh.
+  return !fuzzerutil::HasDuplicates(maybe_fresh_ids) &&
+         std::all_of(maybe_fresh_ids.begin(), maybe_fresh_ids.end(),
+                     [ir_context](uint32_t id) {
+                       return fuzzerutil::IsFreshId(ir_context, id);
+                     });
+}
+
+void TransformationPropagateInstructionUp::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  auto* inst = GetInstructionToPropagate(ir_context, message_.block_id());
+  assert(inst &&
+         "The block must have at least one supported instruction to propagate");
+  assert(inst->result_id() && inst->type_id() &&
+         "|inst| must have a result id and a type id");
+
+  opt::Instruction::OperandList op_phi_operands;
+  const auto predecessor_id_to_fresh_id = fuzzerutil::RepeatedUInt32PairToMap(
+      message_.predecessor_id_to_fresh_id());
+  std::unordered_set<uint32_t> visited_predecessors;
+  for (auto predecessor_id : ir_context->cfg()->preds(message_.block_id())) {
+    // A block can have multiple identical predecessors.
+    if (visited_predecessors.count(predecessor_id)) {
+      continue;
+    }
+
+    visited_predecessors.insert(predecessor_id);
+
+    auto new_result_id = predecessor_id_to_fresh_id.at(predecessor_id);
+
+    // Compute InOperands for the OpPhi instruction to be inserted later.
+    op_phi_operands.push_back({SPV_OPERAND_TYPE_ID, {new_result_id}});
+    op_phi_operands.push_back({SPV_OPERAND_TYPE_ID, {predecessor_id}});
+
+    // Create a clone of the |inst| to be inserted into the |predecessor_id|.
+    std::unique_ptr<opt::Instruction> clone(inst->Clone(ir_context));
+    clone->SetResultId(new_result_id);
+
+    fuzzerutil::UpdateModuleIdBound(ir_context, new_result_id);
+
+    // Adjust |clone|'s operands to account for possible dependencies on OpPhi
+    // instructions from the same basic block.
+    for (uint32_t i = 0; i < clone->NumInOperands(); ++i) {
+      auto& operand = clone->GetInOperand(i);
+      if (operand.type != SPV_OPERAND_TYPE_ID) {
+        // Consider only ids.
+        continue;
+      }
+
+      const auto* dependency_inst =
+          ir_context->get_def_use_mgr()->GetDef(operand.words[0]);
+      assert(dependency_inst && "|clone| depends on an invalid id");
+
+      if (ir_context->get_instr_block(dependency_inst->result_id()) !=
+          ir_context->cfg()->block(message_.block_id())) {
+        // We don't need to adjust anything if |dependency_inst| is from a
+        // different block, a global instruction or a function parameter.
+        continue;
+      }
+
+      assert(dependency_inst->opcode() == SpvOpPhi &&
+             "Propagated instruction can depend only on OpPhis from the same "
+             "basic block or instructions from different basic blocks");
+
+      auto new_id = GetResultIdFromLabelId(*dependency_inst, predecessor_id);
+      assert(new_id && "OpPhi instruction is missing a predecessor");
+      operand.words[0] = new_id;
+    }
+
+    auto* insert_before_inst = fuzzerutil::GetLastInsertBeforeInstruction(
+        ir_context, predecessor_id, clone->opcode());
+    assert(insert_before_inst && "Can't insert |clone| into |predecessor_id");
+
+    insert_before_inst->InsertBefore(std::move(clone));
+  }
+
+  // Insert an OpPhi instruction into the basic block of |inst|.
+  ir_context->get_instr_block(inst)->begin()->InsertBefore(
+      MakeUnique<opt::Instruction>(ir_context, SpvOpPhi, inst->type_id(),
+                                   inst->result_id(),
+                                   std::move(op_phi_operands)));
+
+  // Remove |inst| from the basic block.
+  ir_context->KillInst(inst);
+
+  // We have changed the module so most analyzes are now invalid.
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationPropagateInstructionUp::ToMessage()
+    const {
+  protobufs::Transformation result;
+  *result.mutable_propagate_instruction_up() = message_;
+  return result;
+}
+
+bool TransformationPropagateInstructionUp::IsOpcodeSupported(SpvOp opcode) {
+  // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3605):
+  //  We only support "simple" instructions that don't work with memory.
+  //  We should extend this so that we support the ones that modify the memory
+  //  too.
+  switch (opcode) {
+    case SpvOpUndef:
+    case SpvOpAccessChain:
+    case SpvOpInBoundsAccessChain:
+    case SpvOpArrayLength:
+    case SpvOpVectorExtractDynamic:
+    case SpvOpVectorInsertDynamic:
+    case SpvOpVectorShuffle:
+    case SpvOpCompositeConstruct:
+    case SpvOpCompositeExtract:
+    case SpvOpCompositeInsert:
+    case SpvOpCopyObject:
+    case SpvOpTranspose:
+    case SpvOpConvertFToU:
+    case SpvOpConvertFToS:
+    case SpvOpConvertSToF:
+    case SpvOpConvertUToF:
+    case SpvOpUConvert:
+    case SpvOpSConvert:
+    case SpvOpFConvert:
+    case SpvOpQuantizeToF16:
+    case SpvOpSatConvertSToU:
+    case SpvOpSatConvertUToS:
+    case SpvOpBitcast:
+    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:
+    case SpvOpSelect:
+    case SpvOpIEqual:
+    case SpvOpINotEqual:
+    case SpvOpUGreaterThan:
+    case SpvOpSGreaterThan:
+    case SpvOpUGreaterThanEqual:
+    case SpvOpSGreaterThanEqual:
+    case SpvOpULessThan:
+    case SpvOpSLessThan:
+    case SpvOpULessThanEqual:
+    case SpvOpSLessThanEqual:
+    case SpvOpFOrdEqual:
+    case SpvOpFUnordEqual:
+    case SpvOpFOrdNotEqual:
+    case SpvOpFUnordNotEqual:
+    case SpvOpFOrdLessThan:
+    case SpvOpFUnordLessThan:
+    case SpvOpFOrdGreaterThan:
+    case SpvOpFUnordGreaterThan:
+    case SpvOpFOrdLessThanEqual:
+    case SpvOpFUnordLessThanEqual:
+    case SpvOpFOrdGreaterThanEqual:
+    case SpvOpFUnordGreaterThanEqual:
+    case SpvOpShiftRightLogical:
+    case SpvOpShiftRightArithmetic:
+    case SpvOpShiftLeftLogical:
+    case SpvOpBitwiseOr:
+    case SpvOpBitwiseXor:
+    case SpvOpBitwiseAnd:
+    case SpvOpNot:
+    case SpvOpBitFieldInsert:
+    case SpvOpBitFieldSExtract:
+    case SpvOpBitFieldUExtract:
+    case SpvOpBitReverse:
+    case SpvOpBitCount:
+    case SpvOpCopyLogical:
+    case SpvOpPtrEqual:
+    case SpvOpPtrNotEqual:
+      return true;
+    default:
+      return false;
+  }
+}
+
+opt::Instruction*
+TransformationPropagateInstructionUp::GetInstructionToPropagate(
+    opt::IRContext* ir_context, uint32_t block_id) {
+  auto* block = ir_context->cfg()->block(block_id);
+  assert(block && "|block_id| is invalid");
+
+  for (auto& inst : *block) {
+    // We look for the first instruction in the block that satisfies the
+    // following rules:
+    // - it's not an OpPhi
+    // - it must be supported by this transformation
+    // - it may depend only on instructions from different basic blocks or on
+    //   OpPhi instructions from the same basic block.
+    if (inst.opcode() == SpvOpPhi || !IsOpcodeSupported(inst.opcode()) ||
+        !inst.type_id() || !inst.result_id()) {
+      continue;
+    }
+
+    const auto* inst_type = ir_context->get_type_mgr()->GetType(inst.type_id());
+    assert(inst_type && "|inst| has invalid type");
+
+    if (!ir_context->get_feature_mgr()->HasCapability(
+            SpvCapabilityVariablePointersStorageBuffer) &&
+        ContainsPointers(*inst_type)) {
+      // OpPhi supports pointer operands only with VariablePointers or
+      // VariablePointersStorageBuffer capabilities.
+      //
+      // Note that VariablePointers capability implicitly declares
+      // VariablePointersStorageBuffer capability.
+      continue;
+    }
+
+    if (!HasValidDependencies(ir_context, &inst)) {
+      continue;
+    }
+
+    return &inst;
+  }
+
+  return nullptr;
+}
+
+bool TransformationPropagateInstructionUp::IsApplicableToBlock(
+    opt::IRContext* ir_context, uint32_t block_id) {
+  // Check that |block_id| is valid.
+  const auto* label_inst = ir_context->get_def_use_mgr()->GetDef(block_id);
+  if (!label_inst || label_inst->opcode() != SpvOpLabel) {
+    return false;
+  }
+
+  // Check that |block| has predecessors.
+  const auto& predecessors = ir_context->cfg()->preds(block_id);
+  if (predecessors.empty()) {
+    return false;
+  }
+
+  // The block must contain an instruction to propagate.
+  const auto* inst_to_propagate =
+      GetInstructionToPropagate(ir_context, block_id);
+  if (!inst_to_propagate) {
+    return false;
+  }
+
+  // We should be able to insert |inst_to_propagate| into every predecessor of
+  // |block|.
+  return std::all_of(predecessors.begin(), predecessors.end(),
+                     [ir_context, inst_to_propagate](uint32_t predecessor_id) {
+                       return fuzzerutil::GetLastInsertBeforeInstruction(
+                                  ir_context, predecessor_id,
+                                  inst_to_propagate->opcode()) != nullptr;
+                     });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 89 - 0
3rdparty/spirv-tools/source/fuzz/transformation_propagate_instruction_up.h

@@ -0,0 +1,89 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_PROPAGATE_INSTRUCTION_UP_H_
+#define SOURCE_FUZZ_TRANSFORMATION_PROPAGATE_INSTRUCTION_UP_H_
+
+#include <map>
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationPropagateInstructionUp : public Transformation {
+ public:
+  explicit TransformationPropagateInstructionUp(
+      const protobufs::TransformationPropagateInstructionUp& message);
+
+  TransformationPropagateInstructionUp(
+      uint32_t block_id,
+      const std::map<uint32_t, uint32_t>& predecessor_id_to_fresh_id);
+
+  // - |block_id| must be a valid result id of some OpLabel instruction.
+  // - |block_id| must have at least one predecessor
+  // - |block_id| must contain an instruction that can be propagated using this
+  //   transformation
+  // - the instruction can be propagated if:
+  //   - it's not an OpPhi
+  //   - it is supported by this transformation
+  //   - it depends only on instructions from different basic blocks or on
+  //     OpPhi instructions from the same basic block
+  // - it should be possible to insert the propagated instruction at the end of
+  //   each |block_id|'s predecessor
+  // - |predecessor_id_to_fresh_id| must have an entry for at least every
+  //   predecessor of |block_id|
+  // - each value in the |predecessor_id_to_fresh_id| map must be a fresh id
+  // - all fresh ids in the |predecessor_id_to_fresh_id| must be unique
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Inserts a copy of the propagated instruction into each |block_id|'s
+  // predecessor. Replaces the original instruction with an OpPhi referring
+  // inserted copies.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Returns true if this transformation can be applied to the block with id
+  // |block_id|. Concretely, returns true iff:
+  // - |block_id| is a valid id of some block in the module
+  // - |block_id| has predecessors
+  // - |block_id| contains an instruction that can be propagated
+  // - it is possible to insert the propagated instruction into every
+  //   |block_id|'s predecessor
+  static bool IsApplicableToBlock(opt::IRContext* ir_context,
+                                  uint32_t block_id);
+
+ private:
+  // Returns the instruction that will be propagated into the predecessors of
+  // the |block_id|. Returns nullptr if no such an instruction exists.
+  static opt::Instruction* GetInstructionToPropagate(opt::IRContext* ir_context,
+                                                     uint32_t block_id);
+
+  // Returns true if |opcode| is supported by this transformation.
+  static bool IsOpcodeSupported(SpvOp opcode);
+
+  protobufs::TransformationPropagateInstructionUp message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_PROPAGATE_INSTRUCTION_UP_H_

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

@@ -0,0 +1,232 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+namespace {
+const uint32_t kOpCompositeExtractIndexLowOrderBits = 0;
+const uint32_t kArithmeticInstructionIndexLeftInOperand = 0;
+const uint32_t kArithmeticInstructionIndexRightInOperand = 1;
+}  // namespace
+
+TransformationReplaceAddSubMulWithCarryingExtended::
+    TransformationReplaceAddSubMulWithCarryingExtended(
+        const spvtools::fuzz::protobufs::
+            TransformationReplaceAddSubMulWithCarryingExtended& message)
+    : message_(message) {}
+
+TransformationReplaceAddSubMulWithCarryingExtended::
+    TransformationReplaceAddSubMulWithCarryingExtended(uint32_t struct_fresh_id,
+                                                       uint32_t result_id) {
+  message_.set_struct_fresh_id(struct_fresh_id);
+  message_.set_result_id(result_id);
+}
+
+bool TransformationReplaceAddSubMulWithCarryingExtended::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  // |message_.struct_fresh_id| must be fresh.
+  if (!fuzzerutil::IsFreshId(ir_context, message_.struct_fresh_id())) {
+    return false;
+  }
+
+  // |message_.result_id| must refer to a suitable OpIAdd, OpISub or OpIMul
+  // instruction. The instruction must be defined.
+  auto instruction =
+      ir_context->get_def_use_mgr()->GetDef(message_.result_id());
+  if (instruction == nullptr) {
+    return false;
+  }
+  if (!TransformationReplaceAddSubMulWithCarryingExtended::
+          IsInstructionSuitable(ir_context, *instruction)) {
+    return false;
+  }
+
+  // The struct type for holding the intermediate result must exist in the
+  // module. The struct type is based on the operand type.
+  uint32_t operand_type_id = ir_context->get_def_use_mgr()
+                                 ->GetDef(instruction->GetSingleWordInOperand(
+                                     kArithmeticInstructionIndexLeftInOperand))
+                                 ->type_id();
+
+  uint32_t struct_type_id = fuzzerutil::MaybeGetStructType(
+      ir_context, {operand_type_id, operand_type_id});
+  if (struct_type_id == 0) {
+    return false;
+  }
+  return true;
+}
+
+void TransformationReplaceAddSubMulWithCarryingExtended::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  // |message_.struct_fresh_id| must be fresh.
+  assert(fuzzerutil::IsFreshId(ir_context, message_.struct_fresh_id()) &&
+         "|message_.struct_fresh_id| must be fresh");
+
+  // Get the signedness of an operand if it is an int or the signedness of a
+  // component if it is a vector.
+  auto type_id =
+      ir_context->get_def_use_mgr()->GetDef(message_.result_id())->type_id();
+  auto type = ir_context->get_type_mgr()->GetType(type_id);
+  bool operand_is_signed;
+  if (type->kind() == opt::analysis::Type::kVector) {
+    auto operand_type = type->AsVector()->element_type();
+    operand_is_signed = operand_type->AsInteger()->IsSigned();
+  } else {
+    operand_is_signed = type->AsInteger()->IsSigned();
+  }
+
+  auto original_instruction =
+      ir_context->get_def_use_mgr()->GetDef(message_.result_id());
+
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.struct_fresh_id());
+
+  // Determine the opcode of the new instruction that computes the result into a
+  // struct.
+  SpvOp new_instruction_opcode;
+
+  switch (original_instruction->opcode()) {
+    case SpvOpIAdd:
+      new_instruction_opcode = SpvOpIAddCarry;
+      break;
+    case SpvOpISub:
+      new_instruction_opcode = SpvOpISubBorrow;
+      break;
+    case SpvOpIMul:
+      if (!operand_is_signed) {
+        new_instruction_opcode = SpvOpUMulExtended;
+      } else {
+        new_instruction_opcode = SpvOpSMulExtended;
+      }
+      break;
+    default:
+      assert(false && "The instruction has an unsupported opcode.");
+      return;
+  }
+  // Get the type of struct type id holding the intermediate result based on the
+  // operand type.
+  uint32_t operand_type_id =
+      ir_context->get_def_use_mgr()
+          ->GetDef(original_instruction->GetSingleWordInOperand(
+              kArithmeticInstructionIndexLeftInOperand))
+          ->type_id();
+
+  uint32_t struct_type_id = fuzzerutil::MaybeGetStructType(
+      ir_context, {operand_type_id, operand_type_id});
+  // Avoid unused variables in release mode.
+  (void)struct_type_id;
+  assert(struct_type_id && "The struct type must exist in the module.");
+
+  // Insert the new instruction that computes the result into a struct before
+  // the |original_instruction|.
+  original_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+      ir_context, new_instruction_opcode, struct_type_id,
+      message_.struct_fresh_id(),
+      opt::Instruction::OperandList(
+          {{SPV_OPERAND_TYPE_ID,
+            {original_instruction->GetSingleWordInOperand(
+                kArithmeticInstructionIndexLeftInOperand)}},
+           {SPV_OPERAND_TYPE_ID,
+            {original_instruction->GetSingleWordInOperand(
+                kArithmeticInstructionIndexRightInOperand)}}})));
+
+  // Insert the OpCompositeExtract after the added instruction. This instruction
+  // takes the first component of the struct which represents low-order bits of
+  // the operation. This is the original result.
+  original_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpCompositeExtract, original_instruction->type_id(),
+      message_.result_id(),
+      opt::Instruction::OperandList(
+          {{SPV_OPERAND_TYPE_ID, {message_.struct_fresh_id()}},
+           {SPV_OPERAND_TYPE_LITERAL_INTEGER,
+            {kOpCompositeExtractIndexLowOrderBits}}})));
+
+  // Remove the original instruction.
+  ir_context->KillInst(original_instruction);
+
+  // We have modified the module so most analyzes are now invalid.
+  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+bool TransformationReplaceAddSubMulWithCarryingExtended::IsInstructionSuitable(
+    opt::IRContext* ir_context, const opt::Instruction& instruction) {
+  auto instruction_opcode = instruction.opcode();
+
+  // Only instructions OpIAdd, OpISub, OpIMul are supported.
+  switch (instruction_opcode) {
+    case SpvOpIAdd:
+    case SpvOpISub:
+    case SpvOpIMul:
+      break;
+    default:
+      return false;
+  }
+  uint32_t operand_1_type_id =
+      ir_context->get_def_use_mgr()
+          ->GetDef(instruction.GetSingleWordInOperand(
+              kArithmeticInstructionIndexLeftInOperand))
+          ->type_id();
+
+  uint32_t operand_2_type_id =
+      ir_context->get_def_use_mgr()
+          ->GetDef(instruction.GetSingleWordInOperand(
+              kArithmeticInstructionIndexRightInOperand))
+          ->type_id();
+
+  uint32_t result_type_id = instruction.type_id();
+
+  // Both type ids of the operands and the result type ids must be equal.
+  if (operand_1_type_id != operand_2_type_id) {
+    return false;
+  }
+  if (operand_2_type_id != result_type_id) {
+    return false;
+  }
+
+  // In case of OpIAdd and OpISub, the type must be unsigned.
+  auto type = ir_context->get_type_mgr()->GetType(instruction.type_id());
+
+  switch (instruction_opcode) {
+    case SpvOpIAdd:
+    case SpvOpISub: {
+      // In case of OpIAdd and OpISub if the operand is a vector, the component
+      // type must be unsigned. Otherwise (if the operand is an int), the
+      // operand must be unsigned.
+      bool operand_is_signed =
+          type->AsVector()
+              ? type->AsVector()->element_type()->AsInteger()->IsSigned()
+              : type->AsInteger()->IsSigned();
+      if (operand_is_signed) {
+        return false;
+      }
+    } break;
+    default:
+      break;
+  }
+  return true;
+}
+
+protobufs::Transformation
+TransformationReplaceAddSubMulWithCarryingExtended::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_replace_add_sub_mul_with_carrying_extended() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 69 - 0
3rdparty/spirv-tools/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h

@@ -0,0 +1,69 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SPIRV_TOOLS_TRANSFORMATION_REPLACE_ADD_SUB_MUL_WITH_CARRYING_EXTENDED_H
+#define SPIRV_TOOLS_TRANSFORMATION_REPLACE_ADD_SUB_MUL_WITH_CARRYING_EXTENDED_H
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationReplaceAddSubMulWithCarryingExtended
+    : public Transformation {
+ public:
+  explicit TransformationReplaceAddSubMulWithCarryingExtended(
+      const protobufs::TransformationReplaceAddSubMulWithCarryingExtended&
+          message);
+
+  explicit TransformationReplaceAddSubMulWithCarryingExtended(
+      uint32_t struct_fresh_id, uint32_t result_id);
+
+  // - |message_.struct_fresh_id| must be fresh.
+  // - |message_.result_id| must refer to an OpIAdd or OpISub or OpIMul
+  //   instruction. In this instruction the result type id and the type ids of
+  //   the operands must be the same.
+  // - The type of struct holding the intermediate result must exists in the
+  //   module.
+  // - For OpIAdd, OpISub both operands must be unsigned.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // A transformation that replaces instructions OpIAdd, OpISub, OpIMul with
+  // pairs of instructions. The first one (OpIAddCarry, OpISubBorrow,
+  // OpUMulExtended, OpSMulExtended) computes the result into a struct. The
+  // second one extracts the appropriate component from the struct to yield the
+  // original result.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+  // Checks if an OpIAdd, OpISub or OpIMul instruction can be used by the
+  // transformation.
+  bool static IsInstructionSuitable(opt::IRContext* ir_context,
+                                    const opt::Instruction& instruction);
+
+ private:
+  protobufs::TransformationReplaceAddSubMulWithCarryingExtended message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SPIRV_TOOLS_TRANSFORMATION_REPLACE_ADD_SUB_MUL_WITH_CARRYING_EXTENDED_H

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

@@ -90,6 +90,40 @@ TransformationReplaceConstantWithUniform::MakeLoadInstruction(
                                       operands_for_load);
 }
 
+opt::Instruction*
+TransformationReplaceConstantWithUniform::GetInsertBeforeInstruction(
+    opt::IRContext* ir_context) const {
+  auto* result =
+      FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
+  if (!result) {
+    return nullptr;
+  }
+
+  // The use might be in an OpPhi instruction.
+  if (result->opcode() == SpvOpPhi) {
+    // OpPhi instructions must be the first instructions in a block. Thus, we
+    // can't insert above the OpPhi instruction. Given the predecessor block
+    // that corresponds to the id use, get the last instruction in that block
+    // above which we can insert OpAccessChain and OpLoad.
+    return fuzzerutil::GetLastInsertBeforeInstruction(
+        ir_context,
+        result->GetSingleWordInOperand(
+            message_.id_use_descriptor().in_operand_index() + 1),
+        SpvOpLoad);
+  }
+
+  // The only operand that we could've replaced in the OpBranchConditional is
+  // the condition id. But that operand has a boolean type and uniform variables
+  // can't store booleans (see the spec on OpTypeBool). Thus, |result| can't be
+  // an OpBranchConditional.
+  assert(result->opcode() != SpvOpBranchConditional &&
+         "OpBranchConditional has no operands to replace");
+
+  assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, result) &&
+         "We should be able to insert OpLoad and OpAccessChain at this point");
+  return result;
+}
+
 bool TransformationReplaceConstantWithUniform::IsApplicable(
     opt::IRContext* ir_context,
     const TransformationContext& transformation_context) const {
@@ -188,6 +222,12 @@ bool TransformationReplaceConstantWithUniform::IsApplicable(
     }
   }
 
+  // Once all checks are completed, we should be able to safely insert
+  // OpAccessChain and OpLoad into the module.
+  assert(GetInsertBeforeInstruction(ir_context) &&
+         "There must exist an instruction that we can use to insert "
+         "OpAccessChain and OpLoad above");
+
   return true;
 }
 
@@ -195,7 +235,7 @@ void TransformationReplaceConstantWithUniform::Apply(
     spvtools::opt::IRContext* ir_context,
     TransformationContext* /*unused*/) const {
   // Get the instruction that contains the id use we wish to replace.
-  auto instruction_containing_constant_use =
+  auto* instruction_containing_constant_use =
       FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
   assert(instruction_containing_constant_use &&
          "Precondition requires that the id use can be found.");
@@ -210,12 +250,17 @@ void TransformationReplaceConstantWithUniform::Apply(
           ->GetDef(message_.id_use_descriptor().id_of_interest())
           ->type_id();
 
+  // Get an instruction that will be used to insert OpAccessChain and OpLoad.
+  auto* insert_before_inst = GetInsertBeforeInstruction(ir_context);
+  assert(insert_before_inst &&
+         "There must exist an insertion point for OpAccessChain and OpLoad");
+
   // Add an access chain instruction to target the uniform element.
-  instruction_containing_constant_use->InsertBefore(
+  insert_before_inst->InsertBefore(
       MakeAccessChainInstruction(ir_context, constant_type_id));
 
   // Add a load from this access chain.
-  instruction_containing_constant_use->InsertBefore(
+  insert_before_inst->InsertBefore(
       MakeLoadInstruction(ir_context, constant_type_id));
 
   // Adjust the instruction containing the usage of the constant so that this

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

@@ -84,6 +84,11 @@ class TransformationReplaceConstantWithUniform : public Transformation {
   std::unique_ptr<opt::Instruction> MakeLoadInstruction(
       spvtools::opt::IRContext* ir_context, uint32_t constant_type_id) const;
 
+  // OpAccessChain and OpLoad will be inserted above the instruction returned
+  // by this function. Returns nullptr if no such instruction is present.
+  opt::Instruction* GetInsertBeforeInstruction(
+      opt::IRContext* ir_context) const;
+
   protobufs::TransformationReplaceConstantWithUniform message_;
 };
 

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

@@ -59,14 +59,6 @@ bool TransformationReplaceCopyObjectWithStoreLoad::IsApplicable(
     return false;
   }
 
-  // It must be valid to insert the OpStore and OpLoad instruction before it.
-  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore,
-                                                    copy_object_instruction) ||
-      !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad,
-                                                    copy_object_instruction)) {
-    return false;
-  }
-
   // A pointer type instruction pointing to the value type must be defined.
   auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
       ir_context, copy_object_instruction->type_id(),

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

@@ -109,7 +109,7 @@ protobufs::Transformation TransformationReplaceIdWithSynonym::ToMessage()
 bool TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
     opt::IRContext* ir_context, opt::Instruction* use_instruction,
     uint32_t use_in_operand_index) {
-  if (use_instruction->opcode() == SpvOpAccessChain &&
+  if (spvOpcodeIsAccessChain(use_instruction->opcode()) &&
       use_in_operand_index > 0) {
     // This is an access chain index.  If the (sub-)object being accessed by the
     // given index has struct type then we cannot replace the use with a

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

@@ -45,26 +45,18 @@ bool TransformationReplaceLoadStoreWithCopyMemory::IsApplicable(
     opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
   // This transformation is only applicable to the pair of OpLoad and OpStore
   // instructions.
-  if (message_.load_instruction_descriptor().target_instruction_opcode() !=
-      SpvOpLoad) {
-    return false;
-  }
-  if (message_.store_instruction_descriptor().target_instruction_opcode() !=
-      SpvOpStore) {
-    return false;
-  }
 
   // The OpLoad instruction must be defined.
   auto load_instruction =
       FindInstruction(message_.load_instruction_descriptor(), ir_context);
-  if (!load_instruction) {
+  if (!load_instruction || load_instruction->opcode() != SpvOpLoad) {
     return false;
   }
 
   // The OpStore instruction must be defined.
   auto store_instruction =
       FindInstruction(message_.store_instruction_descriptor(), ir_context);
-  if (!store_instruction) {
+  if (!store_instruction || store_instruction->opcode() != SpvOpStore) {
     return false;
   }
 

+ 38 - 23
3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp

@@ -101,8 +101,9 @@ bool TransformationReplaceParamsWithStruct::IsApplicable(
     return false;
   }
 
-  auto caller_id_to_fresh_composite_id = fuzzerutil::RepeatedUInt32PairToMap(
-      message_.caller_id_to_fresh_composite_id());
+  const auto caller_id_to_fresh_composite_id =
+      fuzzerutil::RepeatedUInt32PairToMap(
+          message_.caller_id_to_fresh_composite_id());
 
   // Check that |callee_id_to_fresh_composite_id| is valid.
   for (const auto* inst :
@@ -151,24 +152,12 @@ void TransformationReplaceParamsWithStruct::Apply(
   // Compute indices of replaced parameters. This will be used to adjust
   // OpFunctionCall instructions and create OpCompositeConstruct instructions at
   // every call site.
-  std::vector<uint32_t> indices_of_replaced_params;
-  {
-    // We want to destroy |params| after the loop because it will contain
-    // dangling pointers when we remove parameters from the function.
-    auto params = fuzzerutil::GetParameters(ir_context, function->result_id());
-    for (auto id : message_.parameter_id()) {
-      auto it = std::find_if(params.begin(), params.end(),
-                             [id](const opt::Instruction* param) {
-                               return param->result_id() == id;
-                             });
-      assert(it != params.end() && "Parameter's id is invalid");
-      indices_of_replaced_params.push_back(
-          static_cast<uint32_t>(it - params.begin()));
-    }
-  }
+  const auto indices_of_replaced_params =
+      ComputeIndicesOfReplacedParameters(ir_context);
 
-  auto caller_id_to_fresh_composite_id = fuzzerutil::RepeatedUInt32PairToMap(
-      message_.caller_id_to_fresh_composite_id());
+  const auto caller_id_to_fresh_composite_id =
+      fuzzerutil::RepeatedUInt32PairToMap(
+          message_.caller_id_to_fresh_composite_id());
 
   // Update all function calls.
   for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) {
@@ -182,12 +171,13 @@ void TransformationReplaceParamsWithStruct::Apply(
     }
 
     // Remove arguments from the function call. We do it in a separate loop
-    // and in reverse order to make sure we have removed correct operands.
-    for (auto it = indices_of_replaced_params.rbegin();
-         it != indices_of_replaced_params.rend(); ++it) {
+    // and in decreasing order to make sure we have removed correct operands.
+    for (auto index : std::set<uint32_t, std::greater<uint32_t>>(
+             indices_of_replaced_params.begin(),
+             indices_of_replaced_params.end())) {
       // +1 since the first in operand to OpFunctionCall is the result id of
       // the function.
-      inst->RemoveInOperand(*it + 1);
+      inst->RemoveInOperand(index + 1);
     }
 
     // Insert OpCompositeConstruct before the function call.
@@ -305,5 +295,30 @@ uint32_t TransformationReplaceParamsWithStruct::MaybeGetRequiredStructType(
   return fuzzerutil::MaybeGetStructType(ir_context, component_type_ids);
 }
 
+std::vector<uint32_t>
+TransformationReplaceParamsWithStruct::ComputeIndicesOfReplacedParameters(
+    opt::IRContext* ir_context) const {
+  assert(!message_.parameter_id().empty() &&
+         "There must be at least one parameter to replace");
+
+  const auto* function = fuzzerutil::GetFunctionFromParameterId(
+      ir_context, message_.parameter_id(0));
+  assert(function && "|parameter_id|s are invalid");
+
+  std::vector<uint32_t> result;
+
+  auto params = fuzzerutil::GetParameters(ir_context, function->result_id());
+  for (auto id : message_.parameter_id()) {
+    auto it = std::find_if(params.begin(), params.end(),
+                           [id](const opt::Instruction* param) {
+                             return param->result_id() == id;
+                           });
+    assert(it != params.end() && "Parameter's id is invalid");
+    result.push_back(static_cast<uint32_t>(it - params.begin()));
+  }
+
+  return result;
+}
+
 }  // namespace fuzz
 }  // namespace spvtools

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

@@ -73,6 +73,12 @@ class TransformationReplaceParamsWithStruct : public Transformation {
   // transformation (see docs on the IsApplicable method to learn more).
   uint32_t MaybeGetRequiredStructType(opt::IRContext* ir_context) const;
 
+  // Returns a vector of indices of parameters to replace. Concretely, i'th
+  // element is the index of the parameter with result id |parameter_id[i]| in
+  // its function.
+  std::vector<uint32_t> ComputeIndicesOfReplacedParameters(
+      opt::IRContext* ir_context) const;
+
   protobufs::TransformationReplaceParamsWithStruct message_;
 };
 

+ 12 - 0
3rdparty/spirv-tools/source/opcode.cpp

@@ -719,3 +719,15 @@ std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode) {
       return {};
   }
 }
+
+bool spvOpcodeIsAccessChain(SpvOp opcode) {
+  switch (opcode) {
+    case SpvOpAccessChain:
+    case SpvOpInBoundsAccessChain:
+    case SpvOpPtrAccessChain:
+    case SpvOpInBoundsPtrAccessChain:
+      return true;
+    default:
+      return false;
+  }
+}

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

@@ -144,4 +144,7 @@ bool spvOpcodeIsImageSample(SpvOp opcode);
 // operands for |opcode|.
 std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode);
 
+// Returns true for opcodes that represents access chain instructions.
+bool spvOpcodeIsAccessChain(SpvOp opcode);
+
 #endif  // SOURCE_OPCODE_H_

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

@@ -226,7 +226,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
   wrap_opkill.cpp
 )
 
-if(MSVC)
+if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")))
   # Enable parallel builds across four cores for this lib
   add_definitions(/MP4)
 endif()

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

@@ -22,6 +22,7 @@
 
 #include "source/cfa.h"
 #include "source/latest_version_glsl_std_450_header.h"
+#include "source/opt/eliminate_dead_functions_util.h"
 #include "source/opt/iterator.h"
 #include "source/opt/reflect.h"
 #include "source/spirv_constant.h"
@@ -727,8 +728,8 @@ bool AggressiveDCEPass::EliminateDeadFunctions() {
        funcIter != get_module()->end();) {
     if (live_function_set.count(&*funcIter) == 0) {
       modified = true;
-      EliminateFunction(&*funcIter);
-      funcIter = funcIter.Erase();
+      funcIter =
+          eliminatedeadfunctionsutil::EliminateFunction(context(), &funcIter);
     } else {
       ++funcIter;
     }
@@ -737,12 +738,6 @@ bool AggressiveDCEPass::EliminateDeadFunctions() {
   return modified;
 }
 
-void AggressiveDCEPass::EliminateFunction(Function* func) {
-  // Remove all of the instruction in the function body
-  func->ForEachInst([this](Instruction* inst) { context()->KillInst(inst); },
-                    true);
-}
-
 bool AggressiveDCEPass::ProcessGlobalValues() {
   // Remove debug and annotation statements referencing dead instructions.
   // This must be done before killing the instructions, otherwise there are

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

@@ -127,9 +127,6 @@ class AggressiveDCEPass : public MemPass {
   // Erases functions that are unreachable from the entry points of the module.
   bool EliminateDeadFunctions();
 
-  // Removes |func| from the module and deletes all its instructions.
-  void EliminateFunction(Function* func);
-
   // For function |func|, mark all Stores to non-function-scope variables
   // and block terminating instructions as live. Recursively mark the values
   // they use. When complete, mark any non-live instructions to be deleted.

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

@@ -196,6 +196,7 @@ bool DeadInsertElimPass::EliminateDeadInsertsOnePass(Function* func) {
       }
       const uint32_t id = ii->result_id();
       get_def_use_mgr()->ForEachUser(id, [&ii, this](Instruction* user) {
+        if (user->IsOpenCL100DebugInstr()) return;
         switch (user->opcode()) {
           case SpvOpCompositeInsert:
           case SpvOpPhi:

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

@@ -21,9 +21,35 @@ namespace eliminatedeadfunctionsutil {
 
 Module::iterator EliminateFunction(IRContext* context,
                                    Module::iterator* func_iter) {
+  bool first_func = *func_iter == context->module()->begin();
+  bool seen_func_end = false;
   (*func_iter)
-      ->ForEachInst([context](Instruction* inst) { context->KillInst(inst); },
-                    true);
+      ->ForEachInst(
+          [context, first_func, func_iter, &seen_func_end](Instruction* inst) {
+            if (inst->opcode() == SpvOpFunctionEnd) {
+              seen_func_end = true;
+            }
+            // Move non-semantic instructions to the previous function or
+            // global values if this is the first function.
+            if (seen_func_end && inst->opcode() == SpvOpExtInst) {
+              assert(inst->IsNonSemanticInstruction());
+              std::unique_ptr<Instruction> clone(inst->Clone(context));
+              context->ForgetUses(inst);
+              context->AnalyzeDefUse(clone.get());
+              if (first_func) {
+                context->AddGlobalValue(std::move(clone));
+              } else {
+                auto prev_func_iter = *func_iter;
+                --prev_func_iter;
+                prev_func_iter->AddNonSemanticInstruction(std::move(clone));
+              }
+              inst->ToNop();
+            } else {
+              context->KillNonSemanticInfo(inst);
+              context->KillInst(inst);
+            }
+          },
+          true, true);
   return func_iter->Erase();
 }
 

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

@@ -1467,7 +1467,7 @@ FoldingRule CompositeConstructFeedingExtract() {
             type_mgr->GetType(element_def->type_id())->AsVector();
         if (element_type) {
           uint32_t vector_size = element_type->element_count();
-          if (vector_size < element_index) {
+          if (vector_size <= element_index) {
             // The element we want comes after this vector.
             element_index -= vector_size;
           } else {

+ 44 - 10
3rdparty/spirv-tools/source/opt/function.cpp

@@ -47,31 +47,40 @@ Function* Function::Clone(IRContext* ctx) const {
   }
 
   clone->SetFunctionEnd(std::unique_ptr<Instruction>(EndInst()->Clone(ctx)));
+
+  clone->non_semantic_.reserve(non_semantic_.size());
+  for (auto& non_semantic : non_semantic_) {
+    clone->AddNonSemanticInstruction(
+        std::unique_ptr<Instruction>(non_semantic->Clone(ctx)));
+  }
   return clone;
 }
 
 void Function::ForEachInst(const std::function<void(Instruction*)>& f,
-                           bool run_on_debug_line_insts) {
+                           bool run_on_debug_line_insts,
+                           bool run_on_non_semantic_insts) {
   WhileEachInst(
       [&f](Instruction* inst) {
         f(inst);
         return true;
       },
-      run_on_debug_line_insts);
+      run_on_debug_line_insts, run_on_non_semantic_insts);
 }
 
 void Function::ForEachInst(const std::function<void(const Instruction*)>& f,
-                           bool run_on_debug_line_insts) const {
+                           bool run_on_debug_line_insts,
+                           bool run_on_non_semantic_insts) const {
   WhileEachInst(
       [&f](const Instruction* inst) {
         f(inst);
         return true;
       },
-      run_on_debug_line_insts);
+      run_on_debug_line_insts, run_on_non_semantic_insts);
 }
 
 bool Function::WhileEachInst(const std::function<bool(Instruction*)>& f,
-                             bool run_on_debug_line_insts) {
+                             bool run_on_debug_line_insts,
+                             bool run_on_non_semantic_insts) {
   if (def_inst_) {
     if (!def_inst_->WhileEachInst(f, run_on_debug_line_insts)) {
       return false;
@@ -99,13 +108,26 @@ bool Function::WhileEachInst(const std::function<bool(Instruction*)>& f,
     }
   }
 
-  if (end_inst_) return end_inst_->WhileEachInst(f, run_on_debug_line_insts);
+  if (end_inst_) {
+    if (!end_inst_->WhileEachInst(f, run_on_debug_line_insts)) {
+      return false;
+    }
+  }
+
+  if (run_on_non_semantic_insts) {
+    for (auto& non_semantic : non_semantic_) {
+      if (!non_semantic->WhileEachInst(f, run_on_debug_line_insts)) {
+        return false;
+      }
+    }
+  }
 
   return true;
 }
 
 bool Function::WhileEachInst(const std::function<bool(const Instruction*)>& f,
-                             bool run_on_debug_line_insts) const {
+                             bool run_on_debug_line_insts,
+                             bool run_on_non_semantic_insts) const {
   if (def_inst_) {
     if (!static_cast<const Instruction*>(def_inst_.get())
              ->WhileEachInst(f, run_on_debug_line_insts)) {
@@ -133,9 +155,21 @@ bool Function::WhileEachInst(const std::function<bool(const Instruction*)>& f,
     }
   }
 
-  if (end_inst_)
-    return static_cast<const Instruction*>(end_inst_.get())
-        ->WhileEachInst(f, run_on_debug_line_insts);
+  if (end_inst_) {
+    if (!static_cast<const Instruction*>(end_inst_.get())
+             ->WhileEachInst(f, run_on_debug_line_insts)) {
+      return false;
+    }
+  }
+
+  if (run_on_non_semantic_insts) {
+    for (auto& non_semantic : non_semantic_) {
+      if (!static_cast<const Instruction*>(non_semantic.get())
+               ->WhileEachInst(f, run_on_debug_line_insts)) {
+        return false;
+      }
+    }
+  }
 
   return true;
 }

+ 25 - 8
3rdparty/spirv-tools/source/opt/function.h

@@ -79,6 +79,11 @@ class Function {
   // Saves the given function end instruction.
   inline void SetFunctionEnd(std::unique_ptr<Instruction> end_inst);
 
+  // Add a non-semantic instruction that succeeds this function in the module.
+  // These instructions are maintained in the order they are added.
+  inline void AddNonSemanticInstruction(
+      std::unique_ptr<Instruction> non_semantic);
+
   // Returns the given function end instruction.
   inline Instruction* EndInst() { return end_inst_.get(); }
   inline const Instruction* EndInst() const { return end_inst_.get(); }
@@ -115,19 +120,24 @@ class Function {
   }
 
   // Runs the given function |f| on instructions in this function, in order,
-  // and optionally on debug line instructions that might precede them.
+  // and optionally on debug line instructions that might precede them and
+  // non-semantic instructions that succceed the function.
   void ForEachInst(const std::function<void(Instruction*)>& f,
-                   bool run_on_debug_line_insts = false);
+                   bool run_on_debug_line_insts = false,
+                   bool run_on_non_semantic_insts = false);
   void ForEachInst(const std::function<void(const Instruction*)>& f,
-                   bool run_on_debug_line_insts = false) const;
+                   bool run_on_debug_line_insts = false,
+                   bool run_on_non_semantic_insts = false) const;
   // Runs the given function |f| on instructions in this function, in order,
-  // and optionally on debug line instructions that might precede them.
-  // If |f| returns false, iteration is terminated and this function returns
-  // false.
+  // and optionally on debug line instructions that might precede them and
+  // non-semantic instructions that succeed the function.  If |f| returns
+  // false, iteration is terminated and this function returns false.
   bool WhileEachInst(const std::function<bool(Instruction*)>& f,
-                     bool run_on_debug_line_insts = false);
+                     bool run_on_debug_line_insts = false,
+                     bool run_on_non_semantic_insts = false);
   bool WhileEachInst(const std::function<bool(const Instruction*)>& f,
-                     bool run_on_debug_line_insts = false) const;
+                     bool run_on_debug_line_insts = false,
+                     bool run_on_non_semantic_insts = false) const;
 
   // Runs the given function |f| on each parameter instruction in this function,
   // in order, and optionally on debug line instructions that might precede
@@ -172,6 +182,8 @@ class Function {
   std::vector<std::unique_ptr<BasicBlock>> blocks_;
   // The OpFunctionEnd instruction.
   std::unique_ptr<Instruction> end_inst_;
+  // Non-semantic instructions succeeded by this function.
+  std::vector<std::unique_ptr<Instruction>> non_semantic_;
 };
 
 // Pretty-prints |func| to |str|. Returns |str|.
@@ -235,6 +247,11 @@ inline void Function::SetFunctionEnd(std::unique_ptr<Instruction> end_inst) {
   end_inst_ = std::move(end_inst);
 }
 
+inline void Function::AddNonSemanticInstruction(
+    std::unique_ptr<Instruction> non_semantic) {
+  non_semantic_.emplace_back(std::move(non_semantic));
+}
+
 }  // namespace opt
 }  // namespace spvtools
 

+ 25 - 12
3rdparty/spirv-tools/source/opt/instruction.cpp

@@ -183,8 +183,9 @@ void Instruction::ToBinaryWithoutAttachedDebugInsts(
     std::vector<uint32_t>* binary) const {
   const uint32_t num_words = 1 + NumOperandWords();
   binary->push_back((num_words << 16) | static_cast<uint16_t>(opcode_));
-  for (const auto& operand : operands_)
+  for (const auto& operand : operands_) {
     binary->insert(binary->end(), operand.words.begin(), operand.words.end());
+  }
 }
 
 void Instruction::ReplaceOperands(const OperandList& new_operands) {
@@ -283,8 +284,7 @@ bool Instruction::IsVulkanStorageImage() const {
 
   // Check if the image is sampled.  If we do not know for sure that it is,
   // then assume it is a storage image.
-  auto s = base_type->GetSingleWordInOperand(kTypeImageSampledIndex);
-  return s != 1;
+  return base_type->GetSingleWordInOperand(kTypeImageSampledIndex) != 1;
 }
 
 bool Instruction::IsVulkanSampledImage() const {
@@ -318,8 +318,7 @@ bool Instruction::IsVulkanSampledImage() const {
 
   // Check if the image is sampled.  If we know for sure that it is,
   // then return true.
-  auto s = base_type->GetSingleWordInOperand(kTypeImageSampledIndex);
-  return s == 1;
+  return base_type->GetSingleWordInOperand(kTypeImageSampledIndex) == 1;
 }
 
 bool Instruction::IsVulkanStorageTexelBuffer() const {
@@ -502,16 +501,16 @@ uint32_t Instruction::GetTypeComponent(uint32_t element) const {
   return subtype;
 }
 
-Instruction* Instruction::InsertBefore(std::unique_ptr<Instruction>&& i) {
-  i.get()->InsertBefore(this);
-  return i.release();
+Instruction* Instruction::InsertBefore(std::unique_ptr<Instruction>&& inst) {
+  inst.get()->InsertBefore(this);
+  return inst.release();
 }
 
 Instruction* Instruction::InsertBefore(
     std::vector<std::unique_ptr<Instruction>>&& list) {
   Instruction* first_node = list.front().get();
-  for (auto& i : list) {
-    i.release()->InsertBefore(this);
+  for (auto& inst : list) {
+    inst.release()->InsertBefore(this);
   }
   list.clear();
   return first_node;
@@ -568,10 +567,13 @@ bool Instruction::IsValidBasePointer() const {
 }
 
 OpenCLDebugInfo100Instructions Instruction::GetOpenCL100DebugOpcode() const {
-  if (opcode() != SpvOpExtInst) return OpenCLDebugInfo100InstructionsMax;
+  if (opcode() != SpvOpExtInst) {
+    return OpenCLDebugInfo100InstructionsMax;
+  }
 
-  if (!context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo())
+  if (!context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()) {
     return OpenCLDebugInfo100InstructionsMax;
+  }
 
   if (GetSingleWordInOperand(kExtInstSetIdInIdx) !=
       context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()) {
@@ -622,6 +624,7 @@ bool Instruction::IsFoldableByFoldScalar() const {
   if (!folder.IsFoldableOpcode(opcode())) {
     return false;
   }
+
   Instruction* type = context()->get_def_use_mgr()->GetDef(type_id());
   if (!folder.IsFoldableType(type)) {
     return false;
@@ -889,6 +892,16 @@ bool Instruction::IsOpcodeSafeToDelete() const {
   }
 }
 
+bool Instruction::IsNonSemanticInstruction() const {
+  if (!HasResultId()) return false;
+  if (opcode() != SpvOpExtInst) return false;
+
+  auto import_inst =
+      context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(0));
+  std::string import_name = import_inst->GetInOperand(0).AsString();
+  return import_name.find("NonSemantic.") == 0;
+}
+
 void DebugScope::ToBinary(uint32_t type_id, uint32_t result_id,
                           uint32_t ext_set,
                           std::vector<uint32_t>* binary) const {

+ 23 - 20
3rdparty/spirv-tools/source/opt/instruction.h

@@ -549,6 +549,9 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
     return GetOpenCL100DebugOpcode() != OpenCLDebugInfo100InstructionsMax;
   }
 
+  // Returns true if this instructions a non-semantic instruction.
+  bool IsNonSemanticInstruction() const;
+
   // Dump this instruction on stderr.  Useful when running interactive
   // debuggers.
   void Dump() const;
@@ -749,21 +752,21 @@ inline void Instruction::ForEachInst(
 }
 
 inline void Instruction::ForEachId(const std::function<void(uint32_t*)>& f) {
-  for (auto& opnd : operands_)
-    if (spvIsIdType(opnd.type)) f(&opnd.words[0]);
+  for (auto& operand : operands_)
+    if (spvIsIdType(operand.type)) f(&operand.words[0]);
 }
 
 inline void Instruction::ForEachId(
     const std::function<void(const uint32_t*)>& f) const {
-  for (const auto& opnd : operands_)
-    if (spvIsIdType(opnd.type)) f(&opnd.words[0]);
+  for (const auto& operand : operands_)
+    if (spvIsIdType(operand.type)) f(&operand.words[0]);
 }
 
 inline bool Instruction::WhileEachInId(
     const std::function<bool(uint32_t*)>& f) {
-  for (auto& opnd : operands_) {
-    if (spvIsInIdType(opnd.type)) {
-      if (!f(&opnd.words[0])) return false;
+  for (auto& operand : operands_) {
+    if (spvIsInIdType(operand.type) && !f(&operand.words[0])) {
+      return false;
     }
   }
   return true;
@@ -771,9 +774,9 @@ inline bool Instruction::WhileEachInId(
 
 inline bool Instruction::WhileEachInId(
     const std::function<bool(const uint32_t*)>& f) const {
-  for (const auto& opnd : operands_) {
-    if (spvIsInIdType(opnd.type)) {
-      if (!f(&opnd.words[0])) return false;
+  for (const auto& operand : operands_) {
+    if (spvIsInIdType(operand.type) && !f(&operand.words[0])) {
+      return false;
     }
   }
   return true;
@@ -796,13 +799,13 @@ inline void Instruction::ForEachInId(
 
 inline bool Instruction::WhileEachInOperand(
     const std::function<bool(uint32_t*)>& f) {
-  for (auto& opnd : operands_) {
-    switch (opnd.type) {
+  for (auto& operand : operands_) {
+    switch (operand.type) {
       case SPV_OPERAND_TYPE_RESULT_ID:
       case SPV_OPERAND_TYPE_TYPE_ID:
         break;
       default:
-        if (!f(&opnd.words[0])) return false;
+        if (!f(&operand.words[0])) return false;
         break;
     }
   }
@@ -811,13 +814,13 @@ inline bool Instruction::WhileEachInOperand(
 
 inline bool Instruction::WhileEachInOperand(
     const std::function<bool(const uint32_t*)>& f) const {
-  for (const auto& opnd : operands_) {
-    switch (opnd.type) {
+  for (const auto& operand : operands_) {
+    switch (operand.type) {
       case SPV_OPERAND_TYPE_RESULT_ID:
       case SPV_OPERAND_TYPE_TYPE_ID:
         break;
       default:
-        if (!f(&opnd.words[0])) return false;
+        if (!f(&operand.words[0])) return false;
         break;
     }
   }
@@ -826,16 +829,16 @@ inline bool Instruction::WhileEachInOperand(
 
 inline void Instruction::ForEachInOperand(
     const std::function<void(uint32_t*)>& f) {
-  WhileEachInOperand([&f](uint32_t* op) {
-    f(op);
+  WhileEachInOperand([&f](uint32_t* operand) {
+    f(operand);
     return true;
   });
 }
 
 inline void Instruction::ForEachInOperand(
     const std::function<void(const uint32_t*)>& f) const {
-  WhileEachInOperand([&f](const uint32_t* op) {
-    f(op);
+  WhileEachInOperand([&f](const uint32_t* operand) {
+    f(operand);
     return true;
   });
 }

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

@@ -213,6 +213,30 @@ Instruction* IRContext::KillInst(Instruction* inst) {
   return next_instruction;
 }
 
+void IRContext::KillNonSemanticInfo(Instruction* inst) {
+  if (!inst->HasResultId()) return;
+  std::vector<Instruction*> work_list;
+  std::vector<Instruction*> to_kill;
+  std::unordered_set<Instruction*> seen;
+  work_list.push_back(inst);
+
+  while (!work_list.empty()) {
+    auto* i = work_list.back();
+    work_list.pop_back();
+    get_def_use_mgr()->ForEachUser(
+        i, [&work_list, &to_kill, &seen](Instruction* user) {
+          if (user->IsNonSemanticInstruction() && seen.insert(user).second) {
+            work_list.push_back(user);
+            to_kill.push_back(user);
+          }
+        });
+  }
+
+  for (auto* dead : to_kill) {
+    KillInst(dead);
+  }
+}
+
 bool IRContext::KillDef(uint32_t id) {
   Instruction* def = get_def_use_mgr()->GetDef(id);
   if (def != nullptr) {

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

@@ -403,6 +403,9 @@ class IRContext {
   // instruction exists.
   Instruction* KillInst(Instruction* inst);
 
+  // Removes the non-semantic instruction tree that uses |inst|'s result id.
+  void KillNonSemanticInfo(Instruction* inst);
+
   // Returns true if all of the given analyses are valid.
   bool AreAnalysesValid(Analysis set) { return (set & valid_analyses_) == set; }
 

+ 12 - 3
3rdparty/spirv-tools/source/opt/ir_loader.cpp

@@ -167,13 +167,22 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
       } else if (IsTypeInst(opcode)) {
         module_->AddType(std::move(spv_inst));
       } else if (IsConstantInst(opcode) || opcode == SpvOpVariable ||
-                 opcode == SpvOpUndef ||
-                 (opcode == SpvOpExtInst &&
-                  spvExtInstIsNonSemantic(inst->ext_inst_type))) {
+                 opcode == SpvOpUndef) {
         module_->AddGlobalValue(std::move(spv_inst));
       } else if (opcode == SpvOpExtInst &&
                  spvExtInstIsDebugInfo(inst->ext_inst_type)) {
         module_->AddExtInstDebugInfo(std::move(spv_inst));
+      } else if (opcode == SpvOpExtInst &&
+                 spvExtInstIsNonSemantic(inst->ext_inst_type)) {
+        // If there are no functions, add the non-semantic instructions to the
+        // global values. Otherwise append it to the list of the last function.
+        auto func_begin = module_->begin();
+        auto func_end = module_->end();
+        if (func_begin == func_end) {
+          module_->AddGlobalValue(std::move(spv_inst));
+        } else {
+          (--func_end)->AddNonSemanticInstruction(std::move(spv_inst));
+        }
       } else {
         Errorf(consumer_, src, loc,
                "Unhandled inst type (opcode: %d) found outside function "

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

@@ -77,6 +77,15 @@ void LocalAccessChainConvertPass::AppendConstantOperands(
 bool LocalAccessChainConvertPass::ReplaceAccessChainLoad(
     const Instruction* address_inst, Instruction* original_load) {
   // Build and append load of variable in ptrInst
+  if (address_inst->NumInOperands() == 1) {
+    // An access chain with no indices is essentially a copy.  All that is
+    // needed is to propagate the address.
+    context()->ReplaceAllUsesWith(
+        address_inst->result_id(),
+        address_inst->GetSingleWordInOperand(kAccessChainPtrIdInIdx));
+    return true;
+  }
+
   std::vector<std::unique_ptr<Instruction>> new_inst;
   uint32_t varId;
   uint32_t varPteTypeId;
@@ -109,6 +118,18 @@ bool LocalAccessChainConvertPass::ReplaceAccessChainLoad(
 bool LocalAccessChainConvertPass::GenAccessChainStoreReplacement(
     const Instruction* ptrInst, uint32_t valId,
     std::vector<std::unique_ptr<Instruction>>* newInsts) {
+  if (ptrInst->NumInOperands() == 1) {
+    // An access chain with no indices is essentially a copy.  However, we still
+    // have to create a new store because the old ones will be deleted.
+    BuildAndAppendInst(
+        SpvOpStore, 0, 0,
+        {{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+          {ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx)}},
+         {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {valId}}},
+        newInsts);
+    return true;
+  }
+
   // Build and append load of variable in ptrInst
   uint32_t varId;
   uint32_t varPteTypeId;
@@ -246,11 +267,13 @@ Pass::Status LocalAccessChainConvertPass::ConvertLocalAccessChains(
           if (!GenAccessChainStoreReplacement(ptrInst, valId, &newInsts)) {
             return Status::Failure;
           }
+          size_t num_of_instructions_to_skip = newInsts.size() - 1;
           dead_instructions.push_back(&*ii);
           ++ii;
           ii = ii.InsertBefore(std::move(newInsts));
-          ++ii;
-          ++ii;
+          for (size_t i = 0; i < num_of_instructions_to_skip; ++i) {
+            ++ii;
+          }
           modified = true;
         } break;
         default:

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

@@ -191,14 +191,13 @@ bool Loop::GetInductionInitValue(const Instruction* induction,
   if (!constant) return false;
 
   if (value) {
-    const analysis::Integer* type =
-        constant->AsIntConstant()->type()->AsInteger();
-
-    if (type->IsSigned()) {
-      *value = constant->AsIntConstant()->GetS32BitValue();
-    } else {
-      *value = constant->AsIntConstant()->GetU32BitValue();
+    const analysis::Integer* type = constant->type()->AsInteger();
+    if (!type) {
+      return false;
     }
+
+    *value = type->IsSigned() ? constant->GetSignExtendedValue()
+                              : constant->GetZeroExtendedValue();
   }
 
   return true;
@@ -682,22 +681,19 @@ bool Loop::FindNumberOfIterations(const Instruction* induction,
   if (!upper_bound) return false;
 
   // Must be integer because of the opcode on the condition.
-  int64_t condition_value = 0;
+  const analysis::Integer* type = upper_bound->type()->AsInteger();
 
-  const analysis::Integer* type =
-      upper_bound->AsIntConstant()->type()->AsInteger();
-
-  if (type->width() > 32) {
+  if (!type || type->width() > 64) {
     return false;
   }
 
-  if (type->IsSigned()) {
-    condition_value = upper_bound->AsIntConstant()->GetS32BitValue();
-  } else {
-    condition_value = upper_bound->AsIntConstant()->GetU32BitValue();
-  }
+  int64_t condition_value = type->IsSigned()
+                                ? upper_bound->GetSignExtendedValue()
+                                : upper_bound->GetZeroExtendedValue();
 
   // Find the instruction which is stepping through the loop.
+  //
+  // GetInductionStepOperation returns nullptr if |step_inst| is OpConstantNull.
   Instruction* step_inst = GetInductionStepOperation(induction);
   if (!step_inst) return false;
 

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

@@ -98,7 +98,10 @@ void Module::ForEachInst(const std::function<void(Instruction*)>& f,
   DELEGATE(ext_inst_debuginfo_);
   DELEGATE(annotations_);
   DELEGATE(types_values_);
-  for (auto& i : functions_) i->ForEachInst(f, run_on_debug_line_insts);
+  for (auto& i : functions_) {
+    i->ForEachInst(f, run_on_debug_line_insts,
+                   /* run_on_non_semantic_insts = */ true);
+  }
 #undef DELEGATE
 }
 
@@ -120,8 +123,9 @@ void Module::ForEachInst(const std::function<void(const Instruction*)>& f,
   for (auto& i : types_values_) DELEGATE(i);
   for (auto& i : ext_inst_debuginfo_) DELEGATE(i);
   for (auto& i : functions_) {
-    static_cast<const Function*>(i.get())->ForEachInst(f,
-                                                       run_on_debug_line_insts);
+    static_cast<const Function*>(i.get())->ForEachInst(
+        f, run_on_debug_line_insts,
+        /* run_on_non_semantic_insts = */ true);
   }
   if (run_on_debug_line_insts) {
     for (auto& i : trailing_dbg_line_info_) DELEGATE(i);

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

@@ -70,7 +70,7 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
         simple_conditional_branch_to_branch_reduction_opportunity.cpp
 )
 
-if(MSVC)
+if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")))
   # Enable parallel builds across four cores for this lib
   add_definitions(/MP4)
 endif()

+ 212 - 34
3rdparty/spirv-tools/source/val/validate_extensions.cpp

@@ -81,6 +81,7 @@ bool DoesDebugInfoOperandMatchExpectation(
     const ValidationState_t& _,
     const std::function<bool(OpenCLDebugInfo100Instructions)>& expectation,
     const Instruction* inst, uint32_t word_index) {
+  if (inst->words().size() <= word_index) return false;
   auto* debug_inst = _.FindDef(inst->word(word_index));
   if (debug_inst->opcode() != SpvOpExtInst ||
       debug_inst->ext_inst_type() != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
@@ -167,9 +168,16 @@ spv_result_t ValidateOperandLexicalScope(
 spv_result_t ValidateOperandDebugType(
     ValidationState_t& _, const std::string& debug_inst_name,
     const Instruction* inst, uint32_t word_index,
-    const std::function<std::string()>& ext_inst_name) {
+    const std::function<std::string()>& ext_inst_name,
+    bool allow_template_param) {
   std::function<bool(OpenCLDebugInfo100Instructions)> expectation =
-      [](OpenCLDebugInfo100Instructions dbg_inst) {
+      [&allow_template_param](OpenCLDebugInfo100Instructions dbg_inst) {
+        if (allow_template_param &&
+            (dbg_inst == OpenCLDebugInfo100DebugTypeTemplateParameter ||
+             dbg_inst ==
+                 OpenCLDebugInfo100DebugTypeTemplateTemplateParameter)) {
+          return true;
+        }
         return OpenCLDebugInfo100DebugTypeBasic <= dbg_inst &&
                dbg_inst <= OpenCLDebugInfo100DebugTypePtrToMember;
       };
@@ -636,6 +644,45 @@ spv_result_t ValidateClspvReflectionInstruction(ValidationState_t& _,
   return SPV_SUCCESS;
 }
 
+bool IsConstIntScalarTypeWith32Or64Bits(ValidationState_t& _,
+                                        Instruction* instr) {
+  if (instr->opcode() != SpvOpConstant) return false;
+  if (!_.IsIntScalarType(instr->type_id())) return false;
+  uint32_t size_in_bits = _.GetBitWidth(instr->type_id());
+  return size_in_bits == 32 || size_in_bits == 64;
+}
+
+bool IsConstWithIntScalarType(ValidationState_t& _, const Instruction* inst,
+                              uint32_t word_index) {
+  auto* int_scalar_const = _.FindDef(inst->word(word_index));
+  if (int_scalar_const->opcode() == SpvOpConstant &&
+      _.IsIntScalarType(int_scalar_const->type_id())) {
+    return true;
+  }
+  return false;
+}
+
+bool IsDebugVariableWithIntScalarType(ValidationState_t& _,
+                                      const Instruction* inst,
+                                      uint32_t word_index) {
+  auto* dbg_int_scalar_var = _.FindDef(inst->word(word_index));
+  if (OpenCLDebugInfo100Instructions(dbg_int_scalar_var->word(4)) ==
+          OpenCLDebugInfo100DebugLocalVariable ||
+      OpenCLDebugInfo100Instructions(dbg_int_scalar_var->word(4)) ==
+          OpenCLDebugInfo100DebugGlobalVariable) {
+    auto* dbg_type = _.FindDef(dbg_int_scalar_var->word(6));
+    if (OpenCLDebugInfo100Instructions(dbg_type->word(4)) ==
+            OpenCLDebugInfo100DebugTypeBasic &&
+        (OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(dbg_type->word(7)) ==
+             OpenCLDebugInfo100Signed ||
+         OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(dbg_type->word(7)) ==
+             OpenCLDebugInfo100Unsigned)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 }  // anonymous namespace
 
 spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) {
@@ -2678,17 +2725,53 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
         break;
       }
       case OpenCLDebugInfo100DebugTypeArray: {
-        auto validate_base_type =
-            ValidateOperandDebugType(_, "Base Type", inst, 5, ext_inst_name);
+        auto validate_base_type = ValidateOperandDebugType(
+            _, "Base Type", inst, 5, ext_inst_name, false);
         if (validate_base_type != SPV_SUCCESS) return validate_base_type;
         for (uint32_t i = 6; i < num_words; ++i) {
-          CHECK_OPERAND("Component Count", SpvOpConstant, i);
+          bool invalid = false;
           auto* component_count = _.FindDef(inst->word(i));
-          if (!_.IsIntScalarType(component_count->type_id()) ||
-              !component_count->word(3)) {
+          if (IsConstIntScalarTypeWith32Or64Bits(_, component_count)) {
+            // TODO: We need a spec discussion for the bindless array.
+            if (!component_count->word(3)) {
+              invalid = true;
+            }
+          } else if (component_count->words().size() > 6 &&
+                     (OpenCLDebugInfo100Instructions(component_count->word(
+                          4)) == OpenCLDebugInfo100DebugLocalVariable ||
+                      OpenCLDebugInfo100Instructions(component_count->word(
+                          4)) == OpenCLDebugInfo100DebugGlobalVariable)) {
+            auto* component_count_type = _.FindDef(component_count->word(6));
+            if (component_count_type->words().size() > 7) {
+              if (OpenCLDebugInfo100Instructions(component_count_type->word(
+                      4)) != OpenCLDebugInfo100DebugTypeBasic ||
+                  OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(
+                      component_count_type->word(7)) !=
+                      OpenCLDebugInfo100Unsigned) {
+                invalid = true;
+              } else {
+                // DebugTypeBasic for DebugLocalVariable/DebugGlobalVariable
+                // must have Unsigned encoding and 32 or 64 as its size in bits.
+                Instruction* size_in_bits =
+                    _.FindDef(component_count_type->word(6));
+                if (!_.IsIntScalarType(size_in_bits->type_id()) ||
+                    (size_in_bits->word(3) != 32 &&
+                     size_in_bits->word(3) != 64)) {
+                  invalid = true;
+                }
+              }
+            } else {
+              invalid = true;
+            }
+          } else {
+            invalid = true;
+          }
+          if (invalid) {
             return _.diag(SPV_ERROR_INVALID_DATA, inst)
-                   << ext_inst_name() << ": Component Count must be positive "
-                   << "integer";
+                   << ext_inst_name() << ": Component Count must be "
+                   << "OpConstant with a 32- or 64-bits integer scalar type or "
+                   << "DebugGlobalVariable or DebugLocalVariable with a 32- or "
+                   << "64-bits unsigned integer scalar type";
           }
         }
         break;
@@ -2706,14 +2789,16 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
       }
       case OpenCLDebugInfo100DebugTypeFunction: {
         auto* return_type = _.FindDef(inst->word(6));
+        // TODO: We need a spec discussion that we have to allow return and
+        // parameter types of a DebugTypeFunction to have template parameter.
         if (return_type->opcode() != SpvOpTypeVoid) {
           auto validate_return = ValidateOperandDebugType(
-              _, "Return Type", inst, 6, ext_inst_name);
+              _, "Return Type", inst, 6, ext_inst_name, true);
           if (validate_return != SPV_SUCCESS) return validate_return;
         }
         for (uint32_t word_index = 7; word_index < num_words; ++word_index) {
           auto validate_param = ValidateOperandDebugType(
-              _, "Parameter Types", inst, word_index, ext_inst_name);
+              _, "Parameter Types", inst, word_index, ext_inst_name, true);
           if (validate_param != SPV_SUCCESS) return validate_param;
         }
         break;
@@ -2727,7 +2812,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
                 },
                 inst, 6)) {
           auto validate_underlying_type = ValidateOperandDebugType(
-              _, "Underlying Types", inst, 6, ext_inst_name);
+              _, "Underlying Types", inst, 6, ext_inst_name, false);
           if (validate_underlying_type != SPV_SUCCESS)
             return validate_underlying_type;
         }
@@ -2784,8 +2869,10 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
       }
       case OpenCLDebugInfo100DebugTypeMember: {
         CHECK_OPERAND("Name", SpvOpString, 5);
+        // TODO: We need a spec discussion that we have to allow member types
+        // to have template parameter.
         auto validate_type =
-            ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name);
+            ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true);
         if (validate_type != SPV_SUCCESS) return validate_type;
         CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
         CHECK_DEBUG_OPERAND("Parent", OpenCLDebugInfo100DebugTypeComposite, 10);
@@ -2823,18 +2910,13 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
       case OpenCLDebugInfo100DebugFunction: {
         CHECK_OPERAND("Name", SpvOpString, 5);
         auto validate_type =
-            ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name);
+            ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, false);
         if (validate_type != SPV_SUCCESS) return validate_type;
         CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
         auto validate_parent =
             ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
         if (validate_parent != SPV_SUCCESS) return validate_parent;
         CHECK_OPERAND("Linkage Name", SpvOpString, 11);
-        // TODO: The current OpenCL.100.DebugInfo spec says "Function
-        // is an OpFunction which is described by this instruction.".
-        // However, the function definition can be opted-out e.g.,
-        // inlining. We assume that Function operand can be a
-        // DebugInfoNone, but we must discuss it and update the spec.
         if (!DoesDebugInfoOperandMatchExpectation(
                 _,
                 [](OpenCLDebugInfo100Instructions dbg_inst) {
@@ -2852,7 +2934,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
       case OpenCLDebugInfo100DebugFunctionDeclaration: {
         CHECK_OPERAND("Name", SpvOpString, 5);
         auto validate_type =
-            ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name);
+            ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, false);
         if (validate_type != SPV_SUCCESS) return validate_type;
         CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
         auto validate_parent =
@@ -2870,9 +2952,6 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
         break;
       }
       case OpenCLDebugInfo100DebugScope: {
-        // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/533): We are
-        // still in spec discussion about what must be "Scope" operand of
-        // DebugScope. Update this code if the conclusion is different.
         auto validate_scope =
             ValidateOperandLexicalScope(_, "Scope", inst, 5, ext_inst_name);
         if (validate_scope != SPV_SUCCESS) return validate_scope;
@@ -2884,8 +2963,10 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
       }
       case OpenCLDebugInfo100DebugLocalVariable: {
         CHECK_OPERAND("Name", SpvOpString, 5);
+        // TODO: We need a spec discussion that we have to allow local variable
+        // types to have template parameter.
         auto validate_type =
-            ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name);
+            ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true);
         if (validate_type != SPV_SUCCESS) return validate_type;
         CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
         auto validate_parent =
@@ -2896,11 +2977,6 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
       case OpenCLDebugInfo100DebugDeclare: {
         CHECK_DEBUG_OPERAND("Local Variable",
                             OpenCLDebugInfo100DebugLocalVariable, 5);
-
-        // TODO: We must discuss DebugDeclare.Variable of
-        // OpenCL.100.DebugInfo. Currently, it says "Variable must be an id of
-        // OpVariable instruction which defines the local variable.", but we
-        // want to allow OpFunctionParameter as well.
         auto* operand = _.FindDef(inst->word(6));
         if (operand->opcode() != SpvOpVariable &&
             operand->opcode() != SpvOpFunctionParameter) {
@@ -2920,18 +2996,120 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
         }
         break;
       }
+      case OpenCLDebugInfo100DebugTypeTemplate: {
+        if (!DoesDebugInfoOperandMatchExpectation(
+                _,
+                [](OpenCLDebugInfo100Instructions dbg_inst) {
+                  return dbg_inst == OpenCLDebugInfo100DebugTypeComposite ||
+                         dbg_inst == OpenCLDebugInfo100DebugFunction;
+                },
+                inst, 5)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                 << ext_inst_name() << ": "
+                 << "expected operand Target must be DebugTypeComposite "
+                 << "or DebugFunction";
+        }
+        for (uint32_t word_index = 6; word_index < num_words; ++word_index) {
+          if (!DoesDebugInfoOperandMatchExpectation(
+                  _,
+                  [](OpenCLDebugInfo100Instructions dbg_inst) {
+                    return dbg_inst ==
+                               OpenCLDebugInfo100DebugTypeTemplateParameter ||
+                           dbg_inst ==
+                               OpenCLDebugInfo100DebugTypeTemplateTemplateParameter;
+                  },
+                  inst, word_index)) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << ext_inst_name() << ": "
+                   << "expected operand Parameters must be "
+                   << "DebugTypeTemplateParameter or "
+                   << "DebugTypeTemplateTemplateParameter";
+          }
+        }
+        break;
+      }
+      case OpenCLDebugInfo100DebugTypeTemplateParameter: {
+        CHECK_OPERAND("Name", SpvOpString, 5);
+        auto validate_actual_type = ValidateOperandDebugType(
+            _, "Actual Type", inst, 6, ext_inst_name, false);
+        if (validate_actual_type != SPV_SUCCESS) return validate_actual_type;
+        if (!DoesDebugInfoOperandMatchExpectation(
+                _,
+                [](OpenCLDebugInfo100Instructions dbg_inst) {
+                  return dbg_inst == OpenCLDebugInfo100DebugInfoNone;
+                },
+                inst, 7)) {
+          CHECK_OPERAND("Value", SpvOpConstant, 7);
+        }
+        CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 8);
+        break;
+      }
+      case OpenCLDebugInfo100DebugGlobalVariable: {
+        CHECK_OPERAND("Name", SpvOpString, 5);
+        auto validate_type =
+            ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, false);
+        if (validate_type != SPV_SUCCESS) return validate_type;
+        CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+        auto validate_scope =
+            ValidateOperandLexicalScope(_, "Scope", inst, 10, ext_inst_name);
+        if (validate_scope != SPV_SUCCESS) return validate_scope;
+        CHECK_OPERAND("Linkage Name", SpvOpString, 11);
+        if (!DoesDebugInfoOperandMatchExpectation(
+                _,
+                [](OpenCLDebugInfo100Instructions dbg_inst) {
+                  return dbg_inst == OpenCLDebugInfo100DebugInfoNone;
+                },
+                inst, 12)) {
+          auto* operand = _.FindDef(inst->word(12));
+          if (operand->opcode() != SpvOpVariable &&
+              operand->opcode() != SpvOpConstant) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << ext_inst_name() << ": "
+                   << "expected operand Variable must be a result id of "
+                      "OpVariable or OpConstant or DebugInfoNone";
+          }
+        }
+        if (num_words == 15) {
+          CHECK_DEBUG_OPERAND("Static Member Declaration",
+                              OpenCLDebugInfo100DebugTypeMember, 14);
+        }
+        break;
+      }
+      case OpenCLDebugInfo100DebugInlinedAt: {
+        auto validate_scope =
+            ValidateOperandLexicalScope(_, "Scope", inst, 6, ext_inst_name);
+        if (validate_scope != SPV_SUCCESS) return validate_scope;
+        if (num_words == 8) {
+          CHECK_DEBUG_OPERAND("Inlined", OpenCLDebugInfo100DebugInlinedAt, 7);
+        }
+        break;
+      }
+      case OpenCLDebugInfo100DebugValue: {
+        CHECK_DEBUG_OPERAND("Local Variable",
+                            OpenCLDebugInfo100DebugLocalVariable, 5);
+        CHECK_DEBUG_OPERAND("Expression", OpenCLDebugInfo100DebugExpression, 7);
+
+        for (uint32_t word_index = 8; word_index < num_words; ++word_index) {
+          // TODO: The following code simply checks if it is a const int scalar
+          // or a DebugLocalVariable or DebugGlobalVariable, but we have to
+          // check it using the same validation for Indexes of OpAccessChain.
+          if (!IsConstWithIntScalarType(_, inst, word_index) &&
+              !IsDebugVariableWithIntScalarType(_, inst, word_index)) {
+            return _.diag(SPV_ERROR_INVALID_DATA, inst)
+                   << ext_inst_name() << ": expected operand Indexes is "
+                   << "OpConstant, DebugGlobalVariable, or "
+                   << "type is OpConstant with an integer scalar type";
+          }
+        }
+        break;
+      }
 
       // TODO: Add validation rules for remaining cases as well.
       case OpenCLDebugInfo100DebugTypePtrToMember:
-      case OpenCLDebugInfo100DebugTypeTemplate:
-      case OpenCLDebugInfo100DebugTypeTemplateParameter:
       case OpenCLDebugInfo100DebugTypeTemplateTemplateParameter:
       case OpenCLDebugInfo100DebugTypeTemplateParameterPack:
-      case OpenCLDebugInfo100DebugGlobalVariable:
       case OpenCLDebugInfo100DebugLexicalBlockDiscriminator:
-      case OpenCLDebugInfo100DebugInlinedAt:
       case OpenCLDebugInfo100DebugInlinedVariable:
-      case OpenCLDebugInfo100DebugValue:
       case OpenCLDebugInfo100DebugMacroDef:
       case OpenCLDebugInfo100DebugMacroUndef:
       case OpenCLDebugInfo100DebugImportedEntity:

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

@@ -437,6 +437,19 @@ spv_result_t GetLocationsForVariable(
 
 spv_result_t ValidateLocations(ValidationState_t& _,
                                const Instruction* entry_point) {
+  // According to Vulkan 14.1 only the following execution models have
+  // locations assigned.
+  switch (entry_point->GetOperandAs<SpvExecutionModel>(0)) {
+    case SpvExecutionModelVertex:
+    case SpvExecutionModelTessellationControl:
+    case SpvExecutionModelTessellationEvaluation:
+    case SpvExecutionModelGeometry:
+    case SpvExecutionModelFragment:
+      break;
+    default:
+      return SPV_SUCCESS;
+  }
+
   // Locations are stored as a combined location and component values.
   std::unordered_set<uint32_t> input_locations;
   std::unordered_set<uint32_t> output_locations_index0;