blob: 9a6522203a78b42c553adf285a0e71d37a05b80b [file] [log] [blame]
// 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 <xnnpack/assembly.h>
.syntax unified
// void xnn_qc8_dwconv_minmax_fp32_ukernel_up16x3__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, 88]
// size_t input_stride, r6, [sp, 92]
// size_t output_increment, r12, [sp, 96]
// size_t input_offset, (r11),[sp, 100]
// const int8_t* zero, r4, [sp, 104]
// const union xnn_qs8_minmax_params params r5, [sp, 108]
// d8-d15, r4-r11,r14(lr) need to be preserved if used. r13(sp),r15(pc) are reserved.
// Register usage
// A0 r5 q4
// A1 r6 q5
// A2 r8 q6
// B r7/r3/lr q12 q13 q14
// C0 r10 q12 q13 q14 q15
// Prod q0 q1 q2 q3
// params structure is 4 bytes
// struct {
// int16_t output_zero_point; d20[0] q10
// int8_t output_min; d20[2] q9
// int8_t output_max; d20[3] q11
// } xnn_qs8_minmax_params.neonv8;
// r7 temp B
// r9 B post increment 80 or 16
// unused q7
BEGIN_FUNCTION xnn_qc8_dwconv_minmax_fp32_ukernel_up16x3__aarch32_neonv8_mla8_cortex_a35
// 88 bytes of stack
PUSH {r4, r5, r6, r7, r8, r9, r10, r11, lr} // 40
SUB sp, sp, 4
VPUSH {d8, d9, d10, d11, d12, d13} // 48
LDR r5, [sp, 108] // params
LDR r10, [sp, 88] // output
LDR r12, [sp, 96] // output_increment
LDR r4, [sp, 104] // zero
VLD1.32 {d20[]}, [r5] // QC8 params
VDUP.8 q9 , d20[2] // output_min
VDUP.8 q11, d20[3] // output_max
VDUP.16 q10, d20[0] // output_zero_point
.p2align 3
0:
LDR r11, [sp, 100] // input_offset
LDMIB r2, {r5, r6} // i0, i1
LDR r8, [r2] // i2
CMP r5, r4 // i0 == zero?
ADDNE r5, r5, r11 // i0 += input_offset
CMP r6, r4 // i1 == zero?
ADDNE r6, r6, r11 // i1 += input_offset
CMP r8, r4 // i2 == zero?
ADDNE r8, r8, r11 // i2 += input_offset
MOV lr, r3
MOV r9, 80
// Is there at least 16 channels for main loop?
SUBS r11, r0, 16
BLO 2f
// Main loop - 16 channels
// lr weights. r3 reset
// r0/r11 loop counter.
// r5 i0
// r6 i1
// r8 i2
// q12 q13 q14 q15 accumulators
.p2align 3
1:
ADD r7, lr, 64 // skip over bias to get weights
VLD1.8 {q4}, [r8]! // i2
VLD1.8 {q12}, [r7]! // w0
VLD1.8 {q5}, [r5]! // i0
VLD1.8 {q13}, [r7]! // w1
VLD1.8 {q6}, [r6]! // i1
VLD1.8 {q14}, [r7] // w2
VMULL.S8 q1, d8, d24 // i2 * w0
VMULL.S8 q2, d9, d25
VMLAL.S8 q1, d10, d26 // i0 * w1
VMLAL.S8 q2, d11, d27
VMULL.S8 q0, d12, d28 // i1 * w2
VLD1.8 {q12, q13}, [lr]! // load bias
VMULL.S8 q3, d13, d29
VLD1.8 {q14, q15}, [lr], r9
VADDW.S16 q12, q12, d0
VADDW.S16 q13, q13, d1
VADDW.S16 q14, q14, d4
VADDW.S16 q15, q15, d5
VADDW.S16 q12, q12, d2
VADDW.S16 q13, q13, d3
VADDW.S16 q14, q14, d6
VLD1.32 {q0, q1}, [lr]! // quant per channel scale values
VADDW.S16 q15, q15, d7
VLD1.32 {q2, q3}, [lr]!
// QC8 FP32 quantization
VCVT.F32.S32 q12, q12
VCVT.F32.S32 q13, q13
VCVT.F32.S32 q14, q14
VCVT.F32.S32 q15, q15
VMUL.F32 q12, q0, q12
VMUL.F32 q13, q1, q13
VMUL.F32 q14, q2, q14
VMUL.F32 q15, q3, q15
VCVTN.S32.F32 q12, q12
VCVTN.S32.F32 q13, q13
VCVTN.S32.F32 q14, q14
VCVTN.S32.F32 q15, q15
VQMOVN.S32 d24, q12
VQMOVN.S32 d25, q13
VQMOVN.S32 d28, q14
VQMOVN.S32 d29, q15
VQADD.S16 q12, q12, q10
VQADD.S16 q14, q14, q10
VQMOVN.S16 d24, q12
VQMOVN.S16 d25, q14
VMIN.S8 q12, q12, q11
VMAX.S8 q12, q12, q9
SUBS r11, r11, 16
VST1.8 {q12}, [r10]!
BHS 1b
2:
// Is there a remainder channels? 1-15
ANDS r11, r11, 15
BNE 4f
3:
LDR r6, [sp, 92] // input_stride
SUBS r1, r1, 1 // output_width
ADD r10, r10, r12 // output += output_increment
ADD r2, r2, r6 // input += input_stride
BNE 0b
VPOP {d8, d9, d10, d11, d12, d13}
ADD sp, sp, 4 // pad
POP {r4, r5, r6, r7, r8, r9, r10, r11, pc}
// Small Remainder - 1-8 channels
4:
CMP r11, 9 // handle 9 or more
ADD r7, lr, 64 // skip over bias to get weights
BHS 5f
MOV r9, 16
VLD1.8 {d8}, [r8] // i2
VLD1.8 {d24}, [r7], r9 // w0
VLD1.8 {d10}, [r5] // i0
VLD1.8 {d26}, [r7], r9 // w1
VLD1.8 {d12}, [r6] // i1
VLD1.8 {d28}, [r7] // w2
VMULL.S8 q1, d8, d24 // i2 * w0
VMLAL.S8 q1, d10, d26 // i0 * w1
VMULL.S8 q0, d12, d28 // i1 * w2
VLD1.8 {q12, q13}, [lr] // load bias
ADD lr, lr, 112
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
VQADD.S16 q12, q12, q10
VQMOVN.S16 d24, q12
VMIN.S8 d24, d24, d22
VMAX.S8 d24, d24, d18
// Store 8
TST r11, 8
BEQ 6f
VST1.8 {d24}, [r10]!
B 3b
.p2align 3
// Large Remainder - 9-15 channels
// Process 16 same as main loop, but conditional store
5:
VLD1.8 {q4}, [r8]! // i2
VLD1.8 {q12}, [r7]! // w0
VLD1.8 {q5}, [r5]! // i0
VLD1.8 {q13}, [r7]! // w1
VLD1.8 {q6}, [r6]! // i1
VLD1.8 {q14}, [r7] // w2
VMULL.S8 q1, d8, d24 // i2 * w0
VMULL.S8 q2, d9, d25
VMLAL.S8 q1, d10, d26 // i0 * w1
VMLAL.S8 q2, d11, d27
VMULL.S8 q0, d12, d28 // i1 * w2
VLD1.8 {q12, q13}, [lr]! // load bias
VMULL.S8 q3, d13, d29
VLD1.8 {q14, q15}, [lr], r9
VADDW.S16 q12, q12, d0
VADDW.S16 q13, q13, d1
VADDW.S16 q14, q14, d4
VADDW.S16 q15, q15, d5
VADDW.S16 q12, q12, d2
VADDW.S16 q13, q13, d3
VADDW.S16 q14, q14, d6
VLD1.32 {q0, q1}, [lr]! // quant per channel scale values
VADDW.S16 q15, q15, d7
VLD1.32 {q2, q3}, [lr]
// QC8 FP32 quantization
VCVT.F32.S32 q12, q12
VCVT.F32.S32 q13, q13
VCVT.F32.S32 q14, q14
VCVT.F32.S32 q15, q15
VMUL.F32 q12, q0, q12
VMUL.F32 q13, q1, q13
VMUL.F32 q14, q2, q14
VMUL.F32 q15, q3, q15
VCVTN.S32.F32 q12, q12
VCVTN.S32.F32 q13, q13
VCVTN.S32.F32 q14, q14
VCVTN.S32.F32 q15, q15
VQMOVN.S32 d24, q12
VQMOVN.S32 d25, q13
VQMOVN.S32 d28, q14
VQMOVN.S32 d29, q15
VQADD.S16 q12, q12, q10
VQADD.S16 q14, q14, q10
VQMOVN.S16 d24, q12
VQMOVN.S16 d25, q14
VMIN.S8 q12, q12, q11
VMAX.S8 q12, q12, q9
// Store 8
VST1.8 {d24}, [r10]!
VMOV d24, d25
// Store 4
6:
TST r11, 4
BEQ 7f
VST1.32 {d24[0]}, [r10]!
VEXT.8 d24, d24, d24, 4
// Store 2
7:
TST r11, 2
BEQ 8f
VST1.16 {d24[0]}, [r10]!
VEXT.8 d24, d24, d24, 2
// Store 1
8:
TST r11, 1
BEQ 3b
VST1.8 {d24[0]}, [r10]!
B 3b
END_FUNCTION xnn_qc8_dwconv_minmax_fp32_ukernel_up16x3__aarch32_neonv8_mla8_cortex_a35
#ifdef __ELF__
.section ".note.GNU-stack","",%progbits
#endif