[clang] [Win32][Coroutine] Fix inalloca arguments lifetime in coroutines (PR #212331)

via cfe-commits cfe-commits at lists.llvm.org
Wed Jul 29 03:50: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/3] [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/3] 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/3] 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;
       }



More information about the cfe-commits mailing list