Skip to content

Commit 89d480f

Browse files
fhahnjoaosaffran
authored andcommitted
[AssumeBundles] Dereferenceable used in bundle only applies at assume. (llvm#126117)
Update LangRef and code using `Dereferenceable` in assume bundles to only use the information if it is safe at the point of use. `Dereferenceable` in an assume bundle is only guaranteed at the point of the assumption, but may not be guaranteed at later points, because the pointer may have been freed. Update code using `Dereferenceable` to only use it if the pointer cannot be freed. This can further be refined to check if the pointer could be freed between assume and use. This follows up on llvm#123196. With that change, it should be safe to expose dereferenceable assumptions more widely as in llvm#121789 PR: llvm#126117
1 parent 2eab36e commit 89d480f

File tree

7 files changed

+191
-15
lines changed

7 files changed

+191
-15
lines changed

llvm/docs/LangRef.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1474,7 +1474,9 @@ Currently, only the following parameter attributes are defined:
14741474
``null_pointer_is_valid`` function attribute is present.
14751475
``n`` should be a positive number. The pointer should be well defined,
14761476
otherwise it is undefined behavior. This means ``dereferenceable(<n>)``
1477-
implies ``noundef``.
1477+
implies ``noundef``. When used in an assume operand bundle, more restricted
1478+
semantics apply. See :ref:`assume operand bundles <assume_opbundles>` for
1479+
more details.
14781480

14791481
``dereferenceable_or_null(<n>)``
14801482
This indicates that the parameter or return value isn't both
@@ -2929,6 +2931,11 @@ the behavior is undefined, unless one of the following exceptions applies:
29292931
(including a zero alignment). If this is the case, then the pointer value
29302932
must be a null pointer, otherwise the behavior is undefined.
29312933

2934+
* ``dereferenceable(<n>)`` operand bundles only guarantee the pointer is
2935+
dereferenceable at the point of the assumption. The pointer may not be
2936+
dereferenceable at later pointers, e.g. because it could have been
2937+
freed.
2938+
29322939
In addition to allowing operand bundles encoding function and parameter
29332940
attributes, an assume operand bundle my also encode a ``separate_storage``
29342941
operand bundle. This has the form:

llvm/lib/Analysis/Loads.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@
2626

2727
using namespace llvm;
2828

29-
extern cl::opt<bool> UseDerefAtPointSemantics;
30-
3129
static bool isAligned(const Value *Base, Align Alignment,
3230
const DataLayout &DL) {
3331
return Base->getPointerAlignment(DL) >= Alignment;
@@ -171,7 +169,12 @@ static bool isDereferenceableAndAlignedPointer(
171169
Size, DL, CtxI, AC, DT, TLI,
172170
Visited, MaxDepth);
173171

174-
if (CtxI && (!UseDerefAtPointSemantics || !V->canBeFreed())) {
172+
// Dereferenceable information from assumptions is only valid if the value
173+
// cannot be freed between the assumption and use. For now just use the
174+
// information for values that cannot be freed in the function.
175+
// TODO: More precisely check if the pointer can be freed between assumption
176+
// and use.
177+
if (CtxI && !V->canBeFreed()) {
175178
/// Look through assumes to see if both dereferencability and alignment can
176179
/// be proven by an assume if needed.
177180
RetainedKnowledge AlignRK;

llvm/test/Analysis/ValueTracking/assume-queries-counter.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ define dso_local i1 @test2(ptr readonly %0) {
4747
ret i1 %2
4848
}
4949

50-
define dso_local i32 @test4(ptr readonly %0, i1 %cond) {
50+
define dso_local i32 @test4(ptr readonly %0, i1 %cond) nofree nosync {
5151
; COUNTER1-LABEL: @test4(
5252
; COUNTER1-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[TMP0:%.*]], i32 4) ]
5353
; COUNTER1-NEXT: br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]]

llvm/test/Analysis/ValueTracking/assume.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ define dso_local i1 @test2(ptr readonly %0) {
5757
ret i1 %2
5858
}
5959

60-
define dso_local i32 @test4(ptr readonly %0, i1 %cond) {
60+
define dso_local i32 @test4(ptr readonly %0, i1 %cond) nofree nosync {
6161
; CHECK-LABEL: @test4(
6262
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[TMP0:%.*]], i32 4) ]
6363
; CHECK-NEXT: br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]]
@@ -91,7 +91,7 @@ A:
9191
ret i32 %6
9292
}
9393

