spirv-fuzz: Add fuzzer pass to change selection controls (#2944)
A new pass that allows the fuzzer to change the 'selection control'
operand of OpSelectionControl instructions.
Fixes #2937.
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index b21d210..5ec62cd 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -38,6 +38,7 @@
fuzzer_pass_add_dead_breaks.h
fuzzer_pass_add_dead_continues.h
fuzzer_pass_add_useful_constructs.h
+ fuzzer_pass_adjust_selection_controls.h
fuzzer_pass_apply_id_synonyms.h
fuzzer_pass_copy_objects.h
fuzzer_pass_obfuscate_constants.h
@@ -64,6 +65,7 @@
transformation_replace_boolean_constant_with_constant_binary.h
transformation_replace_constant_with_uniform.h
transformation_replace_id_with_synonym.h
+ transformation_set_selection_control.h
transformation_split_block.h
uniform_buffer_element_descriptor.h
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
@@ -77,6 +79,7 @@
fuzzer_pass_add_dead_breaks.cpp
fuzzer_pass_add_dead_continues.cpp
fuzzer_pass_add_useful_constructs.cpp
+ fuzzer_pass_adjust_selection_controls.cpp
fuzzer_pass_apply_id_synonyms.cpp
fuzzer_pass_copy_objects.cpp
fuzzer_pass_obfuscate_constants.cpp
@@ -102,6 +105,7 @@
transformation_replace_boolean_constant_with_constant_binary.cpp
transformation_replace_constant_with_uniform.cpp
transformation_replace_id_with_synonym.cpp
+ transformation_set_selection_control.cpp
transformation_split_block.cpp
uniform_buffer_element_descriptor.cpp
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc
diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp
index 8e36a32..e28ecd7 100644
--- a/source/fuzz/fuzzer.cpp
+++ b/source/fuzz/fuzzer.cpp
@@ -23,6 +23,7 @@
#include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
#include "source/fuzz/fuzzer_pass_add_dead_continues.h"
#include "source/fuzz/fuzzer_pass_add_useful_constructs.h"
+#include "source/fuzz/fuzzer_pass_adjust_selection_controls.h"
#include "source/fuzz/fuzzer_pass_apply_id_synonyms.h"
#include "source/fuzz/fuzzer_pass_copy_objects.h"
#include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
@@ -162,6 +163,15 @@
passes[fuzzer_context.RandomIndex(passes)]->Apply();
}
+ // Now apply some passes that it does not make sense to apply repeatedly,
+ // as they do not unlock other passes.
+ if (fuzzer_context.ChooseEven()) {
+ FuzzerPassAdjustSelectionControls(ir_context.get(), &fact_manager,
+ &fuzzer_context,
+ transformation_sequence_out)
+ .Apply();
+ }
+
// Encode the module as a binary.
ir_context->module()->ToBinary(binary_out, false);
diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp
index 69c6e56..5a8f394 100644
--- a/source/fuzz/fuzzer_context.cpp
+++ b/source/fuzz/fuzzer_context.cpp
@@ -25,6 +25,8 @@
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBreak = {5, 80};
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadContinue = {5, 80};
+const std::pair<uint32_t, uint32_t> kChanceOfAdjustingSelectionControl = {20,
+ 90};
const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
@@ -53,6 +55,8 @@
ChooseBetweenMinAndMax(kChanceOfAddingDeadBreak);
chance_of_adding_dead_continue_ =
ChooseBetweenMinAndMax(kChanceOfAddingDeadContinue);
+ chance_of_adjusting_selection_control_ =
+ ChooseBetweenMinAndMax(kChanceOfAdjustingSelectionControl);
chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject);
chance_of_moving_block_down_ =
ChooseBetweenMinAndMax(kChanceOfMovingBlockDown);
diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h
index a1ad66f..f50098e 100644
--- a/source/fuzz/fuzzer_context.h
+++ b/source/fuzz/fuzzer_context.h
@@ -62,6 +62,9 @@
uint32_t GetChanceOfAddingDeadContinue() {
return chance_of_adding_dead_continue_;
}
+ uint32_t GetChanceOfAdjustingSelectionControl() {
+ return chance_of_adjusting_selection_control_;
+ }
uint32_t GetChanceOfCopyingObject() { return chance_of_copying_object_; }
uint32_t GetChanceOfMovingBlockDown() { return chance_of_moving_block_down_; }
uint32_t GetChanceOfObfuscatingConstant() {
@@ -88,6 +91,7 @@
// Keep them in alphabetical order.
uint32_t chance_of_adding_dead_break_;
uint32_t chance_of_adding_dead_continue_;
+ uint32_t chance_of_adjusting_selection_control_;
uint32_t chance_of_copying_object_;
uint32_t chance_of_moving_block_down_;
uint32_t chance_of_obfuscating_constant_;
diff --git a/source/fuzz/fuzzer_pass_add_useful_constructs.cpp b/source/fuzz/fuzzer_pass_add_useful_constructs.cpp
index 6ac4ae9..489a084 100644
--- a/source/fuzz/fuzzer_pass_add_useful_constructs.cpp
+++ b/source/fuzz/fuzzer_pass_add_useful_constructs.cpp
@@ -24,8 +24,6 @@
namespace spvtools {
namespace fuzz {
-using opt::IRContext;
-
FuzzerPassAddUsefulConstructs::FuzzerPassAddUsefulConstructs(
opt::IRContext* ir_context, FactManager* fact_manager,
FuzzerContext* fuzzer_context,
diff --git a/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp b/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
new file mode 100644
index 0000000..442fc49
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
@@ -0,0 +1,76 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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_adjust_selection_controls.h"
+
+#include "source/fuzz/transformation_set_selection_control.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAdjustSelectionControls::FuzzerPassAdjustSelectionControls(
+ opt::IRContext* ir_context, FactManager* fact_manager,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations){};
+
+FuzzerPassAdjustSelectionControls::~FuzzerPassAdjustSelectionControls() =
+ default;
+
+void FuzzerPassAdjustSelectionControls::Apply() {
+ // Consider every merge instruction in the module (via looking through all
+ // functions and blocks).
+ for (auto& function : *GetIRContext()->module()) {
+ for (auto& block : function) {
+ if (auto merge_inst = block.GetMergeInst()) {
+ // Ignore the instruction if it is not a selection merge.
+ if (merge_inst->opcode() != SpvOpSelectionMerge) {
+ continue;
+ }
+
+ // Choose randomly whether to change the selection control for this
+ // instruction.
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfAdjustingSelectionControl())) {
+ continue;
+ }
+
+ // The choices to change the selection control to are the set of valid
+ // controls, minus the current control.
+ std::vector<uint32_t> choices;
+ for (auto control :
+ {SpvSelectionControlMaskNone, SpvSelectionControlFlattenMask,
+ SpvSelectionControlDontFlattenMask}) {
+ if (control == merge_inst->GetSingleWordOperand(1)) {
+ continue;
+ }
+ choices.push_back(control);
+ }
+
+ // Apply the transformation and add it to the output transformation
+ // sequence.
+ TransformationSetSelectionControl transformation(
+ block.id(), choices[GetFuzzerContext()->RandomIndex(choices)]);
+ assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
+ "Transformation should be applicable by construction.");
+ transformation.Apply(GetIRContext(), GetFactManager());
+ *GetTransformations()->add_transformation() =
+ transformation.ToMessage();
+ }
+ }
+ }
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_adjust_selection_controls.h b/source/fuzz/fuzzer_pass_adjust_selection_controls.h
new file mode 100644
index 0000000..b5b255c
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_adjust_selection_controls.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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_ADJUST_SELECTION_CONTROLS_
+#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_SELECTION_CONTROLS_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// A pass that adjusts the selection controls on OpSelectionMerge instructions.
+class FuzzerPassAdjustSelectionControls : public FuzzerPass {
+ public:
+ FuzzerPassAdjustSelectionControls(
+ opt::IRContext* ir_context, FactManager* fact_manager,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassAdjustSelectionControls() override;
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_SELECTION_CONTROLS_
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index 64ccd8e..d1c6e73 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -173,6 +173,7 @@
TransformationAddDeadContinue add_dead_continue = 12;
TransformationCopyObject copy_object = 13;
TransformationReplaceIdWithSynonym replace_id_with_synonym = 14;
+ TransformationSetSelectionControl set_selection_control = 15;
// Add additional option using the next available number.
}
}
@@ -384,6 +385,22 @@
uint32 fresh_id_for_temporary = 3;
}
+message TransformationSetSelectionControl {
+
+ // A transformation that sets the selection control operand of an
+ // OpSelectionMerge instruction.
+
+ // The id of a basic block that should contain OpSelectionMerge.
+ uint32 block_id = 1;
+
+ // The value to which the 'selection control' operand should be set.
+ // Although technically 'selection control' is a literal mask that can be
+ // some combination of 'None', 'Flatten' and 'DontFlatten', the combination
+ // 'Flatten | DontFlatten' does not make sense.
+ uint32 selection_control = 2;
+
+}
+
message TransformationSplitBlock {
// A transformation that splits a basic block into two basic blocks
diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp
index 65966f3..296f71f 100644
--- a/source/fuzz/transformation.cpp
+++ b/source/fuzz/transformation.cpp
@@ -30,6 +30,7 @@
#include "transformation_replace_boolean_constant_with_constant_binary.h"
#include "transformation_replace_constant_with_uniform.h"
#include "transformation_replace_id_with_synonym.h"
+#include "transformation_set_selection_control.h"
#include "transformation_split_block.h"
namespace spvtools {
@@ -76,6 +77,9 @@
case protobufs::Transformation::TransformationCase::kReplaceIdWithSynonym:
return MakeUnique<TransformationReplaceIdWithSynonym>(
message.replace_id_with_synonym());
+ case protobufs::Transformation::TransformationCase::kSetSelectionControl:
+ return MakeUnique<TransformationSetSelectionControl>(
+ message.set_selection_control());
case protobufs::Transformation::TransformationCase::kSplitBlock:
return MakeUnique<TransformationSplitBlock>(message.split_block());
case protobufs::Transformation::TRANSFORMATION_NOT_SET:
diff --git a/source/fuzz/transformation_set_selection_control.cpp b/source/fuzz/transformation_set_selection_control.cpp
new file mode 100644
index 0000000..ebabdef
--- /dev/null
+++ b/source/fuzz/transformation_set_selection_control.cpp
@@ -0,0 +1,60 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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_set_selection_control.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationSetSelectionControl::TransformationSetSelectionControl(
+ const spvtools::fuzz::protobufs::TransformationSetSelectionControl& message)
+ : message_(message) {}
+
+TransformationSetSelectionControl::TransformationSetSelectionControl(
+ uint32_t block_id, uint32_t selection_control) {
+ message_.set_block_id(block_id);
+ message_.set_selection_control(selection_control);
+}
+
+bool TransformationSetSelectionControl::IsApplicable(
+ opt::IRContext* context, const FactManager& /*unused*/) const {
+ assert((message_.selection_control() == SpvSelectionControlMaskNone ||
+ message_.selection_control() == SpvSelectionControlFlattenMask ||
+ message_.selection_control() == SpvSelectionControlDontFlattenMask) &&
+ "Selection control should never be set to something other than "
+ "'None', 'Flatten' or 'DontFlatten'");
+ if (auto block = context->get_instr_block(message_.block_id())) {
+ if (auto merge_inst = block->GetMergeInst()) {
+ return merge_inst->opcode() == SpvOpSelectionMerge;
+ }
+ }
+ // Either the block did not exit, or did not end with OpSelectionMerge.
+ return false;
+}
+
+void TransformationSetSelectionControl::Apply(opt::IRContext* context,
+ FactManager* /*unused*/) const {
+ context->get_instr_block(message_.block_id())
+ ->GetMergeInst()
+ ->SetInOperand(1, {message_.selection_control()});
+}
+
+protobufs::Transformation TransformationSetSelectionControl::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_set_selection_control() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_set_selection_control.h b/source/fuzz/transformation_set_selection_control.h
new file mode 100644
index 0000000..19e0c3c
--- /dev/null
+++ b/source/fuzz/transformation_set_selection_control.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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_SET_SELECTION_CONTROL_H_
+#define SOURCE_FUZZ_TRANSFORMATION_SET_SELECTION_CONTROL_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationSetSelectionControl : public Transformation {
+ public:
+ explicit TransformationSetSelectionControl(
+ const protobufs::TransformationSetSelectionControl& message);
+
+ TransformationSetSelectionControl(uint32_t block_id,
+ uint32_t selection_control);
+
+ // - |message_.block_id| must be a block containing an OpSelectionMerge
+ // instruction.
+ // - |message_.selection_control| must be one of None, Flatten or
+ // DontFlatten.
+ bool IsApplicable(opt::IRContext* context,
+ const FactManager& fact_manager) const override;
+
+ // - The selection control operand of the OpSelectionMergeInstruction in
+ // |message_.block_id| is overwritten with |message_.selection_control|.
+ void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationSetSelectionControl message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_SET_SELECTION_CONTROL_H_
diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt
index 1d9a8fd..f89d990 100644
--- a/test/fuzz/CMakeLists.txt
+++ b/test/fuzz/CMakeLists.txt
@@ -33,6 +33,7 @@
transformation_replace_boolean_constant_with_constant_binary_test.cpp
transformation_replace_constant_with_uniform_test.cpp
transformation_replace_id_with_synonym_test.cpp
+ transformation_set_selection_control_test.cpp
transformation_split_block_test.cpp
uniform_buffer_element_descriptor_test.cpp)
diff --git a/test/fuzz/transformation_set_selection_control_test.cpp b/test/fuzz/transformation_set_selection_control_test.cpp
new file mode 100644
index 0000000..9696417
--- /dev/null
+++ b/test/fuzz/transformation_set_selection_control_test.cpp
@@ -0,0 +1,219 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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_set_selection_control.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationSetSelectionControlTest, VariousScenarios) {
+ // This is a simple transformation; this test captures the important things
+ // to check for.
+
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %8 "i"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 0
+ %16 = OpConstant %6 10
+ %17 = OpTypeBool
+ %20 = OpConstant %6 3
+ %25 = OpConstant %6 1
+ %28 = OpConstant %6 2
+ %38 = OpConstant %6 4
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ OpStore %8 %9
+ OpBranch %10
+ %10 = OpLabel
+ OpLoopMerge %12 %13 None
+ OpBranch %14
+ %14 = OpLabel
+ %15 = OpLoad %6 %8
+ %18 = OpSLessThan %17 %15 %16
+ OpBranchConditional %18 %11 %12
+ %11 = OpLabel
+ %19 = OpLoad %6 %8
+ %21 = OpSGreaterThan %17 %19 %20
+ OpSelectionMerge %23 Flatten
+ OpBranchConditional %21 %22 %23
+ %22 = OpLabel
+ %24 = OpLoad %6 %8
+ %26 = OpIAdd %6 %24 %25
+ OpStore %8 %26
+ OpBranch %23
+ %23 = OpLabel
+ %27 = OpLoad %6 %8
+ %29 = OpSLessThan %17 %27 %28
+ OpSelectionMerge %31 DontFlatten
+ OpBranchConditional %29 %30 %31
+ %30 = OpLabel
+ %32 = OpLoad %6 %8
+ %33 = OpISub %6 %32 %25
+ OpStore %8 %33
+ OpBranch %31
+ %31 = OpLabel
+ %34 = OpLoad %6 %8
+ OpSelectionMerge %37 None
+ OpSwitch %34 %36 0 %35
+ %36 = OpLabel
+ OpBranch %37
+ %35 = OpLabel
+ %39 = OpLoad %6 %8
+ %40 = OpIAdd %6 %39 %38
+ OpStore %8 %40
+ OpBranch %36
+ %37 = OpLabel
+ OpBranch %13
+ %13 = OpLabel
+ %43 = OpLoad %6 %8
+ %44 = OpIAdd %6 %43 %25
+ OpStore %8 %44
+ OpBranch %10
+ %12 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+
+ FactManager fact_manager;
+
+ // %44 is not a block
+ ASSERT_FALSE(
+ TransformationSetSelectionControl(44, SpvSelectionControlFlattenMask)
+ .IsApplicable(context.get(), fact_manager));
+ // %13 does not end with OpSelectionMerge
+ ASSERT_FALSE(
+ TransformationSetSelectionControl(13, SpvSelectionControlMaskNone)
+ .IsApplicable(context.get(), fact_manager));
+ // %10 ends in OpLoopMerge, not OpSelectionMerge
+ ASSERT_FALSE(
+ TransformationSetSelectionControl(10, SpvSelectionControlMaskNone)
+ .IsApplicable(context.get(), fact_manager));
+
+ TransformationSetSelectionControl transformation1(
+ 11, SpvSelectionControlDontFlattenMask);
+ ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
+ transformation1.Apply(context.get(), &fact_manager);
+
+ TransformationSetSelectionControl transformation2(
+ 23, SpvSelectionControlFlattenMask);
+ ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
+ transformation2.Apply(context.get(), &fact_manager);
+
+ TransformationSetSelectionControl transformation3(
+ 31, SpvSelectionControlMaskNone);
+ ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
+ transformation3.Apply(context.get(), &fact_manager);
+
+ TransformationSetSelectionControl transformation4(
+ 31, SpvSelectionControlFlattenMask);
+ ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
+ transformation4.Apply(context.get(), &fact_manager);
+
+ 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
+ OpName %4 "main"
+ OpName %8 "i"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 0
+ %16 = OpConstant %6 10
+ %17 = OpTypeBool
+ %20 = OpConstant %6 3
+ %25 = OpConstant %6 1
+ %28 = OpConstant %6 2
+ %38 = OpConstant %6 4
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ OpStore %8 %9
+ OpBranch %10
+ %10 = OpLabel
+ OpLoopMerge %12 %13 None
+ OpBranch %14
+ %14 = OpLabel
+ %15 = OpLoad %6 %8
+ %18 = OpSLessThan %17 %15 %16
+ OpBranchConditional %18 %11 %12
+ %11 = OpLabel
+ %19 = OpLoad %6 %8
+ %21 = OpSGreaterThan %17 %19 %20
+ OpSelectionMerge %23 DontFlatten
+ OpBranchConditional %21 %22 %23
+ %22 = OpLabel
+ %24 = OpLoad %6 %8
+ %26 = OpIAdd %6 %24 %25
+ OpStore %8 %26
+ OpBranch %23
+ %23 = OpLabel
+ %27 = OpLoad %6 %8
+ %29 = OpSLessThan %17 %27 %28
+ OpSelectionMerge %31 Flatten
+ OpBranchConditional %29 %30 %31
+ %30 = OpLabel
+ %32 = OpLoad %6 %8
+ %33 = OpISub %6 %32 %25
+ OpStore %8 %33
+ OpBranch %31
+ %31 = OpLabel
+ %34 = OpLoad %6 %8
+ OpSelectionMerge %37 Flatten
+ OpSwitch %34 %36 0 %35
+ %36 = OpLabel
+ OpBranch %37
+ %35 = OpLabel
+ %39 = OpLoad %6 %8
+ %40 = OpIAdd %6 %39 %38
+ OpStore %8 %40
+ OpBranch %36
+ %37 = OpLabel
+ OpBranch %13
+ %13 = OpLabel
+ %43 = OpLoad %6 %8
+ %44 = OpIAdd %6 %43 %25
+ OpStore %8 %44
+ OpBranch %10
+ %12 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools