Started on new transformation.
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index bc7d453..82920da 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -36,6 +36,7 @@
fuzzer.h
fuzzer_context.h
fuzzer_pass.h
+ fuzzer_pass_add_dead_blocks.h
fuzzer_pass_add_dead_breaks.h
fuzzer_pass_add_dead_continues.h
fuzzer_pass_add_no_contraction_decorations.h
@@ -65,6 +66,7 @@
transformation_add_constant_boolean.h
transformation_add_constant_composite.h
transformation_add_constant_scalar.h
+ transformation_add_dead_block.h
transformation_add_dead_break.h
transformation_add_dead_continue.h
transformation_add_function.h
@@ -104,6 +106,7 @@
fuzzer.cpp
fuzzer_context.cpp
fuzzer_pass.cpp
+ fuzzer_pass_add_dead_blocks.cpp
fuzzer_pass_add_dead_breaks.cpp
fuzzer_pass_add_dead_continues.cpp
fuzzer_pass_add_no_contraction_decorations.cpp
@@ -132,6 +135,7 @@
transformation_add_constant_boolean.cpp
transformation_add_constant_composite.cpp
transformation_add_constant_scalar.cpp
+ transformation_add_dead_block.cpp
transformation_add_dead_break.cpp
transformation_add_dead_continue.cpp
transformation_add_function.cpp
diff --git a/source/fuzz/fuzzer_pass_add_dead_blocks.cpp b/source/fuzz/fuzzer_pass_add_dead_blocks.cpp
new file mode 100644
index 0000000..f964146
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_add_dead_blocks.cpp
@@ -0,0 +1,33 @@
+// 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_add_dead_blocks.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddDeadBlocks::FuzzerPassAddDeadBlocks(
+ opt::IRContext* ir_context, FactManager* fact_manager,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAddDeadBlocks::~FuzzerPassAddDeadBlocks() = default;
+
+void FuzzerPassAddDeadBlocks::Apply() {
+ assert(false && "Implement");
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_add_dead_blocks.h b/source/fuzz/fuzzer_pass_add_dead_blocks.h
new file mode 100644
index 0000000..0577151
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_add_dead_blocks.h
@@ -0,0 +1,38 @@
+// 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_ADD_DEAD_BLOCKS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BLOCKS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// TODO comment
+class FuzzerPassAddDeadBlocks : public FuzzerPass {
+ public:
+ FuzzerPassAddDeadBlocks(opt::IRContext* ir_context, FactManager* fact_manager,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassAddDeadBlocks();
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BLOCKS_H_
diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp
index 1c39da0..a6e3657 100644
--- a/source/fuzz/fuzzer_util.cpp
+++ b/source/fuzz/fuzzer_util.cpp
@@ -109,6 +109,19 @@
return phi_index == static_cast<uint32_t>(phi_ids.size());
}
+uint32_t MaybeGetBoolConstantId(opt::IRContext* context, bool value) {
+ opt::analysis::Bool bool_type;
+ auto registered_bool_type =
+ context->get_type_mgr()->GetRegisteredType(&bool_type);
+ if (!registered_bool_type) {
+ return 0;
+ }
+ opt::analysis::BoolConstant bool_constant(registered_bool_type->AsBool(),
+ value);
+ return context->get_constant_mgr()->FindDeclaredConstant(
+ &bool_constant, context->get_type_mgr()->GetId(&bool_type));
+}
+
void AddUnreachableEdgeAndUpdateOpPhis(
opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
bool condition_value,
@@ -119,12 +132,8 @@
"Precondition on terminator of bb_from is not satisfied");
// Get the id of the boolean constant to be used as the condition.
- opt::analysis::Bool bool_type;
- opt::analysis::BoolConstant bool_constant(
- context->get_type_mgr()->GetRegisteredType(&bool_type)->AsBool(),
- condition_value);
- uint32_t bool_id = context->get_constant_mgr()->FindDeclaredConstant(
- &bool_constant, context->get_type_mgr()->GetId(&bool_type));
+ uint32_t bool_id = MaybeGetBoolConstantId(context, condition_value);
+ assert(bool_id && "Precondition that condition value must be available is not satisfied");
const bool from_to_edge_already_exists = bb_from->IsSuccessor(bb_to);
auto successor = bb_from->terminator()->GetSingleWordInOperand(0);
@@ -302,7 +311,8 @@
bool IsValid(opt::IRContext* context) {
std::vector<uint32_t> binary;
context->module()->ToBinary(&binary, false);
- return SpirvTools(context->grammar().target_env()).Validate(binary);
+ SpirvTools tools(context->grammar().target_env());
+ return tools.Validate(binary);
}
std::unique_ptr<opt::IRContext> CloneIRContext(opt::IRContext* context) {
@@ -317,6 +327,25 @@
return type && !type->AsFunction();
}
+bool IsMergeOrContinue(opt::IRContext* ir_context, uint32_t block_id) {
+ bool result = false;
+ ir_context->get_def_use_mgr()->WhileEachUse(
+ block_id,
+ [&result](
+ const opt::Instruction* use_instruction,
+ uint32_t /*unused*/) -> bool {
+ switch (use_instruction->opcode()) {
+ case SpvOpLoopMerge:
+ case SpvOpSelectionMerge:
+ result = true;
+ return false;
+ default:
+ return true;
+ }
+ });
+ return result;
+}
+
} // namespace fuzzerutil
} // namespace fuzz
diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h
index af3eb1b..dc477b7 100644
--- a/source/fuzz/fuzzer_util.h
+++ b/source/fuzz/fuzzer_util.h
@@ -49,8 +49,13 @@
opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids);
-// Requires that PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) holds,
-// and that bb_from ends with "OpBranch %some_block". Turns OpBranch into
+// Returns the id of a boolean constant with value |value| if it exists in the
+// module, or 0 otherwise.
+uint32_t MaybeGetBoolConstantId(opt::IRContext* context, bool value);
+
+// Requires that a boolean constant with value |condition_value| is available,
+// that PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) holds, and that
+// bb_from ends with "OpBranch %some_block". Turns OpBranch into
// "OpBranchConditional |condition_value| ...", such that control will branch
// to %some_block, with |bb_to| being the unreachable alternative. Updates
// OpPhi instructions in |bb_to| using |phi_ids| so that the new edge is valid.
@@ -120,6 +125,9 @@
// type.
bool IsNonFunctionTypeId(opt::IRContext* ir_context, uint32_t id);
+// Returns true if and only if |block_id| is a merge block or continue target
+bool IsMergeOrContinue(opt::IRContext* ir_context, uint32_t block_id);
+
} // namespace fuzzerutil
} // namespace fuzz
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index dbd1fb8..ef8074e 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -242,6 +242,7 @@
TransformationAddGlobalVariable add_global_variable = 31;
TransformationAddGlobalUndef add_global_undef = 32;
TransformationAddFunction add_function = 33;
+ TransformationAddDeadBlock add_dead_block = 34;
// Add additional option using the next available number.
}
}
@@ -275,7 +276,7 @@
message TransformationAddConstantScalar {
- // Adds a constant of the given scalar type
+ // Adds a constant of the given scalar type.
// Id for the constant
uint32 fresh_id = 1;
@@ -288,6 +289,29 @@
}
+message TransformationAddDeadBlock {
+
+ // Adds a new block to the module that is statically reachable from an
+ // existing block, but dynamically unreachable.
+
+ // Fresh id for the dead block
+ uint32 fresh_id = 1;
+
+ // Id of an existing block terminated with OpBranch, such that this OpBranch
+ // can be replaced with an OpBranchConditional to its exiting successor or
+ // the dead block
+ uint32 existing_block = 2;
+
+ // Determines whether the condition associated with the OpBranchConditional
+ // is true or false
+ bool condition_value = 3;
+
+ // A sequence of ids suitable for extending OpPhi instructions at the
+ // successor of |existing_block| as a result of adding a new edge
+ repeated uint32 phi_id = 4;
+
+}
+
message TransformationAddDeadBreak {
// A transformation that turns a basic block that unconditionally branches to
diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp
index 1489f85..3f5375e 100644
--- a/source/fuzz/transformation.cpp
+++ b/source/fuzz/transformation.cpp
@@ -19,6 +19,7 @@
#include "source/fuzz/transformation_add_constant_boolean.h"
#include "source/fuzz/transformation_add_constant_composite.h"
#include "source/fuzz/transformation_add_constant_scalar.h"
+#include "source/fuzz/transformation_add_dead_block.h"
#include "source/fuzz/transformation_add_dead_break.h"
#include "source/fuzz/transformation_add_dead_continue.h"
#include "source/fuzz/transformation_add_function.h"
@@ -68,6 +69,9 @@
case protobufs::Transformation::TransformationCase::kAddConstantScalar:
return MakeUnique<TransformationAddConstantScalar>(
message.add_constant_scalar());
+ case protobufs::Transformation::TransformationCase::kAddDeadBlock:
+ return MakeUnique<TransformationAddDeadBlock>(
+ message.add_dead_block());
case protobufs::Transformation::TransformationCase::kAddDeadBreak:
return MakeUnique<TransformationAddDeadBreak>(message.add_dead_break());
case protobufs::Transformation::TransformationCase::kAddDeadContinue:
diff --git a/source/fuzz/transformation_add_dead_block.cpp b/source/fuzz/transformation_add_dead_block.cpp
new file mode 100644
index 0000000..ff9cebc
--- /dev/null
+++ b/source/fuzz/transformation_add_dead_block.cpp
@@ -0,0 +1,107 @@
+// 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_add_dead_block.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddDeadBlock::TransformationAddDeadBlock(
+ const spvtools::fuzz::protobufs::TransformationAddDeadBlock& message)
+ : message_(message) {}
+
+TransformationAddDeadBlock::TransformationAddDeadBlock(
+ uint32_t fresh_id, uint32_t existing_block,
+bool condition_value,
+ std::vector<uint32_t> phi_id) {
+ message_.set_fresh_id(fresh_id);
+ message_.set_existing_block(existing_block);
+ message_.set_condition_value(condition_value);
+ for (auto id : phi_id) {
+ message_.add_phi_id(id);
+ }
+}
+
+bool TransformationAddDeadBlock::IsApplicable(
+ opt::IRContext* context,
+ const spvtools::fuzz::FactManager& /*unused*/) const {
+ // The new block's id must be fresh.
+ if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+ return false;
+ }
+
+ // First, we check that a constant with the same value as
+ // |message_.condition_value| is present.
+ if (!fuzzerutil::MaybeGetBoolConstantId(context, message_.condition_value())) {
+ // The required constant is not present, so the transformation cannot be
+ // applied.
+ return false;
+ }
+
+ // The existing block must indeed exist.
+ auto existing_block = fuzzerutil::MaybeFindBlock(context, message_.existing_block());
+ if (!existing_block) {
+ return false;
+ }
+
+ // It must not head a loop.
+ if (existing_block->IsLoopHeader()) {
+ return false;
+ }
+
+ // It must end with OpBranch.
+ if (existing_block->terminator()->opcode() != SpvOpBranch) {
+ return false;
+ }
+
+ // Its successor must not be a merge block nor continue target.
+ if (fuzzerutil::IsMergeOrContinue(context, existing_block->terminator()->GetSingleWordInOperand(0))) {
+ return false;
+ }
+ return true;
+}
+
+void TransformationAddDeadBlock::Apply(
+ opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
+ // TODO comment
+ fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+ auto existing_block = context->cfg()->block(message_.existing_block());
+ auto successor_block_id = existing_block->terminator()->GetSingleWordInOperand(0);
+ auto bool_id = fuzzerutil::MaybeGetBoolConstantId(context, message_.condition_value());
+
+ auto enclosing_function = existing_block->GetParent();
+ std::unique_ptr<opt::BasicBlock> new_block =
+ MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
+ context, SpvOpLabel, 0, message_.fresh_id(), opt::Instruction::OperandList()));
+ new_block->SetParent(enclosing_function);
+ new_block->AddInstruction(MakeUnique<opt::Instruction>(context, SpvOpBranch, 0, 0, opt::Instruction::OperandList({{SPV_OPERAND_TYPE_ID, {successor_block_id}}})));
+ existing_block->terminator()->SetOpcode(SpvOpBranchConditional);
+ existing_block->terminator()->SetInOperands(
+ {{SPV_OPERAND_TYPE_ID, {bool_id}},
+ {SPV_OPERAND_TYPE_ID, {message_.condition_value() ? successor_block_id : message_.fresh_id()}},
+ {SPV_OPERAND_TYPE_ID, {message_.condition_value() ? message_.fresh_id() : successor_block_id}}});
+ enclosing_function->InsertBasicBlockAfter(std::move(new_block), existing_block);
+ context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationAddDeadBlock::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_add_dead_block() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_add_dead_block.h b/source/fuzz/transformation_add_dead_block.h
new file mode 100644
index 0000000..203b9a9
--- /dev/null
+++ b/source/fuzz/transformation_add_dead_block.h
@@ -0,0 +1,52 @@
+// 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_ADD_DEAD_BLOCK_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BLOCK_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 TransformationAddDeadBlock : public Transformation {
+ public:
+ explicit TransformationAddDeadBlock(
+ const protobufs::TransformationAddDeadBlock& message);
+
+ TransformationAddDeadBlock(uint32_t fresh_id,
+ uint32_t existing_block,
+ bool condition_value,
+ std::vector<uint32_t> phi_id);
+
+ // TODO comment
+ bool IsApplicable(opt::IRContext* context,
+ const FactManager& fact_manager) const override;
+
+ // TODO comment
+ void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationAddDeadBlock message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BLOCK_H_
diff --git a/source/fuzz/transformation_add_dead_break.cpp b/source/fuzz/transformation_add_dead_break.cpp
index a37100b..9fa2df6 100644
--- a/source/fuzz/transformation_add_dead_break.cpp
+++ b/source/fuzz/transformation_add_dead_break.cpp
@@ -111,15 +111,7 @@
opt::IRContext* context, const FactManager& /*unused*/) const {
// First, we check that a constant with the same value as
// |message_.break_condition_value| is present.
- opt::analysis::Bool bool_type;
- auto registered_bool_type =
- context->get_type_mgr()->GetRegisteredType(&bool_type);
- if (!registered_bool_type) {
- return false;
- }
- opt::analysis::BoolConstant bool_constant(registered_bool_type->AsBool(),
- message_.break_condition_value());
- if (!context->get_constant_mgr()->FindConstant(&bool_constant)) {
+ if (!fuzzerutil::MaybeGetBoolConstantId(context, message_.break_condition_value())) {
// The required constant is not present, so the transformation cannot be
// applied.
return false;
diff --git a/source/fuzz/transformation_add_dead_continue.cpp b/source/fuzz/transformation_add_dead_continue.cpp
index 0aacc5b..b614a23 100644
--- a/source/fuzz/transformation_add_dead_continue.cpp
+++ b/source/fuzz/transformation_add_dead_continue.cpp
@@ -37,15 +37,7 @@
opt::IRContext* context, const FactManager& /*unused*/) const {
// First, we check that a constant with the same value as
// |message_.continue_condition_value| is present.
- opt::analysis::Bool bool_type;
- auto registered_bool_type =
- context->get_type_mgr()->GetRegisteredType(&bool_type);
- if (!registered_bool_type) {
- return false;
- }
- opt::analysis::BoolConstant bool_constant(
- registered_bool_type->AsBool(), message_.continue_condition_value());
- if (!context->get_constant_mgr()->FindConstant(&bool_constant)) {
+ if (!fuzzerutil::MaybeGetBoolConstantId(context, message_.continue_condition_value())) {
// The required constant is not present, so the transformation cannot be
// applied.
return false;
diff --git a/source/fuzz/transformation_outline_function.cpp b/source/fuzz/transformation_outline_function.cpp
index 1d1d48e..1937f24 100644
--- a/source/fuzz/transformation_outline_function.cpp
+++ b/source/fuzz/transformation_outline_function.cpp
@@ -151,22 +151,7 @@
// For simplicity, we do not allow the exit block to be a merge block or
// continue target.
- bool exit_block_is_merge_or_continue = false;
- context->get_def_use_mgr()->WhileEachUse(
- exit_block->id(),
- [&exit_block_is_merge_or_continue](
- const opt::Instruction* use_instruction,
- uint32_t /*unused*/) -> bool {
- switch (use_instruction->opcode()) {
- case SpvOpLoopMerge:
- case SpvOpSelectionMerge:
- exit_block_is_merge_or_continue = true;
- return false;
- default:
- return true;
- }
- });
- if (exit_block_is_merge_or_continue) {
+ if (fuzzerutil::IsMergeOrContinue(context, exit_block->id())) {
return false;
}
diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt
index fb9e964..998754d 100644
--- a/test/fuzz/CMakeLists.txt
+++ b/test/fuzz/CMakeLists.txt
@@ -26,6 +26,7 @@
transformation_add_constant_boolean_test.cpp
transformation_add_constant_composite_test.cpp
transformation_add_constant_scalar_test.cpp
+ transformation_add_dead_block_test.cpp
transformation_add_dead_break_test.cpp
transformation_add_dead_continue_test.cpp
transformation_add_global_undef_test.cpp
diff --git a/test/fuzz/fuzz_test_util.cpp b/test/fuzz/fuzz_test_util.cpp
index 1d15ad6..c717961 100644
--- a/test/fuzz/fuzz_test_util.cpp
+++ b/test/fuzz/fuzz_test_util.cpp
@@ -76,6 +76,7 @@
std::vector<uint32_t> binary;
ir->module()->ToBinary(&binary, false);
SpirvTools t(env);
+ t.SetMessageConsumer(kConsoleMessageConsumer);
return t.Validate(binary);
}
diff --git a/test/fuzz/transformation_add_dead_block_test.cpp b/test/fuzz/transformation_add_dead_block_test.cpp
new file mode 100644
index 0000000..1044647
--- /dev/null
+++ b/test/fuzz/transformation_add_dead_block_test.cpp
@@ -0,0 +1,94 @@
+// 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_add_dead_block.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddDeadBlockTest, 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
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpConstantTrue %6
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %8
+ %8 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ // Id 4 is already in use
+ ASSERT_FALSE(TransformationAddDeadBlock(4, 5, true, {}).IsApplicable(context.get(), fact_manager));
+
+ // Id 7 is not a block
+ ASSERT_FALSE(TransformationAddDeadBlock(100, 7, true, {}).IsApplicable(context.get(), fact_manager));
+
+ TransformationAddDeadBlock transformation(100, 5, true, {});
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpConstantTrue %6
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpSelectionMerge %8
+ OpBranchConditional %7 %8 %100
+ %100 = OpLabel
+ OpBranch %8
+ %8 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+// Target block must not be merge or continue
+
+// Source block must not be loop head
+
+// Target block can start with OpPhi; need to give suitable ids in that case
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools