support signatures and param load/stores
This commit is contained in:
18
ast.go
18
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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
TEXT("add")
|
||||
TEXT("add", "func(x, y uint64) uint64")
|
||||
ADDQ(reg.R8, reg.R11)
|
||||
RET()
|
||||
EOF()
|
||||
|
||||
@@ -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] }
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user