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