diff options
Diffstat (limited to 'vendor/github.com/mmcloughlin/avo/printer/goasm.go')
-rw-r--r-- | vendor/github.com/mmcloughlin/avo/printer/goasm.go | 186 |
1 files changed, 186 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, ", ") +} |