[clang] [Win32][Coroutine] Fix inalloca arguments lifetime in coroutines (PR #212331)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 29 04:06:56 PDT 2026
https://github.com/etiennep-chromium updated https://github.com/llvm/llvm-project/pull/212331
>From 5413ef689946031f8abb8b56fd8fe05f47052efb Mon Sep 17 00:00:00 2001
From: Etienne Pierre-doray <etiennep at chromium.org>
Date: Mon, 27 Jul 2026 19:14:00 +0000
Subject: [PATCH 1/4] [Win32][Coroutine] Fix inalloca arguments lifetime in
coroutines
On Win32 x86, arguments passed by value with non-trivial constructors/destructors
require `inalloca`. If a coroutine suspends during the evaluation of these
arguments, the stack frame is popped, destroying the already constructed arguments.
This fix detects when a call inside a coroutine requires `inalloca` and contains
suspend points in its arguments. In this case, we:
1. Force all arguments of the call to be evaluated into temporaries in the
coroutine frame (which survive suspension).
2. Delay the `llvm.stacksave` to the end of argument evaluation (after resume).
3. Allocate the `inalloca` struct on the stack after resume and copy the
temporaries into it.
4. Emit lifetime markers (`llvm.lifetime.start`/`llvm.lifetime.end`) for the
`inalloca` alloca to prevent it from being moved to the coroutine frame
by CoroSplit.
5. Safely clean up the temporaries in the frame.
If an argument is non-copyable/non-movable, we now emit a compilation error.
BUG=LLVM#59382
TAG=agy
CONV=f4ad8337-ec22-4db0-a3a2-b3c51fb6619b
---
clang/lib/CodeGen/CGCall.cpp | 221 ++++++++++++++++++++++++++---------
clang/lib/CodeGen/CGCall.h | 5 +
2 files changed, 171 insertions(+), 55 deletions(-)
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index c0e2456891e9d..b86e509dde0d2 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -27,7 +27,9 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
+#include "clang/AST/ExprCXX.h"
#include "clang/AST/RecordLayout.h"
+#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/CodeGenOptions.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/CodeGen/CGFunctionInfo.h"
@@ -4788,6 +4790,27 @@ static bool isObjCMethodWithTypeParams(const ObjCMethodDecl *method) {
}
#endif
+namespace {
+class CoroSuspendFinder : public RecursiveASTVisitor<CoroSuspendFinder> {
+public:
+ bool FoundSuspend = false;
+ bool VisitCoawaitExpr(CoawaitExpr *) {
+ FoundSuspend = true;
+ return false; // Stop traversal
+ }
+ bool VisitCoyieldExpr(CoyieldExpr *) {
+ FoundSuspend = true;
+ return false; // Stop traversal
+ }
+};
+} // namespace
+
+static bool containsCoroSuspend(const Expr *E) {
+ CoroSuspendFinder Finder;
+ Finder.TraverseStmt(const_cast<Expr *>(E));
+ return Finder.FoundSuspend;
+}
+
/// EmitCallArgs - Emit call arguments for a function.
void CodeGenFunction::EmitCallArgs(
CallArgList &Args, PrototypeWrapper Prototype,
@@ -4886,11 +4909,25 @@ void CodeGenFunction::EmitCallArgs(
std::swap(Args.back(), *(&Args.back() - 1));
};
- // Insert a stack save if we're going to need any inalloca args.
if (hasInAllocaArgs(CGM, ExplicitCC, ArgTypes)) {
assert(getTarget().getTriple().getArch() == llvm::Triple::x86 &&
"inalloca only supported on x86");
- Args.allocateArgumentMemory(*this);
+ if (isCoroutine()) {
+ bool CallHasSuspend = false;
+ for (const Expr *A : ArgRange) {
+ bool hasSuspend = containsCoroSuspend(A);
+ if (hasSuspend) {
+ CallHasSuspend = true;
+ break;
+ }
+ }
+ if (CallHasSuspend) {
+ Args.setForceWriteback(true);
+ }
+ }
+ if (!Args.shouldForceWriteback()) {
+ Args.allocateArgumentMemory(*this);
+ }
}
// Evaluate each argument in the appropriate order.
@@ -4926,6 +4963,10 @@ void CodeGenFunction::EmitCallArgs(
}
}
+ if (Args.shouldForceWriteback()) {
+ Args.allocateArgumentMemory(*this);
+ }
+
if (!LeftToRight) {
// Un-reverse the arguments we just evaluated so they match up with the LLVM
// IR function.
@@ -5023,39 +5064,42 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E,
// In the Microsoft C++ ABI, aggregate arguments are destructed by the callee.
// However, we still have to push an EH-only cleanup in case we unwind before
// we make it to the call.
- if (type->isRecordType() &&
- type->castAsRecordDecl()->isParamDestroyedInCallee()) {
- // If we're using inalloca, use the argument memory. Otherwise, use a
- // temporary.
- AggValueSlot Slot = args.isUsingInAlloca()
- ? createPlaceholderSlot(*this, type)
- : CreateAggTemp(type, "agg.tmp");
-
- bool DestroyedInCallee = true, NeedsCleanup = true;
- if (const auto *RD = type->getAsCXXRecordDecl())
- DestroyedInCallee = RD->hasNonTrivialDestructor();
- else
- NeedsCleanup = type.isDestructedType();
-
- if (DestroyedInCallee)
- Slot.setExternallyDestructed();
-
- EmitAggExpr(E, Slot);
- RValue RV = Slot.asRValue();
- args.add(RV, type);
-
- if (DestroyedInCallee && NeedsCleanup) {
- // Create a no-op GEP between the placeholder and the cleanup so we can
- // RAUW it successfully. It also serves as a marker of the first
- // instruction where the cleanup is active.
- pushFullExprCleanup<DestroyUnpassedArg>(NormalAndEHCleanup,
- Slot.getAddress(), type);
- // This unreachable is a temporary marker which will be removed later.
- llvm::Instruction *IsActive =
- Builder.CreateFlagLoad(llvm::Constant::getNullValue(Int8PtrTy));
- args.addArgCleanupDeactivation(EHStack.stable_begin(), IsActive);
+ if (type->isRecordType()) {
+ bool paramDestroyed = type->castAsRecordDecl()->isParamDestroyedInCallee();
+ if (paramDestroyed) {
+ // If we're using inalloca, use the argument memory. Otherwise, use a
+ // temporary.
+ bool usePlaceholder =
+ args.isUsingInAlloca() && !args.shouldForceWriteback();
+ AggValueSlot Slot = usePlaceholder ? createPlaceholderSlot(*this, type)
+ : CreateAggTemp(type, "agg.tmp");
+
+ bool DestroyedInCallee = true, NeedsCleanup = true;
+ if (const auto *RD = type->getAsCXXRecordDecl())
+ DestroyedInCallee = RD->hasNonTrivialDestructor();
+ else
+ NeedsCleanup = type.isDestructedType();
+
+ if (DestroyedInCallee && !args.shouldForceWriteback())
+ Slot.setExternallyDestructed();
+
+ EmitAggExpr(E, Slot);
+ RValue RV = Slot.asRValue();
+ args.add(RV, type);
+
+ if (DestroyedInCallee && NeedsCleanup && !args.shouldForceWriteback()) {
+ // Create a no-op GEP between the placeholder and the cleanup so we can
+ // RAUW it successfully. It also serves as a marker of the first
+ // instruction where the cleanup is active.
+ pushFullExprCleanup<DestroyUnpassedArg>(NormalAndEHCleanup,
+ Slot.getAddress(), type);
+ // This unreachable is a temporary marker which will be removed later.
+ llvm::Instruction *IsActive =
+ Builder.CreateFlagLoad(llvm::Constant::getNullValue(Int8PtrTy));
+ args.addArgCleanupDeactivation(EHStack.stable_begin(), IsActive);
+ }
+ return;
}
- return;
}
if (HasAggregateEvalKind && isa<ImplicitCastExpr>(E) &&
@@ -5404,9 +5448,13 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
llvm::Instruction *IP = CallArgs.getStackBase();
llvm::AllocaInst *AI;
if (IP) {
- IP = IP->getNextNode();
- AI = new llvm::AllocaInst(ArgStruct, DL.getAllocaAddrSpace(), "argmem",
- IP->getIterator());
+ if (llvm::Instruction *Next = IP->getNextNode()) {
+ AI = new llvm::AllocaInst(ArgStruct, DL.getAllocaAddrSpace(), "argmem",
+ Next->getIterator());
+ } else {
+ AI = new llvm::AllocaInst(ArgStruct, DL.getAllocaAddrSpace(), "argmem",
+ IP->getParent());
+ }
} else {
AI = CreateTempAlloca(ArgStruct, "argmem");
}
@@ -5415,6 +5463,9 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
AI->setUsedWithInAlloca(true);
assert(AI->isUsedWithInAlloca() && !AI->isStaticAlloca());
ArgMemory = RawAddress(AI, ArgStruct, Align);
+ if (isCoroutine()) {
+ EmitLifetimeStart(AI);
+ }
}
ClangToLLVMArgMapping IRFunctionArgs(CGM.getContext(), CallInfo);
@@ -5498,26 +5549,83 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
RawAddress Addr = I->hasLValue()
? I->getKnownLValue().getAddress()
: I->getKnownRValue().getAggregateAddress();
- llvm::Instruction *Placeholder =
- cast<llvm::Instruction>(Addr.getPointer());
-
- if (!ArgInfo.getInAllocaIndirect()) {
- // Replace the placeholder with the appropriate argument slot GEP.
- CGBuilderTy::InsertPoint IP = Builder.saveIP();
- Builder.SetInsertPoint(Placeholder);
- Addr = Builder.CreateStructGEP(ArgMemory,
- ArgInfo.getInAllocaFieldIndex());
- Builder.restoreIP(IP);
+ if (CallArgs.shouldForceWriteback()) {
+ RawAddress Dest = RawAddress::invalid();
+ if (!ArgInfo.getInAllocaIndirect()) {
+ Dest = Builder.CreateStructGEP(ArgMemory,
+ ArgInfo.getInAllocaFieldIndex());
+ } else {
+ Dest = CreateMemTemp(info_it->type, "inalloca.indirect.tmp");
+ Address ArgSlot = Builder.CreateStructGEP(
+ ArgMemory, ArgInfo.getInAllocaFieldIndex());
+ Builder.CreateStore(Dest.getPointer(), ArgSlot);
+ }
+ const CXXRecordDecl *RD = info_it->type->getAsCXXRecordDecl();
+ if (RD && !RD->isTriviallyCopyable()) {
+ const CXXConstructorDecl *CopyCtor = nullptr;
+ const CXXConstructorDecl *MoveCtor = nullptr;
+ for (const CXXConstructorDecl *C : RD->ctors()) {
+ if (C->isDeleted())
+ continue;
+ if (C->isMoveConstructor()) {
+ MoveCtor = C;
+ } else if (C->isCopyConstructor()) {
+ if (!CopyCtor || C->getParamDecl(0)
+ ->getType()
+ ->getPointeeType()
+ .isConstQualified())
+ CopyCtor = C;
+ }
+ }
+ const CXXConstructorDecl *Ctor = MoveCtor ? MoveCtor : CopyCtor;
+ if (!Ctor || Ctor->isDeleted()) {
+ CGM.Error(Loc, "coroutine argument must be copyable or movable "
+ "on this target");
+ EmitAggregateCopy(MakeAddrLValue(Dest, info_it->type),
+ MakeAddrLValue(Addr, info_it->type),
+ info_it->type, AggValueSlot::DoesNotOverlap);
+ } else {
+ CallArgList CtorArgs;
+ llvm::Value *ThisPtr = getAsNaturalPointerTo(
+ Dest, Ctor->getThisType()->getPointeeType());
+ CtorArgs.add(RValue::get(ThisPtr), Ctor->getThisType());
+ QualType ParamTy = Ctor->getParamDecl(0)->getType();
+ llvm::Value *SrcPtr =
+ getAsNaturalPointerTo(Addr, ParamTy->getPointeeType());
+ CtorArgs.add(RValue::get(SrcPtr), ParamTy);
+ EmitCXXConstructorCall(Ctor, Ctor_Complete,
+ /*ForVirtualBase*/ false,
+ /*Delegating*/ false, Dest, CtorArgs,
+ AggValueSlot::DoesNotOverlap, Loc,
+ /*NewPointerIsChecked*/ false);
+ }
+ } else {
+ EmitAggregateCopy(MakeAddrLValue(Dest, info_it->type),
+ MakeAddrLValue(Addr, info_it->type),
+ info_it->type, AggValueSlot::DoesNotOverlap);
+ }
} else {
- // For indirect things such as overaligned structs, replace the
- // placeholder with a regular aggregate temporary alloca. Store the
- // address of this alloca into the struct.
- Addr = CreateMemTemp(info_it->type, "inalloca.indirect.tmp");
- Address ArgSlot = Builder.CreateStructGEP(
- ArgMemory, ArgInfo.getInAllocaFieldIndex());
- Builder.CreateStore(Addr.getPointer(), ArgSlot);
+ llvm::Instruction *Placeholder =
+ cast<llvm::Instruction>(Addr.getPointer());
+
+ if (!ArgInfo.getInAllocaIndirect()) {
+ // Replace the placeholder with the appropriate argument slot GEP.
+ CGBuilderTy::InsertPoint IP = Builder.saveIP();
+ Builder.SetInsertPoint(Placeholder);
+ Addr = Builder.CreateStructGEP(ArgMemory,
+ ArgInfo.getInAllocaFieldIndex());
+ Builder.restoreIP(IP);
+ } else {
+ // For indirect things such as overaligned structs, replace the
+ // placeholder with a regular aggregate temporary alloca. Store the
+ // address of this alloca into the struct.
+ Addr = CreateMemTemp(info_it->type, "inalloca.indirect.tmp");
+ Address ArgSlot = Builder.CreateStructGEP(
+ ArgMemory, ArgInfo.getInAllocaFieldIndex());
+ Builder.CreateStore(Addr.getPointer(), ArgSlot);
+ }
+ deferPlaceholderReplacement(Placeholder, Addr.getPointer());
}
- deferPlaceholderReplacement(Placeholder, Addr.getPointer());
} else if (ArgInfo.getInAllocaIndirect()) {
// Make a temporary alloca and store the address of it into the argument
// struct.
@@ -6289,6 +6397,9 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
// The stack cleanup for inalloca arguments has to run out of the normal
// lexical order, so deactivate it and run it manually here.
CallArgs.freeArgumentMemory(*this);
+ if (isCoroutine() && ArgMemory.isValid()) {
+ EmitLifetimeEnd(ArgMemory.getPointer());
+ }
// Extract the return value.
RValue Ret;
diff --git a/clang/lib/CodeGen/CGCall.h b/clang/lib/CodeGen/CGCall.h
index 145992652934f..488296439eb0c 100644
--- a/clang/lib/CodeGen/CGCall.h
+++ b/clang/lib/CodeGen/CGCall.h
@@ -357,6 +357,9 @@ class CallArgList : public SmallVector<CallArg, 8> {
std::reverse(Writebacks.begin(), Writebacks.end());
}
+ bool shouldForceWriteback() const { return ForceWriteback; }
+ void setForceWriteback(bool V) { ForceWriteback = V; }
+
private:
SmallVector<Writeback, 1> Writebacks;
@@ -367,6 +370,8 @@ class CallArgList : public SmallVector<CallArg, 8> {
/// The stacksave call. It dominates all of the argument evaluation.
llvm::CallInst *StackBase = nullptr;
+
+ bool ForceWriteback = false;
};
/// FunctionArgList - Type for representing both the decl and type
>From 37df429465f43951864ccc41667d02d329d1af6c Mon Sep 17 00:00:00 2001
From: Etienne Pierre-doray <etiennep at chromium.org>
Date: Mon, 27 Jul 2026 20:32:56 +0000
Subject: [PATCH 2/4] CR-efriedma-quic
---
clang/lib/CodeGen/CGCall.cpp | 257 ++++++++++++++--------------
clang/lib/CodeGen/CGCall.h | 5 -
clang/lib/CodeGen/CGExpr.cpp | 5 +
clang/lib/CodeGen/CGExprAgg.cpp | 50 ++++++
clang/lib/CodeGen/CodeGenFunction.h | 12 ++
clang/lib/Sema/SemaExpr.cpp | 65 +++++++
6 files changed, 260 insertions(+), 134 deletions(-)
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index b86e509dde0d2..0312b456df6cd 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -4811,6 +4811,46 @@ static bool containsCoroSuspend(const Expr *E) {
return Finder.FoundSuspend;
}
+static const MaterializeTemporaryExpr *getMTEToPreEvaluate(const Expr *E) {
+ while (true) {
+ E = E->IgnoreParens();
+ if (auto *Cleanups = dyn_cast<ExprWithCleanups>(E)) {
+ E = Cleanups->getSubExpr();
+ continue;
+ }
+ if (auto *Bind = dyn_cast<CXXBindTemporaryExpr>(E)) {
+ E = Bind->getSubExpr();
+ continue;
+ }
+ break;
+ }
+
+ if (auto *CE = dyn_cast<CXXConstructExpr>(E)) {
+ if (CE->getNumArgs() > 0) {
+ const Expr *Arg = CE->getArg(0);
+ while (true) {
+ Arg = Arg->IgnoreParens();
+ if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Arg))
+ return MTE;
+ if (auto *Cleanups = dyn_cast<ExprWithCleanups>(Arg)) {
+ Arg = Cleanups->getSubExpr();
+ continue;
+ }
+ if (auto *Bind = dyn_cast<CXXBindTemporaryExpr>(Arg)) {
+ Arg = Bind->getSubExpr();
+ continue;
+ }
+ if (auto *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
+ Arg = ICE->getSubExpr();
+ continue;
+ }
+ break;
+ }
+ }
+ }
+ return nullptr;
+}
+
/// EmitCallArgs - Emit call arguments for a function.
void CodeGenFunction::EmitCallArgs(
CallArgList &Args, PrototypeWrapper Prototype,
@@ -4909,27 +4949,46 @@ void CodeGenFunction::EmitCallArgs(
std::swap(Args.back(), *(&Args.back() - 1));
};
- if (hasInAllocaArgs(CGM, ExplicitCC, ArgTypes)) {
- assert(getTarget().getTriple().getArch() == llvm::Triple::x86 &&
- "inalloca only supported on x86");
- if (isCoroutine()) {
- bool CallHasSuspend = false;
- for (const Expr *A : ArgRange) {
- bool hasSuspend = containsCoroSuspend(A);
- if (hasSuspend) {
- CallHasSuspend = true;
- break;
- }
+ bool CallHasSuspend = false;
+ if (isCoroutine()) {
+ for (const Expr *A : ArgRange) {
+ if (containsCoroSuspend(A)) {
+ CallHasSuspend = true;
+ break;
}
- if (CallHasSuspend) {
- Args.setForceWriteback(true);
+ }
+ }
+
+ SmallVector<const MaterializeTemporaryExpr *, 4> MTEsToPreEvaluate;
+ if (CallHasSuspend && hasInAllocaArgs(CGM, ExplicitCC, ArgTypes)) {
+ for (const Expr *A : ArgRange) {
+ if (auto *MTE = getMTEToPreEvaluate(A)) {
+ MTEsToPreEvaluate.push_back(MTE);
}
}
- if (!Args.shouldForceWriteback()) {
- Args.allocateArgumentMemory(*this);
+ }
+
+ if (!MTEsToPreEvaluate.empty()) {
+ if (LeftToRight) {
+ for (const MaterializeTemporaryExpr *MTE : MTEsToPreEvaluate) {
+ LValue LV = EmitMaterializeTemporaryExpr(MTE);
+ PreEvaluatedMaterializedTemporaries[MTE] = LV;
+ }
+ } else {
+ for (int i = MTEsToPreEvaluate.size() - 1; i >= 0; --i) {
+ const MaterializeTemporaryExpr *MTE = MTEsToPreEvaluate[i];
+ LValue LV = EmitMaterializeTemporaryExpr(MTE);
+ PreEvaluatedMaterializedTemporaries[MTE] = LV;
+ }
}
}
+ if (hasInAllocaArgs(CGM, ExplicitCC, ArgTypes)) {
+ assert(getTarget().getTriple().getArch() == llvm::Triple::x86 &&
+ "inalloca only supported on x86");
+ Args.allocateArgumentMemory(*this);
+ }
+
// Evaluate each argument in the appropriate order.
size_t CallArgsStart = Args.size();
for (unsigned I = 0, E = ArgTypes.size(); I != E; ++I) {
@@ -4963,10 +5022,6 @@ void CodeGenFunction::EmitCallArgs(
}
}
- if (Args.shouldForceWriteback()) {
- Args.allocateArgumentMemory(*this);
- }
-
if (!LeftToRight) {
// Un-reverse the arguments we just evaluated so they match up with the LLVM
// IR function.
@@ -4975,6 +5030,10 @@ void CodeGenFunction::EmitCallArgs(
// Reverse the writebacks to match the MSVC ABI.
Args.reverseWritebacks();
}
+
+ for (const auto *MTE : MTEsToPreEvaluate) {
+ PreEvaluatedMaterializedTemporaries.erase(MTE);
+ }
}
namespace {
@@ -5064,42 +5123,39 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E,
// In the Microsoft C++ ABI, aggregate arguments are destructed by the callee.
// However, we still have to push an EH-only cleanup in case we unwind before
// we make it to the call.
- if (type->isRecordType()) {
- bool paramDestroyed = type->castAsRecordDecl()->isParamDestroyedInCallee();
- if (paramDestroyed) {
- // If we're using inalloca, use the argument memory. Otherwise, use a
- // temporary.
- bool usePlaceholder =
- args.isUsingInAlloca() && !args.shouldForceWriteback();
- AggValueSlot Slot = usePlaceholder ? createPlaceholderSlot(*this, type)
- : CreateAggTemp(type, "agg.tmp");
-
- bool DestroyedInCallee = true, NeedsCleanup = true;
- if (const auto *RD = type->getAsCXXRecordDecl())
- DestroyedInCallee = RD->hasNonTrivialDestructor();
- else
- NeedsCleanup = type.isDestructedType();
-
- if (DestroyedInCallee && !args.shouldForceWriteback())
- Slot.setExternallyDestructed();
-
- EmitAggExpr(E, Slot);
- RValue RV = Slot.asRValue();
- args.add(RV, type);
-
- if (DestroyedInCallee && NeedsCleanup && !args.shouldForceWriteback()) {
- // Create a no-op GEP between the placeholder and the cleanup so we can
- // RAUW it successfully. It also serves as a marker of the first
- // instruction where the cleanup is active.
- pushFullExprCleanup<DestroyUnpassedArg>(NormalAndEHCleanup,
- Slot.getAddress(), type);
- // This unreachable is a temporary marker which will be removed later.
- llvm::Instruction *IsActive =
- Builder.CreateFlagLoad(llvm::Constant::getNullValue(Int8PtrTy));
- args.addArgCleanupDeactivation(EHStack.stable_begin(), IsActive);
- }
- return;
+ if (type->isRecordType() &&
+ type->castAsRecordDecl()->isParamDestroyedInCallee()) {
+ // If we're using inalloca, use the argument memory. Otherwise, use a
+ // temporary.
+ AggValueSlot Slot = args.isUsingInAlloca()
+ ? createPlaceholderSlot(*this, type)
+ : CreateAggTemp(type, "agg.tmp");
+
+ bool DestroyedInCallee = true, NeedsCleanup = true;
+ if (const auto *RD = type->getAsCXXRecordDecl())
+ DestroyedInCallee = RD->hasNonTrivialDestructor();
+ else
+ NeedsCleanup = type.isDestructedType();
+
+ if (DestroyedInCallee)
+ Slot.setExternallyDestructed();
+
+ EmitAggExpr(E, Slot);
+ RValue RV = Slot.asRValue();
+ args.add(RV, type);
+
+ if (DestroyedInCallee && NeedsCleanup) {
+ // Create a no-op GEP between the placeholder and the cleanup so we can
+ // RAUW it successfully. It also serves as a marker of the first
+ // instruction where the cleanup is active.
+ pushFullExprCleanup<DestroyUnpassedArg>(NormalAndEHCleanup,
+ Slot.getAddress(), type);
+ // This unreachable is a temporary marker which will be removed later.
+ llvm::Instruction *IsActive =
+ Builder.CreateFlagLoad(llvm::Constant::getNullValue(Int8PtrTy));
+ args.addArgCleanupDeactivation(EHStack.stable_begin(), IsActive);
}
+ return;
}
if (HasAggregateEvalKind && isa<ImplicitCastExpr>(E) &&
@@ -5549,83 +5605,26 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
RawAddress Addr = I->hasLValue()
? I->getKnownLValue().getAddress()
: I->getKnownRValue().getAggregateAddress();
- if (CallArgs.shouldForceWriteback()) {
- RawAddress Dest = RawAddress::invalid();
- if (!ArgInfo.getInAllocaIndirect()) {
- Dest = Builder.CreateStructGEP(ArgMemory,
- ArgInfo.getInAllocaFieldIndex());
- } else {
- Dest = CreateMemTemp(info_it->type, "inalloca.indirect.tmp");
- Address ArgSlot = Builder.CreateStructGEP(
- ArgMemory, ArgInfo.getInAllocaFieldIndex());
- Builder.CreateStore(Dest.getPointer(), ArgSlot);
- }
- const CXXRecordDecl *RD = info_it->type->getAsCXXRecordDecl();
- if (RD && !RD->isTriviallyCopyable()) {
- const CXXConstructorDecl *CopyCtor = nullptr;
- const CXXConstructorDecl *MoveCtor = nullptr;
- for (const CXXConstructorDecl *C : RD->ctors()) {
- if (C->isDeleted())
- continue;
- if (C->isMoveConstructor()) {
- MoveCtor = C;
- } else if (C->isCopyConstructor()) {
- if (!CopyCtor || C->getParamDecl(0)
- ->getType()
- ->getPointeeType()
- .isConstQualified())
- CopyCtor = C;
- }
- }
- const CXXConstructorDecl *Ctor = MoveCtor ? MoveCtor : CopyCtor;
- if (!Ctor || Ctor->isDeleted()) {
- CGM.Error(Loc, "coroutine argument must be copyable or movable "
- "on this target");
- EmitAggregateCopy(MakeAddrLValue(Dest, info_it->type),
- MakeAddrLValue(Addr, info_it->type),
- info_it->type, AggValueSlot::DoesNotOverlap);
- } else {
- CallArgList CtorArgs;
- llvm::Value *ThisPtr = getAsNaturalPointerTo(
- Dest, Ctor->getThisType()->getPointeeType());
- CtorArgs.add(RValue::get(ThisPtr), Ctor->getThisType());
- QualType ParamTy = Ctor->getParamDecl(0)->getType();
- llvm::Value *SrcPtr =
- getAsNaturalPointerTo(Addr, ParamTy->getPointeeType());
- CtorArgs.add(RValue::get(SrcPtr), ParamTy);
- EmitCXXConstructorCall(Ctor, Ctor_Complete,
- /*ForVirtualBase*/ false,
- /*Delegating*/ false, Dest, CtorArgs,
- AggValueSlot::DoesNotOverlap, Loc,
- /*NewPointerIsChecked*/ false);
- }
- } else {
- EmitAggregateCopy(MakeAddrLValue(Dest, info_it->type),
- MakeAddrLValue(Addr, info_it->type),
- info_it->type, AggValueSlot::DoesNotOverlap);
- }
+ llvm::Instruction *Placeholder =
+ cast<llvm::Instruction>(Addr.getPointer());
+
+ if (!ArgInfo.getInAllocaIndirect()) {
+ // Replace the placeholder with the appropriate argument slot GEP.
+ CGBuilderTy::InsertPoint IP = Builder.saveIP();
+ Builder.SetInsertPoint(Placeholder);
+ Addr = Builder.CreateStructGEP(ArgMemory,
+ ArgInfo.getInAllocaFieldIndex());
+ Builder.restoreIP(IP);
} else {
- llvm::Instruction *Placeholder =
- cast<llvm::Instruction>(Addr.getPointer());
-
- if (!ArgInfo.getInAllocaIndirect()) {
- // Replace the placeholder with the appropriate argument slot GEP.
- CGBuilderTy::InsertPoint IP = Builder.saveIP();
- Builder.SetInsertPoint(Placeholder);
- Addr = Builder.CreateStructGEP(ArgMemory,
- ArgInfo.getInAllocaFieldIndex());
- Builder.restoreIP(IP);
- } else {
- // For indirect things such as overaligned structs, replace the
- // placeholder with a regular aggregate temporary alloca. Store the
- // address of this alloca into the struct.
- Addr = CreateMemTemp(info_it->type, "inalloca.indirect.tmp");
- Address ArgSlot = Builder.CreateStructGEP(
- ArgMemory, ArgInfo.getInAllocaFieldIndex());
- Builder.CreateStore(Addr.getPointer(), ArgSlot);
- }
- deferPlaceholderReplacement(Placeholder, Addr.getPointer());
+ // For indirect things such as overaligned structs, replace the
+ // placeholder with a regular aggregate temporary alloca. Store the
+ // address of this alloca into the struct.
+ Addr = CreateMemTemp(info_it->type, "inalloca.indirect.tmp");
+ Address ArgSlot = Builder.CreateStructGEP(
+ ArgMemory, ArgInfo.getInAllocaFieldIndex());
+ Builder.CreateStore(Addr.getPointer(), ArgSlot);
}
+ deferPlaceholderReplacement(Placeholder, Addr.getPointer());
} else if (ArgInfo.getInAllocaIndirect()) {
// Make a temporary alloca and store the address of it into the argument
// struct.
diff --git a/clang/lib/CodeGen/CGCall.h b/clang/lib/CodeGen/CGCall.h
index 488296439eb0c..145992652934f 100644
--- a/clang/lib/CodeGen/CGCall.h
+++ b/clang/lib/CodeGen/CGCall.h
@@ -357,9 +357,6 @@ class CallArgList : public SmallVector<CallArg, 8> {
std::reverse(Writebacks.begin(), Writebacks.end());
}
- bool shouldForceWriteback() const { return ForceWriteback; }
- void setForceWriteback(bool V) { ForceWriteback = V; }
-
private:
SmallVector<Writeback, 1> Writebacks;
@@ -370,8 +367,6 @@ class CallArgList : public SmallVector<CallArg, 8> {
/// The stacksave call. It dominates all of the argument evaluation.
llvm::CallInst *StackBase = nullptr;
-
- bool ForceWriteback = false;
};
/// FunctionArgList - Type for representing both the decl and type
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 23802cdeb4811..bd08e056798e3 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -513,6 +513,11 @@ static bool isAAPCS(const TargetInfo &TargetInfo) {
LValue CodeGenFunction::
EmitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *M) {
+ auto It = PreEvaluatedMaterializedTemporaries.find(M);
+ if (It != PreEvaluatedMaterializedTemporaries.end()) {
+ return It->second;
+ }
+
const Expr *E = M->getSubExpr();
assert((!M->getExtendingDecl() || !isa<VarDecl>(M->getExtendingDecl()) ||
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index 3a4291719da74..9d58c3e6aa36d 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -754,8 +754,58 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType,
// Visitor Methods
//===----------------------------------------------------------------------===//
+static void emitNonTrivialCopyOrMove(CodeGenFunction &CGF, AggValueSlot Dest,
+ LValue SrcLV, QualType Type) {
+ const CXXRecordDecl *RD = Type->getAsCXXRecordDecl();
+ assert(RD && "Type must be a CXXRecordDecl");
+
+ const CXXConstructorDecl *CopyCtor = nullptr;
+ const CXXConstructorDecl *MoveCtor = nullptr;
+ for (const CXXConstructorDecl *C : RD->ctors()) {
+ if (C->isDeleted())
+ continue;
+ if (C->isMoveConstructor()) {
+ MoveCtor = C;
+ } else if (C->isCopyConstructor()) {
+ if (!CopyCtor ||
+ C->getParamDecl(0)->getType()->getPointeeType().isConstQualified())
+ CopyCtor = C;
+ }
+ }
+
+ const CXXConstructorDecl *Ctor = MoveCtor ? MoveCtor : CopyCtor;
+ assert(Ctor && "No copy or move constructor found");
+
+ CallArgList CtorArgs;
+ llvm::Value *ThisPtr = CGF.getAsNaturalPointerTo(
+ Dest.getAddress(), Ctor->getThisType()->getPointeeType());
+ CtorArgs.add(RValue::get(ThisPtr), Ctor->getThisType());
+
+ QualType ParamTy = Ctor->getParamDecl(0)->getType();
+ llvm::Value *SrcPtr =
+ CGF.getAsNaturalPointerTo(SrcLV.getAddress(), ParamTy->getPointeeType());
+ CtorArgs.add(RValue::get(SrcPtr), ParamTy);
+
+ CGF.EmitCXXConstructorCall(Ctor, Ctor_Complete,
+ /*ForVirtualBase*/ false,
+ /*Delegating*/ false, Dest.getAddress(), CtorArgs,
+ AggValueSlot::DoesNotOverlap, SourceLocation(),
+ /*NewPointerIsChecked*/ false);
+}
+
void AggExprEmitter::VisitMaterializeTemporaryExpr(
MaterializeTemporaryExpr *E) {
+ if (CGF.hasPreEvaluatedTemporary(E)) {
+ LValue SrcLV = CGF.getPreEvaluatedTemporary(E);
+ QualType Type = E->getType();
+ const CXXRecordDecl *RD = Type->getAsCXXRecordDecl();
+ if (RD && !RD->isTriviallyCopyable()) {
+ emitNonTrivialCopyOrMove(CGF, Dest, SrcLV, Type);
+ } else {
+ EmitFinalDestCopy(Type, SrcLV);
+ }
+ return;
+ }
Visit(E->getSubExpr());
}
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index fd474c09044ef..9ed5e9b532b62 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -1772,6 +1772,8 @@ class CodeGenFunction : public CodeGenTypeCache {
/// expressions.
llvm::DenseMap<const OpaqueValueExpr *, LValue> OpaqueLValues;
llvm::DenseMap<const OpaqueValueExpr *, RValue> OpaqueRValues;
+ llvm::DenseMap<const MaterializeTemporaryExpr *, LValue>
+ PreEvaluatedMaterializedTemporaries;
// VLASizeMap - This keeps track of the associated size for each VLA type.
// We track this by the size expression rather than the type itself because
@@ -3098,6 +3100,16 @@ class CodeGenFunction : public CodeGenTypeCache {
/// already been emitted.
bool isOpaqueValueEmitted(const OpaqueValueExpr *E);
+ bool hasPreEvaluatedTemporary(const MaterializeTemporaryExpr *M) const {
+ return PreEvaluatedMaterializedTemporaries.count(M);
+ }
+ LValue getPreEvaluatedTemporary(const MaterializeTemporaryExpr *M) {
+ auto It = PreEvaluatedMaterializedTemporaries.find(M);
+ assert(It != PreEvaluatedMaterializedTemporaries.end() &&
+ "MTE is not pre-evaluated");
+ return It->second;
+ }
+
/// Get the index of the current ArrayInitLoopExpr, if any.
llvm::Value *getArrayInitIndex() { return ArrayInitIndex; }
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 9fd8c6a0a5451..632049ca354c7 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6209,6 +6209,55 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
return false;
}
+namespace {
+struct CoroSuspendFinder : DynamicRecursiveASTVisitor {
+ bool FoundSuspend = false;
+
+ CoroSuspendFinder() { ShouldVisitImplicitCode = true; }
+
+ bool VisitCoawaitExpr(CoawaitExpr *E) override {
+ FoundSuspend = true;
+ return false; // Stop traversal
+ }
+ bool VisitCoyieldExpr(CoyieldExpr *E) override {
+ FoundSuspend = true;
+ return false; // Stop traversal
+ }
+};
+} // namespace
+
+static bool containsCoroSuspend(const Expr *E) {
+ if (!E)
+ return false;
+ CoroSuspendFinder Finder;
+ Finder.TraverseStmt(const_cast<Expr *>(E));
+ return Finder.FoundSuspend;
+}
+
+static bool isWin32InAllocaRecord(ASTContext &Context, QualType Ty) {
+ const RecordType *RT = Ty->getAs<RecordType>();
+ if (!RT)
+ return false;
+ const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(RT->getDecl());
+ if (!RD)
+ return false;
+
+ const llvm::Triple &Triple = Context.getTargetInfo().getTriple();
+ if (Triple.getArch() != llvm::Triple::x86 || !Triple.isOSWindows())
+ return false;
+ if (!Context.getTargetInfo().getCXXABI().isMicrosoft())
+ return false;
+
+ if (RD->canPassInRegisters())
+ return false;
+
+ TypeInfo Info = Context.getTypeInfo(Context.getCanonicalTagType(RD));
+ if (Info.isAlignRequired() && Info.Align > 4)
+ return false; // passed indirectly
+
+ return true;
+}
+
bool Sema::GatherArgumentsForCall(SourceLocation CallLoc, FunctionDecl *FDecl,
const FunctionProtoType *Proto,
unsigned FirstParam, ArrayRef<Expr *> Args,
@@ -6216,6 +6265,15 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc, FunctionDecl *FDecl,
VariadicCallType CallType, bool AllowExplicit,
bool IsListInitialization) {
unsigned NumParams = Proto->getNumParams();
+ bool CallHasSuspend = false;
+ if (getCurFunction() && getCurFunction()->isCoroutine()) {
+ for (const Expr *A : Args) {
+ if (containsCoroSuspend(A)) {
+ CallHasSuspend = true;
+ break;
+ }
+ }
+ }
bool Invalid = false;
size_t ArgIx = 0;
// Continue to check argument types (even if we have too few/many args).
@@ -6275,6 +6333,13 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc, FunctionDecl *FDecl,
: diag::warn_obt_discarded_at_function_boundary)
<< Arg->getType() << ProtoArgType;
}
+ if (CallHasSuspend && Arg->isPRValue() &&
+ isWin32InAllocaRecord(Context, ProtoArgType)) {
+ ExprResult Materialized =
+ CreateMaterializeTemporaryExpr(Arg->getType(), Arg, false);
+ if (!Materialized.isInvalid())
+ Arg = Materialized.get();
+ }
ExprResult ArgE = PerformCopyInitialization(
Entity, SourceLocation(), Arg, IsListInitialization, AllowExplicit);
>From d134ed5ed5be8f70248b3be2682cef0becfdeb55 Mon Sep 17 00:00:00 2001
From: Etienne Pierre-doray <etiennep at chromium.org>
Date: Wed, 29 Jul 2026 10:49:40 +0000
Subject: [PATCH 3/4] Self review
---
clang/include/clang/AST/Expr.h | 4 +++
clang/lib/AST/Expr.cpp | 16 +++++++++++
clang/lib/CodeGen/CGCall.cpp | 27 ++++--------------
clang/lib/CodeGen/CGExprAgg.cpp | 49 +++------------------------------
clang/lib/CodeGen/CGExprCXX.cpp | 11 +++++++-
clang/lib/Sema/SemaExpr.cpp | 27 +-----------------
6 files changed, 40 insertions(+), 94 deletions(-)
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index a0ab599fa82d2..7ba25ea1295f8 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -247,6 +247,10 @@ class Expr : public ValueStmt {
return static_cast<bool>(getDependence() & ExprDependence::Error);
}
+ /// Whether this expression contains a coroutine suspend point
+ /// (co_await or co_yield).
+ bool containsCoroutineSuspendPoints() const;
+
/// getExprLoc - Return the preferred location for the arrow when diagnosing
/// a problem with a generic expression.
SourceLocation getExprLoc() const LLVM_READONLY;
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 64d61dbc3d128..91d7fe0ffec70 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -3986,6 +3986,22 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
return false;
}
+static bool containsCoroutineSuspendPoints(const Stmt *S) {
+ if (!S)
+ return false;
+ if (isa<CoawaitExpr>(S) || isa<CoyieldExpr>(S) ||
+ isa<DependentCoawaitExpr>(S))
+ return true;
+ for (const Stmt *Child : S->children())
+ if (Child && containsCoroutineSuspendPoints(Child))
+ return true;
+ return false;
+}
+
+bool Expr::containsCoroutineSuspendPoints() const {
+ return ::containsCoroutineSuspendPoints(this);
+}
+
FPOptions Expr::getFPFeaturesInEffect(const LangOptions &LO) const {
if (auto Call = dyn_cast<CallExpr>(this))
return Call->getFPFeaturesInEffect(LO);
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 0312b456df6cd..4cebb5b735dca 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -4790,27 +4790,6 @@ static bool isObjCMethodWithTypeParams(const ObjCMethodDecl *method) {
}
#endif
-namespace {
-class CoroSuspendFinder : public RecursiveASTVisitor<CoroSuspendFinder> {
-public:
- bool FoundSuspend = false;
- bool VisitCoawaitExpr(CoawaitExpr *) {
- FoundSuspend = true;
- return false; // Stop traversal
- }
- bool VisitCoyieldExpr(CoyieldExpr *) {
- FoundSuspend = true;
- return false; // Stop traversal
- }
-};
-} // namespace
-
-static bool containsCoroSuspend(const Expr *E) {
- CoroSuspendFinder Finder;
- Finder.TraverseStmt(const_cast<Expr *>(E));
- return Finder.FoundSuspend;
-}
-
static const MaterializeTemporaryExpr *getMTEToPreEvaluate(const Expr *E) {
while (true) {
E = E->IgnoreParens();
@@ -4952,7 +4931,7 @@ void CodeGenFunction::EmitCallArgs(
bool CallHasSuspend = false;
if (isCoroutine()) {
for (const Expr *A : ArgRange) {
- if (containsCoroSuspend(A)) {
+ if (A && A->containsCoroutineSuspendPoints()) {
CallHasSuspend = true;
break;
}
@@ -5517,6 +5496,10 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
auto Align = CallInfo.getArgStructAlignment();
AI->setAlignment(Align.getAsAlign());
AI->setUsedWithInAlloca(true);
+ if (isCoroutine()) {
+ AI->setMetadata(llvm::LLVMContext::MD_coro_outside_frame,
+ llvm::MDNode::get(CGM.getLLVMContext(), {}));
+ }
assert(AI->isUsedWithInAlloca() && !AI->isStaticAlloca());
ArgMemory = RawAddress(AI, ArgStruct, Align);
if (isCoroutine()) {
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index 9d58c3e6aa36d..fe4f15c2ad882 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -754,56 +754,15 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType,
// Visitor Methods
//===----------------------------------------------------------------------===//
-static void emitNonTrivialCopyOrMove(CodeGenFunction &CGF, AggValueSlot Dest,
- LValue SrcLV, QualType Type) {
- const CXXRecordDecl *RD = Type->getAsCXXRecordDecl();
- assert(RD && "Type must be a CXXRecordDecl");
-
- const CXXConstructorDecl *CopyCtor = nullptr;
- const CXXConstructorDecl *MoveCtor = nullptr;
- for (const CXXConstructorDecl *C : RD->ctors()) {
- if (C->isDeleted())
- continue;
- if (C->isMoveConstructor()) {
- MoveCtor = C;
- } else if (C->isCopyConstructor()) {
- if (!CopyCtor ||
- C->getParamDecl(0)->getType()->getPointeeType().isConstQualified())
- CopyCtor = C;
- }
- }
-
- const CXXConstructorDecl *Ctor = MoveCtor ? MoveCtor : CopyCtor;
- assert(Ctor && "No copy or move constructor found");
-
- CallArgList CtorArgs;
- llvm::Value *ThisPtr = CGF.getAsNaturalPointerTo(
- Dest.getAddress(), Ctor->getThisType()->getPointeeType());
- CtorArgs.add(RValue::get(ThisPtr), Ctor->getThisType());
-
- QualType ParamTy = Ctor->getParamDecl(0)->getType();
- llvm::Value *SrcPtr =
- CGF.getAsNaturalPointerTo(SrcLV.getAddress(), ParamTy->getPointeeType());
- CtorArgs.add(RValue::get(SrcPtr), ParamTy);
-
- CGF.EmitCXXConstructorCall(Ctor, Ctor_Complete,
- /*ForVirtualBase*/ false,
- /*Delegating*/ false, Dest.getAddress(), CtorArgs,
- AggValueSlot::DoesNotOverlap, SourceLocation(),
- /*NewPointerIsChecked*/ false);
-}
-
void AggExprEmitter::VisitMaterializeTemporaryExpr(
MaterializeTemporaryExpr *E) {
if (CGF.hasPreEvaluatedTemporary(E)) {
LValue SrcLV = CGF.getPreEvaluatedTemporary(E);
QualType Type = E->getType();
- const CXXRecordDecl *RD = Type->getAsCXXRecordDecl();
- if (RD && !RD->isTriviallyCopyable()) {
- emitNonTrivialCopyOrMove(CGF, Dest, SrcLV, Type);
- } else {
- EmitFinalDestCopy(Type, SrcLV);
- }
+ assert((!Type->getAsCXXRecordDecl() ||
+ Type->getAsCXXRecordDecl()->isTriviallyCopyable()) &&
+ "Non-trivially copyable MTE should not be visited as aggregate when pre-evaluated");
+ EmitFinalDestCopy(Type, SrcLV);
return;
}
Visit(E->getSubExpr());
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index 82300c3ede183..03d551dda62db 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -624,7 +624,16 @@ void CodeGenFunction::EmitCXXConstructExpr(const CXXConstructExpr *E,
return;
// Elide the constructor if we're constructing from a temporary.
- if (getLangOpts().ElideConstructors && E->isElidable()) {
+ bool DisableElision = false;
+ if (E->getNumArgs() > 0) {
+ if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(
+ E->getArg(0)->IgnoreImpCasts())) {
+ if (hasPreEvaluatedTemporary(MTE))
+ DisableElision = true;
+ }
+ }
+
+ if (getLangOpts().ElideConstructors && E->isElidable() && !DisableElision) {
// FIXME: This only handles the simplest case, where the source object
// is passed directly as the first argument to the constructor.
// This should also handle stepping though implicit casts and
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 632049ca354c7..5777efbf958f1 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6209,31 +6209,6 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
return false;
}
-namespace {
-struct CoroSuspendFinder : DynamicRecursiveASTVisitor {
- bool FoundSuspend = false;
-
- CoroSuspendFinder() { ShouldVisitImplicitCode = true; }
-
- bool VisitCoawaitExpr(CoawaitExpr *E) override {
- FoundSuspend = true;
- return false; // Stop traversal
- }
- bool VisitCoyieldExpr(CoyieldExpr *E) override {
- FoundSuspend = true;
- return false; // Stop traversal
- }
-};
-} // namespace
-
-static bool containsCoroSuspend(const Expr *E) {
- if (!E)
- return false;
- CoroSuspendFinder Finder;
- Finder.TraverseStmt(const_cast<Expr *>(E));
- return Finder.FoundSuspend;
-}
-
static bool isWin32InAllocaRecord(ASTContext &Context, QualType Ty) {
const RecordType *RT = Ty->getAs<RecordType>();
if (!RT)
@@ -6268,7 +6243,7 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc, FunctionDecl *FDecl,
bool CallHasSuspend = false;
if (getCurFunction() && getCurFunction()->isCoroutine()) {
for (const Expr *A : Args) {
- if (containsCoroSuspend(A)) {
+ if (A && A->containsCoroutineSuspendPoints()) {
CallHasSuspend = true;
break;
}
>From a28bee92fcf5fbf3121cb5ec58aee0bd4766d1a8 Mon Sep 17 00:00:00 2001
From: Etienne Pierre-doray <etiennep at chromium.org>
Date: Wed, 29 Jul 2026 11:05:18 +0000
Subject: [PATCH 4/4] Self review
---
clang/lib/CodeGen/CGCall.cpp | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 4cebb5b735dca..e87f332f34b76 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -4928,18 +4928,8 @@ void CodeGenFunction::EmitCallArgs(
std::swap(Args.back(), *(&Args.back() - 1));
};
- bool CallHasSuspend = false;
- if (isCoroutine()) {
- for (const Expr *A : ArgRange) {
- if (A && A->containsCoroutineSuspendPoints()) {
- CallHasSuspend = true;
- break;
- }
- }
- }
-
SmallVector<const MaterializeTemporaryExpr *, 4> MTEsToPreEvaluate;
- if (CallHasSuspend && hasInAllocaArgs(CGM, ExplicitCC, ArgTypes)) {
+ if (isCoroutine() && hasInAllocaArgs(CGM, ExplicitCC, ArgTypes)) {
for (const Expr *A : ArgRange) {
if (auto *MTE = getMTEToPreEvaluate(A)) {
MTEsToPreEvaluate.push_back(MTE);
More information about the cfe-commits
mailing list