Skip to content

Commit 011d6bb

Browse files
committed
ination of 2 commits.
[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 011d6bb

File tree

9 files changed

+139
-0
lines changed

9 files changed

+139
-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+
4036+
Objects of bitwise cloneable types can be bitwise copied by memcpy/memmove. The
4037+
Clang compiler warrants that this behavior is well defined, and won't be
4038+
broken by compiler optimizations.
4039+
4040+
For implicit-lifetime types, the lifetime of the new object is implicitly
4041+
started after the copy. For other types (e.g., classes with virtual methods),
4042+
the lifetime isn't started, and using the object results in undefined behavior
4043+
according to the C++ Standard.
4044+
4045+
This builtin can be used in constant expressions.
4046+
40194047
Atomic Min/Max builtins with memory ordering
40204048
--------------------------------------------
40214049

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,9 @@ Non-comprehensive list of changes in this release
318318
- Builtins ``__builtin_shufflevector()`` and ``__builtin_convertvector()`` may
319319
now be used within constant expressions.
320320

321+
- Added ``__buitlin_bitwise_clonable`` which is used to check whether a type
322+
can be safely copied by memcpy/memmove.
323+
321324
New Compiler Flags
322325
------------------
323326
- ``-fsanitize=implicit-bitfield-conversion`` checks implicit truncation and

clang/include/clang/AST/Type.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,6 +1120,22 @@ 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 using memcpy/memmove.
1124+
///
1125+
/// This is an extension in clang: bitwise clonable types act as trivially
1126+
/// copyable types, meaning their underlying bytes can be safely copied by
1127+
/// memcpy or memmove. After the copy, the destination object has the same
1128+
/// object representation.
1129+
///
1130+
/// However, there are cases where it is not safe to copy:
1131+
/// - When sanitizers, such as AddressSanitizer, add padding with poison,
1132+
/// which can cause issues if those poisoned padding bits are accessed.
1133+
/// - Types with Objective-C lifetimes, where specific runtime
1134+
/// semantics may not be preserved during a bitwise copy.
1135+
///
1136+
// FIXME: each call will trigger a full computation, cache the result.
1137+
bool isBitwiseCloneableType(const ASTContext &Context) const;
1138+
11231139
/// Return true if this is a trivially copyable type
11241140
bool isTriviallyCopyConstructibleType(const ASTContext &Context) const;
11251141

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: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2749,6 +2749,41 @@ bool QualType::isTriviallyCopyableType(const ASTContext &Context) const {
27492749
/*IsCopyConstructible=*/false);
27502750
}
27512751

2752+
bool QualType::isBitwiseCloneableType(const ASTContext & Context) const {
2753+
auto CanonicalType = getCanonicalType();
2754+
if (CanonicalType.hasNonTrivialObjCLifetime())
2755+
return false;
2756+
if (CanonicalType->isArrayType())
2757+
return Context.getBaseElementType(CanonicalType)
2758+
.isBitwiseCloneableType(Context);
2759+
2760+
if (CanonicalType->isIncompleteType())
2761+
return false;
2762+
2763+
if (const auto *RD = CanonicalType->getAsRecordDecl()) { // struct/union/class
2764+
// Never allow memcpy when we're adding poisoned padding bits to the struct.
2765+
// Accessing these posioned bits will trigger false alarms on
2766+
// SanitizeAddressFieldPadding etc.
2767+
if (RD->mayInsertExtraPadding())
2768+
return false;
2769+
2770+
for (auto *const Field : RD->fields()) {
2771+
if (!Field->getType().isBitwiseCloneableType(Context))
2772+
return false;
2773+
}
2774+
2775+
if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
2776+
for (auto Base : CXXRD->bases())
2777+
if (!Base.getType().isBitwiseCloneableType(Context))
2778+
return false;
2779+
for (auto VBase : CXXRD->vbases())
2780+
if (!VBase.getType().isBitwiseCloneableType(Context))
2781+
return false;
2782+
}
2783+
}
2784+
return true;
2785+
}
2786+
27522787
bool QualType::isTriviallyCopyConstructibleType(
27532788
const ASTContext &Context) const {
27542789
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: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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+
union U {
13+
S s;
14+
};
15+
16+
struct Derived : S {};
17+
18+
static_assert(!__is_trivially_copyable(S));
19+
#ifdef SANITIZER_ENABLED
20+
// Don't allow memcpy when the struct has poisoned padding bits.
21+
// The sanitizer adds posion padding bits to struct S.
22+
static_assert(sizeof(S) > 16);
23+
static_assert(!__is_bitwise_cloneable(S));
24+
static_assert(sizeof(U) == sizeof(S)); // no padding bit for U.
25+
static_assert(!__is_bitwise_cloneable(U));
26+
static_assert(!__is_bitwise_cloneable(S[2]));
27+
static_assert(!__is_bitwise_cloneable(Derived));
28+
#else
29+
static_assert(sizeof(S) == 16);
30+
static_assert(__is_bitwise_cloneable(S));
31+
static_assert(__is_bitwise_cloneable(U));
32+
static_assert(__is_bitwise_cloneable(S[2]));
33+
static_assert(__is_bitwise_cloneable(Derived));
34+
#endif
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
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));
7+
8+
struct InComplete;
9+
static_assert(!__is_bitwise_cloneable(InComplete));

clang/test/SemaObjCXX/arc-type-traits.mm

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,3 +221,12 @@
221221
TRAIT_IS_TRUE(__is_trivially_relocatable, HasStrong);
222222
TRAIT_IS_FALSE(__is_trivially_relocatable, HasWeak);
223223
TRAIT_IS_TRUE(__is_trivially_relocatable, HasUnsafeUnretained);
224+
225+
// __is_bitwise_cloneable
226+
TRAIT_IS_FALSE(__is_bitwise_cloneable, __strong id);
227+
TRAIT_IS_FALSE(__is_bitwise_cloneable, __weak id);
228+
TRAIT_IS_FALSE(__is_bitwise_cloneable, __autoreleasing id);
229+
TRAIT_IS_TRUE(__is_trivial, __unsafe_unretained id);
230+
TRAIT_IS_FALSE(__is_bitwise_cloneable, HasStrong);
231+
TRAIT_IS_FALSE(__is_bitwise_cloneable, HasWeak);
232+
TRAIT_IS_TRUE(__is_bitwise_cloneable, HasUnsafeUnretained);

0 commit comments

Comments
 (0)