Skip to content

Commit

Permalink
[coroutines] Add support for llvm.coro.noop intrinsics
Browse files Browse the repository at this point in the history
Summary:
A recent addition to Coroutines TS (https://wg21.link/p0913) adds a pre-defined coroutine noop_coroutine that does nothing.
To implement this feature, we implemented an llvm.coro.noop intrinsic that returns a coroutine handle to a coroutine that does nothing when resumed or destroyed.

Reviewers: EricWF, modocache, rnk, lewissbaker

Reviewed By: modocache

Subscribers: llvm-commits

Differential Revision: https://reviews.llvm.org/D45114

llvm-svn: 328986
  • Loading branch information
GorNishanov committed Apr 2, 2018
1 parent 49d8028 commit b0316d9
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 9 deletions.
26 changes: 26 additions & 0 deletions llvm/docs/Coroutines.rst
Expand Up @@ -880,6 +880,32 @@ Example:
%phi = phi i8* [ null, %entry ], [ %alloc, %coro.alloc ]
%frame = call i8* @llvm.coro.begin(token %id, i8* %phi)
.. _coro.noop:

'llvm.coro.noop' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::

declare i8* @llvm.coro.noop()

Overview:
"""""""""

The '``llvm.coro.noop``' intrinsic returns an address of the coroutine frame of
a coroutine that does nothing when resumed or destroyed.

Arguments:
""""""""""

None

Semantics:
""""""""""

This intrinsic is lowered to refer to a private constant coroutine frame. The
resume and destroy handlers for this frame are empty functions that do nothing.
Note that in different translation units llvm.coro.noop may return different pointers.

.. _coro.frame:

'llvm.coro.frame' Intrinsic
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/IR/Intrinsics.td
Expand Up @@ -768,6 +768,7 @@ def int_coro_free : Intrinsic<[llvm_ptr_ty], [llvm_token_ty, llvm_ptr_ty],
def int_coro_end : Intrinsic<[llvm_i1_ty], [llvm_ptr_ty, llvm_i1_ty], []>;

def int_coro_frame : Intrinsic<[llvm_ptr_ty], [], [IntrNoMem]>;
def int_coro_noop : Intrinsic<[llvm_ptr_ty], [], [IntrNoMem]>;
def int_coro_size : Intrinsic<[llvm_anyint_ty], [], [IntrNoMem]>;

def int_coro_save : Intrinsic<[llvm_token_ty], [llvm_ptr_ty], []>;
Expand Down
48 changes: 44 additions & 4 deletions llvm/lib/Transforms/Coroutines/CoroEarly.cpp
Expand Up @@ -27,10 +27,12 @@ namespace {
class Lowerer : public coro::LowererBase {
IRBuilder<> Builder;
PointerType *const AnyResumeFnPtrTy;
Constant *NoopCoro = nullptr;

void lowerResumeOrDestroy(CallSite CS, CoroSubFnInst::ResumeKind);
void lowerCoroPromise(CoroPromiseInst *Intrin);
void lowerCoroDone(IntrinsicInst *II);
void lowerCoroNoop(IntrinsicInst *II);

public:
Lowerer(Module &M)
Expand Down Expand Up @@ -103,6 +105,41 @@ void Lowerer::lowerCoroDone(IntrinsicInst *II) {
II->eraseFromParent();
}

void Lowerer::lowerCoroNoop(IntrinsicInst *II) {
if (!NoopCoro) {
LLVMContext &C = Builder.getContext();
Module &M = *II->getModule();

// Create a noop.frame struct type.
StructType *FrameTy = StructType::create(C, "NoopCoro.Frame");
auto *FramePtrTy = FrameTy->getPointerTo();
auto *FnTy = FunctionType::get(Type::getVoidTy(C), FramePtrTy,
/*IsVarArgs=*/false);
auto *FnPtrTy = FnTy->getPointerTo();
FrameTy->setBody({FnPtrTy, FnPtrTy});

// Create a Noop function that does nothing.
Function *NoopFn =
Function::Create(FnTy, GlobalValue::LinkageTypes::PrivateLinkage,
"NoopCoro.ResumeDestroy", &M);
NoopFn->setCallingConv(CallingConv::Fast);
auto *Entry = BasicBlock::Create(C, "entry", NoopFn);
ReturnInst::Create(C, Entry);

// Create a constant struct for the frame.
Constant* Values[] = {NoopFn, NoopFn};
Constant* NoopCoroConst = ConstantStruct::get(FrameTy, Values);
NoopCoro = new GlobalVariable(M, NoopCoroConst->getType(), /*isConstant=*/true,
GlobalVariable::PrivateLinkage, NoopCoroConst,
"NoopCoro.Frame.Const");
}

Builder.SetInsertPoint(II);
auto *NoopCoroVoidPtr = Builder.CreateBitCast(NoopCoro, Int8Ptr);
II->replaceAllUsesWith(NoopCoroVoidPtr);
II->eraseFromParent();
}

// Prior to CoroSplit, calls to coro.begin needs to be marked as NoDuplicate,
// as CoroSplit assumes there is exactly one coro.begin. After CoroSplit,
// NoDuplicate attribute will be removed from coro.begin otherwise, it will
Expand Down Expand Up @@ -138,6 +175,9 @@ bool Lowerer::lowerEarlyIntrinsics(Function &F) {
if (cast<CoroEndInst>(&I)->isFallthrough())
CS.setCannotDuplicate();
break;
case Intrinsic::coro_noop:
lowerCoroNoop(cast<IntrinsicInst>(&I));
break;
case Intrinsic::coro_id:
// Mark a function that comes out of the frontend that has a coro.id
// with a coroutine attribute.
Expand Down Expand Up @@ -192,10 +232,10 @@ struct CoroEarly : public FunctionPass {
// This pass has work to do only if we find intrinsics we are going to lower
// in the module.
bool doInitialization(Module &M) override {
if (coro::declaresIntrinsics(M, {"llvm.coro.id", "llvm.coro.destroy",
"llvm.coro.done", "llvm.coro.end",
"llvm.coro.free", "llvm.coro.promise",
"llvm.coro.resume", "llvm.coro.suspend"}))
if (coro::declaresIntrinsics(
M, {"llvm.coro.id", "llvm.coro.destroy", "llvm.coro.done",
"llvm.coro.end", "llvm.coro.noop", "llvm.coro.free",
"llvm.coro.promise", "llvm.coro.resume", "llvm.coro.suspend"}))
L = llvm::make_unique<Lowerer>(M);
return false;
}
Expand Down
7 changes: 4 additions & 3 deletions llvm/lib/Transforms/Coroutines/Coroutines.cpp
Expand Up @@ -125,9 +125,10 @@ static bool isCoroutineIntrinsicName(StringRef Name) {
static const char *const CoroIntrinsics[] = {
"llvm.coro.alloc", "llvm.coro.begin", "llvm.coro.destroy",
"llvm.coro.done", "llvm.coro.end", "llvm.coro.frame",
"llvm.coro.free", "llvm.coro.id", "llvm.coro.param",
"llvm.coro.promise", "llvm.coro.resume", "llvm.coro.save",
"llvm.coro.size", "llvm.coro.subfn.addr", "llvm.coro.suspend",
"llvm.coro.free", "llvm.coro.id", "llvm.coro.noop",
"llvm.coro.param", "llvm.coro.promise", "llvm.coro.resume",
"llvm.coro.save", "llvm.coro.size", "llvm.coro.subfn.addr",
"llvm.coro.suspend",
};
return Intrinsic::lookupLLVMIntrinsicByName(CoroIntrinsics, Name) != -1;
}
Expand Down
23 changes: 21 additions & 2 deletions llvm/test/Transforms/Coroutines/coro-early.ll
@@ -1,7 +1,10 @@
; Tests that CoroEarly pass correctly lowers coro.resume and coro.destroy
; intrinsics.
; Tests that CoroEarly pass correctly lowers coro.resume, coro.destroy
; and other intrinsics managed by this pass.
; RUN: opt < %s -S -coro-early | FileCheck %s

; CHECK: %NoopCoro.Frame = type { void (%NoopCoro.Frame*)*, void (%NoopCoro.Frame*)* }
; CHECK: @NoopCoro.Frame.Const = private constant %NoopCoro.Frame { void (%NoopCoro.Frame*)* @NoopCoro.ResumeDestroy, void (%NoopCoro.Frame*)* @NoopCoro.ResumeDestroy }

; CHECK-LABEL: @callResume(
define void @callResume(i8* %hdl) {
; CHECK-NEXT: entry
Expand Down Expand Up @@ -37,5 +40,21 @@ ehcleanup:
cleanupret from %0 unwind to caller
}


; CHECK-LABEL: @noop(
define i8* @noop() {
; CHECK-NEXT: entry
entry:
; CHECK-NEXT: ret i8* bitcast (%NoopCoro.Frame* @NoopCoro.Frame.Const to i8*)
%n = call i8* @llvm.coro.noop()
ret i8* %n
}

; CHECK-LABEL: define private fastcc void @NoopCoro.ResumeDestroy(%NoopCoro.Frame*) {
; CHECK-NEXT: entry
; CHECK-NEXT: ret void


declare void @llvm.coro.resume(i8*)
declare void @llvm.coro.destroy(i8*)
declare i8* @llvm.coro.noop()

0 comments on commit b0316d9

Please sign in to comment.