Skip to content

Commit

Permalink
build,ir,printer: add InternalFunction
Browse files Browse the repository at this point in the history
Adds support for internal assembly functions. The new InternalFunction()
call outputs a TEXT directive sans the pesky unicode dot used in Go
assembly. This allows Avo to generate an internal assembly function that
is not linked to any symbols in the corresponding package.

Closes mmcloughlin#442
  • Loading branch information
Garrett-Bodley committed Aug 20, 2024
1 parent 13668f4 commit 82f991c
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 2 deletions.
11 changes: 11 additions & 0 deletions build/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ func (c *Context) Function(name string) {
c.file.AddSection(c.function)
}

// InternalFunction starts building a new internal function (not linked to any package) with the given name.
func (c *Context) InternalFunction(name string) {
c.function = ir.NewInternalFunction(name)
c.file.AddSection(c.function)
}

// Doc sets documentation comment lines for the currently active function.
func (c *Context) Doc(lines ...string) {
c.activefunc().Doc = lines
Expand All @@ -112,6 +118,11 @@ func (c *Context) Signature(s *gotypes.Signature) {

// SignatureExpr parses the signature expression and sets it as the active function's signature.
func (c *Context) SignatureExpr(expr string) {
if c.activefunc().IsInternal {
e := fmt.Sprintf("cannot link SignatureExpr \"%s\" with InternalFunction \"%s\"", expr, c.activefunc().Name)
c.adderror(errors.New(e))
return
}
s, err := gotypes.ParseSignatureInPackage(c.types(), expr)
if err != nil {
c.adderror(err)
Expand Down
3 changes: 3 additions & 0 deletions build/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ func Dereference(ptr gotypes.Component) gotypes.Component { return ctx.Dereferen
// Function starts building a new function with the given name.
func Function(name string) { ctx.Function(name) }

// InternalFunction starts building a new internal function (not linked to any package) with the given name.
func InternalFunction(name string) { ctx.InternalFunction(name) }

// Doc sets documentation comment lines for the currently active function.
func Doc(lines ...string) { ctx.Doc(lines...) }

Expand Down
11 changes: 11 additions & 0 deletions ir/ir.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ type Function struct {
Doc []string
Signature *gotypes.Signature
LocalSize int
IsInternal bool

Nodes []Node

Expand All @@ -195,10 +196,20 @@ func (f *Function) section() {}
func NewFunction(name string) *Function {
return &Function{
Name: name,
IsInternal: false,
Signature: gotypes.NewSignatureVoid(),
}
}

// NewInternalFunction builds an empty internal function (not linked to any package) of the given name.
func NewInternalFunction(name string) *Function {
return &Function{
Name: name,
IsInternal: true,
Signature: gotypes.NewSignatureVoid(),
}
}

// AddPragma adds a pragma to this function.
func (f *Function) AddPragma(directive string, args ...string) {
f.Pragmas = append(f.Pragmas, Pragma{
Expand Down
14 changes: 14 additions & 0 deletions ir/ir_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ func TestFunctionLabels(t *testing.T) {
}
}

func TestIsNotInternal(t *testing.T) {
f := NewFunction("isNotInternal")
if f.IsInternal {
t.Fatalf("expected f.IsInternal to be false, got %t", f.IsInternal)
}
}

func TestIsInternal(t *testing.T) {
f := NewInternalFunction("isInternal")
if !f.IsInternal {
t.Fatalf("expected f.IsInternal to be true, got %t", f.IsInternal)
}
}

func TestInputRegisters(t *testing.T) {
cases := []struct {
Name string
Expand Down
13 changes: 11 additions & 2 deletions printer/goasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,22 @@ func (p *goasm) includes(paths []string) {

func (p *goasm) function(f *ir.Function) {
p.NL()
p.Comment(f.Stub())
if !f.IsInternal {
p.Comment(f.Stub())
} else {
p.Comment("Internal Function (not linked to any package)")
}

if len(f.ISA) > 0 {
p.Comment("Requires: " + strings.Join(f.ISA, ", "))
}

if f.IsInternal {
p.Printf("TEXT %s(SB)", f.Name)
} else {
p.Printf("TEXT %s%s(SB)", dot, f.Name)
}

// Reference: https://github.com/golang/go/blob/b115207baf6c2decc3820ada4574ef4e5ad940ec/src/cmd/internal/obj/util.go#L166-L176
//
// if p.As == ATEXT {
Expand All @@ -87,7 +97,6 @@ func (p *goasm) function(f *ir.Function) {
// }
// }
//
p.Printf("TEXT %s%s(SB)", dot, f.Name)
if f.Attributes != 0 {
p.Printf(", %s", f.Attributes.Asm())
}
Expand Down
39 changes: 39 additions & 0 deletions printer/goasm_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package printer_test

import (
"errors"
"strings"
"testing"

"github.com/mmcloughlin/avo/attr"
Expand Down Expand Up @@ -34,6 +36,43 @@ func TestBasic(t *testing.T) {
})
}

func TestInternal(t *testing.T) {
ctx := build.NewContext()
ctx.InternalFunction("internal")
ctx.AllocLocal(16)
ctx.RET()

AssertPrintsLines(t, ctx, printer.NewGoAsm, []string{
"// Code generated by avo. DO NOT EDIT.",
"",
"// Internal Function (not linked to any package)",
"TEXT internal(SB), $16",
"\tRET",
"",
})
}

func TestInternalSigExp(t *testing.T) {
ctx := build.NewContext()
ctx.InternalFunction("internal")
ctx.SignatureExpr("invalidSig(b bool)")
ctx.ADDQ(reg.EAX, reg.ECX)

_, err := ctx.Result()
expect := "cannot link SignatureExpr \"invalidSig(b bool)\" with InternalFunction \"internal\""

var list build.ErrorList
if errors.As(err, &list) {
for _, e := range list {
if strings.Contains(e.Error(), expect) {
return
}
}
}

t.Fatal("Calling SignatureExpr() while the current function is an InternalFunction should generate an error")
}

func TestTextDecl(t *testing.T) {
ctx := build.NewContext()

Expand Down

0 comments on commit 82f991c

Please sign in to comment.