diff --git a/include/llvm/IR/Intrinsics.td b/include/llvm/IR/Intrinsics.td index f15ff5588c2..98142d196c5 100644 --- a/include/llvm/IR/Intrinsics.td +++ b/include/llvm/IR/Intrinsics.td @@ -982,7 +982,7 @@ def int_coro_size : Intrinsic<[llvm_anyint_ty], [], [IntrNoMem]>; def int_coro_save : Intrinsic<[llvm_token_ty], [llvm_ptr_ty], []>; def int_coro_suspend : Intrinsic<[llvm_i8_ty], [llvm_token_ty, llvm_i1_ty], []>; -def int_coro_suspend_retcon : Intrinsic<[llvm_i1_ty], [llvm_vararg_ty], []>; +def int_coro_suspend_retcon : Intrinsic<[llvm_any_ty], [llvm_vararg_ty], []>; def int_coro_prepare_retcon : Intrinsic<[llvm_ptr_ty], [llvm_ptr_ty], [IntrNoMem]>; diff --git a/lib/Transforms/Coroutines/CoroInternal.h b/lib/Transforms/Coroutines/CoroInternal.h index 78e44988428..d2348057c24 100644 --- a/lib/Transforms/Coroutines/CoroInternal.h +++ b/lib/Transforms/Coroutines/CoroInternal.h @@ -171,7 +171,7 @@ struct LLVM_LIBRARY_VISIBILITY Shape { ABI == coro::ABI::RetconOnce); auto FTy = CoroBegin->getFunction()->getFunctionType(); - // This is checked by AnyCoroIdRetconInst::isWellFormed(). + // The safety of all this is checked by checkWFRetconPrototype. if (auto STy = dyn_cast(FTy->getReturnType())) { return STy->elements().slice(1); } else { @@ -179,6 +179,15 @@ struct LLVM_LIBRARY_VISIBILITY Shape { } } + ArrayRef getRetconResumeTypes() const { + assert(ABI == coro::ABI::Retcon || + ABI == coro::ABI::RetconOnce); + + // The safety of all this is checked by checkWFRetconPrototype. + auto FTy = RetconLowering.ResumePrototype->getFunctionType(); + return FTy->params().slice(1); + } + CallingConv::ID getResumeFunctionCC() const { switch (ABI) { case coro::ABI::Switch: diff --git a/lib/Transforms/Coroutines/CoroSplit.cpp b/lib/Transforms/Coroutines/CoroSplit.cpp index b6862d1f67c..318a012b6a7 100644 --- a/lib/Transforms/Coroutines/CoroSplit.cpp +++ b/lib/Transforms/Coroutines/CoroSplit.cpp @@ -144,6 +144,7 @@ private: void createDeclaration(); void replaceEntryBlock(); Value *deriveNewFramePointer(); + void replaceRetconSuspendUses(); void replaceCoroSuspends(); void replaceCoroEnds(); void handleFinalSuspend(); @@ -402,6 +403,52 @@ static Function *createCloneDeclaration(Function &OrigF, coro::Shape &Shape, return NewF; } +/// Replace uses of the active llvm.coro.suspend.retcon call with the +/// arguments to the continuation function. +/// +/// This assumes that the builder has a meaningful insertion point. +void CoroCloner::replaceRetconSuspendUses() { + assert(Shape.ABI == coro::ABI::Retcon || + Shape.ABI == coro::ABI::RetconOnce); + + auto NewS = VMap[ActiveSuspend]; + if (NewS->use_empty()) return; + + // Copy out all the continuation arguments after the buffer pointer into + // an easily-indexed data structure for convenience. + SmallVector Args; + for (auto I = std::next(NewF->arg_begin()), E = NewF->arg_end(); I != E; ++I) + Args.push_back(&*I); + + // If the suspend returns a single scalar value, we can just do a simple + // replacement. + if (!isa(NewS->getType())) { + assert(Args.size() == 1); + NewS->replaceAllUsesWith(Args.front()); + return; + } + + // Try to peephole extracts of an aggregate return. + for (auto UI = NewS->use_begin(), UE = NewS->use_end(); UI != UE; ) { + auto EVI = dyn_cast((UI++)->getUser()); + if (!EVI || EVI->getNumIndices() != 1) + continue; + + EVI->replaceAllUsesWith(Args[EVI->getIndices().front()]); + EVI->eraseFromParent(); + } + + // If we have no remaining uses, we're done. + if (NewS->use_empty()) return; + + // Otherwise, we need to create an aggregate. + Value *Agg = UndefValue::get(NewS->getType()); + for (size_t I = 0, E = Args.size(); I != E; ++I) + Agg = Builder.CreateInsertValue(Agg, Args[I], I); + + NewS->replaceAllUsesWith(Agg); +} + void CoroCloner::replaceCoroSuspends() { Value *SuspendResult; @@ -416,15 +463,12 @@ void CoroCloner::replaceCoroSuspends() { SuspendResult = Builder.getInt8(isSwitchDestroyFunction() ? 1 : 0); break; - // In continuation lowering, replace all of the suspend uses with false to - // indicate that they're not unwinding resumes. We've already mapped the - // active suspend to the appropriate argument, so any other suspend values - // that are still being used must be from previous suspends. It's UB to try - // to suspend during unwind, so they must be from regular resumes. + // In returned-continuation lowering, the arguments from earlier + // continuations are theoretically arbitrary, and they should have been + // spilled. case coro::ABI::RetconOnce: case coro::ABI::Retcon: - SuspendResult = Builder.getInt1(false); - break; + return; } for (AnyCoroSuspendInst *CS : Shape.CoroSuspends) { @@ -619,14 +663,11 @@ void CoroCloner::create() { case coro::ABI::Retcon: case coro::ABI::RetconOnce: - // Replace the active suspend with the should-unwind argument. - // Coerce it to i1 if necessary. + // Replace uses of the active suspend with the corresponding + // continuation-function arguments. assert(ActiveSuspend != nullptr && "no active suspend when lowering a continuation-style coroutine"); - Value *ShouldUnwind = &*std::next(NewF->arg_begin()); - if (!ShouldUnwind->getType()->isIntegerTy(1)) - ShouldUnwind = Builder.CreateIsNotNull(ShouldUnwind); - VMap[ActiveSuspend]->replaceAllUsesWith(ShouldUnwind); + replaceRetconSuspendUses(); break; } diff --git a/lib/Transforms/Coroutines/Coroutines.cpp b/lib/Transforms/Coroutines/Coroutines.cpp index 2e690a78c42..6239674cc69 100644 --- a/lib/Transforms/Coroutines/Coroutines.cpp +++ b/lib/Transforms/Coroutines/Coroutines.cpp @@ -387,6 +387,7 @@ void coro::Shape::buildFrom(Function &F) { // Determine the result value types, and make sure they match up with // the values passed to the suspends. auto ResultTys = getRetconResultTypes(); + auto ResumeTys = getRetconResumeTypes(); for (auto AnySuspend : CoroSuspends) { auto Suspend = dyn_cast(AnySuspend); @@ -396,6 +397,7 @@ void coro::Shape::buildFrom(Function &F) { "coro.suspend.retcon"); } + // Check that the argument types of the suspend match the results. auto SI = Suspend->value_begin(), SE = Suspend->value_end(); auto RI = ResultTys.begin(), RE = ResultTys.end(); for (; SI != SE && RI != RE; ++SI, ++RI) { @@ -411,6 +413,26 @@ void coro::Shape::buildFrom(Function &F) { Prototype->getFunctionType()->dump(); report_fatal_error("wrong number of arguments to coro.suspend.retcon"); } + + // Check that the result type of the suspend matches the resume types. + Type *SResultTy = Suspend->getType(); + ArrayRef SuspendResultTys = + (isa(SResultTy) + ? cast(SResultTy)->elements() + : SResultTy); // forms an ArrayRef using SResultTy, be careful + if (SuspendResultTys.size() != ResumeTys.size()) { + Suspend->dump(); + Prototype->getFunctionType()->dump(); + report_fatal_error("wrong number of results from coro.suspend.retcon"); + } + for (size_t I = 0, E = ResumeTys.size(); I != E; ++I) { + if (SuspendResultTys[I] != ResumeTys[I]) { + Suspend->dump(); + Prototype->getFunctionType()->dump(); + report_fatal_error("result from coro.suspend.retcon does not " + "match corresponding prototype function param"); + } + } } break; } @@ -501,7 +523,7 @@ static void fail(const Instruction *I, const char *Reason, Value *V) { static void checkWFRetconPrototype(const AnyCoroIdRetconInst *I, Value *V) { auto F = dyn_cast(V->stripPointerCasts()); if (!F) - fail(I, "llvm.coro.retcon.* prototype not a Function", V); + fail(I, "llvm.coro.id.retcon.* prototype not a Function", V); auto FT = F->getFunctionType(); @@ -517,23 +539,20 @@ static void checkWFRetconPrototype(const AnyCoroIdRetconInst *I, Value *V) { ResultOkay = false; } if (!ResultOkay) - fail(I, "llvm.coro.retcon prototype must return pointer as first result", - F); + fail(I, "llvm.coro.id.retcon prototype must return pointer as first " + "result", F); if (FT->getReturnType() != I->getFunction()->getFunctionType()->getReturnType()) - fail(I, "llvm.coro.retcon.* prototype return type must be same as" + fail(I, "llvm.coro.id.retcon prototype return type must be same as" "current function return type", F); } else { // No meaningful validation to do here for llvm.coro.id.unique.once. } - if (FT->getNumParams() != 2) - fail(I, "llvm.coro.retcon.* prototype must take exactly two parameters", F); - if (!FT->getParamType(0)->isPointerTy()) - fail(I, "llvm.coro.retcon.* prototype must take pointer as 1st param", F); - if (!FT->getParamType(1)->isIntegerTy()) // an i1, but not for abi purposes - fail(I, "llvm.coro.retcon.* prototype must take integer as 2nd param", F); + if (FT->getNumParams() == 0 || !FT->getParamType(0)->isPointerTy()) + fail(I, "llvm.coro.id.retcon.* prototype must take pointer as " + "its first parameter", F); } /// Check that the given value is a well-formed allocator. diff --git a/test/Transforms/Coroutines/coro-retcon-once-value.ll b/test/Transforms/Coroutines/coro-retcon-once-value.ll index d9db6efb39e..ac49b22ee6b 100644 --- a/test/Transforms/Coroutines/coro-retcon-once-value.ll +++ b/test/Transforms/Coroutines/coro-retcon-once-value.ll @@ -4,14 +4,14 @@ target triple = "x86_64-apple-macosx10.12.0" define {i8*, i32} @f(i8* %buffer, i32* %array) { entry: - %id = call token @llvm.coro.id.retcon.once(i32 8, i32 8, i8* %buffer, i8* bitcast (void (i8*, i8)* @prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) + %id = call token @llvm.coro.id.retcon.once(i32 8, i32 8, i8* %buffer, i8* bitcast (void (i8*, i1)* @prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) %hdl = call i8* @llvm.coro.begin(token %id, i8* null) %load = load i32, i32* %array %load.pos = icmp sgt i32 %load, 0 br i1 %load.pos, label %pos, label %neg pos: - %unwind0 = call i1 (...) @llvm.coro.suspend.retcon(i32 %load) + %unwind0 = call i1 (...) @llvm.coro.suspend.retcon.i1(i32 %load) br i1 %unwind0, label %cleanup, label %pos.cont pos.cont: @@ -19,7 +19,7 @@ pos.cont: br label %cleanup neg: - %unwind1 = call i1 (...) @llvm.coro.suspend.retcon(i32 0) + %unwind1 = call i1 (...) @llvm.coro.suspend.retcon.i1(i32 0) br i1 %unwind1, label %cleanup, label %neg.cont neg.cont: @@ -37,9 +37,9 @@ cleanup: ; CHECK-NEXT: store i32* %array, i32** [[T0]], align 8 ; CHECK-NEXT: %load = load i32, i32* %array, align 4 ; CHECK-NEXT: %load.pos = icmp sgt i32 %load, 0 -; CHECK-NEXT: [[CONT:%.*]] = select i1 %load.pos, void (i8*, i8)* @f.resume.0, void (i8*, i8)* @f.resume.1 +; CHECK-NEXT: [[CONT:%.*]] = select i1 %load.pos, void (i8*, i1)* @f.resume.0, void (i8*, i1)* @f.resume.1 ; CHECK-NEXT: [[VAL:%.*]] = select i1 %load.pos, i32 %load, i32 0 -; CHECK-NEXT: [[CONT_CAST:%.*]] = bitcast void (i8*, i8)* [[CONT]] to i8* +; CHECK-NEXT: [[CONT_CAST:%.*]] = bitcast void (i8*, i1)* [[CONT]] to i8* ; CHECK-NEXT: [[T0:%.*]] = insertvalue { i8*, i32 } undef, i8* [[CONT_CAST]], 0 ; CHECK-NEXT: [[T1:%.*]] = insertvalue { i8*, i32 } [[T0]], i32 [[VAL]], 1 ; CHECK-NEXT: ret { i8*, i32 } [[T1]] @@ -47,8 +47,7 @@ cleanup: ; CHECK-LABEL: define internal void @f.resume.0(i8* noalias nonnull %0, i1 zeroext %1) ; CHECK-NEXT: : -; CHECK-NEXT: [[T0:%.*]] = icmp eq i8 %1, 0 -; CHECK-NEXT: br i1 [[T0]], +; CHECK-NEXT: br i1 %1, ; CHECK: : ; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to i32** ; CHECK-NEXT: [[RELOAD:%.*]] = load i32*, i32** [[T0]], align 8 @@ -60,8 +59,7 @@ cleanup: ; CHECK-LABEL: define internal void @f.resume.1(i8* noalias nonnull %0, i1 zeroext %1) ; CHECK-NEXT: : -; CHECK-NEXT: [[T0:%.*]] = icmp eq i8 %1, 0 -; CHECK-NEXT: br i1 [[T0]], +; CHECK-NEXT: br i1 %1, ; CHECK: : ; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to i32** ; CHECK-NEXT: [[RELOAD:%.*]] = load i32*, i32** [[T0]], align 8 @@ -81,8 +79,8 @@ entry: %value = extractvalue {i8*, i32} %result, 1 call void @print(i32 %value) %cont = extractvalue {i8*, i32} %result, 0 - %cont.cast = bitcast i8* %cont to void (i8*, i8)* - call void %cont.cast(i8* %buffer, i8 zeroext 0) + %cont.cast = bitcast i8* %cont to void (i8*, i1)* + call void %cont.cast(i8* %buffer, i1 zeroext 0) ret void } @@ -95,19 +93,19 @@ entry: ; CHECK-NEXT: store i32* %array, i32** [[BUFFER]], align 8 ; CHECK-NEXT: [[LOAD:%.*]] = load i32, i32* %array, align 4 ; CHECK-NEXT: [[LOAD_POS:%.*]] = icmp sgt i32 [[LOAD]], 0 -; CHECK-NEXT: [[CONT:%.*]] = select i1 [[LOAD_POS]], void (i8*, i8)* @f.resume.0, void (i8*, i8)* @f.resume.1 +; CHECK-NEXT: [[CONT:%.*]] = select i1 [[LOAD_POS]], void (i8*, i1)* @f.resume.0, void (i8*, i1)* @f.resume.1 ; CHECK-NEXT: [[VAL:%.*]] = select i1 [[LOAD_POS]], i32 [[LOAD]], i32 0 ; CHECK-NEXT: call void @print(i32 [[VAL]]) -; CHECK-NEXT: call void [[CONT]](i8* nonnull [[BUFFER_CAST]], i8 zeroext 0) +; CHECK-NEXT: call void [[CONT]](i8* nonnull [[BUFFER_CAST]], i1 zeroext false) ; CHECK-NEXT: ret void declare token @llvm.coro.id.retcon.once(i32, i32, i8*, i8*, i8*, i8*) declare i8* @llvm.coro.begin(token, i8*) -declare i1 @llvm.coro.suspend.retcon(...) +declare i1 @llvm.coro.suspend.retcon.i1(...) declare i1 @llvm.coro.end(i8*, i1) declare i8* @llvm.coro.prepare.retcon(i8*) -declare void @prototype(i8*, i8 zeroext) +declare void @prototype(i8*, i1 zeroext) declare noalias i8* @allocate(i32 %size) declare void @deallocate(i8* %ptr) diff --git a/test/Transforms/Coroutines/coro-retcon-once-value2.ll b/test/Transforms/Coroutines/coro-retcon-once-value2.ll index 8f6360efac3..ad49f24dc54 100644 --- a/test/Transforms/Coroutines/coro-retcon-once-value2.ll +++ b/test/Transforms/Coroutines/coro-retcon-once-value2.ll @@ -5,11 +5,11 @@ target triple = "x86_64-apple-macosx10.12.0" define {i8*, i32*} @f(i8* %buffer, i32* %ptr) "coroutine.presplit"="1" { entry: %temp = alloca i32, align 4 - %id = call token @llvm.coro.id.retcon.once(i32 8, i32 8, i8* %buffer, i8* bitcast (void (i8*, i8)* @prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) + %id = call token @llvm.coro.id.retcon.once(i32 8, i32 8, i8* %buffer, i8* bitcast (void (i8*, i1)* @prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) %hdl = call i8* @llvm.coro.begin(token %id, i8* null) %oldvalue = load i32, i32* %ptr store i32 %oldvalue, i32* %temp - %unwind = call i1 (...) @llvm.coro.suspend.retcon(i32* %temp) + %unwind = call i1 (...) @llvm.coro.suspend.retcon.i1(i32* %temp) br i1 %unwind, label %cleanup, label %cont cont: @@ -33,7 +33,7 @@ cleanup: ; CHECK-NEXT: store i32* %ptr, i32** [[SPILL]] ; CHECK-NEXT: %oldvalue = load i32, i32* %ptr ; CHECK-NEXT: store i32 %oldvalue, i32* %temp -; CHECK-NEXT: [[T0:%.*]] = insertvalue { i8*, i32* } { i8* bitcast (void (i8*, i8)* @f.resume.0 to i8*), i32* undef }, i32* %temp, 1 +; CHECK-NEXT: [[T0:%.*]] = insertvalue { i8*, i32* } { i8* bitcast (void (i8*, i1)* @f.resume.0 to i8*), i32* undef }, i32* %temp, 1 ; CHECK-NEXT: ret { i8*, i32* } [[T0]] ; CHECK-NEXT: } @@ -42,9 +42,8 @@ cleanup: ; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to [[FRAME_T:%.*]]** ; CHECK-NEXT: [[FRAME:%.*]] = load [[FRAME_T]]*, [[FRAME_T]]** [[T0]] ; CHECK-NEXT: bitcast [[FRAME_T]]* [[FRAME]] to i8* -; CHECK-NEXT: [[T0:%.*]] = icmp ne i8 %1, 0 ; CHECK-NEXT: %temp = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 1 -; CHECK-NEXT: br i1 [[T0]], +; CHECK-NEXT: br i1 %1, ; CHECK: : ; CHECK-NEXT: [[TEMP_SLOT:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 1 ; CHECK-NEXT: [[PTR_SLOT:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 0 @@ -60,11 +59,10 @@ cleanup: declare token @llvm.coro.id.retcon.once(i32, i32, i8*, i8*, i8*, i8*) declare i8* @llvm.coro.begin(token, i8*) -declare i1 @llvm.coro.suspend.retcon(...) +declare i1 @llvm.coro.suspend.retcon.i1(...) declare i1 @llvm.coro.end(i8*, i1) -declare i8* @llvm.coro.prepare.retcon(i8*) -declare void @prototype(i8*, i8 zeroext) +declare void @prototype(i8*, i1 zeroext) declare noalias i8* @allocate(i32 %size) declare fastcc void @deallocate(i8* %ptr) diff --git a/test/Transforms/Coroutines/coro-retcon-resume-values.ll b/test/Transforms/Coroutines/coro-retcon-resume-values.ll new file mode 100644 index 00000000000..ac99dd15b98 --- /dev/null +++ b/test/Transforms/Coroutines/coro-retcon-resume-values.ll @@ -0,0 +1,85 @@ +; RUN: opt < %s -enable-coroutines -O2 -S | FileCheck %s + +define i8* @f(i8* %buffer, i32 %n) { +entry: + %id = call token @llvm.coro.id.retcon(i32 8, i32 4, i8* %buffer, i8* bitcast (i8* (i8*, i32, i1)* @prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) + %hdl = call i8* @llvm.coro.begin(token %id, i8* null) + br label %loop + +loop: + %n.val = phi i32 [ %n, %entry ], [ %sum, %resume ] + %values = call { i32, i1 } (...) @llvm.coro.suspend.retcon.sl_i32i1s() + %finished = extractvalue { i32, i1 } %values, 1 + br i1 %finished, label %cleanup, label %resume + +resume: + %input = extractvalue { i32, i1 } %values, 0 + %sum = add i32 %n.val, %input + br label %loop + +cleanup: + call void @print(i32 %n.val) + call i1 @llvm.coro.end(i8* %hdl, i1 0) + unreachable +} + +; CHECK-LABEL: define i8* @f(i8* %buffer, i32 %n) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %buffer to i32* +; CHECK-NEXT: store i32 %n, i32* [[T0]], align 4 +; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i32, i1)* @f.resume.0 to i8*) +; CHECK-NEXT: } + +; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull %0, i32 %1, i1 zeroext %2) +; CHECK-NEXT: : +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to i32* +; CHECK-NEXT: [[T1:%.*]] = load i32, i32* [[T0]], align 4 +; CHECK-NEXT: br i1 %2, +; CHECK: : +; CHECK-NEXT: %sum = add i32 [[T1]], %1 +; CHECK-NEXT: store i32 %sum, i32* [[T0]], align 4 +; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i32, i1)* @f.resume.0 to i8*) +; CHECK: : +; CHECK-NEXT: call void @print(i32 [[T1]]) +; CHECK-NEXT: ret i8* null +; CHECK-NEXT: } + +define i32 @main() { +entry: + %0 = alloca [8 x i8], align 4 + %buffer = bitcast [8 x i8]* %0 to i8* + %prepare = call i8* @llvm.coro.prepare.retcon(i8* bitcast (i8* (i8*, i32)* @f to i8*)) + %f = bitcast i8* %prepare to i8* (i8*, i32)* + %cont0 = call i8* %f(i8* %buffer, i32 1) + %cont0.cast = bitcast i8* %cont0 to i8* (i8*, i32, i1)* + %cont1 = call i8* %cont0.cast(i8* %buffer, i32 2, i1 zeroext false) + %cont1.cast = bitcast i8* %cont1 to i8* (i8*, i32, i1)* + %cont2 = call i8* %cont1.cast(i8* %buffer, i32 4, i1 zeroext false) + %cont2.cast = bitcast i8* %cont2 to i8* (i8*, i32, i1)* + call i8* %cont2.cast(i8* %buffer, i32 100, i1 zeroext true) + ret i32 0 +} + +; Unfortunately, we don't seem to fully optimize this right now due +; to some sort of phase-ordering thing. +; CHECK-LABEL: define i32 @main +; CHECK-NEXT: entry: +; CHECK: [[BUFFER:%.*]] = alloca [8 x i8], align 4 +; CHECK: [[SLOT:%.*]] = bitcast [8 x i8]* [[BUFFER]] to i32* +; CHECK-NEXT: store i32 7, i32* [[SLOT]], align 4 +; CHECK-NEXT: call void @print(i32 7) +; CHECK-NEXT: ret i32 0 + +declare token @llvm.coro.id.retcon(i32, i32, i8*, i8*, i8*, i8*) +declare i8* @llvm.coro.begin(token, i8*) +declare { i32, i1 } @llvm.coro.suspend.retcon.sl_i32i1s(...) +declare i1 @llvm.coro.end(i8*, i1) +declare i8* @llvm.coro.prepare.retcon(i8*) + +declare i8* @prototype(i8*, i32, i1 zeroext) + +declare noalias i8* @allocate(i32 %size) +declare void @deallocate(i8* %ptr) + +declare void @print(i32) + diff --git a/test/Transforms/Coroutines/coro-retcon-resume-values2.ll b/test/Transforms/Coroutines/coro-retcon-resume-values2.ll new file mode 100644 index 00000000000..43f98e958aa --- /dev/null +++ b/test/Transforms/Coroutines/coro-retcon-resume-values2.ll @@ -0,0 +1,100 @@ +; RUN: opt < %s -coro-split -coro-cleanup -S | FileCheck %s + +define i8* @f(i8* %buffer, i32 %n) "coroutine.presplit"="1" { +entry: + %id = call token @llvm.coro.id.retcon(i32 8, i32 4, i8* %buffer, i8* bitcast (i8* (i8*, i32)* @prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) + %hdl = call i8* @llvm.coro.begin(token %id, i8* null) + %value0 = call i32 (...) @llvm.coro.suspend.retcon.i32() + %sum0 = call i32 @add(i32 %n, i32 %value0) + %value1 = call i32 (...) @llvm.coro.suspend.retcon.i32() + %sum1 = call i32 @add(i32 %sum0, i32 %value0) + %sum2 = call i32 @add(i32 %sum1, i32 %value1) + %value2 = call i32 (...) @llvm.coro.suspend.retcon.i32() + %sum3 = call i32 @add(i32 %sum2, i32 %value0) + %sum4 = call i32 @add(i32 %sum3, i32 %value1) + %sum5 = call i32 @add(i32 %sum4, i32 %value2) + call void @print(i32 %sum5) + call i1 @llvm.coro.end(i8* %hdl, i1 0) + unreachable +} + +; CHECK-LABEL: define i8* @f(i8* %buffer, i32 %n) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ALLOC:%.*]] = call i8* @allocate(i32 20) +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %buffer to i8** +; CHECK-NEXT: store i8* [[ALLOC]], i8** [[T0]] +; CHECK-NEXT: [[FRAME:%.*]] = bitcast i8* [[ALLOC]] to [[FRAME_T:%.*]]* +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 0 +; CHECK-NEXT: store i32 %n, i32* [[T0]] +; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i32)* @f.resume.0 to i8*) +; CHECK-NEXT: } + +; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull %0, i32 %1) +; CHECK-NEXT: : +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to [[FRAME_T:%.*]]** +; CHECK-NEXT: [[FRAME:%.*]] = load [[FRAME_T]]*, [[FRAME_T]]** [[T0]] +; CHECK-NEXT: [[VFRAME:%.*]] = bitcast [[FRAME_T]]* [[FRAME]] to i8* +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 1 +; CHECK-NEXT: store i32 %1, i32* [[T0]] +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 0 +; CHECK-NEXT: [[N:%.*]] = load i32, i32* [[T0]] +; CHECK-NEXT: %sum0 = call i32 @add(i32 [[N]], i32 %1) +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 2 +; CHECK-NEXT: store i32 %sum0, i32* [[T0]] +; CHECK-NEXT: [[CONT:%.*]] = bitcast i8* (i8*, i32)* @f.resume.1 to i8* +; CHECK-NEXT: ret i8* [[CONT]] +; CHECK-NEXT: } + +; CHECK-LABEL: define internal i8* @f.resume.1(i8* noalias nonnull %0, i32 %1) +; CHECK-NEXT: : +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to [[FRAME_T:%.*]]** +; CHECK-NEXT: [[FRAME:%.*]] = load [[FRAME_T]]*, [[FRAME_T]]** [[T0]] +; CHECK-NEXT: [[VFRAME:%.*]] = bitcast [[FRAME_T]]* [[FRAME]] to i8* +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 3 +; CHECK-NEXT: store i32 %1, i32* [[T0]] +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 2 +; CHECK-NEXT: [[SUM0:%.*]] = load i32, i32* [[T0]] +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 1 +; CHECK-NEXT: [[VALUE0:%.*]] = load i32, i32* [[T0]] +; CHECK-NEXT: %sum1 = call i32 @add(i32 [[SUM0]], i32 [[VALUE0]]) +; CHECK-NEXT: %sum2 = call i32 @add(i32 %sum1, i32 %1) +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 4 +; CHECK-NEXT: store i32 %sum2, i32* [[T0]] +; CHECK-NEXT: [[CONT:%.*]] = bitcast i8* (i8*, i32)* @f.resume.2 to i8* +; CHECK-NEXT: ret i8* [[CONT]] +; CHECK-NEXT: } + +; CHECK-LABEL: define internal i8* @f.resume.2(i8* noalias nonnull %0, i32 %1) +; CHECK-NEXT: : +; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to [[FRAME_T:%.*]]** +; CHECK-NEXT: [[FRAME:%.*]] = load [[FRAME_T]]*, [[FRAME_T]]** [[T0]] +; CHECK-NEXT: [[VFRAME:%.*]] = bitcast [[FRAME_T]]* [[FRAME]] to i8* +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 4 +; CHECK-NEXT: [[SUM2:%.*]] = load i32, i32* [[T0]] +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 3 +; CHECK-NEXT: [[VALUE1:%.*]] = load i32, i32* [[T0]] +; CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[FRAME_T]], [[FRAME_T]]* [[FRAME]], i32 0, i32 1 +; CHECK-NEXT: [[VALUE0:%.*]] = load i32, i32* [[T0]] +; CHECK-NEXT: %sum3 = call i32 @add(i32 [[SUM2]], i32 [[VALUE0]]) +; CHECK-NEXT: %sum4 = call i32 @add(i32 %sum3, i32 [[VALUE1]]) +; CHECK-NEXT: %sum5 = call i32 @add(i32 %sum4, i32 %1) +; CHECK-NEXT: call void @print(i32 %sum5) +; CHECK-NEXT: [[CONT:%.*]] = bitcast [[FRAME_T]]* [[FRAME]] to i8* +; CHECK-NEXT: call void @deallocate(i8* [[CONT]]) +; CHECK-NEXT: ret i8* null +; CHECK-NEXT: } + +declare token @llvm.coro.id.retcon(i32, i32, i8*, i8*, i8*, i8*) +declare i8* @llvm.coro.begin(token, i8*) +declare i32 @llvm.coro.suspend.retcon.i32(...) +declare i1 @llvm.coro.end(i8*, i1) +declare i8* @llvm.coro.prepare.retcon(i8*) + +declare i8* @prototype(i8*, i32) + +declare noalias i8* @allocate(i32 %size) +declare void @deallocate(i8* %ptr) + +declare i32 @add(i32, i32) +declare void @print(i32) + diff --git a/test/Transforms/Coroutines/coro-retcon-value.ll b/test/Transforms/Coroutines/coro-retcon-value.ll index 39a414dd4d0..cfda73bbe75 100644 --- a/test/Transforms/Coroutines/coro-retcon-value.ll +++ b/test/Transforms/Coroutines/coro-retcon-value.ll @@ -9,7 +9,8 @@ entry: loop: %n.val = phi i32 [ %n, %entry ], [ %inc, %resume ] - %unwind0 = call i1 (...) @llvm.coro.suspend.retcon(i32 %n.val) + %unwind = call i8 (...) @llvm.coro.suspend.retcon.i8(i32 %n.val) + %unwind0 = icmp ne i8 %unwind, 0 br i1 %unwind0, label %cleanup, label %resume resume: @@ -89,7 +90,7 @@ entry: declare token @llvm.coro.id.retcon(i32, i32, i8*, i8*, i8*, i8*) declare i8* @llvm.coro.begin(token, i8*) -declare i1 @llvm.coro.suspend.retcon(...) +declare i8 @llvm.coro.suspend.retcon.i8(...) declare i1 @llvm.coro.end(i8*, i1) declare i8* @llvm.coro.prepare.retcon(i8*) diff --git a/test/Transforms/Coroutines/coro-retcon.ll b/test/Transforms/Coroutines/coro-retcon.ll index 1c35732ee10..298ce579578 100644 --- a/test/Transforms/Coroutines/coro-retcon.ll +++ b/test/Transforms/Coroutines/coro-retcon.ll @@ -3,14 +3,14 @@ define i8* @f(i8* %buffer, i32 %n) { entry: - %id = call token @llvm.coro.id.retcon(i32 8, i32 4, i8* %buffer, i8* bitcast (i8* (i8*, i8)* @prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) + %id = call token @llvm.coro.id.retcon(i32 8, i32 4, i8* %buffer, i8* bitcast (i8* (i8*, i1)* @prototype to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*)) %hdl = call i8* @llvm.coro.begin(token %id, i8* null) br label %loop loop: %n.val = phi i32 [ %n, %entry ], [ %inc, %resume ] call void @print(i32 %n.val) - %unwind0 = call i1 (...) @llvm.coro.suspend.retcon() + %unwind0 = call i1 (...) @llvm.coro.suspend.retcon.i1() br i1 %unwind0, label %cleanup, label %resume resume: @@ -27,20 +27,19 @@ cleanup: ; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %buffer to i32* ; CHECK-NEXT: store i32 %n, i32* [[T0]], align 4 ; CHECK-NEXT: call void @print(i32 %n) -; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i8)* @f.resume.0 to i8*) +; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i1)* @f.resume.0 to i8*) ; CHECK-NEXT: } ; CHECK-LABEL: define internal i8* @f.resume.0(i8* noalias nonnull %0, i1 zeroext %1) ; CHECK-NEXT: : -; CHECK-NEXT: [[T0:%.*]] = icmp eq i8 %1, 0 -; CHECK-NEXT: br i1 [[T0]], +; CHECK-NEXT: br i1 %1, ; CHECK: : ; CHECK-NEXT: [[T0:%.*]] = bitcast i8* %0 to i32* ; CHECK-NEXT: [[T1:%.*]] = load i32, i32* [[T0]], align 4 ; CHECK-NEXT: %inc = add i32 [[T1]], 1 ; CHECK-NEXT: store i32 %inc, i32* [[T0]], align 4 ; CHECK-NEXT: call void @print(i32 %inc) -; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i8)* @f.resume.0 to i8*) +; CHECK-NEXT: ret i8* bitcast (i8* (i8*, i1)* @f.resume.0 to i8*) ; CHECK: : ; CHECK-NEXT: ret i8* null ; CHECK-NEXT: } @@ -52,12 +51,12 @@ entry: %prepare = call i8* @llvm.coro.prepare.retcon(i8* bitcast (i8* (i8*, i32)* @f to i8*)) %f = bitcast i8* %prepare to i8* (i8*, i32)* %cont0 = call i8* %f(i8* %buffer, i32 4) - %cont0.cast = bitcast i8* %cont0 to i8* (i8*, i8)* - %cont1 = call i8* %cont0.cast(i8* %buffer, i8 zeroext 0) - %cont1.cast = bitcast i8* %cont1 to i8* (i8*, i8)* - %cont2 = call i8* %cont1.cast(i8* %buffer, i8 zeroext 0) - %cont2.cast = bitcast i8* %cont2 to i8* (i8*, i8)* - call i8* %cont2.cast(i8* %buffer, i8 zeroext 1) + %cont0.cast = bitcast i8* %cont0 to i8* (i8*, i1)* + %cont1 = call i8* %cont0.cast(i8* %buffer, i1 zeroext false) + %cont1.cast = bitcast i8* %cont1 to i8* (i8*, i1)* + %cont2 = call i8* %cont1.cast(i8* %buffer, i1 zeroext false) + %cont2.cast = bitcast i8* %cont2 to i8* (i8*, i1)* + call i8* %cont2.cast(i8* %buffer, i1 zeroext true) ret i32 0 } @@ -81,11 +80,11 @@ entry: declare token @llvm.coro.id.retcon(i32, i32, i8*, i8*, i8*, i8*) declare i8* @llvm.coro.begin(token, i8*) -declare i1 @llvm.coro.suspend.retcon(...) +declare i1 @llvm.coro.suspend.retcon.i1(...) declare i1 @llvm.coro.end(i8*, i1) declare i8* @llvm.coro.prepare.retcon(i8*) -declare i8* @prototype(i8*, i8 zeroext) +declare i8* @prototype(i8*, i1 zeroext) declare noalias i8* @allocate(i32 %size) declare void @deallocate(i8* %ptr)