1// Copyright 2019 Google LLC 2// 3// This source code is licensed under the BSD-style license found in the 4// LICENSE file in the root directory of this source tree. 5 6#include <xnnpack/assembly.h> 7 8.syntax unified 9 10// void xnn_f32_gemm_ukernel_4x8__aarch32_neon_cortex_a53( 11// size_t mr, r0 12// size_t nc, r1 13// size_t kc, r2 -> r5 -> sp + 0 14// const uint8_t*restrict a, r3 15// size_t a_stride, sp + 100 -> (r7) 16// const void*restrict w, sp + 104 -> r9 17// uint8_t*restrict c, sp + 108 -> r11 18// size_t cm_stride, sp + 112 -> (r6) 19// size_t cn_stride, sp + 116 -> (r0) 20// const union xnn_f32_output_params params[restrict static 1]) sp + 120 -> (r5) 21 22 23// inner loop registers 24// r0, r2 scratch temporaries for loads 25// r14 (lr) unused 26 27// A0 r3 d0 28// A1 r12 d1 29// A2 r10 d2 30// A3 r7 d3 31 32// B r9 d8, d9, d10, d11 33// B d12, d13, d14, d15 34 35// C0 r11 d16-d17 q8 d18-d19 q9 36// C1 r4 d20-d21 q10 d22-d23 q11 37// C2 r8 d24-d25 q12 d26-d27 q13 38// C3 r6 d28-d29 q14 d30-d31 q15 39 40// Clamp (r5) d4 d5 d6 d7 41 42BEGIN_FUNCTION xnn_f32_gemm_ukernel_4x8__aarch32_neon_cortex_a53 43 .arm 44#ifndef __APPLE__ 45 .arch armv7-a 46// .arch_extension mp 47 .fpu neon 48#endif 49 // Push 100 bytes 50 // r2 will be reloaded in outer loop 51 VPUSH {d8-d15} // 64 52 PUSH {r2, r4, r5, r6, r7, r8, r9, r10, r11} // +36 = 100 53 54 LDR r7, [sp, 100] // a_stride 55 LDR r9, [sp, 104] // w 56 LDR r11, [sp, 108] // c 57 LDR r6, [sp, 112] // cm_stride 58 59 // Clamp A and C pointers 60 CMP r0, 2 // if mr >= 2 61 ADD r12, r3, r7 // a1 = a0 + a_stride 62 ADD r4, r11, r6 // c1 = c0 + cm_stride 63 MOVLO r12, r3 // a1 64 MOVLO r4, r11 // c1 65 // if mr > 2 66 ADD r10, r12, r7 // a2 = a1 + a_stride 67 ADD r8, r4, r6 // c2 = c1 + cm_stride 68 MOVLS r10, r12 // a2 69 MOVLS r8, r4 // c2 70 71 CMP r0, 4 // if mr >=4 72 ADD r7, r10, r7 // a3 = a2 + a_stride 73 ADD r6, r8, r6 // c3 = c2 + cm_stride 74 MOVLO r7, r10 // a3 75 MOVLO r6, r8 // c3 76 77 .p2align 3 781: 79 # Load initial bias from w into accumulators 80 VLDM r9!, {d16-d19} // Bias 81 82 SUBS r5, r2, 16 // kc - 16 83 PLD [r3, 0] // Prefetch A 84 PLD [r3, 64] 85 VMOV q10, q8 86 PLD [r12, 0] 87 PLD [r12, 64] 88 VMOV q11, q9 89 PLD [r10, 0] 90 PLD [r10, 64] 91 VMOV q12, q8 92 PLD [r7, 0] 93 PLD [r7, 64] 94 VMOV q13, q9 95 PLD [r9, 0] // Prefetch B 96 PLD [r9, 64] 97 VMOV q14, q8 98 PLD [r9, 128] 99 PLD [r9, 192] 100 VMOV q15, q9 101 PLD [r9, 256] 102 PLD [r9, 320] 103 104 // 118877 us with PLDW vs 119550 us without in e2e 105 // PLDW [r11] // Prefetch C0 106 // PLDW [r4] // Prefetch C1 107 // PLDW [r8] // Prefetch C2 108 // PLDW [r6] // Prefetch C3 109 110 BLO 4f // less than 4 channels? 111 112 // Prologue 113 VLD1.32 {d0}, [r3]! // A0 114 VLD1.32 {d1}, [r12]! // A1 115 VLD1.32 {d2}, [r10]! // A2 116 VLD1.32 {d3}, [r7]! // A3 117 SUBS r5, r5, 16 118 VLDM r9, {d8-d11} // B0 119 LDR r0, [r9, 56] // B1 low VMOV is in BLOCK 0 120 LDR r2, [r9, 60] // B1 high 121 VLDR d13, [r9, 40] // B1 122 123 BLO 3f // less than 4 channels? skip main loop 124 125 # Main loop - 4 floats of A (16 bytes) 126 # 32 FMA + 8 LD64 A + 8 LDR B 127 .p2align 3 1282: 129 # First group of 16 FMA, Second group loads 130 // BLOCK 0 131 VLD1.32 {d4}, [r3]! // A0 132 VMOV d15, r0, r2 // b1 VMOV b from second group 133 VMLA.F32 q8, q4, d0[0] 134 LDR r0, [r12] // A1 low 135 VMLA.F32 q10, q4, d1[0] 136 LDR r2, [r12, 4] // A1 high 137 VMLA.F32 q12, q4, d2[0] 138 PLD [r3, 128] // Prefetch A0 139 140 // BLOCK 1 141 VLDR d12, [r9, 32] // B1 142 VMOV d5, r0, r2 // a1 VMOV 143 VMLA.F32 q14, q4, d3[0] 144 LDR r0, [r9, 72] // B0 low 145 VMLA.F32 q9, q5, d0[0] 146 LDR r2, [r9, 76] // B0 high 147 VMLA.F32 q11, q5, d1[0] 148 PLD [r12, 128] // Prefetch A1 149 150 // BLOCK 2 151 VLD1.32 {d6}, [r10]! // A2 152 VMOV d9, r0, r2 // b0 VMOV 153 VMLA.F32 q13, q5, d2[0] 154 LDR r0, [r7] // A3 low 155 VMLA.F32 q15, q5, d3[0] 156 LDR r2, [r7, 4] // A3 high 157 VMLA.F32 q8, q6, d0[1] 158 PLD [r10, 128] // Prefetch A2 159 160 // BLOCK 3 161 VLDR d14, [r9, 48] // B1 162 VMOV d7, r0, r2 // a3 VMOV 163 VMLA.F32 q10, q6, d1[1] 164 LDR r0, [r9, 88] // B0 low 165 VMLA.F32 q12, q6, d2[1] 166 LDR r2, [r9, 92] // B0 high 167 VMLA.F32 q14, q6, d3[1] 168 PLD [r7, 128] // Prefetch A3 169 170 // BLOCK 4 171 VLDR d8, [r9, 64] // B0 172 VMOV d11, r0, r2 // B0 VMOV 173 VMLA.F32 q9, q7, d0[1] 174 LDR r0, [r9, 104] // B1 low VMOV is in BLOCK 0 175 VMLA.F32 q11, q7, d1[1] 176 LDR r2, [r9, 108] // B1 high 177 VMLA.F32 q13, q7, d2[1] 178 PLD [r9, 384] // Prefetch B 179 180 // BLOCK 5 181 VLDR d10, [r9, 80] // B0 182 VMOV d13, r0, r2 // b1 VMOV b from second group 183 VMLA.F32 q15, q7, d3[1] 184 LDR r0, [r9, 120] // B1 low VMOV is in BLOCK 0 185 NOP 186 LDR r2, [r9, 124] // B1 high 187 NOP 188 PLD [r9, 448] // Prefetch B 189 190 # Second group of 16 FMA, First group of loads 191 // BLOCK 0 192 VLD1.32 {d0}, [r3]! // A0 193 VMOV d15, r0, r2 // b1 VMOV b from second group 194 VMLA.F32 q8, q4, d4[0] 195 LDR r0, [r12, 8] // A1 low 196 VMLA.F32 q10, q4, d5[0] 197 LDR r2, [r12, 12] // A1 high 198 VMLA.F32 q12, q4, d6[0] 199 200 // BLOCK 1 201 VLDR d12, [r9, 96] // B1 202 VMOV d1, r0, r2 // a1 VMOV 203 VMLA.F32 q14, q4, d7[0] 204 LDR r0, [r9, 136] // B0 low 205 VMLA.F32 q9, q5, d4[0] 206 LDR r2, [r9, 140] // B0 high 207 VMLA.F32 q11, q5, d5[0] 208 209 // BLOCK 2 210 VLD1.32 {d2}, [r10]! // A2 211 VMOV d9, r0, r2 // b0 VMOV 212 VMLA.F32 q13, q5, d6[0] 213 LDR r0, [r7, 8] // A3 low 214 VMLA.F32 q15, q5, d7[0] 215 LDR r2, [r7, 12] // A3 high 216 VMLA.F32 q8, q6, d4[1] 217 218 // BLOCK 3 219 VLDR d14, [r9, 112] // B1 220 VMOV d3, r0, r2 // a3 VMOV 221 VMLA.F32 q10, q6, d5[1] 222 LDR r0, [r9, 152] // B0 low 223 VMLA.F32 q12, q6, d6[1] 224 LDR r2, [r9, 156] // B0 high 225 VMLA.F32 q14, q6, d7[1] 226 ADD r12, r12, 16 // A1++ 227 228 // BLOCK 4 229 VLDR d8, [r9, 128] // B0 230 VMOV d11, r0, r2 // B0 VMOV 231 VMLA.F32 q9, q7, d4[1] 232 LDR r0, [r9, 168] // B1 low 233 VMLA.F32 q11, q7, d5[1] 234 LDR r2, [r9, 172] // B1 high 235 VMLA.F32 q13, q7, d6[1] 236 ADD r7, r7, 16 // A3++ 237 238 // BLOCK 5 239 VLDR d10, [r9, 144] // B0 240 VMOV d13, r0, r2 // b1 VMOV b 241 VMLA.F32 q15, q7, d7[1] 242 LDR r0, [r9, 184] // B1 low VMOV is in BLOCK 0 243 SUBS r5, r5, 16 244 LDR r2, [r9, 188] // B1 high 245 ADD r9, r9, 128 // B++ 246 BHS 2b 247 248 # Epilogue - 4 floats of A (16 bytes) 2493: 250 # First group of 16 FMA, Second group loads 251 // BLOCK 0 252 VLD1.32 {d4}, [r3]! // A0 253 VMOV d15, r0, r2 // b1 VMOV b from second group 254 VMLA.F32 q8, q4, d0[0] 255 LDR r0, [r12] // A1 low 256 VMLA.F32 q10, q4, d1[0] 257 LDR r2, [r12, 4] // A1 high 258 VMLA.F32 q12, q4, d2[0] 259 260 // BLOCK 1 261 VLDR d12, [r9, 32] // B1 262 VMOV d5, r0, r2 // a1 VMOV 263 VMLA.F32 q14, q4, d3[0] 264 LDR r0, [r9, 72] // B0 low 265 VMLA.F32 q9, q5, d0[0] 266 LDR r2, [r9, 76] // B0 high 267 VMLA.F32 q11, q5, d1[0] 268 269 // BLOCK 2 270 VLD1.32 {d6}, [r10]! // A2 271 VMOV d9, r0, r2 // b0 VMOV 272 VMLA.F32 q13, q5, d2[0] 273 LDR r0, [r7] // A3 low 274 VMLA.F32 q15, q5, d3[0] 275 LDR r2, [r7, 4] // A3 high 276 VMLA.F32 q8, q6, d0[1] 277 278 // BLOCK 3 279 VLDR d14, [r9, 48] // B1 280 VMOV d7, r0, r2 // a3 VMOV 281 VMLA.F32 q10, q6, d1[1] 282 LDR r0, [r9, 88] // B0 low 283 VMLA.F32 q12, q6, d2[1] 284 LDR r2, [r9, 92] // B0 high 285 VMLA.F32 q14, q6, d3[1] 286 287 // BLOCK 4 288 VLDR d8, [r9, 64] // B0 289 VMOV d11, r0, r2 // B0 VMOV 290 VMLA.F32 q9, q7, d0[1] 291 LDR r0, [r9, 104] // B1 low 292 VMLA.F32 q11, q7, d1[1] 293 LDR r2, [r9, 108] // B1 high 294 VMLA.F32 q13, q7, d2[1] 295 296 // BLOCK 5 297 VLDR d10, [r9, 80] // B0 298 VMOV d13, r0, r2 // b1 VMOV b 299 VMLA.F32 q15, q7, d3[1] 300 LDR r0, [r9, 120] // B1 low VMOV is in BLOCK 0 301 NOP 302 LDR r2, [r9, 124] // B1 high 303 NOP 304 NOP 305 306 # Second group of 16 FMA, First group of loads 307 // BLOCK 0 308 VLDR d12, [r9, 96] // B1 309 VMOV d15, r0, r2 // b1 VMOV b from second group 310 VMLA.F32 q8, q4, d4[0] 311 VMLA.F32 q10, q4, d5[0] 312 VMLA.F32 q12, q4, d6[0] 313 314 // BLOCK 1 315 VLDR d14, [r9, 112] // B1 316 VMLA.F32 q14, q4, d7[0] 317 VMLA.F32 q9, q5, d4[0] 318 VMLA.F32 q11, q5, d5[0] 319 NOP 320 321 // BLOCK 2 322 NOP // VDDR B1 lands here 323 NOP 324 VMLA.F32 q13, q5, d6[0] 325 VMLA.F32 q15, q5, d7[0] 326 VMLA.F32 q8, q6, d4[1] 327 328 // BLOCK 3 329 VMLA.F32 q10, q6, d5[1] 330 VMLA.F32 q12, q6, d6[1] 331 VMLA.F32 q14, q6, d7[1] 332 ADD r12, r12, 8 // A1++ 333 334 // BLOCK 4 335 VMLA.F32 q9, q7, d4[1] 336 VMLA.F32 q11, q7, d5[1] 337 VMLA.F32 q13, q7, d6[1] 338 ADD r7, r7, 8 // A3++ 339 340 // BLOCK 5 341 VMLA.F32 q15, q7, d7[1] 342 ADD r9, r9, 128 // B++ 343 3444: 345 // Is there a remainder?- 1 to 3 floats of A (4, 8 or 12 bytes) 346 TST r5, 12 347 BNE 7f 348 349 .p2align 3 3505: 351 // Load params pointer 352 LDR r0, [sp, 116] // cn_stride 353 LDR r5, [sp, 120] // clamping_params 354 LDR r2, [sp, 0] // kc 355 SUBS r1, r1, 8 356 357 // Load clamping_params values 358 VLD1.32 {d4[],d5[]}, [r5]! 359 VLD1.32 {d6[],d7[]}, [r5] 360 361 // Clamp 362 VMIN.F32 q8, q8, q2 363 VMIN.F32 q9, q9, q2 364 VMIN.F32 q10, q10, q2 365 VMIN.F32 q11, q11, q2 366 VMIN.F32 q12, q12, q2 367 VMIN.F32 q13, q13, q2 368 VMIN.F32 q14, q14, q2 369 VMIN.F32 q15, q15, q2 370 VMAX.F32 q8, q8, q3 371 VMAX.F32 q9, q9, q3 372 VMAX.F32 q10, q10, q3 373 VMAX.F32 q11, q11, q3 374 VMAX.F32 q12, q12, q3 375 VMAX.F32 q13, q13, q3 376 VMAX.F32 q14, q14, q3 377 VMAX.F32 q15, q15, q3 378 379 // Store full 4 x 8 380 BLO 10f 381 VST1.32 {d16-d19}, [r11], r0 382 SUB r7, r7, r2 383 VST1.32 {d20-d23}, [r4], r0 384 SUB r10, r10, r2 385 VST1.32 {d24-d27}, [r8], r0 386 SUB r12, r12, r2 387 VST1.32 {d28-d31}, [r6], r0 388 SUB r3, r3, r2 389 BHI 1b 390 3916: 392 ADD sp, sp, 4 393 POP {r4, r5, r6, r7, r8, r9, r10, r11} 394 VPOP {d8-d15} 395 BX lr 396 397 .p2align 3 3987: 399 // Is there a remainder?- 2 floats of A (8 bytes) 400 TST r5, 8 401 BEQ 8f 402 403 // Remainder - 2 floats of A (8 bytes) 404 VLD1.32 {d0}, [r3]! // A0 405 VLDM r9!, {d8-d11} // B0 406 VLD1.32 {d1}, [r12]! // A1 407 VLD1.32 {d2}, [r10]! // A2 408 VLD1.32 {d3}, [ r7]! // A3 409 410 VMLA.F32 q8, q4, d0[0] 411 VMLA.F32 q9, q5, d0[0] 412 VMLA.F32 q10, q4, d1[0] 413 VMLA.F32 q11, q5, d1[0] 414 VLDM r9!, {d12-d15} // B1 415 VMLA.F32 q12, q4, d2[0] 416 VMLA.F32 q13, q5, d2[0] 417 VMLA.F32 q14, q4, d3[0] 418 VMLA.F32 q15, q5, d3[0] 419 VMLA.F32 q8, q6, d0[1] 420 VMLA.F32 q9, q7, d0[1] 421 VMLA.F32 q10, q6, d1[1] 422 VMLA.F32 q11, q7, d1[1] 423 VMLA.F32 q12, q6, d2[1] 424 VMLA.F32 q13, q7, d2[1] 425 VMLA.F32 q14, q6, d3[1] 426 VMLA.F32 q15, q7, d3[1] 4278: 428 // Is there a remainder?- 1 floats of A (4 bytes) 429 TST r5, 4 430 BEQ 5b 431 4329: 433 // Remainder- 1 floats of A (4 bytes) 434 VLDM r3!, {s0} // A0 435 VLDM r9!, {d8-d11} // B0 436 VLDM r12!, {s2} // A1 437 VLDM r10!, {s4} // A2 438 VLDM r7!, {s6} // A3 439 VMLA.F32 q8, q4, d0[0] 440 VMLA.F32 q9, q5, d0[0] 441 VMLA.F32 q10, q4, d1[0] 442 VMLA.F32 q11, q5, d1[0] 443 VMLA.F32 q12, q4, d2[0] 444 VMLA.F32 q13, q5, d2[0] 445 VMLA.F32 q14, q4, d3[0] 446 VMLA.F32 q15, q5, d3[0] 447 B 5b 448 449 // Store odd width 45010: 451 TST r1, 4 452 BEQ 11f 453 VST1.32 {d16-d17}, [r11]! 454 VMOV q8, q9 455 VST1.32 {d20-d21}, [r4]! 456 VMOV q10, q11 457 VST1.32 {d24-d25}, [r8]! 458 VMOV q12, q13 459 VST1.32 {d28-d29}, [r6]! 460 VMOV q14, q15 461 46211: 463 TST r1, 2 464 BEQ 12f 465 VST1.32 {d16}, [r11]! 466 VMOV d16, d17 467 VST1.32 {d20}, [r4]! 468 VMOV d20, d21 469 VST1.32 {d24}, [r8]! 470 VMOV d24, d25 471 VST1.32 {d28}, [r6]! 472 VMOV d28, d29 473 47412: 475 TST r1, 1 476 BEQ 13f 477 VST1.32 {d16[0]}, [r11] 478 VST1.32 {d20[0]}, [r4] 479 VST1.32 {d24[0]}, [r8] 480 VST1.32 {d28[0]}, [r6] 481 48213: 483 ADD sp, sp, 4 484 POP {r4, r5, r6, r7, r8, r9, r10, r11} 485 VPOP {d8-d15} 486 BX lr 487 488END_FUNCTION xnn_f32_gemm_ukernel_4x8__aarch32_neon_cortex_a53 489 490#ifdef __ELF__ 491.section ".note.GNU-stack","",%progbits 492#endif