printing: commit some refactors (probably broken)

This commit is contained in:
Michael McLoughlin
2018-12-11 00:18:22 -08:00
parent 4dc909a81e
commit c882e52510
21 changed files with 398 additions and 222 deletions

132
build/cli.go Normal file
View File

@@ -0,0 +1,132 @@
package build
import (
"flag"
"io"
"log"
"os"
"github.com/mmcloughlin/avo/pass"
"github.com/mmcloughlin/avo/printer"
)
type Config struct {
ErrOut io.Writer
Passes []pass.Interface
}
func Main(cfg *Config, context *Context) int {
diag := log.New(cfg.ErrOut, "", 0)
f, errs := context.Result()
if errs != nil {
for _, err := range errs {
diag.Println(err)
}
return 1
}
p := pass.Concat(cfg.Passes...)
if err := p.Execute(f); err != nil {
diag.Println(err)
return 1
}
return 0
}
type Flags struct {
errout *outputValue
printers []*printerValue
}
func NewFlags(fs *flag.FlagSet) *Flags {
f := &Flags{}
f.errout = newOutputValue(os.Stderr)
fs.Var(f.errout, "log", "diagnostics output")
goasm := newPrinterValue(printer.NewGoAsm, os.Stdout)
fs.Var(goasm, "out", "assembly output")
f.printers = append(f.printers, goasm)
stubs := newPrinterValue(printer.NewStubs, nil)
fs.Var(stubs, "stubs", "go stub file")
f.printers = append(f.printers, stubs)
return f
}
func (f *Flags) Config() *Config {
pc := printer.NewDefaultConfig()
passes := []pass.Interface{pass.Compile}
for _, pv := range f.printers {
p := pv.Build(pc)
if p != nil {
passes = append(passes, p)
}
}
return &Config{
ErrOut: f.errout.w,
Passes: passes,
}
}
type outputValue struct {
w io.WriteCloser
filename string
}
func newOutputValue(dflt io.WriteCloser) *outputValue {
return &outputValue{w: dflt}
}
func (o *outputValue) String() string {
if o == nil {
return ""
}
return o.filename
}
func (o *outputValue) Set(s string) error {
o.filename = s
if s == "-" {
o.w = nopwritecloser{os.Stdout}
return nil
}
f, err := os.Create(s)
if err != nil {
return err
}
o.w = f
return nil
}
type printerValue struct {
*outputValue
Builder printer.Builder
}
func newPrinterValue(b printer.Builder, dflt io.WriteCloser) *printerValue {
return &printerValue{
outputValue: newOutputValue(dflt),
Builder: b,
}
}
func (p *printerValue) Build(cfg printer.Config) pass.Interface {
if p.outputValue.w == nil {
return nil
}
return &pass.Output{
Writer: p.outputValue.w,
Printer: p.Builder(cfg),
}
}
// nopwritecloser wraps a Writer and provides a null implementation of Close().
type nopwritecloser struct {
io.Writer
}
func (nopwritecloser) Close() error { return nil }

View File

