Skip to content

Commit

Permalink
[AssumeBundles] Dereferenceable used in bundle only apply at assume.
Browse files Browse the repository at this point in the history
  • Loading branch information
fhahn committed Feb 6, 2025
1 parent 6dc41a6 commit 0608821
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 16 deletions.
6 changes: 5 additions & 1 deletion llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1474,7 +1474,11 @@ Currently, only the following parameter attributes are defined:
``null_pointer_is_valid`` function attribute is present.
``n`` should be a positive number. The pointer should be well defined,
otherwise it is undefined behavior. This means ``dereferenceable(<n>)``
implies ``noundef``.
implies ``noundef``. When ``dereferenceable(<n>)`` is used in an
:ref:`assume operand bundls <assume_opbundles>`, the pointer is only
guaranteed to be dereferenceable at the point of the assumption and
may not be dereferenceable at later pointers, e.g. because it could have
been freed.

``dereferenceable_or_null(<n>)``
This indicates that the parameter or return value isn't both
Expand Down
4 changes: 1 addition & 3 deletions llvm/lib/Analysis/Loads.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@

using namespace llvm;

extern cl::opt<bool> UseDerefAtPointSemantics;

static bool isAligned(const Value *Base, Align Alignment,
const DataLayout &DL) {
return Base->getPointerAlignment(DL) >= Alignment;
Expand Down Expand Up @@ -171,7 +169,7 @@ static bool isDereferenceableAndAlignedPointer(
Size, DL, CtxI, AC, DT, TLI,
Visited, MaxDepth);

if (CtxI && (!UseDerefAtPointSemantics || !V->canBeFreed())) {
if (CtxI && !V->canBeFreed()) {
/// Look through assumes to see if both dereferencability and alignment can
/// be proven by an assume if needed.
RetainedKnowledge AlignRK;
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Analysis/ValueTracking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,7 @@ static bool isKnownNonZeroFromAssume(const Value *V, const SimplifyQuery &Q) {
*I, I->bundle_op_info_begin()[Elem.Index])) {
if (RK.WasOn == V &&
(RK.AttrKind == Attribute::NonNull ||
(RK.AttrKind == Attribute::Dereferenceable &&
(RK.AttrKind == Attribute::Dereferenceable && !V->canBeFreed() &&
!NullPointerIsDefined(Q.CxtI->getFunction(),
V->getType()->getPointerAddressSpace()))) &&
isValidAssumeForContext(I, Q.CxtI, Q.DT))
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/Analysis/ValueTracking/assume-queries-counter.ll
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ define dso_local i1 @test2(ptr readonly %0) {
ret i1 %2
}

define dso_local i32 @test4(ptr readonly %0, i1 %cond) {
define dso_local i32 @test4(ptr readonly %0, i1 %cond) nofree nosync {
; COUNTER1-LABEL: @test4(
; COUNTER1-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[TMP0:%.*]], i32 4) ]
; COUNTER1-NEXT: br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]]
Expand Down
4 changes: 2 additions & 2 deletions llvm/test/Analysis/ValueTracking/assume.ll
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ define dso_local i1 @test2(ptr readonly %0) {
ret i1 %2
}

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

define dso_local i32 @test4a(ptr readonly %0, i1 %cond) {
define dso_local i32 @test4a(ptr readonly %0, i1 %cond) nofree nosync {
; CHECK-LABEL: @test4a(
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[TMP0:%.*]], i32 4), "align"(ptr [[TMP0]], i32 8) ]
; CHECK-NEXT: br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]]
Expand Down
56 changes: 55 additions & 1 deletion llvm/test/Transforms/LICM/hoist-speculatable-load.ll
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ define void @f(i32 %ptr_i, ptr %ptr2, i1 %cond) {
; CHECK-NEXT: store i32 0, ptr [[PTR2:%.*]], align 4
; CHECK-NEXT: br label [[FOR_BODY_LR_PH]]
; CHECK: for.body.lr.ph:
; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PTR]], align 4
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.body:
; CHECK-NEXT: [[I_08:%.*]] = phi i32 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INC:%.*]], [[IF_END:%.*]] ]
; CHECK-NEXT: br i1 [[COND]], label [[IF_END]], label [[IF:%.*]]
; CHECK: if:
; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PTR]], align 4, !invariant.load [[META0:![0-9]+]]
; CHECK-NEXT: store i32 [[TMP0]], ptr [[PTR2]], align 4
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
Expand Down Expand Up @@ -56,4 +56,58 @@ exit: ; preds = %if.end, %entry
ret void
}

