Бранимир Караџић 5 лет назад
Родитель
Сommit
e92de52ab1
47 измененных файлов с 2503 добавлено и 127 удалено
  1. 1 1
      3rdparty/spirv-headers/include/spirv/spir-v.xml
  2. 1 1
      3rdparty/spirv-tools/include/generated/build-version.inc
  3. 12 0
      3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
  4. 12 0
      3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
  5. 16 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp
  6. 21 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_context.h
  7. 143 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp
  8. 51 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.h
  9. 1 1
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.h
  10. 64 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.cpp
  11. 40 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.h
  12. 25 1
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp
  13. 1 1
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.h
  14. 1 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp
  15. 59 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp
  16. 40 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h
  17. 104 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
  18. 40 0
      3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
  19. 58 3
      3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto
  20. 12 0
      3rdparty/spirv-tools/source/fuzz/transformation.cpp
  21. 5 13
      3rdparty/spirv-tools/source/fuzz/transformation_add_global_variable.cpp
  22. 4 10
      3rdparty/spirv-tools/source/fuzz/transformation_add_local_variable.cpp
  23. 201 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_parameters.cpp
  24. 70 0
      3rdparty/spirv-tools/source/fuzz/transformation_add_parameters.h
  25. 14 15
      3rdparty/spirv-tools/source/fuzz/transformation_permute_function_parameters.cpp
  26. 94 0
      3rdparty/spirv-tools/source/fuzz/transformation_permute_phi_operands.cpp
  27. 56 0
      3rdparty/spirv-tools/source/fuzz/transformation_permute_phi_operands.h
  28. 23 19
      3rdparty/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp
  29. 4 0
      3rdparty/spirv-tools/source/fuzz/transformation_push_id_through_variable.h
  30. 103 2
      3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp
  31. 4 0
      3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h
  32. 104 0
      3rdparty/spirv-tools/source/fuzz/transformation_swap_conditional_branch_operands.cpp
  33. 58 0
      3rdparty/spirv-tools/source/fuzz/transformation_swap_conditional_branch_operands.h
  34. 1 0
      3rdparty/spirv-tools/source/name_mapper.cpp
  35. 292 5
      3rdparty/spirv-tools/source/opt/debug_info_manager.cpp
  36. 45 0
      3rdparty/spirv-tools/source/opt/debug_info_manager.h
  37. 206 31
      3rdparty/spirv-tools/source/opt/desc_sroa.cpp
  38. 23 0
      3rdparty/spirv-tools/source/opt/desc_sroa.h
  39. 21 6
      3rdparty/spirv-tools/source/opt/ir_context.cpp
  40. 28 1
      3rdparty/spirv-tools/source/opt/ir_context.h
  41. 6 0
      3rdparty/spirv-tools/source/opt/mem_pass.cpp
  42. 4 0
      3rdparty/spirv-tools/source/opt/pass.h
  43. 15 3
      3rdparty/spirv-tools/source/opt/ssa_rewrite_pass.cpp
  44. 1 6
      3rdparty/spirv-tools/source/opt/ssa_rewrite_pass.h
  45. 18 0
      3rdparty/spirv-tools/source/val/validate_decorations.cpp
  46. 378 0
      3rdparty/spirv-tools/source/val/validate_interfaces.cpp
  47. 23 8
      3rdparty/spirv-tools/utils/roll_deps.sh

+ 1 - 1
3rdparty/spirv-headers/include/spirv/spir-v.xml

@@ -145,7 +145,7 @@
 
 
     <!-- SECTION: SPIR-V FP Fast Math Mode Bit Reservations -->
-    <!-- Reserve ranges of bits in the “FP Fast Math Mode” bitfield.
+    <!-- Reserve ranges of bits in the "FP Fast Math Mode" bitfield.
          Each vendor determines the use of values in their own ranges.
          Vendors are not required to disclose those uses.  If the use of a
          value is included in an extension that is adopted by a Khronos

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

@@ -1 +1 @@
-"v2020.4-dev", "SPIRV-Tools v2020.4-dev 685eeed2529a1671c875d204406decacdff43cb3"
+"v2020.4-dev", "SPIRV-Tools v2020.4-dev 0dd31c89ea3db9b4acd6c39bb6f31cc30adf7dc6"

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

@@ -48,6 +48,7 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_add_loads.h
         fuzzer_pass_add_local_variables.h
         fuzzer_pass_add_no_contraction_decorations.h
+        fuzzer_pass_add_parameters.h
         fuzzer_pass_add_stores.h
         fuzzer_pass_add_vector_shuffle_instructions.h
         fuzzer_pass_adjust_branch_weights.h
@@ -64,10 +65,12 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_outline_functions.h
         fuzzer_pass_permute_blocks.h
         fuzzer_pass_permute_function_parameters.h
+        fuzzer_pass_permute_phi_operands.h
         fuzzer_pass_push_ids_through_variables.h
         fuzzer_pass_replace_linear_algebra_instructions.h
         fuzzer_pass_split_blocks.h
         fuzzer_pass_swap_commutable_operands.h
+        fuzzer_pass_swap_conditional_branch_operands.h
         fuzzer_pass_toggle_access_chain_instruction.h
         fuzzer_util.h
         id_use_descriptor.h
@@ -92,6 +95,7 @@ if(SPIRV_BUILD_FUZZER)
         transformation_add_global_variable.h
         transformation_add_local_variable.h
         transformation_add_no_contraction_decoration.h
+        transformation_add_parameters.h
         transformation_add_spec_constant_op.h
         transformation_add_type_array.h
         transformation_add_type_boolean.h
@@ -115,6 +119,7 @@ if(SPIRV_BUILD_FUZZER)
         transformation_move_block_down.h
         transformation_outline_function.h
         transformation_permute_function_parameters.h
+        transformation_permute_phi_operands.h
         transformation_push_id_through_variable.h
         transformation_replace_boolean_constant_with_constant_binary.h
         transformation_replace_constant_with_uniform.h
@@ -127,6 +132,7 @@ if(SPIRV_BUILD_FUZZER)
         transformation_split_block.h
         transformation_store.h
         transformation_swap_commutable_operands.h
+        transformation_swap_conditional_branch_operands.h
         transformation_toggle_access_chain_instruction.h
         transformation_vector_shuffle.h
         uniform_buffer_element_descriptor.h
@@ -150,6 +156,7 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_add_loads.cpp
         fuzzer_pass_add_local_variables.cpp
         fuzzer_pass_add_no_contraction_decorations.cpp
+        fuzzer_pass_add_parameters.cpp
         fuzzer_pass_add_stores.cpp
         fuzzer_pass_add_vector_shuffle_instructions.cpp
         fuzzer_pass_adjust_branch_weights.cpp
@@ -166,10 +173,12 @@ if(SPIRV_BUILD_FUZZER)
         fuzzer_pass_outline_functions.cpp
         fuzzer_pass_permute_blocks.cpp
         fuzzer_pass_permute_function_parameters.cpp
+        fuzzer_pass_permute_phi_operands.cpp
         fuzzer_pass_push_ids_through_variables.cpp
         fuzzer_pass_replace_linear_algebra_instructions.cpp
         fuzzer_pass_split_blocks.cpp
         fuzzer_pass_swap_commutable_operands.cpp
+        fuzzer_pass_swap_conditional_branch_operands.cpp
         fuzzer_pass_toggle_access_chain_instruction.cpp
         fuzzer_util.cpp
         id_use_descriptor.cpp
@@ -193,6 +202,7 @@ if(SPIRV_BUILD_FUZZER)
         transformation_add_global_variable.cpp
         transformation_add_local_variable.cpp
         transformation_add_no_contraction_decoration.cpp
+        transformation_add_parameters.cpp
         transformation_add_spec_constant_op.cpp
         transformation_add_type_array.cpp
         transformation_add_type_boolean.cpp
@@ -216,6 +226,7 @@ if(SPIRV_BUILD_FUZZER)
         transformation_move_block_down.cpp
         transformation_outline_function.cpp
         transformation_permute_function_parameters.cpp
+        transformation_permute_phi_operands.cpp
         transformation_push_id_through_variable.cpp
         transformation_replace_boolean_constant_with_constant_binary.cpp
         transformation_replace_constant_with_uniform.cpp
@@ -228,6 +239,7 @@ if(SPIRV_BUILD_FUZZER)
         transformation_split_block.cpp
         transformation_store.cpp
         transformation_swap_commutable_operands.cpp
+        transformation_swap_conditional_branch_operands.cpp
         transformation_toggle_access_chain_instruction.cpp
         transformation_vector_shuffle.cpp
         uniform_buffer_element_descriptor.cpp

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

@@ -32,6 +32,7 @@
 #include "source/fuzz/fuzzer_pass_add_loads.h"
 #include "source/fuzz/fuzzer_pass_add_local_variables.h"
 #include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
+#include "source/fuzz/fuzzer_pass_add_parameters.h"
 #include "source/fuzz/fuzzer_pass_add_stores.h"
 #include "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h"
 #include "source/fuzz/fuzzer_pass_adjust_branch_weights.h"
@@ -47,10 +48,12 @@
 #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_phi_operands.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_split_blocks.h"
 #include "source/fuzz/fuzzer_pass_swap_commutable_operands.h"
+#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h"
 #include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/pseudo_random_generator.h"
@@ -222,6 +225,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
     MaybeAddPass<FuzzerPassAddLocalVariables>(
         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddParameters>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
     MaybeAddPass<FuzzerPassAddStores>(&passes, ir_context.get(),
                                       &transformation_context, &fuzzer_context,
                                       transformation_sequence_out);
@@ -264,6 +270,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
     MaybeAddPass<FuzzerPassSplitBlocks>(
         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out);
+    MaybeAddPass<FuzzerPassSwapBranchConditionalOperands>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
   }
 
   bool is_first = true;
@@ -301,6 +310,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
   MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
       &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
       transformation_sequence_out);
+  MaybeAddPass<FuzzerPassPermutePhiOperands>(
+      &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
+      transformation_sequence_out);
   MaybeAddPass<FuzzerPassSwapCommutableOperands>(
       &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
       transformation_sequence_out);

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

