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  }