
During inter-procedural SCCP, also infer attributes on arguments, not just return values. This allows other non-interprocedural passes to make use of the information later.
289 lines
8.4 KiB
LLVM
289 lines
8.4 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
|
|
; RUN: opt -S -passes=sccp < %s | FileCheck %s --check-prefixes=CHECK,SCCP
|
|
; RUN: opt -S -passes=ipsccp < %s | FileCheck %s --check-prefixes=CHECK,IPSCCP
|
|
|
|
declare ptr @get()
|
|
|
|
define i1 @test_no_attr(ptr %p) {
|
|
; CHECK-LABEL: define i1 @test_no_attr(
|
|
; CHECK-SAME: ptr [[P:%.*]]) {
|
|
; CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[P]], null
|
|
; CHECK-NEXT: ret i1 [[CMP]]
|
|
;
|
|
%cmp = icmp ne ptr %p, null
|
|
ret i1 %cmp
|
|
}
|
|
|
|
define i1 @test_nonnull(ptr nonnull %p) {
|
|
; CHECK-LABEL: define i1 @test_nonnull(
|
|
; CHECK-SAME: ptr nonnull [[P:%.*]]) {
|
|
; CHECK-NEXT: ret i1 true
|
|
;
|
|
%cmp = icmp ne ptr %p, null
|
|
ret i1 %cmp
|
|
}
|
|
|
|
define i1 @test_nonnull_eq(ptr nonnull %p) {
|
|
; CHECK-LABEL: define i1 @test_nonnull_eq(
|
|
; CHECK-SAME: ptr nonnull [[P:%.*]]) {
|
|
; CHECK-NEXT: ret i1 false
|
|
;
|
|
%cmp = icmp eq ptr %p, null
|
|
ret i1 %cmp
|
|
}
|
|
|
|
define i1 @test_dereferenceable(ptr dereferenceable(4) %p) {
|
|
; CHECK-LABEL: define i1 @test_dereferenceable(
|
|
; CHECK-SAME: ptr dereferenceable(4) [[P:%.*]]) {
|
|
; CHECK-NEXT: ret i1 true
|
|
;
|
|
%cmp = icmp ne ptr %p, null
|
|
ret i1 %cmp
|
|
}
|
|
|
|
define i1 @test_alloca() {
|
|
; CHECK-LABEL: define i1 @test_alloca() {
|
|
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
|
|
; CHECK-NEXT: ret i1 true
|
|
;
|
|
%a = alloca i32
|
|
%cmp = icmp ne ptr %a, null
|
|
ret i1 %cmp
|
|
}
|
|
|
|
define i1 @test_alloca_addrspace() {
|
|
; CHECK-LABEL: define i1 @test_alloca_addrspace() {
|
|
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4, addrspace(1)
|
|
; CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr addrspace(1) [[A]], null
|
|
; CHECK-NEXT: ret i1 [[CMP]]
|
|
;
|
|
%a = alloca i32, addrspace(1)
|
|
%cmp = icmp ne ptr addrspace(1) %a, null
|
|
ret i1 %cmp
|
|
}
|
|
|
|
define i1 @test_alloca_null_pointer_valid() null_pointer_is_valid {
|
|
; CHECK-LABEL: define i1 @test_alloca_null_pointer_valid(
|
|
; CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
|
|
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
|
|
; CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[A]], null
|
|
; CHECK-NEXT: ret i1 [[CMP]]
|
|
;
|
|
%a = alloca i32
|
|
%cmp = icmp ne ptr %a, null
|
|
ret i1 %cmp
|
|
}
|
|
|
|
define i1 @test_load_nonnull(ptr %p) {
|
|
; CHECK-LABEL: define i1 @test_load_nonnull(
|
|
; CHECK-SAME: ptr [[P:%.*]]) {
|
|
; CHECK-NEXT: [[P2:%.*]] = load ptr, ptr [[P]], align 8, !nonnull [[META0:![0-9]+]]
|
|
; CHECK-NEXT: ret i1 true
|
|
;
|
|
%p2 = load ptr, ptr %p, !nonnull !{}
|
|
%cmp = icmp ne ptr %p2, null
|
|
ret i1 %cmp
|
|
}
|
|
|
|
define i1 @test_call_nonnull() {
|
|
; CHECK-LABEL: define i1 @test_call_nonnull() {
|
|
; CHECK-NEXT: [[P:%.*]] = call nonnull ptr @get()
|
|
; CHECK-NEXT: ret i1 true
|
|
;
|
|
%p = call nonnull ptr @get()
|
|
%cmp = icmp ne ptr %p, null
|
|
ret i1 %cmp
|
|
}
|
|
|
|
define i1 @test_call_dereferenceable() {
|
|
; CHECK-LABEL: define i1 @test_call_dereferenceable() {
|
|
; CHECK-NEXT: [[P:%.*]] = call dereferenceable(4) ptr @get()
|
|
; CHECK-NEXT: ret i1 true
|
|
;
|
|
%p = call dereferenceable(4) ptr @get()
|
|
%cmp = icmp ne ptr %p, null
|
|
ret i1 %cmp
|
|
}
|
|
|
|
define i1 @test_gep_no_flags(ptr nonnull %p, i64 %x) {
|
|
; CHECK-LABEL: define i1 @test_gep_no_flags(
|
|
; CHECK-SAME: ptr nonnull [[P:%.*]], i64 [[X:%.*]]) {
|
|
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[X]]
|
|
; CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[GEP]], null
|
|
; CHECK-NEXT: ret i1 [[CMP]]
|
|
;
|
|
%gep = getelementptr i8, ptr %p, i64 %x
|
|
%cmp = icmp ne ptr %gep, null
|
|
ret i1 %cmp
|
|
}
|
|
|
|
define i1 @test_gep_nuw(ptr nonnull %p, i64 %x) {
|
|
; CHECK-LABEL: define i1 @test_gep_nuw(
|
|
; CHECK-SAME: ptr nonnull [[P:%.*]], i64 [[X:%.*]]) {
|
|
; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr [[P]], i64 [[X]]
|
|
; CHECK-NEXT: ret i1 true
|
|
;
|
|
%gep = getelementptr nuw i8, ptr %p, i64 %x
|
|
%cmp = icmp ne ptr %gep, null
|
|
ret i1 %cmp
|
|
}
|
|
|
|
define i1 @test_gep_inbounds(ptr nonnull %p, i64 %x) {
|
|
; CHECK-LABEL: define i1 @test_gep_inbounds(
|
|
; CHECK-SAME: ptr nonnull [[P:%.*]], i64 [[X:%.*]]) {
|
|
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 [[X]]
|
|
; CHECK-NEXT: ret i1 true
|
|
;
|
|
%gep = getelementptr inbounds i8, ptr %p, i64 %x
|
|
%cmp = icmp ne ptr %gep, null
|
|
ret i1 %cmp
|
|
}
|
|
|
|
define i1 @test_gep_inbounds_null_pointer_valid(ptr nonnull %p, i64 %x) null_pointer_is_valid {
|
|
; CHECK-LABEL: define i1 @test_gep_inbounds_null_pointer_valid(
|
|
; CHECK-SAME: ptr nonnull [[P:%.*]], i64 [[X:%.*]]) #[[ATTR0]] {
|
|
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 [[X]]
|
|
; CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[GEP]], null
|
|
; CHECK-NEXT: ret i1 [[CMP]]
|
|
;
|
|
%gep = getelementptr inbounds i8, ptr %p, i64 %x
|
|
%cmp = icmp ne ptr %gep, null
|
|
ret i1 %cmp
|
|
}
|
|
|
|
define i1 @test_select(i1 %c, ptr nonnull %p, i64 %x) {
|
|
; CHECK-LABEL: define i1 @test_select(
|
|
; CHECK-SAME: i1 [[C:%.*]], ptr nonnull [[P:%.*]], i64 [[X:%.*]]) {
|
|
; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr [[P]], i64 [[X]]
|
|
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C]], ptr [[P]], ptr [[GEP]]
|
|
; CHECK-NEXT: ret i1 true
|
|
;
|
|
%gep = getelementptr nuw i8, ptr %p, i64 %x
|
|
%sel = select i1 %c, ptr %p, ptr %gep
|
|
%cmp = icmp ne ptr %sel, null
|
|
ret i1 %cmp
|
|
}
|
|
|
|
define i1 @test_select_not_nuw(i1 %c, ptr nonnull %p, i64 %x) {
|
|
; CHECK-LABEL: define i1 @test_select_not_nuw(
|
|
; CHECK-SAME: i1 [[C:%.*]], ptr nonnull [[P:%.*]], i64 [[X:%.*]]) {
|
|
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[X]]
|
|
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[C]], ptr [[P]], ptr [[GEP]]
|
|
; CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[SEL]], null
|
|
; CHECK-NEXT: ret i1 [[CMP]]
|
|
;
|
|
%gep = getelementptr i8, ptr %p, i64 %x
|
|
%sel = select i1 %c, ptr %p, ptr %gep
|
|
%cmp = icmp ne ptr %sel, null
|
|
ret i1 %cmp
|
|
}
|
|
|
|
define i1 @test_phi(i1 %c, ptr nonnull %p, i64 %x) {
|
|
; CHECK-LABEL: define i1 @test_phi(
|
|
; CHECK-SAME: i1 [[C:%.*]], ptr nonnull [[P:%.*]], i64 [[X:%.*]]) {
|
|
; CHECK-NEXT: [[ENTRY:.*]]:
|
|
; CHECK-NEXT: br i1 [[C]], label %[[IF:.*]], label %[[JOIN:.*]]
|
|
; CHECK: [[IF]]:
|
|
; CHECK-NEXT: [[GEP:%.*]] = getelementptr nuw i8, ptr [[P]], i64 [[X]]
|
|
; CHECK-NEXT: br label %[[JOIN]]
|
|
; CHECK: [[JOIN]]:
|
|
; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ [[P]], %[[ENTRY]] ], [ [[GEP]], %[[IF]] ]
|
|
; CHECK-NEXT: ret i1 true
|
|
;
|
|
entry:
|
|
br i1 %c, label %if, label %join
|
|
|
|
if:
|
|
%gep = getelementptr nuw i8, ptr %p, i64 %x
|
|
br label %join
|
|
|
|
join:
|
|
%phi = phi ptr [ %p, %entry ], [ %gep, %if ]
|
|
%cmp = icmp ne ptr %phi, null
|
|
ret i1 %cmp
|
|
}
|
|
|
|
define internal i1 @ip_test_nonnull_callee(ptr nonnull %p) {
|
|
; SCCP-LABEL: define internal i1 @ip_test_nonnull_callee(
|
|
; SCCP-SAME: ptr nonnull [[P:%.*]]) {
|
|
; SCCP-NEXT: ret i1 true
|
|
;
|
|
; IPSCCP-LABEL: define internal i1 @ip_test_nonnull_callee(
|
|
; IPSCCP-SAME: ptr nonnull [[P:%.*]]) {
|
|
; IPSCCP-NEXT: ret i1 poison
|
|
;
|
|
%cmp = icmp ne ptr %p, null
|
|
ret i1 %cmp
|
|
}
|
|
|
|
define i1 @ip_test_nonnull_caller(ptr %p) {
|
|
; SCCP-LABEL: define i1 @ip_test_nonnull_caller(
|
|
; SCCP-SAME: ptr [[P:%.*]]) {
|
|
; SCCP-NEXT: [[RES:%.*]] = call i1 @ip_test_nonnull_callee(ptr [[P]])
|
|
; SCCP-NEXT: ret i1 [[RES]]
|
|
;
|
|
; IPSCCP-LABEL: define i1 @ip_test_nonnull_caller(
|
|
; IPSCCP-SAME: ptr [[P:%.*]]) {
|
|
; IPSCCP-NEXT: [[RES:%.*]] = call i1 @ip_test_nonnull_callee(ptr [[P]])
|
|
; IPSCCP-NEXT: ret i1 true
|
|
;
|
|
%res = call i1 @ip_test_nonnull_callee(ptr %p)
|
|
ret i1 %res
|
|
}
|
|
|
|
define ptr @ret_nonnull_pointer(ptr nonnull %p) {
|
|
; CHECK-LABEL: define nonnull ptr @ret_nonnull_pointer(
|
|
; CHECK-SAME: ptr nonnull [[P:%.*]]) {
|
|
; CHECK-NEXT: ret ptr [[P]]
|
|
;
|
|
ret ptr %p
|
|
}
|
|
|
|
define ptr @ret_maybe_null_pointer(ptr %p) {
|
|
; CHECK-LABEL: define ptr @ret_maybe_null_pointer(
|
|
; CHECK-SAME: ptr [[P:%.*]]) {
|
|
; CHECK-NEXT: ret ptr [[P]]
|
|
;
|
|
ret ptr %p
|
|
}
|
|
|
|
define internal void @ip_nonnull_arg_callee(ptr %p) {
|
|
; SCCP-LABEL: define internal void @ip_nonnull_arg_callee(
|
|
; SCCP-SAME: ptr [[P:%.*]]) {
|
|
; SCCP-NEXT: ret void
|
|
;
|
|
; IPSCCP-LABEL: define internal void @ip_nonnull_arg_callee(
|
|
; IPSCCP-SAME: ptr nonnull [[P:%.*]]) {
|
|
; IPSCCP-NEXT: ret void
|
|
;
|
|
ret void
|
|
}
|
|
|
|
define internal void @ip_not_nonnull_arg_callee(ptr %p) {
|
|
; CHECK-LABEL: define internal void @ip_not_nonnull_arg_callee(
|
|
; CHECK-SAME: ptr [[P:%.*]]) {
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
ret void
|
|
}
|
|
|
|
define void @ip_nonnull_arg_caller(ptr nonnull %p) {
|
|
; CHECK-LABEL: define void @ip_nonnull_arg_caller(
|
|
; CHECK-SAME: ptr nonnull [[P:%.*]]) {
|
|
; CHECK-NEXT: call void @ip_nonnull_arg_callee(ptr [[P]])
|
|
; CHECK-NEXT: call void @ip_not_nonnull_arg_callee(ptr [[P]])
|
|
; CHECK-NEXT: call void @ip_not_nonnull_arg_callee(ptr null)
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @ip_nonnull_arg_callee(ptr %p)
|
|
call void @ip_not_nonnull_arg_callee(ptr %p)
|
|
call void @ip_not_nonnull_arg_callee(ptr null)
|
|
ret void
|
|
}
|
|
|
|
;.
|
|
; SCCP: [[META0]] = !{}
|
|
;.
|
|
; IPSCCP: [[META0]] = !{}
|
|
;.
|