support signatures and param load/stores

This commit is contained in:
Michael McLoughlin
2018-12-08 21:16:03 -08:00
parent 69ee0e39cb
commit 5431f2edef
9 changed files with 115 additions and 18 deletions

18
ast.go
View File

@@ -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
@@ -126,9 +125,14 @@ type Function struct {
func NewFunction(name string) *Function {
return &Function{
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()
}

View File

@@ -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

View File

@@ -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 (

View File

@@ -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)
}

View File

@@ -6,7 +6,7 @@ import (
)
func main() {
TEXT("add")
TEXT("add", "func(x, y uint64) uint64")
ADDQ(reg.R8, reg.R11)
RET()
EOF()

View File

@@ -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] }

View File

@@ -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.

View File

@@ -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},

View File

@@ -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 {