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