https://github.com/yuxuanchen1997 updated https://github.com/llvm/llvm-project/pull/81195
>From fe55a632883d801a4688d787323be243d031df5b Mon Sep 17 00:00:00 2001 From: Yuxuan Chen <y...@meta.com> Date: Wed, 7 Feb 2024 16:05:42 -0800 Subject: [PATCH] Skip moving parameters if the allocation decision is false --- clang/lib/CodeGen/CGCoroutine.cpp | 120 ++++++++++++++----- clang/test/CodeGenCoroutines/coro-gro.cpp | 6 +- clang/test/CodeGenCoroutines/coro-params.cpp | 48 +++++--- 3 files changed, 130 insertions(+), 44 deletions(-) diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp index 888d30bfb3e1d6..b2933650367c12 100644 --- a/clang/lib/CodeGen/CGCoroutine.cpp +++ b/clang/lib/CodeGen/CGCoroutine.cpp @@ -389,25 +389,12 @@ namespace { ParamReferenceReplacerRAII(CodeGenFunction::DeclMapTy &LocalDeclMap) : LocalDeclMap(LocalDeclMap) {} - void addCopy(DeclStmt const *PM) { - // Figure out what param it refers to. - - assert(PM->isSingleDecl()); - VarDecl const*VD = static_cast<VarDecl const*>(PM->getSingleDecl()); - Expr const *InitExpr = VD->getInit(); - GetParamRef Visitor; - Visitor.Visit(const_cast<Expr*>(InitExpr)); - assert(Visitor.Expr); - DeclRefExpr *DREOrig = Visitor.Expr; - auto *PD = DREOrig->getDecl(); - - auto it = LocalDeclMap.find(PD); - assert(it != LocalDeclMap.end() && "parameter is not found"); - SavedLocals.insert({ PD, it->second }); - - auto copyIt = LocalDeclMap.find(VD); - assert(copyIt != LocalDeclMap.end() && "parameter copy is not found"); - it->second = copyIt->getSecond(); + void substAddress(ValueDecl *D, Address Addr) { + auto it = LocalDeclMap.find(D); + assert(it != LocalDeclMap.end() && "original decl is not found"); + SavedLocals.insert({D, it->second}); + + it->second = Addr; } ~ParamReferenceReplacerRAII() { @@ -629,6 +616,63 @@ struct GetReturnObjectManager { Builder.CreateStore(Builder.getTrue(), GroActiveFlag); } }; + +static ValueDecl *getOriginalParamDeclForParamMove(VarDecl const *VD) { + Expr const *InitExpr = VD->getInit(); + GetParamRef Visitor; + Visitor.Visit(const_cast<Expr *>(InitExpr)); + assert(Visitor.Expr); + return Visitor.Expr->getDecl(); +} + +struct ParamMoveManager { + ParamMoveManager(CodeGenFunction &CGF, + llvm::ArrayRef<const Stmt *> ParamMoves) + : CGF(CGF), ParamMovesVarDecls() { + ParamMovesVarDecls.reserve(ParamMoves.size()); + for (auto *S : ParamMoves) { + auto *PMStmt = cast<DeclStmt>(S); + assert(PMStmt->isSingleDecl()); + auto *ParamMoveVD = static_cast<VarDecl const *>(PMStmt->getSingleDecl()); + ParamMovesVarDecls.push_back(ParamMoveVD); + } + } + + // Because we wrap param moves in the coro.alloc block. It's not always + // necessary to run the corresponding cleanups in the branches. + // We would need to know when to (conditionally) clean them up. + void EmitMovesWithCleanup(Address PMCleanupActiveFlag) { + // Create parameter copies. We do it before creating a promise, since an + // evolution of coroutine TS may allow promise constructor to observe + // parameter copies. + for (auto *VD : ParamMovesVarDecls) { + auto Emission = CGF.EmitAutoVarAlloca(*VD); + CGF.EmitAutoVarInit(Emission); + auto OldTop = CGF.EHStack.stable_begin(); + CGF.EmitAutoVarCleanups(Emission); + auto Top = CGF.EHStack.stable_begin(); + + for (auto I = CGF.EHStack.find(Top), E = CGF.EHStack.find(OldTop); I != E; + I++) { + if (auto *Cleanup = dyn_cast<EHCleanupScope>(&*I)) { + assert(!Cleanup->hasActiveFlag() && + "cleanup already has active flag?"); + Cleanup->setActiveFlag(PMCleanupActiveFlag); + Cleanup->setTestFlagInEHCleanup(); + Cleanup->setTestFlagInNormalCleanup(); + } + } + } + } + + llvm::ArrayRef<const VarDecl *> GetParamMovesVarDecls() { + return ParamMovesVarDecls; + } + +private: + CodeGenFunction &CGF; + SmallVector<const VarDecl *> ParamMovesVarDecls; +}; } // namespace static void emitBodyAndFallthrough(CodeGenFunction &CGF, @@ -648,6 +692,8 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) { auto *EntryBB = Builder.GetInsertBlock(); auto *AllocBB = createBasicBlock("coro.alloc"); auto *InitBB = createBasicBlock("coro.init"); + auto *ParamMoveBB = createBasicBlock("coro.param.move"); + auto *AfterParamMoveBB = createBasicBlock("coro.after.param.move"); auto *FinalBB = createBasicBlock("coro.final"); auto *RetBB = createBasicBlock("coro.ret"); @@ -664,6 +710,9 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) { auto *CoroAlloc = Builder.CreateCall( CGM.getIntrinsic(llvm::Intrinsic::coro_alloc), {CoroId}); + auto PMCleanupActiveFlag = CreateTempAlloca( + Builder.getInt1Ty(), CharUnits::One(), "param.move.cleanup.active"); + Builder.CreateStore(CoroAlloc, PMCleanupActiveFlag); Builder.CreateCondBr(CoroAlloc, AllocBB, InitBB); EmitBlock(AllocBB); @@ -695,6 +744,7 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) { auto *Phi = Builder.CreatePHI(VoidPtrTy, 2); Phi->addIncoming(NullPtr, EntryBB); Phi->addIncoming(AllocateCall, AllocOrInvokeContBB); + auto *CoroBegin = Builder.CreateCall( CGM.getIntrinsic(llvm::Intrinsic::coro_begin), {CoroId, Phi}); CurCoro.Data->CoroBegin = CoroBegin; @@ -719,15 +769,29 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) { DI->getCoroutineParameterMappings().insert( {std::get<0>(Pair), std::get<1>(Pair)}); - // Create parameter copies. We do it before creating a promise, since an - // evolution of coroutine TS may allow promise constructor to observe - // parameter copies. - for (auto *PM : S.getParamMoves()) { - EmitStmt(PM); - ParamReplacer.addCopy(cast<DeclStmt>(PM)); - // TODO: if(CoroParam(...)) need to surround ctor and dtor - // for the copy, so that llvm can elide it if the copy is - // not needed. + Builder.CreateCondBr(CoroAlloc, ParamMoveBB, AfterParamMoveBB); + + EmitBlock(ParamMoveBB); + + ParamMoveManager Mover{*this, ParamMoves}; + Mover.EmitMovesWithCleanup(PMCleanupActiveFlag); + + Builder.CreateBr(AfterParamMoveBB); + + EmitBlock(AfterParamMoveBB); + + for (auto *VD : Mover.GetParamMovesVarDecls()) { + auto NewAddr = LocalDeclMap.find(VD)->getSecond(); + auto *OrigVD = getOriginalParamDeclForParamMove(VD); + auto OldAddr = LocalDeclMap.find(OrigVD)->getSecond(); + + auto *ParamPhi = Builder.CreatePHI(VoidPtrTy, 2); + ParamPhi->addIncoming(NewAddr.getPointer(), ParamMoveBB); + ParamPhi->addIncoming(OldAddr.getPointer(), InitBB); + + ParamReplacer.substAddress( + OrigVD, + Address(ParamPhi, OldAddr.getElementType(), OldAddr.getAlignment())); } EmitStmt(S.getPromiseDeclStmt()); diff --git a/clang/test/CodeGenCoroutines/coro-gro.cpp b/clang/test/CodeGenCoroutines/coro-gro.cpp index d4c3ff589e340a..fb42c7e089b363 100644 --- a/clang/test/CodeGenCoroutines/coro-gro.cpp +++ b/clang/test/CodeGenCoroutines/coro-gro.cpp @@ -29,8 +29,11 @@ void doSomething() noexcept; // CHECK: define{{.*}} i32 @_Z1fv( int f() { // CHECK: %[[RetVal:.+]] = alloca i32 + // CHECK: %[[ParamMoveCleanupActive:.+]] = alloca i1 // CHECK: %[[GroActive:.+]] = alloca i1 // CHECK: %[[CoroGro:.+]] = alloca %struct.GroType, {{.*}} !coro.outside.frame ![[OutFrameMetadata:.+]] + // CHECK: %[[CoroAlloc:.+]] = call i1 @llvm.coro.alloc + // CHECK-NEXT: store i1 %[[CoroAlloc:.+]], ptr %[[ParamMoveCleanupActive:.+]] // CHECK: %[[Size:.+]] = call i64 @llvm.coro.size.i64() // CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef %[[Size]]) @@ -94,6 +97,7 @@ class invoker { // CHECK: define{{.*}} void @_Z1gv({{.*}} %[[AggRes:.+]]) invoker g() { // CHECK: %[[ResultPtr:.+]] = alloca ptr + // CHECK-NEXT: %[[ParamMoveCleanupActive:.+]] = alloca i1 // CHECK-NEXT: %[[Promise:.+]] = alloca %"class.invoker::invoker_promise" // CHECK: store ptr %[[AggRes]], ptr %[[ResultPtr]] @@ -105,4 +109,4 @@ invoker g() { // CHECK: call void @_ZN7invoker15invoker_promise17get_return_objectEv({{.*}} %[[AggRes]] co_return; } -// CHECK: ![[OutFrameMetadata]] = !{} \ No newline at end of file +// CHECK: ![[OutFrameMetadata]] = !{} diff --git a/clang/test/CodeGenCoroutines/coro-params.cpp b/clang/test/CodeGenCoroutines/coro-params.cpp index c5a61a53cb46ed..218e81ec73a7ab 100644 --- a/clang/test/CodeGenCoroutines/coro-params.cpp +++ b/clang/test/CodeGenCoroutines/coro-params.cpp @@ -64,22 +64,36 @@ void consume(int,int,int) noexcept; // TODO: Add support for CopyOnly params // CHECK: define{{.*}} void @_Z1fi8MoveOnly11MoveAndCopy(i32 noundef %val, ptr noundef %[[MoParam:.+]], ptr noundef %[[McParam:.+]]) #0 personality ptr @__gxx_personality_v0 void f(int val, MoveOnly moParam, MoveAndCopy mcParam) { + // CHECK: %[[ValCopy:.+]] = alloca i32, // CHECK: %[[MoCopy:.+]] = alloca %struct.MoveOnly, // CHECK: %[[McCopy:.+]] = alloca %struct.MoveAndCopy, // CHECK: store i32 %val, ptr %[[ValAddr:.+]] + // CHECK: %[[AllocFlag:.+]] = call i1 @llvm.coro.alloc + // CHECK-NEXT: store i1 %[[AllocFlag:.+]] %[[CleanupActiveMem:.+]] // CHECK: call ptr @llvm.coro.begin( - // CHECK: call void @_ZN8MoveOnlyC1EOS_(ptr {{[^,]*}} %[[MoCopy]], ptr noundef nonnull align 4 dereferenceable(4) %[[MoParam]]) - // CHECK-NEXT: call void @llvm.lifetime.start.p0( - // CHECK-NEXT: call void @_ZN11MoveAndCopyC1EOS_(ptr {{[^,]*}} %[[McCopy]], ptr noundef nonnull align 4 dereferenceable(4) %[[McParam]]) # - // CHECK-NEXT: call void @llvm.lifetime.start.p0( - // CHECK-NEXT: invoke void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeC1Ev( - + // CHECK: br i1 %[[CoroAlloc:.+]], label %coro.param.move, label %coro.after.param.move + // CHECK: coro.param.move: + // CHECK: call void @llvm.lifetime.start.p0(i64 4, ptr %[[ValCopy:.+]]) + // CHECK-NEXT: %[[Temp:.+]] = load i32, ptr %val.addr, align 4 + // CHECK-NEXT: store i32 %[[Temp:.+]], ptr %val1, align 4 + // CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr %[[MoCopy:.+]]) #2 + // CHECK-NEXT: call void @_ZN8MoveOnlyC1EOS_(ptr noundef nonnull align 4 dereferenceable(4) %[[MoCopy:.+]], ptr noundef nonnull align 4 dereferenceable(4) %[[MoParam:.+]]) #2 + // CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr %[[McCopy:.+]]) #2 + // CHECK-NEXT: call void @_ZN11MoveAndCopyC1EOS_(ptr noundef nonnull align 4 dereferenceable(4) %[[McCopy:.+]], ptr noundef nonnull align 4 dereferenceable(4) %[[McParam:.+]]) #2 + // CHECK-NEXT: br label %coro.after.param.move + // + // CHECK: %[[ValPhi:.+]] = phi ptr [ %[[ValCopy:.+]], %coro.param.move ], [ %[[ValAddr:.+]], %coro.init ] + // CHECK-NEXT: %[[MoPhi:.+]] = phi ptr [ %[[MoCopy:.+]], %coro.param.move ], [ %[[MoParam:.+]], %coro.init ] + // CHECK-NEXT: %[[McPhi:.+]] = phi ptr [ %[[McCopy:.+]], %coro.param.move ], [ %[[McParam:.+]], %coro.init ] + // CHECK: invoke void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeC1Ev( + + // CHECK: init.ready // CHECK: call void @_ZN14suspend_always12await_resumeEv( // CHECK: %[[IntParam:.+]] = load i32, ptr %{{.*}} - // CHECK: %[[MoGep:.+]] = getelementptr inbounds %struct.MoveOnly, ptr %[[MoCopy]], i32 0, i32 0 + // CHECK: %[[MoGep:.+]] = getelementptr inbounds %struct.MoveOnly, ptr %[[MoPhi]], i32 0, i32 0 // CHECK: %[[MoVal:.+]] = load i32, ptr %[[MoGep]] - // CHECK: %[[McGep:.+]] = getelementptr inbounds %struct.MoveAndCopy, ptr %[[McCopy]], i32 0, i32 0 + // CHECK: %[[McGep:.+]] = getelementptr inbounds %struct.MoveAndCopy, ptr %[[McPhi]], i32 0, i32 0 // CHECK: %[[McVal:.+]] = load i32, ptr %[[McGep]] // CHECK: call void @_Z7consumeiii(i32 noundef %[[IntParam]], i32 noundef %[[MoVal]], i32 noundef %[[McVal]]) @@ -90,13 +104,17 @@ void f(int val, MoveOnly moParam, MoveAndCopy mcParam) { // CHECK: call void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_type13final_suspendEv( // CHECK: call void @_ZN14suspend_always12await_resumeEv( - // Destroy promise, then parameter copies: + // Destroy promise, then test the cleanup flag for parameter copies (if exist): // CHECK: call void @_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeD1Ev(ptr {{[^,]*}} %__promise) // CHECK-NEXT: call void @llvm.lifetime.end.p0( - // CHECK-NEXT: call void @_ZN11MoveAndCopyD1Ev(ptr {{[^,]*}} %[[McCopy]]) - // CHECK-NEXT: call void @llvm.lifetime.end.p0( - // CHECK-NEXT: call void @_ZN8MoveOnlyD1Ev(ptr {{[^,]*}} %[[MoCopy]] - // CHECK-NEXT: call void @llvm.lifetime.end.p0( + // CHECK-NEXT: %[[TempCleanupActive:.+]] = load i1, ptr %[[CleanupActiveMem:.+]] + // CHECK-NEXT: br i1 %[[TempCleanupActive]] + + // The next two may be in different cleanup blocks: + // CHECK: call void @_ZN11MoveAndCopyD1Ev(ptr {{[^,]*}} %[[McCopy]]) + // CHECK: call void @_ZN8MoveOnlyD1Ev(ptr {{[^,]*}} %[[MoCopy]] + + // CHECK: call void @llvm.lifetime.end.p0( // CHECK-NEXT: call void @llvm.lifetime.end.p0( // CHECK-NEXT: call ptr @llvm.coro.free( } @@ -109,13 +127,13 @@ void dependent_params(T x, U, U y) { // CHECK-NEXT: %[[y_copy:.+]] = alloca %struct.B // CHECK: call ptr @llvm.coro.begin - // CHECK-NEXT: call void @llvm.lifetime.start.p0( + // CHECK: call void @llvm.lifetime.start.p0( // CHECK-NEXT: call void @_ZN1AC1EOS_(ptr {{[^,]*}} %[[x_copy]], ptr noundef nonnull align 4 dereferenceable(512) %x) // CHECK-NEXT: call void @llvm.lifetime.start.p0( // CHECK-NEXT: call void @_ZN1BC1EOS_(ptr {{[^,]*}} %[[unnamed_copy]], ptr noundef nonnull align 4 dereferenceable(512) %0) // CHECK-NEXT: call void @llvm.lifetime.start.p0( // CHECK-NEXT: call void @_ZN1BC1EOS_(ptr {{[^,]*}} %[[y_copy]], ptr noundef nonnull align 4 dereferenceable(512) %y) - // CHECK-NEXT: call void @llvm.lifetime.start.p0( + // CHECK: call void @llvm.lifetime.start.p0( // CHECK-NEXT: invoke void @_ZNSt16coroutine_traitsIJv1A1BS1_EE12promise_typeC1Ev( co_return; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits