Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build,ir,printer: add InternalFunction #443

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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