1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
|
package operand
import (
"fmt"
"github.com/mmcloughlin/avo/reg"
)
// Op is an operand.
type Op interface {
Asm() string
}
// Symbol represents a symbol name.
type Symbol struct {
Name string
Static bool // only visible in current source file
}
// NewStaticSymbol builds a static Symbol. Static symbols are only visible in the current source file.
func NewStaticSymbol(name string) Symbol {
return Symbol{Name: name, Static: true}
}
func (s Symbol) String() string {
n := s.Name
if s.Static {
n += "<>"
}
return n
}
// Mem represents a memory reference.
type Mem struct {
Symbol Symbol
Disp int
Base reg.Register
Index reg.Register
Scale uint8
}
// NewParamAddr is a convenience to build a Mem operand pointing to a function
// parameter, which is a named offset from the frame pointer pseudo register.
func NewParamAddr(name string, offset int) Mem {
return Mem{
Symbol: Symbol{
Name: name,
Static: false,
},
Disp: offset,
Base: reg.FramePointer,
}
}
// NewStackAddr returns a memory reference relative to the stack pointer.
func NewStackAddr(offset int) Mem {
return Mem{
Disp: offset,
Base: reg.StackPointer,
}
}
// NewDataAddr returns a memory reference relative to the named data symbol.
func NewDataAddr(sym Symbol, offset int) Mem {
return Mem{
Symbol: sym,
Disp: offset,
Base: reg.StaticBase,
}
}
// Offset returns a reference to m plus idx bytes.
func (m Mem) Offset(idx int) Mem {
a := m
a.Disp += idx
return a
}
// Idx returns a new memory reference with (Index, Scale) set to (r, s).
func (m Mem) Idx(r reg.Register, s uint8) Mem {
a := m
a.Index = r
a.Scale = s
return a
}
// Asm returns an assembly syntax representation of m.
func (m Mem) Asm() string {
a := m.Symbol.String()
if a != "" {
a += fmt.Sprintf("%+d", m.Disp)
} else if m.Disp != 0 {
a += fmt.Sprintf("%d", m.Disp)
}
if m.Base != nil {
a += fmt.Sprintf("(%s)", m.Base.Asm())
}
if m.Index != nil && m.Scale != 0 {
a += fmt.Sprintf("(%s*%d)", m.Index.Asm(), m.Scale)
}
return a
}
// Rel is an offset relative to the instruction pointer.
type Rel int32
// Asm returns an assembly syntax representation of r.
func (r Rel) Asm() string {
return fmt.Sprintf(".%+d", r)
}
// LabelRef is a reference to a label.
type LabelRef string
// Asm returns an assembly syntax representation of l.
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 Constant, Rel, LabelRef:
return nil
}
panic("unknown operand type")
}
// ApplyAllocation returns an operand with allocated registers replaced. Registers missing from the allocation are left alone.
func ApplyAllocation(op Op, a reg.Allocation) Op {
switch op := op.(type) {
case reg.Register:
return a.LookupRegisterDefault(op)
case Mem:
op.Base = a.LookupRegisterDefault(op.Base)
op.Index = a.LookupRegisterDefault(op.Index)
return op
}
return op
}
|