94-
define dso_local i32 @test4a(ptr readonly %0, i1 %cond) {
94+
define dso_local i32 @test4a(ptr readonly %0, i1 %cond) nofree nosync {
9595
; CHECK-LABEL: @test4a(
9696
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[TMP0:%.*]], i32 4), "align"(ptr [[TMP0]], i32 8) ]
9797
; CHECK-NEXT: br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]]

llvm/test/Transforms/LICM/hoist-speculatable-load.ll

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ define void @f(i32 %ptr_i, ptr %ptr2, i1 %cond) {
1111
; CHECK-NEXT: store i32 0, ptr [[PTR2:%.*]], align 4
1212
; CHECK-NEXT: br label [[FOR_BODY_LR_PH]]
1313
; CHECK: for.body.lr.ph:
14-
; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PTR]], align 4
1514
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
1615
; CHECK: for.body:
1716
; CHECK-NEXT: [[I_08:%.*]] = phi i32 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INC:%.*]], [[IF_END:%.*]] ]
1817
; CHECK-NEXT: br i1 [[COND]], label [[IF_END]], label [[IF:%.*]]
1918
; CHECK: if:
19+
; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PTR]], align 4, !invariant.load [[META0:![0-9]+]]
2020
; CHECK-NEXT: store i32 [[TMP0]], ptr [[PTR2]], align 4
2121
; CHECK-NEXT: br label [[IF_END]]
2222
; CHECK: if.end:
@@ -56,4 +56,112 @@ exit: ; preds = %if.end, %entry
5656
ret void
5757
}
5858

