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_