blob: 9b4c85acd3af7ce7e280d08f04d05e7bd56fbd68 [file] [log] [blame]
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Armmap constructs the ARM opcode map from the instruction set CSV file.
//
// Usage:
// armmap [-fmt=format] arm.csv
//
// The known output formats are:
//
// text (default) - print decoding tree in text form
// decoder - print decoding tables for the armasm package
package main
import (
"bufio"
"encoding/csv"
"flag"
"fmt"
"log"
"os"
"sort"
"strconv"
"strings"
)
var format = flag.String("fmt", "text", "output format: text, decoder")
var inputFile string
func usage() {
fmt.Fprintf(os.Stderr, "usage: armmap [-fmt=format] x86.csv\n")
os.Exit(2)
}
func main() {
log.SetFlags(0)
log.SetPrefix("armmap: ")
flag.Usage = usage
flag.Parse()
if flag.NArg() != 1 {
usage()
}
inputFile = flag.Arg(0)
var print func(*Prog)
switch *format {
default:
log.Fatalf("unknown output format %q", *format)
case "text":
print = printText
case "decoder":
print = printDecoder
}
p, err := readCSV(flag.Arg(0))
if err != nil {
log.Fatal(err)
}
print(p)
}
// readCSV reads the CSV file and returns the corresponding Prog.
// It may print details about problems to standard error using the log package.
func readCSV(file string) (*Prog, error) {
// Read input.
// Skip leading blank and # comment lines.
f, err := os.Open(file)
if err != nil {
return nil, err
}
b := bufio.NewReader(f)
for {
c, err := b.ReadByte()
if err != nil {
break
}
if c == '\n' {
continue
}
if c == '#' {
b.ReadBytes('\n')
continue
}
b.UnreadByte()
break
}
table, err := csv.NewReader(b).ReadAll()
if err != nil {
return nil, fmt.Errorf("parsing %s: %v", file, err)
}
if len(table) == 0 {
return nil, fmt.Errorf("empty csv input")
}
if len(table[0]) < 5 {
return nil, fmt.Errorf("csv too narrow: need at least five columns")
}
p := &Prog{}
for _, row := range table {
add(p, row[0], row[1], row[2], row[3], row[4])
}
return p, nil
}
type Prog struct {
Inst []Inst
OpRanges map[string]string
}
type Inst struct {
Text string
Encoding string
Mask uint32
Value uint32
Priority int
OpBase string
OpBits uint64
Args []string
}
type Arg struct {
Name string
Bits uint64
}
// add adds the entry from the CSV described by maskstr, valuestr, text, encoding, tags
// to the program p.
func add(p *Prog, maskstr, valuestr, text, encoding, tags string) {
if strings.Contains(tags, "pseudo") {
return
}
// For now, ignore the VFP floating point instructions.
if strings.HasPrefix(text, "V") && !strings.Contains(tags, "vfp") {
// TODO
return
}
mask, err := strconv.ParseUint(maskstr, 0, 32)
if err != nil {
log.Printf("invalid mask %q", maskstr)
return
}
value, err := strconv.ParseUint(valuestr, 0, 32)
if err != nil {
log.Printf("invalid value %q", valuestr)
return
}
// Parse encoding, building size and offset of each field.
// The first field in the encoding is the largest offset.
fuzzy := uint32(0) // mask of 'should be' bits
fieldOffset := map[string]int{}
fieldWidth := map[string]int{}
off := 32
for _, f := range strings.Split(encoding, "|") {
n := 1
if i := strings.Index(f, ":"); i >= 0 {
n, _ = strconv.Atoi(f[i+1:])
}
off -= n
fieldOffset[f] = off
fieldWidth[f] = n
if f == "(0)" || f == "(1)" {
fuzzy |= 1 << uint(off)
}
}
if off != 0 {
fmt.Fprintf(os.Stderr, "%s: counted %d bits in %s\n", text, 32-off, encoding)
}
// Track which encoding fields we found uses for.
// If we do not find a use for a field, that's an error in the input tables.
fieldUsed := map[string]bool{}
// Split text into opcode and arguments.
var op, argstr string
if i := strings.Index(text, " "); i >= 0 {
op = text[:i]
argstr = text[i:]
} else {
op = text
}
op = strings.TrimSpace(op)
argstr = strings.TrimSpace(argstr)
// Parse opcode suffixes.
i := strings.Index(op, "<")
if i < 0 {
i = len(op)
}
if j := strings.Index(op, "{"); j >= 0 && j < i {
i = j
}
op, suffix := op[:i], op[i:]
if suffix != "" && opSuffix[suffix] == "" {
fmt.Fprintf(os.Stderr, "%s: invalid op suffix %q in %s\n", text, suffix, op+suffix)
}
// Make sure fields needed by opcode suffix are available.
for _, f := range strings.Split(opSuffix[suffix], ",") {
if f != "" && fieldWidth[f] == 0 {
fmt.Fprintf(os.Stderr, "%s: opsuffix %s missing %s in encoding %s\n", text, suffix, f, encoding)
}
fieldUsed[f] = true
}
// Build list of opcodes that can be generated by this suffix.
// For example, the opcodes generated by ADD<c> are ADD.EQ, ADD.NE, etc.
// To simplify the decoding of instruction opcodes, we arrange that this
// sequence aligns with the encoding, so that decoding amounts to extracting
// the right bits, concatenating them, and adding them to the first opcode in
// the sequence. If the condition code is present, we always place it in the
// low order bits, so that x&^15 == FOO_EQ tests whether x is any of the
// conditional FOO instructions.
ops := []string{op}
opBits := uint64(0) // record of bits to extract and add to opcode base
opFields := strings.Split(opSuffix[suffix], ",")
// First the optional elements, like {S} meaning "" or ".S".
for strings.HasPrefix(suffix, "{") {
i := strings.Index(suffix, "}")
var f, option string
option, suffix = suffix[1:i], suffix[i+1:]
f, opFields = opFields[0], opFields[1:]
if option == "W" {
// The {W} option on PLD{W} uses the R bit which is !W.
ops = cross(ops, "."+option, "")
} else {
ops = cross(ops, "", "."+option)
}
if fieldWidth[f] != 1 {
fmt.Fprintf(os.Stderr, "%s: have %d bits for {%s}\n", text, fieldWidth[f], option)
}
// opBits is a sequence of 16-bit chunks describing contiguous bit sections.
// Each chunk is 8-bit offset followed by 8-bit size.
opBits = opBits<<16 | uint64(fieldOffset[f])<<8 | 1
}
// Then the true field substitutions.
haveCond := false
for strings.Contains(suffix, "<") {
var f, literal, x string
if len(opFields) == 0 {
fmt.Fprintf(os.Stderr, "%s: ran out of suffix fields for <%s>\n", text, x)
break
}
f, opFields = opFields[0], opFields[1:]
i := strings.Index(suffix, "<")
j := strings.Index(suffix, ">")
literal, x, suffix = suffix[:i], suffix[i+1:j], suffix[j+1:]
// Add leading literal text to all opcodes.
ops = cross(ops, literal)
// The <c> condition can happen anywhere in the opcode text
// but we want to generate the actual variation in the low bits
// of the list index. Remember when and where we've seen <c> and apply
// it after the loop has finished.
if x == "c" && f == "cond:4" {
haveCond = true
ops = cross(ops, "_COND_")
continue
}
// Otherwise, choices[x] lists the possible expansions of <x>.
// If <x> is of the form <A,B,C> the choices are A, B, and C.
expand := choices[x]
if expand == nil && strings.Contains(x, ",") {
expand = strings.Split(x, ",")
}
if expand == nil {
fmt.Fprintf(os.Stderr, "%s: unknown choices for <%s>\n", text, x)
expand = []string{x}
} else if len(expand) != 1<<uint(fieldWidth[f]) {
fmt.Fprintf(os.Stderr, "%s: have %d choices for <%s> but %d bits\n", text, len(expand), x, fieldWidth[f])
}
opBits = opBits<<16 | uint64(fieldOffset[f])<<8 | uint64(fieldWidth[f])
ops = cross(ops, expand...)
}
if haveCond {
// Apply condtional suffix last.
opBits = opBits<<16 | 28<<8 | 4
ops = crossCond(ops)
}
ops = cross(ops, suffix)
// Now ops is a list of opcodes generated by this opcode pattern.
// We want to make sure that we can arrange for those opcodes to
// happen consecutively in the final opcode numbering.
// Record in p.OpRanges[op] the required consecutive sequence of
// opcode that includes op. To make searches easier, we record
// the sequence as a comma-separated list of strings with commas
// on both ends: [A, B] encodes as ",A,B,".
if p.OpRanges == nil {
p.OpRanges = make(map[string]string)
}
opstr := "," + strings.Join(ops, ",") + ","
for _, op := range ops {
if old := p.OpRanges[op]; old != "" && old != opstr {
if strings.Contains(old, opstr) {
opstr = old
} else if strings.Contains(opstr, old) {
// great, do nothing
} else {
// It would also be okay if there is some subsequence s such that
// old = x+s and opstr = s+y (or vice versa), in which case we should
// record opstr = x+s+y. However, this has not come up in practice.
// Failing that, we can't satisfy the sequencing requirements.
fmt.Fprintf(os.Stderr, "%s: %s appears in both %s and %s\n", text, op, old, opstr)
}
}
}
for _, op := range strings.Split(opstr, ",") {
if op != "" {
p.OpRanges[op] = opstr
}
}
// Process the arguments, building a list of argument descriptions.
// Each argument description has the form <argument>|field@off|field@off...
// where the |field@off suffixes give the name and location of the fields
// needed by the argument. Each such string maps to a different decoding
// type in the generated table, according to the argOps map.
var args []string
for argstr != "" {
// Find longest match among argSuffixes pieces.
best := 0
for a := range argSuffixes {
if argstr == a || strings.HasPrefix(argstr, a+",") {
if best < len(a) {
best = len(a)
}
}
}
if best == 0 {
fmt.Fprintf(os.Stderr, "%s: unknown arg %s\n", text, argstr)
break
}
var arg, desc string
arg, argstr = argstr[:best], strings.TrimSpace(strings.TrimLeft(argstr[best:], ","))
desc = arg
for _, f := range strings.Split(argSuffixes[desc], ",") {
if f == "" {
continue
}
if fieldWidth[f] == 0 {
fmt.Fprintf(os.Stderr, "%s: arg %s missing %s in encoding %s\n", text, arg, f, encoding)
}
fieldUsed[f] = true
desc += fmt.Sprintf("|%s@%d", f, fieldOffset[f])
}
args = append(args, desc)
}
// Check that all encoding fields were used by suffix or argument decoding.
for f := range fieldWidth {
switch f {
case "0", "1", "(0)", "(1)":
// ok
default:
if !fieldUsed[f] {
fmt.Fprintf(os.Stderr, "%s: encoding field %s not used in %s\n", text, f, encoding)
}
}
}
// Determine decoding priority. Instructions that say 'SEE X' in the tag
// are considered lower priority than ones that don't. In theory the
// structure described by the SEE tags might be richer than that, but
// in practice it only has those two levels.
// We leave space for two more priorities according to whether the
// fuzzy bits are set correctly. The full set of priorities then is:
//
// 4 - no SEE tag, fuzzy bits all match
// 3 - no SEE tag, some fuzzy bits don't match
// 2 - SEE tag, fuzzy bits all match
// 1 - SEE tag, some fuzzy bits don't match
//
// You could argue for swapping the middle two levels but so far
// it has not been an issue.
pri := 4
if strings.Contains(tags, "SEE") {
pri = 2
}
inst := Inst{
Text: text,
Encoding: encoding,
Mask: uint32(mask),
Value: uint32(value),
Priority: pri,
OpBase: ops[0],
OpBits: opBits,
Args: args,
}
p.Inst = append(p.Inst, inst)
if fuzzy != 0 {
inst.Mask &^= fuzzy
inst.Priority--
p.Inst = append(p.Inst, inst)
}
}
// opSuffix describes the encoding fields used to resolve a given opcode suffix.
var opSuffix = map[string]string{
"<ADD,SUB>": "op",
"<BIF,BIT,BSL>": "op:2",
"<MLA,MLS><c>.F<32,64>": "op,cond:4,sz",
"<MLS,MLA><c>.F<32,64>": "op,cond:4,sz",
"<BT,TB><c>": "tb,cond:4",
"<TBL,TBX>.8": "op",
"<c>": "cond:4",
"<c>.32": "cond:4",
"<c>.F<32,64>": "cond:4,sz",
"<x><y><c>": "N,M,cond:4",
"<y><c>": "M,cond:4",
"{B}<c>": "B,cond:4",
"{E}<c>.F<32,64>": "E,cond:4,sz",
"{R}<c>": "R,cond:4",
"<c>.F<32,64>.<U,S>32": "cond:4,sz,op",
"<R,><c>.<U,S>32.F<32,64>": "op,cond:4,signed,sz",
"{S}<c>": "S,cond:4",
"{W}": "R",
"{X}<c>": "M,cond:4",
"<B,T><c>.<F32.F16,F16.F32>": "T,cond:4,op",
"<c>.<F64.F32,F32.F64>": "cond:4,sz",
"<c>.FX<S,U><16,32>.F<32,64>": "cond:4,U,sx,sz",
"<c>.F<32,64>.FX<S,U><16,32>": "cond:4,sz,U,sx",
}
// choices[x] describes the choices for filling in "<"+x+">" in an opcode suffix.
// Opcodes that end up containing ZZ take up a numeric sequence value but are
// not exported in the package API.
var choices = map[string][]string{
"c": {".EQ", ".NE", ".CS", ".CC", ".MI", ".PL", ".VS", ".VC", ".HI", ".LS", ".GE", ".LT", ".GT", ".LE", "", ".ZZ"},
"x": {"B", "T"},
"y": {"B", "T"},
}
// argOps maps from argument descriptions to internal decoder name.
var argOps = map[string]string{
// 4-bit register encodings
"<Rm>|Rm:4@0": "arg_R_0",
"<Rn>|Rn:4@0": "arg_R_0",
"<Rt>|Rt:4@0": "arg_R_0",
"<Rm>|Rm:4@8": "arg_R_8",
"<Ra>|Ra:4@12": "arg_R_12",
"<Rd>|Rd:4@12": "arg_R_12",
"<RdLo>|RdLo:4@12": "arg_R_12",
"<Rt>|Rt:4@12": "arg_R_12",
"<Rt_nzcv>|Rt:4@12": "arg_R_12_nzcv",
"<Rd>|Rd:4@16": "arg_R_16",
"<RdHi>|RdHi:4@16": "arg_R_16",
"<Rn>|Rn:4@16": "arg_R_16",
// first and second of consecutive register pair
"<Rt1>|Rt:4@0": "arg_R1_0",
"<Rt1>|Rt:4@12": "arg_R1_12",
"<Rt2>|Rt:4@0": "arg_R2_0",
"<Rt2>|Rt:4@12": "arg_R2_12",
// register arithmetic
"<Rm>,<type> <Rs>|Rm:4@0|Rs:4@8|type:2@5": "arg_R_shift_R",
"<Rm>{,<shift>}|Rm:4@0|imm5:5@7|type:2@5": "arg_R_shift_imm",
"<Rn>{,<shift>}|Rn:4@0|imm5:5@7|sh@6": "arg_R_shift_imm",
"<Rm>{,LSL #<imm5>}|Rm:4@0|imm5:5@7": "arg_R_shift_imm",
"<Rm>{,<rotation>}|Rm:4@0|rotate:2@10": "arg_R_rotate",
// memory references
"<Rn>{!}|Rn:4@16|W@21": "arg_R_16_WB",
"[<Rn>]|Rn:4@16": "arg_mem_R",
"[<Rn>,+/-<Rm>{, <shift>}]{!}|Rn:4@16|U@23|Rm:4@0|type:2@5|imm5:5@7|P@24|W@21": "arg_mem_R_pm_R_shift_imm_W",
"[<Rn>{,#+/-<imm8>}]{!}|Rn:4@16|P@24|U@23|W@21|imm4H:4@8|imm4L:4@0": "arg_mem_R_pm_imm8_W",
"[<Rn>] {,#+/-<imm8>}|Rn:4@16|U@23|imm4H:4@8|imm4L:4@0": "arg_mem_R_pm_imm8_postindex",
"[<Rn>{,#+/-<imm12>}]{!}|Rn:4@16|P@24|U@23|W@21|imm12:12@0": "arg_mem_R_pm_imm12_W",
"[<Rn>],#+/-<imm12>|Rn:4@16|imm12:12@0|U@23": "arg_mem_R_pm_imm12_postindex",
"[<Rn>,#+/-<imm12>]|Rn:4@16|U@23|imm12:12@0": "arg_mem_R_pm_imm12_offset",
"[<Rn>] {,#+/-<imm12>}|Rn:4@16|U@23|imm12:12@0": "arg_mem_R_pm_imm12_postindex",
"[<Rn>], +/-<Rm>|Rn:4@16|U@23|Rm:4@0": "arg_mem_R_pm_R_postindex",
"[<Rn>,+/-<Rm>]{!}|Rn:4@16|U@23|Rm:4@0|P@24|W@21": "arg_mem_R_pm_R_W",
"[<Rn>],+/-<Rm>{, <shift>}|Rn:4@16|Rm:4@0|imm5:5@7|type:2@5|U@23": "arg_mem_R_pm_R_shift_imm_postindex",
"[<Rn>,+/-<Rm>{, <shift>}]|Rn:4@16|U@23|Rm:4@0|type:2@5|imm5:5@7": "arg_mem_R_pm_R_shift_imm_offset",
"[<Rn>{,#+/-<imm8>}]|Rn:4@16|U@23|imm8:8@0": "arg_mem_R_pm_imm8at0_offset",
// pc-relative constants
"<label+12>|imm12:12@0": "arg_label_p_12",
"<label-12>|imm12:12@0": "arg_label_m_12",
"<label+/-12>|imm12:12@0|U@23": "arg_label_pm_12",
"<label+/-4+4>|imm4H:4@8|imm4L:4@0|U@23": "arg_label_pm_4_4",
// constants
"#<const>|imm12:12@0": "arg_const",
"#<imm5>|imm5:5@7": "arg_imm5",
"#<imm5_nz>|imm5:5@7": "arg_imm5_nz",
"#<imm5_32>|imm5:5@7": "arg_imm5_32",
"<label24>|imm24:24@0": "arg_label24",
"#<lsb>|lsb:5@7": "arg_imm5",
"#<width>|lsb:5@7|msb:5@16": "arg_lsb_width",
"#<imm12+4>|imm12:12@8|imm4:4@0": "arg_imm_12at8_4at0",
"#<imm12+4>|imm12:12@0|imm4:4@16": "arg_imm_4at16_12at0",
"<label24H>|imm24:24@0|H@24": "arg_label24H",
"#<option>|option:4@0": "arg_option",
"#<widthm1>|widthm1:5@16": "arg_widthm1",
"#<sat_imm4>|sat_imm:4@16": "arg_satimm4",
"#<sat_imm5>|sat_imm:5@16": "arg_satimm5",
"#<sat_imm4m1>|sat_imm:4@16": "arg_satimm4m1",
"#<sat_imm5m1>|sat_imm:5@16": "arg_satimm5m1",
"#<imm24>|imm24:24@0": "arg_imm24",
// special
"<registers>|register_list:16@0": "arg_registers",
"<registers2>|register_list:16@0": "arg_registers2",
"<registers1>|Rt:4@12": "arg_registers1",
"<endian_specifier>|E@9": "arg_endian",
"SP": "arg_SP",
"APSR": "arg_APSR",
"FPSCR": "arg_FPSCR",
// VFP floating point registers
"<Sd>|Vd:4@12|D@22": "arg_Sd",
"<Sd,Dd>|Vd:4@12|D@22|sz@8": "arg_Sd_Dd",
"<Dd,Sd>|Vd:4@12|D@22|sz@8": "arg_Dd_Sd",
"<Sn>|Vn:4@16|N@7": "arg_Sn",
"<Sn,Dn>|Vn:4@16|N@7|sz@8": "arg_Sn_Dn",
"<Sm>|Vm:4@0|M@5": "arg_Sm",
"<Sm,Dm>|Vm:4@0|M@5|sz@8": "arg_Sm_Dm",
"#0.0": "arg_fp_0",
"#<imm_vfp>|imm4H:4@16|imm4L:4@0|sz@8": "arg_imm_vfp",
"#<fbits>|sx@7|imm4:4@0|i@5": "arg_fbits",
"<Dn[x]>|N@7|Vn:4@16|opc1@21": "arg_Dn_half",
"<Dd[x]>|D@7|Vd:4@16|opc1@21": "arg_Dn_half",
}
// argSuffixes describes the encoding fields needed for a particular suffix.
// The set of keys in argSuffixes also drives the identification of suffix pieces.
// For example, <Rm> and <Rm>{, <type> <Rs>} are both keys in the map
// and matching is done 'longest first', so "<Rm>, <Rm>{, <type> <Rs>}" is
// parsed as just two arguments despite the extra ", ".
// The field order in the map values must match the order expected in
// the argument descriptions in argOps.
var argSuffixes = map[string]string{
"#0": "",
"#0.0": "",
"#<const>": "imm12:12",
"#<fbits>": "sx,imm4:4,i",
"#<imm12+4>": "imm12:12,imm4:4",
"#<imm24>": "imm24:24",
"#<imm3>": "imm3:3",
"#<imm4>": "imm4:4",
"#<imm5>": "imm5:5",
"#<imm5_nz>": "imm5:5",
"#<imm5_32>": "imm5:5",
"#<imm6>": "imm6:6",
"#<immsize>": "size:2",
"#<imm_vfp>": "imm4H:4,imm4L:4,sz",
"#<sat_imm4>": "sat_imm:4",
"#<sat_imm5>": "sat_imm:5",
"#<sat_imm4m1>": "sat_imm:4",
"#<sat_imm5m1>": "sat_imm:5",
"#<lsb>": "lsb:5",
"#<option>": "option:4",
"#<width>": "lsb:5,msb:5",
"#<widthm1>": "widthm1:5",
"+/-<Rm>": "Rm:4,U",
"<Dd>": "D,Vd:4",
"<Dd[x]>": "D,Vd:4,opc1",
"<Dm>": "M,Vm:4",
"<Dm[x]>": "M,Vm:4,size:2",
"<Dn>": "N,Vn:4",
"<Dn[x]>": "N,Vn:4,opc1",
"<Dm[size_x]>": "imm4:4",
"<Qd>": "D,Vd:4",
"<Qm>": "M,Vm:4",
"<Qn>": "N,Vn:4",
"<Ra>": "Ra:4",
"<Rd>": "Rd:4",
"<RdHi>": "RdHi:4",
"<RdLo>": "RdLo:4",
"<Rm>": "Rm:4",
"<Rm>{,<rotation>}": "Rm:4,rotate:2",
"<Rm>{,<shift>}": "Rm:4,imm5:5,type:2",
"<Rm>{,LSL #<imm5>}": "Rm:4,imm5:5",
"<Rn>": "Rn:4",
"<Rn>{!}": "Rn:4,W",
"<Rn>{,<shift>}": "Rn:4,imm5:5,sh",
"<Rs>": "Rs:4",
"<Rt1>": "Rt:4",
"<Rt2>": "Rt:4",
"<Rt>": "Rt:4",
"<Rt_nzcv>": "Rt:4",
"<Sd>": "Vd:4,D",
"<Sm1>": "Vm:4,M",
"<Sm>": "Vm:4,M",
"<Sn>": "Vn:4,N",
"<Sd,Dd>": "Vd:4,D,sz",
"<Dd,Sd>": "Vd:4,D,sz",
"<Sn,Dn>": "Vn:4,N,sz",
"<Sm,Dm>": "Vm:4,M,sz",
"<endian_specifier>": "E",
"<label+/-12>": "imm12:12,U",
"<label+12>": "imm12:12",
"<label-12>": "imm12:12",
"<label24>": "imm24:24",
"<label24H>": "imm24:24,H",
"<label+/-4+4>": "imm4H:4,imm4L:4,U",
"<list4>": "D,Vd:4,type:4",
"<list3>": "D,Vd:4,index_align:4",
"<list3t>": "D,Vd:4,T",
"<list1>": "D,Vd:4",
"<list_len>": "N,Vn:4,len:2",
"<vlist32>": "D,Vd:4,imm8:8",
"<vlist64>": "D,Vd:4,imm8:8",
"<registers>": "register_list:16",
"<registers2>": "register_list:16",
"<registers1>": "Rt:4",
"APSR": "",
"<Rm>,<type> <Rs>": "Rm:4,Rs:4,type:2",
"FPSCR": "",
"SP": "",
"[<Rn>,#+/-<imm12>]": "Rn:4,U,imm12:12",
"[<Rn>,+/-<Rm>]{!}": "Rn:4,U,Rm:4,P,W",
"[<Rn>,+/-<Rm>{, <shift>}]": "Rn:4,U,Rm:4,type:2,imm5:5",
"[<Rn>,+/-<Rm>{, <shift>}]{!}": "Rn:4,U,Rm:4,type:2,imm5:5,P,W",
"[<Rn>] {,#+/-<imm12>}": "Rn:4,U,imm12:12",
"[<Rn>] {,#+/-<imm8>}": "Rn:4,U,imm4H:4,imm4L:4",
"[<Rn>]": "Rn:4",
"[<Rn>],#+/-<imm12>": "Rn:4,imm12:12,U",
"[<Rn>],+/-<Rm>{, <shift>}": "Rn:4,Rm:4,imm5:5,type:2,U",
"[<Rn>]{!}": "Rn:4,Rm:4",
"[<Rn>{@<align>}]{!}": "XXX",
"[<Rn>{,#+/-<imm12>}]{!}": "Rn:4,P,U,W,imm12:12",
"[<Rn>{,#+/-<imm8>}]{!}": "Rn:4,P,U,W,imm4H:4,imm4L:4",
"[<Rn>{,#+/-<imm8>}]": "Rn:4,U,imm8:8",
"[<Rn>], +/-<Rm>": "Rn:4,U,Rm:4",
"#<imm_simd1>": "i,imm3:3,imm4:4,cmode:4",
"#<imm_simd>": "op,i,imm3:3,imm4:4,cmode:4",
"#<imm_vs>": "L,imm6:6",
"#<imm_vsn>": "imm6:6",
}
// cross returns the string concatenation cross product of xs and ys.
func cross(xs []string, ys ...string) []string {
var xys []string
for _, x := range xs {
for _, y := range ys {
xys = append(xys, x+y)
}
}
return xys
}
// crossCond returns the cross product of xs with all the possible
// conditional execution suffixes. It is assumed that each string x in xs
// contains a substring _COND_ marking where the conditional suffix
// should be placed.
func crossCond(xs []string) []string {
ys := choices["c"]
var xys []string
for _, x := range xs {
i := strings.Index(x, "_COND_")
pre, post := x[:i], x[i+6:]
for _, y := range ys {
xys = append(xys, pre+y+post)
}
}
return xys
}
// printText implements the -fmt=text mode, which is not implemented (yet?).
func printText(p *Prog) {
log.Fatal("-fmt=text not implemented")
}
// printDecoder implements the -fmt=decoder mode.
// It emits the tables.go for package armasm's decoder.
func printDecoder(p *Prog) {
fmt.Printf("package armasm\n\n")
// Build list of opcodes sorted by name
// but preserving the sequential ranges needed for opcode decoding.
haveRange := make(map[string]string)
for _, r := range p.OpRanges {
haveRange[r] = r
}
var ranges []string
for _, r := range haveRange {
ranges = append(ranges, r)
}
sort.Strings(ranges)
// Emit const definitions for opcodes.
fmt.Printf("const (\n")
iota := 0
fmt.Printf("\t_ Op = iota\n")
iota++
for _, r := range ranges {
for _, op := range strings.Split(r, ",") {
if op == "" {
continue
}
// Assume if opcode says .EQ it is the start of a 16-wide
// iteration through the conditional suffixes. If so, emit
// blank names until the assigned value is 16-aligned.
if strings.Contains(op, ".EQ") {
for iota&15 != 0 {
fmt.Printf("\t_\n")
iota++
}
}
fmt.Printf("\t%s\n", strings.Replace(op, ".", "_", -1))
iota++
}
}
fmt.Printf(")\n")
// Emit slice mapping opcode number to name string.
fmt.Printf("\nvar opstr = [...]string{\n")
for _, r := range ranges {
for _, op := range strings.Split(r, ",") {
if op == "" {
continue
}
fmt.Printf("\t%s: %q,\n", strings.Replace(op, ".", "_", -1), op)
}
}
fmt.Printf("}\n")
// Emit decoding table.
unknown := map[string]bool{}
fmt.Printf("\nvar instFormats = [...]instFormat{\n")
for _, inst := range p.Inst {
fmt.Printf("\t{%#08x, %#08x, %d, %s, %#x, instArgs{", inst.Mask, inst.Value, inst.Priority, strings.Replace(inst.OpBase, ".", "_", -1), inst.OpBits)
for i, a := range inst.Args {
if i > 0 {
fmt.Printf(", ")
}
str := argOps[a]
if str == "" && !unknown[a] {
fmt.Fprintf(os.Stderr, "%s: unknown arg %s\n", inst.Text, a)
unknown[a] = true
}
fmt.Printf("%s", str)
}
fmt.Printf("}}, // %s %s\n", inst.Text, inst.Encoding)
}
fmt.Printf("}\n")
}