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_minmax_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_minmax_params params[restrict XNN_MIN_ELEMENTS(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_minmax_ukernel_4x8__aarch32_neon_cortex_a53 43 .arm 44#ifndef __APPLE__ 45 .arch armv7-a 46 .fpu neon 47#endif 48 // Push 100 bytes 49 // r2 will be reloaded in outer loop 50 VPUSH {d8-d15} // 64 51 PUSH {r2, r4, r5, r6, r7, r8, r9, r10, r11} // +36 = 100 52 53 LDR r7, [sp, 100] // a_stride 54 LDR r11, [sp, 108] // c 55 LDR r6, [sp, 112] // cm_stride 56 LDR r9, [sp, 104] // w 57 58 // Clamp A and C pointers 59 CMP r0, 2 // if mr >= 2 60 ADD r12, r3, r7 // a1 = a0 + a_stride 61 ADD r4, r11, r6 // c1 = c0 + cm_stride 62 MOVLO r12, r3 // a1 63 MOVLO r4, r11 // c1 64 // if mr > 2 65 ADD r10, r12, r7 // a2 = a1 + a_stride 66 ADD r8, r4, r6 // c2 = c1 + cm_stride 67 MOVLS r10, r12 // a2 68 MOVLS r8, r4 // c2 69 70 CMP r0, 4 // if mr >=4 71 ADD r7, r10, r7 // a3 = a2 + a_stride 72 ADD r6, r8, r6 // c3 = c2 + cm_stride 73 MOVLO r7, r10 // a3 74 MOVLO r6, r8 // c3 75 76 .p2align 3 770: 78 # Load initial bias from w into accumulators 79 VLDM r9!, {d16-d19} // Bias 80 81 SUBS r5, r2, 16 // kc - 16 82 PLD [r3, 0] // Prefetch A 83 PLD [r3, 64] 84 VMOV q10, q8 85 PLD [r12, 0] 86 PLD [r12, 64] 87 VMOV q11, q9 88 PLD [r10, 0] 89 PLD [r10, 64] 90 VMOV q12, q8 91 PLD [r7, 0] 92 PLD [r7, 64] 93 VMOV q13, q9 94 PLD [r9, 0] // Prefetch B 95 PLD [r9, 64] 96 VMOV q14, q8 97 PLD [r9, 128] 98 PLD [r9, 192] 99 VMOV q15, q9 100 PLD [r9, 256] 101 PLD [r9, 320] 102 BLO 4f // less than 4 channels? 103 104 // Prologue 105 VLD1.32 {d0}, [r3]! // A0 106 VLD1.32 {d1}, [r12]! // A1 107 VLD1.32 {d2}, [r10]! // A2 108 VLD1.32 {d3}, [r7]! // A3 109 SUBS r5, r5, 16 110 VLDM r9, {d8-d11} // B0 111 LDR r0, [r9, 56] // B1 low VMOV is in BLOCK 0 112 LDR r2, [r9, 60] // B1 high 113 VLDR d13, [r9, 40] // B1 114 115 BLO 2f // less than 4 channels? skip main loop 116 117 # Main loop - 4 floats of A (16 bytes) 118 # 32 FMA + 8 LD64 A + 8 LDR B 119 .p2align 3 1201: 121 # First group of 16 FMA, Second group loads 122 // BLOCK 0 123 VLD1.32 {d4}, [r3]! // A0 124 VMOV d15, r0, r2 // b1 VMOV b from second group 125 VMLA.F32 q8, q4, d0[0] 126 LDR r0, [r12] // A1 low 127 VMLA.F32 q10, q4, d1[0] 128 LDR r2, [r12, 4] // A1 high 129 VMLA.F32 q12, q4, d2[0] 130 PLD [r3, 128] // Prefetch A0 131 132 // BLOCK 1 133 VLDR d12, [r9, 32] // B1 134 VMOV d5, r0, r2 // a1 VMOV 135 VMLA.F32 q14, q4, d3[0] 136 LDR r0, [r9, 72] // B0 low 137 VMLA.F32 q9, q5, d0[0] 138 LDR r2, [r9, 76] // B0 high 139 VMLA.F32 q11, q5, d1[0] 140 PLD [r12, 128] // Prefetch A1 141 142 // BLOCK 2 143 VLD1.32 {d6}, [r10]! // A2 144 VMOV d9, r0, r2 // b0 VMOV 145 VMLA.F32 q13, q5, d2[0] 146 LDR r0, [r7] // A3 low 147 VMLA.F32 q15, q5, d3[0] 148 LDR r2, [r7, 4] // A3 high 149 VMLA.F32 q8, q6, d0[1] 150 PLD [r10, 128] // Prefetch A2 151 152 // BLOCK 3 153 VLDR d14, [r9, 48] // B1 154 VMOV d7, r0, r2 // a3 VMOV 155 VMLA.F32 q10, q6, d1[1] 156 LDR r0, [r9, 88] // B0 low 157 VMLA.F32 q12, q6, d2[1] 158 LDR r2, [r9, 92] // B0 high 159 VMLA.F32 q14, q6, d3[1] 160 PLD [r7, 128] // Prefetch A3 161 162 // BLOCK 4 163 VLDR d8, [r9, 64] // B0 164 VMOV d11, r0, r2 // B0 VMOV 165 VMLA.F32 q9, q7, d0[1] 166 LDR r0, [r9, 104] // B1 low VMOV is in BLOCK 0 167 VMLA.F32 q11, q7, d1[1] 168 LDR r2, [r9, 108] // B1 high 169 VMLA.F32 q13, q7, d2[1] 170 PLD [r9, 384] // Prefetch B 171 172 // BLOCK 5 173 VLDR d10, [r9, 80] // B0 174 VMOV d13, r0, r2 // b1 VMOV b from second group 175 VMLA.F32 q15, q7, d3[1] 176 LDR r0, [r9, 120] // B1 low VMOV is in BLOCK 0 177 NOP 178 LDR r2, [r9, 124] // B1 high 179 NOP 180 PLD [r9, 448] // Prefetch B 181 182 # Second group of 16 FMA, First group of loads 183 // BLOCK 0 184 VLD1.32 {d0}, [r3]! // A0 185 VMOV d15, r0, r2 // b1 VMOV b from second group 186 VMLA.F32 q8, q4, d4[0] 187 LDR r0, [r12, 8] // A1 low 188 VMLA.F32 q10, q4, d5[0] 189 LDR r2, [r12, 12] // A1 high 190 VMLA.F32 q12, q4, d6[0] 191 // NOP 192 193 // BLOCK 1 194 VLDR d12, [r9, 96] // B1 195 VMOV d1, r0, r2 // a1 VMOV 196 VMLA.F32 q14, q4, d7[0] 197 LDR r0, [r9, 136] // B0 low 198 VMLA.F32 q9, q5, d4[0] 199 LDR r2, [r9, 140] // B0 high 200 VMLA.F32 q11, q5, d5[0] 201 // NOP 202 203 // BLOCK 2 204 VLD1.32 {d2}, [r10]! // A2 205 VMOV d9, r0, r2 // b0 VMOV 206 VMLA.F32 q13, q5, d6[0] 207 LDR r0, [r7, 8] // A3 low 208 VMLA.F32 q15, q5, d7[0] 209 LDR r2, [r7, 12] // A3 high 210 VMLA.F32 q8, q6, d4[1] 211 // NOP 212 213 // BLOCK 3 214 VLDR d14, [r9, 112] // B1 215 VMOV d3, r0, r2 // a3 VMOV 216 VMLA.F32 q10, q6, d5[1] 217 LDR r0, [r9, 152] // B0 low 218 VMLA.F32 q12, q6, d6[1] 219 LDR r2, [r9, 156] // B0 high 220 VMLA.F32 q14, q6, d7[1] 221 ADD r12, r12, 16 // A1++ 222 223 // BLOCK 4 224 VLDR d8, [r9, 128] // B0 225 VMOV d11, r0, r2 // B0 VMOV 226 VMLA.F32 q9, q7, d4[1] 227 LDR r0, [r9, 168] // B1 low 228 VMLA.F32 q11, q7, d5[1] 229 LDR r2, [r9, 172] // B1 high 230 VMLA.F32 q13, q7, d6[1] 231 ADD r7, r7, 16 // A3++ 232 233 // BLOCK 5 234 VLDR d10, [r9, 144] // B0 235 VMOV d13, r0, r2 // b1 VMOV b 236 VMLA.F32 q15, q7, d7[1] 237 LDR r0, [r9, 184] // B1 low VMOV is in BLOCK 0 238 SUBS r5, r5, 16 239 LDR r2, [r9, 188] // B1 high 240 ADD r9, r9, 128 // B++ 241 BHS 1b 242 243 # Epilogue - 4 floats of A (16 bytes) 2442: 245 # First group of 16 FMA, Second group loads 246 // BLOCK 0 247 VLD1.32 {d4}, [r3]! // A0 248 VMOV d15, r0, r2 // b1 VMOV b from second group 249 VMLA.F32 q8, q4, d0[0] 250 LDR r0, [r12] // A1 low 251 VMLA.F32 q10, q4, d1[0] 252 LDR r2, [r12, 4] // A1 high 253 VMLA.F32 q12, q4, d2[0] 254 // NOP 255 256 // BLOCK 1 257 VLDR d12, [r9, 32] // B1 258 VMOV d5, r0, r2 // a1 VMOV 259 VMLA.F32 q14, q4, d3[0] 260 LDR r0, [r9, 72] // B0 low 261 VMLA.F32 q9, q5, d0[0] 262 LDR r2, [r9, 76] // B0 high 263 VMLA.F32 q11, q5, d1[0] 264 // NOP 265 266 // BLOCK 2 267 VLD1.32 {d6}, [r10]! // A2 268 VMOV d9, r0, r2 // b0 VMOV 269 VMLA.F32 q13, q5, d2[0] 270 LDR r0, [r7] // A3 low 271 VMLA.F32 q15, q5, d3[0] 272 LDR r2, [r7, 4] // A3 high 273 VMLA.F32 q8, q6, d0[1] 274 // NOP 275 276 // BLOCK 3 277 VLDR d14, [r9, 48] // B1 278 VMOV d7, r0, r2 // a3 VMOV 279 VMLA.F32 q10, q6, d1[1] 280 LDR r0, [r9, 88] // B0 low 281 VMLA.F32 q12, q6, d2[1] 282 LDR r2, [r9, 92] // B0 high 283 VMLA.F32 q14, q6, d3[1] 284 // NOP 285 286 // BLOCK 4 287 VLDR d8, [r9, 64] // B0 288 VMOV d11, r0, r2 // B0 VMOV 289 VMLA.F32 q9, q7, d0[1] 290 LDR r0, [r9, 104] // B1 low 291 VMLA.F32 q11, q7, d1[1] 292 LDR r2, [r9, 108] // B1 high 293 VMLA.F32 q13, q7, d2[1] 294 // NOP 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 ADD r12, r12, 8 // A1++ 320 321 // BLOCK 2 322 ADD r7, r7, 8 // A3++ VLDR B1 lands here 323 ADD r9, r9, 128 // B++ 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 TST r5, 15 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 339 // BLOCK 5 340 VMLA.F32 q15, q7, d7[1] 341 342 // Is there a remainder?- 1 to 3 floats of A (4, 8 or 12 bytes) 343 BNE 4f 344 345 .p2align 3 3463: 347 // Load params pointer 348 LDR r0, [sp, 116] // cn_stride 349 LDR r5, [sp, 120] // params 350 LDR r2, [sp, 0] // kc 351 SUBS r1, r1, 8 352 353 // Load min/max values 354 VLD1.32 {d4[],d5[]}, [r5]! 355 VLD1.32 {d6[],d7[]}, [r5] 356 357 // Clamp 358 VMAX.F32 q8, q8, q2 359 VMAX.F32 q9, q9, q2 360 VMAX.F32 q10, q10, q2 361 VMAX.F32 q11, q11, q2 362 VMAX.F32 q12, q12, q2 363 VMAX.F32 q13, q13, q2 364 VMAX.F32 q14, q14, q2 365 VMAX.F32 q15, q15, q2 366 VMIN.F32 q8, q8, q3 367 VMIN.F32 q9, q9, q3 368 VMIN.F32 q10, q10, q3 369 VMIN.F32 q11, q11, q3 370 VMIN.F32 q12, q12, q3 371 VMIN.F32 q13, q13, q3 372 VMIN.F32 q14, q14, q3 373 VMIN.F32 q15, q15, q3 374 375 // Store full 4 x 8 376 BLO 6f 377 VST1.32 {d16-d19}, [r11], r0 378 SUB r7, r7, r2 379 VST1.32 {d20-d23}, [r4], r0 380 SUB r10, r10, r2 381 VST1.32 {d24-d27}, [r8], r0 382 SUB r12, r12, r2 383 VST1.32 {d28-d31}, [r6], r0 384 SUB r3, r3, r2 385 BHI 0b 386 387 ADD sp, sp, 4 388 POP {r4, r5, r6, r7, r8, r9, r10, r11} 389 VPOP {d8-d15} 390 BX lr 391 392 .p2align 3 3934: 394 // Is there a remainder?- 2 floats of A (8 bytes) 395 TST r5, 8 396 BEQ 5f 397 398 // Remainder - 2 floats of A (8 bytes) 399 VLD1.32 {d0}, [r3]! // A0 400 VLDM r9!, {d8-d11} // B0 401 VLD1.32 {d1}, [r12]! // A1 402 VLD1.32 {d2}, [r10]! // A2 403 VLD1.32 {d3}, [ r7]! // A3 404 405 VMLA.F32 q8, q4, d0[0] 406 VMLA.F32 q9, q5, d0[0] 407 VMLA.F32 q10, q4, d1[0] 408 VMLA.F32 q11, q5, d1[0] 409 VLDM r9!, {d12-d15} // B1 410 VMLA.F32 q12, q4, d2[0] 411 VMLA.F32 q13, q5, d2[0] 412 VMLA.F32 q14, q4, d3[0] 413 VMLA.F32 q15, q5, d3[0] 414 VMLA.F32 q8, q6, d0[1] 415 VMLA.F32 q9, q7, d0[1] 416 VMLA.F32 q10, q6, d1[1] 417 VMLA.F32 q11, q7, d1[1] 418 VMLA.F32 q12, q6, d2[1] 419 VMLA.F32 q13, q7, d2[1] 420 VMLA.F32 q14, q6, d3[1] 421 VMLA.F32 q15, q7, d3[1] 422 423 // Is there a remainder?- 1 floats of A (4 bytes) 424 TST r5, 4 425 BEQ 3b 426 4275: 428 // Remainder- 1 floats of A (4 bytes) 429 VLDM r3!, {s0} // A0 430 VLDM r9!, {d8-d11} // B0 431 VLDM r12!, {s2} // A1 432 VLDM r10!, {s4} // A2 433 VLDM r7!, {s6} // A3 434 VMLA.F32 q8, q4, d0[0] 435 VMLA.F32 q9, q5, d0[0] 436 VMLA.F32 q10, q4, d1[0] 437 VMLA.F32 q11, q5, d1[0] 438 VMLA.F32 q12, q4, d2[0] 439 VMLA.F32 q13, q5, d2[0] 440 VMLA.F32 q14, q4, d3[0] 441 VMLA.F32 q15, q5, d3[0] 442 B 3b 443 444 // Store odd width 4456: 446 TST r1, 4 447 BEQ 7f 448 VST1.32 {d16-d17}, [r11]! 449 VMOV q8, q9 450 VST1.32 {d20-d21}, [r4]! 451 VMOV q10, q11 452 VST1.32 {d24-d25}, [r8]! 453 VMOV q12, q13 454 VST1.32 {d28-d29}, [r6]! 455 VMOV q14, q15 456 4577: 458 TST r1, 2 459 BEQ 8f 460 VST1.32 {d16}, [r11]! 461 VMOV d16, d17 462 VST1.32 {d20}, [r4]! 463 VMOV d20, d21 464 VST1.32 {d24}, [r8]! 465 VMOV d24, d25 466 VST1.32 {d28}, [r6]! 467 VMOV d28, d29 468 4698: 470 TST r1, 1 471 BEQ 9f 472 VST1.32 {d16[0]}, [r11] 473 VST1.32 {d20[0]}, [r4] 474 VST1.32 {d24[0]}, [r8] 475 VST1.32 {d28[0]}, [r6] 476 4779: 478 ADD sp, sp, 4 479 POP {r4, r5, r6, r7, r8, r9, r10, r11} 480 VPOP {d8-d15} 481 BX lr 482 483END_FUNCTION xnn_f32_gemm_minmax_ukernel_4x8__aarch32_neon_cortex_a53 484 485#ifdef __ELF__ 486.section ".note.GNU-stack","",%progbits 487#endif 488