SPIRV-Tools/source/fuzz/transformation_move_instruction_down.cpp
Vasyl Teliman 92a71657fc
spirv-fuzz: TransformationMoveInstructionDown (#3477)
Swaps an instruction with the next instruction in the block.

Fixes #3457.
2020-08-03 16:45:24 +01:00

220 lines
6.5 KiB
C++

// 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