refactors to code generation
This commit is contained in:
64
internal/cmd/avogen/main.go
Normal file
64
internal/cmd/avogen/main.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@ package gen
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -10,61 +9,65 @@ import (
|
|||||||
"github.com/mmcloughlin/avo/internal/inst"
|
"github.com/mmcloughlin/avo/internal/inst"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LoaderTest struct {
|
type asmtest struct {
|
||||||
sym string // reference to the test function symbol
|
sym string // reference to the test function symbol
|
||||||
rel8 string // label to be used for near jumps
|
rel8 string // label to be used for near jumps
|
||||||
rel32 string // label for far jumps
|
rel32 string // label for far jumps
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LoaderTest) Generate(w io.Writer, is []*inst.Instruction) error {
|
func NewAsmTest() Interface {
|
||||||
p := &printer{w: w}
|
return &asmtest{}
|
||||||
|
}
|
||||||
|
|
||||||
l.sym = "\u00b7loadertest(SB)"
|
func (a *asmtest) Generate(is []*inst.Instruction) ([]byte, error) {
|
||||||
p.printf("TEXT %s, 0, $0\n", l.sym)
|
p := &printer{}
|
||||||
|
|
||||||
|
a.sym = "\u00b7loadertest(SB)"
|
||||||
|
p.Printf("TEXT %s, 0, $0\n", a.sym)
|
||||||
|
|
||||||
// Define a label for far jumps.
|
// Define a label for far jumps.
|
||||||
p.printf("rel32:\n")
|
p.Printf("rel32:\n")
|
||||||
l.rel32 = "rel32"
|
a.rel32 = "rel32"
|
||||||
|
|
||||||
counts := map[string]int{}
|
counts := map[string]int{}
|
||||||
|
|
||||||
for _, i := range is {
|
for _, i := range is {
|
||||||
p.printf("\t// %s %s\n", i.Opcode, i.Summary)
|
p.Printf("\t// %s %s\n", i.Opcode, i.Summary)
|
||||||
if skip, msg := l.skip(i.Opcode); skip {
|
if skip, msg := a.skip(i.Opcode); skip {
|
||||||
p.printf("\t// SKIP: %s\n", msg)
|
p.Printf("\t// SKIP: %s\n", msg)
|
||||||
counts["skip"]++
|
counts["skip"]++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if i.Opcode[0] == 'J' {
|
if i.Opcode[0] == 'J' {
|
||||||
label := fmt.Sprintf("rel8_%s", strings.ToLower(i.Opcode))
|
label := fmt.Sprintf("rel8_%s", strings.ToLower(i.Opcode))
|
||||||
p.printf("%s:\n", label)
|
p.Printf("%s:\n", label)
|
||||||
l.rel8 = label
|
a.rel8 = label
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range i.Forms {
|
for _, f := range i.Forms {
|
||||||
as := l.args(i.Opcode, f.Operands)
|
as := a.args(i.Opcode, f.Operands)
|
||||||
if as == nil {
|
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"]++
|
counts["todo"]++
|
||||||
continue
|
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"]++
|
counts["total"]++
|
||||||
}
|
}
|
||||||
p.printf("\n")
|
p.Printf("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
p.printf("\tRET\n")
|
p.Printf("\tRET\n")
|
||||||
|
|
||||||
for m, c := range counts {
|
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{
|
prefixes := map[string]string{
|
||||||
"PUSH": "PUSH can produce 'unbalanced PUSH/POP' assembler error",
|
"PUSH": "PUSH can produce 'unbalanced PUSH/POP' assembler error",
|
||||||
"POP": "POP 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, ""
|
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.
|
// Special case for CALL, since it needs a different type of rel32 argument than others.
|
||||||
if opcode == "CALL" {
|
if opcode == "CALL" {
|
||||||
return []string{l.sym}
|
return []string{a.sym}
|
||||||
}
|
}
|
||||||
|
|
||||||
as := make([]string, len(ops))
|
as := make([]string, len(ops))
|
||||||
for i, op := range ops {
|
for i, op := range ops {
|
||||||
a := l.arg(op.Type, i)
|
a := a.arg(op.Type, i)
|
||||||
if a == "" {
|
if a == "" {
|
||||||
return nil
|
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.
|
// 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{
|
m := map[string]string{
|
||||||
"1": "$1", // <xs:enumeration value="1" />
|
"1": "$1", // <xs:enumeration value="1" />
|
||||||
"3": "$3", // <xs:enumeration value="3" />
|
"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="vm32z{k}" />
|
||||||
// <xs:enumeration value="vm64z" />
|
// <xs:enumeration value="vm64z" />
|
||||||
// <xs:enumeration value="vm64z{k}" />
|
// <xs:enumeration value="vm64z{k}" />
|
||||||
"rel8": l.rel8, // <xs:enumeration value="rel8" />
|
"rel8": a.rel8, // <xs:enumeration value="rel8" />
|
||||||
"rel32": l.rel32, // <xs:enumeration value="rel32" />
|
"rel32": a.rel32, // <xs:enumeration value="rel32" />
|
||||||
// <xs:enumeration value="{er}" />
|
// <xs:enumeration value="{er}" />
|
||||||
// <xs:enumeration value="{sae}" />
|
// <xs:enumeration value="{sae}" />
|
||||||
|
|
||||||
@@ -1,34 +1,46 @@
|
|||||||
package gen
|
package gen
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"go/format"
|
||||||
|
|
||||||
"github.com/mmcloughlin/avo/internal/inst"
|
"github.com/mmcloughlin/avo/internal/inst"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Interface interface {
|
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 {
|
func (f Func) Generate(is []*inst.Instruction) ([]byte, error) {
|
||||||
return f(w, is)
|
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 {
|
type printer struct {
|
||||||
w io.Writer
|
buf bytes.Buffer
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *printer) printf(format string, args ...interface{}) {
|
func (p *printer) Printf(format string, args ...interface{}) {
|
||||||
if p.err != nil {
|
if p.err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, p.err = fmt.Fprintf(p.w, format, args...)
|
_, p.err = fmt.Fprintf(&p.buf, format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *printer) Err() error {
|
func (p *printer) Result() ([]byte, error) {
|
||||||
return p.err
|
return p.buf.Bytes(), p.err
|
||||||
}
|
}
|
||||||
|
|||||||
61
internal/gen/godata.go
Normal file
61
internal/gen/godata.go
Normal 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()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user