Skip to content

Commit f5ae9de

Browse files
TylerNowickiDanielCChen
authored andcommitted
[Coroutines] Support for Custom ABIs (llvm#111755)
This change extends the current method for creating ABI object to allow users (plugin libraries) to create custom ABI objects for their needs. This is accomplished by inheriting one of the common ABIs and overriding one or more of the methods to create a custom ABI. To use a custom ABI for a given coroutine the coro.begin.custom.abi intrinsic is used in place of the coro.begin intrinsic. This takes an additional i32 arg that specifies the index of an ABI generator for the custom ABI object in a SmallVector passed to the CoroSplitPass ctor. The detailed changes include: * Add the llvm.coro.begin.custom intrinsic used to specify the index of the custom ABI to use for the given coroutine. * Add constructors to CoroSplit that take a list of generators that create the custom ABI object. * Extend the CreateNewABI function used by CoroSplit to return a unique_ptr to an ABI object. * Add has/getCustomABI methods to CoroBeginInst class. * Add a unittest for a custom ABI. See doc update here: llvm#111781
1 parent 0c1b33d commit f5ae9de

File tree

9 files changed

+164
-13
lines changed

9 files changed

+164
-13
lines changed

llvm/include/llvm/Analysis/TargetTransformInfoImpl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,7 @@ class TargetTransformInfoImplBase {
778778
case Intrinsic::experimental_gc_relocate:
779779
case Intrinsic::coro_alloc:
780780
case Intrinsic::coro_begin:
781+
case Intrinsic::coro_begin_custom_abi:
781782
case Intrinsic::coro_free:
782783
case Intrinsic::coro_end:
783784
case Intrinsic::coro_frame:

llvm/include/llvm/IR/Intrinsics.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1719,7 +1719,8 @@ def int_coro_prepare_async : Intrinsic<[llvm_ptr_ty], [llvm_ptr_ty],
17191719
[IntrNoMem]>;
17201720
def int_coro_begin : Intrinsic<[llvm_ptr_ty], [llvm_token_ty, llvm_ptr_ty],
17211721
[WriteOnly<ArgIndex<1>>]>;
1722-
1722+
def int_coro_begin_custom_abi : Intrinsic<[llvm_ptr_ty], [llvm_token_ty, llvm_ptr_ty, llvm_i32_ty],
1723+
[WriteOnly<ArgIndex<1>>]>;
17231724
def int_coro_free : Intrinsic<[llvm_ptr_ty], [llvm_token_ty, llvm_ptr_ty],
17241725
[IntrReadMem, IntrArgMemOnly,
17251726
ReadOnly<ArgIndex<1>>,

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,13 @@ namespace coro {
2929
// This interface/API is to provide an object oriented way to implement ABI
3030
// functionality. This is intended to replace use of the ABI enum to perform
3131
// ABI operations. The ABIs (e.g. Switch, Async, Retcon{Once}) are the common
32-
// ABIs.
32+
// ABIs. However, specific users may need to modify the behavior of these. This
33+
// can be accomplished by inheriting one of the common ABIs and overriding one
34+
// or more of the methods to create a custom ABI. To use a custom ABI for a
35+
// given coroutine the coro.begin.custom.abi intrinsic is used in place of the
36+
// coro.begin intrinsic. This takes an additional i32 arg that specifies the
37+
// index of an ABI generator for the custom ABI object in a SmallVector passed
38+
// to CoroSplitPass ctor.
3339

3440
class BaseABI {
3541
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: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,26 @@ struct Shape;
2828
} // namespace coro
2929

3030
struct CoroSplitPass : PassInfoMixin<CoroSplitPass> {
31+
using BaseABITy =
32+
std::function<std::unique_ptr<coro::BaseABI>(Function &, coro::Shape &)>;
3133

3234
CoroSplitPass(bool OptimizeFrame = false);
35+
36+
CoroSplitPass(SmallVector<BaseABITy> GenCustomABIs,
37+
bool OptimizeFrame = false);
38+
3339
CoroSplitPass(std::function<bool(Instruction &)> MaterializableCallback,
3440
bool OptimizeFrame = false);
3541

42+
CoroSplitPass(std::function<bool(Instruction &)> MaterializableCallback,
43+
SmallVector<BaseABITy> GenCustomABIs,
44+
bool OptimizeFrame = false);
45+
3646
PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM,
3747
LazyCallGraph &CG, CGSCCUpdateResult &UR);
48+
3849
static bool isRequired() { return true; }
3950

40-
using BaseABITy =
41-
std::function<std::unique_ptr<coro::BaseABI>(Function &, coro::Shape &)>;
4251
// Generator for an ABI transformer
4352
BaseABITy CreateAndInitABI;
4453

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: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2200,7 +2200,15 @@ static void addPrepareFunction(const Module &M,
22002200

22012201
static std::unique_ptr<coro::BaseABI>
22022202
CreateNewABI(Function &F, coro::Shape &S,
2203-
std::function<bool(Instruction &)> IsMatCallback) {
2203+
std::function<bool(Instruction &)> IsMatCallback,
2204+
const SmallVector<CoroSplitPass::BaseABITy> GenCustomABIs) {
2205+
if (S.CoroBegin->hasCustomABI()) {
2206+
unsigned CustomABI = S.CoroBegin->getCustomABI();
2207+
if (CustomABI >= GenCustomABIs.size())
2208+
llvm_unreachable("Custom ABI not found amoung those specified");
2209+
return GenCustomABIs[CustomABI](F, S);
2210+
}
2211+
22042212
switch (S.ABI) {
22052213
case coro::ABI::Switch:
22062214
return std::unique_ptr<coro::BaseABI>(
@@ -2221,7 +2229,17 @@ CreateNewABI(Function &F, coro::Shape &S,
22212229
CoroSplitPass::CoroSplitPass(bool OptimizeFrame)
22222230
: CreateAndInitABI([](Function &F, coro::Shape &S) {
22232231
std::unique_ptr<coro::BaseABI> ABI =
2224-
CreateNewABI(F, S, coro::isTriviallyMaterializable);
2232+
CreateNewABI(F, S, coro::isTriviallyMaterializable, {});
2233+
ABI->init();
2234+
return ABI;
2235+
}),
2236+
OptimizeFrame(OptimizeFrame) {}
2237+
2238+
CoroSplitPass::CoroSplitPass(
2239+
SmallVector<CoroSplitPass::BaseABITy> GenCustomABIs, bool OptimizeFrame)
2240+
: CreateAndInitABI([=](Function &F, coro::Shape &S) {
2241+
std::unique_ptr<coro::BaseABI> ABI =
2242+
CreateNewABI(F, S, coro::isTriviallyMaterializable, GenCustomABIs);
22252243
ABI->init();
22262244
return ABI;
22272245
}),
@@ -2232,7 +2250,21 @@ CoroSplitPass::CoroSplitPass(bool OptimizeFrame)
22322250
CoroSplitPass::CoroSplitPass(std::function<bool(Instruction &)> IsMatCallback,
22332251
bool OptimizeFrame)
22342252
: CreateAndInitABI([=](Function &F, coro::Shape &S) {
2235-
std::unique_ptr<coro::BaseABI> ABI = CreateNewABI(F, S, IsMatCallback);
2253+
std::unique_ptr<coro::BaseABI> ABI =
2254+
CreateNewABI(F, S, IsMatCallback, {});
2255+
ABI->init();
2256+
return ABI;
2257+
}),
2258+
OptimizeFrame(OptimizeFrame) {}
2259+
2260+
// For back compatibility, constructor takes a materializable callback and
2261+
// creates a generator for an ABI with a modified materializable callback.
2262+
CoroSplitPass::CoroSplitPass(
2263+
std::function<bool(Instruction &)> IsMatCallback,
2264+
SmallVector<CoroSplitPass::BaseABITy> GenCustomABIs, bool OptimizeFrame)
2265+
: CreateAndInitABI([=](Function &F, coro::Shape &S) {
2266+
std::unique_ptr<coro::BaseABI> ABI =
2267+
CreateNewABI(F, S, IsMatCallback, GenCustomABIs);
22362268
ABI->init();
22372269
return ABI;
22382270
}),

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",
@@ -247,7 +248,8 @@ void coro::Shape::analyze(Function &F,
247248
}
248249
break;
249250
}
250-
case Intrinsic::coro_begin: {
251+
case Intrinsic::coro_begin:
252+
case Intrinsic::coro_begin_custom_abi: {
251253
auto CB = cast<CoroBeginInst>(II);
252254

253255
// 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 std::unique_ptr<coro::BaseABI>(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)