| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734 |
- // 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"
- #include "spirv/unified1/GLSL.std.450.h"
- namespace spvtools {
- namespace fuzz {
- namespace {
- const char* const kExtensionSetName = "GLSL.std.450";
- std::string GetExtensionSet(opt::IRContext* ir_context,
- const opt::Instruction& op_ext_inst) {
- assert(op_ext_inst.opcode() == spv::Op::OpExtInst && "Wrong opcode");
- const auto* ext_inst_import = ir_context->get_def_use_mgr()->GetDef(
- op_ext_inst.GetSingleWordInOperand(0));
- assert(ext_inst_import && "Extension set is not imported");
- return ext_inst_import->GetInOperand(0).AsString();
- }
- } // namespace
- TransformationMoveInstructionDown::TransformationMoveInstructionDown(
- protobufs::TransformationMoveInstructionDown message)
- : message_(std::move(message)) {}
- TransformationMoveInstructionDown::TransformationMoveInstructionDown(
- const protobufs::InstructionDescriptor& instruction) {
- *message_.mutable_instruction() = instruction;
- }
- bool TransformationMoveInstructionDown::IsApplicable(
- opt::IRContext* ir_context,
- const TransformationContext& transformation_context) 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 (!IsInstructionSupported(ir_context, *inst)) {
- 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;
- }
- // We don't risk swapping a memory instruction with an unsupported one.
- if (!IsSimpleInstruction(ir_context, *inst) &&
- !IsInstructionSupported(ir_context, *successor_it)) {
- return false;
- }
- // It must be safe to swap the instructions without changing the semantics of
- // the module.
- if (IsInstructionSupported(ir_context, *successor_it) &&
- !CanSafelySwapInstructions(ir_context, *inst, *successor_it,
- *transformation_context.GetFactManager())) {
- 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 (spvIsInIdType(operand.type) &&
- 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::IsInstructionSupported(
- opt::IRContext* ir_context, const opt::Instruction& inst) {
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3605):
- // Add support for more instructions here.
- return IsSimpleInstruction(ir_context, inst) ||
- IsMemoryInstruction(ir_context, inst) || IsBarrierInstruction(inst);
- }
- bool TransformationMoveInstructionDown::IsSimpleInstruction(
- opt::IRContext* ir_context, const opt::Instruction& inst) {
- switch (inst.opcode()) {
- case spv::Op::OpNop:
- case spv::Op::OpUndef:
- case spv::Op::OpAccessChain:
- case spv::Op::OpInBoundsAccessChain:
- // OpAccessChain and OpInBoundsAccessChain are considered simple
- // instructions since they result in a pointer to the object in memory,
- // not the object itself.
- case spv::Op::OpVectorExtractDynamic:
- case spv::Op::OpVectorInsertDynamic:
- case spv::Op::OpVectorShuffle:
- case spv::Op::OpCompositeConstruct:
- case spv::Op::OpCompositeExtract:
- case spv::Op::OpCompositeInsert:
- case spv::Op::OpCopyObject:
- case spv::Op::OpTranspose:
- case spv::Op::OpConvertFToU:
- case spv::Op::OpConvertFToS:
- case spv::Op::OpConvertSToF:
- case spv::Op::OpConvertUToF:
- case spv::Op::OpUConvert:
- case spv::Op::OpSConvert:
- case spv::Op::OpFConvert:
- case spv::Op::OpQuantizeToF16:
- case spv::Op::OpSatConvertSToU:
- case spv::Op::OpSatConvertUToS:
- case spv::Op::OpBitcast:
- case spv::Op::OpSNegate:
- case spv::Op::OpFNegate:
- case spv::Op::OpIAdd:
- case spv::Op::OpFAdd:
- case spv::Op::OpISub:
- case spv::Op::OpFSub:
- case spv::Op::OpIMul:
- case spv::Op::OpFMul:
- case spv::Op::OpUDiv:
- case spv::Op::OpSDiv:
- case spv::Op::OpFDiv:
- case spv::Op::OpUMod:
- case spv::Op::OpSRem:
- case spv::Op::OpSMod:
- case spv::Op::OpFRem:
- case spv::Op::OpFMod:
- case spv::Op::OpVectorTimesScalar:
- case spv::Op::OpMatrixTimesScalar:
- case spv::Op::OpVectorTimesMatrix:
- case spv::Op::OpMatrixTimesVector:
- case spv::Op::OpMatrixTimesMatrix:
- case spv::Op::OpOuterProduct:
- case spv::Op::OpDot:
- case spv::Op::OpIAddCarry:
- case spv::Op::OpISubBorrow:
- case spv::Op::OpUMulExtended:
- case spv::Op::OpSMulExtended:
- case spv::Op::OpAny:
- case spv::Op::OpAll:
- case spv::Op::OpIsNan:
- case spv::Op::OpIsInf:
- case spv::Op::OpIsFinite:
- case spv::Op::OpIsNormal:
- case spv::Op::OpSignBitSet:
- case spv::Op::OpLessOrGreater:
- case spv::Op::OpOrdered:
- case spv::Op::OpUnordered:
- case spv::Op::OpLogicalEqual:
- case spv::Op::OpLogicalNotEqual:
- case spv::Op::OpLogicalOr:
- case spv::Op::OpLogicalAnd:
- case spv::Op::OpLogicalNot:
- case spv::Op::OpSelect:
- case spv::Op::OpIEqual:
- case spv::Op::OpINotEqual:
- case spv::Op::OpUGreaterThan:
- case spv::Op::OpSGreaterThan:
- case spv::Op::OpUGreaterThanEqual:
- case spv::Op::OpSGreaterThanEqual:
- case spv::Op::OpULessThan:
- case spv::Op::OpSLessThan:
- case spv::Op::OpULessThanEqual:
- case spv::Op::OpSLessThanEqual:
- case spv::Op::OpFOrdEqual:
- case spv::Op::OpFUnordEqual:
- case spv::Op::OpFOrdNotEqual:
- case spv::Op::OpFUnordNotEqual:
- case spv::Op::OpFOrdLessThan:
- case spv::Op::OpFUnordLessThan:
- case spv::Op::OpFOrdGreaterThan:
- case spv::Op::OpFUnordGreaterThan:
- case spv::Op::OpFOrdLessThanEqual:
- case spv::Op::OpFUnordLessThanEqual:
- case spv::Op::OpFOrdGreaterThanEqual:
- case spv::Op::OpFUnordGreaterThanEqual:
- case spv::Op::OpShiftRightLogical:
- case spv::Op::OpShiftRightArithmetic:
- case spv::Op::OpShiftLeftLogical:
- case spv::Op::OpBitwiseOr:
- case spv::Op::OpBitwiseXor:
- case spv::Op::OpBitwiseAnd:
- case spv::Op::OpNot:
- case spv::Op::OpBitFieldInsert:
- case spv::Op::OpBitFieldSExtract:
- case spv::Op::OpBitFieldUExtract:
- case spv::Op::OpBitReverse:
- case spv::Op::OpBitCount:
- case spv::Op::OpCopyLogical:
- return true;
- case spv::Op::OpExtInst: {
- const auto* ext_inst_import =
- ir_context->get_def_use_mgr()->GetDef(inst.GetSingleWordInOperand(0));
- if (ext_inst_import->GetInOperand(0).AsString() != kExtensionSetName) {
- return false;
- }
- switch (static_cast<GLSLstd450>(inst.GetSingleWordInOperand(1))) {
- case GLSLstd450Round:
- case GLSLstd450RoundEven:
- case GLSLstd450Trunc:
- case GLSLstd450FAbs:
- case GLSLstd450SAbs:
- case GLSLstd450FSign:
- case GLSLstd450SSign:
- case GLSLstd450Floor:
- case GLSLstd450Ceil:
- case GLSLstd450Fract:
- case GLSLstd450Radians:
- case GLSLstd450Degrees:
- case GLSLstd450Sin:
- case GLSLstd450Cos:
- case GLSLstd450Tan:
- case GLSLstd450Asin:
- case GLSLstd450Acos:
- case GLSLstd450Atan:
- case GLSLstd450Sinh:
- case GLSLstd450Cosh:
- case GLSLstd450Tanh:
- case GLSLstd450Asinh:
- case GLSLstd450Acosh:
- case GLSLstd450Atanh:
- case GLSLstd450Atan2:
- case GLSLstd450Pow:
- case GLSLstd450Exp:
- case GLSLstd450Log:
- case GLSLstd450Exp2:
- case GLSLstd450Log2:
- case GLSLstd450Sqrt:
- case GLSLstd450InverseSqrt:
- case GLSLstd450Determinant:
- case GLSLstd450MatrixInverse:
- case GLSLstd450ModfStruct:
- case GLSLstd450FMin:
- case GLSLstd450UMin:
- case GLSLstd450SMin:
- case GLSLstd450FMax:
- case GLSLstd450UMax:
- case GLSLstd450SMax:
- case GLSLstd450FClamp:
- case GLSLstd450UClamp:
- case GLSLstd450SClamp:
- case GLSLstd450FMix:
- case GLSLstd450IMix:
- case GLSLstd450Step:
- case GLSLstd450SmoothStep:
- case GLSLstd450Fma:
- case GLSLstd450FrexpStruct:
- case GLSLstd450Ldexp:
- case GLSLstd450PackSnorm4x8:
- case GLSLstd450PackUnorm4x8:
- case GLSLstd450PackSnorm2x16:
- case GLSLstd450PackUnorm2x16:
- case GLSLstd450PackHalf2x16:
- case GLSLstd450PackDouble2x32:
- case GLSLstd450UnpackSnorm2x16:
- case GLSLstd450UnpackUnorm2x16:
- case GLSLstd450UnpackHalf2x16:
- case GLSLstd450UnpackSnorm4x8:
- case GLSLstd450UnpackUnorm4x8:
- case GLSLstd450UnpackDouble2x32:
- case GLSLstd450Length:
- case GLSLstd450Distance:
- case GLSLstd450Cross:
- case GLSLstd450Normalize:
- case GLSLstd450FaceForward:
- case GLSLstd450Reflect:
- case GLSLstd450Refract:
- case GLSLstd450FindILsb:
- case GLSLstd450FindSMsb:
- case GLSLstd450FindUMsb:
- case GLSLstd450NMin:
- case GLSLstd450NMax:
- case GLSLstd450NClamp:
- return true;
- default:
- return false;
- }
- }
- default:
- return false;
- }
- }
- bool TransformationMoveInstructionDown::IsMemoryReadInstruction(
- opt::IRContext* ir_context, const opt::Instruction& inst) {
- switch (inst.opcode()) {
- // Some simple instructions.
- case spv::Op::OpLoad:
- case spv::Op::OpCopyMemory:
- // Image instructions.
- case spv::Op::OpImageSampleImplicitLod:
- case spv::Op::OpImageSampleExplicitLod:
- case spv::Op::OpImageSampleDrefImplicitLod:
- case spv::Op::OpImageSampleDrefExplicitLod:
- case spv::Op::OpImageSampleProjImplicitLod:
- case spv::Op::OpImageSampleProjExplicitLod:
- case spv::Op::OpImageSampleProjDrefImplicitLod:
- case spv::Op::OpImageSampleProjDrefExplicitLod:
- case spv::Op::OpImageFetch:
- case spv::Op::OpImageGather:
- case spv::Op::OpImageDrefGather:
- case spv::Op::OpImageRead:
- case spv::Op::OpImageSparseSampleImplicitLod:
- case spv::Op::OpImageSparseSampleExplicitLod:
- case spv::Op::OpImageSparseSampleDrefImplicitLod:
- case spv::Op::OpImageSparseSampleDrefExplicitLod:
- case spv::Op::OpImageSparseSampleProjImplicitLod:
- case spv::Op::OpImageSparseSampleProjExplicitLod:
- case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
- case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
- case spv::Op::OpImageSparseFetch:
- case spv::Op::OpImageSparseGather:
- case spv::Op::OpImageSparseDrefGather:
- case spv::Op::OpImageSparseRead:
- // Atomic instructions.
- case spv::Op::OpAtomicLoad:
- case spv::Op::OpAtomicExchange:
- case spv::Op::OpAtomicCompareExchange:
- case spv::Op::OpAtomicCompareExchangeWeak:
- case spv::Op::OpAtomicIIncrement:
- case spv::Op::OpAtomicIDecrement:
- case spv::Op::OpAtomicIAdd:
- case spv::Op::OpAtomicISub:
- case spv::Op::OpAtomicSMin:
- case spv::Op::OpAtomicUMin:
- case spv::Op::OpAtomicSMax:
- case spv::Op::OpAtomicUMax:
- case spv::Op::OpAtomicAnd:
- case spv::Op::OpAtomicOr:
- case spv::Op::OpAtomicXor:
- case spv::Op::OpAtomicFlagTestAndSet:
- return true;
- // Extensions.
- case spv::Op::OpExtInst: {
- if (GetExtensionSet(ir_context, inst) != kExtensionSetName) {
- return false;
- }
- switch (static_cast<GLSLstd450>(inst.GetSingleWordInOperand(1))) {
- case GLSLstd450InterpolateAtCentroid:
- case GLSLstd450InterpolateAtOffset:
- case GLSLstd450InterpolateAtSample:
- return true;
- default:
- return false;
- }
- }
- default:
- return false;
- }
- }
- uint32_t TransformationMoveInstructionDown::GetMemoryReadTarget(
- opt::IRContext* ir_context, const opt::Instruction& inst) {
- (void)ir_context; // |ir_context| is only used in assertions.
- assert(IsMemoryReadInstruction(ir_context, inst) &&
- "|inst| is not a memory read instruction");
- switch (inst.opcode()) {
- // Simple instructions.
- case spv::Op::OpLoad:
- // Image instructions.
- case spv::Op::OpImageSampleImplicitLod:
- case spv::Op::OpImageSampleExplicitLod:
- case spv::Op::OpImageSampleDrefImplicitLod:
- case spv::Op::OpImageSampleDrefExplicitLod:
- case spv::Op::OpImageSampleProjImplicitLod:
- case spv::Op::OpImageSampleProjExplicitLod:
- case spv::Op::OpImageSampleProjDrefImplicitLod:
- case spv::Op::OpImageSampleProjDrefExplicitLod:
- case spv::Op::OpImageFetch:
- case spv::Op::OpImageGather:
- case spv::Op::OpImageDrefGather:
- case spv::Op::OpImageRead:
- case spv::Op::OpImageSparseSampleImplicitLod:
- case spv::Op::OpImageSparseSampleExplicitLod:
- case spv::Op::OpImageSparseSampleDrefImplicitLod:
- case spv::Op::OpImageSparseSampleDrefExplicitLod:
- case spv::Op::OpImageSparseSampleProjImplicitLod:
- case spv::Op::OpImageSparseSampleProjExplicitLod:
- case spv::Op::OpImageSparseSampleProjDrefImplicitLod:
- case spv::Op::OpImageSparseSampleProjDrefExplicitLod:
- case spv::Op::OpImageSparseFetch:
- case spv::Op::OpImageSparseGather:
- case spv::Op::OpImageSparseDrefGather:
- case spv::Op::OpImageSparseRead:
- // Atomic instructions.
- case spv::Op::OpAtomicLoad:
- case spv::Op::OpAtomicExchange:
- case spv::Op::OpAtomicCompareExchange:
- case spv::Op::OpAtomicCompareExchangeWeak:
- case spv::Op::OpAtomicIIncrement:
- case spv::Op::OpAtomicIDecrement:
- case spv::Op::OpAtomicIAdd:
- case spv::Op::OpAtomicISub:
- case spv::Op::OpAtomicSMin:
- case spv::Op::OpAtomicUMin:
- case spv::Op::OpAtomicSMax:
- case spv::Op::OpAtomicUMax:
- case spv::Op::OpAtomicAnd:
- case spv::Op::OpAtomicOr:
- case spv::Op::OpAtomicXor:
- case spv::Op::OpAtomicFlagTestAndSet:
- return inst.GetSingleWordInOperand(0);
- case spv::Op::OpCopyMemory:
- return inst.GetSingleWordInOperand(1);
- case spv::Op::OpExtInst: {
- assert(GetExtensionSet(ir_context, inst) == kExtensionSetName &&
- "Extension set is not supported");
- switch (static_cast<GLSLstd450>(inst.GetSingleWordInOperand(1))) {
- case GLSLstd450InterpolateAtCentroid:
- case GLSLstd450InterpolateAtOffset:
- case GLSLstd450InterpolateAtSample:
- return inst.GetSingleWordInOperand(2);
- default:
- // This assertion will fail if not all memory read extension
- // instructions are handled in the switch.
- assert(false && "Not all memory opcodes are handled");
- return 0;
- }
- }
- default:
- // This assertion will fail if not all memory read opcodes are handled in
- // the switch.
- assert(false && "Not all memory opcodes are handled");
- return 0;
- }
- }
- bool TransformationMoveInstructionDown::IsMemoryWriteInstruction(
- opt::IRContext* ir_context, const opt::Instruction& inst) {
- switch (inst.opcode()) {
- // Simple Instructions.
- case spv::Op::OpStore:
- case spv::Op::OpCopyMemory:
- // Image instructions.
- case spv::Op::OpImageWrite:
- // Atomic instructions.
- case spv::Op::OpAtomicStore:
- case spv::Op::OpAtomicExchange:
- case spv::Op::OpAtomicCompareExchange:
- case spv::Op::OpAtomicCompareExchangeWeak:
- case spv::Op::OpAtomicIIncrement:
- case spv::Op::OpAtomicIDecrement:
- case spv::Op::OpAtomicIAdd:
- case spv::Op::OpAtomicISub:
- case spv::Op::OpAtomicSMin:
- case spv::Op::OpAtomicUMin:
- case spv::Op::OpAtomicSMax:
- case spv::Op::OpAtomicUMax:
- case spv::Op::OpAtomicAnd:
- case spv::Op::OpAtomicOr:
- case spv::Op::OpAtomicXor:
- case spv::Op::OpAtomicFlagTestAndSet:
- case spv::Op::OpAtomicFlagClear:
- return true;
- // Extensions.
- case spv::Op::OpExtInst: {
- if (GetExtensionSet(ir_context, inst) != kExtensionSetName) {
- return false;
- }
- auto extension = static_cast<GLSLstd450>(inst.GetSingleWordInOperand(1));
- return extension == GLSLstd450Modf || extension == GLSLstd450Frexp;
- }
- default:
- return false;
- }
- }
- uint32_t TransformationMoveInstructionDown::GetMemoryWriteTarget(
- opt::IRContext* ir_context, const opt::Instruction& inst) {
- (void)ir_context; // |ir_context| is only used in assertions.
- assert(IsMemoryWriteInstruction(ir_context, inst) &&
- "|inst| is not a memory write instruction");
- switch (inst.opcode()) {
- case spv::Op::OpStore:
- case spv::Op::OpCopyMemory:
- case spv::Op::OpImageWrite:
- case spv::Op::OpAtomicStore:
- case spv::Op::OpAtomicExchange:
- case spv::Op::OpAtomicCompareExchange:
- case spv::Op::OpAtomicCompareExchangeWeak:
- case spv::Op::OpAtomicIIncrement:
- case spv::Op::OpAtomicIDecrement:
- case spv::Op::OpAtomicIAdd:
- case spv::Op::OpAtomicISub:
- case spv::Op::OpAtomicSMin:
- case spv::Op::OpAtomicUMin:
- case spv::Op::OpAtomicSMax:
- case spv::Op::OpAtomicUMax:
- case spv::Op::OpAtomicAnd:
- case spv::Op::OpAtomicOr:
- case spv::Op::OpAtomicXor:
- case spv::Op::OpAtomicFlagTestAndSet:
- case spv::Op::OpAtomicFlagClear:
- return inst.GetSingleWordInOperand(0);
- case spv::Op::OpExtInst: {
- assert(GetExtensionSet(ir_context, inst) == kExtensionSetName &&
- "Extension set is not supported");
- switch (static_cast<GLSLstd450>(inst.GetSingleWordInOperand(1))) {
- case GLSLstd450Modf:
- case GLSLstd450Frexp:
- return inst.GetSingleWordInOperand(3);
- default:
- // This assertion will fail if not all memory write extension
- // instructions are handled in the switch.
- assert(false && "Not all opcodes are handled");
- return 0;
- }
- }
- default:
- // This assertion will fail if not all memory write opcodes are handled in
- // the switch.
- assert(false && "Not all opcodes are handled");
- return 0;
- }
- }
- bool TransformationMoveInstructionDown::IsMemoryInstruction(
- opt::IRContext* ir_context, const opt::Instruction& inst) {
- return IsMemoryReadInstruction(ir_context, inst) ||
- IsMemoryWriteInstruction(ir_context, inst);
- }
- bool TransformationMoveInstructionDown::IsBarrierInstruction(
- const opt::Instruction& inst) {
- switch (inst.opcode()) {
- case spv::Op::OpMemoryBarrier:
- case spv::Op::OpControlBarrier:
- case spv::Op::OpMemoryNamedBarrier:
- return true;
- default:
- return false;
- }
- }
- bool TransformationMoveInstructionDown::CanSafelySwapInstructions(
- opt::IRContext* ir_context, const opt::Instruction& a,
- const opt::Instruction& b, const FactManager& fact_manager) {
- assert(IsInstructionSupported(ir_context, a) &&
- IsInstructionSupported(ir_context, b) &&
- "Both opcodes must be supported");
- // One of opcodes is simple - we can swap them without any side-effects.
- if (IsSimpleInstruction(ir_context, a) ||
- IsSimpleInstruction(ir_context, b)) {
- return true;
- }
- // Both parameters are either memory instruction or barriers.
- // One of the opcodes is a barrier - can't swap them.
- if (IsBarrierInstruction(a) || IsBarrierInstruction(b)) {
- return false;
- }
- // Both parameters are memory instructions.
- // Both parameters only read from memory - it's OK to swap them.
- if (!IsMemoryWriteInstruction(ir_context, a) &&
- !IsMemoryWriteInstruction(ir_context, b)) {
- return true;
- }
- // At least one of parameters is a memory read instruction.
- // In theory, we can swap two memory instructions, one of which reads
- // from the memory, if the read target (the pointer the memory is read from)
- // and the write target (the memory is written into):
- // - point to different memory regions
- // - point to the same region with irrelevant value
- // - point to the same region and the region is not used anymore.
- //
- // However, we can't currently determine if two pointers point to two
- // different memory regions. That being said, if two pointers are not
- // synonymous, they still might point to the same memory region. For example:
- // %1 = OpVariable ...
- // %2 = OpAccessChain %1 0
- // %3 = OpAccessChain %1 0
- // In this pseudo-code, %2 and %3 are not synonymous but point to the same
- // memory location. This implies that we can't determine if some memory
- // location is not used in the block.
- //
- // With this in mind, consider two cases (we will build a table for each one):
- // - one instruction only reads from memory, the other one only writes to it.
- // S - both point to the same memory region.
- // D - both point to different memory regions.
- // 0, 1, 2 - neither, one of or both of the memory regions are irrelevant.
- // |-| - can't swap; |+| - can swap.
- // | 0 | 1 | 2 |
- // S : - + +
- // D : + + +
- // - both instructions write to memory. Notation is the same.
- // | 0 | 1 | 2 |
- // S : * + +
- // D : + + +
- // * - we can swap two instructions that write into the same non-irrelevant
- // memory region if the written value is the same.
- //
- // Note that we can't always distinguish between S and D. Also note that
- // in case of S, if one of the instructions is marked with
- // PointeeValueIsIrrelevant, then the pointee of the other one is irrelevant
- // as well even if the instruction is not marked with that fact.
- //
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3723):
- // This procedure can be improved when we can determine if two pointers point
- // to different memory regions.
- // From now on we will denote an instruction that:
- // - only reads from memory - R
- // - only writes into memory - W
- // - reads and writes - RW
- //
- // Both |a| and |b| can be either W or RW at this point. Additionally, at most
- // one of them can be R. The procedure below checks all possible combinations
- // of R, W and RW according to the tables above. We conservatively assume that
- // both |a| and |b| point to the same memory region.
- auto memory_is_irrelevant = [ir_context, &fact_manager](uint32_t id) {
- const auto* inst = ir_context->get_def_use_mgr()->GetDef(id);
- if (!inst->type_id()) {
- return false;
- }
- const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
- assert(type && "|id| has invalid type");
- if (!type->AsPointer()) {
- return false;
- }
- return fact_manager.PointeeValueIsIrrelevant(id);
- };
- if (IsMemoryWriteInstruction(ir_context, a) &&
- IsMemoryWriteInstruction(ir_context, b) &&
- (memory_is_irrelevant(GetMemoryWriteTarget(ir_context, a)) ||
- memory_is_irrelevant(GetMemoryWriteTarget(ir_context, b)))) {
- // We ignore the case when the written value is the same. This is because
- // the written value might not be equal to any of the instruction's
- // operands.
- return true;
- }
- if (IsMemoryReadInstruction(ir_context, a) &&
- IsMemoryWriteInstruction(ir_context, b) &&
- !memory_is_irrelevant(GetMemoryReadTarget(ir_context, a)) &&
- !memory_is_irrelevant(GetMemoryWriteTarget(ir_context, b))) {
- return false;
- }
- if (IsMemoryWriteInstruction(ir_context, a) &&
- IsMemoryReadInstruction(ir_context, b) &&
- !memory_is_irrelevant(GetMemoryWriteTarget(ir_context, a)) &&
- !memory_is_irrelevant(GetMemoryReadTarget(ir_context, b))) {
- return false;
- }
- return IsMemoryReadInstruction(ir_context, a) ||
- IsMemoryReadInstruction(ir_context, b);
- }
- std::unordered_set<uint32_t> TransformationMoveInstructionDown::GetFreshIds()
- const {
- return std::unordered_set<uint32_t>();
- }
- } // namespace fuzz
- } // namespace spvtools
|