This revision was automatically updated to reflect the committed changes.
Closed by commit rG327141fb1d8c: [C++] [Coroutines] Prefer aligned
(de)allocation for coroutines - (authored by ChuanqiXu).
Repository:
rG LLVM Github Monorepo
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
@@ -3189,7 +3189,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);
@@ -3199,7 +3199,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
@@ -6500,6 +6500,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
@@ -6660,7 +6660,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
@@ -1189,6 +1189,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
@@ -11278,7 +11278,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
@@ -227,6 +227,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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits