Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r48866:141e9d3bebff Date: 2011-11-07 16:15 +0100 http://bitbucket.org/pypy/pypy/changeset/141e9d3bebff/
Log: (antocuni, hakan, arigo) Kill 'exception_might_have_happened' and the 'bridge' boolean field of Optimizer. Simplify some of the 'posponedop' mess, by explicitly removing 'guard_no_exception' only if they immediately follow a removed 'call'. This is detected with a new field 'last_emitted_operation', recording the last operation seen by a 'emit_operation'. Use this too to clean up intbounds overflow checking. diff --git a/pypy/jit/metainterp/optimizeopt/__init__.py b/pypy/jit/metainterp/optimizeopt/__init__.py --- a/pypy/jit/metainterp/optimizeopt/__init__.py +++ b/pypy/jit/metainterp/optimizeopt/__init__.py @@ -55,7 +55,7 @@ def optimize_loop_1(metainterp_sd, loop, enable_opts, - inline_short_preamble=True, retraced=False, bridge=False): + inline_short_preamble=True, retraced=False): """Optimize loop.operations to remove internal overheadish operations. """ @@ -64,7 +64,7 @@ if unroll: optimize_unroll(metainterp_sd, loop, optimizations) else: - optimizer = Optimizer(metainterp_sd, loop, optimizations, bridge) + optimizer = Optimizer(metainterp_sd, loop, optimizations) optimizer.propagate_all_forward() def optimize_bridge_1(metainterp_sd, bridge, enable_opts, @@ -76,7 +76,7 @@ except KeyError: pass optimize_loop_1(metainterp_sd, bridge, enable_opts, - inline_short_preamble, retraced, bridge=True) + inline_short_preamble, retraced) if __name__ == '__main__': print ALL_OPTS_NAMES diff --git a/pypy/jit/metainterp/optimizeopt/heap.py b/pypy/jit/metainterp/optimizeopt/heap.py --- a/pypy/jit/metainterp/optimizeopt/heap.py +++ b/pypy/jit/metainterp/optimizeopt/heap.py @@ -234,7 +234,7 @@ or op.is_ovf()): self.posponedop = op else: - self.next_optimization.propagate_forward(op) + Optimization.emit_operation(self, op) def emitting_operation(self, op): if op.has_no_side_effect(): diff --git a/pypy/jit/metainterp/optimizeopt/intbounds.py b/pypy/jit/metainterp/optimizeopt/intbounds.py --- a/pypy/jit/metainterp/optimizeopt/intbounds.py +++ b/pypy/jit/metainterp/optimizeopt/intbounds.py @@ -6,6 +6,7 @@ IntUpperBound) from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method from pypy.jit.metainterp.resoperation import rop +from pypy.jit.metainterp.optimize import InvalidLoop from pypy.rlib.rarithmetic import LONG_BIT @@ -13,30 +14,10 @@ """Keeps track of the bounds placed on integers by guards and remove redundant guards""" - def setup(self): - self.posponedop = None - self.nextop = None - def new(self): - assert self.posponedop is None return OptIntBounds() - - def flush(self): - assert self.posponedop is None - - def setup(self): - self.posponedop = None - self.nextop = None def propagate_forward(self, op): - if op.is_ovf(): - self.posponedop = op - return - if self.posponedop: - self.nextop = op - op = self.posponedop - self.posponedop = None - dispatch_opt(self, op) def opt_default(self, op): @@ -179,68 +160,75 @@ r = self.getvalue(op.result) r.intbound.intersect(b) + def optimize_GUARD_NO_OVERFLOW(self, op): + lastop = self.last_emitted_operation + if lastop is not None: + opnum = lastop.getopnum() + args = lastop.getarglist() + result = lastop.result + # If the INT_xxx_OVF was replaced with INT_xxx, then we can kill + # the GUARD_NO_OVERFLOW. + if (opnum == rop.INT_ADD or + opnum == rop.INT_SUB or + opnum == rop.INT_MUL): + return + # Else, synthesize the non overflowing op for optimize_default to + # reuse, as well as the reverse op + elif opnum == rop.INT_ADD_OVF: + self.pure(rop.INT_ADD, args[:], result) + self.pure(rop.INT_SUB, [result, args[1]], args[0]) + self.pure(rop.INT_SUB, [result, args[0]], args[1]) + elif opnum == rop.INT_SUB_OVF: + self.pure(rop.INT_SUB, args[:], result) + self.pure(rop.INT_ADD, [result, args[1]], args[0]) + self.pure(rop.INT_SUB, [args[0], result], args[1]) + elif opnum == rop.INT_MUL_OVF: + self.pure(rop.INT_MUL, args[:], result) + self.emit_operation(op) + + def optimize_GUARD_OVERFLOW(self, op): + # If INT_xxx_OVF was replaced by INT_xxx, *but* we still see + # GUARD_OVERFLOW, then the loop is invalid. + lastop = self.last_emitted_operation + if lastop is None: + raise InvalidLoop + opnum = lastop.getopnum() + if opnum not in (rop.INT_ADD_OVF, rop.INT_SUB_OVF, rop.INT_MUL_OVF): + raise InvalidLoop + self.emit_operation(op) + def optimize_INT_ADD_OVF(self, op): v1 = self.getvalue(op.getarg(0)) v2 = self.getvalue(op.getarg(1)) resbound = v1.intbound.add_bound(v2.intbound) - if resbound.has_lower and resbound.has_upper and \ - self.nextop.getopnum() == rop.GUARD_NO_OVERFLOW: - # Transform into INT_ADD and remove guard + if resbound.bounded(): + # Transform into INT_ADD. The following guard will be killed + # by optimize_GUARD_NO_OVERFLOW; if we see instead an + # optimize_GUARD_OVERFLOW, then InvalidLoop. op = op.copy_and_change(rop.INT_ADD) - self.optimize_INT_ADD(op) # emit the op - else: - self.emit_operation(op) - r = self.getvalue(op.result) - r.intbound.intersect(resbound) - self.emit_operation(self.nextop) - if self.nextop.getopnum() == rop.GUARD_NO_OVERFLOW: - # Synthesize the non overflowing op for optimize_default to reuse - self.pure(rop.INT_ADD, op.getarglist()[:], op.result) - # Synthesize the reverse op for optimize_default to reuse - self.pure(rop.INT_SUB, [op.result, op.getarg(1)], op.getarg(0)) - self.pure(rop.INT_SUB, [op.result, op.getarg(0)], op.getarg(1)) - + self.emit_operation(op) # emit the op + r = self.getvalue(op.result) + r.intbound.intersect(resbound) def optimize_INT_SUB_OVF(self, op): v1 = self.getvalue(op.getarg(0)) v2 = self.getvalue(op.getarg(1)) resbound = v1.intbound.sub_bound(v2.intbound) - if resbound.has_lower and resbound.has_upper and \ - self.nextop.getopnum() == rop.GUARD_NO_OVERFLOW: - # Transform into INT_SUB and remove guard + if resbound.bounded(): op = op.copy_and_change(rop.INT_SUB) - self.optimize_INT_SUB(op) # emit the op - else: - self.emit_operation(op) - r = self.getvalue(op.result) - r.intbound.intersect(resbound) - self.emit_operation(self.nextop) - if self.nextop.getopnum() == rop.GUARD_NO_OVERFLOW: - # Synthesize the non overflowing op for optimize_default to reuse - self.pure(rop.INT_SUB, op.getarglist()[:], op.result) - # Synthesize the reverse ops for optimize_default to reuse - self.pure(rop.INT_ADD, [op.result, op.getarg(1)], op.getarg(0)) - self.pure(rop.INT_SUB, [op.getarg(0), op.result], op.getarg(1)) - + self.emit_operation(op) # emit the op + r = self.getvalue(op.result) + r.intbound.intersect(resbound) def optimize_INT_MUL_OVF(self, op): v1 = self.getvalue(op.getarg(0)) v2 = self.getvalue(op.getarg(1)) resbound = v1.intbound.mul_bound(v2.intbound) - if resbound.has_lower and resbound.has_upper and \ - self.nextop.getopnum() == rop.GUARD_NO_OVERFLOW: - # Transform into INT_MUL and remove guard + if resbound.bounded(): op = op.copy_and_change(rop.INT_MUL) - self.optimize_INT_MUL(op) # emit the op - else: - self.emit_operation(op) - r = self.getvalue(op.result) - r.intbound.intersect(resbound) - self.emit_operation(self.nextop) - if self.nextop.getopnum() == rop.GUARD_NO_OVERFLOW: - # Synthesize the non overflowing op for optimize_default to reuse - self.pure(rop.INT_MUL, op.getarglist()[:], op.result) - + self.emit_operation(op) + r = self.getvalue(op.result) + r.intbound.intersect(resbound) def optimize_INT_LT(self, op): v1 = self.getvalue(op.getarg(0)) diff --git a/pypy/jit/metainterp/optimizeopt/optimizer.py b/pypy/jit/metainterp/optimizeopt/optimizer.py --- a/pypy/jit/metainterp/optimizeopt/optimizer.py +++ b/pypy/jit/metainterp/optimizeopt/optimizer.py @@ -6,7 +6,7 @@ IntLowerBound, MININT, MAXINT from pypy.jit.metainterp.optimizeopt.util import (make_dispatcher_method, args_dict) -from pypy.jit.metainterp.resoperation import rop, ResOperation +from pypy.jit.metainterp.resoperation import rop, ResOperation, AbstractResOp from pypy.jit.metainterp.typesystem import llhelper, oohelper from pypy.tool.pairtype import extendabletype from pypy.rlib.debug import debug_start, debug_stop, debug_print @@ -249,6 +249,8 @@ CVAL_ZERO_FLOAT = ConstantValue(Const._new(0.0)) llhelper.CVAL_NULLREF = ConstantValue(llhelper.CONST_NULL) oohelper.CVAL_NULLREF = ConstantValue(oohelper.CONST_NULL) +REMOVED = AbstractResOp(None) + class Optimization(object): next_optimization = None @@ -260,6 +262,7 @@ raise NotImplementedError def emit_operation(self, op): + self.last_emitted_operation = op self.next_optimization.propagate_forward(op) # FIXME: Move some of these here? @@ -327,13 +330,13 @@ def forget_numberings(self, box): self.optimizer.forget_numberings(box) + class Optimizer(Optimization): - def __init__(self, metainterp_sd, loop, optimizations=None, bridge=False): + def __init__(self, metainterp_sd, loop, optimizations=None): self.metainterp_sd = metainterp_sd self.cpu = metainterp_sd.cpu self.loop = loop - self.bridge = bridge self.values = {} self.interned_refs = self.cpu.ts.new_ref_dict() self.interned_ints = {} @@ -341,7 +344,6 @@ self.bool_boxes = {} self.producer = {} self.pendingfields = [] - self.exception_might_have_happened = False self.quasi_immutable_deps = None self.opaque_pointers = {} self.replaces_guard = {} @@ -363,6 +365,7 @@ optimizations[-1].next_optimization = self for o in optimizations: o.optimizer = self + o.last_emitted_operation = None o.setup() else: optimizations = [] @@ -497,7 +500,6 @@ return CVAL_ZERO def propagate_all_forward(self): - self.exception_might_have_happened = self.bridge self.clear_newoperations() for op in self.loop.operations: self.first_optimization.propagate_forward(op) diff --git a/pypy/jit/metainterp/optimizeopt/pure.py b/pypy/jit/metainterp/optimizeopt/pure.py --- a/pypy/jit/metainterp/optimizeopt/pure.py +++ b/pypy/jit/metainterp/optimizeopt/pure.py @@ -1,4 +1,4 @@ -from pypy.jit.metainterp.optimizeopt.optimizer import Optimization +from pypy.jit.metainterp.optimizeopt.optimizer import Optimization, REMOVED from pypy.jit.metainterp.resoperation import rop, ResOperation from pypy.jit.metainterp.optimizeopt.util import (make_dispatcher_method, args_dict) @@ -61,7 +61,10 @@ oldop = self.pure_operations.get(args, None) if oldop is not None and oldop.getdescr() is op.getdescr(): assert oldop.getopnum() == op.getopnum() + # this removes a CALL_PURE that has the same (non-constant) + # arguments as a previous CALL_PURE. self.make_equal_to(op.result, self.getvalue(oldop.result)) + self.last_emitted_operation = REMOVED return else: self.pure_operations[args] = op @@ -72,6 +75,13 @@ self.emit_operation(ResOperation(rop.CALL, args, op.result, op.getdescr())) + def optimize_GUARD_NO_EXCEPTION(self, op): + if self.last_emitted_operation is REMOVED: + # it was a CALL_PURE that was killed; so we also kill the + # following GUARD_NO_EXCEPTION + return + self.emit_operation(op) + def flush(self): assert self.posponedop is None diff --git a/pypy/jit/metainterp/optimizeopt/rewrite.py b/pypy/jit/metainterp/optimizeopt/rewrite.py --- a/pypy/jit/metainterp/optimizeopt/rewrite.py +++ b/pypy/jit/metainterp/optimizeopt/rewrite.py @@ -294,12 +294,6 @@ raise InvalidLoop self.optimize_GUARD_CLASS(op) - def optimize_GUARD_NO_EXCEPTION(self, op): - if not self.optimizer.exception_might_have_happened: - return - self.emit_operation(op) - self.optimizer.exception_might_have_happened = False - def optimize_CALL_LOOPINVARIANT(self, op): arg = op.getarg(0) # 'arg' must be a Const, because residual_call in codewriter @@ -310,6 +304,7 @@ resvalue = self.loop_invariant_results.get(key, None) if resvalue is not None: self.make_equal_to(op.result, resvalue) + self.last_emitted_operation = REMOVED return # change the op to be a normal call, from the backend's point of view # there is no reason to have a separate operation for this @@ -444,10 +439,19 @@ except KeyError: pass else: + # this removes a CALL_PURE with all constant arguments. self.make_constant(op.result, result) + self.last_emitted_operation = REMOVED return self.emit_operation(op) + def optimize_GUARD_NO_EXCEPTION(self, op): + if self.last_emitted_operation is REMOVED: + # it was a CALL_PURE or a CALL_LOOPINVARIANT that was killed; + # so we also kill the following GUARD_NO_EXCEPTION + return + self.emit_operation(op) + def optimize_INT_FLOORDIV(self, op): v1 = self.getvalue(op.getarg(0)) v2 = self.getvalue(op.getarg(1)) diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py --- a/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py +++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py @@ -681,25 +681,60 @@ # ---------- - def test_fold_guard_no_exception(self): - ops = """ - [i] - guard_no_exception() [] - i1 = int_add(i, 3) - guard_no_exception() [] + def test_keep_guard_no_exception(self): + ops = """ + [i1] i2 = call(i1, descr=nonwritedescr) guard_no_exception() [i1, i2] - guard_no_exception() [] - i3 = call(i2, descr=nonwritedescr) - jump(i1) # the exception is considered lost when we loop back - """ - expected = """ - [i] - i1 = int_add(i, 3) - i2 = call(i1, descr=nonwritedescr) + jump(i2) + """ + self.optimize_loop(ops, ops) + + def test_keep_guard_no_exception_with_call_pure_that_is_not_folded(self): + ops = """ + [i1] + i2 = call_pure(123456, i1, descr=nonwritedescr) guard_no_exception() [i1, i2] - i3 = call(i2, descr=nonwritedescr) - jump(i1) + jump(i2) + """ + expected = """ + [i1] + i2 = call(123456, i1, descr=nonwritedescr) + guard_no_exception() [i1, i2] + jump(i2) + """ + self.optimize_loop(ops, expected) + + def test_remove_guard_no_exception_with_call_pure_on_constant_args(self): + arg_consts = [ConstInt(i) for i in (123456, 81)] + call_pure_results = {tuple(arg_consts): ConstInt(5)} + ops = """ + [i1] + i3 = same_as(81) + i2 = call_pure(123456, i3, descr=nonwritedescr) + guard_no_exception() [i1, i2] + jump(i2) + """ + expected = """ + [i1] + jump(5) + """ + self.optimize_loop(ops, expected, call_pure_results) + + def test_remove_guard_no_exception_with_duplicated_call_pure(self): + ops = """ + [i1] + i2 = call_pure(123456, i1, descr=nonwritedescr) + guard_no_exception() [i1, i2] + i3 = call_pure(123456, i1, descr=nonwritedescr) + guard_no_exception() [i1, i2, i3] + jump(i3) + """ + expected = """ + [i1] + i2 = call(123456, i1, descr=nonwritedescr) + guard_no_exception() [i1, i2] + jump(i2) """ self.optimize_loop(ops, expected) diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py --- a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py +++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py @@ -931,17 +931,14 @@ [i] guard_no_exception() [] i1 = int_add(i, 3) - guard_no_exception() [] i2 = call(i1, descr=nonwritedescr) guard_no_exception() [i1, i2] - guard_no_exception() [] i3 = call(i2, descr=nonwritedescr) jump(i1) # the exception is considered lost when we loop back """ - # note that 'guard_no_exception' at the very start is kept around - # for bridges, but not for loops preamble = """ [i] + guard_no_exception() [] # occurs at the start of bridges, so keep it i1 = int_add(i, 3) i2 = call(i1, descr=nonwritedescr) guard_no_exception() [i1, i2] @@ -950,6 +947,7 @@ """ expected = """ [i] + guard_no_exception() [] # occurs at the start of bridges, so keep it i1 = int_add(i, 3) i2 = call(i1, descr=nonwritedescr) guard_no_exception() [i1, i2] @@ -959,7 +957,6 @@ self.optimize_loop(ops, expected, preamble) def test_bug_guard_no_exception(self): - py.test.skip("missing optimization for this corner case") ops = """ [] i0 = call(123, descr=nonwritedescr) @@ -6299,12 +6296,15 @@ def test_str2unicode_constant(self): ops = """ [] + escape(1213) p0 = call(0, "xy", descr=s2u_descr) # string -> unicode + guard_no_exception() [] escape(p0) jump() """ expected = """ [] + escape(1213) escape(u"xy") jump() """ @@ -6314,6 +6314,7 @@ ops = """ [p0] p1 = call(0, p0, descr=s2u_descr) # string -> unicode + guard_no_exception() [] escape(p1) jump(p1) """ diff --git a/pypy/jit/metainterp/optimizeopt/vstring.py b/pypy/jit/metainterp/optimizeopt/vstring.py --- a/pypy/jit/metainterp/optimizeopt/vstring.py +++ b/pypy/jit/metainterp/optimizeopt/vstring.py @@ -2,7 +2,8 @@ from pypy.jit.metainterp.history import (BoxInt, Const, ConstInt, ConstPtr, get_const_ptr_for_string, get_const_ptr_for_unicode, BoxPtr, REF, INT) from pypy.jit.metainterp.optimizeopt import optimizer, virtualize -from pypy.jit.metainterp.optimizeopt.optimizer import CONST_0, CONST_1, llhelper +from pypy.jit.metainterp.optimizeopt.optimizer import CONST_0, CONST_1 +from pypy.jit.metainterp.optimizeopt.optimizer import llhelper, REMOVED from pypy.jit.metainterp.optimizeopt.util import make_dispatcher_method from pypy.jit.metainterp.resoperation import rop, ResOperation from pypy.rlib.objectmodel import specialize, we_are_translated @@ -529,6 +530,11 @@ optimize_CALL_PURE = optimize_CALL + def optimize_GUARD_NO_EXCEPTION(self, op): + if self.last_emitted_operation is REMOVED: + return + self.emit_operation(op) + def opt_call_str_STR2UNICODE(self, op): # Constant-fold unicode("constant string"). # More generally, supporting non-constant but virtual cases is @@ -543,6 +549,7 @@ except UnicodeDecodeError: return False self.make_constant(op.result, get_const_ptr_for_unicode(u)) + self.last_emitted_operation = REMOVED return True def opt_call_stroruni_STR_CONCAT(self, op, mode): diff --git a/pypy/jit/metainterp/resoperation.py b/pypy/jit/metainterp/resoperation.py --- a/pypy/jit/metainterp/resoperation.py +++ b/pypy/jit/metainterp/resoperation.py @@ -90,7 +90,10 @@ return op def __repr__(self): - return self.repr() + try: + return self.repr() + except NotImplementedError: + return object.__repr__(self) def repr(self, graytext=False): # RPython-friendly version _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit