github.com/bir3/gocompiler@v0.9.2202/src/xvendor/golang.org/x/arch/arm64/arm64asm/plan9x.go (about) 1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package arm64asm 6 7 import ( 8 "fmt" 9 "io" 10 "sort" 11 "strings" 12 ) 13 14 // GoSyntax returns the Go assembler syntax for the instruction. 15 // The syntax was originally defined by Plan 9. 16 // The pc is the program counter of the instruction, used for 17 // expanding PC-relative addresses into absolute ones. 18 // The symname function queries the symbol table for the program 19 // being disassembled. Given a target address it returns the name 20 // and base address of the symbol containing the target, if any; 21 // otherwise it returns "", 0. 22 // The reader text should read from the text segment using text addresses 23 // as offsets; it is used to display pc-relative loads as constant loads. 24 func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64), text io.ReaderAt) string { 25 if symname == nil { 26 symname = func(uint64) (string, uint64) { return "", 0 } 27 } 28 29 var args []string 30 for _, a := range inst.Args { 31 if a == nil { 32 break 33 } 34 args = append(args, plan9Arg(&inst, pc, symname, a)) 35 } 36 37 op := inst.Op.String() 38 39 switch inst.Op { 40 case LDR, LDRB, LDRH, LDRSB, LDRSH, LDRSW: 41 // Check for PC-relative load. 42 if offset, ok := inst.Args[1].(PCRel); ok { 43 addr := pc + uint64(offset) 44 if _, ok := inst.Args[0].(Reg); !ok { 45 break 46 } 47 if s, base := symname(addr); s != "" && addr == base { 48 args[1] = fmt.Sprintf("$%s(SB)", s) 49 } 50 } 51 } 52 53 // Move addressing mode into opcode suffix. 54 suffix := "" 55 switch inst.Op { 56 case LDR, LDRB, LDRH, LDRSB, LDRSH, LDRSW, STR, STRB, STRH, STUR, STURB, STURH, LD1, ST1: 57 switch mem := inst.Args[1].(type) { 58 case MemImmediate: 59 switch mem.Mode { 60 case AddrOffset: 61 // no suffix 62 case AddrPreIndex: 63 suffix = ".W" 64 case AddrPostIndex, AddrPostReg: 65 suffix = ".P" 66 } 67 } 68 69 case STP, LDP: 70 switch mem := inst.Args[2].(type) { 71 case MemImmediate: 72 switch mem.Mode { 73 case AddrOffset: 74 // no suffix 75 case AddrPreIndex: 76 suffix = ".W" 77 case AddrPostIndex: 78 suffix = ".P" 79 } 80 } 81 } 82 83 switch inst.Op { 84 case BL: 85 return "CALL " + args[0] 86 87 case BLR: 88 r := inst.Args[0].(Reg) 89 regno := uint16(r) & 31 90 return fmt.Sprintf("CALL (R%d)", regno) 91 92 case RET: 93 if r, ok := inst.Args[0].(Reg); ok && r == X30 { 94 return "RET" 95 } 96 97 case B: 98 if cond, ok := inst.Args[0].(Cond); ok { 99 return "B" + cond.String() + " " + args[1] 100 } 101 return "JMP" + " " + args[0] 102 103 case BR: 104 r := inst.Args[0].(Reg) 105 regno := uint16(r) & 31 106 return fmt.Sprintf("JMP (R%d)", regno) 107 108 case MOV: 109 rno := -1 110 switch a := inst.Args[0].(type) { 111 case Reg: 112 rno = int(a) 113 case RegSP: 114 rno = int(a) 115 case RegisterWithArrangementAndIndex: 116 op = "VMOV" 117 case RegisterWithArrangement: 118 op = "VMOV" 119 } 120 if rno >= 0 && rno <= int(WZR) { 121 op = "MOVW" 122 } else if rno >= int(X0) && rno <= int(XZR) { 123 op = "MOVD" 124 } 125 if _, ok := inst.Args[1].(RegisterWithArrangementAndIndex); ok { 126 op = "VMOV" 127 } 128 129 case LDR, LDUR: 130 var rno uint16 131 if r, ok := inst.Args[0].(Reg); ok { 132 rno = uint16(r) 133 } else { 134 rno = uint16(inst.Args[0].(RegSP)) 135 } 136 if rno <= uint16(WZR) { 137 op = "MOVWU" + suffix 138 } else if rno >= uint16(B0) && rno <= uint16(B31) { 139 op = "FMOVB" + suffix 140 args[0] = fmt.Sprintf("F%d", rno&31) 141 } else if rno >= uint16(H0) && rno <= uint16(H31) { 142 op = "FMOVH" + suffix 143 args[0] = fmt.Sprintf("F%d", rno&31) 144 } else if rno >= uint16(S0) && rno <= uint16(S31) { 145 op = "FMOVS" + suffix 146 args[0] = fmt.Sprintf("F%d", rno&31) 147 } else if rno >= uint16(D0) && rno <= uint16(D31) { 148 op = "FMOVD" + suffix 149 args[0] = fmt.Sprintf("F%d", rno&31) 150 } else if rno >= uint16(Q0) && rno <= uint16(Q31) { 151 op = "FMOVQ" + suffix 152 args[0] = fmt.Sprintf("F%d", rno&31) 153 } else { 154 op = "MOVD" + suffix 155 } 156 157 case LDRB: 158 op = "MOVBU" + suffix 159 160 case LDRH: 161 op = "MOVHU" + suffix 162 163 case LDRSW: 164 op = "MOVW" + suffix 165 166 case LDRSB: 167 if r, ok := inst.Args[0].(Reg); ok { 168 rno := uint16(r) 169 if rno <= uint16(WZR) { 170 op = "MOVBW" + suffix 171 } else { 172 op = "MOVB" + suffix 173 } 174 } 175 case LDRSH: 176 if r, ok := inst.Args[0].(Reg); ok { 177 rno := uint16(r) 178 if rno <= uint16(WZR) { 179 op = "MOVHW" + suffix 180 } else { 181 op = "MOVH" + suffix 182 } 183 } 184 case STR, STUR: 185 var rno uint16 186 if r, ok := inst.Args[0].(Reg); ok { 187 rno = uint16(r) 188 } else { 189 rno = uint16(inst.Args[0].(RegSP)) 190 } 191 if rno <= uint16(WZR) { 192 op = "MOVW" + suffix 193 } else if rno >= uint16(B0) && rno <= uint16(B31) { 194 op = "FMOVB" + suffix 195 args[0] = fmt.Sprintf("F%d", rno&31) 196 } else if rno >= uint16(H0) && rno <= uint16(H31) { 197 op = "FMOVH" + suffix 198 args[0] = fmt.Sprintf("F%d", rno&31) 199 } else if rno >= uint16(S0) && rno <= uint16(S31) { 200 op = "FMOVS" + suffix 201 args[0] = fmt.Sprintf("F%d", rno&31) 202 } else if rno >= uint16(D0) && rno <= uint16(D31) { 203 op = "FMOVD" + suffix 204 args[0] = fmt.Sprintf("F%d", rno&31) 205 } else if rno >= uint16(Q0) && rno <= uint16(Q31) { 206 op = "FMOVQ" + suffix 207 args[0] = fmt.Sprintf("F%d", rno&31) 208 } else { 209 op = "MOVD" + suffix 210 } 211 args[0], args[1] = args[1], args[0] 212 213 case STRB, STURB: 214 op = "MOVB" + suffix 215 args[0], args[1] = args[1], args[0] 216 217 case STRH, STURH: 218 op = "MOVH" + suffix 219 args[0], args[1] = args[1], args[0] 220 221 case TBNZ, TBZ: 222 args[0], args[1], args[2] = args[2], args[0], args[1] 223 224 case MADD, MSUB, SMADDL, SMSUBL, UMADDL, UMSUBL: 225 if r, ok := inst.Args[0].(Reg); ok { 226 rno := uint16(r) 227 if rno <= uint16(WZR) { 228 op += "W" 229 } 230 } 231 args[2], args[3] = args[3], args[2] 232 case STLR: 233 if r, ok := inst.Args[0].(Reg); ok { 234 rno := uint16(r) 235 if rno <= uint16(WZR) { 236 op += "W" 237 } 238 } 239 args[0], args[1] = args[1], args[0] 240 241 case STLRB, STLRH: 242 args[0], args[1] = args[1], args[0] 243 244 case STLXR, STXR: 245 if r, ok := inst.Args[1].(Reg); ok { 246 rno := uint16(r) 247 if rno <= uint16(WZR) { 248 op += "W" 249 } 250 } 251 args[1], args[2] = args[2], args[1] 252 253 case STLXRB, STLXRH, STXRB, STXRH: 254 args[1], args[2] = args[2], args[1] 255 256 case BFI, BFXIL, SBFIZ, SBFX, UBFIZ, UBFX: 257 if r, ok := inst.Args[0].(Reg); ok { 258 rno := uint16(r) 259 if rno <= uint16(WZR) { 260 op += "W" 261 } 262 } 263 args[1], args[2], args[3] = args[3], args[1], args[2] 264 265 case LDAXP, LDXP: 266 if r, ok := inst.Args[0].(Reg); ok { 267 rno := uint16(r) 268 if rno <= uint16(WZR) { 269 op += "W" 270 } 271 } 272 args[0] = fmt.Sprintf("(%s, %s)", args[0], args[1]) 273 args[1] = args[2] 274 return op + " " + args[1] + ", " + args[0] 275 276 case STP, LDP: 277 args[0] = fmt.Sprintf("(%s, %s)", args[0], args[1]) 278 args[1] = args[2] 279 280 rno, ok := inst.Args[0].(Reg) 281 if !ok { 282 rno = Reg(inst.Args[0].(RegSP)) 283 } 284 if rno <= WZR { 285 op = op + "W" 286 } else if rno >= S0 && rno <= S31 { 287 op = "F" + op + "S" 288 } else if rno >= D0 && rno <= D31 { 289 op = "F" + op + "D" 290 } else if rno >= Q0 && rno <= Q31 { 291 op = "F" + op + "Q" 292 } 293 op = op + suffix 294 if inst.Op.String() == "STP" { 295 return op + " " + args[0] + ", " + args[1] 296 } else { 297 return op + " " + args[1] + ", " + args[0] 298 } 299 300 case STLXP, STXP: 301 if r, ok := inst.Args[1].(Reg); ok { 302 rno := uint16(r) 303 if rno <= uint16(WZR) { 304 op += "W" 305 } 306 } 307 args[1] = fmt.Sprintf("(%s, %s)", args[1], args[2]) 308 args[2] = args[3] 309 return op + " " + args[1] + ", " + args[2] + ", " + args[0] 310 311 case FCCMP, FCCMPE: 312 args[0], args[1] = args[1], args[0] 313 fallthrough 314 315 case FCMP, FCMPE: 316 if _, ok := inst.Args[1].(Imm); ok { 317 args[1] = "$(0.0)" 318 } 319 fallthrough 320 321 case FADD, FSUB, FMUL, FNMUL, FDIV, FMAX, FMIN, FMAXNM, FMINNM, FCSEL, FMADD, FMSUB, FNMADD, FNMSUB: 322 if strings.HasSuffix(op, "MADD") || strings.HasSuffix(op, "MSUB") { 323 args[2], args[3] = args[3], args[2] 324 } 325 if r, ok := inst.Args[0].(Reg); ok { 326 rno := uint16(r) 327 if rno >= uint16(S0) && rno <= uint16(S31) { 328 op = fmt.Sprintf("%sS", op) 329 } else if rno >= uint16(D0) && rno <= uint16(D31) { 330 op = fmt.Sprintf("%sD", op) 331 } 332 } 333 334 case FCVT: 335 for i := 1; i >= 0; i-- { 336 if r, ok := inst.Args[i].(Reg); ok { 337 rno := uint16(r) 338 if rno >= uint16(H0) && rno <= uint16(H31) { 339 op = fmt.Sprintf("%sH", op) 340 } else if rno >= uint16(S0) && rno <= uint16(S31) { 341 op = fmt.Sprintf("%sS", op) 342 } else if rno >= uint16(D0) && rno <= uint16(D31) { 343 op = fmt.Sprintf("%sD", op) 344 } 345 } 346 } 347 348 case FABS, FNEG, FSQRT, FRINTN, FRINTP, FRINTM, FRINTZ, FRINTA, FRINTX, FRINTI: 349 if r, ok := inst.Args[1].(Reg); ok { 350 rno := uint16(r) 351 if rno >= uint16(S0) && rno <= uint16(S31) { 352 op = fmt.Sprintf("%sS", op) 353 } else if rno >= uint16(D0) && rno <= uint16(D31) { 354 op = fmt.Sprintf("%sD", op) 355 } 356 } 357 358 case FCVTZS, FCVTZU, SCVTF, UCVTF: 359 if _, ok := inst.Args[2].(Imm); !ok { 360 for i := 1; i >= 0; i-- { 361 if r, ok := inst.Args[i].(Reg); ok { 362 rno := uint16(r) 363 if rno >= uint16(S0) && rno <= uint16(S31) { 364 op = fmt.Sprintf("%sS", op) 365 } else if rno >= uint16(D0) && rno <= uint16(D31) { 366 op = fmt.Sprintf("%sD", op) 367 } else if rno <= uint16(WZR) { 368 op += "W" 369 } 370 } 371 } 372 } 373 374 case FMOV: 375 for i := 0; i <= 1; i++ { 376 if r, ok := inst.Args[i].(Reg); ok { 377 rno := uint16(r) 378 if rno >= uint16(S0) && rno <= uint16(S31) { 379 op = fmt.Sprintf("%sS", op) 380 break 381 } else if rno >= uint16(D0) && rno <= uint16(D31) { 382 op = fmt.Sprintf("%sD", op) 383 break 384 } 385 } 386 } 387 388 case SYSL: 389 op1 := int(inst.Args[1].(Imm).Imm) 390 cn := int(inst.Args[2].(Imm_c)) 391 cm := int(inst.Args[3].(Imm_c)) 392 op2 := int(inst.Args[4].(Imm).Imm) 393 sysregno := int32(op1<<16 | cn<<12 | cm<<8 | op2<<5) 394 args[1] = fmt.Sprintf("$%d", sysregno) 395 return op + " " + args[1] + ", " + args[0] 396 397 case CBNZ, CBZ: 398 if r, ok := inst.Args[0].(Reg); ok { 399 rno := uint16(r) 400 if rno <= uint16(WZR) { 401 op += "W" 402 } 403 } 404 args[0], args[1] = args[1], args[0] 405 406 case ADR, ADRP: 407 addr := int64(inst.Args[1].(PCRel)) 408 args[1] = fmt.Sprintf("%d(PC)", addr) 409 410 case MSR: 411 args[0] = inst.Args[0].String() 412 413 case ST1: 414 op = fmt.Sprintf("V%s", op) + suffix 415 args[0], args[1] = args[1], args[0] 416 417 case LD1: 418 op = fmt.Sprintf("V%s", op) + suffix 419 420 case UMOV: 421 op = "VMOV" 422 case NOP: 423 op = "NOOP" 424 425 default: 426 index := sort.SearchStrings(noSuffixOpSet, op) 427 if !(index < len(noSuffixOpSet) && noSuffixOpSet[index] == op) { 428 rno := -1 429 switch a := inst.Args[0].(type) { 430 case Reg: 431 rno = int(a) 432 case RegSP: 433 rno = int(a) 434 case RegisterWithArrangement: 435 op = fmt.Sprintf("V%s", op) 436 } 437 438 if rno >= int(B0) && rno <= int(Q31) && !strings.HasPrefix(op, "F") { 439 op = fmt.Sprintf("V%s", op) 440 } 441 if rno >= 0 && rno <= int(WZR) { 442 // Add "w" to opcode suffix. 443 op += "W" 444 } 445 } 446 op = op + suffix 447 } 448 449 // conditional instructions, replace args. 450 if _, ok := inst.Args[3].(Cond); ok { 451 if _, ok := inst.Args[2].(Reg); ok { 452 args[1], args[2] = args[2], args[1] 453 } else { 454 args[0], args[2] = args[2], args[0] 455 } 456 } 457 // Reverse args, placing dest last. 458 for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 { 459 args[i], args[j] = args[j], args[i] 460 } 461 462 if args != nil { 463 op += " " + strings.Join(args, ", ") 464 } 465 466 return op 467 } 468 469 // No need add "W" to opcode suffix. 470 // Opcode must be inserted in ascending order. 471 var noSuffixOpSet = strings.Fields(` 472 AESD 473 AESE 474 AESIMC 475 AESMC 476 CRC32B 477 CRC32CB 478 CRC32CH 479 CRC32CW 480 CRC32CX 481 CRC32H 482 CRC32W 483 CRC32X 484 LDARB 485 LDARH 486 LDAXRB 487 LDAXRH 488 LDTRH 489 LDXRB 490 LDXRH 491 SHA1C 492 SHA1H 493 SHA1M 494 SHA1P 495 SHA1SU0 496 SHA1SU1 497 SHA256H 498 SHA256H2 499 SHA256SU0 500 SHA256SU1 501 `) 502 503 // floating point instructions without "F" prefix. 504 var fOpsWithoutFPrefix = map[Op]bool{ 505 LDP: true, 506 STP: true, 507 } 508 509 func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg Arg) string { 510 switch a := arg.(type) { 511 case Imm: 512 return fmt.Sprintf("$%d", uint32(a.Imm)) 513 514 case Imm64: 515 return fmt.Sprintf("$%d", int64(a.Imm)) 516 517 case ImmShift: 518 if a.shift == 0 { 519 return fmt.Sprintf("$%d", a.imm) 520 } 521 return fmt.Sprintf("$(%d<<%d)", a.imm, a.shift) 522 523 case PCRel: 524 addr := int64(pc) + int64(a) 525 if s, base := symname(uint64(addr)); s != "" && uint64(addr) == base { 526 return fmt.Sprintf("%s(SB)", s) 527 } 528 return fmt.Sprintf("%d(PC)", a/4) 529 530 case Reg: 531 regenum := uint16(a) 532 regno := uint16(a) & 31 533 534 if regenum >= uint16(B0) && regenum <= uint16(Q31) { 535 if strings.HasPrefix(inst.Op.String(), "F") || strings.HasSuffix(inst.Op.String(), "CVTF") || fOpsWithoutFPrefix[inst.Op] { 536 // FP registers are the same ones as SIMD registers 537 // Print Fn for scalar variant to align with assembler (e.g., FCVT, SCVTF, UCVTF, etc.) 538 return fmt.Sprintf("F%d", regno) 539 } else { 540 // Print Vn to align with assembler (e.g., SHA256H) 541 return fmt.Sprintf("V%d", regno) 542 } 543 544 } 545 return plan9gpr(a) 546 547 case RegSP: 548 regno := uint16(a) & 31 549 if regno == 31 { 550 return "RSP" 551 } 552 return fmt.Sprintf("R%d", regno) 553 554 case RegExtshiftAmount: 555 reg := plan9gpr(a.reg) 556 extshift := "" 557 amount := "" 558 if a.extShift != ExtShift(0) { 559 switch a.extShift { 560 default: 561 extshift = "." + a.extShift.String() 562 563 case lsl: 564 extshift = "<<" 565 amount = fmt.Sprintf("%d", a.amount) 566 return reg + extshift + amount 567 568 case lsr: 569 extshift = ">>" 570 amount = fmt.Sprintf("%d", a.amount) 571 return reg + extshift + amount 572 573 case asr: 574 extshift = "->" 575 amount = fmt.Sprintf("%d", a.amount) 576 return reg + extshift + amount 577 case ror: 578 extshift = "@>" 579 amount = fmt.Sprintf("%d", a.amount) 580 return reg + extshift + amount 581 } 582 if a.amount != 0 { 583 amount = fmt.Sprintf("<<%d", a.amount) 584 } 585 } 586 return reg + extshift + amount 587 588 case MemImmediate: 589 off := "" 590 base := "" 591 regno := uint16(a.Base) & 31 592 if regno == 31 { 593 base = "(RSP)" 594 } else { 595 base = fmt.Sprintf("(R%d)", regno) 596 } 597 if a.imm != 0 && a.Mode != AddrPostReg { 598 off = fmt.Sprintf("%d", a.imm) 599 } else if a.Mode == AddrPostReg { 600 postR := fmt.Sprintf("(R%d)", a.imm) 601 return base + postR 602 } 603 return off + base 604 605 case MemExtend: 606 base := "" 607 index := "" 608 regno := uint16(a.Base) & 31 609 if regno == 31 { 610 base = "(RSP)" 611 } else { 612 base = fmt.Sprintf("(R%d)", regno) 613 } 614 indexreg := plan9gpr(a.Index) 615 616 if a.Extend == lsl { 617 // Refer to ARM reference manual, for byte load/store(register), the index 618 // shift amount must be 0, encoded in "S" as 0 if omitted, or as 1 if present. 619 // a.Amount indicates the index shift amount, encoded in "S" field. 620 // a.ShiftMustBeZero is set true indicates the index shift amount must be 0. 621 // When a.ShiftMustBeZero is true, GNU syntax prints "[Xn, Xm lsl #0]" if "S" 622 // equals to 1, or prints "[Xn, Xm]" if "S" equals to 0. 623 if a.Amount != 0 && !a.ShiftMustBeZero { 624 index = fmt.Sprintf("(%s<<%d)", indexreg, a.Amount) 625 } else if a.ShiftMustBeZero && a.Amount == 1 { 626 // When a.ShiftMustBeZero is ture, Go syntax prints "(Rm<<0)" if "a.Amount" 627 // equals to 1. 628 index = fmt.Sprintf("(%s<<0)", indexreg) 629 } else { 630 index = fmt.Sprintf("(%s)", indexreg) 631 } 632 } else { 633 if a.Amount != 0 && !a.ShiftMustBeZero { 634 index = fmt.Sprintf("(%s.%s<<%d)", indexreg, a.Extend.String(), a.Amount) 635 } else { 636 index = fmt.Sprintf("(%s.%s)", indexreg, a.Extend.String()) 637 } 638 } 639 640 return base + index 641 642 case Cond: 643 switch arg.String() { 644 case "CS": 645 return "HS" 646 case "CC": 647 return "LO" 648 } 649 650 case Imm_clrex: 651 return fmt.Sprintf("$%d", uint32(a)) 652 653 case Imm_dcps: 654 return fmt.Sprintf("$%d", uint32(a)) 655 656 case Imm_option: 657 return fmt.Sprintf("$%d", uint8(a)) 658 659 case Imm_hint: 660 return fmt.Sprintf("$%d", uint8(a)) 661 662 case Imm_fp: 663 var s, pre, numerator, denominator int16 664 var result float64 665 if a.s == 0 { 666 s = 1 667 } else { 668 s = -1 669 } 670 pre = s * int16(16+a.pre) 671 if a.exp > 0 { 672 numerator = (pre << uint8(a.exp)) 673 denominator = 16 674 } else { 675 numerator = pre 676 denominator = (16 << uint8(-1*a.exp)) 677 } 678 result = float64(numerator) / float64(denominator) 679 return strings.TrimRight(fmt.Sprintf("$%f", result), "0") 680 681 case RegisterWithArrangement: 682 result := a.r.String() 683 arrange := a.a.String() 684 c := []rune(arrange) 685 switch len(c) { 686 case 3: 687 c[1], c[2] = c[2], c[1] // .8B -> .B8 688 case 4: 689 c[1], c[2], c[3] = c[3], c[1], c[2] // 16B -> B16 690 } 691 arrange = string(c) 692 result += arrange 693 if a.cnt > 0 { 694 result = "[" + result 695 for i := 1; i < int(a.cnt); i++ { 696 cur := V0 + Reg((uint16(a.r)-uint16(V0)+uint16(i))&31) 697 result += ", " + cur.String() + arrange 698 } 699 result += "]" 700 } 701 return result 702 703 case RegisterWithArrangementAndIndex: 704 result := a.r.String() 705 arrange := a.a.String() 706 result += arrange 707 if a.cnt > 1 { 708 result = "[" + result 709 for i := 1; i < int(a.cnt); i++ { 710 cur := V0 + Reg((uint16(a.r)-uint16(V0)+uint16(i))&31) 711 result += ", " + cur.String() + arrange 712 } 713 result += "]" 714 } 715 return fmt.Sprintf("%s[%d]", result, a.index) 716 717 case Systemreg: 718 return fmt.Sprintf("$%d", uint32(a.op0&1)<<14|uint32(a.op1&7)<<11|uint32(a.cn&15)<<7|uint32(a.cm&15)<<3|uint32(a.op2)&7) 719 720 case Imm_prfop: 721 if strings.Contains(a.String(), "#") { 722 return fmt.Sprintf("$%d", a) 723 } 724 case sysOp: 725 result := a.op.String() 726 if a.r != 0 { 727 result += ", " + plan9gpr(a.r) 728 } 729 return result 730 } 731 732 return strings.ToUpper(arg.String()) 733 } 734 735 // Convert a general-purpose register to plan9 assembly format. 736 func plan9gpr(r Reg) string { 737 regno := uint16(r) & 31 738 if regno == 31 { 739 return "ZR" 740 } 741 return fmt.Sprintf("R%d", regno) 742 }