Commits

Amaury Forgeot d'Arc committed 57875f6 Merge

hg merge default

Comments (0)

Files changed (275)

lib-python/conftest.py

File contents unchanged.

lib_pypy/_ctypes/array.py

File contents unchanged.

lib_pypy/_ctypes/basics.py

File contents unchanged.

lib_pypy/_ctypes/pointer.py

File contents unchanged.

lib_pypy/_ctypes/primitive.py

File contents unchanged.

lib_pypy/_locale.py

File contents unchanged.

lib_pypy/_marshal.py

File contents unchanged.

lib_pypy/_minimal_curses.py

File contents unchanged.

lib_pypy/cPickle.py

File contents unchanged.

lib_pypy/cmath.py

File contents unchanged.

lib_pypy/ctypes_config_cache/dumpcache.py

File contents unchanged.

lib_pypy/ctypes_config_cache/rebuild.py

File contents unchanged.

lib_pypy/ctypes_config_cache/test/test_cache.py

File contents unchanged.

lib_pypy/grp.py

File contents unchanged.

lib_pypy/itertools.py

File contents unchanged.

lib_pypy/msvcrt.py

File contents unchanged.

lib_pypy/pwd.py

File contents unchanged.

lib_pypy/pyexpat.py

File contents unchanged.

lib_pypy/resource.py

File contents unchanged.

lib_pypy/syslog.py

File contents unchanged.

pypy/annotation/annrpython.py

 
     #___ simplification (should be moved elsewhere?) _______
 
