diff --git a/build/context.go b/build/context.go index 38ad6f8..84353ee 100644 --- a/build/context.go +++ b/build/context.go @@ -6,9 +6,11 @@ import ( "github.com/mmcloughlin/avo" "github.com/mmcloughlin/avo/gotypes" "github.com/mmcloughlin/avo/reg" + "golang.org/x/tools/go/packages" ) type Context struct { + pkg *packages.Package file *avo.File function *avo.Function errs []error @@ -22,6 +24,25 @@ func NewContext() *Context { } } +func (c *Context) Package(path string) { + cfg := &packages.Config{ + Mode: packages.LoadTypes, + } + pkgs, err := packages.Load(cfg, path) + if err != nil { + c.AddError(err) + return + } + pkg := pkgs[0] + if len(pkg.Errors) > 0 { + for _, err := range pkg.Errors { + c.AddError(err) + } + return + } + c.pkg = pkg +} + func (c *Context) Function(name string) { c.function = avo.NewFunction(name) c.file.Functions = append(c.file.Functions, c.function) @@ -32,7 +53,7 @@ func (c *Context) Signature(s *gotypes.Signature) { } func (c *Context) SignatureExpr(expr string) { - s, err := gotypes.ParseSignature(expr) + s, err := gotypes.ParseSignatureInPackage(c.pkg.Types, expr) if err != nil { c.AddError(err) return diff --git a/build/global.go b/build/global.go index 7307328..f89fe28 100644 --- a/build/global.go +++ b/build/global.go @@ -14,6 +14,8 @@ import ( // ctx provides a global build context. var ctx = NewContext() +func Package(path string) { ctx.Package(path) } + func TEXT(name, signature string) { ctx.Function(name) ctx.SignatureExpr(signature) diff --git a/examples/components/asm.go b/examples/components/asm.go new file mode 100644 index 0000000..5723c3f --- /dev/null +++ b/examples/components/asm.go @@ -0,0 +1,21 @@ +// +build ignore + +package main + +import ( + . "github.com/mmcloughlin/avo/build" +) + +func main() { + Package("github.com/mmcloughlin/avo/examples/components") + + // Add confirms that we correctly deduce the packing of Struct. + TEXT("Add", "func(x uint64, s Struct, y uint64) uint64") + x := Load(Param("x"), GP64v()) + y := Load(Param("y"), GP64v()) + ADDQ(x, y) + Store(y, ReturnIndex(0)) + RET() + + Generate() +} diff --git a/examples/components/components.go b/examples/components/components.go new file mode 100644 index 0000000..83a72d4 --- /dev/null +++ b/examples/components/components.go @@ -0,0 +1,20 @@ +package components + +type Struct struct { + Byte byte + Uint32 uint32 + Uint64 uint64 + Float32 float32 + Float64 float64 + String string + Slice []Sub + Array [5]Sub + Complex64 complex64 + Complex128 complex128 +} + +type Sub struct { + A uint64 + B [3]byte + C uint16 +} diff --git a/examples/components/components.s b/examples/components/components.s new file mode 100644 index 0000000..d74bbd4 --- /dev/null +++ b/examples/components/components.s @@ -0,0 +1,10 @@ + +#include "textflag.h" + +// func Add(x uint64, s Struct, y uint64) uint64 +TEXT ·Add(SB),0,$0-200 + MOVQ x(FP), AX + MOVQ y+184(FP), CX + ADDQ AX, CX + MOVQ CX, ret+192(FP) + RET diff --git a/examples/components/components_test.go b/examples/components/components_test.go new file mode 100644 index 0000000..0cdcca0 --- /dev/null +++ b/examples/components/components_test.go @@ -0,0 +1,15 @@ +package components + +import ( + "testing" + "testing/quick" +) + +//go:generate go run asm.go -out components.s -stubs stub.go + +func TestAdd(t *testing.T) { + expect := func(x uint64, s Struct, y uint64) uint64 { return x + y } + if err := quick.CheckEqual(Add, expect, nil); err != nil { + t.Fatal(err) + } +} diff --git a/examples/components/stub.go b/examples/components/stub.go new file mode 100644 index 0000000..680d107 --- /dev/null +++ b/examples/components/stub.go @@ -0,0 +1,3 @@ +package components + +func Add(x uint64, s Struct, y uint64) uint64 diff --git a/gotypes/components.go b/gotypes/components.go index 25c394f..dcdeacf 100644 --- a/gotypes/components.go +++ b/gotypes/components.go @@ -56,12 +56,13 @@ func NewComponentFromVar(v *types.Var, offset int) Component { } func (c *component) Resolve() (*Basic, error) { - if !isprimitive(c.typ) { + b := toprimitive(c.typ) + if b == nil { return nil, errors.New("component is not primitive") } return &Basic{ Addr: operand.NewParamAddr(c.name, c.offset), - Type: c.typ.(*types.Basic), + Type: b, }, nil } @@ -181,8 +182,17 @@ func complextofloat(t types.Type) types.Type { panic("bad") } -// isprimitive returns true if the type cannot be broken into components. -func isprimitive(t types.Type) bool { - b, ok := t.(*types.Basic) - return ok && (b.Info()&(types.IsString|types.IsComplex)) == 0 +// toprimitive determines whether t is primitive (cannot be reduced into +// components). If it is, it returns the basic type for t, otherwise returns +// nil. +func toprimitive(t types.Type) *types.Basic { + switch b := t.(type) { + case *types.Basic: + if (b.Info() & (types.IsString | types.IsComplex)) == 0 { + return b + } + case *types.Pointer: + return types.Typ[types.Uintptr] + } + return nil } diff --git a/gotypes/components_test.go b/gotypes/components_test.go new file mode 100644 index 0000000..4d021b9 --- /dev/null +++ b/gotypes/components_test.go @@ -0,0 +1,40 @@ +package gotypes + +import ( + "go/types" + "testing" +) + +func TestBasicKindsArePrimitive(t *testing.T) { + kinds := []types.BasicKind{ + types.Bool, + types.Int, + types.Int8, + types.Int16, + types.Int32, + types.Int64, + types.Uint, + types.Uint8, + types.Uint16, + types.Uint32, + types.Uint64, + types.Uintptr, + types.Float32, + types.Float64, + } + for _, k := range kinds { + AssertPrimitive(t, types.Typ[k]) + } +} + +func TestPointersArePrimitive(t *testing.T) { + typ := types.NewPointer(types.Typ[types.Uint32]) + AssertPrimitive(t, typ) +} + +func AssertPrimitive(t *testing.T, typ types.Type) { + c := NewComponent("primitive", typ, 0) + if _, err := c.Resolve(); err != nil { + t.Errorf("expected type %s to be primitive: got error '%s'", typ, err) + } +} diff --git a/gotypes/signature.go b/gotypes/signature.go index f6052ed..692b205 100644 --- a/gotypes/signature.go +++ b/gotypes/signature.go @@ -10,13 +10,15 @@ import ( ) type Signature struct { + pkg *types.Package sig *types.Signature params *Tuple results *Tuple } -func NewSignature(sig *types.Signature) *Signature { +func NewSignature(pkg *types.Package, sig *types.Signature) *Signature { s := &Signature{ + pkg: pkg, sig: sig, } s.init() @@ -24,11 +26,15 @@ func NewSignature(sig *types.Signature) *Signature { } func NewSignatureVoid() *Signature { - return NewSignature(types.NewSignature(nil, nil, nil, false)) + return NewSignature(nil, types.NewSignature(nil, nil, nil, false)) } func ParseSignature(expr string) (*Signature, error) { - tv, err := types.Eval(token.NewFileSet(), nil, token.NoPos, expr) + return ParseSignatureInPackage(nil, expr) +} + +func ParseSignatureInPackage(pkg *types.Package, expr string) (*Signature, error) { + tv, err := types.Eval(token.NewFileSet(), pkg, token.NoPos, expr) if err != nil { return nil, err } @@ -39,7 +45,7 @@ func ParseSignature(expr string) (*Signature, error) { if !ok { return nil, errors.New("provided type is not a function signature") } - return NewSignature(s), nil + return NewSignature(pkg, s), nil } func (s *Signature) Params() *Tuple { return s.params } @@ -50,7 +56,7 @@ func (s *Signature) Bytes() int { return s.Params().Bytes() + s.Results().Bytes( func (s *Signature) String() string { var buf bytes.Buffer - types.WriteSignature(&buf, s.sig, nil) + types.WriteSignature(&buf, s.sig, types.RelativeTo(s.pkg)) return buf.String() }