diff --git a/build/context.go b/build/context.go index cbfc639c..2373ec0a 100644 --- a/build/context.go +++ b/build/context.go @@ -56,6 +56,12 @@ func (c *Context) Package(path string) { c.pkg = pkg } +// Include adds an include pre-processor directive for the provided path, if it +// is not already present. +func (c *Context) Include(path string) { + c.file.Include(path) +} + // Constraints sets build constraints for the file. func (c *Context) Constraints(t buildtags.ConstraintsConvertable) { cs := t.ToConstraints() @@ -160,6 +166,11 @@ func (c *Context) Label(name string) { c.activefunc().AddLabel(ir.Label(name)) } +// Preprocessor adds a preprocessor macro to the active function. +func (c *Context) Preprocessor(macro string) { + c.activefunc().AddPreprocessor(macro) +} + // Comment adds comment lines to the active function. func (c *Context) Comment(lines ...string) { c.activefunc().AddComment(lines...) diff --git a/build/global.go b/build/global.go index 3a1690c1..c77de6f2 100644 --- a/build/global.go +++ b/build/global.go @@ -55,9 +55,16 @@ func Generate() { } } +// Include adds an include pre-processor directive for the provided path to the +// current file, if it is not already present. +func Include(path string) { ctx.Include(path) } + // Package sets the package the generated file will belong to. Required to be able to reference types in the package. func Package(path string) { ctx.Package(path) } +// Preprocessor adds a pre-processor macro to the current function. +func Preprocessor(macro string) { ctx.Preprocessor(macro) } + // Constraints sets build constraints for the file. func Constraints(t buildtags.ConstraintsConvertable) { ctx.Constraints(t) } diff --git a/ir/ir.go b/ir/ir.go index 871d4881..117d676f 100644 --- a/ir/ir.go +++ b/ir/ir.go @@ -2,7 +2,6 @@ package ir import ( "errors" - "github.com/mmcloughlin/avo/attr" "github.com/mmcloughlin/avo/buildtags" "github.com/mmcloughlin/avo/gotypes" @@ -15,6 +14,16 @@ type Node interface { node() } +// Preprocessor macro within a function. +type Preprocessor string + +func (p Preprocessor) node() {} + +// NewPreprocessor builds a Preprocessor from the provided macro string. +func NewPreprocessor(macro string) Preprocessor { + return Preprocessor(macro) +} + // Label within a function. type Label string @@ -162,6 +171,17 @@ func (f *File) Functions() []*Function { return fns } +// Include adds an include pre-processor directive for the provided path, if it +// is not already present. +func (f *File) Include(path string) { + for _, p := range f.Includes { + if p == path { + return + } + } + f.Includes = append(f.Includes, path) +} + // Pragma represents a function compiler directive. type Pragma struct { Directive string @@ -230,6 +250,11 @@ func (f *Function) AddLabel(l Label) { f.AddNode(l) } +// AddPreprocessor adds a pre-processor macro to f. +func (f *Function) AddPreprocessor(macro string) { + f.AddNode(NewPreprocessor(macro)) +} + // AddComment adds comment lines to f. func (f *Function) AddComment(lines ...string) { f.AddNode(NewComment(lines...)) diff --git a/pass/cfg.go b/pass/cfg.go index d5f6ea4e..3742fdfd 100644 --- a/pass/cfg.go +++ b/pass/cfg.go @@ -49,14 +49,13 @@ func CFG(fn *ir.Function) error { // If it's a branch, locate the target. if cur.IsBranch { lbl := cur.TargetLabel() - if lbl == nil { - return errors.New("no label for branch instruction") + if lbl != nil { + target, found := fn.LabelTarget[*lbl] + if !found { + return fmt.Errorf("unknown label %q", *lbl) + } + cur.Succ = append(cur.Succ, target) } - target, found := fn.LabelTarget[*lbl] - if !found { - return fmt.Errorf("unknown label %q", *lbl) - } - cur.Succ = append(cur.Succ, target) } // Otherwise, could continue to the following instruction. diff --git a/printer/goasm.go b/printer/goasm.go index 23f5b2f7..6bdf0579 100644 --- a/printer/goasm.go +++ b/printer/goasm.go @@ -111,6 +111,9 @@ func (p *goasm) function(f *ir.Function) { for _, line := range n.Lines { p.Printf("\t// %s\n", line) } + case ir.Preprocessor: + p.flush() + p.Printf("#%s\n", string(n)) default: panic("unexpected node type") } diff --git a/printer/goasm_test.go b/printer/goasm_test.go index e8b4fe1e..2bf17575 100644 --- a/printer/goasm_test.go +++ b/printer/goasm_test.go @@ -135,3 +135,51 @@ func TestOpcodeSuffixes(t *testing.T) { "", }) } + +func TestPreprocessor(t *testing.T) { + ctx := build.NewContext() + ctx.Function("preprocessor") + ctx.SignatureExpr("func()") + ctx.Preprocessor("ifndef hasAVX2") + ctx.ADDQ(reg.RBX, reg.RAX) + ctx.VZEROUPPER() + ctx.Preprocessor("else") + ctx.ADDQ(reg.RAX, reg.RBX) + ctx.Preprocessor("endif") + + AssertPrintsLines(t, ctx, printer.NewGoAsm, []string{ + "// Code generated by avo. DO NOT EDIT.", + "", + "// func preprocessor()", + "TEXT ·preprocessor(SB), $0", + "#ifndef hasAVX2", + "\tADDQ BX, AX", + "\tVZEROUPPER", + "#else", + "\tADDQ AX, BX", + "#endif", + "", + }) +} + +func TestInclude(t *testing.T) { + ctx := build.NewContext() + ctx.Include("before.h") + ctx.Function("preprocessor") + ctx.SignatureExpr("func()") + ctx.Include("after.h") + ctx.VZEROUPPER() + + AssertPrintsLines(t, ctx, printer.NewGoAsm, []string{ + "// Code generated by avo. DO NOT EDIT.", + "", + "#include \"before.h\"", + "#include \"after.h\"", + "", + "// func preprocessor()", + "TEXT ·preprocessor(SB), $0", + "\tVZEROUPPER", + "", + }) + +}