diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -1571,6 +1571,7 @@ // Invariants BUILTIN(__builtin_assume, "vb", "nE") +BUILTIN(__builtin_assume_separate_storage, "vvCD*vCD*", "nE") // Multiprecision Arithmetic Builtins. BUILTIN(__builtin_addcb, "UcUcCUcCUcCUc*", "n") diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -13410,6 +13410,7 @@ bool SemaBuiltinArithmeticFence(CallExpr *TheCall); bool SemaBuiltinAssume(CallExpr *TheCall); bool SemaBuiltinAssumeAligned(CallExpr *TheCall); + bool SemaBuiltinAssumeSeparateStorage(CallExpr *TheCall); bool SemaBuiltinLongjmp(CallExpr *TheCall); bool SemaBuiltinSetjmp(CallExpr *TheCall); ExprResult SemaBuiltinAtomicOverloaded(ExprResult TheCallResult); diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -2823,6 +2823,19 @@ Builder.CreateCall(FnAssume, ArgValue); return RValue::get(nullptr); } + case Builtin::BI__builtin_assume_separate_storage: { + const Expr *Arg0 = E->getArg(0); + const Expr *Arg1 = E->getArg(1); + + Value *Value0 = EmitScalarExpr(Arg0); + Value *Value1 = EmitScalarExpr(Arg1); + + Value *Values[] = {Value0, Value1}; + OperandBundleDefT OBD("separate_storage", Values); + Builder.CreateAssumption(ConstantInt::getTrue(getLLVMContext()), {OBD}); + return RValue::get(nullptr); + } + case Builtin::BI__arithmetic_fence: { // Create the builtin call if FastMath is selected, and the target // supports the builtin, otherwise just return the argument. diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -2169,6 +2169,10 @@ if (SemaBuiltinAssumeAligned(TheCall)) return ExprError(); break; + case Builtin::BI__builtin_assume_separate_storage: + if (SemaBuiltinAssumeSeparateStorage(TheCall)) + return ExprError(); + break; case Builtin::BI__builtin_dynamic_object_size: case Builtin::BI__builtin_object_size: if (SemaBuiltinConstantArgRange(TheCall, 1, 0, 3)) @@ -7767,6 +7771,11 @@ return false; } +/// Handle __builtin_assume_separate_storage. For now this is a no-op, but +/// eventually we expect an optional multi-arg variadic version (to handle an +/// excluded range). +bool Sema::SemaBuiltinAssumeSeparateStorage(CallExpr *TheCall) { return false; } + bool Sema::SemaBuiltinOSLogFormat(CallExpr *TheCall) { unsigned BuiltinID = cast(TheCall->getCalleeDecl())->getBuiltinID(); diff --git a/clang/test/CodeGen/builtin-assume-separate-storage.c b/clang/test/CodeGen/builtin-assume-separate-storage.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/builtin-assume-separate-storage.c @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -o - %s | FileCheck %s +void *nonconst(void); + +// CHECK-LABEL: @test1 +void test1(int *a, int *b) { + // CHECK: store ptr %a, ptr [[A_ADDR:%.+]], align + // CHECK: store ptr %b, ptr [[B_ADDR:%.+]], align + // CHECK: [[A:%.+]] = load ptr, ptr [[A_ADDR]] + // CHECK: [[B:%.+]] = load ptr, ptr [[B_ADDR]] + + // CHECK: call void @llvm.assume(i1 true) [ "separate_storage"(ptr [[A]], ptr [[B]]) ] + __builtin_assume_separate_storage(a, b); +} + +// Separate storage assumptions evaluate their arguments unconditionally, like +// assume_aligned but *unlike* assume. Check that we actually do so. +// CHECK-LABEL: @test2 +void test2(int *a, int *b) { + // CHECK: call ptr @nonconst() + // CHECK: call void @llvm.assume + __builtin_assume_separate_storage(a, nonconst()); +} diff --git a/clang/test/Sema/builtin-assume-separate-storage.c b/clang/test/Sema/builtin-assume-separate-storage.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/builtin-assume-separate-storage.c @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify %s + +void *nonconst(void); + +void test1(int *a, int *b) { + __builtin_assume_separate_storage(a, b); + // Separate storage assumptions evaluate their arguments unconditionally, like + // assume_aligned but *unlike* assume. Check that we don't warn on it. + __builtin_assume_separate_storage(a, nonconst()); + __builtin_assume_separate_storage(nonconst(), a); + __builtin_assume_separate_storage(a, 3); // expected-error {{incompatible integer to pointer conversion}} + __builtin_assume_separate_storage(3, a); // expected-error {{incompatible integer to pointer conversion}} +}