Skip to content

Commit 4f1caac

Browse files
committed
- address review comments
- add doc for the builtin in LanguageExtensions.rst - adjust the existing diagnostics for the new builtin - add release note
1 parent 3ab1a07 commit 4f1caac

File tree

7 files changed

+75
-9
lines changed

7 files changed

+75
-9
lines changed

clang/docs/LanguageExtensions.rst

+48
Original file line numberDiff line numberDiff line change
@@ -2526,6 +2526,54 @@ implemented directly in terms of :ref:`extended vector support
25262526
<langext-vectors>` instead of builtins, in order to reduce the number of
25272527
builtins that we need to implement.
25282528
2529+
``__builtin_start_object_lifetime``
2530+
-----------------------------------
2531+
2532+
The builtin is used to instruct compiler to explicitly create an object in-place
2533+
and start the object lifetime without running any initialisation code.
2534+
2535+
**Syntax**:
2536+
2537+
.. code-block:: c++
2538+
2539+
T* __builtin_start_object_lifetime(T* p)
2540+
2541+
2542+
**Example of Use***:
2543+
2544+
.. code-block:: c++
2545+
2546+
struct Foo {};
2547+
2548+
// [buffer, buffer+sizeof(Foo)) is a memory region whose bytes represent a
2549+
// valid object representation of type Foo.
2550+
Foo* make_foo(char* buffer) {
2551+
return __builtin_start_object_lifetime(reinterpret_cast<Foo*>(buffer));
2552+
}
2553+
2554+
**Description**:
2555+
2556+
This builtin creates an object at the given memory location and start
2557+
the lifetime of the object without running any constructor code. It returns a
2558+
pointer to the same memory that the parameter `p` points to, and the returned
2559+
result can be legitimately used to access the object `T`.
2560+
2561+
It can be used to implement C++23's `std::start_lifetime_as` API.
2562+
Unlike the `std::start_lifetime_as` which only works for implicit-lifetime
2563+
types. This builtin doens't have this restriction, it can apply to
2564+
non-implicit-lifetime types.
2565+
2566+
This builtin is a no-op barrier operation taken by the compiler to address object
2567+
value propagation analysis in an opaque manner appropriately, e.g. suppressing
2568+
certain optimizations.
2569+
2570+
This builtin cannot be called in a ``constexpr`` context.
2571+
2572+
NOTE: this builtin is considered experimental at this time. It is known that it
2573+
can cause TBAA miscompile issues when using with `-fstrict-aliasing` flag (which
2574+
is on by default). Until we fix all TBAA issues (which requires more LLVM IR
2575+
support), we suggest to use it with `-fno-strict-aliasing`.
2576+
25292577
``__builtin_alloca``
25302578
--------------------
25312579

clang/docs/ReleaseNotes.rst

+3
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,9 @@ Non-comprehensive list of changes in this release
207207
- ``__typeof_unqual__`` is available in all C modes as an extension, which behaves
208208
like ``typeof_unqual`` from C23, similar to ``__typeof__`` and ``typeof``.
209209

210+
- Added ``__builtin_start_object_lifetime`` for creating object in-place and
211+
starting object lifetime without running any initialisation code.
212+
210213
New Compiler Flags
211214
------------------
212215
- ``-fsanitize=implicit-bitfield-conversion`` checks implicit truncation and

clang/include/clang/Basic/Builtins.td

+1-1
Original file line numberDiff line numberDiff line change
@@ -926,7 +926,7 @@ def Launder : Builtin {
926926
let Prototype = "void*(void*)";
927927
}
928928

