Skip to content

Commit 3ab1a07

Browse files
committed
[clang] Add __builtin_start_object_lifetime builtin.
This patch implements a clang built `__builtin_start_object_lifetime`, it has the same semantics as C++23's `std::start_lifetime_as`, but without the implicit-lifetime type restriction, it can be used for implementing `std::start_lifetime_as` in the future. Due to the current clang lowering, the builtin reuses the existing `__builtin_launder` implementation: - it is a no-op for the most part; - with `-fstrict-vtable-pointers` flag, we update the vtpr assumption correctly (mark the load/store vptr with appropriate invariant group intrinsics) to prevent incorrect vptr load folding; - for now, the builtin is non-constant, cannot be executed in constant evaluation; CAVEAT: - this builtin may cause TBAA miscomplies without the `-fno-strict-alias` flag. These TBAA miscompiles are known issues and may need more LLVM IR support for the fix, fixing them is orthogonal to the implementaton of the builtin. Context: https://discourse.llvm.org/t/extension-for-creating-objects-via-memcpy/76961
1 parent 83bc7b5 commit 3ab1a07

File tree

6 files changed

+100
-1
lines changed

6 files changed

+100
-1
lines changed

clang/include/clang/Basic/Builtins.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,12 @@ def Launder : Builtin {
926926
let Prototype = "void*(void*)";
927927
}
928928

929+
def StartObjectLifeTime : Builtin {
930+
let Spellings = ["__builtin_start_object_lifetime"];
931+
let Attributes = [NoThrow, CustomTypeChecking];
932+
let Prototype = "void*(void*)";
933+
}
934+
929935
def IsConstantEvaluated : LangBuiltin<"CXX_LANG"> {
930936
let Spellings = ["__builtin_is_constant_evaluated"];
931937
let Attributes = [NoThrow, Constexpr];

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4521,6 +4521,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
45214521

45224522
return RValue::get(nullptr);
45234523
}
4524+
case Builtin::BI__builtin_start_object_lifetime:
45244525
case Builtin::BI__builtin_launder: {
45254526
const Expr *Arg = E->getArg(0);
45264527
QualType ArgTy = Arg->getType()->getPointeeType();

clang/lib/Sema/SemaChecking.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "clang/AST/TypeLoc.h"
3939
#include "clang/AST/UnresolvedSet.h"
4040
#include "clang/Basic/AddressSpaces.h"
41+
#include "clang/Basic/Builtins.h"
4142
#include "clang/Basic/CharInfo.h"
4243
#include "clang/Basic/Diagnostic.h"
4344
#include "clang/Basic/IdentifierTable.h"
@@ -2641,6 +2642,7 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
26412642
TheCall->setType(Context.IntTy);
26422643
break;
26432644
}
2645+
case Builtin::BI__builtin_start_object_lifetime:
26442646
case Builtin::BI__builtin_launder:
26452647
return BuiltinLaunder(*this, TheCall);
26462648
case Builtin::BI__sync_fetch_and_add:

clang/test/CodeGen/builtins.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ int main(void) {
143143
P(signbit, (1.0));
144144

145145
R(launder, (&N));
146+
R(start_object_lifetime, (&N));
146147

147148
return 0;
148149
}
@@ -511,6 +512,15 @@ void test_builtin_launder(int *p) {
511512
int *d = __builtin_launder(p);
512513
}
513514

515+
/// It should be a NOP in C since there are no vtables.
516+
// CHECK-LABEL: define{{.*}} void @test_builtin_start_object_lifetime
517+
void test_builtin_start_object_lifetime(int *p) {
518+
// CHECK: [[TMP:%.*]] = load ptr,
519+
// CHECK-NOT: @llvm.launder
520+
// CHECK: store ptr [[TMP]],
521+
int *d = __builtin_start_object_lifetime(p);
522+
}
523+
514524
// __warn_memset_zero_len should be NOP, see https://sourceware.org/bugzilla/show_bug.cgi?id=25399
515525
// CHECK-LABEL: define{{.*}} void @test___warn_memset_zero_len
516526
void test___warn_memset_zero_len(void) {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -fstrict-vtable-pointers -o - %s \
2+
// RUN: | FileCheck --check-prefixes=CHECK,CHECK-STRICT %s
3+
// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -o - %s \
4+
// RUN: | FileCheck --check-prefixes=CHECK,CHECK-NONSTRICT %s
5+
6+
struct TestVirtualFn {
7+
virtual void foo();
8+
};
9+
// CHECK-LABEL: define{{.*}} void @test_dynamic_class
10+
extern "C" void test_dynamic_class(TestVirtualFn *p) {
11+
// CHECK: store ptr %p, ptr %p.addr
12+
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr %p.addr
13+
14+
// CHECK-NONSTRICT-NEXT: store ptr [[TMP0]], ptr %d
15+
16+
// CHECK-STRICT-NEXT: [[TMP2:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[TMP0]])
17+
// CHECK-STRICT-NEXT: store ptr [[TMP2]], ptr %d
18+
19+
// CHECK-NEXT: ret void
20+
TestVirtualFn *d = __builtin_start_object_lifetime(p);
21+
}
22+
23+
// CHECK-LABEL: define{{.*}} void @test_scalar_pointer
24+
extern "C" void test_scalar_pointer(int *p) {
25+
// CHECK: entry
26+
// CHECK-NEXT: %p.addr = alloca ptr
27+
// CHECK-NEXT: %d = alloca ptr
28+
// CHECK-NEXT: store ptr %p, ptr %p.addr, align 8
29+
// CHECK-NEXT: [[TMP:%.*]] = load ptr, ptr %p.addr
30+
// CHECK-NEXT: store ptr [[TMP]], ptr %d
31+
// CHECK-NEXT: ret void
32+
int *d = __builtin_start_object_lifetime(p);
33+
}
34+
35+
struct TestNoInvariant {
36+
int x;
37+
};
38+
// CHECK-LABEL: define{{.*}} void @test_non_dynamic_class
39+
extern "C" void test_non_dynamic_class(TestNoInvariant *p) {
40+
// CHECK: entry
41+
// CHECK-NOT: llvm.launder.invariant.group
42+
// CHECK-NEXT: %p.addr = alloca ptr, align 8
43+
// CHECK-NEXT: %d = alloca ptr
44+
// CHECK-NEXT: store ptr %p, ptr %p.addr
45+
// CHECK-NEXT: [[TMP:%.*]] = load ptr, ptr %p.addr
46+
// CHECK-NEXT: store ptr [[TMP]], ptr %d
47+
// CHECK-NEXT: ret void
48+
TestNoInvariant *d = __builtin_start_object_lifetime(p);
49+
}

clang/test/SemaCXX/builtins.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 %s -fsyntax-only -verify -std=c++11 -fcxx-exceptions
1+
// RUN: %clang_cc1 %s -fsyntax-only -verify -DCXX11 -std=c++11 -fcxx-exceptions
22
// RUN: %clang_cc1 %s -fsyntax-only -verify -std=c++1z -fcxx-exceptions
33
typedef const struct __CFString * CFStringRef;
44
#define CFSTR __builtin___CFStringMakeConstantString
@@ -161,6 +161,37 @@ void test_noexcept(int *i) {
161161
#undef TEST_TYPE
162162
} // end namespace test_launder
163163

164+
namespace test_start_object_lifetime {
165+
// The builtin is non-constant.
166+
constexpr int test_non_constexpr(int i) { // expected-error {{constexpr function never produces a constant expression}}
167+
__builtin_start_object_lifetime(&i); // expected-note {{subexpression not valid in a constant expression}}
168+
#ifdef CXX11
169+
// expected-warning@-2 {{use of this statement in a constexpr function is a C++14 extension}}
170+
#endif
171+
return 0;
172+
}
173+
174+
struct Incomplete; // expected-note {{forward declaration}}
175+
void test_incomplete(Incomplete *i) {
176+
// Requires a complete type
177+
__builtin_start_object_lifetime(i); // expected-error {{incomplete type 'Incomplete' where a complete type is required}}
178+
}
179+
180+
// The builtin is type-generic.
181+
#define TEST_TYPE(Ptr, Type) \
182+
static_assert(__is_same(decltype(__builtin_launder(Ptr)), Type), "expected same type")
183+
void test_type_generic() {
184+
char * p;
185+
int * i;
186+
TEST_TYPE(p, char*);
187+
TEST_TYPE(i, int*);
188+
}
189+
// The builtin is noexcept.
190+
void test_noexcept(int *i) {
191+
static_assert(noexcept(__builtin_start_object_lifetime(i)), "");
192+
}
193+
}
194+
164195
template<typename T> void test_builtin_complex(T v, double d) {
165196
(void)__builtin_complex(v, d); // expected-error {{different types}} expected-error {{not a real floating}}
166197
(void)__builtin_complex(d, v); // expected-error {{different types}} expected-error {{not a real floating}}

0 commit comments

Comments
 (0)