blob: 3851910b1523748a06b02ec008f27e8bc53a4b77 [file] [edit]
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
; RUN: llc -mtriple=thumbv8m.main-none-eabi -mcpu=cortex-m33 < %s | FileCheck %s --check-prefix=THUMBV8
; RUN: llc -mtriple=thumbv7m-none-eabi < %s | FileCheck %s --check-prefix=THUMBV7
; RUN: llc -mtriple=armv7-none-eabi < %s | FileCheck %s --check-prefix=ARM
; Shift out the sign bit instead of clearing it with BIC before the zero test.
define i1 @is_zero_or_int_min_i32(i32 %x) {
; THUMBV8-LABEL: is_zero_or_int_min_i32:
; THUMBV8: @ %bb.0:
; THUMBV8-NEXT: lsls r0, r0, #1
; THUMBV8-NEXT: clz r0, r0
; THUMBV8-NEXT: lsrs r0, r0, #5
; THUMBV8-NEXT: bx lr
;
; THUMBV7-LABEL: is_zero_or_int_min_i32:
; THUMBV7: @ %bb.0:
; THUMBV7-NEXT: lsls r0, r0, #1
; THUMBV7-NEXT: clz r0, r0
; THUMBV7-NEXT: lsrs r0, r0, #5
; THUMBV7-NEXT: bx lr
;
; ARM-LABEL: is_zero_or_int_min_i32:
; ARM: @ %bb.0:
; ARM-NEXT: bic r0, r0, #-2147483648
; ARM-NEXT: clz r0, r0
; ARM-NEXT: lsr r0, r0, #5
; ARM-NEXT: bx lr
%masked = and i32 %x, 2147483647
%cmp = icmp eq i32 %masked, 0
ret i1 %cmp
}
; The same transform works for a different low mask inside an OR.
define i1 @is_zero_low_30_bits_or_i32(i32 %x, i32 %y) {
; THUMBV8-LABEL: is_zero_low_30_bits_or_i32:
; THUMBV8: @ %bb.0:
; THUMBV8-NEXT: orr.w r0, r1, r0, lsl #2
; THUMBV8-NEXT: clz r0, r0
; THUMBV8-NEXT: lsrs r0, r0, #5
; THUMBV8-NEXT: bx lr
;
; THUMBV7-LABEL: is_zero_low_30_bits_or_i32:
; THUMBV7: @ %bb.0:
; THUMBV7-NEXT: orr.w r0, r1, r0, lsl #2
; THUMBV7-NEXT: clz r0, r0
; THUMBV7-NEXT: lsrs r0, r0, #5
; THUMBV7-NEXT: bx lr
;
; ARM-LABEL: is_zero_low_30_bits_or_i32:
; ARM: @ %bb.0:
; ARM-NEXT: orr r0, r1, r0, lsl #2
; ARM-NEXT: clz r0, r0
; ARM-NEXT: lsr r0, r0, #5
; ARM-NEXT: bx lr
%masked = and i32 %x, 1073741823
%combined = or i32 %y, %masked
%cmp = icmp eq i32 %combined, 0
ret i1 %cmp
}
; i64 legalization ORs the halves together. Fold the shift into that OR.
define i1 @is_zero_or_int_min_i64(i64 %x) {
; THUMBV8-LABEL: is_zero_or_int_min_i64:
; THUMBV8: @ %bb.0:
; THUMBV8-NEXT: orr.w r0, r0, r1, lsl #1
; THUMBV8-NEXT: clz r0, r0
; THUMBV8-NEXT: lsrs r0, r0, #5
; THUMBV8-NEXT: bx lr
;
; THUMBV7-LABEL: is_zero_or_int_min_i64:
; THUMBV7: @ %bb.0:
; THUMBV7-NEXT: orr.w r0, r0, r1, lsl #1
; THUMBV7-NEXT: clz r0, r0
; THUMBV7-NEXT: lsrs r0, r0, #5
; THUMBV7-NEXT: bx lr
;
; ARM-LABEL: is_zero_or_int_min_i64:
; ARM: @ %bb.0:
; ARM-NEXT: orr r0, r0, r1, lsl #1
; ARM-NEXT: clz r0, r0
; ARM-NEXT: lsr r0, r0, #5
; ARM-NEXT: bx lr
%masked = and i64 %x, 9223372036854775807
%cmp = icmp eq i64 %masked, 0
ret i1 %cmp
}
declare void @func_1_i64(i64)
declare void @func_2_i64(i64)
declare void @func_1_i32(i32)
declare void @func_2_i32(i32)
; Exercise the i32 branch path as well as the i64 legalization path.
define void @branch_on_nonzero_low_31_bits_i32(i32 %x) {
; THUMBV8-LABEL: branch_on_nonzero_low_31_bits_i32:
; THUMBV8: @ %bb.0:
; THUMBV8-NEXT: lsls r1, r0, #1
; THUMBV8-NEXT: it eq
; THUMBV8-NEXT: beq func_2_i32
; THUMBV8-NEXT: .LBB3_1: @ %yes
; THUMBV8-NEXT: b func_1_i32
;
; THUMBV7-LABEL: branch_on_nonzero_low_31_bits_i32:
; THUMBV7: @ %bb.0:
; THUMBV7-NEXT: lsls r1, r0, #1
; THUMBV7-NEXT: it eq
; THUMBV7-NEXT: beq func_2_i32
; THUMBV7-NEXT: .LBB3_1: @ %yes
; THUMBV7-NEXT: b func_1_i32
;
; ARM-LABEL: branch_on_nonzero_low_31_bits_i32:
; ARM: @ %bb.0:
; ARM-NEXT: mvn r1, #-2147483648
; ARM-NEXT: tst r0, r1
; ARM-NEXT: beq .LBB3_2
; ARM-NEXT: @ %bb.1: @ %yes
; ARM-NEXT: b func_1_i32
; ARM-NEXT: .LBB3_2: @ %no
; ARM-NEXT: b func_2_i32
%masked = and i32 %x, 2147483647
%cmp = icmp ne i32 %masked, 0
br i1 %cmp, label %yes, label %no
yes:
tail call void @func_1_i32(i32 %x)
ret void
no:
tail call void @func_2_i32(i32 %x)
ret void
}
; ARM mode can fold the shift into ORR just like Thumb-2.
define void @branch_on_nonzero_low_30_bits_or_i32(i32 %x, i32 %y) {
; THUMBV8-LABEL: branch_on_nonzero_low_30_bits_or_i32:
; THUMBV8: @ %bb.0:
; THUMBV8-NEXT: orrs.w r1, r1, r0, lsl #2
; THUMBV8-NEXT: it eq
; THUMBV8-NEXT: beq func_2_i32
; THUMBV8-NEXT: .LBB4_1: @ %yes
; THUMBV8-NEXT: b func_1_i32
;
; THUMBV7-LABEL: branch_on_nonzero_low_30_bits_or_i32:
; THUMBV7: @ %bb.0:
; THUMBV7-NEXT: orrs.w r1, r1, r0, lsl #2
; THUMBV7-NEXT: it eq
; THUMBV7-NEXT: beq func_2_i32
; THUMBV7-NEXT: .LBB4_1: @ %yes
; THUMBV7-NEXT: b func_1_i32
;
; ARM-LABEL: branch_on_nonzero_low_30_bits_or_i32:
; ARM: @ %bb.0:
; ARM-NEXT: orrs r1, r1, r0, lsl #2
; ARM-NEXT: beq .LBB4_2
; ARM-NEXT: @ %bb.1: @ %yes
; ARM-NEXT: b func_1_i32
; ARM-NEXT: .LBB4_2: @ %no
; ARM-NEXT: b func_2_i32
%masked = and i32 %x, 1073741823
%combined = or i32 %y, %masked
%cmp = icmp ne i32 %combined, 0
br i1 %cmp, label %yes, label %no
yes:
tail call void @func_1_i32(i32 %x)
ret void
no:
tail call void @func_2_i32(i32 %x)
ret void
}
; Also use the shifted OR when branching on inequality against zero.
define void @branch_on_nonzero_low_63_bits_i64(i64 %x) {
; THUMBV8-LABEL: branch_on_nonzero_low_63_bits_i64:
; THUMBV8: @ %bb.0:
; THUMBV8-NEXT: orrs.w r2, r0, r1, lsl #1
; THUMBV8-NEXT: it eq
; THUMBV8-NEXT: beq func_2_i64
; THUMBV8-NEXT: .LBB5_1: @ %yes
; THUMBV8-NEXT: b func_1_i64
;
; THUMBV7-LABEL: branch_on_nonzero_low_63_bits_i64:
; THUMBV7: @ %bb.0:
; THUMBV7-NEXT: orrs.w r2, r0, r1, lsl #1
; THUMBV7-NEXT: it eq
; THUMBV7-NEXT: beq func_2_i64
; THUMBV7-NEXT: .LBB5_1: @ %yes
; THUMBV7-NEXT: b func_1_i64
;
; ARM-LABEL: branch_on_nonzero_low_63_bits_i64:
; ARM: @ %bb.0:
; ARM-NEXT: orrs r2, r0, r1, lsl #1
; ARM-NEXT: beq .LBB5_2
; ARM-NEXT: @ %bb.1: @ %yes
; ARM-NEXT: b func_1_i64
; ARM-NEXT: .LBB5_2: @ %no
; ARM-NEXT: b func_2_i64
%masked = and i64 %x, 9223372036854775807
%cmp = icmp ne i64 %masked, 0
br i1 %cmp, label %yes, label %no
yes:
tail call void @func_1_i64(i64 %x)
ret void
no:
tail call void @func_2_i64(i64 %x)
ret void
}
; The transform preserves zero/nonzero, not comparisons against other values.
define i1 @masked_or_equals_nonzero(i32 %x, i32 %y) {
; THUMBV8-LABEL: masked_or_equals_nonzero:
; THUMBV8: @ %bb.0:
; THUMBV8-NEXT: bic r0, r0, #-2147483648
; THUMBV8-NEXT: orrs r0, r1
; THUMBV8-NEXT: subs r0, #1
; THUMBV8-NEXT: clz r0, r0
; THUMBV8-NEXT: lsrs r0, r0, #5
; THUMBV8-NEXT: bx lr
;
; THUMBV7-LABEL: masked_or_equals_nonzero:
; THUMBV7: @ %bb.0:
; THUMBV7-NEXT: bic r0, r0, #-2147483648
; THUMBV7-NEXT: orrs r0, r1
; THUMBV7-NEXT: subs r0, #1
; THUMBV7-NEXT: clz r0, r0
; THUMBV7-NEXT: lsrs r0, r0, #5
; THUMBV7-NEXT: bx lr
;
; ARM-LABEL: masked_or_equals_nonzero:
; ARM: @ %bb.0:
; ARM-NEXT: bic r0, r0, #-2147483648
; ARM-NEXT: orr r0, r0, r1
; ARM-NEXT: sub r0, r0, #1
; ARM-NEXT: clz r0, r0
; ARM-NEXT: lsr r0, r0, #5
; ARM-NEXT: bx lr
%masked = and i32 %x, 2147483647
%combined = or i32 %masked, %y
%cmp = icmp eq i32 %combined, 1
ret i1 %cmp
}
; A shift can change the sign, so keep the mask for signed comparisons.
define i1 @masked_or_signed_greater_than_zero(i32 %x, i32 %y) {
; THUMBV8-LABEL: masked_or_signed_greater_than_zero:
; THUMBV8: @ %bb.0:
; THUMBV8-NEXT: bic r0, r0, #-2147483648
; THUMBV8-NEXT: orrs r0, r1
; THUMBV8-NEXT: cmp r0, #0
; THUMBV8-NEXT: mov.w r0, #0
; THUMBV8-NEXT: it gt
; THUMBV8-NEXT: movgt r0, #1
; THUMBV8-NEXT: bx lr
;
; THUMBV7-LABEL: masked_or_signed_greater_than_zero:
; THUMBV7: @ %bb.0:
; THUMBV7-NEXT: bic r0, r0, #-2147483648
; THUMBV7-NEXT: orrs r1, r0
; THUMBV7-NEXT: movs r0, #0
; THUMBV7-NEXT: cmp r1, #0
; THUMBV7-NEXT: it gt
; THUMBV7-NEXT: movgt r0, #1
; THUMBV7-NEXT: bx lr
;
; ARM-LABEL: masked_or_signed_greater_than_zero:
; ARM: @ %bb.0:
; ARM-NEXT: bic r0, r0, #-2147483648
; ARM-NEXT: orr r1, r0, r1
; ARM-NEXT: mov r0, #0
; ARM-NEXT: cmp r1, #0
; ARM-NEXT: movwgt r0, #1
; ARM-NEXT: bx lr
%masked = and i32 %x, 2147483647
%combined = or i32 %masked, %y
%cmp = icmp sgt i32 %combined, 0
ret i1 %cmp
}
; Shifting cannot remove a hole in the mask.
define i1 @is_zero_noncontiguous_masked_or(i32 %x, i32 %y) {
; THUMBV8-LABEL: is_zero_noncontiguous_masked_or:
; THUMBV8: @ %bb.0:
; THUMBV8-NEXT: movw r2, #65533
; THUMBV8-NEXT: movt r2, #32767
; THUMBV8-NEXT: ands r0, r2
; THUMBV8-NEXT: orrs r0, r1
; THUMBV8-NEXT: clz r0, r0
; THUMBV8-NEXT: lsrs r0, r0, #5
; THUMBV8-NEXT: bx lr
;
; THUMBV7-LABEL: is_zero_noncontiguous_masked_or:
; THUMBV7: @ %bb.0:
; THUMBV7-NEXT: movw r2, #65533
; THUMBV7-NEXT: movt r2, #32767
; THUMBV7-NEXT: ands r0, r2
; THUMBV7-NEXT: orrs r0, r1
; THUMBV7-NEXT: clz r0, r0
; THUMBV7-NEXT: lsrs r0, r0, #5
; THUMBV7-NEXT: bx lr
;
; ARM-LABEL: is_zero_noncontiguous_masked_or:
; ARM: @ %bb.0:
; ARM-NEXT: bic r0, r0, #-2147483646
; ARM-NEXT: orr r0, r0, r1
; ARM-NEXT: clz r0, r0
; ARM-NEXT: lsr r0, r0, #5
; ARM-NEXT: bx lr
%masked = and i32 %x, 2147483645
%combined = or i32 %masked, %y
%cmp = icmp eq i32 %combined, 0
ret i1 %cmp
}
; Reuse a live masked value rather than adding a separate shift for its test.
define i1 @is_zero_mask_multiple_uses(i32 %x, ptr %p) {
; THUMBV8-LABEL: is_zero_mask_multiple_uses:
; THUMBV8: @ %bb.0:
; THUMBV8-NEXT: bic r2, r0, #-2147483648
; THUMBV8-NEXT: clz r0, r2
; THUMBV8-NEXT: lsrs r0, r0, #5
; THUMBV8-NEXT: str r2, [r1]
; THUMBV8-NEXT: bx lr
;
; THUMBV7-LABEL: is_zero_mask_multiple_uses:
; THUMBV7: @ %bb.0:
; THUMBV7-NEXT: bic r0, r0, #-2147483648
; THUMBV7-NEXT: str r0, [r1]
; THUMBV7-NEXT: clz r0, r0
; THUMBV7-NEXT: lsrs r0, r0, #5
; THUMBV7-NEXT: bx lr
;
; ARM-LABEL: is_zero_mask_multiple_uses:
; ARM: @ %bb.0:
; ARM-NEXT: bic r0, r0, #-2147483648
; ARM-NEXT: str r0, [r1]
; ARM-NEXT: clz r0, r0
; ARM-NEXT: lsr r0, r0, #5
; ARM-NEXT: bx lr
%masked = and i32 %x, 2147483647
store i32 %masked, ptr %p
%cmp = icmp eq i32 %masked, 0
ret i1 %cmp
}