[pypy-commit] pypy default: Optimization for the JIT: do not escape the frame when seeing code that
arigo
noreply at buildbot.pypy.org
Fri Mar 30 09:42:03 CEST 2012
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r54091:42c400cba53a
Date: 2012-03-30 01:17 +0200
http://bitbucket.org/pypy/pypy/changeset/42c400cba53a/
Log: Optimization for the JIT: do not escape the frame when seeing code
that reads e.g. 'sys.exc_info()[1]' or 'sys.exc_info()[:2]'. The
frame would escape only if we read the last item of the returned
tuple. Lots of tweaks needed, but at least in the simple cases it
should work.
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -296,6 +296,7 @@
self.check_signal_action = None # changed by the signal module
self.user_del_action = UserDelAction(self)
self.frame_trace_action = FrameTraceAction(self)
+ self._code_of_sys_exc_info = None
from pypy.interpreter.pycode import cpython_magic, default_magic
self.our_magic = default_magic
diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -154,6 +154,7 @@
#operationerr.print_detailed_traceback(self.space)
def _convert_exc(self, operr):
+ # Only for the flow object space
return operr
def sys_exc_info(self): # attn: the result is not the wrapped sys.exc_info() !!!
diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py
--- a/pypy/interpreter/function.py
+++ b/pypy/interpreter/function.py
@@ -113,6 +113,12 @@
from pypy.interpreter.pycode import PyCode
code = self.getcode() # hook for the jit
+ #
+ if (jit.we_are_jitted() and code is self.space._code_of_sys_exc_info
+ and nargs == 0):
+ from pypy.module.sys.vm import exc_info_direct
+ return exc_info_direct(self.space, frame)
+ #
fast_natural_arity = code.fast_natural_arity
if nargs == fast_natural_arity:
if nargs == 0:
diff --git a/pypy/interpreter/gateway.py b/pypy/interpreter/gateway.py
--- a/pypy/interpreter/gateway.py
+++ b/pypy/interpreter/gateway.py
@@ -874,6 +874,12 @@
fn.add_to_table()
if gateway.as_classmethod:
fn = ClassMethod(space.wrap(fn))
+ #
+ from pypy.module.sys.vm import exc_info
+ if code._bltin is exc_info:
+ assert space._code_of_sys_exc_info is None
+ space._code_of_sys_exc_info = code
+ #
return fn
diff --git a/pypy/module/sys/test/test_sysmodule.py b/pypy/module/sys/test/test_sysmodule.py
--- a/pypy/module/sys/test/test_sysmodule.py
+++ b/pypy/module/sys/test/test_sysmodule.py
@@ -595,3 +595,121 @@
assert len(frames) == 1
_, other_frame = frames.popitem()
assert other_frame.f_code.co_name in ('other_thread', '?')
+
+
+class AppTestSysExcInfoDirect:
+
+ def setup_method(self, meth):
+ self.seen = []
+ from pypy.module.sys import vm
+ def exc_info_with_tb(*args):
+ self.seen.append("n") # not optimized
+ return self.old[0](*args)
+ def exc_info_without_tb(*args):
+ self.seen.append("y") # optimized
+ return self.old[1](*args)
+ self.old = [vm.exc_info_with_tb, vm.exc_info_without_tb]
+ vm.exc_info_with_tb = exc_info_with_tb
+ vm.exc_info_without_tb = exc_info_without_tb
+ #
+ from pypy.rlib import jit
+ self.old2 = [jit.we_are_jitted]
+ jit.we_are_jitted = lambda: True
+
+ def teardown_method(self, meth):
+ from pypy.module.sys import vm
+ from pypy.rlib import jit
+ vm.exc_info_with_tb = self.old[0]
+ vm.exc_info_without_tb = self.old[1]
+ jit.we_are_jitted = self.old2[0]
+ #
+ assert ''.join(self.seen) == meth.expected
+
+ def test_returns_none(self):
+ import sys
+ assert sys.exc_info() == (None, None, None)
+ assert sys.exc_info()[0] is None
+ assert sys.exc_info()[1] is None
+ assert sys.exc_info()[2] is None
+ assert sys.exc_info()[:2] == (None, None)
+ assert sys.exc_info()[:3] == (None, None, None)
+ assert sys.exc_info()[0:2] == (None, None)
+ assert sys.exc_info()[2:4] == (None,)
+ test_returns_none.expected = 'nnnnnnnn'
+
+ def test_returns_subscr(self):
+ import sys
+ e = KeyError("boom")
+ try:
+ raise e
+ except:
+ assert sys.exc_info()[0] is KeyError # y
+ assert sys.exc_info()[1] is e # y
+ assert sys.exc_info()[2] is not None # n
+ assert sys.exc_info()[-3] is KeyError # y
+ assert sys.exc_info()[-2] is e # y
+ assert sys.exc_info()[-1] is not None # n
+ test_returns_subscr.expected = 'yynyyn'
+
+ def test_returns_slice_2(self):
+ import sys
+ e = KeyError("boom")
+ try:
+ raise e
+ except:
+ foo = sys.exc_info() # n
+ assert sys.exc_info()[:0] == () # y
+ assert sys.exc_info()[:1] == foo[:1] # y
+ assert sys.exc_info()[:2] == foo[:2] # y
+ assert sys.exc_info()[:3] == foo # n
+ assert sys.exc_info()[:4] == foo # n
+ assert sys.exc_info()[:-1] == foo[:2] # y
+ assert sys.exc_info()[:-2] == foo[:1] # y
+ assert sys.exc_info()[:-3] == () # y
+ test_returns_slice_2.expected = 'nyyynnyyy'
+
+ def test_returns_slice_3(self):
+ import sys
+ e = KeyError("boom")
+ try:
+ raise e
+ except:
+ foo = sys.exc_info() # n
+ assert sys.exc_info()[2:2] == () # y
+ assert sys.exc_info()[0:1] == foo[:1] # y
+ assert sys.exc_info()[1:2] == foo[1:2] # y
+ assert sys.exc_info()[0:3] == foo # n
+ assert sys.exc_info()[2:4] == foo[2:] # n
+ assert sys.exc_info()[0:-1] == foo[:2] # y
+ assert sys.exc_info()[0:-2] == foo[:1] # y
+ assert sys.exc_info()[5:-3] == () # y
+ test_returns_slice_3.expected = 'nyyynnyyy'
+
+ def test_strange_invocation(self):
+ import sys
+ e = KeyError("boom")
+ try:
+ raise e
+ except:
+ a = []; k = {}
+ assert sys.exc_info(*a)[:0] == ()
+ assert sys.exc_info(**k)[:0] == ()
+ test_strange_invocation.expected = 'nn'
+
+ def test_call_in_subfunction(self):
+ import sys
+ def g():
+ # this case is not optimized, because we need to search the
+ # frame chain. it's probably not worth the complications
+ return sys.exc_info()[1]
+ e = KeyError("boom")
+ try:
+ raise e
+ except:
+ assert g() is e
+ test_call_in_subfunction.expected = 'n'
+
+
+class AppTestSysExcInfoDirectCallMethod(AppTestSysExcInfoDirect):
+ def setup_class(cls):
+ cls.space = gettestobjspace(**{"objspace.opcodes.CALL_METHOD": True})
diff --git a/pypy/module/sys/vm.py b/pypy/module/sys/vm.py
--- a/pypy/module/sys/vm.py
+++ b/pypy/module/sys/vm.py
@@ -89,6 +89,9 @@
"""Return the (type, value, traceback) of the most recent exception
caught by an except clause in the current stack frame or in an older stack
frame."""
+ return exc_info_with_tb(space) # indirection for the tests
+
+def exc_info_with_tb(space):
operror = space.getexecutioncontext().sys_exc_info()
if operror is None:
return space.newtuple([space.w_None,space.w_None,space.w_None])
@@ -96,6 +99,59 @@
return space.newtuple([operror.w_type, operror.get_w_value(space),
space.wrap(operror.get_traceback())])
+def exc_info_without_tb(space, frame):
+ operror = frame.last_exception
+ return space.newtuple([operror.w_type, operror.get_w_value(space),
+ space.w_None])
+
+def exc_info_direct(space, frame):
+ from pypy.tool import stdlib_opcode
+ # In order to make the JIT happy, we try to return (exc, val, None)
+ # instead of (exc, val, tb). We can do that only if we recognize
+ # the following pattern in the bytecode:
+ # CALL_FUNCTION/CALL_METHOD <-- invoking me
+ # LOAD_CONST 0, 1, -2 or -3
+ # BINARY_SUBSCR
+ # or:
+ # CALL_FUNCTION/CALL_METHOD
+ # LOAD_CONST <=2
+ # SLICE_2
+ # or:
+ # CALL_FUNCTION/CALL_METHOD
+ # LOAD_CONST any integer
+ # LOAD_CONST <=2
+ # SLICE_3
+ need_all_three_args = True
+ co = frame.getcode().co_code
+ p = frame.last_instr
+ if (ord(co[p]) == stdlib_opcode.CALL_FUNCTION or
+ ord(co[p]) == stdlib_opcode.CALL_METHOD):
+ if ord(co[p+3]) == stdlib_opcode.LOAD_CONST:
+ lo = ord(co[p+4])
+ hi = ord(co[p+5])
+ w_constant = frame.getconstant_w((hi * 256) | lo)
+ if space.isinstance_w(w_constant, space.w_int):
+ constant = space.int_w(w_constant)
+ if ord(co[p+6]) == stdlib_opcode.BINARY_SUBSCR:
+ if -3 <= constant <= 1 and constant != -1:
+ need_all_three_args = False
+ elif ord(co[p+6]) == stdlib_opcode.SLICE+2:
+ if constant <= 2:
+ need_all_three_args = False
+ elif (ord(co[p+6]) == stdlib_opcode.LOAD_CONST and
+ ord(co[p+9]) == stdlib_opcode.SLICE+3):
+ lo = ord(co[p+7])
+ hi = ord(co[p+8])
+ w_constant = frame.getconstant_w((hi * 256) | lo)
+ if space.isinstance_w(w_constant, space.w_int):
+ if space.int_w(w_constant) <= 2:
+ need_all_three_args = False
+ #
+ if need_all_three_args or frame.last_exception is None or frame.hide():
+ return exc_info_with_tb(space)
+ else:
+ return exc_info_without_tb(space, frame)
+
def exc_clear(space):
"""Clear global information on the current exception. Subsequent calls
to exc_info() will return (None,None,None) until another exception is
More information about the pypy-commit
mailing list