summaryrefslogtreecommitdiff
path: root/vendor/github.com/mmcloughlin/avo/printer
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/mmcloughlin/avo/printer')
-rw-r--r--vendor/github.com/mmcloughlin/avo/printer/goasm.go186
-rw-r--r--vendor/github.com/mmcloughlin/avo/printer/printer.go98
-rw-r--r--vendor/github.com/mmcloughlin/avo/printer/stubs.go45
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()
+}