define void @f_nofree_nosync(i32 %ptr_i, ptr %ptr2, i1 %cond) nofree nosync {
; CHECK-LABEL: @f_nofree_nosync(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[PTR:%.*]] = inttoptr i32 [[PTR_I:%.*]] to ptr
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR]], i32 16), "dereferenceable"(ptr [[PTR]], i32 16) ]
; CHECK-NEXT: br i1 [[COND:%.*]], label [[FOR_BODY_LR_PH:%.*]], label [[IF0:%.*]]
; CHECK: if0:
; CHECK-NEXT: store i32 0, ptr [[PTR2:%.*]], align 4
; CHECK-NEXT: br label [[FOR_BODY_LR_PH]]
; CHECK: for.body.lr.ph:
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.body:
; CHECK-NEXT: [[I_08:%.*]] = phi i32 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INC:%.*]], [[IF_END:%.*]] ]
; CHECK-NEXT: br i1 [[COND]], label [[IF_END]], label [[IF:%.*]]
; CHECK: if:
; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PTR]], align 4, !invariant.load [[META0]]
; CHECK-NEXT: store i32 [[TMP0]], ptr [[PTR2]], align 4
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_08]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[INC]], 2
; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
%ptr = inttoptr i32 %ptr_i to ptr
call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i32 16), "dereferenceable"(ptr %ptr, i32 16) ]
br i1 %cond, label %for.body.lr.ph, label %if0

if0:
store i32 0, ptr %ptr2, align 4
br label %for.body.lr.ph

for.body.lr.ph: ; preds = %entry
br label %for.body

for.body: ; preds = %for.body.lr.ph, %if.end
%i.08 = phi i32 [ 0, %for.body.lr.ph ], [ %inc, %if.end ]
br i1 %cond, label %if.end, label %if

if:
%0 = load i32, ptr %ptr, align 4, !invariant.load !{}
store i32 %0, ptr %ptr2, align 4
br label %if.end

if.end: ; preds = %for.body
%inc = add nuw nsw i32 %i.08, 1
%cmp = icmp slt i32 %inc, 2
br i1 %cmp, label %for.body, label %exit

exit: ; preds = %if.end, %entry
ret void
}
declare void @llvm.assume(i1 noundef)
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
; RUN: opt -p loop-vectorize -force-vector-width=2 -use-dereferenceable-at-point-semantics -S %s | FileCheck %s
; RUN: opt -p loop-vectorize -force-vector-width=2 -S %s | FileCheck %s

declare void @llvm.assume(i1)

Expand Down Expand Up @@ -1411,7 +1411,6 @@ exit:
}

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

; %a may be freed between the dereferenceable assumption and accesses.
; It is not safe to use with -use-dereferenceable-at-point-semantics.
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 {
; CHECK-LABEL: define void @may_free_local_ptr_align_deref_assumption_in_header_constant_trip_count_loop_invariant_ptr(
; CHECK-SAME: ptr noalias [[B:%.*]], ptr noalias [[C:%.*]]) #[[ATTR1]] {
Expand Down
68 changes: 64 additions & 4 deletions llvm/test/Transforms/SimplifyCFG/speculate-derefable-load.ll
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@
define i64 @align_deref_align(i1 %c, ptr %p) {
; CHECK-LABEL: define i64 @align_deref_align(
; CHECK-SAME: i1 [[C:%.*]], ptr [[P:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 8), "align"(ptr [[P]], i64 8) ]
; CHECK-NEXT: br i1 [[C]], label %[[IF:.*]], label %[[EXIT:.*]]
; CHECK: [[IF]]:
; CHECK-NEXT: [[V:%.*]] = load i64, ptr [[P]], align 8
; CHECK-NEXT: [[RES:%.*]] = select i1 [[C]], i64 [[V]], i64 0
; CHECK-NEXT: br label %[[EXIT]]
; CHECK: [[EXIT]]:
; CHECK-NEXT: [[RES:%.*]] = phi i64 [ [[V]], %[[IF]] ], [ 0, %[[ENTRY]] ]
; CHECK-NEXT: ret i64 [[RES]]
;
entry:
Expand All @@ -23,9 +27,64 @@ exit:
ret i64 %res
}

define i64 @align_deref_align_nofree_nosync(i1 %c, ptr %p) nofree nosync {
; CHECK-LABEL: define i64 @align_deref_align_nofree_nosync(
; CHECK-SAME: i1 [[C:%.*]], ptr [[P:%.*]]) #[[ATTR0:[0-9]+]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 8), "align"(ptr [[P]], i64 8) ]
; CHECK-NEXT: [[V:%.*]] = load i64, ptr [[P]], align 8
; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[C]], i64 [[V]], i64 0
; CHECK-NEXT: ret i64 [[SPEC_SELECT]]
;
entry:
call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %p, i64 8), "align"(ptr %p, i64 8) ]
br i1 %c, label %if, label %exit

if:
%v = load i64, ptr %p, align 8
br label %exit

exit:
%res = phi i64 [ %v, %if ], [ 0, %entry ]
ret i64 %res
}

define i64 @assume_deref_align2(i1 %c1, i32 %x, ptr %p) {
; CHECK-LABEL: define i64 @assume_deref_align2(
; CHECK-SAME: i1 [[C1:%.*]], i32 [[X:%.*]], ptr [[P:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 8), "align"(ptr [[P]], i64 8) ]
; CHECK-NEXT: br i1 [[C1]], label %[[IF1:.*]], label %[[EXIT:.*]]
; CHECK: [[IF1]]:
; CHECK-NEXT: [[C2:%.*]] = icmp ugt i32 [[X]], 10
; CHECK-NEXT: br i1 [[C2]], label %[[IF2:.*]], label %[[EXIT]]
; CHECK: [[IF2]]:
; CHECK-NEXT: [[V:%.*]] = load i64, ptr [[P]], align 8
; CHECK-NEXT: br label %[[EXIT]]
; CHECK: [[EXIT]]:
; CHECK-NEXT: [[RES:%.*]] = phi i64 [ [[V]], %[[IF2]] ], [ 1, %[[IF1]] ], [ 0, %[[ENTRY]] ]
; CHECK-NEXT: ret i64 [[RES]]
;
entry:
call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %p, i64 8), "align"(ptr %p, i64 8) ]
br i1 %c1, label %if1, label %exit

if1:
%c2 = icmp ugt i32 %x, 10
br i1 %c2, label %if2, label %exit

if2:
%v = load i64, ptr %p, align 8
br label %exit

exit:
%res = phi i64 [ %v, %if2 ], [ 1, %if1 ], [ 0, %entry ]
ret i64 %res
}

define i64 @assume_deref_align2_nofree_nosync(i1 %c1, i32 %x, ptr %p) nofree nosync {
; CHECK-LABEL: define i64 @assume_deref_align2_nofree_nosync(
; CHECK-SAME: i1 [[C1:%.*]], i32 [[X:%.*]], ptr [[P:%.*]]) #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P]], i64 8), "align"(ptr [[P]], i64 8) ]
; CHECK-NEXT: [[C2:%.*]] = icmp ugt i32 [[X]], 10
Expand All @@ -51,9 +110,10 @@ exit:
ret i64 %res
}

define i64 @assume_deref_align_not_dominating(i1 %c, ptr %p) {

define i64 @assume_deref_align_not_dominating(i1 %c, ptr %p) nofree nosync {
; CHECK-LABEL: define i64 @assume_deref_align_not_dominating(
; CHECK-SAME: i1 [[C:%.*]], ptr [[P:%.*]]) {
; CHECK-SAME: i1 [[C:%.*]], ptr [[P:%.*]]) #[[ATTR0]] {
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: br i1 [[C]], label %[[IF:.*]], label %[[EXIT:.*]]
; CHECK: [[IF]]:
Expand Down

0 comments on commit 0608821

Please sign in to comment.