Skip to content

Commit 5e87792

Browse files
authored
[LoopInfo] Pointer to stack object may not be loop invariant in a coroutine function (#149936)
A coroutine function may be split to ramp function and resume function, and they have different stack frames, so a pointer to stack objects may have different addresses depending on where it is used, so it's not a loop invariant. It temporarily fixes #149604.
1 parent 34164da commit 5e87792

File tree

5 files changed

+107
-15
lines changed

5 files changed

+107
-15
lines changed

llvm/include/llvm/Analysis/LoopInfo.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,12 @@ class LLVM_ABI Loop : public LoopBase<BasicBlock, Loop> {
5959
};
6060

6161
/// Return true if the specified value is loop invariant.
62-
bool isLoopInvariant(const Value *V) const;
62+
bool isLoopInvariant(const Value *V, bool HasCoroSuspendInst = false) const;
6363

6464
/// Return true if all the operands of the specified instruction are loop
6565
/// invariant.
66-
bool hasLoopInvariantOperands(const Instruction *I) const;
66+
bool hasLoopInvariantOperands(const Instruction *I,
67+
bool HasCoroSuspendInst = false) const;
6768

6869
/// If the given value is an instruction inside of the loop and it can be
6970
/// hoisted, do so to make it trivially loop-invariant.

llvm/include/llvm/Transforms/Utils/LoopUtils.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,8 @@ LLVM_ABI bool hoistRegion(DomTreeNode *, AAResults *, LoopInfo *,
185185
TargetLibraryInfo *, Loop *, MemorySSAUpdater &,
186186
ScalarEvolution *, ICFLoopSafetyInfo *,
187187
SinkAndHoistLICMFlags &, OptimizationRemarkEmitter *,
188-
bool, bool AllowSpeculation);
188+
bool, bool AllowSpeculation,
189+
bool HasCoroSuspendInst = false);
189190

190191
/// Return true if the induction variable \p IV in a Loop whose latch is
191192
/// \p LatchBlock would become dead if the exit test \p Cond were removed.

llvm/lib/Analysis/LoopInfo.cpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,26 @@ static cl::opt<bool, true>
5858
// Loop implementation
5959
//
6060

