pass: test for liveness
This commit is contained in:
@@ -6,17 +6,20 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/mmcloughlin/avo"
|
"github.com/mmcloughlin/avo"
|
||||||
|
"github.com/mmcloughlin/avo/reg"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Context struct {
|
type Context struct {
|
||||||
file *avo.File
|
file *avo.File
|
||||||
function *avo.Function
|
function *avo.Function
|
||||||
errs []error
|
errs []error
|
||||||
|
reg.Collection
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContext() *Context {
|
func NewContext() *Context {
|
||||||
return &Context{
|
return &Context{
|
||||||
file: avo.NewFile(),
|
file: avo.NewFile(),
|
||||||
|
Collection: *reg.NewCollection(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
90
pass/reg_test.go
Normal file
90
pass/reg_test.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package pass_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mmcloughlin/avo/reg"
|
||||||
|
|
||||||
|
"github.com/mmcloughlin/avo/pass"
|
||||||
|
|
||||||
|
"github.com/mmcloughlin/avo"
|
||||||
|
"github.com/mmcloughlin/avo/build"
|
||||||
|
"github.com/mmcloughlin/avo/operand"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLivenessBasic(t *testing.T) {
|
||||||
|
// Build: a = 1, b = 2, a = a+b
|
||||||
|
ctx := build.NewContext()
|
||||||
|
ctx.Function("add")
|
||||||
|
a := ctx.GP64v()
|
||||||
|
b := ctx.GP64v()
|
||||||
|
ctx.MOVQ(operand.Imm(1), a)
|
||||||
|
ctx.MOVQ(operand.Imm(2), b)
|
||||||
|
ctx.ADDQ(a, b)
|
||||||
|
|
||||||
|
AssertLiveness(t, ctx,
|
||||||
|
[][]reg.Register{
|
||||||
|
{},
|
||||||
|
{a},
|
||||||
|
{a, b},
|
||||||
|
},
|
||||||
|
[][]reg.Register{
|
||||||
|
{a},
|
||||||
|
{a, b},
|
||||||
|
{},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AssertLiveness(t *testing.T, ctx *build.Context, in, out [][]reg.Register) {
|
||||||
|
fn := ConstructLiveness(t, ctx)
|
||||||
|
is := fn.Instructions()
|
||||||
|
|
||||||
|
if len(in) != len(is) || len(out) != len(is) {
|
||||||
|
t.Fatalf("%d instructions: %d/%d in/out expectations", len(is), len(in), len(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx, i := range is {
|
||||||
|
AssertRegistersMatchSet(t, in[idx], i.LiveIn)
|
||||||
|
AssertRegistersMatchSet(t, out[idx], i.LiveOut)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AssertRegistersMatchSet(t *testing.T, rs []reg.Register, s map[reg.ID]bool) {
|
||||||
|
if len(rs) != len(s) {
|
||||||
|
t.Fatalf("size mismatch")
|
||||||
|
}
|
||||||
|
for _, r := range rs {
|
||||||
|
if _, found := s[r.ID()]; !found {
|
||||||
|
t.Fatalf("missing register ID %v", r.ID())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConstructLiveness(t *testing.T, ctx *build.Context) *avo.Function {
|
||||||
|
f, errs := ctx.Result()
|
||||||
|
if errs != nil {
|
||||||
|
for _, err := range errs {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(f.Functions) != 1 {
|
||||||
|
t.Fatalf("expect 1 function")
|
||||||
|
}
|
||||||
|
fn := f.Functions[0]
|
||||||
|
|
||||||
|
passes := []func(*avo.Function) error{
|
||||||
|
pass.LabelTarget,
|
||||||
|
pass.CFG,
|
||||||
|
pass.Liveness,
|
||||||
|
}
|
||||||
|
for _, p := range passes {
|
||||||
|
if err := p(fn); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn
|
||||||
|
}
|
||||||
33
reg/collection.go
Normal file
33
reg/collection.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package reg
|
||||||
|
|
||||||
|
type Collection struct {
|
||||||
|
vid map[Kind]VID
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCollection() *Collection {
|
||||||
|
return &Collection{
|
||||||
|
vid: map[Kind]VID{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Collection) VirtualRegister(k Kind, s Size) Virtual {
|
||||||
|
vid := c.vid[k]
|
||||||
|
c.vid[k]++
|
||||||
|
return NewVirtual(vid, k, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Collection) GP8v() Virtual { return c.GPv(B8) }
|
||||||
|
|
||||||
|
func (c *Collection) GP16v() Virtual { return c.GPv(B16) }
|
||||||
|
|
||||||
|
func (c *Collection) GP32v() Virtual { return c.GPv(B32) }
|
||||||
|
|
||||||
|
func (c *Collection) GP64v() Virtual { return c.GPv(B64) }
|
||||||
|
|
||||||
|
func (c *Collection) GPv(s Size) Virtual { return c.VirtualRegister(GP, s) }
|
||||||
|
|
||||||
|
func (c *Collection) Xv() Virtual { return c.VirtualRegister(SSEAVX, B128) }
|
||||||
|
|
||||||
|
func (c *Collection) Yv() Virtual { return c.VirtualRegister(SSEAVX, B256) }
|
||||||
|
|
||||||
|
func (c *Collection) Zv() Virtual { return c.VirtualRegister(SSEAVX, B512) }
|
||||||
14
reg/types.go
14
reg/types.go
@@ -38,11 +38,7 @@ func (f *Family) define(s Spec, id PID, name string) Physical {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *Family) Virtual(id VID, s Size) Virtual {
|
func (f *Family) Virtual(id VID, s Size) Virtual {
|
||||||
return virtual{
|
return NewVirtual(id, f.Kind, s)
|
||||||
id: id,
|
|
||||||
kind: f.Kind,
|
|
||||||
Size: s,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type private interface {
|
type private interface {
|
||||||
@@ -74,6 +70,14 @@ type virtual struct {
|
|||||||
Size
|
Size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewVirtual(id VID, k Kind, s Size) Virtual {
|
||||||
|
return virtual{
|
||||||
|
id: id,
|
||||||
|
kind: k,
|
||||||
|
Size: s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (v virtual) VirtualID() VID { return v.id }
|
func (v virtual) VirtualID() VID { return v.id }
|
||||||
func (v virtual) Kind() Kind { return v.kind }
|
func (v virtual) Kind() Kind { return v.kind }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user