refactors to code generation

This commit is contained in:
Michael McLoughlin
2018-11-24 13:00:27 -08:00
parent 4571841ee5
commit f1e1da6387
4 changed files with 177 additions and 37 deletions

View File

@@ -0,0 +1,64 @@
package main
import (
"flag"
"go/build"
"log"
"os"
"path/filepath"
"github.com/mmcloughlin/avo/internal/gen"
"github.com/mmcloughlin/avo/internal/load"
)
var generators = map[string]gen.Interface{
"asmtest": gen.NewAsmTest(),
"godata": gen.NewGoData(),
}
var datadir = flag.String(
"data",
filepath.Join(build.Default.GOPATH, "src/github.com/mmcloughlin/avo/internal/data"),
"path to data directory",
)
var output = flag.String("output", "", "path to output file (default stdout)")
func main() {
flag.Parse()
// Build generator.
t := flag.Arg(0)
g := generators[t]
if g == nil {
log.Fatalf("unknown generator type '%s'", t)
}
// Determine output writer.
w := os.Stdout
if *output != "" {
f, err := os.Open(*output)
if err != nil {
log.Fatal(err)
}
w = f
}
// Load instructions.
l := load.NewLoaderFromDataDir(*datadir)
is, err := l.Load()
if err != nil {
log.Fatal(err)
}
// Generate output.
b, err := g.Generate(is)
if err != nil {
log.Fatal(err)
}
// Write.
if _, err := w.Write(b); err != nil {
log.Fatal(err)
}
}

View File

@@ -2,7 +2,6 @@ package gen
import (
"fmt"
"io"
"math"
"strconv"
"strings"
@@ -10,61 +9,65 @@ import (
"github.com/mmcloughlin/avo/internal/inst"
)
type LoaderTest struct {
type asmtest struct {
sym string // reference to the test function symbol
rel8 string // label to be used for near jumps
rel32 string // label for far jumps
}
func (l *LoaderTest) Generate(w io.Writer, is []*inst.Instruction) error {
p := &printer{w: w}
func NewAsmTest() Interface {
return &asmtest{}
}
l.sym = "\u00b7loadertest(SB)"
p.printf("TEXT %s, 0, $0\n", l.sym)
func (a *asmtest) Generate(is []*inst.Instruction) ([]byte, error) {
p := &printer{}
a.sym = "\u00b7loadertest(SB)"
p.Printf("TEXT %s, 0, $0\n", a.sym)
// Define a label for far jumps.
p.printf("rel32:\n")
l.rel32 = "rel32"
p.Printf("rel32:\n")
a.rel32 = "rel32"
counts := map[string]int{}
for _, i := range is {
p.printf("\t// %s %s\n", i.Opcode, i.Summary)
if skip, msg := l.skip(i.Opcode); skip {
p.printf("\t// SKIP: %s\n", msg)
p.Printf("\t// %s %s\n", i.Opcode, i.Summary)
if skip, msg := a.skip(i.Opcode); skip {
p.Printf("\t// SKIP: %s\n", msg)
counts["skip"]++
continue
}
if i.Opcode[0] == 'J' {
label := fmt.Sprintf("rel8_%s", strings.ToLower(i.Opcode))
p.printf("%s:\n", label)
l.rel8 = label
p.Printf("%s:\n", label)
a.rel8 = label
}
for _, f := range i.Forms {
as := l.args(i.Opcode, f.Operands)
as := a.args(i.Opcode, f.Operands)
if as == nil {
p.printf("\t// TODO: %s %#v\n", i.Opcode, f.Operands)
p.Printf("\t// TODO: %s %#v\n", i.Opcode, f.Operands)
counts["todo"]++
continue
}
p.printf("\t%s\t%s\n", i.Opcode, strings.Join(as, ", "))
p.Printf("\t%s\t%s\n", i.Opcode, strings.Join(as, ", "))
counts["total"]++
}
p.printf("\n")
p.Printf("\n")
}
p.printf("\tRET\n")
p.Printf("\tRET\n")
for m, c := range counts {
p.printf("// %s: %d\n", m, c)
p.Printf("// %s: %d\n", m, c)
}
return p.Err()
return p.Result()
}
func (l LoaderTest) skip(opcode string) (bool, string) {
func (a asmtest) skip(opcode string) (bool, string) {
prefixes := map[string]string{
"PUSH": "PUSH can produce 'unbalanced PUSH/POP' assembler error",
"POP": "POP can produce 'unbalanced PUSH/POP' assembler error",
@@ -77,15 +80,15 @@ func (l LoaderTest) skip(opcode string) (bool, string) {
return false, ""
}
func (l LoaderTest) args(opcode string, ops []inst.Operand) []string {
func (a asmtest) args(opcode string, ops []inst.Operand) []string {
// Special case for CALL, since it needs a different type of rel32 argument than others.
if opcode == "CALL" {
return []string{l.sym}
return []string{a.sym}
}
as := make([]string, len(ops))
for i, op := range ops {
a := l.arg(op.Type, i)
a := a.arg(op.Type, i)
if a == "" {
return nil
}
@@ -95,7 +98,7 @@ func (l LoaderTest) args(opcode string, ops []inst.Operand) []string {
}
// arg generates an argument for an operand of the given type.
func (l LoaderTest) arg(t string, i int) string {
func (a asmtest) arg(t string, i int) string {
m := map[string]string{
"1": "$1", // <xs:enumeration value="1" />
"3": "$3", // <xs:enumeration value="3" />
@@ -165,8 +168,8 @@ func (l LoaderTest) arg(t string, i int) string {
// <xs:enumeration value="vm32z{k}" />
// <xs:enumeration value="vm64z" />
// <xs:enumeration value="vm64z{k}" />
"rel8": l.rel8, // <xs:enumeration value="rel8" />
"rel32": l.rel32, // <xs:enumeration value="rel32" />
"rel8": a.rel8, // <xs:enumeration value="rel8" />
"rel32": a.rel32, // <xs:enumeration value="rel32" />
// <xs:enumeration value="{er}" />
// <xs:enumeration value="{sae}" />

View File

@@ -1,34 +1,46 @@
package gen
import (
"bytes"
"fmt"
"io"
"go/format"
"github.com/mmcloughlin/avo/internal/inst"
)
type Interface interface {
Generate(io.Writer, []*inst.Instruction) error
Generate([]*inst.Instruction) ([]byte, error)
}
type Func func(io.Writer, []*inst.Instruction) error
type Func func([]*inst.Instruction) ([]byte, error)
func (f Func) Generate(w io.Writer, is []*inst.Instruction) error {
return f(w, is)
func (f Func) Generate(is []*inst.Instruction) ([]byte, error) {
return f(is)
}
// GoFmt formats Go code produced from the given generator.
func GoFmt(i Interface) Interface {
return Func(func(is []*inst.Instruction) ([]byte, error) {
b, err := i.Generate(is)
if err != nil {
return nil, err
}
return format.Source(b)
})
}
type printer struct {
w io.Writer
buf bytes.Buffer
err error
}
func (p *printer) printf(format string, args ...interface{}) {
func (p *printer) Printf(format string, args ...interface{}) {
if p.err != nil {
return
}
_, p.err = fmt.Fprintf(p.w, format, args...)
_, p.err = fmt.Fprintf(&p.buf, format, args...)
}
func (p *printer) Err() error {
return p.err
func (p *printer) Result() ([]byte, error) {
return p.buf.Bytes(), p.err
}

61
internal/gen/godata.go Normal file
View File

@@ -0,0 +1,61 @@
package gen
import (
"github.com/mmcloughlin/avo/internal/inst"
)
type godata struct {
}
func NewGoData() Interface {
return GoFmt(godata{})
}
func (g godata) Generate(is []*inst.Instruction) ([]byte, error) {
p := &printer{}
p.Printf("package inst\n\n")
p.Printf("var Instructions = []Instruction{\n")
for _, i := range is {
p.Printf("{\n")
p.Printf("Opcode: %#v,\n", i.Opcode)
p.Printf("Summary: %#v,\n", i.Summary)
p.Printf("Forms: []Form{\n")
for _, f := range i.Forms {
p.Printf("{\n")
if f.ISA != nil {
p.Printf("ISA: %#v,\n", f.ISA)
}
if f.Operands != nil {
p.Printf("Operands: []Operand{\n")
for _, op := range f.Operands {
p.Printf("{Type: %#v, Action: %#v},\n", op.Type, op.Action)
}
p.Printf("},\n")
}
if f.ImplicitOperands != nil {
p.Printf("ImplicitOperands: []ImplicitOperand{\n")
for _, op := range f.ImplicitOperands {
p.Printf("{Register: %#v, Action: %#v},\n", op.Register, op.Action)
}
p.Printf("},\n")
}
p.Printf("},\n")
}
p.Printf("},\n")
p.Printf("},\n")
}
p.Printf("}\n")
return p.Result()
}