Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions clang/lib/CodeGen/CGCoroutine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -575,17 +575,19 @@ struct CallCoroEnd final : public EHScopeStack::Cleanup {
llvm::Function *CoroEndFn = CGM.getIntrinsic(llvm::Intrinsic::coro_end);
// See if we have a funclet bundle to associate coro.end with. (WinEH)
auto Bundles = getBundlesForCoroEnd(CGF);
auto *CoroEnd =
CGF.Builder.CreateCall(CoroEndFn,
{NullPtr, CGF.Builder.getTrue(),
llvm::ConstantTokenNone::get(CoroEndFn->getContext())},
Bundles);
CGF.Builder.CreateCall(
CoroEndFn,
{NullPtr, CGF.Builder.getTrue(),
llvm::ConstantTokenNone::get(CoroEndFn->getContext())},
Bundles);
if (Bundles.empty()) {
// Otherwise, (landingpad model), create a conditional branch that leads
// either to a cleanup block or a block with EH resume instruction.
auto *ResumeBB = CGF.getEHResumeBlock(/*isCleanup=*/true);
auto *CleanupContBB = CGF.createBasicBlock("cleanup.cont");
CGF.Builder.CreateCondBr(CoroEnd, ResumeBB, CleanupContBB);
auto *CoroWhereFn = CGM.getIntrinsic(llvm::Intrinsic::coro_where);
auto *CoroWhere = CGF.Builder.CreateCall(CoroWhereFn);
CGF.Builder.CreateCondBr(CoroWhere, ResumeBB, CleanupContBB);
CGF.EmitBlock(CleanupContBB);
}
}
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CodeGenCoroutines/coro-builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ void f(int n) {
// CHECK-NEXT: call ptr @llvm.coro.free(token %[[COROID]], ptr %[[FRAME]])
__builtin_coro_free(__builtin_coro_frame());

// CHECK-NEXT: call i1 @llvm.coro.end(ptr %[[FRAME]], i1 false, token none)
// CHECK-NEXT: call void @llvm.coro.end(ptr %[[FRAME]], i1 false, token none)
__builtin_coro_end(__builtin_coro_frame(), 0);

// CHECK-NEXT: call i8 @llvm.coro.suspend(token none, i1 true)
Expand Down
4 changes: 2 additions & 2 deletions clang/test/CodeGenCoroutines/coro-eh-cleanup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ coro_t f() {

// CHECK: [[COROENDBB]]:
// CHECK-NEXT: %[[CLPAD:.+]] = cleanuppad within none
// CHECK-NEXT: call i1 @llvm.coro.end(ptr null, i1 true, token none) [ "funclet"(token %[[CLPAD]]) ]
// CHECK-NEXT: call void @llvm.coro.end(ptr null, i1 true, token none) [ "funclet"(token %[[CLPAD]]) ]
// CHECK-NEXT: cleanupret from %[[CLPAD]] unwind label

// CHECK-LPAD: @_Z1fv(
Expand All @@ -76,7 +76,7 @@ coro_t f() {
// CHECK-LPAD: to label %{{.+}} unwind label %[[UNWINDBB:.+]]

// CHECK-LPAD: [[UNWINDBB]]:
// CHECK-LPAD: %[[I1RESUME:.+]] = call i1 @llvm.coro.end(ptr null, i1 true, token none)
// CHECK-LPAD: %[[I1RESUME:.+]] = call i1 @llvm.coro.where()
// CHECK-LPAD: br i1 %[[I1RESUME]], label %[[EHRESUME:.+]], label
// CHECK-LPAD: [[EHRESUME]]:
// CHECK-LPAD-NEXT: %[[exn:.+]] = load ptr, ptr %exn.slot, align 8
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CodeGenCoroutines/coro-lambda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,4 @@ void f() {
// CHECK: alloca %"struct.Task::promise_type"
// CHECK: call token @llvm.coro.id(
// CHECK: call i8 @llvm.coro.suspend(
// CHECK: call i1 @llvm.coro.end(
// CHECK: call void @llvm.coro.end(
4 changes: 2 additions & 2 deletions clang/test/CodeGenCoroutines/coro-params.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ void f(int val, MoveOnly moParam, MoveAndCopy mcParam, TrivialABI trivialParam)
// CHECK-NEXT: call ptr @llvm.coro.free(

// The original trivial_abi parameter is destroyed when returning from the ramp.
// CHECK: call i1 @llvm.coro.end
// CHECK: call void @llvm.coro.end
// CHECK: call void @_ZN10TrivialABID1Ev(ptr {{[^,]*}} %[[TrivialAlloca]])
}

Expand Down Expand Up @@ -242,6 +242,6 @@ void msabi(MSParm p) {
co_return;

// The local alloca is used for the destructor call at the end of the ramp.
// MSABI: call i1 @llvm.coro.end
// MSABI: call void @llvm.coro.end
// MSABI: call void @"??1MSParm@@QEAA@XZ"(ptr{{.*}} %[[ParamAlloca]])
}
51 changes: 38 additions & 13 deletions llvm/docs/Coroutines.rst
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ The LLVM IR for this coroutine looks like this:
call void @free(ptr %mem)
br label %suspend
suspend:
%unused = call i1 @llvm.coro.end(ptr %hdl, i1 false, token none)
call void @llvm.coro.end(ptr %hdl, i1 false, token none)
ret ptr %hdl
}

Expand Down Expand Up @@ -637,7 +637,7 @@ store the current value produced by a coroutine.
call void @free(ptr %mem)
br label %suspend
suspend:
%unused = call i1 @llvm.coro.end(ptr %hdl, i1 false, token none)
call void @llvm.coro.end(ptr %hdl, i1 false, token none)
ret ptr %hdl
}

Expand Down Expand Up @@ -806,7 +806,7 @@ The LLVM IR for a coroutine using a Coroutine with a custom ABI looks like:
call void @free(ptr %mem)
br label %suspend
suspend:
%unused = call i1 @llvm.coro.end(ptr %hdl, i1 false, token none)
call void @llvm.coro.end(ptr %hdl, i1 false, token none)
ret ptr %hdl
}

Expand Down Expand Up @@ -1444,7 +1444,7 @@ A frontend should emit function attribute `presplitcoroutine` for the coroutine.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::

declare i1 @llvm.coro.end(ptr <handle>, i1 <unwind>, token <result.token>)
declare void @llvm.coro.end(ptr <handle>, i1 <unwind>, token <result.token>)

Overview:
"""""""""
Expand Down Expand Up @@ -1502,7 +1502,8 @@ For landingpad based exception model, it is expected that frontend uses the
.. code-block:: llvm

ehcleanup:
%InResumePart = call i1 @llvm.coro.end(ptr null, i1 true, token none)
call void @llvm.coro.end(ptr null, i1 true, token none)
%InResumePart = call i1 @llvm.coro.where()
br i1 %InResumePart, label %eh.resume, label %cleanup.cont

cleanup.cont:
Expand All @@ -1515,7 +1516,7 @@ For landingpad based exception model, it is expected that frontend uses the
%lpad.val29 = insertvalue { ptr, i32 } %lpad.val, i32 %sel, 1
resume { ptr, i32 } %lpad.val29

The `CoroSpit` pass replaces `coro.end` with ``True`` in the resume functions,
The `CoroSpit` pass replaces `coro.where` with ``True`` in the resume functions,
thus leading to immediate unwind to the caller, whereas in start function it
is replaced with ``False``, thus allowing to proceed to the rest of the cleanup
code that is only needed during initial invocation of the coroutine.
Expand All @@ -1527,7 +1528,7 @@ referring to an enclosing cleanuppad as follows:

ehcleanup:
%tok = cleanuppad within none []
%unused = call i1 @llvm.coro.end(ptr null, i1 true, token none) [ "funclet"(token %tok) ]
call void @llvm.coro.end(ptr null, i1 true, token none) [ "funclet"(token %tok) ]
cleanupret from %tok unwind label %RestOfTheCleanup

The `CoroSplit` pass, if the funclet bundle is present, will insert
Expand Down Expand Up @@ -1592,7 +1593,7 @@ The number of arguments must match the return type of the continuation function:

cleanup:
%tok = call token (...) @llvm.coro.end.results(i8 %val)
call i1 @llvm.coro.end(ptr %hdl, i1 0, token %tok)
call void @llvm.coro.end(ptr %hdl, i1 0, token %tok)
unreachable

...
Expand All @@ -1604,7 +1605,7 @@ The number of arguments must match the return type of the continuation function:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::

declare i1 @llvm.coro.end.async(ptr <handle>, i1 <unwind>, ...)
declare void @llvm.coro.end.async(ptr <handle>, i1 <unwind>, ...)

Overview:
"""""""""
Expand Down Expand Up @@ -1635,10 +1636,10 @@ the function call.

