Files
avo/internal/inst/x86csv.go
Michael McLoughlin 7c2990754f wip
2018-11-20 11:44:44 -06:00

117 lines
2.2 KiB
Go

package inst
import (
"io"
"strconv"
"strings"
"golang.org/x/arch/x86/x86csv"
)
// ReadFromX86CSV reads instruction list from the Go "x86.csv" format.
func ReadFromX86CSV(r io.Reader) ([]Instruction, error) {
c := x86csv.NewReader(r)
rows, err := c.ReadAll()
if err != nil {
return nil, err
}
// Group by Go opcode.
groups := map[string][]*x86csv.Inst{}
for _, row := range rows {
g := row.GoOpcode()
groups[g] = append(groups[g], row)
}
var is []Instruction
for opcode, group := range groups {
i := Instruction{
Opcode: opcode,
}
for _, row := range group {
forms := formsFromRow(row)
i.Forms = append(i.Forms, forms...)
}
is = append(is, i)
}
return is, nil
}
func formsFromRow(row *x86csv.Inst) []Form {
var fs []Form
signatures := argsToSignatures(row.GoArgs())
for _, signature := range signatures {
ops := make([]Operand, len(signature))
for i, t := range signature {
ops[i] = Operand{
Type: t,
}
}
f := Form{
Operands: ops,
CPUID: splitCPUID(row.CPUID),
}
fs = append(fs, f)
}
return fs
}
func expandArg(arg string) []string {
mmprefixes := []string{"", "x", "y"}
for e := 8; e <= 512; e *= 2 {
s := strconv.Itoa(e)
switch arg {
case "r/m" + s:
return []string{"r" + s, "m" + s}
case "r" + s + "V":
return []string{"r" + s}
}
for _, p := range mmprefixes {
if arg == p+"mm2/m"+s {
return []string{p + "mm", "m" + s}
}
}
}
for _, p := range []string{"", "x", "y"} {
switch arg {
case p + "mm1", p + "mm2", p + "mmV":
return []string{p + "mm"}
}
}
return []string{arg}
}
func argsToSignatures(args []string) [][]string {
n := len(args)
if n == 0 {
return [][]string{nil}
}
types := expandArg(args[n-1])
var expanded [][]string
for _, sub := range argsToSignatures(args[:n-1]) {
for _, t := range types {
f := make([]string, n-1, n)
copy(f, sub)
f = append(f, t)
expanded = append(expanded, f)
}
}
return expanded
}
// splitCPUID splits CPUID field into flags, plus handling a few oddities.
func splitCPUID(cpuid string) []string {
switch cpuid {
case "Both AES and AVX flags":
return []string{"AES", "AVX"}
case "HLE or RTM":
return []string{"HLE/RTM"}
}
return strings.Split(cpuid, ",")
}