Skip to content

[Clang] Implement the core language parts of P2786 - Trivial relocation #127636

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 30 commits into from
May 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
fc2ddba
[Clang][WIP] Trivial relocation
cor3ntin Feb 18, 2025
ab57929
add sema tests for __builtin_trivially_relocate
cor3ntin Feb 20, 2025
350f7d7
rename __is_cpp_trivially_relocatable
cor3ntin Feb 20, 2025
d5d3bdd
add docs and fix variable names in SemaChecking
cor3ntin Feb 20, 2025
546d109
restore assert, add codegen test
cor3ntin Feb 20, 2025
d6a51fb
address comments, fix tests
cor3ntin Feb 21, 2025
96a6c9e
address comments
cor3ntin Feb 21, 2025
cd78311
format
cor3ntin Feb 21, 2025
8ee2d73
add extension warning and tests
cor3ntin Feb 21, 2025
a26fa6f
changelog, docs, C++03 tests
cor3ntin Feb 21, 2025
b0539c0
rewrite everything
cor3ntin Feb 21, 2025
ce36a34
address more feedback
cor3ntin Feb 24, 2025
f6aa238
Add comments
cor3ntin Feb 24, 2025
705ff2b
Fix volatile test
cor3ntin Feb 24, 2025
e4016f0
* Use overload on special members
cor3ntin Feb 24, 2025
9b9043a
Make hasDeletedDestructor a member
cor3ntin Feb 24, 2025
2b4df98
__is_trivially_relocatable is true for c++26 relocatable types
cor3ntin Feb 24, 2025
5a3ef1e
Remove the specifier wrapper, stash the location in attributes
cor3ntin Feb 24, 2025
14302d9
reduce destructor lookups
cor3ntin Feb 25, 2025
b623ed7
cleanup
cor3ntin Feb 25, 2025
6a1a537
Defer the computation of relocatability/replaceability to
cor3ntin Feb 25, 2025
fdf7aa1
remove dead code
cor3ntin Feb 25, 2025
cfde8a5
Add VLA tests
cor3ntin Feb 25, 2025
a473deb
Format
cor3ntin Feb 25, 2025
0f1aa03
Make sure __is_trivially_relocatable is false for polymorphic types
cor3ntin Feb 25, 2025
b5a8057
Do not yet set the featurte test macro
cor3ntin Feb 25, 2025
256b8c8
format
cor3ntin Feb 25, 2025
110795a
types with ptrauth are not relocatable for now
cor3ntin May 6, 2025
d6a9e66
Fix ptrauth tests
cor3ntin May 6, 2025
052e98e
format
cor3ntin May 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1680,6 +1680,7 @@ Static assert with user-generated message __cpp_static_assert >= 202306L C
Pack Indexing __cpp_pack_indexing C++26 C++03
``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
Variadic Friends __cpp_variadic_friend C++26 C++03
Trivial Relocatability __cpp_trivial_relocatability C++26 C++03
--------------------------------------------- -------------------------------- ------------- -------------
Designated initializers (N494) C99 C89
Array & element qualification (N2607) C23 C89
Expand Down Expand Up @@ -1861,8 +1862,15 @@ The following type trait primitives are supported by Clang. Those traits marked
* ``__is_trivially_relocatable`` (Clang): Returns true if moving an object
of the given type, and then destroying the source object, is known to be
functionally equivalent to copying the underlying bytes and then dropping the
source object on the floor. This is true of trivial types and types which
source object on the floor. This is true of trivial types,
C++26 relocatable types, and types which
were made trivially relocatable via the ``clang::trivial_abi`` attribute.
* ``__builtin_is_cpp_trivially_relocatable`` (C++): Returns true if an object
is trivially relocatable, as defined by the C++26 standard [meta.unary.prop].
Note that when relocating the caller code should ensure that if the object is polymorphic,
the dynamic type is of the most derived type. Padding bytes should not be copied.
* ``__builtin_is_replaceable`` (C++): Returns true if an object
is replaceable, as defined by the C++26 standard [meta.unary.prop].
* ``__is_trivially_equality_comparable`` (Clang): Returns true if comparing two
objects of the provided type is known to be equivalent to comparing their
object representations. Note that types containing padding bytes are never
Expand Down Expand Up @@ -3722,6 +3730,21 @@ Query for this feature with ``__has_builtin(__builtin_operator_new)`` or
replaceable global (de)allocation functions, but do support calling at least
``::operator new(size_t)`` and ``::operator delete(void*)``.


``__builtin_trivially_relocate``
-----------------------------------

**Syntax**:

.. code-block:: c

T* __builtin_trivially_relocate(T* dest, T* src, size_t count)

Trivially relocates ``count`` objects of relocatable, complete type ``T``
from ``src`` to ``dest`` and returns ``dest``.
This builtin is used to implement ``std::trivially_relocate``.


``__builtin_preserve_access_index``
-----------------------------------

Expand Down
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^

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


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

Expand Down
14 changes: 14 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,20 @@ class ASTContext : public RefCountedBase<ASTContext> {
using ParameterIndexTable = llvm::DenseMap<const VarDecl *, unsigned>;
ParameterIndexTable ParamIndices;

public:
struct CXXRecordDeclRelocationInfo {
unsigned IsRelocatable;
unsigned IsReplaceable;
};
std::optional<CXXRecordDeclRelocationInfo>
getRelocationInfoForCXXRecord(const CXXRecordDecl *) const;
void setRelocationInfoForCXXRecord(const CXXRecordDecl *,
CXXRecordDeclRelocationInfo);

private:
llvm::DenseMap<const CXXRecordDecl *, CXXRecordDeclRelocationInfo>
RelocatableClasses;

ImportDecl *FirstLocalImport = nullptr;
ImportDecl *LastLocalImport = nullptr;

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/AST/DeclCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -1550,6 +1550,9 @@ class CXXRecordDecl : public RecordDecl {
/// Returns the destructor decl for this class.
CXXDestructorDecl *getDestructor() const;

/// Returns the destructor decl for this class.
bool hasDeletedDestructor() const;

/// Returns true if the class destructor, or any implicitly invoked
/// destructors are marked noreturn.
bool isAnyDestructorNoReturn() const { return data().IsAnyDestructorNoReturn; }
Expand Down
3 changes: 0 additions & 3 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -1133,9 +1133,6 @@ class QualType {
/// Return true if this is a trivially copyable type
bool isTriviallyCopyConstructibleType(const ASTContext &Context) const;

/// Return true if this is a trivially relocatable type.
bool isTriviallyRelocatableType(const ASTContext &Context) const;

/// Returns true if it is a class and it might be dynamic.
bool mayBeDynamicClass() const;

Expand Down
16 changes: 16 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -1819,6 +1819,22 @@ def Final : InheritableAttr {
let Documentation = [InternalOnly];
}

def TriviallyRelocatable : InheritableAttr {
let Spellings = [CustomKeyword<"trivially_relocatable_if_eligible">];
let SemaHandler = 0;
// Omitted from docs, since this is language syntax, not an attribute, as far
// as users are concerned.
let Documentation = [InternalOnly];
}

def Replaceable : InheritableAttr {
let Spellings = [CustomKeyword<"replaceable_if_eligible">];
let SemaHandler = 0;
// Omitted from docs, since this is language syntax, not an attribute, as far
// as users are concerned.
let Documentation = [InternalOnly];
}

def MinSize : InheritableAttr {
let Spellings = [Clang<"minsize">];
let Subjects = SubjectList<[Function, ObjCMethod], ErrorDiag>;
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -2853,6 +2853,12 @@ def MemMove : LibBuiltin<"string.h"> {
let AddBuiltinPrefixedAlias = 1;
}

def BuiltinTriviallyRelocate : Builtin {
let Spellings = ["__builtin_trivially_relocate"];
let Attributes = [FunctionWithBuiltinPrefix, CustomTypeChecking, NoThrow];
let Prototype = "void*(void*, void*, size_t)";
}

def StrCpy : LibBuiltin<"string.h"> {
let Spellings = ["strcpy"];
let Attributes = [NoThrow];
Expand Down
12 changes: 12 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1065,12 +1065,24 @@ def ext_ms_abstract_keyword : ExtWarn<
"'abstract' keyword is a Microsoft extension">,
InGroup<MicrosoftAbstract>;

def ext_relocatable_keyword : ExtWarn<
"'%select{trivially_relocatable_if_eligible|replaceable_if_eligible}0' "
"keyword is a C++2c extension">,
InGroup<CXX26>;
def warn_relocatable_keyword : Warning<
"'%select{trivially_relocatable|replaceable}0_if_eligible' "
"keyword is incompatible with standards before C++2c">,
DefaultIgnore, InGroup<CXXPre26Compat>;

def err_access_specifier_interface : Error<
"interface types cannot specify '%select{private|protected}0' access">;

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

def err_duplicate_class_relocation_specifier : Error<
"class already marked '%select{trivially_relocatable_if_eligible|replaceable_if_eligible}0'">;

def err_duplicate_virt_specifier : Error<
"class member already marked '%0'">;

Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -12664,6 +12664,11 @@ def err_builtin_invalid_arg_type: Error<
"%plural{0:|: }3"
"%plural{[0,3]:type|:types}1 (was %4)">;

def err_builtin_trivially_relocate_invalid_arg_type: Error <
"first%select{||| and second}0 argument%select{|||s}0 to "
"'__builtin_trivially_relocate' must be"
" %select{a pointer|non-const|relocatable|of the same type}0">;

def err_builtin_matrix_disabled: Error<
"matrix types extension is disabled. Pass -fenable-matrix to enable it">;
def err_matrix_index_not_integer: Error<
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -557,8 +557,12 @@ TYPE_TRAIT_2(__reference_converts_from_temporary, ReferenceConvertsFromTemporary
// is not exposed to users.
TYPE_TRAIT_2(/*EmptySpellingName*/, IsDeducible, KEYCXX)
TYPE_TRAIT_1(__is_bitwise_cloneable, IsBitwiseCloneable, KEYALL)
TYPE_TRAIT_1(__builtin_is_cpp_trivially_relocatable, IsCppTriviallyRelocatable, KEYCXX)
TYPE_TRAIT_1(__builtin_is_replaceable, IsReplaceable, KEYCXX)
TYPE_TRAIT_1(__builtin_structured_binding_size, StructuredBindingSize, KEYCXX)



// Embarcadero Expression Traits
EXPRESSION_TRAIT(__is_lvalue_expr, IsLValueExpr, KEYCXX)
EXPRESSION_TRAIT(__is_rvalue_expr, IsRValueExpr, KEYCXX)
Expand Down
12 changes: 12 additions & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@ class Parser : public CodeCompletionHandler {
mutable IdentifierInfo *Ident_final;
mutable IdentifierInfo *Ident_GNU_final;
mutable IdentifierInfo *Ident_override;
mutable IdentifierInfo *Ident_trivially_relocatable_if_eligible;
mutable IdentifierInfo *Ident_replaceable_if_eligible;

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

bool isCXX11FinalKeyword() const;

bool isCXX2CTriviallyRelocatableKeyword(Token Tok) const;
bool isCXX2CTriviallyRelocatableKeyword() const;
void ParseCXX2CTriviallyRelocatableSpecifier(SourceLocation &TRS);

bool isCXX2CReplaceableKeyword(Token Tok) const;
bool isCXX2CReplaceableKeyword() const;
void ParseCXX2CReplaceableSpecifier(SourceLocation &MRS);

bool isClassCompatibleKeyword(Token Tok) const;
bool isClassCompatibleKeyword() const;

/// DeclaratorScopeObj - RAII object used in Parser::ParseDirectDeclarator to
Expand Down
17 changes: 17 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -4316,13 +4316,18 @@ class Sema final : public SemaBase {
SourceLocation FinalLoc,
bool IsFinalSpelledSealed,
bool IsAbstract,
SourceLocation TriviallyRelocatable,
SourceLocation Replaceable,
SourceLocation LBraceLoc);

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

ASTContext::CXXRecordDeclRelocationInfo
CheckCXX2CRelocatableAndReplaceable(const clang::CXXRecordDecl *D);

void ActOnTagFinishSkippedDefinition(SkippedDefinitionContext Context);

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

//// Determines if a type is trivially relocatable
/// according to the C++26 rules.
// FIXME: This is in Sema because it requires
// overload resolution, can we move to ASTContext?
bool IsCXXTriviallyRelocatableType(QualType T);

//// Determines if a type is replaceable
/// according to the C++26 rules.
// FIXME: This is in Sema because it requires
// overload resolution, can we move to ASTContext?
bool IsCXXReplaceableType(QualType T);

/// Check the operands of ?: under C++ semantics.
///
/// See C++ [expr.cond]. Note that LHS is never null, even for the GNU x ?: y
Expand Down
18 changes: 18 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1688,6 +1688,24 @@ void ASTContext::getOverriddenMethods(
Overridden.append(OverDecls.begin(), OverDecls.end());
}

std::optional<ASTContext::CXXRecordDeclRelocationInfo>
ASTContext::getRelocationInfoForCXXRecord(const CXXRecordDecl *RD) const {
assert(RD);
CXXRecordDecl *D = RD->getDefinition();
auto it = RelocatableClasses.find(D);
if (it != RelocatableClasses.end())
return it->getSecond();
return std::nullopt;
}

void ASTContext::setRelocationInfoForCXXRecord(
const CXXRecordDecl *RD, CXXRecordDeclRelocationInfo Info) {
assert(RD);
CXXRecordDecl *D = RD->getDefinition();
assert(RelocatableClasses.find(D) == RelocatableClasses.end());
RelocatableClasses.insert({D, Info});
}

void ASTContext::addedLocalImportDecl(ImportDecl *Import) {
assert(!Import->getNextLocalImport() &&
"Import declaration already in the chain");
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4491,6 +4491,7 @@ unsigned FunctionDecl::getMemoryFunctionKind() const {
case Builtin::BImempcpy:
return Builtin::BImempcpy;

case Builtin::BI__builtin_trivially_relocate:
case Builtin::BI__builtin_memmove:
case Builtin::BI__builtin___memmove_chk:
case Builtin::BImemmove:
Expand Down
7 changes: 6 additions & 1 deletion clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1556,7 +1556,6 @@ void CXXRecordDecl::addedEligibleSpecialMemberFunction(const CXXMethodDecl *MD,
if (DD->isNoReturn())
data().IsAnyDestructorNoReturn = true;
}

if (!MD->isImplicit() && !MD->isUserProvided()) {
// This method is user-declared but not user-provided. We can't work
// out whether it's trivial yet (not until we get to the end of the
Expand Down Expand Up @@ -2144,6 +2143,12 @@ CXXDestructorDecl *CXXRecordDecl::getDestructor() const {
return nullptr;
}

bool CXXRecordDecl::hasDeletedDestructor() const {
if (const CXXDestructorDecl *D = getDestructor())
return D->isDeleted();
return false;
}

static bool isDeclContextInNamespace(const DeclContext *DC) {
while (!DC->isTranslationUnit()) {
if (DC->isNamespace())
Expand Down
23 changes: 0 additions & 23 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2896,29 +2896,6 @@ bool QualType::isTriviallyCopyConstructibleType(
/*IsCopyConstructible=*/true);
}

bool QualType::isTriviallyRelocatableType(const ASTContext &Context) const {
QualType BaseElementType = Context.getBaseElementType(*this);

if (BaseElementType->isIncompleteType()) {
return false;
} else if (!BaseElementType->isObjectType()) {
return false;
} else if (const auto *RD = BaseElementType->getAsRecordDecl()) {
return RD->canPassInRegisters();
} else if (BaseElementType.isTriviallyCopyableType(Context)) {
return true;
} else {
switch (isNonTrivialToPrimitiveDestructiveMove()) {
case PCK_Trivial:
return !isDestructedType();
case PCK_ARCStrong:
return true;
default:
return false;
}
}
}

bool QualType::isNonWeakInMRRWithObjCWeak(const ASTContext &Context) const {
return !Context.getLangOpts().ObjCAutoRefCount &&
Context.getLangOpts().ObjCWeak &&
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4210,6 +4210,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
return RValue::get(Dest, *this);
}

case Builtin::BI__builtin_trivially_relocate:
case Builtin::BImemmove:
case Builtin::BI__builtin_memmove: {
Address Dest = EmitPointerWithAlignment(E->getArg(0));
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Frontend/InitPreprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
Builder.defineMacro("__cpp_pack_indexing", "202311L");
Builder.defineMacro("__cpp_deleted_function", "202403L");
Builder.defineMacro("__cpp_variadic_friend", "202403L");
// Builder.defineMacro("__cpp_trivial_relocatability", "202502L");

if (LangOpts.Char8)
Builder.defineMacro("__cpp_char8_t", "202207L");
Expand Down
Loading