In LICM don't place code between merge instruction and branch. (#2252)
Fixes #2210.
diff --git a/source/opt/licm_pass.cpp b/source/opt/licm_pass.cpp
index c553221..82851fd 100644
--- a/source/opt/licm_pass.cpp
+++ b/source/opt/licm_pass.cpp
@@ -124,7 +124,14 @@
if (!pre_header_bb) {
return false;
}
- inst->InsertBefore(std::move(&(*pre_header_bb->tail())));
+ Instruction* insertion_point = &*pre_header_bb->tail();
+ Instruction* previous_node = insertion_point->PreviousNode();
+ if (previous_node && (previous_node->opcode() == SpvOpLoopMerge ||
+ previous_node->opcode() == SpvOpSelectionMerge)) {
+ insertion_point = previous_node;
+ }
+
+ inst->InsertBefore(insertion_point);
context()->set_instr_block(inst, pre_header_bb);
return true;
}
diff --git a/source/opt/licm_pass.h b/source/opt/licm_pass.h
index a94ae11..597fe92 100644
--- a/source/opt/licm_pass.h
+++ b/source/opt/licm_pass.h
@@ -61,7 +61,7 @@
// Returns true if |bb| is immediately contained in |loop|
bool IsImmediatelyContainedInLoop(Loop* loop, Function* f, BasicBlock* bb);
- // Move the instruction to the given BasicBlock
+ // Move the instruction to the preheader of |loop|.
// This method will update the instruction to block mapping for the context
bool HoistInstruction(Loop* loop, Instruction* inst);
};
diff --git a/test/opt/loop_optimizations/hoist_single_nested_loops.cpp b/test/opt/loop_optimizations/hoist_single_nested_loops.cpp
index 7fa1fb0..056f3f0 100644
--- a/test/opt/loop_optimizations/hoist_single_nested_loops.cpp
+++ b/test/opt/loop_optimizations/hoist_single_nested_loops.cpp
@@ -158,6 +158,52 @@
SinglePassRunAndCheck<LICMPass>(before_hoist, after_hoist, true);
}
+TEST_F(PassClassTest, PreHeaderIsAlsoHeader) {
+ // Move OpSLessThan out of the inner loop. The preheader for the inner loop
+ // is the header of the outer loop. The loop merge should not be separated
+ // from the branch in that block.
+ const std::string text = R"(
+ ; CHECK: OpFunction
+ ; CHECK-NEXT: OpLabel
+ ; CHECK-NEXT: OpBranch [[header:%\w+]]
+ ; CHECK: [[header]] = OpLabel
+ ; CHECK-NEXT: OpSLessThan %bool %int_1 %int_1
+ ; CHECK-NEXT: OpLoopMerge
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %2 "main"
+ OpExecutionMode %2 OriginUpperLeft
+ OpSource ESSL 310
+ %void = OpTypeVoid
+ %4 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+ %int_1 = OpConstant %int 1
+ %bool = OpTypeBool
+ %2 = OpFunction %void None %4
+ %18 = OpLabel
+ OpBranch %21
+ %21 = OpLabel
+ OpLoopMerge %22 %23 None
+ OpBranch %24
+ %24 = OpLabel
+ %25 = OpSLessThan %bool %int_1 %int_1
+ OpLoopMerge %26 %27 None
+ OpBranchConditional %25 %27 %26
+ %27 = OpLabel
+ OpBranch %24
+ %26 = OpLabel
+ OpBranch %22
+ %23 = OpLabel
+ OpBranch %21
+ %22 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ SinglePassRunAndMatch<LICMPass>(text, true);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools