spirv-fuzz: Swap operands in OpBranchConditional (#3423)
Fixes #3415.
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index 2eae79a..547256f 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -68,6 +68,7 @@
fuzzer_pass_replace_linear_algebra_instructions.h
fuzzer_pass_split_blocks.h
fuzzer_pass_swap_commutable_operands.h
+ fuzzer_pass_swap_conditional_branch_operands.h
fuzzer_pass_toggle_access_chain_instruction.h
fuzzer_util.h
id_use_descriptor.h
@@ -127,6 +128,7 @@
transformation_split_block.h
transformation_store.h
transformation_swap_commutable_operands.h
+ transformation_swap_conditional_branch_operands.h
transformation_toggle_access_chain_instruction.h
transformation_vector_shuffle.h
uniform_buffer_element_descriptor.h
@@ -170,6 +172,7 @@
fuzzer_pass_replace_linear_algebra_instructions.cpp
fuzzer_pass_split_blocks.cpp
fuzzer_pass_swap_commutable_operands.cpp
+ fuzzer_pass_swap_conditional_branch_operands.cpp
fuzzer_pass_toggle_access_chain_instruction.cpp
fuzzer_util.cpp
id_use_descriptor.cpp
@@ -228,6 +231,7 @@
transformation_split_block.cpp
transformation_store.cpp
transformation_swap_commutable_operands.cpp
+ transformation_swap_conditional_branch_operands.cpp
transformation_toggle_access_chain_instruction.cpp
transformation_vector_shuffle.cpp
uniform_buffer_element_descriptor.cpp
diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp
index 420926a..3d738f7 100644
--- a/source/fuzz/fuzzer.cpp
+++ b/source/fuzz/fuzzer.cpp
@@ -51,6 +51,7 @@
#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h"
#include "source/fuzz/fuzzer_pass_split_blocks.h"
#include "source/fuzz/fuzzer_pass_swap_commutable_operands.h"
+#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h"
#include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h"
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/fuzz/pseudo_random_generator.h"
@@ -264,6 +265,9 @@
MaybeAddPass<FuzzerPassSplitBlocks>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
+ MaybeAddPass<FuzzerPassSwapBranchConditionalOperands>(
+ &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+ transformation_sequence_out);
}
bool is_first = true;
diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp
index ef0de5e..58ec531 100644
--- a/source/fuzz/fuzzer_context.cpp
+++ b/source/fuzz/fuzzer_context.cpp
@@ -68,6 +68,8 @@
const std::pair<uint32_t, uint32_t>
kChanceOfReplacingLinearAlgebraInstructions = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
+const std::pair<uint32_t, uint32_t> kChanceOfSwappingConditionalBranchOperands =
+ {10, 70};
const std::pair<uint32_t, uint32_t> kChanceOfTogglingAccessChainInstruction = {
20, 90};
@@ -170,6 +172,8 @@
chance_of_replacing_linear_algebra_instructions_ =
ChooseBetweenMinAndMax(kChanceOfReplacingLinearAlgebraInstructions);
chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
+ chance_of_swapping_conditional_branch_operands_ =
+ ChooseBetweenMinAndMax(kChanceOfSwappingConditionalBranchOperands);
chance_of_toggling_access_chain_instruction_ =
ChooseBetweenMinAndMax(kChanceOfTogglingAccessChainInstruction);
}
diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h
index 5f1d3be..d73f814 100644
--- a/source/fuzz/fuzzer_context.h
+++ b/source/fuzz/fuzzer_context.h
@@ -195,6 +195,9 @@
return chance_of_replacing_linear_algebra_instructions_;
}
uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
+ uint32_t GetChanceOfSwappingConditionalBranchOperands() {
+ return chance_of_swapping_conditional_branch_operands_;
+ }
uint32_t GetChanceOfTogglingAccessChainInstruction() {
return chance_of_toggling_access_chain_instruction_;
}
@@ -291,6 +294,7 @@
uint32_t chance_of_replacing_id_with_synonym_;
uint32_t chance_of_replacing_linear_algebra_instructions_;
uint32_t chance_of_splitting_block_;
+ uint32_t chance_of_swapping_conditional_branch_operands_;
uint32_t chance_of_toggling_access_chain_instruction_;
// Limits associated with various quantities for which random values are
diff --git a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp
new file mode 100644
index 0000000..dc8b1eb
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp
@@ -0,0 +1,59 @@
+// 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_swap_conditional_branch_operands.h"
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_swap_conditional_branch_operands.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassSwapBranchConditionalOperands::
+ FuzzerPassSwapBranchConditionalOperands(
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassSwapBranchConditionalOperands::
+ ~FuzzerPassSwapBranchConditionalOperands() = default;
+
+void FuzzerPassSwapBranchConditionalOperands::Apply() {
+ ForEachInstructionWithInstructionDescriptor(
+ [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/,
+ opt::BasicBlock::iterator inst_it,
+ const protobufs::InstructionDescriptor& instruction_descriptor) {
+ const auto& inst = *inst_it;
+
+ if (inst.opcode() != SpvOpBranchConditional) {
+ return;
+ }
+
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()
+ ->GetChanceOfSwappingConditionalBranchOperands())) {
+ return;
+ }
+
+ ApplyTransformation(TransformationSwapConditionalBranchOperands(
+ instruction_descriptor, GetFuzzerContext()->GetFreshId()));
+ });
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h
new file mode 100644
index 0000000..f84f3ba
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h
@@ -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_SWAP_BRANCH_CONDITIONAL_OPERANDS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_SWAP_BRANCH_CONDITIONAL_OPERANDS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Randomly decides for each OpBranchConditional instruction in the module
+// whether to swap its operands or not.
+class FuzzerPassSwapBranchConditionalOperands : public FuzzerPass {
+ public:
+ FuzzerPassSwapBranchConditionalOperands(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassSwapBranchConditionalOperands() override;
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_SWAP_BRANCH_CONDITIONAL_OPERANDS_H_
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index 57d85ab..1fa2005 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -378,6 +378,7 @@
TransformationPushIdThroughVariable push_id_through_variable = 47;
TransformationAddSpecConstantOp add_spec_constant_op = 48;
TransformationReplaceLinearAlgebraInstruction replace_linear_algebra_instruction = 49;
+ TransformationSwapConditionalBranchOperands swap_conditional_branch_operands = 50;
// Add additional option using the next available number.
}
}
@@ -1226,6 +1227,21 @@
}
+message TransformationSwapConditionalBranchOperands {
+
+ // Swaps label ids in OpBranchConditional instruction.
+ // Additionally, inverts the guard and swaps branch weights
+ // if present.
+
+ // Descriptor of the instruction to swap operands of.
+ InstructionDescriptor instruction_descriptor = 1;
+
+ // Fresh result id for the OpLogicalNot instruction, used
+ // to invert the guard.
+ uint32 fresh_id = 2;
+
+}
+
message TransformationToggleAccessChainInstruction {
// A transformation that toggles an access chain instruction.
diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp
index 99f77d2..da08e88 100644
--- a/source/fuzz/transformation.cpp
+++ b/source/fuzz/transformation.cpp
@@ -64,6 +64,7 @@
#include "source/fuzz/transformation_split_block.h"
#include "source/fuzz/transformation_store.h"
#include "source/fuzz/transformation_swap_commutable_operands.h"
+#include "source/fuzz/transformation_swap_conditional_branch_operands.h"
#include "source/fuzz/transformation_toggle_access_chain_instruction.h"
#include "source/fuzz/transformation_vector_shuffle.h"
#include "source/util/make_unique.h"
@@ -207,6 +208,10 @@
return MakeUnique<TransformationSwapCommutableOperands>(
message.swap_commutable_operands());
case protobufs::Transformation::TransformationCase::
+ kSwapConditionalBranchOperands:
+ return MakeUnique<TransformationSwapConditionalBranchOperands>(
+ message.swap_conditional_branch_operands());
+ case protobufs::Transformation::TransformationCase::
kToggleAccessChainInstruction:
return MakeUnique<TransformationToggleAccessChainInstruction>(
message.toggle_access_chain_instruction());
diff --git a/source/fuzz/transformation_swap_conditional_branch_operands.cpp b/source/fuzz/transformation_swap_conditional_branch_operands.cpp
new file mode 100644
index 0000000..25f48c4
--- /dev/null
+++ b/source/fuzz/transformation_swap_conditional_branch_operands.cpp
@@ -0,0 +1,104 @@
+// 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_swap_conditional_branch_operands.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationSwapConditionalBranchOperands::
+ TransformationSwapConditionalBranchOperands(
+ const spvtools::fuzz::protobufs::
+ TransformationSwapConditionalBranchOperands& message)
+ : message_(message) {}
+
+TransformationSwapConditionalBranchOperands::
+ TransformationSwapConditionalBranchOperands(
+ const protobufs::InstructionDescriptor& instruction_descriptor,
+ uint32_t fresh_id) {
+ *message_.mutable_instruction_descriptor() = instruction_descriptor;
+ message_.set_fresh_id(fresh_id);
+}
+
+bool TransformationSwapConditionalBranchOperands::IsApplicable(
+ opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ const auto* inst =
+ FindInstruction(message_.instruction_descriptor(), ir_context);
+ return fuzzerutil::IsFreshId(ir_context, message_.fresh_id()) && inst &&
+ inst->opcode() == SpvOpBranchConditional;
+}
+
+void TransformationSwapConditionalBranchOperands::Apply(
+ opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+ auto* branch_inst =
+ FindInstruction(message_.instruction_descriptor(), ir_context);
+ assert(branch_inst);
+
+ auto* block = ir_context->get_instr_block(branch_inst);
+ assert(block);
+
+ // Compute the last instruction in the |block| that allows us to insert
+ // OpLogicalNot above it.
+ auto iter = fuzzerutil::GetIteratorForInstruction(block, branch_inst);
+ if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter)) {
+ // There might be a merge instruction before OpBranchConditional.
+ --iter;
+ }
+
+ assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter) &&
+ "We should now be able to insert SpvOpLogicalNot before |iter|");
+
+ // Get the instruction whose result is used as a condition for
+ // OpBranchConditional.
+ const auto* condition_inst = ir_context->get_def_use_mgr()->GetDef(
+ branch_inst->GetSingleWordInOperand(0));
+ assert(condition_inst);
+
+ // We are swapping the labels in OpBranchConditional. This means that we must
+ // invert the guard as well. We are using OpLogicalNot for that purpose here.
+ iter.InsertBefore(MakeUnique<opt::Instruction>(
+ ir_context, SpvOpLogicalNot, condition_inst->type_id(),
+ message_.fresh_id(),
+ opt::Instruction::OperandList{
+ {SPV_OPERAND_TYPE_ID, {condition_inst->result_id()}}}));
+
+ fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+
+ // Update OpBranchConditional condition operand.
+ branch_inst->GetInOperand(0).words[0] = message_.fresh_id();
+
+ // Swap label operands.
+ std::swap(branch_inst->GetInOperand(1), branch_inst->GetInOperand(2));
+
+ // Additionally, swap branch weights if present.
+ if (branch_inst->NumInOperands() > 3) {
+ std::swap(branch_inst->GetInOperand(3), branch_inst->GetInOperand(4));
+ }
+
+ // Make sure the changes are analyzed.
+ ir_context->InvalidateAnalysesExceptFor(
+ opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation
+TransformationSwapConditionalBranchOperands::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_swap_conditional_branch_operands() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_swap_conditional_branch_operands.h b/source/fuzz/transformation_swap_conditional_branch_operands.h
new file mode 100644
index 0000000..dd68ff8
--- /dev/null
+++ b/source/fuzz/transformation_swap_conditional_branch_operands.h
@@ -0,0 +1,58 @@
+// 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_SWAP_CONDITIONAL_BRANCH_OPERANDS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_SWAP_CONDITIONAL_BRANCH_OPERANDS_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 TransformationSwapConditionalBranchOperands : public Transformation {
+ public:
+ explicit TransformationSwapConditionalBranchOperands(
+ const protobufs::TransformationSwapConditionalBranchOperands& message);
+
+ TransformationSwapConditionalBranchOperands(
+ const protobufs::InstructionDescriptor& instruction_descriptor,
+ uint32_t fresh_id);
+
+ // - |message_.instruction_descriptor| must be a valid descriptor of some
+ // OpBranchConditional instruction in the module.
+ // - |message_.fresh_id| must be a fresh id.
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // Inserts |%fresh_id = OpLogicalNot %bool_type_id %cond_id| before
+ // |OpBranchConditional %cond_id %branch_a %branch_b [%weight_a %weight_b]|.
+ // Replaces %cond_id with %fresh_id and swaps %branch_* and %weight_*
+ // operands.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationSwapConditionalBranchOperands message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_SWAP_CONDITIONAL_BRANCH_OPERANDS_H_
diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt
index 8915271..dd3c181 100644
--- a/test/fuzz/CMakeLists.txt
+++ b/test/fuzz/CMakeLists.txt
@@ -70,6 +70,7 @@
transformation_split_block_test.cpp
transformation_store_test.cpp
transformation_swap_commutable_operands_test.cpp
+ transformation_swap_conditional_branch_operands_test.cpp
transformation_toggle_access_chain_instruction_test.cpp
transformation_vector_shuffle_test.cpp
uniform_buffer_element_descriptor_test.cpp)
diff --git a/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp b/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp
new file mode 100644
index 0000000..4383e07
--- /dev/null
+++ b/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp
@@ -0,0 +1,145 @@
+// 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_swap_conditional_branch_operands.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationSwapConditionalBranchOperandsTest, 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
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 0
+ %11 = OpConstant %6 1
+ %14 = OpTypeBool
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ %12 = OpLoad %6 %8
+ %13 = OpLoad %6 %10
+ %15 = OpSLessThan %14 %12 %13
+ OpSelectionMerge %17 None
+ OpBranchConditional %15 %16 %21 10 20
+ %16 = OpLabel
+ %18 = OpLoad %6 %10
+ %19 = OpLoad %6 %8
+ %20 = OpIAdd %6 %19 %18
+ OpBranch %17
+ %21 = OpLabel
+ %22 = OpLoad %6 %10
+ %23 = OpLoad %6 %8
+ %24 = OpISub %6 %23 %22
+ OpBranch %17
+ %17 = OpLabel
+ %25 = OpPhi %6 %20 %16 %24 %21
+ OpStore %8 %25
+ 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);
+
+ // Invalid instruction descriptor.
+ ASSERT_FALSE(TransformationSwapConditionalBranchOperands(
+ MakeInstructionDescriptor(26, SpvOpPhi, 0), 26)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Descriptor for a wrong instruction.
+ ASSERT_FALSE(TransformationSwapConditionalBranchOperands(
+ MakeInstructionDescriptor(25, SpvOpPhi, 0), 26)
+ .IsApplicable(context.get(), transformation_context));
+
+ // Fresh id is not fresh.
+ ASSERT_FALSE(TransformationSwapConditionalBranchOperands(
+ MakeInstructionDescriptor(15, SpvOpBranchConditional, 0), 25)
+ .IsApplicable(context.get(), transformation_context));
+
+ TransformationSwapConditionalBranchOperands transformation(
+ MakeInstructionDescriptor(15, SpvOpBranchConditional, 0), 26);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+
+ 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
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 0
+ %11 = OpConstant %6 1
+ %14 = OpTypeBool
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ %12 = OpLoad %6 %8
+ %13 = OpLoad %6 %10
+ %15 = OpSLessThan %14 %12 %13
+ %26 = OpLogicalNot %14 %15
+ OpSelectionMerge %17 None
+ OpBranchConditional %26 %21 %16 20 10
+ %16 = OpLabel
+ %18 = OpLoad %6 %10
+ %19 = OpLoad %6 %8
+ %20 = OpIAdd %6 %19 %18
+ OpBranch %17
+ %21 = OpLabel
+ %22 = OpLoad %6 %10
+ %23 = OpLoad %6 %8
+ %24 = OpISub %6 %23 %22
+ OpBranch %17
+ %17 = OpLabel
+ %25 = OpPhi %6 %20 %16 %24 %21
+ OpStore %8 %25
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools