ychen created this revision.
ychen added reviewers: rjmccall, EricWF, GorNishanov.
Herald added subscribers: ChuanqiXu, dexonsmith, lxfind, dang.
ychen requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Default off.

The allocation/deallocation function overload resolutions are following
the `Issue #4` of 
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2014r0.pdf.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D102147

Files:
  clang/docs/ClangCommandLineReference.rst
  clang/include/clang/Basic/Builtins.def
  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/Driver/ToolChains/Clang.cpp
  clang/lib/Sema/SemaCoroutine.cpp
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/lib/Sema/SemaExprCXX.cpp
  clang/test/CodeGenCoroutines/coro-aligned-alloc.cpp

Index: clang/test/CodeGenCoroutines/coro-aligned-alloc.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenCoroutines/coro-aligned-alloc.cpp
@@ -0,0 +1,199 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -fcoroutines-aligned-alloc -std=c++17 \
+// RUN:    -Wno-coroutine-missing-unhandled-exception -emit-llvm %s -o - -disable-llvm-passes \
+// RUN:   | FileCheck %s
+
+namespace std {
+namespace experimental {
+template <typename... T>
+struct coroutine_traits; // expected-note {{declared here}}
+
+template <class Promise = void>
+struct coroutine_handle {
+  coroutine_handle() = default;
+  static coroutine_handle from_address(void *) noexcept { return {}; }
+};
+
+template <>
+struct coroutine_handle<void> {
+  static coroutine_handle from_address(void *) { return {}; }
+  coroutine_handle() = default;
+  template <class PromiseType>
+  coroutine_handle(coroutine_handle<PromiseType>) noexcept {}
+};
+
+} // end namespace experimental
+
+using size_t = decltype(sizeof(0));
+enum class align_val_t : size_t;
+
+} // end namespace std
+
+struct suspend_always {
+  bool await_ready() noexcept { return false; }
+  void await_suspend(std::experimental::coroutine_handle<>) noexcept {}
+  void await_resume() noexcept {}
+};
+
+struct global_new_delete_tag {};
+
+template<>
+struct std::experimental::coroutine_traits<void, global_new_delete_tag> {
+  struct promise_type {
+    void get_return_object() {}
+    suspend_always initial_suspend() { return {}; }
+    suspend_always final_suspend() noexcept { return {}; }
+    void return_void() {}
+  };
+};
+
+// If class scope allocator/deallocator are not defined, use the default global
+// ones.
+// CHECK-LABEL: f0(
+extern "C" void f0(global_new_delete_tag) {
+  // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16
+  // CHECK: %[[NeedAlloc:.+]] = call i1 @llvm.coro.alloc(token %[[ID]])
+  // CHECK: br i1 %[[NeedAlloc]], label %[[CheckAlignBB:.+]], label %[[InitBB:.+]]
+
+  // CHECK: [[CheckAlignBB]]:
+  // CHECK: %[[ALIGN:.+]] = call i64 @llvm.coro.align.i64()
+  // CHECK: %[[CMP:.+]] = icmp ugt i64 %[[ALIGN]], 16
+  // CHECK: br i1 %[[CMP]], label %[[AlignAllocBB:.+]], label %[[AllocBB:.+]]
+
+  // CHECK: [[AllocBB]]:
+  // CHECK-NEXT: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
+  // CHECK-NEXT: %[[MEM:.+]] = call noalias nonnull i8* @_Znwm(i64 %[[SIZE]])
+  // CHECK-NEXT: br label %[[InitBB:.+]]
+
+  // CHECK: [[AlignAllocBB]]:
+  // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
+  // CHECK: %[[ALIGN:.+]] = call i64 @llvm.coro.align.i64()
+  // CHECK: %[[MEM2:.+]] = call noalias nonnull i8* @_ZnwmSt11align_val_t(i64 %[[SIZE]], i64 %[[ALIGN]])
+  // CHECK: call void @llvm.assume(i1 true) [ "align"(i8* %[[MEM2]], i64 %[[ALIGN]]) ]
+  // CHECK: br label %[[InitBB]]
+
+  // CHECK: [[InitBB]]:
+  // CHECK: %[[PHI:.+]] = phi i8* [ null, %{{.+}} ], [ %[[MEM]], %[[AllocBB]] ], [ %[[MEM2]], %[[AlignAllocBB]] ]
+  // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(token %[[ID]], i8* %[[PHI]])
+
+  // CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]])
+  // CHECK: %[[NeedDealloc:.+]] = icmp ne i8* %[[MEM]], null
+  // CHECK: br i1 %[[NeedDealloc]], label %[[CheckAlignBB:.+]], label %[[Afterwards:.+]]
+
+  // CHECK: [[CheckAlignBB]]:
+  // CHECK: %[[ALIGN:.+]] = call i64 @llvm.coro.align.i64()
+  // CHECK: %[[CMP:.+]] = icmp ugt i64 %[[ALIGN]], 16
+  // CHECK: br i1 %[[CMP]], label %[[AlignedFreeBB:.+]], label %[[FreeBB:.+]]
+
+  // CHECK: [[FreeBB]]:
+  // CHECK-NEXT: call void @_ZdlPv(i8* %[[MEM]])
+  // CHECK-NEXT: br label %[[Afterwards]]
+
+  // CHECK: [[AlignedFreeBB]]:
+  // CHECK-NEXT: %[[ALIGN:.+]] = call i64 @llvm.coro.align.i64()
+  // CHECK-NEXT: call void @_ZdlPvSt11align_val_t(i8* %[[MEM]], i64 %[[ALIGN]])
+  // CHECK-NEXT: br label %[[Afterwards]]
+
+  // CHECK: [[Afterwards]]:
+  // CHECK: ret void
+  co_return;
+}
+
+struct promise_new_delete_tag {};
+
+template<>
+struct std::experimental::coroutine_traits<void, promise_new_delete_tag> {
+  struct promise_type {
+    void *operator new(std::size_t);
+    void operator delete(void *);
+    void operator delete(void *, std::align_val_t);
+    void get_return_object() {}
+    suspend_always initial_suspend() { return {}; }
+    suspend_always final_suspend() noexcept { return {}; }
+    void return_void() {}
+  };
+};
+
+// - If class-specific allocator/deallocator have precedence over global
+//   allocator/deallocator.
+// - Aligned deallocator is preferred over non-aligned deallocator for
+//   overaligned coroutine frame; Non-aligned deallocator is preferred over
+//   aligned deallocator for non-overaligned coroutine frame.
+// - Dynamically adjust alignment for alloc and dealloc if the selected
+//   allocator do not have std::align_val_t argument.
+// CHECK-LABEL: f1(
+extern "C" void f1(promise_new_delete_tag) {
+  // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16
+  // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
+  // CHECK: call i8* @_ZNSt12experimental16coroutine_traitsIJv22promise_new_delete_tagEE12promise_typenwEm(i64 %[[SIZE]])
+  // CHECK: call i64 @llvm.coro.align.i64()
+  // CHECK: call i8* @_ZNSt12experimental16coroutine_traitsIJv22promise_new_delete_tagEE12promise_typenwEm(i64
+  // CHECK: call i32 @llvm.coro.raw.frame.ptr.offset.i32()
+
+  // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(
+  // CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]])
+  // CHECK: %[[NeedDealloc:.+]] = icmp ne i8* %[[MEM]], null
+  // CHECK: br i1 %[[NeedDealloc]], label %[[CheckAlignBB:.+]], label %[[Afterwards:.+]]
+
+  // CHECK: [[CheckAlignBB]]:
+  // CHECK: %[[ALIGN:.+]] = call i64 @llvm.coro.align.i64()
+  // CHECK: %[[CMP:.+]] = icmp ugt i64 %[[ALIGN]], 16
+  // CHECK: br i1 %[[CMP]], label %[[AlignedFreeBB:.+]], label %[[FreeBB:.+]]
+
+  // CHECK: [[FreeBB]]:
+  // CHECK-NEXT: call void @_ZNSt12experimental16coroutine_traitsIJv22promise_new_delete_tagEE12promise_typedlEPv(i8* %[[MEM]])
+  // CHECK-NEXT: br label %[[Afterwards]]
+
+  // CHECK: [[AlignedFreeBB]]:
+  // CHECK-NEXT: %[[OFFSET:.+]] = call i32 @llvm.coro.raw.frame.ptr.offset.i32()
+  // CHECK-NEXT: %[[ADDR:.+]] = getelementptr inbounds i8, i8* %[[MEM]], i32 %[[OFFSET]]
+  // CHECK-NEXT: %[[ADDR2:.+]] = bitcast i8* %[[ADDR]] to i8**
+  // CHECK-NEXT: %[[MEM:.+]] = load i8*, i8** %[[ADDR2]], align 8
+  // CHECK-NEXT: %[[ALIGN:.+]] = call i64 @llvm.coro.align.i64()
+  // CHECK-NEXT: call void @_ZNSt12experimental16coroutine_traitsIJv22promise_new_delete_tagEE12promise_typedlEPvSt11align_val_t(i8* %[[MEM]], i64 %[[ALIGN]])
+  // CHECK-NEXT: br label %[[Afterwards]]
+
+  // CHECK: [[Afterwards]]:
+  // CHECK: ret void
+  co_return;
+}
+
+struct promise_matching_placement_new_tag {};
+
+template <>
+struct std::experimental::coroutine_traits<void, promise_matching_placement_new_tag> {
+  struct promise_type {
+    void *operator new(std::size_t);
+    void *operator new(std::size_t, promise_matching_placement_new_tag,
+                       int, float, double);
+    void *operator new(std::size_t, std::align_val_t);
+    void *operator new(std::size_t, std::align_val_t,
+                       promise_matching_placement_new_tag,
+                       int, float, double);
+
+    void operator delete(void *, std::size_t);
+    void operator delete(void *, std::size_t, std::align_val_t);
+    void operator delete(void *);
+    void operator delete(void *, std::align_val_t);
+    void get_return_object() {}
+    suspend_always initial_suspend() { return {}; }
+    suspend_always final_suspend() noexcept { return {}; }
+    void return_void() {}
+  };
+};
+
+// Class-specific placement allocators have precedence over class-specific
+// allocators.
+// Sized class-specific deallocators have precedence over non-sized
+// class-specific deallocators.
+// CHECK-LABEL: f2(
+extern "C" void f2(promise_matching_placement_new_tag) {
+  // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16
+  // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
+  // CHECK: call i8* @_ZNSt12experimental16coroutine_traitsIJv34promise_matching_placement_new_tagEE12promise_typenwEm(i64 %[[SIZE]])
+  // CHECK: call i64 @llvm.coro.align.i64()
+  // CHECK: call i8* @_ZNSt12experimental16coroutine_traitsIJv34promise_matching_placement_new_tagEE12promise_typenwEmSt11align_val_t(i64
+
+  // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJv34promise_matching_placement_new_tagEE12promise_typedlEPvm(i8*
+  // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJv34promise_matching_placement_new_tagEE12promise_typedlEPvmSt11align_val_t(i8*
+  co_return;
+}
Index: clang/lib/Sema/SemaExprCXX.cpp
===================================================================
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -1577,7 +1577,7 @@
 
 /// Determine whether the given function is a non-placement
 /// deallocation function.
