wip
This commit is contained in:
14
ast.go
14
ast.go
@@ -1,5 +1,9 @@
|
|||||||
package avo
|
package avo
|
||||||
|
|
||||||
|
type Asm interface {
|
||||||
|
Asm() string
|
||||||
|
}
|
||||||
|
|
||||||
// GoType represents a Golang type.
|
// GoType represents a Golang type.
|
||||||
type GoType interface{}
|
type GoType interface{}
|
||||||
|
|
||||||
@@ -9,10 +13,18 @@ type Parameter struct {
|
|||||||
Type GoType
|
Type GoType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Operand interface {
|
||||||
|
Asm
|
||||||
|
}
|
||||||
|
|
||||||
// Instruction is a single instruction in a function.
|
// Instruction is a single instruction in a function.
|
||||||
type Instruction struct {
|
type Instruction struct {
|
||||||
Mnemonic string
|
Mnemonic string
|
||||||
Operands []string
|
Operands []Operand
|
||||||
|
}
|
||||||
|
|
||||||
|
type Node interface {
|
||||||
|
Asm
|
||||||
}
|
}
|
||||||
|
|
||||||
// File represents an assembly file.
|
// File represents an assembly file.
|
||||||
|
|||||||
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
printer.go
10
printer.go
@@ -70,7 +70,7 @@ func (p *GoPrinter) function(f *Function) {
|
|||||||
p.printf("TEXT %s%s(SB),0,$%d-%d\n", dot, f.Name(), f.FrameBytes(), f.ArgumentBytes())
|
p.printf("TEXT %s%s(SB),0,$%d-%d\n", dot, f.Name(), f.FrameBytes(), f.ArgumentBytes())
|
||||||
|
|
||||||
for _, i := range f.inst {
|
for _, i := range f.inst {
|
||||||
p.printf("\t%s\t%s\n", i.Mnemonic, strings.Join(i.Operands, ", "))
|
p.printf("\t%s\t%s\n", i.Mnemonic, joinOperands(i.Operands))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,3 +83,11 @@ func (p *GoPrinter) printf(format string, args ...interface{}) {
|
|||||||
p.err = err
|
p.err = err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func joinOperands(operands []Operand) string {
|
||||||
|
asm := make([]string, len(operands))
|
||||||
|
for i, op := range operands {
|
||||||
|
asm[i] = op.Asm()
|
||||||
|
}
|
||||||
|
return strings.Join(asm, ", ")
|
||||||
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ type Register interface {
|
|||||||
Kind() Kind
|
Kind() Kind
|
||||||
Mask() uint16
|
Mask() uint16
|
||||||
Bytes() uint
|
Bytes() uint
|
||||||
Name() string
|
Asm() string
|
||||||
private
|
private
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ type register struct {
|
|||||||
|
|
||||||
func (r register) PhysicalID() uint16 { return r.id }
|
func (r register) PhysicalID() uint16 { return r.id }
|
||||||
func (r register) Kind() Kind { return r.kind }
|
func (r register) Kind() Kind { return r.kind }
|
||||||
func (r register) Name() string { return r.name }
|
func (r register) Asm() string { return r.name }
|
||||||
func (r register) private() {}
|
func (r register) private() {}
|
||||||
|
|
||||||
type Spec uint16
|
type Spec uint16
|
||||||
|
|||||||
Reference in New Issue
Block a user