// 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/transformation_wrap_vector_synonym.h" #include "source/fuzz/data_descriptor.h" #include "source/fuzz/fuzzer_util.h" #include "source/opt/instruction.h" namespace spvtools { namespace fuzz { TransformationWrapVectorSynonym::TransformationWrapVectorSynonym( protobufs::TransformationWrapVectorSynonym message) : message_(std::move(message)) {} TransformationWrapVectorSynonym::TransformationWrapVectorSynonym( uint32_t instruction_id, uint32_t vector_operand1, uint32_t vector_operand2, uint32_t fresh_id, uint32_t pos) { message_.set_instruction_id(instruction_id); message_.set_vector_operand1(vector_operand1); message_.set_vector_operand2(vector_operand2); message_.set_fresh_id(fresh_id); message_.set_scalar_position(pos); } bool TransformationWrapVectorSynonym::IsApplicable( opt::IRContext* ir_context, const TransformationContext& transformation_context) const { // |fresh_id| must be fresh. if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { return false; } const opt::Instruction* instruction = ir_context->get_def_use_mgr()->GetDef(message_.instruction_id()); // |instruction_id| must refer to an existing instruction. if (instruction == nullptr) { return false; } if (!IsInstructionSupported(ir_context, *instruction)) { return false; } // It must be possible to make a synonym of the result id of the scalar // operation if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, instruction)) { return false; } // |vector_operand1| and |vector_operand2| must exist. auto vec1 = ir_context->get_def_use_mgr()->GetDef(message_.vector_operand1()); auto vec2 = ir_context->get_def_use_mgr()->GetDef(message_.vector_operand2()); if (vec1 == nullptr || vec2 == nullptr) { return false; } // The 2 vectors must be the same valid vector type. auto vec1_type_id = vec1->type_id(); auto vec2_type_id = vec2->type_id(); if (vec1_type_id != vec2_type_id) { return false; } if (ir_context->get_def_use_mgr()->GetDef(vec1_type_id)->opcode() != SpvOpTypeVector) { return false; } // |scalar_position| needs to be a non-negative integer less than the vector // length. // OpTypeVector instruction has the component count at index 2. if (message_.scalar_position() >= ir_context->get_def_use_mgr() ->GetDef(vec1_type_id) ->GetSingleWordInOperand(1)) { return false; } if (!transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(message_.vector_operand1(), {message_.scalar_position()}), MakeDataDescriptor(instruction->GetSingleWordInOperand(0), {}))) { return false; } if (!transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(message_.vector_operand2(), {message_.scalar_position()}), MakeDataDescriptor(instruction->GetSingleWordInOperand(1), {}))) { return false; } return true; } void TransformationWrapVectorSynonym::Apply( opt::IRContext* ir_context, TransformationContext* transformation_context) const { // Create an instruction descriptor for the original instruction. auto instruction = ir_context->get_def_use_mgr()->GetDef(message_.instruction_id()); auto destination_block = ir_context->get_instr_block(instruction); // Populate input operand list with two vectors for vector operation. opt::Instruction::OperandList in_operands; in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.vector_operand1()}}); in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.vector_operand2()}}); // Make a new arithmetic instruction: %fresh_id = OpXX %type_id %result_id1 // %result_id2. auto vec_type_id = ir_context->get_def_use_mgr() ->GetDef(message_.vector_operand1()) ->type_id(); auto new_instruction = MakeUnique( ir_context, instruction->opcode(), vec_type_id, message_.fresh_id(), std::move(in_operands)); auto new_instruction_ptr = new_instruction.get(); instruction->InsertBefore(std::move(new_instruction)); ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); ir_context->set_instr_block(new_instruction_ptr, destination_block); // Add |fresh_id| to id bound. fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); // Add synonyms between |fresh_id| and |instruction_id|. transformation_context->GetFactManager()->AddFactDataSynonym( MakeDataDescriptor(message_.fresh_id(), {message_.scalar_position()}), MakeDataDescriptor(message_.instruction_id(), {})); } protobufs::Transformation TransformationWrapVectorSynonym::ToMessage() const { protobufs::Transformation result; *result.mutable_wrap_vector_synonym() = message_; return result; } std::unordered_set TransformationWrapVectorSynonym::GetFreshIds() const { return std::unordered_set{message_.fresh_id()}; } bool TransformationWrapVectorSynonym::IsInstructionSupported( opt::IRContext* ir_context, const opt::Instruction& instruction) { if (!instruction.result_id() || !instruction.type_id()) { return false; } auto type_instruction = ir_context->get_def_use_mgr()->GetDef(instruction.type_id()); if ((type_instruction->opcode() != SpvOpTypeInt && type_instruction->opcode() != SpvOpTypeFloat)) { return false; } switch (instruction.opcode()) { case SpvOpIAdd: case SpvOpISub: case SpvOpIMul: case SpvOpFAdd: case SpvOpFSub: case SpvOpFMul: return true; default: return false; } } } // namespace fuzz } // namespace spvtools