-static bool isNonPlacementDeallocationFunction(Sema &S, FunctionDecl *FD) {
+bool Sema::isNonPlacementDeallocationFunction(Sema &S, FunctionDecl *FD) {
   if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FD))
     return S.isUsualDeallocationFunction(Method);
 
@@ -1602,71 +1602,61 @@
   return UsualParams == FD->getNumParams();
 }
 
-namespace {
-  struct UsualDeallocFnInfo {
-    UsualDeallocFnInfo() : Found(), FD(nullptr) {}
-    UsualDeallocFnInfo(Sema &S, DeclAccessPair Found)
-        : Found(Found), FD(dyn_cast<FunctionDecl>(Found->getUnderlyingDecl())),
-          Destroying(false), HasSizeT(false), HasAlignValT(false),
-          CUDAPref(Sema::CFP_Native) {
-      // A function template declaration is never a usual deallocation function.
-      if (!FD)
-        return;
-      unsigned NumBaseParams = 1;
-      if (FD->isDestroyingOperatorDelete()) {
-        Destroying = true;
-        ++NumBaseParams;
-      }
-
-      if (NumBaseParams < FD->getNumParams() &&
-          S.Context.hasSameUnqualifiedType(
-              FD->getParamDecl(NumBaseParams)->getType(),
-              S.Context.getSizeType())) {
-        ++NumBaseParams;
-        HasSizeT = true;
-      }
-
-      if (NumBaseParams < FD->getNumParams() &&
-          FD->getParamDecl(NumBaseParams)->getType()->isAlignValT()) {
-        ++NumBaseParams;
-        HasAlignValT = true;
-      }
+Sema::UsualDeallocFnInfo::UsualDeallocFnInfo() : Found(), FD(nullptr) {}
+Sema::UsualDeallocFnInfo::UsualDeallocFnInfo(Sema &S, DeclAccessPair Found)
+    : Found(Found), FD(dyn_cast<FunctionDecl>(Found->getUnderlyingDecl())),
+      Destroying(false), HasSizeT(false), HasAlignValT(false),
+      CUDAPref(Sema::CFP_Native) {
+  // A function template declaration is never a usual deallocation function.
+  if (!FD)
+    return;
+  unsigned NumBaseParams = 1;
+  if (FD->isDestroyingOperatorDelete()) {
+    Destroying = true;
+    ++NumBaseParams;
+  }
 
-      // In CUDA, determine how much we'd like / dislike to call this.
-      if (S.getLangOpts().CUDA)
-        if (auto *Caller = dyn_cast<FunctionDecl>(S.CurContext))
-          CUDAPref = S.IdentifyCUDAPreference(Caller, FD);
-    }
+  if (NumBaseParams < FD->getNumParams() &&
+      S.Context.hasSameUnqualifiedType(
+          FD->getParamDecl(NumBaseParams)->getType(),
+          S.Context.getSizeType())) {
+    ++NumBaseParams;
+    HasSizeT = true;
+  }
 
-    explicit operator bool() const { return FD; }
+  if (NumBaseParams < FD->getNumParams() &&
+      FD->getParamDecl(NumBaseParams)->getType()->isAlignValT()) {
+    ++NumBaseParams;
+    HasAlignValT = true;
+  }
 
-    bool isBetterThan(const UsualDeallocFnInfo &Other, bool WantSize,
-                      bool WantAlign) const {
-      // C++ P0722:
-      //   A destroying operator delete is preferred over a non-destroying
-      //   operator delete.
-      if (Destroying != Other.Destroying)
-        return Destroying;
+  // In CUDA, determine how much we'd like / dislike to call this.
+  if (S.getLangOpts().CUDA)
+    if (auto *Caller = dyn_cast<FunctionDecl>(S.CurContext))
+      CUDAPref = S.IdentifyCUDAPreference(Caller, FD);
+}
 
-      // C++17 [expr.delete]p10:
-      //   If the type has new-extended alignment, a function with a parameter
-      //   of type std::align_val_t is preferred; otherwise a function without
-      //   such a parameter is preferred
-      if (HasAlignValT != Other.HasAlignValT)
-        return HasAlignValT == WantAlign;
+bool Sema::UsualDeallocFnInfo::isBetterThan(const UsualDeallocFnInfo &Other,
+                                            bool WantSize,
+                                            bool WantAlign) const {
+  // C++ P0722:
+  //   A destroying operator delete is preferred over a non-destroying
+  //   operator delete.
+  if (Destroying != Other.Destroying)
+    return Destroying;
 
-      if (HasSizeT != Other.HasSizeT)
-        return HasSizeT == WantSize;
+  // C++17 [expr.delete]p10:
+  //   If the type has new-extended alignment, a function with a parameter
+  //   of type std::align_val_t is preferred; otherwise a function without
+  //   such a parameter is preferred
+  if (HasAlignValT != Other.HasAlignValT)
+    return HasAlignValT == WantAlign;
 
-      // Use CUDA call preference as a tiebreaker.
-      return CUDAPref > Other.CUDAPref;
-    }
+  if (HasSizeT != Other.HasSizeT)
+    return HasSizeT == WantSize;
 
-    DeclAccessPair Found;
-    FunctionDecl *FD;
-    bool Destroying, HasSizeT, HasAlignValT;
-    Sema::CUDAFunctionPreference CUDAPref;
-  };
+  // Use CUDA call preference as a tiebreaker.
+  return CUDAPref > Other.CUDAPref;
 }
 
 /// Determine whether a type has new-extended alignment. This may be called when
@@ -1681,14 +1671,14 @@
 
 /// Select the correct "usual" deallocation function to use from a selection of
 /// deallocation functions (either global or class-scope).
-static UsualDeallocFnInfo resolveDeallocationOverload(
+static Sema::UsualDeallocFnInfo resolveDeallocationOverload(
     Sema &S, LookupResult &R, bool WantSize, bool WantAlign,
-    llvm::SmallVectorImpl<UsualDeallocFnInfo> *BestFns = nullptr) {
-  UsualDeallocFnInfo Best;
+    llvm::SmallVectorImpl<Sema::UsualDeallocFnInfo> *BestFns = nullptr) {
+  Sema::UsualDeallocFnInfo Best;
 
   for (auto I = R.begin(), E = R.end(); I != E; ++I) {
-    UsualDeallocFnInfo Info(S, I.getPair());
-    if (!Info || !isNonPlacementDeallocationFunction(S, Info.FD) ||
+    Sema::UsualDeallocFnInfo Info(S, I.getPair());
+    if (!Info || !Sema::isNonPlacementDeallocationFunction(S, Info.FD) ||
         Info.CUDAPref == Sema::CFP_Never)
       continue;
 
@@ -3075,10 +3065,10 @@
   }
 }
 
-FunctionDecl *Sema::FindUsualDeallocationFunction(SourceLocation StartLoc,
-                                                  bool CanProvideSize,
-                                                  bool Overaligned,
-                                                  DeclarationName Name) {
+Sema::UsualDeallocFnInfo
+Sema::FindUsualDeallocationFunction(SourceLocation StartLoc,
+                                    bool CanProvideSize, bool Overaligned,
+                                    DeclarationName Name) {
   DeclareGlobalNewDelete();
 
   LookupResult FoundDelete(*this, Name, StartLoc, LookupOrdinaryName);
@@ -3091,7 +3081,7 @@
   auto Result = resolveDeallocationOverload(*this, FoundDelete, CanProvideSize,
                                             Overaligned);
   assert(Result.FD && "operator delete missing from global scope?");
-  return Result.FD;
+  return Result;
 }
 
 FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc,
@@ -3107,13 +3097,16 @@
   // If there's no class-specific operator delete, look up the global
   // non-array delete.
   return FindUsualDeallocationFunction(
-      Loc, true, hasNewExtendedAlignment(*this, Context.getRecordType(RD)),
-      Name);
+             Loc, true,
+             hasNewExtendedAlignment(*this, Context.getRecordType(RD)), Name)
+      .FD;
 }
 
 bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
                                     DeclarationName Name,
-                                    FunctionDecl *&Operator, bool Diagnose) {
+                                    FunctionDecl *&Operator,
+                                    const bool *WantSize, const bool *WantAlign,
+                                    bool Diagnose) {
   LookupResult Found(*this, Name, StartLoc, LookupOrdinaryName);
   // Try to find operator delete/operator delete[] in class scope.
   LookupQualifiedName(Found, RD);
@@ -3123,13 +3116,16 @@
 
   Found.suppressDiagnostics();
 
-  bool Overaligned = hasNewExtendedAlignment(*this, Context.getRecordType(RD));
+  bool Overaligned =
+      WantAlign ? *WantAlign
+                : hasNewExtendedAlignment(*this, Context.getRecordType(RD));
 
   // C++17 [expr.delete]p10:
   //   If the deallocation functions have class scope, the one without a
   //   parameter of type std::size_t is selected.
   llvm::SmallVector<UsualDeallocFnInfo, 4> Matches;
-  resolveDeallocationOverload(*this, Found, /*WantSize*/ false,
+  resolveDeallocationOverload(*this, Found,
+                              /*WantSize*/ WantSize ? *WantSize : false,
                               /*WantAlign*/ Overaligned, &Matches);
 
   // If we could find an overload, use it.
@@ -3621,7 +3617,8 @@
 
       // Look for a global declaration.
       OperatorDelete = FindUsualDeallocationFunction(StartLoc, CanProvideSize,
-                                                     Overaligned, DeleteName);
+                                                     Overaligned, DeleteName)
+                           .FD;
     }
 
     MarkFunctionReferenced(StartLoc, OperatorDelete);
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -9210,9 +9210,11 @@
   if (CSM == CXXDestructor && MD->isVirtual()) {
     FunctionDecl *OperatorDelete = nullptr;
     DeclarationName Name =
-      Context.DeclarationNames.getCXXOperatorName(OO_Delete);
+        Context.DeclarationNames.getCXXOperatorName(OO_Delete);
     if (FindDeallocationFunction(MD->getLocation(), MD->getParent(), Name,
-                                 OperatorDelete, /*Diagnose*/false)) {
+                                 OperatorDelete, /*WantSize*/ nullptr,
+                                 /*WantAlign*/ nullptr,
+                                 /*Diagnose*/ false)) {
       if (Diagnose)
         Diag(RD->getLocation(), diag::note_deleted_dtor_no_operator_delete);
       return true;
Index: clang/lib/Sema/SemaCoroutine.cpp
===================================================================
--- clang/lib/Sema/SemaCoroutine.cpp
+++ clang/lib/Sema/SemaCoroutine.cpp
@@ -25,6 +25,7 @@
 #include "clang/Sema/ScopeInfo.h"
 #include "clang/Sema/SemaInternal.h"
 #include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallVector.h"
 
 using namespace clang;
 using namespace sema;
@@ -1060,30 +1061,71 @@
 }
 
 // Find an appropriate delete for the promise.
-static FunctionDecl *findDeleteForPromise(Sema &S, SourceLocation Loc,
-                                          QualType PromiseType) {
-  FunctionDecl *OperatorDelete = nullptr;
-
+static void
+findDeleteForPromise(Sema &S, SourceLocation Loc, QualType PromiseType,
+                     bool UseAlignedAlloc,
+                     Sema::UsualDeallocFnInfo &OperatorDelete,
+                     Sema::UsualDeallocFnInfo &AlignedOperatorDelete) {
   DeclarationName DeleteName =
       S.Context.DeclarationNames.getCXXOperatorName(OO_Delete);
 
+  // Try to find operator delete in class scope.
+
   auto *PointeeRD = PromiseType->getAsCXXRecordDecl();
   assert(PointeeRD && "PromiseType must be a CxxRecordDecl type");
+  LookupResult Found(S, DeleteName, Loc, S.LookupOrdinaryName);
+  S.LookupQualifiedName(Found, PointeeRD);
+  if (Found.isAmbiguous())
+    return;
 
-  if (S.FindDeallocationFunction(Loc, PointeeRD, DeleteName, OperatorDelete))
-    return nullptr;
+  Found.suppressDiagnostics();
+
+  for (auto I = Found.begin(), E = Found.end(); I != E; ++I) {
+    Sema::UsualDeallocFnInfo Info(S, I.getPair());
+    if (!Info || !Sema::isNonPlacementDeallocationFunction(S, Info.FD) ||
+        Info.CUDAPref == Sema::CFP_Never || Info.Destroying)
+      continue;
+
+    if (!OperatorDelete) {
+      OperatorDelete = Info;
+      if (UseAlignedAlloc)
+        AlignedOperatorDelete = Info;
+      continue;
+    }
+
+    if (Info.isBetterThan(OperatorDelete, /*WantSize*/ true,
+                          /*WantAlign*/ false))
+      OperatorDelete = Info;
+
+    if (UseAlignedAlloc &&
+        Info.isBetterThan(AlignedOperatorDelete, /*WantSize*/ true,
+                          /*WantAlign*/ true))
+      AlignedOperatorDelete = Info;
+  }
+
+  const bool CanProvideSize = S.isCompleteType(Loc, PromiseType);
 
   if (!OperatorDelete) {
     // Look for a global declaration.
-    const bool CanProvideSize = S.isCompleteType(Loc, PromiseType);
     const bool Overaligned = false;
     OperatorDelete = S.FindUsualDeallocationFunction(Loc, CanProvideSize,
                                                      Overaligned, DeleteName);
   }
-  S.MarkFunctionReferenced(Loc, OperatorDelete);
-  return OperatorDelete;
-}
+  S.MarkFunctionReferenced(Loc, OperatorDelete.FD);
 
+  if (!UseAlignedAlloc) {
+    AlignedOperatorDelete = OperatorDelete;
+    return;
+  }
+
+  if (!AlignedOperatorDelete) {
+    // Look for a global declaration.
+    const bool Overaligned = true;
+    AlignedOperatorDelete = S.FindUsualDeallocationFunction(
+        Loc, CanProvideSize, Overaligned, DeleteName);
+  }
+  S.MarkFunctionReferenced(Loc, AlignedOperatorDelete.FD);
+}
 
 void Sema::CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body) {
   FunctionScopeInfo *Fn = getCurFunction();
@@ -1254,22 +1296,21 @@
   if (S.RequireCompleteType(Loc, PromiseType, diag::err_incomplete_type))
     return false;
 
-  const bool RequiresNoThrowAlloc = ReturnStmtOnAllocFailure != nullptr;
-
-  // [dcl.fct.def.coroutine]/7
+  // [dcl.fct.def.coroutine]/9
   // Lookup allocation functions using a parameter list composed of the
   // requested size of the coroutine state being allocated, followed by
   // the coroutine function's arguments. If a matching allocation function
   // exists, use it. Otherwise, use an allocation function that just takes
   // the requested size.
 
-  FunctionDecl *OperatorNew = nullptr;
-  FunctionDecl *OperatorDelete = nullptr;
   FunctionDecl *UnusedResult = nullptr;
   bool PassAlignment = false;
+  FunctionDecl *OperatorNew = nullptr;
+  FunctionDecl *AlignedOperatorNew = nullptr;
   SmallVector<Expr *, 1> PlacementArgs;
+  SmallVector<Expr *, 1> AlignedPlacementArgs;
 
-  // [dcl.fct.def.coroutine]/7
+  // [dcl.fct.def.coroutine]/9
   // "The allocation function’s name is looked up in the scope of P.
   // [...] If the lookup finds an allocation function in the scope of P,
   // overload resolution is performed on a function call created by assembling
@@ -1309,123 +1350,230 @@
 
     PlacementArgs.push_back(PDRefExpr.get());
   }
+
+  // Tentatively implement P2014R0 - "Proposed resolution for US061+US062 -
+  // aligned allocation of coroutine frames" Option 1.
+  const bool UseAlignedAlloc = S.getLangOpts().AlignedAllocation &&
+                               S.getLangOpts().CoroutinesAlignedAlloc;
+
+  if (UseAlignedAlloc) {
+    AlignedPlacementArgs = PlacementArgs;
+    PassAlignment = true;
+    S.FindAllocationFunctions(Loc, SourceRange(), /*NewScope*/ Sema::AFS_Class,
+                              /*DeleteScope*/ Sema::AFS_Both, PromiseType,
+                              /*isArray*/ false, PassAlignment,
+                              AlignedPlacementArgs, AlignedOperatorNew,
+                              UnusedResult, /*Diagnose*/ false);
+  }
+
+  // [dcl.fct.def.coroutine]/9
+  // "If no matching function is found, 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 (UseAlignedAlloc && !AlignedOperatorNew && !AlignedPlacementArgs.empty()) {
+    AlignedPlacementArgs.clear();
+    PassAlignment = true;
+    S.FindAllocationFunctions(Loc, SourceRange(), /*NewScope*/ Sema::AFS_Class,
+                              /*DeleteScope*/ Sema::AFS_Both, PromiseType,
+                              /*isArray*/ false, PassAlignment,
+                              AlignedPlacementArgs, AlignedOperatorNew,
+                              UnusedResult, /*Diagnose*/ false);
+  }
+
+  PassAlignment = false;
   S.FindAllocationFunctions(Loc, SourceRange(), /*NewScope*/ Sema::AFS_Class,
                             /*DeleteScope*/ Sema::AFS_Both, PromiseType,
                             /*isArray*/ false, PassAlignment, PlacementArgs,
                             OperatorNew, UnusedResult, /*Diagnose*/ false);
 
