Files
clang-p2996/llvm/test/CodeGen/X86/implicit-null-check.ll
Harald van Dijk a21abc782a [X86] Align i128 to 16 bytes in x86 datalayouts
This is an attempt at rebooting https://reviews.llvm.org/D28990

I've included AutoUpgrade changes to modify the data layout to satisfy the compatible layout check. But this does mean alloca, loads, stores, etc in old IR will automatically get this new alignment.

This should fix PR46320.

Reviewed By: echristo, rnk, tmgross

Differential Revision: https://reviews.llvm.org/D86310
2023-10-11 10:23:38 +01:00

646 lines
16 KiB
LLVM

; RUN: llc -verify-machineinstrs -O3 -mtriple=x86_64-apple-macosx -enable-implicit-null-checks < %s | FileCheck %s
define i32 @imp_null_check_load(ptr %x) {
; CHECK-LABEL: imp_null_check_load:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: Ltmp0:
; CHECK-NEXT: movl (%rdi), %eax ## on-fault: LBB0_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: retq
; CHECK-NEXT: LBB0_1: ## %is_null
; CHECK-NEXT: movl $42, %eax
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i32 42
not_null:
%t = load i32, ptr %x
ret i32 %t
}
; TODO: can make implicit
define i32 @imp_null_check_unordered_load(ptr %x) {
; CHECK-LABEL: imp_null_check_unordered_load:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: Ltmp1:
; CHECK-NEXT: movl (%rdi), %eax ## on-fault: LBB1_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: retq
; CHECK-NEXT: LBB1_1: ## %is_null
; CHECK-NEXT: movl $42, %eax
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i32 42
not_null:
%t = load atomic i32, ptr %x unordered, align 4
ret i32 %t
}
; TODO: Can be converted into implicit check.
;; Probably could be implicit, but we're conservative for now
define i32 @imp_null_check_seq_cst_load(ptr %x) {
; CHECK-LABEL: imp_null_check_seq_cst_load:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: testq %rdi, %rdi
; CHECK-NEXT: je LBB2_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: movl (%rdi), %eax
; CHECK-NEXT: retq
; CHECK-NEXT: LBB2_1: ## %is_null
; CHECK-NEXT: movl $42, %eax
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i32 42
not_null:
%t = load atomic i32, ptr %x seq_cst, align 4
ret i32 %t
}
;; Might be memory mapped IO, so can't rely on fault behavior
define i32 @imp_null_check_volatile_load(ptr %x) {
; CHECK-LABEL: imp_null_check_volatile_load:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: testq %rdi, %rdi
; CHECK-NEXT: je LBB3_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: movl (%rdi), %eax
; CHECK-NEXT: retq
; CHECK-NEXT: LBB3_1: ## %is_null
; CHECK-NEXT: movl $42, %eax
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i32 42
not_null:
%t = load volatile i32, ptr %x, align 4
ret i32 %t
}
define i8 @imp_null_check_load_i8(ptr %x) {
; CHECK-LABEL: imp_null_check_load_i8:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: Ltmp2:
; CHECK-NEXT: movb (%rdi), %al ## on-fault: LBB4_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: retq
; CHECK-NEXT: LBB4_1: ## %is_null
; CHECK-NEXT: movb $42, %al
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i8 42
not_null:
%t = load i8, ptr %x
ret i8 %t
}
define i256 @imp_null_check_load_i256(ptr %x) {
; CHECK-LABEL: imp_null_check_load_i256:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: movq %rdi, %rax
; CHECK-NEXT: Ltmp3:
; CHECK-NEXT: movaps (%rsi), %xmm0 ## on-fault: LBB5_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: movaps 16(%rsi), %xmm1
; CHECK-NEXT: movaps %xmm1, 16(%rax)
; CHECK-NEXT: movaps %xmm0, (%rax)
; CHECK-NEXT: retq
; CHECK-NEXT: LBB5_1: ## %is_null
; CHECK-NEXT: xorps %xmm0, %xmm0
; CHECK-NEXT: movaps %xmm0, 16(%rax)
; CHECK-NEXT: movq $0, 8(%rax)
; CHECK-NEXT: movq $42, (%rax)
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i256 42
not_null:
%t = load i256, ptr %x
ret i256 %t
}
define i32 @imp_null_check_gep_load(ptr %x) {
; CHECK-LABEL: imp_null_check_gep_load:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: Ltmp4:
; CHECK-NEXT: movl 128(%rdi), %eax ## on-fault: LBB6_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: retq
; CHECK-NEXT: LBB6_1: ## %is_null
; CHECK-NEXT: movl $42, %eax
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i32 42
not_null:
%x.gep = getelementptr i32, ptr %x, i32 32
%t = load i32, ptr %x.gep
ret i32 %t
}
define i32 @imp_null_check_add_result(ptr %x, i32 %p) {
; CHECK-LABEL: imp_null_check_add_result:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: Ltmp5:
; CHECK-NEXT: addl (%rdi), %esi ## on-fault: LBB7_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: movl %esi, %eax
; CHECK-NEXT: retq
; CHECK-NEXT: LBB7_1: ## %is_null
; CHECK-NEXT: movl $42, %eax
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i32 42
not_null:
%t = load i32, ptr %x
%p1 = add i32 %t, %p
ret i32 %p1
}
define i32 @imp_null_check_sub_result(ptr %x, i32 %p) {
; CHECK-LABEL: imp_null_check_sub_result:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: Ltmp6:
; CHECK-NEXT: movl (%rdi), %eax ## on-fault: LBB8_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: subl %esi, %eax
; CHECK-NEXT: retq
; CHECK-NEXT: LBB8_1: ## %is_null
; CHECK-NEXT: movl $42, %eax
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i32 42
not_null:
%t = load i32, ptr %x
%p1 = sub i32 %t, %p
ret i32 %p1
}
define i32 @imp_null_check_mul_result(ptr %x, i32 %p) {
; CHECK-LABEL: imp_null_check_mul_result:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: Ltmp7:
; CHECK-NEXT: imull (%rdi), %esi ## on-fault: LBB9_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: movl %esi, %eax
; CHECK-NEXT: retq
; CHECK-NEXT: LBB9_1: ## %is_null
; CHECK-NEXT: movl $42, %eax
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i32 42
not_null:
%t = load i32, ptr %x
%p1 = mul i32 %t, %p
ret i32 %p1
}
define i32 @imp_null_check_udiv_result(ptr %x, i32 %p) {
; CHECK-LABEL: imp_null_check_udiv_result:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: Ltmp8:
; CHECK-NEXT: movl (%rdi), %eax ## on-fault: LBB10_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: xorl %edx, %edx
; CHECK-NEXT: divl %esi
; CHECK-NEXT: retq
; CHECK-NEXT: LBB10_1: ## %is_null
; CHECK-NEXT: movl $42, %eax
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i32 42
not_null:
%t = load i32, ptr %x
%p1 = udiv i32 %t, %p
ret i32 %p1
}
define i32 @imp_null_check_shl_result(ptr %x, i32 %p) {
; CHECK-LABEL: imp_null_check_shl_result:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: Ltmp9:
; CHECK-NEXT: movl (%rdi), %eax ## on-fault: LBB11_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: movl %esi, %ecx
; CHECK-NEXT: shll %cl, %eax
; CHECK-NEXT: retq
; CHECK-NEXT: LBB11_1: ## %is_null
; CHECK-NEXT: movl $42, %eax
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i32 42
not_null:
%t = load i32, ptr %x
%p1 = shl i32 %t, %p
ret i32 %p1
}
define i32 @imp_null_check_lshr_result(ptr %x, i32 %p) {
; CHECK-LABEL: imp_null_check_lshr_result:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: Ltmp10:
; CHECK-NEXT: movl (%rdi), %eax ## on-fault: LBB12_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: movl %esi, %ecx
; CHECK-NEXT: shrl %cl, %eax
; CHECK-NEXT: retq
; CHECK-NEXT: LBB12_1: ## %is_null
; CHECK-NEXT: movl $42, %eax
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i32 42
not_null:
%t = load i32, ptr %x
%p1 = lshr i32 %t, %p
ret i32 %p1
}
define i32 @imp_null_check_hoist_over_unrelated_load(ptr %x, ptr %y, ptr %z) {
; CHECK-LABEL: imp_null_check_hoist_over_unrelated_load:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: Ltmp11:
; CHECK-NEXT: movl (%rdi), %eax ## on-fault: LBB13_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: movl (%rsi), %ecx
; CHECK-NEXT: movl %ecx, (%rdx)
; CHECK-NEXT: retq
; CHECK-NEXT: LBB13_1: ## %is_null
; CHECK-NEXT: movl $42, %eax
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i32 42
not_null:
%t0 = load i32, ptr %y
%t1 = load i32, ptr %x
store i32 %t0, ptr %z
ret i32 %t1
}
define i32 @imp_null_check_via_mem_comparision(ptr %x, i32 %val) {
; CHECK-LABEL: imp_null_check_via_mem_comparision:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: Ltmp12:
; CHECK-NEXT: cmpl %esi, 4(%rdi) ## on-fault: LBB14_3
; CHECK-NEXT: ## %bb.1: ## %not_null
; CHECK-NEXT: jge LBB14_2
; CHECK-NEXT: ## %bb.4: ## %ret_100
; CHECK-NEXT: movl $100, %eax
; CHECK-NEXT: retq
; CHECK-NEXT: LBB14_3: ## %is_null
; CHECK-NEXT: movl $42, %eax
; CHECK-NEXT: retq
; CHECK-NEXT: LBB14_2: ## %ret_200
; CHECK-NEXT: movl $200, %eax
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i32 42
not_null:
%x.loc = getelementptr i32, ptr %x, i32 1
%t = load i32, ptr %x.loc
%m = icmp slt i32 %t, %val
br i1 %m, label %ret_100, label %ret_200
ret_100:
ret i32 100
ret_200:
ret i32 200
}
define i32 @imp_null_check_gep_load_with_use_dep(ptr %x, i32 %a) {
; CHECK-LABEL: imp_null_check_gep_load_with_use_dep:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: ## kill: def $esi killed $esi def $rsi
; CHECK-NEXT: Ltmp13:
; CHECK-NEXT: movl (%rdi), %eax ## on-fault: LBB15_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: addl %edi, %esi
; CHECK-NEXT: leal 4(%rax,%rsi), %eax
; CHECK-NEXT: retq
; CHECK-NEXT: LBB15_1: ## %is_null
; CHECK-NEXT: movl $42, %eax
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i32 42
not_null:
%x.loc = getelementptr i32, ptr %x, i32 1
%y = ptrtoint ptr %x.loc to i32
%b = add i32 %a, %y
%t = load i32, ptr %x
%z = add i32 %t, %b
ret i32 %z
}
;; TODO: We could handle this case as we can lift the fence into the
;; previous block before the conditional without changing behavior.
define i32 @imp_null_check_load_fence1(ptr %x) {
; CHECK-LABEL: imp_null_check_load_fence1:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: testq %rdi, %rdi
; CHECK-NEXT: je LBB16_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: ##MEMBARRIER
; CHECK-NEXT: movl (%rdi), %eax
; CHECK-NEXT: retq
; CHECK-NEXT: LBB16_1: ## %is_null
; CHECK-NEXT: movl $42, %eax
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i32 42
not_null:
fence acquire
%t = load i32, ptr %x
ret i32 %t
}
;; TODO: We could handle this case as we can lift the fence into the
;; previous block before the conditional without changing behavior.
define i32 @imp_null_check_load_fence2(ptr %x) {
; CHECK-LABEL: imp_null_check_load_fence2:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: testq %rdi, %rdi
; CHECK-NEXT: je LBB17_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: mfence
; CHECK-NEXT: movl (%rdi), %eax
; CHECK-NEXT: retq
; CHECK-NEXT: LBB17_1: ## %is_null
; CHECK-NEXT: movl $42, %eax
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i32 42
not_null:
fence seq_cst
%t = load i32, ptr %x
ret i32 %t
}
define void @imp_null_check_store(ptr %x) {
; CHECK-LABEL: imp_null_check_store:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: Ltmp14:
; CHECK-NEXT: movl $1, (%rdi) ## on-fault: LBB18_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: retq
; CHECK-NEXT: LBB18_1: ## %is_null
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret void
not_null:
store i32 1, ptr %x
ret void
}
;; TODO: can be implicit
define void @imp_null_check_unordered_store(ptr %x) {
; CHECK-LABEL: imp_null_check_unordered_store:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: Ltmp15:
; CHECK-NEXT: movl $1, (%rdi) ## on-fault: LBB19_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: retq
; CHECK-NEXT: LBB19_1: ## %is_null
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret void
not_null:
store atomic i32 1, ptr %x unordered, align 4
ret void
}
define i32 @imp_null_check_neg_gep_load(ptr %x) {
; CHECK-LABEL: imp_null_check_neg_gep_load:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: Ltmp16:
; CHECK-NEXT: movl -128(%rdi), %eax ## on-fault: LBB20_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: retq
; CHECK-NEXT: LBB20_1: ## %is_null
; CHECK-NEXT: movl $42, %eax
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i32 42
not_null:
%x.gep = getelementptr i32, ptr %x, i32 -32
%t = load i32, ptr %x.gep
ret i32 %t
}
; This redefines the null check reg by doing a zero-extend and a shift on
; itself.
; Converted into implicit null check since both of these operations do not
; change the nullness of %x (i.e. if it is null, it remains null).
define i64 @imp_null_check_load_shift_addr(ptr %x) {
; CHECK-LABEL: imp_null_check_load_shift_addr:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: shlq $6, %rdi
; CHECK-NEXT: Ltmp17:
; CHECK-NEXT: movq 8(%rdi), %rax ## on-fault: LBB21_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: retq
; CHECK-NEXT: LBB21_1: ## %is_null
; CHECK-NEXT: movl $42, %eax
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i64 42
not_null:
%y = ptrtoint ptr %x to i64
%shry = shl i64 %y, 6
%y.ptr = inttoptr i64 %shry to ptr
%x.loc = getelementptr i64, ptr %y.ptr, i64 1
%t = load i64, ptr %x.loc
ret i64 %t
}
; Same as imp_null_check_load_shift_addr but shift is by 3 and this is now
; converted into complex addressing.
define i64 @imp_null_check_load_shift_by_3_addr(ptr %x) {
; CHECK-LABEL: imp_null_check_load_shift_by_3_addr:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: Ltmp18:
; CHECK-NEXT: movq 8(,%rdi,8), %rax ## on-fault: LBB22_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: retq
; CHECK-NEXT: LBB22_1: ## %is_null
; CHECK-NEXT: movl $42, %eax
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i64 42
not_null:
%y = ptrtoint ptr %x to i64
%shry = shl i64 %y, 3
%y.ptr = inttoptr i64 %shry to ptr
%x.loc = getelementptr i64, ptr %y.ptr, i64 1
%t = load i64, ptr %x.loc
ret i64 %t
}
define i64 @imp_null_check_load_shift_add_addr(ptr %x) {
; CHECK-LABEL: imp_null_check_load_shift_add_addr:
; CHECK: ## %bb.0: ## %entry
; CHECK-NEXT: Ltmp19:
; CHECK-NEXT: movq 3526(,%rdi,8), %rax ## on-fault: LBB23_1
; CHECK-NEXT: ## %bb.2: ## %not_null
; CHECK-NEXT: retq
; CHECK-NEXT: LBB23_1: ## %is_null
; CHECK-NEXT: movl $42, %eax
; CHECK-NEXT: retq
entry:
%c = icmp eq ptr %x, null
br i1 %c, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i64 42
not_null:
%y = ptrtoint ptr %x to i64
%shry = shl i64 %y, 3
%shry.add = add i64 %shry, 3518
%y.ptr = inttoptr i64 %shry.add to ptr
%x.loc = getelementptr i64, ptr %y.ptr, i64 1
%t = load i64, ptr %x.loc
ret i64 %t
}
!0 = !{}