From 284ee13adaaa7d1dc52f46362d46115561b4b97d Mon Sep 17 00:00:00 2001 From: Michael McLoughlin Date: Fri, 11 Jan 2019 10:33:41 -0800 Subject: [PATCH] build: support comments in functions (#41) --- build/context.go | 5 +++++ build/global.go | 3 +++ ir/ir.go | 19 +++++++++++++++++++ pass/cfg.go | 40 ++++++++++++++++++++-------------------- pass/cfg_test.go | 1 + printer/goasm.go | 20 +++++++++++++++++--- tests/fmt/asm.go | 26 ++++++++++++++++++++++++++ tests/fmt/fmt.s | 16 ++++++++++++++++ tests/fmt/gen.go | 4 ++++ tests/fmt/stub.go | 6 ++++++ 10 files changed, 117 insertions(+), 23 deletions(-) create mode 100644 tests/fmt/asm.go create mode 100644 tests/fmt/fmt.s create mode 100644 tests/fmt/gen.go create mode 100644 tests/fmt/stub.go diff --git a/build/context.go b/build/context.go index 637742b..28948e6 100644 --- a/build/context.go +++ b/build/context.go @@ -133,6 +133,11 @@ func (c *Context) Label(name string) { c.activefunc().AddLabel(ir.Label(name)) } +// Comment adds comment lines to the active function. +func (c *Context) Comment(lines ...string) { + c.activefunc().AddComment(lines...) +} + func (c *Context) activefunc() *ir.Function { if c.function == nil { c.adderrormessage("no active function") diff --git a/build/global.go b/build/global.go index 8088550..d0722d3 100644 --- a/build/global.go +++ b/build/global.go @@ -125,5 +125,8 @@ func AllocLocal(size int) operand.Mem { return ctx.AllocLocal(size) } // Label adds a label to the active function. func Label(name string) { ctx.Label(name) } +// Comment adds comment lines to the active function. +func Comment(lines ...string) { ctx.Comment(lines...) } + // ConstData builds a static data section containing just the given constant. func ConstData(name string, v operand.Constant) operand.Mem { return ctx.ConstData(name, v) } diff --git a/ir/ir.go b/ir/ir.go index 4d05d8d..85c60b3 100644 --- a/ir/ir.go +++ b/ir/ir.go @@ -20,6 +20,20 @@ type Label string func (l Label) node() {} +// Comment represents a multi-line comment. +type Comment struct { + Lines []string +} + +func (c *Comment) node() {} + +// NewComment builds a Comment consisting of the provided lines. +func NewComment(lines ...string) *Comment { + return &Comment{ + Lines: lines, + } +} + // Instruction is a single instruction in a function. type Instruction struct { Opcode string @@ -176,6 +190,11 @@ func (f *Function) AddLabel(l Label) { f.AddNode(l) } +// AddComment adds comment lines to f. +func (f *Function) AddComment(lines ...string) { + f.AddNode(NewComment(lines...)) +} + // AddNode appends a Node to f. func (f *Function) AddNode(n Node) { f.Nodes = append(f.Nodes, n) diff --git a/pass/cfg.go b/pass/cfg.go index 32111b1..643265c 100644 --- a/pass/cfg.go +++ b/pass/cfg.go @@ -11,27 +11,27 @@ import ( // label name to the following instruction. func LabelTarget(fn *ir.Function) error { target := map[ir.Label]*ir.Instruction{} - for idx := 0; idx < len(fn.Nodes); idx++ { - // Is this a label? - lbl, ok := fn.Nodes[idx].(ir.Label) - if !ok { - continue + var empty ir.Label + pending := empty + for _, node := range fn.Nodes { + switch n := node.(type) { + case ir.Label: + if pending != empty { + return errors.New("instruction should follow a label") + } + pending = n + if _, found := target[pending]; found { + return fmt.Errorf("duplicate label \"%s\"", pending) + } + case *ir.Instruction: + if pending != empty { + target[pending] = n + pending = empty + } } - // Check for a duplicate label. - if _, found := target[lbl]; found { - return fmt.Errorf("duplicate label \"%s\"", lbl) - } - // Advance to next node. - if idx == len(fn.Nodes)-1 { - return errors.New("function ends with label") - } - idx++ - // Should be an instruction. - i, ok := fn.Nodes[idx].(*ir.Instruction) - if !ok { - return errors.New("instruction should follow a label") - } - target[lbl] = i + } + if pending != empty { + return errors.New("function ends with label") } fn.LabelTarget = target return nil diff --git a/pass/cfg_test.go b/pass/cfg_test.go index 09761ea..42f1004 100644 --- a/pass/cfg_test.go +++ b/pass/cfg_test.go @@ -18,6 +18,7 @@ func TestLabelTarget(t *testing.T) { f := ir.NewFunction("happypath") for lbl, i := range expect { f.AddLabel(lbl) + f.AddComment("comments should be ignored") f.AddInstruction(i) f.AddInstruction(&ir.Instruction{Opcode: "IDK"}) } diff --git a/printer/goasm.go b/printer/goasm.go index 8d67162..66522a5 100644 --- a/printer/goasm.go +++ b/printer/goasm.go @@ -84,6 +84,15 @@ func (p *goasm) function(f *ir.Function) { p.Printf(", %s\n", textsize(f)) w := p.tabwriter() + clear := true + flush := func() { + w.Flush() + w = p.tabwriter() + if !clear { + p.NL() + clear = true + } + } for _, node := range f.Nodes { switch n := node.(type) { case *ir.Instruction: @@ -93,10 +102,15 @@ func (p *goasm) function(f *ir.Function) { fmt.Fprintf(w, "\t%s", joinOperands(n.Operands)) } fmt.Fprint(w, "\n") + clear = false case ir.Label: - w.Flush() - w = p.tabwriter() - p.Printf("\n%s:\n", n) + flush() + p.Printf("%s:\n", n) + case *ir.Comment: + flush() + for _, line := range n.Lines { + p.Printf("\t// %s\n", line) + } default: panic("unexpected node type") } diff --git a/tests/fmt/asm.go b/tests/fmt/asm.go new file mode 100644 index 0000000..67db674 --- /dev/null +++ b/tests/fmt/asm.go @@ -0,0 +1,26 @@ +// +build ignore + +package main + +import ( + . "github.com/mmcloughlin/avo/build" + . "github.com/mmcloughlin/avo/reg" +) + +func main() { + TEXT("Formatting", NOSPLIT, "func()") + Doc("Formatting contains various cases to test the formatter.") + + ADDQ(R8, R8) + Comment("One comment line between instructions.") + ADDQ(R8, R8) + + Comment("Comment before label.") + Label("label") + Comment("Comment after label.") + ADDQ(R8, R8) + + RET() + + Generate() +} diff --git a/tests/fmt/fmt.s b/tests/fmt/fmt.s new file mode 100644 index 0000000..43fcb5d --- /dev/null +++ b/tests/fmt/fmt.s @@ -0,0 +1,16 @@ +// Code generated by command: go run asm.go -out fmt.s -stubs stub.go. DO NOT EDIT. + +#include "textflag.h" + +// func Formatting() +TEXT ·Formatting(SB), NOSPLIT, $0 + ADDQ R8, R8 + + // One comment line between instructions. + ADDQ R8, R8 + + // Comment before label. +label: + // Comment after label. + ADDQ R8, R8 + RET diff --git a/tests/fmt/gen.go b/tests/fmt/gen.go new file mode 100644 index 0000000..6099393 --- /dev/null +++ b/tests/fmt/gen.go @@ -0,0 +1,4 @@ +// Package fmt tests assembly printer formatting. +package fmt + +//go:generate go run asm.go -out fmt.s -stubs stub.go diff --git a/tests/fmt/stub.go b/tests/fmt/stub.go new file mode 100644 index 0000000..e698910 --- /dev/null +++ b/tests/fmt/stub.go @@ -0,0 +1,6 @@ +// Code generated by command: go run asm.go -out fmt.s -stubs stub.go. DO NOT EDIT. + +package fmt + +// Formatting contains various cases to test the formatter. +func Formatting()