| # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py |
| # |
| # Check that we can fold (x & mask) -> x when (x & mask) is known to equal x. |
| # |
| # RUN: llc -mtriple aarch64 -run-pass=aarch64-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s |
| |
| --- |
| name: remove_and_with_one_bit |
| legalized: true |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $w0, $w1 |
| ; G_ICMP produces a single bit. The mask is 1. |
| ; |
| ; cmp = 000...0? |
| ; mask = 000...01 |
| ; cmp & mask = 000...0? |
| ; |
| ; Remove the G_AND. |
| ; |
| ; CHECK-LABEL: name: remove_and_with_one_bit |
| ; CHECK: liveins: $w0, $w1 |
| ; CHECK: %x:_(s32) = COPY $w0 |
| ; CHECK: %y:_(s32) = COPY $w1 |
| ; CHECK: %cmp:_(s32) = G_ICMP intpred(eq), %x(s32), %y |
| ; CHECK: $w0 = COPY %cmp(s32) |
| ; CHECK: RET_ReallyLR implicit $w0 |
| %x:_(s32) = COPY $w0 |
| %y:_(s32) = COPY $w1 |
| %cmp:_(s32) = G_ICMP intpred(eq), %x(s32), %y |
| %mask:_(s32) = G_CONSTANT i32 1 |
| %and:_(s32) = G_AND %cmp(s32), %mask |
| $w0 = COPY %and(s32) |
| RET_ReallyLR implicit $w0 |
| |
| ... |
| --- |
| name: remove_and_all_ones_mask |
| legalized: true |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $w0, $w1, $w2 |
| ; -1 is all ones. Therefore z & -1 = z. Remove the G_AND. |
| ; |
| ; CHECK-LABEL: name: remove_and_all_ones_mask |
| ; CHECK: liveins: $w0, $w1, $w2 |
| ; CHECK: %z:_(s32) = COPY $w2 |
| ; CHECK: $w0 = COPY %z(s32) |
| ; CHECK: RET_ReallyLR implicit $w0 |
| %x:_(s32) = COPY $w0 |
| %y:_(s32) = COPY $w1 |
| %z:_(s32) = COPY $w2 |
| %mask:_(s32) = G_CONSTANT i32 -1 |
| %and:_(s32) = G_AND %z(s32), %mask |
| $w0 = COPY %and(s32) |
| RET_ReallyLR implicit $w0 |
| |
| ... |
| --- |
| name: remove_and_all_ones_zext |
| legalized: true |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $w0, $w1, $w2 |
| ; %z is a s32, so it can be at most the all-ones value on 32 bits. |
| ; In decimal this is 4294967295. Any zero-extension of %z is at most this |
| ; value. |
| ; |
| ; Therefore, zext(z) & 4294967295 == z. Remove the G_AND. |
| ; |
| ; CHECK-LABEL: name: remove_and_all_ones_zext |
| ; CHECK: liveins: $w0, $w1, $w2 |
| ; CHECK: %z:_(s32) = COPY $w2 |
| ; CHECK: %ext:_(s64) = G_ZEXT %z(s32) |
| ; CHECK: $x0 = COPY %ext(s64) |
| ; CHECK: RET_ReallyLR implicit $x0 |
| %x:_(s32) = COPY $w0 |
| %y:_(s32) = COPY $w1 |
| %z:_(s32) = COPY $w2 |
| %ext:_(s64) = G_ZEXT %z |
| %mask:_(s64) = G_CONSTANT i64 4294967295 |
| %and:_(s64) = G_AND %ext(s64), %mask |
| $x0 = COPY %and(s64) |
| RET_ReallyLR implicit $x0 |
| |
| ... |
| --- |
| name: remove_and_all_ones_anyext |
| legalized: true |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $w0, $w1, $w2 |
| ; This is the same as the zext case. |
| ; |
| ; CHECK-LABEL: name: remove_and_all_ones_anyext |
| ; CHECK: liveins: $w0, $w1, $w2 |
| ; CHECK: %z:_(s32) = COPY $w2 |
| ; CHECK: %ext:_(s64) = G_ZEXT %z(s32) |
| ; CHECK: $x0 = COPY %ext(s64) |
| ; CHECK: RET_ReallyLR implicit $x0 |
| %x:_(s32) = COPY $w0 |
| %y:_(s32) = COPY $w1 |
| %z:_(s32) = COPY $w2 |
| %ext:_(s64) = G_ZEXT %z |
| %mask:_(s64) = G_CONSTANT i64 4294967295 |
| %and:_(s64) = G_AND %ext(s64), %mask |
| $x0 = COPY %and(s64) |
| RET_ReallyLR implicit $x0 |
| |
| ... |
| --- |
| name: dont_remove_all_ones_sext |
| legalized: true |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $w0, $w1, $w2 |
| ; We don't know if the sign bit is set on %z. So, the value in %ext may have |
| ; higher bits set than 4294967295. |
| ; |
| ; CHECK-LABEL: name: dont_remove_all_ones_sext |
| ; CHECK: liveins: $w0, $w1, $w2 |
| ; CHECK: %z:_(s32) = COPY $w2 |
| ; CHECK: %ext:_(s64) = G_SEXT %z(s32) |
| ; CHECK: %mask:_(s64) = G_CONSTANT i64 4294967295 |
| ; CHECK: %and:_(s64) = G_AND %ext, %mask |
| ; CHECK: $x0 = COPY %and(s64) |
| ; CHECK: RET_ReallyLR implicit $x0 |
| %x:_(s32) = COPY $w0 |
| %y:_(s32) = COPY $w1 |
| %z:_(s32) = COPY $w2 |
| %ext:_(s64) = G_SEXT %z |
| %mask:_(s64) = G_CONSTANT i64 4294967295 |
| %and:_(s64) = G_AND %ext(s64), %mask |
| $x0 = COPY %and(s64) |
| RET_ReallyLR implicit $x0 |
| |
| ... |
| --- |
| name: remove_and_positive_constant_sext |
| legalized: true |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $w0, $w1, $w2 |
| ; We know the sign bit is not set on %z. Therefore, |
| ; |
| ; z = ext = 42 = 000...0101010 |
| ; mask = 0000...0111111 |
| ; |
| ; So z & mask == z |
| ; CHECK-LABEL: name: remove_and_positive_constant_sext |
| ; CHECK: liveins: $w0, $w1, $w2 |
| ; CHECK: %z:_(s32) = G_CONSTANT i32 42 |
| ; CHECK: %ext:_(s64) = G_SEXT %z(s32) |
| ; CHECK: $x0 = COPY %ext(s64) |
| ; CHECK: RET_ReallyLR implicit $x0 |
| %x:_(s32) = COPY $w0 |
| %y:_(s32) = COPY $w1 |
| %z:_(s32) = G_CONSTANT i32 42 |
| %ext:_(s64) = G_SEXT %z |
| %mask:_(s64) = G_CONSTANT i64 63 |
| %and:_(s64) = G_AND %ext(s64), %mask |
| $x0 = COPY %and(s64) |
| RET_ReallyLR implicit $x0 |
| |
| ... |
| --- |
| name: not_a_mask |
| legalized: true |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $w0, $w1 |
| ; 6 is not a mask, so we should still have the G_AND. |
| ; |
| ; CHECK-LABEL: name: not_a_mask |
| ; CHECK: liveins: $w0, $w1 |
| ; CHECK: %x:_(s32) = COPY $w0 |
| ; CHECK: %y:_(s32) = COPY $w1 |
| ; CHECK: %cmp:_(s32) = G_ICMP intpred(eq), %x(s32), %y |
| ; CHECK: %mask:_(s32) = G_CONSTANT i32 6 |
| ; CHECK: %and:_(s32) = G_AND %cmp, %mask |
| ; CHECK: $w0 = COPY %and(s32) |
| ; CHECK: RET_ReallyLR implicit $w0 |
| %x:_(s32) = COPY $w0 |
| %y:_(s32) = COPY $w1 |
| %cmp:_(s32) = G_ICMP intpred(eq), %x(s32), %y |
| %mask:_(s32) = G_CONSTANT i32 6 |
| %and:_(s32) = G_AND %cmp(s32), %mask |
| $w0 = COPY %and(s32) |
| RET_ReallyLR implicit $w0 |
| |
| ... |
| --- |
| name: unknown_val |
| legalized: true |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $w0, $w1, $w2 |
| ; We don't know what's in $w2, so we can't remove the G_AND without a mask |
| ; that fills every bit in the type. |
| ; |
| ; CHECK-LABEL: name: unknown_val |
| ; CHECK: liveins: $w0, $w1, $w2 |
| ; CHECK: %z:_(s32) = COPY $w2 |
| ; CHECK: %one:_(s32) = G_CONSTANT i32 32 |
| ; CHECK: %and:_(s32) = G_AND %z, %one |
| ; CHECK: $w0 = COPY %and(s32) |
| ; CHECK: RET_ReallyLR implicit $w0 |
| %x:_(s32) = COPY $w0 |
| %y:_(s32) = COPY $w1 |
| %z:_(s32) = COPY $w2 |
| %one:_(s32) = G_CONSTANT i32 32 |
| %and:_(s32) = G_AND %z(s32), %one |
| $w0 = COPY %and(s32) |
| RET_ReallyLR implicit $w0 |
| ... |
| --- |
| name: remove_and_assert_zext |
| legalized: true |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $w0 |
| ; G_ASSERT_ZEXT communicates that only the bottom 8 bits of %x can be set. |
| ; So, the G_AND can be removed. |
| |
| ; CHECK-LABEL: name: remove_and_assert_zext |
| ; CHECK: liveins: $w0 |
| ; CHECK: %x:_(s32) = COPY $w0 |
| ; CHECK: %assert_zext:_(s32) = G_ASSERT_ZEXT %x, 8 |
| ; CHECK: $w0 = COPY %assert_zext(s32) |
| ; CHECK: RET_ReallyLR implicit $w0 |
| %x:_(s32) = COPY $w0 |
| %assert_zext:_(s32) = G_ASSERT_ZEXT %x(s32), 8 |
| %mask:_(s32) = G_CONSTANT i32 255 |
| %and:_(s32) = G_AND %assert_zext(s32), %mask |
| $w0 = COPY %and(s32) |
| RET_ReallyLR implicit $w0 |
| ... |
| --- |
| name: dont_remove_and_assert_zext_wrong_mask |
| legalized: true |
| tracksRegLiveness: true |
| body: | |
| bb.0: |
| liveins: $w0 |
| ; The mask here is for 8 bits, not 16. |
| |
| ; CHECK-LABEL: name: dont_remove_and_assert_zext |
| ; CHECK: liveins: $w0 |
| ; CHECK: %x:_(s32) = COPY $w0 |
| ; CHECK: %assert_zext:_(s32) = G_ASSERT_ZEXT %x, 16 |
| ; CHECK: %mask:_(s32) = G_CONSTANT i32 255 |
| ; CHECK: %and:_(s32) = G_AND %assert_zext, %mask |
| ; CHECK: $w0 = COPY %and(s32) |
| ; CHECK: RET_ReallyLR implicit $w0 |
| %x:_(s32) = COPY $w0 |
| %assert_zext:_(s32) = G_ASSERT_ZEXT %x(s32), 16 |
| %mask:_(s32) = G_CONSTANT i32 255 |
| %and:_(s32) = G_AND %assert_zext(s32), %mask |
| $w0 = COPY %and(s32) |
| RET_ReallyLR implicit $w0 |