929-
def StartObjectLifeTime : Builtin {
929+
def StartObjectLifetime : Builtin {
930930
let Spellings = ["__builtin_start_object_lifetime"];
931931
let Attributes = [NoThrow, CustomTypeChecking];
932932
let Prototype = "void*(void*)";

clang/include/clang/Basic/DiagnosticSemaKinds.td

+2-2
Original file line numberDiff line numberDiff line change
@@ -12025,9 +12025,9 @@ def warn_noderef_on_non_pointer_or_array : Warning<
1202512025
def warn_noderef_to_dereferenceable_pointer : Warning<
1202612026
"casting to dereferenceable pointer removes 'noderef' attribute">, InGroup<NoDeref>;
1202712027

12028-
def err_builtin_launder_invalid_arg : Error<
12028+
def err_builtin_launder_or_start_object_lifetime_invalid_arg : Error<
1202912029
"%select{non-pointer|function pointer|void pointer}0 argument to "
12030-
"'__builtin_launder' is not allowed">;
12030+
"'%select{__builtin_launder|__builtin_start_object_lifetime}1' is not allowed">;
1203112031

1203212032
def err_builtin_invalid_arg_type: Error <
1203312033
"%ordinal0 argument must be "

clang/lib/CodeGen/CGBuiltin.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -4522,10 +4522,14 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
45224522
return RValue::get(nullptr);
45234523
}
45244524
case Builtin::BI__builtin_start_object_lifetime:
4525+
// FIXME: we need some TBAA fences to prevent strict-aliasing miscompiles.
45254526
case Builtin::BI__builtin_launder: {
45264527
const Expr *Arg = E->getArg(0);
45274528
QualType ArgTy = Arg->getType()->getPointeeType();
45284529
Value *Ptr = EmitScalarExpr(Arg);
4530+
// Arguments of __builtin_launder and __builtin_start_object_lifetime may
4531+
// need the LLVM IR launder.invariant.group intrinsic barrier to prevent
4532+
// alising-based optimizations (e.g. -fstrict-vtable-pointers).
45294533
if (TypeRequiresBuiltinLaunder(CGM, ArgTy))
45304534
Ptr = Builder.CreateLaunderInvariantGroup(Ptr);
45314535

clang/lib/Sema/SemaChecking.cpp

+12-4
Original file line numberDiff line numberDiff line change
@@ -2156,7 +2156,13 @@ static ExprResult PointerAuthAuthAndResign(Sema &S, CallExpr *Call) {
21562156
return Call;
21572157
}
21582158

2159-
static ExprResult BuiltinLaunder(Sema &S, CallExpr *TheCall) {
2159+
// Semantic check for function arguments of __builtin_launder or
2160+
// __builtin_start_object_lifetime.
2161+
static ExprResult SemaBuiltinLaunderOrStartObjectLifetime(Sema &S,
2162+
CallExpr *TheCall,
2163+
unsigned BuiltinID) {
2164+
assert(BuiltinID == Builtin::BI__builtin_launder ||
2165+
BuiltinID == Builtin::BI__builtin_start_object_lifetime);
21602166
if (checkArgCount(S, TheCall, 1))
21612167
return ExprError();
21622168

@@ -2187,8 +2193,10 @@ static ExprResult BuiltinLaunder(Sema &S, CallExpr *TheCall) {
21872193
return std::optional<unsigned>{};
21882194
}();
21892195
if (DiagSelect) {
2190-
S.Diag(TheCall->getBeginLoc(), diag::err_builtin_launder_invalid_arg)
2191-
<< *DiagSelect << TheCall->getSourceRange();
2196+
S.Diag(TheCall->getBeginLoc(),
2197+
diag::err_builtin_launder_or_start_object_lifetime_invalid_arg)
2198+
<< *DiagSelect << (BuiltinID == Builtin::BI__builtin_launder ? 0 : 1)
2199+
<< TheCall->getSourceRange();
21922200
return ExprError();
21932201
}
21942202

@@ -2644,7 +2652,7 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
26442652
}
26452653
case Builtin::BI__builtin_start_object_lifetime:
26462654
case Builtin::BI__builtin_launder:
2647-
return BuiltinLaunder(*this, TheCall);
2655+
return SemaBuiltinLaunderOrStartObjectLifetime(*this, TheCall, BuiltinID);
26482656
case Builtin::BI__sync_fetch_and_add:
26492657
case Builtin::BI__sync_fetch_and_add_1:
26502658
case Builtin::BI__sync_fetch_and_add_2:

clang/test/SemaCXX/builtins.cpp

+5-2
Original file line numberDiff line numberDiff line change
@@ -172,14 +172,17 @@ constexpr int test_non_constexpr(int i) { // expected-error {{constexpr function
172172
}
173173

174174
struct Incomplete; // expected-note {{forward declaration}}
175-
void test_incomplete(Incomplete *i) {
175+
void test_diag(Incomplete *i) {
176176
// Requires a complete type
177177
__builtin_start_object_lifetime(i); // expected-error {{incomplete type 'Incomplete' where a complete type is required}}
178+
179+
int x;
180+
__builtin_start_object_lifetime(x); // expected-error {{non-pointer argument to '__builtin_start_object_lifetime' is not allowed}}
178181
}
179182

180183
// The builtin is type-generic.
181184
#define TEST_TYPE(Ptr, Type) \
182-
static_assert(__is_same(decltype(__builtin_launder(Ptr)), Type), "expected same type")
185+
static_assert(__is_same(decltype(__builtin_start_object_lifetime(Ptr)), Type), "expected same type")
183186
void test_type_generic() {
184187
char * p;
185188
int * i;

0 commit comments

Comments
 (0)