• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
28 namespace 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.
34 struct NEON_32_Kernel12x4Depth2 : KernelBase {
35   typedef KernelFormat<KernelSideFormat<CellFormat<4, 2>, 3>,
36                        KernelSideFormat<CellFormat<4, 2>, 1> >
37       Format;
38 
NameNEON_32_Kernel12x4Depth239   const char* Name() const override { return "NEON, 12x4, depth 2"; }
40 
41   // TODO(benoitjacob): reorder function arguments so dst comes last
RunNEON_32_Kernel12x4Depth242   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 
293 struct 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 
NameNEON_32_Kernel12x4Depth2Assuming12BitProducts299   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
RunNEON_32_Kernel12x4Depth2Assuming12BitProducts304   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 
667 struct 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;
NameNEON_32bit_GEMM_Int8Operands_LhsNonzero672   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
RunNEON_32bit_GEMM_Int8Operands_LhsNonzero677   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 
929 struct 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;
NameNEON_64bit_GEMM_Int8Operands_LhsNonzero934   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
RunNEON_64bit_GEMM_Int8Operands_LhsNonzero939   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 
1265 // Our main GEMM kernel.
1266 struct NEON_64_Kernel12x8Depth2 : KernelBase {
1267   typedef KernelFormat<KernelSideFormat<CellFormat<4, 2>, 3>,
1268                        KernelSideFormat<CellFormat<4, 2>, 2> >
1269       Format;
1270 
NameNEON_64_Kernel12x8Depth21271   const char* Name() const override { return "NEON, 12x8, depth 2"; }
1272 
1273   // TODO(benoitjacob): reorder function arguments so dst comes last
RunNEON_64_Kernel12x8Depth21274   void Run(std::int32_t* dst_ptr, std::size_t dst_row_stride,
1275            std::size_t dst_col_stride, const std::uint8_t* lhs_ptr,
1276            const std::uint8_t* rhs_ptr, std::size_t start_depth,
1277            std::size_t run_depth) const override {
1278     ScopedProfilingLabel label("optimized kernel (NEON 12x8)");
1279 // See comments above for why we need local numerical labels in our asm.
1280 #define GEMMLOWP_LABEL_CLEAR_ACCUMULATORS "1"
1281 #define GEMMLOWP_LABEL_BEFORE_LOOP "2"
1282 #define GEMMLOWP_LABEL_LOOP "3"
1283 #define GEMMLOWP_LABEL_AFTER_LOOP "4"
1284 
1285     assert(dst_row_stride == 1);
1286     asm volatile(
1287         // Load 1 Rhs cell of size 2x8
1288         "ld1 {v5.8b}, [%[rhs_ptr]], #8\n"
1289         "ld1 {v6.8b}, [%[rhs_ptr]], #8\n"
1290 
1291         // Load 3 Lhs cells of size 4x2 each
1292         "ld1 {v2.8b}, [%[lhs_ptr]], #8\n"
1293         "ld1 {v3.8b}, [%[lhs_ptr]], #8\n"
1294         "ld1 {v4.8b}, [%[lhs_ptr]], #8\n"
1295 
1296         // Multiply dst_col_stride by 4 == sizeof(int32) to use
1297         // it as a byte offset below.
1298         "lsl %[dst_col_stride], %[dst_col_stride], #2\n"
1299 
1300         "cmp %[start_depth], #0\n"
1301         "beq " GEMMLOWP_LABEL_CLEAR_ACCUMULATORS
1302         "f\n"
1303 
1304         // Load accumulators
1305         "mov x1, %[dst_ptr]\n"
1306         "mov x0, x1\n"
1307         "ld1 {v8.16b}, [x0], #16\n"
1308         "subs %[run_depth], %[run_depth], #2\n"
1309         "ld1 {v16.16b}, [x0], #16\n"
1310         "add x1, x1, %[dst_col_stride]\n"
1311         "ld1 {v24.16b}, [x0]\n"
1312         "mov x0, x1\n"
1313         "ld1 {v9.16b}, [x0], #16\n"
1314         "add x1, x1, %[dst_col_stride]\n"
1315         "ld1 {v17.16b}, [x0], #16\n"
1316         "ld1 {v25.16b}, [x0]\n"
1317         "mov x0, x1\n"
1318         "ld1 {v10.16b}, [x0], #16\n"
1319         "add x1, x1, %[dst_col_stride]\n"
1320         "ld1 {v18.16b}, [x0], #16\n"
1321         "ld1 {v26.16b}, [x0]\n"
1322         "mov x0, x1\n"
1323         "ld1 {v11.16b}, [x0], #16\n"
1324         "add x1, x1, %[dst_col_stride]\n"
1325         "ld1 {v19.16b}, [x0], #16\n"
1326         "ld1 {v27.16b}, [x0]\n"
1327         "mov x0, x1\n"
1328         "ld1 {v12.16b}, [x0], #16\n"
1329         "add x1, x1, %[dst_col_stride]\n"
1330         "ld1 {v20.16b}, [x0], #16\n"
1331         "ld1 {v28.16b}, [x0]\n"
1332         "mov x0, x1\n"
1333         "ld1 {v13.16b}, [x0], #16\n"
1334         "add x1, x1, %[dst_col_stride]\n"
1335         "ld1 {v21.16b}, [x0], #16\n"
1336         "ld1 {v29.16b}, [x0]\n"
1337         "mov x0, x1\n"
1338         "ld1 {v14.16b}, [x0], #16\n"
1339         "add x1, x1, %[dst_col_stride]\n"
1340         "ld1 {v22.16b}, [x0], #16\n"
1341         "ld1 {v30.16b}, [x0]\n"
1342         "mov x0, x1\n"
1343         "ld1 {v15.16b}, [x0], #16\n"
1344         "ld1 {v23.16b}, [x0], #16\n"
1345         "ld1 {v31.16b}, [x0]\n"
1346 
1347         "b " GEMMLOWP_LABEL_BEFORE_LOOP "f\n"
1348 
1349         GEMMLOWP_LABEL_CLEAR_ACCUMULATORS
1350         ":\n"
1351 
1352         // Clear accumulator registers (see layout below)
1353         "dup v8.4s, wzr\n"
1354         "subs %[run_depth], %[run_depth], #2\n"
1355         "dup v9.4s, wzr\n"
1356         "dup v10.4s, wzr\n"
1357         "dup v11.4s, wzr\n"
1358         "dup v12.4s, wzr\n"
1359         "dup v13.4s, wzr\n"
1360         "dup v14.4s, wzr\n"
1361         "dup v15.4s, wzr\n"
1362         "dup v16.4s, wzr\n"
1363         "dup v17.4s, wzr\n"
1364         "dup v18.4s, wzr\n"
1365         "dup v19.4s, wzr\n"
1366         "dup v20.4s, wzr\n"
1367         "dup v21.4s, wzr\n"
1368         "dup v22.4s, wzr\n"
1369         "dup v23.4s, wzr\n"
1370         "dup v24.4s, wzr\n"
1371         "dup v25.4s, wzr\n"
1372         "dup v26.4s, wzr\n"
1373         "dup v27.4s, wzr\n"
1374         "dup v28.4s, wzr\n"
1375         "dup v29.4s, wzr\n"
1376         "dup v30.4s, wzr\n"
1377         "dup v31.4s, wzr\n"
1378 
1379         GEMMLOWP_LABEL_BEFORE_LOOP
1380         ":\n"
1381 
1382         "beq " GEMMLOWP_LABEL_AFTER_LOOP "f\n"
1383 
1384         GEMMLOWP_LABEL_LOOP
1385         ":\n"
1386 
1387         // Overview of register layout:
1388         //
1389         // A 2x8 block of 2 2x4 cells of Rhs is stored in 16bit in v0--v1.
1390         // A 12x2 block of 3 4x2 cells Lhs is stored in 16bit in v2--v4.
1391         // A 12x8 block of accumulators is stored in 32bit in v8--v31.
1392         //
1393         //                         +--------+--------+-----+--------+--------+
1394         //                         |v0.h[0] |v0.h[1] | ... |v1.h[2] |v1.h[3] |
1395         //                    Rhs  +--------+--------+-----+--------+--------+
1396         //                         |v0.h[4] |v0.h[5] | ... |v1.h[6] |v1.h[7] |
1397         //                         +--------+--------+-----+--------+--------+
1398         //
1399         //                         |        |        |     |        |        |
1400         //
1401         //    Lhs                  |        |        |     |        |        |
1402         //
1403         //  +-------+-------+ - -  +--------+--------+-----+--------+--------+
1404         //  |v2.h[0]|v2.h[4]|      |v8.s[0] |v9.s[0] | ... |v14.s[0]|v15.s[0]|
1405         //  |v2.h[1]|v2.h[5]|      |v8.s[1] |v9.s[1] | ... |v14.s[1]|v15.s[1]|
1406         //  |v2.h[2]|v2.h[6]|      |v8.s[2] |v9.s[2] | ... |v14.s[2]|v15.s[2]|
1407         //  |v2.h[3]|v2.h[7]|      |v8.s[3] |v9.s[3] | ... |v14.s[3]|v15.s[3]|
1408         //  +-------+-------+ - -  +--------+--------+-----+--------+--------+
1409         //  |v3.h[0]|v3.h[4]|      |v16.s[0]|v17.s[0]| ... |v22.s[0]|v23.s[0]|
1410         //  |v3.h[1]|v3.h[5]|      |v16.s[1]|v17.s[1]| ... |v22.s[1]|v23.s[1]|
1411         //  |v3.h[2]|v3.h[6]|      |v16.s[2]|v17.s[2]| ... |v22.s[2]|v23.s[2]|
1412         //  |v3.h[3]|v3.h[7]|      |v16.s[3]|v17.s[3]| ... |v22.s[3]|v23.s[3]|
1413         //  +-------+-------+ - -  +--------+--------+-----+--------+--------+
1414         //  |v4.h[0]|v4.h[4]|      |v24.s[0]|v25.s[0]| ... |v30.s[0]|v31.s[0]|
1415         //  |v4.h[1]|v4.h[5]|      |v24.s[1]|v25.s[1]| ... |v30.s[1]|v31.s[1]|
1416         //  |v4.h[2]|v4.h[6]|      |v24.s[2]|v25.s[2]| ... |v30.s[2]|v31.s[2]|
1417         //  |v4.h[3]|v4.h[7]|      |v24.s[3]|v25.s[3]| ... |v30.s[3]|v31.s[3]|
1418         //  +-------+-------+ - -  +--------+--------+-----+--------+--------+
1419         //
1420         //                            Accumulator
1421 
1422         // Expand Lhs/Rhs cells to 16 bit.
1423         "uxtl v0.8h, v5.8b\n"
1424         "ld1 {v5.8b}, [%[rhs_ptr]], #8\n"
1425         "uxtl v1.8h, v6.8b\n"
1426         "ld1 {v6.8b}, [%[rhs_ptr]], #8\n"
1427         "uxtl v2.8h, v2.8b\n"
1428         "uxtl v3.8h, v3.8b\n"
1429         "uxtl v4.8h, v4.8b\n"
1430 
1431         // Multiply-accumulate, top third
1432         "umlal v8.4s, v2.4h, v0.h[0]\n"
1433         "umlal v9.4s, v2.4h, v0.h[1]\n"
1434         "umlal v10.4s, v2.4h, v0.h[2]\n"
1435         "umlal v11.4s, v2.4h, v0.h[3]\n"
1436         "umlal v12.4s, v2.4h, v1.h[0]\n"
1437         "umlal v13.4s, v2.4h, v1.h[1]\n"
1438         "umlal v14.4s, v2.4h, v1.h[2]\n"
1439         "umlal v15.4s, v2.4h, v1.h[3]\n"
1440         "umlal2 v8.4s, v2.8h, v0.h[4]\n"
1441         "umlal2 v9.4s, v2.8h, v0.h[5]\n"
1442         "umlal2 v10.4s, v2.8h, v0.h[6]\n"
1443         "umlal2 v11.4s, v2.8h, v0.h[7]\n"
1444         "umlal2 v12.4s, v2.8h, v1.h[4]\n"
1445         "umlal2 v13.4s, v2.8h, v1.h[5]\n"
1446         "umlal2 v14.4s, v2.8h, v1.h[6]\n"
1447         "umlal2 v15.4s, v2.8h, v1.h[7]\n"
1448         "ld1 {v2.8b}, [%[lhs_ptr]], #8\n"
1449 
1450         // Multiply-accumulate, middle third
1451         "umlal v16.4s, v3.4h, v0.h[0]\n"
1452         "umlal v17.4s, v3.4h, v0.h[1]\n"
1453         "umlal v18.4s, v3.4h, v0.h[2]\n"
1454         "umlal v19.4s, v3.4h, v0.h[3]\n"
1455         "umlal v20.4s, v3.4h, v1.h[0]\n"
1456         "umlal v21.4s, v3.4h, v1.h[1]\n"
1457         "umlal v22.4s, v3.4h, v1.h[2]\n"
1458         "umlal v23.4s, v3.4h, v1.h[3]\n"
1459         "umlal2 v16.4s, v3.8h, v0.h[4]\n"
1460         "umlal2 v17.4s, v3.8h, v0.h[5]\n"
1461         "umlal2 v18.4s, v3.8h, v0.h[6]\n"
1462         "umlal2 v19.4s, v3.8h, v0.h[7]\n"
1463         "umlal2 v20.4s, v3.8h, v1.h[4]\n"
1464         "umlal2 v21.4s, v3.8h, v1.h[5]\n"
1465         "umlal2 v22.4s, v3.8h, v1.h[6]\n"
1466         "umlal2 v23.4s, v3.8h, v1.h[7]\n"
1467         "ld1 {v3.8b}, [%[lhs_ptr]], #8\n"
1468 
1469         "subs %[run_depth], %[run_depth], #2\n"
1470 
1471         // Multiply-accumulate, bottom third
1472         "umlal v24.4s, v4.4h, v0.h[0]\n"
1473         "umlal v25.4s, v4.4h, v0.h[1]\n"
1474         "umlal v26.4s, v4.4h, v0.h[2]\n"
1475         "umlal v27.4s, v4.4h, v0.h[3]\n"
1476         "umlal v28.4s, v4.4h, v1.h[0]\n"
1477         "umlal v29.4s, v4.4h, v1.h[1]\n"
1478         "umlal v30.4s, v4.4h, v1.h[2]\n"
1479         "umlal v31.4s, v4.4h, v1.h[3]\n"
1480         "umlal2 v24.4s, v4.8h, v0.h[4]\n"
1481         "umlal2 v25.4s, v4.8h, v0.h[5]\n"
1482         "umlal2 v26.4s, v4.8h, v0.h[6]\n"
1483         "umlal2 v27.4s, v4.8h, v0.h[7]\n"
1484         "umlal2 v28.4s, v4.8h, v1.h[4]\n"
1485         "umlal2 v29.4s, v4.8h, v1.h[5]\n"
1486         "umlal2 v30.4s, v4.8h, v1.h[6]\n"
1487         "umlal2 v31.4s, v4.8h, v1.h[7]\n"
1488         "ld1 {v4.8b}, [%[lhs_ptr]], #8\n"
1489 
1490         "bne " GEMMLOWP_LABEL_LOOP "b\n"
1491 
1492         GEMMLOWP_LABEL_AFTER_LOOP
1493         ":\n"
1494 
1495         // Expand Lhs/Rhs cells to 16 bit.
1496         "uxtl v0.8h, v5.8b\n"
1497         "uxtl v1.8h, v6.8b\n"
1498         "uxtl v2.8h, v2.8b\n"
1499         "uxtl v3.8h, v3.8b\n"
1500         "uxtl v4.8h, v4.8b\n"
1501 
1502         // Multiply-accumulate, level of depth 0
1503         "umlal v8.4s, v2.4h, v0.h[0]\n"
1504         "umlal v9.4s, v2.4h, v0.h[1]\n"
1505         "umlal v10.4s, v2.4h, v0.h[2]\n"
1506         "umlal v11.4s, v2.4h, v0.h[3]\n"
1507         "umlal v12.4s, v2.4h, v1.h[0]\n"
1508         "umlal v13.4s, v2.4h, v1.h[1]\n"
1509         "umlal v14.4s, v2.4h, v1.h[2]\n"
1510         "umlal v15.4s, v2.4h, v1.h[3]\n"
1511         "umlal v16.4s, v3.4h, v0.h[0]\n"
1512         "umlal v17.4s, v3.4h, v0.h[1]\n"
1513         "umlal v18.4s, v3.4h, v0.h[2]\n"
1514         "umlal v19.4s, v3.4h, v0.h[3]\n"
1515         "umlal v20.4s, v3.4h, v1.h[0]\n"
1516         "umlal v21.4s, v3.4h, v1.h[1]\n"
1517         "umlal v22.4s, v3.4h, v1.h[2]\n"
1518         "umlal v23.4s, v3.4h, v1.h[3]\n"
1519         "umlal v24.4s, v4.4h, v0.h[0]\n"
1520         "umlal v25.4s, v4.4h, v0.h[1]\n"
1521         "umlal v26.4s, v4.4h, v0.h[2]\n"
1522         "umlal v27.4s, v4.4h, v0.h[3]\n"
1523         "umlal v28.4s, v4.4h, v1.h[0]\n"
1524         "umlal v29.4s, v4.4h, v1.h[1]\n"
1525         "umlal v30.4s, v4.4h, v1.h[2]\n"
1526         "umlal v31.4s, v4.4h, v1.h[3]\n"
1527 
1528         // Multiply-accumulate, level of depth 1
1529         "umlal2 v8.4s, v2.8h, v0.h[4]\n"
1530         "umlal2 v9.4s, v2.8h, v0.h[5]\n"
1531         "umlal2 v10.4s, v2.8h, v0.h[6]\n"
1532         "umlal2 v11.4s, v2.8h, v0.h[7]\n"
1533         "umlal2 v12.4s, v2.8h, v1.h[4]\n"
1534         "umlal2 v13.4s, v2.8h, v1.h[5]\n"
1535         "umlal2 v14.4s, v2.8h, v1.h[6]\n"
1536         "umlal2 v15.4s, v2.8h, v1.h[7]\n"
1537         "umlal2 v16.4s, v3.8h, v0.h[4]\n"
1538         "umlal2 v17.4s, v3.8h, v0.h[5]\n"
1539         "umlal2 v18.4s, v3.8h, v0.h[6]\n"
1540         "umlal2 v19.4s, v3.8h, v0.h[7]\n"
1541         "umlal2 v20.4s, v3.8h, v1.h[4]\n"
1542         "umlal2 v21.4s, v3.8h, v1.h[5]\n"
1543         "umlal2 v22.4s, v3.8h, v1.h[6]\n"
1544         "umlal2 v23.4s, v3.8h, v1.h[7]\n"
1545         "umlal2 v24.4s, v4.8h, v0.h[4]\n"
1546         "umlal2 v25.4s, v4.8h, v0.h[5]\n"
1547         "umlal2 v26.4s, v4.8h, v0.h[6]\n"
1548         "umlal2 v27.4s, v4.8h, v0.h[7]\n"
1549         "umlal2 v28.4s, v4.8h, v1.h[4]\n"
1550         "umlal2 v29.4s, v4.8h, v1.h[5]\n"
1551         "umlal2 v30.4s, v4.8h, v1.h[6]\n"
1552         "umlal2 v31.4s, v4.8h, v1.h[7]\n"
1553 
1554         // Store accumulators
1555         "mov x1, %[dst_ptr]\n"
1556         "mov x0, x1\n"
1557         "st1 {v8.16b}, [x0], #16\n"
1558         "subs %[run_depth], %[run_depth], #2\n"
1559         "st1 {v16.16b}, [x0], #16\n"
1560         "add x1, x1, %[dst_col_stride]\n"
1561         "st1 {v24.16b}, [x0]\n"
1562         "mov x0, x1\n"
1563         "st1 {v9.16b}, [x0], #16\n"
1564         "add x1, x1, %[dst_col_stride]\n"
1565         "st1 {v17.16b}, [x0], #16\n"
1566         "st1 {v25.16b}, [x0]\n"
1567         "mov x0, x1\n"
1568         "st1 {v10.16b}, [x0], #16\n"
1569         "add x1, x1, %[dst_col_stride]\n"
1570         "st1 {v18.16b}, [x0], #16\n"
1571         "st1 {v26.16b}, [x0]\n"
1572         "mov x0, x1\n"
1573         "st1 {v11.16b}, [x0], #16\n"
1574         "add x1, x1, %[dst_col_stride]\n"
1575         "st1 {v19.16b}, [x0], #16\n"
1576         "st1 {v27.16b}, [x0]\n"
1577         "mov x0, x1\n"
1578         "st1 {v12.16b}, [x0], #16\n"
1579         "add x1, x1, %[dst_col_stride]\n"
1580         "st1 {v20.16b}, [x0], #16\n"
1581         "st1 {v28.16b}, [x0]\n"
1582         "mov x0, x1\n"
1583         "st1 {v13.16b}, [x0], #16\n"
1584         "add x1, x1, %[dst_col_stride]\n"
1585         "st1 {v21.16b}, [x0], #16\n"
1586         "st1 {v29.16b}, [x0]\n"
1587         "mov x0, x1\n"
1588         "st1 {v14.16b}, [x0], #16\n"
1589         "add x1, x1, %[dst_col_stride]\n"
1590         "st1 {v22.16b}, [x0], #16\n"
1591         "st1 {v30.16b}, [x0]\n"
1592         "mov x0, x1\n"
1593         "st1 {v15.16b}, [x0], #16\n"
1594         "st1 {v23.16b}, [x0], #16\n"
1595         "st1 {v31.16b}, [x0]\n"
1596 #undef GEMMLOWP_LABEL_CLEAR_ACCUMULATORS
1597 #undef GEMMLOWP_LABEL_BEFORE_LOOP
1598 #undef GEMMLOWP_LABEL_LOOP
1599 #undef GEMMLOWP_LABEL_AFTER_LOOP
1600         :  // outputs
1601         [lhs_ptr] "+r"(lhs_ptr), [rhs_ptr] "+r"(rhs_ptr),
1602         [dst_ptr] "+r"(dst_ptr),
1603         [run_depth] "+r"(run_depth)
1604         :  // inputs
1605         [start_depth] "r"(start_depth),
1606         [dst_col_stride] "r"(dst_col_stride)
1607         :  // clobbers
1608         "cc", "memory", "x0", "x1", "v0", "v1", "v2", "v3", "v4", "v5", "v6",
1609         "v7", "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", "v16",
1610         "v17", "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26",
1611         "v27", "v28", "v29", "v30", "v31");
1612   }
1613 };
1614 
1615 #endif  // GEMMLOWP_NEON_64
1616 
1617 }  // namespace gemmlowp
1618 
1619 #endif  // GEMMLOWP_INTERNAL_KERNEL_NEON_H_
1620