-  // [dcl.fct.def.coroutine]/7
-  // "If no matching function is found, 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()) {
     PlacementArgs.clear();
+    PassAlignment = false;
     S.FindAllocationFunctions(Loc, SourceRange(), /*NewScope*/ Sema::AFS_Class,
                               /*DeleteScope*/ Sema::AFS_Both, PromiseType,
                               /*isArray*/ false, PassAlignment, PlacementArgs,
                               OperatorNew, UnusedResult, /*Diagnose*/ false);
   }
 
-  // [dcl.fct.def.coroutine]/7
+  if (UseAlignedAlloc && !AlignedOperatorNew && OperatorNew) {
+    AlignedOperatorNew = OperatorNew;
+    AlignedPlacementArgs = PlacementArgs;
+  }
+
+  // [dcl.fct.def.coroutine]/9
   // "The allocation function’s name is looked up in the scope of P. If this
   // lookup fails, the allocation function’s name is looked up in the global
   // scope."
+  if (UseAlignedAlloc && !AlignedOperatorNew) {
+    PassAlignment = true;
+    S.FindAllocationFunctions(Loc, SourceRange(), /*NewScope*/ Sema::AFS_Global,
+                              /*DeleteScope*/ Sema::AFS_Both, PromiseType,
+                              /*isArray*/ false, PassAlignment,
+                              AlignedPlacementArgs, AlignedOperatorNew,
+                              UnusedResult);
+  }
+
   if (!OperatorNew) {
+    PassAlignment = false;
     S.FindAllocationFunctions(Loc, SourceRange(), /*NewScope*/ Sema::AFS_Global,
                               /*DeleteScope*/ Sema::AFS_Both, PromiseType,
                               /*isArray*/ false, PassAlignment, PlacementArgs,
                               OperatorNew, UnusedResult);
   }
 
