blob: a29d1d34d28ea8d9ea75336d5dd3943cf640970a [file] [log] [blame]
// 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,
bool ignore_inapplicable_transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
transformations, ignore_inapplicable_transformations) {}
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,
[this, instruction_descriptor](
opt::IRContext* ir_context,
opt::Instruction* instruction) -> bool {
if (!instruction->result_id() || !instruction->type_id()) {
return false;
}
if (!ir_context->get_type_mgr()
->GetType(instruction->type_id())
->AsVector()) {
return false;
}
if (!GetTransformationContext()
->GetFactManager()
->IdIsIrrelevant(instruction->result_id()) &&
!fuzzerutil::CanMakeSynonymOf(ir_context,
*GetTransformationContext(),
*instruction)) {
// If the id is irrelevant, we can use it since it will not
// participate in DataSynonym fact. Otherwise, we should be
// able to produce a synonym out of the id.
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