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