diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -2356,6 +2356,46 @@ Query for this feature with ``__has_builtin(__builtin_assume)``. +.. _langext-__builtin_assume_separate_storage: + +``__builtin_assume_separate_storage`` +-------------------- + +``__builtin_assume_separate_storage`` is used to provide the optimizer with the +knowledge that its two arguments point to separately allocated objects. + +**Syntax**: + +.. code-block:: c++ + + __builtin_assume_separate_storage(const volatile void *, const volatile void *) + +**Example of Use**: + +.. code-block:: c++ + + int foo(int *x, int *y) { + __builtin_assume_separate_storage(x, y); + *x = 0; + *y = 1; + // The optimizer may optimize this to return 0 without reloading from *x. + return *x; + } + +**Description**: + +The arguments to this function are assumed to point into separately allocated +storage (either different variable definitions or different dynamic storage +allocations). The optimizer may use this fact to aid in alias analysis. If the +arguments point into the same storage, the behavior is undefined. Note that the +definition of "storage" here refers to the outermost enclosing allocation of any +particular object (so for example, it's never correct to call this function +passing the addresses of fields in the same struct, elements of the same array, +etc.). + +Query for this feature with ``__has_builtin(__builtin_assume_separate_storage)``. + + ``__builtin_offsetof`` ---------------------- diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -108,6 +108,8 @@ optimizations. - Clang now supports ``__builtin_nondeterministic_value`` that returns a nondeterministic value of the same type as the provided argument. +- Clang now supports ``__builtin_assume_separate_storage`` that indicates that + its arguments point to objects in separate storage allocations. New Compiler Flags ------------------ 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 @@ -1579,6 +1579,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/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -2827,6 +2827,18 @@ 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/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,36 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -o - %s | FileCheck %s +void *nonconst(void); + +// CHECK-LABEL: @test1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[B_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[A:%.*]], ptr [[A_ADDR]], align 8 +// CHECK-NEXT: store ptr [[B:%.*]], ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.assume(i1 true) [ "separate_storage"(ptr [[TMP0]], ptr [[TMP1]]) ] +// CHECK-NEXT: ret 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 actually do so. +// CHECK-LABEL: @test2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[B_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[A:%.*]], ptr [[A_ADDR]], align 8 +// CHECK-NEXT: store ptr [[B:%.*]], ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @nonconst() +// CHECK-NEXT: call void @llvm.assume(i1 true) [ "separate_storage"(ptr [[TMP0]], ptr [[CALL]]) ] +// CHECK-NEXT: ret void +// +void test2(int *a, int *b) { + __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}} +}