Files
avo/internal/gen/optab.go
2026-03-06 20:14:02 +00:00

300 lines
7.6 KiB
Go

package gen
import (
"fmt"
"sort"
"strconv"
"strings"
"sources.truenas.cloud/code/avo/internal/api"
"sources.truenas.cloud/code/avo/internal/inst"
"sources.truenas.cloud/code/avo/internal/prnt"
"sources.truenas.cloud/code/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, ", ") + "}"
}