-    # it should be!
-    # now simplify_calls is moved to transform.py.
-    # i kept reverse_binding here for future(?) purposes though. --sanxiyn
-
-    def reverse_binding(self, known_variables, cell):
-        """This is a hack."""
-        # In simplify_calls, when we are trying to create the new
-        # SpaceOperation, all we have are SomeValues.  But SpaceOperations take
-        # Variables, not SomeValues.  Trouble is, we don't always have a
-        # Variable that just happens to be bound to the given SomeValue.
-        # A typical example would be if the tuple of arguments was created
-        # from another basic block or even another function.  Well I guess
-        # there is no clean solution, short of making the transformations
-        # more syntactic (e.g. replacing a specific sequence of SpaceOperations
-        # with another one).  This is a real hack because we have to use
-        # the identity of 'cell'.
-        if cell.is_constant():
-            return Constant(cell.const)
-        else:
-            for v in known_variables:
-                if self.bindings[v] is cell:
-                    return v
-            else:
-                raise CannotSimplify
-
     def simplify(self, block_subset=None, extra_passes=None):
         # Generic simplifications
         transform.transform_graph(self, block_subset=block_subset,
 RPythonAnnotator._registeroperations(annmodel)
 
 
-class CannotSimplify(Exception):
-    pass
-
-
 class BlockedInference(Exception):
     """This exception signals the type inference engine that the situation
     is currently blocked, and that it should try to progress elsewhere."""

pypy/annotation/description.py

File contents unchanged.

pypy/annotation/test/test_annrpython.py

File contents unchanged.

pypy/config/pypyoption.py

File contents unchanged.

pypy/config/support.py

             f = open(filename_or_file, "r")
         else:
             f = filename_or_file
-        return max([int(re.split('processor.*(\d+)', line)[1])
+        count = max([int(re.split('processor.*(\d+)', line)[1])
                     for line in f.readlines()
                     if line.startswith('processor')]) + 1
+        if count >= 4:
+            return max(count // 2, 3)
+        else:
+            return count
     except:
         return 1 # we really don't want to explode here, at worst we have 1

pypy/config/test/test_support.py

     saved = os.environ
     try:
         os.environ = FakeEnviron(None)
-        assert detect_number_of_processors(StringIO(cpuinfo)) == 4
+        assert detect_number_of_processors(StringIO(cpuinfo)) == 3
         assert detect_number_of_processors('random crap that does not exist') == 1
         os.environ = FakeEnviron('-j2')
         assert detect_number_of_processors(StringIO(cpuinfo)) == 1

pypy/config/translationoption.py

File contents unchanged.
-import py, sys, os
+import py, sys, os, textwrap, types
 from pypy.interpreter.gateway import app2interp_temp
 from pypy.interpreter.error import OperationError
+from pypy.interpreter.function import Method
 from pypy.tool.pytest import appsupport
 from pypy.tool.option import make_config, make_objspace
 from pypy.config.config import ConflictConfigError
 rsyncdirs = ['.', '../lib-python', '../lib_pypy', '../demo']
 rsyncignore = ['_cache']
 
-# PyPy's command line extra options (these are added 
-# to py.test's standard options) 
+# PyPy's command line extra options (these are added
+# to py.test's standard options)
 #
 
 def _set_platform(opt, opt_str, value, parser):
 
 _SPACECACHE={}
 def gettestobjspace(name=None, **kwds):
-    """ helper for instantiating and caching space's for testing. 
-    """ 
+    """ helper for instantiating and caching space's for testing.
+    """
     try:
         config = make_config(option, objspace=name, **kwds)
     except ConflictConfigError, e:
     except AttributeError:
         pass
 
-# 
-# Interfacing/Integrating with py.test's collection process 
+#
+# Interfacing/Integrating with py.test's collection process
 #
 #
 
 def ensure_pytest_builtin_helpers(helpers='skip raises'.split()):
     """ hack (py.test.) raises and skip into builtins, needed
-        for applevel tests to run directly on cpython but 
+        for applevel tests to run directly on cpython but
         apparently earlier on "raises" was already added
-        to module's globals. 
-    """ 
+        to module's globals.
+    """
     import __builtin__
-    for helper in helpers: 
+    for helper in helpers:
         if not hasattr(__builtin__, helper):
             setattr(__builtin__, helper, getattr(py.test, helper))
 
 def pytest_pycollect_makemodule(path, parent):
     return PyPyModule(path, parent)
 
-class PyPyModule(py.test.collect.Module): 
-    """ we take care of collecting classes both at app level 
-        and at interp-level (because we need to stick a space 
-        at the class) ourselves. 
-    """    
+class PyPyModule(py.test.collect.Module):
+    """ we take care of collecting classes both at app level
+        and at interp-level (because we need to stick a space
+        at the class) ourselves.
+    """
     def __init__(self, *args, **kwargs):
         if hasattr(sys, 'pypy_objspaceclass'):
             option.conf_iocapture = "sys" # pypy cannot do FD-based
         #    return True
         return False
 
-    def setup(self): 
+    def setup(self):
         # stick py.test raise in module globals -- carefully
-        ensure_pytest_builtin_helpers() 
-        super(PyPyModule, self).setup() 
-        #    if hasattr(mod, 'objspacename'): 
+        ensure_pytest_builtin_helpers()
+        super(PyPyModule, self).setup()
+        #    if hasattr(mod, 'objspacename'):
         #        mod.space = getttestobjspace(mod.objspacename)
 
-    def makeitem(self, name, obj): 
-        if isclass(obj) and self.classnamefilter(name): 
-            if name.startswith('AppTest'): 
+    def makeitem(self, name, obj):
+        if isclass(obj) and self.classnamefilter(name):
+            if name.startswith('AppTest'):
                 return AppClassCollector(name, parent=self)
             elif name.startswith('ExpectTest'):
                 if option.rundirect:
             #    return AppExpectClassCollector(name, parent=self)
             else:
                 return IntClassCollector(name, parent=self)
-            
-        elif hasattr(obj, 'func_code') and self.funcnamefilter(name): 
-            if name.startswith('app_test_'): 
+
+        elif hasattr(obj, 'func_code') and self.funcnamefilter(name):
+            if name.startswith('app_test_'):
                 assert not obj.func_code.co_flags & 32, \
-                    "generator app level functions? you must be joking" 
-                return AppTestFunction(name, parent=self) 
-            elif obj.func_code.co_flags & 32: # generator function 
-                return self.Generator(name, parent=self) 
-            else: 
-                return IntTestFunction(name, parent=self) 
+                    "generator app level functions? you must be joking"
+                return AppTestFunction(name, parent=self)
+            elif obj.func_code.co_flags & 32: # generator function
+                return self.Generator(name, parent=self)
+            else:
+                return IntTestFunction(name, parent=self)
 
-def skip_on_missing_buildoption(**ropts): 
+def skip_on_missing_buildoption(**ropts):
     __tracebackhide__ = True
     import sys
     options = getattr(sys, 'pypy_translation_info', None)
         py.test.skip("not running on translated pypy "
                      "(btw, i would need options: %s)" %
                      (ropts,))
-    for opt in ropts: 
-        if not options.has_key(opt) or options[opt] != ropts[opt]: 
+    for opt in ropts:
+        if not options.has_key(opt) or options[opt] != ropts[opt]:
             break
     else:
         return
-    py.test.skip("need translated pypy with: %s, got %s" 
+    py.test.skip("need translated pypy with: %s, got %s"
                  %(ropts,options))
 
 def getwithoutbinding(x, name):
             tb = sys.exc_info()[2]
             if e.match(space, space.w_KeyboardInterrupt):
                 raise OpErrKeyboardInterrupt, OpErrKeyboardInterrupt(), tb
-            appexcinfo = appsupport.AppExceptionInfo(space, e) 
-            if appexcinfo.traceback: 
+            appexcinfo = appsupport.AppExceptionInfo(space, e)
+            if appexcinfo.traceback:
                 raise AppError, AppError(appexcinfo), tb
             raise
 
         target = self.obj
         if option.runappdirect:
             return target()
-        space = gettestobjspace() 
+        space = gettestobjspace()
         filename = self._getdynfilename(target)
         func = app2interp_temp(target, filename=filename)
         print "executing", func
         code = getattr(func, 'im_func', func).func_code
         return "[%s:%s]" % (code.co_filename, code.co_firstlineno)
 
-class AppTestMethod(AppTestFunction): 
-
-    def setup(self): 
-        super(AppTestMethod, self).setup() 
-        instance = self.parent.obj 
-        w_instance = self.parent.w_instance 
-        space = instance.space  
-        for name in dir(instance): 
-            if name.startswith('w_'): 
+class AppTestMethod(AppTestFunction):
+    def setup(self):
+        super(AppTestMethod, self).setup()
+        instance = self.parent.obj
+        w_instance = self.parent.w_instance
+        space = instance.space
+        for name in dir(instance):
+            if name.startswith('w_'):
                 if option.runappdirect:
                     # if the value is a function living on the class,
                     # don't turn it into a bound method here
                     obj = getwithoutbinding(instance, name)
                     setattr(instance, name[2:], obj)
                 else:
-                    space.setattr(w_instance, space.wrap(name[2:]), 
-                                  getattr(instance, name)) 
+                    obj = getattr(instance, name)
+                    if isinstance(obj, types.MethodType):
+                        source = py.std.inspect.getsource(obj).lstrip()
+                        w_func = space.appexec([], textwrap.dedent("""
+                        ():
+                            %s
+                            return %s
+                        """) % (source, name))
+                        w_obj = Method(space, w_func, w_instance, space.w_None)
+                    else:
+                        w_obj = obj
+                    space.setattr(w_instance, space.wrap(name[2:]), w_obj)
 
     def runtest_perform(self):
         target = self.obj
         if option.runappdirect:
             return target()
-        space = target.im_self.space 
+        space = target.im_self.space
         filename = self._getdynfilename(target)
-        func = app2interp_temp(target.im_func, filename=filename) 
-        w_instance = self.parent.w_instance 
-        self.execute_appex(space, func, space, w_instance) 
+        func = app2interp_temp(target.im_func, filename=filename)
+        w_instance = self.parent.w_instance
+        self.execute_appex(space, func, space, w_instance)
 
 class PyPyClassCollector(py.test.collect.Class):
     def setup(self):
-        cls = self.obj 
+        cls = self.obj
         if not hasattr(cls, 'spaceconfig'):
-            cls.space = LazyObjSpaceGetter() 
+            cls.space = LazyObjSpaceGetter()
         else:
             assert hasattr(cls, 'space') # set by pytest_runtest_setup
-        super(PyPyClassCollector, self).setup() 
+        super(PyPyClassCollector, self).setup()
 
 class IntInstanceCollector(py.test.collect.Instance):
-    Function = IntTestFunction 
-    
-class IntClassCollector(PyPyClassCollector): 
+    Function = IntTestFunction
+
+class IntClassCollector(PyPyClassCollector):
     Instance = IntInstanceCollector
 
     def _haskeyword(self, keyword):
     def _keywords(self):
         return super(IntClassCollector, self)._keywords() + ['interplevel']
 
-class AppClassInstance(py.test.collect.Instance): 
-    Function = AppTestMethod 
+class AppClassInstance(py.test.collect.Instance):
+    Function = AppTestMethod
 
-    def setup(self): 
-        super(AppClassInstance, self).setup()         
-        instance = self.obj 
-        space = instance.space 
-        w_class = self.parent.w_class 
+    def setup(self):
+        super(AppClassInstance, self).setup()
+        instance = self.obj
+        space = instance.space
+        w_class = self.parent.w_class
         if option.runappdirect:
             self.w_instance = instance
         else:
             self.w_instance = space.call_function(w_class)
 
-class AppClassCollector(PyPyClassCollector): 
-    Instance = AppClassInstance 
+class AppClassCollector(PyPyClassCollector):
+    Instance = AppClassInstance
 
     def _haskeyword(self, keyword):
         return keyword == 'applevel' or \
     def _keywords(self):
         return super(AppClassCollector, self)._keywords() + ['applevel']
 
-    def setup(self): 
-        super(AppClassCollector, self).setup()        
-        cls = self.obj 
-        space = cls.space 
-        clsname = cls.__name__ 
+    def setup(self):
+        super(AppClassCollector, self).setup()
+        cls = self.obj
+        space = cls.space
+        clsname = cls.__name__
         if option.runappdirect:
             w_class = cls
         else:
                                           space.wrap(clsname),
                                           space.newtuple([]),
                                           space.newdict())
-        self.w_class = w_class 
+        self.w_class = w_class
 
 class ExpectTestMethod(py.test.collect.Function):
     def safe_name(target):

pypy/doc/confrest.py

             html.a("documentation", href=self.get_doclink("docindex.html"),
                    class_="menu"),
             " ", 
-            html.a("svn", href="https://codespeak.net/viewvc/pypy/trunk/",
+            html.a("hg", href="https://bitbucket.org/pypy/pypy",
                    class_="menu"),
             " ", 
             html.a("issues",

pypy/doc/cpython_differences.txt

File contents unchanged.
 extensively). PyPy needs a CPython running on the target platform to
 bootstrap, as cross compilation is not really meant to work yet.
 At the moment you need CPython 2.4 (with ctypes) or CPython 2.5 or 2.6
-for the translation process.
+for the translation process. PyPy's JIT requires an x86 or x86_64 CPU.
 
-PyPy also basically works in a 64-bit Linux environment, but
-*it requires a 32-bit Intel CPU* for the JIT right now.  (It works
-fine in a 32-bit chroot of an Intel 64.). There is also an ongoing
-summer of code project to provide 64bit support for JIT. It's scheduled
-to be finished by end of summer 2010.
 
 ------------------------------------------------
 Which Python version (2.x?) does PyPy implement?

pypy/doc/how-to-release.txt

 * write release announcement pypy/doc/release-x.y(.z).txt
   the release announcement should contain a direct link to the download page
 * update pypy.org (under extradoc/pypy.org), rebuild and commit
+
+* update http://codespeak.net/pypy/trunk:
+   code0> + chmod -R yourname:users /www/codespeak.net/htdocs/pypy/trunk
+   local> cd ..../pypy/doc && py.test
+   local> cd ..../pypy
+   local> rsync -az doc codespeak.net:/www/codespeak.net/htdocs/pypy/trunk/pypy/
+
 * post announcement on morepypy.blogspot.com
 * send announcements to pypy-dev, python-list,
   python-announce, python-dev ...

pypy/doc/index.txt

File contents unchanged.

pypy/doc/release-1.4.0.txt

File contents unchanged.

pypy/interpreter/argument.py

File contents unchanged.

pypy/interpreter/baseobjspace.py

File contents unchanged.

pypy/interpreter/executioncontext.py

File contents unchanged.

pypy/interpreter/function.py

File contents unchanged.

pypy/interpreter/gateway.py

File contents unchanged.

pypy/interpreter/generator.py

File contents unchanged.

pypy/interpreter/pycode.py

File contents unchanged.

pypy/interpreter/pyopcode.py

File contents unchanged.

pypy/interpreter/pyparser/genpytokenize.py

File contents unchanged.

pypy/interpreter/pyparser/pytokenize.py

File contents unchanged.

pypy/interpreter/pyparser/pytokenizer.py

File contents unchanged.

pypy/interpreter/pyparser/test/test_pyparse.py

File contents unchanged.

pypy/interpreter/test/test_argument.py

File contents unchanged.

pypy/interpreter/test/test_compiler.py

File contents unchanged.

pypy/interpreter/test/test_function.py

File contents unchanged.

pypy/interpreter/test/test_gateway.py

File contents unchanged.

pypy/interpreter/typedef.py

File contents unchanged.

pypy/jit/backend/llgraph/llimpl.py

     else:
         log.info("compiling new bridge")
 
+def compile_add_guard_jump_target(loop, loop_target):
+    loop = _from_opaque(loop)
+    loop_target = _from_opaque(loop_target)
+    op = loop.operations[-1]
+    assert op.is_guard()
+    op.jump_target = loop_target
+
 def compile_add_fail(loop, fail_index):
     loop = _from_opaque(loop)
     index = len(loop.operations)-1
 setannotation(compile_add_ref_result, annmodel.SomeInteger())
 setannotation(compile_add_float_result, annmodel.SomeInteger())
 setannotation(compile_add_jump_target, annmodel.s_None)
+setannotation(compile_add_guard_jump_target, annmodel.s_None)
 setannotation(compile_add_fail, annmodel.SomeInteger())
 setannotation(compile_add_fail_arg, annmodel.s_None)
 setannotation(compile_redirect_fail, annmodel.s_None)

pypy/jit/backend/llgraph/runner.py

                         llimpl.compile_add_fail_arg(c, var2index[box])
                     else:
                         llimpl.compile_add_fail_arg(c, -1)
+                        
             x = op.result
             if x is not None:
                 if isinstance(x, history.BoxInt):

pypy/jit/backend/llsupport/asmmemmgr.py

File contents unchanged.

pypy/jit/backend/llsupport/descr.py

File contents unchanged.

pypy/jit/backend/llsupport/gc.py

File contents unchanged.

pypy/jit/backend/llsupport/llmodel.py

File contents unchanged.

pypy/jit/backend/llsupport/test/test_asmmemmgr.py

File contents unchanged.

pypy/jit/backend/llsupport/test/test_gc.py

File contents unchanged.

pypy/jit/backend/model.py

File contents unchanged.

pypy/jit/backend/test/runner_test.py

File contents unchanged.

pypy/jit/backend/test/test_ll_random.py

File contents unchanged.

pypy/jit/backend/test/test_random.py

File contents unchanged.

pypy/jit/backend/x86/assembler.py

File contents unchanged.

pypy/jit/backend/x86/codebuf.py

File contents unchanged.

pypy/jit/backend/x86/jump.py

     srccount = {}    # maps dst_locations to how many times the same
                      # location appears in src_locations
     for dst in dst_locations:
-        srccount[dst._getregkey()] = 0
+        key = dst._getregkey()
+        assert key not in srccount, "duplicate value in dst_locations!"
+        srccount[key] = 0
     for i in range(len(dst_locations)):
         src = src_locations[i]
         if isinstance(src, ImmedLoc):

pypy/jit/backend/x86/regalloc.py

File contents unchanged.

pypy/jit/backend/x86/regloc.py

File contents unchanged.

pypy/jit/backend/x86/runner.py

File contents unchanged.

pypy/jit/backend/x86/test/test_gc_integration.py

File contents unchanged.

pypy/jit/backend/x86/test/test_jump.py

File contents unchanged.

pypy/jit/backend/x86/test/test_loop_spec.py

-import py
-from pypy.jit.backend.x86.test.test_basic import Jit386Mixin
-from pypy.jit.metainterp.test import test_loop_spec
-
-class TestLoopSpec(Jit386Mixin, test_loop_spec.LoopSpecTest):
-    # for the individual tests see
-    # ====> ../../../metainterp/test/test_loop.py
-    pass

pypy/jit/backend/x86/test/test_loop_unroll.py

+import py
+from pypy.jit.backend.x86.test.test_basic import Jit386Mixin
+from pypy.jit.metainterp.test import test_loop_unroll
+
+class TestLoopSpec(Jit386Mixin, test_loop_unroll.LoopUnrollTest):
+    # for the individual tests see
+    # ====> ../../../metainterp/test/test_loop.py
+    pass

pypy/jit/backend/x86/test/test_regalloc.py

File contents unchanged.

pypy/jit/backend/x86/test/test_runner.py

File contents unchanged.

pypy/jit/backend/x86/test/test_ztranslation.py

File contents unchanged.

pypy/jit/codewriter/effectinfo.py

File contents unchanged.

pypy/jit/codewriter/jtransform.py

File contents unchanged.

pypy/jit/codewriter/support.py

 def _ll_2_int_floordiv_ovf_zer(x, y):
     if y == 0:
         raise ZeroDivisionError
-    if ((x + sys.maxint) & y) == -1:    # detect "x = -sys.maxint-1, y = -1".
+    if x == -sys.maxint - 1 and y == -1:
         raise OverflowError
     return llop.int_floordiv(lltype.Signed, x, y)
 
 def _ll_2_int_floordiv_ovf(x, y):
-    if ((x + sys.maxint) & y) == -1:    # detect "x = -sys.maxint-1, y = -1".
+    if x == -sys.maxint - 1 and y == -1:        
         raise OverflowError
     return llop.int_floordiv(lltype.Signed, x, y)
 
 def _ll_2_int_mod_ovf_zer(x, y):
     if y == 0:
         raise ZeroDivisionError
-    if ((x + sys.maxint) & y) == -1:    # detect "x = -sys.maxint-1, y = -1".
+    if x == -sys.maxint - 1 and y == -1:
         raise OverflowError
     return llop.int_mod(lltype.Signed, x, y)
 
 def _ll_2_int_mod_ovf(x, y):
-    if ((x + sys.maxint) & y) == -1:    # detect "x = -sys.maxint-1, y = -1".
+    if x == -sys.maxint - 1 and y == -1:
         raise OverflowError
     return llop.int_mod(lltype.Signed, x, y)
 

pypy/jit/codewriter/test/test_jtransform.py

File contents unchanged.

pypy/jit/conftest.py

File contents unchanged.

pypy/jit/metainterp/compile.py

 from pypy.jit.metainterp.history import AbstractFailDescr, BoxInt
 from pypy.jit.metainterp.history import BoxPtr, BoxObj, BoxFloat, Const
 from pypy.jit.metainterp import history
-from pypy.jit.metainterp.specnode import NotSpecNode, more_general_specnodes
 from pypy.jit.metainterp.typesystem import llhelper, oohelper
 from pypy.jit.metainterp.optimizeutil import InvalidLoop
 from pypy.jit.metainterp.resume import NUMBERING
             extraloops = [loop]
         metainterp_sd.stats.view(errmsg=errmsg, extraloops=extraloops)
 
-def create_empty_loop(metainterp):
+def create_empty_loop(metainterp, name_prefix=''):
     name = metainterp.staticdata.stats.name_for_new_loop()
-    return TreeLoop(name)
+    return TreeLoop(name_prefix + name)
 
 def make_loop_token(nb_args, jitdriver_sd):
     loop_token = LoopToken()
-    loop_token.specnodes = [prebuiltNotSpecNode] * nb_args
     loop_token.outermost_jitdriver_sd = jitdriver_sd
     return loop_token
 
-def record_loop_or_bridge(loop):
+def record_loop_or_bridge(metainterp_sd, loop):
     """Do post-backend recordings and cleanups on 'loop'.
     """
     # get the original loop token (corresponding to 'loop', or if that is
     # a bridge, to the loop that this bridge belongs to)
     looptoken = loop.token
     assert looptoken is not None
+    if metainterp_sd.warmrunnerdesc is not None:    # for tests
+        assert looptoken.generation > 0     # has been registered with memmgr
     wref = weakref.ref(looptoken)
     for op in loop.operations:
         descr = op.getdescr()
             if descr is not looptoken:
                 looptoken.record_jump_to(descr)
             op.setdescr(None)    # clear reference, mostly for tests
+            if not we_are_translated():
+                op._jumptarget_number = descr.number
     # mostly for tests: make sure we don't keep a reference to the LoopToken
     loop.token = None
     if not we_are_translated():
-        loop._number = looptoken.number
+        loop._looptoken_number = looptoken.number
 
 # ____________________________________________________________
 
-def compile_new_loop(metainterp, old_loop_tokens, start):
+def compile_new_loop(metainterp, old_loop_tokens, greenkey, start,
+                     full_preamble_needed=True):
     """Try to compile a new loop by closing the current history back
     to the first operation.
     """
     loop_token = make_loop_token(len(loop.inputargs), jitdriver_sd)
     loop.token = loop_token
     loop.operations[-1].setdescr(loop_token)     # patch the target of the JUMP
+
+    loop.preamble = create_empty_loop(metainterp, 'Preamble ')
+    loop.preamble.inputargs = loop.inputargs
+    loop.preamble.token = make_loop_token(len(loop.inputargs), jitdriver_sd)
+
     try:
         old_loop_token = jitdriver_sd.warmstate.optimize_loop(
             metainterp_sd, old_loop_tokens, loop)
     if old_loop_token is not None:
         metainterp.staticdata.log("reusing old loop")
         return old_loop_token
-    send_loop_to_backend(metainterp_sd, loop, "loop")
-    insert_loop_token(old_loop_tokens, loop_token)
-    record_loop_or_bridge(loop)
-    return loop_token
+
+    if loop.preamble.operations is not None:
+        send_loop_to_backend(metainterp_sd, loop, "loop")
+        record_loop_or_bridge(metainterp_sd, loop)
+        token = loop.preamble.token
+        if full_preamble_needed or not loop.preamble.token.short_preamble:
+            send_loop_to_backend(metainterp_sd, loop.preamble, "entry bridge")
+            insert_loop_token(old_loop_tokens, loop.preamble.token)
+            jitdriver_sd.warmstate.attach_unoptimized_bridge_from_interp(
+                greenkey, loop.preamble.token)
+            record_loop_or_bridge(metainterp_sd, loop.preamble)
+        return token
+    else:
+        send_loop_to_backend(metainterp_sd, loop, "loop")
+        insert_loop_token(old_loop_tokens, loop_token)
+        jitdriver_sd.warmstate.attach_unoptimized_bridge_from_interp(
+            greenkey, loop.token)
+        record_loop_or_bridge(metainterp_sd, loop)
+        return loop_token
 
 def insert_loop_token(old_loop_tokens, loop_token):
     # Find where in old_loop_tokens we should insert this new loop_token.
     # The following algo means "as late as possible, but before another
     # loop token that would be more general and so completely mask off
     # the new loop_token".
-    for i in range(len(old_loop_tokens)):
-        if more_general_specnodes(old_loop_tokens[i].specnodes,
-                                  loop_token.specnodes):
-            old_loop_tokens.insert(i, loop_token)
-            break
-    else:
-        old_loop_tokens.append(loop_token)
+    # XXX do we still need a list?
+    old_loop_tokens.append(loop_token)
 
 def send_loop_to_backend(metainterp_sd, loop, type):
     globaldata = metainterp_sd.globaldata
     globaldata.loopnumbering += 1
 
     metainterp_sd.logger_ops.log_loop(loop.inputargs, loop.operations, n, type)
+    short = loop.token.short_preamble
+    if short:
+        metainterp_sd.logger_ops.log_short_preamble(short[-1].inputargs,
+                                                    short[-1].operations)
+
     if not we_are_translated():
         show_loop(metainterp_sd, loop)
         loop.check_consistency()
         raise metainterp_sd.ExitFrameWithExceptionRef(cpu, value)
 
 
-prebuiltNotSpecNode = NotSpecNode()
-
 class TerminatingLoopToken(LoopToken):
     terminating = True
 
     def __init__(self, nargs, finishdescr):
-        self.specnodes = [prebuiltNotSpecNode]*nargs
         self.finishdescr = finishdescr
 
 def make_done_loop_tokens():
         # know exactly what we must do (ResumeGuardDescr/ResumeFromInterpDescr)
         prepare_last_operation(new_loop, target_loop_token)
         resumekey.compile_and_attach(metainterp, new_loop)
-        record_loop_or_bridge(new_loop)
+        compile_known_target_bridges(metainterp, new_loop)
+        record_loop_or_bridge(metainterp_sd, new_loop)
     return target_loop_token
 
+# For backends that not supports emitting guards with preset jump
+# targets, emit mini-bridges containing the jump
+def compile_known_target_bridges(metainterp, bridge):
+    for op in bridge.operations:
+        if op.is_guard():
+            target = op.getjumptarget()
+            if target:
+                mini = create_empty_loop(metainterp, 'fallback')
+                mini.inputargs = op.getfailargs()[:]
+                jmp = ResOperation(rop.JUMP, mini.inputargs[:], None, target)
+                mini.operations = [jmp]
+                descr = op.getdescr()
+                assert isinstance(descr, ResumeGuardDescr)
+                mini.token = bridge.token
+                
+                #descr.compile_and_attach(metainterp, mini)
+                if not we_are_translated():
+                    descr._debug_suboperations = mini.operations
+                send_bridge_to_backend(metainterp.staticdata, descr,
+                                       mini.inputargs, mini.operations,
+                                       bridge.token)
+                record_loop_or_bridge(metainterp.staticdata, mini)
+
+
 def prepare_last_operation(new_loop, target_loop_token):
     op = new_loop.operations[-1]
     if not isinstance(target_loop_token, TerminatingLoopToken):
         # normal case
-        op.setdescr(target_loop_token)     # patch the jump target
+        #op.setdescr(target_loop_token)     # patch the jump target
+        pass
     else:
         # The target_loop_token is a pseudo loop token,
         # e.g. loop_tokens_done_with_this_frame_void[0]

pypy/jit/metainterp/executor.py

File contents unchanged.

pypy/jit/metainterp/graphpage.py

         for graph, highlight in graphs:
             if getattr(graph, 'token', None) is not None:
                 resopgen.jumps_to_graphs[graph.token] = graph
-            if getattr(graph, '_number', None) is not None:
-                resopgen.jumps_to_graphs[graph._number] = graph
+            if getattr(graph, '_looptoken_number', None) is not None:
+                resopgen.jumps_to_graphs[graph._looptoken_number] = graph
         
         for graph, highlight in graphs:
             resopgen.add_graph(graph, highlight)
                              (graphindex, opindex))
                 break
         if op.getopnum() == rop.JUMP:
-            tgt = op.getdescr()
             tgt_g = -1
-            if tgt is None:
-                tgt_g = graphindex
+            tgt = None
+            tgt_number = getattr(op, '_jumptarget_number', None)
+            if tgt_number is not None:
+                tgt = self.jumps_to_graphs.get(tgt_number)
             else:
-                if tgt in self.jumps_to_graphs:
-                    tgt = self.jumps_to_graphs[tgt]
+                tgt_descr = op.getdescr()
+                if tgt_descr is None:
+                    tgt_g = graphindex
                 else:
-                    tgt = self.jumps_to_graphs.get(tgt.number)
-                if tgt is not None:
-                    tgt_g = self.graphs.index(tgt)
+                    tgt = self.jumps_to_graphs.get(tgt_descr.number)
+                    if tgt is None:
+                        tgt = self.jumps_to_graphs.get(tgt_descr)
+            if tgt is not None:
+                tgt_g = self.graphs.index(tgt)
             if tgt_g != -1:
                 self.genedge((graphindex, opstartindex),
                              (tgt_g, 0),

pypy/jit/metainterp/history.py

     def _get_str(self):    # for debugging only
         return self.constbox()._get_str()
 
+    def forget_value(self):
+        raise NotImplementedError
+
 class BoxInt(Box):
     type = INT
     _attrs_ = ('value',)
                 assert isinstance(value, Symbolic)
         self.value = value
 
+    def forget_value(self):
+        self.value = 0
+        
     def clonebox(self):
         return BoxInt(self.value)
 
         assert isinstance(floatval, float)
         self.value = floatval
 
+    def forget_value(self):
+        self.value = 0.0
+
     def clonebox(self):
         return BoxFloat(self.value)
 
         assert lltype.typeOf(value) == llmemory.GCREF
         self.value = value
 
+    def forget_value(self):
+        self.value = lltype.nullptr(llmemory.GCREF.TO)
+
     def clonebox(self):
         return BoxPtr(self.value)
 
         assert ootype.typeOf(value) is ootype.Object
         self.value = value
 
+    def forget_value(self):
+        self.value = ootype.NULL
+
     def clonebox(self):
         return BoxObj(self.value)
 
     was compiled; but the LoopDescr remains alive and points to the
     generated assembler.
     """
+    short_preamble = None
     terminating = False # see TerminatingLoopToken in compile.py
     outermost_jitdriver_sd = None
-    # specnodes = ...
     # and more data specified by the backend when the loop is compiled
     number = -1
     generation = r_int64(0)

pypy/jit/metainterp/jitprof.py

 ABORT_TOO_LONG
 ABORT_BRIDGE
 ABORT_ESCAPE
+ABORT_BAD_LOOP
 NVIRTUALS
 NVHOLES
 NVREUSED
         self._print_intline("abort: trace too long", cnt[ABORT_TOO_LONG])
         self._print_intline("abort: compiling", cnt[ABORT_BRIDGE])
         self._print_intline("abort: vable escape", cnt[ABORT_ESCAPE])
+        self._print_intline("abort: bad loop", cnt[ABORT_BAD_LOOP])
         self._print_intline("nvirtuals", cnt[NVIRTUALS])
         self._print_intline("nvholes", cnt[NVHOLES])
         self._print_intline("nvreused", cnt[NVREUSED])

pypy/jit/metainterp/logger.py

             self._log_operations(inputargs, operations)
             debug_stop("jit-log-opt-bridge")
 
+    def log_short_preamble(self, inputargs, operations):
+        debug_start("jit-log-short-preamble")
+        self._log_operations(inputargs, operations)
+        debug_stop("jit-log-short-preamble")            
+
     def repr_of_descr(self, descr):
         return descr.repr_of_descr()
 

pypy/jit/metainterp/memmgr.py

File contents unchanged.

pypy/jit/metainterp/nounroll_optimize.py

+
+from pypy.rlib.debug import debug_start, debug_stop
+from pypy.jit.metainterp.optimizeopt import optimize_loop_1, optimize_bridge_1
+
+def optimize_loop(metainterp_sd, old_loop_tokens, loop):
+    debug_start("jit-optimize")
+    try:
+        return _optimize_loop(metainterp_sd, old_loop_tokens, loop)
+    finally:
+        debug_stop("jit-optimize")
+
+def _optimize_loop(metainterp_sd, old_loop_tokens, loop):
+    cpu = metainterp_sd.cpu
+    metainterp_sd.logger_noopt.log_loop(loop.inputargs, loop.operations)
+    if old_loop_tokens:
+        return old_loop_tokens[0]
+    optimize_loop_1(metainterp_sd, loop, False)
+    return None
+
+def optimize_bridge(metainterp_sd, old_loop_tokens, bridge):
+    debug_start("jit-optimize")
+    try:
+        return _optimize_bridge(metainterp_sd, old_loop_tokens, bridge)
+    finally:
+        debug_stop("jit-optimize")
+
+def _optimize_bridge(metainterp_sd, old_loop_tokens, bridge):
+    cpu = metainterp_sd.cpu    
+    metainterp_sd.logger_noopt.log_loop(bridge.inputargs, bridge.operations)
+    if old_loop_tokens:
+        old_loop_token = old_loop_tokens[0]
+        bridge.operations[-1].setdescr(old_loop_token)   # patch jump target
+        optimize_bridge_1(metainterp_sd, bridge)
+        return old_loop_token
+    return None

pypy/jit/metainterp/optimize.py

 
 # ____________________________________________________________
 
-from pypy.jit.metainterp.optimizefindnode import PerfectSpecializationFinder
-from pypy.jit.metainterp.optimizeopt import optimize_loop_1
-from pypy.jit.metainterp.specnode import equals_specnodes
+from pypy.jit.metainterp.optimizeopt import optimize_loop_1, optimize_bridge_1
 
 def optimize_loop(metainterp_sd, old_loop_tokens, loop):
     debug_start("jit-optimize")
 def _optimize_loop(metainterp_sd, old_loop_tokens, loop):
     cpu = metainterp_sd.cpu
     metainterp_sd.logger_noopt.log_loop(loop.inputargs, loop.operations)
-    finder = PerfectSpecializationFinder(cpu)
-    finder.find_nodes_loop(loop)
-    for old_loop_token in old_loop_tokens:
-        if equals_specnodes(old_loop_token.specnodes, loop.token.specnodes):
-            return old_loop_token
+    # XXX do we really still need a list?
+    if old_loop_tokens:
+        return old_loop_tokens[0]
     optimize_loop_1(metainterp_sd, loop)
     return None
 
 # ____________________________________________________________
 
-from pypy.jit.metainterp.optimizefindnode import BridgeSpecializationFinder
-from pypy.jit.metainterp.optimizeopt import optimize_bridge_1
-
 def optimize_bridge(metainterp_sd, old_loop_tokens, bridge):
     debug_start("jit-optimize")
     try:
         debug_stop("jit-optimize")
 
 def _optimize_bridge(metainterp_sd, old_loop_tokens, bridge):
-    cpu = metainterp_sd.cpu    
+    cpu = metainterp_sd.cpu
     metainterp_sd.logger_noopt.log_loop(bridge.inputargs, bridge.operations)
-    finder = BridgeSpecializationFinder(cpu)
-    finder.find_nodes_bridge(bridge)
-    for old_loop_token in old_loop_tokens:
-        if finder.bridge_matches(old_loop_token.specnodes):
-            bridge.operations[-1].setdescr(old_loop_token)   # patch jump target
-            optimize_bridge_1(metainterp_sd, bridge)
-            return old_loop_token
+    if old_loop_tokens:
+        old_loop_token = old_loop_tokens[0]
+        bridge.operations[-1].setdescr(old_loop_token)   # patch jump target
+        optimize_bridge_1(metainterp_sd, bridge)
+        return old_loop_tokens[0]
+        #return bridge.operations[-1].getdescr()
     return None
 
 # ____________________________________________________________

pypy/jit/metainterp/optimize_nopspec.py

-
-from pypy.rlib.debug import debug_start, debug_stop
-from pypy.jit.metainterp.optimizeopt import optimize_loop_1, optimize_bridge_1
-from pypy.jit.metainterp.optimizefindnode import PerfectSpecializationFinder
-from pypy.jit.metainterp.optimizefindnode import BridgeSpecializationFinder
-
-def optimize_loop(metainterp_sd, old_loop_tokens, loop):
-    debug_start("jit-optimize")
-    try:
-        return _optimize_loop(metainterp_sd, old_loop_tokens, loop)
-    finally:
-        debug_stop("jit-optimize")
-
-def _optimize_loop(metainterp_sd, old_loop_tokens, loop):
-    cpu = metainterp_sd.cpu
-    metainterp_sd.logger_noopt.log_loop(loop.inputargs, loop.operations)
-    # XXX the following lines are probably still needed, to discard invalid
-    # loops. bit silly to run a full perfect specialization and throw the
-    # result away.
-    finder = PerfectSpecializationFinder(cpu)
-    finder.find_nodes_loop(loop, False)
-    if old_loop_tokens:
-        return old_loop_tokens[0]
-    optimize_loop_1(metainterp_sd, loop)
-    return None
-
-def optimize_bridge(metainterp_sd, old_loop_tokens, bridge):
-    debug_start("jit-optimize")
-    try:
-        return _optimize_bridge(metainterp_sd, old_loop_tokens, bridge)
-    finally:
-        debug_stop("jit-optimize")
-
-def _optimize_bridge(metainterp_sd, old_loop_tokens, bridge):
-    cpu = metainterp_sd.cpu    
-    metainterp_sd.logger_noopt.log_loop(bridge.inputargs, bridge.operations)
-    # XXX same comment as above applies
-    finder = BridgeSpecializationFinder(cpu)
-    finder.find_nodes_bridge(bridge)
-    if old_loop_tokens:
-        old_loop_token = old_loop_tokens[0]
-        bridge.operations[-1].setdescr(old_loop_token)   # patch jump target
-        optimize_bridge_1(metainterp_sd, bridge)
-        return old_loop_token
-    return None

pypy/jit/metainterp/optimizefindnode.py

-from pypy.jit.metainterp.specnode import SpecNode
-from pypy.jit.metainterp.specnode import NotSpecNode, prebuiltNotSpecNode
-from pypy.jit.metainterp.specnode import ConstantSpecNode
-from pypy.jit.metainterp.specnode import VirtualInstanceSpecNode
-from pypy.jit.metainterp.specnode import VirtualArraySpecNode
-from pypy.jit.metainterp.specnode import VirtualStructSpecNode
-from pypy.jit.metainterp.history import AbstractValue, ConstInt, Const
-from pypy.jit.metainterp.resoperation import rop
-from pypy.jit.metainterp.executor import execute_nonspec
-from pypy.jit.metainterp.optimizeutil import _findall, sort_descrs
-from pypy.jit.metainterp.optimizeutil import InvalidLoop
-
-# ____________________________________________________________
-
-UNIQUE_UNKNOWN = '\x00'
-UNIQUE_NO      = '\x01'
-UNIQUE_INST    = '\x02'
-UNIQUE_ARRAY   = '\x03'
-UNIQUE_STRUCT  = '\x04'
-
-class InstanceNode(object):
-    """An instance of this class is used to match the start and
-    the end of the loop, so it contains both 'origfields' that represents
-    the field's status at the start and 'curfields' that represents it
-    at the current point (== the end when optimizefindnode is complete).
-    """
-    escaped = False     # if True, then all the rest of the info is pointless
-    unique = UNIQUE_UNKNOWN   # for find_unique_nodes()
-
-    # fields used to store the shape of the potential VirtualInstance
-    knownclsbox = None  # set only on freshly-allocated or fromstart structures
-    origfields = None   # optimization; equivalent to an empty dict
-    curfields = None    # optimization; equivalent to an empty dict
-
-    knownvaluebox = None # a Const with the value of this box, if constant
-
-    # fields used to store the shape of the potential VirtualList
-    arraydescr = None   # set only on freshly-allocated or fromstart arrays
-    #arraysize = ..     # valid if and only if arraydescr is not None
-    origitems = None    # optimization; equivalent to an empty dict
-    curitems = None     # optimization; equivalent to an empty dict
-
-    # fields used to store the shape of the potential VirtualStruct
-    structdescr = None  # set only on freshly-allocated or fromstart structs
-    #origfields = ..    # same as above
-    #curfields = ..     # same as above
-
-    dependencies = None
-
-    def __init__(self, fromstart=False):
-        self.fromstart = fromstart    # for loops only: present since the start
-
-    def is_constant(self):
-        return self.knownvaluebox is not None
-
-    def add_escape_dependency(self, other):
-        assert not self.escaped
-        if self.dependencies is None:
-            self.dependencies = []
-        self.dependencies.append(other)
-
-    def mark_escaped(self):
-        # invariant: if escaped=True, then dependencies is None
-        if not self.escaped:
-            self.escaped = True
-            self.unique = UNIQUE_NO
-            # ^^^ always set unique to UNIQUE_NO when we set escaped to True.
-            # See for example test_find_nodes_store_into_loop_constant_2.
-            if self.dependencies is not None:
-                deps = self.dependencies
-                self.dependencies = None
-                for box in deps:
-                    box.mark_escaped()
-
-    def set_unique_nodes(self):
-        if self.fromstart:
-            self.mark_escaped()
-        if self.escaped or self.unique != UNIQUE_UNKNOWN:
-            # this node is not suitable for being a virtual, or we
-            # encounter it more than once when doing the recursion
-            self.unique = UNIQUE_NO
-        elif self.knownclsbox is not None:
-            self.unique = UNIQUE_INST
-            if self.curfields is not None:
-                for subnode in self.curfields.itervalues():
-                    subnode.set_unique_nodes()
-        elif self.arraydescr is not None:
-            self.unique = UNIQUE_ARRAY
-            if self.curitems is not None:
-                for subnode in self.curitems.itervalues():
-                    subnode.set_unique_nodes()
-        elif self.structdescr is not None:
-            self.unique = UNIQUE_STRUCT
-            if self.curfields is not None:
-                for subnode in self.curfields.itervalues():
-                    subnode.set_unique_nodes()
-        else:
-            assert 0, "most probably unreachable"
-
-    def __repr__(self):
-        flags = ''
-        if self.escaped:     flags += 'e'
-        if self.fromstart:   flags += 's'
-        if self.knownclsbox: flags += 'c'
-        if self.arraydescr:  flags += str(self.arraysize)
-        if self.structdescr: flags += 'S'
-        return "<InstanceNode (%s)>" % (flags,)
-
-# ____________________________________________________________
-# General find_nodes_xxx() interface, for both loops and bridges
-
-class NodeFinder(object):
-    """Abstract base class."""
-    node_escaped = InstanceNode()
-    node_escaped.unique = UNIQUE_NO
-    node_escaped.escaped = True
-
-    def __init__(self, cpu):
-        self.cpu = cpu
-        self.nodes = {}     # Box -> InstanceNode
-
-    def getnode(self, box):
-        if isinstance(box, Const):
-            return self.set_constant_node(box, box)
-        return self.nodes.get(box, self.node_escaped)
-
-    def set_constant_node(self, box, constbox):
-        assert isinstance(constbox, Const)
-        node = InstanceNode()
-        node.unique = UNIQUE_NO
-        node.escaped = True
-        node.knownvaluebox = constbox
-        self.nodes[box] = node
-        return node
-
-    def get_constant_box(self, box):
-        if isinstance(box, Const):
-            return box
-        try:
-            node = self.nodes[box]
-        except KeyError:
-            return None
-        else:
-            return node.knownvaluebox
-
-    def find_nodes(self, operations):
-        for op in operations:
-            opnum = op.getopnum()
-            for value, func in find_nodes_ops:
-                if opnum == value:
-                    func(self, op)
-                    break
-            else:
-                self.find_nodes_default(op)
-
-    def find_nodes_default(self, op):
-        if op.is_always_pure():
-            for i in range(op.numargs()):
-                arg = op.getarg(i)
-                if self.get_constant_box(arg) is None:
-                    break
-            else:
-                # all constant arguments: we can constant-fold
-                argboxes = [self.get_constant_box(op.getarg(i))
-                            for i in range(op.numargs())]
-                resbox = execute_nonspec(self.cpu, None,
-                                         op.getopnum(), argboxes, op.getdescr())
-                self.set_constant_node(op.result, resbox.constbox())
-        # default case: mark the arguments as escaping
-        for i in range(op.numargs()):
-            self.getnode(op.getarg(i)).mark_escaped()
-
-    def find_nodes_no_escape(self, op):
-        pass    # for operations that don't escape their arguments
-
-    find_nodes_PTR_EQ        = find_nodes_no_escape
-    find_nodes_PTR_NE        = find_nodes_no_escape
-    ##find_nodes_INSTANCEOF    = find_nodes_no_escape
-    find_nodes_GUARD_NONNULL = find_nodes_no_escape
-    find_nodes_GUARD_ISNULL  = find_nodes_no_escape
-
-    def find_nodes_NEW_WITH_VTABLE(self, op):
-        instnode = InstanceNode()
-        box = op.getarg(0)
-        assert isinstance(box, Const)
-        instnode.knownclsbox = box
-        self.nodes[op.result] = instnode
-
-    def find_nodes_NEW(self, op):
-        instnode = InstanceNode()
-        instnode.structdescr = op.getdescr()
-        self.nodes[op.result] = instnode
-
-    def find_nodes_NEW_ARRAY(self, op):
-        lengthbox = op.getarg(0)
-        lengthbox = self.get_constant_box(lengthbox)
-        if lengthbox is None:
-            return     # var-sized arrays are not virtual
-        arraynode = InstanceNode()
-        arraynode.arraysize = lengthbox.getint()
-        arraynode.arraydescr = op.getdescr()
-        self.nodes[op.result] = arraynode
-
-    def find_nodes_ARRAYLEN_GC(self, op):
-        arraynode = self.getnode(op.getarg(0))
-        if arraynode.arraydescr is not None:
-            resbox = ConstInt(arraynode.arraysize)
-            self.set_constant_node(op.result, resbox)
-
-    def find_nodes_GUARD_CLASS(self, op):
-        instnode = self.getnode(op.getarg(0))
-        if instnode.fromstart:    # only useful (and safe) in this case
-            box = op.getarg(1)
-            assert isinstance(box, Const)
-            instnode.knownclsbox = box
-
-    def find_nodes_GUARD_VALUE(self, op):
-        instnode = self.getnode(op.getarg(0))
-        if instnode.fromstart:    # only useful (and safe) in this case
-            box = op.getarg(1)
-            assert isinstance(box, Const)
-            instnode.knownvaluebox = box
-
-    def find_nodes_SETFIELD_GC(self, op):
-        instnode = self.getnode(op.getarg(0))
-        fieldnode = self.getnode(op.getarg(1))
-        if instnode.escaped:
-            fieldnode.mark_escaped()
-            return     # nothing to be gained from tracking the field
-        field = op.getdescr()
-        assert isinstance(field, AbstractValue)
-        if instnode.curfields is None:
-            instnode.curfields = {}
-        instnode.curfields[field] = fieldnode
-        instnode.add_escape_dependency(fieldnode)
-
-    def find_nodes_GETFIELD_GC(self, op):
-        instnode = self.getnode(op.getarg(0))
-        if instnode.escaped:
-            return     # nothing to be gained from tracking the field
-        field = op.getdescr()
-        assert isinstance(field, AbstractValue)
-        if instnode.curfields is not None and field in instnode.curfields:
-            fieldnode = instnode.curfields[field]
-        elif instnode.origfields is not None and field in instnode.origfields:
-            fieldnode = instnode.origfields[field]
-        elif instnode.fromstart:
-            fieldnode = InstanceNode(fromstart=True)
-            instnode.add_escape_dependency(fieldnode)
-            if instnode.origfields is None:
-                instnode.origfields = {}
-            instnode.origfields[field] = fieldnode
-        else:
-            return    # nothing to be gained from tracking the field
-        self.nodes[op.result] = fieldnode
-
-    find_nodes_GETFIELD_GC_PURE = find_nodes_GETFIELD_GC
-
-    def find_nodes_SETARRAYITEM_GC(self, op):
-        indexbox = op.getarg(1)
-        indexbox = self.get_constant_box(indexbox)
-        if indexbox is None:
-            self.find_nodes_default(op)            # not a Const index
-            return
-        arraynode = self.getnode(op.getarg(0))
-        itemnode = self.getnode(op.getarg(2))
-        if arraynode.escaped:
-            itemnode.mark_escaped()
-            return     # nothing to be gained from tracking the item
-        if arraynode.curitems is None:
-            arraynode.curitems = {}
-        arraynode.curitems[indexbox.getint()] = itemnode
-        arraynode.add_escape_dependency(itemnode)
-
-    def find_nodes_GETARRAYITEM_GC(self, op):
-        indexbox = op.getarg(1)
-        indexbox = self.get_constant_box(indexbox)
-        if indexbox is None:
-            self.find_nodes_default(op)            # not a Const index
-            return
-        arraynode = self.getnode(op.getarg(0))
-        if arraynode.escaped:
-            return     # nothing to be gained from tracking the item
-        index = indexbox.getint()
-        if arraynode.curitems is not None and index in arraynode.curitems:
-            itemnode = arraynode.curitems[index]
-        elif arraynode.origitems is not None and index in arraynode.origitems:
-            itemnode = arraynode.origitems[index]
-        elif arraynode.fromstart:
-            itemnode = InstanceNode(fromstart=True)
-            arraynode.add_escape_dependency(itemnode)
-            if arraynode.origitems is None:
-                arraynode.origitems = {}
-            arraynode.origitems[index] = itemnode
-        else:
-            return    # nothing to be gained from tracking the item
-        self.nodes[op.result] = itemnode
-
-    find_nodes_GETARRAYITEM_GC_PURE = find_nodes_GETARRAYITEM_GC
-
-    def find_nodes_JUMP(self, op):
-        # only set up the 'unique' field of the InstanceNodes;
-        # real handling comes later (build_result_specnodes() for loops).
-        for i in range(op.numargs()):
-            box = op.getarg(i)
-            self.getnode(box).set_unique_nodes()
-
-    def find_nodes_FINISH(self, op):
-        # only for bridges, and only for the ones that end in a 'return'
-        # or 'raise'; all other cases end with a JUMP.
-        for i in range(op.numargs()):
-            box = op.getarg(i)
-            self.getnode(box).unique = UNIQUE_NO
-
-find_nodes_ops = _findall(NodeFinder, 'find_nodes_')
-
-# ____________________________________________________________
-# Perfect specialization -- for loops only
-
-class PerfectSpecializationFinder(NodeFinder):
-    node_fromstart = InstanceNode(fromstart=True)
-
-    def find_nodes_loop(self, loop, build_specnodes=True):
-        self._loop = loop
-        self.setup_input_nodes(loop.inputargs)
-        self.find_nodes(loop.operations)
-        if build_specnodes:
-            self.build_result_specnodes(loop)
-
-    def show(self):
-        from pypy.jit.metainterp.viewnode import viewnodes, view
-        op = self._loop.operations[-1]
-        assert op.getopnum() == rop.JUMP
-        exitnodes = [self.getnode(arg) for arg in op.args]
-        viewnodes(self.inputnodes, exitnodes)
-        if hasattr(self._loop.token, "specnodes"):
-            view(*self._loop.token.specnodes)
-
-
-    def setup_input_nodes(self, inputargs):
-        inputnodes = []
-        for box in inputargs:
-            instnode = InstanceNode(fromstart=True)
-            inputnodes.append(instnode)
-            self.nodes[box] = instnode
-        self.inputnodes = inputnodes
-
-    def build_result_specnodes(self, loop):
-        # Build the list of specnodes based on the result
-        # computed by NodeFinder.find_nodes().
-        op = loop.operations[-1]
-        assert op.getopnum() == rop.JUMP
-        assert len(self.inputnodes) == op.numargs()
-        while True:
-            self.restart_needed = False
-            specnodes = []
-            for i in range(op.numargs()):
-                inputnode = self.inputnodes[i]
-                exitnode = self.getnode(op.getarg(i))
-                specnodes.append(self.intersect(inputnode, exitnode))
-            if not self.restart_needed:
-                break
-        loop.token.specnodes = specnodes
-
-    def intersect(self, inputnode, exitnode):
-        assert inputnode.fromstart
-        if inputnode.is_constant() and \
-           exitnode.is_constant():
-            if inputnode.knownvaluebox.same_constant(exitnode.knownvaluebox):
-                return ConstantSpecNode(inputnode.knownvaluebox)
-            else:
-                raise InvalidLoop
-        if inputnode.escaped:
-            return prebuiltNotSpecNode
-        unique = exitnode.unique
-        if unique == UNIQUE_NO:
-            if inputnode is not self.node_fromstart:
-                # Mark the input node as escaped, and schedule a complete
-                # restart of intersect().  This is needed because there is
-                # an order dependency: calling inputnode.mark_escaped()
-                # might set the field exitnode.unique to UNIQUE_NO in some
-                # other node.  If inputnode is node_fromstart, there is no
-                # problem (and it must not be mutated by mark_escaped() then).
-                inputnode.mark_escaped()
-                self.restart_needed = True
-            return prebuiltNotSpecNode
-        if unique == UNIQUE_INST:
-            return self.intersect_instance(inputnode, exitnode)
-        if unique == UNIQUE_ARRAY:
-            return self.intersect_array(inputnode, exitnode)
-        if unique == UNIQUE_STRUCT:
-            return self.intersect_struct(inputnode, exitnode)
-        assert 0, "unknown value for exitnode.unique: %d" % ord(unique)
-
-    def compute_common_fields(self, orig, d):
-        fields = []
-        if orig is not None:
-            if d is not None:
-                d = d.copy()
-            else:
-                d = {}
-            for ofs in orig:
-                d.setdefault(ofs, self.node_escaped)
-        if d is not None:
-            lst = d.keys()
-            # we always use the "standardized" order of fields
-            sort_descrs(lst)
-            for ofs in lst:
-                try:
-                    if orig is None:
-                        raise KeyError
-                    node = orig[ofs]
-                except KeyError:
-                    # field stored at exit, but not read at input.  Must
-                    # still be allocated, otherwise it will be incorrectly
-                    # uninitialized after a guard failure.
-                    node = self.node_fromstart
-                specnode = self.intersect(node, d[ofs])
-                fields.append((ofs, specnode))
-        return fields
-
-    def intersect_instance(self, inputnode, exitnode):
-        if (inputnode.knownclsbox is not None and
-            not inputnode.knownclsbox.same_constant(exitnode.knownclsbox)):
-            # unique match, but the class is known to be a mismatch
-            raise InvalidLoop
-        #
-        fields = self.compute_common_fields(inputnode.origfields,
-                                            exitnode.curfields)
-        return VirtualInstanceSpecNode(exitnode.knownclsbox, fields)
-
-    def intersect_array(self, inputnode, exitnode):
-        assert inputnode.arraydescr is None
-        #
-        items = []
-        for i in range(exitnode.arraysize):
-            if exitnode.curitems is None:
-                exitsubnode = self.node_escaped
-            else:
-                exitsubnode = exitnode.curitems.get(i, self.node_escaped)
-            if inputnode.origitems is None:
-                node = self.node_fromstart
-            else:
-                node = inputnode.origitems.get(i, self.node_fromstart)
-            specnode = self.intersect(node, exitsubnode)
-            items.append(specnode)
-        return VirtualArraySpecNode(exitnode.arraydescr, items)
-