61-
bool Loop::isLoopInvariant(const Value *V) const {
62-
if (const Instruction *I = dyn_cast<Instruction>(V))
63-
return !contains(I);
61+
bool Loop::isLoopInvariant(const Value *V, bool HasCoroSuspendInst) const {
62+
if (const Instruction *I = dyn_cast<Instruction>(V)) {
63+
// FIXME: this is semantically inconsistent. We're tracking a proper fix in
64+
// issue #149604.
65+
// If V is a pointer to stack object and L contains a coro.suspend function
66+
// call, then V may not be loop invariant because the ramp function and
67+
// resume function have different stack frames.
68+
if (HasCoroSuspendInst && isa<AllocaInst>(I))
69+
return false;
70+
else
71+
return !contains(I);
72+
}
6473
return true; // All non-instructions are loop invariant
6574
}
6675

67-
bool Loop::hasLoopInvariantOperands(const Instruction *I) const {
68-
return all_of(I->operands(), [this](Value *V) { return isLoopInvariant(V); });
76+
bool Loop::hasLoopInvariantOperands(const Instruction *I,
77+
bool HasCoroSuspendInst) const {
78+
return all_of(I->operands(), [&](Value *V) {
79+
return isLoopInvariant(V, HasCoroSuspendInst);
80+
});
6981
}
7082

7183
bool Loop::makeLoopInvariant(Value *V, bool &Changed, Instruction *InsertPt,

llvm/lib/Transforms/Scalar/LICM.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ bool LoopInvariantCodeMotion::runOnLoop(Loop *L, AAResults *AA, LoopInfo *LI,
472472
if (Preheader)
473473
Changed |= hoistRegion(DT->getNode(L->getHeader()), AA, LI, DT, AC, TLI, L,
474474
MSSAU, SE, &SafetyInfo, Flags, ORE, LoopNestMode,
475-
LicmAllowSpeculation);
475+
LicmAllowSpeculation, HasCoroSuspendInst);
476476

477477
// Now that all loop invariants have been removed from the loop, promote any
478478
// memory references to scalars that we can.
@@ -881,7 +881,7 @@ bool llvm::hoistRegion(DomTreeNode *N, AAResults *AA, LoopInfo *LI,
881881
ICFLoopSafetyInfo *SafetyInfo,
882882
SinkAndHoistLICMFlags &Flags,
883883
OptimizationRemarkEmitter *ORE, bool LoopNestMode,
884-
bool AllowSpeculation) {
884+
bool AllowSpeculation, bool HasCoroSuspendInst) {
885885
// Verify inputs.
886886
assert(N != nullptr && AA != nullptr && LI != nullptr && DT != nullptr &&
887887
CurLoop != nullptr && SafetyInfo != nullptr &&
@@ -914,11 +914,11 @@ bool llvm::hoistRegion(DomTreeNode *N, AAResults *AA, LoopInfo *LI,
914914
// TODO: It may be safe to hoist if we are hoisting to a conditional block
915915
// and we have accurately duplicated the control flow from the loop header
916916
// to that block.
917-
if (CurLoop->hasLoopInvariantOperands(&I) &&
917+
if (CurLoop->hasLoopInvariantOperands(&I, HasCoroSuspendInst) &&
918918
canSinkOrHoistInst(I, AA, DT, CurLoop, MSSAU, true, Flags, ORE) &&
919-
isSafeToExecuteUnconditionally(
920-
I, DT, TLI, CurLoop, SafetyInfo, ORE,
921-
Preheader->getTerminator(), AC, AllowSpeculation)) {
919+
isSafeToExecuteUnconditionally(I, DT, TLI, CurLoop, SafetyInfo, ORE,
920+
Preheader->getTerminator(), AC,
921+
AllowSpeculation)) {
922922
hoist(I, DT, CurLoop, CFH.getOrCreateHoistedBlock(BB), SafetyInfo,
923923
MSSAU, SE, ORE);
924924
HoistedInstructions.push_back(&I);
@@ -964,7 +964,7 @@ bool llvm::hoistRegion(DomTreeNode *N, AAResults *AA, LoopInfo *LI,
964964
SafetyInfo->doesNotWriteMemoryBefore(I, CurLoop);
965965
};
966966
if ((IsInvariantStart(I) || isGuard(&I)) &&
967-
CurLoop->hasLoopInvariantOperands(&I) &&
967+
CurLoop->hasLoopInvariantOperands(&I, HasCoroSuspendInst) &&
968968
MustExecuteWithoutWritesBefore(I)) {
969969
hoist(I, DT, CurLoop, CFH.getOrCreateHoistedBlock(BB), SafetyInfo,
970970
MSSAU, SE, ORE);
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
2+
; RUN: opt < %s -passes=licm -S | FileCheck %s
3+
4+
; %fca.0 and %fca.1 should not be hoisted out of the loop because the ramp
5+
; function and resume function have different stack frames, so %pointer1 and
6+
; %pointer2 have different values before and after @llvm.coro.suspend.
7+
8+
define ptr @f(i32 %n) presplitcoroutine {
9+
; CHECK-LABEL: define ptr @f(
10+
; CHECK-SAME: i32 [[N:%.*]]) #[[ATTR0:[0-9]+]] {
11+
; CHECK-NEXT: [[ENTRY:.*]]:
12+
; CHECK-NEXT: [[POINTER1:%.*]] = alloca ptr, align 8
13+
; CHECK-NEXT: [[POINTER2:%.*]] = alloca ptr, align 8
14+
; CHECK-NEXT: [[ID:%.*]] = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
15+
; CHECK-NEXT: [[SIZE:%.*]] = call i32 @llvm.coro.size.i32()
16+
; CHECK-NEXT: [[ALLOC:%.*]] = call ptr @malloc(i32 [[SIZE]])
17+
; CHECK-NEXT: [[HDL:%.*]] = call noalias ptr @llvm.coro.begin(token [[ID]], ptr [[ALLOC]])
18+
; CHECK-NEXT: br label %[[LOOP:.*]]
19+
; CHECK: [[LOOP]]:
20+
; CHECK-NEXT: [[N_VAL:%.*]] = phi i32 [ [[N]], %[[ENTRY]] ], [ [[INC:%.*]], %[[RESUME:.*]] ]
21+
; CHECK-NEXT: [[INC]] = add nsw i32 [[N_VAL]], 1
22+
; CHECK-NEXT: call void @print(i32 [[N_VAL]])
23+
; CHECK-NEXT: [[TMP0:%.*]] = call i8 @llvm.coro.suspend(token none, i1 false)
24+
; CHECK-NEXT: switch i8 [[TMP0]], label %[[SUSPEND_LOOPEXIT:.*]] [
25+
; CHECK-NEXT: i8 0, label %[[RESUME]]
26+
; CHECK-NEXT: i8 1, label %[[CLEANUP:.*]]
27+
; CHECK-NEXT: ]
28+
; CHECK: [[RESUME]]:
29+
; CHECK-NEXT: [[FCA_0:%.*]] = insertvalue [2 x ptr] poison, ptr [[POINTER1]], 0
30+
; CHECK-NEXT: [[FCA_1:%.*]] = insertvalue [2 x ptr] [[FCA_0]], ptr [[POINTER2]], 1
31+
; CHECK-NEXT: call void @foo([2 x ptr] [[FCA_1]])
32+
; CHECK-NEXT: br label %[[LOOP]]
33+
; CHECK: [[CLEANUP]]:
34+
; CHECK-NEXT: [[MEM:%.*]] = call ptr @llvm.coro.free(token [[ID]], ptr [[HDL]])
35+
; CHECK-NEXT: call void @free(ptr [[MEM]])
36+
; CHECK-NEXT: br label %[[SUSPEND:.*]]
37+
; CHECK: [[SUSPEND_LOOPEXIT]]:
38+
; CHECK-NEXT: br label %[[SUSPEND]]
39+
; CHECK: [[SUSPEND]]:
40+
; CHECK-NEXT: [[UNUSED:%.*]] = call i1 @llvm.coro.end(ptr [[HDL]], i1 false, token none)
41+
; CHECK-NEXT: ret ptr [[HDL]]
42+
;
43+
entry:
44+
%pointer1 = alloca ptr
45+
%pointer2 = alloca ptr
46+
%id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
47+
%size = call i32 @llvm.coro.size.i32()
48+
%alloc = call ptr @malloc(i32 %size)
49+
%hdl = call noalias ptr @llvm.coro.begin(token %id, ptr %alloc)
50+
br label %loop
51+
52+
loop:
53+
%n.val = phi i32 [ %n, %entry ], [ %inc, %resume ]
54+
%inc = add nsw i32 %n.val, 1
55+
call void @print(i32 %n.val)
56+
%0 = call i8 @llvm.coro.suspend(token none, i1 false)
57+
switch i8 %0, label %suspend [i8 0, label %resume
58+
i8 1, label %cleanup]
59+
60+
resume:
61+
%fca.0 = insertvalue [2 x ptr] poison, ptr %pointer1, 0
62+
%fca.1 = insertvalue [2 x ptr] %fca.0, ptr %pointer2, 1
63+
call void @foo([2 x ptr] %fca.1)
64+
br label %loop
65+
66+
cleanup:
67+
%mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
68+
call void @free(ptr %mem)
69+
br label %suspend
70+
suspend:
71+
%unused = call i1 @llvm.coro.end(ptr %hdl, i1 false, token none)
72+
ret ptr %hdl
73+
}
74+
75+
declare void @free(ptr)
76+
declare ptr @malloc(i32)
77+
declare void @print(i32)
78+
declare void @foo([2 x ptr])

0 commit comments

Comments
 (0)