Skip to content

Commit 0d20cfe

Browse files
author
tnowicki
committed
[Coroutines] Support Custom ABIs and plugin libraries
* Add the llvm.coro.begin.custom intrinsic * Add constructors to CoroSplit that take a list of generators that create the custom ABI object. * Add has/getCustomABI methods to CoroBeginInst class. * Add a unittest for the custom ABI.
1 parent c1c55a5 commit 0d20cfe

File tree

9 files changed

+171
-12
lines changed

9 files changed

+171
-12
lines changed

llvm/include/llvm/Analysis/TargetTransformInfoImpl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,7 @@ class TargetTransformInfoImplBase {
767767
case Intrinsic::experimental_gc_relocate:
768768
case Intrinsic::coro_alloc:
769769
case Intrinsic::coro_begin:
770+
case Intrinsic::coro_begin_custom_abi:
770771
case Intrinsic::coro_free:
771772
case Intrinsic::coro_end:
772773
case Intrinsic::coro_frame:

llvm/include/llvm/IR/Intrinsics.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1709,7 +1709,8 @@ def int_coro_prepare_async : Intrinsic<[llvm_ptr_ty], [llvm_ptr_ty],
17091709
[IntrNoMem]>;
17101710
def int_coro_begin : Intrinsic<[llvm_ptr_ty], [llvm_token_ty, llvm_ptr_ty],
17111711
[WriteOnly<ArgIndex<1>>]>;
1712-
1712+
def int_coro_begin_custom_abi : Intrinsic<[llvm_ptr_ty], [llvm_token_ty, llvm_ptr_ty, llvm_i32_ty],
1713+
[WriteOnly<ArgIndex<1>>]>;
17131714
def int_coro_free : Intrinsic<[llvm_ptr_ty], [llvm_token_ty, llvm_ptr_ty],
17141715
[IntrReadMem, IntrArgMemOnly,
17151716
ReadOnly<ArgIndex<1>>,

llvm/include/llvm/Transforms/Coroutines/ABI.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,13 @@ namespace coro {
2727
// This interface/API is to provide an object oriented way to implement ABI
2828
// functionality. This is intended to replace use of the ABI enum to perform
2929
// ABI operations. The ABIs (e.g. Switch, Async, Retcon{Once}) are the common
30-
// ABIs.
30+
// ABIs. However, specific users often need to modify the behavior of these,
31+
// such as for C++20 or Swift. This can be accomplished by inheriting one of
32+
// the common ABIs and overriding one or more of the methods to create a custom
33+
// ABI. The custom ABI is specified with the coro.begin.custom.abi intrinsic
34+
// instead of the coro.begin intrinsic by providing an i32 in the last arg.
35+
// This is used to lookup a generator for the custom ABI from a set of
36+
// generators provided to the CoroSplitPass constructor.
3137

3238
class BaseABI {
3339
public:

llvm/include/llvm/Transforms/Coroutines/CoroInstr.h

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ class AnyCoroIdInst : public IntrinsicInst {
124124
IntrinsicInst *getCoroBegin() {
125125
for (User *U : users())
126126
if (auto *II = dyn_cast<IntrinsicInst>(U))
127-
if (II->getIntrinsicID() == Intrinsic::coro_begin)
127+
if (II->getIntrinsicID() == Intrinsic::coro_begin ||
128+
II->getIntrinsicID() == Intrinsic::coro_begin_custom_abi)
128129
return II;
129130
llvm_unreachable("no coro.begin associated with coro.id");
130131
}
@@ -442,20 +443,30 @@ class CoroFreeInst : public IntrinsicInst {
442443
}
443444
};
444445

445-
/// This class represents the llvm.coro.begin instructions.
446+
/// This class represents the llvm.coro.begin or llvm.coro.begin.custom.abi
447+
/// instructions.
446448
class CoroBeginInst : public IntrinsicInst {
447-
enum { IdArg, MemArg };
449+
enum { IdArg, MemArg, CustomABIArg };
448450

449451
public:
450452
AnyCoroIdInst *getId() const {
451453
return cast<AnyCoroIdInst>(getArgOperand(IdArg));
452454
}
453455

456+
bool hasCustomABI() const {
457+
return getIntrinsicID() == Intrinsic::coro_begin_custom_abi;
458+
}
459+
460+
int getCustomABI() const {
461+
return cast<ConstantInt>(getArgOperand(CustomABIArg))->getZExtValue();
462+
}
463+
454464
Value *getMem() const { return getArgOperand(MemArg); }
455465

456466
// Methods for support type inquiry through isa, cast, and dyn_cast:
457467
static bool classof(const IntrinsicInst *I) {
458-
return I->getIntrinsicID() == Intrinsic::coro_begin;
468+
return I->getIntrinsicID() == Intrinsic::coro_begin ||
469+
I->getIntrinsicID() == Intrinsic::coro_begin_custom_abi;
459470
}
460471
static bool classof(const Value *V) {
461472
return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));

llvm/include/llvm/Transforms/Coroutines/CoroSplit.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,17 @@ struct CoroSplitPass : PassInfoMixin<CoroSplitPass> {
3232
using BaseABITy = std::function<coro::BaseABI *(Function &, coro::Shape &)>;
3333

3434
CoroSplitPass(bool OptimizeFrame = false);
35+
36+
CoroSplitPass(SmallVector<BaseABITy> GenCustomABIs,
37+
bool OptimizeFrame = false);
38+
39+
// For back compatibility, constructor takes a materiaizlable callback.
40+
CoroSplitPass(std::function<bool(Instruction &)> MaterializableCallback,
41+
bool OptimizeFrame = false);
42+
43+
// For back compatibility, constructor takes a materiaizlable callback.
3544
CoroSplitPass(std::function<bool(Instruction &)> MaterializableCallback,
45+
SmallVector<BaseABITy> GenCustomABIs,
3646
bool OptimizeFrame = false);
3747

3848
PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM,

llvm/lib/Transforms/Coroutines/CoroCleanup.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ bool Lowerer::lower(Function &F) {
5353
default:
5454
continue;
5555
case Intrinsic::coro_begin:
56+
case Intrinsic::coro_begin_custom_abi:
5657
II->replaceAllUsesWith(II->getArgOperand(1));
5758
break;
5859
case Intrinsic::coro_free:
@@ -112,7 +113,8 @@ static bool declaresCoroCleanupIntrinsics(const Module &M) {
112113
M, {"llvm.coro.alloc", "llvm.coro.begin", "llvm.coro.subfn.addr",
113114
"llvm.coro.free", "llvm.coro.id", "llvm.coro.id.retcon",
114115
"llvm.coro.id.async", "llvm.coro.id.retcon.once",
115-
"llvm.coro.async.size.replace", "llvm.coro.async.resume"});
116+
"llvm.coro.async.size.replace", "llvm.coro.async.resume",
117+
"llvm.coro.begin.custom.abi"});
116118
}
117119

118120
PreservedAnalyses CoroCleanupPass::run(Module &M,

llvm/lib/Transforms/Coroutines/CoroSplit.cpp

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2197,7 +2197,16 @@ static void addPrepareFunction(const Module &M,
21972197
Fns.push_back(PrepareFn);
21982198
}
21992199

2200-
static coro::BaseABI *CreateNewABI(Function &F, coro::Shape &S) {
2200+
static coro::BaseABI *
2201+
CreateNewABI(Function &F, coro::Shape &S,
2202+
const SmallVector<CoroSplitPass::BaseABITy> GenCustomABIs) {
2203+
if (S.CoroBegin->hasCustomABI()) {
2204+
unsigned CustomABI = S.CoroBegin->getCustomABI();
2205+
if (CustomABI >= GenCustomABIs.size())
2206+
llvm_unreachable("Custom ABI not found amoung those specified");
2207+
return GenCustomABIs[CustomABI](F, S);
2208+
}
2209+
22012210
switch (S.ABI) {
22022211
case coro::ABI::Switch:
22032212
return new coro::SwitchABI(F, S);
@@ -2213,15 +2222,32 @@ static coro::BaseABI *CreateNewABI(Function &F, coro::Shape &S) {
22132222

22142223
CoroSplitPass::CoroSplitPass(bool OptimizeFrame)
22152224
: CreateAndInitABI([](Function &F, coro::Shape &S) {
2216-
coro::BaseABI *ABI = CreateNewABI(F, S);
2225+
coro::BaseABI *ABI = CreateNewABI(F, S, {});
2226+
ABI->init();
2227+
return ABI;
2228+
}),
2229+
OptimizeFrame(OptimizeFrame) {}
2230+
2231+
CoroSplitPass::CoroSplitPass(
2232+
SmallVector<CoroSplitPass::BaseABITy> GenCustomABIs, bool OptimizeFrame)
2233+
: CreateAndInitABI([=](Function &F, coro::Shape &S) {
2234+
coro::BaseABI *ABI = CreateNewABI(F, S, GenCustomABIs);
22172235
ABI->init();
22182236
return ABI;
22192237
}),
22202238
OptimizeFrame(OptimizeFrame) {}
22212239

22222240
static coro::BaseABI *
22232241
CreateNewABIIsMat(Function &F, coro::Shape &S,
2224-
std::function<bool(Instruction &)> IsMatCallback) {
2242+
std::function<bool(Instruction &)> IsMatCallback,
2243+
const SmallVector<CoroSplitPass::BaseABITy> GenCustomABIs) {
2244+
if (S.CoroBegin->hasCustomABI()) {
2245+
unsigned CustomABI = S.CoroBegin->getCustomABI();
2246+
if (CustomABI >= GenCustomABIs.size())
2247+
llvm_unreachable("Custom ABI not found amoung those specified");
2248+
return GenCustomABIs[CustomABI](F, S);
2249+
}
2250+
22252251
switch (S.ABI) {
22262252
case coro::ABI::Switch:
22272253
return new coro::SwitchABI(F, S, IsMatCallback);
@@ -2240,7 +2266,20 @@ CreateNewABIIsMat(Function &F, coro::Shape &S,
22402266
CoroSplitPass::CoroSplitPass(std::function<bool(Instruction &)> IsMatCallback,
22412267
bool OptimizeFrame)
22422268
: CreateAndInitABI([=](Function &F, coro::Shape &S) {
2243-
coro::BaseABI *ABI = CreateNewABIIsMat(F, S, IsMatCallback);
2269+
coro::BaseABI *ABI = CreateNewABIIsMat(F, S, IsMatCallback, {});
2270+
ABI->init();
2271+
return ABI;
2272+
}),
2273+
OptimizeFrame(OptimizeFrame) {}
2274+
2275+
// For back compatibility, constructor takes a materializable callback and
2276+
// creates a generator for an ABI with a modified materializable callback.
2277+
CoroSplitPass::CoroSplitPass(
2278+
std::function<bool(Instruction &)> IsMatCallback,
2279+
SmallVector<CoroSplitPass::BaseABITy> GenCustomABIs, bool OptimizeFrame)
2280+
: CreateAndInitABI([=](Function &F, coro::Shape &S) {
2281+
coro::BaseABI *ABI =
2282+
CreateNewABIIsMat(F, S, IsMatCallback, GenCustomABIs);
22442283
ABI->init();
22452284
return ABI;
22462285
}),

llvm/lib/Transforms/Coroutines/Coroutines.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ static const char *const CoroIntrinsics[] = {
7373
"llvm.coro.await.suspend.handle",
7474
"llvm.coro.await.suspend.void",
7575
"llvm.coro.begin",
76+
"llvm.coro.begin.custom.abi",
7677
"llvm.coro.destroy",
7778
"llvm.coro.done",
7879
"llvm.coro.end",
@@ -246,7 +247,8 @@ void coro::Shape::analyze(Function &F,
246247
}
247248
break;
248249
}
249-
case Intrinsic::coro_begin: {
250+
case Intrinsic::coro_begin:
251+
case Intrinsic::coro_begin_custom_abi: {
250252
auto CB = cast<CoroBeginInst>(II);
251253

252254
// Ignore coro id's that aren't pre-split.

llvm/unittests/Transforms/Coroutines/ExtraRematTest.cpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,4 +182,91 @@ TEST_F(ExtraRematTest, TestCoroRematWithCallback) {
182182
CallInst *CI = getCallByName(Resume1, "should.remat");
183183
ASSERT_TRUE(CI);
184184
}
185+
186+
StringRef TextCoroBeginCustomABI = R"(
187+
define ptr @f(i32 %n) presplitcoroutine {
188+
entry:
189+
%id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
190+
%size = call i32 @llvm.coro.size.i32()
191+
%alloc = call ptr @malloc(i32 %size)
192+
%hdl = call ptr @llvm.coro.begin.custom.abi(token %id, ptr %alloc, i32 0)
193+
194+
%inc1 = add i32 %n, 1
195+
%val2 = call i32 @should.remat(i32 %inc1)
196+
%sp1 = call i8 @llvm.coro.suspend(token none, i1 false)
197+
switch i8 %sp1, label %suspend [i8 0, label %resume1
198+
i8 1, label %cleanup]
199+
resume1:
200+
%inc2 = add i32 %val2, 1
201+
%sp2 = call i8 @llvm.coro.suspend(token none, i1 false)
202+
switch i8 %sp1, label %suspend [i8 0, label %resume2
203+
i8 1, label %cleanup]
204+
205+
resume2:
206+
call void @print(i32 %val2)
207+
call void @print(i32 %inc2)
208+
br label %cleanup
209+
210+
cleanup:
211+
%mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
212+
call void @free(ptr %mem)
213+
br label %suspend
214+
suspend:
215+
call i1 @llvm.coro.end(ptr %hdl, i1 0)
216+
ret ptr %hdl
217+
}
218+
219+
declare ptr @llvm.coro.free(token, ptr)
220+
declare i32 @llvm.coro.size.i32()
221+
declare i8 @llvm.coro.suspend(token, i1)
222+
declare void @llvm.coro.resume(ptr)
223+
declare void @llvm.coro.destroy(ptr)
224+
225+
declare token @llvm.coro.id(i32, ptr, ptr, ptr)
226+
declare i1 @llvm.coro.alloc(token)
227+
declare ptr @llvm.coro.begin.custom.abi(token, ptr, i32)
228+
declare i1 @llvm.coro.end(ptr, i1)
229+
230+
declare i32 @should.remat(i32)
231+
232+
declare noalias ptr @malloc(i32)
233+
declare void @print(i32)
234+
declare void @free(ptr)
235+
)";
236+
237+
// SwitchABI with overridden isMaterializable
238+
class ExtraCustomABI : public coro::SwitchABI {
239+
public:
240+
ExtraCustomABI(Function &F, coro::Shape &S)
241+
: coro::SwitchABI(F, S, ExtraMaterializable) {}
242+
};
243+
244+
TEST_F(ExtraRematTest, TestCoroRematWithCustomABI) {
245+
ParseAssembly(TextCoroBeginCustomABI);
246+
247+
ASSERT_TRUE(M);
248+
249+
CoroSplitPass::BaseABITy GenCustomABI = [](Function &F, coro::Shape &S) {
250+
return new ExtraCustomABI(F, S);
251+
};
252+
253+
CGSCCPassManager CGPM;
254+
CGPM.addPass(CoroSplitPass({GenCustomABI}));
255+
MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));
256+
MPM.run(*M, MAM);
257+
258+
// Verify that extra rematerializable instruction has been rematerialized
259+
Function *F = M->getFunction("f.resume");
260+
ASSERT_TRUE(F) << "could not find split function f.resume";
261+
262+
BasicBlock *Resume1 = getBasicBlockByName(F, "resume1");
263+
ASSERT_TRUE(Resume1)
264+
<< "could not find expected BB resume1 in split function";
265+
266+
// With callback the extra rematerialization of the function should have
267+
// happened
268+
CallInst *CI = getCallByName(Resume1, "should.remat");
269+
ASSERT_TRUE(CI);
270+
}
271+
185272
} // namespace

0 commit comments

Comments
 (0)