59+
define void @f_nofree_nosync(i32 %ptr_i, ptr %ptr2, i1 %cond) nofree nosync {
60+
; CHECK-LABEL: @f_nofree_nosync(
61+
; CHECK-NEXT: entry:
62+
; CHECK-NEXT: [[PTR:%.*]] = inttoptr i32 [[PTR_I:%.*]] to ptr
63+
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR]], i32 16), "dereferenceable"(ptr [[PTR]], i32 16) ]
64+
; CHECK-NEXT: br i1 [[COND:%.*]], label [[FOR_BODY_LR_PH:%.*]], label [[IF0:%.*]]
65+
; CHECK: if0:
66+
; CHECK-NEXT: store i32 0, ptr [[PTR2:%.*]], align 4
67+
; CHECK-NEXT: br label [[FOR_BODY_LR_PH]]
68+
; CHECK: for.body.lr.ph:
69+
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
70+
; CHECK: for.body:
71+
; CHECK-NEXT: [[I_08:%.*]] = phi i32 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INC:%.*]], [[IF_END:%.*]] ]
72+
; CHECK-NEXT: br i1 [[COND]], label [[IF_END]], label [[IF:%.*]]
73+
; CHECK: if:
74+
; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PTR]], align 4, !invariant.load [[META0]]
75+
; CHECK-NEXT: store i32 [[TMP0]], ptr [[PTR2]], align 4
76+
; CHECK-NEXT: br label [[IF_END]]
77+
; CHECK: if.end:
78+
; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_08]], 1
79+
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[INC]], 2
80+
; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[EXIT:%.*]]
81+
; CHECK: exit:
82+
; CHECK-NEXT: ret void
83+
;
84+
entry:
85+
%ptr = inttoptr i32 %ptr_i to ptr
86+
call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i32 16), "dereferenceable"(ptr %ptr, i32 16) ]
87+
br i1 %cond, label %for.body.lr.ph, label %if0
88+
89+
if0:
90+
store i32 0, ptr %ptr2, align 4
91+
br label %for.body.lr.ph
92+
93+
for.body.lr.ph: ; preds = %entry
94+
br label %for.body
95+
96+
for.body: ; preds = %for.body.lr.ph, %if.end
97+
%i.08 = phi i32 [ 0, %for.body.lr.ph ], [ %inc, %if.end ]
98+
br i1 %cond, label %if.end, label %if
99+
100+
if:
101+
%0 = load i32, ptr %ptr, align 4, !invariant.load !{}
102+
store i32 %0, ptr %ptr2, align 4
103+
br label %if.end
104+
105+
if.end: ; preds = %for.body
106+
%inc = add nuw nsw i32 %i.08, 1
107+
%cmp = icmp slt i32 %inc, 2
108+
br i1 %cmp, label %for.body, label %exit
109+
110+
exit: ; preds = %if.end, %entry
111+
ret void
112+
}
113+
114+
define void @f_without_ptrtoint_and_with_nofree_nosync(ptr %ptr, ptr %ptr2, i1 %cond) nofree nosync {
115+
; CHECK-LABEL: @f_without_ptrtoint_and_with_nofree_nosync(
116+
; CHECK-NEXT: entry:
117+
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR:%.*]], i32 16), "dereferenceable"(ptr [[PTR]], i32 16) ]
118+
; CHECK-NEXT: br i1 [[COND:%.*]], label [[FOR_BODY_LR_PH:%.*]], label [[IF0:%.*]]
119+
; CHECK: if0:
120+
; CHECK-NEXT: store i32 0, ptr [[PTR2:%.*]], align 4
121+
; CHECK-NEXT: br label [[FOR_BODY_LR_PH]]
122+
; CHECK: for.body.lr.ph:
123+
; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PTR]], align 4
124+
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
125+
; CHECK: for.body:
126+
; CHECK-NEXT: [[I_08:%.*]] = phi i32 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INC:%.*]], [[IF_END:%.*]] ]
127+
; CHECK-NEXT: br i1 [[COND]], label [[IF_END]], label [[IF:%.*]]
128+
; CHECK: if:
129+
; CHECK-NEXT: store i32 [[TMP0]], ptr [[PTR2]], align 4
130+
; CHECK-NEXT: br label [[IF_END]]
131+
; CHECK: if.end:
132+
; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_08]], 1
133+
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[INC]], 2
134+
; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[EXIT:%.*]]
135+
; CHECK: exit:
136+
; CHECK-NEXT: ret void
137+
;
138+
entry:
139+
call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i32 16), "dereferenceable"(ptr %ptr, i32 16) ]
140+
br i1 %cond, label %for.body.lr.ph, label %if0
141+
142+
if0:
143+
store i32 0, ptr %ptr2, align 4
144+
br label %for.body.lr.ph
145+
146+
for.body.lr.ph: ; preds = %entry
147+
br label %for.body
148+
149+
for.body: ; preds = %for.body.lr.ph, %if.end
150+
%i.08 = phi i32 [ 0, %for.body.lr.ph ], [ %inc, %if.end ]
151+
br i1 %cond, label %if.end, label %if
152+
153+
if:
154+
%0 = load i32, ptr %ptr, align 4, !invariant.load !{}
155+
store i32 %0, ptr %ptr2, align 4
156+
br label %if.end
157+
158+
if.end: ; preds = %for.body
159+
%inc = add nuw nsw i32 %i.08, 1
160+
%cmp = icmp slt i32 %inc, 2
161+
br i1 %cmp, label %for.body, label %exit
162+
163+
exit: ; preds = %if.end, %entry
164+
ret void
165+
}
166+
59167
declare void @llvm.assume(i1 noundef)

