| @@ -12,335 +12,279 @@ | |||
| // const int *input_sums, const int *weight_bias, int act_min, int act_max, int out_zp, | |||
| // int *multiplier, int *left_shift, int *right_shift, int stride, int per_channel, | |||
| // int *filter_zp); | |||
| // #-52: a, #-48: b, #-44: dst, #-40: row | |||
| // #-48: a, #-44: b, #-40: dst, #-36: row | |||
| // #0: col, #4: deep16, #8: input_sums, #12: weight_bias, #16: act_min, #20: act_max, #24: out_zp | |||
| // #28: multiplier, #32: left_shift, #36: right_shift, #40: stride, #44: per_channel, #48: filter_zp | |||
| MatmulInt8Opt: | |||
| push {r0-r11, lr} | |||
| vpush {q4-q7} | |||
| add sp, sp, #116 | |||
| ldr r0, [sp, #-52] // load a ptr | |||
| vld1.8 {d0, d1, d2, d3}, [r0]! | |||
| ldr r1, [sp, #-48] // load b ptr | |||
| vld1.8 {d8, d9, d10, d11}, [r1]! | |||
| ldr r4, [sp] // col | |||
| ldr r7, [sp, #40] // output stride | |||
| mov r8, #0 // output channels offset | |||
| ldr r10, [sp, #44] | |||
| cmp r10, #0 | |||
| beq L1 | |||
| ldr r6, [sp, #8] // load intpu_sums ptr if per_channel | |||
| L1: | |||
| cmp r4, #0 // if at the end of col | |||
| ble End1 | |||
| ldr r3, [sp, #-40] // reset row counter | |||
| ldr r6, [sp, #8] // reload intpu_sums ptr if per_tensor | |||
| L2: | |||
| cmp r3, #0 // if at the end of row | |||
| ble End2 | |||
| ldr r5, [sp, #4] // reset deep16 | |||
| sub r5, r5, #16 | |||
| vmov.i32 q6, #0 | |||
| vmov.i32 q7, #0 | |||
| vmov.i32 q8, #0 | |||
| vmov.i32 q9, #0 | |||
| vmov.i32 q10, #0 | |||
| vmov.i32 q11, #0 | |||
| vmov.i32 q12, #0 | |||
| vmov.i32 q13, #0 | |||
| cmp r5, #0 | |||
| beq L3Tail | |||
| L3: | |||
| vmull.s8 q14, d0, d8 | |||
| vmull.s8 q2, d0, d10 | |||
| vmull.s8 q15, d2, d8 | |||
| vmull.s8 q3, d2, d10 | |||
| vmlal.s8 q14, d1, d9 | |||
| vmlal.s8 q2, d1, d11 | |||
| vmlal.s8 q15, d3, d9 | |||
| vmlal.s8 q3, d3, d11 | |||
| vld1.8 {d0, d1, d2, d3}, [r0]! | |||
| vpadal.s16 q6, q14 | |||
| vpadal.s16 q7, q2 | |||
| vpadal.s16 q8, q15 | |||
| vpadal.s16 q9, q3 | |||
| vmull.s8 q14, d0, d8 | |||
| vmull.s8 q2, d0, d10 | |||
| vmull.s8 q15, d2, d8 | |||
| vmull.s8 q3, d2, d10 | |||
| vmlal.s8 q14, d1, d9 | |||
| vmlal.s8 q2, d1, d11 | |||
| vmlal.s8 q15, d3, d9 | |||
| vmlal.s8 q3, d3, d11 | |||
| vld1.8 {d0, d1, d2, d3}, [r0]! | |||
| vpadal.s16 q10, q14 | |||
| vld1.8 {d8, d9, d10, d11}, [r1]! | |||
| vpadal.s16 q11, q2 | |||
| vpadal.s16 q12, q15 | |||
| vpadal.s16 q13, q3 | |||
| sub r5, r5, #16 // deep16 -= 16 | |||
| cmp r5, #0 | |||
| bgt L3 | |||
| L3Tail: | |||
| vmull.s8 q14, d0, d8 | |||
| vmull.s8 q2, d0, d10 | |||
| vmull.s8 q15, d2, d8 | |||
| vmull.s8 q3, d2, d10 | |||
| vmlal.s8 q14, d1, d9 | |||
| vmlal.s8 q2, d1, d11 | |||
| vmlal.s8 q15, d3, d9 | |||
| vmlal.s8 q3, d3, d11 | |||
| vld1.8 {d0, d1, d2, d3}, [r0]! | |||
| vpadal.s16 q6, q14 | |||
| vpadal.s16 q7, q2 | |||
| vpadal.s16 q8, q15 | |||
| vpadal.s16 q9, q3 | |||
| vmull.s8 q14, d0, d8 | |||
| vmull.s8 q2, d0, d10 | |||
| vmull.s8 q15, d2, d8 | |||
| vmull.s8 q3, d2, d10 | |||
| vmlal.s8 q14, d1, d9 | |||
| vmlal.s8 q2, d1, d11 | |||
| vmlal.s8 q15, d3, d9 | |||
| vmlal.s8 q3, d3, d11 | |||
| vpadal.s16 q10, q14 | |||
| vpadal.s16 q11, q2 | |||
| vpadal.s16 q12, q15 | |||
| vpadal.s16 q13, q3 | |||
| vpadd.i32 d0, d12, d13 | |||
| vpadd.i32 d1, d14, d15 | |||
| vpadd.i32 d2, d16, d17 | |||
| vpadd.i32 d3, d18, d19 | |||
| vpadd.i32 d4, d20, d21 | |||
| vpadd.i32 d5, d22, d23 | |||
| vpadd.i32 d6, d24, d25 | |||
| vpadd.i32 d7, d26, d27 | |||
| vpadd.i32 d28, d0, d1 | |||
| vpadd.i32 d29, d2, d3 | |||
| vpadd.i32 d30, d4, d5 | |||
| vpadd.i32 d31, d6, d7 | |||
| cmp r3, #4 | |||
| ble LAST_ROW | |||
| vld1.8 {d0, d1, d2, d3}, [r0]! | |||
| ldr r1, [sp, #-48] // reload b ptr | |||
| vld1.8 {d8, d9, d10, d11}, [r1]! | |||
| b AddWeightBias | |||
| LAST_ROW: | |||
| ldr r0, [sp, #-52] // reload a ptr | |||
| vld1.8 {d0, d1, d2, d3}, [r0]! | |||
| ldr r1, [sp, #-48] // reload b ptr | |||
| ldr r9, [sp, #4] | |||
| mov r10, #2 | |||
| mul r9, r9, r10 // the stride of b | |||
| add r1, r1, r9 // b ptr + stride | |||
| str r1, [sp, #-48] | |||
| vld1.8 {d8, d9, d10, d11}, [r1]! | |||
| AddWeightBias: | |||
| ldr r9, [sp, #12] // reload weight_bias ptr | |||
| add r9, r9, r8 | |||
| vld1.32 {d26}, [r9]! | |||
| vadd.i32 d28, d28, d26 | |||
| vadd.i32 d29, d29, d26 | |||
| vadd.i32 d30, d30, d26 | |||
| vadd.i32 d31, d31, d26 | |||
| ldr r10, [sp, #44] | |||
| cmp r10, #0 | |||
| bgt PerChannel | |||
| PerTensor: | |||
| // Substract input_sums | |||
| vld1.32 {d24, d25}, [r6]! | |||
| vdup.32 d20, d24[0] | |||
| vdup.32 d21, d24[1] | |||
| vdup.32 d22, d25[0] | |||
| vdup.32 d23, d25[1] | |||
| vsub.s32 d28, d28, d20 | |||
| vsub.s32 d29, d29, d21 | |||
| vsub.s32 d30, d30, d22 | |||
| vsub.s32 d31, d31, d23 | |||
| // Apply left shift | |||
| ldr r10, [sp, #32] | |||
| ldr r11, [r10]! | |||
| vdup.32 q9, r11 | |||
| vshl.s32 q14, q14, q9 | |||
| vshl.s32 q15, q15, q9 | |||
| // Apply the fixed-point part of the multiplier | |||
| ldr r10, [sp, #28] | |||
| ldr r11, [r10] | |||
| vdup.32 q8, r11 | |||
| vqrdmulh.s32 q14, q14, q8 | |||
| vqrdmulh.s32 q15, q15, q8 | |||
| // Apply right shift | |||
| ldr r10, [sp, #36] | |||
| ldr r11, [r10] | |||
| vdup.32 q7, r11 | |||
| vand q6, q7, q14 | |||
| vshr.s32 q6, q6, #31 | |||
| vqadd.s32 q14, q14, q6 | |||
| vrshl.s32 q14, q14, q7 | |||
| vand q3, q7, q15 | |||
| vshr.s32 q3, q3, #31 | |||
| vqadd.s32 q15, q15, q3 | |||
| vrshl.s32 q15, q15, q7 | |||
| b AddDstZP | |||
| PerChannel: | |||
| // Substract input_sums | |||
| vld1.32 {d24, d25}, [r6]! | |||
| vdup.32 d20, d24[0] | |||
| vdup.32 d21, d24[1] | |||
| vdup.32 d22, d25[0] | |||
| vdup.32 d23, d25[1] | |||
| ldr r10, [sp, #48] | |||
| vld1.32 {d19}, [r10] | |||
| vmul.s32 d24, d20, d19 | |||
| vmul.s32 d25, d21, d19 | |||
| vmul.s32 d26, d22, d19 | |||
| vmul.s32 d27, d23, d19 | |||
| vsub.s32 d28, d28, d24 | |||
| vsub.s32 d29, d29, d25 | |||
| vsub.s32 d30, d30, d26 | |||
| vsub.s32 d31, d31, d27 | |||
| // Apply left shift | |||
| ldr r10, [sp, #32] | |||
| add r10, r10, r8 | |||
| vld1.32 {d23}, [r10] | |||
| vshl.s32 d28, d28, d23 | |||
| vshl.s32 d29, d29, d23 | |||
| vshl.s32 d30, d30, d23 | |||
| vshl.s32 d31, d31, d23 | |||
| // Apply the fixed-point part of the multiplier | |||
| ldr r10, [sp, #28] | |||
| add r10, r10, r8 | |||
| vld1.32 {d22}, [r10] | |||
| vqrdmulh.s32 d28, d28, d22 | |||
| vqrdmulh.s32 d29, d29, d22 | |||
| vqrdmulh.s32 d30, d30, d22 | |||
| vqrdmulh.s32 d31, d31, d22 | |||
| // Apply right shift | |||
| ldr r10, [sp, #36] | |||
| add r10, r10, r8 | |||
| vld1.32 {d21}, [r10] | |||
| vand d20, d21, d28 | |||
| vshr.s32 d20, d20, #31 | |||
| vqadd.s32 d28, d28, d20 | |||
| vrshl.s32 d28, d28, d21 | |||
| vand d19, d21, d29 | |||
| vshr.s32 d19, d19, #31 | |||
| vqadd.s32 d29, d29, d19 | |||
| vrshl.s32 d29, d29, d21 | |||
| vand d18, d21, d30 | |||
| vshr.s32 d18, d18, #31 | |||
| vqadd.s32 d30, d30, d18 | |||
| vrshl.s32 d30, d30, d21 | |||
| vand d17, d21, d31 | |||
| vshr.s32 d17, d17, #31 | |||
| vqadd.s32 d31, d31, d17 | |||
| vrshl.s32 d31, d31, d21 | |||
| AddDstZP: | |||
| // Add the destination zero point | |||
| ldr r10, [sp, #24] | |||
| vdup.32 q2, r10 | |||
| vadd.i32 q14, q14, q2 | |||
| vadd.i32 q15, q15, q2 | |||
| // Apply the act_min bound | |||
| ldr r10, [sp, #16] | |||
| vdup.32 q3, r10 | |||
| vmax.s32 q14, q14, q3 | |||
| vmax.s32 q15, q15, q3 | |||
| // Apply the act_max bound | |||
| ldr r10, [sp, #20] | |||
| vdup.32 q2, r10 | |||
| vmin.s32 q14, q14, q2 | |||
| vmin.s32 q15, q15, q2 | |||
| // Cast-and-saturate from int32 to int16 | |||
| vqmovn.s32 d28, q14 | |||
| vqmovn.s32 d29, q15 | |||
| // Cast-and-saturate from int16 to int8 | |||
| vqmovn.s16 d30, q14 | |||
| // start to write | |||
| cmp r4, #2 | |||
| bge WriteCol2 | |||
| cmp r4, #1 | |||
| beq WriteCol1 | |||
| b EndWrite | |||
| WriteCol2: | |||
| vst1.16 {d30[0]}, [r2], r7 | |||
| cmp r3, #1 | |||
| beq EndWrite | |||
| vst1.16 {d30[1]}, [r2], r7 | |||
| cmp r3, #2 | |||
| beq EndWrite | |||
| vst1.16 {d30[2]}, [r2], r7 | |||
| cmp r3, #3 | |||
| beq EndWrite | |||
| vst1.16 {d30[3]}, [r2], r7 | |||
| b EndWrite | |||
| WriteCol1: | |||
| vst1.8 {d30[0]}, [r2], r7 | |||
| cmp r3, #1 | |||
| beq EndWrite | |||
| vst1.8 {d30[2]}, [r2], r7 | |||
| cmp r3, #2 | |||
| beq EndWrite | |||
| vst1.8 {d30[4]}, [r2], r7 | |||
| cmp r3, #3 | |||
| beq EndWrite | |||
| vst1.8 {d30[6]}, [r2], r7 | |||
| b EndWrite | |||
| EndWrite: | |||
| sub r3, r3, #4 // a row counter -= 4 | |||
| b L2 | |||
| End2: | |||
| sub r4, r4, #2 // b col counter -= 2 | |||
| ldr r2, [sp, #-44] // load dst ptr | |||
| add r2, r2, #2 // dst ptr + offset | |||
| str r2, [sp, #-44] | |||
| ldr r10, [sp, #48] | |||
| add r10, r10, #8 | |||
| str r10, [sp, #48] | |||
| add r8, r8, #8 // output channels offset + 2*sizeof(int) | |||
| b L1 | |||
| End1: | |||
| sub sp, sp, #116 | |||
| vpop {q4-q7} | |||
| pop {r0-r11, pc} | |||
| push {r0-r8, r10, r11, lr} | |||
| vpush {q4-q7} | |||
| add sp, sp, #112 | |||
| ldr r5, [sp, #4] | |||
| ldr r6, [sp, #8] // reload a_sums ptr | |||
| ldr r8, [sp, #40] | |||
| mov r10, #4 | |||
| mul r10, r10, r5 // lhs step | |||
| mov r11, #4 | |||
| mul r11, r11, r8 // dst step | |||
| LoopRow: | |||
| ldr r1, [sp, #-44] //reload rhs ptr | |||
| ldr r4, [sp] // reload rhs col | |||
| ldr lr, [sp, #-40] | |||
| vmov.32 d4[0], lr // reload dst ptr | |||
| ldr lr, [sp, #32] | |||
| vmov.32 d4[1], lr // reload left shift | |||
| ldr lr, [sp, #28] | |||
| vmov.32 d5[0], lr // reload multiplier | |||
| ldr lr, [sp, #36] | |||
| vmov.32 d5[1], lr // reload right shift | |||
| ldr r7, [sp, #48] // reload filter_zp | |||
| ldr r12, [sp, #12] // reload bias ptr | |||
| LoopCol: | |||
| vmov.32 r2, d4[0] // reload dst ptr | |||
| ldr r0, [sp, #-48] //reload lhs ptr | |||
| ldr r5, [sp, #4] // reaload depth | |||
| vmov.i32 q6, #0 | |||
| vmov.i32 q7, #0 | |||
| vmov.i32 q8, #0 | |||
| vmov.i32 q9, #0 | |||
| vmov.i32 q10, #0 | |||
| vmov.i32 q11, #0 | |||
| vmov.i32 q12, #0 | |||
| vmov.i32 q13, #0 | |||
| LoopDepth: | |||
| vld1.8 {q0-q1}, [r0]! | |||
| vld1.8 {q4-q5}, [r1]! | |||
| vmull.s8 q14, d0, d8 | |||
| vmull.s8 q15, d2, d8 | |||
| vmlal.s8 q14, d1, d9 | |||
| vmlal.s8 q15, d3, d9 | |||
| vpadal.s16 q6, q14 | |||
| vpadal.s16 q8, q15 | |||
| vmull.s8 q14, d0, d10 | |||
| vmull.s8 q15, d2, d10 | |||
| vmlal.s8 q14, d1, d11 | |||
| vmlal.s8 q15, d3, d11 | |||
| vld1.8 {q0-q1}, [r0]! | |||
| vpadal.s16 q7, q14 | |||
| vpadal.s16 q9, q15 | |||
| vmull.s8 q14, d0, d8 | |||
| vmull.s8 q15, d2, d8 | |||
| vmlal.s8 q14, d1, d9 | |||
| vmlal.s8 q15, d3, d9 | |||
| vpadal.s16 q10, q14 | |||
| vpadal.s16 q12, q15 | |||
| vmull.s8 q14, d0, d10 | |||
| vmull.s8 q15, d2, d10 | |||
| vmlal.s8 q14, d1, d11 | |||
| vmlal.s8 q15, d3, d11 | |||
| vpadal.s16 q11, q14 | |||
| vpadal.s16 q13, q15 | |||
| cmp r5, #16 | |||
| ble LoopDepthEnd | |||
| sub r5, r5, #16 | |||
| b LoopDepth | |||
| LoopDepthEnd: | |||
| vpadd.i32 d12, d12, d13 | |||
| vpadd.i32 d14, d14, d15 | |||
| vpadd.i32 d16, d16, d17 | |||
| vpadd.i32 d18, d18, d19 | |||
| vpadd.i32 d20, d20, d21 | |||
| vpadd.i32 d22, d22, d23 | |||
| vpadd.i32 d24, d24, d25 | |||
| vpadd.i32 d26, d26, d27 | |||
| vpadd.i32 d28, d12, d14 | |||
| vpadd.i32 d29, d16, d18 | |||
| vpadd.i32 d30, d20, d22 | |||
| vpadd.i32 d31, d24, d26 | |||
| Bias: | |||
| cmp r12, #0 | |||
| beq NoBias | |||
| vld1.32 {d26}, [r12]! | |||
| vadd.i32 d28, d28, d26 | |||
| vadd.i32 d29, d29, d26 | |||
| vadd.i32 d30, d30, d26 | |||
| vadd.i32 d31, d31, d26 | |||
| NoBias: | |||
| ldr lr, [sp, #44] | |||
| cmp lr, #0 | |||
| bne PerChannel | |||
| PerTensor: | |||
| vld1.32 {d24, d25}, [r6] | |||
| vdup.32 d20, d24[0] | |||
| vdup.32 d21, d24[1] | |||
| vdup.32 d22, d25[0] | |||
| vdup.32 d23, d25[1] | |||
| vsub.s32 d28, d28, d20 | |||
| vsub.s32 d29, d29, d21 | |||
| vsub.s32 d30, d30, d22 | |||
| vsub.s32 d31, d31, d23 | |||
| vmov.32 lr, d4[1] | |||
| vld1.32 {q9[]}, [lr] | |||
| vshl.s32 q14, q14, q9 | |||
| vshl.s32 q15, q15, q9 | |||
| vmov.32 lr, d5[0] | |||
| vld1.32 {q8[]}, [lr] | |||
| vqrdmulh.s32 q14, q14, q8 | |||
| vqrdmulh.s32 q15, q15, q8 | |||
| vmov.32 lr, d5[1] | |||
| vld1.32 {q7[]}, [lr] | |||
| vand q6, q7, q14 | |||
| vshr.s32 q6, q6, #31 | |||
| vqadd.s32 q14, q14, q6 | |||
| vrshl.s32 q14, q14, q7 | |||
| vand q5, q7, q15 | |||
| vshr.s32 q5, q5, #31 | |||
| vqadd.s32 q15, q15, q5 | |||
| vrshl.s32 q15, q15, q7 | |||
| b Quantize | |||
| PerChannel: | |||
| vld1.32 {d24, d25}, [r6] | |||
| vdup.32 d20, d24[0] | |||
| vdup.32 d21, d24[1] | |||
| vdup.32 d22, d25[0] | |||
| vdup.32 d23, d25[1] | |||
| vld1.32 {d19}, [r7]! | |||
| vmul.s32 d24, d20, d19 | |||
| vmul.s32 d25, d21, d19 | |||
| vmul.s32 d26, d22, d19 | |||
| vmul.s32 d27, d23, d19 | |||
| vsub.s32 d28, d28, d24 | |||
| vsub.s32 d29, d29, d25 | |||
| vsub.s32 d30, d30, d26 | |||
| vsub.s32 d31, d31, d27 | |||
| vmov.32 lr, d4[1] | |||
| vld1.32 {d23}, [lr]! | |||
| vmov.32 d4[1], lr | |||
| vshl.s32 d28, d28, d23 | |||
| vshl.s32 d29, d29, d23 | |||
| vshl.s32 d30, d30, d23 | |||
| vshl.s32 d31, d31, d23 | |||
| vmov.32 lr, d5[0] | |||
| vld1.32 {d22}, [lr]! | |||
| vmov.32 d5[0], lr | |||
| vqrdmulh.s32 d28, d28, d22 | |||
| vqrdmulh.s32 d29, d29, d22 | |||
| vqrdmulh.s32 d30, d30, d22 | |||
| vqrdmulh.s32 d31, d31, d22 | |||
| vmov.32 lr, d5[1] | |||
| vld1.32 {d21}, [lr]! | |||
| vmov.32 d5[1], lr | |||
| vand d20, d21, d28 | |||
| vshr.s32 d20, d20, #31 | |||
| vqadd.s32 d28, d28, d20 | |||
| vrshl.s32 d28, d28, d21 | |||
| vand d19, d21, d29 | |||
| vshr.s32 d19, d19, #31 | |||
| vqadd.s32 d29, d29, d19 | |||
| vrshl.s32 d29, d29, d21 | |||
| vand d18, d21, d30 | |||
| vshr.s32 d18, d18, #31 | |||
| vqadd.s32 d30, d30, d18 | |||
| vrshl.s32 d30, d30, d21 | |||
| vand d17, d21, d31 | |||
| vshr.s32 d17, d17, #31 | |||
| vqadd.s32 d31, d31, d17 | |||
| vrshl.s32 d31, d31, d21 | |||
| Quantize: | |||
| ldr lr, [sp, #24] | |||
| vdup.32 q0, lr | |||
| vadd.i32 q14, q14, q0 | |||
| vadd.i32 q15, q15, q0 | |||
| ldr lr, [sp, #16] | |||
| vdup.32 q1, lr | |||
| vmax.s32 q14, q14, q1 | |||
| vmax.s32 q15, q15, q1 | |||
| ldr lr, [sp, #20] | |||
| vdup.32 q0, lr | |||
| vmin.s32 q14, q14, q0 | |||
| vmin.s32 q15, q15, q0 | |||
| vqmovn.s32 d28, q14 | |||
| vqmovn.s32 d29, q15 | |||
| vqmovn.s16 d30, q14 | |||
| cmp r4, #1 | |||
| beq Write1 | |||
| b Write2 | |||
| Write1: | |||
| vmov.32 lr, d4[0] | |||
| add lr, lr, #1 | |||
| vmov.32 d4[0], lr | |||
| vst1.8 {d30[0]}, [r2], r8 | |||
| cmp r3, #1 | |||
| beq WriteEnd | |||
| vst1.8 {d30[2]}, [r2], r8 | |||
| cmp r3, #2 | |||
| beq WriteEnd | |||
| vst1.8 {d30[4]}, [r2], r8 | |||
| cmp r3, #3 | |||
| beq WriteEnd | |||
| vst1.8 {d30[6]}, [r2], r8 | |||
| b WriteEnd | |||
| Write2: | |||
| vmov.32 lr, d4[0] | |||
| add lr, lr, #2 | |||
| vmov.32 d4[0], lr | |||
| vst1.16 {d30[0]}, [r2], r8 | |||
| cmp r3, #1 | |||
| beq WriteEnd | |||
| vst1.16 {d30[1]}, [r2], r8 | |||
| cmp r3, #2 | |||
| beq WriteEnd | |||
| vst1.16 {d30[2]}, [r2], r8 | |||
| cmp r3, #3 | |||
| beq WriteEnd | |||
| vst1.16 {d30[3]}, [r2], r8 | |||
| WriteEnd: | |||
| cmp r4, #2 | |||
| ble LoopColEnd | |||
| sub r4, r4, #2 | |||
| b LoopCol | |||
| LoopColEnd: | |||
| cmp r3, #4 | |||
| ble LoopRowEnd | |||
| ldr lr, [sp, #-48] | |||
| add lr, lr, r10 | |||
| str lr, [sp, #-48] | |||
| ldr lr, [sp, #-40] | |||
| add lr, lr, r11 | |||
| str lr, [sp, #-40] | |||
| sub r3, r3, #4 | |||
| add r6, r6, #16 | |||
| b LoopRow | |||
| LoopRowEnd: | |||
| sub sp, sp, #112 | |||
| vpop {q4-q7} | |||
| pop {r0-r8, r10, r11, pc} | |||
| #endif | |||
| #endif | |||