mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-11-15 20:06:46 +00:00
0cb1019e9c
Rather than just splitting the input type and hoping for the best, apply a bit more cleverness. Just splitting the types until the source is legal often leads to an illegal result time, which is then widened and a scalarization step is introduced which leads to truly horrible code generation. With the loop vectorizer, these sorts of operations are much more common, and so it's worth extra effort to do them well. Add a legalization hook for the operands of a TRUNCATE node, which will be encountered after the result type has been legalized, but if the operand type is still illegal. If simple splitting of both types ends up with the result type of each half still being legal, just do that (v16i16 -> v16i8 on ARM, for example). If, however, that would result in an illegal result type (v8i32 -> v8i8 on ARM, for example), we can get more clever with power-two vectors. Specifically, split the input type, but also widen the result element size, then concatenate the halves and truncate again. For example on ARM, To perform a "%res = v8i8 trunc v8i32 %in" we transform to: %inlo = v4i32 extract_subvector %in, 0 %inhi = v4i32 extract_subvector %in, 4 %lo16 = v4i16 trunc v4i32 %inlo %hi16 = v4i16 trunc v4i32 %inhi %in16 = v8i16 concat_vectors v4i16 %lo16, v4i16 %hi16 %res = v8i8 trunc v8i16 %in16 This allows instruction selection to generate three VMOVN instructions instead of a sequences of moves, stores and loads. Update the ARMTargetTransformInfo to take this improved legalization into account. Consider the simplified IR: define <16 x i8> @test1(<16 x i32>* %ap) { %a = load <16 x i32>* %ap %tmp = trunc <16 x i32> %a to <16 x i8> ret <16 x i8> %tmp } define <8 x i8> @test2(<8 x i32>* %ap) { %a = load <8 x i32>* %ap %tmp = trunc <8 x i32> %a to <8 x i8> ret <8 x i8> %tmp } Previously, we would generate the truly hideous: .syntax unified .section __TEXT,__text,regular,pure_instructions .globl _test1 .align 2 _test1: @ @test1 @ BB#0: push {r7} mov r7, sp sub sp, sp, #20 bic sp, sp, #7 add r1, r0, #48 add r2, r0, #32 vld1.64 {d24, d25}, [r0:128] vld1.64 {d16, d17}, [r1:128] vld1.64 {d18, d19}, [r2:128] add r1, r0, #16 vmovn.i32 d22, q8 vld1.64 {d16, d17}, [r1:128] vmovn.i32 d20, q9 vmovn.i32 d18, q12 vmov.u16 r0, d22[3] strb r0, [sp, #15] vmov.u16 r0, d22[2] strb r0, [sp, #14] vmov.u16 r0, d22[1] strb r0, [sp, #13] vmov.u16 r0, d22[0] vmovn.i32 d16, q8 strb r0, [sp, #12] vmov.u16 r0, d20[3] strb r0, [sp, #11] vmov.u16 r0, d20[2] strb r0, [sp, #10] vmov.u16 r0, d20[1] strb r0, [sp, #9] vmov.u16 r0, d20[0] strb r0, [sp, #8] vmov.u16 r0, d18[3] strb r0, [sp, #3] vmov.u16 r0, d18[2] strb r0, [sp, #2] vmov.u16 r0, d18[1] strb r0, [sp, #1] vmov.u16 r0, d18[0] strb r0, [sp] vmov.u16 r0, d16[3] strb r0, [sp, #7] vmov.u16 r0, d16[2] strb r0, [sp, #6] vmov.u16 r0, d16[1] strb r0, [sp, #5] vmov.u16 r0, d16[0] strb r0, [sp, #4] vldmia sp, {d16, d17} vmov r0, r1, d16 vmov r2, r3, d17 mov sp, r7 pop {r7} bx lr .globl _test2 .align 2 _test2: @ @test2 @ BB#0: push {r7} mov r7, sp sub sp, sp, #12 bic sp, sp, #7 vld1.64 {d16, d17}, [r0:128] add r0, r0, #16 vld1.64 {d20, d21}, [r0:128] vmovn.i32 d18, q8 vmov.u16 r0, d18[3] vmovn.i32 d16, q10 strb r0, [sp, #3] vmov.u16 r0, d18[2] strb r0, [sp, #2] vmov.u16 r0, d18[1] strb r0, [sp, #1] vmov.u16 r0, d18[0] strb r0, [sp] vmov.u16 r0, d16[3] strb r0, [sp, #7] vmov.u16 r0, d16[2] strb r0, [sp, #6] vmov.u16 r0, d16[1] strb r0, [sp, #5] vmov.u16 r0, d16[0] strb r0, [sp, #4] ldm sp, {r0, r1} mov sp, r7 pop {r7} bx lr Now, however, we generate the much more straightforward: .syntax unified .section __TEXT,__text,regular,pure_instructions .globl _test1 .align 2 _test1: @ @test1 @ BB#0: add r1, r0, #48 add r2, r0, #32 vld1.64 {d20, d21}, [r0:128] vld1.64 {d16, d17}, [r1:128] add r1, r0, #16 vld1.64 {d18, d19}, [r2:128] vld1.64 {d22, d23}, [r1:128] vmovn.i32 d17, q8 vmovn.i32 d16, q9 vmovn.i32 d18, q10 vmovn.i32 d19, q11 vmovn.i16 d17, q8 vmovn.i16 d16, q9 vmov r0, r1, d16 vmov r2, r3, d17 bx lr .globl _test2 .align 2 _test2: @ @test2 @ BB#0: vld1.64 {d16, d17}, [r0:128] add r0, r0, #16 vld1.64 {d18, d19}, [r0:128] vmovn.i32 d16, q8 vmovn.i32 d17, q9 vmovn.i16 d16, q8 vmov r0, r1, d16 bx lr git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@179989 91177308-0d34-0410-b5e6-96231b3b80d8
154 lines
4.1 KiB
LLVM
154 lines
4.1 KiB
LLVM
; We currently estimate the cost of sext/zext/trunc v8(v16)i32 <-> v8(v16)i8
|
|
; instructions as expensive. If lowering is improved the cost model needs to
|
|
; change.
|
|
; RUN: opt < %s -cost-model -analyze -mtriple=thumbv7-apple-ios6.0.0 -march=arm -mcpu=cortex-a8 | FileCheck %s --check-prefix=COST
|
|
%T0_5 = type <8 x i8>
|
|
%T1_5 = type <8 x i32>
|
|
; CHECK: func_cvt5:
|
|
define void @func_cvt5(%T0_5* %loadaddr, %T1_5* %storeaddr) {
|
|
; CHECK: vmovl.s8
|
|
; CHECK: vmovl.s16
|
|
; CHECK: vmovl.s16
|
|
%v0 = load %T0_5* %loadaddr
|
|
; COST: func_cvt5
|
|
; COST: cost of 3 {{.*}} sext
|
|
%r = sext %T0_5 %v0 to %T1_5
|
|
store %T1_5 %r, %T1_5* %storeaddr
|
|
ret void
|
|
}
|
|
;; We currently estimate the cost of this instruction as expensive. If lowering
|
|
;; is improved the cost needs to change.
|
|
%TA0_5 = type <8 x i8>
|
|
%TA1_5 = type <8 x i32>
|
|
; CHECK: func_cvt1:
|
|
define void @func_cvt1(%TA0_5* %loadaddr, %TA1_5* %storeaddr) {
|
|
; CHECK: vmovl.u8
|
|
; CHECK: vmovl.u16
|
|
; CHECK: vmovl.u16
|
|
%v0 = load %TA0_5* %loadaddr
|
|
; COST: func_cvt1
|
|
; COST: cost of 3 {{.*}} zext
|
|
%r = zext %TA0_5 %v0 to %TA1_5
|
|
store %TA1_5 %r, %TA1_5* %storeaddr
|
|
ret void
|
|
}
|
|
|
|
%T0_51 = type <8 x i32>
|
|
%T1_51 = type <8 x i8>
|
|
; CHECK: func_cvt51:
|
|
define void @func_cvt51(%T0_51* %loadaddr, %T1_51* %storeaddr) {
|
|
; CHECK: vmovn.i32
|
|
; CHECK: vmovn.i32
|
|
; CHECK: vmovn.i16
|
|
%v0 = load %T0_51* %loadaddr
|
|
; COST: func_cvt51
|
|
; COST: cost of 3 {{.*}} trunc
|
|
%r = trunc %T0_51 %v0 to %T1_51
|
|
store %T1_51 %r, %T1_51* %storeaddr
|
|
ret void
|
|
}
|
|
|
|
%TT0_5 = type <16 x i8>
|
|
%TT1_5 = type <16 x i32>
|
|
; CHECK: func_cvt52:
|
|
define void @func_cvt52(%TT0_5* %loadaddr, %TT1_5* %storeaddr) {
|
|
; CHECK: vmovl.s16
|
|
; CHECK: vmovl.s16
|
|
; CHECK: vmovl.s16
|
|
; CHECK: vmovl.s16
|
|
%v0 = load %TT0_5* %loadaddr
|
|
; COST: func_cvt52
|
|
; COST: cost of 6 {{.*}} sext
|
|
%r = sext %TT0_5 %v0 to %TT1_5
|
|
store %TT1_5 %r, %TT1_5* %storeaddr
|
|
ret void
|
|
}
|
|
;; We currently estimate the cost of this instruction as expensive. If lowering
|
|
;; is improved the cost needs to change.
|
|
%TTA0_5 = type <16 x i8>
|
|
%TTA1_5 = type <16 x i32>
|
|
; CHECK: func_cvt12:
|
|
define void @func_cvt12(%TTA0_5* %loadaddr, %TTA1_5* %storeaddr) {
|
|
; CHECK: vmovl.u16
|
|
; CHECK: vmovl.u16
|
|
; CHECK: vmovl.u16
|
|
; CHECK: vmovl.u16
|
|
%v0 = load %TTA0_5* %loadaddr
|
|
; COST: func_cvt12
|
|
; COST: cost of 6 {{.*}} zext
|
|
%r = zext %TTA0_5 %v0 to %TTA1_5
|
|
store %TTA1_5 %r, %TTA1_5* %storeaddr
|
|
ret void
|
|
}
|
|
|
|
%TT0_51 = type <16 x i32>
|
|
%TT1_51 = type <16 x i8>
|
|
; CHECK: func_cvt512:
|
|
define void @func_cvt512(%TT0_51* %loadaddr, %TT1_51* %storeaddr) {
|
|
; CHECK: vmovn.i32
|
|
; CHECK: vmovn.i32
|
|
; CHECK: vmovn.i32
|
|
; CHECK: vmovn.i32
|
|
; CHECK: vmovn.i16
|
|
; CHECK: vmovn.i16
|
|
%v0 = load %TT0_51* %loadaddr
|
|
; COST: func_cvt512
|
|
; COST: cost of 6 {{.*}} trunc
|
|
%r = trunc %TT0_51 %v0 to %TT1_51
|
|
store %TT1_51 %r, %TT1_51* %storeaddr
|
|
ret void
|
|
}
|
|
|
|
; CHECK: sext_v4i16_v4i64:
|
|
define void @sext_v4i16_v4i64(<4 x i16>* %loadaddr, <4 x i64>* %storeaddr) {
|
|
; CHECK: vmovl.s32
|
|
; CHECK: vmovl.s32
|
|
%v0 = load <4 x i16>* %loadaddr
|
|
; COST: sext_v4i16_v4i64
|
|
; COST: cost of 3 {{.*}} sext
|
|
%r = sext <4 x i16> %v0 to <4 x i64>
|
|
store <4 x i64> %r, <4 x i64>* %storeaddr
|
|
ret void
|
|
}
|
|
|
|
; CHECK: zext_v4i16_v4i64:
|
|
define void @zext_v4i16_v4i64(<4 x i16>* %loadaddr, <4 x i64>* %storeaddr) {
|
|
; CHECK: vmovl.u32
|
|
; CHECK: vmovl.u32
|
|
%v0 = load <4 x i16>* %loadaddr
|
|
; COST: zext_v4i16_v4i64
|
|
; COST: cost of 3 {{.*}} zext
|
|
%r = zext <4 x i16> %v0 to <4 x i64>
|
|
store <4 x i64> %r, <4 x i64>* %storeaddr
|
|
ret void
|
|
}
|
|
|
|
; CHECK: sext_v8i16_v8i64:
|
|
define void @sext_v8i16_v8i64(<8 x i16>* %loadaddr, <8 x i64>* %storeaddr) {
|
|
; CHECK: vmovl.s32
|
|
; CHECK: vmovl.s32
|
|
; CHECK: vmovl.s32
|
|
; CHECK: vmovl.s32
|
|
%v0 = load <8 x i16>* %loadaddr
|
|
; COST: sext_v8i16_v8i64
|
|
; COST: cost of 6 {{.*}} sext
|
|
%r = sext <8 x i16> %v0 to <8 x i64>
|
|
store <8 x i64> %r, <8 x i64>* %storeaddr
|
|
ret void
|
|
}
|
|
|
|
; CHECK: zext_v8i16_v8i64:
|
|
define void @zext_v8i16_v8i64(<8 x i16>* %loadaddr, <8 x i64>* %storeaddr) {
|
|
; CHECK: vmovl.u32
|
|
; CHECK: vmovl.u32
|
|
; CHECK: vmovl.u32
|
|
; CHECK: vmovl.u32
|
|
%v0 = load <8 x i16>* %loadaddr
|
|
; COST: zext_v8i16_v8i64
|
|
; COST: cost of 6 {{.*}} zext
|
|
%r = zext <8 x i16> %v0 to <8 x i64>
|
|
store <8 x i64> %r, <8 x i64>* %storeaddr
|
|
ret void
|
|
}
|
|
|