llvm/test/Transforms/LoopVectorize/dereferenceable-info-from-assumption-constant-size.ll

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
2-
; RUN: opt -p loop-vectorize -force-vector-width=2 -use-dereferenceable-at-point-semantics -S %s | FileCheck %s
2+
; RUN: opt -p loop-vectorize -force-vector-width=2 -S %s | FileCheck %s
33

44
declare void @llvm.assume(i1)
55

@@ -1411,7 +1411,6 @@ exit:
14111411
}
14121412

14131413
; %a may be freed between the dereferenceable assumption and accesses.
1414-
; It is not safe to use with -use-dereferenceable-at-point-semantics.
14151414
define void @may_free_align_deref_assumption_in_header_constant_trip_count_loop_invariant_ptr(ptr noalias %a, ptr noalias %b, ptr noalias %c) {
14161415
; CHECK-LABEL: define void @may_free_align_deref_assumption_in_header_constant_trip_count_loop_invariant_ptr(
14171416
; CHECK-SAME: ptr noalias [[A:%.*]], ptr noalias [[B:%.*]], ptr noalias [[C:%.*]]) {
@@ -1505,7 +1504,6 @@ exit:
15051504
}
15061505

15071506
; %a may be freed between the dereferenceable assumption and accesses.
1508-
; It is not safe to use with -use-dereferenceable-at-point-semantics.
15091507
define void @may_free_local_ptr_align_deref_assumption_in_header_constant_trip_count_loop_invariant_ptr(ptr noalias %b, ptr noalias %c) nofree nosync {
15101508
; CHECK-LABEL: define void @may_free_local_ptr_align_deref_assumption_in_header_constant_trip_count_loop_invariant_ptr(
15111509
; CHECK-SAME: ptr noalias [[B:%.*]], ptr noalias [[C:%.*]]) #[[ATTR1]] {

llvm/test/Transforms/SimplifyCFG/speculate-derefable-load.ll

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@
44
define i64 @align_deref_align(i1 %c, ptr %p) {
55
; CHECK-LABEL: define i64 @align_deref_align(
66
; CHECK-SAME: i1 [[C:%.*]], ptr [[P:%.*]]) {
7-
; CHECK-NEXT: [[ENTRY:.*:]]
7+
; CHECK-NEXT: [[ENTRY:.*]]:
88
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 8), "align"(ptr [[P]], i64 8) ]
9+
; CHECK-NEXT: br i1 [[C]], label %[[IF:.*]], label %[[EXIT:.*]]
10+
; CHECK: [[IF]]:
911
; CHECK-NEXT: [[V:%.*]] = load i64, ptr [[P]], align 8
10-
; CHECK-NEXT: [[RES:%.*]] = select i1 [[C]], i64 [[V]], i64 0
12+
; CHECK-NEXT: br label %[[EXIT]]
13+
; CHECK: [[EXIT]]:
14+
; CHECK-NEXT: [[RES:%.*]] = phi i64 [ [[V]], %[[IF]] ], [ 0, %[[ENTRY]] ]
1115
; CHECK-NEXT: ret i64 [[RES]]
1216
;
1317
entry:
@@ -23,9 +27,64 @@ exit:
2327
ret i64 %res
2428
}
2529

30+
define i64 @align_deref_align_nofree_nosync(i1 %c, ptr %p) nofree nosync {
31+
; CHECK-LABEL: define i64 @align_deref_align_nofree_nosync(
32+
; CHECK-SAME: i1 [[C:%.*]], ptr [[P:%.*]]) #[[ATTR0:[0-9]+]] {
33+
; CHECK-NEXT: [[ENTRY:.*:]]
34+
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 8), "align"(ptr [[P]], i64 8) ]
35+
; CHECK-NEXT: [[V:%.*]] = load i64, ptr [[P]], align 8
36+
; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[C]], i64 [[V]], i64 0
37+
; CHECK-NEXT: ret i64 [[SPEC_SELECT]]
38+
;
39+
entry:
40+
call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %p, i64 8), "align"(ptr %p, i64 8) ]
41+
br i1 %c, label %if, label %exit
42+
43+
if:
44+
%v = load i64, ptr %p, align 8
45+
br label %exit
46+
47+
exit:
48+
%res = phi i64 [ %v, %if ], [ 0, %entry ]
49+
ret i64 %res
50+
}
51+
2652
define i64 @assume_deref_align2(i1 %c1, i32 %x, ptr %p) {
2753
; CHECK-LABEL: define i64 @assume_deref_align2(
2854
; CHECK-SAME: i1 [[C1:%.*]], i32 [[X:%.*]], ptr [[P:%.*]]) {
55+
; CHECK-NEXT: [[ENTRY:.*]]:
56+
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 8), "align"(ptr [[P]], i64 8) ]
57+
; CHECK-NEXT: br i1 [[C1]], label %[[IF1:.*]], label %[[EXIT:.*]]
58+
; CHECK: [[IF1]]:
59+
; CHECK-NEXT: [[C2:%.*]] = icmp ugt i32 [[X]], 10
60+
; CHECK-NEXT: br i1 [[C2]], label %[[IF2:.*]], label %[[EXIT]]
61+
; CHECK: [[IF2]]:
62+
; CHECK-NEXT: [[V:%.*]] = load i64, ptr [[P]], align 8
63+
; CHECK-NEXT: br label %[[EXIT]]
64+
; CHECK: [[EXIT]]:
65+
; CHECK-NEXT: [[RES:%.*]] = phi i64 [ [[V]], %[[IF2]] ], [ 1, %[[IF1]] ], [ 0, %[[ENTRY]] ]
66+
; CHECK-NEXT: ret i64 [[RES]]
67+
;
68+
entry:
69+
call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %p, i64 8), "align"(ptr %p, i64 8) ]
70+
br i1 %c1, label %if1, label %exit
71+
72+
if1:
73+
%c2 = icmp ugt i32 %x, 10
74+
br i1 %c2, label %if2, label %exit
75+
76+
if2:
77+
%v = load i64, ptr %p, align 8
78+
br label %exit
79+
80+
exit:
81+
%res = phi i64 [ %v, %if2 ], [ 1, %if1 ], [ 0, %entry ]
82+
ret i64 %res
83+
}
84+
85+
define i64 @assume_deref_align2_nofree_nosync(i1 %c1, i32 %x, ptr %p) nofree nosync {
86+
; CHECK-LABEL: define i64 @assume_deref_align2_nofree_nosync(
87+
; CHECK-SAME: i1 [[C1:%.*]], i32 [[X:%.*]], ptr [[P:%.*]]) #[[ATTR0]] {
2988
; CHECK-NEXT: [[ENTRY:.*:]]
3089
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 8), "align"(ptr [[P]], i64 8) ]
3190
; CHECK-NEXT: [[C2:%.*]] = icmp ugt i32 [[X]], 10
@@ -51,9 +110,10 @@ exit:
51110
ret i64 %res
52111
}
53112

54-
define i64 @assume_deref_align_not_dominating(i1 %c, ptr %p) {
113+
114+
define i64 @assume_deref_align_not_dominating(i1 %c, ptr %p) nofree nosync {
55115
; CHECK-LABEL: define i64 @assume_deref_align_not_dominating(
56-
; CHECK-SAME: i1 [[C:%.*]], ptr [[P:%.*]]) {
116+
; CHECK-SAME: i1 [[C:%.*]], ptr [[P:%.*]]) #[[ATTR0]] {
57117
; CHECK-NEXT: [[ENTRY:.*]]:
58118
; CHECK-NEXT: br i1 [[C]], label %[[IF:.*]], label %[[EXIT:.*]]
59119
; CHECK: [[IF]]:

0 commit comments

Comments
 (0)