| // Copyright 2021 Google LLC |
| // |
| // This source code is licensed under the BSD-style license found in the |
| // LICENSE file in the root directory of this source tree. |
| |
| $assert REQUANTIZATION in ["FP32", "RNDNU"] |
| $assert not CHANNELWISE or REQUANTIZATION == "FP32" |
| $assert DATATYPE in ["QC8", "QS8", "QU8"] |
| $assert DATATYPE != "QC8" or REQUANTIZATION == "FP32" |
| |
| #include <xnnpack/assembly.h> |
| |
| .syntax unified |
| |
| $PARAMS_UNION = "xnn_qs8_minmax_params" if CHANNELWISE else "xnn_qs8_conv_minmax_params" |
| $ISA = "neonv8" if ARMV8 else "neon" |
| $XMIN = "VMIN.U8" if DATATYPE == "QU8" else "VMIN.S8" |
| $XMAX = "VMAX.U8" if DATATYPE == "QU8" else "VMAX.S8" |
| $XXTL = "VMOVL.U8" if DATATYPE == "QU8" else "VMOVL.S8" |
| $SQXTXN = "VQMOVUN.S16" if DATATYPE == "QU8" else "VQMOVN.S16" |
| $XINT8_T = "uint8_t" if DATATYPE == "QU8" else "int8_t" |
| # LINT.IfChange |
| // void xnn_${DATATYPE.lower()}_igemm_minmax_${REQUANTIZATION.lower()}_ukernel_4x8__aarch32_${ISA}_mlal_lane${"_prfm" if PREFETCH else ""}_ld64 |
| // size_t mr, (r0) |
| // size_t nc, r1 |
| // size_t kc, (r2) -> r5 -> sp + 44 |
| // size_t ks, (r3) -> sp + 48 -> r14 |
| // const ${XINT8_T}**restrict a, sp + 88 -> r2 |
| // const void*restrict w, sp + 92 -> r9 |
| // ${XINT8_T}*restrict c, sp + 96 -> r11 |
| // size_t cm_stride, sp + 100 -> (r6) |
| // size_t cn_stride, sp + 104 -> (r7) |
| // size_t a_offset, sp + 108 -> (r5) |
| // const ${XINT8_T}* zero, sp + 112 -> (r7) |
| // ${PARAMS_UNION}*params); sp + 116 -> (r5) |
| |
| // d8-d15, r4-r11,r14(lr) need to be preserved if used. r13(sp),r15(pc) are reserved. |
| |
| // Register usage |
| // A0 r3 d0-d1 q0 |
| // A1 r12 d2-d3 q1 |
| // A2 r10 d4-d5 q2 |
| // A3 r0 d6-d7 q3 |
| |
| // B r9 d10-d11 q5 |
| |
| // C0 r11 d16-d17 q8 d18-d19 q9 |
| // C1 r4 d20-d21 q10 d22-d23 q11 |
| // C2 r8 d24-d25 q12 d26-d27 q13 |
| // C3 r6 d28-d29 q14 d30-d31 q15 |
| |
| // Unused d13-d15 |
| |
| $if REQUANTIZATION == "RNDNU" and DATATYPE != "QU8": |
| // params structure is 16 bytes |
| // struct { |
| // int32_t right_pre_shift; d12[0] |
| // int32_t multiplier; d12[1] |
| // int32_t right_post_shift; d13[0] |
| // int16_t output_zero_point; d13[2] |
| // int8_t output_min; d13[6] |
| // int8_t output_max; d13[7] |
| // } rndnu_neon; |
| $elif REQUANTIZATION == "RNDNU" and DATATYPE == "QU8": |
| // params structure is 20 bytes |
| // struct { |
| // ${XINT8_T} kernel_zero_point[4]; d14 |
| // int32_t right_pre_shift; d12[0] |
| // int32_t multiplier; d12[1] |
| // int32_t right_post_shift; d13[0] |
| // int16_t output_zero_point; d13[2] |
| // ${XINT8_T} output_min; d13[6] |
| // ${XINT8_T} output_max; d13[7] |
| // } rndnu_neon; |
| $elif DATATYPE == "QC8" and not ARMV8: |
| // params structure is 10 bytes |
| // struct { |
| // float magic_bias; d12[0] |
| // int32_t magic_bias_less_output_zero_point; d12[1] |
| // int8_t output_min; d13[6] |
| // int8_t output_max; d13[7] |
| // } xnn_qs8_minmax_params.neon; |
| $else: |
| // params structure is 4 bytes |
| // struct { |
| // int16_t output_zero_point; d13[2] |
| // int8_t output_min; d13[6] |
| // int8_t output_max; d13[7] |
| // } xnn_qs8_minmax_params.neonv8; |
| |
| BEGIN_FUNCTION xnn_${DATATYPE.lower()}_igemm_minmax_${REQUANTIZATION.lower()}_ukernel_4x8__aarch32_${ISA}_mlal_lane${"_prfm" if PREFETCH else ""}_ld64 |
| # Push 88 bytes |
| # r2 will be reloaded in outer loop. r3 is ks |
| PUSH {r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, lr} // +44 |
| $if DATATYPE == "QU8": |
| SUB sp, sp, 4 // +4 |
| VPUSH {d10-d14} // +40 = 88 |
| $else: |
| SUB sp, sp, 12 // +12 |
| VPUSH {d10-d13} // +32 = 88 |
| |
| LDR r11, [sp, 96] // c |
| LDR r6, [sp, 100] // cm_stride |
| LDR r2, [sp, 88] // a |
| LDR r9, [sp, 92] // w |
| LDR r5, [sp, 116] // params |
| MOV r14, r3 // p = ks |
| |
| # Clamp C pointers |
| CMP r0, 2 // if mr >= 2 |
| ADD r4, r11, r6 // c1 = c0 + cm_stride |
| MOVLO r4, r11 // c1 |
| // if mr > 2 |
| ADD r8, r4, r6 // c2 = c1 + cm_stride |
| MOVLS r8, r4 // c2 |
| CMP r0, 4 // if mr >=4 |
| ADD r6, r8, r6 // c3 = c2 + cm_stride |
| MOVLO r6, r8 // c3 |
| |
| # Load params values |
| $if DATATYPE == "QU8": |
| VLD1.32 {d14[]}, [r5]! // QU8 kernel_zero_point |
| $if REQUANTIZATION == "RNDNU": |
| VLDM r5, {d12-d13} // RNDNU params |
| $elif DATATYPE == "QC8" and ARMV8: |
| VLD1.32 {d13[]}, [r5] // QC8 neonv8 params |
| $elif DATATYPE == "QC8" and not ARMV8: |
| VLDM r5!, {d12} // QC8 neon params |
| VLD1.16 {d13[]}, [r5] |
| |
| $if PREFETCH: |
| PLD [r9, 64] // Prefetch B |
| PLD [r9, 128] |
| PLD [r9, 192] |
| PLD [r9, 256] |
| PLD [r9, 320] |
| PLD [r9, 384] |
| |
| .p2align 3 |
| 0: |
| # Load initial bias from w into accumulators |
| VLDM r9!, {d16-d19} // Bias |
| VMOV q10, q8 |
| VMOV q11, q9 |
| VMOV q12, q8 |
| VMOV q13, q9 |
| VMOV q14, q8 |
| VMOV q15, q9 |
| |
| .p2align 3 |
| 1: |
| # Load next 4 A pointers |
| LDR r3, [r2, 0] |
| LDR r12, [r2, 4] |
| LDR r10, [r2, 8] |
| LDR r0, [r2, 12] |
| ADD r2, r2, 16 |
| |
| $if PREFETCH: |
| PLD [r3, 64] |
| PLD [r12, 64] |
| PLD [r10, 64] |
| PLD [r0, 64] |
| |
| # Add a_offset |
| LDR r5, [sp, 108] // a_offset |
| LDR r7, [sp, 112] // zero |
| CMP r3, r7 // if a0 == zero |
| ADD r3, r3, r5 // a0 += a_offset |
| MOVEQ r3, r7 // a0 = zero, else += a0 + a_offset |
| CMP r12, r7 // if a1 == zero |
| ADD r12, r12, r5 // a1 += a_offset |
| MOVEQ r12, r7 // a1 = zero, else += a1 + a_offset |
| CMP r10, r7 // if a2 == zero |
| ADD r10, r10, r5 // a2 += a_offset |
| MOVEQ r10, r7 // a2 = zero, else += a2 + a_offset |
| CMP r0, r7 // if a3 == zero |
| ADD r0, r0, r5 // a3 += a_offset |
| LDR r5, [sp, 44] // kc |
| MOVEQ r0, r7 // a3 = zero, else += a3 + a_offset |
| |
| SUBS r5, r5, 8 // kc - 8 |
| BLO 4f // less than 8 channels? |
| |
| # Main loop - 8 bytes |
| # 64 bytes for weights. |
| .p2align 3 |
| 2: |
| VLD1.8 {d0}, [r3]! // A0 |
| VLD1.8 {d10}, [r9]! // B |
| VLD1.8 {d2}, [r12]! // A1 |
| VLD1.8 {d4}, [r10]! // A2 |
| VLD1.8 {d6}, [r0]! // A3 |
| SUBS r5, r5, 8 |
| $if PREFETCH: |
| PLD [r3, 128] |
| ${XXTL} q0, d0 |
| $if PREFETCH: |
| PLD [r12, 128] |
| $if DATATYPE == "QU8": |
| VSUBL.U8 q5, d10, d14 |
| $else: |
| VMOVL.S8 q5, d10 |
| $if PREFETCH: |
| PLD [r10, 128] |
| ${XXTL} q1, d2 |
| $if PREFETCH: |
| PLD [r0, 128] |
| ${XXTL} q2, d4 |
| $if PREFETCH: |
| PLD [r9, 448] |
| ${XXTL} q3, d6 |
| VMLAL.S16 q8, d10, d0[0] |
| VMLAL.S16 q9, d11, d0[0] |
| VMLAL.S16 q10, d10, d2[0] |
| VMLAL.S16 q11, d11, d2[0] |
| VMLAL.S16 q12, d10, d4[0] |
| VMLAL.S16 q13, d11, d4[0] |
| VMLAL.S16 q14, d10, d6[0] |
| VMLAL.S16 q15, d11, d6[0] |
| |
| VLD1.8 {d10}, [r9]! |
| $if DATATYPE == "QU8": |
| VSUBL.U8 q5, d10, d14 |
| $else: |
| VMOVL.S8 q5, d10 |
| VMLAL.S16 q8, d10, d0[1] |
| VMLAL.S16 q9, d11, d0[1] |
| VMLAL.S16 q10, d10, d2[1] |
| VMLAL.S16 q11, d11, d2[1] |
| VMLAL.S16 q12, d10, d4[1] |
| VMLAL.S16 q13, d11, d4[1] |
| VMLAL.S16 q14, d10, d6[1] |
| VMLAL.S16 q15, d11, d6[1] |
| |
| VLD1.8 {d10}, [r9]! |
| $if DATATYPE == "QU8": |
| VSUBL.U8 q5, d10, d14 |
| $else: |
| VMOVL.S8 q5, d10 |
| VMLAL.S16 q8, d10, d0[2] |
| VMLAL.S16 q9, d11, d0[2] |
| VMLAL.S16 q10, d10, d2[2] |
| VMLAL.S16 q11, d11, d2[2] |
| VMLAL.S16 q12, d10, d4[2] |
| VMLAL.S16 q13, d11, d4[2] |
| VMLAL.S16 q14, d10, d6[2] |
| VMLAL.S16 q15, d11, d6[2] |
| |
| VLD1.8 {d10}, [r9]! |
| $if DATATYPE == "QU8": |
| VSUBL.U8 q5, d10, d14 |
| $else: |
| VMOVL.S8 q5, d10 |
| VMLAL.S16 q8, d10, d0[3] |
| VMLAL.S16 q9, d11, d0[3] |
| VMLAL.S16 q10, d10, d2[3] |
| VMLAL.S16 q11, d11, d2[3] |
| VMLAL.S16 q12, d10, d4[3] |
| VMLAL.S16 q13, d11, d4[3] |
| VMLAL.S16 q14, d10, d6[3] |
| VMLAL.S16 q15, d11, d6[3] |
| |
| VLD1.8 {d10}, [r9]! |
| $if DATATYPE == "QU8": |
| VSUBL.U8 q5, d10, d14 |
| $else: |
| VMOVL.S8 q5, d10 |
| VMLAL.S16 q8, d10, d1[0] |
| VMLAL.S16 q9, d11, d1[0] |
| VMLAL.S16 q10, d10, d3[0] |
| VMLAL.S16 q11, d11, d3[0] |
| VMLAL.S16 q12, d10, d5[0] |
| VMLAL.S16 q13, d11, d5[0] |
| VMLAL.S16 q14, d10, d7[0] |
| VMLAL.S16 q15, d11, d7[0] |
| |
| VLD1.8 {d10}, [r9]! |
| $if DATATYPE == "QU8": |
| VSUBL.U8 q5, d10, d14 |
| $else: |
| VMOVL.S8 q5, d10 |
| VMLAL.S16 q8, d10, d1[1] |
| VMLAL.S16 q9, d11, d1[1] |
| VMLAL.S16 q10, d10, d3[1] |
| VMLAL.S16 q11, d11, d3[1] |
| VMLAL.S16 q12, d10, d5[1] |
| VMLAL.S16 q13, d11, d5[1] |
| VMLAL.S16 q14, d10, d7[1] |
| VMLAL.S16 q15, d11, d7[1] |
| |
| VLD1.8 {d10}, [r9]! |
| $if DATATYPE == "QU8": |
| VSUBL.U8 q5, d10, d14 |
| $else: |
| VMOVL.S8 q5, d10 |
| VMLAL.S16 q8, d10, d1[2] |
| VMLAL.S16 q9, d11, d1[2] |
| VMLAL.S16 q10, d10, d3[2] |
| VMLAL.S16 q11, d11, d3[2] |
| VMLAL.S16 q12, d10, d5[2] |
| VMLAL.S16 q13, d11, d5[2] |
| VMLAL.S16 q14, d10, d7[2] |
| VMLAL.S16 q15, d11, d7[2] |
| |
| VLD1.8 {d10}, [r9]! |
| $if DATATYPE == "QU8": |
| VSUBL.U8 q5, d10, d14 |
| $else: |
| VMOVL.S8 q5, d10 |
| VMLAL.S16 q8, d10, d1[3] |
| VMLAL.S16 q9, d11, d1[3] |
| VMLAL.S16 q10, d10, d3[3] |
| VMLAL.S16 q11, d11, d3[3] |
| VMLAL.S16 q12, d10, d5[3] |
| VMLAL.S16 q13, d11, d5[3] |
| VMLAL.S16 q14, d10, d7[3] |
| VMLAL.S16 q15, d11, d7[3] |
| BHS 2b |
| |
| # Is there a remainder?- 1-7 bytes of A |
| ADDS r5, r5, 8 |
| BNE 4f |
| |
| 3: |
| # ks loop |
| SUBS r14, r14, 16 // ks -= MR * sizeof(void*) |
| BHI 1b |
| |
| LDR r7, [sp, 104] // cn_stride |
| LDR r14, [sp, 48] // p = ks |
| |
| $if REQUANTIZATION == "RNDNU": |
| # RNDNU quantization |
| VDUP.32 q0, d12[0] // right_pre_shift |
| |
| VQSHL.S32 q8, q8, q0 |
| VQSHL.S32 q9, q9, q0 |
| VQSHL.S32 q10, q10, q0 |
| VQSHL.S32 q11, q11, q0 |
| VQSHL.S32 q12, q12, q0 |
| VQSHL.S32 q13, q13, q0 |
| VQSHL.S32 q14, q14, q0 |
| VQSHL.S32 q15, q15, q0 |
| |
| VDUP.32 q2, d13[0] // right_post_shift |
| |
| VQDMULH.S32 q8, q8, d12[1] // multiplier |
| VQDMULH.S32 q9, q9, d12[1] |
| VQDMULH.S32 q10, q10, d12[1] |
| VQDMULH.S32 q11, q11, d12[1] |
| VQDMULH.S32 q12, q12, d12[1] |
| VQDMULH.S32 q13, q13, d12[1] |
| VQDMULH.S32 q14, q14, d12[1] |
| VQDMULH.S32 q15, q15, d12[1] |
| |
| VRSHL.S32 q8, q8, q2 |
| VRSHL.S32 q9, q9, q2 |
| VRSHL.S32 q10, q10, q2 |
| VRSHL.S32 q11, q11, q2 |
| VRSHL.S32 q12, q12, q2 |
| VRSHL.S32 q13, q13, q2 |
| VRSHL.S32 q14, q14, q2 |
| VRSHL.S32 q15, q15, q2 |
| $elif DATATYPE == "QC8" and ARMV8: |
| # QC8 FP32 quantization |
| VLD1.8 {q0-q1}, [r9]! |
| |
| VCVT.F32.S32 q8, q8 |
| VCVT.F32.S32 q9, q9 |
| VCVT.F32.S32 q10, q10 |
| VCVT.F32.S32 q11, q11 |
| VCVT.F32.S32 q12, q12 |
| VCVT.F32.S32 q13, q13 |
| VCVT.F32.S32 q14, q14 |
| VCVT.F32.S32 q15, q15 |
| |
| VMUL.F32 q8, q8, q0 // multiplier |
| VMUL.F32 q9, q9, q1 |
| VMUL.F32 q10, q10, q0 |
| VMUL.F32 q11, q11, q1 |
| VMUL.F32 q12, q12, q0 |
| VMUL.F32 q13, q13, q1 |
| VMUL.F32 q14, q14, q0 |
| VMUL.F32 q15, q15, q1 |
| |
| VCVTN.S32.F32 q8, q8 |
| VCVTN.S32.F32 q9, q9 |
| VCVTN.S32.F32 q10, q10 |
| VCVTN.S32.F32 q11, q11 |
| VCVTN.S32.F32 q12, q12 |
| VCVTN.S32.F32 q13, q13 |
| VCVTN.S32.F32 q14, q14 |
| VCVTN.S32.F32 q15, q15 |
| $elif DATATYPE == "QC8" and not ARMV8: |
| # QC8 FP32 quantization |
| VLD1.8 {q0-q1}, [r9]! |
| |
| VDUP.32 q2, d12[0] // magic_bias |
| VDUP.32 q3, d12[1] // magic_bias_less_output_zero_point |
| |
| VCVT.F32.S32 q8, q8 |
| VCVT.F32.S32 q9, q9 |
| VCVT.F32.S32 q10, q10 |
| VCVT.F32.S32 q11, q11 |
| VCVT.F32.S32 q12, q12 |
| VCVT.F32.S32 q13, q13 |
| VCVT.F32.S32 q14, q14 |
| VCVT.F32.S32 q15, q15 |
| |
| VMUL.F32 q8, q8, q0 // multiplier |
| VMUL.F32 q9, q9, q1 |
| VMUL.F32 q10, q10, q0 |
| VMUL.F32 q11, q11, q1 |
| VMUL.F32 q12, q12, q0 |
| VMUL.F32 q13, q13, q1 |
| VMUL.F32 q14, q14, q0 |
| VMUL.F32 q15, q15, q1 |
| |
| VADD.F32 q8, q8, q2 // magic_bias |
| VADD.F32 q9, q9, q2 |
| VADD.F32 q10, q10, q2 |
| VADD.F32 q11, q11, q2 |
| VADD.F32 q12, q12, q2 |
| VADD.F32 q13, q13, q2 |
| VADD.F32 q14, q14, q2 |
| VADD.F32 q15, q15, q2 |
| |
| VQSUB.S32 q8, q8, q3 // magic_bias_less_output_zero_point |
| VQSUB.S32 q9, q9, q3 |
| VQSUB.S32 q10, q10, q3 |
| VQSUB.S32 q11, q11, q3 |
| VQSUB.S32 q12, q12, q3 |
| VQSUB.S32 q13, q13, q3 |
| VQSUB.S32 q14, q14, q3 |
| VQSUB.S32 q15, q15, q3 |
| |
| $if DATATYPE != "QC8" or ARMV8: |
| VDUP.16 q0, d13[2] // output_zero_point |
| |
| VQMOVN.S32 d16, q8 |
| VQMOVN.S32 d17, q9 |
| VQMOVN.S32 d18, q10 |
| VQMOVN.S32 d19, q11 |
| VQMOVN.S32 d20, q12 |
| VQMOVN.S32 d21, q13 |
| VQMOVN.S32 d22, q14 |
| VQMOVN.S32 d23, q15 |
| |
| $if DATATYPE != "QC8" or ARMV8: |
| VQADD.S16 q8, q8, q0 |
| VQADD.S16 q9, q9, q0 |
| VQADD.S16 q10, q10, q0 |
| VQADD.S16 q11, q11, q0 |
| |
| VDUP.8 q12, d13[6] // output_min |
| |
| ${SQXTXN} d0, q8 |
| ${SQXTXN} d1, q9 |
| ${SQXTXN} d2, q10 |
| ${SQXTXN} d3, q11 |
| |
| VDUP.8 q13, d13[7] // output_max |
| |
| ${XMAX} q0, q0, q12 |
| ${XMAX} q1, q1, q12 |
| |
| SUBS r1, r1, 8 // nc -= 8 |
| |
| ${XMIN} q0, q0, q13 |
| ${XMIN} q1, q1, q13 |
| |
| # Store full 4 x 8 |
| BLO 5f |
| VST1.8 {d3}, [r6], r7 |
| VST1.8 {d2}, [r8], r7 |
| VST1.8 {d1}, [r4], r7 |
| VST1.8 {d0}, [r11], r7 |
| SUB r2, r2, r14 // a -= ks |
| BHI 0b |
| |
| $if DATATYPE == "QU8": |
| VPOP {d10-d14} |
| ADD sp, sp, 12 // skip pad of 4, r2, r3 |
| $else: |
| VPOP {d10-d13} |
| ADD sp, sp, 20 // skip pad of 12, r2, r3 |
| POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} |
| |
| # Remainder- 1 to 7 bytes of A |
| .p2align 3 |
| 4: |
| AND r5, r5, 7 // kc remainder 1 to 7 |
| |
| VLD1.8 {d0}, [r3] |
| VLD1.8 {d10}, [r9]! |
| VLD1.8 {d2}, [r12] |
| VLD1.8 {d4}, [r10] |
| VLD1.8 {d6}, [r0] |
| |
| ${XXTL} q0, d0 |
| $if DATATYPE == "QU8": |
| VSUBL.U8 q5, d10, d14 |
| $else: |
| VMOVL.S8 q5, d10 |
| ${XXTL} q1, d2 |
| ${XXTL} q2, d4 |
| ${XXTL} q3, d6 |
| VMLAL.S16 q8, d10, d0[0] |
| VMLAL.S16 q9, d11, d0[0] |
| VMLAL.S16 q10, d10, d2[0] |
| VMLAL.S16 q11, d11, d2[0] |
| VMLAL.S16 q12, d10, d4[0] |
| VMLAL.S16 q13, d11, d4[0] |
| VMLAL.S16 q14, d10, d6[0] |
| VMLAL.S16 q15, d11, d6[0] |
| CMP r5, 2 |
| BLO 3b |
| |
| VLD1.8 {d10}, [r9]! |
| $if DATATYPE == "QU8": |
| VSUBL.U8 q5, d10, d14 |
| $else: |
| VMOVL.S8 q5, d10 |
| VMLAL.S16 q8, d10, d0[1] |
| VMLAL.S16 q9, d11, d0[1] |
| VMLAL.S16 q10, d10, d2[1] |
| VMLAL.S16 q11, d11, d2[1] |
| VMLAL.S16 q12, d10, d4[1] |
| VMLAL.S16 q13, d11, d4[1] |
| VMLAL.S16 q14, d10, d6[1] |
| VMLAL.S16 q15, d11, d6[1] |
| BEQ 3b |
| |
| VLD1.8 {d10}, [r9]! |
| $if DATATYPE == "QU8": |
| VSUBL.U8 q5, d10, d14 |
| $else: |
| VMOVL.S8 q5, d10 |
| VMLAL.S16 q8, d10, d0[2] |
| VMLAL.S16 q9, d11, d0[2] |
| VMLAL.S16 q10, d10, d2[2] |
| VMLAL.S16 q11, d11, d2[2] |
| VMLAL.S16 q12, d10, d4[2] |
| VMLAL.S16 q13, d11, d4[2] |
| VMLAL.S16 q14, d10, d6[2] |
| VMLAL.S16 q15, d11, d6[2] |
| CMP r5, 4 |
| BLO 3b |
| |
| VLD1.8 {d10}, [r9]! |
| $if DATATYPE == "QU8": |
| VSUBL.U8 q5, d10, d14 |
| $else: |
| VMOVL.S8 q5, d10 |
| VMLAL.S16 q8, d10, d0[3] |
| VMLAL.S16 q9, d11, d0[3] |
| VMLAL.S16 q10, d10, d2[3] |
| VMLAL.S16 q11, d11, d2[3] |
| VMLAL.S16 q12, d10, d4[3] |
| VMLAL.S16 q13, d11, d4[3] |
| VMLAL.S16 q14, d10, d6[3] |
| VMLAL.S16 q15, d11, d6[3] |
| BEQ 3b |
| |
| VLD1.8 {d10}, [r9]! |
| $if DATATYPE == "QU8": |
| VSUBL.U8 q5, d10, d14 |
| $else: |
| VMOVL.S8 q5, d10 |
| VMLAL.S16 q8, d10, d1[0] |
| VMLAL.S16 q9, d11, d1[0] |
| VMLAL.S16 q10, d10, d3[0] |
| VMLAL.S16 q11, d11, d3[0] |
| VMLAL.S16 q12, d10, d5[0] |
| VMLAL.S16 q13, d11, d5[0] |
| VMLAL.S16 q14, d10, d7[0] |
| VMLAL.S16 q15, d11, d7[0] |
| CMP r5, 6 |
| BLO 3b |
| |
| VLD1.8 {d10}, [r9]! |
| $if DATATYPE == "QU8": |
| VSUBL.U8 q5, d10, d14 |
| $else: |
| VMOVL.S8 q5, d10 |
| VMLAL.S16 q8, d10, d1[1] |
| VMLAL.S16 q9, d11, d1[1] |
| VMLAL.S16 q10, d10, d3[1] |
| VMLAL.S16 q11, d11, d3[1] |
| VMLAL.S16 q12, d10, d5[1] |
| VMLAL.S16 q13, d11, d5[1] |
| VMLAL.S16 q14, d10, d7[1] |
| VMLAL.S16 q15, d11, d7[1] |
| BEQ 3b |
| |
| VLD1.8 {d10}, [r9]! |
| $if DATATYPE == "QU8": |
| VSUBL.U8 q5, d10, d14 |
| $else: |
| VMOVL.S8 q5, d10 |
| VMLAL.S16 q8, d10, d1[2] |
| VMLAL.S16 q9, d11, d1[2] |
| VMLAL.S16 q10, d10, d3[2] |
| VMLAL.S16 q11, d11, d3[2] |
| VMLAL.S16 q12, d10, d5[2] |
| VMLAL.S16 q13, d11, d5[2] |
| VMLAL.S16 q14, d10, d7[2] |
| VMLAL.S16 q15, d11, d7[2] |
| B 3b |
| |
| # Store odd width |
| .p2align 3 |
| 5: |
| TST r1, 4 |
| BEQ 6f |
| VST1.32 {d3[0]}, [r6]! |
| VST1.32 {d2[0]}, [r8]! |
| VST1.32 {d1[0]}, [r4]! |
| VST1.32 {d0[0]}, [r11]! |
| VEXT.8 q1, q1, q1, 4 |
| VEXT.8 q0, q0, q0, 4 |
| 6: |
| TST r1, 2 |
| BEQ 7f |
| VST1.16 {d3[0]}, [r6]! |
| VST1.16 {d2[0]}, [r8]! |
| VST1.16 {d1[0]}, [r4]! |
| VST1.16 {d0[0]}, [r11]! |
| VEXT.8 q1, q1, q1, 2 |
| VEXT.8 q0, q0, q0, 2 |
| |
| 7: |
| TST r1, 1 |
| BEQ 8f |
| VST1.8 {d3[0]}, [r6] |
| VST1.8 {d2[0]}, [r8] |
| VST1.8 {d1[0]}, [r4] |
| VST1.8 {d0[0]}, [r11] |
| |
| 8: |
| $if DATATYPE == "QU8": |
| VPOP {d10-d14} |
| ADD sp, sp, 12 // skip pad of 4, r2, r3 |
| $else: |
| VPOP {d10-d13} |
| ADD sp, sp, 20 // skip pad of 12, r2, r3 |
| POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} |
| |
| END_FUNCTION xnn_${DATATYPE.lower()}_igemm_minmax_${REQUANTIZATION.lower()}_ukernel_4x8__aarch32_${ISA}_mlal_lane${"_prfm" if PREFETCH else ""}_ld64 |
| # LINT.ThenChange(../qc8-igemm/4x8-fp32-aarch32-neonv8-mlal-lane-ld64.cc,4x8-rndnu-aarch32-neon-mlal-lane-ld64.cc) |
| #ifdef __ELF__ |
| .section ".note.GNU-stack","",%progbits |
| #endif |