Skip to content

Commit b7b0a7a

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 65640c1 commit b7b0a7a

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
@@ -2866,6 +2866,41 @@ etc.).
28662866
28672867
Query for this feature with ``__has_builtin(__builtin_assume_separate_storage)``.
28682868
2869+
``__builtin_assume_dereferenceable``
2870+
-------------------------------------
2871+
2872+
``__builtin_assume_derefernceable`` is used to provide the optimizer with the
2873+
knowledge that the pointer argument P is dereferenceable up to the specified
2874+
number of bytes.
2875+
2876+
**Syntax**:
2877+
2878+
.. code-block:: c++
2879+
2880+
__builtin_assume_dereferenceable(const void *, size_t)
2881+
2882+
**Example of Use**:
2883+
2884+
.. code-block:: c++
2885+
2886+
int foo(int *x, int y) {
2887+
__builtin_assume_dereferenceable(x, 4);
2888+
int z = 0;
2889+
if (y == 1) {
2890+
// The optimizer may execute the load of x unconditionally.
2891+
z = *x;
2892+
}
2893+
return z;
2894+
}
2895+
2896+
**Description**:
2897+
2898+
The arguments to this function provide a start pointer ``P`` and a size ``S``.
2899+
``P`` must be non-null and ``S`` at least 1. The optimizer may assume that
2900+
``S`` bytes are dereferenceable starting at ``P``.
2901+
2902+
Query for this feature with ``__has_builtin(__builtin_assume_dereferenceable)``.
2903+
28692904
28702905
``__builtin_offsetof``
28712906
----------------------

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
@@ -3839,6 +3839,16 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
38393839
AlignmentCI, OffsetValue);
38403840
return RValue::get(PtrValue);
38413841
}
3842+
case Builtin::BI__builtin_assume_dereferenceable: {
3843+
const Expr *Ptr = E->getArg(0);
3844+
Value *PtrValue = EmitScalarExpr(Ptr);
3845+
Value *SizeValue = EmitScalarExpr(E->getArg(1));
3846+
if (SizeValue->getType() != IntPtrTy)
3847+
SizeValue =
3848+
Builder.CreateIntCast(SizeValue, IntPtrTy, false, "casted.size");
3849+
Builder.CreateDereferenceableAssumption(PtrValue, SizeValue);
3850+
return RValue::get(nullptr);
3851+
}
38423852
case Builtin::BI__assume:
38433853
case Builtin::BI__builtin_assume: {
38443854
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
@@ -2684,6 +2684,11 @@ class IRBuilderBase {
26842684
CallInst *CreateAlignmentAssumption(const DataLayout &DL, Value *PtrValue,
26852685
Value *Alignment,
26862686
Value *OffsetValue = nullptr);
2687+
2688+
/// Create an assume intrinsic call that represents an dereferencable
2689+
/// assumption on the provided pointer.
2690+
///
2691+
CallInst *CreateDereferenceableAssumption(Value *PtrValue, Value *SizeValue);
26872692
};
26882693

26892694
/// 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
@@ -1284,6 +1284,16 @@ CallInst *IRBuilderBase::CreateAlignmentAssumption(const DataLayout &DL,
12841284
return CreateAlignmentAssumptionHelper(DL, PtrValue, Alignment, OffsetValue);
12851285
}
12861286

1287+
CallInst *IRBuilderBase::CreateDereferenceableAssumption(Value *PtrValue,
1288+
Value *SizeValue) {
1289+
assert(isa<PointerType>(PtrValue->getType()) &&
1290+
"trying to create an deferenceable assumption on a non-pointer?");
1291+
SmallVector<Value *, 4> Vals({PtrValue, SizeValue});
1292+
OperandBundleDefT<Value *> DereferenceableOpB("dereferenceable", Vals);
1293+
return CreateAssumption(ConstantInt::getTrue(getContext()),
1294+
{DereferenceableOpB});
1295+
}
1296+
12871297
IRBuilderDefaultInserter::~IRBuilderDefaultInserter() = default;
12881298
IRBuilderCallbackInserter::~IRBuilderCallbackInserter() = default;
12891299
IRBuilderFolder::~IRBuilderFolder() = default;

0 commit comments

Comments
 (0)