spirv-reduce: Allow merging unreachable blocks (#4303)
This change allows the reducer to merge together blocks even when they
are unreachable, but keeps the restriction of reachability in place
for the optimizer.
Fixes #4302.
diff --git a/source/fuzz/transformation_merge_blocks.cpp b/source/fuzz/transformation_merge_blocks.cpp
index 2223679..dbf782e 100644
--- a/source/fuzz/transformation_merge_blocks.cpp
+++ b/source/fuzz/transformation_merge_blocks.cpp
@@ -43,6 +43,9 @@
}
auto first_block = ir_context->cfg()->block(predecessors.at(0));
+ if (!ir_context->IsReachable(*first_block)) {
+ return false;
+ }
return opt::blockmergeutil::CanMergeWithSuccessor(ir_context, first_block);
}
diff --git a/source/fuzz/transformation_merge_blocks.h b/source/fuzz/transformation_merge_blocks.h
index f6306c5..f3462fa 100644
--- a/source/fuzz/transformation_merge_blocks.h
+++ b/source/fuzz/transformation_merge_blocks.h
@@ -31,6 +31,7 @@
TransformationMergeBlocks(uint32_t block_id);
// - |message_.block_id| must be the id of a block, b
+ // - b must be statically reachable in the control flow graph of its function
// - b must have a single predecessor, a
// - b must be the sole successor of a
// - Replacing a with the merge of a and b (and removing b) must lead to a
diff --git a/source/opt/block_merge_pass.cpp b/source/opt/block_merge_pass.cpp
index c7315ba..04e47f1 100644
--- a/source/opt/block_merge_pass.cpp
+++ b/source/opt/block_merge_pass.cpp
@@ -28,7 +28,9 @@
bool BlockMergePass::MergeBlocks(Function* func) {
bool modified = false;
for (auto bi = func->begin(); bi != func->end();) {
- if (blockmergeutil::CanMergeWithSuccessor(context(), &*bi)) {
+ // Don't bother trying to merge unreachable blocks.
+ if (context()->IsReachable(*bi) &&
+ blockmergeutil::CanMergeWithSuccessor(context(), &*bi)) {
blockmergeutil::MergeWithSuccessor(context(), func, bi);
// Reprocess block.
modified = true;
diff --git a/source/opt/block_merge_util.cpp b/source/opt/block_merge_util.cpp
index 39b5074..15e8c6f 100644
--- a/source/opt/block_merge_util.cpp
+++ b/source/opt/block_merge_util.cpp
@@ -103,9 +103,6 @@
return false;
}
- // Don't bother trying to merge unreachable blocks.
- if (!context->IsReachable(*block)) return false;
-
Instruction* merge_inst = block->GetMergeInst();
const bool pred_is_header = IsHeader(block);
if (pred_is_header && lab_id != merge_inst->GetSingleWordInOperand(0u)) {
diff --git a/test/reduce/merge_blocks_test.cpp b/test/reduce/merge_blocks_test.cpp
index 8506ee0..c472301 100644
--- a/test/reduce/merge_blocks_test.cpp
+++ b/test/reduce/merge_blocks_test.cpp
@@ -647,6 +647,64 @@
MergeBlocksReductionPassTest_LoopReturn_Helper(true);
}
+TEST(MergeBlocksReductionPassTest, MergeUnreachable) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %11 = OpTypeBool
+ %12 = OpConstantFalse %11
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ %9 = OpLabel
+ OpBranch %100
+ %100 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context =
+ BuildModule(env, consumer, shader, kReduceAssembleOption);
+ const auto ops =
+ MergeBlocksReductionOpportunityFinder().GetAvailableOpportunities(
+ context.get(), 0);
+ ASSERT_EQ(1, ops.size());
+
+ ASSERT_TRUE(ops[0]->PreconditionHolds());
+ ops[0]->TryToApply();
+
+ std::string after = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %11 = OpTypeBool
+ %12 = OpConstantFalse %11
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpReturn
+ %9 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ CheckEqual(env, after, context.get());
+}
+
} // namespace
} // namespace reduce
} // namespace spvtools