| // Copyright (c) 2021 Shiyu Liu | |
| // | |
| // 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_wrap_vector_synonym.h" | |
| #include "source/fuzz/fuzzer_context.h" | |
| #include "source/fuzz/fuzzer_util.h" | |
| #include "source/fuzz/transformation_composite_construct.h" | |
| #include "source/fuzz/transformation_wrap_vector_synonym.h" | |
| namespace spvtools { | |
| namespace fuzz { | |
| FuzzerPassWrapVectorSynonym::FuzzerPassWrapVectorSynonym( | |
| 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 FuzzerPassWrapVectorSynonym::Apply() { | |
| ForEachInstructionWithInstructionDescriptor( | |
| [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/, | |
| opt::BasicBlock::iterator instruction_iterator, | |
| const protobufs::InstructionDescriptor& instruction_descriptor) | |
| -> void { | |
| // Randomly decide whether to wrap it to a vector operation. | |
| if (!GetFuzzerContext()->ChoosePercentage( | |
| GetFuzzerContext()->GetChanceOfWrappingVectorSynonym())) { | |
| return; | |
| } | |
| // The transformation is not applicable if the instruction has missing | |
| // result id, type id, or is not supported type. | |
| if (!TransformationWrapVectorSynonym::IsInstructionSupported( | |
| GetIRContext(), *instruction_iterator)) { | |
| return; | |
| } | |
| // It must be valid to insert an OpCompositeConstruct instruction | |
| // before |instruction_iterator|. | |
| if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( | |
| spv::Op::OpCompositeConstruct, instruction_iterator)) { | |
| return; | |
| } | |
| // Get the scalar operands from the original instruction. | |
| opt::Instruction* operand1 = GetIRContext()->get_def_use_mgr()->GetDef( | |
| instruction_iterator->GetSingleWordInOperand(0)); | |
| opt::Instruction* operand2 = GetIRContext()->get_def_use_mgr()->GetDef( | |
| instruction_iterator->GetSingleWordInOperand(1)); | |
| // We need to be able to make a synonym of the scalar operation's result | |
| // id, as well as the operand ids (for example, they cannot be | |
| // irrelevant). | |
| if (!fuzzerutil::CanMakeSynonymOf(GetIRContext(), | |
| *GetTransformationContext(), | |
| *instruction_iterator)) { | |
| return; | |
| } | |
| if (!fuzzerutil::CanMakeSynonymOf( | |
| GetIRContext(), *GetTransformationContext(), *operand1)) { | |
| return; | |
| } | |
| if (!fuzzerutil::CanMakeSynonymOf( | |
| GetIRContext(), *GetTransformationContext(), *operand2)) { | |
| return; | |
| } | |
| // Get a random vector size from 2 to 4. | |
| uint32_t vector_size = GetFuzzerContext()->GetWidthOfWrappingVector(); | |
| // Randomly choose a position that target ids should be placed at. | |
| // The position is in range [0, n - 1], where n is the size of the | |
| // vector. | |
| uint32_t position = | |
| GetFuzzerContext()->GetRandomIndexForWrappingVector(vector_size); | |
| // Stores the ids of scalar constants. | |
| std::vector<uint32_t> vec1_components; | |
| std::vector<uint32_t> vec2_components; | |
| // Populate components based on vector type and size. | |
| for (uint32_t i = 0; i < vector_size; ++i) { | |
| if (i == position) { | |
| vec1_components.emplace_back(operand1->result_id()); | |
| vec2_components.emplace_back(operand2->result_id()); | |
| } else { | |
| vec1_components.emplace_back( | |
| FindOrCreateZeroConstant(operand1->type_id(), true)); | |
| vec2_components.emplace_back( | |
| FindOrCreateZeroConstant(operand2->type_id(), true)); | |
| } | |
| } | |
| // Add two OpCompositeConstruct to the module with result id returned. | |
| // The added vectors may have different types, for instance if the | |
| // scalar instruction operates on integers with differing sign. | |
| // Add the first OpCompositeConstruct that wraps the id of the first | |
| // operand. | |
| uint32_t result_id1 = GetFuzzerContext()->GetFreshId(); | |
| ApplyTransformation(TransformationCompositeConstruct( | |
| FindOrCreateVectorType(operand1->type_id(), vector_size), | |
| vec1_components, instruction_descriptor, result_id1)); | |
| // Add the second OpCompositeConstruct that wraps the id of the second | |
| // operand. | |
| uint32_t result_id2 = GetFuzzerContext()->GetFreshId(); | |
| ApplyTransformation(TransformationCompositeConstruct( | |
| FindOrCreateVectorType(operand2->type_id(), vector_size), | |
| vec2_components, instruction_descriptor, result_id2)); | |
| // The result of the vector instruction that | |
| // TransformationWrapVectorSynonym will create should be a vector of the | |
| // right size, with the scalar instruction's result type as its element | |
| // type. This can be distinct from the types of the operands, if the | |
| // scalar instruction adds two signed integers and stores the result in | |
| // an unsigned id, for example. A transformation is applied to add the | |
| // right type to the module. | |
| FindOrCreateVectorType(instruction_iterator->type_id(), vector_size); | |
| // Apply transformation to do vector operation and add synonym between | |
| // the result vector id and the id of the original instruction. | |
| ApplyTransformation(TransformationWrapVectorSynonym( | |
| instruction_iterator->result_id(), result_id1, result_id2, | |
| GetFuzzerContext()->GetFreshId(), position)); | |
| }); | |
| } | |
| } // namespace fuzz | |
| } // namespace spvtools |