Skip to content

Commit 300d402

Browse files
authored
[Clang] Implement the core language parts of P2786 - Trivial relocation (#127636)
This adds - The parsing of `trivially_relocatable_if_eligible`, `replaceable_if_eligible` keywords - `__builtin_trivially_relocate`, implemented in terms of memmove. In the future this should - Add the appropriate start/end lifetime markers that llvm does not have (`start_lifetime_as`) - Add support for ptrauth when that's upstreamed - the `__builtin_is_cpp_trivially_relocatable` and `__builtin_is_replaceable` traits Fixes #127609
1 parent 3b4f9c5 commit 300d402

30 files changed

+1126
-65
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1680,6 +1680,7 @@ Static assert with user-generated message __cpp_static_assert >= 202306L C
16801680
Pack Indexing __cpp_pack_indexing C++26 C++03
16811681
``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
16821682
Variadic Friends __cpp_variadic_friend C++26 C++03
1683+
Trivial Relocatability __cpp_trivial_relocatability C++26 C++03
16831684
--------------------------------------------- -------------------------------- ------------- -------------
16841685
Designated initializers (N494) C99 C89
16851686
Array & element qualification (N2607) C23 C89
@@ -1861,8 +1862,15 @@ The following type trait primitives are supported by Clang. Those traits marked
18611862
* ``__is_trivially_relocatable`` (Clang): Returns true if moving an object
18621863
of the given type, and then destroying the source object, is known to be
18631864
functionally equivalent to copying the underlying bytes and then dropping the
1864-
source object on the floor. This is true of trivial types and types which
1865+
source object on the floor. This is true of trivial types,
1866+
C++26 relocatable types, and types which
18651867
were made trivially relocatable via the ``clang::trivial_abi`` attribute.
1868+
* ``__builtin_is_cpp_trivially_relocatable`` (C++): Returns true if an object
1869+
is trivially relocatable, as defined by the C++26 standard [meta.unary.prop].
1870+
Note that when relocating the caller code should ensure that if the object is polymorphic,
1871+
the dynamic type is of the most derived type. Padding bytes should not be copied.
1872+
* ``__builtin_is_replaceable`` (C++): Returns true if an object
1873+
is replaceable, as defined by the C++26 standard [meta.unary.prop].
18661874
* ``__is_trivially_equality_comparable`` (Clang): Returns true if comparing two
18671875
objects of the provided type is known to be equivalent to comparing their
18681876
object representations. Note that types containing padding bytes are never
@@ -3722,6 +3730,21 @@ Query for this feature with ``__has_builtin(__builtin_operator_new)`` or
37223730
replaceable global (de)allocation functions, but do support calling at least
37233731
``::operator new(size_t)`` and ``::operator delete(void*)``.
37243732
3733+
3734+
``__builtin_trivially_relocate``
3735+
-----------------------------------
3736+
3737+
**Syntax**:
3738+
3739+
.. code-block:: c
3740+
3741+
T* __builtin_trivially_relocate(T* dest, T* src, size_t count)
3742+
3743+
Trivially relocates ``count`` objects of relocatable, complete type ``T``
3744+
from ``src`` to ``dest`` and returns ``dest``.
3745+
This builtin is used to implement ``std::trivially_relocate``.
3746+
3747+
37253748
``__builtin_preserve_access_index``
37263749
-----------------------------------
37273750

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ C++2c Feature Support
106106
^^^^^^^^^^^^^^^^^^^^^
107107

108108
- Implemented `P1061R10 Structured Bindings can introduce a Pack <https://wg21.link/P1061R10>`_.
109+
- Implemented `P2786R13 Trivial Relocatability <https://wg21.link/P2786R13>`_.
110+
109111

110112
- Implemented `P0963R3 Structured binding declaration as a condition <https://wg21.link/P0963R3>`_.
111113

clang/include/clang/AST/ASTContext.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,20 @@ class ASTContext : public RefCountedBase<ASTContext> {
617617
using ParameterIndexTable = llvm::DenseMap<const VarDecl *, unsigned>;
618618
ParameterIndexTable ParamIndices;
619619

620+
public:
621+
struct CXXRecordDeclRelocationInfo {
622+
unsigned IsRelocatable;
623+
unsigned IsReplaceable;
624+
};
625+
std::optional<CXXRecordDeclRelocationInfo>
626+
getRelocationInfoForCXXRecord(const CXXRecordDecl *) const;
627+
void setRelocationInfoForCXXRecord(const CXXRecordDecl *,
628+
CXXRecordDeclRelocationInfo);
629+
630+
private:
631+
llvm::DenseMap<const CXXRecordDecl *, CXXRecordDeclRelocationInfo>
632+
RelocatableClasses;
633+
620634
ImportDecl *FirstLocalImport = nullptr;
621635
ImportDecl *LastLocalImport = nullptr;
622636

clang/include/clang/AST/DeclCXX.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1550,6 +1550,9 @@ class CXXRecordDecl : public RecordDecl {
15501550
/// Returns the destructor decl for this class.
15511551
CXXDestructorDecl *getDestructor() const;
15521552

1553+
/// Returns the destructor decl for this class.
1554+
bool hasDeletedDestructor() const;
1555+
15531556
/// Returns true if the class destructor, or any implicitly invoked
15541557
/// destructors are marked noreturn.
15551558
bool isAnyDestructorNoReturn() const { return data().IsAnyDestructorNoReturn; }

clang/include/clang/AST/Type.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,9 +1133,6 @@ class QualType {
11331133
/// Return true if this is a trivially copyable type
11341134
bool isTriviallyCopyConstructibleType(const ASTContext &Context) const;
11351135

1136-
/// Return true if this is a trivially relocatable type.
1137-
bool isTriviallyRelocatableType(const ASTContext &Context) const;
1138-
11391136
/// Returns true if it is a class and it might be dynamic.
11401137
bool mayBeDynamicClass() const;
11411138

clang/include/clang/Basic/Attr.td

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1819,6 +1819,22 @@ def Final : InheritableAttr {
18191819
let Documentation = [InternalOnly];
18201820
}
18211821

1822+
def TriviallyRelocatable : InheritableAttr {
1823+
let Spellings = [CustomKeyword<"trivially_relocatable_if_eligible">];
1824+
let SemaHandler = 0;
1825+
// Omitted from docs, since this is language syntax, not an attribute, as far
1826+
// as users are concerned.
1827+
let Documentation = [InternalOnly];
1828+
}
1829+
1830+
def Replaceable : InheritableAttr {
1831+
let Spellings = [CustomKeyword<"replaceable_if_eligible">];
1832+
let SemaHandler = 0;
1833+
// Omitted from docs, since this is language syntax, not an attribute, as far
1834+
// as users are concerned.
1835+
let Documentation = [InternalOnly];
1836+
}
1837+
18221838
def MinSize : InheritableAttr {
18231839
let Spellings = [Clang<"minsize">];
18241840
let Subjects = SubjectList<[Function, ObjCMethod], ErrorDiag>;

clang/include/clang/Basic/Builtins.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2853,6 +2853,12 @@ def MemMove : LibBuiltin<"string.h"> {
28532853
let AddBuiltinPrefixedAlias = 1;
28542854
}
28552855

2856+
def BuiltinTriviallyRelocate : Builtin {
2857+
let Spellings = ["__builtin_trivially_relocate"];
2858+
let Attributes = [FunctionWithBuiltinPrefix, CustomTypeChecking, NoThrow];
2859+
let Prototype = "void*(void*, void*, size_t)";
2860+
}
2861+
28562862
def StrCpy : LibBuiltin<"string.h"> {
28572863
let Spellings = ["strcpy"];
28582864
let Attributes = [NoThrow];

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,12 +1065,24 @@ def ext_ms_abstract_keyword : ExtWarn<
10651065
"'abstract' keyword is a Microsoft extension">,
10661066
InGroup<MicrosoftAbstract>;
10671067

1068+
def ext_relocatable_keyword : ExtWarn<
1069+
"'%select{trivially_relocatable_if_eligible|replaceable_if_eligible}0' "
1070+
"keyword is a C++2c extension">,
1071+
InGroup<CXX26>;
1072+
def warn_relocatable_keyword : Warning<
1073+
"'%select{trivially_relocatable|replaceable}0_if_eligible' "
1074+
"keyword is incompatible with standards before C++2c">,
1075+
DefaultIgnore, InGroup<CXXPre26Compat>;
1076+
10681077
def err_access_specifier_interface : Error<
10691078
"interface types cannot specify '%select{private|protected}0' access">;
10701079

10711080
def err_duplicate_class_virt_specifier : Error<
10721081
"class already marked '%0'">;
10731082

1083+
def err_duplicate_class_relocation_specifier : Error<
1084+
"class already marked '%select{trivially_relocatable_if_eligible|replaceable_if_eligible}0'">;
1085+
10741086
def err_duplicate_virt_specifier : Error<
10751087
"class member already marked '%0'">;
10761088

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12664,6 +12664,11 @@ def err_builtin_invalid_arg_type: Error<
1266412664
"%plural{0:|: }3"
1266512665
"%plural{[0,3]:type|:types}1 (was %4)">;
1266612666

12667+
def err_builtin_trivially_relocate_invalid_arg_type: Error <
12668+
"first%select{||| and second}0 argument%select{|||s}0 to "
12669+
"'__builtin_trivially_relocate' must be"
12670+
" %select{a pointer|non-const|relocatable|of the same type}0">;
12671+
1266712672
def err_builtin_matrix_disabled: Error<
1266812673
"matrix types extension is disabled. Pass -fenable-matrix to enable it">;
1266912674
def err_matrix_index_not_integer: Error<

clang/include/clang/Basic/TokenKinds.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,8 +557,12 @@ TYPE_TRAIT_2(__reference_converts_from_temporary, ReferenceConvertsFromTemporary
557557
// is not exposed to users.
558558
TYPE_TRAIT_2(/*EmptySpellingName*/, IsDeducible, KEYCXX)
559559
TYPE_TRAIT_1(__is_bitwise_cloneable, IsBitwiseCloneable, KEYALL)
560+
TYPE_TRAIT_1(__builtin_is_cpp_trivially_relocatable, IsCppTriviallyRelocatable, KEYCXX)
561+
TYPE_TRAIT_1(__builtin_is_replaceable, IsReplaceable, KEYCXX)
560562
TYPE_TRAIT_1(__builtin_structured_binding_size, StructuredBindingSize, KEYCXX)
561563

564+
565+
562566
// Embarcadero Expression Traits
563567
EXPRESSION_TRAIT(__is_lvalue_expr, IsLValueExpr, KEYCXX)
564568
EXPRESSION_TRAIT(__is_rvalue_expr, IsRValueExpr, KEYCXX)

clang/include/clang/Parse/Parser.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,8 @@ class Parser : public CodeCompletionHandler {
264264
mutable IdentifierInfo *Ident_final;
265265
mutable IdentifierInfo *Ident_GNU_final;
266266
mutable IdentifierInfo *Ident_override;
267+
mutable IdentifierInfo *Ident_trivially_relocatable_if_eligible;
268+
mutable IdentifierInfo *Ident_replaceable_if_eligible;
267269

268270
// C++2a contextual keywords.
269271
mutable IdentifierInfo *Ident_import;
@@ -3196,6 +3198,16 @@ class Parser : public CodeCompletionHandler {
31963198
SourceLocation FriendLoc);
31973199

31983200
bool isCXX11FinalKeyword() const;
3201+
3202+
bool isCXX2CTriviallyRelocatableKeyword(Token Tok) const;
3203+
bool isCXX2CTriviallyRelocatableKeyword() const;
3204+
void ParseCXX2CTriviallyRelocatableSpecifier(SourceLocation &TRS);
3205+
3206+
bool isCXX2CReplaceableKeyword(Token Tok) const;
3207+
bool isCXX2CReplaceableKeyword() const;
3208+
void ParseCXX2CReplaceableSpecifier(SourceLocation &MRS);
3209+
3210+
bool isClassCompatibleKeyword(Token Tok) const;
31993211
bool isClassCompatibleKeyword() const;
32003212

32013213
/// DeclaratorScopeObj - RAII object used in Parser::ParseDirectDeclarator to

clang/include/clang/Sema/Sema.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4316,13 +4316,18 @@ class Sema final : public SemaBase {
43164316
SourceLocation FinalLoc,
43174317
bool IsFinalSpelledSealed,
43184318
bool IsAbstract,
4319+
SourceLocation TriviallyRelocatable,
4320+
SourceLocation Replaceable,
43194321
SourceLocation LBraceLoc);
43204322

43214323
/// ActOnTagFinishDefinition - Invoked once we have finished parsing
43224324
/// the definition of a tag (enumeration, class, struct, or union).
43234325
void ActOnTagFinishDefinition(Scope *S, Decl *TagDecl,
43244326
SourceRange BraceRange);
43254327

4328+
ASTContext::CXXRecordDeclRelocationInfo
4329+
CheckCXX2CRelocatableAndReplaceable(const clang::CXXRecordDecl *D);
4330+
43264331
void ActOnTagFinishSkippedDefinition(SkippedDefinitionContext Context);
43274332

43284333
/// ActOnTagDefinitionError - Invoked when there was an unrecoverable
@@ -8637,6 +8642,18 @@ class Sema final : public SemaBase {
86378642
ExprResult &LHS, ExprResult &RHS,
86388643
SourceLocation QuestionLoc);
86398644

8645+
//// Determines if a type is trivially relocatable
8646+
/// according to the C++26 rules.
8647+
// FIXME: This is in Sema because it requires
8648+
// overload resolution, can we move to ASTContext?
8649+
bool IsCXXTriviallyRelocatableType(QualType T);
8650+
8651+
//// Determines if a type is replaceable
8652+
/// according to the C++26 rules.
8653+
// FIXME: This is in Sema because it requires
8654+
// overload resolution, can we move to ASTContext?
8655+
bool IsCXXReplaceableType(QualType T);
8656+
86408657
/// Check the operands of ?: under C++ semantics.
86418658
///
86428659
/// See C++ [expr.cond]. Note that LHS is never null, even for the GNU x ?: y

clang/lib/AST/ASTContext.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,6 +1688,24 @@ void ASTContext::getOverriddenMethods(
16881688
Overridden.append(OverDecls.begin(), OverDecls.end());
16891689
}
16901690

1691+
std::optional<ASTContext::CXXRecordDeclRelocationInfo>
1692+
ASTContext::getRelocationInfoForCXXRecord(const CXXRecordDecl *RD) const {
1693+
assert(RD);
1694+
CXXRecordDecl *D = RD->getDefinition();
1695+
auto it = RelocatableClasses.find(D);
1696+
if (it != RelocatableClasses.end())
1697+
return it->getSecond();
1698+
return std::nullopt;
1699+
}
1700+
1701+
void ASTContext::setRelocationInfoForCXXRecord(
1702+
const CXXRecordDecl *RD, CXXRecordDeclRelocationInfo Info) {
1703+
assert(RD);
1704+
CXXRecordDecl *D = RD->getDefinition();
1705+
assert(RelocatableClasses.find(D) == RelocatableClasses.end());
1706+
RelocatableClasses.insert({D, Info});
1707+
}
1708+
16911709
void ASTContext::addedLocalImportDecl(ImportDecl *Import) {
16921710
assert(!Import->getNextLocalImport() &&
16931711
"Import declaration already in the chain");

clang/lib/AST/Decl.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4491,6 +4491,7 @@ unsigned FunctionDecl::getMemoryFunctionKind() const {
44914491
case Builtin::BImempcpy:
44924492
return Builtin::BImempcpy;
44934493

4494+
case Builtin::BI__builtin_trivially_relocate:
44944495
case Builtin::BI__builtin_memmove:
44954496
case Builtin::BI__builtin___memmove_chk:
44964497
case Builtin::BImemmove:

clang/lib/AST/DeclCXX.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1556,7 +1556,6 @@ void CXXRecordDecl::addedEligibleSpecialMemberFunction(const CXXMethodDecl *MD,
15561556
if (DD->isNoReturn())
15571557
data().IsAnyDestructorNoReturn = true;
15581558
}
1559-
15601559
if (!MD->isImplicit() && !MD->isUserProvided()) {
15611560
// This method is user-declared but not user-provided. We can't work
15621561
// out whether it's trivial yet (not until we get to the end of the
@@ -2144,6 +2143,12 @@ CXXDestructorDecl *CXXRecordDecl::getDestructor() const {
21442143
return nullptr;
21452144
}
21462145

2146+
bool CXXRecordDecl::hasDeletedDestructor() const {
2147+
if (const CXXDestructorDecl *D = getDestructor())
2148+
return D->isDeleted();
2149+
return false;
2150+
}
2151+
21472152
static bool isDeclContextInNamespace(const DeclContext *DC) {
21482153
while (!DC->isTranslationUnit()) {
21492154
if (DC->isNamespace())

clang/lib/AST/Type.cpp

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2896,29 +2896,6 @@ bool QualType::isTriviallyCopyConstructibleType(
28962896
/*IsCopyConstructible=*/true);
28972897
}
28982898

2899-
bool QualType::isTriviallyRelocatableType(const ASTContext &Context) const {
2900-
QualType BaseElementType = Context.getBaseElementType(*this);
2901-
2902-
if (BaseElementType->isIncompleteType()) {
2903-
return false;
2904-
} else if (!BaseElementType->isObjectType()) {
2905-
return false;
2906-
} else if (const auto *RD = BaseElementType->getAsRecordDecl()) {
2907-
return RD->canPassInRegisters();
2908-
} else if (BaseElementType.isTriviallyCopyableType(Context)) {
2909-
return true;
2910-
} else {
2911-
switch (isNonTrivialToPrimitiveDestructiveMove()) {
2912-
case PCK_Trivial:
2913-
return !isDestructedType();
2914-
case PCK_ARCStrong:
2915-
return true;
2916-
default:
2917-
return false;
2918-
}
2919-
}
2920-
}
2921-
29222899
bool QualType::isNonWeakInMRRWithObjCWeak(const ASTContext &Context) const {
29232900
return !Context.getLangOpts().ObjCAutoRefCount &&
29242901
Context.getLangOpts().ObjCWeak &&

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4210,6 +4210,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
42104210
return RValue::get(Dest, *this);
42114211
}
42124212

4213+
case Builtin::BI__builtin_trivially_relocate:
42134214
case Builtin::BImemmove:
42144215
case Builtin::BI__builtin_memmove: {
42154216
Address Dest = EmitPointerWithAlignment(E->getArg(0));

clang/lib/Frontend/InitPreprocessor.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
772772
Builder.defineMacro("__cpp_pack_indexing", "202311L");
773773
Builder.defineMacro("__cpp_deleted_function", "202403L");
774774
Builder.defineMacro("__cpp_variadic_friend", "202403L");
775+
// Builder.defineMacro("__cpp_trivial_relocatability", "202502L");
775776

776777
if (LangOpts.Char8)
777778
Builder.defineMacro("__cpp_char8_t", "202207L");

0 commit comments

Comments
 (0)