.. code-block:: llvm

call i1 (ptr, i1, ...) @llvm.coro.end.async(
ptr %hdl, i1 0,
ptr @must_tail_call_return,
ptr %ctxt, ptr %task, ptr %actor)
call void (ptr, i1, ...) @llvm.coro.end.async(
ptr %hdl, i1 0,
ptr @must_tail_call_return,
ptr %ctxt, ptr %task, ptr %actor)
unreachable

.. _coro.suspend:
Expand Down Expand Up @@ -2117,6 +2118,30 @@ Example:
%hdl.result = ... ; get address of returned coroutine handle
ret ptr %hdl.result

'llvm.coro.where' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::

declare i1 @llvm.coro.where()

Overview:
"""""""""

The '``llvm.coro.where``' intrinsic returns a bool value that marks coroutine resume
part and start part.

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

None

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

The `CoroSpit` pass replaces `coro.where` with ``True`` in the resume functions,
whereas in start function it is replaced with ``False``, thus allowing the frontend
separate resume part and start part.

Coroutine Transformation Passes
===============================
CoroEarly
Expand Down
5 changes: 3 additions & 2 deletions llvm/include/llvm/IR/Intrinsics.td
Original file line number Diff line number Diff line change
Expand Up @@ -1775,12 +1775,13 @@ def int_coro_free : Intrinsic<[llvm_ptr_ty], [llvm_token_ty, llvm_ptr_ty],
[IntrReadMem, IntrArgMemOnly,
ReadOnly<ArgIndex<1>>,
NoCapture<ArgIndex<1>>]>;
def int_coro_end : Intrinsic<[llvm_i1_ty], [llvm_ptr_ty, llvm_i1_ty, llvm_token_ty], []>;
def int_coro_end : Intrinsic<[], [llvm_ptr_ty, llvm_i1_ty, llvm_token_ty], []>;
def int_coro_end_results : Intrinsic<[llvm_token_ty], [llvm_vararg_ty]>;
def int_coro_end_async
: Intrinsic<[llvm_i1_ty], [llvm_ptr_ty, llvm_i1_ty, llvm_vararg_ty], []>;
: Intrinsic<[], [llvm_ptr_ty, llvm_i1_ty, llvm_vararg_ty], []>;

def int_coro_frame : Intrinsic<[llvm_ptr_ty], [], [IntrNoMem]>;
def int_coro_where : Intrinsic<[llvm_i1_ty], [], [IntrNoMem]>;
def int_coro_noop : Intrinsic<[llvm_ptr_ty], [], [IntrNoMem]>;
def int_coro_size : Intrinsic<[llvm_anyint_ty], [], [IntrNoMem]>;
def int_coro_align : Intrinsic<[llvm_anyint_ty], [], [IntrNoMem]>;
Expand Down
12 changes: 12 additions & 0 deletions llvm/include/llvm/Transforms/Coroutines/CoroInstr.h
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,18 @@ class CoroFrameInst : public IntrinsicInst {
}
};

/// This represents the llvm.coro.where instruction.
class CoroWhereInst : public IntrinsicInst {
public:
// Methods to support type inquiry through isa, cast, and dyn_cast:
static bool classof(const IntrinsicInst *I) {
return I->getIntrinsicID() == Intrinsic::coro_where;
}
static bool classof(const Value *V) {
return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
}
};

/// This represents the llvm.coro.free instruction.
class CoroFreeInst : public IntrinsicInst {
enum { IdArg, FrameArg };
Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/Transforms/Coroutines/CoroShape.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ enum class ABI {
struct Shape {
CoroBeginInst *CoroBegin = nullptr;
SmallVector<AnyCoroEndInst *, 4> CoroEnds;
SmallVector<CoroWhereInst *, 2> CoroWheres;
SmallVector<CoroSizeInst *, 2> CoroSizes;
SmallVector<CoroAlignInst *, 2> CoroAligns;
SmallVector<AnyCoroSuspendInst *, 4> CoroSuspends;
Expand All @@ -65,6 +66,7 @@ struct Shape {
void clear() {
CoroBegin = nullptr;
CoroEnds.clear();
CoroWheres.clear();
CoroSizes.clear();
CoroAligns.clear();
CoroSuspends.clear();
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Transforms/Coroutines/CoroCleanup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ bool Lowerer::lower(Function &F) {
case Intrinsic::coro_subfn_addr:
lowerSubFn(Builder, cast<CoroSubFnInst>(II));
break;
case Intrinsic::coro_end:
case Intrinsic::coro_suspend_retcon:
case Intrinsic::coro_where:
if (IsPrivateAndUnprocessed) {
II->replaceAllUsesWith(PoisonValue::get(II->getType()));
} else
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Transforms/Coroutines/CoroCloner.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ class BaseCloner {
void replaceRetconOrAsyncSuspendUses();
void replaceCoroSuspends();
void replaceCoroEnds();
void replaceCoroWhere();
void replaceSwiftErrorOps();
void salvageDebugInfo();
void handleFinalSuspend();
Expand Down
29 changes: 21 additions & 8 deletions llvm/lib/Transforms/Coroutines/CoroSplit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -388,10 +388,6 @@ static void replaceCoroEnd(AnyCoroEndInst *End, const coro::Shape &Shape,
replaceUnwindCoroEnd(End, Shape, FramePtr, InResume, CG);
else
replaceFallthroughCoroEnd(End, Shape, FramePtr, InResume, CG);

auto &Context = End->getContext();
End->replaceAllUsesWith(InResume ? ConstantInt::getTrue(Context)
: ConstantInt::getFalse(Context));
End->eraseFromParent();
}

Expand Down Expand Up @@ -562,6 +558,15 @@ void coro::BaseCloner::replaceCoroEnds() {
}
}

void coro::BaseCloner::replaceCoroWhere() {
auto &Ctx = OrigF.getContext();
for (auto *CW : Shape.CoroWheres) {
auto *NewCW = cast<CoroWhereInst>(VMap[CW]);
NewCW->replaceAllUsesWith(ConstantInt::getTrue(Ctx));
NewCW->eraseFromParent();
}
}

static void replaceSwiftErrorOps(Function &F, coro::Shape &Shape,
ValueToValueMapTy *VMap) {
if (Shape.ABI == coro::ABI::Async && Shape.CoroSuspends.empty())
Expand Down Expand Up @@ -1077,6 +1082,8 @@ void coro::BaseCloner::create() {
// Remove coro.end intrinsics.
replaceCoroEnds();

replaceCoroWhere();

// Salvage debug info that points into the coroutine frame.
salvageDebugInfo();
}
Expand Down Expand Up @@ -1951,11 +1958,16 @@ static void removeCoroEndsFromRampFunction(const coro::Shape &Shape) {
replaceCoroEnd(End, Shape, Shape.FramePtr, /*in resume*/ false, nullptr);
}
} else {
for (llvm::AnyCoroEndInst *End : Shape.CoroEnds) {
auto &Context = End->getContext();
End->replaceAllUsesWith(ConstantInt::getFalse(Context));
for (llvm::AnyCoroEndInst *End : Shape.CoroEnds)
End->eraseFromParent();
}
}
}

static void removeCoroWhereFromRampFunction(const coro::Shape &Shape) {
for (auto *CW : Shape.CoroWheres) {
auto &Ctx = CW->getContext();
CW->replaceAllUsesWith(ConstantInt::getFalse(Ctx));
CW->eraseFromParent();
}
}

Expand Down Expand Up @@ -2020,6 +2032,7 @@ static void doSplitCoroutine(Function &F, SmallVectorImpl<Function *> &Clones,
coro::salvageDebugInfo(ArgToAllocaMap, *DVR, false /*UseEntryValue*/);

removeCoroEndsFromRampFunction(Shape);
removeCoroWhereFromRampFunction(Shape);

if (shouldCreateNoAllocVariant)
SwitchCoroutineSplitter::createNoAllocVariant(F, Shape, Clones);
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Transforms/Coroutines/Coroutines.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ static Intrinsic::ID NonOverloadedCoroIntrinsics[] = {
Intrinsic::coro_save,
Intrinsic::coro_subfn_addr,
Intrinsic::coro_suspend,
Intrinsic::coro_where,
};

bool coro::isSuspendBlock(BasicBlock *BB) {
Expand Down Expand Up @@ -275,6 +276,9 @@ void coro::Shape::analyze(Function &F,
}
}
break;
case Intrinsic::coro_where:
CoroWheres.push_back(cast<CoroWhereInst>(II));
break;
case Intrinsic::coro_promise:
assert(CoroPromise == nullptr &&
"CoroEarly must ensure coro.promise unique");
Expand Down
4 changes: 2 additions & 2 deletions llvm/test/Analysis/GlobalsModRef/nonescaping-noalias.ll
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ define ptr @test1_tls_noopt(ptr %coro, ptr %param) presplitcoroutine {
; CHECK-NEXT: store i32 [[V]], ptr [[PARAM]], align 4
; CHECK-NEXT: ret ptr [[CORO]]
; CHECK: suspend:
; CHECK-NEXT: [[TMP1:%.*]] = call i1 @llvm.coro.end(ptr [[CORO]], i1 false, token none)
; CHECK-NEXT: call void @llvm.coro.end(ptr [[CORO]], i1 false, token none)
; CHECK-NEXT: ret ptr [[CORO]]
;
entry:
Expand All @@ -79,7 +79,7 @@ resume:
ret ptr %coro

suspend:
call i1 @llvm.coro.end(ptr %coro, i1 0, token none)
call void @llvm.coro.end(ptr %coro, i1 0, token none)
ret ptr %coro
}

Expand Down
6 changes: 3 additions & 3 deletions llvm/test/Assembler/auto_upgrade_intrinsics.ll
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ entry:
ret void
}

declare i1 @llvm.coro.end(ptr, i1)
declare void @llvm.coro.end(ptr, i1)
define void @test.coro.end(ptr %ptr) {
; CHECK-LABEL: @test.coro.end(
; CHECK: call i1 @llvm.coro.end(ptr %ptr, i1 false, token none)
call i1 @llvm.coro.end(ptr %ptr, i1 false)
; CHECK: call void @llvm.coro.end(ptr %ptr, i1 false, token none)
call void @llvm.coro.end(ptr %ptr, i1 false)
ret void
}

Expand Down
Loading