build: support comments in functions (#41)

This commit is contained in:
Michael McLoughlin
2019-01-11 10:33:41 -08:00
parent 27cea3be8b
commit 284ee13ada
10 changed files with 117 additions and 23 deletions

View File

@@ -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")

View File

@@ -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) }

View File

@@ -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)

View File

@@ -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

View File

@@ -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"})
}

View File

@@ -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")
}

26
tests/fmt/asm.go Normal file
View File

@@ -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()
}

16
tests/fmt/fmt.s Normal file
View File

@@ -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

4
tests/fmt/gen.go Normal file
View File

@@ -0,0 +1,4 @@
// Package fmt tests assembly printer formatting.
package fmt
//go:generate go run asm.go -out fmt.s -stubs stub.go

6
tests/fmt/stub.go Normal file
View File

@@ -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()