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_a55( 11// size_t mr, r0 12// size_t nc, r1 13// size_t kc, r2 -> r5 14// const uint8_t*restrict a, r3 15// size_t a_stride, sp + 96 -> (r7) 16// const void*restrict w, sp + 100 -> r9 17// uint8_t*restrict c, sp + 104 -> r11 18// size_t cm_stride, sp + 108 -> (r6) 19// size_t cn_stride, sp + 112 -> (r0) 20// minmax_params*params, sp + 116 -> (r5) 21 22// d8-d15, r4-r11,r14(lr) need to be preserved if used. r13(sp),r15(pc) are reserved. 23 24// Register usage 25 26// r14 (lr) unused 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_gemm_minmax_ukernel_4x8__aarch32_neon_cortex_a55 44 .arm 45#ifndef __APPLE__ 46 .arch armv7-a 47 .fpu neon 48#endif 49 # Push 96 bytes 50 VPUSH {d8-d15} // 64 51 PUSH {r4, r5, r6, r7, r8, r9, r10, r11} // +32 = 96 52 53 LDR r7, [sp, 96] // a_stride 54 LDR r11, [sp, 104] // c 55 LDR r6, [sp, 108] // cm_stride 56 LDR r9, [sp, 100] // 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 VLDR d15, [r9, 56] // B1CK 0 112 VLDR d13, [r9, 40] // B1 113 BLO 2f // less than 4 channels? skip main loop 114 115 # Main loop - 4 floats of A (16 bytes) 116 # 32 FMA + 8 LD64 A + 8 LDR B 117 .p2align 3 1181: 119 # First group of 16 FMA, Second group loads 120 # BLOCK 0 121 VMLA.F32 q8, q4, d0[0] 122 VLD1.32 {d4}, [r3]! // A0 123 VMLA.F32 q10, q4, d1[0] 124 VLD1.32 {d5}, [r12]! // A1 125 VMLA.F32 q12, q4, d2[0] 126 127 # BLOCK 1 128 VMLA.F32 q14, q4, d3[0] 129 VLDR d12, [r9, 32] // B1 130 VMLA.F32 q9, q5, d0[0] 131 VLDR d9, [r9, 72] // B0 132 VMLA.F32 q11, q5, d1[0] 133 134 # BLOCK 2 135 VMLA.F32 q13, q5, d2[0] 136 VLD1.32 {d6}, [r10]! // A2 137 VMLA.F32 q15, q5, d3[0] 138 VLD1.32 {d7}, [r7]! // A3 139 VMLA.F32 q8, q6, d0[1] 140 141 # BLOCK 3 142 VMLA.F32 q10, q6, d1[1] 143 VLDR d14, [r9, 48] // B1 144 VMLA.F32 q12, q6, d2[1] 145 VLDR d11, [r9, 88] // B0 146 VMLA.F32 q14, q6, d3[1] 147 148 # BLOCK 4 149 VMLA.F32 q9, q7, d0[1] 150 VLDR d8, [r9, 64] // B0 151 VMLA.F32 q11, q7, d1[1] 152 VLDR d13, [r9, 104] // B1 153 VMLA.F32 q13, q7, d2[1] 154 VLDR d10, [r9, 80] // B0 155 156 # BLOCK 5 157 VMLA.F32 q15, q7, d3[1] 158 VLDR d15, [r9, 120] // B1 159 160 # Second group of 16 FMA, First group of loads 161 # BLOCK 0 162 VMLA.F32 q8, q4, d4[0] 163 VLD1.32 {d0}, [r3]! // A0 164 VMLA.F32 q10, q4, d5[0] 165 VLD1.32 {d1}, [r12]! // A1 166 VMLA.F32 q12, q4, d6[0] 167 168 # BLOCK 1 169 VMLA.F32 q14, q4, d7[0] 170 VLDR d12, [r9, 96] // B1 171 VMLA.F32 q9, q5, d4[0] 172 VLDR d9, [r9, 136] // B0 173 VMLA.F32 q11, q5, d5[0] 174 175 # BLOCK 2 176 VMLA.F32 q13, q5, d6[0] 177 VLD1.32 {d2}, [r10]! // A2 178 VMLA.F32 q15, q5, d7[0] 179 VLD1.32 {d3}, [r7]! // A3 180 VMLA.F32 q8, q6, d4[1] 181 182 # BLOCK 3 183 VMLA.F32 q10, q6, d5[1] 184 VLDR d14, [r9, 112] // B1 185 VMLA.F32 q12, q6, d6[1] 186 VLDR d11, [r9, 152] // B0 187 VMLA.F32 q14, q6, d7[1] 188 SUBS r5, r5, 16 189 190 # BLOCK 4 191 VMLA.F32 q9, q7, d4[1] 192 VLDR d8, [r9, 128] // B0 193 VMLA.F32 q11, q7, d5[1] 194 VLDR d13, [r9, 168] // B1 195 VMLA.F32 q13, q7, d6[1] 196 VLDR d10, [r9, 144] // B0 197 198 # BLOCK 5 199 VMLA.F32 q15, q7, d7[1] 200 VLDR d15, [r9, 184] // B1 201 ADD r9, r9, 128 // B++ 202 BHS 1b 203 204 205 # Epilogue - 4 floats of A (16 bytes) 2062: 207 # First group of 16 FMA, Second group loads 208 # BLOCK 0 209 VMLA.F32 q8, q4, d0[0] 210 VLD1.32 {d4}, [r3]! // A0 211 VMLA.F32 q10, q4, d1[0] 212 VLD1.32 {d5}, [r12]! // A1 213 VMLA.F32 q12, q4, d2[0] 214 215 # BLOCK 1 216 VMLA.F32 q14, q4, d3[0] 217 VLDR d12, [r9, 32] // B1 218 VMLA.F32 q9, q5, d0[0] 219 VLDR d9, [r9, 72] // B0 220 VMLA.F32 q11, q5, d1[0] 221 222 # BLOCK 2 223 VMLA.F32 q13, q5, d2[0] 224 VLD1.32 {d6}, [r10]! // A2 225 VMLA.F32 q15, q5, d3[0] 226 VLD1.32 {d7}, [r7]! // A3 227 VMLA.F32 q8, q6, d0[1] 228 229 # BLOCK 3 230 VMLA.F32 q10, q6, d1[1] 231 VLDR d14, [r9, 48] // B1 232 VMLA.F32 q12, q6, d2[1] 233 VLDR d11, [r9, 88] // B0 234 VMLA.F32 q14, q6, d3[1] 235 236 # BLOCK 4 237 VMLA.F32 q9, q7, d0[1] 238 VLDR d8, [r9, 64] // B0 239 VMLA.F32 q11, q7, d1[1] 240 VLDR d13, [r9, 104] // B1 241 VMLA.F32 q13, q7, d2[1] 242 VLDR d10, [r9, 80] // B0 243 244 # BLOCK 5 245 VMLA.F32 q15, q7, d3[1] 246 VLDR d15, [r9, 120] // B1 247 248 # Second group of 16 FMA, First group of loads 249 # BLOCK 0 250 VMLA.F32 q8, q4, d4[0] 251 VLDR d12, [r9, 96] // B1 252 VMLA.F32 q10, q4, d5[0] 253 VMLA.F32 q12, q4, d6[0] 254 255 # BLOCK 1 256 VMLA.F32 q14, q4, d7[0] 257 VLDR d14, [r9, 112] // B1 258 VMLA.F32 q9, q5, d4[0] 259 VMLA.F32 q11, q5, d5[0] 260 261 # BLOCK 2 262 VMLA.F32 q13, q5, d6[0] 263 VMLA.F32 q15, q5, d7[0] 264 VMLA.F32 q8, q6, d4[1] 265 ADD r9, r9, 128 // B++ 266 267 # BLOCK 3 268 VMLA.F32 q10, q6, d5[1] 269 VMLA.F32 q12, q6, d6[1] 270 VMLA.F32 q14, q6, d7[1] 271 TST r5, 15 272 273 # BLOCK 4 274 VMLA.F32 q9, q7, d4[1] 275 VMLA.F32 q11, q7, d5[1] 276 VMLA.F32 q13, q7, d6[1] 277 278 # BLOCK 5 279 VMLA.F32 q15, q7, d7[1] 280 281 # Is there a remainder?- 1 to 3 floats of A (4, 8 or 12 bytes) 282 BNE 4f 283 284 .p2align 3 2853: 286 # Load params pointer 287 LDR r0, [sp, 112] // cn_stride 288 LDR r5, [sp, 116] // params 289 SUBS r1, r1, 8 290 291 # Load min/max values 292 VLD1.32 {d4[],d5[]}, [r5]! 293 VLD1.32 {d6[],d7[]}, [r5] 294 295 # Clamp 296 VMAX.F32 q8, q8, q2 297 VMAX.F32 q9, q9, q2 298 VMAX.F32 q10, q10, q2 299 VMAX.F32 q11, q11, q2 300 VMAX.F32 q12, q12, q2 301 VMAX.F32 q13, q13, q2 302 VMAX.F32 q14, q14, q2 303 VMAX.F32 q15, q15, q2 304 VMIN.F32 q8, q8, q3 305 VMIN.F32 q9, q9, q3 306 VMIN.F32 q10, q10, q3 307 VMIN.F32 q11, q11, q3 308 VMIN.F32 q12, q12, q3 309 VMIN.F32 q13, q13, q3 310 VMIN.F32 q14, q14, q3 311 VMIN.F32 q15, q15, q3 312 313 # Store full 4 x 8 314 BLO 6f 315 VST1.32 {d16-d19}, [r11], r0 316 SUB r7, r7, r2 317 VST1.32 {d20-d23}, [r4], r0 318 SUB r10, r10, r2 319 VST1.32 {d24-d27}, [r8], r0 320 SUB r12, r12, r2 321 VST1.32 {d28-d31}, [r6], r0 322 SUB r3, r3, r2 323 BHI 0b 324 325 POP {r4, r5, r6, r7, r8, r9, r10, r11} 326 VPOP {d8-d15} 327 BX lr 328 329 .p2align 3 3304: 331 # Is there a remainder?- 2 floats of A (8 bytes) 332 TST r5, 8 333 BEQ 5f 334 335 # Remainder - 2 floats of A (8 bytes) 336 VLD1.32 {d0}, [r3]! // A0 337 VLDM r9!, {d8-d11} // B0 338 VLD1.32 {d1}, [r12]! // A1 339 VLD1.32 {d2}, [r10]! // A2 340 VLD1.32 {d3}, [ r7]! // A3 341 342 VMLA.F32 q8, q4, d0[0] 343 VMLA.F32 q9, q5, d0[0] 344 VMLA.F32 q10, q4, d1[0] 345 VMLA.F32 q11, q5, d1[0] 346 VLDM r9!, {d12-d15} // B1 347 VMLA.F32 q12, q4, d2[0] 348 VMLA.F32 q13, q5, d2[0] 349 VMLA.F32 q14, q4, d3[0] 350 VMLA.F32 q15, q5, d3[0] 351 VMLA.F32 q8, q6, d0[1] 352 VMLA.F32 q9, q7, d0[1] 353 VMLA.F32 q10, q6, d1[1] 354 VMLA.F32 q11, q7, d1[1] 355 VMLA.F32 q12, q6, d2[1] 356 VMLA.F32 q13, q7, d2[1] 357 VMLA.F32 q14, q6, d3[1] 358 VMLA.F32 q15, q7, d3[1] 359 360 # Is there a remainder?- 1 floats of A (4 bytes) 361 TST r5, 4 362 BEQ 3b 363 3645: 365 # Remainder- 1 floats of A (4 bytes) 366 VLDM r3!, {s0} // A0 367 VLDM r9!, {d8-d11} // B0 368 VLDM r12!, {s2} // A1 369 VLDM r10!, {s4} // A2 370 VLDM r7!, {s6} // A3 371 VMLA.F32 q8, q4, d0[0] 372 VMLA.F32 q9, q5, d0[0] 373 VMLA.F32 q10, q4, d1[0] 374 VMLA.F32 q11, q5, d1[0] 375 VMLA.F32 q12, q4, d2[0] 376 VMLA.F32 q13, q5, d2[0] 377 VMLA.F32 q14, q4, d3[0] 378 VMLA.F32 q15, q5, d3[0] 379 B 3b 380 381 # Store odd width 3826: 383 TST r1, 4 384 BEQ 7f 385 VST1.32 {d16-d17}, [r11]! 386 VST1.32 {d20-d21}, [r4]! 387 VMOV q8, q9 388 VMOV q10, q11 389 VST1.32 {d24-d25}, [r8]! 390 VST1.32 {d28-d29}, [r6]! 391 VMOV q12, q13 392 VMOV q14, q15 393 3947: 395 TST r1, 2 396 BEQ 8f 397 VST1.32 {d16}, [r11]! 398 VST1.32 {d20}, [r4]! 399 VMOV d16, d17 400 VMOV d20, d21 401 VST1.32 {d24}, [r8]! 402 VST1.32 {d28}, [r6]! 403 VMOV d24, d25 404 VMOV d28, d29 405 4068: 407 TST r1, 1 408 BEQ 9f 409 VST1.32 {d16[0]}, [r11] 410 VST1.32 {d20[0]}, [r4] 411 VST1.32 {d24[0]}, [r8] 412 VST1.32 {d28[0]}, [r6] 413 4149: 415 POP {r4, r5, r6, r7, r8, r9, r10, r11} 416 VPOP {d8-d15} 417 BX lr 418 419END_FUNCTION xnn_f32_gemm_minmax_ukernel_4x8__aarch32_neon_cortex_a55 420 421#ifdef __ELF__ 422.section ".note.GNU-stack","",%progbits 423#endif 424