1// Auto-generated file. Do not edit! 2// Template: src/qs8-gemm/4x8-aarch32-neon-mlal-lane-cortex-a53.S.in 3// Generator: tools/xngen 4// 5// Copyright 2021 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 11#include <xnnpack/assembly.h> 12 13.syntax unified 14 15// void xnn_qc8_gemm_minmax_fp32_ukernel_4x8__aarch32_neonv8_mlal_lane_prfm_cortex_a53( 16// size_t mr, r0 17// size_t nc, r1 18// size_t kc, (r2) -> sp + 56 -> r5 19// const int8_t*restrict a, r3 20// size_t a_stride, sp + 96 -> (r7) 21// const void*restrict w, sp + 100 -> r9 22// int8_t*restrict c, sp + 104 -> r11 23// size_t cm_stride, sp + 108 -> (r6) 24// size_t cn_stride, sp + 112 -> r7 25// xnn_qs8_minmax_params params) sp + 116 -> (r5) 26 27// d8-d15, r4-r11,r14(lr) need to be preserved if used. r13(sp),r15(pc) are reserved. 28 29// Register usage 30// A0 r3 d0-d1 q0 31// A1 r12 d2-d3 q1 32// A2 r10 d4-d5 q2 33// A3 r0 d6-d7 q3 34 35// B r9 d8-d9 q4 q5 36 37// C0 r11 d16-d17 q8 d18-d19 q9 38// C1 r4 d20-d21 q10 d22-d23 q11 39// C2 r8 d24-d25 q12 d26-d27 q13 40// C3 r6 d28-d29 q14 d30-d31 q15 41 42// r2,r14 A53 gpr temporary loads 43// Unused d15 44 45// params structure is 4 bytes 46// struct { 47// int16_t output_zero_point; d13[2] 48// int8_t output_min; d13[6] 49// int8_t output_max; d13[7] 50// } xnn_qs8_minmax_params.neonv8; 51 52BEGIN_FUNCTION xnn_qc8_gemm_minmax_fp32_ukernel_4x8__aarch32_neonv8_mlal_lane_prfm_cortex_a53 53 # Push 96 bytes 54 PUSH {r2, r4, r5, r6, r7, r8, r9, r10, r11, lr} // 40 55 SUB sp, sp, 8 // +8 56 VPUSH {d8-d13} // +48 = 96 57 58 LDR r7, [sp, 96] // a_stride 59 LDR r11, [sp, 104] // c 60 LDR r6, [sp, 108] // cm_stride 61 LDR r9, [sp, 100] // w 62 LDR r5, [sp, 116] // params 63 64 # Clamp A and C pointers 65 CMP r0, 2 // if mr >= 2 66 ADD r12, r3, r7 // a1 = a0 + a_stride 67 ADD r4, r11, r6 // c1 = c0 + cm_stride 68 MOVLO r12, r3 // a1 69 MOVLO r4, r11 // c1 70 // if mr > 2 71 ADD r10, r12, r7 // a2 = a1 + a_stride 72 ADD r8, r4, r6 // c2 = c1 + cm_stride 73 MOVLS r10, r12 // a2 74 MOVLS r8, r4 // c2 75 76 CMP r0, 4 // if mr >=4 77 ADD r0, r10, r7 // a3 = a2 + a_stride 78 ADD r6, r8, r6 // c3 = c2 + cm_stride 79 MOVLO r0, r10 // a3 80 MOVLO r6, r8 // c3 81 82 # Load params values 83 VLD1.32 {d13[]}, [r5] // QC8 neonv8 params 84 LDR r7, [sp, 112] // cn_stride 85 86 PLD [r9, 64] // Prefetch B 87 PLD [r9, 128] 88 PLD [r9, 192] 89 PLD [r9, 256] 90 PLD [r9, 320] 91 PLD [r9, 384] 92 93 .p2align 3 940: 95 # Load initial bias from w into accumulators 96 VLDM r9!, {d16-d19} // Bias 97 SUBS r5, r2, 8 // k = kc - 8 98 99 VMOV q10, q8 100 PLD [r3, 64] // Prefetch A 101 VMOV q11, q9 102 PLD [r12, 64] 103 VMOV q12, q8 104 PLD [r10, 64] 105 VMOV q13, q9 106 PLD [r0, 64] 107 VMOV q14, q8 108 VMOV q15, q9 109 BLO 4f // less than 8 channels? 110 111 // Prologue - load 4A's and B0 112 VLD1.8 {d0}, [r3]! // A0 113 VLD1.8 {d2}, [r12]! // A1 114 VLD1.8 {d4}, [r10]! // A2 115 VLD1.8 {d6}, [r0]! // A3 116 VLD1.8 {d8}, [r9]! // B0 117 118 SUBS r5, r5, 8 // k = k - 8 119 BLO 2f // less than 8 channels? 120 121 // Main loop - 8 bytes 122 // 64 bytes for weights. 123 // 5 VMOVL = 4 A and 1 B = 5 cycles 124 // 7 blocks with VLD B, VMOVL, 8 VMLA = 10 cycles 125 // 1 blocks with VLD B, VMLA = 9 cycles 126 // total = 84 cycles 127 .p2align 3 1281: 129 // Extend - 5 cycles 130 VMOVL.S8 q0, d0 131 PLD [r3, 128] 132 VMOVL.S8 q4, d8 133 PLD [r9, 448] 134 VMOVL.S8 q1, d2 135 PLD [r12, 128] 136 VMOVL.S8 q2, d4 137 PLD [r0, 128] 138 VMOVL.S8 q3, d6 139 PLD [r10, 128] 140 141 // BLOCK 0 - 10 cycles 142 VLD1.8 {d10}, [r9]! // B1 143 VMLAL.S16 q8, d8, d0[0] 144 VMLAL.S16 q9, d9, d0[0] 145 VMLAL.S16 q10, d8, d2[0] 146 VMLAL.S16 q11, d9, d2[0] 147 VMOVL.S8 q5, d10 148 VMLAL.S16 q12, d8, d4[0] 149 VMLAL.S16 q13, d9, d4[0] 150 VMLAL.S16 q14, d8, d6[0] 151 VMLAL.S16 q15, d9, d6[0] 152 153 // BLOCK 1 - 10 cycles 154 VLD1.8 {d8}, [r9]! // B2 155 VMLAL.S16 q8, d10, d0[1] 156 VMLAL.S16 q9, d11, d0[1] 157 VMLAL.S16 q10, d10, d2[1] 158 VMLAL.S16 q11, d11, d2[1] 159 VMOVL.S8 q4, d8 160 VMLAL.S16 q12, d10, d4[1] 161 VMLAL.S16 q13, d11, d4[1] 162 VMLAL.S16 q14, d10, d6[1] 163 VMLAL.S16 q15, d11, d6[1] 164 165 // BLOCK 2 - 10 cycles 166 VLD1.8 {d10}, [r9]! // B3 167 VMLAL.S16 q8, d8, d0[2] 168 VMLAL.S16 q9, d9, d0[2] 169 VMLAL.S16 q10, d8, d2[2] 170 VMLAL.S16 q11, d9, d2[2] 171 VMOVL.S8 q5, d10 172 VMLAL.S16 q12, d8, d4[2] 173 VMLAL.S16 q13, d9, d4[2] 174 VMLAL.S16 q14, d8, d6[2] 175 VMLAL.S16 q15, d9, d6[2] 176 177 // BLOCK 3 - 10 cycles 178 VLD1.8 {d8}, [r9]! // B4 179 VMLAL.S16 q8, d10, d0[3] 180 VMLAL.S16 q9, d11, d0[3] 181 VMLAL.S16 q10, d10, d2[3] 182 VMLAL.S16 q11, d11, d2[3] 183 VMOVL.S8 q4, d8 184 VMLAL.S16 q12, d10, d4[3] 185 LDR r2, [r3] // A0 low 186 VMLAL.S16 q13, d11, d4[3] 187 LDR r14, [r3, 4] // A0 high 188 VMLAL.S16 q14, d10, d6[3] 189 ADD r3, r3, 8 190 VMLAL.S16 q15, d11, d6[3] 191 192 // BLOCK 4 - 10 cycles 193 VLD1.8 {d10}, [r9]! // B5 194 VMOV d0, r2, r14 // A0 VMOV 195 VMLAL.S16 q8, d8, d1[0] 196 VMLAL.S16 q9, d9, d1[0] 197 VMLAL.S16 q10, d8, d3[0] 198 VMLAL.S16 q11, d9, d3[0] 199 VMOVL.S8 q5, d10 200 VMLAL.S16 q12, d8, d5[0] 201 LDR r2, [r12] // A1 low 202 VMLAL.S16 q13, d9, d5[0] 203 LDR r14, [r12, 4] // A1 high 204 VMLAL.S16 q14, d8, d7[0] 205 ADD r12, r12, 8 206 VMLAL.S16 q15, d9, d7[0] 207 208 // BLOCK 5 - 10 cycles 209 VLD1.8 {d8}, [r9]! // B6 210 VMOV d2, r2, r14 // A1 VMOV 211 VMLAL.S16 q8, d10, d1[1] 212 VMLAL.S16 q9, d11, d1[1] 213 VMLAL.S16 q10, d10, d3[1] 214 VMLAL.S16 q11, d11, d3[1] 215 VMOVL.S8 q4, d8 216 VMLAL.S16 q12, d10, d5[1] 217 LDR r2, [r10] // A2 low 218 VMLAL.S16 q13, d11, d5[1] 219 LDR r14, [r10, 4] // A2 high 220 VMLAL.S16 q14, d10, d7[1] 221 ADD r10, r10, 8 222 VMLAL.S16 q15, d11, d7[1] 223 224 // BLOCK 6 - 10 cycles 225 VLD1.8 {d10}, [r9]! // B7 226 VMOV d4, r2, r14 // A2 VMOV 227 VMLAL.S16 q8, d8, d1[2] 228 VMLAL.S16 q9, d9, d1[2] 229 VMLAL.S16 q10, d8, d3[2] 230 VMLAL.S16 q11, d9, d3[2] 231 VMOVL.S8 q5, d10 232 VMLAL.S16 q12, d8, d5[2] 233 LDR r2, [r0] // A3 low 234 VMLAL.S16 q13, d9, d5[2] 235 LDR r14, [r0, 4] // A3 high 236 VMLAL.S16 q14, d8, d7[2] 237 ADD r0, r0, 8 238 VMLAL.S16 q15, d9, d7[2] 239 240 // BLOCK 7 - 9 cycles 241 VLD1.8 {d8}, [r9]! // B0 242 VMOV d6, r2, r14 // A3 VMOV 243 VMLAL.S16 q8, d10, d1[3] 244 VMLAL.S16 q9, d11, d1[3] 245 VMLAL.S16 q10, d10, d3[3] 246 VMLAL.S16 q11, d11, d3[3] 247 VMLAL.S16 q12, d10, d5[3] 248 VMLAL.S16 q13, d11, d5[3] 249 SUBS r5, r5, 8 250 VMLAL.S16 q14, d10, d7[3] 251 VMLAL.S16 q15, d11, d7[3] 252 BHS 1b 253 254 // Epilogue 255 256 .p2align 3 2572: 258 VMOVL.S8 q0, d0 259 VMOVL.S8 q4, d8 260 VMOVL.S8 q1, d2 261 VMOVL.S8 q2, d4 262 VMOVL.S8 q3, d6 263 264 VLD1.8 {d10}, [r9]! // B1 265 VMLAL.S16 q8, d8, d0[0] 266 VMLAL.S16 q9, d9, d0[0] 267 VMLAL.S16 q10, d8, d2[0] 268 VMLAL.S16 q11, d9, d2[0] 269 VMOVL.S8 q5, d10 270 VMLAL.S16 q12, d8, d4[0] 271 VMLAL.S16 q13, d9, d4[0] 272 VMLAL.S16 q14, d8, d6[0] 273 VMLAL.S16 q15, d9, d6[0] 274 275 VLD1.8 {d8}, [r9]! // B2 276 VMLAL.S16 q8, d10, d0[1] 277 VMLAL.S16 q9, d11, d0[1] 278 VMLAL.S16 q10, d10, d2[1] 279 VMLAL.S16 q11, d11, d2[1] 280 VMOVL.S8 q4, d8 281 VMLAL.S16 q12, d10, d4[1] 282 VMLAL.S16 q13, d11, d4[1] 283 VMLAL.S16 q14, d10, d6[1] 284 VMLAL.S16 q15, d11, d6[1] 285 286 VLD1.8 {d10}, [r9]! // B3 287 VMLAL.S16 q8, d8, d0[2] 288 VMLAL.S16 q9, d9, d0[2] 289 VMLAL.S16 q10, d8, d2[2] 290 VMLAL.S16 q11, d9, d2[2] 291 VMOVL.S8 q5, d10 292 VMLAL.S16 q12, d8, d4[2] 293 VMLAL.S16 q13, d9, d4[2] 294 VMLAL.S16 q14, d8, d6[2] 295 VMLAL.S16 q15, d9, d6[2] 296 297 VLD1.8 {d8}, [r9]! // B4 298 VMLAL.S16 q8, d10, d0[3] 299 VMLAL.S16 q9, d11, d0[3] 300 VMLAL.S16 q10, d10, d2[3] 301 VMLAL.S16 q11, d11, d2[3] 302 VMOVL.S8 q4, d8 303 VMLAL.S16 q12, d10, d4[3] 304 VMLAL.S16 q13, d11, d4[3] 305 VMLAL.S16 q14, d10, d6[3] 306 VMLAL.S16 q15, d11, d6[3] 307 308 VLD1.8 {d10}, [r9]! // B5 309 VMLAL.S16 q8, d8, d1[0] 310 VMLAL.S16 q9, d9, d1[0] 311 VMLAL.S16 q10, d8, d3[0] 312 VMLAL.S16 q11, d9, d3[0] 313 VMOVL.S8 q5, d10 314 VMLAL.S16 q12, d8, d5[0] 315 VMLAL.S16 q13, d9, d5[0] 316 VMLAL.S16 q14, d8, d7[0] 317 VMLAL.S16 q15, d9, d7[0] 318 319 VLD1.8 {d8}, [r9]! // B6 320 VMLAL.S16 q8, d10, d1[1] 321 VMLAL.S16 q9, d11, d1[1] 322 VMLAL.S16 q10, d10, d3[1] 323 VMLAL.S16 q11, d11, d3[1] 324 VMOVL.S8 q4, d8 325 VMLAL.S16 q12, d10, d5[1] 326 VMLAL.S16 q13, d11, d5[1] 327 VMLAL.S16 q14, d10, d7[1] 328 VMLAL.S16 q15, d11, d7[1] 329 330 VLD1.8 {d10}, [r9]! // B7 331 VMLAL.S16 q8, d8, d1[2] 332 VMLAL.S16 q9, d9, d1[2] 333 VMLAL.S16 q10, d8, d3[2] 334 VMLAL.S16 q11, d9, d3[2] 335 VMOVL.S8 q5, d10 336 VMLAL.S16 q12, d8, d5[2] 337 VMLAL.S16 q13, d9, d5[2] 338 VMLAL.S16 q14, d8, d7[2] 339 VMLAL.S16 q15, d9, d7[2] 340 341 VMLAL.S16 q8, d10, d1[3] 342 VMLAL.S16 q9, d11, d1[3] 343 VMLAL.S16 q10, d10, d3[3] 344 VMLAL.S16 q11, d11, d3[3] 345 VMLAL.S16 q12, d10, d5[3] 346 VMLAL.S16 q13, d11, d5[3] 347 ADDS r5, r5, 8 348 VMLAL.S16 q14, d10, d7[3] 349 VMLAL.S16 q15, d11, d7[3] 350 351 # Is there a remainder?- 1-7 bytes of A 352 BNE 4f 353 3543: 355 # QC8 FP32 quantization 356 VLD1.8 {q0-q1}, [r9]! 357 358 VCVT.F32.S32 q8, q8 359 VCVT.F32.S32 q9, q9 360 VCVT.F32.S32 q10, q10 361 VCVT.F32.S32 q11, q11 362 VCVT.F32.S32 q12, q12 363 VCVT.F32.S32 q13, q13 364 VCVT.F32.S32 q14, q14 365 VCVT.F32.S32 q15, q15 366 367 VMUL.F32 q8, q8, q0 // multiplier 368 VMUL.F32 q9, q9, q1 369 VMUL.F32 q10, q10, q0 370 VMUL.F32 q11, q11, q1 371 VMUL.F32 q12, q12, q0 372 VMUL.F32 q13, q13, q1 373 VMUL.F32 q14, q14, q0 374 VMUL.F32 q15, q15, q1 375 376 VCVTN.S32.F32 q8, q8 377 VCVTN.S32.F32 q9, q9 378 VCVTN.S32.F32 q10, q10 379 VCVTN.S32.F32 q11, q11 380 VCVTN.S32.F32 q12, q12 381 VCVTN.S32.F32 q13, q13 382 VCVTN.S32.F32 q14, q14 383 VCVTN.S32.F32 q15, q15 384 385 VDUP.16 q0, d13[2] // output_zero_point 386 387 VQMOVN.S32 d16, q8 388 VQMOVN.S32 d17, q9 389 VQMOVN.S32 d18, q10 390 VQMOVN.S32 d19, q11 391 VQMOVN.S32 d20, q12 392 VQMOVN.S32 d21, q13 393 VQMOVN.S32 d22, q14 394 VQMOVN.S32 d23, q15 395 396 VQADD.S16 q8, q8, q0 397 VQADD.S16 q9, q9, q0 398 VQADD.S16 q10, q10, q0 399 VQADD.S16 q11, q11, q0 400 401 VDUP.8 q12, d13[6] // output_min 402 403 VQMOVN.S16 d0, q8 404 VQMOVN.S16 d1, q9 405 VQMOVN.S16 d2, q10 406 VQMOVN.S16 d3, q11 407 408 VDUP.8 q13, d13[7] // output_max 409 410 VMAX.S8 q0, q0, q12 411 VMAX.S8 q1, q1, q12 412 413 LDR r2, [sp, 56] // kc 414 SUBS r1, r1, 8 415 416 VMIN.S8 q0, q0, q13 417 VMIN.S8 q1, q1, q13 418 419 # Store full 4 x 8 420 BLO 5f 421 VST1.8 {d0}, [r11], r7 422 SUB r3, r3, r2 423 VST1.8 {d1}, [r4], r7 424 SUB r12, r12, r2 425 VST1.8 {d2}, [r8], r7 426 SUB r10, r10, r2 427 VST1.8 {d3}, [r6], r7 428 SUB r0, r0, r2 429 BHI 0b 430 431 VPOP {d8-d13} 432 ADD sp, sp, 12 // skip pad of 8 + r2 433 POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} 434 435 # Remainder- 1 to 7 bytes of A 436 .p2align 3 4374: 438 AND r5, r5, 7 // kc remainder 1 to 7 439 440 VLD1.8 {d0}, [r3], r5 441 VLD1.8 {d8}, [r9]! 442 VLD1.8 {d2}, [r12], r5 443 VLD1.8 {d4}, [r10], r5 444 VLD1.8 {d6}, [r0], r5 445 446 VMOVL.S8 q0, d0 447 VMOVL.S8 q4, d8 448 VMOVL.S8 q1, d2 449 VMOVL.S8 q2, d4 450 VMOVL.S8 q3, d6 451 VMLAL.S16 q8, d8, d0[0] 452 VMLAL.S16 q9, d9, d0[0] 453 VMLAL.S16 q10, d8, d2[0] 454 VMLAL.S16 q11, d9, d2[0] 455 VMLAL.S16 q12, d8, d4[0] 456 VMLAL.S16 q13, d9, d4[0] 457 VMLAL.S16 q14, d8, d6[0] 458 VMLAL.S16 q15, d9, d6[0] 459 CMP r5, 2 460 BLO 3b 461 462 VLD1.8 {d8}, [r9]! 463 VMOVL.S8 q4, d8 464 VMLAL.S16 q8, d8, d0[1] 465 VMLAL.S16 q9, d9, d0[1] 466 VMLAL.S16 q10, d8, d2[1] 467 VMLAL.S16 q11, d9, d2[1] 468 VMLAL.S16 q12, d8, d4[1] 469 VMLAL.S16 q13, d9, d4[1] 470 VMLAL.S16 q14, d8, d6[1] 471 VMLAL.S16 q15, d9, d6[1] 472 BEQ 3b 473 474 VLD1.8 {d8}, [r9]! 475 VMOVL.S8 q4, d8 476 VMLAL.S16 q8, d8, d0[2] 477 VMLAL.S16 q9, d9, d0[2] 478 VMLAL.S16 q10, d8, d2[2] 479 VMLAL.S16 q11, d9, d2[2] 480 VMLAL.S16 q12, d8, d4[2] 481 VMLAL.S16 q13, d9, d4[2] 482 VMLAL.S16 q14, d8, d6[2] 483 VMLAL.S16 q15, d9, d6[2] 484 CMP r5, 4 485 BLO 3b 486 487 VLD1.8 {d8}, [r9]! 488 VMOVL.S8 q4, d8 489 VMLAL.S16 q8, d8, d0[3] 490 VMLAL.S16 q9, d9, d0[3] 491 VMLAL.S16 q10, d8, d2[3] 492 VMLAL.S16 q11, d9, d2[3] 493 VMLAL.S16 q12, d8, d4[3] 494 VMLAL.S16 q13, d9, d4[3] 495 VMLAL.S16 q14, d8, d6[3] 496 VMLAL.S16 q15, d9, d6[3] 497 BEQ 3b 498 499 VLD1.8 {d8}, [r9]! 500 VMOVL.S8 q4, d8 501 VMLAL.S16 q8, d8, d1[0] 502 VMLAL.S16 q9, d9, d1[0] 503 VMLAL.S16 q10, d8, d3[0] 504 VMLAL.S16 q11, d9, d3[0] 505 VMLAL.S16 q12, d8, d5[0] 506 VMLAL.S16 q13, d9, d5[0] 507 VMLAL.S16 q14, d8, d7[0] 508 VMLAL.S16 q15, d9, d7[0] 509 CMP r5, 6 510 BLO 3b 511 512 VLD1.8 {d8}, [r9]! 513 VMOVL.S8 q4, d8 514 VMLAL.S16 q8, d8, d1[1] 515 VMLAL.S16 q9, d9, d1[1] 516 VMLAL.S16 q10, d8, d3[1] 517 VMLAL.S16 q11, d9, d3[1] 518 VMLAL.S16 q12, d8, d5[1] 519 VMLAL.S16 q13, d9, d5[1] 520 VMLAL.S16 q14, d8, d7[1] 521 VMLAL.S16 q15, d9, d7[1] 522 BEQ 3b 523 524 VLD1.8 {d8}, [r9]! 525 VMOVL.S8 q4, d8 526 VMLAL.S16 q8, d8, d1[2] 527 VMLAL.S16 q9, d9, d1[2] 528 VMLAL.S16 q10, d8, d3[2] 529 VMLAL.S16 q11, d9, d3[2] 530 VMLAL.S16 q12, d8, d5[2] 531 VMLAL.S16 q13, d9, d5[2] 532 VMLAL.S16 q14, d8, d7[2] 533 VMLAL.S16 q15, d9, d7[2] 534 B 3b 535 536 # Store odd width 537 .p2align 3 5385: 539 TST r1, 4 540 BEQ 6f 541 VST1.32 {d0[0]}, [r11]! 542 VST1.32 {d1[0]}, [r4]! 543 VST1.32 {d2[0]}, [r8]! 544 VST1.32 {d3[0]}, [r6]! 545 VEXT.8 q0, q0, q0, 4 546 VEXT.8 q1, q1, q1, 4 5476: 548 TST r1, 2 549 BEQ 7f 550 VST1.16 {d0[0]}, [r11]! 551 VST1.16 {d1[0]}, [r4]! 552 VST1.16 {d2[0]}, [r8]! 553 VST1.16 {d3[0]}, [r6]! 554 VEXT.8 q0, q0, q0, 2 555 VEXT.8 q1, q1, q1, 2 556 5577: 558 TST r1, 1 559 BEQ 8f 560 VST1.8 {d0[0]}, [r11] 561 VST1.8 {d1[0]}, [r4] 562 VST1.8 {d2[0]}, [r8] 563 VST1.8 {d3[0]}, [r6] 564 5658: 566 VPOP {d8-d13} 567 ADD sp, sp, 12 // skip pad of 8 + r2 568 POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} 569 570 571END_FUNCTION xnn_qc8_gemm_minmax_fp32_ukernel_4x8__aarch32_neonv8_mlal_lane_prfm_cortex_a53 572 573#ifdef __ELF__ 574.section ".note.GNU-stack","",%progbits 575#endif 576 577