[pypy-svn] r67604 - in pypy/trunk/pypy: jit/metainterp jit/metainterp/test rlib translator

cfbolz at codespeak.net cfbolz at codespeak.net
Thu Sep 10 12:12:41 CEST 2009


Author: cfbolz
Date: Thu Sep 10 12:12:41 2009
New Revision: 67604

Modified:
   pypy/trunk/pypy/jit/metainterp/history.py
   pypy/trunk/pypy/jit/metainterp/pyjitpl.py
   pypy/trunk/pypy/jit/metainterp/test/test_basic.py
   pypy/trunk/pypy/jit/metainterp/test/test_recursive.py
   pypy/trunk/pypy/jit/metainterp/warmspot.py
   pypy/trunk/pypy/rlib/jit.py
   pypy/trunk/pypy/translator/driver.py
Log:
merge agressive-inlining branch


Modified: pypy/trunk/pypy/jit/metainterp/history.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/history.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/history.py	Thu Sep 10 12:12:41 2009
@@ -788,6 +788,7 @@
 
     compiled_count = 0
     enter_count = 0
+    aborted_count = 0
 
     def __init__(self):
         self.loops = []
@@ -853,10 +854,9 @@
 
 class Options:
     logger_noopt = None
-    def __init__(self, specialize=True, listops=False, inline=False):
+    def __init__(self, specialize=True, listops=False):
         self.specialize = specialize
         self.listops = listops
-        self.inline = inline
     def _freeze_(self):
         return True
 

Modified: pypy/trunk/pypy/jit/metainterp/pyjitpl.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/pyjitpl.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/pyjitpl.py	Thu Sep 10 12:12:41 2009
@@ -645,12 +645,12 @@
 
     @arguments("descr", "varargs")
     def opimpl_recursive_call(self, calldescr, varargs):
-        if self.metainterp.staticdata.options.inline:
+        warmrunnerstate = self.metainterp.staticdata.state
+        if warmrunnerstate.inlining:
             num_green_args = self.metainterp.staticdata.num_green_args
             portal_code = self.metainterp.staticdata.portal_code
             greenkey = varargs[1:num_green_args + 1]
             if self.metainterp.staticdata.state.can_inline_callable(greenkey):
-                self.metainterp.in_recursion += 1
                 return self.perform_call(portal_code, varargs[1:])
         return self.execute_with_exc(rop.CALL, varargs, descr=calldescr)
 
@@ -1128,14 +1128,20 @@
     def newframe(self, jitcode):
         if not we_are_translated():
             self._debug_history.append(['enter', jitcode, None])
+        if jitcode is self.staticdata.portal_code:
+            self.in_recursion += 1
         f = MIFrame(self, jitcode)
         self.framestack.append(f)
         return f
 
-    def finishframe(self, resultbox):
+    def popframe(self):
         frame = self.framestack.pop()
         if frame.jitcode is self.staticdata.portal_code:
             self.in_recursion -= 1
+        return frame
+
+    def finishframe(self, resultbox):
+        frame = self.popframe()
         if not we_are_translated():
             self._debug_history.append(['leave', frame.jitcode, None])
         if self.framestack:
@@ -1178,11 +1184,29 @@
                 return True
             if not we_are_translated():
                 self._debug_history.append(['leave_exc', frame.jitcode, None])
-            self.framestack.pop()
+            self.popframe()
         if not self.is_blackholing():
             self.compile_exit_frame_with_exception(excvaluebox)
         raise self.staticdata.ExitFrameWithExceptionRef(self.cpu, excvaluebox.getref_base())
 
+    def check_recursion_invariant(self):
+        in_recursion = -1
+        for frame in self.framestack:
+            jitcode = frame.jitcode
+            if jitcode is self.staticdata.portal_code:
+                in_recursion += 1
+        if in_recursion != self.in_recursion:
+            print "in_recursion problem!!!"
+            print in_recursion, self.in_recursion
+            for frame in self.framestack:
+                jitcode = frame.jitcode
+                if jitcode is self.staticdata.portal_code:
+                    print "P",
+                else:
+                    print " ",
+                print jitcode.name
+            raise Exception
+
     def raise_overflow_error(self):
         etype, evalue = self.cpu.get_overflow_error()
         return self.finishframe_exception(
@@ -1196,6 +1220,7 @@
             self.cpu.ts.get_exc_value_box(evalue))
 
     def create_empty_history(self):
+        warmrunnerstate = self.staticdata.state
         self.history = history.History(self.cpu)
         if self.staticdata.stats is not None:
             self.staticdata.stats.history = self.history
@@ -1245,6 +1270,19 @@
             op.pc = self.framestack[-1].pc
             op.name = self.framestack[-1].jitcode.name
 
+    def switch_to_blackhole_if_trace_too_long(self):
+        if not self.is_blackholing():
+            warmrunnerstate = self.staticdata.state
+            if len(self.history.operations) > warmrunnerstate.trace_limit:
+                self.history = history.BlackHole(self.cpu)
+                if not we_are_translated():
+                    self.staticdata.stats.aborted_count += 1
+                    history.log.event('ABORTING TRACING' + self.history.extratext)
+                elif DEBUG:
+                    debug_print('~~~ ABORTING TRACING', self.history.extratext)
+                self.staticdata.profiler.end_tracing()
+                self.staticdata.profiler.start_blackhole()
+
     def _interpret(self):
         # Execute the frames forward until we raise a DoneWithThisFrame,
         # a ContinueRunningNormally, or a GenerateMergePoint exception.
@@ -1256,6 +1294,9 @@
         try:
             while True:
                 self.framestack[-1].run_one_step()
+                self.switch_to_blackhole_if_trace_too_long()
+                if not we_are_translated():
+                    self.check_recursion_invariant()
         finally:
             if self.is_blackholing():
                 self.staticdata.profiler.end_blackhole()
@@ -1292,7 +1333,8 @@
             return self.designate_target_loop(gmp)
 
     def handle_guard_failure(self, exec_result, key):
-        self.initialize_state_from_guard_failure(exec_result)
+        from pypy.jit.metainterp.warmspot import ContinueRunningNormallyBase
+        resumedescr = self.initialize_state_from_guard_failure(exec_result)
         assert isinstance(key, compile.ResumeGuardDescr)
         top_history = key.find_toplevel_history()
         source_loop = top_history.source_link
@@ -1302,12 +1344,18 @@
         self.resumekey = key
         self.seen_can_enter_jit = False
         guard_op = key.get_guard_op()
+        started_as_blackhole = self.is_blackholing()
         try:
             self.prepare_resume_from_failure(guard_op.opnum)
             self.interpret()
             assert False, "should always raise"
         except GenerateMergePoint, gmp:
             return self.designate_target_loop(gmp)
+        except ContinueRunningNormallyBase:
+            if not started_as_blackhole:
+                warmrunnerstate = self.staticdata.state
+                warmrunnerstate.reset_counter_from_failure(resumedescr)
+            raise
 
     def forget_consts(self, boxes, startindex=0):
         for i in range(startindex, len(boxes)):
@@ -1499,7 +1547,7 @@
                                         *args[1:])
 
     def initialize_state_from_start(self, *args):
-        self.in_recursion = 0
+        self.in_recursion = -1 # always one portal around
         self.staticdata._setup_once()
         self.staticdata.profiler.start_tracing()
         self.create_empty_history()
@@ -1516,7 +1564,7 @@
 
     def initialize_state_from_guard_failure(self, guard_failure):
         # guard failure: rebuild a complete MIFrame stack
-        self.in_recursion = 0
+        self.in_recursion = -1 # always one portal around
         resumedescr = guard_failure.descr
         assert isinstance(resumedescr, compile.ResumeGuardDescr)
         warmrunnerstate = self.staticdata.state
@@ -1542,6 +1590,7 @@
             # the BlackHole is invalid because it doesn't start with
             # guard_failure.key.guard_op.suboperations, but that's fine
         self.rebuild_state_after_failure(resumedescr, guard_failure.args)
+        return resumedescr
 
     def initialize_virtualizable(self, original_boxes):
         vinfo = self.staticdata.virtualizable_info
@@ -1642,10 +1691,7 @@
             jitcode, pc, exception_target = resumereader.consume_frame_info()
             env = resumereader.consume_boxes()
             f = self.newframe(jitcode)
-            if jitcode is self.staticdata.portal_code:
-                self.in_recursion += 1
             f.setup_resume_at_op(pc, exception_target, env)
-        self.in_recursion -= 1 # always one portal around
 
     def check_synchronized_virtualizable(self):
         if not we_are_translated():

Modified: pypy/trunk/pypy/jit/metainterp/test/test_basic.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_basic.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_basic.py	Thu Sep 10 12:12:41 2009
@@ -1,4 +1,5 @@
 import py
