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