Florian Hahn 65640c1d4c
[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 https://github.com/llvm/llvm-project/pull/123196.

With that change, it should be safe to expose dereferenceable
assumptions more widely as in
https://github.com/llvm/llvm-project/pull/121789

PR: https://github.com/llvm/llvm-project/pull/126117
2025-02-13 20:41:23 +01:00

162 lines
4.9 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
define i32 @assume_add(i32 %a, i32 %b) {
; CHECK-LABEL: @assume_add(
; CHECK-NEXT: [[T1:%.*]] = add i32 [[A:%.*]], [[B:%.*]]
; CHECK-NEXT: [[LAST_TWO_DIGITS:%.*]] = and i32 [[T1]], 3
; CHECK-NEXT: [[T2:%.*]] = icmp eq i32 [[LAST_TWO_DIGITS]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[T2]])
; CHECK-NEXT: [[T3:%.*]] = or disjoint i32 [[T1]], 3
; CHECK-NEXT: ret i32 [[T3]]
;
%t1 = add i32 %a, %b
%last_two_digits = and i32 %t1, 3
%t2 = icmp eq i32 %last_two_digits, 0
call void @llvm.assume(i1 %t2)
%t3 = add i32 %t1, 3
ret i32 %t3
}
define void @assume_not() {
; CHECK-LABEL: @assume_not(
; CHECK-NEXT: entry-block:
; CHECK-NEXT: [[TMP0:%.*]] = call i1 @get_val()
; CHECK-NEXT: [[TMP1:%.*]] = xor i1 [[TMP0]], true
; CHECK-NEXT: call void @llvm.assume(i1 [[TMP1]])
; CHECK-NEXT: ret void
;
entry-block:
%0 = call i1 @get_val()
%1 = xor i1 %0, true
call void @llvm.assume(i1 %1)
ret void
}
declare i1 @get_val()
declare void @llvm.assume(i1)
define dso_local i1 @test1(ptr readonly %0) {
; CHECK-LABEL: @test1(
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[TMP0:%.*]]) ]
; CHECK-NEXT: ret i1 false
;
call void @llvm.assume(i1 true) ["nonnull"(ptr %0)]
%2 = icmp eq ptr %0, null
ret i1 %2
}
define dso_local i1 @test2(ptr readonly %0) {
; CHECK-LABEL: @test2(
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(ptr [[TMP0:%.*]]) ]
; CHECK-NEXT: ret i1 false
;
%2 = icmp eq ptr %0, null
call void @llvm.assume(i1 true) ["nonnull"(ptr %0)]
ret i1 %2
}
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:%.*]]
; CHECK: B:
; CHECK-NEXT: br label [[A]]
; CHECK: A:
; CHECK-NEXT: br i1 false, label [[TMP4:%.*]], label [[TMP2:%.*]]
; CHECK: 2:
; CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[TMP0]], align 4
; CHECK-NEXT: br label [[TMP4]]
; CHECK: 4:
; CHECK-NEXT: [[TMP5:%.*]] = phi i32 [ [[TMP3]], [[TMP2]] ], [ poison, [[A]] ]
; CHECK-NEXT: ret i32 [[TMP5]]
;
call void @llvm.assume(i1 true) ["dereferenceable"(ptr %0, i32 4)]
br i1 %cond, label %A, label %B
B:
br label %A
A:
%2 = icmp eq ptr %0, null
br i1 %2, label %5, label %3
3: ; preds = %1
%4 = load i32, ptr %0, align 4
br label %5
5: ; preds = %1, %3
%6 = phi i32 [ %4, %3 ], [ 0, %A ]
ret i32 %6
}
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:%.*]]
; CHECK: B:
; CHECK-NEXT: br label [[A]]
; CHECK: A:
; CHECK-NEXT: br i1 false, label [[TMP4:%.*]], label [[TMP2:%.*]]
; CHECK: 2:
; CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[TMP0]], align 4
; CHECK-NEXT: br label [[TMP4]]
; CHECK: 4:
; CHECK-NEXT: [[TMP5:%.*]] = phi i32 [ [[TMP3]], [[TMP2]] ], [ poison, [[A]] ]
; CHECK-NEXT: ret i32 [[TMP5]]
;
call void @llvm.assume(i1 true) ["dereferenceable"(ptr %0, i32 4), "align"(ptr %0, i32 8)]
br i1 %cond, label %A, label %B
B:
br label %A
A:
%2 = icmp eq ptr %0, null
br i1 %2, label %5, label %3
3: ; preds = %1
%4 = load i32, ptr %0, align 4
br label %5
5: ; preds = %1, %3
%6 = phi i32 [ %4, %3 ], [ 0, %A ]
ret i32 %6
}
define dso_local i32 @test4b(ptr readonly %0, i1 %cond) null_pointer_is_valid {
; CHECK-LABEL: @test4b(
; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[TMP0:%.*]], i32 4) ]
; CHECK-NEXT: br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]]
; CHECK: B:
; CHECK-NEXT: br label [[A]]
; CHECK: A:
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
; CHECK-NEXT: br i1 [[TMP2]], label [[TMP5:%.*]], label [[TMP3:%.*]]
; CHECK: 3:
; CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP0]], align 4
; CHECK-NEXT: br label [[TMP5]]
; CHECK: 5:
; CHECK-NEXT: [[TMP6:%.*]] = phi i32 [ [[TMP4]], [[TMP3]] ], [ 0, [[A]] ]
; CHECK-NEXT: ret i32 [[TMP6]]
;
call void @llvm.assume(i1 true) ["dereferenceable"(ptr %0, i32 4)]
br i1 %cond, label %A, label %B
B:
br label %A
A:
%2 = icmp eq ptr %0, null
br i1 %2, label %5, label %3
3: ; preds = %1
%4 = load i32, ptr %0, align 4
br label %5
5: ; preds = %1, %3
%6 = phi i32 [ %4, %3 ], [ 0, %A ]
ret i32 %6
}