diff --git a/ast.go b/ast.go index 9eab21a..62f1e74 100644 --- a/ast.go +++ b/ast.go @@ -2,6 +2,7 @@ package avo import ( "github.com/mmcloughlin/avo/operand" + "github.com/mmcloughlin/avo/reg" ) type Asm interface { @@ -46,6 +47,8 @@ type Instruction struct { Succ []*Instruction } +func (i Instruction) node() {} + func (i Instruction) TargetLabel() *Label { if !i.IsBranch { return nil @@ -60,7 +63,28 @@ func (i Instruction) TargetLabel() *Label { return nil } -func (i Instruction) node() {} +func (i Instruction) InputRegisters() []reg.Register { + var rs []reg.Register + for _, op := range i.Inputs { + rs = append(rs, operand.Registers(op)...) + } + for _, op := range i.Outputs { + if operand.IsMem(op) { + rs = append(rs, operand.Registers(op)...) + } + } + return rs +} + +func (i Instruction) OutputRegisters() []reg.Register { + var rs []reg.Register + for _, op := range i.Outputs { + if r, ok := op.(reg.Register); ok { + rs = append(rs, r) + } + } + return rs +} // File represents an assembly file. type File struct { diff --git a/operand/checks.go b/operand/checks.go index 2d4e98f..4aeb1b9 100644 --- a/operand/checks.go +++ b/operand/checks.go @@ -6,6 +6,22 @@ import ( "github.com/mmcloughlin/avo/reg" ) +// Pure type assertion checks: + +// IsRegister returns whether op has type reg.Register. +func IsRegister(op Op) bool { _, ok := op.(reg.Register); return ok } + +// IsMem returns whether op has type Mem. +func IsMem(op Op) bool { _, ok := op.(Mem); return ok } + +// IsImm returns whether op has type Imm. +func IsImm(op Op) bool { _, ok := op.(Imm); return ok } + +// IsRel returns whether op has type Rel. +func IsRel(op Op) bool { _, ok := op.(Rel); return ok } + +// Checks corresponding to specific operand types in the Intel Manual: + // Is1 returns true if op is the immediate constant 1. func Is1(op Op) bool { i, ok := op.(Imm) diff --git a/operand/types.go b/operand/types.go index 3445277..f4b4677 100644 --- a/operand/types.go +++ b/operand/types.go @@ -12,8 +12,8 @@ type Op interface { type Mem struct { Disp int - Base reg.Physical - Index reg.Physical + Base reg.Register + Index reg.Register Scale uint8 } @@ -50,3 +50,23 @@ type LabelRef string func (l LabelRef) Asm() string { return string(l) } + +// Registers returns the list of all operands involved in the given operand. +func Registers(op Op) []reg.Register { + switch op := op.(type) { + case reg.Register: + return []reg.Register{op} + case Mem: + var r []reg.Register + if op.Base != nil { + r = append(r, op.Base) + } + if op.Index != nil { + r = append(r, op.Index) + } + return r + case Imm, Rel, LabelRef: + return nil + } + panic("unknown operand type") +} diff --git a/operand/types_test.go b/operand/types_test.go index 2a839e0..e3e13d7 100644 --- a/operand/types_test.go +++ b/operand/types_test.go @@ -1,6 +1,7 @@ package operand import ( + "reflect" "testing" "github.com/mmcloughlin/avo/reg" @@ -26,3 +27,23 @@ func TestMemAsm(t *testing.T) { } } } + +func TestRegisters(t *testing.T) { + cases := []struct { + Op Op + Expect []reg.Register + }{ + {reg.R11, []reg.Register{reg.R11}}, + {Mem{Base: reg.EAX}, []reg.Register{reg.EAX}}, + {Mem{Base: reg.RBX, Index: reg.R10}, []reg.Register{reg.RBX, reg.R10}}, + {Imm(42), nil}, + {Rel(42), nil}, + {LabelRef("idk"), nil}, + } + for _, c := range cases { + got := Registers(c.Op) + if !reflect.DeepEqual(got, c.Expect) { + t.Errorf("Registers(%#v) = %#v expected %#v", c.Op, got, c.Expect) + } + } +}