reduce: fix loop to selection pass for loops with combined header/continue block (#2480)
* Fix #2478. The fix is to just not try to simplify such loops.
* Also added `BasicBlock::MergeBlockId()` and `BasicBlock::ContinueBlockId()`.
* Some minor changes to `structured_loop_to_selection_reduction_opportunity.cpp`.
* Added test.
diff --git a/source/opt/basic_block.cpp b/source/opt/basic_block.cpp
index aafee51..4da0a12 100644
--- a/source/opt/basic_block.cpp
+++ b/source/opt/basic_block.cpp
@@ -184,6 +184,12 @@
return mbid;
}
+uint32_t BasicBlock::MergeBlockId() const {
+ uint32_t mbid = MergeBlockIdIfAny();
+ assert(mbid && "Expected block to have a corresponding merge block");
+ return mbid;
+}
+
uint32_t BasicBlock::ContinueBlockIdIfAny() const {
auto merge_ii = cend();
--merge_ii;
@@ -197,6 +203,12 @@
return cbid;
}
+uint32_t BasicBlock::ContinueBlockId() const {
+ uint32_t cbid = ContinueBlockIdIfAny();
+ assert(cbid && "Expected block to have a corresponding continue target");
+ return cbid;
+}
+
std::ostream& operator<<(std::ostream& str, const BasicBlock& block) {
str << block.PrettyPrint();
return str;
diff --git a/source/opt/basic_block.h b/source/opt/basic_block.h
index ff3a412..e53f3ea 100644
--- a/source/opt/basic_block.h
+++ b/source/opt/basic_block.h
@@ -183,10 +183,16 @@
// block, if any. If none, returns zero.
uint32_t MergeBlockIdIfAny() const;
+ // Returns MergeBlockIdIfAny() and asserts that it is non-zero.
+ uint32_t MergeBlockId() const;
+
// Returns the ID of the continue block declared by a merge instruction in
// this block, if any. If none, returns zero.
uint32_t ContinueBlockIdIfAny() const;
+ // Returns ContinueBlockIdIfAny() and asserts that it is non-zero.
+ uint32_t ContinueBlockId() const;
+
// Returns the terminator instruction. Assumes the terminator exists.
Instruction* terminator() { return &*tail(); }
const Instruction* terminator() const { return &*ctail(); }
diff --git a/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp b/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp
index acde5df..8e25837 100644
--- a/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp
+++ b/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp
@@ -23,7 +23,6 @@
namespace {
const uint32_t kMergeNodeIndex = 0;
-const uint32_t kContinueNodeIndex = 1;
} // namespace
bool StructuredLoopToSelectionReductionOpportunity::PreconditionHolds() {
@@ -43,15 +42,11 @@
// (1) Redirect edges that point to the loop's continue target to their
// closest merge block.
- RedirectToClosestMergeBlock(
- loop_construct_header_->GetLoopMergeInst()->GetSingleWordOperand(
- kContinueNodeIndex));
+ RedirectToClosestMergeBlock(loop_construct_header_->ContinueBlockId());
// (2) Redirect edges that point to the loop's merge block to their closest
// merge block (which might be that of an enclosing selection, for instance).
- RedirectToClosestMergeBlock(
- loop_construct_header_->GetLoopMergeInst()->GetSingleWordOperand(
- kMergeNodeIndex));
+ RedirectToClosestMergeBlock(loop_construct_header_->MergeBlockId());
// (3) Turn the loop construct header into a selection.
ChangeLoopToSelection();
@@ -127,12 +122,8 @@
// original_target_id must either be the merge target or continue construct
// for the loop being operated on.
- assert(original_target_id ==
- loop_construct_header_->GetMergeInst()->GetSingleWordOperand(
- kMergeNodeIndex) ||
- original_target_id ==
- loop_construct_header_->GetMergeInst()->GetSingleWordOperand(
- kContinueNodeIndex));
+ assert(original_target_id == loop_construct_header_->MergeBlockId() ||
+ original_target_id == loop_construct_header_->ContinueBlockId());
auto terminator = context_->cfg()->block(source_id)->terminator();
@@ -221,7 +212,7 @@
const analysis::Bool* bool_type =
context_->get_type_mgr()->GetRegisteredType(&temp)->AsBool();
auto const_mgr = context_->get_constant_mgr();
- auto true_const = const_mgr->GetConstant(bool_type, {true});
+ auto true_const = const_mgr->GetConstant(bool_type, {1});
auto true_const_result_id =
const_mgr->GetDefiningInstruction(true_const)->result_id();
auto original_branch_id = terminator->GetSingleWordOperand(0);
diff --git a/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp b/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp
index 7d150fb..6d835fc 100644
--- a/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp
+++ b/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp
@@ -33,10 +33,9 @@
std::set<uint32_t> merge_block_ids;
for (auto& function : *context->module()) {
for (auto& block : function) {
- auto merge_inst = block.GetMergeInst();
- if (merge_inst) {
- merge_block_ids.insert(
- merge_inst->GetSingleWordOperand(kMergeNodeIndex));
+ auto merge_block_id = block.MergeBlockIdIfAny();
+ if (merge_block_id) {
+ merge_block_ids.insert(merge_block_id);
}
}
}
@@ -50,11 +49,19 @@
continue;
}
+ uint32_t continue_block_id =
+ loop_merge_inst->GetSingleWordOperand(kContinueNodeIndex);
+
// Check whether the loop construct's continue target is the merge block
// of some structured control flow construct. If it is, we cautiously do
// not consider applying a transformation.
- if (merge_block_ids.find(loop_merge_inst->GetSingleWordOperand(
- kContinueNodeIndex)) != merge_block_ids.end()) {
+ if (merge_block_ids.find(continue_block_id) != merge_block_ids.end()) {
+ continue;
+ }
+
+ // Check whether the loop header block is also the continue target. If it
+ // is, we cautiously do not consider applying a transformation.
+ if (block.id() == continue_block_id) {
continue;
}
diff --git a/test/reduce/structured_loop_to_selection_test.cpp b/test/reduce/structured_loop_to_selection_test.cpp
index c40bcf9..d4f6459 100644
--- a/test/reduce/structured_loop_to_selection_test.cpp
+++ b/test/reduce/structured_loop_to_selection_test.cpp
@@ -3587,6 +3587,40 @@
CheckEqual(env, after_op_0, context.get());
}
+TEST(StructuredLoopToSelectionReductionPassTest,
+ LoopWithCombinedHeaderAndContinue) {
+ // A shader containing a loop where the header is also the continue target.
+ // For now, we don't simplify such loops.
+
+ 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 = OpTypeBool
+ %30 = OpConstantFalse %6
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %10
+ %10 = OpLabel ; loop header and continue target
+ OpLoopMerge %12 %10 None
+ OpBranchConditional %30 %10 %12
+ %12 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption);
+ const auto ops = StructuredLoopToSelectionReductionOpportunityFinder()
+ .GetAvailableOpportunities(context.get());
+ ASSERT_EQ(0, ops.size());
+}
+
} // namespace
} // namespace reduce
} // namespace spvtools