| ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6 | |
| ; RUN: llc -mtriple=aarch64 -verify-machineinstrs < %s | FileCheck %s | |
| ; RUN: llc -mtriple=aarch64_be -mattr=+sve -verify-machineinstrs < %s | FileCheck %s --check-prefix=BE | |
| ; Test the following DAG combine: | |
| ; uzp1(nvcast(x >> k), nvcast(y >> k)) -> uzp2(nvcast(x), nvcast(y)) | |
| ; (where k = sizeof(x)/2) | |
| define void @lshr_trunc_i16_load_scalable(ptr %src, ptr %dst) #0 { | |
| ; CHECK-LABEL: lshr_trunc_i16_load_scalable: | |
| ; CHECK: // %bb.0: | |
| ; CHECK-NEXT: ldr z0, [x0, #1, mul vl] | |
| ; CHECK-NEXT: ldr z1, [x0] | |
| ; CHECK-NEXT: uzp2 z0.b, z1.b, z0.b | |
| ; CHECK-NEXT: str z0, [x1] | |
| ; CHECK-NEXT: ret | |
| ; | |
| ; BE-LABEL: lshr_trunc_i16_load_scalable: | |
| ; BE: // %bb.0: | |
| ; BE-NEXT: ptrue p0.h | |
| ; BE-NEXT: ld1h { z0.h }, p0/z, [x0, #1, mul vl] | |
| ; BE-NEXT: ld1h { z1.h }, p0/z, [x0] | |
| ; BE-NEXT: ptrue p0.b | |
| ; BE-NEXT: uzp2 z0.b, z1.b, z0.b | |
| ; BE-NEXT: st1b { z0.b }, p0, [x1] | |
| ; BE-NEXT: ret | |
| %x = load <vscale x 16 x i16>, ptr %src, align 2 | |
| %shift = lshr <vscale x 16 x i16> %x, splat (i16 8) | |
| %result = trunc <vscale x 16 x i16> %shift to <vscale x 16 x i8> | |
| store <vscale x 16 x i8> %result, ptr %dst, align 1 | |
| ret void | |
| } | |
| define void @lshr_trunc_i16_load_fixed_length(ptr %src, ptr %dst) #0 { | |
| ; CHECK-LABEL: lshr_trunc_i16_load_fixed_length: | |
| ; CHECK: // %bb.0: | |
| ; CHECK-NEXT: ldp q1, q0, [x0] | |
| ; CHECK-NEXT: uzp2 v0.16b, v1.16b, v0.16b | |
| ; CHECK-NEXT: str q0, [x1] | |
| ; CHECK-NEXT: ret | |
| ; | |
| ; BE-LABEL: lshr_trunc_i16_load_fixed_length: | |
| ; BE: // %bb.0: | |
| ; BE-NEXT: add x8, x0, #16 | |
| ; BE-NEXT: ld1 { v0.8h }, [x0] | |
| ; BE-NEXT: ld1 { v1.8h }, [x8] | |
| ; BE-NEXT: uzp2 v0.16b, v0.16b, v1.16b | |
| ; BE-NEXT: st1 { v0.16b }, [x1] | |
| ; BE-NEXT: ret | |
| %x = load < 16 x i16>, ptr %src, align 2 | |
| %shift = lshr < 16 x i16> %x, splat (i16 8) | |
| %result = trunc < 16 x i16> %shift to < 16 x i8> | |
| store < 16 x i8> %result, ptr %dst, align 1 | |
| ret void | |
| } | |
| define void @ashr_trunc_i16_load(ptr %src, ptr %dst)#0 { | |
| ; CHECK-LABEL: ashr_trunc_i16_load: | |
| ; CHECK: // %bb.0: | |
| ; CHECK-NEXT: ldr z0, [x0, #1, mul vl] | |
| ; CHECK-NEXT: ldr z1, [x0] | |
| ; CHECK-NEXT: uzp2 z0.b, z1.b, z0.b | |
| ; CHECK-NEXT: str z0, [x1] | |
| ; CHECK-NEXT: ret | |
| ; | |
| ; BE-LABEL: ashr_trunc_i16_load: | |
| ; BE: // %bb.0: | |
| ; BE-NEXT: ptrue p0.h | |
| ; BE-NEXT: ld1h { z0.h }, p0/z, [x0, #1, mul vl] | |
| ; BE-NEXT: ld1h { z1.h }, p0/z, [x0] | |
| ; BE-NEXT: ptrue p0.b | |
| ; BE-NEXT: uzp2 z0.b, z1.b, z0.b | |
| ; BE-NEXT: st1b { z0.b }, p0, [x1] | |
| ; BE-NEXT: ret | |
| %x = load <vscale x 16 x i16>, ptr %src, align 2 | |
| %shift = ashr <vscale x 16 x i16> %x, splat (i16 8) | |
| %result = trunc <vscale x 16 x i16> %shift to <vscale x 16 x i8> | |
| store <vscale x 16 x i8> %result, ptr %dst, align 1 | |
| ret void | |
| } | |
| define void @lshr_trunc_i32_load(ptr %src, ptr %dst) #0 { | |
| ; CHECK-LABEL: lshr_trunc_i32_load: | |
| ; CHECK: // %bb.0: | |
| ; CHECK-NEXT: ldr z0, [x0, #1, mul vl] | |
| ; CHECK-NEXT: ldr z1, [x0] | |
| ; CHECK-NEXT: uzp2 z0.h, z1.h, z0.h | |
| ; CHECK-NEXT: str z0, [x1] | |
| ; CHECK-NEXT: ret | |
| ; | |
| ; BE-LABEL: lshr_trunc_i32_load: | |
| ; BE: // %bb.0: | |
| ; BE-NEXT: ptrue p0.s | |
| ; BE-NEXT: ld1w { z0.s }, p0/z, [x0, #1, mul vl] | |
| ; BE-NEXT: ld1w { z1.s }, p0/z, [x0] | |
| ; BE-NEXT: ptrue p0.h | |
| ; BE-NEXT: uzp2 z0.h, z1.h, z0.h | |
| ; BE-NEXT: st1h { z0.h }, p0, [x1] | |
| ; BE-NEXT: ret | |
| %x = load <vscale x 8 x i32>, ptr %src, align 2 | |
| %shift = lshr <vscale x 8 x i32> %x, splat (i32 16) | |
| %result = trunc <vscale x 8 x i32> %shift to <vscale x 8 x i16> | |
| store <vscale x 8 x i16> %result, ptr %dst, align 1 | |
| ret void | |
| } | |
| define void @lshr_trunc_i64_load(ptr %src, ptr %dst) #0 { | |
| ; CHECK-LABEL: lshr_trunc_i64_load: | |
| ; CHECK: // %bb.0: | |
| ; CHECK-NEXT: ldr z0, [x0, #1, mul vl] | |
| ; CHECK-NEXT: ldr z1, [x0] | |
| ; CHECK-NEXT: uzp2 z0.s, z1.s, z0.s | |
| ; CHECK-NEXT: str z0, [x1] | |
| ; CHECK-NEXT: ret | |
| ; | |
| ; BE-LABEL: lshr_trunc_i64_load: | |
| ; BE: // %bb.0: | |
| ; BE-NEXT: ptrue p0.d | |
| ; BE-NEXT: ld1d { z0.d }, p0/z, [x0, #1, mul vl] | |
| ; BE-NEXT: ld1d { z1.d }, p0/z, [x0] | |
| ; BE-NEXT: ptrue p0.s | |
| ; BE-NEXT: uzp2 z0.s, z1.s, z0.s | |
| ; BE-NEXT: st1w { z0.s }, p0, [x1] | |
| ; BE-NEXT: ret | |
| %x = load <vscale x 4 x i64>, ptr %src, align 2 | |
| %shift = lshr <vscale x 4 x i64> %x, splat (i64 32) | |
| %result = trunc <vscale x 4 x i64> %shift to <vscale x 4 x i32> | |
| store <vscale x 4 x i32> %result, ptr %dst, align 1 | |
| ret void | |
| } | |
| ; Negative test: the shift amount must be equal to the destination width. | |
| define void @lshr_trunc_wrong_amount(ptr %src, ptr %dst) #0 { | |
| ; CHECK-LABEL: lshr_trunc_wrong_amount: | |
| ; CHECK: // %bb.0: | |
| ; CHECK-NEXT: ldr z0, [x0] | |
| ; CHECK-NEXT: ldr z1, [x0, #1, mul vl] | |
| ; CHECK-NEXT: lsr z1.h, z1.h, #7 | |
| ; CHECK-NEXT: lsr z0.h, z0.h, #7 | |
| ; CHECK-NEXT: uzp1 z0.b, z0.b, z1.b | |
| ; CHECK-NEXT: str z0, [x1] | |
| ; CHECK-NEXT: ret | |
| ; | |
| ; BE-LABEL: lshr_trunc_wrong_amount: | |
| ; BE: // %bb.0: | |
| ; BE-NEXT: ptrue p0.h | |
| ; BE-NEXT: ld1h { z0.h }, p0/z, [x0] | |
| ; BE-NEXT: ld1h { z1.h }, p0/z, [x0, #1, mul vl] | |
| ; BE-NEXT: ptrue p0.b | |
| ; BE-NEXT: lsr z1.h, z1.h, #7 | |
| ; BE-NEXT: lsr z0.h, z0.h, #7 | |
| ; BE-NEXT: uzp1 z0.b, z0.b, z1.b | |
| ; BE-NEXT: st1b { z0.b }, p0, [x1] | |
| ; BE-NEXT: ret | |
| %x = load <vscale x 16 x i16>, ptr %src, align 2 | |
| %shift = lshr <vscale x 16 x i16> %x, splat (i16 7) | |
| %result = trunc <vscale x 16 x i16> %shift to <vscale x 16 x i8> | |
| store <vscale x 16 x i8> %result, ptr %dst, align 1 | |
| ret void | |
| } | |
| attributes #0 = { "target-features"="+sve" } |