forked from OSchip/llvm-project
[C++] [Coroutines] Prefer aligned (de)allocation for coroutines -
implement the option2 of P2014R0 This implements the option2 of https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2014r0.pdf. This also fixes https://github.com/llvm/llvm-project/issues/56671. Although wg21 didn't get consensus for the direction of the problem, we're happy to have some implementation and user experience first. And from issue56671, the option2 should be the pursued one. Reviewed By: ychen Differential Revision: https://reviews.llvm.org/D133341
This commit is contained in:
parent
c932cef32a
commit
327141fb1d
|
@ -227,6 +227,18 @@ Non-comprehensive list of changes in this release
|
|||
New Compiler Flags
|
||||
------------------
|
||||
|
||||
- Implemented `-fcoro-aligned-allocation` flag. This flag implements
|
||||
Option 2 of P2014R0 aligned allocation of coroutine frames
|
||||
(`P2014R0 <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2014r0.pdf>`_).
|
||||
With this flag, the coroutines will try to lookup aligned allocation
|
||||
function all the time. The compiler will emit an error if it fails to
|
||||
find aligned allocation function. So if the user code implemented self
|
||||
defined allocation function for coroutines, the existing code will be
|
||||
broken. A little divergence with P2014R0 is that clang will lookup
|
||||
`::operator new(size_t, std::aligned_val_t, nothrow_t)` if there is
|
||||
`get_return_object_on_allocation_failure`. We feel this is more consistent
|
||||
with the intention.
|
||||
|
||||
Deprecated Compiler Flags
|
||||
-------------------------
|
||||
|
||||
|
|
|
@ -1635,6 +1635,7 @@ LANGBUILTIN(__builtin_coro_done, "bv*", "n", COR_LANG)
|
|||
LANGBUILTIN(__builtin_coro_promise, "v*v*IiIb", "n", COR_LANG)
|
||||
|
||||
LANGBUILTIN(__builtin_coro_size, "z", "n", COR_LANG)
|
||||
LANGBUILTIN(__builtin_coro_align, "z", "n", COR_LANG)
|
||||
LANGBUILTIN(__builtin_coro_frame, "v*", "n", COR_LANG)
|
||||
LANGBUILTIN(__builtin_coro_noop, "v*", "n", COR_LANG)
|
||||
LANGBUILTIN(__builtin_coro_free, "v*v*", "n", COR_LANG)
|
||||
|
|
|
@ -66,7 +66,10 @@ def DeprecatedCoroutine :
|
|||
DiagGroup<"deprecated-coroutine", [DeprecatedExperimentalCoroutine]>;
|
||||
def AlwaysInlineCoroutine :
|
||||
DiagGroup<"always-inline-coroutine">;
|
||||
def Coroutine : DiagGroup<"coroutine", [CoroutineMissingUnhandledException, DeprecatedCoroutine, AlwaysInlineCoroutine]>;
|
||||
def CoroNonAlignedAllocationFunction :
|
||||
DiagGroup<"coro-non-aligned-allocation-funciton">;
|
||||
def Coroutine : DiagGroup<"coroutine", [CoroutineMissingUnhandledException, DeprecatedCoroutine,
|
||||
AlwaysInlineCoroutine, CoroNonAlignedAllocationFunction]>;
|
||||
def ObjCBoolConstantConversion : DiagGroup<"objc-bool-constant-conversion">;
|
||||
def ConstantConversion : DiagGroup<"constant-conversion",
|
||||
[BitFieldConstantConversion,
|
||||
|
|
|
@ -11278,7 +11278,16 @@ def err_coroutine_unusable_new : Error<
|
|||
"'operator new' provided by %0 is not usable with the function signature of %1"
|
||||
>;
|
||||
def err_coroutine_unfound_nothrow_new : Error <
|
||||
"unable to find '::operator new(size_t, nothrow_t)' for %0"
|
||||
"unable to find %select{'::operator new(size_t, nothrow_t)'|"
|
||||
"'::operator new(size_t, align_val_t, nothrow_t)'}1 for %0"
|
||||
>;
|
||||
def warn_non_aligned_allocation_function : Warning <
|
||||
"under -fcoro-aligned-allocation, the non-aligned allocation function "
|
||||
"for the promise type %0 has higher precedence than the global aligned "
|
||||
"allocation function">,
|
||||
InGroup<CoroNonAlignedAllocationFunction>;
|
||||
def err_conflicting_aligned_options : Error <
|
||||
"conflicting option '-fcoro-aligned-allocation' and '-fno-aligned-allocation'"
|
||||
>;
|
||||
} // end of coroutines issue category
|
||||
|
||||
|
|
|
@ -154,6 +154,7 @@ LANGOPT(NoBuiltin , 1, 0, "disable builtin functions")
|
|||
LANGOPT(NoMathBuiltin , 1, 0, "disable math builtin functions")
|
||||
LANGOPT(GNUAsm , 1, 1, "GNU-style inline assembly")
|
||||
LANGOPT(Coroutines , 1, 0, "C++20 coroutines")
|
||||
LANGOPT(CoroAlignedAllocation, 1, 0, "prefer Aligned Allocation according to P2014 Option 2")
|
||||
LANGOPT(DllExportInlines , 1, 1, "dllexported classes dllexport inline methods")
|
||||
LANGOPT(RelaxedTemplateTemplateArgs, 1, 0, "C++17 relaxed matching of template template arguments")
|
||||
LANGOPT(ExperimentalLibrary, 1, 0, "enable unstable and experimental library features")
|
||||
|
|
|
@ -1189,6 +1189,11 @@ defm coroutines_ts : BoolFOption<"coroutines-ts",
|
|||
PosFlag<SetTrue, [CC1Option], "Enable support for the C++ Coroutines TS">,
|
||||
NegFlag<SetFalse>>;
|
||||
|
||||
defm coro_aligned_allocation : BoolFOption<"coro-aligned-allocation",
|
||||
LangOpts<"CoroAlignedAllocation">, DefaultFalse,
|
||||
PosFlag<SetTrue, [CC1Option], "Prefer aligned allocation for C++ Coroutines">,
|
||||
NegFlag<SetFalse>>;
|
||||
|
||||
defm experimental_library : BoolFOption<"experimental-library",
|
||||
LangOpts<"ExperimentalLibrary">, DefaultFalse,
|
||||
PosFlag<SetTrue, [CC1Option, CoreOption], "Control whether unstable and experimental library features are enabled. "
|
||||
|
|
|
@ -6660,7 +6660,8 @@ public:
|
|||
|
||||
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,
|
||||
|
|
|
@ -4675,6 +4675,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
|
|||
return EmitCoroutineIntrinsic(E, Intrinsic::coro_suspend);
|
||||
case Builtin::BI__builtin_coro_size:
|
||||
return EmitCoroutineIntrinsic(E, Intrinsic::coro_size);
|
||||
case Builtin::BI__builtin_coro_align:
|
||||
return EmitCoroutineIntrinsic(E, Intrinsic::coro_align);
|
||||
|
||||
// OpenCL v2.0 s6.13.16.2, Built-in pipe read and write functions
|
||||
case Builtin::BIread_pipe:
|
||||
|
|
|
@ -683,6 +683,13 @@ RValue CodeGenFunction::EmitCoroutineIntrinsic(const CallExpr *E,
|
|||
llvm::Function *F = CGM.getIntrinsic(llvm::Intrinsic::coro_size, T);
|
||||
return RValue::get(Builder.CreateCall(F));
|
||||
}
|
||||
case llvm::Intrinsic::coro_align: {
|
||||
auto &Context = getContext();
|
||||
CanQualType SizeTy = Context.getSizeType();
|
||||
llvm::IntegerType *T = Builder.getIntNTy(Context.getTypeSize(SizeTy));
|
||||
llvm::Function *F = CGM.getIntrinsic(llvm::Intrinsic::coro_align, T);
|
||||
return RValue::get(Builder.CreateCall(F));
|
||||
}
|
||||
// The following three intrinsics take a token parameter referring to a token
|
||||
// returned by earlier call to @llvm.coro.id. Since we cannot represent it in
|
||||
// builtins, we patch it up here.
|
||||
|
|
|
@ -6500,6 +6500,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
|
|||
CmdArgs.push_back("-fcoroutines-ts");
|
||||
}
|
||||
|
||||
if (Args.hasFlag(options::OPT_fcoro_aligned_allocation,
|
||||
options::OPT_fno_coro_aligned_allocation, false) &&
|
||||
types::isCXX(InputType))
|
||||
CmdArgs.push_back("-fcoro-aligned-allocation");
|
||||
|
||||
Args.AddLastArg(CmdArgs, options::OPT_fdouble_square_bracket_attributes,
|
||||
options::OPT_fno_double_square_bracket_attributes);
|
||||
|
||||
|
|
|
@ -1030,6 +1030,13 @@ static Expr *buildStdNoThrowDeclRef(Sema &S, SourceLocation Loc) {
|
|||
return DR.get();
|
||||
}
|
||||
|
||||
static TypeSourceInfo *getTypeSourceInfoForStdAlignValT(Sema &S,
|
||||
SourceLocation Loc) {
|
||||
EnumDecl *StdAlignValT = S.getStdAlignValT();
|
||||
QualType StdAlignValDecl = S.Context.getTypeDeclType(StdAlignValT);
|
||||
return S.Context.getTrivialTypeSourceInfo(StdAlignValDecl);
|
||||
}
|
||||
|
||||
// Find an appropriate delete for the promise.
|
||||
static bool findDeleteForPromise(Sema &S, SourceLocation Loc, QualType PromiseType,
|
||||
FunctionDecl *&OperatorDelete) {
|
||||
|
@ -1039,12 +1046,15 @@ static bool findDeleteForPromise(Sema &S, SourceLocation Loc, QualType PromiseTy
|
|||
auto *PointeeRD = PromiseType->getAsCXXRecordDecl();
|
||||
assert(PointeeRD && "PromiseType must be a CxxRecordDecl type");
|
||||
|
||||
const bool Overaligned = S.getLangOpts().CoroAlignedAllocation;
|
||||
|
||||
// [dcl.fct.def.coroutine]p12
|
||||
// The deallocation function's name is looked up by searching for it in the
|
||||
// scope of the promise type. If nothing is found, a search is performed in
|
||||
// the global scope.
|
||||
if (S.FindDeallocationFunction(Loc, PointeeRD, DeleteName, OperatorDelete,
|
||||
/*Diagnose*/ true, /*WantSize*/ true))
|
||||
/*Diagnose*/ true, /*WantSize*/ true,
|
||||
/*WantAligned*/ Overaligned))
|
||||
return false;
|
||||
|
||||
// [dcl.fct.def.coroutine]p12
|
||||
|
@ -1057,7 +1067,6 @@ static bool findDeleteForPromise(Sema &S, SourceLocation Loc, QualType PromiseTy
|
|||
// Look for a global declaration.
|
||||
// Coroutines can always provide their required size.
|
||||
const bool CanProvideSize = true;
|
||||
const bool Overaligned = false;
|
||||
// Sema::FindUsualDeallocationFunction will try to find the one with two
|
||||
// parameters first. It will return the deallocation function with one
|
||||
// parameter if failed.
|
||||
|
@ -1324,7 +1333,6 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
|
|||
// lvalue that denotes the parameter copy corresponding to p_i.
|
||||
|
||||
FunctionDecl *OperatorNew = nullptr;
|
||||
bool PassAlignment = false;
|
||||
SmallVector<Expr *, 1> PlacementArgs;
|
||||
|
||||
const bool PromiseContainsNew = [this, &PromiseType]() -> bool {
|
||||
|
@ -1338,8 +1346,13 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
|
|||
return !R.empty() && !R.isAmbiguous();
|
||||
}();
|
||||
|
||||
// Helper function to indicate whether the last lookup found the aligned
|
||||
// allocation function.
|
||||
bool PassAlignment = S.getLangOpts().CoroAlignedAllocation;
|
||||
auto LookupAllocationFunction = [&](Sema::AllocationFunctionScope NewScope =
|
||||
Sema::AFS_Both) {
|
||||
Sema::AFS_Both,
|
||||
bool WithoutPlacementArgs = false,
|
||||
bool ForceNonAligned = false) {
|
||||
// [dcl.fct.def.coroutine]p9
|
||||
// The allocation function's name is looked up by searching for it in the
|
||||
// scope of the promise type.
|
||||
|
@ -1349,10 +1362,13 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
|
|||
if (NewScope == Sema::AFS_Both)
|
||||
NewScope = PromiseContainsNew ? Sema::AFS_Class : Sema::AFS_Global;
|
||||
|
||||
PassAlignment = !ForceNonAligned && S.getLangOpts().CoroAlignedAllocation;
|
||||
FunctionDecl *UnusedResult = nullptr;
|
||||
S.FindAllocationFunctions(Loc, SourceRange(), NewScope,
|
||||
/*DeleteScope*/ Sema::AFS_Both, PromiseType,
|
||||
/*isArray*/ false, PassAlignment, PlacementArgs,
|
||||
/*isArray*/ false, PassAlignment,
|
||||
WithoutPlacementArgs ? MultiExprArg{}
|
||||
: PlacementArgs,
|
||||
OperatorNew, UnusedResult, /*Diagnose*/ false);
|
||||
};
|
||||
|
||||
|
@ -1364,15 +1380,58 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
|
|||
|
||||
LookupAllocationFunction();
|
||||
|
||||
// [dcl.fct.def.coroutine]p9
|
||||
// If no viable function is found ([over.match.viable]), overload resolution
|
||||
// is performed again on a function call created by passing just the amount of
|
||||
// space required as an argument of type std::size_t.
|
||||
if (!OperatorNew && !PlacementArgs.empty() && PromiseContainsNew) {
|
||||
PlacementArgs.clear();
|
||||
LookupAllocationFunction();
|
||||
if (PromiseContainsNew && !PlacementArgs.empty()) {
|
||||
// [dcl.fct.def.coroutine]p9
|
||||
// If no viable function is found ([over.match.viable]), overload
|
||||
// resolution
|
||||
// is performed again on a function call created by passing just the amount
|
||||
// of space required as an argument of type std::size_t.
|
||||
//
|
||||
// Proposed Change of [dcl.fct.def.coroutine]p9 in P2014R0:
|
||||
// Otherwise, overload resolution is performed again on a function call
|
||||
// created
|
||||
// by passing the amount of space requested as an argument of type
|
||||
// std::size_t as the first argument, and the requested alignment as
|
||||
// an argument of type std:align_val_t as the second argument.
|
||||
if (!OperatorNew ||
|
||||
(S.getLangOpts().CoroAlignedAllocation && !PassAlignment))
|
||||
LookupAllocationFunction(/*NewScope*/ Sema::AFS_Class,
|
||||
/*WithoutPlacementArgs*/ true);
|
||||
}
|
||||
|
||||
// Proposed Change of [dcl.fct.def.coroutine]p12 in P2014R0:
|
||||
// Otherwise, overload resolution is performed again on a function call
|
||||
// created
|
||||
// by passing the amount of space requested as an argument of type
|
||||
// std::size_t as the first argument, and the lvalues p1 ... pn as the
|
||||
// succeeding arguments. Otherwise, overload resolution is performed again
|
||||
// on a function call created by passing just the amount of space required as
|
||||
// an argument of type std::size_t.
|
||||
//
|
||||
// So within the proposed change in P2014RO, the priority order of aligned
|
||||
// allocation functions wiht promise_type is:
|
||||
//
|
||||
// void* operator new( std::size_t, std::align_val_t, placement_args... );
|
||||
// void* operator new( std::size_t, std::align_val_t);
|
||||
// void* operator new( std::size_t, placement_args... );
|
||||
// void* operator new( std::size_t);
|
||||
|
||||
// Helper variable to emit warnings.
|
||||
bool FoundNonAlignedInPromise = false;
|
||||
if (PromiseContainsNew && S.getLangOpts().CoroAlignedAllocation)
|
||||
if (!OperatorNew || !PassAlignment) {
|
||||
FoundNonAlignedInPromise = OperatorNew;
|
||||
|
||||
LookupAllocationFunction(/*NewScope*/ Sema::AFS_Class,
|
||||
/*WithoutPlacementArgs*/ false,
|
||||
/*ForceNonAligned*/ true);
|
||||
|
||||
if (!OperatorNew && !PlacementArgs.empty())
|
||||
LookupAllocationFunction(/*NewScope*/ Sema::AFS_Class,
|
||||
/*WithoutPlacementArgs*/ true,
|
||||
/*ForceNonAligned*/ true);
|
||||
}
|
||||
|
||||
bool IsGlobalOverload =
|
||||
OperatorNew && !isa<CXXRecordDecl>(OperatorNew->getDeclContext());
|
||||
// If we didn't find a class-local new declaration and non-throwing new
|
||||
|
@ -1387,11 +1446,21 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
|
|||
LookupAllocationFunction(Sema::AFS_Global);
|
||||
}
|
||||
|
||||
// If we found a non-aligned allocation function in the promise_type,
|
||||
// it indicates the user forgot to update the allocation function. Let's emit
|
||||
// a warning here.
|
||||
if (FoundNonAlignedInPromise) {
|
||||
S.Diag(OperatorNew->getLocation(),
|
||||
diag::warn_non_aligned_allocation_function)
|
||||
<< &FD;
|
||||
}
|
||||
|
||||
if (!OperatorNew) {
|
||||
if (PromiseContainsNew)
|
||||
S.Diag(Loc, diag::err_coroutine_unusable_new) << PromiseType << &FD;
|
||||
else if (RequiresNoThrowAlloc)
|
||||
S.Diag(Loc, diag::err_coroutine_unfound_nothrow_new) << &FD;
|
||||
S.Diag(Loc, diag::err_coroutine_unfound_nothrow_new)
|
||||
<< &FD << S.getLangOpts().CoroAlignedAllocation;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -1422,15 +1491,34 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
|
|||
Expr *FrameSize =
|
||||
S.BuildBuiltinCallExpr(Loc, Builtin::BI__builtin_coro_size, {});
|
||||
|
||||
// Make new call.
|
||||
Expr *FrameAlignment = nullptr;
|
||||
|
||||
if (S.getLangOpts().CoroAlignedAllocation) {
|
||||
FrameAlignment =
|
||||
S.BuildBuiltinCallExpr(Loc, Builtin::BI__builtin_coro_align, {});
|
||||
|
||||
TypeSourceInfo *AlignValTy = getTypeSourceInfoForStdAlignValT(S, Loc);
|
||||
if (!AlignValTy)
|
||||
return false;
|
||||
|
||||
FrameAlignment = S.BuildCXXNamedCast(Loc, tok::kw_static_cast, AlignValTy,
|
||||
FrameAlignment, SourceRange(Loc, Loc),
|
||||
SourceRange(Loc, Loc))
|
||||
.get();
|
||||
}
|
||||
|
||||
// Make new call.
|
||||
ExprResult NewRef =
|
||||
S.BuildDeclRefExpr(OperatorNew, OperatorNew->getType(), VK_LValue, Loc);
|
||||
if (NewRef.isInvalid())
|
||||
return false;
|
||||
|
||||
SmallVector<Expr *, 2> NewArgs(1, FrameSize);
|
||||
llvm::append_range(NewArgs, PlacementArgs);
|
||||
if (S.getLangOpts().CoroAlignedAllocation && PassAlignment)
|
||||
NewArgs.push_back(FrameAlignment);
|
||||
|
||||
if (OperatorNew->getNumParams() > NewArgs.size())
|
||||
llvm::append_range(NewArgs, PlacementArgs);
|
||||
|
||||
ExprResult NewExpr =
|
||||
S.BuildCallExpr(S.getCurScope(), NewRef.get(), Loc, NewArgs, Loc);
|
||||
|
@ -1459,9 +1547,29 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
|
|||
// used, the size of the block is passed as the corresponding argument.
|
||||
const auto *OpDeleteType =
|
||||
OpDeleteQualType.getTypePtr()->castAs<FunctionProtoType>();
|
||||
if (OpDeleteType->getNumParams() > 1)
|
||||
if (OpDeleteType->getNumParams() > DeleteArgs.size() &&
|
||||
S.getASTContext().hasSameType(
|
||||
OpDeleteType->getParamType(DeleteArgs.size()), FrameSize->getType()))
|
||||
DeleteArgs.push_back(FrameSize);
|
||||
|
||||
// Proposed Change of [dcl.fct.def.coroutine]p12 in P2014R0:
|
||||
// If deallocation function lookup finds a usual deallocation function with
|
||||
// a pointer parameter, size parameter and alignment parameter then this
|
||||
// will be the selected deallocation function, otherwise if lookup finds a
|
||||
// usual deallocation function with both a pointer parameter and a size
|
||||
// parameter, then this will be the selected deallocation function.
|
||||
// Otherwise, if lookup finds a usual deallocation function with only a
|
||||
// pointer parameter, then this will be the selected deallocation
|
||||
// function.
|
||||
//
|
||||
// So we are not forced to pass alignment to the deallocation function.
|
||||
if (S.getLangOpts().CoroAlignedAllocation &&
|
||||
OpDeleteType->getNumParams() > DeleteArgs.size() &&
|
||||
S.getASTContext().hasSameType(
|
||||
OpDeleteType->getParamType(DeleteArgs.size()),
|
||||
FrameAlignment->getType()))
|
||||
DeleteArgs.push_back(FrameAlignment);
|
||||
|
||||
ExprResult DeleteExpr =
|
||||
S.BuildCallExpr(S.getCurScope(), DeleteRef.get(), Loc, DeleteArgs, Loc);
|
||||
DeleteExpr =
|
||||
|
|
|
@ -3189,7 +3189,7 @@ FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc,
|
|||
bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
|
||||
DeclarationName Name,
|
||||
FunctionDecl *&Operator, bool Diagnose,
|
||||
bool WantSize) {
|
||||
bool WantSize, bool WantAligned) {
|
||||
LookupResult Found(*this, Name, StartLoc, LookupOrdinaryName);
|
||||
// Try to find operator delete/operator delete[] in class scope.
|
||||
LookupQualifiedName(Found, RD);
|
||||
|
@ -3199,7 +3199,8 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
|
|||
|
||||
Found.suppressDiagnostics();
|
||||
|
||||
bool Overaligned = hasNewExtendedAlignment(*this, Context.getRecordType(RD));
|
||||
bool Overaligned =
|
||||
WantAligned || hasNewExtendedAlignment(*this, Context.getRecordType(RD));
|
||||
|
||||
// C++17 [expr.delete]p10:
|
||||
// If the deallocation functions have class scope, the one without a
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue