Skip to content

Commit 596d134

Browse files
committed
[Clang] Add __builtin_assume_dereferenceable to encode deref assumption.
This patch adds a new __builtin_assume_dereferenceable to encode dereferenceability of a pointer using llvm.assume with an operand bundle. For now the builtin only accepts constant sizes, I am planning to drop this restriction in a follow-up change. This can be used to better optimize cases where a pointer is known to be dereferenceable, e.g. unconditionally loading from p2 when vectorizing the loop. int *get_ptr(); void foo(int* src, int x) { int *p2 = get_ptr(); __builtin_assume_aligned(p2, 4); __builtin_assume_dereferenceable(p2, 4000); for (unsigned I = 0; I != 1000; ++I) { int x = src[I]; if (x == 0) x = p2[I]; src[I] = x; } }
1 parent 32bc029 commit 596d134

File tree

7 files changed

+141
-0
lines changed

7 files changed

+141
-0
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2761,6 +2761,41 @@ etc.).
27612761
27622762
Query for this feature with ``__has_builtin(__builtin_assume_separate_storage)``.
27632763
2764+
``__builtin_assume_dereferenceable``
2765+
-------------------------------------
2766+
2767+
``__builtin_assume_derefernceable`` is used to provide the optimizer with the
2768+
knowledge that the pointer argument P is dereferenceable up to the specified
2769+
number of bytes.
2770+
2771+
**Syntax**:
2772+
2773+
.. code-block:: c++
2774+
2775+
__builtin_assume_dereferenceable(const void *, size_t)
2776+
2777+
**Example of Use**:
2778+
2779+
.. code-block:: c++
2780+
2781+
int foo(int *x, int y) {
2782+
__builtin_assume_dereferenceable(x, 4);
2783+
int z = 0;
2784+
if (y == 1) {
2785+
// The optimizer may execute the load of x unconditionally.
2786+
z = *x;
2787+
}
2788+
return z;
2789+
}
2790+
2791+
**Description**:
2792+
2793+
The arguments to this function provide a start pointer ``P`` and a size ``S``.
2794+
``P`` must be non-null and ``S`` at least 1. The optimizer may assume that
2795+
``S`` bytes are dereferenceable starting at ``P``.
2796+
2797+
Query for this feature with ``__has_builtin(__builtin_assume_dereferenceable)``.
2798+
27642799
27652800
``__builtin_offsetof``
27662801
----------------------

clang/include/clang/Basic/Builtins.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,12 @@ def BuiltinAssumeAligned : Builtin {
839839
let Prototype = "void*(void const*, size_t, ...)";
840840
}
841841

