aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Transforms/Coroutines/Coroutines.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2021-02-16 20:13:02 +0000
committerDimitry Andric <dim@FreeBSD.org>2021-02-16 20:13:02 +0000
commitb60736ec1405bb0a8dd40989f67ef4c93da068ab (patch)
tree5c43fbb7c9fc45f0f87e0e6795a86267dbd12f9d /llvm/lib/Transforms/Coroutines/Coroutines.cpp
parentcfca06d7963fa0909f90483b42a6d7d194d01e08 (diff)
Diffstat (limited to 'llvm/lib/Transforms/Coroutines/Coroutines.cpp')
-rw-r--r--llvm/lib/Transforms/Coroutines/Coroutines.cpp109
1 files changed, 104 insertions, 5 deletions
diff --git a/llvm/lib/Transforms/Coroutines/Coroutines.cpp b/llvm/lib/Transforms/Coroutines/Coroutines.cpp
index 87c3a8b0d0cf..6699a5c46313 100644
--- a/llvm/lib/Transforms/Coroutines/Coroutines.cpp
+++ b/llvm/lib/Transforms/Coroutines/Coroutines.cpp
@@ -69,7 +69,7 @@ static void addCoroutineScalarOptimizerPasses(const PassManagerBuilder &Builder,
static void addCoroutineSCCPasses(const PassManagerBuilder &Builder,
legacy::PassManagerBase &PM) {
- PM.add(createCoroSplitLegacyPass());
+ PM.add(createCoroSplitLegacyPass(Builder.OptLevel != 0));
}
static void addCoroutineOptimizerLastPasses(const PassManagerBuilder &Builder,
@@ -124,17 +124,23 @@ static bool isCoroutineIntrinsicName(StringRef Name) {
// NOTE: Must be sorted!
static const char *const CoroIntrinsics[] = {
"llvm.coro.alloc",
+ "llvm.coro.async.context.alloc",
+ "llvm.coro.async.context.dealloc",
+ "llvm.coro.async.store_resume",
"llvm.coro.begin",
"llvm.coro.destroy",
"llvm.coro.done",
"llvm.coro.end",
+ "llvm.coro.end.async",
"llvm.coro.frame",
"llvm.coro.free",
"llvm.coro.id",
+ "llvm.coro.id.async",
"llvm.coro.id.retcon",
"llvm.coro.id.retcon.once",
"llvm.coro.noop",
"llvm.coro.param",
+ "llvm.coro.prepare.async",
"llvm.coro.prepare.retcon",
"llvm.coro.promise",
"llvm.coro.resume",
@@ -142,6 +148,7 @@ static bool isCoroutineIntrinsicName(StringRef Name) {
"llvm.coro.size",
"llvm.coro.subfn.addr",
"llvm.coro.suspend",
+ "llvm.coro.suspend.async",
"llvm.coro.suspend.retcon",
};
return Intrinsic::lookupLLVMIntrinsicByName(CoroIntrinsics, Name) != -1;
@@ -269,6 +276,12 @@ void coro::Shape::buildFrom(Function &F) {
if (II->use_empty())
UnusedCoroSaves.push_back(cast<CoroSaveInst>(II));
break;
+ case Intrinsic::coro_suspend_async: {
+ auto *Suspend = cast<CoroSuspendAsyncInst>(II);
+ Suspend->checkWellFormed();
+ CoroSuspends.push_back(Suspend);
+ break;
+ }
case Intrinsic::coro_suspend_retcon: {
auto Suspend = cast<CoroSuspendRetconInst>(II);
CoroSuspends.push_back(Suspend);
@@ -304,11 +317,16 @@ void coro::Shape::buildFrom(Function &F) {
CoroBegin = CB;
break;
}
+ case Intrinsic::coro_end_async:
case Intrinsic::coro_end:
- CoroEnds.push_back(cast<CoroEndInst>(II));
- if (CoroEnds.back()->isFallthrough()) {
+ CoroEnds.push_back(cast<AnyCoroEndInst>(II));
+ if (auto *AsyncEnd = dyn_cast<CoroAsyncEndInst>(II)) {
+ AsyncEnd->checkWellFormed();
+ }
+ if (CoroEnds.back()->isFallthrough() && isa<CoroEndInst>(II)) {
// Make sure that the fallthrough coro.end is the first element in the
// CoroEnds vector.
+ // Note: I don't think this is neccessary anymore.
if (CoroEnds.size() > 1) {
if (CoroEnds.front()->isFallthrough())
report_fatal_error(
@@ -341,7 +359,7 @@ void coro::Shape::buildFrom(Function &F) {
}
// Replace all coro.ends with unreachable instruction.
- for (CoroEndInst *CE : CoroEnds)
+ for (AnyCoroEndInst *CE : CoroEnds)
changeToUnreachable(CE, /*UseLLVMTrap=*/false);
return;
@@ -371,7 +389,23 @@ void coro::Shape::buildFrom(Function &F) {
}
break;
}
-
+ case Intrinsic::coro_id_async: {
+ auto *AsyncId = cast<CoroIdAsyncInst>(Id);
+ AsyncId->checkWellFormed();
+ this->ABI = coro::ABI::Async;
+ this->AsyncLowering.Context = AsyncId->getStorage();
+ this->AsyncLowering.ContextArgNo = AsyncId->getStorageArgumentIndex();
+ this->AsyncLowering.ContextHeaderSize = AsyncId->getStorageSize();
+ this->AsyncLowering.ContextAlignment =
+ AsyncId->getStorageAlignment().value();
+ this->AsyncLowering.AsyncFuncPointer = AsyncId->getAsyncFunctionPointer();
+ auto &Context = F.getContext();
+ auto *Int8PtrTy = Type::getInt8PtrTy(Context);
+ auto *VoidTy = Type::getVoidTy(Context);
+ this->AsyncLowering.AsyncFuncTy =
+ FunctionType::get(VoidTy, {Int8PtrTy, Int8PtrTy, Int8PtrTy}, false);
+ break;
+ };
case Intrinsic::coro_id_retcon:
case Intrinsic::coro_id_retcon_once: {
auto ContinuationId = cast<AnyCoroIdRetconInst>(Id);
@@ -512,6 +546,8 @@ Value *coro::Shape::emitAlloc(IRBuilder<> &Builder, Value *Size,
addCallToCallGraph(CG, Call, Alloc);
return Call;
}
+ case coro::ABI::Async:
+ llvm_unreachable("can't allocate memory in coro async-lowering");
}
llvm_unreachable("Unknown coro::ABI enum");
}
@@ -532,6 +568,8 @@ void coro::Shape::emitDealloc(IRBuilder<> &Builder, Value *Ptr,
addCallToCallGraph(CG, Call, Dealloc);
return;
}
+ case coro::ABI::Async:
+ llvm_unreachable("can't allocate memory in coro async-lowering");
}
llvm_unreachable("Unknown coro::ABI enum");
}
@@ -633,6 +671,67 @@ void AnyCoroIdRetconInst::checkWellFormed() const {
checkWFDealloc(this, getArgOperand(DeallocArg));
}
+static void checkAsyncFuncPointer(const Instruction *I, Value *V) {
+ auto *AsyncFuncPtrAddr = dyn_cast<GlobalVariable>(V->stripPointerCasts());
+ if (!AsyncFuncPtrAddr)
+ fail(I, "llvm.coro.id.async async function pointer not a global", V);
+
+ auto *StructTy =
+ cast<StructType>(AsyncFuncPtrAddr->getType()->getPointerElementType());
+ if (StructTy->isOpaque() || !StructTy->isPacked() ||
+ StructTy->getNumElements() != 2 ||
+ !StructTy->getElementType(0)->isIntegerTy(32) ||
+ !StructTy->getElementType(1)->isIntegerTy(32))
+ fail(I,
+ "llvm.coro.id.async async function pointer argument's type is not "
+ "<{i32, i32}>",
+ V);
+}
+
+void CoroIdAsyncInst::checkWellFormed() const {
+ checkConstantInt(this, getArgOperand(SizeArg),
+ "size argument to coro.id.async must be constant");
+ checkConstantInt(this, getArgOperand(AlignArg),
+ "alignment argument to coro.id.async must be constant");
+ checkConstantInt(this, getArgOperand(StorageArg),
+ "storage argument offset to coro.id.async must be constant");
+ checkAsyncFuncPointer(this, getArgOperand(AsyncFuncPtrArg));
+}
+
+static void checkAsyncContextProjectFunction(const Instruction *I,
+ Function *F) {
+ auto *FunTy = cast<FunctionType>(F->getType()->getPointerElementType());
+ if (!FunTy->getReturnType()->isPointerTy() ||
+ !FunTy->getReturnType()->getPointerElementType()->isIntegerTy(8))
+ fail(I,
+ "llvm.coro.suspend.async resume function projection function must "
+ "return an i8* type",
+ F);
+ if (FunTy->getNumParams() != 1 || !FunTy->getParamType(0)->isPointerTy() ||
+ !FunTy->getParamType(0)->getPointerElementType()->isIntegerTy(8))
+ fail(I,
+ "llvm.coro.suspend.async resume function projection function must "
+ "take one i8* type as parameter",
+ F);
+}
+
+void CoroSuspendAsyncInst::checkWellFormed() const {
+ checkAsyncContextProjectFunction(this, getAsyncContextProjectionFunction());
+}
+
+void CoroAsyncEndInst::checkWellFormed() const {
+ auto *MustTailCallFunc = getMustTailCallFunction();
+ if (!MustTailCallFunc)
+ return;
+ auto *FnTy =
+ cast<FunctionType>(MustTailCallFunc->getType()->getPointerElementType());
+ if (FnTy->getNumParams() != (getNumArgOperands() - 3))
+ fail(this,
+ "llvm.coro.end.async must tail call function argument type must "
+ "match the tail arguments",
+ MustTailCallFunc);
+}
+
void LLVMAddCoroEarlyPass(LLVMPassManagerRef PM) {
unwrap(PM)->add(createCoroEarlyLegacyPass());
}