ChuanqiXu updated this revision to Diff 460315.
ChuanqiXu marked 6 inline comments as done.
ChuanqiXu added a comment.

Address comments.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D133341/new/

https://reviews.llvm.org/D133341

Files:
  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
  clang/test/CodeGenCoroutines/coro-aligned-alloc-2.cpp
  clang/test/CodeGenCoroutines/coro-aligned-alloc.cpp
  clang/test/SemaCXX/coroutine-alloc-4.cpp

Index: clang/test/SemaCXX/coroutine-alloc-4.cpp
===================================================================
--- /dev/null
+++ 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;
+}
Index: clang/test/CodeGenCoroutines/coro-aligned-alloc.cpp
===================================================================
--- /dev/null
+++ 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;
+}
Index: clang/test/CodeGenCoroutines/coro-aligned-alloc-2.cpp
===================================================================
--- /dev/null
+++ 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;
+}
Index: clang/lib/Sema/SemaExprCXX.cpp
===================================================================
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -3177,7 +3177,7 @@
 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);
@@ -3187,7 +3187,8 @@
 
   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
Index: clang/lib/Sema/SemaCoroutine.cpp
===================================================================
--- clang/lib/Sema/SemaCoroutine.cpp
+++ clang/lib/Sema/SemaCoroutine.cpp
@@ -1030,6 +1030,13 @@
   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 @@
   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 @@
     // 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 @@
   // 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 @@
     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 @@
     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 @@
 
   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 @@
     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 @@
   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 @@
   //   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 =
Index: clang/lib/Driver/ToolChains/Clang.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Clang.cpp
+++ clang/lib/Driver/ToolChains/Clang.cpp
@@ -6417,6 +6417,11 @@
     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);
 
Index: clang/lib/CodeGen/CGCoroutine.cpp
===================================================================
--- clang/lib/CodeGen/CGCoroutine.cpp
+++ clang/lib/CodeGen/CGCoroutine.cpp
@@ -683,6 +683,13 @@
     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.
Index: clang/lib/CodeGen/CGBuiltin.cpp
===================================================================
--- clang/lib/CodeGen/CGBuiltin.cpp
+++ clang/lib/CodeGen/CGBuiltin.cpp
@@ -4675,6 +4675,8 @@
     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:
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -6647,7 +6647,8 @@
 
   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,
Index: clang/include/clang/Driver/Options.td
===================================================================
--- clang/include/clang/Driver/Options.td
+++ clang/include/clang/Driver/Options.td
@@ -1186,6 +1186,11 @@
   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. "
Index: clang/include/clang/Basic/LangOptions.def
===================================================================
--- clang/include/clang/Basic/LangOptions.def
+++ clang/include/clang/Basic/LangOptions.def
@@ -154,6 +154,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(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")
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11273,7 +11273,16 @@
   "'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
 
Index: clang/include/clang/Basic/DiagnosticGroups.td
===================================================================
--- clang/include/clang/Basic/DiagnosticGroups.td
+++ clang/include/clang/Basic/DiagnosticGroups.td
@@ -66,7 +66,10 @@
   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,
Index: clang/include/clang/Basic/Builtins.def
===================================================================
--- clang/include/clang/Basic/Builtins.def
+++ clang/include/clang/Basic/Builtins.def
@@ -1635,6 +1635,7 @@
 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)
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -158,6 +158,18 @@
 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
 -------------------------
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to