Skip to content

Commit f1d437f

Browse files
nate-chandlerbnbarham
authored andcommitted
[Coro] Add variant of retcon.once ABI.
Like async coroutines, it's fixed-per-function-size frame is caller allocated--the size is stored in a global "coro function pointer". Like retcon coroutines, dynamic allocations are performed via intrinsic-provided allocation and deallocation functions. Unlike both, it takes an allocator struct as an argument which is forwarded to the allocation/deallocation functions. (cherry picked from commit b0e1dc9)
1 parent f6795cb commit f1d437f

File tree

9 files changed

+246
-38
lines changed

9 files changed

+246
-38
lines changed

llvm/include/llvm/IR/Intrinsics.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1742,6 +1742,10 @@ def int_coro_id_retcon_once : Intrinsic<[llvm_token_ty],
17421742
[llvm_i32_ty, llvm_i32_ty, llvm_ptr_ty,
17431743
llvm_ptr_ty, llvm_ptr_ty, llvm_ptr_ty, llvm_vararg_ty],
17441744
[]>;
1745+
def int_coro_id_retcon_once_dynamic : Intrinsic<[llvm_token_ty],
1746+
[llvm_i32_ty, llvm_i32_ty, llvm_ptr_ty, llvm_ptr_ty, llvm_ptr_ty,
1747+
llvm_ptr_ty, llvm_ptr_ty, llvm_ptr_ty],
1748+
[]>;
17451749
def int_coro_alloc : Intrinsic<[llvm_i1_ty], [llvm_token_ty], []>;
17461750
def int_coro_id_async : Intrinsic<[llvm_token_ty],
17471751
[llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_ptr_ty],

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

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ class AnyCoroIdInst : public IntrinsicInst {
135135
auto ID = I->getIntrinsicID();
136136
return ID == Intrinsic::coro_id || ID == Intrinsic::coro_id_retcon ||
137137
ID == Intrinsic::coro_id_retcon_once ||
138+
ID == Intrinsic::coro_id_retcon_once_dynamic ||
138139
ID == Intrinsic::coro_id_async;
139140
}
140141

@@ -314,6 +315,72 @@ class CoroIdRetconOnceInst : public AnyCoroIdRetconInst {
314315
}
315316
};
316317

