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 5package arm64asm 6 7import ( 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. 24func 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: 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(S0) && rno <= uint16(S31) { 139 op = "FMOVS" + suffix 140 args[0] = fmt.Sprintf("F%d", rno&31) 141 } else if rno >= uint16(D0) && rno <= uint16(D31) { 142 op = "FMOVD" + suffix 143 args[0] = fmt.Sprintf("F%d", rno&31) 144 } else { 145 op = "MOVD" + suffix 146 } 147 148 case LDRB: 149 op = "MOVBU" + suffix 150 151 case LDRH: 152 op = "MOVHU" + suffix 153 154 case LDRSW: 155 op = "MOVW" + suffix 156 157 case LDRSB: 158 if r, ok := inst.Args[0].(Reg); ok { 159 rno := uint16(r) 160 if rno <= uint16(WZR) { 161 op = "MOVBW" + suffix 162 } else { 163 op = "MOVB" + suffix 164 } 165 } 166 case LDRSH: 167 if r, ok := inst.Args[0].(Reg); ok { 168 rno := uint16(r) 169 if rno <= uint16(WZR) { 170 op = "MOVHW" + suffix 171 } else { 172 op = "MOVH" + suffix 173 } 174 } 175 case STR, STUR: 176 var rno uint16 177 if r, ok := inst.Args[0].(Reg); ok { 178 rno = uint16(r) 179 } else { 180 rno = uint16(inst.Args[0].(RegSP)) 181 } 182 if rno <= uint16(WZR) { 183 op = "MOVW" + suffix 184 } else if rno >= uint16(S0) && rno <= uint16(S31) { 185 op = "FMOVS" + suffix 186 args[0] = fmt.Sprintf("F%d", rno&31) 187 } else if rno >= uint16(D0) && rno <= uint16(D31) { 188 op = "FMOVD" + suffix 189 args[0] = fmt.Sprintf("F%d", rno&31) 190 } else { 191 op = "MOVD" + suffix 192 } 193 args[0], args[1] = args[1], args[0] 194 195 case STRB, STURB: 196 op = "MOVB" + suffix 197 args[0], args[1] = args[1], args[0] 198 199 case STRH, STURH: 200 op = "MOVH" + suffix 201 args[0], args[1] = args[1], args[0] 202 203 case TBNZ, TBZ: 204 args[0], args[1], args[2] = args[2], args[0], args[1] 205 206 case MADD, MSUB, SMADDL, SMSUBL, UMADDL, UMSUBL: 207 if r, ok := inst.Args[0].(Reg); ok { 208 rno := uint16(r) 209 if rno <= uint16(WZR) { 210 op += "W" 211 } 212 } 213 args[2], args[3] = args[3], args[2] 214 case STLR: 215 if r, ok := inst.Args[0].(Reg); ok { 216 rno := uint16(r) 217 if rno <= uint16(WZR) { 218 op += "W" 219 } 220 } 221 args[0], args[1] = args[1], args[0] 222 223 case STLRB, STLRH: 224 args[0], args[1] = args[1], args[0] 225 226 case STLXR, STXR: 227 if r, ok := inst.Args[1].(Reg); ok { 228 rno := uint16(r) 229 if rno <= uint16(WZR) { 230 op += "W" 231 } 232 } 233 args[1], args[2] = args[2], args[1] 234 235 case STLXRB, STLXRH, STXRB, STXRH: 236 args[1], args[2] = args[2], args[1] 237 238 case BFI, BFXIL, SBFIZ, SBFX, UBFIZ, UBFX: 239 if r, ok := inst.Args[0].(Reg); ok { 240 rno := uint16(r) 241 if rno <= uint16(WZR) { 242 op += "W" 243 } 244 } 245 args[1], args[2], args[3] = args[3], args[1], args[2] 246 247 case LDAXP, LDXP: 248 if r, ok := inst.Args[0].(Reg); ok { 249 rno := uint16(r) 250 if rno <= uint16(WZR) { 251 op += "W" 252 } 253 } 254 fallthrough 255 256 case STP, LDP: 257 args[0] = fmt.Sprintf("(%s, %s)", args[0], args[1]) 258 args[1] = args[2] 259 if op == "STP" { 260 op = op + suffix 261 return op + " " + args[0] + ", " + args[1] 262 } else if op == "LDP" { 263 op = op + suffix 264 return op + " " + args[1] + ", " + args[0] 265 } else if op == "LDAXP" || op == "LDXP" || op == "LDAXPW" || op == "LDXPW" { 266 return op + " " + args[1] + ", " + args[0] 267 } 268 269 case STLXP, STXP: 270 if r, ok := inst.Args[1].(Reg); ok { 271 rno := uint16(r) 272 if rno <= uint16(WZR) { 273 op += "W" 274 } 275 } 276 args[1] = fmt.Sprintf("(%s, %s)", args[1], args[2]) 277 args[2] = args[3] 278 return op + " " + args[1] + ", " + args[2] + ", " + args[0] 279 280 case FCCMP, FCCMPE: 281 args[0], args[1] = args[1], args[0] 282 fallthrough 283 284 case FCMP, FCMPE: 285 if _, ok := inst.Args[1].(Imm); ok { 286 args[1] = "$(0.0)" 287 } 288 fallthrough 289 290 case FADD, FSUB, FMUL, FNMUL, FDIV, FMAX, FMIN, FMAXNM, FMINNM, FCSEL, FMADD, FMSUB, FNMADD, FNMSUB: 291 if strings.HasSuffix(op, "MADD") || strings.HasSuffix(op, "MSUB") { 292 args[2], args[3] = args[3], args[2] 293 } 294 if r, ok := inst.Args[0].(Reg); ok { 295 rno := uint16(r) 296 if rno >= uint16(S0) && rno <= uint16(S31) { 297 op = fmt.Sprintf("%sS", op) 298 } else if rno >= uint16(D0) && rno <= uint16(D31) { 299 op = fmt.Sprintf("%sD", op) 300 } 301 } 302 303 case FCVT: 304 for i := 1; i >= 0; i-- { 305 if r, ok := inst.Args[i].(Reg); ok { 306 rno := uint16(r) 307 if rno >= uint16(H0) && rno <= uint16(H31) { 308 op = fmt.Sprintf("%sH", op) 309 } else if rno >= uint16(S0) && rno <= uint16(S31) { 310 op = fmt.Sprintf("%sS", op) 311 } else if rno >= uint16(D0) && rno <= uint16(D31) { 312 op = fmt.Sprintf("%sD", op) 313 } 314 } 315 } 316 317 case FABS, FNEG, FSQRT, FRINTN, FRINTP, FRINTM, FRINTZ, FRINTA, FRINTX, FRINTI: 318 if r, ok := inst.Args[1].(Reg); ok { 319 rno := uint16(r) 320 if rno >= uint16(S0) && rno <= uint16(S31) { 321 op = fmt.Sprintf("%sS", op) 322 } else if rno >= uint16(D0) && rno <= uint16(D31) { 323 op = fmt.Sprintf("%sD", op) 324 } 325 } 326 327 case FCVTZS, FCVTZU, SCVTF, UCVTF: 328 if _, ok := inst.Args[2].(Imm); !ok { 329 for i := 1; i >= 0; i-- { 330 if r, ok := inst.Args[i].(Reg); ok { 331 rno := uint16(r) 332 if rno >= uint16(S0) && rno <= uint16(S31) { 333 op = fmt.Sprintf("%sS", op) 334 } else if rno >= uint16(D0) && rno <= uint16(D31) { 335 op = fmt.Sprintf("%sD", op) 336 } else if rno <= uint16(WZR) { 337 op += "W" 338 } 339 } 340 } 341 } 342 343 case FMOV: 344 for i := 0; i <= 1; i++ { 345 if r, ok := inst.Args[i].(Reg); ok { 346 rno := uint16(r) 347 if rno >= uint16(S0) && rno <= uint16(S31) { 348 op = fmt.Sprintf("%sS", op) 349 break 350 } else if rno >= uint16(D0) && rno <= uint16(D31) { 351 op = fmt.Sprintf("%sD", op) 352 break 353 } 354 } 355 } 356 357 case SYSL: 358 op1 := int(inst.Args[1].(Imm).Imm) 359 cn := int(inst.Args[2].(Imm_c)) 360 cm := int(inst.Args[3].(Imm_c)) 361 op2 := int(inst.Args[4].(Imm).Imm) 362 sysregno := int32(op1<<16 | cn<<12 | cm<<8 | op2<<5) 363 args[1] = fmt.Sprintf("$%d", sysregno) 364 return op + " " + args[1] + ", " + args[0] 365 366 case CBNZ, CBZ: 367 if r, ok := inst.Args[0].(Reg); ok { 368 rno := uint16(r) 369 if rno <= uint16(WZR) { 370 op += "W" 371 } 372 } 373 args[0], args[1] = args[1], args[0] 374 375 case ADR, ADRP: 376 addr := int64(inst.Args[1].(PCRel)) 377 args[1] = fmt.Sprintf("%d(PC)", addr) 378 379 case MSR: 380 args[0] = inst.Args[0].String() 381 382 case ST1: 383 op = fmt.Sprintf("V%s", op) + suffix 384 args[0], args[1] = args[1], args[0] 385 386 case LD1: 387 op = fmt.Sprintf("V%s", op) + suffix 388 389 case UMOV: 390 op = "VMOV" 391 392 default: 393 index := sort.SearchStrings(noSuffixOpSet, op) 394 if !(index < len(noSuffixOpSet) && noSuffixOpSet[index] == op) { 395 rno := -1 396 switch a := inst.Args[0].(type) { 397 case Reg: 398 rno = int(a) 399 case RegSP: 400 rno = int(a) 401 case RegisterWithArrangement: 402 op = fmt.Sprintf("V%s", op) 403 } 404 405 if rno >= int(B0) && rno <= int(Q31) && !strings.HasPrefix(op, "F") { 406 op = fmt.Sprintf("V%s", op) 407 } 408 if rno >= 0 && rno <= int(WZR) { 409 // Add "w" to opcode suffix. 410 op += "W" 411 } 412 } 413 op = op + suffix 414 } 415 416 // conditional instructions, replace args. 417 if _, ok := inst.Args[3].(Cond); ok { 418 if _, ok := inst.Args[2].(Reg); ok { 419 args[1], args[2] = args[2], args[1] 420 } else { 421 args[0], args[2] = args[2], args[0] 422 } 423 } 424 // Reverse args, placing dest last. 425 for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 { 426 args[i], args[j] = args[j], args[i] 427 } 428 429 if args != nil { 430 op += " " + strings.Join(args, ", ") 431 } 432 433 return op 434} 435 436// No need add "W" to opcode suffix. 437// Opcode must be inserted in ascending order. 438var noSuffixOpSet = strings.Fields(` 439AESD 440AESE 441AESIMC 442AESMC 443CRC32B 444CRC32CB 445CRC32CH 446CRC32CW 447CRC32CX 448CRC32H 449CRC32W 450CRC32X 451LDARB 452LDARH 453LDAXRB 454LDAXRH 455LDTRH 456LDXRB 457LDXRH 458SHA1C 459SHA1H 460SHA1M 461SHA1P 462SHA1SU0 463SHA1SU1 464SHA256H 465SHA256H2 466SHA256SU0 467SHA256SU1 468`) 469 470func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg Arg) string { 471 switch a := arg.(type) { 472 case Imm: 473 return fmt.Sprintf("$%d", uint32(a.Imm)) 474 475 case Imm64: 476 return fmt.Sprintf("$%d", int64(a.Imm)) 477 478 case ImmShift: 479 if a.shift == 0 { 480 return fmt.Sprintf("$%d", a.imm) 481 } 482 return fmt.Sprintf("$(%d<<%d)", a.imm, a.shift) 483 484 case PCRel: 485 addr := int64(pc) + int64(a) 486 if s, base := symname(uint64(addr)); s != "" && uint64(addr) == base { 487 return fmt.Sprintf("%s(SB)", s) 488 } 489 return fmt.Sprintf("%d(PC)", a/4) 490 491 case Reg: 492 regenum := uint16(a) 493 regno := uint16(a) & 31 494 495 if regenum >= uint16(B0) && regenum <= uint16(D31) { 496 if strings.HasPrefix(inst.Op.String(), "F") || strings.HasSuffix(inst.Op.String(), "CVTF") { 497 // FP registers are the same ones as SIMD registers 498 // Print Fn for scalar variant to align with assembler (e.g., FCVT, SCVTF, UCVTF, etc.) 499 return fmt.Sprintf("F%d", regno) 500 } else { 501 return fmt.Sprintf("V%d", regno) 502 } 503 504 } else if regenum >= uint16(Q0) && regenum <= uint16(Q31) { 505 // Print Vn to align with assembler (e.g., SHA256H) 506 return fmt.Sprintf("V%d", regno) 507 } 508 509 if regno == 31 { 510 return "ZR" 511 } 512 return fmt.Sprintf("R%d", regno) 513 514 case RegSP: 515 regno := uint16(a) & 31 516 if regno == 31 { 517 return "RSP" 518 } 519 return fmt.Sprintf("R%d", regno) 520 521 case RegExtshiftAmount: 522 reg := "" 523 regno := uint16(a.reg) & 31 524 if regno == 31 { 525 reg = "ZR" 526 } else { 527 reg = fmt.Sprintf("R%d", uint16(a.reg)&31) 528 } 529 extshift := "" 530 amount := "" 531 if a.extShift != ExtShift(0) { 532 switch a.extShift { 533 default: 534 extshift = "." + a.extShift.String() 535 536 case lsl: 537 extshift = "<<" 538 amount = fmt.Sprintf("%d", a.amount) 539 return reg + extshift + amount 540 541 case lsr: 542 extshift = ">>" 543 amount = fmt.Sprintf("%d", a.amount) 544 return reg + extshift + amount 545 546 case asr: 547 extshift = "->" 548 amount = fmt.Sprintf("%d", a.amount) 549 return reg + extshift + amount 550 case ror: 551 extshift = "@>" 552 amount = fmt.Sprintf("%d", a.amount) 553 return reg + extshift + amount 554 } 555 if a.amount != 0 { 556 amount = fmt.Sprintf("<<%d", a.amount) 557 } 558 } 559 return reg + extshift + amount 560 561 case MemImmediate: 562 off := "" 563 base := "" 564 regno := uint16(a.Base) & 31 565 if regno == 31 { 566 base = "(RSP)" 567 } else { 568 base = fmt.Sprintf("(R%d)", regno) 569 } 570 if a.imm != 0 && a.Mode != AddrPostReg { 571 off = fmt.Sprintf("%d", a.imm) 572 } else if a.Mode == AddrPostReg { 573 postR := fmt.Sprintf("(R%d)", a.imm) 574 return base + postR 575 } 576 return off + base 577 578 case MemExtend: 579 base := "" 580 index := "" 581 indexreg := "" 582 regno := uint16(a.Base) & 31 583 if regno == 31 { 584 base = "(RSP)" 585 } else { 586 base = fmt.Sprintf("(R%d)", regno) 587 } 588 regno = uint16(a.Index) & 31 589 if regno == 31 { 590 indexreg = "ZR" 591 } else { 592 indexreg = fmt.Sprintf("R%d", regno) 593 } 594 595 if a.Extend == lsl { 596 // a.Amount indicates the index shift amount, encoded in "S" field. 597 // a.ShiftMustBeZero is set true when the index shift amount must be 0, 598 // even if the a.Amount field is not 0. 599 // When a.ShiftMustBeZero is ture, GNU syntax prints #0 shift amount if 600 // "S" equals to 1, or does not print #0 shift amount if "S" equals to 0. 601 // Go syntax should never print a zero index shift amount. 602 if a.Amount != 0 && !a.ShiftMustBeZero { 603 index = fmt.Sprintf("(%s<<%d)", indexreg, a.Amount) 604 } else { 605 index = fmt.Sprintf("(%s)", indexreg) 606 } 607 } else { 608 if a.Amount != 0 && !a.ShiftMustBeZero { 609 index = fmt.Sprintf("(%s.%s<<%d)", indexreg, a.Extend.String(), a.Amount) 610 } else { 611 index = fmt.Sprintf("(%s.%s)", indexreg, a.Extend.String()) 612 } 613 } 614 615 return base + index 616 617 case Cond: 618 switch arg.String() { 619 case "CS": 620 return "HS" 621 case "CC": 622 return "LO" 623 } 624 625 case Imm_clrex: 626 return fmt.Sprintf("$%d", uint32(a)) 627 628 case Imm_dcps: 629 return fmt.Sprintf("$%d", uint32(a)) 630 631 case Imm_option: 632 return fmt.Sprintf("$%d", uint8(a)) 633 634 case Imm_hint: 635 return fmt.Sprintf("$%d", uint8(a)) 636 637 case Imm_fp: 638 var s, pre, numerator, denominator int16 639 var result float64 640 if a.s == 0 { 641 s = 1 642 } else { 643 s = -1 644 } 645 pre = s * int16(16+a.pre) 646 if a.exp > 0 { 647 numerator = (pre << uint8(a.exp)) 648 denominator = 16 649 } else { 650 numerator = pre 651 denominator = (16 << uint8(-1*a.exp)) 652 } 653 result = float64(numerator) / float64(denominator) 654 return strings.TrimRight(fmt.Sprintf("$%f", result), "0") 655 656 case RegisterWithArrangement: 657 result := a.r.String() 658 arrange := a.a.String() 659 c := []rune(arrange) 660 switch len(c) { 661 case 3: 662 c[1], c[2] = c[2], c[1] // .8B -> .B8 663 case 4: 664 c[1], c[2], c[3] = c[3], c[1], c[2] // 16B -> B16 665 } 666 arrange = string(c) 667 result += arrange 668 if a.cnt > 0 { 669 result = "[" + result 670 for i := 1; i < int(a.cnt); i++ { 671 cur := V0 + Reg((uint16(a.r)-uint16(V0)+uint16(i))&31) 672 result += ", " + cur.String() + arrange 673 } 674 result += "]" 675 } 676 return result 677 678 case RegisterWithArrangementAndIndex: 679 result := a.r.String() 680 arrange := a.a.String() 681 result += arrange 682 if a.cnt > 1 { 683 result = "[" + result 684 for i := 1; i < int(a.cnt); i++ { 685 cur := V0 + Reg((uint16(a.r)-uint16(V0)+uint16(i))&31) 686 result += ", " + cur.String() + arrange 687 } 688 result += "]" 689 } 690 return fmt.Sprintf("%s[%d]", result, a.index) 691 692 case Systemreg: 693 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) 694 695 case Imm_prfop: 696 if strings.Contains(a.String(), "#") { 697 return fmt.Sprintf("$%d", a) 698 } 699 } 700 701 return strings.ToUpper(arg.String()) 702} 703