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