spirv-fuzz: TransformationMoveInstructionDown (#3477)

Swaps an instruction with the next instruction in the block.

Fixes #3457.
This commit is contained in:
Vasyl Teliman 2020-08-03 18:45:24 +03:00 committed by GitHub
parent b78f4b1518
commit 92a71657fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 580 additions and 0 deletions

View File

@ -72,6 +72,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_outline_functions.h
fuzzer_pass_permute_blocks.h
fuzzer_pass_permute_function_parameters.h
fuzzer_pass_permute_instructions.h
fuzzer_pass_permute_phi_operands.h
fuzzer_pass_push_ids_through_variables.h
fuzzer_pass_replace_copy_memories_with_loads_stores.h
@ -134,6 +135,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_load.h
transformation_merge_blocks.h
transformation_move_block_down.h
transformation_move_instruction_down.h
transformation_outline_function.h
transformation_permute_function_parameters.h
transformation_permute_phi_operands.h
@ -203,6 +205,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_outline_functions.cpp
fuzzer_pass_permute_blocks.cpp
fuzzer_pass_permute_function_parameters.cpp
fuzzer_pass_permute_instructions.cpp
fuzzer_pass_permute_phi_operands.cpp
fuzzer_pass_push_ids_through_variables.cpp
fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
@ -264,6 +267,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_load.cpp
transformation_merge_blocks.cpp
transformation_move_block_down.cpp
transformation_move_instruction_down.cpp
transformation_outline_function.cpp
transformation_permute_function_parameters.cpp
transformation_permute_phi_operands.cpp

View File

@ -54,6 +54,7 @@
#include "source/fuzz/fuzzer_pass_outline_functions.h"
#include "source/fuzz/fuzzer_pass_permute_blocks.h"
#include "source/fuzz/fuzzer_pass_permute_function_parameters.h"
#include "source/fuzz/fuzzer_pass_permute_instructions.h"
#include "source/fuzz/fuzzer_pass_permute_phi_operands.h"
#include "source/fuzz/fuzzer_pass_push_ids_through_variables.h"
#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h"
@ -281,6 +282,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
MaybeAddPass<FuzzerPassPermuteFunctionParameters>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassPermuteInstructions>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassPushIdsThroughVariables>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);

View File

@ -76,6 +76,7 @@ const std::pair<uint32_t, uint32_t> kChanceOfMergingBlocks = {20, 95};
const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfOutliningFunction = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfPermutingInstructions = {20, 70};
const std::pair<uint32_t, uint32_t> kChanceOfPermutingParameters = {30, 90};
const std::pair<uint32_t, uint32_t> kChanceOfPermutingPhiOperands = {30, 90};
const std::pair<uint32_t, uint32_t> kChanceOfPushingIdThroughVariable = {5, 50};
@ -214,6 +215,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
ChooseBetweenMinAndMax(kChanceOfObfuscatingConstant);
chance_of_outlining_function_ =
ChooseBetweenMinAndMax(kChanceOfOutliningFunction);
chance_of_permuting_instructions_ =
ChooseBetweenMinAndMax(kChanceOfPermutingInstructions);
chance_of_permuting_parameters_ =
ChooseBetweenMinAndMax(kChanceOfPermutingParameters);
chance_of_permuting_phi_operands_ =

View File

