Upgrade to Go 1.23. Upgrade also requires bumping `golangci-lint` to v1.62.2, and upgrading third-party test versions for some failing cases.
300 lines
7.6 KiB
Go
300 lines
7.6 KiB
Go
package gen
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/mmcloughlin/avo/internal/api"
|
|
"github.com/mmcloughlin/avo/internal/inst"
|
|
"github.com/mmcloughlin/avo/internal/prnt"
|
|
"github.com/mmcloughlin/avo/printer"
|
|
)
|
|
|
|
type optab struct {
|
|
prnt.Generator
|
|
|
|
cfg printer.Config
|
|
|
|
table *Table
|
|
}
|
|
|
|
// NewOptab builds the operator table. This contains a more compact
|
|
// representation of the instruction database, containing the data needed for
|
|
// instruction builders to match against provided operands, and build the
|
|
// selected instruction.
|
|
func NewOptab(cfg printer.Config) Interface {
|
|
return GoFmt(&optab{cfg: cfg})
|
|
}
|
|
|
|
func (t *optab) Generate(is []inst.Instruction) ([]byte, error) {
|
|
t.Printf("// %s\n\n", t.cfg.GeneratedWarning())
|
|
t.Printf("package x86\n\n")
|
|
t.Printf("import (\n")
|
|
t.Printf("\t%q\n", api.ImportPath(api.OperandPackage))
|
|
t.Printf("\t%q\n", api.ImportPath(api.RegisterPackage))
|
|
t.Printf(")\n\n")
|
|
|
|
// Generate instruction data table.
|
|
t.table = NewTable(is)
|
|
|
|
// Size constants.
|
|
t.maxOperands(is)
|
|
|
|
// Types.
|
|
t.operandTypeEnum(is)
|
|
t.implicitRegisterEnum(is)
|
|
t.enum(t.table.Suffix())
|
|
t.suffixesType(is)
|
|
t.suffixesClassEnum(is)
|
|
t.isasEnum(is)
|
|
t.opcodeEnum(is)
|
|
|
|
// Forms table.
|
|
t.forms(is)
|
|
|
|
return t.Result()
|
|
}
|
|
|
|
func (t *optab) maxOperands(is []inst.Instruction) {
|
|
mx := 0
|
|
for _, i := range inst.Instructions {
|
|
for _, f := range i.Forms {
|
|
a := len(f.Operands) + len(f.ImplicitOperands)
|
|
if a > mx {
|
|
mx = a
|
|
}
|
|
}
|
|
}
|
|
|
|
t.Comment("maxoperands is the maximum number of operands in an instruction form, including implicit operands.")
|
|
t.Printf("const maxoperands = %d\n\n", mx)
|
|
}
|
|
|
|
func (t *optab) operandTypeEnum(is []inst.Instruction) {
|
|
// Operand type enum.
|
|
e := t.table.OperandType()
|
|
t.enum(e)
|
|
|
|
// Operand match function.
|
|
types := inst.OperandTypes(is)
|
|
t.Printf("func (%s %s) Match(op %s) bool {\n", e.Receiver(), e.Name(), api.OperandType)
|
|
t.Printf("\tswitch %s {\n", e.Receiver())
|
|
t.Printf("\t\tdefault: return false\n")
|
|
for _, typ := range types {
|
|
t.Printf("\t\tcase %s: return %s(op)\n", t.table.OperandTypeConst(typ), api.CheckerName(typ))
|
|
}
|
|
t.Printf("\t}\n")
|
|
t.Printf("}\n\n")
|
|
}
|
|
|
|
func (t *optab) implicitRegisterEnum(is []inst.Instruction) {
|
|
// Implicit register enum.
|
|
e := t.table.ImplicitRegister()
|
|
t.enum(e)
|
|
|
|
// Register conversion function.
|
|
registers := inst.ImplicitRegisters(is)
|
|
t.Printf("func (%s %s) Register() %s {\n", e.Receiver(), e.Name(), api.RegisterType)
|
|
t.Printf("\tswitch %s {\n", e.Receiver())
|
|
t.Printf("\t\tdefault: panic(\"unexpected implicit register type\")\n")
|
|
for _, r := range registers {
|
|
t.Printf("\t\tcase %s: return %s\n", t.table.ImplicitRegisterConst(r), api.ImplicitRegister(r))
|
|
}
|
|
t.Printf("\t}\n")
|
|
t.Printf("}\n\n")
|
|
}
|
|
|
|
func (t *optab) suffixesType(is []inst.Instruction) {
|
|
// Declare the type as an array. This requires us to know the maximum number
|
|
// of suffixes an instruction can have.
|
|
mx := 0
|
|
for _, class := range inst.SuffixesClasses(is) {
|
|
for _, suffixes := range class {
|
|
if len(suffixes) > mx {
|
|
mx = len(suffixes)
|
|
}
|
|
}
|
|
}
|
|
|
|
t.Comment("maxsuffixes is the maximum number of suffixes an instruction can have.")
|
|
t.Printf("const maxsuffixes = %d\n\n", mx)
|
|
|
|
name := t.table.SuffixesTypeName()
|
|
t.Printf("type %s [maxsuffixes]%s\n", name, t.table.Suffix().Name())
|
|
|
|
// Conversion function to list of strings.
|
|
mapname := name + "stringsmap"
|
|
|
|
t.Printf("func (s %s) Strings() []string {\n", name)
|
|
t.Printf("return %s[s]", mapname)
|
|
t.Printf("}\n")
|
|
|
|
var entries []string
|
|
for _, class := range inst.SuffixesClasses(is) {
|
|
for _, suffixes := range class {
|
|
entry := fmt.Sprintf("%s: %s", t.table.SuffixesList(suffixes), stringsliteral(suffixes.Strings()))
|
|
entries = append(entries, entry)
|
|
}
|
|
}
|
|
|
|
t.Printf("var %s = map[%s][]string{\n", mapname, name)
|
|
sort.Strings(entries)
|
|
for _, entry := range entries {
|
|
t.Printf("%s,\n", entry)
|
|
}
|
|
t.Printf("}\n")
|
|
}
|
|
|
|
func (t *optab) suffixesClassEnum(is []inst.Instruction) {
|
|
// Suffixes class enum.
|
|
e := t.table.SuffixesClass()
|
|
t.enum(e)
|
|
|
|
// Mapping method to the set of accepted suffixes.
|
|
sets := map[string]string{}
|
|
for key, class := range inst.SuffixesClasses(is) {
|
|
var entries []string
|
|
for _, suffixes := range class {
|
|
entry := fmt.Sprintf("%s: true", t.table.SuffixesConst(suffixes))
|
|
entries = append(entries, entry)
|
|
}
|
|
|
|
sort.Strings(entries)
|
|
sets[api.SuffixesClassIdentifier(key)] = "{" + strings.Join(entries, ", ") + "}"
|
|
}
|
|
|
|
settype := fmt.Sprintf("map[%s]bool", t.table.SuffixesTypeName())
|
|
t.mapping(e, "SuffixesSet", settype, "nil", sets)
|
|
}
|
|
|
|
func (t *optab) isasEnum(is []inst.Instruction) {
|
|
// ISAs enum.
|
|
e := t.table.ISAs()
|
|
t.enum(e)
|
|
|
|
// Mapping method to produce the list of ISAs.
|
|
lists := map[string]string{}
|
|
for _, isas := range inst.ISACombinations(is) {
|
|
lists[api.ISAsIdentifier(isas)] = stringsliteral(isas)
|
|
}
|
|
t.mapping(e, "List", "[]string", "nil", lists)
|
|
}
|
|
|
|
func (t *optab) opcodeEnum(is []inst.Instruction) {
|
|
e := t.table.Opcode()
|
|
t.enum(e)
|
|
t.stringmethod(e)
|
|
}
|
|
|
|
func (t *optab) forms(is []inst.Instruction) {
|
|
// We require all instructions for a given opcode to be in a contiguous
|
|
// block. This is likely true already but we'll make a sorted copy to ensure
|
|
// the optab is robust to changes elsewhere.
|
|
is = append([]inst.Instruction(nil), is...)
|
|
sort.Slice(is, func(i, j int) bool {
|
|
return is[i].Opcode < is[j].Opcode
|
|
})
|
|
|
|
// Output instruction forms table.
|
|
table := "forms"
|
|
t.Printf("var %s = []form{\n", table)
|
|
for _, i := range is {
|
|
for _, f := range i.Forms {
|
|
t.Printf("{")
|
|
|
|
// Basic properties.
|
|
t.Printf("%s, ", t.table.OpcodeConst(i.Opcode))
|
|
t.Printf("%s, ", t.table.SuffixesClassConst(f.SuffixesClass()))
|
|
t.Printf("%s, ", Features(i, f))
|
|
t.Printf("%s, ", t.table.ISAsConst(f.ISA))
|
|
|
|
// Operands.
|
|
t.Printf("%d, ", len(f.Operands))
|
|
t.Printf("oprnds{")
|
|
for _, op := range f.Operands {
|
|
t.Printf(
|
|
"{uint8(%s),false,%s},",
|
|
t.table.OperandTypeConst(op.Type),
|
|
Action(op.Action),
|
|
)
|
|
}
|
|
for _, op := range f.ImplicitOperands {
|
|
t.Printf(
|
|
"{uint8(%s),true,%s},",
|
|
t.table.ImplicitRegisterConst(op.Register),
|
|
Action(op.Action),
|
|
)
|
|
}
|
|
t.Printf("}")
|
|
|
|
t.Printf("},\n")
|
|
}
|
|
}
|
|
t.Printf("}\n\n")
|
|
|
|
// Build mapping from opcode to corresponding forms.
|
|
forms := map[string]string{}
|
|
n := 0
|
|
for _, i := range is {
|
|
e := n + len(i.Forms)
|
|
forms[i.Opcode] = fmt.Sprintf("%s[%d:%d]", table, n, e)
|
|
n = e
|
|
}
|
|
|
|
t.mapping(t.table.Opcode(), "Forms", "[]form", "nil", forms)
|
|
}
|
|
|
|
func (t *optab) enum(e *Enum) {
|
|
// Type declaration.
|
|
t.Comment(e.Doc()...)
|
|
t.Printf("type %s %s\n\n", e.Name(), e.UnderlyingType())
|
|
|
|
// Supported values.
|
|
t.Printf("const (\n")
|
|
t.Printf("\t%s %s = iota\n", e.None(), e.name)
|
|
for _, name := range e.ConstNames() {
|
|
t.Printf("\t%s\n", name)
|
|
}
|
|
t.Printf("\t%s\n", e.MaxName())
|
|
t.Printf(")\n\n")
|
|
}
|
|
|
|
func (t *optab) mapping(e *Enum, name, ret, zero string, to map[string]string) {
|
|
table := strings.ToLower(e.Name() + name + "table")
|
|
|
|
r := e.Receiver()
|
|
t.Printf("func (%s %s) %s() %s {\n", r, e.Name(), name, ret)
|
|
t.Printf("if %s < %s && %s < %s {\n", e.None(), r, r, e.MaxName())
|
|
t.Printf("return %s[%s-1]\n", table, r)
|
|
t.Printf("}\n")
|
|
t.Printf("return %s\n", zero)
|
|
t.Printf("}\n\n")
|
|
|
|
t.Printf("var %s = []%s{\n", table, ret)
|
|
for _, value := range e.Values() {
|
|
t.Printf("\t%s,\n", to[value])
|
|
}
|
|
t.Printf("}\n\n")
|
|
}
|
|
|
|
func (t *optab) stringmethod(e *Enum) {
|
|
s := map[string]string{}
|
|
for _, value := range e.Values() {
|
|
s[value] = strconv.Quote(value)
|
|
}
|
|
t.mapping(e, "String", "string", `""`, s)
|
|
}
|
|
|
|
func stringsliteral(ss []string) string {
|
|
if ss == nil {
|
|
return "nil"
|
|
}
|
|
var quoted []string
|
|
for _, s := range ss {
|
|
quoted = append(quoted, strconv.Quote(s))
|
|
}
|
|
return "{" + strings.Join(quoted, ", ") + "}"
|
|
}
|