/
vmstate.py
204 lines (162 loc) · 6.28 KB
/
vmstate.py
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
from rpython.rlib import jit, debug, unroll
from util import bail
NUM_REGS = 8
REG_NAMES = [ "R%d" % x for x in range(NUM_REGS) ]
STACKSIZE = 1024
# each arg is one green var
def get_printable_location(pc, pgm):
# RPython can not call __str__
return "%d:%s" % (pc, pgm.instrs[pc].str())
# The union of the red and green vars must include the live variables
# at the jit merge point.
#
# We specialise the interpreter upon the values of the green variables.
# Red variables, on the other hand, will stay variables in the trace.
jd = jit.JitDriver(
greens = [ "pc", "self" ], # specialise.
reds = [], # don't specialise
get_printable_location = get_printable_location,
)
# -- Instructions
class Instr:
# immutable fields are, obviously, not changing after their definition.
# The JIT can avoid superfluous lookups/reads out of constant objects.
#
# field[*] means that the contents of the list (which is a field) are
# themselves constant.
#
# field?, means that the field is very likely to be immutable. When such
# a field changes, an expensive invalidation of asm code occurs.
_immutable_fields_ = ["handler", "operands[*]"]
def __init__(self, handler, name, opers):
self.handler = handler
self.name = name
self.operands = opers
def execute(self, program):
self.handler(self.operands, program)
def __str__(self):
return self.str()
def str(self):
# rpython cannot call __str__ for example in get_printable_location()
arg_str = ", ".join([ x.str() for x in self.operands ])
return ("%s %s" % (self.name, arg_str))
# -- Operands
class Operand(object):
def set(self, program, v):
raise TypeError("Cannot set %s to a %s" % (self, v))
def __str__(self):
return self.str()
class RegOperand(Operand):
_immutable_fields_ = ["register"]
def __init__(self, v):
self.register = v
def str(self): return "reg(%s)" % self.register
def evaluate(self, program): return program.get_reg(self.register)
def set(self, program, v): program.set_reg(self.register, v)
class LabelOperand(Operand):
_immutable_fields_ = ["label"]
def __init__(self, v):
self.label = v
def str(self): return "label(%s)" % self.label
def dispatch(self, program): program.set_pc(program.get_label(self.label))
class ConstOperand(Operand):
_immutable_fields_ = ["value"]
def __init__(self, v):
self.value = v
def str(self): return "const(%s)" % self.value
def evaluate(self, program): return self.value
# This is a metaprogramming construct used below to unroll loops
# read on...
unrolling_reg_range = unroll.unrolling_iterable(range(NUM_REGS))
class VMState(object):
_immutable_fields_ = ["instrs[*]", "label_map"]
def __init__(self, instrs, labels):
self.instrs = instrs
self.label_map = labels
self.stack = None
# unrolls the loop via metaprogramming.
# We resist the temptation to use a list for registers, as
# rpython must consider the fact that mutating the list may
# affect multiple registers (and other lists of the same type).
# If the regsiters are separate
# fields, then they are absolutely independent of each other and
# of other lists of the same type.
for i in unrolling_reg_range:
setattr(self, "r%s" % i, 0)
def init_stack(self, initstack):
# The stack is a constant size list. This means that rpython
# does not need to check if re-allocation/downsizing should
# occur.
self.stack = initstack + [0] * (STACKSIZE - len(initstack))
# The next line is just an assertion of the above comment.
debug.make_sure_not_resized(self.stack)
self.sp = len(initstack)
def pop(self):
if self.sp == 0:
bail("stack underflow")
self.sp -= 1
result = self.stack[self.sp]
#self.stack[self.sp] = 0 # XXX
return result
def push(self, x):
if self.sp >= STACKSIZE:
bail("stack overflow")
self.stack[self.sp] = x
self.sp += 1
def pick(self, x):
index = self.sp - x - 1
if not (0 <= index < self.sp):
bail("stack underflow")
return self.stack[index]
def advance_pc(self): self.r0 += 1
def set_pc(self, x): self.r0 = x
def get_pc(self): return self.r0
def set_reg(self, r, v):
for i in unrolling_reg_range:
if r == i:
setattr(self, "r%s" % i, v)
break
else:
bail("unknown register %s" % r)
def get_reg(self, x):
for i in unrolling_reg_range:
if x == i:
return getattr(self, "r%s" % i)
bail("unknown register %s" % x)
def get_stack(self):
sp = self.sp # asserting upon a field does not help type inference, use local
assert(sp >= 0)
return self.stack[:sp]
def get_label(self, x):
label = self._get_label(x)
if label == -1:
bail("undefined label: %s" % x)
return label
# An elidable function is one which behaves deterministically. The elidable
# hint suggests that a method is elidable, even if it depends upon
# object state. The JIT can take advantage of this.
@jit.elidable
def _get_label(self, key): return self.label_map.get(key, -1)
def get_regs(self):
rs = []
for i in unrolling_reg_range:
rs.append(getattr(self, "r%s" % i))
return rs
def run(self, initstack):
self.init_stack(initstack)
pc = self.r0 # green fields may not be fields
# main interpreter loop
while True:
# This is the reference point for the JIT to specialise a
# trace. For example here, if the pc is the same as a pc
# value seen before, then we can assume the same happens as before.
jd.jit_merge_point(pc=pc, self=self)
# fetch the instr
if pc >= len(self.instrs): break # end program
instr = self.instrs[pc]
instr.execute(self)
pc = self.r0
return self.get_regs()
def dump_vm_state(self):
print("stack: " + str(self.get_stack()))
print("regs: " + str(self.get_regs()))