+import sys
 from pypy.rlib.jit import JitDriver, we_are_jitted, hint, dont_look_inside
 from pypy.jit.metainterp.warmspot import ll_meta_interp, get_stats
 from pypy.jit.backend.llgraph import runner
@@ -49,6 +50,8 @@
         assert get_stats().enter_count <= count
     def check_jumps(self, maxcount):
         assert get_stats().exec_jumps <= maxcount
+    def check_aborted_count(self, maxcount):
+        assert get_stats().aborted_count == maxcount
 
     def meta_interp(self, *args, **kwds):
         kwds['CPUClass'] = self.CPUClass
@@ -70,6 +73,8 @@
         class FakeWarmRunnerDesc:
             def attach_unoptimized_bridge_from_interp(self, greenkey, newloop):
                 pass
+
+            trace_limit = sys.maxint
         
         if policy is None:
             policy = JitPolicy()

Modified: pypy/trunk/pypy/jit/metainterp/test/test_recursive.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_recursive.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_recursive.py	Thu Sep 10 12:12:41 2009
@@ -4,7 +4,7 @@
 from pypy.jit.metainterp import simple_optimize
 from pypy.jit.metainterp.policy import StopAtXPolicy
 from pypy.rpython.annlowlevel import hlstr
-from pypy.jit.metainterp.warmspot import CannotInlineCanEnterJit
+from pypy.jit.metainterp.warmspot import CannotInlineCanEnterJit, get_stats
 
 class RecursiveTests:
 
@@ -213,6 +213,170 @@
         res = self.meta_interp(main, [100], optimizer=simple_optimize, inline=True)
         assert res == 0
 
