Skip to content

Commit cff8d71

Browse files
authored
[SCEV] forgetValue: support (with-overflow-inst op0, op1) (#98015)
The use-def walk in forgetValue() was skipping instructions with non-SCEVable types. However, SCEV may look past with.overflow intrinsics returning aggregates. Fixes #97586.
1 parent be7239e commit cff8d71

File tree

3 files changed

+108
-1
lines changed

3 files changed

+108
-1
lines changed

llvm/lib/Analysis/ScalarEvolution.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8412,7 +8412,7 @@ void ScalarEvolution::visitAndClearUsers(
84128412
SmallVectorImpl<const SCEV *> &ToForget) {
84138413
while (!Worklist.empty()) {
84148414
Instruction *I = Worklist.pop_back_val();
8415-
if (!isSCEVable(I->getType()))
8415+
if (!isSCEVable(I->getType()) && !isa<WithOverflowInst>(I))
84168416
continue;
84178417

84188418
ValueExprMapType::iterator It =
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
; RUN: opt < %s -S -passes='print<scalar-evolution>,loop-unroll<peeling;full-unroll-max=0>,print<scalar-evolution>' 2>&1 | FileCheck %s
2+
;
3+
; This test ensures that (extractvalue 0 (with-overflow-inst op0, op1))
4+
; is invalidated by LoopPeel when the operands of with-overflow-inst
5+
; are changed.
6+
;
7+
; In the following case, LoopPeel modifies the CFG into another one
8+
; with %bb7 not dominating %bb2 and %bb3 although %extractvalue is
9+
; still the step for the %bb3 loop. %call has been modified and uses
10+
; different operands but the SCEV value for %extractvalue has not been
11+
; invalidated and still refers to %load in its SCEV operands
12+
; (SCEV(%extractvalue) := -2 + -2 * %load).
13+
;
14+
; When LoopUnroll tries to compute the SCEV for the %bb3 Phi, the
15+
; stale data for %extractvalue is used whereas %load is not available
16+
; in %bb3 which is wrong.
17+
;
18+
; for more details and nice pictures: https://github.com/llvm/llvm-project/issues/97586
19+
;
20+
; Although the reason for the bug was in forgetValue, it is still relevant to
21+
; test if LoopPeel invalidates %extractvalue after changing %call.
22+
;
23+
; forgetValue only walks the users, so calling it on the IV Phis does not
24+
; invalidate %extractvalue (thus forgetLoop does not invalidate it too).
25+
; It has to be done by LoopPeel itself.
26+
27+
28+
define void @loop_peeling_smul_with_overflow() {
29+
; before loop-unroll
30+
; CHECK: Classifying expressions for: @loop_peeling_smul_with_overflow
31+
; CHECK: %extractvalue = extractvalue { i32, i1 } %call, 0
32+
; CHECK-NEXT: --> (-2 + (-2 * %load))
33+
; CHECK: %phi4 = phi i32 [ %add, %bb3 ], [ 0, %bb2 ]
34+
; CHECK-NEXT: --> {0,+,(-2 + (-2 * %load))}<nuw><nsw><%bb3>
35+
; after loop-unroll
36+
; CHECK: Classifying expressions for: @loop_peeling_smul_with_overflow
37+
; CHECK: %extractvalue = extractvalue { i32, i1 } %call, 0
38+
; CHECK-NEXT: --> (-2 * %add8.lcssa)
39+
; CHECK: %phi4 = phi i32 [ %add, %bb3 ], [ 0, %bb2 ]
40+
; CHECK-NEXT: --> {0,+,(-2 * %add8.lcssa)}<nuw><nsw><%bb3>
41+
;
42+
bb:
43+
br label %bb1
44+
45+
bb1: ; preds = %bb3, %bb
46+
%phi = phi i32 [ 0, %bb ], [ %phi4, %bb3 ]
47+
br label %bb5
48+
49+
bb2: ; preds = %bb7
50+
%call = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 %add8, i32 -2)
51+
%extractvalue = extractvalue { i32, i1 } %call, 0
52+
br label %bb3
53+
54+
bb3: ; preds = %bb3, %bb2
55+
%phi4 = phi i32 [ %add, %bb3 ], [ 0, %bb2 ]
56+
%add = add i32 %extractvalue, %phi4
57+
br i1 false, label %bb3, label %bb1
58+
59+
bb5: ; preds = %bb7, %bb1
60+
%phi6 = phi i32 [ 1, %bb1 ], [ 0, %bb7 ]
61+
%icmp = icmp eq i32 %phi, 0
62+
br i1 %icmp, label %bb9, label %bb7
63+
64+
bb7: ; preds = %bb5
65+
%load = load i32, ptr addrspace(1) null, align 4
66+
%add8 = add i32 %load, 1
67+
br i1 false, label %bb2, label %bb5
68+
69+
bb9: ; preds = %bb5
70+
ret void
71+
}

llvm/unittests/Analysis/ScalarEvolutionTest.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,4 +1589,40 @@ TEST_F(ScalarEvolutionsTest, ApplyLoopGuards) {
15891589
});
15901590
}
15911591

1592+
TEST_F(ScalarEvolutionsTest, ForgetValueWithOverflowInst) {
1593+
LLVMContext C;
1594+
SMDiagnostic Err;
1595+
std::unique_ptr<Module> M = parseAssemblyString(
1596+
"declare { i32, i1 } @llvm.smul.with.overflow.i32(i32, i32) "
1597+
"define void @foo(i32 %i) { "
1598+
"entry: "
1599+
" br label %loop.body "
1600+
"loop.body: "
1601+
" %iv = phi i32 [ %iv.next, %loop.body ], [ 0, %entry ] "
1602+
" %iv.next = add nsw i32 %iv, 1 "
1603+
" %call = call {i32, i1} @llvm.smul.with.overflow.i32(i32 %iv, i32 -2) "
1604+
" %extractvalue = extractvalue {i32, i1} %call, 0 "
1605+
" %cmp = icmp eq i32 %iv.next, 16 "
1606+
" br i1 %cmp, label %exit, label %loop.body "
1607+
"exit: "
1608+
" ret void "
1609+
"} ",
1610+
Err, C);
1611+
1612+
ASSERT_TRUE(M && "Could not parse module?");
1613+
ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");
1614+
1615+
runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
1616+
auto *ExtractValue = getInstructionByName(F, "extractvalue");
1617+
auto *IV = getInstructionByName(F, "iv");
1618+
1619+
auto *ExtractValueScev = SE.getSCEV(ExtractValue);
1620+
EXPECT_NE(ExtractValueScev, nullptr);
1621+
1622+
SE.forgetValue(IV);
1623+
auto *ExtractValueScevForgotten = SE.getExistingSCEV(ExtractValue);
1624+
EXPECT_EQ(ExtractValueScevForgotten, nullptr);
1625+
});
1626+
}
1627+
15921628
} // end namespace llvm

0 commit comments

Comments
 (0)