Skip to content

Commit 65640c1

Browse files
authored
[AssumeBundles] Dereferenceable used in bundle only applies at assume. (#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 #123196. With that change, it should be safe to expose dereferenceable assumptions more widely as in #121789 PR: #126117
1 parent 5decab1 commit 65640c1

File tree

7 files changed

+191
-15
lines changed

7 files changed

+191
-15
lines changed

llvm/docs/LangRef.rst

+8-1
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

+6-3
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

+1-1
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

+2-2
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

+109-1
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

+1-3
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

+64-4
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)