Index: llvm/lib/Transforms/Coroutines/CoroFrame.cpp =================================================================== --- llvm/lib/Transforms/Coroutines/CoroFrame.cpp +++ llvm/lib/Transforms/Coroutines/CoroFrame.cpp @@ -2333,14 +2333,16 @@ SuspendCrossingInfo &Checker) { DominatorTree DT(F); - // Collect all possible basic blocks which may dominate all uses of allocas. + // Collect all possible basic blocks which may dominate all uses of allocas + // except the entry block. SmallPtrSet DomSet; - DomSet.insert(&F.getEntryBlock()); for (auto *CSI : Shape.CoroSuspends) { BasicBlock *SuspendBlock = CSI->getParent(); assert(isSuspendBlock(SuspendBlock) && SuspendBlock->getSingleSuccessor() && "should have split coro.suspend into its own block"); - DomSet.insert(SuspendBlock->getSingleSuccessor()); + BasicBlock *SwitchBlock = SuspendBlock->getSingleSuccessor(); + for (auto *BB : llvm::successors(SwitchBlock)) + DomSet.insert(BB); } for (Instruction &I : instructions(F)) { @@ -2391,18 +2393,17 @@ // Sink lifetime.start markers to dominate block when they are // only used outside the region. if (Valid && Lifetimes.size() != 0) { - // May be AI itself, when the type of AI is i8* - auto *NewBitCast = [&](AllocaInst *AI) -> Value* { - if (isa(Lifetimes[0]->getOperand(1))) - return AI; - auto *Int8PtrTy = Type::getInt8PtrTy(F.getContext()); - return CastInst::Create(Instruction::BitCast, AI, Int8PtrTy, "", - DomBB->getTerminator()); - }(AI); - auto *NewLifetime = Lifetimes[0]->clone(); - NewLifetime->replaceUsesOfWith(NewLifetime->getOperand(1), NewBitCast); - NewLifetime->insertBefore(DomBB->getTerminator()); + if (isa(NewLifetime->getOperand(1))) + NewLifetime->insertBefore(DomBB->getFirstNonPHI()); + else { + auto *NewBitCast = CastInst::Create( + Instruction::BitCast, AI, Type::getInt8PtrTy(F.getContext()), "", + DomBB->getFirstNonPHI()); + NewLifetime->replaceUsesOfWith(NewLifetime->getOperand(1), + NewBitCast); + NewLifetime->insertAfter(NewBitCast); + } // All the outsided lifetime.start markers are no longer necessary. for (Instruction *S : Lifetimes) Index: llvm/test/Transforms/Coroutines/coro-split-musttail4.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/Coroutines/coro-split-musttail4.ll @@ -0,0 +1,68 @@ +; Tests that sink lifetime doesn't affect musttail. +; RUN: opt < %s -coro-split -S | FileCheck %s +; RUN: opt < %s -passes=coro-split -S | FileCheck %s + +declare void @fakeresume1(i8*) + +declare void @fakeresume2(i64* align 8) + +define void @g() #0 { +entry: + %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) + %alloc = call i8* @malloc(i64 16) #3 + %alloc.var = alloca i8 + call void @llvm.lifetime.start.p0i8(i64 1, i8* %alloc.var) + %vFrame = call noalias nonnull i8* @llvm.coro.begin(token %id, i8* %alloc) + + %save = call token @llvm.coro.save(i8* null) + %suspend = call i8 @llvm.coro.suspend(token %save, i1 false) + call fastcc void @fakeresume1(i8* null) + switch i8 %suspend, label %exit [ + i8 0, label %await.suspend + i8 1, label %exit + ] +await.suspend: + %save2 = call token @llvm.coro.save(i8* null) + call fastcc void @fakeresume2(i64* align 8 null) + %suspend2 = call i8 @llvm.coro.suspend(token %save2, i1 false) + switch i8 %suspend2, label %exit [ + i8 0, label %await.ready + i8 1, label %exit + ] +await.ready: + call void @consume(i8* %alloc.var) + call void @llvm.lifetime.end.p0i8(i64 1, i8* %alloc.var) + br label %exit +exit: + call i1 @llvm.coro.end(i8* null, i1 false) + ret void +} + +; Verify that in the initial function resume is not marked with musttail. +; CHECK-LABEL: @g( +; CHECK-NOT: musttail call fastcc void @fakeresume1(i8* null) + +; Verify that in the resume part resume call is marked with musttail. +; CHECK-LABEL: @g.resume( +; CHECK: musttail call fastcc void @fakeresume2(i64* align 8 null) +; CHECK-NEXT: ret void + +declare token @llvm.coro.id(i32, i8* readnone, i8* nocapture readonly, i8*) #1 +declare i1 @llvm.coro.alloc(token) #2 +declare i64 @llvm.coro.size.i64() #3 +declare i8* @llvm.coro.begin(token, i8* writeonly) #2 +declare token @llvm.coro.save(i8*) #2 +declare i8* @llvm.coro.frame() #3 +declare i8 @llvm.coro.suspend(token, i1) #2 +declare i8* @llvm.coro.free(token, i8* nocapture readonly) #1 +declare i1 @llvm.coro.end(i8*, i1) #2 +declare i8* @llvm.coro.subfn.addr(i8* nocapture readonly, i8) #1 +declare i8* @malloc(i64) +declare void @consume(i8*) +declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) +declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) + +attributes #0 = { "coroutine.presplit"="1" } +attributes #1 = { argmemonly nounwind readonly } +attributes #2 = { nounwind } +attributes #3 = { nounwind readnone }