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