-  bool IsGlobalOverload =
-      OperatorNew && !isa<CXXRecordDecl>(OperatorNew->getDeclContext());
-  // If we didn't find a class-local new declaration and non-throwing new
-  // was is required then we need to lookup the non-throwing global operator
-  // instead.
-  if (RequiresNoThrowAlloc && (!OperatorNew || IsGlobalOverload)) {
+  assert(OperatorNew);
+  assert(!UseAlignedAlloc || AlignedOperatorNew);
+
+  // [dcl.fct.def.coroutine]/10
+  // "The unqualified-id get_return_object_on_allocation_failure is looked up in
+  // the scope of class P by class member access lookup. If a declaration is
+  // found, then the result of a call to an allocation function used to obtain
+  // storage for the coroutine state is assumed to return nullptr if it fails to
+  // obtain storage, and if a global allocation function is selected, the
+  // ::operator new(size_t, nothrow_t) form shall be used."
+  auto FindNoThrowAlloc = [&](FunctionDecl *&Operator,
+                              SmallVector<Expr *, 1> &PlaceArgs,
+                              bool PassAlignment) {
+    // Check if the operator is globally overloaded.
+    if (isa<CXXRecordDecl>(Operator->getDeclContext()))
+      return true;
+
+    // If we didn't find a class-local new declaration and non-throwing new
+    // was is required then we need to lookup the non-throwing global operator
+    // instead.
     auto *StdNoThrow = buildStdNoThrowDeclRef(S, Loc);
     if (!StdNoThrow)
       return false;
-    PlacementArgs = {StdNoThrow};
-    OperatorNew = nullptr;
+    PlaceArgs = {StdNoThrow};
+    Operator = nullptr;
+
     S.FindAllocationFunctions(Loc, SourceRange(), /*NewScope*/ Sema::AFS_Both,
                               /*DeleteScope*/ Sema::AFS_Both, PromiseType,
-                              /*isArray*/ false, PassAlignment, PlacementArgs,
-                              OperatorNew, UnusedResult);
-  }
+                              /*isArray*/ false, PassAlignment, PlaceArgs,
+                              Operator, UnusedResult);
+    return true;
+  };
 
