Author: Armin Rigo <armin.r...@gmail.com> Branch: Changeset: r88431:ab8a0b092d2d Date: 2016-11-17 13:14 +0000 http://bitbucket.org/pypy/pypy/changeset/ab8a0b092d2d/
Log: Merged in clean-exported-state (pull request #490) Clean exported state diff --git a/rpython/jit/metainterp/compile.py b/rpython/jit/metainterp/compile.py --- a/rpython/jit/metainterp/compile.py +++ b/rpython/jit/metainterp/compile.py @@ -108,7 +108,7 @@ """ log_noopt = False - def __init__(self, trace, celltoken, state, + def __init__(self, trace, celltoken, state, runtime_boxes, call_pure_results=None, enable_opts=None, inline_short_preamble=True): self.trace = trace @@ -117,6 +117,8 @@ self.state = state self.call_pure_results = call_pure_results self.inline_short_preamble = inline_short_preamble + assert runtime_boxes is not None + self.runtime_boxes = runtime_boxes def optimize(self, metainterp_sd, jitdriver_sd, optimizations, unroll): from rpython.jit.metainterp.optimizeopt.unroll import UnrollOptimizer @@ -124,7 +126,11 @@ assert unroll # we should not be here if it's disabled opt = UnrollOptimizer(metainterp_sd, jitdriver_sd, optimizations) return opt.optimize_peeled_loop(self.trace, self.celltoken, self.state, - self.call_pure_results, self.inline_short_preamble) + self.runtime_boxes, self.call_pure_results, self.inline_short_preamble) + + def forget_optimization_info(self): + self.state.forget_optimization_info() + CompileData.forget_optimization_info(self) def show_procedures(metainterp_sd, procedure=None, error=None): # debugging @@ -290,7 +296,7 @@ start_descr = TargetToken(jitcell_token, original_jitcell_token=jitcell_token) jitcell_token.target_tokens = [start_descr] - loop_data = UnrolledLoopData(trace, jitcell_token, start_state, + loop_data = UnrolledLoopData(trace, jitcell_token, start_state, jumpargs, call_pure_results=call_pure_results, enable_opts=enable_opts) try: @@ -360,7 +366,7 @@ history.record(rop.JUMP, jumpargs[:], None, descr=loop_jitcell_token) enable_opts = jitdriver_sd.warmstate.enable_opts call_pure_results = metainterp.call_pure_results - loop_data = UnrolledLoopData(trace, loop_jitcell_token, start_state, + loop_data = UnrolledLoopData(trace, loop_jitcell_token, start_state, jumpargs, call_pure_results=call_pure_results, enable_opts=enable_opts) try: @@ -372,6 +378,7 @@ history.cut(cut) history.record(rop.JUMP, jumpargs[:], None, descr=loop_jitcell_token) loop_data = UnrolledLoopData(trace, loop_jitcell_token, start_state, + jumpargs, call_pure_results=call_pure_results, enable_opts=enable_opts, inline_short_preamble=False) @@ -516,7 +523,7 @@ for item in lst: item.set_forwarded(None) # XXX we should really do it, but we need to remember the values - # somehoe for ContinueRunningNormally + # somehow for ContinueRunningNormally if reset_values: item.reset_value() diff --git a/rpython/jit/metainterp/optimizeopt/heap.py b/rpython/jit/metainterp/optimizeopt/heap.py --- a/rpython/jit/metainterp/optimizeopt/heap.py +++ b/rpython/jit/metainterp/optimizeopt/heap.py @@ -12,7 +12,7 @@ from rpython.jit.metainterp.optimize import InvalidLoop from rpython.jit.metainterp.resoperation import rop, ResOperation, OpHelpers,\ AbstractResOp, GuardResOp -from rpython.rlib.objectmodel import we_are_translated +from rpython.rlib.objectmodel import we_are_translated, we_are_debug from rpython.jit.metainterp.optimizeopt import info @@ -172,7 +172,7 @@ def _getfield(self, opinfo, descr, optheap, true_force=True): res = opinfo.getfield(descr, optheap) - if not we_are_translated() and res: + if we_are_debug() and res: if isinstance(opinfo, info.AbstractStructPtrInfo): assert opinfo in self.cached_infos if isinstance(res, PreambleOp): @@ -202,7 +202,7 @@ def _getfield(self, opinfo, descr, optheap, true_force=True): res = opinfo.getitem(descr, self.index, optheap) - if not we_are_translated() and res: + if we_are_debug() and res: if isinstance(opinfo, info.ArrayPtrInfo): assert opinfo in self.cached_infos if (isinstance(res, PreambleOp) and diff --git a/rpython/jit/metainterp/optimizeopt/optimizer.py b/rpython/jit/metainterp/optimizeopt/optimizer.py --- a/rpython/jit/metainterp/optimizeopt/optimizer.py +++ b/rpython/jit/metainterp/optimizeopt/optimizer.py @@ -24,9 +24,20 @@ llhelper.CONST_NULLREF = llhelper.CONST_NULL REMOVED = AbstractResOp() +def check_no_forwarding(lsts): + for lst in lsts: + for op in lst: + assert op.get_forwarded() is None + class LoopInfo(object): label_op = None + def _check_no_forwarding(self): + pass + + def forget_optimization_info(self): + pass + class BasicLoopInfo(LoopInfo): def __init__(self, inputargs, quasi_immutable_deps, jump_op): self.inputargs = inputargs @@ -555,7 +566,8 @@ return (BasicLoopInfo(trace.inputargs, self.quasi_immutable_deps, last_op), self._newoperations) - def _clean_optimization_info(self, lst): + @staticmethod + def _clean_optimization_info(lst): for op in lst: if op.get_forwarded() is not None: op.set_forwarded(None) diff --git a/rpython/jit/metainterp/optimizeopt/shortpreamble.py b/rpython/jit/metainterp/optimizeopt/shortpreamble.py --- a/rpython/jit/metainterp/optimizeopt/shortpreamble.py +++ b/rpython/jit/metainterp/optimizeopt/shortpreamble.py @@ -5,6 +5,7 @@ rop, AbstractResOp, AbstractInputArg from rpython.jit.metainterp.history import Const, make_hashable_int,\ TreeLoop +from rpython.jit.metainterp.optimize import InvalidLoop from rpython.jit.metainterp.optimizeopt import info class PreambleOp(AbstractResOp): @@ -18,7 +19,7 @@ See force_op_from_preamble for details how the extra things are put. """ op = None - + def __init__(self, op, preamble_op, invented_name): self.op = op self.preamble_op = preamble_op @@ -51,7 +52,13 @@ class AbstractShortOp(object): """ An operation that is potentially produced by the short preamble """ - pass + res = None + + def _check_no_forwarding(self): + assert self.res.get_forwarded() is None + + def forget_optimization_info(self): + self.res.clear_forwarded() class HeapOp(AbstractShortOp): def __init__(self, res, getfield_op): @@ -101,6 +108,14 @@ descr=sop.getdescr()) return ProducedShortOp(self, preamble_op) + def _check_no_forwarding(self): + AbstractShortOp._check_no_forwarding(self) + assert self.getfield_op.get_forwarded() is None + + def forget_optimization_info(self): + AbstractShortOp.forget_optimization_info(self) + self.getfield_op.clear_forwarded() + def __repr__(self): return "HeapOp(%r)" % (self.res,) @@ -193,6 +208,16 @@ l.append(pop) return l + def _check_no_forwarding(self): + AbstractShortOp._check_no_forwarding(self) + self.one._check_no_forwarding() + self.two._check_no_forwarding() + + def forget_optimization_info(self): + AbstractShortOp.forget_optimization_info(self) + self.one.forget_optimization_info() + self.two.forget_optimization_info() + def repr(self, memo): return "CompoundOp(%s, %s, %s)" % (self.res.repr(memo), self.one.repr(memo), @@ -203,7 +228,7 @@ class ProducedShortOp(AbstractProducedShortOp): invented_name = False - + def __init__(self, short_op, preamble_op): self.short_op = short_op self.preamble_op = preamble_op @@ -215,6 +240,14 @@ def repr(self, memo): return self.short_op.repr(memo) + def _check_no_forwarding(self): + self.short_op._check_no_forwarding() + assert self.preamble_op.get_forwarded() is None + + def forget_optimization_info(self): + self.short_op.forget_optimization_info() + self.preamble_op.clear_forwarded() + def __repr__(self): return "%r -> %r" % (self.short_op, self.preamble_op) @@ -235,6 +268,14 @@ def repr(self, memo): return "INP(%s)" % (self.res.repr(memo),) + def _check_no_forwarding(self): + AbstractShortOp._check_no_forwarding(self) + assert self.preamble_op.get_forwarded() is None + + def forget_optimization_info(self): + AbstractShortOp.forget_optimization_info(self) + self.preamble_op.clear_forwarded() + def __repr__(self): return "INP(%r -> %r)" % (self.res, self.preamble_op) @@ -454,16 +495,23 @@ self.sb = sb self.extra_same_as = self.sb.extra_same_as self.target_token = target_token + self.build_inplace = False def setup(self, jump_args, short, label_args): self.jump_args = jump_args self.short = short self.label_args = label_args + self.build_inplace = True def add_preamble_op(self, preamble_op): """ Notice that we're actually using the preamble_op, add it to label and jump """ + # Could this be considered a speculative error? + # This check should only fail when trying to jump to an existing trace + # by forcing portions of the virtualstate. + if not self.build_inplace: + raise InvalidLoop("Forcing boxes would modify an existing short preamble") op = preamble_op.op.get_box_replacement() if preamble_op.invented_name: self.extra_same_as.append(op) diff --git a/rpython/jit/metainterp/optimizeopt/test/test_util.py b/rpython/jit/metainterp/optimizeopt/test/test_util.py --- a/rpython/jit/metainterp/optimizeopt/test/test_util.py +++ b/rpython/jit/metainterp/optimizeopt/test/test_util.py @@ -573,6 +573,7 @@ # compile_data.enable_opts = self.enable_opts state = optimize_trace(metainterp_sd, None, compile_data) + state[0]._check_no_forwarding() return state def _convert_call_pure_results(self, d): @@ -621,7 +622,7 @@ start_state, preamble_ops = self._do_optimize_loop(preamble_data) preamble_data.forget_optimization_info() loop_data = compile.UnrolledLoopData(preamble_data.trace, - celltoken, start_state, call_pure_results) + celltoken, start_state, runtime_boxes, call_pure_results) loop_info, ops = self._do_optimize_loop(loop_data) preamble = TreeLoop('preamble') preamble.inputargs = start_state.renamed_inputargs diff --git a/rpython/jit/metainterp/optimizeopt/unroll.py b/rpython/jit/metainterp/optimizeopt/unroll.py --- a/rpython/jit/metainterp/optimizeopt/unroll.py +++ b/rpython/jit/metainterp/optimizeopt/unroll.py @@ -6,7 +6,7 @@ from rpython.jit.metainterp.optimizeopt import info, intutils from rpython.jit.metainterp.optimize import InvalidLoop, SpeculativeError from rpython.jit.metainterp.optimizeopt.optimizer import Optimizer,\ - Optimization, LoopInfo, MININT, MAXINT, BasicLoopInfo + Optimization, LoopInfo, MININT, MAXINT, BasicLoopInfo, check_no_forwarding from rpython.jit.metainterp.optimizeopt.vstring import StrPtrInfo from rpython.jit.metainterp.optimizeopt.virtualstate import ( VirtualStateConstructor, VirtualStatesCantMatch) @@ -35,7 +35,7 @@ def setinfo_from_preamble_list(self, lst, infos): for item in lst: - if item is None: + if item is None or isinstance(item, Const): continue i = infos.get(item, None) if i is not None: @@ -97,7 +97,6 @@ elif isinstance(preamble_info, info.FloatConstInfo): op.set_forwarded(preamble_info._const) - class UnrollOptimizer(Optimization): """Unroll the loop into two iterations. The first one will become the preamble or entry bridge (don't think there is a @@ -115,26 +114,22 @@ return modifier.get_virtual_state(args) def _check_no_forwarding(self, lsts, check_newops=True): - for lst in lsts: - for op in lst: - assert op.get_forwarded() is None + check_no_forwarding(lsts) if check_newops: assert not self.optimizer._newoperations - def optimize_preamble(self, trace, runtime_boxes, call_pure_results, memo): info, newops = self.optimizer.propagate_all_forward( trace.get_iter(), call_pure_results, flush=False) exported_state = self.export_state(info.jump_op.getarglist(), - info.inputargs, - runtime_boxes, memo) + info.inputargs, memo) exported_state.quasi_immutable_deps = info.quasi_immutable_deps # we need to absolutely make sure that we've cleaned up all # the optimization info self.optimizer._clean_optimization_info(self.optimizer._newoperations) return exported_state, self.optimizer._newoperations - def optimize_peeled_loop(self, trace, celltoken, state, + def optimize_peeled_loop(self, trace, celltoken, state, runtime_boxes, call_pure_results, inline_short_preamble=True): trace = trace.get_iter() try: @@ -186,7 +181,7 @@ try: new_virtual_state = self.jump_to_existing_trace( - end_jump, label_op, state.runtime_boxes, force_boxes=False) + end_jump, label_op, runtime_boxes, force_boxes=False) except InvalidLoop: # inlining short preamble failed, jump to preamble self.jump_to_preamble(celltoken, end_jump, info) @@ -199,7 +194,7 @@ # to the preamble. try: new_virtual_state = self.jump_to_existing_trace( - end_jump, label_op, state.runtime_boxes, force_boxes=True) + end_jump, label_op, runtime_boxes, force_boxes=True) except InvalidLoop: pass @@ -276,8 +271,7 @@ debug_print("Retrace count reached, jumping to preamble") return self.jump_to_preamble(cell_token, jump_op, info) exported_state = self.export_state(info.jump_op.getarglist(), - info.inputargs, runtime_boxes, - box_names_memo) + info.inputargs, box_names_memo) exported_state.quasi_immutable_deps = self.optimizer.quasi_immutable_deps self.optimizer._clean_optimization_info(self.optimizer._newoperations) return exported_state, self.optimizer._newoperations @@ -440,8 +434,7 @@ continue self._expand_info(item, infos) - def export_state(self, original_label_args, renamed_inputargs, - runtime_boxes, memo): + def export_state(self, original_label_args, renamed_inputargs, memo): end_args = [self.optimizer.force_box_for_end_of_preamble(a) for a in original_label_args] self.optimizer.flush() @@ -462,16 +455,17 @@ op = produced_op.short_op.res if not isinstance(op, Const): self._expand_info(op, infos) - self.optimizer._clean_optimization_info(end_args) return ExportedState(label_args, end_args, virtual_state, infos, short_boxes, renamed_inputargs, - short_inputargs, runtime_boxes, memo) + short_inputargs, memo) def import_state(self, targetargs, exported_state): # the mapping between input args (from old label) and what we need # to actually emit. Update the info assert (len(exported_state.next_iteration_args) == len(targetargs)) + self._check_no_forwarding([targetargs]) + exported_state._check_no_forwarding() for i, target in enumerate(exported_state.next_iteration_args): source = targetargs[i] assert source is not target @@ -527,13 +521,11 @@ * renamed_inputargs - the start label arguments in optimized version * short_inputargs - the renamed inputargs for short preamble * quasi_immutable_deps - for tracking quasi immutables - * runtime_boxes - runtime values for boxes, necessary when generating - guards to jump to """ def __init__(self, end_args, next_iteration_args, virtual_state, exported_infos, short_boxes, renamed_inputargs, - short_inputargs, runtime_boxes, memo): + short_inputargs, memo): self.end_args = end_args self.next_iteration_args = next_iteration_args self.virtual_state = virtual_state @@ -541,8 +533,8 @@ self.short_boxes = short_boxes self.renamed_inputargs = renamed_inputargs self.short_inputargs = short_inputargs - self.runtime_boxes = runtime_boxes self.dump(memo) + self.forget_optimization_info() def dump(self, memo): if have_debug_prints(): @@ -552,5 +544,35 @@ debug_print(" " + box.repr(memo)) debug_stop("jit-log-exported-state") + def _check_no_forwarding(self): + """ Ensures that no optimization state is attached to relevant operations + before importing anything. """ + # Some of these may be redunant + check_no_forwarding([ + self.end_args, + self.next_iteration_args, + self.renamed_inputargs, + self.short_inputargs, + self.exported_infos.keys()]) + for box in self.short_boxes: + box._check_no_forwarding() + + def forget_optimization_info(self): + """ Clean up optimization info on all operations stored in the ExportedState. + + This function needs to be called when exporting the optimizer state to + prevent leaking of optimization information between invocations of the + optimizer. + + That includes cleaning up in the event that optimize_peeled_loop() fails + with an InvalidLoop exception, as optimize_peeled_loop() mutates the + contents of ExportedState. + """ + Optimizer._clean_optimization_info(self.renamed_inputargs) + for box in self.exported_infos.iterkeys(): + box.clear_forwarded() + for box in self.short_boxes: + box.forget_optimization_info() + def final(self): return False diff --git a/rpython/jit/metainterp/pyjitpl.py b/rpython/jit/metainterp/pyjitpl.py --- a/rpython/jit/metainterp/pyjitpl.py +++ b/rpython/jit/metainterp/pyjitpl.py @@ -1976,6 +1976,8 @@ self.aborted_tracing_greenkey = None def retrace_needed(self, trace, exported_state): + if not we_are_translated(): + exported_state._check_no_forwarding() self.partial_trace = trace self.retracing_from = self.potential_retrace_position self.exported_state = exported_state diff --git a/rpython/jit/metainterp/resoperation.py b/rpython/jit/metainterp/resoperation.py --- a/rpython/jit/metainterp/resoperation.py +++ b/rpython/jit/metainterp/resoperation.py @@ -52,6 +52,10 @@ llop.debug_print(lltype.Void, "setting forwarded on:", self.__class__.__name__) raise SettingForwardedOnAbstractValue() + def clear_forwarded(self): + if self.get_forwarded() is not None: + self.set_forwarded(None) + @specialize.arg(1) def get_box_replacement(op, not_const=False): # Read the chain "op, op._forwarded, op._forwarded._forwarded..." diff --git a/rpython/rlib/objectmodel.py b/rpython/rlib/objectmodel.py --- a/rpython/rlib/objectmodel.py +++ b/rpython/rlib/objectmodel.py @@ -295,10 +295,15 @@ malloc_zero_filled = CDefinedIntSymbolic('MALLOC_ZERO_FILLED', default=0) _translated_to_c = CDefinedIntSymbolic('1 /*_translated_to_c*/', default=0) +_rpy_assert_value = CDefinedIntSymbolic('RPY_ASSERT_VALUE', default=1) def we_are_translated_to_c(): return we_are_translated() and _translated_to_c +def we_are_debug(): + """ Returns True when not translated or translated with debugging enabled. """ + return not we_are_translated() or (_translated_to_c and _rpy_assert_value) + # ____________________________________________________________ def instantiate(cls, nonmovable=False): diff --git a/rpython/translator/c/src/support.h b/rpython/translator/c/src/support.h --- a/rpython/translator/c/src/support.h +++ b/rpython/translator/c/src/support.h @@ -31,8 +31,10 @@ RPY_EXTERN void RPyAssertFailed(const char* filename, long lineno, const char* function, const char *msg); +# define RPY_ASSERT_VALUE 1 #else # define RPyAssert(x, msg) /* nothing */ +# define RPY_ASSERT_VALUE 0 #endif RPY_EXTERN _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit