This commit is contained in:
Michael McLoughlin
2018-11-21 13:02:18 -06:00
parent cb259ce43b
commit 59e6af7d36
10 changed files with 553 additions and 2 deletions

20
internal/load/ifind.sh Executable file
View File

@@ -0,0 +1,20 @@
#!/bin/bash
inst=$1
eval $(go env)
section() {
echo '------------------------------------------'
echo $1
echo '------------------------------------------'
}
section 'stdlib cmd/'
grep -iR ${inst} ${GOROOT}/src/cmd/{asm,internal/obj/x86}
section 'x/arch/x86 repo'
grep -iR ${inst} ${GOPATH}/src/golang.org/x/arch/x86/
section '*_amd64.s files in stdlib'
find ${GOROOT}/src -name '*_amd64.s' | xargs grep -i ${inst}

181
internal/load/load.go Normal file
View File

@@ -0,0 +1,181 @@
package load
import (
"path/filepath"
"strconv"
"strings"
"github.com/mmcloughlin/avo/internal/inst"
"github.com/mmcloughlin/avo/internal/opcodescsv"
"github.com/mmcloughlin/avo/internal/opcodesxml"
)
const (
defaultCSVName = "x86.v0.2.csv"
defaultOpcodesXMLName = "x86_64.xml"
)
type Loader struct {
X86CSVPath string
OpcodesXMLPath string
alias map[opcodescsv.Alias]string
}
func NewLoaderFromDataDir(dir string) *Loader {
return &Loader{
X86CSVPath: filepath.Join(dir, defaultCSVName),
OpcodesXMLPath: filepath.Join(dir, defaultOpcodesXMLName),
}
}
func (l *Loader) Load() ([]*inst.Instruction, error) {
if err := l.init(); err != nil {
return nil, err
}
// Load Opcodes XML file.
iset, err := opcodesxml.ReadFile(l.OpcodesXMLPath)
if err != nil {
return nil, err
}
// Load opcodes XML data, grouped by Go opcode.
im := map[string]*inst.Instruction{}
for _, i := range iset.Instructions {
for _, f := range i.Forms {
if !l.include(f) {
continue
}
opcode := l.goname(f)
if im[opcode] == nil {
im[opcode] = &inst.Instruction{
Opcode: opcode,
Summary: i.Summary,
}
}
im[opcode].Forms = append(im[opcode].Forms, l.form(f))
}
}
// Convert to a slice to return.
is := make([]*inst.Instruction, 0, len(im))
for _, i := range im {
is = append(is, i)
}
return is, nil
}
func (l *Loader) init() error {
icsv, err := opcodescsv.ReadFile(l.X86CSVPath)
if err != nil {
return err
}
l.alias, err = opcodescsv.BuildAliasMap(icsv)
if err != nil {
return err
}
return nil
}
// include decides whether to include the instruction form in the avo listing.
// This discards some opcodes that are not supported in Go.
func (l Loader) include(f opcodesxml.Form) bool {
// Exclude certain ISAs simply not present in Go.
for _, isa := range f.ISA {
switch isa.ID {
case "TBM", "CLZERO", "MONITORX", "FEMMS":
return false
}
}
// x86 csv contains a number of CMOV* instructions which are actually not valid
// Go instructions. The valid Go forms should have different opcodes from GNU.
// Therefore a decent "heuristic" is CMOV* instructions that do not have
// aliases.
if strings.HasPrefix(f.GASName, "cmov") && l.lookupAlias(f) == "" {
return false
}
// Some specific exclusions.
switch f.GASName {
case "callq":
return false
}
return true
}
func (l Loader) lookupAlias(f opcodesxml.Form) string {
a := opcodescsv.Alias{Opcode: f.GASName, DataSize: datasize(f)}
return l.alias[a]
}
func (l Loader) goname(f opcodesxml.Form) string {
// Use go opcode from Opcodes XML where available.
if f.GoName != "" {
return f.GoName
}
// Return alias if available.
if a := l.lookupAlias(f); a != "" {
return a
}
n := strings.ToUpper(f.GASName)
// Some need data sizes added to them.
// TODO(mbm): is there a better way of determining which ones these are?
s := datasize(f)
suffix := map[int]string{16: "W", 32: "L", 64: "Q"}
switch n {
case "RDRAND", "RDSEED":
n += suffix[s]
}
return n
}
func (l Loader) form(f opcodesxml.Form) inst.Form {
return inst.Form{
Operands: operands(f.Operands),
}
}
// operands maps Opcodes XML operands to avo format.
func operands(ops []opcodesxml.Operand) []inst.Operand {
r := make([]inst.Operand, 0, len(ops))
for _, op := range ops {
r = append(r, operand(op))
}
return r
}
// operand maps an Opcodes XML operand to avo format.
func operand(op opcodesxml.Operand) inst.Operand {
return inst.Operand{
Type: op.Type,
Action: inst.ActionFromReadWrite(op.Input, op.Output),
}
}
// datasize (intelligently) guesses the datasize of an instruction form.
func datasize(f opcodesxml.Form) int {
for _, op := range f.Operands {
if !op.Output {
continue
}
for s := 8; s <= 64; s *= 2 {
if strings.HasSuffix(op.Type, strconv.Itoa(s)) {
return s
}
}
}
return 0
}

View File

@@ -0,0 +1,31 @@
package load_test
import (
"bytes"
"testing"
"github.com/mmcloughlin/avo/internal/gen"
"github.com/mmcloughlin/avo/internal/inst"
"github.com/mmcloughlin/avo/internal/load"
"github.com/mmcloughlin/avo/internal/test"
)
func Load(t *testing.T) []*inst.Instruction {
t.Helper()
l := load.NewLoaderFromDataDir("testdata")
is, err := l.Load()
if err != nil {
t.Fatal(err)
}
return is
}
func TestAssembles(t *testing.T) {
is := Load(t)
g := &gen.LoaderTest{}
var buf bytes.Buffer
g.Generate(&buf, is)
test.Assembles(t, buf.Bytes())
}

1
internal/load/testdata Symbolic link
View File

@@ -0,0 +1 @@
../data