Author: Armin Rigo <[email protected]>
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
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit