whatcanGOwrong
This commit is contained in:
@@ -0,0 +1,757 @@
|
||||
// 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")
|
||||
}
|
||||
Reference in New Issue
Block a user