From 5431f2edefefa5f2f7a601f6e042cc55f79d8b50 Mon Sep 17 00:00:00 2001 From: Michael McLoughlin Date: Sat, 8 Dec 2018 21:16:03 -0800 Subject: [PATCH] support signatures and param load/stores --- ast.go | 20 ++++++++++++++------ build/context.go | 25 ++++++++++++++++++++----- build/global.go | 6 +++++- build/pseudo.go | 30 ++++++++++++++++++++++++++++-- examples/add/asm.go | 2 +- gotypes/signature.go | 22 ++++++++++++++++++++-- operand/checks.go | 12 +++++++++++- operand/checks_test.go | 15 +++++++++++++++ printer.go | 1 + 9 files changed, 115 insertions(+), 18 deletions(-) diff --git a/ast.go b/ast.go index 9ffabac..cba96bb 100644 --- a/ast.go +++ b/ast.go @@ -1,8 +1,7 @@ package avo import ( - "go/types" - + "github.com/mmcloughlin/avo/gotypes" "github.com/mmcloughlin/avo/operand" "github.com/mmcloughlin/avo/reg" ) @@ -112,7 +111,7 @@ func NewFile() *File { // Function represents an assembly function. type Function struct { Name string - Signature *types.Signature + Signature *gotypes.Signature Nodes []Node @@ -125,10 +124,15 @@ type Function struct { func NewFunction(name string) *Function { return &Function{ - Name: name, + Name: name, + Signature: gotypes.NewSignatureVoid(), } } +func (f *Function) SetSignature(s *gotypes.Signature) { + f.Signature = s +} + func (f *Function) AddInstruction(i *Instruction) { f.AddNode(i) } @@ -153,6 +157,11 @@ func (f *Function) Instructions() []*Instruction { return is } +// Stub returns the Go function declaration. +func (f *Function) Stub() string { + return "func " + f.Name + f.Signature.String() +} + // FrameBytes returns the size of the stack frame in bytes. func (f *Function) FrameBytes() int { // TODO(mbm): implement Function.FrameBytes() @@ -161,6 +170,5 @@ func (f *Function) FrameBytes() int { // ArgumentBytes returns the size of the arguments in bytes. func (f *Function) ArgumentBytes() int { - // TODO(mbm): implement Function.ArgumentBytes() - return 0 + return f.Signature.Bytes() } diff --git a/build/context.go b/build/context.go index ea86117..9b1212b 100644 --- a/build/context.go +++ b/build/context.go @@ -5,6 +5,8 @@ import ( "io" "log" + "github.com/mmcloughlin/avo/gotypes" + "github.com/mmcloughlin/avo" "github.com/mmcloughlin/avo/reg" ) @@ -28,20 +30,33 @@ func (c *Context) Function(name string) { c.file.Functions = append(c.file.Functions, c.function) } +func (c *Context) Signature(s *gotypes.Signature) { + c.activefunc().SetSignature(s) +} + +func (c *Context) SignatureExpr(expr string) { + s, err := gotypes.ParseSignature(expr) + if err != nil { + c.AddError(err) + return + } + c.Signature(s) +} + func (c *Context) Instruction(i *avo.Instruction) { - c.node(i) + c.activefunc().AddNode(i) } func (c *Context) Label(l avo.Label) { - c.node(l) + c.activefunc().AddLabel(l) } -func (c *Context) node(n avo.Node) { +func (c *Context) activefunc() *avo.Function { if c.function == nil { c.AddErrorMessage("no active function") - return + return avo.NewFunction("") } - c.function.AddNode(n) + return c.function } //go:generate avogen -output zinstructions.go build diff --git a/build/global.go b/build/global.go index ebfda3c..317b5bc 100644 --- a/build/global.go +++ b/build/global.go @@ -12,7 +12,11 @@ import ( // ctx provides a global build context. var ctx = NewContext() -func TEXT(name string) { ctx.Function(name) } +func TEXT(name, signature string) { + ctx.Function(name) + ctx.SignatureExpr(signature) +} + func LABEL(name string) { ctx.Label(avo.Label(name)) } var ( diff --git a/build/pseudo.go b/build/pseudo.go index 1f708f6..d5ed6e7 100644 --- a/build/pseudo.go +++ b/build/pseudo.go @@ -8,11 +8,37 @@ import ( //go:generate avogen -output zmov.go mov -func (c *Context) Load(src gotypes.Component, dst reg.Register) { +func (c *Context) Param(name string) gotypes.Component { + return c.activefunc().Signature.Params().Lookup(name) +} + +func (c *Context) ParamIndex(i int) gotypes.Component { + return c.activefunc().Signature.Params().At(i) +} + +func (c *Context) Return(name string) gotypes.Component { + return c.activefunc().Signature.Results().Lookup(name) +} + +func (c *Context) ReturnIndex(i int) gotypes.Component { + return c.activefunc().Signature.Results().At(i) +} + +func (c *Context) Load(src gotypes.Component, dst reg.Register) reg.Register { b, err := src.Resolve() + if err != nil { + c.AddError(err) + return dst + } + c.mov(b.Addr, dst, int(gotypes.Sizes.Sizeof(b.Type)), int(dst.Bytes()), b.Type) + return dst +} + +func (c *Context) Store(src reg.Register, dst gotypes.Component) { + b, err := dst.Resolve() if err != nil { c.AddError(err) return } - c.mov(b.Addr, dst, int(gotypes.Sizes.Sizeof(b.Type)), int(dst.Bytes()), b.Type) + c.mov(src, b.Addr, int(src.Bytes()), int(gotypes.Sizes.Sizeof(b.Type)), b.Type) } diff --git a/examples/add/asm.go b/examples/add/asm.go index 4c37ddf..ff83217 100644 --- a/examples/add/asm.go +++ b/examples/add/asm.go @@ -6,7 +6,7 @@ import ( ) func main() { - TEXT("add") + TEXT("add", "func(x, y uint64) uint64") ADDQ(reg.R8, reg.R11) RET() EOF() diff --git a/gotypes/signature.go b/gotypes/signature.go index f9210b2..f6052ed 100644 --- a/gotypes/signature.go +++ b/gotypes/signature.go @@ -1,7 +1,9 @@ package gotypes import ( + "bytes" "errors" + "fmt" "go/token" "go/types" "strconv" @@ -21,6 +23,10 @@ func NewSignature(sig *types.Signature) *Signature { return s } +func NewSignatureVoid() *Signature { + return NewSignature(types.NewSignature(nil, nil, nil, false)) +} + func ParseSignature(expr string) (*Signature, error) { tv, err := types.Eval(token.NewFileSet(), nil, token.NoPos, expr) if err != nil { @@ -42,6 +48,12 @@ func (s *Signature) Results() *Tuple { return s.results } 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) + return buf.String() +} + func (s *Signature) init() { p := s.sig.Params() r := s.sig.Results() @@ -66,7 +78,7 @@ type Tuple struct { func newTuple(t *types.Tuple, offsets []int64, defaultprefix string) *Tuple { tuple := &Tuple{ byname: map[string]Component{}, - size: int(offsets[t.Len()]), + size: int(offsets[t.Len()] - offsets[0]), } for i := 0; i < t.Len(); i++ { v := t.At(i) @@ -86,7 +98,13 @@ func newTuple(t *types.Tuple, offsets []int64, defaultprefix string) *Tuple { return tuple } -func (t *Tuple) Lookup(name string) Component { return t.byname[name] } +func (t *Tuple) Lookup(name string) Component { + e := t.byname[name] + if e == nil { + return componenterr(fmt.Sprintf("unknown variable \"%s\"", name)) + } + return e +} func (t *Tuple) At(i int) Component { return t.components[i] } diff --git a/operand/checks.go b/operand/checks.go index 4aeb1b9..aa56c45 100644 --- a/operand/checks.go +++ b/operand/checks.go @@ -109,6 +109,11 @@ func IsR64(op Op) bool { return IsGP(op, 8) } +// IsPseudo returns true if op is a pseudo register. +func IsPseudo(op Op) bool { + return IsRegisterKindSize(op, reg.Internal, 0) +} + // IsGP returns true if op is a general-purpose register of size n bytes. func IsGP(op Op, n uint) bool { return IsRegisterKindSize(op, reg.GP, n) @@ -171,7 +176,12 @@ func IsM64(op Op) bool { func IsMSize(op Op, n uint) bool { // TODO(mbm): should memory operands have a size attribute as well? m, ok := op.(Mem) - return ok && IsGP(m.Base, n) && (m.Index == nil || IsGP(m.Index, n)) + return ok && IsMRegSize(m.Base, n) && (m.Index == nil || IsMRegSize(m.Index, n)) +} + +// IsMRegSize returns true if op is a register that can be used in a memory operand of size n bytes. +func IsMRegSize(op Op, n uint) bool { + return IsPseudo(op) || IsGP(op, n) } // IsM128 returns true if op is a 128-bit memory operand. diff --git a/operand/checks_test.go b/operand/checks_test.go index fb8ec09..30aa881 100644 --- a/operand/checks_test.go +++ b/operand/checks_test.go @@ -85,6 +85,14 @@ func TestChecks(t *testing.T) { {IsYmm, reg.X3, false}, {IsYmm, reg.Z3, false}, + // Pseudo registers. + {IsPseudo, reg.FramePointer, true}, + {IsPseudo, reg.ProgramCounter, true}, + {IsPseudo, reg.StaticBase, true}, + {IsPseudo, reg.StackPointer, true}, + {IsPseudo, reg.ECX, false}, + {IsPseudo, reg.X9, false}, + // Memory operands {IsM, Mem{Base: reg.CX}, true}, {IsM, Mem{Base: reg.ECX}, true}, @@ -113,6 +121,13 @@ func TestChecks(t *testing.T) { {IsM256, Mem{Base: reg.RBX, Index: reg.R12, Scale: 2}, true}, {IsM256, Mem{Base: reg.R13L}, false}, + // Argument references (special cases of memory operands) + {IsM, NewParamAddr("foo", 4), true}, + {IsM8, NewParamAddr("foo", 4), true}, + {IsM16, NewParamAddr("foo", 4), true}, + {IsM32, NewParamAddr("foo", 4), true}, + {IsM64, NewParamAddr("foo", 4), true}, + // Vector memory operands {IsVm32x, Mem{Base: reg.R14, Index: reg.X11}, true}, {IsVm32x, Mem{Base: reg.R14L, Index: reg.X11}, false}, diff --git a/printer.go b/printer.go index 4090bb4..3ba5743 100644 --- a/printer.go +++ b/printer.go @@ -68,6 +68,7 @@ func (p *GoPrinter) multicomment(lines []string) { } func (p *GoPrinter) function(f *Function) { + p.printf("// %s\n", f.Stub()) p.printf("TEXT %s%s(SB),0,$%d-%d\n", dot, f.Name, f.FrameBytes(), f.ArgumentBytes()) for _, node := range f.Nodes {