From 26dba32c4361771b14387100bf7419506c00348a Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 8 Oct 2019 11:25:34 +0100 Subject: [PATCH] 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. --- source/fuzz/CMakeLists.txt | 4 + source/fuzz/fuzzer.cpp | 10 + source/fuzz/fuzzer_context.cpp | 4 + source/fuzz/fuzzer_context.h | 4 + .../fuzzer_pass_add_useful_constructs.cpp | 2 - .../fuzzer_pass_adjust_selection_controls.cpp | 76 ++++++ .../fuzzer_pass_adjust_selection_controls.h | 39 ++++ source/fuzz/protobufs/spvtoolsfuzz.proto | 17 ++ source/fuzz/transformation.cpp | 4 + .../transformation_set_selection_control.cpp | 60 +++++ .../transformation_set_selection_control.h | 54 +++++ test/fuzz/CMakeLists.txt | 1 + ...nsformation_set_selection_control_test.cpp | 219 ++++++++++++++++++ 13 files changed, 492 insertions(+), 2 deletions(-) create mode 100644 source/fuzz/fuzzer_pass_adjust_selection_controls.cpp create mode 100644 source/fuzz/fuzzer_pass_adjust_selection_controls.h create mode 100644 source/fuzz/transformation_set_selection_control.cpp create mode 100644 source/fuzz/transformation_set_selection_control.h create mode 100644 test/fuzz/transformation_set_selection_control_test.cpp diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt index b21d210d..5ec62cd4 100644 --- a/source/fuzz/CMakeLists.txt +++ b/source/fuzz/CMakeLists.txt @@ -38,6 +38,7 @@ if(SPIRV_BUILD_FUZZER) 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 @@ if(SPIRV_BUILD_FUZZER) 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 @@ if(SPIRV_BUILD_FUZZER) 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 @@ if(SPIRV_BUILD_FUZZER) 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 8e36a323..e28ecd79 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 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( 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 69c6e568..5a8f3941 100644 --- a/source/fuzz/fuzzer_context.cpp +++ b/source/fuzz/fuzzer_context.cpp @@ -25,6 +25,8 @@ namespace { const std::pair kChanceOfAddingDeadBreak = {5, 80}; const std::pair kChanceOfAddingDeadContinue = {5, 80}; +const std::pair kChanceOfAdjustingSelectionControl = {20, + 90}; const std::pair kChanceOfCopyingObject = {20, 50}; const std::pair kChanceOfMovingBlockDown = {20, 50}; const std::pair kChanceOfObfuscatingConstant = {10, 90}; @@ -53,6 +55,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, 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 a1ad66f8..f50098e8 100644 --- a/source/fuzz/fuzzer_context.h +++ b/source/fuzz/fuzzer_context.h @@ -62,6 +62,9 @@ class FuzzerContext { 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 @@ class FuzzerContext { // 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 6ac4ae97..489a0844 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 00000000..442fc496 --- /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 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 00000000..b5b255ce --- /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 64ccd8ef..d1c6e736 100644 --- a/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -173,6 +173,7 @@ message Transformation { 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 @@ message TransformationReplaceIdWithSynonym { 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 65966f35..296f71f3 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 @@ std::unique_ptr Transformation::FromMessage( case protobufs::Transformation::TransformationCase::kReplaceIdWithSynonym: return MakeUnique( message.replace_id_with_synonym()); + case protobufs::Transformation::TransformationCase::kSetSelectionControl: + return MakeUnique( + message.set_selection_control()); case protobufs::Transformation::TransformationCase::kSplitBlock: return MakeUnique(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 00000000..ebabdef2 --- /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 00000000..19e0c3cf --- /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 1d9a8fd6..f89d9902 100644 --- a/test/fuzz/CMakeLists.txt +++ b/test/fuzz/CMakeLists.txt @@ -33,6 +33,7 @@ if (${SPIRV_BUILD_FUZZER}) 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 00000000..9696417b --- /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