// Copyright (c) 2020 Vasyl Teliman // // 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_move_instruction_down.h" #include "source/fuzz/fuzzer_util.h" #include "source/fuzz/instruction_descriptor.h" namespace spvtools { namespace fuzz { TransformationMoveInstructionDown::TransformationMoveInstructionDown( const protobufs::TransformationMoveInstructionDown& message) : message_(message) {} TransformationMoveInstructionDown::TransformationMoveInstructionDown( const protobufs::InstructionDescriptor& instruction) { *message_.mutable_instruction() = instruction; } bool TransformationMoveInstructionDown::IsApplicable( opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // |instruction| must be valid. auto* inst = FindInstruction(message_.instruction(), ir_context); if (!inst) { return false; } // Instruction's opcode must be supported by this transformation. if (!IsOpcodeSupported(inst->opcode())) { return false; } auto* inst_block = ir_context->get_instr_block(inst); assert(inst_block && "Global instructions and function parameters are not supported"); auto inst_it = fuzzerutil::GetIteratorForInstruction(inst_block, inst); assert(inst_it != inst_block->end() && "Can't get an iterator for the instruction"); // |instruction| can't be the last instruction in the block. auto successor_it = ++inst_it; if (successor_it == inst_block->end()) { return false; } // Check that we can insert |instruction| after |inst_it|. auto successors_successor_it = ++inst_it; if (successors_successor_it == inst_block->end() || !fuzzerutil::CanInsertOpcodeBeforeInstruction(inst->opcode(), successors_successor_it)) { return false; } // Check that |instruction|'s successor doesn't depend on the |instruction|. if (inst->result_id()) { for (uint32_t i = 0; i < successor_it->NumInOperands(); ++i) { const auto& operand = successor_it->GetInOperand(i); if (operand.type == SPV_OPERAND_TYPE_ID && operand.words[0] == inst->result_id()) { return false; } } } return true; } void TransformationMoveInstructionDown::Apply( opt::IRContext* ir_context, TransformationContext* /*unused*/) const { auto* inst = FindInstruction(message_.instruction(), ir_context); assert(inst && "The instruction should've been validated in the IsApplicable"); auto inst_it = fuzzerutil::GetIteratorForInstruction( ir_context->get_instr_block(inst), inst); // Move the instruction down in the block. inst->InsertAfter(&*++inst_it); ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisNone); } protobufs::Transformation TransformationMoveInstructionDown::ToMessage() const { protobufs::Transformation result; *result.mutable_move_instruction_down() = message_; return result; } bool TransformationMoveInstructionDown::IsOpcodeSupported(SpvOp opcode) { // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3605): // We only support "simple" instructions that work don't with memory. // We should extend this so that we support the ones that modify the memory // too. switch (opcode) { case SpvOpNop: case SpvOpUndef: case SpvOpAccessChain: case SpvOpInBoundsAccessChain: case SpvOpArrayLength: case SpvOpVectorExtractDynamic: case SpvOpVectorInsertDynamic: case SpvOpVectorShuffle: case SpvOpCompositeConstruct: case SpvOpCompositeExtract: case SpvOpCompositeInsert: case SpvOpCopyObject: case SpvOpTranspose: case SpvOpConvertFToU: case SpvOpConvertFToS: case SpvOpConvertSToF: case SpvOpConvertUToF: case SpvOpUConvert: case SpvOpSConvert: case SpvOpFConvert: case SpvOpQuantizeToF16: case SpvOpSatConvertSToU: case SpvOpSatConvertUToS: case SpvOpBitcast: case SpvOpSNegate: case SpvOpFNegate: case SpvOpIAdd: case SpvOpFAdd: case SpvOpISub: case SpvOpFSub: case SpvOpIMul: case SpvOpFMul: case SpvOpUDiv: case SpvOpSDiv: case SpvOpFDiv: case SpvOpUMod: case SpvOpSRem: case SpvOpSMod: case SpvOpFRem: case SpvOpFMod: case SpvOpVectorTimesScalar: case SpvOpMatrixTimesScalar: case SpvOpVectorTimesMatrix: case SpvOpMatrixTimesVector: case SpvOpMatrixTimesMatrix: case SpvOpOuterProduct: case SpvOpDot: case SpvOpIAddCarry: case SpvOpISubBorrow: case SpvOpUMulExtended: case SpvOpSMulExtended: case SpvOpAny: case SpvOpAll: case SpvOpIsNan: case SpvOpIsInf: case SpvOpIsFinite: case SpvOpIsNormal: case SpvOpSignBitSet: case SpvOpLessOrGreater: case SpvOpOrdered: case SpvOpUnordered: case SpvOpLogicalEqual: case SpvOpLogicalNotEqual: case SpvOpLogicalOr: case SpvOpLogicalAnd: case SpvOpLogicalNot: case SpvOpSelect: case SpvOpIEqual: case SpvOpINotEqual: case SpvOpUGreaterThan: case SpvOpSGreaterThan: case SpvOpUGreaterThanEqual: case SpvOpSGreaterThanEqual: case SpvOpULessThan: case SpvOpSLessThan: case SpvOpULessThanEqual: case SpvOpSLessThanEqual: case SpvOpFOrdEqual: case SpvOpFUnordEqual: case SpvOpFOrdNotEqual: case SpvOpFUnordNotEqual: case SpvOpFOrdLessThan: case SpvOpFUnordLessThan: case SpvOpFOrdGreaterThan: case SpvOpFUnordGreaterThan: case SpvOpFOrdLessThanEqual: case SpvOpFUnordLessThanEqual: case SpvOpFOrdGreaterThanEqual: case SpvOpFUnordGreaterThanEqual: case SpvOpShiftRightLogical: case SpvOpShiftRightArithmetic: case SpvOpShiftLeftLogical: case SpvOpBitwiseOr: case SpvOpBitwiseXor: case SpvOpBitwiseAnd: case SpvOpNot: case SpvOpBitFieldInsert: case SpvOpBitFieldSExtract: case SpvOpBitFieldUExtract: case SpvOpBitReverse: case SpvOpBitCount: case SpvOpCopyLogical: case SpvOpPtrEqual: case SpvOpPtrNotEqual: return true; default: return false; } } } // namespace fuzz } // namespace spvtools