
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
162 lines
4.9 KiB
LLVM
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
|
|
}
|