summaryrefslogtreecommitdiff
path: root/vendor/github.com/mmcloughlin/avo/operand/types.go
blob: 878425ec1de9e7e3e34a1e7c774e125412da2bd4 (plain)
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
}