-  if (!OperatorNew)
-    return false;
+  if (ReturnStmtOnAllocFailure) {
+    if (!FindNoThrowAlloc(OperatorNew, PlacementArgs, false))
+      return false;
+    if (UseAlignedAlloc &&
+        !FindNoThrowAlloc(AlignedOperatorNew, AlignedPlacementArgs, true))
+      return false;
 
-  if (RequiresNoThrowAlloc) {
-    const auto *FT = OperatorNew->getType()->castAs<FunctionProtoType>();
-    if (!FT->isNothrow(/*ResultIfDependent*/ false)) {
-      S.Diag(OperatorNew->getLocation(),
+    auto DiagNoThrow = [&](FunctionDecl *Operator) {
+      if (!Operator)
+        return true;
+      const auto *FT = Operator->getType()->castAs<FunctionProtoType>();
+      if (FT->isNothrow(/*ResultIfDependent*/ false))
+        return true;
+      S.Diag(Operator->getLocation(),
              diag::err_coroutine_promise_new_requires_nothrow)
-          << OperatorNew;
+          << Operator;
       S.Diag(Loc, diag::note_coroutine_promise_call_implicitly_required)
-          << OperatorNew;
+          << Operator;
+      return false;
+    };
+
+    bool NoThrow = DiagNoThrow(OperatorNew);
+    NoThrow = DiagNoThrow(AlignedOperatorNew) || NoThrow;
+    if (!NoThrow)
       return false;
-    }
   }
 
-  if ((OperatorDelete = findDeleteForPromise(S, Loc, PromiseType)) == nullptr)
-    return false;
+  Sema::UsualDeallocFnInfo OperatorDelete;
+  Sema::UsualDeallocFnInfo AlignedOperatorDelete;
+  findDeleteForPromise(S, Loc, PromiseType, UseAlignedAlloc, OperatorDelete,
+                       AlignedOperatorDelete);
 
-  Expr *FramePtr =
-      buildBuiltinCall(S, Loc, Builtin::BI__builtin_coro_frame, {});
+  // Make new call.
 
   Expr *FrameSize =
       buildBuiltinCall(S, Loc, Builtin::BI__builtin_coro_size, {});
+  Expr *FrameAlign =
+      buildBuiltinCall(S, Loc, Builtin::BI__builtin_coro_align, {});
+
+  // Cast __builtin_coro_align return value to std::align_val_t.
+  if (UseAlignedAlloc) {
+    QualType AlignValT = S.Context.getTypeDeclType(S.getStdAlignValT());
+    FrameAlign =
+        S.BuildCStyleCastExpr(FrameAlign->getExprLoc(),
+                              S.Context.getTrivialTypeSourceInfo(AlignValT),
+                              FrameAlign->getExprLoc(), FrameAlign)
+            .get();
+  }
 
-  // Make new call.
+  auto MakeNewCall = [&](FunctionDecl *Operator,
+                         SmallVector<Expr *, 1> &PlaceArgs, Expr *&Allocate) {
+    ExprResult NewRef =
+        S.BuildDeclRefExpr(Operator, Operator->getType(), VK_LValue, Loc);
+    if (NewRef.isInvalid())
+      return false;
 
-  ExprResult NewRef =
-      S.BuildDeclRefExpr(OperatorNew, OperatorNew->getType(), VK_LValue, Loc);
-  if (NewRef.isInvalid())
-    return false;
+    SmallVector<Expr *, 2> NewArgs(1, FrameSize);
+    if (Operator->getNumParams() > 1 &&
+        Operator->getParamDecl(1)->getType()->isAlignValT())
+      NewArgs.insert(NewArgs.begin() + 1, FrameAlign);
+    NewArgs.append(PlaceArgs);
 
-  SmallVector<Expr *, 2> NewArgs(1, FrameSize);
-  for (auto Arg : PlacementArgs)
-    NewArgs.push_back(Arg);
+    ExprResult NewExpr =
+        S.BuildCallExpr(S.getCurScope(), NewRef.get(), Loc, NewArgs, Loc);
+    NewExpr = S.ActOnFinishFullExpr(NewExpr.get(), /*DiscardedValue*/ false);
+    if (NewExpr.isInvalid())
+      return false;
+    Allocate = NewExpr.get();
+    return true;
+  };
 
-  ExprResult NewExpr =
-      S.BuildCallExpr(S.getCurScope(), NewRef.get(), Loc, NewArgs, Loc);
-  NewExpr = S.ActOnFinishFullExpr(NewExpr.get(), /*DiscardedValue*/ false);
-  if (NewExpr.isInvalid())
+  if (!MakeNewCall(OperatorNew, PlacementArgs, this->Allocate))
     return false;
 
-  // Make delete call.
-
-  QualType OpDeleteQualType = OperatorDelete->getType();
+  if (UseAlignedAlloc) {
+    if (!MakeNewCall(AlignedOperatorNew, AlignedPlacementArgs,
+                     this->AlignedAllocate))
+      return false;
+  } else {
+    this->AlignedAllocate = this->Allocate;
+  }
 
-  ExprResult DeleteRef =
-      S.BuildDeclRefExpr(OperatorDelete, OpDeleteQualType, VK_LValue, Loc);
-  if (DeleteRef.isInvalid())
-    return false;
+  // Make delete call.
 
+  Expr *FramePtr =
+      buildBuiltinCall(S, Loc, Builtin::BI__builtin_coro_frame, {});
   Expr *CoroFree =
       buildBuiltinCall(S, Loc, Builtin::BI__builtin_coro_free, {FramePtr});
 
-  SmallVector<Expr *, 2> DeleteArgs{CoroFree};
+  auto MakeDeleteCall = [&](const Sema::UsualDeallocFnInfo &Operator,
+                            Expr *&Deallocate) {
+    ExprResult DeleteRef =
+        S.BuildDeclRefExpr(Operator.FD, Operator.FD->getType(), VK_LValue, Loc);
+    if (DeleteRef.isInvalid())
+      return false;
 
-  // Check if we need to pass the size.
-  const auto *OpDeleteType =
-      OpDeleteQualType.getTypePtr()->castAs<FunctionProtoType>();
-  if (OpDeleteType->getNumParams() > 1)
-    DeleteArgs.push_back(FrameSize);
+    SmallVector<Expr *, 2> DeleteArgs{CoroFree};
+    if (Operator.HasSizeT)
+      DeleteArgs.push_back(FrameSize);
+    if (Operator.HasAlignValT)
+      DeleteArgs.push_back(FrameAlign);
+
+    ExprResult DeleteExpr =
+        S.BuildCallExpr(S.getCurScope(), DeleteRef.get(), Loc, DeleteArgs, Loc);
+    DeleteExpr =
+        S.ActOnFinishFullExpr(DeleteExpr.get(), /*DiscardedValue*/ false);
+    if (DeleteExpr.isInvalid())
+      return false;
+    Deallocate = DeleteExpr.get();
+    return true;
+  };
 
-  ExprResult DeleteExpr =
-      S.BuildCallExpr(S.getCurScope(), DeleteRef.get(), Loc, DeleteArgs, Loc);
-  DeleteExpr =
-      S.ActOnFinishFullExpr(DeleteExpr.get(), /*DiscardedValue*/ false);
-  if (DeleteExpr.isInvalid())
+  if (!MakeDeleteCall(OperatorDelete, this->Deallocate))
     return false;
-
-  this->Allocate = NewExpr.get();
-  this->AlignedAllocate = this->Allocate;
-  this->Deallocate = DeleteExpr.get();
-  this->AlignedDeallocate = this->Deallocate;
+  if (UseAlignedAlloc) {
+    if (!MakeDeleteCall(AlignedOperatorDelete, this->AlignedDeallocate))
+      return false;
+  } else {
+    this->AlignedDeallocate = this->Deallocate;
+  }
 
   return true;
 }
Index: clang/lib/Driver/ToolChains/Clang.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Clang.cpp
+++ clang/lib/Driver/ToolChains/Clang.cpp
@@ -5895,6 +5895,12 @@
     CmdArgs.push_back("-fcoroutines-ts");
   }
 
