Skip to content

Commit 68b26fe

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 7fcd58b commit 68b26fe

File tree

7 files changed

+75
-9
lines changed

7 files changed

+75
-9
lines changed

clang/docs/LanguageExtensions.rst

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

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ Non-comprehensive list of changes in this release
193193
with support for any unsigned integer type. Like the previous builtins, these
194194
new builtins are constexpr and may be used in constant expressions.
195195

196+
- Added ``__builtin_start_object_lifetime`` for creating object in-place and
197+
starting object lifetime without running any initialisation code.
198+
196199
New Compiler Flags
197200
------------------
198201

clang/include/clang/Basic/Builtins.td

Lines changed: 1 addition & 1 deletion
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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12013,9 +12013,9 @@ def warn_noderef_on_non_pointer_or_array : Warning<
1201312013
def warn_noderef_to_dereferenceable_pointer : Warning<
1201412014
"casting to dereferenceable pointer removes 'noderef' attribute">, InGroup<NoDeref>;
1201512015

12016-
def err_builtin_launder_invalid_arg : Error<
12016+
def err_builtin_launder_or_start_object_lifetime_invalid_arg : Error<
1201712017
"%select{non-pointer|function pointer|void pointer}0 argument to "
12018-
"'__builtin_launder' is not allowed">;
12018+
"'%select{__builtin_launder|__builtin_start_object_lifetime}1' is not allowed">;
1201912019

1202012020
def err_builtin_invalid_arg_type: Error <
1202112021
"%ordinal0 argument must be "

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4510,10 +4510,14 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
45104510
return RValue::get(nullptr);
45114511
}
45124512
case Builtin::BI__builtin_start_object_lifetime:
4513+
// FIXME: we need some TBAA fences to prevent strict-aliasing miscompiles.
45134514
case Builtin::BI__builtin_launder: {
45144515
const Expr *Arg = E->getArg(0);
45154516
QualType ArgTy = Arg->getType()->getPointeeType();
45164517
Value *Ptr = EmitScalarExpr(Arg);
4518+
// Arguments of __builtin_launder and __builtin_start_object_lifetime may
4519+
// need the LLVM IR launder.invariant.group intrinsic barrier to prevent
4520+
// alising-based optimizations (e.g. -fstrict-vtable-pointers).
45174521
if (TypeRequiresBuiltinLaunder(CGM, ArgTy))
45184522
Ptr = Builder.CreateLaunderInvariantGroup(Ptr);
45194523

clang/lib/Sema/SemaChecking.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2157,7 +2157,13 @@ static ExprResult SemaPointerAuthAuthAndResign(Sema &S, CallExpr *Call) {
21572157
return Call;
21582158
}
21592159

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

@@ -2188,8 +2194,10 @@ static ExprResult SemaBuiltinLaunder(Sema &S, CallExpr *TheCall) {
21882194
return std::optional<unsigned>{};
21892195
}();
21902196
if (DiagSelect) {
2191-
S.Diag(TheCall->getBeginLoc(), diag::err_builtin_launder_invalid_arg)
2192-
<< *DiagSelect << TheCall->getSourceRange();
2197+
S.Diag(TheCall->getBeginLoc(),
2198+
diag::err_builtin_launder_or_start_object_lifetime_invalid_arg)
2199+
<< *DiagSelect << (BuiltinID == Builtin::BI__builtin_launder ? 0 : 1)
2200+
<< TheCall->getSourceRange();
21932201
return ExprError();
21942202
}
21952203

@@ -2645,7 +2653,7 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
26452653
}
26462654
case Builtin::BI__builtin_start_object_lifetime:
26472655
case Builtin::BI__builtin_launder:
2648-
return SemaBuiltinLaunder(*this, TheCall);
2656+
return SemaBuiltinLaunderOrStartObjectLifetime(*this, TheCall, BuiltinID);
26492657
case Builtin::BI__sync_fetch_and_add:
26502658
case Builtin::BI__sync_fetch_and_add_1:
26512659
case Builtin::BI__sync_fetch_and_add_2:

clang/test/SemaCXX/builtins.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,14 +167,17 @@ constexpr int test_non_constexpr(int i) { // expected-error {{constexpr function
167167
}
168168

169169
struct Incomplete; // expected-note {{forward declaration}}
170-
void test_incomplete(Incomplete *i) {
170+
void test_diag(Incomplete *i) {
171171
// Requires a complete type
172172
__builtin_start_object_lifetime(i); // expected-error {{incomplete type 'Incomplete' where a complete type is required}}
173+
174+
int x;
175+
__builtin_start_object_lifetime(x); // expected-error {{non-pointer argument to '__builtin_start_object_lifetime' is not allowed}}
173176
}
174177

175178
// The builtin is type-generic.
176179
#define TEST_TYPE(Ptr, Type) \
177-
static_assert(__is_same(decltype(__builtin_launder(Ptr)), Type), "expected same type")
180+
static_assert(__is_same(decltype(__builtin_start_object_lifetime(Ptr)), Type), "expected same type")
178181
void test_type_generic() {
179182
char * p;
180183
int * i;

0 commit comments

Comments
 (0)