spirv-fuzz: Implement vector shuffle fuzzer pass (#3412)

Fixes #3108.
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index de5f14f..2eae79a 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -49,6 +49,7 @@
         fuzzer_pass_add_local_variables.h
         fuzzer_pass_add_no_contraction_decorations.h
         fuzzer_pass_add_stores.h
+        fuzzer_pass_add_vector_shuffle_instructions.h
         fuzzer_pass_adjust_branch_weights.h
         fuzzer_pass_adjust_function_controls.h
         fuzzer_pass_adjust_loop_controls.h
@@ -150,6 +151,7 @@
         fuzzer_pass_add_local_variables.cpp
         fuzzer_pass_add_no_contraction_decorations.cpp
         fuzzer_pass_add_stores.cpp
+        fuzzer_pass_add_vector_shuffle_instructions.cpp
         fuzzer_pass_adjust_branch_weights.cpp
         fuzzer_pass_adjust_function_controls.cpp
         fuzzer_pass_adjust_loop_controls.cpp
diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp
index 12ed877..420926a 100644
--- a/source/fuzz/fuzzer.cpp
+++ b/source/fuzz/fuzzer.cpp
@@ -33,6 +33,7 @@
 #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_stores.h"
+#include "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h"
 #include "source/fuzz/fuzzer_pass_adjust_branch_weights.h"
 #include "source/fuzz/fuzzer_pass_adjust_function_controls.h"
 #include "source/fuzz/fuzzer_pass_adjust_loop_controls.h"
@@ -224,6 +225,9 @@
     MaybeAddPass<FuzzerPassAddStores>(&passes, ir_context.get(),
                                       &transformation_context, &fuzzer_context,
                                       transformation_sequence_out);
+    MaybeAddPass<FuzzerPassAddVectorShuffleInstructions>(
+        &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+        transformation_sequence_out);
     MaybeAddPass<FuzzerPassApplyIdSynonyms>(
         &passes, ir_context.get(), &transformation_context, &fuzzer_context,
         transformation_sequence_out);
diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp
index 26a1a23..ef0de5e 100644
--- a/source/fuzz/fuzzer_context.cpp
+++ b/source/fuzz/fuzzer_context.cpp
@@ -40,6 +40,7 @@
     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};
 const std::pair<uint32_t, uint32_t> kChanceOfAdjustingBranchWeights = {20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfAdjustingFunctionControl = {20,
                                                                          70};
@@ -126,6 +127,8 @@
   chance_of_adding_no_contraction_decoration_ =
       ChooseBetweenMinAndMax(kChanceOfAddingNoContractionDecoration);
   chance_of_adding_store_ = ChooseBetweenMinAndMax(kChanceOfAddingStore);
+  chance_of_adding_vector_shuffle_ =
+      ChooseBetweenMinAndMax(kChanceOfAddingVectorShuffle);
   chance_of_adding_vector_type_ =
       ChooseBetweenMinAndMax(kChanceOfAddingVectorType);
   chance_of_adjusting_branch_weights_ =
diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h
index 524f86f..5f1d3be 100644
--- a/source/fuzz/fuzzer_context.h
+++ b/source/fuzz/fuzzer_context.h
@@ -136,6 +136,9 @@
     return chance_of_adding_no_contraction_decoration_;
   }
   uint32_t GetChanceOfAddingStore() { return chance_of_adding_store_; }
+  uint32_t GetChanceOfAddingVectorShuffle() {
+    return chance_of_adding_vector_shuffle_;
+  }
   uint32_t GetChanceOfAddingVectorType() {
     return chance_of_adding_vector_type_;
   }
@@ -201,18 +204,6 @@
   uint32_t GetMaximumEquivalenceClassSizeForDataSynonymFactClosure() {
     return max_equivalence_class_size_for_data_synonym_fact_closure_;
   }
-  uint32_t GetRandomIndexForAccessChain(uint32_t composite_size_bound) {
-    return random_generator_->RandomUint32(composite_size_bound);
-  }
-  uint32_t GetRandomLoopControlPartialCount() {
-    return random_generator_->RandomUint32(max_loop_control_partial_count_);
-  }
-  uint32_t GetRandomLoopControlPeelCount() {
-    return random_generator_->RandomUint32(max_loop_control_peel_count_);
-  }
-  uint32_t GetRandomLoopLimit() {
-    return random_generator_->RandomUint32(max_loop_limit_);
-  }
   std::pair<uint32_t, uint32_t> GetRandomBranchWeights() {
     std::pair<uint32_t, uint32_t> branch_weights = {0, 0};
 
@@ -225,6 +216,29 @@
 
     return branch_weights;
   }
