diff options
Diffstat (limited to 'vendor/github.com/mmcloughlin/avo/printer')
-rw-r--r-- | vendor/github.com/mmcloughlin/avo/printer/goasm.go | 186 | ||||
-rw-r--r-- | vendor/github.com/mmcloughlin/avo/printer/printer.go | 98 | ||||
-rw-r--r-- | vendor/github.com/mmcloughlin/avo/printer/stubs.go | 45 |
3 files changed, 329 insertions, 0 deletions
diff --git a/vendor/github.com/mmcloughlin/avo/printer/goasm.go b/vendor/github.com/mmcloughlin/avo/printer/goasm.go new file mode 100644 index 0000000..0d8a12c --- /dev/null +++ b/vendor/github.com/mmcloughlin/avo/printer/goasm.go @@ -0,0 +1,186 @@ +package printer + +import ( + "strconv" + "strings" + + "github.com/mmcloughlin/avo/internal/prnt" + "github.com/mmcloughlin/avo/ir" + "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 + + instructions []*ir.Instruction + clear bool +} + +// NewGoAsm constructs a printer for writing Go assembly files. +func NewGoAsm(cfg Config) Printer { + return &goasm{cfg: cfg} +} + +func (p *goasm) Print(f *ir.File) ([]byte, error) { + p.header(f) + for _, s := range f.Sections { + switch s := s.(type) { + case *ir.Function: + p.function(s) + case *ir.Global: + p.global(s) + default: + panic("unknown section type") + } + } + return p.Result() +} + +func (p *goasm) header(f *ir.File) { + p.Comment(p.cfg.GeneratedWarning()) + + if len(f.Constraints) > 0 { + p.NL() + p.Printf(f.Constraints.GoString()) + } + + if len(f.Includes) > 0 { + p.NL() + p.includes(f.Includes) + } +} + +func (p *goasm) includes(paths []string) { + for _, path := range paths { + p.Printf("#include \"%s\"\n", path) + } +} + +func (p *goasm) function(f *ir.Function) { + p.NL() + p.Comment(f.Stub()) + + if len(f.ISA) > 0 { + p.Comment("Requires: " + strings.Join(f.ISA, ", ")) + } + + // Reference: https://github.com/golang/go/blob/b115207baf6c2decc3820ada4574ef4e5ad940ec/src/cmd/internal/obj/util.go#L166-L176 + // + // if p.As == ATEXT { + // // If there are attributes, print them. Otherwise, skip the comma. + // // In short, print one of these two: + // // TEXT foo(SB), DUPOK|NOSPLIT, $0 + // // TEXT foo(SB), $0 + // s := p.From.Sym.Attribute.TextAttrString() + // if s != "" { + // fmt.Fprintf(&buf, "%s%s", sep, s) + // sep = ", " + // } + // } + // + p.Printf("TEXT %s%s(SB)", dot, f.Name) + if f.Attributes != 0 { + p.Printf(", %s", f.Attributes.Asm()) + } + p.Printf(", %s\n", textsize(f)) + + p.clear = true + for _, node := range f.Nodes { + switch n := node.(type) { + case *ir.Instruction: + p.instruction(n) + if n.IsTerminal || n.IsUnconditionalBranch() { + p.flush() + } + case ir.Label: + p.flush() + p.ensureclear() + p.Printf("%s:\n", n) + case *ir.Comment: + p.flush() + p.ensureclear() + for _, line := range n.Lines { + p.Printf("\t// %s\n", line) + } + default: + panic("unexpected node type") + } + } + p.flush() +} + +func (p *goasm) instruction(i *ir.Instruction) { + p.instructions = append(p.instructions, i) + p.clear = false +} + +func (p *goasm) flush() { + if len(p.instructions) == 0 { + return + } + + // Determine instruction width. Instructions with no operands are not + // considered in this calculation. + width := 0 + for _, i := range p.instructions { + if len(i.Operands) > 0 && len(i.Opcode) > width { + width = len(i.Opcode) + } + } + + // Output instruction block. + for _, i := range p.instructions { + if len(i.Operands) > 0 { + p.Printf("\t%-*s%s\n", width+1, i.Opcode, joinOperands(i.Operands)) + } else { + p.Printf("\t%s\n", i.Opcode) + } + } + + p.instructions = nil +} + +func (p *goasm) ensureclear() { + if !p.clear { + p.NL() + p.clear = true + } +} + +func (p *goasm) global(g *ir.Global) { + p.NL() + for _, d := range g.Data { + a := operand.NewDataAddr(g.Symbol, d.Offset) + p.Printf("DATA %s/%d, %s\n", a.Asm(), d.Value.Bytes(), d.Value.Asm()) + } + p.Printf("GLOBL %s(SB), %s, $%d\n", g.Symbol, g.Attributes.Asm(), g.Size) +} + +func textsize(f *ir.Function) string { + // Reference: https://github.com/golang/go/blob/b115207baf6c2decc3820ada4574ef4e5ad940ec/src/cmd/internal/obj/util.go#L260-L265 + // + // case TYPE_TEXTSIZE: + // if a.Val.(int32) == objabi.ArgsSizeUnknown { + // str = fmt.Sprintf("$%d", a.Offset) + // } else { + // str = fmt.Sprintf("$%d-%d", a.Offset, a.Val.(int32)) + // } + // + s := "$" + strconv.Itoa(f.FrameBytes()) + if argsize := f.ArgumentBytes(); argsize > 0 { + return s + "-" + strconv.Itoa(argsize) + } + return s +} + +func joinOperands(operands []operand.Op) string { + asm := make([]string, len(operands)) + for i, op := range operands { + asm[i] = op.Asm() + } + return strings.Join(asm, ", ") +} diff --git a/vendor/github.com/mmcloughlin/avo/printer/printer.go b/vendor/github.com/mmcloughlin/avo/printer/printer.go new file mode 100644 index 0000000..b562c74 --- /dev/null +++ b/vendor/github.com/mmcloughlin/avo/printer/printer.go @@ -0,0 +1,98 @@ +// Package printer implements printing of avo files in various formats. +package printer + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/mmcloughlin/avo/internal/stack" + "github.com/mmcloughlin/avo/ir" +) + +// Printer can produce output for an avo File. +type Printer interface { + Print(*ir.File) ([]byte, error) +} + +// Builder can construct a printer. +type Builder func(Config) Printer + +// Config represents general printing configuration. +type Config struct { + // Command-line arguments passed to the generator. If provided, this will be + // included in a code generation warning. + Argv []string + + // Name of the code generator. + Name string + + // Name of Go package the generated code will belong to. + Pkg string +} + +// NewDefaultConfig produces a config with Name "avo". +// The package name is guessed from the current directory. +func NewDefaultConfig() Config { + return Config{ + Name: "avo", + Pkg: pkg(), + } +} + +// NewArgvConfig constructs a Config from os.Args. +// The package name is guessed from the current directory. +func NewArgvConfig() Config { + return Config{ + Argv: os.Args, + Pkg: pkg(), + } +} + +// NewGoRunConfig produces a Config for a generator that's expected to be +// executed via "go run ...". +func NewGoRunConfig() Config { + path := mainfile() + if path == "" { + return NewDefaultConfig() + } + argv := []string{"go", "run", filepath.Base(path)} + if len(os.Args) > 1 { + argv = append(argv, os.Args[1:]...) + } + return Config{ + Argv: argv, + Pkg: pkg(), + } +} + +// GeneratedBy returns a description of the code generator. +func (c Config) GeneratedBy() string { + if c.Argv == nil { + return c.Name + } + return fmt.Sprintf("command: %s", strings.Join(c.Argv, " ")) +} + +// GeneratedWarning returns text for a code generation warning. Conforms to https://golang.org/s/generatedcode. +func (c Config) GeneratedWarning() string { + return fmt.Sprintf("Code generated by %s. DO NOT EDIT.", c.GeneratedBy()) +} + +// mainfile attempts to determine the file path of the main function by +// inspecting the stack. Returns empty string on failure. +func mainfile() string { + if m := stack.Main(); m != nil { + return m.File + } + return "" +} + +// pkg guesses the name of the package from the working directory. +func pkg() string { + if cwd, err := os.Getwd(); err == nil { + return filepath.Base(cwd) + } + return "" +} diff --git a/vendor/github.com/mmcloughlin/avo/printer/stubs.go b/vendor/github.com/mmcloughlin/avo/printer/stubs.go new file mode 100644 index 0000000..171bc62 --- /dev/null +++ b/vendor/github.com/mmcloughlin/avo/printer/stubs.go @@ -0,0 +1,45 @@ +package printer + +import ( + "github.com/mmcloughlin/avo/internal/prnt" + "github.com/mmcloughlin/avo/ir" +) + +type stubs struct { + cfg Config + prnt.Generator +} + +// NewStubs constructs a printer for writing stub function declarations. +func NewStubs(cfg Config) Printer { + return &stubs{cfg: cfg} +} + +func (s *stubs) Print(f *ir.File) ([]byte, error) { + s.Comment(s.cfg.GeneratedWarning()) + + if len(f.Constraints) > 0 { + s.NL() + s.Printf(f.Constraints.GoString()) + } + + s.NL() + s.Printf("package %s\n", s.cfg.Pkg) + for _, fn := range f.Functions() { + s.NL() + s.Comment(fn.Doc...) + for _, pragma := range fn.Pragmas { + s.pragma(pragma) + } + s.Printf("%s\n", fn.Stub()) + } + return s.Result() +} + +func (s *stubs) pragma(p ir.Pragma) { + s.Printf("//go:%s", p.Directive) + for _, arg := range p.Arguments { + s.Printf(" %s", arg) + } + s.NL() +} |