Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1392 lines (1279 sloc) 34.2 KB
// 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.
// X86map constructs the x86 opcode map from the instruction set CSV file.
//
// Usage:
// x86map [-fmt=format] x86.csv
//
// The known output formats are:
//
// text (default) - print decoding tree in text form
// decoder - print decoding tables for the x86asm package
// scanner - print scanning tables for x86scan package
package main
import (
"bufio"
"bytes"
"encoding/csv"
"flag"
"fmt"
"io"
"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: x86map [-fmt=format] x86.csv\n")
os.Exit(2)
}
func main() {
log.SetFlags(0)
log.SetPrefix("x86map: ")
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
case "scanner":
print = printScanner
}
p, err := readCSV(flag.Arg(0))
if err != nil {
log.Fatal(err)
}
//p = mergeTail(p)
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]) < 6 {
return nil, fmt.Errorf("csv too narrow: need at least six columns")
}
p := &Prog{}
for _, row := range table {
add(p, row[0], row[1], row[2], row[3], row[4], row[5])
}
check(p)
return p, nil
}
// A Prog is a single node in the tree representing the instruction format.
// Collectively the tree of nodes form a kind of program for decoding.
// Each Prog has a single action, identifying the kind of node it is,
// and then children to be executed according to the action.
// For example, the Prog with Action="decode" has children named for each
// possible next byte in the input, and those children are the decoding
// tree to execute for the corresponding bytes.
type Prog struct {
Path string
Action string
Child map[string]*Prog
PC int
TailID int
}
// keys returns the child keys in sorted order.
func (p *Prog) keys() []string {
var keys []string
for key := range p.Child {
keys = append(keys, key)
}
sort.Strings(keys)
return keys
}
// findChildLeaf finds a leaf node in the subtree rooted at p
// and returns that node's full path. The path is useful in error
// messages as an example of where a particular subtree is headed.
func (p *Prog) findChildLeaf() string {
for {
if len(p.Child) == 0 {
return p.Path
}
p = p.Child[p.keys()[0]]
}
}
// walk advances from p to apply the given action and key.
// If p has no action yet, the action is recorded as p.Action.
// Otherwise the action must match p's action: every node in the
// tree can have at most one action, although possibly with many
// alternative keys.
// If p already has an alternative with the given key, walk returns
// that preexisting subtree. Otherwise walk allocates a new Prog
// representing that subtree and returns that node.
func (p *Prog) walk(action, key, text, opcode string) *Prog {
if p.Action == "" {
p.Action = action
} else if p.Action != action {
log.Printf("%s; %s: conflicting paths %s and %s|%s %s\n", text, opcode, p.findChildLeaf(), p.Path, action, key)
return new(Prog)
}
q := p.Child[key]
if q == nil {
if p.Child == nil {
p.Child = make(map[string]*Prog)
}
q = new(Prog)
q.Path = fmt.Sprintf("%s|%s %s", p.Path, action, key)
p.Child[key] = q
}
return q
}
// add adds a single instructions to the tree rooted at root.
// The string arguments match the CSV: instruction mnemonic,
// opcode encoding, validity in 32- and 64-bit modes, CPUID
// feature set (ignored), and additional tags.
//
// In effect, add adds a new path through the tree leading to
// the given instruction, but it reuses as much of the existing
// tree structure as possible. For example if there have already
// been instructions added starting with 0F and this instruction
// also starts with 0F, that 0F subtree node is reused instead of
// allocating a parallel one. To maximize the reuse, the check action
// sequence along the path being added is the same for every instruction:
// encoding pieces needed to make a decision, 64-bit mode check,
// rex check, prefix check, address size check, data size check,
// register vs memory argument check. Once all those checks have
// been applied, the assumption is that we have uniquely identified
// an instruction, and at that point it is okay to diverge from the
// uniform pattern to set the opcode and read the specific arguments
// corresponding to the instruction at hand.
//
// The maximimal reuse of the existing tree means that the tree
// resulting from all adds have been done amounts to a decision tree.
// There is one detail that makes it non-deterministic: some checks
// do not matter to some instructions and those are recorded as "any" keys.
// If you are decoding and there is a key for the specific thing you are
// seeing as well as the "any" key, both must be considered. To avoid
// adding complexity to the decoder execution, the 'check' function
// removes this case by merging "any" trees into specific keys when
// present.
func add(root *Prog, text, opcode, valid32, valid64, cpuid, tags string) {
// These are not real instructions: they are either
// prefixes for other instructions, composite instructions
// built from multiple individual instructions, or alternate
// mnemonics of other encodings.
// Discard for disassembly, because we want a unique decoding.
if strings.Contains(tags, "pseudo") {
return
}
// Treat REX.W + opcode as being like having an "operand64" tag.
// The REX.W flag sets the operand size to 64 bits; in this way it is
// not much different than the 66 prefix that inverts 32 vs 16 bits.
if strings.Contains(opcode, "REX.W") {
if !strings.Contains(tags, "operand64") {
if tags != "" {
tags += ","
}
tags += "operand64"
}
}
// If there is more than one operand size given, we need to do
// a separate add for each size, because we need multiple
// keys to be added in the operand size branch, and the code makes
// a linear pass through the tree adding just one key to each node.
// We would need to do the same for any other possible repeated tag
// (for example, if an instruction could have multiple address sizes)
// but so far operand size is the only tag we have needed to repeat.
if strings.Count(tags, "operand") > 1 {
f := strings.Split(tags, ",")
var ops []string
w := 0
for _, tag := range f {
if strings.HasPrefix(tag, "operand") {
ops = append(ops, tag)
} else {
if strings.Contains(tag, "operand") {
log.Fatalf("unknown tag %q", tag)
}
f[w] = tag
w++
}
}
f = f[:w]
for _, op := range ops {
add(root, text, opcode, valid32, valid64, cpuid, strings.Join(append(f, op), ","))
}
return
}
p := root
walk := func(action, item string) {
p = p.walk(action, item, text, opcode)
}
// Ignore VEX instructions for now.
if strings.HasPrefix(opcode, "VEX") {
if !strings.HasPrefix(text, "VMOVNTDQ") &&
!strings.HasPrefix(text, "VMOVDQA") &&
!strings.HasPrefix(text, "VMOVDQU") &&
!strings.HasPrefix(text, "VZEROUPPER") {
return
}
if !strings.HasPrefix(opcode, "VEX.256") && !strings.HasPrefix(text, "VZEROUPPER") {
return
}
if !strings.Contains(tags, "VEXC4") {
add(root, text, opcode, valid32, valid64, cpuid, tags+",VEXC4")
}
encoding := strings.Fields(opcode)
walk("decode", encoding[1])
walk("is64", "any")
if strings.Contains(tags, "VEXC4") {
walk("prefix", "C4")
} else {
walk("prefix", "C5")
}
for _, pref := range strings.Split(encoding[0], ".") {
if isVexEncodablePrefix[pref] {
walk("prefix", pref)
}
}
}
var rex, prefix string
encoding := strings.Fields(opcode)
if len(encoding) > 0 && strings.HasPrefix(encoding[0], "REX") {
rex = encoding[0]
encoding = encoding[1:]
if len(encoding) > 0 && encoding[0] == "+" {
encoding = encoding[1:]
}
}
if len(encoding) > 0 && isPrefix[encoding[0]] {
prefix = encoding[0]
encoding = encoding[1:]
}
if rex == "" && len(encoding) > 0 && strings.HasPrefix(encoding[0], "REX") {
rex = encoding[0]
if rex == "REX" {
log.Printf("REX without REX.W: %s %s", text, opcode)
}
encoding = encoding[1:]
if len(encoding) > 0 && encoding[0] == "+" {
encoding = encoding[1:]
}
}
if len(encoding) > 0 && isPrefix[encoding[0]] {
log.Printf("%s %s: too many prefixes", text, opcode)
return
}
var haveModRM, havePlus bool
var usedReg string
for len(encoding) > 0 && (isHex(encoding[0]) || isSlashNum(encoding[0])) {
key := encoding[0]
if isSlashNum(key) {
if usedReg != "" {
log.Printf("%s %s: multiple modrm checks", text, opcode)
}
haveModRM = true
usedReg = key
}
if i := strings.Index(key, "+"); i >= 0 {
key = key[:i+1]
havePlus = true
}
walk("decode", key)
encoding = encoding[1:]
}
if valid32 != "V" {
walk("is64", "1")
} else if valid64 != "V" {
walk("is64", "0")
} else {
walk("is64", "any")
}
if prefix == "" {
prefix = "0"
}
walk("prefix", prefix)
if strings.Contains(tags, "address16") {
walk("addrsize", "16")
} else if strings.Contains(tags, "address32") {
walk("addrsize", "32")
} else if strings.Contains(tags, "address64") {
walk("addrsize", "64")
} else {
walk("addrsize", "any")
}
if strings.Contains(tags, "operand16") {
walk("datasize", "16")
} else if strings.Contains(tags, "operand32") {
walk("datasize", "32")
} else if strings.Contains(tags, "operand64") {
walk("datasize", "64")
} else {
walk("datasize", "any")
}
if len(encoding) > 0 && encoding[0] == "/r" {
haveModRM = true
}
if haveModRM {
if strings.Contains(tags, "modrm_regonly") {
walk("ismem", "0")
} else if strings.Contains(tags, "modrm_memonly") {
walk("ismem", "1")
} else {
walk("ismem", "any")
}
}
walk("op", strings.Fields(text)[0])
if len(encoding) > 0 && strings.HasPrefix(encoding[0], "VEX") {
for _, field := range encoding[2:] {
walk("read", field)
}
} else {
for _, field := range encoding {
walk("read", field)
}
}
var usedRM string
for _, arg := range strings.Fields(text)[1:] {
arg = strings.TrimRight(arg, ",")
if usesReg[arg] && !haveModRM && !havePlus {
log.Printf("%s %s: no modrm field to use for %s", text, opcode, arg)
continue
}
if usesRM[arg] && !haveModRM {
log.Printf("%s %s: no modrm field to use for %s", text, opcode, arg)
continue
}
if usesReg[arg] {
if usedReg != "" {
log.Printf("%s %s: modrm reg field used by both %s and %s", text, opcode, usedReg, arg)
continue
}
usedReg = arg
}
if usesRM[arg] {
if usedRM != "" {
log.Printf("%s %s: modrm r/m field used by both %s and %s", text, opcode, usedRM, arg)
continue
}
usedRM = arg
}
walk("arg", arg)
}
walk("match", "!")
}
// allKeys records the list of all possible child keys for actions that support "any".
var allKeys = map[string][]string{
"is64": {"0", "1"},
"ismem": {"0", "1"},
"addrsize": {"16", "32", "64"},
"datasize": {"16", "32", "64"},
}
// check checks that the program tree is well-formed.
// It also merges "any" keys into specific decoding keys in order to
// create an invariant that a particular check node either has a
// single "any" child - making it a no-op - or has no "any" children.
// See the discussion of "any" in the comment for add above.
func check(p *Prog) {
if p.Child["any"] != nil && len(p.Child) > 1 {
for _, key := range p.keys() {
if key != "any" {
mergeCopy(p.Child[key], p.Child["any"])
}
}
if allKeys[p.Action] == nil {
log.Printf("%s: unknown key space for %s=any", p.Path, p.Action)
}
for _, key := range allKeys[p.Action] {
if p.Child[key] == nil {
p.Child[key] = p.Child["any"]
}
}
delete(p.Child, "any")
}
for _, q := range p.Child {
check(q)
}
switch p.Action {
case "op", "read", "arg":
if len(p.Child) > 1 {
log.Printf("%s: multiple children for action=%s: %v", p.Path, p.Action, p.keys())
}
}
}
// mergeCopy merges a copy of the tree rooted at src into dst.
// It is only used once no more paths will be added to the tree,
// so it is safe to introduce cross-links that make the program
// a dag rather than a tree.
func mergeCopy(dst, src *Prog) {
//log.Printf("merge %s|%s and %s|%s\n", dst.Path, dst.Action, src.Path, src.Action)
if dst.Action != src.Action {
log.Printf("cannot merge %s|%s and %s|%s", dst.Path, dst.Action, src.Path, src.Action)
return
}
for _, key := range src.keys() {
if dst.Child[key] == nil {
// Create new subtree by creating cross-link.
dst.Child[key] = src.Child[key]
} else {
// Merge src subtree into existing dst subtree.
mergeCopy(dst.Child[key], src.Child[key])
}
}
}
// set returns a map mapping each of the words in all to true.
func set(all string) map[string]bool {
m := map[string]bool{}
for _, f := range strings.Fields(all) {
m[f] = true
}
return m
}
// isPrefix records the x86 opcode prefix bytes.
var isPrefix = set(`
26
2E
36
3E
64
65
66
67
F0
F2
F3
`)
// usesReg records the argument codes that use the modrm reg field.
var usesReg = set(`
r8
r16
r32
r64
`)
// usesRM records the argument codes that use the modrm r/m field.
var usesRM = set(`
r/m8
r/m16
r/m32
r/m64
`)
var isVexEncodablePrefix = set(`
0F
0F38
0F3A
66
F3
F2
`)
// isHex reports whether the argument is a two digit hex number
// possibly followed by a +foo suffix.
func isHex(s string) bool {
if i := strings.Index(s, "+"); i >= 0 {
s = s[:i]
}
if len(s) != 2 {
return false
}
for i := 0; i < len(s); i++ {
c := s[i]
if '0' <= c && c <= '9' || 'A' <= c && c <= 'F' {
continue
}
return false
}
return true
}
// isSlashNum reports whether the argument is /n for some number n in [0,7].
func isSlashNum(s string) bool {
return len(s) == 2 && s[0] == '/' && '0' <= s[1] && s[1] <= '7'
}
// mergeTail is supposed to merge common subtrees (program tails),
// reducing the size of the final program code.
// It identifies the subtrees using a bottom-up canonicalization.
//
// THIS CODE DOES NOT WORK. IT NEEDS TO BE DEBUGGED.
func mergeTail(p *Prog, emitted map[string]*Prog) *Prog {
if emitted == nil {
emitted = make(map[string]*Prog)
}
if p.Action == "match" {
return p
}
for _, key := range p.keys() {
p.Child[key] = mergeTail(p.Child[key], emitted)
}
op := ""
for _, key := range p.keys() {
q := p.Child[key]
if q.Action != "op" || len(q.Child) > 1 {
op = ""
break
}
qop := q.keys()[0]
if op == "" {
op = qop
} else if op != qop {
op = ""
break
}
}
if op != "" {
// Pull 'op x' up above the discriminator.
p1 := new(Prog)
*p1 = *p
for _, key := range p.keys() {
p1.Child[key] = p.Child[key].Child[op]
}
p.Action = "op"
p.Child = map[string]*Prog{op: p1}
}
var buf bytes.Buffer
fmt.Fprintf(&buf, "%s\n", p.Action)
for _, key := range p.keys() {
fmt.Fprintf(&buf, "%s %d\n", key, p.Child[key].TailID)
}
key := buf.String()
if q := emitted[key]; q != nil {
return q
}
emitted[key] = p
p.TailID = len(emitted)
return p
}
// printText prints the tree in textual form.
func printText(p *Prog) {
printTree(os.Stdout, p, 0, false)
}
var tabs = strings.Repeat(" ", 100)
func printTree(w io.Writer, p *Prog, depth int, compact bool) {
if compact && len(p.Child) == 1 {
fmt.Fprintf(w, "%.*s%s", 4*depth, tabs, p.Action)
for len(p.Child) == 1 {
key := p.keys()[0]
child := p.Child[key]
fmt.Fprintf(w, " %s %s", key, child.Action)
p = child
}
fmt.Fprintf(w, "\n")
} else {
fmt.Fprintf(w, "%.*s%s\n", 4*depth, tabs, p.Action)
}
for _, key := range p.keys() {
fmt.Fprintf(w, "%.*s%s\n", 4*(depth+1), tabs, key)
printTree(w, p.Child[key], depth+2, compact)
}
}
// printDecoder prints a Go array containing the decoder program.
// It runs in two passes, both of which traverse and could generate
// the entire program. The first pass records the PC for each Prog node,
// and the second pass emits the actual program, using the PCs as jump
// targets in the places where the program is a dag rather than a tree.
func printDecoder(p *Prog) {
opMap := map[string]bool{
"PAUSE": true,
}
printDecoderPass(p, 1, false, opMap)
fmt.Printf("// DO NOT EDIT\n")
fmt.Printf("// generated by: x86map -fmt=decoder %s\n", inputFile)
fmt.Printf("\n")
fmt.Printf("package x86asm\n\n")
fmt.Printf("var decoder = [...]uint16{\n\tuint16(xFail),\n")
printDecoderPass(p, 1, true, opMap)
fmt.Printf("}\n\n")
var ops []string
for op := range opMap {
ops = append(ops, op)
}
sort.Strings(ops)
fmt.Printf("const (\n")
fmt.Printf("\t_ Op = iota\n\n")
last := ""
for _, op := range ops {
fmt.Printf("\t%s\n", op)
last = op
}
fmt.Printf(")\n\n")
fmt.Printf("const maxOp = %s\n\n", last)
fmt.Printf("var opNames = [...]string{\n")
for _, op := range ops {
fmt.Printf("\t%s: \"%s\",\n", op, op)
}
fmt.Printf("}\n")
}
// printScanner prints the decoding table for a scanner.
// The scanner can identify instruction boundaries but does not do
// full decoding. It is meant to be lighter weight than the x86asm
// decoder tables.
func printScanner(p *Prog) {
walkScanTree(p, -1)
var out []uint16
out = append(out, 0)
emitScanFunc(p, &out)
fmt.Printf("var scanProg = []uint16{\n")
fmt.Printf("\t/*0*/ 0, // dead\n")
for i := 1; i < len(out); i++ {
fmt.Printf("\t/*%d*/ ", i)
switch out[i] {
default:
log.Fatalf("malformed program %#x", out[i])
case scanMatch:
fmt.Printf("scanMatch,\n")
continue
case scanJump:
fmt.Printf("scanJump, %d,\n", out[i+1])
i++
continue
case scanSwitchByte:
fmt.Printf("scanSwitchByte,\n")
for j := 0; j < 256/8; j++ {
fmt.Printf("\t")
fmt.Printf("/* %#02x-%#02x */", j*8, j*8+7)
for k := 0; k < 8; k++ {
fmt.Printf(" %d,", out[i+1+j*8+k])
}
fmt.Printf("\n")
}
i += 256
continue
case scanSwitchSlash:
fmt.Printf("scanSwitchSlash, %d,\n", out[i+1])
n := int(out[i+1])
for j := 0; j < n; j++ {
fmt.Printf("\t/* byte */ %#x, %d,\n", out[i+2+2*j], out[i+2+2*j+1])
}
for j := 0; j < 8; j++ {
fmt.Printf("\t/* /%d */ %d,\n", j, out[i+2+2*n+j])
}
i += 1 + 2*n + 8
continue
case scanSwitchPrefix:
fmt.Printf("scanSwitchPrefix, %d,\n", out[i+1])
n := int(out[i+1])
for j := 0; j < n; j++ {
fmt.Printf("\t/* prefix */ %#x, %d,\n", out[i+2+2*j], out[i+2+2*j+1])
}
i += 1 + 2*n
continue
case scanSwitchIs64:
fmt.Printf("scanSwitchIs64, %d, %d\n", out[i+1], out[i+2])
i += 2
continue
case scanSwitchDatasize:
fmt.Printf("scanSwitchDatasize, %d, %d, %d\n", out[i+1], out[i+2], out[i+3])
i += 3
continue
case scanSwitchIsMem:
fmt.Printf("scanSwitchIsMem, %d, %d\n", out[i+1], out[i+2])
i += 2
continue
case scanReadModRM:
fmt.Printf("scanReadModRM,\n")
continue
case scanReadIB:
fmt.Printf("scanReadIB,\n")
continue
case scanReadIW:
fmt.Printf("scanReadIW,\n")
continue
case scanReadIWD:
fmt.Printf("scanReadIWD,\n")
continue
case scanReadIWDO:
fmt.Printf("scanReadIWDO,\n")
continue
case scanReadCWD:
fmt.Printf("scanReadCWD,\n")
continue
case scanReadCB:
fmt.Printf("scanReadCB,\n")
continue
case scanReadCDP:
fmt.Printf("scanReadCDP,\n")
continue
case scanReadCM:
fmt.Printf("scanReadCM,\n")
continue
}
}
fmt.Printf("}\n")
}
func walkScanTree(p *Prog, is64 int) {
keys := p.keys()
for _, key := range keys {
if p.Action == "is64" {
switch key {
case "0":
is64 = 0
case "1":
is64 = 1
}
}
walkScanTree(p.Child[key], is64)
}
switch p.Action {
case "read", "match":
// keep
return
case "decode":
if len(keys) >= 8 && keys[0] == "/0" && keys[7] == "/7" && allSame(p, keys) {
p.Action = "read"
p.Child = map[string]*Prog{"/r": p.Child[keys[0]]}
return
}
case "op", "arg":
// drop
*p = *p.Child[keys[0]]
return
case "prefix":
if len(keys) >= 1 && keys[0] == "0" && allSame(p, keys) {
*p = *p.Child[keys[0]]
return
}
case "is64", "addrsize", "datasize", "ismem":
if len(keys) == 1 && keys[0] == "any" {
*p = *p.Child[keys[0]]
return
}
nkey := len(allKeys[p.Action])
if p.Action == "addrsize" {
nkey = 2
}
if p.Action == "datasize" && is64 == 0 {
nkey = 2
}
if len(keys) == nkey && allSame(p, keys) {
*p = *p.Child[keys[0]]
return
}
}
switch p.Action {
case "datasize":
if len(keys) == 2 && is64 == 0 || len(keys) == 3 {
if treeText(p.Child["16"]) == "read iw match ! \n" && treeText(p.Child["32"]) == "read id match ! \n" && (len(keys) == 2 || treeText(p.Child["64"]) == "read id match ! \n") {
p.Action = "read"
p.Child = map[string]*Prog{"iwd/d": p.Child["16"].Child["iw"]}
return
}
if len(keys) == 3 && treeText(p.Child["16"]) == "read iw match ! \n" && treeText(p.Child["32"]) == "read id match ! \n" && treeText(p.Child["64"]) == "read io match ! \n" {
p.Action = "read"
p.Child = map[string]*Prog{"iwdo/d": p.Child["16"].Child["iw"]}
return
}
if treeText(p.Child["16"]) == "read /r read iw match ! \n" && treeText(p.Child["32"]) == "read /r read id match ! \n" && (len(keys) == 2 || treeText(p.Child["64"]) == "read /r read id match ! \n") {
p.Action = "read"
p.Child = map[string]*Prog{"/r": {Action: "read", Child: map[string]*Prog{"iwd/d": p.Child["16"].Child["/r"].Child["iw"]}}}
return
}
if treeText(p.Child["16"]) == "read cw match ! \n" && treeText(p.Child["32"]) == "read cd match ! \n" && (len(keys) == 2 || treeText(p.Child["64"]) == "read cd match ! \n") {
p.Action = "read"
p.Child = map[string]*Prog{"cwd/d": p.Child["16"].Child["cw"]}
return
}
if treeText(p.Child["16"]) == "read cd match ! \n" && treeText(p.Child["32"]) == "read cp match ! \n" && (len(keys) == 2 || treeText(p.Child["64"]) == "read cp match ! \n") {
p.Action = "read"
p.Child = map[string]*Prog{"cdp/d": p.Child["16"].Child["cd"]}
return
}
fmt.Printf("!! %q\n", treeText(p.Child["16"]))
}
case "is64":
if len(keys) == 2 && treeText(p.Child["0"]) == "read cwd/d match ! \n" && treeText(p.Child["1"]) == "read cd match ! \n" {
*p = *p.Child["0"]
return
}
if len(keys) == 2 && treeText(p.Child["0"]) == "read iwd/d match ! \n" && treeText(p.Child["1"]) == "read iwdo/d match ! \n" {
*p = *p.Child["1"]
return
}
}
/*
match := make(map[string][]string)
for _, key := range keys {
text := treeText(p.Child[key])
match[text] = append(match[text], key)
}
child := make(map[string]*Prog)
for _, keys := range match {
child[strings.Join(keys, ",")] = p.Child[keys[0]]
}
p.Child = child
*/
}
func treeText(p *Prog) string {
var buf bytes.Buffer
printTree(&buf, p, 0, true)
return buf.String()
}
func allSame(p *Prog, keys []string) bool {
var tree string
for i, key := range keys {
if i == 0 {
tree = treeText(p.Child[key])
continue
}
if treeText(p.Child[key]) != tree {
return false
}
}
return true
}
var scanCache = map[string]uint16{}
const (
_ uint16 = iota
scanMatch
scanJump
scanSwitchByte
scanSwitchSlash
scanSwitchIs64
scanSwitchDatasize
scanSwitchIsMem
scanSwitchPrefix
scanReadModRM
scanReadIB
scanReadIW
scanReadIWD
scanReadIWDO
scanReadCWD
scanReadCB
scanReadCDP
scanReadCM
)
func decodeKeyPlus(key string) (val, n int) {
n = 1
if strings.HasSuffix(key, "+") {
n = 8
key = key[:len(key)-1]
}
v, err := strconv.ParseUint(key, 16, 8)
if err != nil {
log.Fatalf("unexpected decode key %q", key)
}
return int(v), n
}
func decodeKey(key string) int {
val, n := decodeKeyPlus(key)
if n != 1 {
log.Panicf("unexpected decode key+ %q", key)
}
return val
}
func emitScanFunc(p *Prog, out *[]uint16) uint16 {
keys := p.keys()
text := treeText(p)
if off, ok := scanCache[text]; ok {
return off
}
start := uint16(len(*out))
scanCache[text] = start
switch p.Action {
case "decode":
if keys[0][0] != '/' {
*out = append(*out, scanSwitchByte)
off := len(*out)
for i := 0; i < 256; i++ {
*out = append(*out, 0)
}
for _, key := range keys {
val, n := decodeKeyPlus(key)
dst := emitScanFunc(p.Child[key], out)
for j := 0; j < n; j++ {
(*out)[off+val+j] = dst
}
}
return start
}
n := len(keys)
for n > 0 && keys[n-1][0] != '/' {
n--
}
total := 0
for i := n; i < len(keys); i++ {
key := keys[i]
_, n := decodeKeyPlus(key)
total += n
}
*out = append(*out, scanSwitchSlash, uint16(total))
off := len(*out)
for i := 0; i < total; i++ {
*out = append(*out, 0, 0)
}
for i := 0; i < 8; i++ {
*out = append(*out, 0)
}
for i := n; i < len(keys); i++ {
key := keys[i]
val, valn := decodeKeyPlus(key)
targ := emitScanFunc(p.Child[key], out)
for j := 0; j < valn; j++ {
(*out)[off] = uint16(val + j)
off++
(*out)[off] = targ
off++
}
}
for i := 0; i < n; i++ {
key := keys[i]
if len(key) != 2 || key[0] != '/' || key[1] < '0' || '8' <= key[1] {
log.Fatalf("unexpected decode key %q", key)
}
(*out)[off+int(key[1]-'0')] = emitScanFunc(p.Child[key], out)
}
return start
case "read":
switch keys[0] {
default:
log.Fatalf("unexpected read %q", keys[0])
case "/r":
*out = append(*out, scanReadModRM)
case "ib":
*out = append(*out, scanReadIB)
case "iw":
*out = append(*out, scanReadIW)
case "cb":
*out = append(*out, scanReadCB)
case "cm":
*out = append(*out, scanReadCM)
case "iwd/d":
*out = append(*out, scanReadIWD)
case "iwdo/d":
*out = append(*out, scanReadIWDO)
case "cwd/d":
*out = append(*out, scanReadCWD)
case "cdp/d":
*out = append(*out, scanReadCDP)
}
next := p.Child[keys[0]]
if next.Action == "match" {
*out = append(*out, scanMatch)
} else {
*out = append(*out, scanJump, 0)
off := len(*out)
(*out)[off-1] = emitScanFunc(next, out)
}
return start
case "match":
*out = append(*out, scanMatch)
return start
case "is64":
*out = append(*out, scanSwitchIs64, 0, 0)
if next := p.Child["0"]; next != nil {
(*out)[start+1] = emitScanFunc(next, out)
}
if next := p.Child["1"]; next != nil {
(*out)[start+2] = emitScanFunc(next, out)
}
return start
case "ismem":
*out = append(*out, scanSwitchIsMem, 0, 0)
if next := p.Child["0"]; next != nil {
(*out)[start+1] = emitScanFunc(next, out)
}
if next := p.Child["1"]; next != nil {
(*out)[start+2] = emitScanFunc(next, out)
}
return start
case "datasize":
*out = append(*out, scanSwitchDatasize, 0, 0, 0)
if next := p.Child["16"]; next != nil {
(*out)[start+1] = emitScanFunc(next, out)
}
if next := p.Child["32"]; next != nil {
(*out)[start+2] = emitScanFunc(next, out)
}
if next := p.Child["64"]; next != nil {
(*out)[start+3] = emitScanFunc(next, out)
}
return start
case "prefix":
*out = append(*out, scanSwitchPrefix, uint16(len(keys)))
n := len(keys)
for i := 0; i < n; i++ {
*out = append(*out, uint16(decodeKey(keys[i])), 0)
}
for i := 0; i < n; i++ {
(*out)[int(start)+2+2*i+1] = emitScanFunc(p.Child[keys[i]], out)
}
return start
}
log.Fatalf("unexpected action %q", p.Action)
return start
}
// printDecoderPass prints the decoding table program for p,
// assuming that we are emitting code at the given program counter.
// It returns the new current program counter, that is, the program
// counter after the printed instructions.
// If printing==false, printDecoderPass does not print the actual
// code words but still does the PC computation.
func printDecoderPass(p *Prog, pc int, printing bool, ops map[string]bool) int {
// Record PC on first pass.
if p.PC == 0 {
p.PC = pc
}
// If PC doesn't match, we've already printed this code
// because it was reached some other way. Jump to that copy.
if p.PC != pc {
if printing {
fmt.Printf("/*%d*/\tuint16(xJump), %d,\n", pc, p.PC)
}
return pc + 2
}
// Otherwise, emit the code for the given action.
// At the bottom, if next is non-nil, emit code for next.
// Then emit the code for the children named by the keys.
keys := p.keys()
var next *Prog
switch p.Action {
default:
log.Printf("printDecoderPass: unknown action %q: %s", p.Action, p.Path)
case "decode":
// Decode hex bytes or /n modrm op checks.
// Hex bytes take priority, so do them first.
// Hex bytes of the form "40+" indicate an
// 8 entry-wide swath of codes: 40, 41, ..., 47.
hex := 0
slash := 0
for _, key := range keys {
if isHex(key) {
if strings.Contains(key, "+") {
hex += 8
} else {
hex++
}
}
if isSlashNum(key) {
slash++
}
}
if hex > 0 {
// TODO(rsc): Introduce an xCondByte256 that has 256 child entries
// and no explicit keys. That will cut the size in half for large
// tables.
if printing {
fmt.Printf("/*%d*/\tuint16(xCondByte), %d,\n", pc, hex)
for _, key := range keys {
if !isHex(key) {
continue
}
if i := strings.Index(key, "+"); i >= 0 {
nextPC := p.Child[key].PC
n, _ := strconv.ParseUint(key[:i], 16, 0)
for j := 0; j < 8; j++ {
fmt.Printf("\t%#02x, %d,\n", int(n)+j, nextPC)
}
continue
}
fmt.Printf("\t0x%s, %d,\n", key, p.Child[key].PC)
}
}
pc += 2 + 2*hex
// All other condition checks fail the decoding if nothing is found,
// but this one falls through so that we can then do /n checks.
// If there are no upcoming /n checks, insert an explicit failure.
if slash == 0 {
if printing {
fmt.Printf("\tuint16(xFail),\n")
}
pc++
}
}
if slash > 0 {
if printing {
fmt.Printf("/*%d*/\tuint16(xCondSlashR),\n", pc)
for i := 0; i < 8; i++ {
fmt.Printf("\t%d, // %d\n", p.childPC(fmt.Sprintf("/%d", i)), i)
}
}
pc += 1 + 8
}
case "is64":
// Decode based on processor mode: 64-bit or not.
if len(keys) == 1 && keys[0] == "any" {
next = p.Child["any"]
break
}
if p.Child["any"] != nil {
log.Printf("%s: mixed is64 keys: %v", p.Path, keys)
}
if printing {
fmt.Printf("/*%d*/\tuint16(xCondIs64), %d, %d,\n", pc, p.childPC("0"), p.childPC("1"))
}
pc += 3
case "prefix":
// Decode based on presence of prefix.
// The "0" prefix means "none of the above", so if there's
// nothing else, it's the same as "any".
if len(keys) == 1 && (keys[0] == "any" || keys[0] == "0") {
next = p.Child["any"]
break
}
if p.Child["any"] != nil {
log.Printf("%s: mixed prefix keys: %v", p.Path, keys)
}
// Emit the prefixes in reverse sorted order, so that F3 and F2 are
// considered before 66, and the fallback 0 is considered last.
if printing {
fmt.Printf("/*%d*/\tuint16(xCondPrefix), %d,\n", pc, len(keys))
for i := len(keys) - 1; i >= 0; i-- {
key := keys[i]
nextPC := p.Child[key].PC
fmt.Printf("\t0x%s, %d,\n", key, nextPC)
}
}
pc += 2 + 2*len(keys)
case "addrsize":
// Decode based on address size attribute.
if len(keys) == 1 && keys[0] == "any" {
next = p.Child["any"]
break
}
if p.Child["any"] != nil {
log.Printf("%s: mixed addrsize keys: %v", p.Path, keys)
}
if printing {
fmt.Printf("/*%d*/\tuint16(xCondAddrSize), %d, %d, %d,\n", pc, p.childPC("16"), p.childPC("32"), p.childPC("64"))
}
pc += 4
case "datasize":
// Decode based on operand size attribute.
if len(keys) == 1 && keys[0] == "any" {
next = p.Child["any"]
break
}
if p.Child["any"] != nil {
log.Printf("%s: mixed datasize keys: %v", p.Path, keys)
}
if printing {
fmt.Printf("/*%d*/\tuint16(xCondDataSize), %d, %d, %d,\n", pc, p.childPC("16"), p.childPC("32"), p.childPC("64"))
}
pc += 4
case "ismem":
// Decode based on modrm form: memory or register reference.
if len(keys) == 1 && keys[0] == "any" {
next = p.Child["any"]
break
}
if p.Child["any"] != nil {
log.Printf("%s: mixed ismem keys: %v", p.Path, keys)
}
if printing {
fmt.Printf("/*%d*/\tuint16(xCondIsMem), %d, %d,\n", pc, p.childPC("0"), p.childPC("1"))
}
pc += 3
case "op":
// Set opcode.
ops[keys[0]] = true
if printing {
fmt.Printf("/*%d*/\tuint16(xSetOp), uint16(%s),\n", pc, keys[0])
}
next = p.Child[keys[0]]
pc += 2
case "read":
// Read argument bytes.
if printing {
fmt.Printf("/*%d*/\tuint16(xRead%s),\n", pc, xOp(keys[0]))
}
next = p.Child[keys[0]]
pc++
case "arg":
// Record instruction argument (interpret bytes loaded with read).
if printing {
fmt.Printf("/*%d*/\tuint16(xArg%s),\n", pc, xOp(keys[0]))
}
next = p.Child[keys[0]]
pc++
case "match":
// Finish match.
if printing {
fmt.Printf("/*%d*/\tuint16(xMatch),\n", pc)
}
pc++
return pc
}
if next != nil {
pc = printDecoderPass(next, pc, printing, ops)
}
for _, key := range keys {
q := p.Child[key]
if q.PC == 0 || q.PC == pc {
pc = printDecoderPass(q, pc, printing, ops)
}
}
return pc
}
// childPC returns the PC for the given child key.
// If the key is not present, it returns PC 0,
// which is known to be an xFail instruction.
func (p *Prog) childPC(key string) int {
q := p.Child[key]
if q == nil {
return 0
}
return q.PC
}
// isLower reports whether c is an ASCII lower case letter.
func isLower(c byte) bool {
return 'a' <= c && c <= 'z'
}
// isLetterDigit reports whether c is an ASCII letter or digit.
func isLetterDigit(c byte) bool {
return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9'
}
// xOp converts arg, an Intel manual shorthand, into a decoder opcode suffix.
// The standard form is LeadingUpperLetter with a few punctuation symbols
// turned into purely lower case words: M16and32, M16colon32, CR0dashCR7.
func xOp(arg string) string {
var buf []byte
for i := 0; i < len(arg); i++ {
c := arg[i]
if isLower(c) && (i == 0 || !isLetterDigit(arg[i-1])) {
c -= 'a' - 'A'
}
buf = append(buf, c)
}
return argFix.Replace(string(buf))
}
var argFix = strings.NewReplacer(
"/R", "SlashR",
"/", "",
"<", "",
">", "",
"+", "plus",
"-", "dash",
":", "colon",
"&", "and",
"ST(0)", "ST",
"ST(I)", "STi",
"ST(I)+Op", "STi",
)
You can’t perform that action at this time.