+    def test_exception_in_inlined_function(self):
+        from pypy.rpython.annlowlevel import hlstr
+        def p(code, pc):
+            code = hlstr(code)
+            return "%s %d %s" % (code, pc, code[pc])
+        def c(code, pc):
+            return "l" not in hlstr(code)
+        myjitdriver = JitDriver(greens=['code', 'pc'], reds=['n'],
+                                get_printable_location=p, can_inline=c)
+
+        class Exc(Exception):
+            pass
+        
+        def f(code, n):
+            pc = 0
+            while pc < len(code):
+
+                myjitdriver.jit_merge_point(n=n, code=code, pc=pc)
+                op = code[pc]
+                if op == "-":
+                    n -= 1
+                elif op == "c":
+                    try:
+                        n = f("---i---", n)
+                    except Exc:
+                        pass
+                elif op == "i":
+                    if n % 5 == 1:
+                        raise Exc
+                elif op == "l":
+                    if n > 0:
+                        myjitdriver.can_enter_jit(n=n, code=code, pc=0)
+                        pc = 0
+                        continue
+                else:
+                    assert 0
+                pc += 1
+            return n
+        def main(n):
+            return f("c-l", n)
+        res = self.meta_interp(main, [100], optimizer=simple_optimize, inline=True)
+        assert res == main(100)
+
+    def test_recurse_during_blackholing(self):
+        # this passes, if the blackholing shortcut for calls is turned off
+        # it fails, it is very delicate in terms of parameters,
+        # bridge/loop creation order
+        from pypy.rpython.annlowlevel import hlstr
+        def p(code, pc):
+            code = hlstr(code)
+            return "%s %d %s" % (code, pc, code[pc])
+        def c(code, pc):
+            return "l" not in hlstr(code)
+        myjitdriver = JitDriver(greens=['code', 'pc'], reds=['n'],
+                                get_printable_location=p, can_inline=c)
+        
+        def f(code, n):
+            pc = 0
+            while pc < len(code):
+
+                myjitdriver.jit_merge_point(n=n, code=code, pc=pc)
+                op = code[pc]
+                if op == "-":
+                    n -= 1
+                elif op == "c":
+                    if n < 70 and n % 3 == 1:
+                        print "F"
+                        n = f("--", n)
+                elif op == "l":
+                    if n > 0:
+                        myjitdriver.can_enter_jit(n=n, code=code, pc=0)
+                        pc = 0
+                        continue
+                else:
+                    assert 0
+                pc += 1
+            return n
+        def main(n):
+            myjitdriver.set_param('threshold', 3)
+            myjitdriver.set_param('trace_eagerness', 5)            
+            return f("c-l", n)
+        expected = main(100)
+        res = self.meta_interp(main, [100], optimizer=simple_optimize, inline=True)
+        assert res == expected
+
+    def check_max_trace_length(self, length):
+        for loop in get_stats().loops:
+            assert len(loop.operations) <= length + 5 # because we only check once per metainterp bytecode
+            for op in loop.operations:
+                if op.is_guard():
+                    assert len(op.suboperations) <= length + 5
+
+    def test_inline_trace_limit(self):
+        myjitdriver = JitDriver(greens=[], reds=['n'])
+        def recursive(n):
+            if n > 0:
+                return recursive(n - 1) + 1
+            return 0
+        def loop(n):            
+            myjitdriver.set_param("threshold", 10)
+            pc = 0
+            while n:
+                myjitdriver.can_enter_jit(n=n)
+                myjitdriver.jit_merge_point(n=n)
+                n = recursive(n)
+                n -= 1
+            return n
+        TRACE_LIMIT = 66
+        res = self.meta_interp(loop, [100], optimizer=simple_optimize, inline=True, trace_limit=TRACE_LIMIT)
+        assert res == 0
+        self.check_max_trace_length(TRACE_LIMIT)
+        self.check_enter_count(15) # maybe
+        self.check_aborted_count(7)
+
+    def test_trace_limit_bridge(self):
+        def recursive(n):
+            if n > 0:
+                return recursive(n - 1) + 1
+            return 0
+        myjitdriver = JitDriver(greens=[], reds=['n'])
+        def loop(n):
+            myjitdriver.set_param("threshold", 4)
+            myjitdriver.set_param("trace_eagerness", 2)
+            while n:
+                myjitdriver.can_enter_jit(n=n)
+                myjitdriver.jit_merge_point(n=n)
+                if n % 5 == 0:
+                    n -= 1
+                if n < 50:
+                    n = recursive(n)
+                n -= 1
+        TRACE_LIMIT = 20
+        res = self.meta_interp(loop, [100], optimizer=simple_optimize, inline=True, trace_limit=TRACE_LIMIT)
+        self.check_max_trace_length(TRACE_LIMIT)
+        self.check_aborted_count(8)
+        self.check_enter_count_at_most(30)
+
+    def test_set_param_inlining(self):
+        myjitdriver = JitDriver(greens=[], reds=['n', 'recurse'])
+        def loop(n, recurse=False):
+            while n:
+                myjitdriver.jit_merge_point(n=n, recurse=recurse)
+                n -= 1
+                if not recurse:
+                    loop(10, True)
+                    myjitdriver.can_enter_jit(n=n, recurse=recurse)
+            return n
+        TRACE_LIMIT = 66
+ 
+        def main(inline):
+            myjitdriver.set_param("threshold", 10)
+            if inline:
+                myjitdriver.set_param('inlining', True)
+            else:
+                myjitdriver.set_param('inlining', False)
+            return loop(100)
+
+        res = self.meta_interp(main, [0], optimizer=simple_optimize, trace_limit=TRACE_LIMIT)
+        self.check_loops(call=1)
+
+        res = self.meta_interp(main, [1], optimizer=simple_optimize, trace_limit=TRACE_LIMIT)
+        self.check_loops(call=0)
+
+
 
 class TestLLtype(RecursiveTests, LLJitMixin):
     pass

Modified: pypy/trunk/pypy/jit/metainterp/warmspot.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/warmspot.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/warmspot.py	Thu Sep 10 12:12:41 2009
@@ -26,7 +26,9 @@
 # ____________________________________________________________
 # Bootstrapping
 
-def apply_jit(translator, backend_name="auto", debug_level="steps", **kwds):
+def apply_jit(translator, backend_name="auto", debug_level="steps",
+              inline=False,
+              **kwds):
     if 'CPUClass' not in kwds:
         from pypy.jit.backend.detect_cpu import getcpuclass
         kwds['CPUClass'] = getcpuclass(backend_name)
@@ -38,9 +40,9 @@
     warmrunnerdesc = WarmRunnerDesc(translator,
                                     translate_support_code=True,
                                     listops=True,
-                                    #inline=True,
                                     profile=profile,
                                     **kwds)
+    warmrunnerdesc.state.set_param_inlining(inline)    
     warmrunnerdesc.finish()
     translator.warmrunnerdesc = warmrunnerdesc    # for later debugging
 
@@ -52,13 +54,15 @@
     clear_tcache()
     return jittify_and_run(interp, graph, args, backendopt=backendopt, **kwds)
 