842+
def BuiltinAssumeDereferenceable : Builtin {
843+
let Spellings = ["__builtin_assume_dereferenceable"];
844+
let Attributes = [NoThrow, Const, Constexpr];
845+
let Prototype = "void(void const*, _Constant size_t)";
846+
}
847+
842848
def BuiltinFree : Builtin {
843849
let Spellings = ["__builtin_free"];
844850
let Attributes = [FunctionWithBuiltinPrefix, NoThrow];

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3726,6 +3726,16 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
37263726
AlignmentCI, OffsetValue);
37273727
return RValue::get(PtrValue);
37283728
}
3729+
case Builtin::BI__builtin_assume_dereferenceable: {
3730+
const Expr *Ptr = E->getArg(0);
3731+
Value *PtrValue = EmitScalarExpr(Ptr);
3732+
Value *SizeValue = EmitScalarExpr(E->getArg(1));
3733+
if (SizeValue->getType() != IntPtrTy)
3734+
SizeValue =
3735+
Builder.CreateIntCast(SizeValue, IntPtrTy, false, "casted.size");
3736+
Builder.CreateDereferenceableAssumption(PtrValue, SizeValue);
3737+
return RValue::get(nullptr);
3738+
}
37293739
case Builtin::BI__assume:
37303740
case Builtin::BI__builtin_assume: {
37313741
if (E->getArg(0)->HasSideEffects(getContext()))
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
2+
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -o - %s | FileCheck %s
3+
4+
// CHECK-LABEL: @test1(
5+
// CHECK-NEXT: entry:
6+
// CHECK-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8
7+
// CHECK-NEXT: store ptr [[A:%.*]], ptr [[A_ADDR]], align 8
8+
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8
9+
// CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[TMP0]], i64 10) ]
10+
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[A_ADDR]], align 8
11+
// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 0
12+
// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
13+
// CHECK-NEXT: ret i32 [[TMP2]]
14+
//
15+
int test1(int *a) {
16+
__builtin_assume_dereferenceable(a, 10);
17+
return a[0];
18+
}
19+
20+
// CHECK-LABEL: @test2(
21+
// CHECK-NEXT: entry:
22+
// CHECK-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8
23+
// CHECK-NEXT: store ptr [[A:%.*]], ptr [[A_ADDR]], align 8
24+
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8
25+
// CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[TMP0]], i64 32) ]
26+
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[A_ADDR]], align 8
27+
// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 0
28+
// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
29+
// CHECK-NEXT: ret i32 [[TMP2]]
30+
//
31+
int test2(int *a) {
32+
__builtin_assume_dereferenceable(a, 32ull);
33+
return a[0];
34+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// RUN: %clang_cc1 -DSIZE_T_64 -fsyntax-only -Wno-strict-prototypes -triple x86_64-linux -verify %s
2+
3+
int test1(int *a) {
4+
__builtin_assume_dereferenceable(a, 32);
5+
return a[0];
6+
}
7+
8+
int test2(int *a) {
9+
__builtin_assume_dereferenceable(a, 32ull);
10+
return a[0];
11+
}
12+
13+
int test3(int *a) {
14+
__builtin_assume_dereferenceable(a, 32u);
15+
return a[0];
16+
}
17+
18+
int test4(int *a, unsigned size) {
19+
a = __builtin_assume_dereferenceable(a, size); // expected-error {{argument to '__builtin_assume_dereferenceable' must be a constant integer}}
20+
return a[0];
21+
}
22+
23+
int test5(int *a, unsigned long long size) {
24+
a = __builtin_assume_dereferenceable(a, size); // expected-error {{argument to '__builtin_assume_dereferenceable' must be a constant integer}}
25+
return a[0];
26+
}
27+
28+
int test6(float a) {
29+
__builtin_assume_dereferenceable(a, 2); // expected-error {{passing 'float' to parameter of incompatible type 'const void *'}}
30+
return 0;;
31+
}
32+
33+
int test7(int *a) {
34+
__builtin_assume_dereferenceable(a, 32, 1); // expected-error {{too many arguments to function call, expected 2, have 3}}
35+
return a[0];
36+
}
37+
38+
int test8(int *a) {
39+
__builtin_assume_dereferenceable(a); // expected-error {{too few arguments to function call, expected 2, have 1}}
40+
return a[0];
41+
}

llvm/include/llvm/IR/IRBuilder.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2678,6 +2678,11 @@ class IRBuilderBase {
26782678
CallInst *CreateAlignmentAssumption(const DataLayout &DL, Value *PtrValue,
26792679
Value *Alignment,
26802680
Value *OffsetValue = nullptr);
2681+
2682+
/// Create an assume intrinsic call that represents an dereferencable
2683+
/// assumption on the provided pointer.
2684+
///
2685+
CallInst *CreateDereferenceableAssumption(Value *PtrValue, Value *SizeValue);
26812686
};
26822687

26832688
/// This provides a uniform API for creating instructions and inserting

llvm/lib/IR/IRBuilder.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1274,6 +1274,16 @@ CallInst *IRBuilderBase::CreateAlignmentAssumption(const DataLayout &DL,
12741274
return CreateAlignmentAssumptionHelper(DL, PtrValue, Alignment, OffsetValue);
12751275
}
12761276

1277+
CallInst *IRBuilderBase::CreateDereferenceableAssumption(Value *PtrValue,
1278+
Value *SizeValue) {
1279+
assert(isa<PointerType>(PtrValue->getType()) &&
1280+
"trying to create an deferenceable assumption on a non-pointer?");
1281+
SmallVector<Value *, 4> Vals({PtrValue, SizeValue});
1282+
OperandBundleDefT<Value *> DereferenceableOpB("dereferenceable", Vals);
1283+
return CreateAssumption(ConstantInt::getTrue(getContext()),
1284+
{DereferenceableOpB});
1285+
}
1286+
12771287
IRBuilderDefaultInserter::~IRBuilderDefaultInserter() = default;
12781288
IRBuilderCallbackInserter::~IRBuilderCallbackInserter() = default;
12791289
IRBuilderFolder::~IRBuilderFolder() = default;

0 commit comments

Comments
 (0)