diff --git a/internal/gen/loadertest.go b/internal/gen/loadertest.go index db3a74a..b5cb7e1 100644 --- a/internal/gen/loadertest.go +++ b/internal/gen/loadertest.go @@ -16,11 +16,16 @@ func (l LoaderTest) Generate(w io.Writer, is []*inst.Instruction) error { for _, i := range is { p.printf("\t// %s %s\n", i.Opcode, i.Summary) + if strings.HasPrefix(i.Opcode, "RET") { + p.printf("\t// SKIP: early RET instruction would cause assembler error") + continue + } + for _, f := range i.Forms { as := args(f.Operands) p.printf("\t// %#v\n", f.Operands) if as == nil { - p.printf("\t// skip\n") + p.printf("\t// SKIP:\n") continue } p.printf("\t%s\t%s\n", i.Opcode, strings.Join(as, ", ")) @@ -66,11 +71,10 @@ func arg(t string) string { // // // - // - "r64": "R8", + "r64": "R15", // // // - // + "xmm": "X7", // // // // diff --git a/internal/load/load.go b/internal/load/load.go index 098e18f..18e903a 100644 --- a/internal/load/load.go +++ b/internal/load/load.go @@ -1,6 +1,7 @@ package load import ( + "log" "path/filepath" "strconv" "strings" @@ -81,16 +82,24 @@ func (l *Loader) init() error { return err } + for a, op := range l.alias { + log.Printf("alias: %#v -> %s", a, op) + } + 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. + // Exclude certain ISAs simply not present in Go (AMD-only is a common reason). for _, isa := range f.ISA { switch isa.ID { - case "TBM", "CLZERO", "MONITORX", "FEMMS": + case "TBM", "CLZERO", "MONITORX", "FEMMS", "FMA4", "XOP", "SSE4A": + return false + } + // TODO(mbm): support AVX512 + if strings.HasPrefix(isa.ID, "AVX512") { return false } } @@ -113,27 +122,32 @@ func (l Loader) include(f opcodesxml.Form) bool { } func (l Loader) lookupAlias(f opcodesxml.Form) string { - a := opcodescsv.Alias{Opcode: f.GASName, DataSize: datasize(f)} + a := opcodescsv.Alias{ + Opcode: f.GASName, + DataSize: datasize(f), + NumOperands: len(f.Operands), + } 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 } + // Use go opcode from Opcodes XML where available. + if f.GoName != "" { + return f.GoName + } + + // Fallback to GAS name. 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"} + suffix := map[int]string{16: "W", 32: "L", 64: "Q", 128: "X", 256: "Y"} switch n { case "RDRAND", "RDSEED": n += suffix[s] @@ -150,9 +164,10 @@ func (l Loader) form(f opcodesxml.Form) inst.Form { // 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)) + n := len(ops) + r := make([]inst.Operand, len(ops)) + for i, op := range ops { + r[n-1-i] = operand(op) } return r } @@ -167,14 +182,29 @@ func operand(op opcodesxml.Operand) inst.Operand { // datasize (intelligently) guesses the datasize of an instruction form. func datasize(f opcodesxml.Form) int { + // Determine from encoding bits. + e := f.Encoding + switch { + case e.VEX != nil && e.VEX.W == nil: + return 128 << e.VEX.L + } + + // Guess from operand types. + size := 0 for _, op := range f.Operands { - if !op.Output { - continue + s := operandsize(op) + if s != 0 && (size == 0 || op.Output) { + size = s } - for s := 8; s <= 64; s *= 2 { - if strings.HasSuffix(op.Type, strconv.Itoa(s)) { - return s - } + } + + return size +} + +func operandsize(op opcodesxml.Operand) int { + for s := 8; s <= 256; s *= 2 { + if strings.HasSuffix(op.Type, strconv.Itoa(s)) { + return s } } return 0 diff --git a/internal/opcodescsv/analysis.go b/internal/opcodescsv/analysis.go index c813a46..7f696aa 100644 --- a/internal/opcodescsv/analysis.go +++ b/internal/opcodescsv/analysis.go @@ -8,8 +8,9 @@ import ( ) type Alias struct { - Opcode string - DataSize int + Opcode string + DataSize int + NumOperands int } // BuildAliasMap constructs a map from AT&T/GNU/Intel to Go syntax. @@ -21,9 +22,18 @@ func BuildAliasMap(is []*x86csv.Inst) (map[Alias]string, error) { return nil, err } + if strings.Contains(i.GoOpcode(), "/") { + continue + } + for _, alt := range []string{i.IntelOpcode(), i.GNUOpcode()} { if strings.ToUpper(alt) != i.GoOpcode() { - m[Alias{Opcode: strings.ToLower(alt), DataSize: s}] = i.GoOpcode() + a := Alias{ + Opcode: strings.ToLower(alt), + DataSize: s, + NumOperands: len(i.GoArgs()), + } + m[a] = i.GoOpcode() } } } diff --git a/internal/opcodesxml/opcodesxml.go b/internal/opcodesxml/opcodesxml.go index 273b4e6..3865005 100644 --- a/internal/opcodesxml/opcodesxml.go +++ b/internal/opcodesxml/opcodesxml.go @@ -72,7 +72,7 @@ type Encoding struct { type REX struct { Mandatory bool `xml:"mandatory,attr"` - W int `xml:"W,attr"` + W uint `xml:"W,attr"` R string `xml:"R,attr"` X string `xml:"X,attr"` B string `xml:"B,attr"` @@ -80,8 +80,8 @@ type REX struct { type VEX struct { Type string `xml:"type,attr"` - W int `xml:"W,attr"` - L int `xml:"L,attr"` + W *uint `xml:"W,attr"` + L uint `xml:"L,attr"` M5 string `xml:"m-mmmm,attr"` PP string `xml:"pp,attr"` R string `xml:"R,attr"` @@ -93,7 +93,7 @@ type VEX struct { type EVEX struct { M2 string `xml:"mm,attr"` PP string `xml:"pp,attr"` - W int `xml:"W,attr"` + W *uint `xml:"W,attr"` LL string `xml:"LL,attr"` V4 string `xml:"vvvv,attr"` V string `xml:"V,attr"`