mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-24 03:33:20 +01:00
Generalize llvm.coro.suspend.retcon to allow an arbitrary number of arguments to be passed back to the continuation function.
llvm-svn: 368789
This commit is contained in:
parent
3f8b48091d
commit
5ad355756d
@ -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]>;
|
||||
|
||||
|
@ -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<StructType>(FTy->getReturnType())) {
|
||||
return STy->elements().slice(1);
|
||||
} else {
|
||||
@ -179,6 +179,15 @@ struct LLVM_LIBRARY_VISIBILITY Shape {
|
||||
}
|
||||
}
|
||||
|
||||
ArrayRef<Type*> 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:
|
||||
|
@ -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<Value*, 8> 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<StructType>(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<ExtractValueInst>((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;
|
||||
}
|
||||
|
||||
|
@ -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<CoroSuspendRetconInst>(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<Type*> SuspendResultTys =
|
||||
(isa<StructType>(SResultTy)
|
||||
? cast<StructType>(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<Function>(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.
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
85
test/Transforms/Coroutines/coro-retcon-resume-values.ll
Normal file
85
test/Transforms/Coroutines/coro-retcon-resume-values.ll
Normal file
@ -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)
|
||||
|
100
test/Transforms/Coroutines/coro-retcon-resume-values2.ll
Normal file
100
test/Transforms/Coroutines/coro-retcon-resume-values2.ll
Normal file
@ -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)
|
||||
|
@ -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*)
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user