...

Source file src/cmd/vendor/golang.org/x/arch/arm/armasm/plan9x.go

Documentation: cmd/vendor/golang.org/x/arch/arm/armasm

     1  // Copyright 2014 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 armasm
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"fmt"
    11  	"io"
    12  	"math"
    13  	"strings"
    14  )
    15  
    16  // GoSyntax returns the Go assembler syntax for the instruction.
    17  // The syntax was originally defined by Plan 9.
    18  // The pc is the program counter of the instruction, used for expanding
    19  // PC-relative addresses into absolute ones.
    20  // The symname function queries the symbol table for the program
    21  // being disassembled. Given a target address it returns the name and base
    22  // address of the symbol containing the target, if any; otherwise it returns "", 0.
    23  // The reader r should read from the text segment using text addresses
    24  // as offsets; it is used to display pc-relative loads as constant loads.
    25  func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64), text io.ReaderAt) string {
    26  	if symname == nil {
    27  		symname = func(uint64) (string, uint64) { return "", 0 }
    28  	}
    29  
    30  	var args []string
    31  	for _, a := range inst.Args {
    32  		if a == nil {
    33  			break
    34  		}
    35  		args = append(args, plan9Arg(&inst, pc, symname, a))
    36  	}
    37  
    38  	op := inst.Op.String()
    39  
    40  	switch inst.Op &^ 15 {
    41  	case LDR_EQ, LDRB_EQ, LDRH_EQ, LDRSB_EQ, LDRSH_EQ, VLDR_EQ:
    42  		// Check for RET
    43  		reg, _ := inst.Args[0].(Reg)
    44  		mem, _ := inst.Args[1].(Mem)
    45  		if inst.Op&^15 == LDR_EQ && reg == R15 && mem.Base == SP && mem.Sign == 0 && mem.Mode == AddrPostIndex {
    46  			return fmt.Sprintf("RET%s #%d", op[3:], mem.Offset)
    47  		}
    48  
    49  		// Check for PC-relative load.
    50  		if mem.Base == PC && mem.Sign == 0 && mem.Mode == AddrOffset && text != nil {
    51  			addr := uint32(pc) + 8 + uint32(mem.Offset)
    52  			buf := make([]byte, 8)
    53  			switch inst.Op &^ 15 {
    54  			case LDRB_EQ, LDRSB_EQ:
    55  				if _, err := text.ReadAt(buf[:1], int64(addr)); err != nil {
    56  					break
    57  				}
    58  				args[1] = fmt.Sprintf("$%#x", buf[0])
    59  
    60  			case LDRH_EQ, LDRSH_EQ:
    61  				if _, err := text.ReadAt(buf[:2], int64(addr)); err != nil {
    62  					break
    63  				}
    64  				args[1] = fmt.Sprintf("$%#x", binary.LittleEndian.Uint16(buf))
    65  
    66  			case LDR_EQ:
    67  				if _, err := text.ReadAt(buf[:4], int64(addr)); err != nil {
    68  					break
    69  				}
    70  				x := binary.LittleEndian.Uint32(buf)
    71  				if s, base := symname(uint64(x)); s != "" && uint64(x) == base {
    72  					args[1] = fmt.Sprintf("$%s(SB)", s)
    73  				} else {
    74  					args[1] = fmt.Sprintf("$%#x", x)
    75  				}
    76  
    77  			case VLDR_EQ:
    78  				switch {
    79  				case strings.HasPrefix(args[0], "D"): // VLDR.F64
    80  					if _, err := text.ReadAt(buf, int64(addr)); err != nil {
    81  						break
    82  					}
    83  					args[1] = fmt.Sprintf("$%f", math.Float64frombits(binary.LittleEndian.Uint64(buf)))
    84  				case strings.HasPrefix(args[0], "S"): // VLDR.F32
    85  					if _, err := text.ReadAt(buf[:4], int64(addr)); err != nil {
    86  						break
    87  					}
    88  					args[1] = fmt.Sprintf("$%f", math.Float32frombits(binary.LittleEndian.Uint32(buf)))
    89  				default:
    90  					panic(fmt.Sprintf("wrong FP register: %v", inst))
    91  				}
    92  			}
    93  		}
    94  	}
    95  
    96  	// Move addressing mode into opcode suffix.
    97  	suffix := ""
    98  	switch inst.Op &^ 15 {
    99  	case PLD, PLI, PLD_W:
   100  		if mem, ok := inst.Args[0].(Mem); ok {
   101  			args[0], suffix = memOpTrans(mem)
   102  		} else {
   103  			panic(fmt.Sprintf("illegal instruction: %v", inst))
   104  		}
   105  	case LDR_EQ, LDRB_EQ, LDRSB_EQ, LDRH_EQ, LDRSH_EQ, STR_EQ, STRB_EQ, STRH_EQ, VLDR_EQ, VSTR_EQ, LDREX_EQ, LDREXH_EQ, LDREXB_EQ:
   106  		if mem, ok := inst.Args[1].(Mem); ok {
   107  			args[1], suffix = memOpTrans(mem)
   108  		} else {
   109  			panic(fmt.Sprintf("illegal instruction: %v", inst))
   110  		}
   111  	case SWP_EQ, SWP_B_EQ, STREX_EQ, STREXB_EQ, STREXH_EQ:
   112  		if mem, ok := inst.Args[2].(Mem); ok {
   113  			args[2], suffix = memOpTrans(mem)
   114  		} else {
   115  			panic(fmt.Sprintf("illegal instruction: %v", inst))
   116  		}
   117  	}
   118  
   119  	// Reverse args, placing dest last.
   120  	for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 {
   121  		args[i], args[j] = args[j], args[i]
   122  	}
   123  	// For MLA-like instructions, the addend is the third operand.
   124  	switch inst.Op &^ 15 {
   125  	case SMLAWT_EQ, SMLAWB_EQ, MLA_EQ, MLA_S_EQ, MLS_EQ, SMMLA_EQ, SMMLS_EQ, SMLABB_EQ, SMLATB_EQ, SMLABT_EQ, SMLATT_EQ, SMLAD_EQ, SMLAD_X_EQ, SMLSD_EQ, SMLSD_X_EQ:
   126  		args = []string{args[1], args[2], args[0], args[3]}
   127  	}
   128  	// For STREX like instructions, the memory operands comes first.
   129  	switch inst.Op &^ 15 {
   130  	case STREX_EQ, STREXB_EQ, STREXH_EQ, SWP_EQ, SWP_B_EQ:
   131  		args = []string{args[1], args[0], args[2]}
   132  	}
   133  
   134  	// special process for FP instructions
   135  	op, args = fpTrans(&inst, op, args)
   136  
   137  	// LDR/STR like instructions -> MOV like
   138  	switch inst.Op &^ 15 {
   139  	case MOV_EQ:
   140  		op = "MOVW" + op[3:]
   141  	case LDR_EQ, MSR_EQ, MRS_EQ:
   142  		op = "MOVW" + op[3:] + suffix
   143  	case VMRS_EQ, VMSR_EQ:
   144  		op = "MOVW" + op[4:] + suffix
   145  	case LDRB_EQ, UXTB_EQ:
   146  		op = "MOVBU" + op[4:] + suffix
   147  	case LDRSB_EQ:
   148  		op = "MOVBS" + op[5:] + suffix
   149  	case SXTB_EQ:
   150  		op = "MOVBS" + op[4:] + suffix
   151  	case LDRH_EQ, UXTH_EQ:
   152  		op = "MOVHU" + op[4:] + suffix
   153  	case LDRSH_EQ:
   154  		op = "MOVHS" + op[5:] + suffix
   155  	case SXTH_EQ:
   156  		op = "MOVHS" + op[4:] + suffix
   157  	case STR_EQ:
   158  		op = "MOVW" + op[3:] + suffix
   159  		args[0], args[1] = args[1], args[0]
   160  	case STRB_EQ:
   161  		op = "MOVB" + op[4:] + suffix
   162  		args[0], args[1] = args[1], args[0]
   163  	case STRH_EQ:
   164  		op = "MOVH" + op[4:] + suffix
   165  		args[0], args[1] = args[1], args[0]
   166  	case VSTR_EQ:
   167  		args[0], args[1] = args[1], args[0]
   168  	default:
   169  		op = op + suffix
   170  	}
   171  
   172  	if args != nil {
   173  		op += " " + strings.Join(args, ", ")
   174  	}
   175  
   176  	return op
   177  }
   178  
   179  // assembler syntax for the various shifts.
   180  // @x> is a lie; the assembler uses @> 0
   181  // instead of @x> 1, but i wanted to be clear that it
   182  // was a different operation (rotate right extended, not rotate right).
   183  var plan9Shift = []string{"<<", ">>", "->", "@>", "@x>"}
   184  
   185  func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg Arg) string {
   186  	switch a := arg.(type) {
   187  	case Endian:
   188  
   189  	case Imm:
   190  		return fmt.Sprintf("$%d", uint32(a))
   191  
   192  	case Mem:
   193  
   194  	case PCRel:
   195  		addr := uint32(pc) + 8 + uint32(a)
   196  		if s, base := symname(uint64(addr)); s != "" && uint64(addr) == base {
   197  			return fmt.Sprintf("%s(SB)", s)
   198  		}
   199  		return fmt.Sprintf("%#x", addr)
   200  
   201  	case Reg:
   202  		if a < 16 {
   203  			return fmt.Sprintf("R%d", int(a))
   204  		}
   205  
   206  	case RegList:
   207  		var buf bytes.Buffer
   208  		start := -2
   209  		end := -2
   210  		fmt.Fprintf(&buf, "[")
   211  		flush := func() {
   212  			if start >= 0 {
   213  				if buf.Len() > 1 {
   214  					fmt.Fprintf(&buf, ",")
   215  				}
   216  				if start == end {
   217  					fmt.Fprintf(&buf, "R%d", start)
   218  				} else {
   219  					fmt.Fprintf(&buf, "R%d-R%d", start, end)
   220  				}
   221  				start = -2
   222  				end = -2
   223  			}
   224  		}
   225  		for i := 0; i < 16; i++ {
   226  			if a&(1<<uint(i)) != 0 {
   227  				if i == end+1 {
   228  					end++
   229  					continue
   230  				}
   231  				start = i
   232  				end = i
   233  			} else {
   234  				flush()
   235  			}
   236  		}
   237  		flush()
   238  		fmt.Fprintf(&buf, "]")
   239  		return buf.String()
   240  
   241  	case RegShift:
   242  		return fmt.Sprintf("R%d%s$%d", int(a.Reg), plan9Shift[a.Shift], int(a.Count))
   243  
   244  	case RegShiftReg:
   245  		return fmt.Sprintf("R%d%sR%d", int(a.Reg), plan9Shift[a.Shift], int(a.RegCount))
   246  	}
   247  	return strings.ToUpper(arg.String())
   248  }
   249  
   250  // convert memory operand from GNU syntax to Plan 9 syntax, for example,
   251  // [r5] -> (R5)
   252  // [r6, #4080] -> 0xff0(R6)
   253  // [r2, r0, ror #1] -> (R2)(R0@>1)
   254  // inst [r2, -r0, ror #1] -> INST.U (R2)(R0@>1)
   255  // input:
   256  //
   257  //	a memory operand
   258  //
   259  // return values:
   260  //
   261  //	corresponding memory operand in Plan 9 syntax
   262  //	.W/.P/.U suffix
   263  func memOpTrans(mem Mem) (string, string) {
   264  	suffix := ""
   265  	switch mem.Mode {
   266  	case AddrOffset, AddrLDM:
   267  		// no suffix
   268  	case AddrPreIndex, AddrLDM_WB:
   269  		suffix = ".W"
   270  	case AddrPostIndex:
   271  		suffix = ".P"
   272  	}
   273  	off := ""
   274  	if mem.Offset != 0 {
   275  		off = fmt.Sprintf("%#x", mem.Offset)
   276  	}
   277  	base := fmt.Sprintf("(R%d)", int(mem.Base))
   278  	index := ""
   279  	if mem.Sign != 0 {
   280  		sign := ""
   281  		if mem.Sign < 0 {
   282  			suffix += ".U"
   283  		}
   284  		shift := ""
   285  		if mem.Count != 0 {
   286  			shift = fmt.Sprintf("%s%d", plan9Shift[mem.Shift], mem.Count)
   287  		}
   288  		index = fmt.Sprintf("(%sR%d%s)", sign, int(mem.Index), shift)
   289  	}
   290  	return off + base + index, suffix
   291  }
   292  
   293  type goFPInfo struct {
   294  	op        Op
   295  	transArgs []int  // indexes of arguments which need transformation
   296  	gnuName   string // instruction name in GNU syntax
   297  	goName    string // instruction name in Plan 9 syntax
   298  }
   299  
   300  var fpInst []goFPInfo = []goFPInfo{
   301  	{VADD_EQ_F32, []int{2, 1, 0}, "VADD", "ADDF"},
   302  	{VADD_EQ_F64, []int{2, 1, 0}, "VADD", "ADDD"},
   303  	{VSUB_EQ_F32, []int{2, 1, 0}, "VSUB", "SUBF"},
   304  	{VSUB_EQ_F64, []int{2, 1, 0}, "VSUB", "SUBD"},
   305  	{VMUL_EQ_F32, []int{2, 1, 0}, "VMUL", "MULF"},
   306  	{VMUL_EQ_F64, []int{2, 1, 0}, "VMUL", "MULD"},
   307  	{VNMUL_EQ_F32, []int{2, 1, 0}, "VNMUL", "NMULF"},
   308  	{VNMUL_EQ_F64, []int{2, 1, 0}, "VNMUL", "NMULD"},
   309  	{VMLA_EQ_F32, []int{2, 1, 0}, "VMLA", "MULAF"},
   310  	{VMLA_EQ_F64, []int{2, 1, 0}, "VMLA", "MULAD"},
   311  	{VMLS_EQ_F32, []int{2, 1, 0}, "VMLS", "MULSF"},
   312  	{VMLS_EQ_F64, []int{2, 1, 0}, "VMLS", "MULSD"},
   313  	{VNMLA_EQ_F32, []int{2, 1, 0}, "VNMLA", "NMULAF"},
   314  	{VNMLA_EQ_F64, []int{2, 1, 0}, "VNMLA", "NMULAD"},
   315  	{VNMLS_EQ_F32, []int{2, 1, 0}, "VNMLS", "NMULSF"},
   316  	{VNMLS_EQ_F64, []int{2, 1, 0}, "VNMLS", "NMULSD"},
   317  	{VDIV_EQ_F32, []int{2, 1, 0}, "VDIV", "DIVF"},
   318  	{VDIV_EQ_F64, []int{2, 1, 0}, "VDIV", "DIVD"},
   319  	{VNEG_EQ_F32, []int{1, 0}, "VNEG", "NEGF"},
   320  	{VNEG_EQ_F64, []int{1, 0}, "VNEG", "NEGD"},
   321  	{VABS_EQ_F32, []int{1, 0}, "VABS", "ABSF"},
   322  	{VABS_EQ_F64, []int{1, 0}, "VABS", "ABSD"},
   323  	{VSQRT_EQ_F32, []int{1, 0}, "VSQRT", "SQRTF"},
   324  	{VSQRT_EQ_F64, []int{1, 0}, "VSQRT", "SQRTD"},
   325  	{VCMP_EQ_F32, []int{1, 0}, "VCMP", "CMPF"},
   326  	{VCMP_EQ_F64, []int{1, 0}, "VCMP", "CMPD"},
   327  	{VCMP_E_EQ_F32, []int{1, 0}, "VCMP.E", "CMPF"},
   328  	{VCMP_E_EQ_F64, []int{1, 0}, "VCMP.E", "CMPD"},
   329  	{VLDR_EQ, []int{1}, "VLDR", "MOV"},
   330  	{VSTR_EQ, []int{1}, "VSTR", "MOV"},
   331  	{VMOV_EQ_F32, []int{1, 0}, "VMOV", "MOVF"},
   332  	{VMOV_EQ_F64, []int{1, 0}, "VMOV", "MOVD"},
   333  	{VMOV_EQ_32, []int{1, 0}, "VMOV", "MOVW"},
   334  	{VMOV_EQ, []int{1, 0}, "VMOV", "MOVW"},
   335  	{VCVT_EQ_F64_F32, []int{1, 0}, "VCVT", "MOVFD"},
   336  	{VCVT_EQ_F32_F64, []int{1, 0}, "VCVT", "MOVDF"},
   337  	{VCVT_EQ_F32_U32, []int{1, 0}, "VCVT", "MOVWF.U"},
   338  	{VCVT_EQ_F32_S32, []int{1, 0}, "VCVT", "MOVWF"},
   339  	{VCVT_EQ_S32_F32, []int{1, 0}, "VCVT", "MOVFW"},
   340  	{VCVT_EQ_U32_F32, []int{1, 0}, "VCVT", "MOVFW.U"},
   341  	{VCVT_EQ_F64_U32, []int{1, 0}, "VCVT", "MOVWD.U"},
   342  	{VCVT_EQ_F64_S32, []int{1, 0}, "VCVT", "MOVWD"},
   343  	{VCVT_EQ_S32_F64, []int{1, 0}, "VCVT", "MOVDW"},
   344  	{VCVT_EQ_U32_F64, []int{1, 0}, "VCVT", "MOVDW.U"},
   345  }
   346  
   347  // convert FP instructions from GNU syntax to Plan 9 syntax, for example,
   348  // vadd.f32 s0, s3, s4 -> ADDF F0, S3, F2
   349  // vsub.f64 d0, d2, d4 -> SUBD F0, F2, F4
   350  // vldr s2, [r11] -> MOVF (R11), F1
   351  // inputs: instruction name and arguments in GNU syntax
   352  // return values: corresponding instruction name and arguments in Plan 9 syntax
   353  func fpTrans(inst *Inst, op string, args []string) (string, []string) {
   354  	for _, fp := range fpInst {
   355  		if inst.Op&^15 == fp.op {
   356  			// remove gnu syntax suffixes
   357  			op = strings.Replace(op, ".F32", "", -1)
   358  			op = strings.Replace(op, ".F64", "", -1)
   359  			op = strings.Replace(op, ".S32", "", -1)
   360  			op = strings.Replace(op, ".U32", "", -1)
   361  			op = strings.Replace(op, ".32", "", -1)
   362  			// compose op name
   363  			if fp.op == VLDR_EQ || fp.op == VSTR_EQ {
   364  				switch {
   365  				case strings.HasPrefix(args[fp.transArgs[0]], "D"):
   366  					op = "MOVD" + op[len(fp.gnuName):]
   367  				case strings.HasPrefix(args[fp.transArgs[0]], "S"):
   368  					op = "MOVF" + op[len(fp.gnuName):]
   369  				default:
   370  					panic(fmt.Sprintf("wrong FP register: %v", inst))
   371  				}
   372  			} else {
   373  				op = fp.goName + op[len(fp.gnuName):]
   374  			}
   375  			// transform registers
   376  			for ix, ri := range fp.transArgs {
   377  				switch {
   378  				case strings.HasSuffix(args[ri], "[1]"): // MOVW Rx, Dy[1]
   379  					break
   380  				case strings.HasSuffix(args[ri], "[0]"): // Dx[0] -> Fx
   381  					args[ri] = strings.Replace(args[ri], "[0]", "", -1)
   382  					fallthrough
   383  				case strings.HasPrefix(args[ri], "D"): // Dx -> Fx
   384  					args[ri] = "F" + args[ri][1:]
   385  				case strings.HasPrefix(args[ri], "S"):
   386  					if inst.Args[ix].(Reg)&1 == 0 { // Sx -> Fy, y = x/2, if x is even
   387  						args[ri] = fmt.Sprintf("F%d", (inst.Args[ix].(Reg)-S0)/2)
   388  					}
   389  				case strings.HasPrefix(args[ri], "$"): // CMPF/CMPD $0, Fx
   390  					break
   391  				case strings.HasPrefix(args[ri], "R"): // MOVW Rx, Dy[1]
   392  					break
   393  				default:
   394  					panic(fmt.Sprintf("wrong FP register: %v", inst))
   395  				}
   396  			}
   397  			break
   398  		}
   399  	}
   400  	return op, args
   401  }
   402  

View as plain text