@@ -2,13 +2,9 @@ package build
import (
"errors"
"io"
"log"
"github.com/mmcloughlin/avo/gotypes"
"github.com/mmcloughlin/avo/pass"
"github.com/mmcloughlin/avo"
"github.com/mmcloughlin/avo/gotypes"
"github.com/mmcloughlin/avo/reg"
)
@@ -73,28 +69,3 @@ func (c *Context) AddErrorMessage(msg string) {
func (c *Context) Result() (*avo.File, []error) {
return c.file, c.errs
}
func (c *Context) Main(wout, werr io.Writer) int {
diag := log.New(werr, "", 0)
f, errs := c.Result()
if errs != nil {
for _, err := range errs {
diag.Println(err)
}
return 1
}
if err := pass.Compile.Execute(f); err != nil {
diag.Println(err)
return 1
}
p := avo.NewGoPrinter(wout)
if err := p.Print(f); err != nil {
diag.Println(err)
return 1
}
return 0
}

View File

@@ -2,8 +2,6 @@ package build
import (
"flag"
"io"
"log"
"os"
"github.com/mmcloughlin/avo/gotypes"
@@ -23,26 +21,14 @@ func TEXT(name, signature string) {
func LABEL(name string) { ctx.Label(avo.Label(name)) }
var (
output = flag.String("output", "", "output filename (default stdout)")
)
var flags = NewFlags(flag.CommandLine)
func EOF() {
func Generate() {
if !flag.Parsed() {
flag.Parse()
}
var w io.Writer = os.Stdout
if *output != "" {
if f, err := os.Create(*output); err != nil {
log.Fatal(err)
} else {
defer f.Close()
w = f
}
}
os.Exit(ctx.Main(w, os.Stderr))
cfg := flags.Config()
os.Exit(Main(cfg, ctx))
}
func GP8v() reg.Virtual { return ctx.GP8v() }

10
examples/add/add.s Normal file
View File

@@ -0,0 +1,10 @@
#include "textflag.h"
// func Add(x uint64, y uint64) uint64
TEXT ·Add(SB),0,$0-24
MOVQ x(FP), CX
MOVQ y+8(FP), AX
ADDQ CX, AX
MOVQ CX, ret+16(FP)
RET

12
examples/add/add_test.go Normal file
View File

@@ -0,0 +1,12 @@
package add
import (
"testing"
"testing/quick"
)
//go:generate go run asm.go -out add.s -stubs stub.go
func TestAdd(t *testing.T) {
quick.CheckEqual(Add, func(x, y uint64) uint64 { return x + y }, nil)
}

View File

@@ -1,3 +1,5 @@
// +build ignore
package main
import (
@@ -5,11 +7,11 @@ import (
)
func main() {
TEXT("add", "func(x, y uint64) uint64")
TEXT("Add", "func(x, y uint64) uint64")
x := Load(Param("x"), GP64v())
y := Load(Param("y"), GP64v())
ADDQ(x, y)
Store(x, ReturnIndex(0))
RET()
EOF()
Generate()
}

3
examples/add/stub.go Normal file
View File

@@ -0,0 +1,3 @@
package add
func Add(x uint64, y uint64) uint64

View File

@@ -10,6 +10,7 @@ import (
"github.com/mmcloughlin/avo/internal/gen"
"github.com/mmcloughlin/avo/internal/inst"
"github.com/mmcloughlin/avo/internal/load"
"github.com/mmcloughlin/avo/printer"
)
var generators = map[string]gen.Builder{
@@ -45,9 +46,7 @@ func main() {
log.Fatalf("unknown generator type '%s'", t)
}
g := builder(gen.Config{
Argv: os.Args,
})
g := builder(printer.NewDefaultConfig())
// Determine output writer.
w := os.Stdout

View File

@@ -7,17 +7,19 @@ import (
"strings"
"github.com/mmcloughlin/avo/internal/inst"
"github.com/mmcloughlin/avo/internal/prnt"
"github.com/mmcloughlin/avo/printer"
)
type asmtest struct {
cfg Config
cfg printer.Config
sym string // reference to the test function symbol
rel8 string // label to be used for near jumps
rel32 string // label for far jumps
generator
prnt.Generator
}
func NewAsmTest(cfg Config) Interface {
func NewAsmTest(cfg printer.Config) Interface {
return &asmtest{cfg: cfg}
}

View File

@@ -1,13 +1,17 @@
package gen
import "github.com/mmcloughlin/avo/internal/inst"
import (
"github.com/mmcloughlin/avo/internal/inst"
"github.com/mmcloughlin/avo/internal/prnt"
"github.com/mmcloughlin/avo/printer"
)
type build struct {
cfg Config
generator
cfg printer.Config
prnt.Generator
}
func NewBuild(cfg Config) Interface {
func NewBuild(cfg printer.Config) Interface {
return GoFmt(&build{cfg: cfg})
}

View File

@@ -6,14 +6,16 @@ import (
"strings"
"github.com/mmcloughlin/avo/internal/inst"
"github.com/mmcloughlin/avo/internal/prnt"
"github.com/mmcloughlin/avo/printer"
)
type ctors struct {
cfg Config
generator
cfg printer.Config
prnt.Generator
}
func NewCtors(cfg Config) Interface {
func NewCtors(cfg printer.Config) Interface {
return GoFmt(&ctors{cfg: cfg})
}

View File

@@ -3,15 +3,18 @@ package gen
import (
"strings"
"github.com/mmcloughlin/avo/internal/prnt"
"github.com/mmcloughlin/avo/printer"
"github.com/mmcloughlin/avo/internal/inst"
)
type ctorstest struct {
cfg Config
generator
cfg printer.Config
prnt.Generator
}
func NewCtorsTest(cfg Config) Interface {
func NewCtorsTest(cfg printer.Config) Interface {
return GoFmt(&ctorstest{cfg: cfg})
}

View File

@@ -1,12 +1,10 @@
package gen
import (
"bytes"
"fmt"
"go/format"
"strings"
"github.com/mmcloughlin/avo/internal/inst"
"github.com/mmcloughlin/avo/printer"
)
type Interface interface {
@@ -19,23 +17,7 @@ func (f Func) Generate(is []inst.Instruction) ([]byte, error) {
return f(is)
}
type Config struct {
Name string
Argv []string
}
func (c Config) GeneratedBy() string {
if c.Argv == nil {
return c.Name
}
return fmt.Sprintf("command: %s", strings.Join(c.Argv, " "))
}
func (c Config) GeneratedWarning() string {
return fmt.Sprintf("Code generated by %s. DO NOT EDIT.", c.GeneratedBy())
}
type Builder func(Config) Interface
type Builder func(printer.Config) Interface
// GoFmt formats Go code produced from the given generator.
func GoFmt(i Interface) Interface {
@@ -47,27 +29,3 @@ func GoFmt(i Interface) Interface {
return format.Source(b)
})
}
type generator struct {
buf bytes.Buffer
err error
}
func (g *generator) Printf(format string, args ...interface{}) {
if g.err != nil {
return
}
if _, err := fmt.Fprintf(&g.buf, format, args...); err != nil {
g.AddError(err)
}
}
func (g *generator) AddError(err error) {
if err != nil && g.err == nil {
g.err = err
}
}
func (g *generator) Result() ([]byte, error) {
return g.buf.Bytes(), g.err
}

View File

@@ -2,14 +2,16 @@ package gen
import (
"github.com/mmcloughlin/avo/internal/inst"
"github.com/mmcloughlin/avo/internal/prnt"
"github.com/mmcloughlin/avo/printer"
)
type godata struct {
cfg Config
generator
cfg printer.Config
prnt.Generator
}
func NewGoData(cfg Config) Interface {
func NewGoData(cfg printer.Config) Interface {
return GoFmt(&godata{cfg: cfg})
}
@@ -65,11 +67,11 @@ func (g *godata) Generate(is []inst.Instruction) ([]byte, error) {
}
type godatatest struct {
cfg Config
generator
cfg printer.Config
prnt.Generator
}
func NewGoDataTest(cfg Config) Interface {
func NewGoDataTest(cfg printer.Config) Interface {
return GoFmt(&godatatest{cfg: cfg})
}

View File

@@ -7,14 +7,16 @@ import (
"strings"
"github.com/mmcloughlin/avo/internal/inst"
"github.com/mmcloughlin/avo/internal/prnt"
"github.com/mmcloughlin/avo/printer"
)
type mov struct {
cfg Config
generator
cfg printer.Config
prnt.Generator
}
func NewMOV(cfg Config) Interface {
func NewMOV(cfg printer.Config) Interface {
return GoFmt(&mov{cfg: cfg})
}

40
internal/prnt/printer.go Normal file
View File

@@ -0,0 +1,40 @@
package prnt
import (
"bytes"
"fmt"
)
type Generator struct {
buf bytes.Buffer
err error
}
func (g *Generator) Printf(format string, args ...interface{}) {
if g.err != nil {
return
}
if _, err := fmt.Fprintf(&g.buf, format, args...); err != nil {
g.AddError(err)
}
}
func (g *Generator) NL() {
g.Printf("\n")
}
func (g *Generator) Comment(lines ...string) {
for _, line := range lines {
g.Printf("// %s\n", line)
}
}
func (g *Generator) AddError(err error) {
if err != nil && g.err == nil {
g.err = err
}
}
func (g *Generator) Result() ([]byte, error) {
return g.buf.Bytes(), g.err
}

View File

@@ -1,6 +1,11 @@
package pass
import "github.com/mmcloughlin/avo"
import (
"io"
"github.com/mmcloughlin/avo"
"github.com/mmcloughlin/avo/printer"
)
var Compile = Concat(
FunctionPass(LabelTarget),
@@ -42,3 +47,19 @@ func Concat(passes ...Interface) Interface {
return nil
})
}
type Output struct {
Writer io.WriteCloser
Printer printer.Printer
}
func (o *Output) Execute(f *avo.File) error {
b, err := o.Printer.Print(f)
if err != nil {
return err
}
if _, err = o.Writer.Write(b); err != nil {
return err
}
return o.Writer.Close()
}

View File

@@ -1,102 +0,0 @@
package avo
import (
"fmt"
"io"
"strings"
"github.com/mmcloughlin/avo/operand"
)
// dot is the pesky unicode dot used in Go assembly.
const dot = "\u00b7"
type Printer interface {
Print(*File) error
}
type GoPrinter struct {
w io.Writer
by string // generated by
err error
}
func NewGoPrinter(w io.Writer) *GoPrinter {
return &GoPrinter{
w: w,
by: "avo",
}
}
func (p *GoPrinter) SetGeneratedBy(by string) {
p.by = by
}
func (p *GoPrinter) Print(f *File) error {
p.header()
for _, fn := range f.Functions {
p.function(fn)
}
return p.err
}
func (p *GoPrinter) header() {
p.generated()
p.nl()
p.incl("textflag.h")
p.nl()
}
func (p *GoPrinter) generated() {
p.comment(fmt.Sprintf("Code generated by %s. DO NOT EDIT.", p.by))
}
func (p *GoPrinter) incl(path string) {
p.printf("#include \"%s\"\n", path)
}
func (p *GoPrinter) comment(line string) {
p.multicomment([]string{line})
}
func (p *GoPrinter) multicomment(lines []string) {
for _, line := range lines {
p.printf("// %s\n", line)
}
}
func (p *GoPrinter) function(f *Function) {
p.printf("// %s\n", f.Stub())
p.printf("TEXT %s%s(SB),0,$%d-%d\n", dot, f.Name, f.FrameBytes(), f.ArgumentBytes())
for _, node := range f.Nodes {
switch n := node.(type) {
case *Instruction:
p.printf("\t%s\t%s\n", n.Opcode, joinOperands(n.Operands))
case Label:
p.printf("%s:\n", n)
default:
panic("unexpected node type")
}
}
}
func (p *GoPrinter) nl() {
p.printf("\n")
}
func (p *GoPrinter) printf(format string, args ...interface{}) {
if _, err := fmt.Fprintf(p.w, format, args...); err != nil {
p.err = err
}
}
func joinOperands(operands []operand.Op) string {
asm := make([]string, len(operands))
for i, op := range operands {
asm[i] = op.Asm()
}
return strings.Join(asm, ", ")
}

63
printer/goasm.go Normal file
View File

@@ -0,0 +1,63 @@
package printer
import (
"strings"
"github.com/mmcloughlin/avo"
"github.com/mmcloughlin/avo/internal/prnt"
"github.com/mmcloughlin/avo/operand"
)
// dot is the pesky unicode dot used in Go assembly.
const dot = "\u00b7"
type goasm struct {
cfg Config
prnt.Generator
}
func NewGoAsm(cfg Config) Printer {
return &goasm{cfg: cfg}
}
func (p *goasm) Print(f *avo.File) ([]byte, error) {
p.header()
for _, fn := range f.Functions {
p.function(fn)
}
return p.Result()
}
func (p *goasm) header() {
p.NL()
p.include("textflag.h")
p.NL()
}
func (p *goasm) include(path string) {
p.Printf("#include \"%s\"\n", path)
}
func (p *goasm) function(f *avo.Function) {
p.Comment(f.Stub())
p.Printf("TEXT %s%s(SB),0,$%d-%d\n", dot, f.Name, f.FrameBytes(), f.ArgumentBytes())
for _, node := range f.Nodes {
switch n := node.(type) {
case *avo.Instruction:
p.Printf("\t%s\t%s\n", n.Opcode, joinOperands(n.Operands))
case avo.Label:
p.Printf("%s:\n", n)
default:
panic("unexpected node type")
}
}
}
func joinOperands(operands []operand.Op) string {
asm := make([]string, len(operands))
for i, op := range operands {
asm[i] = op.Asm()
}
return strings.Join(asm, ", ")
}

43
printer/printer.go Normal file
View File

@@ -0,0 +1,43 @@
package printer
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/mmcloughlin/avo"
)
type Printer interface {
Print(*avo.File) ([]byte, error)
}
type Builder func(Config) Printer
type Config struct {
Name string
Argv []string
Pkg string
}
func NewDefaultConfig() Config {
cfg := Config{
Argv: os.Args,
}
if cwd, err := os.Getwd(); err == nil {
cfg.Pkg = filepath.Base(cwd)
}
return cfg
}
func (c Config) GeneratedBy() string {
if c.Argv == nil {
return c.Name
}
return fmt.Sprintf("command: %s", strings.Join(c.Argv, " "))
}
func (c Config) GeneratedWarning() string {
return fmt.Sprintf("Code generated by %s. DO NOT EDIT.", c.GeneratedBy())
}

23
printer/stubs.go Normal file
View File

@@ -0,0 +1,23 @@
package printer
import (
"github.com/mmcloughlin/avo"
"github.com/mmcloughlin/avo/internal/prnt"
)
type stubs struct {
cfg Config
prnt.Generator
}
func NewStubs(cfg Config) Printer {
return &stubs{cfg: cfg}
}
func (s *stubs) Print(f *avo.File) ([]byte, error) {
s.Printf("package %s\n\n", s.cfg.Pkg)
for _, fn := range f.Functions {
s.Printf("%s\n", fn.Stub())
}
return s.Result()
}