+  std::vector<uint32_t> GetRandomComponentsForVectorShuffle(
+      uint32_t max_component_index) {
+    // Component count must be in range [2, 4].
+    std::vector<uint32_t> components(random_generator_->RandomUint32(2) + 2);
+
+    for (uint32_t& component : components) {
+      component = random_generator_->RandomUint32(max_component_index);
+    }
+
+    return components;
+  }
+  uint32_t GetRandomIndexForAccessChain(uint32_t composite_size_bound) {
+    return random_generator_->RandomUint32(composite_size_bound);
+  }
+  uint32_t GetRandomLoopControlPartialCount() {
+    return random_generator_->RandomUint32(max_loop_control_partial_count_);
+  }
+  uint32_t GetRandomLoopControlPeelCount() {
+    return random_generator_->RandomUint32(max_loop_control_peel_count_);
+  }
+  uint32_t GetRandomLoopLimit() {
+    return random_generator_->RandomUint32(max_loop_limit_);
+  }
   uint32_t GetRandomSizeForNewArray() {
     // Ensure that the array size is non-zero.
     return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1;
@@ -254,6 +268,7 @@
   uint32_t chance_of_adding_matrix_type_;
   uint32_t chance_of_adding_no_contraction_decoration_;
   uint32_t chance_of_adding_store_;
+  uint32_t chance_of_adding_vector_shuffle_;
   uint32_t chance_of_adding_vector_type_;
   uint32_t chance_of_adjusting_branch_weights_;
   uint32_t chance_of_adjusting_function_control_;
diff --git a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp
new file mode 100644
index 0000000..21b5310
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp
@@ -0,0 +1,133 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_vector_shuffle.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddVectorShuffleInstructions::FuzzerPassAddVectorShuffleInstructions(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+FuzzerPassAddVectorShuffleInstructions::
+    ~FuzzerPassAddVectorShuffleInstructions() = default;
+
+void FuzzerPassAddVectorShuffleInstructions::Apply() {
+  ForEachInstructionWithInstructionDescriptor(
+      [this](opt::Function* function, opt::BasicBlock* block,
+             opt::BasicBlock::iterator instruction_iterator,
+             const protobufs::InstructionDescriptor& instruction_descriptor)
+          -> void {
+        assert(instruction_iterator->opcode() ==
+                   instruction_descriptor.target_instruction_opcode() &&
+               "The opcode of the instruction we might insert before must be "
+               "the same as the opcode in the descriptor for the instruction");
+
+        // Randomly decide whether to try adding an OpVectorShuffle instruction.
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfAddingVectorShuffle())) {
+          return;
+        }
+
+        // It must be valid to insert an OpVectorShuffle instruction
+        // before |instruction_iterator|.
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
+                SpvOpVectorShuffle, instruction_iterator)) {
+          return;
+        }
+
+        // Looks for vectors that we might consider to use as OpVectorShuffle
+        // operands.
+        std::vector<opt::Instruction*> vector_instructions =
+            FindAvailableInstructions(
+                function, block, instruction_iterator,
+                [instruction_descriptor](
+                    opt::IRContext* ir_context,
+                    opt::Instruction* instruction) -> bool {
+                  if (!instruction->type_id()) {
+                    return false;
+                  }
+
+                  if (!ir_context->get_type_mgr()
+                           ->GetType(instruction->type_id())
+                           ->AsVector()) {
+                    return false;
+                  }
+
+                  return fuzzerutil::IdIsAvailableBeforeInstruction(
+                      ir_context,
+                      FindInstruction(instruction_descriptor, ir_context),
+                      instruction->result_id());
+                });
+
+        // If there are no vector instructions, then return.
+        if (vector_instructions.empty()) {
+          return;
+        }
+
+        auto vector_1_instruction =
+            vector_instructions[GetFuzzerContext()->RandomIndex(
+                vector_instructions)];
+        auto vector_1_type = GetIRContext()
+                                 ->get_type_mgr()
+                                 ->GetType(vector_1_instruction->type_id())
+                                 ->AsVector();
+
+        auto vector_2_instruction =
+            GetFuzzerContext()->RemoveAtRandomIndex(&vector_instructions);
+        auto vector_2_type = GetIRContext()
+                                 ->get_type_mgr()
+                                 ->GetType(vector_2_instruction->type_id())
+                                 ->AsVector();
+
+        // |vector_1| and |vector_2| must have the same element type as each
+        // other. The loop is guaranteed to terminate because each iteration
+        // removes on possible choice for |vector_2|, and there is at least one
+        // choice that will cause the loop to exit - namely |vector_1|.
+        while (vector_1_type->element_type() != vector_2_type->element_type()) {
+          vector_2_instruction =
+              GetFuzzerContext()->RemoveAtRandomIndex(&vector_instructions);
+          vector_2_type = GetIRContext()
+                              ->get_type_mgr()
+                              ->GetType(vector_2_instruction->type_id())
+                              ->AsVector();
+        }
+
+        // Gets components and creates the appropriate result vector type.
+        std::vector<uint32_t> components =
+            GetFuzzerContext()->GetRandomComponentsForVectorShuffle(
+                vector_1_type->element_count() +
+                vector_2_type->element_count());
+        FindOrCreateVectorType(GetIRContext()->get_type_mgr()->GetId(
+                                   vector_1_type->element_type()),
+                               static_cast<uint32_t>(components.size()));
+
+        // Applies the vector shuffle transformation.
+        ApplyTransformation(TransformationVectorShuffle(
+            instruction_descriptor, GetFuzzerContext()->GetFreshId(),
+            vector_1_instruction->result_id(),
+            vector_2_instruction->result_id(), components));
+      });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h
new file mode 100644
index 0000000..99b9f24
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_VECTOR_SHUFFLE_INSTRUCTIONS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_VECTOR_SHUFFLE_INSTRUCTIONS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Adds OpVectorShuffle instructions to the module.
+class FuzzerPassAddVectorShuffleInstructions : public FuzzerPass {
+ public:
+  FuzzerPassAddVectorShuffleInstructions(
+      opt::IRContext* ir_context, TransformationContext* transformation_context,
+      FuzzerContext* fuzzer_context,
+      protobufs::TransformationSequence* transformations);
+
+  ~FuzzerPassAddVectorShuffleInstructions();
+
+  void Apply() override;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_FUZZER_PASS_ADD_VECTOR_SHUFFLE_INSTRUCTIONS_H_