+  if (Args.hasFlag(options::OPT_fcoroutines_aligned_alloc,
+                   options::OPT_fno_coroutines_aligned_alloc, false) &&
+      types::isCXX(InputType)) {
+    CmdArgs.push_back("-fcoroutines-aligned-alloc");
+  }
+
   Args.AddLastArg(CmdArgs, options::OPT_fdouble_square_bracket_attributes,
                   options::OPT_fno_double_square_bracket_attributes);
 
Index: clang/lib/CodeGen/CGBuiltin.cpp
===================================================================
--- clang/lib/CodeGen/CGBuiltin.cpp
+++ clang/lib/CodeGen/CGBuiltin.cpp
@@ -4486,6 +4486,14 @@
     return RValue::get(Builder.CreateCall(F));
   }
 
+  case Builtin::BI__builtin_coro_align: {
+    auto &Context = getContext();
+    auto SizeTy = Context.getSizeType();
+    auto *T = Builder.getIntNTy(Context.getTypeSize(SizeTy));
+    Function *F = CGM.getIntrinsic(Intrinsic::coro_align, T);
+    return RValue::get(Builder.CreateCall(F));
+  }
+
   case Builtin::BI__builtin_coro_id:
     return EmitCoroutineIntrinsic(E, Intrinsic::coro_id);
   case Builtin::BI__builtin_coro_promise:
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -6256,13 +6256,42 @@
   void DeclareGlobalAllocationFunction(DeclarationName Name, QualType Return,
                                        ArrayRef<QualType> Params);
 
