| ; 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 | |
| } |