ychen updated this revision to Diff 344609.
ychen added a comment.
- rebase
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D102147/new/
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 alignas(32) 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 i8** @llvm.coro.raw.frame.ptr.addr()
+
+ // 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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits