1
"""Register allocation.
6
from pypy.rlib.objectmodel import we_are_translated
7
from pypy.rpython.lltypesystem import lltype
8
from pypy.jit.codegen.i386.operation import *
14
INITIAL_STACK_EBP_OFS = -1
15
stack_op_cache = StackOpCache()
16
stack_op_cache.lst = []
19
"Return the mem operand that designates the nth stack-spilled location"
21
lst = stack_op_cache.lst
23
ofs = WORD * (StackOpCache.INITIAL_STACK_EBP_OFS - len(lst))
24
lst.append(mem(ebp, ofs))
27
def stack_n_from_op(op):
28
ofs = op.ofs_relative_to_ebp()
29
return StackOpCache.INITIAL_STACK_EBP_OFS - ofs / WORD
31
def write_stack_reserve(mc, stackn):
33
mc.SUB(esp, IMM32(WORD * stackn)) # always encode offset on 32 bits
36
def write_stack_adj(mc, stackn):
38
# always encode offset on 32 bits
39
mc.LEA(esp, fixedsize_ebp_ofs(-WORD * stackn))
43
class RegAllocator(object):
45
def __init__(self, operations):
46
self.operations = operations
47
self.operationindex = len(operations)
48
self.lifetime = {} # {variable: operation_index}
49
self.suggested_location = {} # {variable: location}
50
self.var2loc = {gv_frame_base: ebp}
54
def set_final(self, final_vars_gv, locations=None):
55
for i in range(len(final_vars_gv)):
58
if locations is not None:
59
self.suggested_location[v] = locations[i]
61
def compute_lifetimes(self):
62
for i in range(len(self.operations)-1, -1, -1):
63
self.operationindex = i
64
op = self.operations[i]
65
if not op.side_effects and op not in self.lifetime:
66
self.operations[i] = dead_operation # operation not used
68
op.mark_used_vars(self)
71
if v.is_const or v in self.lifetime:
74
self.lifetime[v] = self.operationindex
75
return True # variable is dying here
77
def using_inplace(self, v, vtarget):
79
# this operation 'vtarget' can modify its argument 'v'
80
# in-place, and 'v' is not alive after the operation.
81
# Propagate the suggested location for 'vtarget' backwards to 'v'.
83
self.suggested_location[v] = self.suggested_location[vtarget]
84
return True # got a suggestion
87
return False # got no suggestion
89
def suggests(self, v, loc):
90
self.suggested_location[v] = loc
93
return self.lifetime.keys()
97
AVAILABLE_REGS = (eax.bitmask |
104
def init_reg_alloc(self, inputvars_gv, inputlocations):
105
self.registers_free = self.AVAILABLE_REGS # bitmask
106
self.cc_used_by = None
107
self.stack_op_used = {}
110
self.vars_in_use = {} # {variable: dying_operation_index}
111
self.operationindex = 0
112
self.inputvars_gv = inputvars_gv
113
self.inputlocations = inputlocations
115
def force_loc_used(self, v, loc):
116
ok = self.consume_loc(v, loc)
119
def consume_loc(self, v, loc):
120
if isinstance(loc, MODRM):
121
if loc not in self.stack_op_used:
122
self.stack_op_used[loc] = None
123
n = stack_n_from_op(loc)
124
if n >= self.nstackmax:
125
self.nstackmax = n + 1
127
elif isinstance(loc, REG):
128
if self.registers_free & loc.bitmask:
129
self.registers_free &= ~loc.bitmask
131
elif isinstance(loc, CCFLAG):
132
if self.cc_used_by is None:
137
def _no_longer_in_use(self, v):
138
del self.vars_in_use[v]
139
loc = self.var2loc[v]
140
if isinstance(loc, CCFLAG):
141
assert self.cc_used_by is v
142
self._mark_loc_as_free(loc)
144
def _mark_loc_as_free(self, loc):
145
if isinstance(loc, MODRM):
146
del self.stack_op_used[loc]
147
elif isinstance(loc, REG):
148
self.registers_free |= loc.bitmask
149
elif isinstance(loc, CCFLAG):
150
self.cc_used_by = None
152
def generate_operations(self, mc):
153
if not we_are_translated():
156
# reserve locations for the inputvars
157
for i in range(len(self.inputvars_gv)):
158
v = self.inputvars_gv[i]
159
if v in self.lifetime: # else: input argument is not used
160
loc = self.inputlocations[i]
161
if v in self.var2loc: # duplicate inputvars_gv, which is ok
162
assert self.var2loc[v] == loc
164
self.var2loc[v] = loc
165
self.vars_in_use[v] = self.lifetime[v]
166
self.force_loc_used(v, loc)
167
if not we_are_translated():
168
print 'in %20s: %s' % (loc, short(v))
172
# Generate all operations.
173
# Actual registers or stack locations are allocated as we go.
174
for i in range(len(self.operations)):
175
self.registers_pinned = 0 # bitmask
176
op = self.operations[i]
181
if not we_are_translated():
183
self.operationindex = i + 1
185
def _showprogress(self):
189
self.using = self.lst.append
190
def using_inplace(self, v, _):
192
def suggests(self, v, loc):
195
i = self.operationindex
196
op = self.operations[i]
197
op.mark_used_vars(col)
198
args = [short(v) for v in col.lst]
199
args = ', '.join(args)
200
print ' | %20s: %s (%s)' % (self.var2loc.get(op, ''),
202
for v, endtime in self.vars_in_use.items():
206
def _use_another_stack_loc(self):
207
for i in range(self.nstackidx, self.nstackmax):
209
if loc not in self.stack_op_used:
210
self.nstackidx = i + 1
213
for i in range(self.nstackidx):
215
if loc not in self.stack_op_used:
216
self.nstackidx = i + 1
219
i = self.nstackidx = self.nstackmax
220
self.nstackmax = i + 1
222
assert loc not in self.stack_op_used
223
self.stack_op_used[loc] = None
226
def reserve_extra_stack(self, extra):
231
while max > base and stack_op(max-1) not in self.stack_op_used:
233
self.nstackmax = max + extra
235
def get_operand(self, v):
237
return imm(v.revealconst(lltype.Signed))
239
return self.var2loc[v]
241
def grab_operand(self, v):
242
"""Like get_operand() but if the result is in a register, it won't
243
be spilled before the end of the current instruction."""
244
loc = self.get_operand(v)
245
if isinstance(loc, REG):
246
self.registers_pinned |= loc.bitmask
249
def _use_next_modrm(self, v, regnum_must_be_before=8):
250
"""Select the next mod/rm location to use for the new operation 'v'.
251
If 'v' is None, this will always return a register; else it might
252
decide to immediately create 'v' in a stack location.
254
#print self.registers_free
255
if self.registers_free:
256
for i in range(regnum_must_be_before-1, -1, -1):
257
if self.registers_free & (1 << i):
258
self.registers_free &= ~ (1 << i)
260
# spill the register holding the variable that has the longest
261
# time remaining to live (it may be our 'v' itself)
263
dyinglimit = self.operationindex # must pick vars dying after that
266
dyinglimit = self.lifetime[v]
267
spillvar = v # initial guess, can be overridden in the loop below
269
for v1, dying in self.vars_in_use.iteritems():
270
if dying > dyinglimit:
271
loc = self.var2loc[v1]
272
if not isinstance(loc, REG):
274
if loc.op >= regnum_must_be_before:
275
continue # never reached if regnum_must_be_before == 8
276
if loc.bitmask & self.registers_pinned:
277
continue # don't spill this register
282
raise OutOfRegistersError
283
#print 'time span of %s: now is %d, lives until %d' % (
284
# v, self.operationindex, self.lifetime[v])
286
return self._use_another_stack_loc()
288
assert regloc is not None
289
self._spill(spillvar, regloc)
292
def _spill(self, spillvar, oldloc):
293
spillloc = self._use_another_stack_loc()
294
if not we_are_translated():
295
print ' # %20s: SPILL %s' % (spillloc, oldloc)
296
self.mc.MOV(spillloc, oldloc)
297
self.var2loc[spillvar] = spillloc
300
def _use_next_reg(self):
301
return self._use_next_modrm(None)
303
def _use_next_reg_abcd(self):
304
return self._use_next_modrm(None, regnum_must_be_before=4)
306
def _created(self, v, loc):
307
assert v not in self.var2loc
308
assert loc is not None
309
self.vars_in_use[v] = ltime = self.lifetime[v]
310
assert ltime > self.operationindex
311
self.var2loc[v] = loc
312
if isinstance(loc, REG):
313
self.registers_pinned |= loc.bitmask
315
def release(self, v):
316
"""Stop using argument 'v'. Must be called for each used argument.
317
Warning: if an operation uses v1 and v2, and if v1 could be equal to
318
v2, then calling release(v1) will also immediately release(v2)...
319
The best is to call release() on all operands at once.
321
ok = self.lastuse(v) and v in self.vars_in_use
323
self._no_longer_in_use(v)
326
def lastuse(self, v):
327
"""Is this the last time the argument 'v' is used?"""
331
endtime = self.lifetime[v]
332
assert endtime >= self.operationindex
333
return endtime == self.operationindex
335
def create(self, v, suggested_loc=None):
336
"""Create the result of the operation 'v', possibly at the
337
suggested location. CAN SPILL ONE REGISTER."""
338
if suggested_loc is not None and self.consume_loc(v, suggested_loc):
339
self._created(v, suggested_loc)
341
suggested_loc = self.suggested_location.get(v, None)
342
if suggested_loc is not None and self.consume_loc(v, suggested_loc):
343
self._created(v, suggested_loc)
345
loc = self._use_next_modrm(v)
346
self._created(v, loc)
349
def create_reg(self, v, srcloc=None):
350
"""Create the result of the operation 'v' in any register
351
currently available. CAN SPILL ONE REGISTER."""
352
suggested_loc = self.suggested_location.get(v, None)
353
if (isinstance(suggested_loc, REG) and
354
self.consume_loc(v, suggested_loc)):
357
loc = self._use_next_reg()
358
self._created(v, loc)
359
if srcloc is not None and loc is not srcloc:
360
# if srcop was spilled, srcloc still contains its value right now
361
# and then it's possible that srcop == dstop (no MOV needed then)
362
self.mc.MOV(loc, srcloc)
365
def create_exactly_at(self, v, loc):
366
"""Create the result of the operation 'v' at 'loc'."""
367
ok = self.consume_loc(v, loc)
369
self._created(v, loc)
371
def create_in_cc(self, v, ccloc):
372
"""Create the result of the operation 'v' in the given cc flags.
373
Doesn't move stuff around."""
374
assert self.cc_used_by is None
375
self._created(v, ccloc)
378
def create_scratch_reg(self, srcloc=None):
379
"""Return a scratch register for the current operation.
380
Warning, this might be the same register as one of the input args.
381
CAN SPILL ONE REGISTER. You must eventually call end_clobber()."""
382
reg = self._use_next_reg()
383
if srcloc is not None and reg is not srcloc:
384
self.mc.MOV(reg, srcloc)
387
def create_scratch_reg8(self, srcloc=None):
388
reg32 = self._use_next_reg_abcd()
389
reg8 = reg32.lowest8bits()
390
if srcloc is not None and reg8 is not srcloc and reg32 is not srcloc:
391
if srcloc.width == 1:
392
self.mc.MOV(reg8, srcloc)
394
self.mc.MOV(reg32, srcloc)
397
def operation_result_is_used(self, v):
398
return v in self.lifetime
400
def clobber(self, reg):
401
"""Clobbers a register, i.e. move away a value that would be there.
402
It might go to a different register or to the stack.
403
You must eventually call end_clobber()."""
404
assert isinstance(reg, REG)
405
if not self.registers_free & reg.bitmask:
406
assert not (reg.bitmask & self.registers_pinned)
407
for v1 in self.vars_in_use:
408
if self.var2loc[v1] == reg:
411
assert self.registers_free & reg.bitmask
412
self.registers_free &= ~reg.bitmask
414
def clobber2(self, reg1, reg2):
415
"""Clobbers two registers. Unlike two individual clobber() calls,
416
where the first call might overwrite the other reg, this one
417
preserves the current content of both 'reg1' and 'reg2'.
418
You must eventually call end_clobber() twice."""
419
if not self.registers_free & reg2.bitmask:
420
# order trick: if reg2 is free but reg1 used, doing clobber() in
421
# the following order could first move reg1 to reg2, and then
422
# immediately away from reg2.
423
self.clobber(reg1) # <- here reg1 cannot go to reg2
426
self.clobber(reg2) # reg2 is free, so it doesn't go anywhere
429
def clobber3(self, reg1, reg2, reg3):
430
if not self.registers_free & reg3.bitmask:
431
self.clobber2(reg1, reg2) # they cannot go to reg3
434
self.clobber(reg3) # free, so doesn't go anywhere
435
self.clobber2(reg1, reg2)
437
def end_clobber(self, reg):
438
if isinstance(reg, REG):
439
bitmask = reg.bitmask
441
assert isinstance(reg, REG8)
443
bitmask = reg.bitmask
444
self.registers_free |= bitmask
446
def clobber_cc(self):
449
self.cc_used_by = None
450
# pick a newloc that is either one of [eax, ecx, edx, ebx]
451
# or a stack location
452
oldloc = self.var2loc[v]
453
newloc = self._use_next_modrm(v, regnum_must_be_before=4)
454
if not we_are_translated():
455
print ' # %20s: MOVE AWAY FROM %s' % (newloc, oldloc)
456
assert isinstance(oldloc, CCFLAG)
458
newloc8 = newloc.lowest8bits()
459
if isinstance(newloc, REG):
460
oldloc.SETCOND(mc, newloc8)
461
mc.MOVZX(newloc, newloc8)
463
mc.MOV(newloc, imm8(0))
464
oldloc.SETCOND(mc, newloc8)
465
self._mark_loc_as_free(oldloc)
466
self.var2loc[v] = newloc
469
"""Temporarily prevent 'loc' from being overwritten by the
470
functions marked as 'moves stuff around'. Return True if the
471
lock is sucessful, False if the location was not free in the
473
return self.consume_loc(None, loc)
475
def unlock(self, loc):
476
"""Call sometime after a lock() that returned True."""
477
self._mark_loc_as_free(loc)
479
def _move_away(self, v):
480
# move 'v' away, into a newly allocated register or stack location,
481
# possibly spilling another register
482
oldloc = self.var2loc[v]
483
newloc = self._use_next_modrm(v)
484
if not we_are_translated():
485
print ' # %20s: MOVE AWAY FROM %s' % (newloc, oldloc)
486
self.mc.MOV(newloc, oldloc)
487
self._mark_loc_as_free(oldloc)
488
self.var2loc[v] = newloc
492
if not we_are_translated():
493
def unpackbitmask(x):
494
return dict.fromkeys([r for r in registers if x & r.bitmask])
495
rf = unpackbitmask(self.AVAILABLE_REGS)
497
for v in self.vars_in_use:
498
loc = self.var2loc[v]
499
assert loc not in locs_seen
501
if isinstance(loc, REG):
503
assert unpackbitmask(self.registers_free) == rf
507
def generate_final_moves(self, final_vars_gv, locations):
508
# XXX naive algo for now
510
for i in range(len(final_vars_gv)):
513
srcloc = self.var2loc[v]
514
dstloc = locations[i]
516
if not we_are_translated():
517
print ' > %20s--->->->---%s' % (srcloc, dstloc)
518
if isinstance(srcloc, CCFLAG):
519
self.mc.PUSH(imm8(0))
520
srcloc.SETCOND(self.mc, mem8(esp))
527
for i in range(len(final_vars_gv)):
530
dstloc = locations[i]
531
self.mc.MOV(dstloc, imm(v.revealconst(lltype.Signed)))
534
class OutOfRegistersError(Exception):
537
def short(op, memo={}):
538
key = op.__class__.__name__
539
d = memo.setdefault(key, {})
544
return '%s-%d' % (key, n)
546
# ____________________________________________________________
548
class DeadOperation(Operation):
551
def mark_used_vars(self, allocator):
553
def generate(self, allocator):
555
dead_operation = DeadOperation()
556
gv_frame_base = GenVar()
558
class Place(Operation):
559
"""Place of a variable that must live in the stack. Its position is
560
choosen by the register allocator and put in the 'offset' attribute."""
561
def __init__(self, x):
563
def mark_used_vars(self, allocator):
564
if self.x is not None:
565
allocator.using(self.x)
566
def get_offset(self):
568
def generate(self, allocator):
569
assert allocator.operation_result_is_used(self), "place not absorbed!"
570
loc = allocator._use_another_stack_loc()
571
allocator._created(self, loc)
572
if self.x is not None:
573
srcop = allocator.get_operand(self.x)
575
allocator.mc.MOV(loc, srcop)
576
except FailedToImplement:
577
# loc and srcop are both in the stack - need a temporary reg
578
tmpop = allocator.create_scratch_reg(srcop)
579
# loc and srcop still valid, as they are already in the stack
580
# so cannot have been spilled by create_scratch_reg()
581
allocator.mc.MOV(loc, tmpop)
582
allocator.end_clobber(tmpop)
583
allocator.release(self.x)
584
self.x = None # hack to avoid that the Place keeps a lot of
586
self.offset = loc.ofs_relative_to_ebp()
588
class OpAbsorbPlace(Op1):
590
def generate(self, allocator):
591
allocator.release(self.x)
592
if allocator.operation_result_is_used(self):
593
loc = allocator.get_operand(self.x)
594
allocator.create_exactly_at(self, loc)
596
class StorageInStack(Place):
597
def generate(self, allocator):
598
# force the variable to be in the stack
599
srcop = allocator.get_operand(self.x)
600
if not isinstance(srcop, MODRM):
602
srcop = allocator._spill(self.x, srcop)
603
allocator._mark_loc_as_free(oldop)
604
# record its location
605
self.offset = srcop.ofs_relative_to_ebp()
606
# hack to avoid this instance keeping a lot of memory around
609
class OpTouch(Operation):
610
side_effects = True # don't remove me!
611
def __init__(self, args_gv):
612
self.args_gv = args_gv
613
def mark_used_vars(self, allocator):
614
for v in self.args_gv:
616
def generate(self, allocator):
617
for v in self.args_gv: