Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r50308:062e9d06c908 Date: 2011-12-08 16:40 +0100 http://bitbucket.org/pypy/pypy/changeset/062e9d06c908/
Log: Hack: record at least some partial information about which frame locations are freed, and if a hint is present, try to allocate the spilled boxes into these free locations. The hints are initialized based on looking ahead at the final JUMP operation. The goal is to reduce the number of frame-to-frame MOVs that must be emitted in the JUMP, by spilling boxes directly to the correct location, when possible. diff --git a/pypy/jit/backend/llsupport/regalloc.py b/pypy/jit/backend/llsupport/regalloc.py --- a/pypy/jit/backend/llsupport/regalloc.py +++ b/pypy/jit/backend/llsupport/regalloc.py @@ -23,9 +23,12 @@ return self.frame_bindings.get(box, None) def loc(self, box): - res = self.get(box) - if res is not None: - return res + try: + return self.frame_bindings[box] + except KeyError: + return self.get_new_loc(box) + + def get_new_loc(self, box): size = self.frame_size(box.type) self.frame_depth += ((-self.frame_depth) & (size-1)) # ^^^ frame_depth is rounded up to a multiple of 'size', assuming @@ -67,8 +70,17 @@ self.position = -1 self.frame_manager = frame_manager self.assembler = assembler + self.hint_frame_locations = {} # {Box: StackLoc} + self.freed_frame_locations = {} # {StackLoc: None} + + 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.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.longevity[v][1] > self.position def next_instruction(self, incr=1): @@ -84,11 +96,16 @@ point for all variables that might be in registers. """ self._check_type(v) - if isinstance(v, Const) or v not in self.reg_bindings: + if isinstance(v, Const): return if v not in self.longevity or self.longevity[v][1] <= self.position: - self.free_regs.append(self.reg_bindings[v]) - del self.reg_bindings[v] + if v in self.reg_bindings: + self.free_regs.append(self.reg_bindings[v]) + del self.reg_bindings[v] + if self.frame_manager is not None: + if v in self.frame_manager.frame_bindings: + loc = self.frame_manager.frame_bindings[v] + self.freed_frame_locations[loc] = None def possibly_free_vars(self, vars): """ Same as 'possibly_free_var', but for all v in vars. @@ -160,6 +177,23 @@ self.reg_bindings[v] = loc return loc + def _frame_loc(self, v): + # first check if it's already in the frame_manager + try: + return self.frame_manager.frame_bindings[v] + except KeyError: + pass + # check if we have a hint for this box + if v in self.hint_frame_locations: + # if we do, check that the hinted location is known to be free + loc = self.hint_frame_locations[v] + if loc in self.freed_frame_locations: + del self.freed_frame_locations[loc] + self.frame_manager.frame_bindings[v] = loc + return loc + # no valid hint. make up a new free location + return self.frame_manager.get_new_loc(v) + def _spill_var(self, v, forbidden_vars, selected_reg, need_lower_byte=False): v_to_spill = self._pick_variable_to_spill(v, forbidden_vars, @@ -167,7 +201,7 @@ loc = self.reg_bindings[v_to_spill] del self.reg_bindings[v_to_spill] if self.frame_manager.get(v_to_spill) is None: - newloc = self.frame_manager.loc(v_to_spill) + newloc = self._frame_loc(v_to_spill) self.assembler.regalloc_mov(loc, newloc) return loc @@ -244,7 +278,7 @@ except KeyError: if box in self.bindings_to_frame_reg: return self.frame_reg - return self.frame_manager.loc(box) + return self._frame_loc(box) def return_constant(self, v, forbidden_vars=[], selected_reg=None): """ Return the location of the constant v. If 'selected_reg' is @@ -292,7 +326,7 @@ self.reg_bindings[v] = loc self.assembler.regalloc_mov(prev_loc, loc) else: - loc = self.frame_manager.loc(v) + loc = self._frame_loc(v) self.assembler.regalloc_mov(prev_loc, loc) def force_result_in_reg(self, result_v, v, forbidden_vars=[]): @@ -311,7 +345,7 @@ self.reg_bindings[result_v] = loc return loc if v not in self.reg_bindings: - prev_loc = self.frame_manager.loc(v) + prev_loc = self._frame_loc(v) loc = self.force_allocate_reg(v, forbidden_vars) self.assembler.regalloc_mov(prev_loc, loc) assert v in self.reg_bindings @@ -331,7 +365,7 @@ def _sync_var(self, v): if not self.frame_manager.get(v): reg = self.reg_bindings[v] - to = self.frame_manager.loc(v) + to = self._frame_loc(v) self.assembler.regalloc_mov(reg, to) # otherwise it's clean diff --git a/pypy/jit/backend/llsupport/test/test_regalloc.py b/pypy/jit/backend/llsupport/test/test_regalloc.py --- a/pypy/jit/backend/llsupport/test/test_regalloc.py +++ b/pypy/jit/backend/llsupport/test/test_regalloc.py @@ -348,3 +348,50 @@ spilled2 = rm.force_allocate_reg(b5) assert spilled2 is loc rm._check_invariants() + + + def test_hint_frame_locations_1(self): + b0, b1 = newboxes(0, 1) + longevity = {b0: (0, 1), b1: (0, 1)} + fm = TFrameManager() + asm = MockAsm() + rm = RegisterManager(longevity, frame_manager=fm, assembler=asm) + rm.hint_frame_locations[b0] = "some_stack_loc" + rm.freed_frame_locations["some_stack_loc"] = None + rm.force_allocate_reg(b0) + rm.force_allocate_reg(b1) + rm.force_spill_var(b0) + rm.force_spill_var(b1) + assert rm.loc(b0) == "some_stack_loc" + assert isinstance(rm.loc(b1), FakeFramePos) + rm._check_invariants() + + def test_hint_frame_locations_2(self): + b0, b1, b2 = newboxes(0, 1, 2) + longevity = {b0: (0, 1), b1: (0, 2), b2: (0, 2)} + fm = TFrameManager() + asm = MockAsm() + rm = RegisterManager(longevity, frame_manager=fm, assembler=asm) + rm.force_allocate_reg(b0) + rm.force_allocate_reg(b1) + rm.force_allocate_reg(b2) + rm.force_spill_var(b0) + loc = rm.loc(b0) + assert isinstance(loc, FakeFramePos) + rm.position = 1 + assert loc not in rm.freed_frame_locations + rm.possibly_free_var(b0) + assert loc in rm.freed_frame_locations + # + rm.hint_frame_locations[b1] = loc + rm.force_spill_var(b1) + loc1 = rm.loc(b1) + assert loc1 is loc + assert rm.freed_frame_locations == {} + # + rm.hint_frame_locations[b2] = loc + rm.force_spill_var(b2) + loc2 = rm.loc(b2) + assert loc2 is not loc1 # because it's not in freed_frame_locations + # + rm._check_invariants() diff --git a/pypy/jit/backend/x86/assembler.py b/pypy/jit/backend/x86/assembler.py --- a/pypy/jit/backend/x86/assembler.py +++ b/pypy/jit/backend/x86/assembler.py @@ -690,6 +690,7 @@ def _assemble(self, regalloc, operations): self._regalloc = regalloc + regalloc.compute_hint_frame_locations(operations) regalloc.walk_operations(operations) if we_are_translated() or self.cpu.dont_keepalive_stuff: self._regalloc = None # else keep it around for debugging diff --git a/pypy/jit/backend/x86/regalloc.py b/pypy/jit/backend/x86/regalloc.py --- a/pypy/jit/backend/x86/regalloc.py +++ b/pypy/jit/backend/x86/regalloc.py @@ -1318,6 +1318,29 @@ self.rm.possibly_free_var(tmpbox_low) self.rm.possibly_free_var(tmpbox_high) + def compute_hint_frame_locations(self, operations): + # optimization only: fill in the 'hint_frame_locations' dictionary + # of rm and xrm based on the JUMP at the end of the loop, by looking + # at where we would like the boxes to be after the jump. + op = operations[-1] + if op.getopnum() != rop.JUMP: + return + descr = op.getdescr() + assert isinstance(descr, LoopToken) + nonfloatlocs, floatlocs = self.assembler.target_arglocs(descr) + for i in range(op.numargs()): + box = op.getarg(i) + if isinstance(box, Box): + loc = nonfloatlocs[i] + if isinstance(loc, StackLoc): + assert box.type != FLOAT + self.rm.hint_frame_locations[box] = loc + else: + loc = floatlocs[i] + if isinstance(loc, StackLoc): + assert box.type == FLOAT + self.xrm.hint_frame_locations[box] = loc + def consider_jump(self, op): assembler = self.assembler assert self.jump_target_descr is None _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit