llvm-project/llvm/test/CodeGen/X86/implicit-null-checks.mir
Sanjoy Das 4a8fe09040 [ImplicitNullCheck] Fix an edge case where we were hoisting incorrectly
ImplicitNullCheck keeps track of one instruction that the memory
operation depends on that it also hoists with the memory operation.
When hoisting this dependency, it would sometimes clobber a live-in
value to the basic block we were hoisting the two things out of.  Fix
this by explicitly looking for such dependencies.

I also noticed two redundant checks on `MO.isDef()` in IsMIOperandSafe.
They're redundant since register MachineOperands are either Defs or Uses
-- there is no third kind.  I'll change the checks to asserts in a later
commit.

llvm-svn: 287213
2016-11-17 07:29:40 +00:00

373 lines
9.6 KiB
YAML

# RUN: llc -run-pass implicit-null-checks -mtriple=x86_64-apple-macosx -o - %s | FileCheck %s
--- |
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx"
;; Positive test
define i32 @imp_null_check_with_bitwise_op_0(i32* %x, i32 %val) {
entry:
br i1 undef, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i32 42
not_null:
br i1 undef, label %ret_100, label %ret_200
ret_100:
ret i32 100
ret_200:
ret i32 200
}
;; Negative test. The regalloc is such that we cannot hoist the
;; instruction materializing 2200000 into %eax
define i32 @imp_null_check_with_bitwise_op_1(i32* %x, i32 %val, i32* %ptr) {
entry:
br i1 undef, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i32 undef
not_null:
br i1 undef, label %ret_100, label %ret_200
ret_100:
ret i32 100
ret_200:
ret i32 200
}
;; Negative test: IR is identical to
;; @imp_null_check_with_bitwise_op_0 but MIR differs.
define i32 @imp_null_check_with_bitwise_op_2(i32* %x, i32 %val) {
entry:
br i1 undef, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i32 42
not_null:
br i1 undef, label %ret_100, label %ret_200
ret_100:
ret i32 100
ret_200:
ret i32 200
}
;; Negative test: IR is identical to
;; @imp_null_check_with_bitwise_op_0 but MIR differs.
define i32 @imp_null_check_with_bitwise_op_3(i32* %x, i32 %val) {
entry:
br i1 undef, label %is_null, label %not_null, !make.implicit !0
is_null:
ret i32 42
not_null:
br i1 undef, label %ret_100, label %ret_200
ret_100:
ret i32 100
ret_200:
ret i32 200
}
declare void @f() readonly
define i32 @no_hoist_across_call(i32* %ptr) {
entry:
%is_null = icmp eq i32* %ptr, null
br i1 %is_null, label %leave, label %stay, !make.implicit !0
stay:
call void @f()
%val = load i32, i32* %ptr
ret i32 %val
leave:
ret i32 0
}
define i32 @dependency_live_in_hazard(i32* %ptr, i32** %ptr2, i32* %ptr3) #0 {
entry:
%val = load i32*, i32** %ptr2
%ptr_is_null = icmp eq i32* %ptr, null
br i1 %ptr_is_null, label %is_null, label %not_null, !make.implicit !0
not_null: ; preds = %entry
%addend = load i32, i32* %val
%result = load i32, i32* %ptr
%result.shr = lshr i32 %result, 4
%result.and = and i32 %result.shr, 4095
%result.add = add i32 %addend, %result.and
ret i32 %result.add
is_null: ; preds = %entry
ret i32 0
}
attributes #0 = { "target-features"="+bmi,+bmi2" }
!0 = !{}
...
---
name: imp_null_check_with_bitwise_op_0
# CHECK-LABEL: name: imp_null_check_with_bitwise_op_0
alignment: 4
tracksRegLiveness: true
liveins:
- { reg: '%rdi' }
- { reg: '%esi' }
# CHECK: bb.0.entry:
# CHECK: %eax = MOV32ri 2200000
# CHECK-NEXT: %eax = FAULTING_LOAD_OP %bb.3.is_null, {{[0-9]+}}, killed %eax, killed %rdi, 1, _, 0, _, implicit-def dead %eflags :: (load 4 from %ir.x)
# CHECK-NEXT: JMP_1 %bb.1.not_null
body: |
bb.0.entry:
successors: %bb.3.is_null, %bb.1.not_null
liveins: %esi, %rdi
TEST64rr %rdi, %rdi, implicit-def %eflags
JE_1 %bb.3.is_null, implicit %eflags
bb.1.not_null:
successors: %bb.4.ret_100, %bb.2.ret_200
liveins: %esi, %rdi
%eax = MOV32ri 2200000
%eax = AND32rm killed %eax, killed %rdi, 1, _, 0, _, implicit-def dead %eflags :: (load 4 from %ir.x)
CMP32rr killed %eax, killed %esi, implicit-def %eflags
JE_1 %bb.4.ret_100, implicit %eflags
bb.2.ret_200:
%eax = MOV32ri 200
RET 0, %eax
bb.3.is_null:
%eax = MOV32ri 42
RET 0, %eax
bb.4.ret_100:
%eax = MOV32ri 100
RET 0, %eax
...
---
name: imp_null_check_with_bitwise_op_1
alignment: 4
tracksRegLiveness: true
liveins:
- { reg: '%rdi' }
- { reg: '%esi' }
- { reg: '%rdx' }
# CHECK: bb.0.entry:
# CHECK: %eax = MOV32rm killed %rdx, 1, _, 0, _ :: (volatile load 4 from %ir.ptr)
# CHECK-NEXT: TEST64rr %rdi, %rdi, implicit-def %eflags
# CHECK-NEXT: JE_1 %bb.3.is_null, implicit %eflags
body: |
bb.0.entry:
successors: %bb.3.is_null, %bb.1.not_null
liveins: %esi, %rdi, %rdx
%eax = MOV32rm killed %rdx, 1, _, 0, _ :: (volatile load 4 from %ir.ptr)
TEST64rr %rdi, %rdi, implicit-def %eflags
JE_1 %bb.3.is_null, implicit %eflags
bb.1.not_null:
successors: %bb.4.ret_100, %bb.2.ret_200
liveins: %esi, %rdi
%eax = MOV32ri 2200000
%eax = AND32rm killed %eax, killed %rdi, 1, _, 0, _, implicit-def dead %eflags :: (load 4 from %ir.x)
CMP32rr killed %eax, killed %esi, implicit-def %eflags
JE_1 %bb.4.ret_100, implicit %eflags
bb.2.ret_200:
successors: %bb.3.is_null
%eax = MOV32ri 200
bb.3.is_null:
liveins: %eax, %ah, %al, %ax, %bh, %bl, %bp, %bpl, %bx, %eax, %ebp, %ebx, %rax, %rbp, %rbx, %r12, %r13, %r14, %r15, %r12b, %r13b, %r14b, %r15b, %r12d, %r13d, %r14d, %r15d, %r12w, %r13w, %r14w, %r15w
RET 0, %eax
bb.4.ret_100:
%eax = MOV32ri 100
RET 0, %eax
...
---
name: imp_null_check_with_bitwise_op_2
# CHECK-LABEL: name: imp_null_check_with_bitwise_op_2
alignment: 4
tracksRegLiveness: true
liveins:
- { reg: '%rdi' }
- { reg: '%esi' }
# CHECK: bb.0.entry:
# CHECK: TEST64rr %rdi, %rdi, implicit-def %eflags
# CHECK-NEXT: JE_1 %bb.3.is_null, implicit %eflags
body: |
bb.0.entry:
successors: %bb.3.is_null, %bb.1.not_null
liveins: %esi, %rdi
TEST64rr %rdi, %rdi, implicit-def %eflags
JE_1 %bb.3.is_null, implicit %eflags
bb.1.not_null:
successors: %bb.4.ret_100, %bb.2.ret_200
liveins: %esi, %rdi
%eax = MOV32ri 2200000
%eax = ADD32ri killed %eax, 100, implicit-def dead %eflags
%eax = AND32rm killed %eax, killed %rdi, 1, _, 0, _, implicit-def dead %eflags :: (load 4 from %ir.x)
CMP32rr killed %eax, killed %esi, implicit-def %eflags
JE_1 %bb.4.ret_100, implicit %eflags
bb.2.ret_200:
%eax = MOV32ri 200
RET 0, %eax
bb.3.is_null:
%eax = MOV32ri 42
RET 0, %eax
bb.4.ret_100:
%eax = MOV32ri 100
RET 0, %eax
...
---
name: imp_null_check_with_bitwise_op_3
# CHECK-LABEL: name: imp_null_check_with_bitwise_op_3
alignment: 4
tracksRegLiveness: true
liveins:
- { reg: '%rdi' }
- { reg: '%rsi' }
# CHECK: bb.0.entry:
# CHECK: TEST64rr %rdi, %rdi, implicit-def %eflags
# CHECK-NEXT: JE_1 %bb.3.is_null, implicit %eflags
body: |
bb.0.entry:
successors: %bb.3.is_null, %bb.1.not_null
liveins: %rsi, %rdi
TEST64rr %rdi, %rdi, implicit-def %eflags
JE_1 %bb.3.is_null, implicit %eflags
bb.1.not_null:
successors: %bb.4.ret_100, %bb.2.ret_200
liveins: %rsi, %rdi
%rdi = MOV64ri 5000
%rdi = AND64rm killed %rdi, killed %rdi, 1, _, 0, _, implicit-def dead %eflags :: (load 4 from %ir.x)
CMP64rr killed %rdi, killed %rsi, implicit-def %eflags
JE_1 %bb.4.ret_100, implicit %eflags
bb.2.ret_200:
%eax = MOV32ri 200
RET 0, %eax
bb.3.is_null:
%eax = MOV32ri 42
RET 0, %eax
bb.4.ret_100:
%eax = MOV32ri 100
RET 0, %eax
...
---
name: no_hoist_across_call
# CHECK-LABEL: name: no_hoist_across_call
alignment: 4
tracksRegLiveness: true
liveins:
- { reg: '%rdi' }
calleeSavedRegisters: [ '%bh', '%bl', '%bp', '%bpl', '%bx', '%ebp', '%ebx',
'%rbp', '%rbx', '%r12', '%r13', '%r14', '%r15',
'%r12b', '%r13b', '%r14b', '%r15b', '%r12d', '%r13d',
'%r14d', '%r15d', '%r12w', '%r13w', '%r14w', '%r15w' ]
# CHECK: body:
# CHECK-NOT: FAULTING_LOAD_OP
# CHECK: bb.1.stay:
# CHECK: CALL64pcrel32
body: |
bb.0.entry:
successors: %bb.2.leave, %bb.1.stay
liveins: %rdi, %rbx
frame-setup PUSH64r killed %rbx, implicit-def %rsp, implicit %rsp
CFI_INSTRUCTION def_cfa_offset 16
CFI_INSTRUCTION offset %rbx, -16
%rbx = MOV64rr %rdi
TEST64rr %rbx, %rbx, implicit-def %eflags
JE_1 %bb.2.leave, implicit killed %eflags
bb.1.stay:
liveins: %rbx
CALL64pcrel32 @f, csr_64, implicit %rsp, implicit-def %rsp
%eax = MOV32rm killed %rbx, 1, _, 0, _ :: (load 4 from %ir.ptr)
%rbx = POP64r implicit-def %rsp, implicit %rsp
RETQ %eax
bb.2.leave:
%eax = XOR32rr undef %eax, undef %eax, implicit-def dead %eflags
%rbx = POP64r implicit-def %rsp, implicit %rsp
RETQ %eax
...
---
name: dependency_live_in_hazard
# CHECK-LABEL: name: dependency_live_in_hazard
# CHECK: bb.0.entry:
# CHECK-NOT: FAULTING_LOAD_OP
# CHECK: bb.1.not_null:
# Make sure that the BEXTR32rm instruction below is not used to emit
# an implicit null check -- hoisting it will require hosting the move
# to %esi and we cannot do that without clobbering the use of %rsi in
# the first instruction in bb.1.not_null.
alignment: 4
tracksRegLiveness: true
liveins:
- { reg: '%rdi' }
- { reg: '%rsi' }
body: |
bb.0.entry:
successors: %bb.2.is_null, %bb.1.not_null
liveins: %rdi, %rsi
TEST64rr %rdi, %rdi, implicit-def %eflags
JE_1 %bb.2.is_null, implicit killed %eflags
bb.1.not_null:
liveins: %rdi, %rsi
%rcx = MOV64rm killed %rsi, 1, _, 0, _ :: (load 8 from %ir.ptr2)
%esi = MOV32ri 3076
%eax = BEXTR32rm killed %rdi, 1, _, 0, _, killed %esi, implicit-def dead %eflags :: (load 4 from %ir.ptr)
%eax = ADD32rm killed %eax, killed %rcx, 1, _, 0, _, implicit-def dead %eflags :: (load 4 from %ir.val)
RETQ %eax
bb.2.is_null:
%eax = XOR32rr undef %eax, undef %eax, implicit-def dead %eflags
RETQ %eax
...