// Copyright 2022 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. #include .syntax unified // void xnn_qc8_dwconv_minmax_fp32_ukernel_up8x3__aarch32_neonv8_mla8_cortex_a35( // size_t channels, r0, r11 // size_t output_width, r1 // const int8_t** input, r2 // const void* weights, r3 // int8_t* output, r10, [sp, 40] // size_t input_stride, r9, [sp, 44] // size_t output_increment, r12, [sp, 48] // size_t input_offset, r7, [sp, 52] // const int8_t* zero, r4, [sp, 56] // const union xnn_qs8_minmax_params params r5, [sp, 60] // d8-d15, r4-r11,r14(lr) need to be preserved if used. r13(sp),r15(pc) are reserved. // Register usage // A0 r5 d4 // A1 r6 d5 // A2 r8 d6 // B r3/lr d7 d16 d17 // C0 r10 q12 q13 q14 q15 // Prod q0 q1 // params structure is 4 bytes // struct { // int16_t output_zero_point; d20[0] q10 // int8_t output_min; d20[2] d18 q9 // int8_t output_max; d20[3] d19 // } xnn_qs8_minmax_params.neonv8; // unused q4 q5 q6 q7 q11 BEGIN_FUNCTION xnn_qc8_dwconv_minmax_fp32_ukernel_up8x3__aarch32_neonv8_mla8_cortex_a35 // 40 bytes of stack. 36 + 4 pad PUSH {r4, r5, r6, r7, r8, r9, r10, r11, lr} // 40 SUB sp, sp, 4 LDR r5, [sp, 60] // params LDR r10, [sp, 40] // output LDR r9, [sp, 44] // input_stride LDR r12, [sp, 48] // output_increment LDR r7, [sp, 52] // input_offset LDR r4, [sp, 56] // zero VLD1.32 {d20[]}, [r5] // QC8 params VDUP.8 d18, d20[2] // output_min VDUP.8 d19, d20[3] // output_max VDUP.16 q10, d20[0] // output_zero_point .p2align 3 0: LDMIB r2, {r5, r6} // i0, i1 LDR r8, [r2] // i2 CMP r5, r4 // i0 == zero? ADDNE r5, r5, r7 // i0 += input_offset CMP r6, r4 // i1 == zero? ADDNE r6, r6, r7 // i1 += input_offset CMP r8, r4 // i2 == zero? ADDNE r8, r8, r7 // i2 += input_offset MOV lr, r3 MOV r11, r0 // channel count as is, fall into loop // Main loop - 8 channels // lr weights. r3 reset // r0/r11 loop counter. // r5 i0 // r6 i1 // r8 i2 // q12 q13 q14 q15 accumulators // Weights are: // 32 bias - 8 int // 24 weights - 3 * 8 byte // 32 quant scale - 8 int // 88 bytes total .p2align 3 1: VLD1.8 {q12, q13}, [lr]! // load bias VLD1.8 {d4}, [r8]! // i2 VLD1.8 {d7}, [lr]! // w0 VLD1.8 {d5}, [r5]! // i0 VLD1.8 {d16}, [lr]! // w1 VLD1.8 {d6}, [r6]! // i1 VLD1.8 {d17}, [lr]! // w2 VMULL.S8 q1, d4, d7 // i2 * w0 VMLAL.S8 q1, d5, d16 // i0 * w1 VMULL.S8 q0, d6, d17 // i1 * w2 VADDW.S16 q12, q12, d0 VADDW.S16 q13, q13, d1 VADDW.S16 q12, q12, d2 VADDW.S16 q13, q13, d3 VLD1.32 {q0, q1}, [lr]! // quant per channel scale values // QC8 FP32 quantization VCVT.F32.S32 q12, q12 VCVT.F32.S32 q13, q13 VMUL.F32 q12, q0, q12 VMUL.F32 q13, q1, q13 VCVTN.S32.F32 q12, q12 VCVTN.S32.F32 q13, q13 VQMOVN.S32 d24, q12 VQMOVN.S32 d25, q13 SUBS r11, r11, 8 // 8 channels per loop VQADD.S16 q12, q12, q10 VQMOVN.S16 d24, q12 VMIN.S8 d24, d24, d19 VMAX.S8 d24, d24, d18 BLO 3f // less than 8? VST1.8 {d24}, [r10]! BHI 1b // at least 1, continue loop 2: SUBS r1, r1, 1 // output_width ADD r10, r10, r12 // output += output_increment ADD r2, r2, r9 // input += input_stride BNE 0b ADD sp, sp, 4 POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} .p2align 3 // Store 4 3: TST r11, 4 BEQ 4f VST1.32 {d24[0]}, [r10]! VEXT.8 d24, d24, d24, 4 // Store 2 4: TST r11, 2 BEQ 5f VST1.16 {d24[0]}, [r10]! VEXT.8 d24, d24, d24, 2 // Store 1 5: TST r11, 1 BEQ 2b VST1.8 {d24[0]}, [r10]! B 2b END_FUNCTION xnn_qc8_dwconv_minmax_fp32_ukernel_up8x3__aarch32_neonv8_mla8_cortex_a35 #ifdef __ELF__ .section ".note.GNU-stack","",%progbits #endif