2018-11-06 21:10:54 -05:00
|
|
|
package avo
|
|
|
|
|
|
2018-12-02 12:28:33 -08:00
|
|
|
import (
|
2018-12-27 11:57:46 -08:00
|
|
|
"errors"
|
|
|
|
|
|
2018-12-08 21:16:03 -08:00
|
|
|
"github.com/mmcloughlin/avo/gotypes"
|
2018-12-02 12:28:33 -08:00
|
|
|
"github.com/mmcloughlin/avo/operand"
|
2018-12-02 22:29:30 -08:00
|
|
|
"github.com/mmcloughlin/avo/reg"
|
2018-12-02 12:28:33 -08:00
|
|
|
)
|
|
|
|
|
|
2018-11-20 11:44:44 -06:00
|
|
|
type Asm interface {
|
|
|
|
|
Asm() string
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-30 21:37:17 -08:00
|
|
|
type Node interface {
|
|
|
|
|
node()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Label string
|
|
|
|
|
|
|
|
|
|
func (l Label) node() {}
|
|
|
|
|
|
2018-11-06 21:10:54 -05:00
|
|
|
// Instruction is a single instruction in a function.
|
|
|
|
|
type Instruction struct {
|
2018-11-27 22:38:53 -08:00
|
|
|
Opcode string
|
2018-12-02 12:28:33 -08:00
|
|
|
Operands []operand.Op
|
|
|
|
|
|
2018-12-02 17:57:12 -08:00
|
|
|
Inputs []operand.Op
|
|
|
|
|
Outputs []operand.Op
|
|
|
|
|
|
2018-12-02 12:28:33 -08:00
|
|
|
IsTerminal bool
|
|
|
|
|
IsBranch bool
|
|
|
|
|
IsConditional bool
|
|
|
|
|
|
|
|
|
|
// CFG.
|
|
|
|
|
Pred []*Instruction
|
|
|
|
|
Succ []*Instruction
|
2018-12-02 23:59:29 -08:00
|
|
|
|
|
|
|
|
// LiveIn/LiveOut are sets of live register IDs pre/post execution.
|
2018-12-03 22:39:43 -08:00
|
|
|
LiveIn reg.Set
|
|
|
|
|
LiveOut reg.Set
|
2018-12-02 12:28:33 -08:00
|
|
|
}
|
|
|
|
|
|
2018-12-02 23:59:29 -08:00
|
|
|
func (i *Instruction) node() {}
|
2018-12-02 22:29:30 -08:00
|
|
|
|
2018-12-02 12:28:33 -08:00
|
|
|
func (i Instruction) TargetLabel() *Label {
|
|
|
|
|
if !i.IsBranch {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2018-12-02 13:50:55 -08:00
|
|
|
if len(i.Operands) == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2018-12-02 12:28:33 -08:00
|
|
|
if ref, ok := i.Operands[0].(operand.LabelRef); ok {
|
|
|
|
|
lbl := Label(ref)
|
|
|
|
|
return &lbl
|
|
|
|
|
}
|
|
|
|
|
return nil
|
2018-11-20 11:44:44 -06:00
|
|
|
}
|
|
|
|
|
|
2018-12-05 00:05:57 -08:00
|
|
|
func (i Instruction) Registers() []reg.Register {
|
|
|
|
|
var rs []reg.Register
|
|
|
|
|
for _, op := range i.Operands {
|
|
|
|
|
rs = append(rs, operand.Registers(op)...)
|
|
|
|
|
}
|
|
|
|
|
return rs
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-02 22:29:30 -08:00
|
|
|
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
|
|
|
|
|
}
|
2018-11-30 21:37:17 -08:00
|
|
|
|
2018-12-26 18:58:12 -08:00
|
|
|
type Section interface {
|
|
|
|
|
section()
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-06 21:10:54 -05:00
|
|
|
// File represents an assembly file.
|
|
|
|
|
type File struct {
|
2018-12-26 18:58:12 -08:00
|
|
|
Sections []Section
|
2018-11-30 20:43:31 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewFile() *File {
|
|
|
|
|
return &File{}
|
2018-11-06 21:10:54 -05:00
|
|
|
}
|
|
|
|
|
|
2018-12-27 11:57:46 -08:00
|
|
|
func (f *File) AddSection(s Section) {
|
|
|
|
|
f.Sections = append(f.Sections, s)
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-26 18:58:12 -08:00
|
|
|
func (f *File) Functions() []*Function {
|
|
|
|
|
var fns []*Function
|
|
|
|
|
for _, s := range f.Sections {
|
|
|
|
|
if fn, ok := s.(*Function); ok {
|
|
|
|
|
fns = append(fns, fn)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return fns
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-06 21:10:54 -05:00
|
|
|
// Function represents an assembly function.
|
|
|
|
|
type Function struct {
|
2018-12-30 23:35:49 -08:00
|
|
|
Name string
|
|
|
|
|
Attributes Attribute
|
|
|
|
|
Doc []string
|
|
|
|
|
Signature *gotypes.Signature
|
|
|
|
|
LocalSize int
|
2018-12-06 21:58:51 -08:00
|
|
|
|
2018-12-05 00:27:42 -08:00
|
|
|
Nodes []Node
|
2018-12-02 12:28:33 -08:00
|
|
|
|
|
|
|
|
// LabelTarget maps from label name to the following instruction.
|
|
|
|
|
LabelTarget map[Label]*Instruction
|
2018-12-05 00:05:57 -08:00
|
|
|
|
|
|
|
|
// Register allocation.
|
|
|
|
|
Allocation reg.Allocation
|
2018-11-06 21:10:54 -05:00
|
|
|
}
|
|
|
|
|
|
2018-12-27 11:57:46 -08:00
|
|
|
func (f *Function) section() {}
|
2018-12-26 18:58:12 -08:00
|
|
|
|
2018-11-06 21:10:54 -05:00
|
|
|
func NewFunction(name string) *Function {
|
|
|
|
|
return &Function{
|
2018-12-08 21:16:03 -08:00
|
|
|
Name: name,
|
|
|
|
|
Signature: gotypes.NewSignatureVoid(),
|
2018-11-06 21:10:54 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-08 21:16:03 -08:00
|
|
|
func (f *Function) SetSignature(s *gotypes.Signature) {
|
|
|
|
|
f.Signature = s
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-21 00:30:59 -08:00
|
|
|
func (f *Function) AllocLocal(size int) operand.Mem {
|
|
|
|
|
ptr := operand.NewStackAddr(f.LocalSize)
|
|
|
|
|
f.LocalSize += size
|
|
|
|
|
return ptr
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-02 12:28:33 -08:00
|
|
|
func (f *Function) AddInstruction(i *Instruction) {
|
2018-11-30 21:37:17 -08:00
|
|
|
f.AddNode(i)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f *Function) AddLabel(l Label) {
|
|
|
|
|
f.AddNode(l)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f *Function) AddNode(n Node) {
|
2018-12-02 12:28:33 -08:00
|
|
|
f.Nodes = append(f.Nodes, n)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Instructions returns just the list of instruction nodes.
|
|
|
|
|
func (f *Function) Instructions() []*Instruction {
|
|
|
|
|
var is []*Instruction
|
|
|
|
|
for _, n := range f.Nodes {
|
|
|
|
|
i, ok := n.(*Instruction)
|
|
|
|
|
if ok {
|
|
|
|
|
is = append(is, i)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return is
|
2018-11-06 21:10:54 -05:00
|
|
|
}
|
|
|
|
|
|
2018-12-08 21:16:03 -08:00
|
|
|
// Stub returns the Go function declaration.
|
|
|
|
|
func (f *Function) Stub() string {
|
|
|
|
|
return "func " + f.Name + f.Signature.String()
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-06 21:10:54 -05:00
|
|
|
// FrameBytes returns the size of the stack frame in bytes.
|
|
|
|
|
func (f *Function) FrameBytes() int {
|
2018-12-21 00:30:59 -08:00
|
|
|
return f.LocalSize
|
2018-11-06 21:10:54 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ArgumentBytes returns the size of the arguments in bytes.
|
|
|
|
|
func (f *Function) ArgumentBytes() int {
|
2018-12-08 21:16:03 -08:00
|
|
|
return f.Signature.Bytes()
|
2018-11-06 21:10:54 -05:00
|
|
|
}
|
2018-12-27 11:57:46 -08:00
|
|
|
|
|
|
|
|
type Datum struct {
|
|
|
|
|
Offset int
|
|
|
|
|
Value operand.Constant
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewDatum(offset int, v operand.Constant) Datum {
|
|
|
|
|
return Datum{
|
|
|
|
|
Offset: offset,
|
|
|
|
|
Value: v,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Interval returns the range of bytes this datum will occupy within its section.
|
|
|
|
|
func (d Datum) Interval() (int, int) {
|
|
|
|
|
return d.Offset, d.Offset + d.Value.Bytes()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (d Datum) Overlaps(other Datum) bool {
|
|
|
|
|
s, e := d.Interval()
|
|
|
|
|
so, eo := other.Interval()
|
|
|
|
|
return !(eo <= s || e <= so)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Global struct {
|
2018-12-30 23:35:49 -08:00
|
|
|
Symbol operand.Symbol
|
|
|
|
|
Attributes Attribute
|
|
|
|
|
Data []Datum
|
|
|
|
|
Size int
|
2018-12-27 11:57:46 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewGlobal(sym operand.Symbol) *Global {
|
|
|
|
|
return &Global{
|
2018-12-30 23:35:49 -08:00
|
|
|
Symbol: sym,
|
|
|
|
|
Attributes: RODATA,
|
2018-12-27 11:57:46 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewStaticGlobal(name string) *Global {
|
|
|
|
|
return NewGlobal(operand.NewStaticSymbol(name))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (g *Global) section() {}
|
|
|
|
|
|
|
|
|
|
func (g *Global) Base() operand.Mem {
|
|
|
|
|
return operand.NewDataAddr(g.Symbol, 0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (g *Global) Grow(size int) {
|
|
|
|
|
if g.Size < size {
|
|
|
|
|
g.Size = size
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (g *Global) AddDatum(d Datum) error {
|
|
|
|
|
for _, other := range g.Data {
|
|
|
|
|
if d.Overlaps(other) {
|
|
|
|
|
return errors.New("overlaps existing datum")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
g.add(d)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (g *Global) Append(v operand.Constant) {
|
|
|
|
|
g.add(Datum{
|
|
|
|
|
Offset: g.Size,
|
|
|
|
|
Value: v,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (g *Global) add(d Datum) {
|
|
|
|
|
_, end := d.Interval()
|
|
|
|
|
g.Grow(end)
|
|
|
|
|
g.Data = append(g.Data, d)
|
|
|
|
|
}
|