@@ -38,6 +38,7 @@ const std::pair<uint32_t, uint32_t> kChanceOfAddingLocalVariable = {20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingMatrixType = {20, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingNoContractionDecoration = {
     5, 70};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingParameters = {5, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingStore = {5, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorType = {20, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorShuffle = {20, 70};
@@ -63,11 +64,14 @@ const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfOutliningFunction = {10, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfPermutingParameters = {30, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfPermutingPhiOperands = {30, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfPushingIdThroughVariable = {5, 50};
 const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
 const std::pair<uint32_t, uint32_t>
     kChanceOfReplacingLinearAlgebraInstructions = {10, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
+const std::pair<uint32_t, uint32_t> kChanceOfSwappingConditionalBranchOperands =
+    {10, 70};
 const std::pair<uint32_t, uint32_t> kChanceOfTogglingAccessChainInstruction = {
     20, 90};
 
@@ -78,6 +82,10 @@ const uint32_t kDefaultMaxLoopControlPartialCount = 100;
 const uint32_t kDefaultMaxLoopControlPeelCount = 100;
 const uint32_t kDefaultMaxLoopLimit = 20;
 const uint32_t kDefaultMaxNewArraySizeLimit = 100;
+// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3424):
+//  think whether there is a better limit on the maximum number of parameters.
+const uint32_t kDefaultMaxNumberOfFunctionParameters = 128;
+const uint32_t kDefaultMaxNumberOfNewParameters = 15;
 
 // Default functions for controlling how deep to go during recursive
 // generation/transformation. Keep them in alphabetical order.
@@ -101,6 +109,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
       max_loop_control_peel_count_(kDefaultMaxLoopControlPeelCount),
       max_loop_limit_(kDefaultMaxLoopLimit),
       max_new_array_size_limit_(kDefaultMaxNewArraySizeLimit),
+      max_number_of_function_parameters_(kDefaultMaxNumberOfFunctionParameters),
+      max_number_of_new_parameters_(kDefaultMaxNumberOfNewParameters),
       go_deeper_in_constant_obfuscation_(
           kDefaultGoDeeperInConstantObfuscation) {
   chance_of_adding_access_chain_ =
@@ -126,6 +136,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
       ChooseBetweenMinAndMax(kChanceOfAddingMatrixType);
   chance_of_adding_no_contraction_decoration_ =
       ChooseBetweenMinAndMax(kChanceOfAddingNoContractionDecoration);
+  chance_of_adding_parameters =
+      ChooseBetweenMinAndMax(kChanceOfAddingParameters);
   chance_of_adding_store_ = ChooseBetweenMinAndMax(kChanceOfAddingStore);
   chance_of_adding_vector_shuffle_ =
       ChooseBetweenMinAndMax(kChanceOfAddingVectorShuffle);
@@ -163,6 +175,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
       ChooseBetweenMinAndMax(kChanceOfOutliningFunction);
   chance_of_permuting_parameters_ =
       ChooseBetweenMinAndMax(kChanceOfPermutingParameters);
+  chance_of_permuting_phi_operands_ =
+      ChooseBetweenMinAndMax(kChanceOfPermutingPhiOperands);
   chance_of_pushing_id_through_variable_ =
       ChooseBetweenMinAndMax(kChanceOfPushingIdThroughVariable);
   chance_of_replacing_id_with_synonym_ =
@@ -170,6 +184,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
   chance_of_replacing_linear_algebra_instructions_ =
       ChooseBetweenMinAndMax(kChanceOfReplacingLinearAlgebraInstructions);
   chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
+  chance_of_swapping_conditional_branch_operands_ =
+      ChooseBetweenMinAndMax(kChanceOfSwappingConditionalBranchOperands);
   chance_of_toggling_access_chain_instruction_ =
       ChooseBetweenMinAndMax(kChanceOfTogglingAccessChainInstruction);
 }

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

@@ -135,6 +135,7 @@ class FuzzerContext {
   uint32_t GetChanceOfAddingNoContractionDecoration() {
     return chance_of_adding_no_contraction_decoration_;
   }
+  uint32_t GetChanceOfAddingParameters() { return chance_of_adding_parameters; }
   uint32_t GetChanceOfAddingStore() { return chance_of_adding_store_; }
   uint32_t GetChanceOfAddingVectorShuffle() {
     return chance_of_adding_vector_shuffle_;
@@ -185,6 +186,9 @@ class FuzzerContext {
   uint32_t GetChanceOfPermutingParameters() {
     return chance_of_permuting_parameters_;
   }
+  uint32_t GetChanceOfPermutingPhiOperands() {
+    return chance_of_permuting_phi_operands_;
+  }
   uint32_t GetChanceOfPushingIdThroughVariable() {
     return chance_of_pushing_id_through_variable_;
   }
@@ -195,6 +199,9 @@ class FuzzerContext {
     return chance_of_replacing_linear_algebra_instructions_;
   }
   uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
+  uint32_t GetChanceOfSwappingConditionalBranchOperands() {
+    return chance_of_swapping_conditional_branch_operands_;
+  }
   uint32_t GetChanceOfTogglingAccessChainInstruction() {
     return chance_of_toggling_access_chain_instruction_;
   }
@@ -204,6 +211,9 @@ class FuzzerContext {
   uint32_t GetMaximumEquivalenceClassSizeForDataSynonymFactClosure() {
     return max_equivalence_class_size_for_data_synonym_fact_closure_;
   }
+  uint32_t GetMaximumNumberOfFunctionParameters() {
+    return max_number_of_function_parameters_;
+  }
   std::pair<uint32_t, uint32_t> GetRandomBranchWeights() {
     std::pair<uint32_t, uint32_t> branch_weights = {0, 0};
 
@@ -239,6 +249,12 @@ class FuzzerContext {
   uint32_t GetRandomLoopLimit() {
     return random_generator_->RandomUint32(max_loop_limit_);
   }
+  uint32_t GetRandomNumberOfNewParameters(uint32_t num_of_params) {
+    assert(num_of_params < GetMaximumNumberOfFunctionParameters());
+    return ChooseBetweenMinAndMax(
+        {1, std::min(max_number_of_new_parameters_,
+                     GetMaximumNumberOfFunctionParameters() - num_of_params)});
+  }
   uint32_t GetRandomSizeForNewArray() {
     // Ensure that the array size is non-zero.
     return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1;
@@ -267,6 +283,7 @@ class FuzzerContext {
   uint32_t chance_of_adding_local_variable_;
   uint32_t chance_of_adding_matrix_type_;
   uint32_t chance_of_adding_no_contraction_decoration_;
+  uint32_t chance_of_adding_parameters;
   uint32_t chance_of_adding_store_;
   uint32_t chance_of_adding_vector_shuffle_;
   uint32_t chance_of_adding_vector_type_;
@@ -287,10 +304,12 @@ class FuzzerContext {
   uint32_t chance_of_obfuscating_constant_;
   uint32_t chance_of_outlining_function_;
   uint32_t chance_of_permuting_parameters_;
+  uint32_t chance_of_permuting_phi_operands_;
   uint32_t chance_of_pushing_id_through_variable_;
   uint32_t chance_of_replacing_id_with_synonym_;
   uint32_t chance_of_replacing_linear_algebra_instructions_;
   uint32_t chance_of_splitting_block_;
+  uint32_t chance_of_swapping_conditional_branch_operands_;
   uint32_t chance_of_toggling_access_chain_instruction_;
 
   // Limits associated with various quantities for which random values are
@@ -301,6 +320,8 @@ class FuzzerContext {
   uint32_t max_loop_control_peel_count_;
   uint32_t max_loop_limit_;
   uint32_t max_new_array_size_limit_;
+  uint32_t max_number_of_function_parameters_;
+  uint32_t max_number_of_new_parameters_;
 
   // Functions to determine with what probability to go deeper when generating
   // or mutating constructs recursively.

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

@@ -0,0 +1,143 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_add_parameters.h"
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_add_parameters.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddParameters::FuzzerPassAddParameters(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassAddParameters::~FuzzerPassAddParameters() = default;
+
+void FuzzerPassAddParameters::Apply() {
+  const auto& type_candidates = ComputeTypeCandidates();
+
+  if (type_candidates.empty()) {
+    // The module contains no suitable types to use in new parameters.
+    return;
+  }
+
+  // Iterate over all functions in the module.
+  for (const auto& function : *GetIRContext()->module()) {
+    // Skip all entry-point functions - we don't want to change those.
+    if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(),
+                                         function.result_id())) {
+      continue;
+    }
+
+    if (GetNumberOfParameters(function) >=
+        GetFuzzerContext()->GetMaximumNumberOfFunctionParameters()) {
+      continue;
+    }
+
+    if (!GetFuzzerContext()->ChoosePercentage(
+            GetFuzzerContext()->GetChanceOfAddingParameters())) {
+      continue;
+    }
+
+    const auto* type_inst =
+        fuzzerutil::GetFunctionType(GetIRContext(), &function);
+    assert(type_inst);
+
+    // -1 because we don't take return type into account.
+    auto num_old_parameters = type_inst->NumInOperands() - 1;
+    auto num_new_parameters =
+        GetFuzzerContext()->GetRandomNumberOfNewParameters(
+            GetNumberOfParameters(function));
+
+    std::vector<uint32_t> all_types(num_old_parameters);
+    std::vector<uint32_t> new_types(num_new_parameters);
+    std::vector<uint32_t> parameter_ids(num_new_parameters);
+    std::vector<uint32_t> constant_ids(num_new_parameters);
+
+    // Get type ids for old parameters.
+    for (uint32_t i = 0; i < num_old_parameters; ++i) {
+      // +1 since we don't take return type into account.
+      all_types[i] = type_inst->GetSingleWordInOperand(i + 1);
+    }
+
+    for (uint32_t i = 0; i < num_new_parameters; ++i) {
+      // Get type ids for new parameters.
+      new_types[i] =
+          type_candidates[GetFuzzerContext()->RandomIndex(type_candidates)];
+
+      // Create constants to initialize new parameters from.
+      constant_ids[i] = FindOrCreateZeroConstant(new_types[i]);
+    }
+
+    // Append new parameters to the old ones.
+    all_types.insert(all_types.end(), new_types.begin(), new_types.end());
+
+    // Generate result ids for new parameters.
+    for (auto& id : parameter_ids) {
+      id = GetFuzzerContext()->GetFreshId();
+    }
+
+    auto result_type_id = type_inst->GetSingleWordInOperand(0);
+    ApplyTransformation(TransformationAddParameters(
+        function.result_id(),
+        FindOrCreateFunctionType(result_type_id, all_types),
+        std::move(new_types), std::move(parameter_ids),
+        std::move(constant_ids)));
+  }
+}
+
+std::vector<uint32_t> FuzzerPassAddParameters::ComputeTypeCandidates() const {
+  std::vector<uint32_t> result;
+
+  for (const auto* type_inst : GetIRContext()->module()->GetTypes()) {
+    // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
+    //  the number of types we support here is limited by the number of types
+    //  supported by |FindOrCreateZeroConstant|.
+    switch (type_inst->opcode()) {
+      case SpvOpTypeBool:
+      case SpvOpTypeInt:
+      case SpvOpTypeFloat:
+      case SpvOpTypeArray:
+      case SpvOpTypeMatrix:
+      case SpvOpTypeVector:
+      case SpvOpTypeStruct: {
+        result.push_back(type_inst->result_id());
+      } break;
+      default:
+        // Ignore other types.
+        break;
+    }
+  }
+
+  return result;
+}
+
+uint32_t FuzzerPassAddParameters::GetNumberOfParameters(
+    const opt::Function& function) const {
+  const auto* type = GetIRContext()->get_type_mgr()->GetType(
+      function.DefInst().GetSingleWordInOperand(1));
+  assert(type && type->AsFunction());
+
+  return static_cast<uint32_t>(type->AsFunction()->param_types().size());
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

@@ -0,0 +1,51 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_PARAMETERS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_PARAMETERS_H_
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Randomly decides for each non-entry-point function in the module whether to
+// add new parameters to it. If so, randomly determines the number of parameters
+// to add, their type and creates constants used to initialize them.
+class FuzzerPassAddParameters : public FuzzerPass {
+ public:
+  FuzzerPassAddParameters(opt::IRContext* ir_context,
+                          TransformationContext* transformation_context,
+                          FuzzerContext* fuzzer_context,
+                          protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddParameters() override;
+
+  void Apply() override;
+
+ private:
+  // Uses types, defined in the module, to compute a vector of their ids, which
+  // will be used as type ids of new parameters.
+  std::vector<uint32_t> ComputeTypeCandidates() const;
+
+  // Returns number of parameters of |function|.
+  uint32_t GetNumberOfParameters(const opt::Function& function) const;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_PARAMETERS_H_

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

@@ -34,7 +34,7 @@ class FuzzerPassPermuteFunctionParameters : public FuzzerPass {
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassPermuteFunctionParameters();
+  ~FuzzerPassPermuteFunctionParameters() override;
 
   void Apply() override;
 };

+ 64 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.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 <numeric>
+#include <vector>
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_pass_permute_phi_operands.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_permute_phi_operands.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassPermutePhiOperands::FuzzerPassPermutePhiOperands(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassPermutePhiOperands::~FuzzerPassPermutePhiOperands() = default;
+
+void FuzzerPassPermutePhiOperands::Apply() {
+  ForEachInstructionWithInstructionDescriptor(
+      [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/,
+             opt::BasicBlock::iterator inst_it,
+             const protobufs::InstructionDescriptor& /*unused*/) {
+        const auto& inst = *inst_it;
+
+        if (inst.opcode() != SpvOpPhi) {
+          return;
+        }
+
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfPermutingPhiOperands())) {
+          return;
+        }
+
+        // Create a vector of indices for each pair of operands in the |inst|.
+        // OpPhi always has an even number of operands.
+        std::vector<uint32_t> permutation(inst.NumInOperands() / 2);
+        std::iota(permutation.begin(), permutation.end(), 0);
+        GetFuzzerContext()->Shuffle(&permutation);
+
+        ApplyTransformation(TransformationPermutePhiOperands(
+            inst.result_id(), std::move(permutation)));
+      });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 40 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.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_PHI_OPERANDS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_PHI_OPERANDS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Iterates over all instructions in the module and randomly decides for each
+// OpPhi instruction whether to permute its operands.
+class FuzzerPassPermutePhiOperands : public FuzzerPass {
+ public:
+  FuzzerPassPermutePhiOperands(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassPermutePhiOperands() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_PHI_OPERANDS_H_

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

@@ -104,13 +104,37 @@ void FuzzerPassPushIdsThroughVariables::Apply() {
         // If the pointer type does not exist, then create it.
         FindOrCreatePointerType(basic_type_id, variable_storage_class);
 
+        // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
+        //  type support here is limited by the FindOrCreateZeroConstant
+        //  function.
+        const auto* type_inst =
+            GetIRContext()->get_def_use_mgr()->GetDef(basic_type_id);
+        assert(type_inst);
+        switch (type_inst->opcode()) {
+          case SpvOpTypeBool:
+          case SpvOpTypeFloat:
+          case SpvOpTypeInt:
+          case SpvOpTypeArray:
+          case SpvOpTypeMatrix:
+          case SpvOpTypeVector:
+          case SpvOpTypeStruct:
+            break;
+          default:
+            return;
+        }
+
+        // Create a constant to initialize the variable from. This might update
+        // module's id bound so it must be done before any fresh ids are
+        // computed.
+        auto initializer_id = FindOrCreateZeroConstant(basic_type_id);
+
         // Applies the push id through variable transformation.
         ApplyTransformation(TransformationPushIdThroughVariable(
             value_instructions[GetFuzzerContext()->RandomIndex(
                                    value_instructions)]
                 ->result_id(),
             GetFuzzerContext()->GetFreshId(), GetFuzzerContext()->GetFreshId(),
-            variable_storage_class, instruction_descriptor));
+            variable_storage_class, initializer_id, instruction_descriptor));
       });
 }
 

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

@@ -30,7 +30,7 @@ class FuzzerPassPushIdsThroughVariables : public FuzzerPass {
       FuzzerContext* fuzzer_context,
       protobufs::TransformationSequence* transformations);
 
-  ~FuzzerPassPushIdsThroughVariables();
+  ~FuzzerPassPushIdsThroughVariables() override;
 
   void Apply() override;
 };

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

@@ -42,6 +42,7 @@ void FuzzerPassReplaceLinearAlgebraInstructions::Apply() {
     // addressed the following conditional can use the function
     // |spvOpcodeIsLinearAlgebra|.
     if (instruction->opcode() != SpvOpVectorTimesScalar &&
+        instruction->opcode() != SpvOpMatrixTimesScalar &&
         instruction->opcode() != SpvOpDot) {
       return;
     }

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

@@ -0,0 +1,59 @@
+// 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_swap_conditional_branch_operands.h"
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_swap_conditional_branch_operands.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassSwapBranchConditionalOperands::
+    FuzzerPassSwapBranchConditionalOperands(
+        opt::IRContext* ir_context,
+        TransformationContext* transformation_context,
+        FuzzerContext* fuzzer_context,
+        protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassSwapBranchConditionalOperands::
+    ~FuzzerPassSwapBranchConditionalOperands() = default;
+
+void FuzzerPassSwapBranchConditionalOperands::Apply() {
+  ForEachInstructionWithInstructionDescriptor(
+      [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/,
+             opt::BasicBlock::iterator inst_it,
+             const protobufs::InstructionDescriptor& instruction_descriptor) {
+        const auto& inst = *inst_it;
+
+        if (inst.opcode() != SpvOpBranchConditional) {
+          return;
+        }
+
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()
+                    ->GetChanceOfSwappingConditionalBranchOperands())) {
+          return;
+        }
+
+        ApplyTransformation(TransformationSwapConditionalBranchOperands(
+            instruction_descriptor, GetFuzzerContext()->GetFreshId()));
+      });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 40 - 0
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.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_SWAP_BRANCH_CONDITIONAL_OPERANDS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_SWAP_BRANCH_CONDITIONAL_OPERANDS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Randomly decides for each OpBranchConditional instruction in the module
+// whether to swap its operands or not.
+class FuzzerPassSwapBranchConditionalOperands : public FuzzerPass {
+ public:
+  FuzzerPassSwapBranchConditionalOperands(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassSwapBranchConditionalOperands() override;
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_SWAP_BRANCH_CONDITIONAL_OPERANDS_H_

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

@@ -12,6 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <algorithm>
+#include <unordered_set>
+
 #include "source/fuzz/fuzzer_util.h"
 
 #include "source/opt/build_module.h"
@@ -583,6 +586,107 @@ void AddVariableIdToEntryPointInterfaces(opt::IRContext* context, uint32_t id) {
   }
 }
 
+void AddGlobalVariable(opt::IRContext* context, uint32_t result_id,
+                       uint32_t type_id, SpvStorageClass storage_class,
+                       uint32_t initializer_id) {
+  // Check various preconditions.
+  assert((storage_class == SpvStorageClassPrivate ||
+          storage_class == SpvStorageClassWorkgroup) &&
+         "Variable's storage class must be either Private or Workgroup");
+
+  auto* type_inst = context->get_def_use_mgr()->GetDef(type_id);
+  (void)type_inst;  // Variable becomes unused in release mode.
+  assert(type_inst && type_inst->opcode() == SpvOpTypePointer &&
+         GetStorageClassFromPointerType(type_inst) == storage_class &&
+         "Variable's type is invalid");
+
+  if (storage_class == SpvStorageClassWorkgroup) {
+    assert(initializer_id == 0);
+  }
+
+  if (initializer_id != 0) {
+    const auto* constant_inst =
+        context->get_def_use_mgr()->GetDef(initializer_id);
+    (void)constant_inst;  // Variable becomes unused in release mode.
+    assert(constant_inst && spvOpcodeIsConstant(constant_inst->opcode()) &&
+           GetPointeeTypeIdFromPointerType(type_inst) ==
+               constant_inst->type_id() &&
+           "Initializer is invalid");
+  }
+
+  opt::Instruction::OperandList operands = {
+      {SPV_OPERAND_TYPE_STORAGE_CLASS, {static_cast<uint32_t>(storage_class)}}};
+
+  if (initializer_id) {
+    operands.push_back({SPV_OPERAND_TYPE_ID, {initializer_id}});
+  }
+
+  context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
+      context, SpvOpVariable, type_id, result_id, std::move(operands)));
+
+  AddVariableIdToEntryPointInterfaces(context, result_id);
+}
+
+void AddLocalVariable(opt::IRContext* context, uint32_t result_id,
+                      uint32_t type_id, uint32_t function_id,
+                      uint32_t initializer_id) {
+  // Check various preconditions.
+  auto* type_inst = context->get_def_use_mgr()->GetDef(type_id);
+  (void)type_inst;  // Variable becomes unused in release mode.
+  assert(type_inst && type_inst->opcode() == SpvOpTypePointer &&
+         GetStorageClassFromPointerType(type_inst) == SpvStorageClassFunction &&
+         "Variable's type is invalid");
+
+  const auto* constant_inst =
+      context->get_def_use_mgr()->GetDef(initializer_id);
+  (void)constant_inst;  // Variable becomes unused in release mode.
+  assert(constant_inst && spvOpcodeIsConstant(constant_inst->opcode()) &&
+         GetPointeeTypeIdFromPointerType(type_inst) ==
+             constant_inst->type_id() &&
+         "Initializer is invalid");
+
+  auto* function = FindFunction(context, function_id);
+  assert(function && "Function id is invalid");
+
+  function->begin()->begin()->InsertBefore(MakeUnique<opt::Instruction>(
+      context, SpvOpVariable, type_id, result_id,
+      opt::Instruction::OperandList{
+          {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}},
+          {SPV_OPERAND_TYPE_ID, {initializer_id}}}));
+}
+
+bool HasDuplicates(const std::vector<uint32_t>& arr) {
+  return std::unordered_set<uint32_t>(arr.begin(), arr.end()).size() !=
+         arr.size();
+}
+
+bool IsPermutationOfRange(const std::vector<uint32_t>& arr, uint32_t lo,
+                          uint32_t hi) {
+  if (arr.empty()) {
+    return lo > hi;
+  }
+
+  if (HasDuplicates(arr)) {
+    return false;
+  }
+
+  auto min_max = std::minmax_element(arr.begin(), arr.end());
+  return arr.size() == hi - lo + 1 && *min_max.first == lo &&
+         *min_max.second == hi;
+}
+
+std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
+                                             uint32_t function_id) {
+  auto* function = FindFunction(ir_context, function_id);
+  assert(function && "|function_id| is invalid");
+
+  std::vector<opt::Instruction*> result;
+  function->ForEachParam(
+      [&result](opt::Instruction* inst) { result.push_back(inst); });
+
+  return result;
+}
+
 }  // namespace fuzzerutil
 
 }  // namespace fuzz

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

@@ -226,6 +226,46 @@ bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces(
 // from an entry point function, to be listed in that function's interface.
 void AddVariableIdToEntryPointInterfaces(opt::IRContext* context, uint32_t id);
 
+// Adds a global variable with storage class |storage_class| to the module, with
+// type |type_id| and either no initializer or |initializer_id| as an
+// initializer, depending on whether |initializer_id| is 0. The global variable
+// has result id |result_id|.
+//
+// - |type_id| must be the id of a pointer type with the same storage class as
+//   |storage_class|.
+// - |storage_class| must be Private or Workgroup.
+// - |initializer_id| must be 0 if |storage_class| is Workgroup, and otherwise
+//   may either be 0 or the id of a constant whose type is the pointee type of
+//   |type_id|.
+void AddGlobalVariable(opt::IRContext* context, uint32_t result_id,
+                       uint32_t type_id, SpvStorageClass storage_class,
+                       uint32_t initializer_id);
+
+// Adds an instruction to the start of |function_id|, of the form:
+//   |result_id| = OpVariable |type_id| Function |initializer_id|.
+//
+// - |type_id| must be the id of a pointer type with Function storage class.
+// - |initializer_id| must be the id of a constant with the same type as the
+//   pointer's pointee type.
+// - |function_id| must be the id of a function.
+void AddLocalVariable(opt::IRContext* context, uint32_t result_id,
+                      uint32_t type_id, uint32_t function_id,
+                      uint32_t initializer_id);
+
+// Returns true if the vector |arr| has duplicates.
+bool HasDuplicates(const std::vector<uint32_t>& arr);
+
+// Checks that the given vector |arr| contains a permutation of a range
+// [lo, hi]. That being said, all elements in the range are present without
+// duplicates. If |arr| is empty, returns true iff |lo > hi|.
+bool IsPermutationOfRange(const std::vector<uint32_t>& arr, uint32_t lo,
+                          uint32_t hi);
+
+// Returns OpFunctionParameter instructions corresponding to the function
+// with result id |function_id|.
+std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
+                                             uint32_t function_id);
+
 }  // namespace fuzzerutil
 
 }  // namespace fuzz

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

@@ -378,6 +378,9 @@ message Transformation {
     TransformationPushIdThroughVariable push_id_through_variable = 47;
     TransformationAddSpecConstantOp add_spec_constant_op = 48;
     TransformationReplaceLinearAlgebraInstruction replace_linear_algebra_instruction = 49;
+    TransformationSwapConditionalBranchOperands swap_conditional_branch_operands = 50;
+    TransformationPermutePhiOperands permute_phi_operands = 51;
+    TransformationAddParameters add_parameters = 52;
     // Add additional option using the next available number.
   }
 }
@@ -621,6 +624,27 @@ message TransformationAddNoContractionDecoration {
 
 }
 
+message TransformationAddParameters {
+
+  // Adds new parameters into the function.
+
+  // Result id of the function to add parameters to.
+  uint32 function_id = 1;
+
+  // New type of the function.
+  uint32 new_type_id = 2;
+
+  // Type ids of parameters to add to the function.
+  repeated uint32 new_parameter_type = 3;
+
+  // Result ids for new parameters.
+  repeated uint32 new_parameter_id = 4;
+
+  // Constants to initialize new parameters from.
+  repeated uint32 constant_id = 5;
+
+}
+
 message TransformationAddSpecConstantOp {
 
   // Adds OpSpecConstantOp into the module.
@@ -1004,6 +1028,19 @@ message TransformationPermuteFunctionParameters {
 
 }
 
+message TransformationPermutePhiOperands {
+
+  // Permutes operands of some OpPhi instruction.
+
+  // Result id of the instruction to apply the transformation to.
+  uint32 result_id = 1;
+
+  // A sequence of numbers in the range [0, n/2 - 1] where |n| is the number
+  // of operands of the OpPhi instruction with |result_id|.
+  repeated uint32 permutation = 2;
+
+}
+
 message TransformationPushIdThroughVariable {
 
   // A transformation that makes |value_synonym_id| and |value_id| to be
@@ -1019,12 +1056,15 @@ message TransformationPushIdThroughVariable {
   // A fresh id for the variable to be stored to.
   uint32 variable_id = 3;
 
+  // Constant to initialize the variable from.
+  uint32 initializer_id = 4;
+
   // The variable storage class (global or local).
-  uint32 variable_storage_class = 4;
+  uint32 variable_storage_class = 5;
 
   // A descriptor for an instruction which the new OpStore
   // and OpLoad instructions might be inserted before.
-  InstructionDescriptor instruction_descriptor = 5;
+  InstructionDescriptor instruction_descriptor = 6;
 
 }
 
@@ -1094,13 +1134,13 @@ message TransformationReplaceLinearAlgebraInstruction {
   // This transformation is only applicable if the described instruction has one of the following opcodes.
   // Supported:
   //   OpVectorTimesScalar
+  //   OpMatrixTimesScalar
   //   OpDot
   // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
   // Right now we only support certain operations. When this issue is addressed
   // the supporting comments can be removed.
   // To be supported in the future:
   //   OpTranspose
-  //   OpMatrixTimesScalar
   //   OpVectorTimesMatrix
   //   OpMatrixTimesVector
   //   OpMatrixTimesMatrix
@@ -1226,6 +1266,21 @@ message TransformationSwapCommutableOperands {
 
 }
 
+message TransformationSwapConditionalBranchOperands {
+
+  // Swaps label ids in OpBranchConditional instruction.
+  // Additionally, inverts the guard and swaps branch weights
+  // if present.
+
+  // Descriptor of the instruction to swap operands of.
+  InstructionDescriptor instruction_descriptor = 1;
+
+  // Fresh result id for the OpLogicalNot instruction, used
+  // to invert the guard.
+  uint32 fresh_id = 2;
+
+}
+
 message TransformationToggleAccessChainInstruction {
 
   // A transformation that toggles an access chain instruction.

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

@@ -30,6 +30,7 @@
 #include "source/fuzz/transformation_add_global_variable.h"
 #include "source/fuzz/transformation_add_local_variable.h"
 #include "source/fuzz/transformation_add_no_contraction_decoration.h"
+#include "source/fuzz/transformation_add_parameters.h"
 #include "source/fuzz/transformation_add_spec_constant_op.h"
 #include "source/fuzz/transformation_add_type_array.h"
 #include "source/fuzz/transformation_add_type_boolean.h"
@@ -52,6 +53,7 @@
 #include "source/fuzz/transformation_move_block_down.h"
 #include "source/fuzz/transformation_outline_function.h"
 #include "source/fuzz/transformation_permute_function_parameters.h"
+#include "source/fuzz/transformation_permute_phi_operands.h"
 #include "source/fuzz/transformation_push_id_through_variable.h"
 #include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
 #include "source/fuzz/transformation_replace_constant_with_uniform.h"
@@ -64,6 +66,7 @@
 #include "source/fuzz/transformation_split_block.h"
 #include "source/fuzz/transformation_store.h"
 #include "source/fuzz/transformation_swap_commutable_operands.h"
+#include "source/fuzz/transformation_swap_conditional_branch_operands.h"
 #include "source/fuzz/transformation_toggle_access_chain_instruction.h"
 #include "source/fuzz/transformation_vector_shuffle.h"
 #include "source/util/make_unique.h"
@@ -112,6 +115,8 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
         kAddNoContractionDecoration:
       return MakeUnique<TransformationAddNoContractionDecoration>(
           message.add_no_contraction_decoration());
+    case protobufs::Transformation::TransformationCase::kAddParameters:
+      return MakeUnique<TransformationAddParameters>(message.add_parameters());
     case protobufs::Transformation::TransformationCase::kAddSpecConstantOp:
       return MakeUnique<TransformationAddSpecConstantOp>(
           message.add_spec_constant_op());
@@ -169,6 +174,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
         kPermuteFunctionParameters:
       return MakeUnique<TransformationPermuteFunctionParameters>(
           message.permute_function_parameters());
+    case protobufs::Transformation::TransformationCase::kPermutePhiOperands:
+      return MakeUnique<TransformationPermutePhiOperands>(
+          message.permute_phi_operands());
     case protobufs::Transformation::TransformationCase::kPushIdThroughVariable:
       return MakeUnique<TransformationPushIdThroughVariable>(
           message.push_id_through_variable());
@@ -206,6 +214,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
     case protobufs::Transformation::TransformationCase::kSwapCommutableOperands:
       return MakeUnique<TransformationSwapCommutableOperands>(
           message.swap_commutable_operands());
+    case protobufs::Transformation::TransformationCase::
+        kSwapConditionalBranchOperands:
+      return MakeUnique<TransformationSwapConditionalBranchOperands>(
+          message.swap_conditional_branch_operands());
     case protobufs::Transformation::TransformationCase::
         kToggleAccessChainInstruction:
       return MakeUnique<TransformationToggleAccessChainInstruction>(

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

@@ -93,20 +93,12 @@ bool TransformationAddGlobalVariable::IsApplicable(
 void TransformationAddGlobalVariable::Apply(
     opt::IRContext* ir_context,
     TransformationContext* transformation_context) const {
-  opt::Instruction::OperandList input_operands;
-  input_operands.push_back(
-      {SPV_OPERAND_TYPE_STORAGE_CLASS, {message_.storage_class()}});
-  if (message_.initializer_id()) {
-    input_operands.push_back(
-        {SPV_OPERAND_TYPE_ID, {message_.initializer_id()}});
-  }
-  ir_context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpVariable, message_.type_id(), message_.fresh_id(),
-      input_operands));
-  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+  fuzzerutil::AddGlobalVariable(
+      ir_context, message_.fresh_id(), message_.type_id(),
+      static_cast<SpvStorageClass>(message_.storage_class()),
+      message_.initializer_id());
 
-  fuzzerutil::AddVariableIdToEntryPointInterfaces(ir_context,
-                                                  message_.fresh_id());
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
 
   if (message_.value_is_irrelevant()) {
     transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(

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

@@ -70,18 +70,12 @@ bool TransformationAddLocalVariable::IsApplicable(
 void TransformationAddLocalVariable::Apply(
     opt::IRContext* ir_context,
     TransformationContext* transformation_context) const {
+  fuzzerutil::AddLocalVariable(ir_context, message_.fresh_id(),
+                               message_.type_id(), message_.function_id(),
+                               message_.initializer_id());
+
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-  fuzzerutil::FindFunction(ir_context, message_.function_id())
-      ->begin()
-      ->begin()
-      ->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpVariable, message_.type_id(), message_.fresh_id(),
-          opt::Instruction::OperandList(
-              {{SPV_OPERAND_TYPE_STORAGE_CLASS,
-                {
 
-                    SpvStorageClassFunction}},
-               {SPV_OPERAND_TYPE_ID, {message_.initializer_id()}}})));
   if (message_.value_is_irrelevant()) {
     transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
         message_.fresh_id());

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

@@ -0,0 +1,201 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_parameters.h"
+
+#include <source/spirv_constant.h>
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddParameters::TransformationAddParameters(
+    const protobufs::TransformationAddParameters& message)
+    : message_(message) {}
+
+TransformationAddParameters::TransformationAddParameters(
+    uint32_t function_id, uint32_t new_type_id,
+    const std::vector<uint32_t>& new_parameter_type,
+    const std::vector<uint32_t>& new_parameter_id,
+    const std::vector<uint32_t>& constant_id) {
+  message_.set_function_id(function_id);
+  message_.set_new_type_id(new_type_id);
+
+  for (auto id : new_parameter_type) {
+    message_.add_new_parameter_type(id);
+  }
+
+  for (auto id : new_parameter_id) {
+    message_.add_new_parameter_id(id);
+  }
+
+  for (auto id : constant_id) {
+    message_.add_constant_id(id);
+  }
+}
+
+bool TransformationAddParameters::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  // Check that function exists
+  const auto* function =
+      fuzzerutil::FindFunction(ir_context, message_.function_id());
+  if (!function ||
+      fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
+    return false;
+  }
+
+  // Validate new parameters.
+  const auto& new_type_ids = message_.new_parameter_type();
+  const auto& new_parameter_ids = message_.new_parameter_id();
+  const auto& constant_ids = message_.constant_id();
+
+  // All three vectors must have the same size.
+  if (new_type_ids.size() != new_parameter_ids.size() ||
+      new_type_ids.size() != constant_ids.size()) {
+    return false;
+  }
+
+  // Vectors must have at least one component.
+  if (new_type_ids.empty()) {
+    return false;
+  }
+
+  // Check that type ids exist in the module and are not OpTypeVoid.
+  for (auto id : new_type_ids) {
+    const auto* type = ir_context->get_type_mgr()->GetType(id);
+    if (!type || type->AsVoid()) {
+      return false;
+    }
+  }
+
+  // Check that all parameter ids are fresh.
+  for (auto id : new_parameter_ids) {
+    if (!fuzzerutil::IsFreshId(ir_context, id)) {
+      return false;
+    }
+  }
+
+  // Check that constants exist and have valid type.
+  for (int i = 0, n = constant_ids.size(); i < n; ++i) {
+    const auto* inst = ir_context->get_def_use_mgr()->GetDef(constant_ids[i]);
+    if (!inst || inst->type_id() != new_type_ids[i]) {
+      return false;
+    }
+  }
+
+  // Validate new function type.
+  const auto* old_type_inst = fuzzerutil::GetFunctionType(ir_context, function);
+  const auto* new_type_inst =
+      ir_context->get_def_use_mgr()->GetDef(message_.new_type_id());
+
+  // Both types must exist.
+  assert(old_type_inst && old_type_inst->opcode() == SpvOpTypeFunction);
+  if (!new_type_inst || new_type_inst->opcode() != SpvOpTypeFunction) {
+    return false;
+  }
+
+  auto num_old_parameters = old_type_inst->NumInOperands();
+  auto num_new_parameters = new_type_ids.size();
+
+  // New function type has been added to the module which means that it's valid.
+  // Thus, we don't need to check whether the limit on the number of arguments
+  // is satisfied.
+
+  // New type = old type + new parameters.
+  if (new_type_inst->NumInOperands() !=
+      num_old_parameters + num_new_parameters) {
+    return false;
+  }
+
+  // Check that old parameters and the return type are preserved.
+  for (uint32_t i = 0; i < num_old_parameters; ++i) {
+    if (new_type_inst->GetSingleWordInOperand(i) !=
+        old_type_inst->GetSingleWordInOperand(i)) {
+      return false;
+    }
+  }
+
+  // Check that new parameters have been appended.
+  for (int i = 0; i < num_new_parameters; ++i) {
+    if (new_type_inst->GetSingleWordInOperand(i + num_old_parameters) !=
+        new_type_ids[i]) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void TransformationAddParameters::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  // Retrieve all data from the message
+  auto function_id = message_.function_id();
+  const auto& new_parameter_type = message_.new_parameter_type();
+  const auto& new_parameter_id = message_.new_parameter_id();
+  const auto& constant_id = message_.constant_id();
+
+  // Find the function that will be transformed
+  auto* function = fuzzerutil::FindFunction(ir_context, function_id);
+  assert(function && "Can't find the function");
+
+  // Change function's type
+  function->DefInst().SetInOperand(1, {message_.new_type_id()});
+
+  // Add new parameters to the function.
+  for (int i = 0, n = new_parameter_id.size(); i < n; ++i) {
+    function->AddParameter(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpFunctionParameter, new_parameter_type[i],
+        new_parameter_id[i], opt::Instruction::OperandList()));
+
+    // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
+    //  Add an PointeeValueIsIrrelevant fact if the parameter is a pointer.
+  }
+
+  // Fix all OpFunctionCall instructions.
+  ir_context->get_def_use_mgr()->ForEachUser(
+      &function->DefInst(),
+      [function_id, &constant_id](opt::Instruction* call) {
+        if (call->opcode() != SpvOpFunctionCall ||
+            call->GetSingleWordInOperand(0) != function_id) {
+          return;
+        }
+
+        for (auto id : constant_id) {
+          // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
+          //  it would be good to mark this usage of |id| as irrelevant, so that
+          //  we can perform some interesting transformations on it later.
+          call->AddOperand({SPV_OPERAND_TYPE_ID, {id}});
+        }
+      });
+
+  // Update module's id bound. We can safely dereference the result of
+  // max_element since |new_parameter_id| is guaranteed to have elements.
+  fuzzerutil::UpdateModuleIdBound(
+      ir_context,
+      *std::max_element(new_parameter_id.begin(), new_parameter_id.end()));
+
+  // Make sure our changes are analyzed.
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddParameters::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_add_parameters() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 70 - 0
3rdparty/spirv-tools/source/fuzz/transformation_add_parameters.h

@@ -0,0 +1,70 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETERS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETERS_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 TransformationAddParameters : public Transformation {
+ public:
+  explicit TransformationAddParameters(
+      const protobufs::TransformationAddParameters& message);
+
+  TransformationAddParameters(uint32_t function_id, uint32_t new_type_id,
+                              const std::vector<uint32_t>& new_parameter_type,
+                              const std::vector<uint32_t>& new_parameter_id,
+                              const std::vector<uint32_t>& constant_id);
+
+  // - |function_id| must be a valid result id of some non-entry-point function
+  //   in the module.
+  // - |new_type_id| must be a result id of OpTypeFunction instruction.
+  // - New type of the function must have the same return type. New function
+  //   parameters must be appended to the old ones.
+  // - |new_parameter_type| contains result ids of some OpType* instructions in
+  //   the module. It may not contain result ids of OpTypeVoid.
+  // - |new_parameter_id| contains fresh ids.
+  // - |constant_id| contains result ids used to initialize new parameters. Type
+  //   ids of these instructions must be the same as |new_parameter_type| (i.e.
+  //   |new_parameter_type[i] == GetDef(constant_id[i])->type_id()|).
+  // - |new_parameter_id|, |new_parameter_type| and |constant_id| should all
+  //   have the same size and may not be empty.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // - Creates new OpFunctionParameter instructions for the function with
+  //   |function_id|.
+  // - Changes type of the function to |new_type_id|.
+  // - Adds ids from |constant_id| to every OpFunctionCall instruction that
+  //   calls the function.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationAddParameters message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETERS_H_

+ 14 - 15
3rdparty/spirv-tools/source/fuzz/transformation_permute_function_parameters.cpp

@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <unordered_set>
 #include <vector>
 
 #include "source/fuzz/fuzzer_util.h"
@@ -53,7 +52,8 @@ bool TransformationPermuteFunctionParameters::IsApplicable(
   const auto* function_type = fuzzerutil::GetFunctionType(ir_context, function);
   assert(function_type && "Function type is null");
 
-  const auto& permutation = message_.permutation();
+  std::vector<uint32_t> permutation(message_.permutation().begin(),
+                                    message_.permutation().end());
 
   // Don't take return type into account
   auto arg_size = function_type->NumInOperands() - 1;
@@ -63,21 +63,20 @@ bool TransformationPermuteFunctionParameters::IsApplicable(
     return false;
   }
 
-  // Check that all indices are valid
-  // and unique integers from the [0, n-1] set
-  std::unordered_set<uint32_t> unique_indices;
-  for (auto index : permutation) {
-    // We don't compare |index| with 0 since it's an unsigned integer
-    if (index >= arg_size) {
-      return false;
-    }
-
-    unique_indices.insert(index);
+  // Check that permutation doesn't have duplicated values.
+  assert(!fuzzerutil::HasDuplicates(permutation) &&
+         "Permutation has duplicates");
+
+  // Check that elements in permutation are in range [0, arg_size - 1].
+  //
+  // We must check whether the permutation is empty first because in that case
+  // |arg_size - 1| will produce |std::numeric_limits<uint32_t>::max()| since
+  // it's an unsigned integer.
+  if (!permutation.empty() &&
+      !fuzzerutil::IsPermutationOfRange(permutation, 0, arg_size - 1)) {
+    return false;
   }
 
-  // Check that permutation doesn't have duplicated values
-  assert(unique_indices.size() == arg_size && "Permutation has duplicates");
-
   // Check that new function's type is valid:
   //   - Has the same number of operands
   //   - Has the same result type as the old one

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

@@ -0,0 +1,94 @@
+// 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 <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_permute_phi_operands.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationPermutePhiOperands::TransformationPermutePhiOperands(
+    const spvtools::fuzz::protobufs::TransformationPermutePhiOperands& message)
+    : message_(message) {}
+
+TransformationPermutePhiOperands::TransformationPermutePhiOperands(
+    uint32_t result_id, const std::vector<uint32_t>& permutation) {
+  message_.set_result_id(result_id);
+
+  for (auto index : permutation) {
+    message_.add_permutation(index);
+  }
+}
+
+bool TransformationPermutePhiOperands::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  // Check that |message_.result_id| is valid.
+  const auto* inst =
+      ir_context->get_def_use_mgr()->GetDef(message_.result_id());
+  if (!inst || inst->opcode() != SpvOpPhi) {
+    return false;
+  }
+
+  // Check that |message_.permutation| has expected size.
+  auto expected_permutation_size = inst->NumInOperands() / 2;
+  if (static_cast<uint32_t>(message_.permutation().size()) !=
+      expected_permutation_size) {
+    return false;
+  }
+
+  // Check that |message_.permutation| has elements in range
+  // [0, expected_permutation_size - 1].
+  std::vector<uint32_t> permutation(message_.permutation().begin(),
+                                    message_.permutation().end());
+  assert(!fuzzerutil::HasDuplicates(permutation) &&
+         "Permutation has duplicates");
+
+  // We must check whether the permutation is empty first because in that case
+  // |expected_permutation_size - 1| will produce
+  // |std::numeric_limits<uint32_t>::max()| since it's an unsigned integer.
+  return permutation.empty() ||
+         fuzzerutil::IsPermutationOfRange(permutation, 0,
+                                          expected_permutation_size - 1);
+}
+
+void TransformationPermutePhiOperands::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.result_id());
+  assert(inst);
+
+  opt::Instruction::OperandList permuted_operands;
+  permuted_operands.reserve(inst->NumInOperands());
+
+  for (auto index : message_.permutation()) {
+    permuted_operands.push_back(std::move(inst->GetInOperand(2 * index)));
+    permuted_operands.push_back(std::move(inst->GetInOperand(2 * index + 1)));
+  }
+
+  inst->SetInOperands(std::move(permuted_operands));
+
+  // Make sure our changes are analyzed
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationPermutePhiOperands::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_permute_phi_operands() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

+ 56 - 0
3rdparty/spirv-tools/source/fuzz/transformation_permute_phi_operands.h

@@ -0,0 +1,56 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_PERMUTE_PHI_OPERANDS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_PERMUTE_PHI_OPERANDS_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 TransformationPermutePhiOperands : public Transformation {
+ public:
+  explicit TransformationPermutePhiOperands(
+      const protobufs::TransformationPermutePhiOperands& message);
+
+  TransformationPermutePhiOperands(uint32_t result_id,
+                                   const std::vector<uint32_t>& permutation);
+
+  // - |result_id| must be a valid id of some OpPhi instruction in the module.
+  // - |permutation| must contain elements in the range [0, n/2 - 1] where |n|
+  //   is a number of operands to the instruction with |result_id|. All elements
+  //   must be unique (i.e. |permutation.size() == n / 2|).
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Permutes operands of the OpPhi instruction with |result_id| according to
+  // the elements in |permutation|.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationPermutePhiOperands message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_PERMUTE_PHI_OPERANDS_H_

+ 23 - 19
3rdparty/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp

@@ -27,12 +27,13 @@ TransformationPushIdThroughVariable::TransformationPushIdThroughVariable(
 
 TransformationPushIdThroughVariable::TransformationPushIdThroughVariable(
     uint32_t value_id, uint32_t value_synonym_id, uint32_t variable_id,
-    uint32_t variable_storage_class,
+    uint32_t variable_storage_class, uint32_t initializer_id,
     const protobufs::InstructionDescriptor& instruction_descriptor) {
   message_.set_value_id(value_id);
   message_.set_value_synonym_id(value_synonym_id);
   message_.set_variable_id(variable_id);
   message_.set_variable_storage_class(variable_storage_class);
+  message_.set_initializer_id(initializer_id);
   *message_.mutable_instruction_descriptor() = instruction_descriptor;
 }
 
@@ -85,6 +86,14 @@ bool TransformationPushIdThroughVariable::IsApplicable(
           message_.variable_storage_class() == SpvStorageClassFunction) &&
          "The variable storage class must be private or function.");
 
+  // Check that initializer is valid.
+  const auto* constant_inst =
+      ir_context->get_def_use_mgr()->GetDef(message_.initializer_id());
+  if (!constant_inst || !spvOpcodeIsConstant(constant_inst->opcode()) ||
+      value_instruction->type_id() != constant_inst->type_id()) {
+    return false;
+  }
+
   // |message_.value_id| must be available at the insertion point.
   return fuzzerutil::IdIsAvailableBeforeInstruction(
       ir_context, instruction_to_insert_before, message_.value_id());
@@ -103,28 +112,23 @@ void TransformationPushIdThroughVariable::Apply(
   assert(pointer_type_id && "The required pointer type must be available.");
 
   // Adds whether a global or local variable.
-  fuzzerutil::UpdateModuleIdBound(ir_context, message_.variable_id());
   if (message_.variable_storage_class() == SpvStorageClassPrivate) {
-    ir_context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
-        ir_context, SpvOpVariable, pointer_type_id, message_.variable_id(),
-        opt::Instruction::OperandList(
-            {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassPrivate}}})));
-
-    fuzzerutil::AddVariableIdToEntryPointInterfaces(ir_context,
-                                                    message_.variable_id());
+    fuzzerutil::AddGlobalVariable(ir_context, message_.variable_id(),
+                                  pointer_type_id, SpvStorageClassPrivate,
+                                  message_.initializer_id());
   } else {
-    ir_context
-        ->get_instr_block(
-            FindInstruction(message_.instruction_descriptor(), ir_context))
-        ->GetParent()
-        ->begin()
-        ->begin()
-        ->InsertBefore(MakeUnique<opt::Instruction>(
-            ir_context, SpvOpVariable, pointer_type_id, message_.variable_id(),
-            opt::Instruction::OperandList({{SPV_OPERAND_TYPE_STORAGE_CLASS,
-                                            {SpvStorageClassFunction}}})));
+    auto function_id = ir_context
+                           ->get_instr_block(FindInstruction(
+                               message_.instruction_descriptor(), ir_context))
+                           ->GetParent()
+                           ->result_id();
+    fuzzerutil::AddLocalVariable(ir_context, message_.variable_id(),
+                                 pointer_type_id, function_id,
+                                 message_.initializer_id());
   }
 
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.variable_id());
+
   // Stores value id to variable id.
   FindInstruction(message_.instruction_descriptor(), ir_context)
       ->InsertBefore(MakeUnique<opt::Instruction>(

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

@@ -31,6 +31,7 @@ class TransformationPushIdThroughVariable : public Transformation {
   TransformationPushIdThroughVariable(
       uint32_t value_id, uint32_t value_synonym_fresh_id,
       uint32_t variable_fresh_id, uint32_t variable_storage_class,
+      uint32_t initializer_id,
       const protobufs::InstructionDescriptor& instruction_descriptor);
 
   // - |message_.value_id| must be an instruction result id that has the same
@@ -39,6 +40,9 @@ class TransformationPushIdThroughVariable : public Transformation {
   // - |message_.variable_id| must be fresh
   // - |message_.variable_storage_class| must be either StorageClassPrivate or
   //   StorageClassFunction
+  // - |message_.initializer_id| must be a result id of some constant in the
+  //   module. Its type must be equal to the pointee type of the variable that
+  //   will be created.
   // - |message_.instruction_descriptor| must identify an instruction
   //   which it is valid to insert the OpStore and OpLoad instructions before it
   //   and must be belongs to a reachable block.

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

@@ -46,6 +46,7 @@ bool TransformationReplaceLinearAlgebraInstruction::IsApplicable(
   // the following conditional can use the function |spvOpcodeIsLinearAlgebra|.
   // It must be a supported linear algebra instruction.
   if (instruction->opcode() != SpvOpVectorTimesScalar &&
+      instruction->opcode() != SpvOpMatrixTimesScalar &&
       instruction->opcode() != SpvOpDot) {
     return false;
   }
@@ -77,6 +78,9 @@ void TransformationReplaceLinearAlgebraInstruction::Apply(
     case SpvOpVectorTimesScalar:
       ReplaceOpVectorTimesScalar(ir_context, linear_algebra_instruction);
       break;
+    case SpvOpMatrixTimesScalar:
+      ReplaceOpMatrixTimesScalar(ir_context, linear_algebra_instruction);
+      break;
     case SpvOpDot:
       ReplaceOpDot(ir_context, linear_algebra_instruction);
       break;
@@ -110,7 +114,21 @@ uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount(
                                ->type_id())
                  ->AsVector()
                  ->element_count();
-    case SpvOpDot: {
+    case SpvOpMatrixTimesScalar: {
+      // For each matrix column, |1 + column.size| OpCompositeExtract,
+      // |column.size| OpFMul and 1 OpCompositeConstruct instructions will be
+      // inserted.
+      auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
+          instruction->GetSingleWordInOperand(0));
+      auto matrix_type =
+          ir_context->get_type_mgr()->GetType(matrix_instruction->type_id());
+      return 2 * matrix_type->AsMatrix()->element_count() *
+             (1 + matrix_type->AsMatrix()
+                      ->element_type()
+                      ->AsVector()
+                      ->element_count());
+    }
+    case SpvOpDot:
       // For each pair of vector components, 2 OpCompositeExtract and 1 OpFMul
       // will be inserted. The first two OpFMul instructions will result the
       // first OpFAdd instruction to be inserted. For each remaining OpFMul, 1
@@ -124,7 +142,6 @@ uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount(
                      ->AsVector()
                      ->element_count() -
              2;
-    }
     default:
       assert(false && "Unsupported linear algebra instruction.");
       return 0;
@@ -179,6 +196,90 @@ void TransformationReplaceLinearAlgebraInstruction::ReplaceOpVectorTimesScalar(
   }
 }
 
+void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesScalar(
+    opt::IRContext* ir_context,
+    opt::Instruction* linear_algebra_instruction) const {
+  // Gets OpMatrixTimesScalar in operands.
+  auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
+      linear_algebra_instruction->GetSingleWordInOperand(0));
+  auto scalar_instruction = ir_context->get_def_use_mgr()->GetDef(
+      linear_algebra_instruction->GetSingleWordInOperand(1));
+
+  // Gets matrix information.
+  uint32_t matrix_column_count = ir_context->get_type_mgr()
+                                     ->GetType(matrix_instruction->type_id())
+                                     ->AsMatrix()
+                                     ->element_count();
+  auto matrix_column_type = ir_context->get_type_mgr()
+                                ->GetType(matrix_instruction->type_id())
+                                ->AsMatrix()
+                                ->element_type();
+  uint32_t matrix_column_size = matrix_column_type->AsVector()->element_count();
+
+  std::vector<uint32_t> composite_construct_ids(matrix_column_count);
+  uint32_t fresh_id_index = 0;
+
+  for (uint32_t i = 0; i < matrix_column_count; i++) {
+    // Extracts |matrix| column.
+    uint32_t matrix_extract_id = message_.fresh_ids(fresh_id_index++);
+    fuzzerutil::UpdateModuleIdBound(ir_context, matrix_extract_id);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpCompositeExtract,
+        ir_context->get_type_mgr()->GetId(matrix_column_type),
+        matrix_extract_id,
+        opt::Instruction::OperandList(
+            {{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}},
+             {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
+
+    std::vector<uint32_t> float_multiplication_ids(matrix_column_size);
+
+    for (uint32_t j = 0; j < matrix_column_size; j++) {
+      // Extracts |column| component.
+      uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++);
+      fuzzerutil::UpdateModuleIdBound(ir_context, column_extract_id);
+      linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpCompositeExtract, scalar_instruction->type_id(),
+          column_extract_id,
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {matrix_extract_id}},
+               {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
+
+      // Multiplies the |column| component with the |scalar|.
+      float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++);
+      fuzzerutil::UpdateModuleIdBound(ir_context, float_multiplication_ids[j]);
+      linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpFMul, scalar_instruction->type_id(),
+          float_multiplication_ids[j],
+          opt::Instruction::OperandList(
+              {{SPV_OPERAND_TYPE_ID, {column_extract_id}},
+               {SPV_OPERAND_TYPE_ID, {scalar_instruction->result_id()}}})));
+    }
+
+    // Constructs a new column multiplied by |scalar|.
+    opt::Instruction::OperandList composite_construct_in_operands;
+    for (uint32_t& float_multiplication_id : float_multiplication_ids) {
+      composite_construct_in_operands.push_back(
+          {SPV_OPERAND_TYPE_ID, {float_multiplication_id}});
+    }
+    composite_construct_ids[i] = message_.fresh_ids(fresh_id_index++);
+    fuzzerutil::UpdateModuleIdBound(ir_context, composite_construct_ids[i]);
+    linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
+        ir_context, SpvOpCompositeConstruct,
+        ir_context->get_type_mgr()->GetId(matrix_column_type),
+        composite_construct_ids[i], composite_construct_in_operands));
+  }
+
+  // The OpMatrixTimesScalar instruction is changed to an OpCompositeConstruct
+  // instruction.
+  linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
+  linear_algebra_instruction->SetInOperand(0, {composite_construct_ids[0]});
+  linear_algebra_instruction->SetInOperand(1, {composite_construct_ids[1]});
+  for (uint32_t i = 2; i < composite_construct_ids.size(); i++) {
+    linear_algebra_instruction->AddOperand(
+        {SPV_OPERAND_TYPE_ID, {composite_construct_ids[i]}});
+  }
+}
+
 void TransformationReplaceLinearAlgebraInstruction::ReplaceOpDot(
     opt::IRContext* ir_context,
     opt::Instruction* linear_algebra_instruction) const {

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

@@ -56,6 +56,10 @@ class TransformationReplaceLinearAlgebraInstruction : public Transformation {
   void ReplaceOpVectorTimesScalar(opt::IRContext* ir_context,
                                   opt::Instruction* instruction) const;
 
+  // Replaces an OpMatrixTimesScalar instruction.
+  void ReplaceOpMatrixTimesScalar(opt::IRContext* ir_context,
+                                  opt::Instruction* instruction) const;
+
   // Replaces an OpDot instruction.
   void ReplaceOpDot(opt::IRContext* ir_context,
                     opt::Instruction* instruction) const;

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

@@ -0,0 +1,104 @@
+// 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_swap_conditional_branch_operands.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationSwapConditionalBranchOperands::
+    TransformationSwapConditionalBranchOperands(
+        const spvtools::fuzz::protobufs::
+            TransformationSwapConditionalBranchOperands& message)
+    : message_(message) {}
+
+TransformationSwapConditionalBranchOperands::
+    TransformationSwapConditionalBranchOperands(
+        const protobufs::InstructionDescriptor& instruction_descriptor,
+        uint32_t fresh_id) {
+  *message_.mutable_instruction_descriptor() = instruction_descriptor;
+  message_.set_fresh_id(fresh_id);
+}
+
+bool TransformationSwapConditionalBranchOperands::IsApplicable(
+    opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+  const auto* inst =
+      FindInstruction(message_.instruction_descriptor(), ir_context);
+  return fuzzerutil::IsFreshId(ir_context, message_.fresh_id()) && inst &&
+         inst->opcode() == SpvOpBranchConditional;
+}
+
+void TransformationSwapConditionalBranchOperands::Apply(
+    opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+  auto* branch_inst =
+      FindInstruction(message_.instruction_descriptor(), ir_context);
+  assert(branch_inst);
+
+  auto* block = ir_context->get_instr_block(branch_inst);
+  assert(block);
+
+  // Compute the last instruction in the |block| that allows us to insert
+  // OpLogicalNot above it.
+  auto iter = fuzzerutil::GetIteratorForInstruction(block, branch_inst);
+  if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter)) {
+    // There might be a merge instruction before OpBranchConditional.
+    --iter;
+  }
+
+  assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter) &&
+         "We should now be able to insert SpvOpLogicalNot before |iter|");
+
+  // Get the instruction whose result is used as a condition for
+  // OpBranchConditional.
+  const auto* condition_inst = ir_context->get_def_use_mgr()->GetDef(
+      branch_inst->GetSingleWordInOperand(0));
+  assert(condition_inst);
+
+  // We are swapping the labels in OpBranchConditional. This means that we must
+  // invert the guard as well. We are using OpLogicalNot for that purpose here.
+  iter.InsertBefore(MakeUnique<opt::Instruction>(
+      ir_context, SpvOpLogicalNot, condition_inst->type_id(),
+      message_.fresh_id(),
+      opt::Instruction::OperandList{
+          {SPV_OPERAND_TYPE_ID, {condition_inst->result_id()}}}));
+
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+
+  // Update OpBranchConditional condition operand.
+  branch_inst->GetInOperand(0).words[0] = message_.fresh_id();
+
+  // Swap label operands.
+  std::swap(branch_inst->GetInOperand(1), branch_inst->GetInOperand(2));
+
+  // Additionally, swap branch weights if present.
+  if (branch_inst->NumInOperands() > 3) {
+    std::swap(branch_inst->GetInOperand(3), branch_inst->GetInOperand(4));
+  }
+
+  // Make sure the changes are analyzed.
+  ir_context->InvalidateAnalysesExceptFor(
+      opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation
+TransformationSwapConditionalBranchOperands::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_swap_conditional_branch_operands() = message_;
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools

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

@@ -0,0 +1,58 @@
+// 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_SWAP_CONDITIONAL_BRANCH_OPERANDS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_SWAP_CONDITIONAL_BRANCH_OPERANDS_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 TransformationSwapConditionalBranchOperands : public Transformation {
+ public:
+  explicit TransformationSwapConditionalBranchOperands(
+      const protobufs::TransformationSwapConditionalBranchOperands& message);
+
+  TransformationSwapConditionalBranchOperands(
+      const protobufs::InstructionDescriptor& instruction_descriptor,
+      uint32_t fresh_id);
+
+  // - |message_.instruction_descriptor| must be a valid descriptor of some
+  //   OpBranchConditional instruction in the module.
+  // - |message_.fresh_id| must be a fresh id.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Inserts |%fresh_id = OpLogicalNot %bool_type_id %cond_id| before
+  // |OpBranchConditional %cond_id %branch_a %branch_b [%weight_a %weight_b]|.
+  // Replaces %cond_id with %fresh_id and swaps %branch_* and %weight_*
+  // operands.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  protobufs::Transformation ToMessage() const override;
+
+ private:
+  protobufs::TransformationSwapConditionalBranchOperands message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_SWAP_CONDITIONAL_BRANCH_OPERANDS_H_

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

@@ -153,6 +153,7 @@ void FriendlyNameMapper::SaveBuiltInName(uint32_t target_id,
     CASE(SubgroupLocalInvocationId)
     GLCASE(VertexIndex)
     GLCASE(InstanceIndex)
+    GLCASE(BaseInstance)
     CASE(SubgroupEqMaskKHR)
     CASE(SubgroupGeMaskKHR)
     CASE(SubgroupGtMaskKHR)

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

@@ -24,7 +24,18 @@ static const uint32_t kOpLineOperandLineIndex = 1;
 static const uint32_t kLineOperandIndexDebugFunction = 7;
 static const uint32_t kLineOperandIndexDebugLexicalBlock = 5;
 static const uint32_t kDebugFunctionOperandFunctionIndex = 13;
+static const uint32_t kDebugFunctionOperandParentIndex = 9;
+static const uint32_t kDebugTypeCompositeOperandParentIndex = 9;
+static const uint32_t kDebugLexicalBlockOperandParentIndex = 7;
 static const uint32_t kDebugInlinedAtOperandInlinedIndex = 6;
+static const uint32_t kDebugExpressOperandOperationIndex = 4;
+static const uint32_t kDebugDeclareOperandLocalVariableIndex = 4;
+static const uint32_t kDebugDeclareOperandVariableIndex = 5;
+static const uint32_t kDebugValueOperandLocalVariableIndex = 4;
+static const uint32_t kDebugValueOperandExpressionIndex = 6;
+static const uint32_t kDebugOperationOperandOperationIndex = 4;
+static const uint32_t kOpVariableOperandStorageClassIndex = 2;
+static const uint32_t kDebugLocalVariableOperandParentIndex = 9;
 
 namespace spvtools {
 namespace opt {
@@ -36,7 +47,8 @@ void SetInlinedOperand(Instruction* dbg_inlined_at, uint32_t inlined_operand) {
   assert(dbg_inlined_at->GetOpenCL100DebugOpcode() ==
          OpenCLDebugInfo100DebugInlinedAt);
   if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex) {
-    dbg_inlined_at->AddOperand({SPV_OPERAND_TYPE_RESULT_ID, {inlined_operand}});
+    dbg_inlined_at->AddOperand(
+        {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inlined_operand}});
   } else {
     dbg_inlined_at->SetOperand(kDebugInlinedAtOperandInlinedIndex,
                                {inlined_operand});
@@ -53,6 +65,12 @@ uint32_t GetInlinedOperand(Instruction* dbg_inlined_at) {
       kDebugInlinedAtOperandInlinedIndex);
 }
 
+bool IsEmptyDebugExpression(Instruction* instr) {
+  return instr->GetOpenCL100DebugOpcode() ==
+             OpenCLDebugInfo100DebugExpression &&
+         instr->NumOperands() == kDebugExpressOperandOperationIndex;
+}
+
 }  // namespace
 
 DebugInfoManager::DebugInfoManager(IRContext* c) : context_(c) {
@@ -83,6 +101,20 @@ void DebugInfoManager::RegisterDbgFunction(Instruction* inst) {
   fn_id_to_dbg_fn_[fn_id] = inst;
 }
 
+void DebugInfoManager::RegisterDbgDeclare(uint32_t var_id,
+                                          Instruction* dbg_declare) {
+  assert(dbg_declare->GetOpenCL100DebugOpcode() ==
+             OpenCLDebugInfo100DebugDeclare ||
+         dbg_declare->GetOpenCL100DebugOpcode() ==
+             OpenCLDebugInfo100DebugValue);
+  auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_id);
+  if (dbg_decl_itr == var_id_to_dbg_decl_.end()) {
+    var_id_to_dbg_decl_[var_id] = {dbg_declare};
+  } else {
+    dbg_decl_itr->second.insert(dbg_declare);
+  }
+}
+
 uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
                                                 const DebugScope& scope) {
   if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() ==
@@ -139,10 +171,12 @@ uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
   // |scope| already has DebugInlinedAt. We put the existing DebugInlinedAt
   // into the Inlined operand of this new DebugInlinedAt.
   if (scope.GetInlinedAt() != kNoInlinedAt) {
-    inlined_at->AddOperand({spv_operand_type_t::SPV_OPERAND_TYPE_RESULT_ID,
-                            {scope.GetInlinedAt()}});
+    inlined_at->AddOperand(
+        {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {scope.GetInlinedAt()}});
   }
   RegisterDbgInst(inlined_at.get());
+  if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+    context()->get_def_use_mgr()->AnalyzeInstDefUse(inlined_at.get());
   context()->module()->AddExtInstDebugInfo(std::move(inlined_at));
   return result_id;
 }
@@ -218,11 +252,11 @@ Instruction* DebugInfoManager::GetDebugInfoNone() {
       context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
       result_id,
       {
-          {SPV_OPERAND_TYPE_RESULT_ID,
+          {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
            {context()
                 ->get_feature_mgr()
                 ->GetExtInstImportId_OpenCL100DebugInfo()}},
-          {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+          {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
            {static_cast<uint32_t>(OpenCLDebugInfo100DebugInfoNone)}},
       }));
 
@@ -232,9 +266,38 @@ Instruction* DebugInfoManager::GetDebugInfoNone() {
           std::move(dbg_info_none_inst));
 
   RegisterDbgInst(debug_info_none_inst_);
+  if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+    context()->get_def_use_mgr()->AnalyzeInstDefUse(debug_info_none_inst_);
   return debug_info_none_inst_;
 }
 
+Instruction* DebugInfoManager::GetEmptyDebugExpression() {
+  if (empty_debug_expr_inst_ != nullptr) return empty_debug_expr_inst_;
+
+  uint32_t result_id = context()->TakeNextId();
+  std::unique_ptr<Instruction> empty_debug_expr(new Instruction(
+      context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
+      result_id,
+      {
+          {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+           {context()
+                ->get_feature_mgr()
+                ->GetExtInstImportId_OpenCL100DebugInfo()}},
+          {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+           {static_cast<uint32_t>(OpenCLDebugInfo100DebugExpression)}},
+      }));
+
+  // Add to the front of |ext_inst_debuginfo_|.
+  empty_debug_expr_inst_ =
+      context()->module()->ext_inst_debuginfo_begin()->InsertBefore(
+          std::move(empty_debug_expr));
+
+  RegisterDbgInst(empty_debug_expr_inst_);
+  if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+    context()->get_def_use_mgr()->AnalyzeInstDefUse(empty_debug_expr_inst_);
+  return empty_debug_expr_inst_;
+}
+
 Instruction* DebugInfoManager::GetDebugInlinedAt(uint32_t dbg_inlined_at_id) {
   auto* inlined_at = GetDbgInst(dbg_inlined_at_id);
   if (inlined_at == nullptr) return nullptr;
@@ -252,12 +315,162 @@ Instruction* DebugInfoManager::CloneDebugInlinedAt(uint32_t clone_inlined_at_id,
   std::unique_ptr<Instruction> new_inlined_at(inlined_at->Clone(context()));
   new_inlined_at->SetResultId(context()->TakeNextId());
   RegisterDbgInst(new_inlined_at.get());
+  if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+    context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inlined_at.get());
   if (insert_before != nullptr)
     return insert_before->InsertBefore(std::move(new_inlined_at));
   return context()->module()->ext_inst_debuginfo_end()->InsertBefore(
       std::move(new_inlined_at));
 }
 
+uint32_t DebugInfoManager::GetParentScope(uint32_t child_scope) {
+  auto dbg_scope_itr = id_to_dbg_inst_.find(child_scope);
+  assert(dbg_scope_itr != id_to_dbg_inst_.end());
+  OpenCLDebugInfo100Instructions debug_opcode =
+      dbg_scope_itr->second->GetOpenCL100DebugOpcode();
+  uint32_t parent_scope = kNoDebugScope;
+  switch (debug_opcode) {
+    case OpenCLDebugInfo100DebugFunction:
+      parent_scope = dbg_scope_itr->second->GetSingleWordOperand(
+          kDebugFunctionOperandParentIndex);
+      break;
+    case OpenCLDebugInfo100DebugLexicalBlock:
+      parent_scope = dbg_scope_itr->second->GetSingleWordOperand(
+          kDebugLexicalBlockOperandParentIndex);
+      break;
+    case OpenCLDebugInfo100DebugTypeComposite:
+      parent_scope = dbg_scope_itr->second->GetSingleWordOperand(
+          kDebugTypeCompositeOperandParentIndex);
+      break;
+    case OpenCLDebugInfo100DebugCompilationUnit:
+      // DebugCompilationUnit does not have a parent scope.
+      break;
+    default:
+      assert(false &&
+             "Unreachable. A debug scope instruction must be "
+             "DebugFunction, DebugTypeComposite, DebugLexicalBlock, "
+             "or DebugCompilationUnit.");
+      break;
+  }
+  return parent_scope;
+}
+
+bool DebugInfoManager::IsAncestorOfScope(uint32_t scope, uint32_t ancestor) {
+  uint32_t ancestor_scope_itr = scope;
+  while (ancestor_scope_itr != kNoDebugScope) {
+    if (ancestor == ancestor_scope_itr) return true;
+    ancestor_scope_itr = GetParentScope(ancestor_scope_itr);
+  }
+  return false;
+}
+
+bool DebugInfoManager::IsDeclareVisibleToInstr(Instruction* dbg_declare,
+                                               uint32_t instr_scope_id) {
+  if (instr_scope_id == kNoDebugScope) return false;
+
+  uint32_t dbg_local_var_id =
+      dbg_declare->GetSingleWordOperand(kDebugDeclareOperandLocalVariableIndex);
+  auto dbg_local_var_itr = id_to_dbg_inst_.find(dbg_local_var_id);
+  assert(dbg_local_var_itr != id_to_dbg_inst_.end());
+  uint32_t decl_scope_id = dbg_local_var_itr->second->GetSingleWordOperand(
+      kDebugLocalVariableOperandParentIndex);
+
+  // If the scope of DebugDeclare is an ancestor scope of the instruction's
+  // scope, the local variable is visible to the instruction.
+  return IsAncestorOfScope(instr_scope_id, decl_scope_id);
+}
+
+void DebugInfoManager::AddDebugValue(Instruction* scope_and_line,
+                                     uint32_t variable_id, uint32_t value_id,
+                                     Instruction* insert_pos) {
+  auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
+  if (dbg_decl_itr == var_id_to_dbg_decl_.end()) return;
+
+  uint32_t instr_scope_id = scope_and_line->GetDebugScope().GetLexicalScope();
+  for (auto* dbg_decl_or_val : dbg_decl_itr->second) {
+    if (!IsDeclareVisibleToInstr(dbg_decl_or_val, instr_scope_id)) continue;
+
+    uint32_t result_id = context()->TakeNextId();
+    std::unique_ptr<Instruction> new_dbg_value(new Instruction(
+        context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
+        result_id,
+        {
+            {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+             {context()
+                  ->get_feature_mgr()
+                  ->GetExtInstImportId_OpenCL100DebugInfo()}},
+            {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+             {static_cast<uint32_t>(OpenCLDebugInfo100DebugValue)}},
+            {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+             {dbg_decl_or_val->GetSingleWordOperand(
+                 kDebugValueOperandLocalVariableIndex)}},
+            {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {value_id}},
+            {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+             {GetEmptyDebugExpression()->result_id()}},
+        }));
+
+    if (dbg_decl_or_val->NumOperands() >
+        kDebugValueOperandExpressionIndex + 1) {
+      assert(dbg_decl_or_val->GetOpenCL100DebugOpcode() ==
+             OpenCLDebugInfo100DebugValue);
+      for (uint32_t i = kDebugValueOperandExpressionIndex + 1;
+           i < dbg_decl_or_val->NumOperands(); ++i) {
+        new_dbg_value->AddOperand({spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+                                   {dbg_decl_or_val->GetSingleWordOperand(i)}});
+      }
+    }
+
+    // Avoid inserting the new DebugValue between OpPhi or OpVariable
+    // instructions.
+    Instruction* insert_before = insert_pos->NextNode();
+    while (insert_before->opcode() == SpvOpPhi ||
+           insert_before->opcode() == SpvOpVariable) {
+      insert_before = insert_before->NextNode();
+    }
+
+    Instruction* added_dbg_value =
+        insert_before->InsertBefore(std::move(new_dbg_value));
+    added_dbg_value->UpdateDebugInfo(scope_and_line);
+    AnalyzeDebugInst(added_dbg_value);
+    if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+      context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_value);
+  }
+}
+
+uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare(
+    Instruction* inst) {
+  if (inst->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugValue) return 0;
+
+  auto* expr =
+      GetDbgInst(inst->GetSingleWordOperand(kDebugValueOperandExpressionIndex));
+  if (expr == nullptr) return 0;
+  if (expr->NumOperands() != kDebugExpressOperandOperationIndex + 1) return 0;
+
+  auto* operation = GetDbgInst(
+      expr->GetSingleWordOperand(kDebugExpressOperandOperationIndex));
+  if (operation == nullptr) return 0;
+  if (operation->GetSingleWordOperand(kDebugOperationOperandOperationIndex) !=
+      OpenCLDebugInfo100Deref) {
+    return 0;
+  }
+
+  uint32_t var_id =
+      inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
+  if (!context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) {
+    assert(false &&
+           "Checking a DebugValue can be used for declare needs DefUseManager");
+    return 0;
+  }
+
+  auto* var = context()->get_def_use_mgr()->GetDef(var_id);
+  if (var->opcode() == SpvOpVariable &&
+      SpvStorageClass(var->GetSingleWordOperand(
+          kOpVariableOperandStorageClassIndex)) == SpvStorageClassFunction) {
+    return var_id;
+  }
+  return 0;
+}
+
 void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
   if (dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100InstructionsMax)
     return;
@@ -275,12 +488,37 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
       dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) {
     debug_info_none_inst_ = dbg_inst;
   }
+
+  if (empty_debug_expr_inst_ == nullptr && IsEmptyDebugExpression(dbg_inst)) {
+    empty_debug_expr_inst_ = dbg_inst;
+  }
+
+  if (dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) {
+    uint32_t var_id =
+        dbg_inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
+    RegisterDbgDeclare(var_id, dbg_inst);
+  }
+
+  if (uint32_t var_id = GetVariableIdOfDebugValueUsedForDeclare(dbg_inst)) {
+    RegisterDbgDeclare(var_id, dbg_inst);
+  }
 }
 
 void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
   debug_info_none_inst_ = nullptr;
+  empty_debug_expr_inst_ = nullptr;
   module.ForEachInst([this](Instruction* cpi) { AnalyzeDebugInst(cpi); });
 
+  // Move |empty_debug_expr_inst_| to the beginning of the debug instruction
+  // list.
+  if (empty_debug_expr_inst_ != nullptr &&
+      empty_debug_expr_inst_->PreviousNode() != nullptr &&
+      empty_debug_expr_inst_->PreviousNode()->GetOpenCL100DebugOpcode() !=
+          OpenCLDebugInfo100InstructionsMax) {
+    empty_debug_expr_inst_->InsertBefore(
+        &*context()->module()->ext_inst_debuginfo_begin());
+  }
+
   // Move |debug_info_none_inst_| to the beginning of the debug instruction
   // list.
   if (debug_info_none_inst_ != nullptr &&
@@ -292,6 +530,55 @@ void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
   }
 }
 
+void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
+  if (instr == nullptr ||
+      instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100InstructionsMax) {
+    return;
+  }
+
+  id_to_dbg_inst_.erase(instr->result_id());
+
+  if (instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) {
+    auto fn_id =
+        instr->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex);
+    fn_id_to_dbg_fn_.erase(fn_id);
+  }
+
+  if (instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare ||
+      instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) {
+    auto var_or_value_id =
+        instr->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
+    auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_or_value_id);
+    if (dbg_decl_itr != var_id_to_dbg_decl_.end()) {
+      dbg_decl_itr->second.erase(instr);
+    }
+  }
+
+  if (debug_info_none_inst_ == instr) {
+    debug_info_none_inst_ = nullptr;
+    for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin();
+         dbg_instr_itr != context()->module()->ext_inst_debuginfo_end();
+         ++dbg_instr_itr) {
+      if (instr != &*dbg_instr_itr &&
+          dbg_instr_itr->GetOpenCL100DebugOpcode() ==
+              OpenCLDebugInfo100DebugInfoNone) {
+        debug_info_none_inst_ = &*dbg_instr_itr;
+      }
+    }
+  }
+
+  if (empty_debug_expr_inst_ == instr) {
+    empty_debug_expr_inst_ = nullptr;
+    for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin();
+         dbg_instr_itr != context()->module()->ext_inst_debuginfo_end();
+         ++dbg_instr_itr) {
+      if (instr != &*dbg_instr_itr && IsEmptyDebugExpression(&*dbg_instr_itr)) {
+        empty_debug_expr_inst_ = &*dbg_instr_itr;
+      }
+    }
+  }
+}
+
 }  // namespace analysis
 }  // namespace opt
 }  // namespace spvtools

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

@@ -16,6 +16,7 @@
 #define SOURCE_OPT_DEBUG_INFO_MANAGER_H_
 
 #include <unordered_map>
+#include <unordered_set>
 
 #include "source/opt/instruction.h"
 #include "source/opt/module.h"
@@ -128,6 +129,15 @@ class DebugInfoManager {
   uint32_t BuildDebugInlinedAtChain(uint32_t callee_inlined_at,
                                     DebugInlinedAtContext* inlined_at_ctx);
 
+  // Generates a DebugValue instruction with value |value_id| for every local
+  // variable that is in the scope of |scope_and_line| and whose memory is
+  // |variable_id| and inserts it after the instruction |insert_pos|.
+  void AddDebugValue(Instruction* scope_and_line, uint32_t variable_id,
+                     uint32_t value_id, Instruction* insert_pos);
+
+  // Erases |instr| from data structures of this class.
+  void ClearDebugInfo(Instruction* instr);
+
  private:
   IRContext* context() { return context_; }
 
@@ -147,6 +157,31 @@ class DebugInfoManager {
   // in |inst| must not already be registered.
   void RegisterDbgFunction(Instruction* inst);
 
+  // Register the DebugDeclare or DebugValue with Deref operation
+  // |dbg_declare| into |var_id_to_dbg_decl_| using OpVariable id
+  // |var_id| as a key.
+  void RegisterDbgDeclare(uint32_t var_id, Instruction* dbg_declare);
+
+  // Returns a DebugExpression instruction without Operation operands.
+  Instruction* GetEmptyDebugExpression();
+
+  // Returns the id of Value operand if |inst| is DebugValue who has Deref
+  // operation and its Value operand is a result id of OpVariable with
+  // Function storage class. Otherwise, returns 0.
+  uint32_t GetVariableIdOfDebugValueUsedForDeclare(Instruction* inst);
+
+  // Returns true if a scope |ancestor| is |scope| or an ancestor scope
+  // of |scope|.
+  bool IsAncestorOfScope(uint32_t scope, uint32_t ancestor);
+
+  // Returns true if the declaration of a local variable |dbg_declare|
+  // is visible in the scope of an instruction |instr_scope_id|.
+  bool IsDeclareVisibleToInstr(Instruction* dbg_declare,
+                               uint32_t instr_scope_id);
+
+  // Returns the parent scope of the scope |child_scope|.
+  uint32_t GetParentScope(uint32_t child_scope);
+
   IRContext* context_;
 
   // Mapping from ids of OpenCL.DebugInfo.100 extension instructions
@@ -157,9 +192,19 @@ class DebugInfoManager {
   // operand is the function.
   std::unordered_map<uint32_t, Instruction*> fn_id_to_dbg_fn_;
 
+  // Mapping from variable or value ids to DebugDeclare or DebugValue
+  // instructions whose operand is the variable or value.
+  std::unordered_map<uint32_t, std::unordered_set<Instruction*>>
+      var_id_to_dbg_decl_;
+
   // DebugInfoNone instruction. We need only a single DebugInfoNone.
   // To reuse the existing one, we keep it using this member variable.
   Instruction* debug_info_none_inst_;
+
+  // DebugExpression instruction without Operation operands. We need only
+  // a single DebugExpression without Operation operands. To reuse the
+  // existing one, we keep it using this member variable.
+  Instruction* empty_debug_expr_inst_;
 };
 
 }  // namespace analysis

+ 206 - 31
3rdparty/spirv-tools/source/opt/desc_sroa.cpp

@@ -56,7 +56,23 @@ bool DescriptorScalarReplacement::IsCandidate(Instruction* var) {
   uint32_t var_type_id = ptr_type_inst->GetSingleWordInOperand(1);
   Instruction* var_type_inst =
       context()->get_def_use_mgr()->GetDef(var_type_id);
-  if (var_type_inst->opcode() != SpvOpTypeArray) {
+  if (var_type_inst->opcode() != SpvOpTypeArray &&
+      var_type_inst->opcode() != SpvOpTypeStruct) {
+    return false;
+  }
+
+  // All structures with descriptor assignments must be replaced by variables,
+  // one for each of their members - with the exceptions of buffers.
+  // Buffers are represented as structures, but we shouldn't replace a buffer
+  // with its elements. All buffers have offset decorations for members of their
+  // structure types.
+  bool has_offset_decoration = false;
+  context()->get_decoration_mgr()->ForEachDecoration(
+      var_type_inst->result_id(), SpvDecorationOffset,
+      [&has_offset_decoration](const Instruction&) {
+        has_offset_decoration = true;
+      });
+  if (has_offset_decoration) {
     return false;
   }
 
@@ -84,9 +100,11 @@ bool DescriptorScalarReplacement::IsCandidate(Instruction* var) {
 }
 
 bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) {
-  std::vector<Instruction*> work_list;
+  std::vector<Instruction*> access_chain_work_list;
+  std::vector<Instruction*> load_work_list;
   bool failed = !get_def_use_mgr()->WhileEachUser(
-      var->result_id(), [this, &work_list](Instruction* use) {
+      var->result_id(),
+      [this, &access_chain_work_list, &load_work_list](Instruction* use) {
         if (use->opcode() == SpvOpName) {
           return true;
         }
@@ -98,7 +116,10 @@ bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) {
         switch (use->opcode()) {
           case SpvOpAccessChain:
           case SpvOpInBoundsAccessChain:
-            work_list.push_back(use);
+            access_chain_work_list.push_back(use);
+            return true;
+          case SpvOpLoad:
+            load_work_list.push_back(use);
             return true;
           default:
             context()->EmitErrorMessage(
@@ -112,11 +133,16 @@ bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) {
     return false;
   }
 
-  for (Instruction* use : work_list) {
+  for (Instruction* use : access_chain_work_list) {
     if (!ReplaceAccessChain(var, use)) {
       return false;
     }
   }
+  for (Instruction* use : load_work_list) {
+    if (!ReplaceLoadedValue(var, use)) {
+      return false;
+    }
+  }
   return true;
 }
 
@@ -177,21 +203,36 @@ uint32_t DescriptorScalarReplacement::GetReplacementVariable(Instruction* var,
     uint32_t ptr_type_id = var->type_id();
     Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id);
     assert(ptr_type_inst->opcode() == SpvOpTypePointer &&
-           "Variable should be a pointer to an array.");
-    uint32_t arr_type_id = ptr_type_inst->GetSingleWordInOperand(1);
-    Instruction* arr_type_inst = get_def_use_mgr()->GetDef(arr_type_id);
-    assert(arr_type_inst->opcode() == SpvOpTypeArray &&
-           "Variable should be a pointer to an array.");
-
-    uint32_t array_len_id = arr_type_inst->GetSingleWordInOperand(1);
-    const analysis::Constant* array_len_const =
-        context()->get_constant_mgr()->FindDeclaredConstant(array_len_id);
-    assert(array_len_const != nullptr && "Array length must be a constant.");
-    uint32_t array_len = array_len_const->GetU32();
-
-    replacement_vars = replacement_variables_
-                           .insert({var, std::vector<uint32_t>(array_len, 0)})
-                           .first;
+           "Variable should be a pointer to an array or structure.");
+    uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1);
+    Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(pointee_type_id);
+    const bool is_array = pointee_type_inst->opcode() == SpvOpTypeArray;
+    const bool is_struct = pointee_type_inst->opcode() == SpvOpTypeStruct;
+    assert((is_array || is_struct) &&
+           "Variable should be a pointer to an array or structure.");
+
+    // For arrays, each array element should be replaced with a new replacement
+    // variable
+    if (is_array) {
+      uint32_t array_len_id = pointee_type_inst->GetSingleWordInOperand(1);
+      const analysis::Constant* array_len_const =
+          context()->get_constant_mgr()->FindDeclaredConstant(array_len_id);
+      assert(array_len_const != nullptr && "Array length must be a constant.");
+      uint32_t array_len = array_len_const->GetU32();
+
+      replacement_vars = replacement_variables_
+                             .insert({var, std::vector<uint32_t>(array_len, 0)})
+                             .first;
+    }
+    // For structures, each member should be replaced with a new replacement
+    // variable
+    if (is_struct) {
+      const uint32_t num_members = pointee_type_inst->NumInOperands();
+      replacement_vars =
+          replacement_variables_
+              .insert({var, std::vector<uint32_t>(num_members, 0)})
+              .first;
+    }
   }
 
   if (replacement_vars->second[idx] == 0) {
@@ -212,12 +253,17 @@ uint32_t DescriptorScalarReplacement::CreateReplacementVariable(
   uint32_t ptr_type_id = var->type_id();
   Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id);
   assert(ptr_type_inst->opcode() == SpvOpTypePointer &&
-         "Variable should be a pointer to an array.");
-  uint32_t arr_type_id = ptr_type_inst->GetSingleWordInOperand(1);
-  Instruction* arr_type_inst = get_def_use_mgr()->GetDef(arr_type_id);
-  assert(arr_type_inst->opcode() == SpvOpTypeArray &&
-         "Variable should be a pointer to an array.");
-  uint32_t element_type_id = arr_type_inst->GetSingleWordInOperand(0);
+         "Variable should be a pointer to an array or structure.");
+  uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1);
+  Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(pointee_type_id);
+  const bool is_array = pointee_type_inst->opcode() == SpvOpTypeArray;
+  const bool is_struct = pointee_type_inst->opcode() == SpvOpTypeStruct;
+  assert((is_array || is_struct) &&
+         "Variable should be a pointer to an array or structure.");
+
+  uint32_t element_type_id =
+      is_array ? pointee_type_inst->GetSingleWordInOperand(0)
+               : pointee_type_inst->GetSingleWordInOperand(idx);
 
   uint32_t ptr_element_type_id = context()->get_type_mgr()->FindPointerToType(
       element_type_id, storage_class);
@@ -242,19 +288,42 @@ uint32_t DescriptorScalarReplacement::CreateReplacementVariable(
 
     uint32_t decoration = new_decoration->GetSingleWordInOperand(1u);
     if (decoration == SpvDecorationBinding) {
-      uint32_t new_binding = new_decoration->GetSingleWordInOperand(2) + idx;
+      uint32_t new_binding = new_decoration->GetSingleWordInOperand(2);
+      if (is_array) {
+        new_binding += idx * GetNumBindingsUsedByType(ptr_element_type_id);
+      }
+      if (is_struct) {
+        // The binding offset that should be added is the sum of binding numbers
+        // used by previous members of the current struct.
+        for (uint32_t i = 0; i < idx; ++i) {
+          new_binding += GetNumBindingsUsedByType(
+              pointee_type_inst->GetSingleWordInOperand(i));
+        }
+      }
       new_decoration->SetInOperand(2, {new_binding});
     }
     context()->AddAnnotationInst(std::move(new_decoration));
   }
 
   // Create a new OpName for the replacement variable.
+  std::vector<std::unique_ptr<Instruction>> names_to_add;
   for (auto p : context()->GetNames(var->result_id())) {
     Instruction* name_inst = p.second;
     std::string name_str = utils::MakeString(name_inst->GetOperand(1).words);
-    name_str += "[";
-    name_str += utils::ToString(idx);
-    name_str += "]";
+    if (is_array) {
+      name_str += "[" + utils::ToString(idx) + "]";
+    }
+    if (is_struct) {
+      Instruction* member_name_inst =
+          context()->GetMemberName(pointee_type_inst->result_id(), idx);
+      name_str += ".";
+      if (member_name_inst)
+        name_str += utils::MakeString(member_name_inst->GetOperand(2).words);
+      else
+        // In case the member does not have a name assigned to it, use the
+        // member index.
+        name_str += utils::ToString(idx);
+    }
 
     std::unique_ptr<Instruction> new_name(new Instruction(
         context(), SpvOpName, 0, 0,
@@ -262,12 +331,118 @@ uint32_t DescriptorScalarReplacement::CreateReplacementVariable(
             {SPV_OPERAND_TYPE_ID, {id}},
             {SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}}));
     Instruction* new_name_inst = new_name.get();
-    context()->AddDebug2Inst(std::move(new_name));
     get_def_use_mgr()->AnalyzeInstDefUse(new_name_inst);
+    names_to_add.push_back(std::move(new_name));
   }
 
+  // We shouldn't add the new names when we are iterating over name ranges
+  // above. We can add all the new names now.
+  for (auto& new_name : names_to_add)
+    context()->AddDebug2Inst(std::move(new_name));
+
   return id;
 }
 
+uint32_t DescriptorScalarReplacement::GetNumBindingsUsedByType(
+    uint32_t type_id) {
+  Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
+
+  // If it's a pointer, look at the underlying type.
+  if (type_inst->opcode() == SpvOpTypePointer) {
+    type_id = type_inst->GetSingleWordInOperand(1);
+    type_inst = get_def_use_mgr()->GetDef(type_id);
+  }
+
+  // Arrays consume N*M binding numbers where N is the array length, and M is
+  // the number of bindings used by each array element.
+  if (type_inst->opcode() == SpvOpTypeArray) {
+    uint32_t element_type_id = type_inst->GetSingleWordInOperand(0);
+    uint32_t length_id = type_inst->GetSingleWordInOperand(1);
+    const analysis::Constant* length_const =
+        context()->get_constant_mgr()->FindDeclaredConstant(length_id);
+    // OpTypeArray's length must always be a constant
+    assert(length_const != nullptr);
+    uint32_t num_elems = length_const->GetU32();
+    return num_elems * GetNumBindingsUsedByType(element_type_id);
+  }
+
+  // The number of bindings consumed by a structure is the sum of the bindings
+  // used by its members.
+  if (type_inst->opcode() == SpvOpTypeStruct) {
+    uint32_t sum = 0;
+    for (uint32_t i = 0; i < type_inst->NumInOperands(); i++)
+      sum += GetNumBindingsUsedByType(type_inst->GetSingleWordInOperand(i));
+    return sum;
+  }
+
+  // All other types are considered to take up 1 binding number.
+  return 1;
+}
+
+bool DescriptorScalarReplacement::ReplaceLoadedValue(Instruction* var,
+                                                     Instruction* value) {
+  // |var| is the global variable that has to be eliminated (OpVariable).
+  // |value| is the OpLoad instruction that has loaded |var|.
+  // The function expects all users of |value| to be OpCompositeExtract
+  // instructions. Otherwise the function returns false with an error message.
+  assert(value->opcode() == SpvOpLoad);
+  assert(value->GetSingleWordInOperand(0) == var->result_id());
+  std::vector<Instruction*> work_list;
+  bool failed = !get_def_use_mgr()->WhileEachUser(
+      value->result_id(), [this, &work_list](Instruction* use) {
+        if (use->opcode() != SpvOpCompositeExtract) {
+          context()->EmitErrorMessage(
+              "Variable cannot be replaced: invalid instruction", use);
+          return false;
+        }
+        work_list.push_back(use);
+        return true;
+      });
+
+  if (failed) {
+    return false;
+  }
+
+  for (Instruction* use : work_list) {
+    if (!ReplaceCompositeExtract(var, use)) {
+      return false;
+    }
+  }
+
+  // All usages of the loaded value have been killed. We can kill the OpLoad.
+  context()->KillInst(value);
+  return true;
+}
+
+bool DescriptorScalarReplacement::ReplaceCompositeExtract(
+    Instruction* var, Instruction* extract) {
+  assert(extract->opcode() == SpvOpCompositeExtract);
+  // We're currently only supporting extractions of one index at a time. If we
+  // need to, we can handle cases with multiple indexes in the future.
+  if (extract->NumInOperands() != 2) {
+    context()->EmitErrorMessage(
+        "Variable cannot be replaced: invalid instruction", extract);
+    return false;
+  }
+
+  uint32_t replacement_var =
+      GetReplacementVariable(var, extract->GetSingleWordInOperand(1));
+
+  // The result type of the OpLoad is the same as the result type of the
+  // OpCompositeExtract.
+  uint32_t load_id = TakeNextId();
+  std::unique_ptr<Instruction> load(
+      new Instruction(context(), SpvOpLoad, extract->type_id(), load_id,
+                      std::initializer_list<Operand>{
+                          {SPV_OPERAND_TYPE_ID, {replacement_var}}}));
+  Instruction* load_instr = load.get();
+  get_def_use_mgr()->AnalyzeInstDefUse(load_instr);
+  context()->set_instr_block(load_instr, context()->get_instr_block(extract));
+  extract->InsertBefore(std::move(load));
+  context()->ReplaceAllUsesWith(extract->result_id(), load_id);
+  context()->KillInst(extract);
+  return true;
+}
+
 }  // namespace opt
 }  // namespace spvtools

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

@@ -61,6 +61,20 @@ class DescriptorScalarReplacement : public Pass {
   // |true| if successful.
   bool ReplaceAccessChain(Instruction* var, Instruction* use);
 
+  // Replaces the given compososite variable |var| loaded by OpLoad |value| with
+  // replacement variables, one for each component that's accessed in the
+  // shader. Assumes that |value| is only used by OpCompositeExtract
+  // instructions, one index at a time. Returns true on success, and false
+  // otherwise.
+  bool ReplaceLoadedValue(Instruction* var, Instruction* value);
+
+  // Replaces the given OpCompositeExtract |extract| and all of its references
+  // with an OpLoad of a replacement variable. |var| is the variable with
+  // composite type whose value is being used by |extract|. Assumes that
+  // |extract| is extracting one index only. Returns true on success, and false
+  // otherwise.
+  bool ReplaceCompositeExtract(Instruction* var, Instruction* extract);
+
   // Returns the id of the variable that will be used to replace the |idx|th
   // element of |var|.  The variable is created if it has not already been
   // created.
@@ -70,6 +84,15 @@ class DescriptorScalarReplacement : public Pass {
   // element of |var|.
   uint32_t CreateReplacementVariable(Instruction* var, uint32_t idx);
 
+  // Returns the number of bindings used by the given |type_id|.
+  // All types are considered to use 1 binding slot, except:
+  // 1- A pointer type consumes as many binding numbers as its pointee.
+  // 2- An array of size N consumes N*M binding numbers, where M is the number
+  // of bindings used by each array element.
+  // 3- The number of bindings consumed by a structure is the sum of the
+  // bindings used by its members.
+  uint32_t GetNumBindingsUsedByType(uint32_t type_id);
+
   // A map from an OpVariable instruction to the set of variables that will be
   // used to replace it. The entry |replacement_variables_[var][i]| is the id of
   // a variable that will be used in the place of the the ith element of the

+ 21 - 6
3rdparty/spirv-tools/source/opt/ir_context.cpp

@@ -97,8 +97,9 @@ void IRContext::InvalidateAnalysesExceptFor(
 }
 
 void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) {
-  // The ConstantManager contains Type pointers. If the TypeManager goes
-  // away, the ConstantManager has to go away.
+  // The ConstantManager and DebugInfoManager contain Type pointers. If the
+  // TypeManager goes away, the ConstantManager and DebugInfoManager have to
+  // go away.
   if (analyses_to_invalidate & kAnalysisTypes) {
     analyses_to_invalidate |= kAnalysisConstants;
     analyses_to_invalidate |= kAnalysisDebugInfo;
@@ -179,6 +180,9 @@ Instruction* IRContext::KillInst(Instruction* inst) {
       decoration_mgr_->RemoveDecoration(inst);
     }
   }
+  if (AreAnalysesValid(kAnalysisDebugInfo)) {
+    get_debug_info_mgr()->ClearDebugInfo(inst);
+  }
   if (type_mgr_ && IsTypeInst(inst->opcode())) {
     type_mgr_->RemoveId(inst->result_id());
   }
@@ -218,6 +222,13 @@ bool IRContext::KillDef(uint32_t id) {
   return false;
 }
 
+void IRContext::KillDebugDeclareInsts(Function* fn) {
+  fn->ForEachInst([this](Instruction* inst) {
+    if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare)
+      KillInst(inst);
+  });
+}
+
 bool IRContext::ReplaceAllUsesWith(uint32_t before, uint32_t after) {
   return ReplaceAllUsesWithPredicate(
       before, after, [](Instruction*, uint32_t) { return true; });
@@ -344,6 +355,9 @@ void IRContext::ForgetUses(Instruction* inst) {
       get_decoration_mgr()->RemoveDecoration(inst);
     }
   }
+  if (AreAnalysesValid(kAnalysisDebugInfo)) {
+    get_debug_info_mgr()->ClearDebugInfo(inst);
+  }
   RemoveFromIdToName(inst);
 }
 
@@ -356,6 +370,9 @@ void IRContext::AnalyzeUses(Instruction* inst) {
       get_decoration_mgr()->AddDecoration(inst);
     }
   }
+  if (AreAnalysesValid(kAnalysisDebugInfo)) {
+    get_debug_info_mgr()->AnalyzeDebugInst(inst);
+  }
   if (id_to_name_ &&
       (inst->opcode() == SpvOpName || inst->opcode() == SpvOpMemberName)) {
     id_to_name_->insert({inst->GetSingleWordInOperand(0), inst});
@@ -394,6 +411,7 @@ void IRContext::KillOperandFromDebugInstructions(Instruction* inst) {
       if (operand.words[0] == id) {
         operand.words[0] =
             get_debug_info_mgr()->GetDebugInfoNone()->result_id();
+        get_def_use_mgr()->AnalyzeInstUse(&*it);
       }
     }
   }
@@ -408,13 +426,10 @@ void IRContext::KillOperandFromDebugInstructions(Instruction* inst) {
       if (operand.words[0] == id) {
         operand.words[0] =
             get_debug_info_mgr()->GetDebugInfoNone()->result_id();
+        get_def_use_mgr()->AnalyzeInstUse(&*it);
       }
     }
   }
-  // Notice that we do not need anythings to do for local variables.
-  // DebugLocalVariable does not have an OpVariable operand. Instead,
-  // DebugDeclare/DebugValue has an OpVariable operand for a local
-  // variable. The function inlining pass handles it properly.
 }
 
 void IRContext::AddCombinatorsForCapability(uint32_t capability) {

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

@@ -357,6 +357,13 @@ class IRContext {
   inline IteratorRange<std::multimap<uint32_t, Instruction*>::iterator>
   GetNames(uint32_t id);
 
+  // Returns an OpMemberName instruction that targets |struct_type_id| at
+  // index |index|. Returns nullptr if no such instruction exists.
+  // While the SPIR-V spec does not prohibit having multiple OpMemberName
+  // instructions for the same structure member, it is hard to imagine a member
+  // having more than one name. This method returns the first one it finds.
+  inline Instruction* GetMemberName(uint32_t struct_type_id, uint32_t index);
+
   // Sets the message consumer to the given |consumer|. |consumer| which will be
   // invoked every time there is a message to be communicated to the outside.
   void SetMessageConsumer(MessageConsumer c) { consumer_ = std::move(c); }
@@ -396,6 +403,9 @@ class IRContext {
   // instruction exists.
   Instruction* KillInst(Instruction* inst);
 
+  // Deletes DebugDeclare instructions in the given function |fn|.
+  void KillDebugDeclareInsts(Function* fn);
+
   // Returns true if all of the given analyses are valid.
   bool AreAnalysesValid(Analysis set) { return (set & valid_analyses_) == set; }
 
@@ -1061,7 +1071,9 @@ void IRContext::AddDebug1Inst(std::unique_ptr<Instruction>&& d) {
 void IRContext::AddDebug2Inst(std::unique_ptr<Instruction>&& d) {
   if (AreAnalysesValid(kAnalysisNameMap)) {
     if (d->opcode() == SpvOpName || d->opcode() == SpvOpMemberName) {
-      id_to_name_->insert({d->result_id(), d.get()});
+      // OpName and OpMemberName do not have result-ids. The target of the
+      // instruction is at InOperand index 0.
+      id_to_name_->insert({d->GetSingleWordInOperand(0), d.get()});
     }
   }
   module()->AddDebug2Inst(std::move(d));
@@ -1135,6 +1147,21 @@ IRContext::GetNames(uint32_t id) {
   return make_range(std::move(result.first), std::move(result.second));
 }
 
+Instruction* IRContext::GetMemberName(uint32_t struct_type_id, uint32_t index) {
+  if (!AreAnalysesValid(kAnalysisNameMap)) {
+    BuildIdToNameMap();
+  }
+  auto result = id_to_name_->equal_range(struct_type_id);
+  for (auto i = result.first; i != result.second; ++i) {
+    auto* name_instr = i->second;
+    if (name_instr->opcode() == SpvOpMemberName &&
+        name_instr->GetSingleWordInOperand(1) == index) {
+      return name_instr;
+    }
+  }
+  return nullptr;
+}
+
 }  // namespace opt
 }  // namespace spvtools
 

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

@@ -20,6 +20,7 @@
 #include <set>
 #include <vector>
 
+#include "OpenCLDebugInfo100.h"
 #include "source/cfa.h"
 #include "source/opt/basic_block.h"
 #include "source/opt/dominator_analysis.h"
@@ -225,6 +226,11 @@ MemPass::MemPass() {}
 
 bool MemPass::HasOnlySupportedRefs(uint32_t varId) {
   return get_def_use_mgr()->WhileEachUser(varId, [this](Instruction* user) {
+    auto dbg_op = user->GetOpenCL100DebugOpcode();
+    if (dbg_op == OpenCLDebugInfo100DebugDeclare ||
+        dbg_op == OpenCLDebugInfo100DebugValue) {
+      return true;
+    }
     SpvOp op = user->opcode();
     if (op != SpvOpStore && op != SpvOpLoad && op != SpvOpName &&
         !IsNonTypeDecorate(op)) {

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

@@ -71,6 +71,10 @@ class Pass {
     return context()->get_def_use_mgr();
   }
 
+  analysis::DebugInfoManager* get_debug_info_mgr() const {
+    return context()->get_debug_info_mgr();
+  }
+
   analysis::DecorationManager* get_decoration_mgr() const {
     return context()->get_decoration_mgr();
   }

+ 15 - 3
3rdparty/spirv-tools/source/opt/ssa_rewrite_pass.cpp

@@ -307,6 +307,7 @@ void SSARewriter::ProcessStore(Instruction* inst, BasicBlock* bb) {
   }
   if (pass_->IsTargetVar(var_id)) {
     WriteVariable(var_id, bb, val_id);
+    pass_->get_debug_info_mgr()->AddDebugValue(inst, var_id, val_id, inst);
 
 #if SSA_REWRITE_DEBUGGING_LEVEL > 1
     std::cerr << "\tFound store '%" << var_id << " = %" << val_id << "': "
@@ -437,6 +438,8 @@ bool SSARewriter::ApplyReplacements() {
 
   // Add Phi instructions from completed Phi candidates.
   std::vector<Instruction*> generated_phis;
+  // Add DebugValue instructions for Phi instructions.
+  std::vector<Instruction*> dbg_values_for_phis;
   for (const PhiCandidate* phi_candidate : phis_to_generate_) {
 #if SSA_REWRITE_DEBUGGING_LEVEL > 2
     std::cerr << "Phi candidate: " << phi_candidate->PrettyPrint(pass_->cfg())
@@ -447,9 +450,10 @@ bool SSARewriter::ApplyReplacements() {
            "Tried to instantiate a Phi instruction from an incomplete Phi "
            "candidate");
 
+    auto* local_var = pass_->get_def_use_mgr()->GetDef(phi_candidate->var_id());
+
     // Build the vector of operands for the new OpPhi instruction.
-    uint32_t type_id = pass_->GetPointeeTypeId(
-        pass_->get_def_use_mgr()->GetDef(phi_candidate->var_id()));
+    uint32_t type_id = pass_->GetPointeeTypeId(local_var);
     std::vector<Operand> phi_operands;
     uint32_t arg_ix = 0;
     std::unordered_map<uint32_t, uint32_t> already_seen;
@@ -479,11 +483,17 @@ bool SSARewriter::ApplyReplacements() {
     pass_->get_def_use_mgr()->AnalyzeInstDef(&*phi_inst);
     pass_->context()->set_instr_block(&*phi_inst, phi_candidate->bb());
     auto insert_it = phi_candidate->bb()->begin();
-    insert_it.InsertBefore(std::move(phi_inst));
+    insert_it = insert_it.InsertBefore(std::move(phi_inst));
     pass_->context()->get_decoration_mgr()->CloneDecorations(
         phi_candidate->var_id(), phi_candidate->result_id(),
         {SpvDecorationRelaxedPrecision});
 
+    // Add DebugValue for the new OpPhi instruction.
+    insert_it->SetDebugScope(local_var->GetDebugScope());
+    pass_->get_debug_info_mgr()->AddDebugValue(
+        &*insert_it, phi_candidate->var_id(), phi_candidate->result_id(),
+        &*insert_it);
+
     modified = true;
   }
 
@@ -604,6 +614,8 @@ Pass::Status SSARewriter::RewriteFunctionIntoSSA(Function* fp) {
             << fp->PrettyPrint(0) << "\n";
 #endif
 
+  if (modified) pass_->context()->KillDebugDeclareInsts(fp);
+
   return modified ? Pass::Status::SuccessWithChange
                   : Pass::Status::SuccessWithoutChange;
 }

+ 1 - 6
3rdparty/spirv-tools/source/opt/ssa_rewrite_pass.h

@@ -39,8 +39,7 @@ namespace opt {
 // (https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6)
 class SSARewriter {
  public:
-  SSARewriter(MemPass* pass)
-      : pass_(pass), first_phi_id_(pass_->get_module()->IdBound()) {}
+  SSARewriter(MemPass* pass) : pass_(pass) {}
 
   // Rewrites SSA-target variables in function |fp| into SSA.  This is the
   // entry point for the SSA rewrite algorithm.  SSA-target variables are
@@ -287,10 +286,6 @@ class SSARewriter {
 
   // Memory pass requesting the SSA rewriter.
   MemPass* pass_;
-
-  // ID of the first Phi created by the SSA rewriter.  During rewriting, any
-  // ID bigger than this corresponds to a Phi candidate.
-  uint32_t first_phi_id_;
 };
 
 class SSARewritePass : public MemPass {

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

@@ -1540,6 +1540,21 @@ spv_result_t CheckBlockDecoration(ValidationState_t& vstate,
   return SPV_SUCCESS;
 }
 
+spv_result_t CheckLocationDecoration(ValidationState_t& vstate,
+                                     const Instruction& inst,
+                                     const Decoration& decoration) {
+  if (inst.opcode() == SpvOpVariable) return SPV_SUCCESS;
+
+  if (decoration.struct_member_index() != Decoration::kInvalidMember &&
+      inst.opcode() == SpvOpTypeStruct) {
+    return SPV_SUCCESS;
+  }
+
+  return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+         << "Location decoration can only be applied to a variable or member "
+            "of a structure type";
+}
+
 #define PASS_OR_BAIL_AT_LINE(X, LINE)           \
   {                                             \
     spv_result_t e##LINE = (X);                 \
@@ -1590,6 +1605,9 @@ spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) {
         case SpvDecorationBufferBlock:
           PASS_OR_BAIL(CheckBlockDecoration(vstate, *inst, decoration));
           break;
+        case SpvDecorationLocation:
+          PASS_OR_BAIL(CheckLocationDecoration(vstate, *inst, decoration));
+          break;
         default:
           break;
       }

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

@@ -102,6 +102,373 @@ spv_result_t check_interface_variable(ValidationState_t& _,
   return SPV_SUCCESS;
 }
 
+// This function assumes a base location has been determined already. As such
+// any further location decorations are invalid.
+// TODO: if this code turns out to be slow, there is an opportunity to cache
+// the result for a given type id.
+spv_result_t NumConsumedLocations(ValidationState_t& _, const Instruction* type,
+                                  uint32_t* num_locations) {
+  *num_locations = 0;
+  switch (type->opcode()) {
+    case SpvOpTypeInt:
+    case SpvOpTypeFloat:
+      // Scalars always consume a single location.
+      *num_locations = 1;
+      break;
+    case SpvOpTypeVector:
+      // 3- and 4-component 64-bit vectors consume two locations.
+      if ((_.ContainsSizedIntOrFloatType(type->id(), SpvOpTypeInt, 64) ||
+           _.ContainsSizedIntOrFloatType(type->id(), SpvOpTypeFloat, 64)) &&
+          (type->GetOperandAs<uint32_t>(2) > 2)) {
+        *num_locations = 2;
+      } else {
+        *num_locations = 1;
+      }
+      break;
+    case SpvOpTypeMatrix:
+      // Matrices consume locations equal to the underlying vector type for
+      // each column.
+      NumConsumedLocations(_, _.FindDef(type->GetOperandAs<uint32_t>(1)),
+                           num_locations);
+      *num_locations *= type->GetOperandAs<uint32_t>(2);
+      break;
+    case SpvOpTypeArray: {
+      // Arrays consume locations equal to the underlying type times the number
+      // of elements in the vector.
+      NumConsumedLocations(_, _.FindDef(type->GetOperandAs<uint32_t>(1)),
+                           num_locations);
+      bool is_int = false;
+      bool is_const = false;
+      uint32_t value = 0;
+      // Attempt to evaluate the number of array elements.
+      std::tie(is_int, is_const, value) =
+          _.EvalInt32IfConst(type->GetOperandAs<uint32_t>(2));
+      if (is_int && is_const) *num_locations *= value;
+      break;
+    }
+    case SpvOpTypeStruct: {
+      // Members cannot have location decorations at this point.
+      if (_.HasDecoration(type->id(), SpvDecorationLocation)) {
+        return _.diag(SPV_ERROR_INVALID_DATA, type)
+               << "Members cannot be assigned a location";
+      }
+
+      // Structs consume locations equal to the sum of the locations consumed
+      // by the members.
+      for (uint32_t i = 1; i < type->operands().size(); ++i) {
+        uint32_t member_locations = 0;
+        if (auto error = NumConsumedLocations(
+                _, _.FindDef(type->GetOperandAs<uint32_t>(i)),
+                &member_locations)) {
+          return error;
+        }
+        *num_locations += member_locations;
+      }
+      break;
+    }
+    default:
+      break;
+  }
+
+  return SPV_SUCCESS;
+}
+
+// Returns the number of components consumed by types that support a component
+// decoration.
+uint32_t NumConsumedComponents(ValidationState_t& _, const Instruction* type) {
+  uint32_t num_components = 0;
+  switch (type->opcode()) {
+    case SpvOpTypeInt:
+    case SpvOpTypeFloat:
+      // 64-bit types consume two components.
+      if (type->GetOperandAs<uint32_t>(1) == 64) {
+        num_components = 2;
+      } else {
+        num_components = 1;
+      }
+      break;
+    case SpvOpTypeVector:
+      // Vectors consume components equal to the underlying type's consumption
+      // times the number of elements in the vector. Note that 3- and 4-element
+      // vectors cannot have a component decoration (i.e. assumed to be zero).
+      num_components =
+          NumConsumedComponents(_, _.FindDef(type->GetOperandAs<uint32_t>(1)));
+      num_components *= type->GetOperandAs<uint32_t>(2);
+      break;
+    default:
+      // This is an error that is validated elsewhere.
+      break;
+  }
+
+  return num_components;
+}
+
+// Populates |locations| (and/or |output_index1_locations|) with the use
+// location and component coordinates for |variable|. Indices are calculated as
+// 4 * location + component.
+spv_result_t GetLocationsForVariable(
+    ValidationState_t& _, const Instruction* entry_point,
+    const Instruction* variable, std::vector<bool>* locations,
+    std::vector<bool>* output_index1_locations) {
+  const bool is_fragment = entry_point->GetOperandAs<SpvExecutionModel>(0) ==
+                           SpvExecutionModelFragment;
+  const bool is_output =
+      variable->GetOperandAs<SpvStorageClass>(2) == SpvStorageClassOutput;
+  auto ptr_type_id = variable->GetOperandAs<uint32_t>(0);
+  auto ptr_type = _.FindDef(ptr_type_id);
+  auto type_id = ptr_type->GetOperandAs<uint32_t>(2);
+  auto type = _.FindDef(type_id);
+
+  // Check for Location, Component and Index decorations on the variable. The
+  // validator allows duplicate decorations if the location/component/index are
+  // equal. Also track Patch and PerTaskNV decorations.
+  bool has_location = false;
+  uint32_t location = 0;
+  bool has_component = false;
+  uint32_t component = 0;
+  bool has_index = false;
+  uint32_t index = 0;
+  bool has_patch = false;
+  bool has_per_task_nv = false;
+  bool has_per_vertex_nv = false;
+  for (auto& dec : _.id_decorations(variable->id())) {
+    if (dec.dec_type() == SpvDecorationLocation) {
+      if (has_location && dec.params()[0] != location) {
+        return _.diag(SPV_ERROR_INVALID_DATA, variable)
+               << "Variable has conflicting location decorations";
+      }
+      has_location = true;
+      location = dec.params()[0];
+    } else if (dec.dec_type() == SpvDecorationComponent) {
+      if (has_component && dec.params()[0] != component) {
+        return _.diag(SPV_ERROR_INVALID_DATA, variable)
+               << "Variable has conflicting component decorations";
+      }
+      has_component = true;
+      component = dec.params()[0];
+    } else if (dec.dec_type() == SpvDecorationIndex) {
+      if (!is_output || !is_fragment) {
+        return _.diag(SPV_ERROR_INVALID_DATA, variable)
+               << "Index can only be applied to Fragment output variables";
+      }
+      if (has_index && dec.params()[0] != index) {
+        return _.diag(SPV_ERROR_INVALID_DATA, variable)
+               << "Variable has conflicting index decorations";
+      }
+      has_index = true;
+      index = dec.params()[0];
+    } else if (dec.dec_type() == SpvDecorationBuiltIn) {
+      // Don't check built-ins.
+      return SPV_SUCCESS;
+    } else if (dec.dec_type() == SpvDecorationPatch) {
+      has_patch = true;
+    } else if (dec.dec_type() == SpvDecorationPerTaskNV) {
+      has_per_task_nv = true;
+    } else if (dec.dec_type() == SpvDecorationPerVertexNV) {
+      has_per_vertex_nv = true;
+    }
+  }
+
+  // Vulkan 14.1.3: Tessellation control and mesh per-vertex outputs and
+  // tessellation control, evaluation and geometry per-vertex inputs have a
+  // layer of arraying that is not included in interface matching.
+  bool is_arrayed = false;
+  switch (entry_point->GetOperandAs<SpvExecutionModel>(0)) {
+    case SpvExecutionModelTessellationControl:
+      if (!has_patch) {
+        is_arrayed = true;
+      }
+      break;
+    case SpvExecutionModelTessellationEvaluation:
+      if (!is_output && !has_patch) {
+        is_arrayed = true;
+      }
+      break;
+    case SpvExecutionModelGeometry:
+      if (!is_output) {
+        is_arrayed = true;
+      }
+      break;
+    case SpvExecutionModelFragment:
+      if (!is_output && has_per_vertex_nv) {
+        is_arrayed = true;
+      }
+      break;
+    case SpvExecutionModelMeshNV:
+      if (is_output && !has_per_task_nv) {
+        is_arrayed = true;
+      }
+      break;
+    default:
+      break;
+  }
+
+  // Unpack arrayness.
+  if (is_arrayed && (type->opcode() == SpvOpTypeArray ||
+                     type->opcode() == SpvOpTypeRuntimeArray)) {
+    type_id = type->GetOperandAs<uint32_t>(1);
+    type = _.FindDef(type_id);
+  }
+
+  if (type->opcode() == SpvOpTypeStruct) {
+    // Don't check built-ins.
+    if (_.HasDecoration(type_id, SpvDecorationBuiltIn)) return SPV_SUCCESS;
+  }
+
+  // Only block-decorated structs don't need a location on the variable.
+  const bool is_block = _.HasDecoration(type_id, SpvDecorationBlock);
+  if (!has_location && !is_block) {
+    return _.diag(SPV_ERROR_INVALID_DATA, variable)
+           << "Variable must be decorated with a location";
+  }
+
+  const std::string storage_class = is_output ? "output" : "input";
+  if (has_location) {
+    auto sub_type = type;
+    bool is_int = false;
+    bool is_const = false;
+    uint32_t array_size = 1;
+    // If the variable is still arrayed, mark the locations/components per
+    // index.
+    if (type->opcode() == SpvOpTypeArray) {
+      // Determine the array size if possible and get the element type.
+      std::tie(is_int, is_const, array_size) =
+          _.EvalInt32IfConst(type->GetOperandAs<uint32_t>(2));
+      if (!is_int || !is_const) array_size = 1;
+      auto sub_type_id = type->GetOperandAs<uint32_t>(1);
+      sub_type = _.FindDef(sub_type_id);
+    }
+
+    for (uint32_t array_idx = 0; array_idx < array_size; ++array_idx) {
+      uint32_t num_locations = 0;
+      if (auto error = NumConsumedLocations(_, sub_type, &num_locations))
+        return error;
+
+      uint32_t num_components = NumConsumedComponents(_, sub_type);
+      uint32_t array_location = location + (num_locations * array_idx);
+      uint32_t start = array_location * 4;
+      uint32_t end = (array_location + num_locations) * 4;
+      if (num_components != 0) {
+        start += component;
+        end = array_location * 4 + component + num_components;
+      }
+
+      auto locs = locations;
+      if (has_index && index == 1) locs = output_index1_locations;
+
+      if (end > locs->size()) {
+        locs->resize(end, false);
+      }
+      for (uint32_t i = start; i < end; ++i) {
+        if (locs->at(i)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
+                 << "Entry-point has conflicting " << storage_class
+                 << " location assignment at location " << i / 4
+                 << ", component " << i % 4;
+        }
+        (*locs)[i] = true;
+      }
+    }
+  } else {
+    // For Block-decorated structs with no location assigned to the variable,
+    // each member of the block must be assigned a location. Also record any
+    // member component assignments. The validator allows duplicate decorations
+    // if they agree on the location/component.
+    std::unordered_map<uint32_t, uint32_t> member_locations;
+    std::unordered_map<uint32_t, uint32_t> member_components;
+    for (auto& dec : _.id_decorations(type_id)) {
+      if (dec.dec_type() == SpvDecorationLocation) {
+        auto where = member_locations.find(dec.struct_member_index());
+        if (where == member_locations.end()) {
+          member_locations[dec.struct_member_index()] = dec.params()[0];
+        } else if (where->second != dec.params()[0]) {
+          return _.diag(SPV_ERROR_INVALID_DATA, type)
+                 << "Member index " << dec.struct_member_index()
+                 << " has conflicting location assignments";
+        }
+      } else if (dec.dec_type() == SpvDecorationComponent) {
+        auto where = member_components.find(dec.struct_member_index());
+        if (where == member_components.end()) {
+          member_components[dec.struct_member_index()] = dec.params()[0];
+        } else if (where->second != dec.params()[0]) {
+          return _.diag(SPV_ERROR_INVALID_DATA, type)
+                 << "Member index " << dec.struct_member_index()
+                 << " has conflicting component assignments";
+        }
+      }
+    }
+
+    for (uint32_t i = 1; i < type->operands().size(); ++i) {
+      auto where = member_locations.find(i - 1);
+      if (where == member_locations.end()) {
+        return _.diag(SPV_ERROR_INVALID_DATA, type)
+               << "Member index " << i - 1
+               << " is missing a location assignment";
+      }
+
+      location = where->second;
+      auto member = _.FindDef(type->GetOperandAs<uint32_t>(i));
+      uint32_t num_locations = 0;
+      if (auto error = NumConsumedLocations(_, member, &num_locations))
+        return error;
+
+      // If the component is not specified, it is assumed to be zero.
+      uint32_t num_components = NumConsumedComponents(_, member);
+      component = 0;
+      if (member_components.count(i - 1)) {
+        component = member_components[i - 1];
+      }
+
+      uint32_t start = location * 4;
+      uint32_t end = (location + num_locations) * 4;
+      if (num_components != 0) {
+        start += component;
+        end = location * 4 + component + num_components;
+      }
+      if (end > locations->size()) {
+        locations->resize(end, false);
+      }
+      for (uint32_t l = start; l < end; ++l) {
+        if (locations->at(l)) {
+          return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
+                 << "Entry-point has conflicting " << storage_class
+                 << " location assignment at location " << l / 4
+                 << ", component " << l % 4;
+        }
+        (*locations)[l] = true;
+      }
+    }
+  }
+
+  return SPV_SUCCESS;
+}
+
+spv_result_t ValidateLocations(ValidationState_t& _,
+                               const Instruction* entry_point) {
+  // Reserve space for 16 locations with 4 components each.
+  std::vector<bool> input_locations(16 * 4, false);
+  std::vector<bool> output_locations_index0(16 * 4, false);
+  std::vector<bool> output_locations_index1(16 * 4, false);
+  for (uint32_t i = 3; i < entry_point->operands().size(); ++i) {
+    auto interface_id = entry_point->GetOperandAs<uint32_t>(i);
+    auto interface_var = _.FindDef(interface_id);
+    auto storage_class = interface_var->GetOperandAs<SpvStorageClass>(2);
+    if (storage_class != SpvStorageClassInput &&
+        storage_class != SpvStorageClassOutput) {
+      continue;
+    }
+
+    auto locations = (storage_class == SpvStorageClassInput)
+                         ? &input_locations
+                         : &output_locations_index0;
+    if (auto error = GetLocationsForVariable(
+            _, entry_point, interface_var, locations, &output_locations_index1))
+      return error;
+  }
+
+  return SPV_SUCCESS;
+}
+
 }  // namespace
 
 spv_result_t ValidateInterfaces(ValidationState_t& _) {
@@ -114,6 +481,17 @@ spv_result_t ValidateInterfaces(ValidationState_t& _) {
     }
   }
 
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    for (auto& inst : _.ordered_instructions()) {
+      if (inst.opcode() == SpvOpEntryPoint) {
+        if (auto error = ValidateLocations(_, &inst)) {
+          return error;
+        }
+      }
+      if (inst.opcode() == SpvOpTypeVoid) break;
+    }
+  }
+
   return SPV_SUCCESS;
 }
 

+ 23 - 8
3rdparty/spirv-tools/utils/roll_deps.sh

@@ -13,19 +13,34 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Attempts to roll all entries in DEPS to origin/master and creates a
-# commit.
+# Attempts to roll all entries in DEPS to tip-of-tree and create a commit.
 #
 # Depends on roll-dep from depot_path being in PATH.
 
+effcee_dir="third_party/effcee/"
+effcee_trunk="origin/main"
+googletest_dir="third_party/googletest/"
+googletest_trunk="origin/master"
+re2_dir="third_party/re2/"
+re2_trunk="origin/master"
+spirv_headers_dir="third_party/spirv-headers/"
+spirv_headers_trunk="origin/master"
+
 # This script assumes it's parent directory is the repo root.
 repo_path=$(dirname "$0")/..
 
-effcee_dir="external/effcee/"
-googletest_dir="external/googletest/"
-re2_dir="external/re2/"
-spirv_headers_dir="external/spirv-headers/"
-
 cd "$repo_path"
 
-roll-dep "$@" "${effcee_dir}" "${googletest_dir}" "${re2_dir}" "${spirv_headers_dir}"
+if [[ $(git diff --stat) != '' ]]; then
+    echo "Working tree is dirty, commit changes before attempting to roll DEPS"
+    exit 1
+fi
+
+old_head=$(git rev-parse HEAD)
+
+roll-dep --ignore-dirty-tree --roll-to="${effcee_trunk}" "${effcee_dir}"
+roll-dep --ignore-dirty-tree --roll-to="${googletest_trunk}" "${googletest_dir}"
+roll-dep --ignore-dirty-tree --roll-to="${re2_trunk}" "${re2_dir}"
+roll-dep --ignore-dirty-tree --roll-to="${spirv_headers_trunk}" "${spirv_headers_dir}"
+
+git rebase --interactive "${old_head}"