1// Copyright 2015 The Gemmlowp Authors. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// kernel_neon.h: a collection of NEON optimized kernels.
16// Check in kernel_default.h which one(s) are actually used by default.
17// Others are mere experiments; they are still covered by tests
18// in case they might be useful some day.
19
20#ifndef GEMMLOWP_INTERNAL_KERNEL_NEON_H_
21#define GEMMLOWP_INTERNAL_KERNEL_NEON_H_
22
23#include "kernel.h"
24
25#include <arm_neon.h>
26#include <cassert>
27
28namespace gemmlowp {
29
30// The kernels here are specifically arm 32bit assembly, not arm 64bit.
31#ifdef GEMMLOWP_NEON_32
32
33// Our main GEMM kernel.
34struct NEON_32_Kernel12x4Depth2 : KernelBase {
35  typedef KernelFormat<KernelSideFormat<CellFormat<4, 2>, 3>,
36                       KernelSideFormat<CellFormat<4, 2>, 1> >
37      Format;
38
39  const char* Name() const override { return "NEON, 12x4, depth 2"; }
40
41  // TODO(benoitjacob): reorder function arguments so dst comes last
42  void Run(std::int32_t* dst_ptr, std::size_t dst_row_stride,
43           std::size_t dst_col_stride, const std::uint8_t* lhs_ptr,
44           const std::uint8_t* rhs_ptr, std::size_t start_depth,
45           std::size_t run_depth) const override {
46    ScopedProfilingLabel label("optimized kernel (NEON 12x4)");
47
48// For iOS assembler, the %= style of local labels cause compilation errors,
49//  so use numerical ones instead. See
50// http://stackoverflow.com/questions/3898435/labels-in-gcc-inline-assembly
51// If you add any labels, remember to undef them at the end.
52#define GEMMLOWP_LABEL_CLEAR_ACCUMULATORS "1"
53#define GEMMLOWP_LABEL_BEFORE_LOOP "2"
54#define GEMMLOWP_LABEL_LOOP "3"
55#define GEMMLOWP_LABEL_AFTER_LOOP "4"
56
57    assert(dst_row_stride == 1);
58    asm volatile(
59        // Overview of register layout:
60        //
61        // A 2x4 cell of Rhs is stored in 16bit in d0--d1 (q0).
62        // A 12x2 block of 3 4x2 cells Lhs is stored in 16bit in d2--d7
63        // (q1--q3).
64        // A 12x4 block of accumulators is stored in 32bit in q4--q15.
65        //
66        //                   +-----+-----+-----+-----+
67        //                   |d0[0]|d0[1]|d0[2]|d0[3]|
68        //              Rhs  +-----+-----+-----+-----+
69        //                   |d1[0]|d1[1]|d1[2]|d1[3]|
70        //                   +-----+-----+-----+-----+
71        //
72        //                   |     |     |     |     |
73        //
74        //    Lhs            |     |     |     |     |
75        //
76        //  +--+--+ - - - -  +-----+-----+-----+-----+
77        //  |d2|d3|          | q4  | q5  | q6  | q7  |
78        //  |d2|d3|          | q4  | q5  | q6  | q7  |
79        //  |d2|d3|          | q4  | q5  | q6  | q7  |
80        //  |d2|d3|          | q4  | q5  | q6  | q7  |
81        //  +--+--+ - - - -  +-----+-----+-----+-----+
82        //  |d4|d5|          | q8  | q9  | q10 | q11 |
83        //  |d4|d5|          | q8  | q9  | q10 | q11 |
84        //  |d4|d5|          | q8  | q9  | q10 | q11 |
85        //  |d4|d5|          | q8  | q9  | q10 | q11 |
86        //  +--+--+ - - - -  +-----+-----+-----+-----+
87        //  |d6|d7|          | q12 | q13 | q14 | q15 |
88        //  |d6|d7|          | q12 | q13 | q14 | q15 |
89        //  |d6|d7|          | q12 | q13 | q14 | q15 |
90        //  |d6|d7|          | q12 | q13 | q14 | q15 |
91        //  +--+--+ - - - -  +-----+-----+-----+-----+
92        //
93        //                            Accumulator
94
95        // Load 1 Rhs cell of size 2x4
96        "vld1.8 {d0}, [%[rhs_ptr]]!\n"
97        // Load 3 Lhs cells of size 4x2 each
98        "vld1.8 {d2}, [%[lhs_ptr]]!\n"
99        "vld1.8 {d4}, [%[lhs_ptr]]!\n"
100        "vld1.8 {d6}, [%[lhs_ptr]]!\n"
101
102        // Check if start_depth==0 to decide whether we will clear
103        // accumulators or load existing accumulators.
104        "cmp %[start_depth], #0\n"
105
106        // Multiply dst_col_stride by 4 == sizeof(int32) to use
107        // it as a byte offset below.
108        "lsl %[dst_col_stride], #2\n"
109
110        "beq " GEMMLOWP_LABEL_CLEAR_ACCUMULATORS
111        "f\n"
112
113        // Load accumulators (start_depth != 0)
114        "mov r1, %[dst_ptr]\n"
115        "subs %[run_depth], #2\n"
116        "mov r0, r1\n"
117        "vld1.32 {d8, d9},   [r0]!\n"
118        "add r1, %[dst_col_stride]\n"
119        "vld1.32 {d16, d17}, [r0]!\n"
120        "vld1.32 {d24, d25}, [r0]\n"
121        "mov r0, r1\n"
122        "vld1.32 {d10, d11}, [r0]!\n"
123        "add r1, %[dst_col_stride]\n"
124        "vld1.32 {d18, d19}, [r0]!\n"
125        "vld1.32 {d26, d27}, [r0]\n"
126        "mov r0, r1\n"
127        "vld1.32 {d12, d13}, [r0]!\n"
128        "add r1, %[dst_col_stride]\n"
129        "vld1.32 {d20, d21}, [r0]!\n"
130        "vld1.32 {d28, d29}, [r0]\n"
131        "mov r0, r1\n"
132        "vld1.32 {d14, d15}, [r0]!\n"
133        "vld1.32 {d22, d23}, [r0]!\n"
134        "vld1.32 {d30, d31}, [r0]\n"
135
136        "b " GEMMLOWP_LABEL_BEFORE_LOOP "f\n"
137
138        GEMMLOWP_LABEL_CLEAR_ACCUMULATORS
139        ":\n"
140
141        // Clear accumulators (start_depth == 0)
142        "vmov.s32 q4, #0\n"
143        "subs %[run_depth], #2\n"
144        "vmov.s32 q8, q4\n"
145        "vmov.s32 q12, q4\n"
146        "vmov.s32 q5, q4\n"
147        "vmov.s32 q9, q4\n"
148        "vmov.s32 q13, q4\n"
149        "vmov.s32 q6, q4\n"
150        "vmov.s32 q10, q4\n"
151        "vmov.s32 q14, q4\n"
152        "vmov.s32 q7, q4\n"
153        "vmov.s32 q11, q4\n"
154        "vmov.s32 q15, q4\n"
155
156        GEMMLOWP_LABEL_BEFORE_LOOP
157        ":\n"
158
159        // If there are only two levels of depth, skip the loop.
160        "beq " GEMMLOWP_LABEL_AFTER_LOOP "f\n"
161
162        GEMMLOWP_LABEL_LOOP
163        ":\n"
164        // Expand Lhs/Rhs cells to 16 bit.
165        // Note: moving theses vmovls further down to allow for
166        // longer data pipelining helps a little on A57 but is
167        // harmful on A53 --- It looks as if A53 doesn't like
168        // interleaving vmovl's into the vmlal's.
169        "vmovl.u8 q0, d0\n"
170        "vmovl.u8 q1, d2\n"
171        "vmovl.u8 q2, d4\n"
172        "vmovl.u8 q3, d6\n"
173
174        // Multiply-accumulate, level of depth 0
175        "vmlal.u16 q4, d2, d0[0]\n"
176        "vmlal.u16 q5, d2, d0[1]\n"
177        "vmlal.u16 q6, d2, d0[2]\n"
178        "vmlal.u16 q7, d2, d0[3]\n"
179        "vldr d2, [%[lhs_ptr]]\n"
180        "vmlal.u16 q8, d4, d0[0]\n"
181        "vmlal.u16 q9, d4, d0[1]\n"
182        "vmlal.u16 q10, d4, d0[2]\n"
183        "vmlal.u16 q11, d4, d0[3]\n"
184        "vldr d4, [%[lhs_ptr], #8]\n"
185        "vmlal.u16 q12, d6, d0[0]\n"
186        "vmlal.u16 q13, d6, d0[1]\n"
187        "vmlal.u16 q14, d6, d0[2]\n"
188        "vmlal.u16 q15, d6, d0[3]\n"
189        "vldr d6, [%[lhs_ptr], #16]\n"
190        "vldr d0, [%[rhs_ptr]]\n"
191
192        // Multiply-accumulate, level of depth 1
193        "vmlal.u16 q4, d3, d1[0]\n"
194        "vmlal.u16 q5, d3, d1[1]\n"
195        "add %[lhs_ptr], #24\n"
196        "vmlal.u16 q6, d3, d1[2]\n"
197        "vmlal.u16 q7, d3, d1[3]\n"
198        "add %[rhs_ptr], #8\n"
199        "vmlal.u16 q8, d5, d1[0]\n"
200        "vmlal.u16 q9, d5, d1[1]\n"
201        "subs %[run_depth], #2\n"
202        "vmlal.u16 q10, d5, d1[2]\n"
203        "vmlal.u16 q11, d5, d1[3]\n"
204        "vmlal.u16 q12, d7, d1[0]\n"
205        "vmlal.u16 q13, d7, d1[1]\n"
206        "vmlal.u16 q14, d7, d1[2]\n"
207        "vmlal.u16 q15, d7, d1[3]\n"
208
209        "bne " GEMMLOWP_LABEL_LOOP "b\n"
210
211        GEMMLOWP_LABEL_AFTER_LOOP
212        ":\n"
213
214        // Do remaining arithmetic for the last 2 levels of depth.
215
216        // Expand Lhs/Rhs cells to 16 bit.
217        "vmovl.u8 q0, d0\n"
218        "vmovl.u8 q1, d2\n"
219        "vmovl.u8 q2, d4\n"
220        "vmovl.u8 q3, d6\n"
221
222        // Multiply-accumulate, level of depth 0
223        "vmlal.u16 q4, d2, d0[0]\n"
224        "vmlal.u16 q5, d2, d0[1]\n"
225        "vmlal.u16 q6, d2, d0[2]\n"
226        "vmlal.u16 q7, d2, d0[3]\n"
227        "vmlal.u16 q8, d4, d0[0]\n"
228        "vmlal.u16 q9, d4, d0[1]\n"
229        "vmlal.u16 q10, d4, d0[2]\n"
230        "vmlal.u16 q11, d4, d0[3]\n"
231        "vmlal.u16 q12, d6, d0[0]\n"
232        "vmlal.u16 q13, d6, d0[1]\n"
233        "vmlal.u16 q14, d6, d0[2]\n"
234        "vmlal.u16 q15, d6, d0[3]\n"
235
236        // Multiply-accumulate, level of depth 1
237        "vmlal.u16 q4, d3, d1[0]\n"
238        "vmlal.u16 q5, d3, d1[1]\n"
239        "vmlal.u16 q6, d3, d1[2]\n"
240        "vmlal.u16 q7, d3, d1[3]\n"
241        "vmlal.u16 q8, d5, d1[0]\n"
242        "vmlal.u16 q9, d5, d1[1]\n"
243        "vmlal.u16 q10, d5, d1[2]\n"
244        "vmlal.u16 q11, d5, d1[3]\n"
245        "vmlal.u16 q12, d7, d1[0]\n"
246        "vmlal.u16 q13, d7, d1[1]\n"
247        "vmlal.u16 q14, d7, d1[2]\n"
248        "vmlal.u16 q15, d7, d1[3]\n"
249
250        // Store accumulators
251        "mov r1, %[dst_ptr]\n"
252        "mov r0, r1\n"
253        "vst1.32 {d8, d9},   [r0]!\n"
254        "add r1, %[dst_col_stride]\n"
255        "vst1.32 {d16, d17}, [r0]!\n"
256        "vst1.32 {d24, d25}, [r0]\n"
257        "mov r0, r1\n"
258        "vst1.32 {d10, d11}, [r0]!\n"
259        "add r1, %[dst_col_stride]\n"
260        "vst1.32 {d18, d19}, [r0]!\n"
261        "vst1.32 {d26, d27}, [r0]\n"
262        "mov r0, r1\n"
263        "vst1.32 {d12, d13}, [r0]!\n"
264        "add r1, %[dst_col_stride]\n"
265        "vst1.32 {d20, d21}, [r0]!\n"
266        "vst1.32 {d28, d29}, [r0]\n"
267        "mov r0, r1\n"
268        "vst1.32 {d14, d15}, [r0]!\n"
269        "vst1.32 {d22, d23}, [r0]!\n"
270        "vst1.32 {d30, d31}, [r0]\n"
271        :  // outputs
272        [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
273        [dst_ptr] "+r"(dst_ptr),
274        [run_depth] "+r"(run_depth)
275        :  // inputs
276        [start_depth] "r"(start_depth),
277        [dst_col_stride] "r"(dst_col_stride)
278        :  // clobbers
279        "cc", "memory", "r0", "r1",
280        // note: someone on internet says that quad registers are
281        // unsupported in the clobber list!
282        "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "d10",
283        "d11", "d12", "d13", "d14", "d15", "d16", "d17", "d18", "d19", "d20",
284        "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29", "d30",
285        "d31");
286#undef GEMMLOWP_LABEL_CLEAR_ACCUMULATORS
287#undef GEMMLOWP_LABEL_BEFORE_LOOP
288#undef GEMMLOWP_LABEL_LOOP
289#undef GEMMLOWP_LABEL_AFTER_LOOP
290  }
291};
292
293struct NEON_32_Kernel12x4Depth2Assuming12BitProducts : KernelBase {
294  typedef KernelFormat<
295      KernelSideFormat<CellFormat<4, 2, CellOrder::WidthMajor>, 3>,
296      KernelSideFormat<CellFormat<4, 2, CellOrder::WidthMajor>, 1> >
297      Format;
298
299  const char* Name() const override {
300    return "NEON, 12x4, depth 2, assuming 12-bit products";
301  }
302
303  // TODO(benoitjacob): reorder function arguments so dst comes last
304  void Run(std::int32_t* dst_ptr, std::size_t dst_row_stride,
305           std::size_t dst_col_stride, const std::uint8_t* lhs_ptr,
306           const std::uint8_t* rhs_ptr, std::size_t start_depth,
307           std::size_t run_depth) const override {
308    ScopedProfilingLabel label(
309        "optimized kernel (NEON 12x4, assuming 12-bit products)");
310    assert(dst_row_stride == 1);
311
312// See comments above for why we need local numerical labels in our asm.
313#define GEMMLOWP_LOOP_NEON_32_KERNEL_12X4_DEPTH2_ASSUMING_12BIT_PRODUCTS "1"
314#define GEMMLOWP_LOAD_GLOBAL_ACCUMULATORS_NEON_32_KERNEL_12X4_DEPTH2_12BIT "2"
315#define GEMMLOWP_LABEL_32 "3"
316#define GEMMLOWP_LABEL_24 "4"
317#define GEMMLOWP_LABEL_16 "5"
318#define GEMMLOWP_LABEL_8 "6"
319#define GEMMLOWP_LABEL_2 "7"
320
321    // This kernel is special in that it uses local 16-bit accumulators.
322    // Because it assumes that each product fits in 12 bits, it can accumulate
323    // 16 products into a local 16-bit accumulator without risking overflow.
324    // At that point, it must accumulate these local 16-bit accumulators back
325    // into global 32-bit accumulators, which have to be stored in memory for
326    // lack of register space.
327    // This 12x4 block of global accumulators is laid out as 3 cells of size 4x4
328    // stored in diagonal-major order like this for the first 4x4 cell:
329    //
330    //   0   4   8  12
331    //  13   1   5   9
332    //  10  14   2   6
333    //   7  11  15   3
334    //
335    // and likewise for the 2nd  cell (16--31) and 3rd cell (32--47)
336    std::int32_t global_accumulators[3 * 4 * 4];
337    asm volatile(
338        // Compute stride between consecutive columns, in bytes
339        "mov r0, #4\n"  // multiply by 4 = sizeof(int32)
340        "mul %[dst_col_stride], r0\n"
341
342        "cmp %[start_depth], #0\n"
343        "bne"
344        " " GEMMLOWP_LOAD_GLOBAL_ACCUMULATORS_NEON_32_KERNEL_12X4_DEPTH2_12BIT
345        "f\n"
346
347        // If start_depth==0, we need to clear our global accumulators
348        "mov r0, %[global_accumulators]\n"
349        "vmov.s32 q8, #0\n"
350        "vmov.s32 q9, q8\n"
351        "vst1.32 {d16,d17,d18,d19}, [r0]!\n"
352        "vst1.32 {d16,d17,d18,d19}, [r0]!\n"
353        "vst1.32 {d16,d17,d18,d19}, [r0]!\n"
354        "vst1.32 {d16,d17,d18,d19}, [r0]!\n"
355        "vst1.32 {d16,d17,d18,d19}, [r0]!\n"
356        "vst1.32 {d16,d17,d18,d19}, [r0]!\n"
357        "b " GEMMLOWP_LOOP_NEON_32_KERNEL_12X4_DEPTH2_ASSUMING_12BIT_PRODUCTS
358        "f\n"
359
360        // If start_depth!=0, we need to load our existing global accumulators
361        GEMMLOWP_LOAD_GLOBAL_ACCUMULATORS_NEON_32_KERNEL_12X4_DEPTH2_12BIT
362        ":\n"
363        // Load global accumulators from destination matrix, column-major
364        "mov r1, %[dst_ptr]\n"
365        "mov r0, %[dst_col_stride]\n"
366        "sub r0, #32\n"
367        "vld1.32 {d0,d1}, [r1]!\n"
368        "vld1.32 {d8,d9}, [r1]!\n"
369        "vld1.32 {d16,d17}, [r1], r0\n"
370        "vld1.32 {d2,d3}, [r1]!\n"
371        "vld1.32 {d10,d11}, [r1]!\n"
372        "vld1.32 {d18,d19}, [r1], r0\n"
373        "vld1.32 {d4,d5}, [r1]!\n"
374        "vld1.32 {d12,d13}, [r1]!\n"
375        "vld1.32 {d20,d21}, [r1], r0\n"
376        "vld1.32 {d6,d7}, [r1]!\n"
377        "vld1.32 {d14,d15}, [r1]!\n"
378        "vld1.32 {d22,d23}, [r1], r0\n"
379        // Now we need to convert the global accumulator registers to
380        // 4x4-block-wise diagonal-major order. What we effectively want to do
381        // is to rotate the rows, however the accumulators are stored in
382        // column-major order in registers. So we achieve this by
383        // transposing, rotating the registers, and transposing again each
384        // 4x4 block.
385        //
386        // Transpose 3 4x4 blocks separately
387        "vtrn.32 q0, q1\n"
388        "vtrn.32 q2, q3\n"
389        "vswp d1, d4\n"
390        "vswp d3, d6\n"
391        "vtrn.32 q4, q5\n"
392        "vtrn.32 q6, q7\n"
393        "vswp d9, d12\n"
394        "vswp d11, d14\n"
395        "vtrn.32 q8, q9\n"
396        "vtrn.32 q10, q11\n"
397        "vswp d17, d20\n"
398        "vswp d19, d22\n"
399        // Rotate the registers
400        "vext.32 q1, q1, q1, #1\n"
401        "vext.32 q2, q2, q2, #2\n"
402        "vext.32 q3, q3, q3, #3\n"
403        "vext.32 q5, q5, q5, #1\n"
404        "vext.32 q6, q6, q6, #2\n"
405        "vext.32 q7, q7, q7, #3\n"
406        "vext.32 q9, q9, q9, #1\n"
407        "vext.32 q10, q10, q10, #2\n"
408        "vext.32 q11, q11, q11, #3\n"
409        // Transpose again and store into our global accumulators
410        // buffer. These two operations are done at once using vst4.
411        "mov r0, %[global_accumulators]\n"
412        "vst4.32 {d0,d2,d4,d6}, [r0]!\n"
413        "vst4.32 {d1,d3,d5,d7}, [r0]!\n"
414        "vst4.32 {d8,d10,d12,d14}, [r0]!\n"
415        "vst4.32 {d9,d11,d13,d15}, [r0]!\n"
416        "vst4.32 {d16,d18,d20,d22}, [r0]!\n"
417        "vst4.32 {d17,d19,d21,d23}, [r0]!\n"
418
419        /* Main loop */
420
421        GEMMLOWP_LOOP_NEON_32_KERNEL_12X4_DEPTH2_ASSUMING_12BIT_PRODUCTS
422        ":\n"
423
424    // Overview of register layout:
425    //
426    // Registers q4--q16 are the local 16-bit accumulators.
427    // However, each entry in the result matrix is represented
428    // by *two* local 16-bit accumulators: one for even levels
429    // of depth and one for odd levels of depth. These correspond
430    // to the scalars at even and odd indices within each q-register.
431    // Thus we effectively use 32 bits of register space for each
432    // entry in the result matrix. The accumulators register layout
433    // is the same as was described above for the global 32-bit
434    // accumulators (3 cells of size 4x4 in diagonal-major order)
435    // with the only difference that instead of 32bit values we have
436    // pairs of 16bit values.
437    //
438    // A 2x4 cell of Rhs is stored in 8bit in d0.
439    // A 12x2 block of 3 4x2 cells Lhs is stored in 8bit in d1--d3.
440    //
441    //                      +--------+--------+--------+--------+
442    //                      |d0[0]   |d0[2]   |d0[4]   |d0[6]   |
443    //                 Rhs  +--------+--------+--------+--------+
444    //                      |d0[1]   |d0[3]   |d0[5]   |d0[7]   |
445    //                      +--------+--------+--------+--------+
446    //
447    //                      |        |        |        |        |
448    //
449    //    Lhs               |        |        |        |        |
450    //
451    //  +-----+-----+ - - - +--------+--------+--------+--------+
452    //  |d1[0]|d1[1]|       |q4[0,1] |q5[0,1] |q6[0,1] |q7[0,1] |
453    //  |d1[2]|d1[3]|       |q7[2,3] |q4[2,3] |q5[2,3] |q6[2,3] |
454    //  |d1[4]|d1[5]|       |q6[4,5] |q7[4,5] |q4[4,5] |q5[4,5] |
455    //  |d1[6]|d1[7]|       |q5[6,7] |q6[6,7] |q7[6,7] |q4[6,7] |
456    //  +-----+-----+ - - - +--------+--------+--------+--------+
457    //  |d2[0]|d2[1]|       |q8[0,1] |q8[0,1] |q8[0,1] |q8[0,1] |
458    //  |d2[2]|d2[3]|       |q9[2,3] |q9[2,3] |q9[2,3] |q9[2,3] |
459    //  |d2[4]|d2[5]|       |q10[4,5]|q10[4,5]|q10[4,5]|q10[4,5]|
460    //  |d2[6]|d2[7]|       |q11[6,7]|q11[6,7]|q11[6,7]|q11[6,7]|
461    //  +-----+-----+ - - - +--------+--------+--------+--------+
462    //  |d3[0]|d3[1]|       |q12[0,1]|q12[0,1]|q12[0,1]|q12[0,1]|
463    //  |d3[2]|d3[3]|       |q13[2,3]|q13[2,3]|q13[2,3]|q13[2,3]|
464    //  |d3[4]|d3[5]|       |q14[4,5]|q14[4,5]|q14[4,5]|q14[4,5]|
465    //  |d3[6]|d3[7]|       |q15[6,7]|q15[6,7]|q15[6,7]|q15[6,7]|
466    //  +-----+-----+ - - - +--------+--------+--------+--------+
467    //
468    //                            Local 16-bit accumulators
469    //                         Note: 2 scalars per matrix entry
470
471#define GEMMLOWP_ACCUMULATE_2_LEVELS_OF_DEPTH \
472  /* Load 3 Lhs cells of size 4x2 */          \
473  "vld1.8 {d1,d2,d3}, [%[lhs_ptr]:64]!\n"     \
474                                              \
475  /* Load 1 Rhs cell of size 2x4 */           \
476  "vld1.8 {d0}, [%[rhs_ptr]:64]!\n"           \
477                                              \
478  /* Multiply-accumulate */                   \
479  "vmlal.u8 q4, d1, d0\n"                     \
480  "vmlal.u8 q8, d2, d0\n"                     \
481  "vmlal.u8 q12, d3, d0\n"                    \
482  "vext.8 d0, d0, d0, #2\n"                   \
483  "vmlal.u8 q5, d1, d0\n"                     \
484  "vmlal.u8 q9, d2, d0\n"                     \
485  "vmlal.u8 q13, d3, d0\n"                    \
486  "vext.8 d0, d0, d0, #2\n"                   \
487  "vmlal.u8 q6, d1, d0\n"                     \
488  "vmlal.u8 q10, d2, d0\n"                    \
489  "vmlal.u8 q14, d3, d0\n"                    \
490  "vext.8 d0, d0, d0, #2\n"                   \
491  "vmlal.u8 q7, d1, d0\n"                     \
492  "vmlal.u8 q11, d2, d0\n"                    \
493  "vmlal.u8 q15, d3, d0\n"                    \
494                                              \
495  "sub %[run_depth], #2\n"
496
497#define GEMMLOWP_ACCUMULATE_8_LEVELS_OF_DEPTH \
498  GEMMLOWP_ACCUMULATE_2_LEVELS_OF_DEPTH       \
499  GEMMLOWP_ACCUMULATE_2_LEVELS_OF_DEPTH       \
500  GEMMLOWP_ACCUMULATE_2_LEVELS_OF_DEPTH       \
501  GEMMLOWP_ACCUMULATE_2_LEVELS_OF_DEPTH
502
503        // Clear local 16-bit accumulators
504        "vmov.s32 q4, #0\n"
505        "vmov.s32 q5, q4\n"
506        "vmov.s32 q6, q4\n"
507        "vmov.s32 q7, q4\n"
508        "vmov.s32 q8, q4\n"
509        "vmov.s32 q9, q4\n"
510        "vmov.s32 q10, q4\n"
511        "vmov.s32 q11, q4\n"
512        "vmov.s32 q12, q4\n"
513        "vmov.s32 q13, q4\n"
514        "vmov.s32 q14, q4\n"
515        "vmov.s32 q15, q4\n"
516
517        // Select a suitable number of depth levels
518        // to process at this iteration. TODO (benoitjacob) I guess that
519        // someone who really knows asm should make this a jump table.
520        "cmp %[run_depth], #32\n"
521        "bge " GEMMLOWP_LABEL_32
522        "f\n"
523        "cmp %[run_depth], #24\n"
524        "bge " GEMMLOWP_LABEL_24
525        "f\n"
526        "cmp %[run_depth], #16\n"
527        "bge " GEMMLOWP_LABEL_16
528        "f\n"
529        "cmp %[run_depth], #8\n"
530        "bge " GEMMLOWP_LABEL_8
531        "f\n"
532        "b " GEMMLOWP_LABEL_2 "f\n"
533
534        GEMMLOWP_LABEL_32
535        ":\n" GEMMLOWP_ACCUMULATE_8_LEVELS_OF_DEPTH GEMMLOWP_LABEL_24
536        ":\n" GEMMLOWP_ACCUMULATE_8_LEVELS_OF_DEPTH GEMMLOWP_LABEL_16
537        ":\n" GEMMLOWP_ACCUMULATE_8_LEVELS_OF_DEPTH GEMMLOWP_LABEL_8
538        ":\n" GEMMLOWP_ACCUMULATE_2_LEVELS_OF_DEPTH
539            GEMMLOWP_ACCUMULATE_2_LEVELS_OF_DEPTH
540                GEMMLOWP_ACCUMULATE_2_LEVELS_OF_DEPTH GEMMLOWP_LABEL_2
541        ":\n" GEMMLOWP_ACCUMULATE_2_LEVELS_OF_DEPTH
542
543        // Accumulate the local accumulators into the global accumulators.
544        // This is about summing adjacent pairs of 16-bit scalars into
545        // single 32-bit scalars, so we use pairwise long addition (vpadal).
546        "mov r0, %[global_accumulators]\n"
547        "mov r1, %[global_accumulators]\n"
548        "vld1.32 {d0,d1,d2,d3}, [r0]!\n"
549        "vld1.32 {d4,d5,d6,d7}, [r0]!\n"
550        "vpadal.u16 q0, q4\n"
551        "vpadal.u16 q1, q5\n"
552        "vpadal.u16 q2, q6\n"
553        "vpadal.u16 q3, q7\n"
554        "vst1.32 {d0,d1,d2,d3}, [r1]!\n"
555        "vst1.32 {d4,d5,d6,d7}, [r1]!\n"
556        "vld1.32 {d0,d1,d2,d3}, [r0]!\n"
557        "vld1.32 {d4,d5,d6,d7}, [r0]!\n"
558        "vpadal.u16 q0, q8\n"
559        "vpadal.u16 q1, q9\n"
560        "vpadal.u16 q2, q10\n"
561        "vpadal.u16 q3, q11\n"
562        "vst1.32 {d0,d1,d2,d3}, [r1]!\n"
563        "vst1.32 {d4,d5,d6,d7}, [r1]!\n"
564        "vld1.32 {d0,d1,d2,d3}, [r0]!\n"
565        "vld1.32 {d4,d5,d6,d7}, [r0]!\n"
566        "vpadal.u16 q0, q12\n"
567        "vpadal.u16 q1, q13\n"
568        "vpadal.u16 q2, q14\n"
569        "vpadal.u16 q3, q15\n"
570        "vst1.32 {d0,d1,d2,d3}, [r1]!\n"
571        "vst1.32 {d4,d5,d6,d7}, [r1]!\n"
572
573        // Loop.
574        "cmp %[run_depth], #0\n"
575        "bne " GEMMLOWP_LOOP_NEON_32_KERNEL_12X4_DEPTH2_ASSUMING_12BIT_PRODUCTS
576        "b\n"
577
578#undef GEMMLOWP_CLEAR_LOCAL_ACCUMULATORS
579#undef GEMMLOWP_ACCUMULATE_8_LEVELS_OF_DEPTH
580#undef GEMMLOWP_ACCUMULATE_2_LEVELS_OF_DEPTH
581#undef GEMMLOWP_ADD_TO_GLOBAL_ACCUMULATORS
582
583        /* end of main loop */
584
585        // Store the global accumulators to the destination matrix
586        // (column-major)
587        // This is the reverse of the steps that we followed at the beginning
588        // when we load the global accumulators from the destination matrix.
589        // The problem is the same: how to convert 4x4 blocks
590        // between column-major and diagonal-major orders.
591        // Like above, we do this by rotating rows, and we achieve that by
592        // tranposing, rotating columns, and transposing again.
593        //
594        // Load and transpose 4x4 blocks of global accumulators
595        // These two steps are done at once by the vld4 instruction.
596        "mov r0, %[global_accumulators]\n"
597        "vld4.32 {d0,d2,d4,d6}, [r0]!\n"
598        "vld4.32 {d1,d3,d5,d7}, [r0]!\n"
599        "vld4.32 {d8,d10,d12,d14}, [r0]!\n"
600        "vld4.32 {d9,d11,d13,d15}, [r0]!\n"
601        "vld4.32 {d16,d18,d20,d22}, [r0]!\n"
602        "vld4.32 {d17,d19,d21,d23}, [r0]!\n"
603        // Rotate the rows of each 4x4 block
604        "vext.32 q1, q1, q1, #3\n"
605        "vext.32 q2, q2, q2, #2\n"
606        "vext.32 q3, q3, q3, #1\n"
607        "vext.32 q5, q5, q5, #3\n"
608        "vext.32 q6, q6, q6, #2\n"
609        "vext.32 q7, q7, q7, #1\n"
610        "vext.32 q9, q9, q9, #3\n"
611        "vext.32 q10, q10, q10, #2\n"
612        "vext.32 q11, q11, q11, #1\n"
613        // Transpose again each 4x4 block
614        "vtrn.32 q0, q1\n"
615        "vtrn.32 q2, q3\n"
616        "vswp d1, d4\n"
617        "vswp d3, d6\n"
618        "vtrn.32 q4, q5\n"
619        "vtrn.32 q6, q7\n"
620        "vswp d9, d12\n"
621        "vswp d11, d14\n"
622        "vtrn.32 q8, q9\n"
623        "vtrn.32 q10, q11\n"
624        "vswp d17, d20\n"
625        "vswp d19, d22\n"
626        // Store into the column-major destination matrix
627        "mov r1, %[dst_ptr]\n"
628        "mov r0, %[dst_col_stride]\n"
629        "sub r0, #32\n"
630        "vst1.32 {d0,d1}, [r1]!\n"
631        "vst1.32 {d8,d9}, [r1]!\n"
632        "vst1.32 {d16,d17}, [r1], r0\n"
633        "vst1.32 {d2,d3}, [r1]!\n"
634        "vst1.32 {d10,d11}, [r1]!\n"
635        "vst1.32 {d18,d19}, [r1], r0\n"
636        "vst1.32 {d4,d5}, [r1]!\n"
637        "vst1.32 {d12,d13}, [r1]!\n"
638        "vst1.32 {d20,d21}, [r1], r0\n"
639        "vst1.32 {d6,d7}, [r1]!\n"
640        "vst1.32 {d14,d15}, [r1]!\n"
641        "vst1.32 {d22,d23}, [r1], r0\n"
642        :  // outputs
643        [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
644        [dst_ptr] "+r"(dst_ptr),
645        [run_depth] "+r"(run_depth)
646        :  // inputs
647        [start_depth] "r"(start_depth), [dst_col_stride] "r"(dst_col_stride),
648        [global_accumulators] "r"(&global_accumulators[0])
649        :  // clobbers
650        "cc", "memory", "r0", "r1",
651        // note: someone on internet says that quad registers are
652        // unsupported in the clobber list!
653        "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "d10",
654        "d11", "d12", "d13", "d14", "d15", "d16", "d17", "d18", "d19", "d20",
655        "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29", "d30",
656        "d31");
657#undef GEMMLOWP_LOOP_NEON_32_KERNEL_12X4_DEPTH2_ASSUMING_12BIT_PRODUCTS
658#undef GEMMLOWP_LOAD_GLOBAL_ACCUMULATORS_NEON_32_KERNEL_12X4_DEPTH2_12BIT
659#undef GEMMLOWP_LABEL_32
660#undef GEMMLOWP_LABEL_24
661#undef GEMMLOWP_LABEL_16
662#undef GEMMLOWP_LABEL_8
663#undef GEMMLOWP_LABEL_2
664  }
665};
666
667struct NEON_32bit_GEMM_Int8Operands_LhsNonzero : KernelBase {
668  typedef KernelFormat<
669      KernelSideFormatInt8<CellFormat<4, 16, CellOrder::WidthMajor>, 1>,
670      KernelSideFormatInt8<CellFormat<2, 16, CellOrder::WidthMajor>, 1> >
671      Format;
672  const char* Name() const override {
673    return "NEON, 4x2, depth 16, accumulating two within signed int16";
674  }
675
676  // TODO(benoitjacob): reorder function arguments so dst comes last
677  void Run(std::int32_t* dst_ptr, std::size_t dst_row_stride,
678           std::size_t dst_col_stride, const std::uint8_t* lhs_ptr,
679           const std::uint8_t* rhs_ptr, std::size_t start_depth,
680           std::size_t run_depth) const override {
681#define GEMMLOWP_LABEL_AFTER_LOOP "1"
682#define GEMMLOWP_LABEL_LOOP "2"
683#define GEMMLOWP_LABEL_ACCUMULATE_EXISTING_DST_VALUES "3"
684#define GEMMLOWP_LABEL_STORE "4"
685    asm volatile(
686        // Multiply dst_col_stride by 4 == sizeof(int32) to use
687        // it as a byte offset below.
688        "lsl %[dst_col_stride], %[dst_col_stride], #2\n"
689
690        // Overview of register layout:
691        //
692        // A 2x16 block of Rhs is stored in 8 bit in d0--d3.
693        // A 4x16 block of Lhs is stored in 8 bit in d4--d7. That is only
694        // half of the register space required, so we loop over these registers
695        // twice. Only half of it, a 2x16 block, is stored in d4--d7 at
696        // any given time.
697        //
698        // A 4x2 block of accumulators is stored in q8--q15 (as 4x32 bit
699        // components which need to be horizontally-added at the end)
700        //
701        // The Lhs vectors are multiplied by the Rhs vectors with a widening
702        // multiply over the 8 first levels of depth, producing int16x8
703        // vectors of products for each position in the accumulator matrix.
704        // Here comes the special trick: since the operands are signed int8,
705        // their range being [ -2^7 , 2^7 ), their products are in range
706        // [ -2^14 , 2^14 - 1 ), meaning that we can add two such values
707        // without any risk of overflowing int16.
708        // We thus proceed with the 8 next levels of depth, multiplying
709        // again Lhs by Rhs, accumulating into this existing int16x8 vector.
710        //
711        // Only then, having processed 16 levels of depth, do we need to
712        // horizontally add these int16x8 accumulators into the final
713        // int32x4 accumulators.
714        //
715        // As we do not have enough registers to store all 16 int16x8
716        // temporary-16bit-accumulators, we have them cycle through q4--q7.
717        //
718        //
719        // Register layout (ignoring the q4--q7 temporary 16bit accumulators):
720        //
721        //                               +----+----+
722        //                               | d0 | d2 |
723        //                               | .  | .  |
724        //                               | .  | .  |
725        //                               | .  | .  |
726        //                       Rhs     +----+----+
727        //                               | d1 | d3 |
728        //                               | .  | .  |
729        //                               | .  | .  |
730        //                               | .  | .  |
731        //                               +----+----+
732        //
733        //                               |    |    |
734        //
735        //    Lhs                        |    |    |
736        //
737        //  +--------+--------+ - - - -  +----+----+
738        //  | d4 ... | d5 ... |          | q8 | q9 |
739        //  | d6 ... | d7 ... |          | q10| q11|
740        //  | d4 ... | d5 ... |          | q12| q13|
741        //  | d6 ... | d7 ... |          | q14| q15|
742        //  +--------+--------+ - - - -  +----+----+
743        //
744        //                               Accumulator
745        //
746
747        // Clear accumulators, and, interleaved with it,
748        // initial loads of the first loop iteration,
749        // taken out of the loop so that in the loop itself we have
750        // optimal streaming of data from memory.
751        "vldr d0, [%[rhs_ptr], #0]\n"
752        "vmov.i32 q8, #0\n"
753        "vldr d4, [%[lhs_ptr], #0]\n"
754        "vmov.i32 q9, #0\n"
755        "vldr d2, [%[rhs_ptr], #16]\n"
756        "vmov.i32 q10, q8\n"
757        "vldr d6, [%[lhs_ptr], #16]\n"
758        "vmov.i32 q11, q8\n"
759        "vldr d1, [%[rhs_ptr], #8]\n"
760        "vmov.i32 q12, q8\n"
761        "vldr d5, [%[lhs_ptr], #8]\n"
762        "vmov.i32 q13, q8\n"
763        "vldr d3, [%[rhs_ptr], #24]\n"
764        "vmov.i32 q14, q8\n"
765        "vldr d7, [%[lhs_ptr], #24]\n"
766        "vmov.i32 q15, q8\n"
767
768        // General loop.
769        GEMMLOWP_LABEL_LOOP
770        ":\n"
771
772        // Multiply 8 first levels of depth.
773        "vmull.s8    q4,  d0,  d4\n"
774        "add %[rhs_ptr], %[rhs_ptr], #32\n"
775        "vmull.s8    q5,  d2,  d4\n"
776        "vldr d4, [%[lhs_ptr], #32]\n"
777        "vmull.s8    q6,  d0,  d6\n"
778        "vmull.s8    q7,  d2,  d6\n"
779        "vldr d6, [%[lhs_ptr], #48]\n"
780
781        // Multiply-accumulate second-half, again into the same
782        // 16bit local accumulator registers. This is where we
783        // take advantage of having int8 instead of uint8 and therefore
784        // being able to accumulate two products into int16.
785        "vmlal.s8    q4,  d1,  d5\n"
786        "vmlal.s8    q5,  d3,  d5\n"
787        "vldr d5, [%[lhs_ptr], #40]\n"
788        "vmlal.s8    q6,  d1,  d7\n"
789        "vmlal.s8    q7,  d3,  d7\n"
790        "vldr d7, [%[lhs_ptr], #56]\n"
791
792        // Add pairwise, accumulate into 32-bit accumulators.
793        "vpadal.s16   q8,  q4\n"
794        "add %[lhs_ptr], %[lhs_ptr], #64\n"
795        "vpadal.s16   q9,  q5\n"
796        "subs %[run_depth], %[run_depth], #16\n"
797        "vpadal.s16   q10, q6\n"
798        "vpadal.s16   q11, q7\n"
799
800        "beq " GEMMLOWP_LABEL_AFTER_LOOP
801        "f\n"
802
803        // Multiply first half.
804        "vmull.s8    q4,  d0,  d4\n"
805        "vmull.s8    q5,  d2,  d4\n"
806        "vldr d4, [%[lhs_ptr], #0]\n"
807        "vmull.s8    q6,  d0,  d6\n"
808        "vldr d0, [%[rhs_ptr], #0]\n"
809        "vmull.s8    q7,  d2,  d6\n"
810        "vldr d2, [%[rhs_ptr], #16]\n"
811
812        // Multiply-accumulate second-half, again into the same
813        // 16bit local accumulator registers. This is where we
814        // take advantage of having int8 instead of uint8 and therefore
815        // being able to accumulate two products into int16.
816        "vmlal.s8    q4,  d1,  d5\n"
817        "vldr d6, [%[lhs_ptr], #16]\n"
818        "vmlal.s8    q5,  d3,  d5\n"
819        "vldr d5, [%[lhs_ptr], #8]\n"
820        "vmlal.s8    q6,  d1,  d7\n"
821        "vldr d1, [%[rhs_ptr], #8]\n"
822        "vmlal.s8    q7,  d3,  d7\n"
823        "vldr d3, [%[rhs_ptr], #24]\n"
824
825        // Add pairwise, accumulate into 32-bit accumulators.
826        "vpadal.s16   q12, q4\n"
827        "vldr d7, [%[lhs_ptr], #24]\n"
828        "vpadal.s16   q13, q5\n"
829        "vpadal.s16   q14, q6\n"
830        "vpadal.s16   q15, q7\n"
831
832        "b " GEMMLOWP_LABEL_LOOP "b\n"
833
834        GEMMLOWP_LABEL_AFTER_LOOP
835        ":\n"
836
837        // Multiply first half.
838        "vmull.s8    q4,  d0,  d4\n"
839        "vmull.s8    q5,  d2,  d4\n"
840        "vmull.s8    q6,  d0,  d6\n"
841        "vmull.s8    q7,  d2,  d6\n"
842
843        // Multiply-accumulate second-half, again into the same
844        // 16bit local accumulator registers. This is where we
845        // take advantage of having int8 instead of uint8 and therefore
846        // being able to accumulate two products into int16.
847        "vmlal.s8    q4,  d1,  d5\n"
848        "vmlal.s8    q5,  d3,  d5\n"
849        "vmlal.s8    q6,  d1,  d7\n"
850        "vmlal.s8    q7,  d3,  d7\n"
851
852        // Add pairwise, accumulate into 32-bit accumulators.
853        "vpadal.s16   q12, q4\n"
854        "vpadal.s16   q13, q5\n"
855        "vpadal.s16   q14, q6\n"
856        "vpadal.s16   q15, q7\n"
857        "cmp %[start_depth], #0\n"
858
859        // Reduce 32bit accumulators horizontally.
860        "vpadd.s32 d0, d16, d17\n"
861        "vpadd.s32 d1, d18, d19\n"
862        "vpadd.s32 d2, d20, d21\n"
863        "vpadd.s32 d3, d22, d23\n"
864        "vpadd.s32 d4, d24, d25\n"
865        "vpadd.s32 d5, d26, d27\n"
866        "vpadd.s32 d6, d28, d29\n"
867        "vpadd.s32 d7, d30, d31\n"
868
869        "bne " GEMMLOWP_LABEL_ACCUMULATE_EXISTING_DST_VALUES
870        "f\n"
871
872        // Reduce 32bit accumulators horizontally, second pass
873        // (each pass adds pairwise. we need to add 4-wise).
874        "vpadd.s32 d8, d0, d2\n"
875        "vpadd.s32 d9, d4, d6\n"
876        "vpadd.s32 d10, d1, d3\n"
877        "vpadd.s32 d11, d5, d7\n"
878
879        "b " GEMMLOWP_LABEL_STORE "f\n"
880
881        GEMMLOWP_LABEL_ACCUMULATE_EXISTING_DST_VALUES
882        ":\n"
883
884        // Reduce 32bit accumulators horizontally, second pass
885        // (each pass adds pairwise. we need to add 4-wise),
886        // and load destination values from memory.
887        "mov r0, %[dst_ptr]\n"
888        "vld1.32 {d16, d17}, [r0], %[dst_col_stride]\n"
889        "vpadd.s32 d8, d0, d2\n"
890        "vpadd.s32 d9, d4, d6\n"
891        "vld1.32 {d18, d19}, [r0]\n"
892        "vpadd.s32 d10, d1, d3\n"
893        "vpadd.s32 d11, d5, d7\n"
894
895        // Add horizontally-reduced accumulators into
896        // the values loaded from memory
897        "vadd.s32 q4, q8, q4\n"
898        "vadd.s32 q5, q9, q5\n"
899
900        GEMMLOWP_LABEL_STORE
901        ":\n"
902        // Store back into memory
903        "mov r0, %[dst_ptr]\n"
904        "vst1.32 {d8, d9}, [r0], %[dst_col_stride]\n"
905        "vst1.32 {d10, d11}, [r0]\n"
906        :  // outputs
907        [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
908        [dst_ptr] "+r"(dst_ptr), [run_depth] "+r"(run_depth)
909        :  // inputs
910        [start_depth] "r"(start_depth),
911        [dst_col_stride] "r"(dst_col_stride)
912        :  // clobbers
913        "cc", "memory", "r0", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
914        "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", "d16", "d17",
915        "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27",
916        "d28", "d29", "d30", "d31");
917#undef GEMMLOWP_LABEL_LOOP
918#undef GEMMLOWP_LABEL_AFTER_LOOP
919#undef GEMMLOWP_LABEL_ACCUMULATE_EXISTING_DST_VALUES
920#undef GEMMLOWP_LABEL_STORE
921  }
922};
923
924#endif  // GEMMLOWP_NEON_32
925
926// The kernels here are specifically arm 64bit assembly, not arm 32bit.
927#ifdef GEMMLOWP_NEON_64
928
929struct NEON_64bit_GEMM_Int8Operands_LhsNonzero : KernelBase {
930  typedef KernelFormat<
931      KernelSideFormatInt8<CellFormat<4, 16, CellOrder::WidthMajor>, 1>,
932      KernelSideFormatInt8<CellFormat<4, 16, CellOrder::WidthMajor>, 1> >
933      Format;
934  const char* Name() const override {
935    return "NEON, 4x4, depth 16, accumulating two within signed int16";
936  }
937
938  // TODO(benoitjacob): reorder function arguments so dst comes last
939  void Run(std::int32_t* dst_ptr, std::size_t dst_row_stride,
940           std::size_t dst_col_stride, const std::uint8_t* lhs_ptr,
941           const std::uint8_t* rhs_ptr, std::size_t start_depth,
942           std::size_t run_depth) const override {
943#define GEMMLOWP_LABEL_AFTER_LOOP_LAST16 "1"
944#define GEMMLOWP_LABEL_LOOP "2"
945#define GEMMLOWP_LABEL_ACCUMULATE_EXISTING_DST_VALUES "3"
946#define GEMMLOWP_LABEL_STORE "4"
947    asm volatile(
948        // Clear accumulators, and, interleaved with it,
949        // initial loads of the first loop iteration,
950        // taken out of the loop so that in the loop itself we have
951        // optimal streaming of data from memory.
952        "ld1 {v0.16b}, [%[rhs_ptr]], #16\n"
953        "dup v16.4s, wzr\n"
954        "ld1 {v4.16b}, [%[lhs_ptr]], #16\n"
955        "dup v17.4s, wzr\n"
956        "ld1 {v1.16b}, [%[rhs_ptr]], #16\n"
957        "dup v18.4s, wzr\n"
958        "ld1 {v5.16b}, [%[lhs_ptr]], #16\n"
959        "dup v19.4s, wzr\n"
960        "ld1 {v2.16b}, [%[rhs_ptr]], #16\n"
961        "dup v20.4s, wzr\n"
962        "ld1 {v3.16b}, [%[rhs_ptr]], #16\n"
963        "dup v21.4s, wzr\n"
964        "ld1 {v6.16b}, [%[lhs_ptr]], #16\n"
965        "dup v22.4s, wzr\n"
966        "ld1 {v7.16b}, [%[lhs_ptr]], #16\n"
967        "dup v23.4s, wzr\n"
968        "dup v24.4s, wzr\n"
969        "dup v25.4s, wzr\n"
970        "dup v26.4s, wzr\n"
971        "dup v27.4s, wzr\n"
972        "dup v28.4s, wzr\n"
973        "dup v29.4s, wzr\n"
974        "dup v30.4s, wzr\n"
975        "dup v31.4s, wzr\n"
976
977        // Multiply dst_col_stride by 4 == sizeof(int32) to use
978        // it as a byte offset below.
979        "lsl %[dst_col_stride], %[dst_col_stride], #2\n"
980
981        // Initial arithmetic of the first loop iteration,
982        // taken out of the loop so that in the loop itself we have
983        // optimal streaming of data from memory.
984        "smull    v8.8h,  v0.8b,  v4.8b\n"
985        "smull    v9.8h,  v1.8b,  v4.8b\n"
986        "smull    v10.8h,  v2.8b,  v4.8b\n"
987        "smull    v11.8h,  v3.8b,  v4.8b\n"
988        "smull    v12.8h,  v0.8b,  v5.8b\n"
989        "smull    v13.8h,  v1.8b,  v5.8b\n"
990        "smull    v14.8h,  v2.8b,  v5.8b\n"
991        "smull    v15.8h,  v3.8b,  v5.8b\n"
992
993        // Multiply-accumulate second-half, again into the same
994        // 16bit local accumulator registers. This is where we
995        // take advantage of having int8 instead of uint8 and therefore
996        // being able to accumulate two products into int16.
997        "smlal2   v8.8h,  v0.16b,  v4.16b\n"
998        "smlal2   v9.8h,  v1.16b,  v4.16b\n"
999        "smlal2   v10.8h,  v2.16b,  v4.16b\n"
1000        "smlal2   v11.8h,  v3.16b,  v4.16b\n"
1001        "smlal2   v12.8h,  v0.16b,  v5.16b\n"
1002        "smlal2   v13.8h,  v1.16b,  v5.16b\n"
1003        "smlal2   v14.8h,  v2.16b,  v5.16b\n"
1004        "smlal2   v15.8h,  v3.16b,  v5.16b\n"
1005
1006        "subs %[run_depth], %[run_depth], #16\n"
1007
1008        // If the loop depth is only 16, then we can skip the general loop
1009        // and go straight to the final part of the code.
1010        "beq " GEMMLOWP_LABEL_AFTER_LOOP_LAST16 "f\n"
1011
1012        // General loop.
1013        GEMMLOWP_LABEL_LOOP
1014        ":\n"
1015
1016        // Overview of register layout:
1017        //
1018        // A 4x16 block of Rhs is stored in 8 bit in v0--v3.
1019        // A 4x16 block of Lhs is stored in 8 bit in v4--v7.
1020        //
1021        // A 4x4 block of accumulators is stored in v16-v31 (as 4x32 bit
1022        // components which need to be horizontally-added at the end)
1023        //
1024        // The Lhs vectors are multiplied by the Rhs vectors with a widening
1025        // multiply over the 8 first levels of depth, producing int16x8
1026        // vectors of products for each position in the accumulator matrix.
1027        // Here comes the special trick: since the operands are signed int8,
1028        // their range being [ -2^7 , 2^7 ), their products are in range
1029        // [ -2^14 , 2^14 - 1 ), meaning that we can add two such values
1030        // without any risk of overflowing int16.
1031        // We thus proceed with the 8 next levels of depth, multiplying
1032        // again Lhs by Rhs, accumulating into this existing int16x8 vector.
1033        //
1034        // Only then, having processed 16 levels of depth, do we need to
1035        // horizontally add these int16x8 accumulators into the final
1036        // int32x4 accumulators.
1037        //
1038        // As we do not have enough registers to store all 16 int16x8
1039        // temporary-16bit-accumulators, we have them cycle through v8--v15.
1040        //
1041        //
1042        // Register layout (ignoring the v8--v15 temporary 16bit accumulators):
1043        //
1044        //                               +--------+--------+--------+--------+
1045        //                               |v0.b[0] |v1.b[0] |v2.b[0] |v3.b[0] |
1046        //                          Rhs  +--------+--------+--------+--------+
1047        //                               |  ...   |  ...   |  ...   |  ...   |
1048        //                               +--------+--------+--------+--------|
1049        //                               |v0.b[15]|v1.b[15]|v2.b[15]|v3.b[15]|
1050        //                               +--------+--------+--------+--------+
1051        //
1052        //                               |        |        |        |        |
1053        //
1054        //    Lhs                        |        |        |        |        |
1055        //
1056        //  +-------+-----+--------+ - - +--------+--------+--------+--------+
1057        //  |v4.b[0]| ... |v4.b[15]|     | v16.4s | v17.4s | v18.4s | v19.4s |
1058        //  |v5.b[0]| ... |v5.b[15]|     | v20.4s | v21.4s | v22.4s | v23.4s |
1059        //  |v6.b[0]| ... |v6.b[15]|     | v24.4s | v25.4s | v26.4s | v27.4s |
1060        //  |v7.b[0]| ... |v7.b[15]|     | v28.4s | v29.4s | v30.4s | v31.4s |
1061        //  +-------+--------------+ - - +--------+--------+--------+--------+
1062        //
1063        //                                                Accumulator
1064        //
1065
1066        // Some multiplications and 16-bit accumulation were already done above,
1067        // so we start right away in the middle.
1068        "sadalp  v16.4s, v8.8h\n"
1069        "ld1 {v4.16b}, [%[lhs_ptr]], #16\n"
1070        "smull    v8.8h,  v0.8b,  v6.8b\n"
1071        "sadalp  v17.4s, v9.8h\n"
1072        "ld1 {v5.16b}, [%[lhs_ptr]], #16\n"
1073        "smull    v9.8h,  v1.8b,  v6.8b\n"
1074        "sadalp  v18.4s, v10.8h\n"
1075        "smull    v10.8h,  v2.8b,  v6.8b\n"
1076        "sadalp  v19.4s, v11.8h\n"
1077        "smull    v11.8h,  v3.8b,  v6.8b\n"
1078        "sadalp  v20.4s, v12.8h\n"
1079        "smull    v12.8h,  v0.8b,  v7.8b\n"
1080        "sadalp  v21.4s, v13.8h\n"
1081        "smull    v13.8h,  v1.8b,  v7.8b\n"
1082        "sadalp  v22.4s, v14.8h\n"
1083        "smull    v14.8h,  v2.8b,  v7.8b\n"
1084        "sadalp  v23.4s, v15.8h\n"
1085        "smull    v15.8h,  v3.8b,  v7.8b\n"
1086
1087        // Multiply-accumulate second-half, again into the same
1088        // 16bit local accumulator registers. This is where we
1089        // take advantage of having int8 instead of uint8 and therefore
1090        // being able to accumulate two products into int16.
1091        "smlal2   v8.8h,  v0.16b,  v6.16b\n"
1092        "smlal2   v9.8h,  v1.16b,  v6.16b\n"
1093        "smlal2   v10.8h,  v2.16b,  v6.16b\n"
1094        "smlal2   v11.8h,  v3.16b,  v6.16b\n"
1095
1096        "ld1 {v6.16b}, [%[lhs_ptr]], #16\n"
1097
1098        "smlal2   v12.8h,  v0.16b,  v7.16b\n"
1099        "ld1 {v0.16b}, [%[rhs_ptr]], #16\n"
1100        "smlal2   v13.8h,  v1.16b,  v7.16b\n"
1101        "ld1 {v1.16b}, [%[rhs_ptr]], #16\n"
1102        "smlal2   v14.8h,  v2.16b,  v7.16b\n"
1103        "ld1 {v2.16b}, [%[rhs_ptr]], #16\n"
1104        "smlal2   v15.8h,  v3.16b,  v7.16b\n"
1105        "ld1 {v3.16b}, [%[rhs_ptr]], #16\n"
1106
1107        "sadalp  v24.4s, v8.8h\n"
1108        "smull    v8.8h,  v0.8b,  v4.8b\n"
1109        "sadalp  v25.4s, v9.8h\n"
1110        "ld1 {v7.16b}, [%[lhs_ptr]], #16\n"
1111        "smull    v9.8h,  v1.8b,  v4.8b\n"
1112        "sadalp  v26.4s, v10.8h\n"
1113        "smull    v10.8h,  v2.8b,  v4.8b\n"
1114        "sadalp  v27.4s, v11.8h\n"
1115        "smull    v11.8h,  v3.8b,  v4.8b\n"
1116        "sadalp  v28.4s, v12.8h\n"
1117        "smull    v12.8h,  v0.8b,  v5.8b\n"
1118        "sadalp  v29.4s, v13.8h\n"
1119        "smull    v13.8h,  v1.8b,  v5.8b\n"
1120        "sadalp  v30.4s, v14.8h\n"
1121        "smull    v14.8h,  v2.8b,  v5.8b\n"
1122        "sadalp  v31.4s, v15.8h\n"
1123        "smull    v15.8h,  v3.8b,  v5.8b\n"
1124
1125        // Multiply-accumulate second-half, again into the same
1126        // 16bit local accumulator registers. This is where we
1127        // take advantage of having int8 instead of uint8 and therefore
1128        // being able to accumulate two products into int16.
1129        "smlal2   v8.8h,  v0.16b,  v4.16b\n"
1130        "smlal2   v9.8h,  v1.16b,  v4.16b\n"
1131        "smlal2   v10.8h,  v2.16b,  v4.16b\n"
1132        "smlal2   v11.8h,  v3.16b,  v4.16b\n"
1133
1134        // Loop. Decrement loop index (depth) by 16, since we just handled
1135        // 16 levels of depth.  Do this subs a bit before the end of the loop
1136        // for better dispatch on A57.
1137        "subs %[run_depth], %[run_depth], #16\n"
1138
1139        "smlal2   v12.8h,  v0.16b,  v5.16b\n"
1140        "smlal2   v13.8h,  v1.16b,  v5.16b\n"
1141        "smlal2   v14.8h,  v2.16b,  v5.16b\n"
1142        "smlal2   v15.8h,  v3.16b,  v5.16b\n"
1143
1144        "bne " GEMMLOWP_LABEL_LOOP "b\n"
1145
1146        // Final code for the last 16 levels of depth.
1147        // There is nothing to load anymore, only some arithmetic to finish.
1148        GEMMLOWP_LABEL_AFTER_LOOP_LAST16
1149        ":\n"
1150
1151        // Some multiplications and 16-bit accumulation were already done above,
1152        // so we start right away in the middle.
1153        "sadalp  v16.4s, v8.8h\n"
1154        "smull    v8.8h,  v0.8b,  v6.8b\n"
1155        "sadalp  v17.4s, v9.8h\n"
1156        "smull    v9.8h,  v1.8b,  v6.8b\n"
1157        "sadalp  v18.4s, v10.8h\n"
1158        "smull    v10.8h,  v2.8b,  v6.8b\n"
1159        "sadalp  v19.4s, v11.8h\n"
1160        "smull    v11.8h,  v3.8b,  v6.8b\n"
1161        "sadalp  v20.4s, v12.8h\n"
1162        "smull    v12.8h,  v0.8b,  v7.8b\n"
1163        "sadalp  v21.4s, v13.8h\n"
1164        "smull    v13.8h,  v1.8b,  v7.8b\n"
1165        "sadalp  v22.4s, v14.8h\n"
1166        "smull    v14.8h,  v2.8b,  v7.8b\n"
1167        "sadalp  v23.4s, v15.8h\n"
1168        "smull    v15.8h,  v3.8b,  v7.8b\n"
1169
1170        // Multiply-accumulate second-half, again into the same
1171        // 16bit local accumulator registers. This is where we
1172        // take advantage of having int8 instead of uint8 and therefore
1173        // being able to accumulate two products into int16.
1174        "smlal2   v8.8h,  v0.16b,  v6.16b\n"
1175        "smlal2   v9.8h,  v1.16b,  v6.16b\n"
1176        "smlal2   v10.8h,  v2.16b,  v6.16b\n"
1177        "smlal2   v11.8h,  v3.16b,  v6.16b\n"
1178        "smlal2   v12.8h,  v0.16b,  v7.16b\n"
1179        "smlal2   v13.8h,  v1.16b,  v7.16b\n"
1180        "smlal2   v14.8h,  v2.16b,  v7.16b\n"
1181        "smlal2   v15.8h,  v3.16b,  v7.16b\n"
1182
1183        "sadalp  v24.4s, v8.8h\n"
1184        "sadalp  v25.4s, v9.8h\n"
1185        "sadalp  v26.4s, v10.8h\n"
1186        "sadalp  v27.4s, v11.8h\n"
1187        "sadalp  v28.4s, v12.8h\n"
1188        "sadalp  v29.4s, v13.8h\n"
1189        "sadalp  v30.4s, v14.8h\n"
1190        "sadalp  v31.4s, v15.8h\n"
1191
1192        // Reduce 32bit accumulators horizontally.
1193        "addp v0.4s, v16.4s, v20.4s\n"
1194        "addp v2.4s, v17.4s, v21.4s\n"
1195        "addp v4.4s, v18.4s, v22.4s\n"
1196        "addp v6.4s, v19.4s, v23.4s\n"
1197        "addp v1.4s, v24.4s, v28.4s\n"
1198        "addp v3.4s, v25.4s, v29.4s\n"
1199        "addp v5.4s, v26.4s, v30.4s\n"
1200        "addp v7.4s, v27.4s, v31.4s\n"
1201
1202        "cmp %[start_depth], #0\n"
1203        "bne " GEMMLOWP_LABEL_ACCUMULATE_EXISTING_DST_VALUES
1204        "f\n"
1205
1206        // Reduce 32bit accumulators horizontally, second pass
1207        // (each pass adds pairwise. we need to add 4-wise).
1208        "addp v12.4s, v0.4s, v1.4s\n"
1209        "addp v13.4s, v2.4s, v3.4s\n"
1210        "addp v14.4s, v4.4s, v5.4s\n"
1211        "addp v15.4s, v6.4s, v7.4s\n"
1212
1213        "b " GEMMLOWP_LABEL_STORE "f\n"
1214
1215        GEMMLOWP_LABEL_ACCUMULATE_EXISTING_DST_VALUES
1216        ":\n"
1217
1218        // Reduce 32bit accumulators horizontally, second pass
1219        // (each pass adds pairwise. we need to add 4-wise),
1220        // and load destination values from memory.
1221        "mov x0, %[dst_ptr]\n"
1222        "ld1 {v12.16b}, [x0], %[dst_col_stride]\n"
1223        "addp v8.4s, v0.4s, v1.4s\n"
1224        "ld1 {v13.16b}, [x0], %[dst_col_stride]\n"
1225        "addp v9.4s, v2.4s, v3.4s\n"
1226        "ld1 {v14.16b}, [x0], %[dst_col_stride]\n"
1227        "addp v10.4s, v4.4s, v5.4s\n"
1228        "ld1 {v15.16b}, [x0]\n"
1229        "addp v11.4s, v6.4s, v7.4s\n"
1230
1231        // Add horizontally-reduced accumulators into
1232        // the values loaded from memory
1233        "add v12.4s, v12.4s, v8.4s\n"
1234        "add v13.4s, v13.4s, v9.4s\n"
1235        "add v14.4s, v14.4s, v10.4s\n"
1236        "add v15.4s, v15.4s, v11.4s\n"
1237
1238        GEMMLOWP_LABEL_STORE
1239        ":\n"
1240        // Store back into memory
1241        "mov x0, %[dst_ptr]\n"
1242        "st1 {v12.16b}, [x0], %[dst_col_stride]\n"
1243        "st1 {v13.16b}, [x0], %[dst_col_stride]\n"
1244        "st1 {v14.16b}, [x0], %[dst_col_stride]\n"
1245        "st1 {v15.16b}, [x0]\n"
1246        :  // outputs
1247        [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
1248        [dst_ptr] "+r"(dst_ptr), [run_depth] "+r"(run_depth),
1249        [dst_col_stride] "+r"(dst_col_stride)
1250        :  // inputs
1251        [start_depth] "r"(start_depth)
1252        :  // clobbers
1253        "cc", "memory", "x0", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
1254        "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17",
1255        "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27",
1256        "v28", "v29", "v30", "v31");
1257#undef GEMMLOWP_LABEL_LOOP
1258#undef GEMMLOWP_LABEL_AFTER_LOOP_LAST16
1259#undef GEMMLOWP_LABEL_ACCUMULATE_EXISTING_DST_VALUES
1260#undef GEMMLOWP_LABEL_STORE
1261  }
1262};
1263
1264// Our main GEMM kernel.
1265struct NEON_64_Kernel12x8Depth2 : KernelBase {
1266  typedef KernelFormat<KernelSideFormat<CellFormat<4, 2>, 3>,
1267                       KernelSideFormat<CellFormat<4, 2>, 2> >
1268      Format;
1269
1270  const char* Name() const override { return "NEON, 12x8, depth 2"; }
1271
1272  // TODO(benoitjacob): reorder function arguments so dst comes last
1273  void Run(std::int32_t* dst_ptr, std::size_t dst_row_stride,
1274           std::size_t dst_col_stride, const std::uint8_t* lhs_ptr,
1275           const std::uint8_t* rhs_ptr, std::size_t start_depth,
1276           std::size_t run_depth) const override {
1277    ScopedProfilingLabel label("optimized kernel (NEON 12x8)");
1278// See comments above for why we need local numerical labels in our asm.
1279#define GEMMLOWP_LABEL_CLEAR_ACCUMULATORS "1"
1280#define GEMMLOWP_LABEL_BEFORE_LOOP "2"
1281#define GEMMLOWP_LABEL_LOOP "3"
1282#define GEMMLOWP_LABEL_AFTER_LOOP "4"
1283
1284    assert(dst_row_stride == 1);
1285    asm volatile(
1286        // Load 1 Rhs cell of size 2x8
1287        "ld1 {v5.8b}, [%[rhs_ptr]], #8\n"
1288        "ld1 {v6.8b}, [%[rhs_ptr]], #8\n"
1289
1290        // Load 3 Lhs cells of size 4x2 each
1291        "ld1 {v2.8b}, [%[lhs_ptr]], #8\n"
1292        "ld1 {v3.8b}, [%[lhs_ptr]], #8\n"
1293        "ld1 {v4.8b}, [%[lhs_ptr]], #8\n"
1294
1295        // Multiply dst_col_stride by 4 == sizeof(int32) to use
1296        // it as a byte offset below.
1297        "lsl %[dst_col_stride], %[dst_col_stride], #2\n"
1298
1299        "cmp %[start_depth], #0\n"
1300        "beq " GEMMLOWP_LABEL_CLEAR_ACCUMULATORS
1301        "f\n"
1302
1303        // Load accumulators
1304        "mov x1, %[dst_ptr]\n"
1305        "mov x0, x1\n"
1306        "ld1 {v8.16b}, [x0], #16\n"
1307        "subs %[run_depth], %[run_depth], #2\n"
1308        "ld1 {v16.16b}, [x0], #16\n"
1309        "add x1, x1, %[dst_col_stride]\n"
1310        "ld1 {v24.16b}, [x0]\n"
1311        "mov x0, x1\n"
1312        "ld1 {v9.16b}, [x0], #16\n"
1313        "add x1, x1, %[dst_col_stride]\n"
1314        "ld1 {v17.16b}, [x0], #16\n"
1315        "ld1 {v25.16b}, [x0]\n"
1316        "mov x0, x1\n"
1317        "ld1 {v10.16b}, [x0], #16\n"
1318        "add x1, x1, %[dst_col_stride]\n"
1319        "ld1 {v18.16b}, [x0], #16\n"
1320        "ld1 {v26.16b}, [x0]\n"
1321        "mov x0, x1\n"
1322        "ld1 {v11.16b}, [x0], #16\n"
1323        "add x1, x1, %[dst_col_stride]\n"
1324        "ld1 {v19.16b}, [x0], #16\n"
1325        "ld1 {v27.16b}, [x0]\n"
1326        "mov x0, x1\n"
1327        "ld1 {v12.16b}, [x0], #16\n"
1328        "add x1, x1, %[dst_col_stride]\n"
1329        "ld1 {v20.16b}, [x0], #16\n"
1330        "ld1 {v28.16b}, [x0]\n"
1331        "mov x0, x1\n"
1332        "ld1 {v13.16b}, [x0], #16\n"
1333        "add x1, x1, %[dst_col_stride]\n"
1334        "ld1 {v21.16b}, [x0], #16\n"
1335        "ld1 {v29.16b}, [x0]\n"
1336        "mov x0, x1\n"
1337        "ld1 {v14.16b}, [x0], #16\n"
1338        "add x1, x1, %[dst_col_stride]\n"
1339        "ld1 {v22.16b}, [x0], #16\n"
1340        "ld1 {v30.16b}, [x0]\n"
1341        "mov x0, x1\n"
1342        "ld1 {v15.16b}, [x0], #16\n"
1343        "ld1 {v23.16b}, [x0], #16\n"
1344        "ld1 {v31.16b}, [x0]\n"
1345
1346        "b " GEMMLOWP_LABEL_BEFORE_LOOP "f\n"
1347
1348        GEMMLOWP_LABEL_CLEAR_ACCUMULATORS
1349        ":\n"
1350
1351        // Clear accumulator registers (see layout below)
1352        "dup v8.4s, wzr\n"
1353        "subs %[run_depth], %[run_depth], #2\n"
1354        "dup v9.4s, wzr\n"
1355        "dup v10.4s, wzr\n"
1356        "dup v11.4s, wzr\n"
1357        "dup v12.4s, wzr\n"
1358        "dup v13.4s, wzr\n"
1359        "dup v14.4s, wzr\n"
1360        "dup v15.4s, wzr\n"
1361        "dup v16.4s, wzr\n"
1362        "dup v17.4s, wzr\n"
1363        "dup v18.4s, wzr\n"
1364        "dup v19.4s, wzr\n"
1365        "dup v20.4s, wzr\n"
1366        "dup v21.4s, wzr\n"
1367        "dup v22.4s, wzr\n"
1368        "dup v23.4s, wzr\n"
1369        "dup v24.4s, wzr\n"
1370        "dup v25.4s, wzr\n"
1371        "dup v26.4s, wzr\n"
1372        "dup v27.4s, wzr\n"
1373        "dup v28.4s, wzr\n"
1374        "dup v29.4s, wzr\n"
1375        "dup v30.4s, wzr\n"
1376        "dup v31.4s, wzr\n"
1377
1378        GEMMLOWP_LABEL_BEFORE_LOOP
1379        ":\n"
1380
1381        "beq " GEMMLOWP_LABEL_AFTER_LOOP "f\n"
1382
1383        GEMMLOWP_LABEL_LOOP
1384        ":\n"
1385
1386        // Overview of register layout:
1387        //
1388        // A 2x8 block of 2 2x4 cells of Rhs is stored in 16bit in v0--v1.
1389        // A 12x2 block of 3 4x2 cells Lhs is stored in 16bit in v2--v4.
1390        // A 12x8 block of accumulators is stored in 32bit in v8--v31.
1391        //
1392        //                         +--------+--------+-----+--------+--------+
1393        //                         |v0.h[0] |v0.h[1] | ... |v1.h[2] |v1.h[3] |
1394        //                    Rhs  +--------+--------+-----+--------+--------+
1395        //                         |v0.h[4] |v0.h[5] | ... |v1.h[6] |v1.h[7] |
1396        //                         +--------+--------+-----+--------+--------+
1397        //
1398        //                         |        |        |     |        |        |
1399        //
1400        //    Lhs                  |        |        |     |        |        |
1401        //
1402        //  +-------+-------+ - -  +--------+--------+-----+--------+--------+
1403        //  |v2.h[0]|v2.h[4]|      |v8.s[0] |v9.s[0] | ... |v14.s[0]|v15.s[0]|
1404        //  |v2.h[1]|v2.h[5]|      |v8.s[1] |v9.s[1] | ... |v14.s[1]|v15.s[1]|
1405        //  |v2.h[2]|v2.h[6]|      |v8.s[2] |v9.s[2] | ... |v14.s[2]|v15.s[2]|
1406        //  |v2.h[3]|v2.h[7]|      |v8.s[3] |v9.s[3] | ... |v14.s[3]|v15.s[3]|
1407        //  +-------+-------+ - -  +--------+--------+-----+--------+--------+
1408        //  |v3.h[0]|v3.h[4]|      |v16.s[0]|v17.s[0]| ... |v22.s[0]|v23.s[0]|
1409        //  |v3.h[1]|v3.h[5]|      |v16.s[1]|v17.s[1]| ... |v22.s[1]|v23.s[1]|
1410        //  |v3.h[2]|v3.h[6]|      |v16.s[2]|v17.s[2]| ... |v22.s[2]|v23.s[2]|
1411        //  |v3.h[3]|v3.h[7]|      |v16.s[3]|v17.s[3]| ... |v22.s[3]|v23.s[3]|
1412        //  +-------+-------+ - -  +--------+--------+-----+--------+--------+
1413        //  |v4.h[0]|v4.h[4]|      |v24.s[0]|v25.s[0]| ... |v30.s[0]|v31.s[0]|
1414        //  |v4.h[1]|v4.h[5]|      |v24.s[1]|v25.s[1]| ... |v30.s[1]|v31.s[1]|
1415        //  |v4.h[2]|v4.h[6]|      |v24.s[2]|v25.s[2]| ... |v30.s[2]|v31.s[2]|
1416        //  |v4.h[3]|v4.h[7]|      |v24.s[3]|v25.s[3]| ... |v30.s[3]|v31.s[3]|
1417        //  +-------+-------+ - -  +--------+--------+-----+--------+--------+
1418        //
1419        //                            Accumulator
1420
1421        // Expand Lhs/Rhs cells to 16 bit.
1422        "uxtl v0.8h, v5.8b\n"
1423        "ld1 {v5.8b}, [%[rhs_ptr]], #8\n"
1424        "uxtl v1.8h, v6.8b\n"
1425        "ld1 {v6.8b}, [%[rhs_ptr]], #8\n"
1426        "uxtl v2.8h, v2.8b\n"
1427        "uxtl v3.8h, v3.8b\n"
1428        "uxtl v4.8h, v4.8b\n"
1429
1430        // Multiply-accumulate, top third
1431        "umlal v8.4s, v2.4h, v0.h[0]\n"
1432        "umlal v9.4s, v2.4h, v0.h[1]\n"
1433        "umlal v10.4s, v2.4h, v0.h[2]\n"
1434        "umlal v11.4s, v2.4h, v0.h[3]\n"
1435        "umlal v12.4s, v2.4h, v1.h[0]\n"
1436        "umlal v13.4s, v2.4h, v1.h[1]\n"
1437        "umlal v14.4s, v2.4h, v1.h[2]\n"
1438        "umlal v15.4s, v2.4h, v1.h[3]\n"
1439        "umlal2 v8.4s, v2.8h, v0.h[4]\n"
1440        "umlal2 v9.4s, v2.8h, v0.h[5]\n"
1441        "umlal2 v10.4s, v2.8h, v0.h[6]\n"
1442        "umlal2 v11.4s, v2.8h, v0.h[7]\n"
1443        "umlal2 v12.4s, v2.8h, v1.h[4]\n"
1444        "umlal2 v13.4s, v2.8h, v1.h[5]\n"
1445        "umlal2 v14.4s, v2.8h, v1.h[6]\n"
1446        "umlal2 v15.4s, v2.8h, v1.h[7]\n"
1447        "ld1 {v2.8b}, [%[lhs_ptr]], #8\n"
1448
1449        // Multiply-accumulate, middle third
1450        "umlal v16.4s, v3.4h, v0.h[0]\n"
1451        "umlal v17.4s, v3.4h, v0.h[1]\n"
1452        "umlal v18.4s, v3.4h, v0.h[2]\n"
1453        "umlal v19.4s, v3.4h, v0.h[3]\n"
1454        "umlal v20.4s, v3.4h, v1.h[0]\n"
1455        "umlal v21.4s, v3.4h, v1.h[1]\n"
1456        "umlal v22.4s, v3.4h, v1.h[2]\n"
1457        "umlal v23.4s, v3.4h, v1.h[3]\n"
1458        "umlal2 v16.4s, v3.8h, v0.h[4]\n"
1459        "umlal2 v17.4s, v3.8h, v0.h[5]\n"
1460        "umlal2 v18.4s, v3.8h, v0.h[6]\n"
1461        "umlal2 v19.4s, v3.8h, v0.h[7]\n"
1462        "umlal2 v20.4s, v3.8h, v1.h[4]\n"
1463        "umlal2 v21.4s, v3.8h, v1.h[5]\n"
1464        "umlal2 v22.4s, v3.8h, v1.h[6]\n"
1465        "umlal2 v23.4s, v3.8h, v1.h[7]\n"
1466        "ld1 {v3.8b}, [%[lhs_ptr]], #8\n"
1467
1468        "subs %[run_depth], %[run_depth], #2\n"
1469
1470        // Multiply-accumulate, bottom third
1471        "umlal v24.4s, v4.4h, v0.h[0]\n"
1472        "umlal v25.4s, v4.4h, v0.h[1]\n"
1473        "umlal v26.4s, v4.4h, v0.h[2]\n"
1474        "umlal v27.4s, v4.4h, v0.h[3]\n"
1475        "umlal v28.4s, v4.4h, v1.h[0]\n"
1476        "umlal v29.4s, v4.4h, v1.h[1]\n"
1477        "umlal v30.4s, v4.4h, v1.h[2]\n"
1478        "umlal v31.4s, v4.4h, v1.h[3]\n"
1479        "umlal2 v24.4s, v4.8h, v0.h[4]\n"
1480        "umlal2 v25.4s, v4.8h, v0.h[5]\n"
1481        "umlal2 v26.4s, v4.8h, v0.h[6]\n"
1482        "umlal2 v27.4s, v4.8h, v0.h[7]\n"
1483        "umlal2 v28.4s, v4.8h, v1.h[4]\n"
1484        "umlal2 v29.4s, v4.8h, v1.h[5]\n"
1485        "umlal2 v30.4s, v4.8h, v1.h[6]\n"
1486        "umlal2 v31.4s, v4.8h, v1.h[7]\n"
1487        "ld1 {v4.8b}, [%[lhs_ptr]], #8\n"
1488
1489        "bne " GEMMLOWP_LABEL_LOOP "b\n"
1490
1491        GEMMLOWP_LABEL_AFTER_LOOP
1492        ":\n"
1493
1494        // Expand Lhs/Rhs cells to 16 bit.
1495        "uxtl v0.8h, v5.8b\n"
1496        "uxtl v1.8h, v6.8b\n"
1497        "uxtl v2.8h, v2.8b\n"
1498        "uxtl v3.8h, v3.8b\n"
1499        "uxtl v4.8h, v4.8b\n"
1500
1501        // Multiply-accumulate, level of depth 0
1502        "umlal v8.4s, v2.4h, v0.h[0]\n"
1503        "umlal v9.4s, v2.4h, v0.h[1]\n"
1504        "umlal v10.4s, v2.4h, v0.h[2]\n"
1505        "umlal v11.4s, v2.4h, v0.h[3]\n"
1506        "umlal v12.4s, v2.4h, v1.h[0]\n"
1507        "umlal v13.4s, v2.4h, v1.h[1]\n"
1508        "umlal v14.4s, v2.4h, v1.h[2]\n"
1509        "umlal v15.4s, v2.4h, v1.h[3]\n"
1510        "umlal v16.4s, v3.4h, v0.h[0]\n"
1511        "umlal v17.4s, v3.4h, v0.h[1]\n"
1512        "umlal v18.4s, v3.4h, v0.h[2]\n"
1513        "umlal v19.4s, v3.4h, v0.h[3]\n"
1514        "umlal v20.4s, v3.4h, v1.h[0]\n"
1515        "umlal v21.4s, v3.4h, v1.h[1]\n"
1516        "umlal v22.4s, v3.4h, v1.h[2]\n"
1517        "umlal v23.4s, v3.4h, v1.h[3]\n"
1518        "umlal v24.4s, v4.4h, v0.h[0]\n"
1519        "umlal v25.4s, v4.4h, v0.h[1]\n"
1520        "umlal v26.4s, v4.4h, v0.h[2]\n"
1521        "umlal v27.4s, v4.4h, v0.h[3]\n"
1522        "umlal v28.4s, v4.4h, v1.h[0]\n"
1523        "umlal v29.4s, v4.4h, v1.h[1]\n"
1524        "umlal v30.4s, v4.4h, v1.h[2]\n"
1525        "umlal v31.4s, v4.4h, v1.h[3]\n"
1526
1527        // Multiply-accumulate, level of depth 1
1528        "umlal2 v8.4s, v2.8h, v0.h[4]\n"
1529        "umlal2 v9.4s, v2.8h, v0.h[5]\n"
1530        "umlal2 v10.4s, v2.8h, v0.h[6]\n"
1531        "umlal2 v11.4s, v2.8h, v0.h[7]\n"
1532        "umlal2 v12.4s, v2.8h, v1.h[4]\n"
1533        "umlal2 v13.4s, v2.8h, v1.h[5]\n"
1534        "umlal2 v14.4s, v2.8h, v1.h[6]\n"
1535        "umlal2 v15.4s, v2.8h, v1.h[7]\n"
1536        "umlal2 v16.4s, v3.8h, v0.h[4]\n"
1537        "umlal2 v17.4s, v3.8h, v0.h[5]\n"
1538        "umlal2 v18.4s, v3.8h, v0.h[6]\n"
1539        "umlal2 v19.4s, v3.8h, v0.h[7]\n"
1540        "umlal2 v20.4s, v3.8h, v1.h[4]\n"
1541        "umlal2 v21.4s, v3.8h, v1.h[5]\n"
1542        "umlal2 v22.4s, v3.8h, v1.h[6]\n"
1543        "umlal2 v23.4s, v3.8h, v1.h[7]\n"
1544        "umlal2 v24.4s, v4.8h, v0.h[4]\n"
1545        "umlal2 v25.4s, v4.8h, v0.h[5]\n"
1546        "umlal2 v26.4s, v4.8h, v0.h[6]\n"
1547        "umlal2 v27.4s, v4.8h, v0.h[7]\n"
1548        "umlal2 v28.4s, v4.8h, v1.h[4]\n"
1549        "umlal2 v29.4s, v4.8h, v1.h[5]\n"
1550        "umlal2 v30.4s, v4.8h, v1.h[6]\n"
1551        "umlal2 v31.4s, v4.8h, v1.h[7]\n"
1552
1553        // Store accumulators
1554        "mov x1, %[dst_ptr]\n"
1555        "mov x0, x1\n"
1556        "st1 {v8.16b}, [x0], #16\n"
1557        "subs %[run_depth], %[run_depth], #2\n"
1558        "st1 {v16.16b}, [x0], #16\n"
1559        "add x1, x1, %[dst_col_stride]\n"
1560        "st1 {v24.16b}, [x0]\n"
1561        "mov x0, x1\n"
1562        "st1 {v9.16b}, [x0], #16\n"
1563        "add x1, x1, %[dst_col_stride]\n"
1564        "st1 {v17.16b}, [x0], #16\n"
1565        "st1 {v25.16b}, [x0]\n"
1566        "mov x0, x1\n"
1567        "st1 {v10.16b}, [x0], #16\n"
1568        "add x1, x1, %[dst_col_stride]\n"
1569        "st1 {v18.16b}, [x0], #16\n"
1570        "st1 {v26.16b}, [x0]\n"
1571        "mov x0, x1\n"
1572        "st1 {v11.16b}, [x0], #16\n"
1573        "add x1, x1, %[dst_col_stride]\n"
1574        "st1 {v19.16b}, [x0], #16\n"
1575        "st1 {v27.16b}, [x0]\n"
1576        "mov x0, x1\n"
1577        "st1 {v12.16b}, [x0], #16\n"
1578        "add x1, x1, %[dst_col_stride]\n"
1579        "st1 {v20.16b}, [x0], #16\n"
1580        "st1 {v28.16b}, [x0]\n"
1581        "mov x0, x1\n"
1582        "st1 {v13.16b}, [x0], #16\n"
1583        "add x1, x1, %[dst_col_stride]\n"
1584        "st1 {v21.16b}, [x0], #16\n"
1585        "st1 {v29.16b}, [x0]\n"
1586        "mov x0, x1\n"
1587        "st1 {v14.16b}, [x0], #16\n"
1588        "add x1, x1, %[dst_col_stride]\n"
1589        "st1 {v22.16b}, [x0], #16\n"
1590        "st1 {v30.16b}, [x0]\n"
1591        "mov x0, x1\n"
1592        "st1 {v15.16b}, [x0], #16\n"
1593        "st1 {v23.16b}, [x0], #16\n"
1594        "st1 {v31.16b}, [x0]\n"
1595#undef GEMMLOWP_LABEL_CLEAR_ACCUMULATORS
1596#undef GEMMLOWP_LABEL_BEFORE_LOOP
1597#undef GEMMLOWP_LABEL_LOOP
1598#undef GEMMLOWP_LABEL_AFTER_LOOP
1599        :  // outputs
1600        [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
1601        [dst_ptr] "+r"(dst_ptr),
1602        [run_depth] "+r"(run_depth)
1603        :  // inputs
1604        [start_depth] "r"(start_depth),
1605        [dst_col_stride] "r"(dst_col_stride)
1606        :  // clobbers
1607        "cc", "memory", "x0", "x1", "v0", "v1", "v2", "v3", "v4", "v5", "v6",
1608        "v7", "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", "v16",
1609        "v17", "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26",
1610        "v27", "v28", "v29", "v30", "v31");
1611  }
1612};
1613
1614#endif  // GEMMLOWP_NEON_64
1615
1616}  // namespace gemmlowp
1617
1618#endif  // GEMMLOWP_INTERNAL_KERNEL_NEON_H_
1619