wip
This commit is contained in:
2261
internal/inst/testdata/x86.v0.2.csv
vendored
Normal file
2261
internal/inst/testdata/x86.v0.2.csv
vendored
Normal file
File diff suppressed because it is too large
Load Diff
24
internal/inst/types.go
Normal file
24
internal/inst/types.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package inst
|
||||
|
||||
type Instruction struct {
|
||||
Opcode string
|
||||
Forms []Form
|
||||
}
|
||||
|
||||
type Form struct {
|
||||
Operands []Operand
|
||||
CPUID []string
|
||||
}
|
||||
|
||||
type Operand struct {
|
||||
Type string
|
||||
Action Action
|
||||
}
|
||||
|
||||
type Action uint8
|
||||
|
||||
const (
|
||||
R Action = 0x1
|
||||
W Action = 0x2
|
||||
RW Action = R | W
|
||||
)
|
||||
116
internal/inst/x86csv.go
Normal file
116
internal/inst/x86csv.go
Normal file
@@ -0,0 +1,116 @@
|
||||
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, ",")
|
||||
}
|
||||
94
internal/inst/x86csv_test.go
Normal file
94
internal/inst/x86csv_test.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package inst
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
//go:generate curl --output testdata/x86.v0.2.csv https://raw.githubusercontent.com/golang/arch/master/x86/x86.v0.2.csv
|
||||
|
||||
const csvpath = "testdata/x86.v0.2.csv"
|
||||
|
||||
func LoadX86CSV(t *testing.T) []Instruction {
|
||||
t.Helper()
|
||||
f, err := os.Open(csvpath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
is, err := ReadFromX86CSV(f)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return is
|
||||
}
|
||||
|
||||
func TestExpandArg(t *testing.T) {
|
||||
cases := []struct {
|
||||
Arg string
|
||||
Types []string
|
||||
}{
|
||||
{"imm8", []string{"imm8"}},
|
||||
{"r/m32", []string{"r32", "m32"}},
|
||||
{"r16", []string{"r16"}},
|
||||
{"mm1", []string{"mm"}},
|
||||
{"xmm1", []string{"xmm"}},
|
||||
{"xmmV", []string{"xmm"}},
|
||||
{"xmm2/m128", []string{"xmm", "m128"}},
|
||||
{"xmm2/m64", []string{"xmm", "m64"}},
|
||||
{"ymm1", []string{"ymm"}},
|
||||
{"ymmV", []string{"ymm"}},
|
||||
{"ymm2/m256", []string{"ymm", "m256"}},
|
||||
}
|
||||
for _, c := range cases {
|
||||
types := expandArg(c.Arg)
|
||||
if !reflect.DeepEqual(c.Types, types) {
|
||||
t.Errorf("expanded %v to %s expected %s", c.Arg, types, c.Types)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestX86CSVOperandTypes(t *testing.T) {
|
||||
t.Skip("have not handled all cases yet")
|
||||
|
||||
is := LoadX86CSV(t)
|
||||
|
||||
types := map[string]bool{}
|
||||
for _, i := range is {
|
||||
for _, f := range i.Forms {
|
||||
for _, op := range f.Operands {
|
||||
types[op.Type] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for tipe := range types {
|
||||
if strings.Contains(tipe, "/") {
|
||||
t.Errorf("operand type %#v contains a slash (should be split)", tipe)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestCPUIDFlags helps catch any oddities in x86csv CPUID flags.
|
||||
func TestX86CSVCPUIDFlags(t *testing.T) {
|
||||
is := LoadX86CSV(t)
|
||||
|
||||
flags := map[string]bool{}
|
||||
for _, i := range is {
|
||||
for _, f := range i.Forms {
|
||||
for _, flag := range f.CPUID {
|
||||
flags[flag] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for flag := range flags {
|
||||
if strings.Contains(flag, " ") {
|
||||
t.Errorf("CPUID flag %#v contains whitespace", flag)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user