+  // CUDA function call preference. Must be ordered numerically from
+  // worst to best.
+  enum CUDAFunctionPreference {
+    CFP_Never,      // Invalid caller/callee combination.
+    CFP_WrongSide,  // Calls from host-device to host or device
+                    // function that do not match current compilation
+                    // mode.
+    CFP_HostDevice, // Any calls to host/device functions.
+    CFP_SameSide,   // Calls from host-device to host or device
+                    // function matching current compilation mode.
+    CFP_Native,     // host-to-host or device-to-device calls.
+  };
+
+  struct UsualDeallocFnInfo {
+    UsualDeallocFnInfo();
+    UsualDeallocFnInfo(Sema &S, DeclAccessPair Found);
+    explicit operator bool() const { return FD; }
+    bool isBetterThan(const UsualDeallocFnInfo &Other, bool WantSize,
+                      bool WantAlign) const;
+
+    DeclAccessPair Found;
+    FunctionDecl *FD;
+    bool Destroying, HasSizeT, HasAlignValT;
+    Sema::CUDAFunctionPreference CUDAPref;
+  };
+
+  static bool isNonPlacementDeallocationFunction(Sema &S, FunctionDecl *FD);
   bool FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
-                                DeclarationName Name, FunctionDecl* &Operator,
+                                DeclarationName Name, FunctionDecl *&Operator,
+                                const bool *WantSize = nullptr,
+                                const bool *WantAlign = nullptr,
                                 bool Diagnose = true);
-  FunctionDecl *FindUsualDeallocationFunction(SourceLocation StartLoc,
-                                              bool CanProvideSize,
-                                              bool Overaligned,
-                                              DeclarationName Name);
+  UsualDeallocFnInfo FindUsualDeallocationFunction(SourceLocation StartLoc,
+                                                   bool CanProvideSize,
+                                                   bool Overaligned,
+                                                   DeclarationName Name);
   FunctionDecl *FindDeallocationFunctionForDestructor(SourceLocation StartLoc,
                                                       CXXRecordDecl *RD);
 
@@ -12146,19 +12175,6 @@
 
   static bool isCUDAImplicitHostDeviceFunction(const FunctionDecl *D);
 
-  // CUDA function call preference. Must be ordered numerically from
-  // worst to best.
-  enum CUDAFunctionPreference {
-    CFP_Never,      // Invalid caller/callee combination.
-    CFP_WrongSide,  // Calls from host-device to host or device
-                    // function that do not match current compilation
-                    // mode.
-    CFP_HostDevice, // Any calls to host/device functions.
-    CFP_SameSide,   // Calls from host-device to host or device
-                    // function matching current compilation mode.
-    CFP_Native,     // host-to-host or device-to-device calls.
-  };
-
   /// Identifies relative preference of a given Caller/Callee
   /// combination, based on their host/device attributes.
   /// \param Caller function which needs address of \p Callee.
Index: clang/include/clang/Driver/Options.td
===================================================================
--- clang/include/clang/Driver/Options.td
+++ clang/include/clang/Driver/Options.td
@@ -1105,6 +1105,10 @@
   LangOpts<"Coroutines">, Default<cpp20.KeyPath>,
   PosFlag<SetTrue, [CC1Option], "Enable support for the C++ Coroutines TS">,
   NegFlag<SetFalse>>;
+defm coroutines_aligned_alloc : BoolFOption<"coroutines-aligned-alloc",
+  LangOpts<"CoroutinesAlignedAlloc">, DefaultFalse,
+  PosFlag<SetTrue, [CC1Option], "Enable support for using aligned allocation/deallocation for the C++ Coroutines TS">,
+  NegFlag<SetFalse>>;
 
 def fembed_bitcode_EQ : Joined<["-"], "fembed-bitcode=">,
     Group<f_Group>, Flags<[NoXarchOption, CC1Option, CC1AsOption]>, MetaVarName<"<option>">,
Index: clang/include/clang/Basic/LangOptions.def
===================================================================
--- clang/include/clang/Basic/LangOptions.def
+++ clang/include/clang/Basic/LangOptions.def
@@ -145,6 +145,7 @@
 LANGOPT(NoMathBuiltin     , 1, 0, "disable math builtin functions")
 LANGOPT(GNUAsm            , 1, 1, "GNU-style inline assembly")
 LANGOPT(Coroutines        , 1, 0, "C++20 coroutines")
+LANGOPT(CoroutinesAlignedAlloc, 1, 0, "C++20 coroutines aligned allocation")
 LANGOPT(DllExportInlines  , 1, 1, "dllexported classes dllexport inline methods")
 LANGOPT(RelaxedTemplateTemplateArgs, 1, 0, "C++17 relaxed matching of template template arguments")
 
Index: clang/include/clang/Basic/Builtins.def
===================================================================
--- clang/include/clang/Basic/Builtins.def
+++ clang/include/clang/Basic/Builtins.def
@@ -1583,6 +1583,7 @@
 BUILTIN(__builtin_coro_promise, "v*v*IiIb", "n")
 
 BUILTIN(__builtin_coro_size, "z", "n")
+BUILTIN(__builtin_coro_align, "z", "n")
 BUILTIN(__builtin_coro_frame, "v*", "n")
 BUILTIN(__builtin_coro_noop, "v*", "n")
 BUILTIN(__builtin_coro_free, "v*v*", "n")
Index: clang/docs/ClangCommandLineReference.rst
===================================================================
--- clang/docs/ClangCommandLineReference.rst
+++ clang/docs/ClangCommandLineReference.rst
@@ -1418,6 +1418,10 @@
 
 Enable support for the C++ Coroutines TS
 
+.. option:: -fcoroutines-aligned-alloc, -fno-coroutines-aligned-alloc
+
+Enable support for using aligned allocation/deallocation for the C++ Coroutines TS
+
 .. option:: -fcoverage-mapping, -fno-coverage-mapping
 
 Generate coverage mapping to enable code coverage analysis
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to