Skip to content

Commit fa9149a

Browse files
committed
[Coro] Hide promise alloca for later passes
1 parent a618ae2 commit fa9149a

File tree

6 files changed

+106
-18
lines changed

6 files changed

+106
-18
lines changed

llvm/docs/Coroutines.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2121,10 +2121,11 @@ Coroutine Transformation Passes
21212121
===============================
21222122
CoroEarly
21232123
---------
2124-
The pass CoroEarly lowers coroutine intrinsics that hide the details of the
2125-
structure of the coroutine frame, but, otherwise not needed to be preserved to
2126-
help later coroutine passes. This pass lowers `coro.frame`_, `coro.done`_,
2127-
and `coro.promise`_ intrinsics.
2124+
The CoroEarly pass ensures later middle end passes correctly interpret coroutine
2125+
semantics and lowers coroutine intrinsics that not needed to be preserved to
2126+
help later coroutine passes. This pass lowers `coro.promise`_ that outside the
2127+
coroutine body, `coro.frame`_ and `coro.done`_ intrinsics. It replace uses of
2128+
promise alloca with `coro.promise`_ intrinsic.
21282129

21292130
.. _CoroSplit:
21302131

llvm/include/llvm/Transforms/Coroutines/CoroShape.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,15 +79,17 @@ struct Shape {
7979

8080
// Scan the function and collect the above intrinsics for later processing
8181
void analyze(Function &F, SmallVectorImpl<CoroFrameInst *> &CoroFrames,
82-
SmallVectorImpl<CoroSaveInst *> &UnusedCoroSaves);
82+
SmallVectorImpl<CoroSaveInst *> &UnusedCoroSaves,
83+
SmallVectorImpl<CoroPromiseInst *> &CoroPromises);
8384
// If for some reason, we were not able to find coro.begin, bailout.
8485
void invalidateCoroutine(Function &F,
8586
SmallVectorImpl<CoroFrameInst *> &CoroFrames);
8687
// Perform ABI related initial transformation
8788
void initABI();
8889
// Remove orphaned and unnecessary intrinsics
8990
void cleanCoroutine(SmallVectorImpl<CoroFrameInst *> &CoroFrames,
90-
SmallVectorImpl<CoroSaveInst *> &UnusedCoroSaves);
91+
SmallVectorImpl<CoroSaveInst *> &UnusedCoroSaves,
92+
SmallVectorImpl<CoroPromiseInst *> &CoroPromises);
9193

