Index: include/clang/Basic/Builtins.def =================================================================== --- include/clang/Basic/Builtins.def +++ include/clang/Basic/Builtins.def @@ -477,6 +477,7 @@ BUILTIN(__builtin_vsprintf, "ic*cC*a", "nFP:1:") BUILTIN(__builtin_vsnprintf, "ic*zcC*a", "nFP:2:") BUILTIN(__builtin_thread_pointer, "v*", "nc") +BUILTIN(__builtin_launder, "v*v*", "nt") // GCC exception builtins BUILTIN(__builtin_eh_return, "vzv*", "r") // FIXME: Takes intptr_t, not size_t! Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -9337,4 +9337,7 @@ InGroup, DefaultIgnore; def note_shadow_field : Note<"declared here">; +def err_builtin_launder_non_pointer_arg : + Error<"non-pointer argument to '__builtin_launder'">; + } // end of sema component. Index: lib/CodeGen/CGBuiltin.cpp =================================================================== --- lib/CodeGen/CGBuiltin.cpp +++ lib/CodeGen/CGBuiltin.cpp @@ -1669,6 +1669,11 @@ return RValue::get(nullptr); } + case Builtin::BI__builtin_launder: { + Value *Ptr = EmitScalarExpr(E->getArg(0)); + Ptr = Builder.CreateInvariantGroupBarrier(Ptr); + return RValue::get(Ptr); + } case Builtin::BI__sync_fetch_and_add: case Builtin::BI__sync_fetch_and_sub: case Builtin::BI__sync_fetch_and_or: Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -850,6 +850,20 @@ return false; } +static bool SemaBuiltinLaunder(Sema& S, CallExpr *TheCall) { + if (checkArgCount(S, TheCall, 1)) return true; + Expr *Arg = TheCall->getArg(0); + QualType ArgT = Arg->getType(); + if (!ArgT->isPointerType()) { + S.Diag(TheCall->getLocStart(), diag::err_builtin_launder_non_pointer_arg) + << TheCall->getSourceRange(); + return true; + } + TheCall->setType(Arg->getType()); + // FIXME: Add attributes to argument + return false; // OK +} + ExprResult Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, CallExpr *TheCall) { @@ -967,6 +981,10 @@ if (checkArgCount(*this, TheCall, 1)) return true; TheCall->setType(Context.IntTy); break; + case Builtin::BI__builtin_launder: + if (SemaBuiltinLaunder(*this, TheCall)) + return ExprError(); + break; case Builtin::BI__sync_fetch_and_add: case Builtin::BI__sync_fetch_and_add_1: case Builtin::BI__sync_fetch_and_add_2: Index: test/CodeGen/builtins.c =================================================================== --- test/CodeGen/builtins.c +++ test/CodeGen/builtins.c @@ -132,6 +132,8 @@ R(extract_return_addr, (&N)); P(signbit, (1.0)); + R(launder, (&N)); + return 0; } @@ -396,6 +398,20 @@ return __builtin_readcyclecounter(); } +// CHECK-LABEL: define void @test_builtin_launder +void test_builtin_launder(int *p) { + // CHECK: entry + // CHECK-NEXT: %p.addr = alloca i32* + // CHECK-NEXT: %d = alloca i32* + // CHECK-NEXT: store i32* %p, i32** %p.addr, align 8 + // CHECK-NEXT: [[TMP:%.*]] = load i32*, i32** %p.addr + // CHECK-NEXT: [[TMP1:%.*]] = bitcast i32* [[TMP]] to i8* + // CHECK-NEXT: [[TMP2:%.*]] = call i8* @llvm.invariant.group.barrier.p0i8(i8* [[TMP1]]) + // CHECK-NEXT: [[TMP3:%.*]] = bitcast i8* [[TMP2]] to i32* + // CHECK-NEXT: store i32* [[TMP3]], i32** %d + int *d = __builtin_launder(p); +} + // Behavior of __builtin_os_log differs between platforms, so only test on X86 #ifdef __x86_64__ Index: test/Preprocessor/feature_tests.c =================================================================== --- test/Preprocessor/feature_tests.c +++ test/Preprocessor/feature_tests.c @@ -14,6 +14,7 @@ !__has_builtin(__builtin_convertvector) || \ !__has_builtin(__builtin_trap) || \ !__has_builtin(__c11_atomic_init) || \ + !__has_builtin(__builtin_launder) || \ !__has_feature(attribute_analyzer_noreturn) || \ !__has_feature(attribute_overloadable) #error Clang should have these Index: test/Sema/builtins.c =================================================================== --- test/Sema/builtins.c +++ test/Sema/builtins.c @@ -248,3 +248,14 @@ return buf; } + +void test_builtin_launder(char *p, void *vp, const volatile int *ip) { + __builtin_launder(); // expected-error {{too few arguments to function call, expected 1, have 0}} + __builtin_launder(p, p); // expected-error {{too many arguments to function call, expected 1, have 2}} + int x; + __builtin_launder(x); // expected-error {{non-pointer argument to '__builtin_launder'}} + char *d = __builtin_launder(p); + void *vd = __builtin_launder(vp); + const volatile int *id = __builtin_launder(ip); + int *id2 = __builtin_launder(ip); // expected-warning {{discards qualifiers}} +} Index: test/SemaCXX/builtins.cpp =================================================================== --- test/SemaCXX/builtins.cpp +++ test/SemaCXX/builtins.cpp @@ -53,3 +53,22 @@ void synchronize_args() { __sync_synchronize(0); // expected-error {{too many arguments}} } + +#define TEST_TYPE(Ptr, Type) \ +static_assert(__is_same(decltype(__builtin_launder(Ptr)), Type), "expected same type") + +void test_builtin_launder(char *p, void *vp, const volatile int *ip, const float*& fp) { + int x; + __builtin_launder(x); // expected-error {{non-pointer argument to '__builtin_launder'}} + TEST_TYPE(p, char*); + TEST_TYPE(vp, void*); + TEST_TYPE(ip, const volatile int*); + TEST_TYPE(fp, const float*); + char *d = __builtin_launder(p); + void *vd = __builtin_launder(vp); + const volatile int *id = __builtin_launder(ip); + int *id2 = __builtin_launder(ip); // expected-error {{cannot initialize a variable of type 'int *' with an rvalue of type 'const volatile int *'}} + const float* fd = __builtin_launder(fp); +} + +#undef TEST_TYPE