-def jittify_and_run(interp, graph, args, repeat=1, hash_bits=None, backendopt=False,
-                    **kwds):
+def jittify_and_run(interp, graph, args, repeat=1, hash_bits=None, backendopt=False, trace_limit=sys.maxint,
+                    inline=False, **kwds):
     translator = interp.typer.annotator.translator
     translator.config.translation.gc = "boehm"
     warmrunnerdesc = WarmRunnerDesc(translator, backendopt=backendopt, **kwds)
     warmrunnerdesc.state.set_param_threshold(3)          # for tests
     warmrunnerdesc.state.set_param_trace_eagerness(2)    # for tests
+    warmrunnerdesc.state.set_param_trace_limit(trace_limit)
+    warmrunnerdesc.state.set_param_inlining(inline)
     warmrunnerdesc.state.create_tables_now()             # for tests
     if hash_bits:
         warmrunnerdesc.state.set_param_hash_bits(hash_bits)
@@ -120,6 +124,9 @@
 class JitException(Exception):
     _go_through_llinterp_uncaught_ = True     # ugh
 
+class ContinueRunningNormallyBase(JitException):
+    pass
+
 class CannotInlineCanEnterJit(JitException):
     pass
 
@@ -409,7 +416,7 @@
             def __str__(self):
                 return 'ExitFrameWithExceptionRef(%s)' % (self.value,)
 
-        class ContinueRunningNormally(JitException):
+        class ContinueRunningNormally(ContinueRunningNormallyBase):
             def __init__(self, argboxes):
                 # accepts boxes as argument, but unpacks them immediately
                 # before we raise the exception -- the boxes' values will
@@ -728,6 +735,12 @@
         def set_param_trace_eagerness(self, value):
             self.trace_eagerness = value
 
+        def set_param_trace_limit(self, value):
+            self.trace_limit = value
+
+        def set_param_inlining(self, value):
+            self.inlining = value
+
         def set_param_hash_bits(self, value):
             if value < 1:
                 value = 1
@@ -772,7 +785,13 @@
                     self.create_tables_now()
                     return
                 metainterp = MetaInterp(metainterp_sd)
-                loop = metainterp.compile_and_run_once(*args)
+                try:
+                    loop = metainterp.compile_and_run_once(*args)
+                except warmrunnerdesc.ContinueRunningNormally:
+                    # the trace got too long, reset the counter
+                    self.mccounters[argshash] = 0
+                    raise
+
             else:
                 # machine code was already compiled for these greenargs
                 # (or we have a hash collision)
@@ -861,6 +880,9 @@
             key.counter += 1
             return key.counter >= self.trace_eagerness
 
+        def reset_counter_from_failure(self, key):
+            key.counter = 0
+
         def attach_unoptimized_bridge_from_interp(self, greenkey, bridge):
             greenargs = self.unwrap_greenkey(greenkey)
             newcell = MachineCodeEntryPoint(bridge, *greenargs)

Modified: pypy/trunk/pypy/rlib/jit.py
==============================================================================
--- pypy/trunk/pypy/rlib/jit.py	(original)
+++ pypy/trunk/pypy/rlib/jit.py	Thu Sep 10 12:12:41 2009
@@ -1,3 +1,4 @@
+import sys
 from pypy.rpython.extregistry import ExtRegistryEntry
 from pypy.rlib.objectmodel import CDefinedIntSymbolic
 from pypy.rlib.unroll import unrolling_iterable
@@ -82,6 +83,8 @@
 PARAMETERS = {'threshold': 1000,
               'trace_eagerness': 200,
               'hash_bits': 14,
+              'trace_limit': 10000,
+              'inlining': False,
               }
 unroll_parameters = unrolling_iterable(PARAMETERS.keys())
 

Modified: pypy/trunk/pypy/translator/driver.py
==============================================================================
--- pypy/trunk/pypy/translator/driver.py	(original)
+++ pypy/trunk/pypy/translator/driver.py	Thu Sep 10 12:12:41 2009
@@ -362,7 +362,7 @@
         from pypy.jit.metainterp.warmspot import apply_jit
         apply_jit(self.translator, policy=self.jitpolicy,
                   debug_level=self.config.translation.jit_debug,
-                  backend_name=self.config.translation.jit_backend)
+                  backend_name=self.config.translation.jit_backend, inline=True)
         #
         self.log.info("the JIT compiler was generated")
     #



More information about the Pypy-commit mailing list