1// Copyright 2022 Google LLC 2// 3// This source code is licensed under the BSD-style license found in the 4// LICENSE file in the root directory of this source tree. 5 6#include <xnnpack/assembly.h> 7 8.syntax unified 9 10// void xnn_qc8_dwconv_minmax_fp32_ukernel_up8x3__aarch32_neonv8_mla8_cortex_a35( 11// size_t channels, r0, r11 12// size_t output_width, r1 13// const int8_t** input, r2 14// const void* weights, r3 15// int8_t* output, r10, [sp, 40] 16// size_t input_stride, r9, [sp, 44] 17// size_t output_increment, r12, [sp, 48] 18// size_t input_offset, r7, [sp, 52] 19// const int8_t* zero, r4, [sp, 56] 20// const union xnn_qs8_minmax_params params r5, [sp, 60] 21 22// d8-d15, r4-r11,r14(lr) need to be preserved if used. r13(sp),r15(pc) are reserved. 23 24// Register usage 25// A0 r5 d4 26// A1 r6 d5 27// A2 r8 d6 28 29// B r3/lr d7 d16 d17 30 31// C0 r10 q12 q13 q14 q15 32 33// Prod q0 q1 34 35// params structure is 4 bytes 36// struct { 37// int16_t output_zero_point; d20[0] q10 38// int8_t output_min; d20[2] d18 q9 39// int8_t output_max; d20[3] d19 40// } xnn_qs8_minmax_params.neonv8; 41 42// unused q4 q5 q6 q7 q11 43 44BEGIN_FUNCTION xnn_qc8_dwconv_minmax_fp32_ukernel_up8x3__aarch32_neonv8_mla8_cortex_a35 45 // 40 bytes of stack. 36 + 4 pad 46 PUSH {r4, r5, r6, r7, r8, r9, r10, r11, lr} // 40 47 SUB sp, sp, 4 48 LDR r5, [sp, 60] // params 49 LDR r10, [sp, 40] // output 50 LDR r9, [sp, 44] // input_stride 51 LDR r12, [sp, 48] // output_increment 52 LDR r7, [sp, 52] // input_offset 53 LDR r4, [sp, 56] // zero 54 55 VLD1.32 {d20[]}, [r5] // QC8 params 56 VDUP.8 d18, d20[2] // output_min 57 VDUP.8 d19, d20[3] // output_max 58 VDUP.16 q10, d20[0] // output_zero_point 59 60 .p2align 3 610: 62 LDMIB r2, {r5, r6} // i0, i1 63 LDR r8, [r2] // i2 64 CMP r5, r4 // i0 == zero? 65 ADDNE r5, r5, r7 // i0 += input_offset 66 CMP r6, r4 // i1 == zero? 67 ADDNE r6, r6, r7 // i1 += input_offset 68 CMP r8, r4 // i2 == zero? 69 ADDNE r8, r8, r7 // i2 += input_offset 70 71 MOV lr, r3 72 MOV r11, r0 // channel count as is, fall into loop 73 74// Main loop - 8 channels 75// lr weights. r3 reset 76// r0/r11 loop counter. 77// r5 i0 78// r6 i1 79// r8 i2 80// q12 q13 q14 q15 accumulators 81// Weights are: 82// 32 bias - 8 int 83// 24 weights - 3 * 8 byte 84// 32 quant scale - 8 int 85// 88 bytes total 86 87 .p2align 3 881: 89 VLD1.8 {q12, q13}, [lr]! // load bias 90 91 VLD1.8 {d4}, [r8]! // i2 92 VLD1.8 {d7}, [lr]! // w0 93 VLD1.8 {d5}, [r5]! // i0 94 VLD1.8 {d16}, [lr]! // w1 95 VLD1.8 {d6}, [r6]! // i1 96 VLD1.8 {d17}, [lr]! // w2 97 98 VMULL.S8 q1, d4, d7 // i2 * w0 99 VMLAL.S8 q1, d5, d16 // i0 * w1 100 VMULL.S8 q0, d6, d17 // i1 * w2 101 102 103 VADDW.S16 q12, q12, d0 104 VADDW.S16 q13, q13, d1 105 VADDW.S16 q12, q12, d2 106 VADDW.S16 q13, q13, d3 107 108 VLD1.32 {q0, q1}, [lr]! // quant per channel scale values 109 110 // QC8 FP32 quantization 111 112 VCVT.F32.S32 q12, q12 113 VCVT.F32.S32 q13, q13 114 115 VMUL.F32 q12, q0, q12 116 VMUL.F32 q13, q1, q13 117 118 VCVTN.S32.F32 q12, q12 119 VCVTN.S32.F32 q13, q13 120 121 VQMOVN.S32 d24, q12 122 VQMOVN.S32 d25, q13 123 SUBS r11, r11, 8 // 8 channels per loop 124 125 VQADD.S16 q12, q12, q10 126 VQMOVN.S16 d24, q12 127 VMIN.S8 d24, d24, d19 128 VMAX.S8 d24, d24, d18 129 130 BLO 3f // less than 8? 131 132 VST1.8 {d24}, [r10]! 133 BHI 1b // at least 1, continue loop 134 1352: 136 SUBS r1, r1, 1 // output_width 137 ADD r10, r10, r12 // output += output_increment 138 ADD r2, r2, r9 // input += input_stride 139 BNE 0b 140 141 ADD sp, sp, 4 142 POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} 143 144 .p2align 3 145 // Store 4 1463: 147 TST r11, 4 148 BEQ 4f 149 VST1.32 {d24[0]}, [r10]! 150 VEXT.8 d24, d24, d24, 4 151 152 // Store 2 1534: 154 TST r11, 2 155 BEQ 5f 156 VST1.16 {d24[0]}, [r10]! 157 VEXT.8 d24, d24, d24, 2 158 159 // Store 1 1605: 161 TST r11, 1 162 BEQ 2b 163 VST1.8 {d24[0]}, [r10]! 164 B 2b 165 166 167END_FUNCTION xnn_qc8_dwconv_minmax_fp32_ukernel_up8x3__aarch32_neonv8_mla8_cortex_a35 168#ifdef __ELF__ 169.section ".note.GNU-stack","",%progbits 170#endif 171