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# LINT.IfChange 11// void xnn_f32_igemm_minmax_ukernel_4x8__aarch32_neon${"_prfm" if PREFETCH else ""}_cortex_a75( 12// size_t mr, r0 13// size_t nc, r1 14// size_t kc, r2 -> r5 -> sp + 68 15// size_t ks, r3 -> sp + 72 -> r14 16// const float**restrict a, sp + 112 -> r2 17// const void*restrict w, sp + 116 -> r9 18// uint8_t*restrict c, sp + 120 -> r11 19// size_t cm_stride, sp + 124 -> (r6) 20// size_t cn_stride, sp + 128 -> (r7) 21// size_t a_offset, sp + 132 -> (r5) 22// const float* zero, sp + 136 -> (r7) 23// minmax_params*params, sp + 140 -> (r5) 24 25// d8-d15, r4-r11,r14(lr) need to be preserved if used. r13(sp),r15(pc) are reserved. 26 27// Register usage 28// A0 r3 d0 29// A1 r12 d1 30// A2 r10 d2 31// A3 r0 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${"_prfm" if PREFETCH else ""}_cortex_a75 44 .arm 45#ifndef __APPLE__ 46 .arch armv7-a 47 .fpu neon 48#endif 49 # Push 112 bytes 50 # r2 will be reloaded in outer loop. r3 is ks 51 PUSH {r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, lr} // +44 52 SUB sp, sp, 4 // 4 53 VPUSH {d8-d15} // +64 = 112 54 55 LDR r11, [sp, 120] // c 56 LDR r6, [sp, 124] // cm_stride 57 LDR r2, [sp, 112] // a 58 LDR r9, [sp, 116] // w 59 MOV r14, r3 // p = ks 60 61 # Clamp C pointers 62 CMP r0, 2 // if mr >= 2 63 ADD r4, r11, r6 // c1 = c0 + cm_stride 64 MOVLO r4, r11 // c1 65 // if mr > 2 66 ADD r8, r4, r6 // c2 = c1 + cm_stride 67 MOVLS r8, r4 // c2 68 CMP r0, 4 // if mr >=4 69 ADD r6, r8, r6 // c3 = c2 + cm_stride 70 MOVLO r6, r8 // c3 71 72 .p2align 3 730: 74 # Load initial bias from w into accumulators 75 VLDM r9!, {d16-d19} // Bias 76 VMOV q10, q8 77 VMOV q11, q9 78 VMOV q12, q8 79 VMOV q13, q9 80 VMOV q14, q8 81 VMOV q15, q9 82 83 $if PREFETCH: 84 PLD [r9, 0] // Prefetch B 85 PLD [r9, 64] 86 PLD [r9, 128] 87 PLD [r9, 192] 88 PLD [r9, 256] 89 PLD [r9, 320] 90 PLD [r9, 384] 91 921: 93 # Load next 4 A pointers 94 LDR r3, [r2, 0] 95 LDR r12, [r2, 4] 96 LDR r10, [r2, 8] 97 LDR r0, [r2, 12] 98 ADD r2, r2, 16 99 100 # Add a_offset 101 LDR r5, [sp, 132] // a_offset 102 LDR r7, [sp, 136] // zero 103 CMP r3, r7 // if a0 == zero 104 ADD r3, r3, r5 // a0 += a_offset 105 MOVEQ r3, r7 // a0 = zero, else += a0 + a_offset 106 CMP r12, r7 // if a1 == zero 107 ADD r12, r12, r5 // a1 += a_offset 108 MOVEQ r12, r7 // a1 = zero, else += a1 + a_offset 109 CMP r10, r7 // if a2 == zero 110 ADD r10, r10, r5 // a2 += a_offset 111 MOVEQ r10, r7 // a2 = zero, else += a2 + a_offset 112 CMP r0, r7 // if a3 == zero 113 ADD r0, r0, r5 // a3 += a_offset 114 LDR r5, [sp, 68] // kc 115 MOVEQ r0, r7 // a3 = zero, else += a3 + a_offset 116 117 $if PREFETCH: 118 PLD [r3, 0] // Prefetch A 119 PLD [r3, 64] 120 PLD [r12, 0] 121 PLD [r12, 64] 122 PLD [r10, 0] 123 PLD [r10, 64] 124 PLD [r0, 0] 125 PLD [r0, 64] 126 127 SUBS r5, r5, 16 // kc - 16 128 BLO 5f // less than 4 channels? 129 130 # Prologue 131 VLD1.32 {d0}, [r3]! // A0 132 VLDM r9!, {d8-d11} // B0 133 VLD1.32 {d1}, [r12]! // A1 134 VLD1.32 {d2}, [r10]! // A2 135 VLD1.32 {d3}, [ r0]! // A3 136 137 SUBS r5, r5, 16 138 BLO 3f // less than 4 channels? skip main loop 139 140 .p2align 3 141 142 # Main loop - 4 floats of A (16 bytes) 1432: 144 VMLA.F32 q8, q4, d0[0] 145 VLDM r9!, {d12-d15} // B1 146 VMLA.F32 q10, q4, d1[0] 147 VMLA.F32 q12, q4, d2[0] 148 VLD1.32 {d4}, [r3]! // A0 149 VMLA.F32 q14, q4, d3[0] 150 VMLA.F32 q9, q5, d0[0] 151 VLD1.32 {d5}, [r12]! // A1 152 VMLA.F32 q11, q5, d1[0] 153 VMLA.F32 q13, q5, d2[0] 154 VMLA.F32 q15, q5, d3[0] 155 VLD1.32 {d6}, [r10]! // A2 156 VMLA.F32 q8, q6, d0[1] 157 VMLA.F32 q10, q6, d1[1] 158 VLD1.32 {d7}, [ r0]! // A3 159 VMLA.F32 q12, q6, d2[1] 160 VMLA.F32 q14, q6, d3[1] 161 VLDM r9!, {d8-d11} // B0 162 VMLA.F32 q9, q7, d0[1] 163 VMLA.F32 q11, q7, d1[1] 164 VMLA.F32 q13, q7, d2[1] 165 VMLA.F32 q15, q7, d3[1] 166 167 VMLA.F32 q8, q4, d4[0] 168 VLDM r9!, {d12-d15} // B1 169 VMLA.F32 q10, q4, d5[0] 170 $if PREFETCH: 171 PLD [r3, 128] // Prefetch A0 172 VMLA.F32 q12, q4, d6[0] 173 VLD1.32 {d0}, [r3]! // A0 174 VMLA.F32 q14, q4, d7[0] 175 $if PREFETCH: 176 PLD [r12, 128] // Prefetch A1 177 VMLA.F32 q9, q5, d4[0] 178 VLD1.32 {d1}, [r12]! // A1 179 VMLA.F32 q11, q5, d5[0] 180 $if PREFETCH: 181 PLD [r10, 128] // Prefetch A2 182 VMLA.F32 q13, q5, d6[0] 183 VLD1.32 {d2}, [r10]! // A2 184 VMLA.F32 q15, q5, d7[0] 185 $if PREFETCH: 186 PLD [r0, 128] // Prefetch A3 187 VMLA.F32 q8, q6, d4[1] 188 VLD1.32 {d3}, [ r0]! // A3 189 VMLA.F32 q10, q6, d5[1] 190 $if PREFETCH: 191 PLD [r9, 352] // Prefetch B 192 VMLA.F32 q12, q6, d6[1] 193 $if PREFETCH: 194 PLD [r9, 416] // Prefetch B 195 VMLA.F32 q14, q6, d7[1] 196 VLDM r9!, {d8-d11} // B0 197 VMLA.F32 q9, q7, d4[1] 198 VMLA.F32 q11, q7, d5[1] 199 SUBS r5, r5, 16 200 VMLA.F32 q13, q7, d6[1] 201 VMLA.F32 q15, q7, d7[1] 202 BHS 2b 203 204 # Epilogue 2053: 206 VMLA.F32 q8, q4, d0[0] 207 VLDM r9!, {d12-d15} // B1 208 VMLA.F32 q10, q4, d1[0] 209 VMLA.F32 q12, q4, d2[0] 210 VLD1.32 {d4}, [r3]! // A0 211 VMLA.F32 q14, q4, d3[0] 212 VMLA.F32 q9, q5, d0[0] 213 VLD1.32 {d5}, [r12]! // A1 214 VMLA.F32 q11, q5, d1[0] 215 VMLA.F32 q13, q5, d2[0] 216 VMLA.F32 q15, q5, d3[0] 217 VLD1.32 {d6}, [r10]! // A2 218 VMLA.F32 q8, q6, d0[1] 219 VMLA.F32 q10, q6, d1[1] 220 VLD1.32 {d7}, [ r0]! // A3 221 VMLA.F32 q12, q6, d2[1] 222 VMLA.F32 q14, q6, d3[1] 223 VLDM r9!, {d8-d11} // B0 224 VMLA.F32 q9, q7, d0[1] 225 VMLA.F32 q11, q7, d1[1] 226 VMLA.F32 q13, q7, d2[1] 227 VMLA.F32 q15, q7, d3[1] 228 229 VMLA.F32 q8, q4, d4[0] 230 VLDM r9!, {d12-d15} // B1 231 VMLA.F32 q10, q4, d5[0] 232 VMLA.F32 q12, q4, d6[0] 233 VMLA.F32 q14, q4, d7[0] 234 VMLA.F32 q9, q5, d4[0] 235 VMLA.F32 q11, q5, d5[0] 236 VMLA.F32 q13, q5, d6[0] 237 VMLA.F32 q15, q5, d7[0] 238 VMLA.F32 q8, q6, d4[1] 239 VMLA.F32 q10, q6, d5[1] 240 VMLA.F32 q12, q6, d6[1] 241 VMLA.F32 q14, q6, d7[1] 242 VMLA.F32 q9, q7, d4[1] 243 VMLA.F32 q11, q7, d5[1] 244 VMLA.F32 q13, q7, d6[1] 245 VMLA.F32 q15, q7, d7[1] 246 247 # Is there a remainder?- 1 to 3 floats of A (4, 8 or 12 bytes) 248 TST r5, 12 249 BNE 5f 250 251 .p2align 3 2524: 253 # ks loop 254 SUBS r14, r14, 16 // ks -= MR * sizeof(void*) 255 BHI 1b 256 257 # Load params pointer 258 LDR r5, [sp, 140] // params 259 LDR r7, [sp, 128] // cn_stride 260 LDR r14, [sp, 72] // p = ks 261 262 # Load min/max values 263 VLD1.32 {d4[],d5[]}, [r5]! 264 SUBS r1, r1, 8 265 VLD1.32 {d6[],d7[]}, [r5] 266 267 # Clamp 268 VMAX.F32 q8, q8, q2 269 VMAX.F32 q9, q9, q2 270 VMAX.F32 q10, q10, q2 271 VMAX.F32 q11, q11, q2 272 VMAX.F32 q12, q12, q2 273 VMAX.F32 q13, q13, q2 274 VMAX.F32 q14, q14, q2 275 VMAX.F32 q15, q15, q2 276 VMIN.F32 q8, q8, q3 277 VMIN.F32 q9, q9, q3 278 VMIN.F32 q10, q10, q3 279 VMIN.F32 q11, q11, q3 280 VMIN.F32 q12, q12, q3 281 VMIN.F32 q13, q13, q3 282 VMIN.F32 q14, q14, q3 283 VMIN.F32 q15, q15, q3 284 285 # Store full 4 x 8 286 BLO 7f 287 VST1.32 {d28-d31}, [r6], r7 288 VST1.32 {d24-d27}, [r8], r7 289 VST1.32 {d20-d23}, [r4], r7 290 VST1.32 {d16-d19}, [r11], r7 291 SUB r2, r2, r14 // a -= ks 292 BHI 0b 293 294 VPOP {d8-d15} 295 ADD sp, sp, 12 // skip pad, r2, r3 296 POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} 297 298 .p2align 3 2995: 300 # Is there a remainder?- 2 floats of A (8 bytes) 301 TST r5, 8 302 BEQ 6f 303 304 # Remainder - 2 floats of A (8 bytes) 305 VLD1.32 {d0}, [r3]! // A0 306 VLDM r9!, {d8-d11} // B0 307 VLD1.32 {d1}, [r12]! // A1 308 VLD1.32 {d2}, [r10]! // A2 309 VLD1.32 {d3}, [ r0]! // A3 310 311 VMLA.F32 q8, q4, d0[0] 312 VMLA.F32 q9, q5, d0[0] 313 VMLA.F32 q10, q4, d1[0] 314 VMLA.F32 q11, q5, d1[0] 315 VLDM r9!, {d12-d15} // B1 316 VMLA.F32 q12, q4, d2[0] 317 VMLA.F32 q13, q5, d2[0] 318 VMLA.F32 q14, q4, d3[0] 319 VMLA.F32 q15, q5, d3[0] 320 VMLA.F32 q8, q6, d0[1] 321 VMLA.F32 q9, q7, d0[1] 322 VMLA.F32 q10, q6, d1[1] 323 VMLA.F32 q11, q7, d1[1] 324 VMLA.F32 q12, q6, d2[1] 325 VMLA.F32 q13, q7, d2[1] 326 VMLA.F32 q14, q6, d3[1] 327 VMLA.F32 q15, q7, d3[1] 328 329 # Is there a remainder?- 1 float of A (4 bytes) 330 TST r5, 4 331 BEQ 4b 332 3336: 334 # Remainder- 1 float of A (4 bytes) 335 VLDM r3!, {s0} // A0 336 VLDM r9!, {d8-d11} // B0 337 VLDM r12!, {s2} // A1 338 VLDM r10!, {s4} // A2 339 VLDM r0!, {s6} // A3 340 VMLA.F32 q8, q4, d0[0] 341 VMLA.F32 q9, q5, d0[0] 342 VMLA.F32 q10, q4, d1[0] 343 VMLA.F32 q11, q5, d1[0] 344 VMLA.F32 q12, q4, d2[0] 345 VMLA.F32 q13, q5, d2[0] 346 VMLA.F32 q14, q4, d3[0] 347 VMLA.F32 q15, q5, d3[0] 348 B 4b 349 350 # Store odd width 3517: 352 TST r1, 4 353 BEQ 8f 354 VST1.32 {d28-d29}, [r6]! 355 VST1.32 {d24-d25}, [r8]! 356 VMOV q14, q15 357 VMOV q12, q13 358 VST1.32 {d20-d21}, [r4]! 359 VST1.32 {d16-d17}, [r11]! 360 VMOV q10, q11 361 VMOV q8, q9 362 3638: 364 TST r1, 2 365 BEQ 9f 366 VST1.32 {d28}, [r6]! 367 VST1.32 {d24}, [r8]! 368 VMOV d28, d29 369 VMOV d24, d25 370 VST1.32 {d20}, [r4]! 371 VST1.32 {d16}, [r11]! 372 VMOV d20, d21 373 VMOV d16, d17 374 3759: 376 TST r1, 1 377 BEQ 10f 378 VST1.32 {d28[0]}, [r6]! 379 VST1.32 {d24[0]}, [r8]! 380 VST1.32 {d20[0]}, [r4]! 381 VST1.32 {d16[0]}, [r11]! 382 38310: 384 VPOP {d8-d15} 385 ADD sp, sp, 12 // skip pad, r2, r3 386 POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} 387 388END_FUNCTION xnn_f32_igemm_minmax_ukernel_4x8__aarch32_neon${"_prfm" if PREFETCH else ""}_cortex_a75 389# LINT.ThenChange(4x8-aarch32-neon-cortex-a75.cc) 390 391#ifdef __ELF__ 392.section ".note.GNU-stack","",%progbits 393#endif 394