From 4e57f842add22dd25924b22fb3e7e4ef6bdf9629 Mon Sep 17 00:00:00 2001 From: Thomas Pelletier Date: Wed, 11 Oct 2023 22:00:29 -0400 Subject: [PATCH 1/4] Support pre-processor macros and includes --- build/context.go | 11 ++++++++++ build/global.go | 7 +++++++ ir/ir.go | 27 +++++++++++++++++++++++- printer/goasm.go | 3 +++ printer/goasm_test.go | 48 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 95 insertions(+), 1 deletion(-) 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/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", + "", + }) + +} From 68e3cb7d3500d8e35842cfd19d3747dbbd986ce5 Mon Sep 17 00:00:00 2001 From: Thomas Pelletier Date: Tue, 16 Jan 2024 19:03:07 +0000 Subject: [PATCH 2/4] Replace generic preprocessor with specific nodes --- build/context.go | 21 ++++++++++++-- build/global.go | 8 ++++-- ir/ir.go | 66 +++++++++++++++++++++++++++++++++++++------ preproc/cond.go | 27 ++++++++++++++++++ printer/goasm.go | 13 +++++++-- printer/goasm_test.go | 6 ++-- 6 files changed, 121 insertions(+), 20 deletions(-) create mode 100644 preproc/cond.go diff --git a/build/context.go b/build/context.go index 2373ec0a..27be4df6 100644 --- a/build/context.go +++ b/build/context.go @@ -166,9 +166,24 @@ 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) +// PreprocIfdef adds an #ifdef macro to the active function. +func (c *Context) PreprocIfdef(condition string) { + c.activefunc().AddPreprocIfdef(condition) +} + +// PreprocIfndef adds an #ifndef macro to the active function. +func (c *Context) PreprocIfndef(condition string) { + c.activefunc().AddPreprocIfndef(condition) +} + +// PreprocElse adds an #else macro to the active function. +func (c *Context) PreprocElse() { + c.activefunc().AddPreprocElse() +} + +// PreprocEndif adds an #endif macro to the active function. +func (c *Context) PreprocEndif() { + c.activefunc().AddPreprocEndif() } // Comment adds comment lines to the active function. diff --git a/build/global.go b/build/global.go index c77de6f2..8a8fdcdd 100644 --- a/build/global.go +++ b/build/global.go @@ -15,6 +15,11 @@ import ( // ctx provides a global build context. var ctx = NewContext() +// Returns the global context. +func GlobalContext() *Context { + return ctx +} + // TEXT starts building a new function called name, with attributes a, and sets its signature (see SignatureExpr). func TEXT(name string, a attr.Attribute, signature string) { ctx.Function(name) @@ -62,9 +67,6 @@ 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 117d676f..d2285bfb 100644 --- a/ir/ir.go +++ b/ir/ir.go @@ -2,6 +2,7 @@ package ir import ( "errors" + "github.com/mmcloughlin/avo/attr" "github.com/mmcloughlin/avo/buildtags" "github.com/mmcloughlin/avo/gotypes" @@ -14,14 +15,46 @@ type Node interface { node() } -// Preprocessor macro within a function. -type Preprocessor string +// PreprocIfDef is the #ifdef preprocessor macro node, represented as its +// condition expression. +type PreprocIfdef string + +func (p PreprocIfdef) node() {} + +// NewPreprocIfdef builds a PreprocIfdef node from the provided condition. +func NewPreprocIfdef(condition string) PreprocIfdef { + return PreprocIfdef(condition) +} + +// PreprocIfndef is the #ifndef preprocessor macro node, represented as its +// condition expression. +type PreprocIfndef string -func (p Preprocessor) node() {} +func (p PreprocIfndef) node() {} -// NewPreprocessor builds a Preprocessor from the provided macro string. -func NewPreprocessor(macro string) Preprocessor { - return Preprocessor(macro) +// NewPreprocIfndef builds a PreprocIfndef node from the provided condition. +func NewPreprocIfndef(condition string) PreprocIfndef { + return PreprocIfndef(condition) +} + +// PreprocElse is the #else preprocessor macro node. +type PreprocElse struct{} + +func (p PreprocElse) node() {} + +// NewPreprocElse builds a PreprocElse node. +func NewPreprocElse() PreprocElse { + return PreprocElse{} +} + +// PreprocEndif is the #endif preprocessor macro node. +type PreprocEndif struct{} + +func (p PreprocEndif) node() {} + +// NewPreprocEnd builds a PreprocEnd node. +func NewPreprocEndif() PreprocEndif { + return PreprocEndif{} } // Label within a function. @@ -250,9 +283,24 @@ 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)) +// AddPreprocIfdef adds an #ifdef preprocessor macro to f. +func (f *Function) AddPreprocIfdef(condition string) { + f.AddNode(NewPreprocIfdef(condition)) +} + +// AddPreprocIfndef adds an #ifndef preprocessor macro to f. +func (f *Function) AddPreprocIfndef(condition string) { + f.AddNode(NewPreprocIfndef(condition)) +} + +// AddPreprocElse adds an #else preprocessor macro to f. +func (f *Function) AddPreprocElse() { + f.AddNode(NewPreprocElse()) +} + +// AddPreprocEndif adds an #endif preprocessor macro to f. +func (f *Function) AddPreprocEndif() { + f.AddNode(NewPreprocEndif()) } // AddComment adds comment lines to f. diff --git a/preproc/cond.go b/preproc/cond.go new file mode 100644 index 00000000..81abe1f7 --- /dev/null +++ b/preproc/cond.go @@ -0,0 +1,27 @@ +package preproc + +import "github.com/mmcloughlin/avo/build" + +// Ifdef inserts an #ifdef preprocessor macro. Condition should be a valid +// preprocessor expression, though this is not checked. +func Ifdef(condition string) { + build.GlobalContext().PreprocIfdef(condition) +} + +// Ifndef inserts an #ifndef preprocessor macro. Condition should be a valid +// preprocessor expression, though this is not checked. +func Ifndef(condition string) { + build.GlobalContext().PreprocIfndef(condition) +} + +// Else inserts an #else preprocessor macro. It needs to be preceded by an +// #ifdef or #ifndef macro. +func Else() { + build.GlobalContext().PreprocElse() +} + +// End inserts an #endif preprocessor macro. It needs to be preceded by an +// #ifdef, #ifndef, or #else macro. +func Endif() { + build.GlobalContext().PreprocEndif() +} diff --git a/printer/goasm.go b/printer/goasm.go index 6bdf0579..a53528ff 100644 --- a/printer/goasm.go +++ b/printer/goasm.go @@ -111,9 +111,18 @@ func (p *goasm) function(f *ir.Function) { for _, line := range n.Lines { p.Printf("\t// %s\n", line) } - case ir.Preprocessor: + case ir.PreprocIfdef: p.flush() - p.Printf("#%s\n", string(n)) + p.Printf("#ifdef %s\n", string(n)) + case ir.PreprocIfndef: + p.flush() + p.Printf("#ifndef %s\n", string(n)) + case ir.PreprocElse: + p.flush() + p.Printf("#else\n") + case ir.PreprocEndif: + p.flush() + p.Printf("#endif\n") default: panic("unexpected node type") } diff --git a/printer/goasm_test.go b/printer/goasm_test.go index 2bf17575..8a34a580 100644 --- a/printer/goasm_test.go +++ b/printer/goasm_test.go @@ -140,12 +140,12 @@ func TestPreprocessor(t *testing.T) { ctx := build.NewContext() ctx.Function("preprocessor") ctx.SignatureExpr("func()") - ctx.Preprocessor("ifndef hasAVX2") + ctx.PreprocIfndef("hasAVX2") ctx.ADDQ(reg.RBX, reg.RAX) ctx.VZEROUPPER() - ctx.Preprocessor("else") + ctx.PreprocElse() ctx.ADDQ(reg.RAX, reg.RBX) - ctx.Preprocessor("endif") + ctx.PreprocEndif() AssertPrintsLines(t, ctx, printer.NewGoAsm, []string{ "// Code generated by avo. DO NOT EDIT.", From 78a053c1881dd1a87e2eba7da7cf28293abd2ac4 Mon Sep 17 00:00:00 2001 From: Thomas Pelletier Date: Tue, 16 Jan 2024 19:09:25 +0000 Subject: [PATCH 3/4] Rename Include to IncludeHeader --- build/context.go | 6 +++--- build/global.go | 2 +- ir/ir.go | 6 +++--- printer/goasm_test.go | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/build/context.go b/build/context.go index 27be4df6..f5b107b3 100644 --- a/build/context.go +++ b/build/context.go @@ -56,10 +56,10 @@ func (c *Context) Package(path string) { c.pkg = pkg } -// Include adds an include pre-processor directive for the provided path, if it +// IncludeHeader 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) +func (c *Context) IncludeHeader(path string) { + c.file.IncludeHeader(path) } // Constraints sets build constraints for the file. diff --git a/build/global.go b/build/global.go index 8a8fdcdd..f0629df1 100644 --- a/build/global.go +++ b/build/global.go @@ -62,7 +62,7 @@ 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) } +func Include(path string) { ctx.IncludeHeader(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) } diff --git a/ir/ir.go b/ir/ir.go index d2285bfb..e97a5dd2 100644 --- a/ir/ir.go +++ b/ir/ir.go @@ -204,9 +204,9 @@ 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) { +// IncludeHeader adds an include pre-processor directive for the provided path, +// if it is not already present. +func (f *File) IncludeHeader(path string) { for _, p := range f.Includes { if p == path { return diff --git a/printer/goasm_test.go b/printer/goasm_test.go index 8a34a580..afe360dc 100644 --- a/printer/goasm_test.go +++ b/printer/goasm_test.go @@ -164,10 +164,10 @@ func TestPreprocessor(t *testing.T) { func TestInclude(t *testing.T) { ctx := build.NewContext() - ctx.Include("before.h") + ctx.IncludeHeader("before.h") ctx.Function("preprocessor") ctx.SignatureExpr("func()") - ctx.Include("after.h") + ctx.IncludeHeader("after.h") ctx.VZEROUPPER() AssertPrintsLines(t, ctx, printer.NewGoAsm, []string{ From 74a964e7232e75eb709785ae03b43e1bae912b60 Mon Sep 17 00:00:00 2001 From: Thomas Pelletier Date: Tue, 16 Jan 2024 19:17:53 +0000 Subject: [PATCH 4/4] Missed one Include to rename --- build/global.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/global.go b/build/global.go index f0629df1..2e791695 100644 --- a/build/global.go +++ b/build/global.go @@ -60,9 +60,9 @@ 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.IncludeHeader(path) } +// IncludeHeader adds an include pre-processor directive for the provided path +// to the current file, if it is not already present. +func IncludeHeader(path string) { ctx.IncludeHeader(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) }