318+
/// This represents the llvm.coro.id.retcon.once.dynamic instruction.
319+
class LLVM_LIBRARY_VISIBILITY CoroIdRetconOnceDynamicInst
320+
: public AnyCoroIdInst {
321+
enum {
322+
SizeArg,
323+
AlignArg,
324+
CoroFuncPtrArg,
325+
AllocatorArg,
326+
StorageArg,
327+
PrototypeArg,
328+
AllocArg,
329+
DeallocArg
330+
};
331+
332+
public:
333+
void checkWellFormed() const;
334+
335+
uint64_t getStorageSize() const {
336+
return cast<ConstantInt>(getArgOperand(SizeArg))->getZExtValue();
337+
}
338+
339+
Align getStorageAlignment() const {
340+
return cast<ConstantInt>(getArgOperand(AlignArg))->getAlignValue();
341+
}
342+
343+
Value *getStorage() const { return getArgOperand(StorageArg); }
344+
345+
/// Return the coro function pointer address. This should be the address of
346+
/// a coro function pointer struct for the current coro function.
347+
/// struct coro_function_pointer {
348+
/// uint32_t frame size;
349+
/// uint32_t relative_pointer(coro_function);
350+
/// };
351+
GlobalVariable *getCoroFunctionPointer() const {
352+
return cast<GlobalVariable>(
353+
getArgOperand(CoroFuncPtrArg)->stripPointerCasts());
354+
}
355+
356+
/// Return the prototype for the continuation function. The type,
357+
/// attributes, and calling convention of the continuation function(s)
358+
/// are taken from this declaration.
359+
Function *getPrototype() const {
360+
return cast<Function>(getArgOperand(PrototypeArg)->stripPointerCasts());
361+
}
362+
363+
/// Return the function to use for allocating memory.
364+
Function *getAllocFunction() const {
365+
return cast<Function>(getArgOperand(AllocArg)->stripPointerCasts());
366+
}
367+
368+
/// Return the function to use for deallocating memory.
369+
Function *getDeallocFunction() const {
370+
return cast<Function>(getArgOperand(DeallocArg)->stripPointerCasts());
371+
}
372+
373+
Value *getAllocator() const { return getArgOperand(AllocatorArg); }
374+
375+
// Methods to support type inquiry through isa, cast, and dyn_cast:
376+
static bool classof(const IntrinsicInst *I) {
377+
return I->getIntrinsicID() == Intrinsic::coro_id_retcon_once_dynamic;
378+
}
379+
static bool classof(const Value *V) {
380+
return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
381+
}
382+
};
383+
317384
/// This represents the llvm.coro.id.async instruction.
318385
class CoroIdAsyncInst : public AnyCoroIdInst {
319386
enum { SizeArg, AlignArg, StorageArg, AsyncFuncPtrArg };

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ enum class ABI {
4545
/// single continuation function. The continuation function is available as an
4646
/// intrinsic.
4747
Async,
48+
49+
/// The variant of RetconOnce which features a dynamically-sized caller
50+
/// allocation.
51+
RetconOnceDynamic,
4852
};
4953

5054
// Holds structural Coroutine Intrinsics for a particular function and other
@@ -127,9 +131,18 @@ struct Shape {
127131
Function *ResumePrototype;
128132
Function *Alloc;
129133
Function *Dealloc;
134+
Value *Allocator;
130135
BasicBlock *ReturnBlock;
131136
bool IsFrameInlineInStorage;
132137
ConstantInt* TypeId;
138+
GlobalVariable *CoroFuncPointer;
139+
Value *Storage;
140+
uint64_t StorageSize;
141+
Align StorageAlignment;
142+
// computed during splitting:
143+
uint64_t ContextSize;
144+
145+
Align getStorageAlignment() const { return Align(StorageAlignment); }
133146
};
134147

135148
struct AsyncLoweringStorage {
@@ -194,6 +207,7 @@ struct Shape {
194207
/*IsVarArg=*/false);
195208
case coro::ABI::Retcon:
196209
case coro::ABI::RetconOnce:
210+
case coro::ABI::RetconOnceDynamic:
197211
return RetconLowering.ResumePrototype->getFunctionType();
198212
case coro::ABI::Async:
199213
// Not used. The function type depends on the active suspend.
@@ -216,7 +230,8 @@ struct Shape {
216230
}
217231

218232
ArrayRef<Type *> getRetconResumeTypes() const {
219-
assert(ABI == coro::ABI::Retcon || ABI == coro::ABI::RetconOnce);
233+
assert(ABI == coro::ABI::Retcon || ABI == coro::ABI::RetconOnce ||
234+
ABI == coro::ABI::RetconOnceDynamic);
220235

221236
// The safety of all this is checked by checkWFRetconPrototype.
222237
auto FTy = RetconLowering.ResumePrototype->getFunctionType();
@@ -230,6 +245,7 @@ struct Shape {
230245

231246
case coro::ABI::Retcon:
232247
case coro::ABI::RetconOnce:
248+
case coro::ABI::RetconOnceDynamic:
233249
return RetconLowering.ResumePrototype->getCallingConv();
234250
case coro::ABI::Async:
235251
return AsyncLowering.AsyncCC;
@@ -262,7 +278,7 @@ struct Shape {
262278
/// \param CG - if non-null, will be updated for the new call
263279
void emitDealloc(IRBuilder<> &Builder, Value *Ptr, CallGraph *CG) const;
264280

265-
Shape() = default;
281+
Shape() = delete;
266282
explicit Shape(Function &F) {
267283
SmallVector<CoroFrameInst *, 8> CoroFrames;
268284
SmallVector<CoroSaveInst *, 2> UnusedCoroSaves;

llvm/lib/Transforms/Coroutines/CoroCleanup.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ bool Lowerer::lower(Function &F) {
6969
case Intrinsic::coro_id:
7070
case Intrinsic::coro_id_retcon:
7171
case Intrinsic::coro_id_retcon_once:
72+
case Intrinsic::coro_id_retcon_once_dynamic:
7273
case Intrinsic::coro_id_async:
7374
II->replaceAllUsesWith(ConstantTokenNone::get(Context));
7475
break;

llvm/lib/Transforms/Coroutines/CoroEarly.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ void Lowerer::lowerEarlyIntrinsics(Function &F) {
209209
break;
210210
case Intrinsic::coro_id_retcon:
211211
case Intrinsic::coro_id_retcon_once:
212+
case Intrinsic::coro_id_retcon_once_dynamic:
212213
case Intrinsic::coro_id_async:
213214
F.setPresplitCoroutine();
214215
break;

llvm/lib/Transforms/Coroutines/CoroFrame.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,19 @@ static StructType *buildFrameType(Function &F, coro::Shape &Shape,
962962
B.getStructAlign() <= Id->getStorageAlignment());
963963
break;
964964
}
965+
case coro::ABI::RetconOnceDynamic: {
966+
// In the dynamic retcon.once ABI, the frame is always inline in the
967+
// storage.
968+
Shape.RetconLowering.IsFrameInlineInStorage = true;
969+
Shape.RetconLowering.ContextSize =
970+
alignTo(Shape.FrameSize, Shape.RetconLowering.StorageAlignment);
971+
if (Shape.RetconLowering.StorageAlignment < Shape.FrameAlign) {
972+
report_fatal_error(
973+
"The alignment requirment of frame variables cannot be higher than "
974+
"the alignment of the coro function context");
975+
}
976+
break;
977+
}
965978
case coro::ABI::Async: {
966979
Shape.AsyncLowering.FrameOffset =
967980
alignTo(Shape.AsyncLowering.ContextHeaderSize, Shape.FrameAlign);
@@ -1188,7 +1201,8 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
11881201

11891202
// retcon and retcon.once lowering assumes all uses have been sunk.
11901203
if (Shape.ABI == coro::ABI::Retcon || Shape.ABI == coro::ABI::RetconOnce ||
1191-
Shape.ABI == coro::ABI::Async) {
1204+
Shape.ABI == coro::ABI::Async ||
1205+
Shape.ABI == coro::ABI::RetconOnceDynamic) {
11921206
// If we found any allocas, replace all of their remaining uses with Geps.
11931207
Builder.SetInsertPoint(SpillBlock, SpillBlock->begin());
11941208
for (const auto &P : FrameData.Allocas) {
@@ -2078,7 +2092,8 @@ void coro::BaseABI::buildCoroutineFrame(bool OptimizeFrame) {
20782092

20792093
const DominatorTree DT(F);
20802094
if (Shape.ABI != coro::ABI::Async && Shape.ABI != coro::ABI::Retcon &&
2081-
Shape.ABI != coro::ABI::RetconOnce)
2095+
Shape.ABI != coro::ABI::RetconOnce &&
2096+
Shape.ABI != coro::ABI::RetconOnceDynamic)
20822097
sinkLifetimeStartMarkers(F, Shape, Checker, DT);
20832098

20842099
// All values (that are not allocas) that needs to be spilled to the frame.
@@ -2098,7 +2113,8 @@ void coro::BaseABI::buildCoroutineFrame(bool OptimizeFrame) {
20982113
LLVM_DEBUG(dumpSpills("Spills", Spills));
20992114

21002115
if (Shape.ABI == coro::ABI::Retcon || Shape.ABI == coro::ABI::RetconOnce ||
2101-
Shape.ABI == coro::ABI::Async)
2116+
Shape.ABI == coro::ABI::Async ||
2117+
Shape.ABI == coro::ABI::RetconOnceDynamic)
21022118
sinkSpillUsesAfterCoroBegin(DT, Shape.CoroBegin, Spills, Allocas);
21032119

21042120
// Build frame

0 commit comments

Comments
 (0)