@ -206,6 +206,9 @@ class FuzzerContext {
uint32_t GetChanceOfOutliningFunction() {
return chance_of_outlining_function_;
}
uint32_t GetChanceOfPermutingInstructions() {
return chance_of_permuting_instructions_;
}
uint32_t GetChanceOfPermutingParameters() {
return chance_of_permuting_parameters_;
}
@ -364,6 +367,7 @@ class FuzzerContext {
uint32_t chance_of_moving_block_down_;
uint32_t chance_of_obfuscating_constant_;
uint32_t chance_of_outlining_function_;
uint32_t chance_of_permuting_instructions_;
uint32_t chance_of_permuting_parameters_;
uint32_t chance_of_permuting_phi_operands_;
uint32_t chance_of_pushing_id_through_variable_;

View File

@ -0,0 +1,64 @@
// 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/fuzzer_pass_permute_instructions.h"
#include "source/fuzz/fuzzer_context.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/instruction_descriptor.h"
#include "source/fuzz/transformation_move_instruction_down.h"
namespace spvtools {
namespace fuzz {
FuzzerPassPermuteInstructions::FuzzerPassPermuteInstructions(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
transformations) {}
FuzzerPassPermuteInstructions::~FuzzerPassPermuteInstructions() = default;
void FuzzerPassPermuteInstructions::Apply() {
// We are iterating over all instructions in all basic blocks.
for (auto& function : *GetIRContext()->module()) {
for (auto& block : function) {
// We need to collect all instructions in the block into a separate vector
// since application of the transformation below might invalidate
// iterators.
std::vector<opt::Instruction*> instructions;
for (auto& instruction : block) {
instructions.push_back(&instruction);
}
// We consider all instructions in reverse to increase the possible number
// of applied transformations.
for (auto it = instructions.rbegin(); it != instructions.rend(); ++it) {
if (!GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()->GetChanceOfPermutingInstructions())) {
continue;
}
while (MaybeApplyTransformation(TransformationMoveInstructionDown(
MakeInstructionDescriptor(GetIRContext(), *it)))) {
// Apply the transformation as many times as possible.
}
}
}
}
}
} // namespace fuzz
} // namespace spvtools

View File

@ -0,0 +1,40 @@
// 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.
#ifndef SOURCE_FUZZ_FUZZER_PASS_PERMUTE_INSTRUCTIONS_H_
#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_INSTRUCTIONS_H_
#include "source/fuzz/fuzzer_pass.h"
namespace spvtools {
namespace fuzz {
// Permutes instructions in every block of all while preserving the module's
// semantics.
class FuzzerPassPermuteInstructions : public FuzzerPass {
public:
FuzzerPassPermuteInstructions(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations);
~FuzzerPassPermuteInstructions() override;
void Apply() override;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_INSTRUCTIONS_H_

View File

@ -408,6 +408,7 @@ message Transformation {
TransformationReplaceCopyMemoryWithLoadStore replace_copy_memory_with_load_store = 61;
TransformationReplaceLoadStoreWithCopyMemory replace_load_store_with_copy_memory = 62;
TransformationAddLoopPreheader add_loop_preheader = 63;
TransformationMoveInstructionDown move_instruction_down = 64;
// Add additional option using the next available number.
}
}
@ -1124,6 +1125,15 @@ message TransformationMoveBlockDown {
uint32 block_id = 1;
}
message TransformationMoveInstructionDown {
// Swaps |instruction| with the next instruction in the block.
// The instruction to move down.
InstructionDescriptor instruction = 1;
}
message TransformationOutlineFunction {
// A transformation that outlines a single-entry single-exit region of a

View File

@ -56,6 +56,7 @@
#include "source/fuzz/transformation_load.h"
#include "source/fuzz/transformation_merge_blocks.h"
#include "source/fuzz/transformation_move_block_down.h"
#include "source/fuzz/transformation_move_instruction_down.h"
#include "source/fuzz/transformation_outline_function.h"
#include "source/fuzz/transformation_permute_function_parameters.h"
#include "source/fuzz/transformation_permute_phi_operands.h"
@ -194,6 +195,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
return MakeUnique<TransformationMergeBlocks>(message.merge_blocks());
case protobufs::Transformation::TransformationCase::kMoveBlockDown:
return MakeUnique<TransformationMoveBlockDown>(message.move_block_down());
case protobufs::Transformation::TransformationCase::kMoveInstructionDown:
return MakeUnique<TransformationMoveInstructionDown>(
message.move_instruction_down());
case protobufs::Transformation::TransformationCase::kOutlineFunction:
return MakeUnique<TransformationOutlineFunction>(
message.outline_function());

View File

@ -0,0 +1,219 @@
// 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

View File

@ -0,0 +1,62 @@
// 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.
#ifndef SOURCE_FUZZ_TRANSFORMATION_MOVE_INSTRUCTION_DOWN_H_
#define SOURCE_FUZZ_TRANSFORMATION_MOVE_INSTRUCTION_DOWN_H_
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/fuzz/transformation.h"
#include "source/fuzz/transformation_context.h"
#include "source/opt/ir_context.h"
namespace spvtools {
namespace fuzz {
class TransformationMoveInstructionDown : public Transformation {
public:
explicit TransformationMoveInstructionDown(
const protobufs::TransformationMoveInstructionDown& message);
explicit TransformationMoveInstructionDown(
const protobufs::InstructionDescriptor& instruction);
// - |instruction| should be a descriptor of a valid instruction in the module
// - |instruction|'s opcode should be supported by this transformation
// - neither |instruction| nor its successor may be the last instruction in
// the block
// - |instruction|'s successor may not be dependent on the |instruction|
// - it should be possible to insert |instruction|'s opcode after its
// successor
bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;
// Swaps |instruction| with its successor.
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
protobufs::Transformation ToMessage() const override;
private:
// Returns true if the |opcode| is supported by this transformation (i.e.
// we can move an instruction with this opcode).
static bool IsOpcodeSupported(SpvOp opcode);
protobufs::TransformationMoveInstructionDown message_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_TRANSFORMATION_MOVE_INSTRUCTION_DOWN_H_

View File

@ -63,6 +63,7 @@ if (${SPIRV_BUILD_FUZZER})
transformation_load_test.cpp
transformation_merge_blocks_test.cpp
transformation_move_block_down_test.cpp
transformation_move_instruction_down_test.cpp
transformation_outline_function_test.cpp
transformation_permute_function_parameters_test.cpp
transformation_permute_phi_operands_test.cpp

View File

@ -0,0 +1,165 @@
// 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/instruction_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationMoveInstructionDownTest, BasicTest) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%9 = OpConstant %6 0
%16 = OpTypeBool
%17 = OpConstantFalse %16
%20 = OpUndef %6
%13 = OpTypePointer Function %6
%4 = OpFunction %2 None %3
%5 = OpLabel
%12 = OpVariable %13 Function
%10 = OpIAdd %6 %9 %9
%11 = OpISub %6 %9 %10
OpStore %12 %10
%14 = OpLoad %6 %12
%15 = OpIMul %6 %9 %14
OpSelectionMerge %19 None
OpBranchConditional %17 %18 %19
%18 = OpLabel
OpBranch %19
%19 = OpLabel
%22 = OpIAdd %6 %15 %15
%21 = OpIAdd %6 %15 %15
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
validator_options);
// Instruction descriptor is invalid.
ASSERT_FALSE(TransformationMoveInstructionDown(
MakeInstructionDescriptor(30, SpvOpNop, 0))
.IsApplicable(context.get(), transformation_context));
// Opcode is not supported.
ASSERT_FALSE(TransformationMoveInstructionDown(
MakeInstructionDescriptor(5, SpvOpLabel, 0))
.IsApplicable(context.get(), transformation_context));
ASSERT_FALSE(TransformationMoveInstructionDown(
MakeInstructionDescriptor(12, SpvOpVariable, 0))
.IsApplicable(context.get(), transformation_context));
ASSERT_FALSE(TransformationMoveInstructionDown(
MakeInstructionDescriptor(11, SpvOpStore, 0))
.IsApplicable(context.get(), transformation_context));
ASSERT_FALSE(TransformationMoveInstructionDown(
MakeInstructionDescriptor(14, SpvOpLoad, 0))
.IsApplicable(context.get(), transformation_context));
// Can't move the last instruction in the block.
ASSERT_FALSE(TransformationMoveInstructionDown(
MakeInstructionDescriptor(15, SpvOpBranchConditional, 0))
.IsApplicable(context.get(), transformation_context));
// Can't move the instruction if the next instruction is the last one in the
// block.
ASSERT_FALSE(TransformationMoveInstructionDown(
MakeInstructionDescriptor(21, SpvOpIAdd, 0))
.IsApplicable(context.get(), transformation_context));
// Can't insert instruction's opcode after its successor.
ASSERT_FALSE(TransformationMoveInstructionDown(
MakeInstructionDescriptor(15, SpvOpIMul, 0))
.IsApplicable(context.get(), transformation_context));
// Instruction's successor depends on the instruction.
ASSERT_FALSE(TransformationMoveInstructionDown(
MakeInstructionDescriptor(10, SpvOpIAdd, 0))
.IsApplicable(context.get(), transformation_context));
{
TransformationMoveInstructionDown transformation(
MakeInstructionDescriptor(11, SpvOpISub, 0));
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
transformation.Apply(context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
}
{
TransformationMoveInstructionDown transformation(
MakeInstructionDescriptor(22, SpvOpIAdd, 0));
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
transformation.Apply(context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
}
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%9 = OpConstant %6 0
%16 = OpTypeBool
%17 = OpConstantFalse %16
%20 = OpUndef %6
%13 = OpTypePointer Function %6
%4 = OpFunction %2 None %3
%5 = OpLabel
%12 = OpVariable %13 Function
%10 = OpIAdd %6 %9 %9
OpStore %12 %10
%11 = OpISub %6 %9 %10
%14 = OpLoad %6 %12
%15 = OpIMul %6 %9 %14
OpSelectionMerge %19 None
OpBranchConditional %17 %18 %19
%18 = OpLabel
OpBranch %19
%19 = OpLabel
%21 = OpIAdd %6 %15 %15
%22 = OpIAdd %6 %15 %15
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools