[clang] 327141f - [C++] [Coroutines] Prefer aligned (de)allocation for coroutines -

Chuanqi Xu via cfe-commits cfe-commits at lists.llvm.org
Wed Sep 21 20:28:49 PDT 2022


Author: Chuanqi Xu
Date: 2022-09-22T11:28:29+08:00
New Revision: 327141fb1d8ca35b323107a43d57886eb77e7384

URL: https://github.com/llvm/llvm-project/commit/327141fb1d8ca35b323107a43d57886eb77e7384
DIFF: https://github.com/llvm/llvm-project/commit/327141fb1d8ca35b323107a43d57886eb77e7384.diff

LOG: [C++] [Coroutines] Prefer aligned (de)allocation for coroutines -
implement the option2 of P2014R0

This implements the option2 of
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2014r0.pdf.

This also fixes https://github.com/llvm/llvm-project/issues/56671.

Although wg21 didn't get consensus for the direction of the problem,
we're happy to have some implementation and user experience first. And
from issue56671, the option2 should be the pursued one.

Reviewed By: ychen

Differential Revision: https://reviews.llvm.org/D133341

Added: 
    clang/test/CodeGenCoroutines/coro-aligned-alloc-2.cpp
    clang/test/CodeGenCoroutines/coro-aligned-alloc.cpp
    clang/test/SemaCXX/coroutine-alloc-4.cpp

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/Basic/Builtins.def
    clang/include/clang/Basic/DiagnosticGroups.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Basic/LangOptions.def
    clang/include/clang/Driver/Options.td
    clang/include/clang/Sema/Sema.h
    clang/lib/CodeGen/CGBuiltin.cpp
    clang/lib/CodeGen/CGCoroutine.cpp
    clang/lib/Driver/ToolChains/Clang.cpp
    clang/lib/Sema/SemaCoroutine.cpp
    clang/lib/Sema/SemaExprCXX.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index c7c912718da7..c50dd90f1a4a 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -227,6 +227,18 @@ Non-comprehensive list of changes in this release
 New Compiler Flags
 ------------------
 
+- Implemented `-fcoro-aligned-allocation` flag. This flag implements
+  Option 2 of P2014R0 aligned allocation of coroutine frames
+  (`P2014R0 <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2014r0.pdf>`_).
+  With this flag, the coroutines will try to lookup aligned allocation
+  function all the time. The compiler will emit an error if it fails to
+  find aligned allocation function. So if the user code implemented self
+  defined allocation function for coroutines, the existing code will be
+  broken. A little divergence with P2014R0 is that clang will lookup
+  `::operator new(size_­t, std::aligned_val_t, nothrow_­t)` if there is
+  `get_­return_­object_­on_­allocation_­failure`. We feel this is more consistent
+  with the intention.
+
 Deprecated Compiler Flags
 -------------------------
 

diff  --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def
index dc7671ed16ce..b7c52a7292c8 100644
--- a/clang/include/clang/Basic/Builtins.def
+++ b/clang/include/clang/Basic/Builtins.def
@@ -1635,6 +1635,7 @@ LANGBUILTIN(__builtin_coro_done, "bv*", "n", COR_LANG)
 LANGBUILTIN(__builtin_coro_promise, "v*v*IiIb", "n", COR_LANG)
 
 LANGBUILTIN(__builtin_coro_size, "z", "n", COR_LANG)
+LANGBUILTIN(__builtin_coro_align, "z", "n", COR_LANG)
 LANGBUILTIN(__builtin_coro_frame, "v*", "n", COR_LANG)
 LANGBUILTIN(__builtin_coro_noop, "v*", "n", COR_LANG)
 LANGBUILTIN(__builtin_coro_free, "v*v*", "n", COR_LANG)

diff  --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 825092b34339..ff88c8acec4c 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -66,7 +66,10 @@ def DeprecatedCoroutine :
   DiagGroup<"deprecated-coroutine", [DeprecatedExperimentalCoroutine]>;
 def AlwaysInlineCoroutine :
   DiagGroup<"always-inline-coroutine">;
-def Coroutine : DiagGroup<"coroutine", [CoroutineMissingUnhandledException, DeprecatedCoroutine, AlwaysInlineCoroutine]>;
+def CoroNonAlignedAllocationFunction :
+  DiagGroup<"coro-non-aligned-allocation-funciton">;
+def Coroutine : DiagGroup<"coroutine", [CoroutineMissingUnhandledException, DeprecatedCoroutine,
+                                        AlwaysInlineCoroutine, CoroNonAlignedAllocationFunction]>;
 def ObjCBoolConstantConversion : DiagGroup<"objc-bool-constant-conversion">;
 def ConstantConversion : DiagGroup<"constant-conversion",
                                    [BitFieldConstantConversion,

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 8100573db7c8..5fbcc8c93781 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11278,7 +11278,16 @@ def err_coroutine_unusable_new : Error<
   "'operator new' provided by %0 is not usable with the function signature of %1"
 >;
 def err_coroutine_unfound_nothrow_new : Error <
-  "unable to find '::operator new(size_t, nothrow_t)' for %0"
+  "unable to find %select{'::operator new(size_t, nothrow_t)'|"
+  "'::operator new(size_t, align_val_t, nothrow_t)'}1 for %0"
+>;
+def warn_non_aligned_allocation_function : Warning <
+  "under -fcoro-aligned-allocation, the non-aligned allocation function "
+  "for the promise type %0 has higher precedence than the global aligned "
+  "allocation function">,
+  InGroup<CoroNonAlignedAllocationFunction>;
+def err_conflicting_aligned_options : Error <
+  "conflicting option '-fcoro-aligned-allocation' and '-fno-aligned-allocation'"
 >;
 } // end of coroutines issue category
 

diff  --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index d962d295621e..85d7f7a36854 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -154,6 +154,7 @@ LANGOPT(NoBuiltin         , 1, 0, "disable builtin functions")
 LANGOPT(NoMathBuiltin     , 1, 0, "disable math builtin functions")
 LANGOPT(GNUAsm            , 1, 1, "GNU-style inline assembly")
 LANGOPT(Coroutines        , 1, 0, "C++20 coroutines")
+LANGOPT(CoroAlignedAllocation, 1, 0, "prefer Aligned Allocation according to P2014 Option 2")
 LANGOPT(DllExportInlines  , 1, 1, "dllexported classes dllexport inline methods")
 LANGOPT(RelaxedTemplateTemplateArgs, 1, 0, "C++17 relaxed matching of template template arguments")
 LANGOPT(ExperimentalLibrary, 1, 0, "enable unstable and experimental library features")

diff  --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index f7eac47f1bf1..65028569862e 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -1189,6 +1189,11 @@ defm coroutines_ts : BoolFOption<"coroutines-ts",
   PosFlag<SetTrue, [CC1Option], "Enable support for the C++ Coroutines TS">,
   NegFlag<SetFalse>>;
 
+defm coro_aligned_allocation : BoolFOption<"coro-aligned-allocation",
+  LangOpts<"CoroAlignedAllocation">, DefaultFalse,
+  PosFlag<SetTrue, [CC1Option], "Prefer aligned allocation for C++ Coroutines">,
+  NegFlag<SetFalse>>;
+
 defm experimental_library : BoolFOption<"experimental-library",
   LangOpts<"ExperimentalLibrary">, DefaultFalse,
   PosFlag<SetTrue, [CC1Option, CoreOption], "Control whether unstable and experimental library features are enabled. "

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 2ca7a20a9954..870fe5e88ed3 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -6660,7 +6660,8 @@ class Sema final {
 
   bool FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
                                 DeclarationName Name, FunctionDecl *&Operator,
-                                bool Diagnose = true, bool WantSize = false);
+                                bool Diagnose = true, bool WantSize = false,
+                                bool WantAligned = false);
   FunctionDecl *FindUsualDeallocationFunction(SourceLocation StartLoc,
                                               bool CanProvideSize,
                                               bool Overaligned,

diff  --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index c333443adf94..77a2c19a025a 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -4675,6 +4675,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     return EmitCoroutineIntrinsic(E, Intrinsic::coro_suspend);
   case Builtin::BI__builtin_coro_size:
     return EmitCoroutineIntrinsic(E, Intrinsic::coro_size);
+  case Builtin::BI__builtin_coro_align:
+    return EmitCoroutineIntrinsic(E, Intrinsic::coro_align);
 
   // OpenCL v2.0 s6.13.16.2, Built-in pipe read and write functions
   case Builtin::BIread_pipe:

diff  --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp
index abe8d2644a1f..775a4341558a 100644
--- a/clang/lib/CodeGen/CGCoroutine.cpp
+++ b/clang/lib/CodeGen/CGCoroutine.cpp
@@ -683,6 +683,13 @@ RValue CodeGenFunction::EmitCoroutineIntrinsic(const CallExpr *E,
     llvm::Function *F = CGM.getIntrinsic(llvm::Intrinsic::coro_size, T);
     return RValue::get(Builder.CreateCall(F));
   }
+  case llvm::Intrinsic::coro_align: {
+    auto &Context = getContext();
+    CanQualType SizeTy = Context.getSizeType();
+    llvm::IntegerType *T = Builder.getIntNTy(Context.getTypeSize(SizeTy));
+    llvm::Function *F = CGM.getIntrinsic(llvm::Intrinsic::coro_align, T);
+    return RValue::get(Builder.CreateCall(F));
+  }
   // The following three intrinsics take a token parameter referring to a token
   // returned by earlier call to @llvm.coro.id. Since we cannot represent it in
   // builtins, we patch it up here.

diff  --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 4fe68f6896ef..02683f1365f6 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6500,6 +6500,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
     CmdArgs.push_back("-fcoroutines-ts");
   }
 
+  if (Args.hasFlag(options::OPT_fcoro_aligned_allocation,
+                   options::OPT_fno_coro_aligned_allocation, false) &&
+      types::isCXX(InputType))
+    CmdArgs.push_back("-fcoro-aligned-allocation");
+
   Args.AddLastArg(CmdArgs, options::OPT_fdouble_square_bracket_attributes,
                   options::OPT_fno_double_square_bracket_attributes);
 

diff  --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index 34ed416da3dd..d39dac79fb5d 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -1030,6 +1030,13 @@ static Expr *buildStdNoThrowDeclRef(Sema &S, SourceLocation Loc) {
   return DR.get();
 }
 
+static TypeSourceInfo *getTypeSourceInfoForStdAlignValT(Sema &S,
+                                                        SourceLocation Loc) {
+  EnumDecl *StdAlignValT = S.getStdAlignValT();
+  QualType StdAlignValDecl = S.Context.getTypeDeclType(StdAlignValT);
+  return S.Context.getTrivialTypeSourceInfo(StdAlignValDecl);
+}
+
 // Find an appropriate delete for the promise.
 static bool findDeleteForPromise(Sema &S, SourceLocation Loc, QualType PromiseType,
                                  FunctionDecl *&OperatorDelete) {
@@ -1039,12 +1046,15 @@ static bool findDeleteForPromise(Sema &S, SourceLocation Loc, QualType PromiseTy
   auto *PointeeRD = PromiseType->getAsCXXRecordDecl();
   assert(PointeeRD && "PromiseType must be a CxxRecordDecl type");
 
+  const bool Overaligned = S.getLangOpts().CoroAlignedAllocation;
+
   // [dcl.fct.def.coroutine]p12
   // The deallocation function's name is looked up by searching for it in the
   // scope of the promise type. If nothing is found, a search is performed in
   // the global scope.
   if (S.FindDeallocationFunction(Loc, PointeeRD, DeleteName, OperatorDelete,
-                                 /*Diagnose*/ true, /*WantSize*/ true))
+                                 /*Diagnose*/ true, /*WantSize*/ true,
+                                 /*WantAligned*/ Overaligned))
     return false;
 
   // [dcl.fct.def.coroutine]p12
@@ -1057,7 +1067,6 @@ static bool findDeleteForPromise(Sema &S, SourceLocation Loc, QualType PromiseTy
     // Look for a global declaration.
     // Coroutines can always provide their required size.
     const bool CanProvideSize = true;
-    const bool Overaligned = false;
     // Sema::FindUsualDeallocationFunction will try to find the one with two
     // parameters first. It will return the deallocation function with one
     // parameter if failed.
@@ -1324,7 +1333,6 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
   // lvalue that denotes the parameter copy corresponding to p_i.
 
   FunctionDecl *OperatorNew = nullptr;
-  bool PassAlignment = false;
   SmallVector<Expr *, 1> PlacementArgs;
 
   const bool PromiseContainsNew = [this, &PromiseType]() -> bool {
@@ -1338,8 +1346,13 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
     return !R.empty() && !R.isAmbiguous();
   }();
 
+  // Helper function to indicate whether the last lookup found the aligned
+  // allocation function.
+  bool PassAlignment = S.getLangOpts().CoroAlignedAllocation;
   auto LookupAllocationFunction = [&](Sema::AllocationFunctionScope NewScope =
-                                          Sema::AFS_Both) {
+                                          Sema::AFS_Both,
+                                      bool WithoutPlacementArgs = false,
+                                      bool ForceNonAligned = false) {
     // [dcl.fct.def.coroutine]p9
     //   The allocation function's name is looked up by searching for it in the
     // scope of the promise type.
@@ -1349,10 +1362,13 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
     if (NewScope == Sema::AFS_Both)
       NewScope = PromiseContainsNew ? Sema::AFS_Class : Sema::AFS_Global;
 
+    PassAlignment = !ForceNonAligned && S.getLangOpts().CoroAlignedAllocation;
     FunctionDecl *UnusedResult = nullptr;
     S.FindAllocationFunctions(Loc, SourceRange(), NewScope,
                               /*DeleteScope*/ Sema::AFS_Both, PromiseType,
-                              /*isArray*/ false, PassAlignment, PlacementArgs,
+                              /*isArray*/ false, PassAlignment,
+                              WithoutPlacementArgs ? MultiExprArg{}
+                                                   : PlacementArgs,
                               OperatorNew, UnusedResult, /*Diagnose*/ false);
   };
 
@@ -1364,15 +1380,58 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
 
   LookupAllocationFunction();
 
-  // [dcl.fct.def.coroutine]p9
-  //   If no viable function is found ([over.match.viable]), overload resolution
-  // is performed again on a function call created by passing just the amount of
-  // space required as an argument of type std::size_t.
-  if (!OperatorNew && !PlacementArgs.empty() && PromiseContainsNew) {
-    PlacementArgs.clear();
-    LookupAllocationFunction();
+  if (PromiseContainsNew && !PlacementArgs.empty()) {
+    // [dcl.fct.def.coroutine]p9
+    //   If no viable function is found ([over.match.viable]), overload
+    //   resolution
+    // is performed again on a function call created by passing just the amount
+    // of space required as an argument of type std::size_t.
+    //
+    // Proposed Change of [dcl.fct.def.coroutine]p9 in P2014R0:
+    //   Otherwise, overload resolution is performed again on a function call
+    //   created
+    // by passing the amount of space requested as an argument of type
+    // std::size_t as the first argument, and the requested alignment as
+    // an argument of type std:align_val_t as the second argument.
+    if (!OperatorNew ||
+        (S.getLangOpts().CoroAlignedAllocation && !PassAlignment))
+      LookupAllocationFunction(/*NewScope*/ Sema::AFS_Class,
+                               /*WithoutPlacementArgs*/ true);
   }
 
+  // Proposed Change of [dcl.fct.def.coroutine]p12 in P2014R0:
+  //   Otherwise, overload resolution is performed again on a function call
+  //   created
+  // by passing the amount of space requested as an argument of type
+  // std::size_t as the first argument, and the lvalues p1 ... pn as the
+  // succeeding arguments. Otherwise, overload resolution is performed again
+  // on a function call created by passing just the amount of space required as
+  // an argument of type std::size_t.
+  //
+  // So within the proposed change in P2014RO, the priority order of aligned
+  // allocation functions wiht promise_type is:
+  //
+  //    void* operator new( std::size_t, std::align_val_t, placement_args... );
+  //    void* operator new( std::size_t, std::align_val_t);
+  //    void* operator new( std::size_t, placement_args... );
+  //    void* operator new( std::size_t);
+
+  // Helper variable to emit warnings.
+  bool FoundNonAlignedInPromise = false;
+  if (PromiseContainsNew && S.getLangOpts().CoroAlignedAllocation)
+    if (!OperatorNew || !PassAlignment) {
+      FoundNonAlignedInPromise = OperatorNew;
+
+      LookupAllocationFunction(/*NewScope*/ Sema::AFS_Class,
+                               /*WithoutPlacementArgs*/ false,
+                               /*ForceNonAligned*/ true);
+
+      if (!OperatorNew && !PlacementArgs.empty())
+        LookupAllocationFunction(/*NewScope*/ Sema::AFS_Class,
+                                 /*WithoutPlacementArgs*/ true,
+                                 /*ForceNonAligned*/ true);
+    }
+
   bool IsGlobalOverload =
       OperatorNew && !isa<CXXRecordDecl>(OperatorNew->getDeclContext());
   // If we didn't find a class-local new declaration and non-throwing new
@@ -1387,11 +1446,21 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
     LookupAllocationFunction(Sema::AFS_Global);
   }
 
+  // If we found a non-aligned allocation function in the promise_type,
+  // it indicates the user forgot to update the allocation function. Let's emit
+  // a warning here.
+  if (FoundNonAlignedInPromise) {
+    S.Diag(OperatorNew->getLocation(),
+           diag::warn_non_aligned_allocation_function)
+        << &FD;
+  }
+
   if (!OperatorNew) {
     if (PromiseContainsNew)
       S.Diag(Loc, diag::err_coroutine_unusable_new) << PromiseType << &FD;
     else if (RequiresNoThrowAlloc)
-      S.Diag(Loc, diag::err_coroutine_unfound_nothrow_new) << &FD;
+      S.Diag(Loc, diag::err_coroutine_unfound_nothrow_new)
+          << &FD << S.getLangOpts().CoroAlignedAllocation;
 
     return false;
   }
@@ -1422,15 +1491,34 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
   Expr *FrameSize =
       S.BuildBuiltinCallExpr(Loc, Builtin::BI__builtin_coro_size, {});
 
-  // Make new call.
+  Expr *FrameAlignment = nullptr;
+
+  if (S.getLangOpts().CoroAlignedAllocation) {
+    FrameAlignment =
+        S.BuildBuiltinCallExpr(Loc, Builtin::BI__builtin_coro_align, {});
+
+    TypeSourceInfo *AlignValTy = getTypeSourceInfoForStdAlignValT(S, Loc);
+    if (!AlignValTy)
+      return false;
+
+    FrameAlignment = S.BuildCXXNamedCast(Loc, tok::kw_static_cast, AlignValTy,
+                                         FrameAlignment, SourceRange(Loc, Loc),
+                                         SourceRange(Loc, Loc))
+                         .get();
+  }
 
+  // Make new call.
   ExprResult NewRef =
       S.BuildDeclRefExpr(OperatorNew, OperatorNew->getType(), VK_LValue, Loc);
   if (NewRef.isInvalid())
     return false;
 
   SmallVector<Expr *, 2> NewArgs(1, FrameSize);
-  llvm::append_range(NewArgs, PlacementArgs);
+  if (S.getLangOpts().CoroAlignedAllocation && PassAlignment)
+    NewArgs.push_back(FrameAlignment);
+
+  if (OperatorNew->getNumParams() > NewArgs.size())
+    llvm::append_range(NewArgs, PlacementArgs);
 
   ExprResult NewExpr =
       S.BuildCallExpr(S.getCurScope(), NewRef.get(), Loc, NewArgs, Loc);
@@ -1459,9 +1547,29 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
   //   used, the size of the block is passed as the corresponding argument.
   const auto *OpDeleteType =
       OpDeleteQualType.getTypePtr()->castAs<FunctionProtoType>();
-  if (OpDeleteType->getNumParams() > 1)
+  if (OpDeleteType->getNumParams() > DeleteArgs.size() &&
+      S.getASTContext().hasSameType(
+          OpDeleteType->getParamType(DeleteArgs.size()), FrameSize->getType()))
     DeleteArgs.push_back(FrameSize);
 
+  // Proposed Change of [dcl.fct.def.coroutine]p12 in P2014R0:
+  //   If deallocation function lookup finds a usual deallocation function with
+  //   a pointer parameter, size parameter and alignment parameter then this
+  //   will be the selected deallocation function, otherwise if lookup finds a
+  //   usual deallocation function with both a pointer parameter and a size
+  //   parameter, then this will be the selected deallocation function.
+  //   Otherwise, if lookup finds a usual deallocation function with only a
+  //   pointer parameter, then this will be the selected deallocation
+  //   function.
+  //
+  // So we are not forced to pass alignment to the deallocation function.
+  if (S.getLangOpts().CoroAlignedAllocation &&
+      OpDeleteType->getNumParams() > DeleteArgs.size() &&
+      S.getASTContext().hasSameType(
+          OpDeleteType->getParamType(DeleteArgs.size()),
+          FrameAlignment->getType()))
+    DeleteArgs.push_back(FrameAlignment);
+
   ExprResult DeleteExpr =
       S.BuildCallExpr(S.getCurScope(), DeleteRef.get(), Loc, DeleteArgs, Loc);
   DeleteExpr =

diff  --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 0826a9106020..3e65b3e254f3 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -3189,7 +3189,7 @@ FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc,
 bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
                                     DeclarationName Name,
                                     FunctionDecl *&Operator, bool Diagnose,
-                                    bool WantSize) {
+                                    bool WantSize, bool WantAligned) {
   LookupResult Found(*this, Name, StartLoc, LookupOrdinaryName);
   // Try to find operator delete/operator delete[] in class scope.
   LookupQualifiedName(Found, RD);
@@ -3199,7 +3199,8 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
 
   Found.suppressDiagnostics();
 
-  bool Overaligned = hasNewExtendedAlignment(*this, Context.getRecordType(RD));
+  bool Overaligned =
+      WantAligned || hasNewExtendedAlignment(*this, Context.getRecordType(RD));
 
   // C++17 [expr.delete]p10:
   //   If the deallocation functions have class scope, the one without a

diff  --git a/clang/test/CodeGenCoroutines/coro-aligned-alloc-2.cpp b/clang/test/CodeGenCoroutines/coro-aligned-alloc-2.cpp
new file mode 100644
index 000000000000..a3ee964a22a1
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-aligned-alloc-2.cpp
@@ -0,0 +1,123 @@
+// Tests that the combination of -fcoro-aligned-allocation and -fsized-deallocation works well.
+// Test the compiler will chose sized deallocation correctly.
+// This is only enabled with `-fsized-deallocation` which is off by default.
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 \
+// RUN:   -fcoro-aligned-allocation -S -emit-llvm %s -o - -disable-llvm-passes \
+// RUN:   -fsized-deallocation \
+// RUN:   | FileCheck %s
+
+#include "Inputs/coroutine.h"
+
+namespace std {
+    typedef __SIZE_TYPE__ size_t;
+    enum class align_val_t : size_t {};
+}
+
+struct task {
+  struct promise_type {
+    auto initial_suspend() { return std::suspend_always{}; }
+    auto final_suspend() noexcept { return std::suspend_always{}; }
+    auto get_return_object() { return task{}; }
+    void unhandled_exception() {}
+    void return_value(int) {}
+  };
+};
+
+// CHECK: define{{.*}}@_Z1fv
+// CHECK: coro.free:
+// CHECK: %[[coro_size:.+]] = call{{.*}}@llvm.coro.size
+// CHECK: %[[coro_align:.+]] = call{{.*}}@llvm.coro.align
+// CHECK: call{{.*}}void @_ZdlPvmSt11align_val_t(ptr{{.*}}, i64{{.*}}%[[coro_size]], i64{{.*}}%[[coro_align]])
+
+task f() {
+  co_return 43;
+}
+
+struct task2 {
+  struct promise_type {
+    auto initial_suspend() { return std::suspend_always{}; }
+    auto final_suspend() noexcept { return std::suspend_always{}; }
+    auto get_return_object() { return task2{}; }
+    void unhandled_exception() {}
+    void return_value(int) {}
+    void operator delete(void *ptr);
+  };
+};
+
+// CHECK: define{{.*}}@_Z2f2v
+// CHECK: %[[FREE_HANDLE:.+]] = call{{.*}}ptr @llvm.coro.free(
+// CHECK: coro.free:
+// CHECK: call{{.*}}void @_ZN5task212promise_typedlEPv(ptr{{.*}} %[[FREE_HANDLE]])
+
+task2 f2() {
+  co_return 43;
+}
+
+struct task3 {
+  struct promise_type {
+    auto initial_suspend() { return std::suspend_always{}; }
+    auto final_suspend() noexcept { return std::suspend_always{}; }
+    auto get_return_object() { return task3{}; }
+    void unhandled_exception() {}
+    void return_value(int) {}
+    void operator delete(void *ptr, std::size_t);
+    void operator delete(void *ptr);
+  };
+};
+
+// CHECK: define{{.*}}@_Z2f3v
+// CHECK: %[[FREE_HANDLE:.+]] = call{{.*}}ptr @llvm.coro.free(
+// CHECK: coro.free:
+// CHECK: %[[coro_size:.+]] = call{{.*}}@llvm.coro.size
+// CHECK: call{{.*}}void @_ZN5task312promise_typedlEPvm(ptr{{.*}} %[[FREE_HANDLE]], i64{{.*}}%[[coro_size]]
+
+task3 f3() {
+  co_return 43;
+}
+
+struct task4 {
+  struct promise_type {
+    auto initial_suspend() { return std::suspend_always{}; }
+    auto final_suspend() noexcept { return std::suspend_always{}; }
+    auto get_return_object() { return task4{}; }
+    void unhandled_exception() {}
+    void return_value(int) {}
+    void operator delete(void *ptr, std::size_t);
+    void operator delete(void *ptr, std::align_val_t);
+    void operator delete(void *ptr);
+  };
+};
+
+// CHECK: define{{.*}}@_Z2f4v
+// CHECK: %[[FREE_HANDLE:.+]] = call{{.*}}ptr @llvm.coro.free(
+// CHECK: coro.free:
+// CHECK: %[[coro_align:.+]] = call{{.*}}@llvm.coro.align
+// CHECK: call{{.*}}void @_ZN5task412promise_typedlEPvSt11align_val_t(ptr{{.*}} %[[FREE_HANDLE]], i64{{.*}}%[[coro_align]])
+
+task4 f4() {
+  co_return 43;
+}
+
+struct task5 {
+  struct promise_type {
+    auto initial_suspend() { return std::suspend_always{}; }
+    auto final_suspend() noexcept { return std::suspend_always{}; }
+    auto get_return_object() { return task5{}; }
+    void unhandled_exception() {}
+    void return_value(int) {}
+    void operator delete(void *ptr, std::size_t);
+    void operator delete(void *ptr, std::size_t, std::align_val_t);
+    void operator delete(void *ptr);
+  };
+};
+
+// CHECK: define{{.*}}@_Z2f5v
+// CHECK: %[[FREE_HANDLE:.+]] = call{{.*}}ptr @llvm.coro.free(
+// CHECK: coro.free:
+// CHECK: %[[coro_size:.+]] = call{{.*}}@llvm.coro.size
+// CHECK: %[[coro_align:.+]] = call{{.*}}@llvm.coro.align
+// CHECK: call{{.*}}void @_ZN5task512promise_typedlEPvmSt11align_val_t(ptr{{.*}} %[[FREE_HANDLE]], i64{{.*}}%[[coro_size]], i64{{.*}}%[[coro_align]])
+
+task5 f5() {
+  co_return 43;
+}

diff  --git a/clang/test/CodeGenCoroutines/coro-aligned-alloc.cpp b/clang/test/CodeGenCoroutines/coro-aligned-alloc.cpp
new file mode 100644
index 000000000000..d14c3d372ddb
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-aligned-alloc.cpp
@@ -0,0 +1,190 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 \
+// RUN:   -fcoro-aligned-allocation -S -emit-llvm %s -o - -disable-llvm-passes \
+// RUN:   | FileCheck %s
+
+#include "Inputs/coroutine.h"
+
+namespace std {
+    typedef __SIZE_TYPE__ size_t;
+    enum class align_val_t : size_t {};
+}
+
+struct task {
+  struct promise_type {
+    auto initial_suspend() { return std::suspend_always{}; }
+    auto final_suspend() noexcept { return std::suspend_always{}; }
+    auto get_return_object() { return task{}; }
+    void unhandled_exception() {}
+    void return_value(int) {}
+  };
+};
+
+// CHECK: define{{.*}}@_Z1fv(
+// CHECK: coro.alloc:
+// CHECK: %[[coro_size:.+]] = call{{.*}}@llvm.coro.size
+// CHECK: %[[coro_align:.+]] = call{{.*}}@llvm.coro.align
+// CHECK: %[[aligned_new:.+]] = call{{.*}}@_ZnwmSt11align_val_t({{.*}}%[[coro_size]],{{.*}}%[[coro_align]])
+
+// CHECK: coro.free:
+// CHECK: %[[coro_align_for_free:.+]] = call{{.*}}@llvm.coro.align
+// CHECK: call void @_ZdlPvSt11align_val_t({{.*}}[[coro_align_for_free]]
+
+task f() {
+    co_return 43;
+}
+
+struct task2 {
+  struct promise_type {
+    auto initial_suspend() { return std::suspend_always{}; }
+    auto final_suspend() noexcept { return std::suspend_always{}; }
+    auto get_return_object() { return task2{}; }
+    void unhandled_exception() {}
+    void return_value(int) {}
+    static task2 get_return_object_on_allocation_failure() { return task2{}; }
+  };
+};
+
+namespace std {
+  struct nothrow_t {};
+  constexpr nothrow_t nothrow = {};
+}
+
+void *operator new(std::size_t, std::align_val_t, std::nothrow_t) noexcept;
+
+// CHECK: define{{.*}}@_Z2f2v(
+// CHECK: coro.alloc:
+// CHECK: %[[coro_size:.+]] = call{{.*}}@llvm.coro.size
+// CHECK: %[[coro_align:.+]] = call{{.*}}@llvm.coro.align
+// CHECK: %[[aligned_new:.+]] = call{{.*}}@_ZnwmSt11align_val_tSt9nothrow_t({{.*}}%[[coro_size]],{{.*}}%[[coro_align]])
+
+// CHECK: coro.free:
+// CHECK: %[[coro_align_for_free:.+]] = call{{.*}}@llvm.coro.align
+// CHECK: call void @_ZdlPvSt11align_val_t({{.*}}[[coro_align_for_free]]
+
+task2 f2() {
+    co_return 43;
+}
+
+struct task3 {
+  struct promise_type {
+    auto initial_suspend() { return std::suspend_always{}; }
+    auto final_suspend() noexcept { return std::suspend_always{}; }
+    auto get_return_object() { return task3{}; }
+    void unhandled_exception() {}
+    void return_value(int) {}
+    void operator delete(void *ptr);
+  };
+};
+
+// CHECK: define{{.*}}@_Z2f3v
+// CHECK: coro.free:
+// CHECK: call{{.*}}void @_ZN5task312promise_typedlEPv(
+
+task3 f3() {
+  co_return 43;
+}
+
+struct task4 {
+  struct promise_type {
+    auto initial_suspend() { return std::suspend_always{}; }
+    auto final_suspend() noexcept { return std::suspend_always{}; }
+    auto get_return_object() { return task4{}; }
+    void unhandled_exception() {}
+    void return_value(int) {}
+    void operator delete(void *ptr, std::align_val_t);
+    void operator delete(void *ptr);
+  };
+};
+
+// CHECK: define{{.*}}@_Z2f4v
+// CHECK: coro.free:
+// CHECK: %[[coro_align_for_free:.+]] = call{{.*}}@llvm.coro.align
+// CHECK: call{{.*}}void @_ZN5task412promise_typedlEPvSt11align_val_t({{.*}}, i64{{.*}}[[coro_align_for_free]]
+
+task4 f4() {
+  co_return 43;
+}
+
+struct task5 {
+  struct promise_type {
+    auto initial_suspend() { return std::suspend_always{}; }
+    auto final_suspend() noexcept { return std::suspend_always{}; }
+    auto get_return_object() { return task5{}; }
+    void unhandled_exception() {}
+    void return_value(int) {}
+    void *operator new(std::size_t);
+  };
+};
+
+// CHECK: define{{.*}}@_Z2f5v
+// CHECK: coro.alloc:
+// CHECK: %[[coro_size:.+]] = call{{.*}}@llvm.coro.size
+// CHECK: call{{.*}}ptr @_ZN5task512promise_typenwEm(i64{{.*}}%[[coro_size]])
+task5 f5() {
+  co_return 43;
+}
+
+struct task6 {
+  struct promise_type {
+    auto initial_suspend() { return std::suspend_always{}; }
+    auto final_suspend() noexcept { return std::suspend_always{}; }
+    auto get_return_object() { return task6{}; }
+    void unhandled_exception() {}
+    void return_value(int) {}
+    void *operator new(std::size_t);
+    void *operator new(std::size_t, int i);
+  };
+};
+
+// CHECK: define{{.*}}@_Z2f6i
+// CHECK: coro.alloc:
+// CHECK: %[[coro_size:.+]] = call{{.*}}@llvm.coro.size
+// CHECK: call{{.*}}ptr @_ZN5task612promise_typenwEmi(i64{{.*}}%[[coro_size]],
+task6 f6(int i) {
+  co_return i;
+}
+
+struct task7 {
+  struct promise_type {
+    auto initial_suspend() { return std::suspend_always{}; }
+    auto final_suspend() noexcept { return std::suspend_always{}; }
+    auto get_return_object() { return task7{}; }
+    void unhandled_exception() {}
+    void return_value(int) {}
+    void *operator new(std::size_t);
+    void *operator new(std::size_t, int i);
+    void *operator new(std::size_t, std::align_val_t);
+  };
+};
+
+// CHECK: define{{.*}}@_Z2f7i
+// CHECK: coro.alloc:
+// CHECK: %[[coro_size:.+]] = call{{.*}}@llvm.coro.size
+// CHECK: %[[coro_align:.+]] = call{{.*}}@llvm.coro.align
+// CHECK: call{{.*}}ptr @_ZN5task712promise_typenwEmSt11align_val_t(i64{{.*}}%[[coro_size]], i64{{.*}}[[coro_align]])
+task7 f7(int i) {
+  co_return i;
+}
+
+struct task8 {
+  struct promise_type {
+    auto initial_suspend() { return std::suspend_always{}; }
+    auto final_suspend() noexcept { return std::suspend_always{}; }
+    auto get_return_object() { return task8{}; }
+    void unhandled_exception() {}
+    void return_value(int) {}
+    void *operator new(std::size_t);
+    void *operator new(std::size_t, int i);
+    void *operator new(std::size_t, std::align_val_t);
+    void *operator new(std::size_t, std::align_val_t, int i);
+  };
+};
+
+// CHECK: define{{.*}}@_Z2f8i
+// CHECK: coro.alloc:
+// CHECK: %[[coro_size:.+]] = call{{.*}}@llvm.coro.size
+// CHECK: %[[coro_align:.+]] = call{{.*}}@llvm.coro.align
+// CHECK: call{{.*}}ptr @_ZN5task812promise_typenwEmSt11align_val_ti(i64{{.*}}%[[coro_size]], i64{{.*}}[[coro_align]],
+task8 f8(int i) {
+  co_return i;
+}

diff  --git a/clang/test/SemaCXX/coroutine-alloc-4.cpp b/clang/test/SemaCXX/coroutine-alloc-4.cpp
new file mode 100644
index 000000000000..acad2779a254
--- /dev/null
+++ b/clang/test/SemaCXX/coroutine-alloc-4.cpp
@@ -0,0 +1,116 @@
+// Tests that we'll find aligned allocation funciton properly.
+// RUN: %clang_cc1 %s -std=c++20 %s -fsyntax-only -verify -fcoro-aligned-allocation
+
+#include "Inputs/std-coroutine.h"
+
+namespace std {
+    typedef __SIZE_TYPE__ size_t;
+    enum class align_val_t : size_t {};
+}
+
+struct task {
+  struct promise_type {
+    auto initial_suspend() { return std::suspend_always{}; }
+    auto final_suspend() noexcept { return std::suspend_always{}; }
+    auto get_return_object() { return task{}; }
+    void unhandled_exception() {}
+    void return_value(int) {}
+    void *operator new(std::size_t); // expected-warning 1+{{under -fcoro-aligned-allocation, the non-aligned allocation function for the promise type 'f' has higher precedence than the global aligned allocation function}}
+  };
+};
+
+task f() {
+    co_return 43;
+}
+
+struct task2 {
+  struct promise_type {
+    auto initial_suspend() { return std::suspend_always{}; }
+    auto final_suspend() noexcept { return std::suspend_always{}; }
+    auto get_return_object() { return task2{}; }
+    void unhandled_exception() {}
+    void return_value(int) {}
+    void *operator new(std::size_t, std::align_val_t);
+  };
+};
+
+// no diagnostic expected
+task2 f1() {
+    co_return 43;
+}
+
+struct task3 {
+  struct promise_type {
+    auto initial_suspend() { return std::suspend_always{}; }
+    auto final_suspend() noexcept { return std::suspend_always{}; }
+    auto get_return_object() { return task3{}; }
+    void unhandled_exception() {}
+    void return_value(int) {}
+    void *operator new(std::size_t, std::align_val_t) noexcept;
+    void *operator new(std::size_t) noexcept;
+    static auto get_return_object_on_allocation_failure() { return task3{}; }
+  };
+};
+
+// no diagnostic expected
+task3 f2() {
+    co_return 43;
+}
+
+struct task4 {
+  struct promise_type {
+    auto initial_suspend() { return std::suspend_always{}; }
+    auto final_suspend() noexcept { return std::suspend_always{}; }
+    auto get_return_object() { return task4{}; }
+    void unhandled_exception() {}
+    void return_value(int) {}
+    void *operator new(std::size_t, std::align_val_t, int, double, int) noexcept;
+  };
+};
+
+// no diagnostic expected
+task4 f3(int, double, int) {
+    co_return 43;
+}
+
+struct task5 {
+  struct promise_type {
+    auto initial_suspend() { return std::suspend_always{}; }
+    auto final_suspend() noexcept { return std::suspend_always{}; }
+    auto get_return_object() { return task5{}; }
+    void unhandled_exception() {}
+    void return_value(int) {}
+  };
+};
+
+// no diagnostic expected.
+// The aligned allocation will be declared by the compiler.
+task5 f4() {
+    co_return 43;
+}
+
+namespace std {
+  struct nothrow_t {};
+  constexpr nothrow_t nothrow = {};
+}
+
+struct task6 {
+  struct promise_type {
+    auto initial_suspend() { return std::suspend_always{}; }
+    auto final_suspend() noexcept { return std::suspend_always{}; }
+    auto get_return_object() { return task6{}; }
+    void unhandled_exception() {}
+    void return_value(int) {}
+    static task6 get_return_object_on_allocation_failure() { return task6{}; }
+  };
+};
+
+task6 f5() { // expected-error 1+{{unable to find '::operator new(size_t, align_val_t, nothrow_t)' for 'f5'}}
+    co_return 43;
+}
+
+void *operator new(std::size_t, std::align_val_t, std::nothrow_t) noexcept; 
+
+task6 f6() {
+    co_return 43;
+}


        


More information about the cfe-commits mailing list