9294
// Field indexes for special fields in the switch lowering.
9395
struct SwitchFieldIndex {
@@ -265,13 +267,14 @@ struct Shape {
265267
explicit Shape(Function &F) {
266268
SmallVector<CoroFrameInst *, 8> CoroFrames;
267269
SmallVector<CoroSaveInst *, 2> UnusedCoroSaves;
270+
SmallVector<CoroPromiseInst *, 2> CoroPromises;
268271

269-
analyze(F, CoroFrames, UnusedCoroSaves);
272+
analyze(F, CoroFrames, UnusedCoroSaves, CoroPromises);
270273
if (!CoroBegin) {
271274
invalidateCoroutine(F, CoroFrames);
272275
return;
273276
}
274-
cleanCoroutine(CoroFrames, UnusedCoroSaves);
277+
cleanCoroutine(CoroFrames, UnusedCoroSaves, CoroPromises);
275278
}
276279
};
277280

llvm/lib/Transforms/Coroutines/CoroEarly.cpp

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "llvm/Transforms/Coroutines/CoroEarly.h"
1010
#include "CoroInternal.h"
1111
#include "llvm/IR/DIBuilder.h"
12+
#include "llvm/IR/Dominators.h"
1213
#include "llvm/IR/Function.h"
1314
#include "llvm/IR/IRBuilder.h"
1415
#include "llvm/IR/InstIterator.h"
@@ -165,6 +166,7 @@ static void setCannotDuplicate(CoroIdInst *CoroId) {
165166

166167
void Lowerer::lowerEarlyIntrinsics(Function &F) {
167168
CoroIdInst *CoroId = nullptr;
169+
CoroBeginInst *CoroBegin = nullptr;
168170
SmallVector<CoroFreeInst *, 4> CoroFrees;
169171
bool HasCoroSuspend = false;
170172
for (Instruction &I : llvm::make_early_inc_range(instructions(F))) {
@@ -175,6 +177,23 @@ void Lowerer::lowerEarlyIntrinsics(Function &F) {
175177
switch (CB->getIntrinsicID()) {
176178
default:
177179
continue;
180+
case Intrinsic::coro_begin:
181+
case Intrinsic::coro_begin_custom_abi: {
182+
auto CBI = cast<CoroBeginInst>(&I);
183+
184+
// Ignore coro id's that aren't pre-split.
185+
auto Id = dyn_cast<CoroIdInst>(CBI->getId());
186+
if (Id && !Id->getInfo().isPreSplit())
187+
break;
188+
189+
if (CoroBegin)
190+
report_fatal_error(
191+
"coroutine should have exactly one defining @llvm.coro.begin");
192+
CBI->addRetAttr(Attribute::NonNull);
193+
CBI->addRetAttr(Attribute::NoAlias);
194+
CoroBegin = CBI;
195+
break;
196+
}
178197
case Intrinsic::coro_free:
179198
CoroFrees.push_back(cast<CoroFreeInst>(&I));
180199
break;
@@ -218,22 +237,44 @@ void Lowerer::lowerEarlyIntrinsics(Function &F) {
218237
case Intrinsic::coro_destroy:
219238
lowerResumeOrDestroy(*CB, CoroSubFnInst::DestroyIndex);
220239
break;
221-
case Intrinsic::coro_promise:
222-
lowerCoroPromise(cast<CoroPromiseInst>(&I));
240+
case Intrinsic::coro_promise: {
241+
bool OutsideCoro = CoroBegin == nullptr;
242+
if (OutsideCoro)
243+
lowerCoroPromise(cast<CoroPromiseInst>(&I));
223244
break;
245+
}
224246
case Intrinsic::coro_done:
225247
lowerCoroDone(cast<IntrinsicInst>(&I));
226248
break;
227249
}
228250
}
229251

230-
// Make sure that all CoroFree reference the coro.id intrinsic.
231-
// Token type is not exposed through coroutine C/C++ builtins to plain C, so
232-
// we allow specifying none and fixing it up here.
233-
if (CoroId)
252+
if (CoroId) {
253+
// Make sure that all CoroFree reference the coro.id intrinsic.
254+
// Token type is not exposed through coroutine C/C++ builtins to plain C, so
255+
// we allow specifying none and fixing it up here.
234256
for (CoroFreeInst *CF : CoroFrees)
235257
CF->setArgOperand(0, CoroId);
236258

259+
if (auto *PA = CoroId->getPromise()) {
260+
assert(CoroBegin && "Use Switch-Resumed ABI but missing coro.begin");
261+
262+
Builder.SetInsertPoint(*CoroBegin->getInsertionPointAfterDef());
263+
264+
auto *Alignment = Builder.getInt32(PA->getAlign().value());
265+
auto *FromPromise = Builder.getInt1(false);
266+
SmallVector<Value *, 3> Arg{CoroBegin, Alignment, FromPromise};
267+
auto *PI =
268+
Builder.CreateIntrinsic(Builder.getPtrTy(), Intrinsic::coro_promise,
269+
Arg, {}, "promise.addr");
270+
PA->replaceUsesWithIf(PI, [CoroId](Use &U) {
271+
bool IsBitcast = U == U.getUser()->stripPointerCasts();
272+
bool IsCoroId = U.getUser() == CoroId;
273+
return !IsBitcast && !IsCoroId;
274+
});
275+
}
276+
}
277+
237278
// Coroutine suspention could potentially lead to any argument modified
238279
// outside of the function, hence arguments should not have noalias
239280
// attributes.

llvm/lib/Transforms/Coroutines/Coroutines.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,8 @@ static CoroSaveInst *createCoroSave(CoroBeginInst *CoroBegin,
192192
// Collect "interesting" coroutine intrinsics.
193193
void coro::Shape::analyze(Function &F,
194194
SmallVectorImpl<CoroFrameInst *> &CoroFrames,
195-
SmallVectorImpl<CoroSaveInst *> &UnusedCoroSaves) {
195+
SmallVectorImpl<CoroSaveInst *> &UnusedCoroSaves,
196+
SmallVectorImpl<CoroPromiseInst *> &CoroPromises) {
196197
clear();
197198

198199
bool HasFinalSuspend = false;
@@ -286,6 +287,9 @@ void coro::Shape::analyze(Function &F,
286287
}
287288
}
288289
break;
290+
case Intrinsic::coro_promise:
291+
CoroPromises.push_back(cast<CoroPromiseInst>(II));
292+
break;
289293
}
290294
}
291295
}
@@ -477,7 +481,8 @@ void coro::AnyRetconABI::init() {
477481

478482
void coro::Shape::cleanCoroutine(
479483
SmallVectorImpl<CoroFrameInst *> &CoroFrames,
480-
SmallVectorImpl<CoroSaveInst *> &UnusedCoroSaves) {
484+
SmallVectorImpl<CoroSaveInst *> &UnusedCoroSaves,
485+
SmallVectorImpl<CoroPromiseInst *> &CoroPromises) {
481486
// The coro.frame intrinsic is always lowered to the result of coro.begin.
482487
for (CoroFrameInst *CF : CoroFrames) {
483488
CF->replaceAllUsesWith(CoroBegin);
@@ -489,6 +494,11 @@ void coro::Shape::cleanCoroutine(
489494
for (CoroSaveInst *CoroSave : UnusedCoroSaves)
490495
CoroSave->eraseFromParent();
491496
UnusedCoroSaves.clear();
497+
498+
for (auto *PI : CoroPromises) {
499+
PI->replaceAllUsesWith(getPromiseAlloca());
500+
PI->eraseFromParent();
501+
}
492502
}
493503

494504
static void propagateCallAttrsFromCallee(CallInst *Call, Function *Callee) {

llvm/test/Instrumentation/AddressSanitizer/skip-coro.ll

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ define ptr @foo() #0 {
1515
entry:
1616
%__promise = alloca %struct.Promise, align 8
1717
%0 = call token @llvm.coro.id(i32 16, ptr nonnull %__promise, ptr null, ptr null)
18-
%1 = call ptr @llvm.coro.noop()
19-
ret ptr %1
18+
%1 = call ptr @llvm.coro.begin(token %0, ptr null)
19+
%2 = call ptr @llvm.coro.noop()
20+
ret ptr %2
2021
}
2122

2223
declare token @llvm.coro.id(i32, ptr readnone, ptr nocapture readonly, ptr)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
; Test that store-load operation that crosses suspension point will not be eliminated by DSE
2+
; Coro result conversion function that attempts to modify promise shall produce this pattern
3+
; RUN: opt < %s -passes='coro-early,dse' -S | FileCheck %s
4+
5+
define void @fn() presplitcoroutine {
6+
%__promise = alloca ptr, align 8
7+
%id = call token @llvm.coro.id(i32 16, ptr %__promise, ptr @fn, ptr null)
8+
%hdl = call ptr @llvm.coro.begin(token %id, ptr null)
9+
; CHECK: %promise.addr = call ptr @llvm.coro.promise(ptr %hdl, i32 8, i1 false)
10+
%save = call token @llvm.coro.save(ptr null)
11+
%sp = call i8 @llvm.coro.suspend(token %save, i1 false)
12+
%flag = icmp ule i8 %sp, 1
13+
br i1 %flag, label %resume, label %suspend
14+
15+
resume:
16+
; CHECK: call void @use_value(ptr %promise.addr)
17+
call void @use_value(ptr %__promise)
18+
br label %suspend
19+
20+
suspend:
21+
call i1 @llvm.coro.end(ptr null, i1 false, token none)
22+
; load value when resume
23+
; CHECK: %null = load ptr, ptr %promise.addr, align 8
24+
%null = load ptr, ptr %__promise, align 8
25+
call void @use_value(ptr %null)
26+
; store value when suspend
27+
; CHECK: store ptr null, ptr %promise.addr, align 8
28+
store ptr null, ptr %__promise, align 8
29+
ret void
30+
}
31+
32+
declare void @use_value(ptr)

0 commit comments

Comments
 (0)