Home | History | Annotate | Download | only in 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 LDR_EQ, LDRB_EQ, LDRSB_EQ, LDRH_EQ, LDRSH_EQ, STR_EQ, STRB_EQ, STRH_EQ, VLDR_EQ, VSTR_EQ:
    100 		mem, _ := inst.Args[1].(Mem)
    101 		switch mem.Mode {
    102 		case AddrOffset, AddrLDM:
    103 			// no suffix
    104 		case AddrPreIndex, AddrLDM_WB:
    105 			suffix = ".W"
    106 		case AddrPostIndex:
    107 			suffix = ".P"
    108 		}
    109 		off := ""
    110 		if mem.Offset != 0 {
    111 			off = fmt.Sprintf("%#x", mem.Offset)
    112 		}
    113 		base := fmt.Sprintf("(R%d)", int(mem.Base))
    114 		index := ""
    115 		if mem.Sign != 0 {
    116 			sign := ""
    117 			if mem.Sign < 0 {
    118 				suffix += ".U"
    119 			}
    120 			shift := ""
    121 			if mem.Count != 0 {
    122 				shift = fmt.Sprintf("%s%d", plan9Shift[mem.Shift], mem.Count)
    123 			}
    124 			index = fmt.Sprintf("(%sR%d%s)", sign, int(mem.Index), shift)
    125 		}
    126 		args[1] = off + base + index
    127 	}
    128 
    129 	// Reverse args, placing dest last.
    130 	for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 {
    131 		args[i], args[j] = args[j], args[i]
    132 	}
    133 	// For MLA-like instructions, the addend is the third operand.
    134 	switch inst.Op &^ 15 {
    135 	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:
    136 		args = []string{args[1], args[2], args[0], args[3]}
    137 	}
    138 
    139 	switch inst.Op &^ 15 {
    140 	case MOV_EQ:
    141 		op = "MOVW" + op[3:]
    142 
    143 	case LDR_EQ:
    144 		op = "MOVW" + op[3:] + suffix
    145 	case LDRB_EQ:
    146 		op = "MOVBU" + op[4:] + suffix
    147 	case LDRSB_EQ:
    148 		op = "MOVBS" + op[5:] + suffix
    149 	case LDRH_EQ:
    150 		op = "MOVHU" + op[4:] + suffix
    151 	case LDRSH_EQ:
    152 		op = "MOVHS" + op[5:] + suffix
    153 	case VLDR_EQ:
    154 		switch {
    155 		case strings.HasPrefix(args[1], "D"): // VLDR.F64
    156 			op = "MOVD" + op[4:] + suffix
    157 			args[1] = "F" + args[1][1:] // Dx -> Fx
    158 		case strings.HasPrefix(args[1], "S"): // VLDR.F32
    159 			op = "MOVF" + op[4:] + suffix
    160 			if inst.Args[0].(Reg)&1 == 0 { // Sx -> Fy, y = x/2, if x is even
    161 				args[1] = fmt.Sprintf("F%d", (inst.Args[0].(Reg)-S0)/2)
    162 			}
    163 		default:
    164 			panic(fmt.Sprintf("wrong FP register: %v", inst))
    165 		}
    166 
    167 	case STR_EQ:
    168 		op = "MOVW" + op[3:] + suffix
    169 		args[0], args[1] = args[1], args[0]
    170 	case STRB_EQ:
    171 		op = "MOVB" + op[4:] + suffix
    172 		args[0], args[1] = args[1], args[0]
    173 	case STRH_EQ:
    174 		op = "MOVH" + op[4:] + suffix
    175 		args[0], args[1] = args[1], args[0]
    176 	case VSTR_EQ:
    177 		switch {
    178 		case strings.HasPrefix(args[1], "D"): // VSTR.F64
    179 			op = "MOVD" + op[4:] + suffix
    180 			args[1] = "F" + args[1][1:] // Dx -> Fx
    181 		case strings.HasPrefix(args[1], "S"): // VSTR.F32
    182 			op = "MOVF" + op[4:] + suffix
    183 			if inst.Args[0].(Reg)&1 == 0 { // Sx -> Fy, y = x/2, if x is even
    184 				args[1] = fmt.Sprintf("F%d", (inst.Args[0].(Reg)-S0)/2)
    185 			}
    186 		default:
    187 			panic(fmt.Sprintf("wrong FP register: %v", inst))
    188 		}
    189 		args[0], args[1] = args[1], args[0]
    190 	}
    191 
    192 	if args != nil {
    193 		op += " " + strings.Join(args, ", ")
    194 	}
    195 
    196 	return op
    197 }
    198 
    199 // assembler syntax for the various shifts.
    200 // @x> is a lie; the assembler uses @> 0
    201 // instead of @x> 1, but i wanted to be clear that it
    202 // was a different operation (rotate right extended, not rotate right).
    203 var plan9Shift = []string{"<<", ">>", "->", "@>", "@x>"}
    204 
    205 func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg Arg) string {
    206 	switch a := arg.(type) {
    207 	case Endian:
    208 
    209 	case Imm:
    210 		return fmt.Sprintf("$%d", uint32(a))
    211 
    212 	case Mem:
    213 
    214 	case PCRel:
    215 		addr := uint32(pc) + 8 + uint32(a)
    216 		if s, base := symname(uint64(addr)); s != "" && uint64(addr) == base {
    217 			return fmt.Sprintf("%s(SB)", s)
    218 		}
    219 		return fmt.Sprintf("%#x", addr)
    220 
    221 	case Reg:
    222 		if a < 16 {
    223 			return fmt.Sprintf("R%d", int(a))
    224 		}
    225 
    226 	case RegList:
    227 		var buf bytes.Buffer
    228 		start := -2
    229 		end := -2
    230 		fmt.Fprintf(&buf, "[")
    231 		flush := func() {
    232 			if start >= 0 {
    233 				if buf.Len() > 1 {
    234 					fmt.Fprintf(&buf, ",")
    235 				}
    236 				if start == end {
    237 					fmt.Fprintf(&buf, "R%d", start)
    238 				} else {
    239 					fmt.Fprintf(&buf, "R%d-R%d", start, end)
    240 				}
    241 				start = -2
    242 				end = -2
    243 			}
    244 		}
    245 		for i := 0; i < 16; i++ {
    246 			if a&(1<<uint(i)) != 0 {
    247 				if i == end+1 {
    248 					end++
    249 					continue
    250 				}
    251 				start = i
    252 				end = i
    253 			} else {
    254 				flush()
    255 			}
    256 		}
    257 		flush()
    258 		fmt.Fprintf(&buf, "]")
    259 		return buf.String()
    260 
    261 	case RegShift:
    262 		return fmt.Sprintf("R%d%s$%d", int(a.Reg), plan9Shift[a.Shift], int(a.Count))
    263 
    264 	case RegShiftReg:
    265 		return fmt.Sprintf("R%d%sR%d", int(a.Reg), plan9Shift[a.Shift], int(a.RegCount))
    266 	}
    267 	return strings.ToUpper(arg.String())
    268 }
    269