Author: Richard Plangger <planri...@gmail.com> Branch: fix-longevity Changeset: r82460:98153a101dda Date: 2016-02-23 19:09 +0100 http://bitbucket.org/pypy/pypy/changeset/98153a101dda/
Log: (remi, plan_rich) refactored free_reg into two lists (caller saved and callee saved list) diff --git a/rpython/jit/backend/llsupport/regalloc.py b/rpython/jit/backend/llsupport/regalloc.py --- a/rpython/jit/backend/llsupport/regalloc.py +++ b/rpython/jit/backend/llsupport/regalloc.py @@ -1,7 +1,7 @@ import os -from rpython.jit.metainterp.history import Const, REF, JitCellToken +from rpython.jit.metainterp.history import Const, Box, REF, JitCellToken from rpython.rlib.objectmodel import we_are_translated, specialize -from rpython.jit.metainterp.resoperation import rop, AbstractValue +from rpython.jit.metainterp.resoperation import rop from rpython.rtyper.lltypesystem import lltype from rpython.rtyper.lltypesystem.lloperation import llop @@ -10,7 +10,7 @@ except ImportError: OrderedDict = dict # too bad -class TempVar(AbstractValue): +class TempBox(Box): def __init__(self): pass @@ -267,7 +267,6 @@ raise NotImplementedError("Purely abstract") class RegisterManager(object): - """ Class that keeps track of register allocations """ box_types = None # or a list of acceptable types @@ -276,10 +275,10 @@ save_around_call_regs = [] frame_reg = None - def __init__(self, live_ranges, frame_manager=None, assembler=None): + def __init__(self, longevity, frame_manager=None, assembler=None): self.free_regs = self.all_regs[:] self.free_regs.reverse() - self.live_ranges = live_ranges + self.longevity = longevity self.temp_boxes = [] if not we_are_translated(): self.reg_bindings = OrderedDict() @@ -293,19 +292,19 @@ def is_still_alive(self, v): # Check if 'v' is alive at the current position. # Return False if the last usage is strictly before. - return self.live_ranges.last_use(v) >= self.position + return self.longevity[v][1] >= self.position def stays_alive(self, v): # Check if 'v' stays alive after the current position. # Return False if the last usage is before or at position. - return self.live_ranges.last_use(v) > self.position + return self.longevity[v][1] > self.position def next_instruction(self, incr=1): self.position += incr def _check_type(self, v): if not we_are_translated() and self.box_types is not None: - assert isinstance(v, TempVar) or v.type in self.box_types + assert isinstance(v, TempBox) or v.type in self.box_types def possibly_free_var(self, v): """ If v is stored in a register and v is not used beyond the @@ -315,7 +314,7 @@ self._check_type(v) if isinstance(v, Const): return - if not self.live_ranges.exists(v) or self.live_ranges.last_use(v) <= self.position: + if v not in self.longevity or self.longevity[v][1] <= self.position: if v in self.reg_bindings: self.free_regs.append(self.reg_bindings[v]) del self.reg_bindings[v] @@ -347,9 +346,9 @@ else: assert len(self.reg_bindings) + len(self.free_regs) == len(self.all_regs) assert len(self.temp_boxes) == 0 - if self.live_ranges.longevity: + if self.longevity: for v in self.reg_bindings: - assert self.live_ranges.last_use(v) > self.position + assert self.longevity[v][1] > self.position def try_allocate_reg(self, v, selected_reg=None, need_lower_byte=False): """ Try to allocate a register, if we have one free. @@ -425,7 +424,7 @@ continue if need_lower_byte and reg in self.no_lower_byte_regs: continue - max_age = self.live_ranges.last_use(next) + max_age = self.longevity[next][1] if cur_max_age < max_age: cur_max_age = max_age candidate = next @@ -443,8 +442,8 @@ Will not spill a variable from 'forbidden_vars'. """ self._check_type(v) - if isinstance(v, TempVar): - self.live_ranges.new_live_range(v, self.position, self.position) + if isinstance(v, TempBox): + self.longevity[v] = (self.position, self.position) loc = self.try_allocate_reg(v, selected_reg, need_lower_byte=need_lower_byte) if loc: @@ -554,14 +553,13 @@ loc = self.force_allocate_reg(v, forbidden_vars) self.assembler.regalloc_mov(prev_loc, loc) assert v in self.reg_bindings - if self.live_ranges.last_use(v) > self.position: + if self.longevity[v][1] > self.position: # we need to find a new place for variable v and # store result in the same place loc = self.reg_bindings[v] del self.reg_bindings[v] - if self.frame_manager.get(v) is None or self.free_regs: + if self.frame_manager.get(v) is None: self._move_variable_away(v, loc) - self.reg_bindings[result_v] = loc else: self._reallocate_from_to(v, result_v) @@ -583,7 +581,7 @@ 1 (save all), or 2 (save default+PTRs). """ for v, reg in self.reg_bindings.items(): - if v not in force_store and self.live_ranges.last_use(v) <= self.position: + if v not in force_store and self.longevity[v][1] <= self.position: # variable dies del self.reg_bindings[v] self.free_regs.append(reg) @@ -635,78 +633,69 @@ locs = [] base_ofs = self.assembler.cpu.get_baseofs_of_frame_field() for box in inputargs: - assert not isinstance(box, Const) + assert isinstance(box, Box) loc = self.fm.get_new_loc(box) locs.append(loc.value - base_ofs) - if looptoken.compiled_loop_token is not None: # <- for tests + if looptoken.compiled_loop_token is not None: + # for tests looptoken.compiled_loop_token._ll_initial_locs = locs - def next_op_can_accept_cc(self, operations, i): - op = operations[i] - next_op = operations[i + 1] - opnum = next_op.getopnum() - if (opnum != rop.GUARD_TRUE and opnum != rop.GUARD_FALSE - and opnum != rop.COND_CALL): + def can_merge_with_next_guard(self, op, i, operations): + if (op.getopnum() == rop.CALL_MAY_FORCE or + op.getopnum() == rop.CALL_ASSEMBLER or + op.getopnum() == rop.CALL_RELEASE_GIL): + assert operations[i + 1].getopnum() == rop.GUARD_NOT_FORCED + return True + if not op.is_comparison(): + if op.is_ovf(): + if (operations[i + 1].getopnum() != rop.GUARD_NO_OVERFLOW and + operations[i + 1].getopnum() != rop.GUARD_OVERFLOW): + not_implemented("int_xxx_ovf not followed by " + "guard_(no)_overflow") + return True return False - if next_op.getarg(0) is not op: + if (operations[i + 1].getopnum() != rop.GUARD_TRUE and + operations[i + 1].getopnum() != rop.GUARD_FALSE): return False - if self.longevity[op][1] > i + 1: + if operations[i + 1].getarg(0) is not op.result: return False - if opnum != rop.COND_CALL: - if op in operations[i + 1].getfailargs(): - return False - else: - if op in operations[i + 1].getarglist()[1:]: - return False + if (self.longevity[op.result][1] > i + 1 or + op.result in operations[i + 1].getfailargs()): + return False return True - def locs_for_call_assembler(self, op): + def locs_for_call_assembler(self, op, guard_op): descr = op.getdescr() assert isinstance(descr, JitCellToken) if op.numargs() == 2: self.rm._sync_var(op.getarg(1)) return [self.loc(op.getarg(0)), self.fm.loc(op.getarg(1))] else: - assert op.numargs() == 1 return [self.loc(op.getarg(0))] -class LiveRanges(object): - def __init__(self, longevity, last_real_usage, dist_to_next_call): - self.longevity = longevity - self.last_real_usage = last_real_usage - self.dist_to_next_call = dist_to_next_call - - def exists(self, var): - return var in self.longevity - - def last_use(self, var): - return self.longevity[var][1] - - def new_live_range(self, var, start, end): - self.longevity[var] = (start, end) - -def compute_var_live_ranges(inputargs, operations): +def compute_vars_longevity(inputargs, operations): # compute a dictionary that maps variables to index in # operations that is a "last-time-seen" - # returns a Longevity object with longevity/useful. Non-useful variables are ones that + # returns a pair longevity/useful. Non-useful variables are ones that # never appear in the assembler or it does not matter if they appear on # stack or in registers. Main example is loop arguments that go # only to guard operations or to jump or to finish + produced = {} last_used = {} last_real_usage = {} - dist_to_next_call = [0] * len(operations) - last_call_pos = -1 for i in range(len(operations)-1, -1, -1): op = operations[i] - if op.type != 'v': - if op not in last_used and op.has_no_side_effect(): + if op.result: + if op.result not in last_used and op.has_no_side_effect(): continue + assert op.result not in produced + produced[op.result] = i opnum = op.getopnum() for j in range(op.numargs()): arg = op.getarg(j) - if isinstance(arg, Const): + if not isinstance(arg, Box): continue if arg not in last_used: last_used[arg] = i @@ -717,40 +706,26 @@ for arg in op.getfailargs(): if arg is None: # hole continue - assert not isinstance(arg, Const) + assert isinstance(arg, Box) if arg not in last_used: last_used[arg] = i - if op.is_call(): - last_call_pos = i - dist_to_next_call[i] = last_call_pos - i # longevity = {} - for i, arg in enumerate(operations): - if arg.type != 'v' and arg in last_used: - assert not isinstance(arg, Const) - assert i < last_used[arg] - longevity[arg] = (i, last_used[arg]) + for arg in produced: + if arg in last_used: + assert isinstance(arg, Box) + assert produced[arg] < last_used[arg] + longevity[arg] = (produced[arg], last_used[arg]) del last_used[arg] for arg in inputargs: - assert not isinstance(arg, Const) + assert isinstance(arg, Box) if arg not in last_used: longevity[arg] = (-1, -1) else: longevity[arg] = (0, last_used[arg]) del last_used[arg] assert len(last_used) == 0 - - if not we_are_translated(): - produced = {} - for arg in inputargs: - produced[arg] = None - for op in operations: - for arg in op.getarglist(): - if not isinstance(arg, Const): - assert arg in produced - produced[op] = None - - return LiveRanges(longevity, last_real_usage, dist_to_next_call) + return longevity, last_real_usage def is_comparison_or_ovf_op(opnum): from rpython.jit.metainterp.resoperation import opclasses @@ -759,7 +734,7 @@ # any instance field, we can use a fake object class Fake(cls): pass - op = Fake() + op = Fake(None) return op.is_comparison() or op.is_ovf() def valid_addressing_size(size): _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit