Skip to content

Commit 49747cd

Browse files
committed
[clang] Implement a bitwise_copyable builtin type trait.
This patch implements a `__is_bitwise_copyable` builtin in clang. The bitwise copyable types act as the trivially copyable types, but they support a wider range of types (e.g. classes with virtual methods) -- their underlying types can be safely copied by `memcopy` or `memmove`, the clang compiler guarantees that both source and destination objects have the same *object* representations after the copy operation, and the lifetime of the destination object implicitly starts. A particular use case of this builtin is to clone an object via memcopy (without running the constructor): ``` Message* clone(const Message* src, char* buffer, int size) { if constexpr __is_bitwise_copyable(Message) { // bitwise copy to buffer __builtin_memcpy(buffer, src, size); // using __builtin_launder to prevent miscompile for -fstrict-vtable-pointers. return __builtin_launder(reinterpret_cast<Message*>(buffer)); } // Fallback the operator new, which calls the constructor to start the lifetime. return new(buffer) Message(src); } ``` Note that the definition of bitwise copyable is not tied to the Rule Of Five, so users of this builtin must guarantee that program semantic constraints are satisfied, e.g. no double resource deallocations. Context: https://discourse.llvm.org/t/extension-for-creating-objects-via-memcpy
1 parent 8cdecd4 commit 49747cd

File tree

7 files changed

+78
-0
lines changed

7 files changed

+78
-0
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4016,6 +4016,34 @@ Note that the `size` argument must be a compile time constant.
40164016
40174017
Note that this intrinsic cannot yet be called in a ``constexpr`` context.
40184018
4019+
``__is_bitwise_cloneable``
4020+
--------------------------
4021+
4022+
A type trait is used to check whether a type can be safely copied by memcpy.
4023+
4024+
**Syntax**:
4025+
4026+
.. code-block:: c++
4027+
4028+
bool __is_bitwise_cloneable(Type)
4029+
4030+
**Description**:
4031+
4032+
This trait is similar to `std::is_trivially_copyable`, but additionally allows
4033+
to have user-defined constructors, virtual functions and virtual bases. It is up
4034+
to the user code to guarantee that a bitwise copy results in non-broken object
4035+
and that the lifetime of an object is properly started.
4036+
4037+
Objects of bitwise cloneable types can be bitwise copied by memcpy/memmove. The
4038+
Clang compiler warrants that this behavior is well defined, and won't be
4039+
broken by compiler optimizations.
4040+
4041+
After the copy, the lifetime of the new object isn't started yet (unless the
4042+
type is trivially copyable). Users must ensure its lifetime is started to avoid
4043+
undefined behavior.
4044+
4045+
This builtin can be used in constant expressions.
4046+
40194047
Atomic Min/Max builtins with memory ordering
40204048
--------------------------------------------
40214049

clang/include/clang/AST/Type.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,6 +1120,14 @@ class QualType {
11201120
/// Return true if this is a trivially copyable type (C++0x [basic.types]p9)
11211121
bool isTriviallyCopyableType(const ASTContext &Context) const;
11221122

1123+
/// Return true if the type is safe to bitwise copy by memcpy.
1124+
///
1125+
/// This is an extension in clang: bitwise clonable types act as trivially
1126+
/// copyable types, their underlying bytes can be safely copied by memcpy or
1127+
/// memmove. Clang guarantees that the destination has the same **object**
1128+
/// representations after the copy.
1129+
bool isBitwiseCloneableType(const ASTContext &Context) const;
1130+
11231131
/// Return true if this is a trivially copyable type
11241132
bool isTriviallyCopyConstructibleType(const ASTContext &Context) const;
11251133

clang/include/clang/Basic/TokenKinds.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,8 @@ TYPE_TRAIT_2(__reference_converts_from_temporary, ReferenceConvertsFromTemporary
542542
// is not exposed to users.
543543
TYPE_TRAIT_2(/*EmptySpellingName*/, IsDeducible, KEYCXX)
544544

545+
TYPE_TRAIT_1(__is_bitwise_cloneable, IsBitwiseCloneable, KEYALL)
546+
545547
// Embarcadero Expression Traits
546548
EXPRESSION_TRAIT(__is_lvalue_expr, IsLValueExpr, KEYCXX)
547549
EXPRESSION_TRAIT(__is_rvalue_expr, IsRValueExpr, KEYCXX)

clang/lib/AST/Type.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2749,6 +2749,17 @@ bool QualType::isTriviallyCopyableType(const ASTContext &Context) const {
27492749
/*IsCopyConstructible=*/false);
27502750
}
27512751

2752+
bool QualType::isBitwiseCloneableType(const ASTContext & Context) const {
2753+
if (const auto *RD = getCanonicalType()->getAsCXXRecordDecl()) {
2754+
// Never allow memcpy when we're adding poisoned padding bits to the struct.
2755+
// Accessing these posioned bits will trigger false alarm on
2756+
// SanitizeAddressFieldPadding etc.
2757+
if (RD->mayInsertExtraPadding())
2758+
return false;
2759+
}
2760+
return true;
2761+
}
2762+
27522763
bool QualType::isTriviallyCopyConstructibleType(
27532764
const ASTContext &Context) const {
27542765
return isTriviallyCopyableTypeImpl(*this, Context,

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5125,6 +5125,7 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,
51255125
case UTT_IsStandardLayout:
51265126
case UTT_IsPOD:
51275127
case UTT_IsLiteral:
5128+
case UTT_IsBitwiseCloneable:
51285129
// By analogy, is_trivially_relocatable and is_trivially_equality_comparable
51295130
// impose the same constraints.
51305131
case UTT_IsTriviallyRelocatable:
@@ -5618,6 +5619,8 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
56185619
return C.hasUniqueObjectRepresentations(T);
56195620
case UTT_IsTriviallyRelocatable:
56205621
return T.isTriviallyRelocatableType(C);
5622+
case UTT_IsBitwiseCloneable:
5623+
return T.isBitwiseCloneableType(C);
56215624
case UTT_IsReferenceable:
56225625
return T.isReferenceable();
56235626
case UTT_CanPassInRegs:
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux -DSANITIZER_ENABLED -fsanitize=address -fsanitize-address-field-padding=1 %s
2+
// RUN: %clang_cc1 -triple x86_64-unknown-linux %s
3+
4+
struct S {
5+
~S() {}
6+
virtual void foo() {}
7+
8+
int buffer[1];
9+
int other_field = 0;
10+
};
11+
12+
static_assert(!__is_trivially_copyable(S));
13+
#ifdef SANITIZER_ENABLED
14+
static_assert(sizeof(S) > 16);
15+
// Don't allow memcpy when the struct has poisoned padding bits.
16+
static_assert(!__is_bitwise_cloneable(S));
17+
#else
18+
static_assert(sizeof(S) == 16);
19+
static_assert(__is_bitwise_cloneable(S));
20+
#endif
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify %s
2+
// expected-no-diagnostics
3+
//
4+
struct DynamicClass { virtual int Foo(); };
5+
static_assert(!__is_trivially_copyable(DynamicClass));
6+
static_assert(__is_bitwise_cloneable(DynamicClass));

0 commit comments

Comments
 (0)