[pypy-svn] r70836 - in pypy/branch/stringbuilder2: . ctypes_configure lib-python lib-python/modified-2.5.2/test pypy pypy/config pypy/doc pypy/doc/config pypy/doc/jit pypy/interpreter pypy/interpreter/test pypy/jit/backend pypy/jit/backend/llgraph pypy/jit/backend/llgraph/test pypy/jit/backend/llsupport pypy/jit/backend/llvm/test pypy/jit/backend/test pypy/jit/backend/x86 pypy/jit/backend/x86/test pypy/jit/metainterp pypy/jit/metainterp/test pypy/jit/tool pypy/jit/tool/test pypy/lib pypy/lib/_ctypes pypy/lib/app_test pypy/lib/app_test/ctypes_tests pypy/module/__builtin__ pypy/module/__builtin__/test pypy/module/__pypy__ pypy/module/_demo pypy/module/_demo/test pypy/module/_stackless pypy/module/exceptions pypy/module/imp pypy/module/imp/test pypy/module/oracle pypy/module/posix pypy/module/posix/test pypy/module/pypyjit pypy/module/pypyjit/test pypy/module/sys pypy/module/thread pypy/module/zipimport pypy/module/zipimport/test pypy/objspace pypy/objspace/flow pypy/objspace/std pypy/objspace/std/test pypy/objspace/test pypy/rlib pypy/rlib/test pypy/rpython pypy/rpython/lltypesystem pypy/rpython/lltypesystem/test pypy/rpython/memory/gctransform pypy/rpython/memory/gctransform/test pypy/rpython/module pypy/rpython/test pypy/rpython/tool pypy/tool pypy/tool/test pypy/translator pypy/translator/backendopt pypy/translator/backendopt/test pypy/translator/benchmark pypy/translator/benchmark/test pypy/translator/c pypy/translator/c/src pypy/translator/c/test pypy/translator/cli pypy/translator/goal pypy/translator/jvm pypy/translator/platform pypy/translator/platform/test pypy/translator/test

fijal at codespeak.net fijal at codespeak.net
Mon Jan 25 15:25:54 CET 2010


Author: fijal
Date: Mon Jan 25 15:25:48 2010
New Revision: 70836

Added:
   pypy/branch/stringbuilder2/lib-python/modified-2.5.2/test/test_runpy.py
      - copied unchanged from r70835, pypy/trunk/lib-python/modified-2.5.2/test/test_runpy.py
   pypy/branch/stringbuilder2/pypy/doc/config/objspace.usemodules.imp.txt
      - copied unchanged from r70835, pypy/trunk/pypy/doc/config/objspace.usemodules.imp.txt
   pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_virtualref.py
      - copied unchanged from r70835, pypy/trunk/pypy/jit/backend/x86/test/test_virtualref.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_virtualref.py
      - copied unchanged from r70835, pypy/trunk/pypy/jit/metainterp/test/test_virtualref.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/virtualref.py
      - copied unchanged from r70835, pypy/trunk/pypy/jit/metainterp/virtualref.py
   pypy/branch/stringbuilder2/pypy/module/_demo/test/   (props changed)
      - copied from r70835, pypy/trunk/pypy/module/_demo/test/
   pypy/branch/stringbuilder2/pypy/module/imp/   (props changed)
      - copied from r70835, pypy/trunk/pypy/module/imp/
   pypy/branch/stringbuilder2/pypy/module/pypyjit/test/test_pyframe.py
      - copied unchanged from r70835, pypy/trunk/pypy/module/pypyjit/test/test_pyframe.py
   pypy/branch/stringbuilder2/pypy/rlib/_jit_vref.py
      - copied unchanged from r70835, pypy/trunk/pypy/rlib/_jit_vref.py
   pypy/branch/stringbuilder2/pypy/rlib/test/test__jit_vref.py
      - copied unchanged from r70835, pypy/trunk/pypy/rlib/test/test__jit_vref.py
   pypy/branch/stringbuilder2/pypy/tool/package.py
      - copied unchanged from r70835, pypy/trunk/pypy/tool/package.py
   pypy/branch/stringbuilder2/pypy/tool/test/test_package.py
      - copied unchanged from r70835, pypy/trunk/pypy/tool/test/test_package.py
   pypy/branch/stringbuilder2/pypy/translator/c/src/debug_print.h
      - copied unchanged from r70835, pypy/trunk/pypy/translator/c/src/debug_print.h
   pypy/branch/stringbuilder2/pypy/translator/c/src/debug_traceback.h
      - copied unchanged from r70835, pypy/trunk/pypy/translator/c/src/debug_traceback.h
Removed:
   pypy/branch/stringbuilder2/lib-python/modified-2.5.2/test/test___all__.py
   pypy/branch/stringbuilder2/lib-python/modified-2.5.2/test/test_importhooks.py
   pypy/branch/stringbuilder2/pypy/lib/app_test/test_imp_extra.py
   pypy/branch/stringbuilder2/pypy/lib/imp.py
   pypy/branch/stringbuilder2/pypy/module/__builtin__/app_misc.py
   pypy/branch/stringbuilder2/pypy/module/__builtin__/importing.py
   pypy/branch/stringbuilder2/pypy/module/__builtin__/test/test_import.py
   pypy/branch/stringbuilder2/pypy/module/thread/importlock.py
   pypy/branch/stringbuilder2/pypy/translator/benchmark/test/
   pypy/branch/stringbuilder2/pypy/translator/c/src/debug.h
   pypy/branch/stringbuilder2/pypy/translator/c/src/debuginfo.h
   pypy/branch/stringbuilder2/pypy/translator/c/src/trace.h
Modified:
   pypy/branch/stringbuilder2/LICENSE
   pypy/branch/stringbuilder2/ctypes_configure/cbuild.py
   pypy/branch/stringbuilder2/lib-python/   (props changed)
   pypy/branch/stringbuilder2/lib-python/conftest.py
   pypy/branch/stringbuilder2/lib-python/modified-2.5.2/test/infinite_reload.py
   pypy/branch/stringbuilder2/lib-python/modified-2.5.2/test/test_import.py
   pypy/branch/stringbuilder2/pypy/   (props changed)
   pypy/branch/stringbuilder2/pypy/config/pypyoption.py
   pypy/branch/stringbuilder2/pypy/doc/coding-guide.txt
   pypy/branch/stringbuilder2/pypy/doc/config/objspace.std.withcelldict.txt
   pypy/branch/stringbuilder2/pypy/doc/confrest.py
   pypy/branch/stringbuilder2/pypy/doc/getting-started.txt
   pypy/branch/stringbuilder2/pypy/doc/jit/pyjitpl5.txt
   pypy/branch/stringbuilder2/pypy/interpreter/argument.py
   pypy/branch/stringbuilder2/pypy/interpreter/baseobjspace.py
   pypy/branch/stringbuilder2/pypy/interpreter/error.py
   pypy/branch/stringbuilder2/pypy/interpreter/executioncontext.py
   pypy/branch/stringbuilder2/pypy/interpreter/function.py
   pypy/branch/stringbuilder2/pypy/interpreter/gateway.py
   pypy/branch/stringbuilder2/pypy/interpreter/generator.py
   pypy/branch/stringbuilder2/pypy/interpreter/mixedmodule.py
   pypy/branch/stringbuilder2/pypy/interpreter/module.py
   pypy/branch/stringbuilder2/pypy/interpreter/pycode.py
   pypy/branch/stringbuilder2/pypy/interpreter/pyframe.py
   pypy/branch/stringbuilder2/pypy/interpreter/pyopcode.py
   pypy/branch/stringbuilder2/pypy/interpreter/pytraceback.py
   pypy/branch/stringbuilder2/pypy/interpreter/test/test_executioncontext.py
   pypy/branch/stringbuilder2/pypy/interpreter/test/test_gateway.py
   pypy/branch/stringbuilder2/pypy/interpreter/test/test_module.py
   pypy/branch/stringbuilder2/pypy/interpreter/test/test_zzpickle_and_slow.py
   pypy/branch/stringbuilder2/pypy/jit/backend/llgraph/llimpl.py
   pypy/branch/stringbuilder2/pypy/jit/backend/llgraph/runner.py
   pypy/branch/stringbuilder2/pypy/jit/backend/llgraph/test/test_llgraph.py
   pypy/branch/stringbuilder2/pypy/jit/backend/llsupport/gc.py
   pypy/branch/stringbuilder2/pypy/jit/backend/llvm/test/conftest.py   (props changed)
   pypy/branch/stringbuilder2/pypy/jit/backend/model.py
   pypy/branch/stringbuilder2/pypy/jit/backend/test/runner_test.py
   pypy/branch/stringbuilder2/pypy/jit/backend/test/support.py
   pypy/branch/stringbuilder2/pypy/jit/backend/test/test_random.py
   pypy/branch/stringbuilder2/pypy/jit/backend/x86/assembler.py
   pypy/branch/stringbuilder2/pypy/jit/backend/x86/regalloc.py
   pypy/branch/stringbuilder2/pypy/jit/backend/x86/runner.py
   pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_gc_integration.py   (contents, props changed)
   pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_recursive.py
   pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_regalloc.py
   pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_runner.py
   pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_tlc.py
   pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_ztranslation.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/codewriter.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/compile.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/effectinfo.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/executor.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/history.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/jitprof.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/logger.py   (props changed)
   pypy/branch/stringbuilder2/pypy/jit/metainterp/optimizeopt.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/optimizeutil.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/policy.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/pyjitpl.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/resoperation.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/resume.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/support.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_basic.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_codewriter.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_compile.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_effectinfo.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_executor.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_history.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_jitprof.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_optimizefindnode.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_optimizeopt.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_pyjitpl.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_recursive.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_resume.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_virtualizable.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_warmspot.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_warmstate.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_ztranslation.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/virtualizable.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/warmspot.py
   pypy/branch/stringbuilder2/pypy/jit/metainterp/warmstate.py
   pypy/branch/stringbuilder2/pypy/jit/tool/jitoutput.py
   pypy/branch/stringbuilder2/pypy/jit/tool/showstats.py
   pypy/branch/stringbuilder2/pypy/jit/tool/test/test_jitoutput.py
   pypy/branch/stringbuilder2/pypy/lib/_ctypes/array.py
   pypy/branch/stringbuilder2/pypy/lib/_ctypes/primitive.py
   pypy/branch/stringbuilder2/pypy/lib/_ctypes/structure.py
   pypy/branch/stringbuilder2/pypy/lib/_ctypes/union.py
   pypy/branch/stringbuilder2/pypy/lib/app_test/ctypes_tests/test_keepalive.py
   pypy/branch/stringbuilder2/pypy/lib/app_test/test_runpy.py
   pypy/branch/stringbuilder2/pypy/module/__builtin__/__init__.py
   pypy/branch/stringbuilder2/pypy/module/__builtin__/descriptor.py
   pypy/branch/stringbuilder2/pypy/module/__builtin__/test/test_descriptor.py
   pypy/branch/stringbuilder2/pypy/module/__pypy__/__init__.py
   pypy/branch/stringbuilder2/pypy/module/_demo/__init__.py
   pypy/branch/stringbuilder2/pypy/module/_stackless/interp_coroutine.py
   pypy/branch/stringbuilder2/pypy/module/exceptions/   (props changed)
   pypy/branch/stringbuilder2/pypy/module/imp/test/   (props changed)
   pypy/branch/stringbuilder2/pypy/module/oracle/__init__.py
   pypy/branch/stringbuilder2/pypy/module/oracle/interp_error.py
   pypy/branch/stringbuilder2/pypy/module/posix/__init__.py
   pypy/branch/stringbuilder2/pypy/module/posix/interp_posix.py
   pypy/branch/stringbuilder2/pypy/module/posix/test/test_posix2.py
   pypy/branch/stringbuilder2/pypy/module/pypyjit/interp_jit.py
   pypy/branch/stringbuilder2/pypy/module/pypyjit/test/test_pypy_c.py
   pypy/branch/stringbuilder2/pypy/module/sys/vm.py
   pypy/branch/stringbuilder2/pypy/module/thread/__init__.py
   pypy/branch/stringbuilder2/pypy/module/zipimport/__init__.py
   pypy/branch/stringbuilder2/pypy/module/zipimport/interp_zipimport.py
   pypy/branch/stringbuilder2/pypy/module/zipimport/test/test_zipimport.py
   pypy/branch/stringbuilder2/pypy/objspace/descroperation.py
   pypy/branch/stringbuilder2/pypy/objspace/flow/flowcontext.py
   pypy/branch/stringbuilder2/pypy/objspace/std/celldict.py
   pypy/branch/stringbuilder2/pypy/objspace/std/objspace.py
   pypy/branch/stringbuilder2/pypy/objspace/std/rangeobject.py
   pypy/branch/stringbuilder2/pypy/objspace/std/test/test_celldict.py
   pypy/branch/stringbuilder2/pypy/objspace/std/test/test_setobject.py   (props changed)
   pypy/branch/stringbuilder2/pypy/objspace/std/test/test_userobject.py
   pypy/branch/stringbuilder2/pypy/objspace/test/test_descriptor.py
   pypy/branch/stringbuilder2/pypy/objspace/test/test_descroperation.py
   pypy/branch/stringbuilder2/pypy/rlib/debug.py
   pypy/branch/stringbuilder2/pypy/rlib/jit.py
   pypy/branch/stringbuilder2/pypy/rlib/rgc.py
   pypy/branch/stringbuilder2/pypy/rpython/llinterp.py
   pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/ll2ctypes.py
   pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/lloperation.py
   pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/opimpl.py
   pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/rclass.py
   pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/rlist.py
   pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/test/test_ll2ctypes.py
   pypy/branch/stringbuilder2/pypy/rpython/memory/gctransform/framework.py
   pypy/branch/stringbuilder2/pypy/rpython/memory/gctransform/test/test_framework.py
   pypy/branch/stringbuilder2/pypy/rpython/module/ll_os.py
   pypy/branch/stringbuilder2/pypy/rpython/rlist.py
   pypy/branch/stringbuilder2/pypy/rpython/rptr.py
   pypy/branch/stringbuilder2/pypy/rpython/rtyper.py
   pypy/branch/stringbuilder2/pypy/rpython/rvirtualizable2.py
   pypy/branch/stringbuilder2/pypy/rpython/test/test_rlist.py
   pypy/branch/stringbuilder2/pypy/rpython/test/test_rptr.py
   pypy/branch/stringbuilder2/pypy/rpython/test/test_rvirtualizable2.py
   pypy/branch/stringbuilder2/pypy/rpython/tool/rffi_platform.py
   pypy/branch/stringbuilder2/pypy/tool/gcc_cache.py
   pypy/branch/stringbuilder2/pypy/tool/udir.py
   pypy/branch/stringbuilder2/pypy/translator/backendopt/test/test_writeanalyze.py
   pypy/branch/stringbuilder2/pypy/translator/backendopt/writeanalyze.py
   pypy/branch/stringbuilder2/pypy/translator/benchmark/   (props changed)
   pypy/branch/stringbuilder2/pypy/translator/benchmark/bench-custom.py
   pypy/branch/stringbuilder2/pypy/translator/benchmark/benchmarks.py
   pypy/branch/stringbuilder2/pypy/translator/benchmark/jitbench.py
   pypy/branch/stringbuilder2/pypy/translator/benchmark/result.py
   pypy/branch/stringbuilder2/pypy/translator/c/funcgen.py
   pypy/branch/stringbuilder2/pypy/translator/c/gc.py
   pypy/branch/stringbuilder2/pypy/translator/c/genc.py
   pypy/branch/stringbuilder2/pypy/translator/c/src/g_include.h
   pypy/branch/stringbuilder2/pypy/translator/c/src/main.h
   pypy/branch/stringbuilder2/pypy/translator/c/test/test_boehm.py
   pypy/branch/stringbuilder2/pypy/translator/c/test/test_genc.py
   pypy/branch/stringbuilder2/pypy/translator/c/test/test_refcount.py   (props changed)
   pypy/branch/stringbuilder2/pypy/translator/c/test/test_stackless.py
   pypy/branch/stringbuilder2/pypy/translator/c/test/test_standalone.py
   pypy/branch/stringbuilder2/pypy/translator/cli/opcodes.py
   pypy/branch/stringbuilder2/pypy/translator/driver.py
   pypy/branch/stringbuilder2/pypy/translator/exceptiontransform.py
   pypy/branch/stringbuilder2/pypy/translator/goal/app_main.py
   pypy/branch/stringbuilder2/pypy/translator/jvm/opcodes.py
   pypy/branch/stringbuilder2/pypy/translator/platform/__init__.py
   pypy/branch/stringbuilder2/pypy/translator/platform/darwin.py
   pypy/branch/stringbuilder2/pypy/translator/platform/linux.py
   pypy/branch/stringbuilder2/pypy/translator/platform/posix.py
   pypy/branch/stringbuilder2/pypy/translator/platform/test/test_darwin.py
   pypy/branch/stringbuilder2/pypy/translator/platform/test/test_platform.py
   pypy/branch/stringbuilder2/pypy/translator/test/test_exceptiontransform.py
Log:
merge from trunk:
svn merge -r 70153:HEAD svn+ssh://codespeak.net/svn/pypy/trunk .


Modified: pypy/branch/stringbuilder2/LICENSE
==============================================================================
--- pypy/branch/stringbuilder2/LICENSE	(original)
+++ pypy/branch/stringbuilder2/LICENSE	Mon Jan 25 15:25:48 2010
@@ -27,7 +27,7 @@
     DEALINGS IN THE SOFTWARE.
 
 
-PyPy Copyright holders 2003-2009
+PyPy Copyright holders 2003-2010
 ----------------------------------- 
 
 Except when otherwise stated (look for LICENSE files or information at
@@ -86,6 +86,7 @@
     Guenter Jantzen
     Dinu Gherman
     Georg Brandl
+    Benjamin Peterson
     Ben Young
     Nicolas Chauvat
     Jean-Paul Calderone

Modified: pypy/branch/stringbuilder2/ctypes_configure/cbuild.py
==============================================================================
--- pypy/branch/stringbuilder2/ctypes_configure/cbuild.py	(original)
+++ pypy/branch/stringbuilder2/ctypes_configure/cbuild.py	Mon Jan 25 15:25:48 2010
@@ -5,8 +5,6 @@
 
 debug = 0
 
-log = py.log.Producer("cbuild")
-
 configdir = py.path.local.make_numbered_dir(prefix='ctypes_configure')
 
 class ExternalCompilationInfo(object):
@@ -327,7 +325,7 @@
 def log_spawned_cmd(spawn):
     def spawn_and_log(cmd, *args, **kwds):
         if debug:
-            log.execute(' '.join(cmd))
+            print ' '.join(cmd)
         return spawn(cmd, *args, **kwds)
     return spawn_and_log
 

Modified: pypy/branch/stringbuilder2/lib-python/conftest.py
==============================================================================
--- pypy/branch/stringbuilder2/lib-python/conftest.py	(original)
+++ pypy/branch/stringbuilder2/lib-python/conftest.py	Mon Jan 25 15:25:48 2010
@@ -311,7 +311,7 @@
     RegrTest('test_normalization.py'),
     RegrTest('test_ntpath.py'),
     RegrTest('test_opcodes.py', core=True),
-    RegrTest('test_openpty.py', skip="unsupported extension module"),
+    RegrTest('test_openpty.py'),
     RegrTest('test_operations.py', core=True),
     RegrTest('test_operator.py', core=True),
     RegrTest('test_optparse.py'),

Modified: pypy/branch/stringbuilder2/lib-python/modified-2.5.2/test/infinite_reload.py
==============================================================================
--- pypy/branch/stringbuilder2/lib-python/modified-2.5.2/test/infinite_reload.py	(original)
+++ pypy/branch/stringbuilder2/lib-python/modified-2.5.2/test/infinite_reload.py	Mon Jan 25 15:25:48 2010
@@ -3,8 +3,5 @@
 #  reload()ing. This module is imported by test_import.py:test_infinite_reload
 #  to make sure this doesn't happen any more.
 
-print 1
 import infinite_reload
-print 2
 reload(infinite_reload)
-print 3

Modified: pypy/branch/stringbuilder2/lib-python/modified-2.5.2/test/test_import.py
==============================================================================
--- pypy/branch/stringbuilder2/lib-python/modified-2.5.2/test/test_import.py	(original)
+++ pypy/branch/stringbuilder2/lib-python/modified-2.5.2/test/test_import.py	Mon Jan 25 15:25:48 2010
@@ -1,4 +1,4 @@
-from test.test_support import TESTFN, TestFailed, check_impl_detail
+from test.test_support import TESTFN, TestFailed
 
 import os
 import random
@@ -56,6 +56,11 @@
         os.unlink(source)
 
     try:
+        #--- the block below is to check that "reload" manages to import
+        #--- the .pyc file alone.  We don't support it in PyPy in the default
+        #--- configuration.
+        return
+
         try:
             reload(mod)
         except ImportError, err:
@@ -238,5 +243,4 @@
     finally:
         sys.path.pop(0)
 
-if check_impl_detail():
-    test_infinite_reload()
+test_infinite_reload()

Modified: pypy/branch/stringbuilder2/pypy/config/pypyoption.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/config/pypyoption.py	(original)
+++ pypy/branch/stringbuilder2/pypy/config/pypyoption.py	Mon Jan 25 15:25:48 2010
@@ -16,7 +16,7 @@
 
 default_modules = essential_modules.copy()
 default_modules.update(dict.fromkeys(
-    ["_codecs", "gc", "_weakref", "marshal", "errno",
+    ["_codecs", "gc", "_weakref", "marshal", "errno", "imp",
      "math", "_sre", "_pickle_support", "operator",
      "parser", "symbol", "token", "_ast", "_random", "__pypy__",
      "_testing"]))
@@ -345,7 +345,7 @@
         config.objspace.std.suggest(withprebuiltint=True)
         config.objspace.std.suggest(withrangelist=True)
         config.objspace.std.suggest(withprebuiltchar=True)
-        config.objspace.std.suggest(withsharingdict=True)
+        config.objspace.std.suggest(withinlineddict=True)
         config.objspace.std.suggest(withstrslice=True)
         config.objspace.std.suggest(withstrjoin=True)
         # xxx other options? ropes maybe?

Modified: pypy/branch/stringbuilder2/pypy/doc/coding-guide.txt
==============================================================================
--- pypy/branch/stringbuilder2/pypy/doc/coding-guide.txt	(original)
+++ pypy/branch/stringbuilder2/pypy/doc/coding-guide.txt	Mon Jan 25 15:25:48 2010
@@ -160,7 +160,8 @@
 An example can be found in the current implementation which is quite
 elegant: For the definition of all the opcodes of the Python
 interpreter, the module ``dis`` is imported and used to initialize our
-bytecode interpreter.  (See ``__initclass__`` in `pyopcode.py`_).  This
+bytecode interpreter.  (See ``__initclass__`` in
+`pypy/interpreter/pyopcode.py`_).  This
 saves us from adding extra modules to PyPy. The import code is run at
 startup time, and we are allowed to use the CPython builtin import
 function.
@@ -173,8 +174,6 @@
 enables the code generator to emit efficient machine level replacements
 for pure integer objects, for instance.
 
-.. _`pyopcode.py`: http://codespeak.net/svn/pypy/dist/pypy/interpreter/pyopcode.py
-
 Restricted Python
 =================
 

Modified: pypy/branch/stringbuilder2/pypy/doc/config/objspace.std.withcelldict.txt
==============================================================================
--- pypy/branch/stringbuilder2/pypy/doc/config/objspace.std.withcelldict.txt	(original)
+++ pypy/branch/stringbuilder2/pypy/doc/config/objspace.std.withcelldict.txt	Mon Jan 25 15:25:48 2010
@@ -1,2 +1,2 @@
-Enable cell-dicts. This makes global lookups nearly as fast as the lookup of a
-local.
+Enable cell-dicts. This optimization is not helpful without the JIT. In the
+presence of the JIT, it greatly helps looking up globals.

Modified: pypy/branch/stringbuilder2/pypy/doc/confrest.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/doc/confrest.py	(original)
+++ pypy/branch/stringbuilder2/pypy/doc/confrest.py	Mon Jan 25 15:25:48 2010
@@ -5,6 +5,17 @@
 html = py.xml.html
 
 class PyPyPage(Page): 
+    googlefragment = """
+<script type="text/javascript">
+var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
+document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
+</script>
+<script type="text/javascript">
+try {
+var pageTracker = _gat._getTracker("UA-7778406-2");
+pageTracker._trackPageview();
+} catch(err) {}</script>
+"""
     def fill_menubar(self):
         self.menubar = html.div(
             html.a("home", 
@@ -31,6 +42,14 @@
     def get_doclink(self, target):
         return relpath(self.targetpath.strpath,
                        self.project.docpath.join(target).strpath)
+
+    def unicode(self, doctype=True): 
+        page = self._root.unicode() 
+        page = page.replace("</body>", self.googlefragment + "</body>")
+        if doctype: 
+            return self.doctype + page 
+        else: 
+            return page 
         
 
 class Project(Project): 

Modified: pypy/branch/stringbuilder2/pypy/doc/getting-started.txt
==============================================================================
--- pypy/branch/stringbuilder2/pypy/doc/getting-started.txt	(original)
+++ pypy/branch/stringbuilder2/pypy/doc/getting-started.txt	Mon Jan 25 15:25:48 2010
@@ -35,22 +35,31 @@
 Before you can play with PyPy, you will need to obtain a copy
 of the sources.  This can be done either by `downloading them
 from the download page`_ or by checking them out from the
-repository using subversion.  We suggest using subversion as it
-offers access to the most recent versions.
+repository using subversion.  We suggest using subversion if one
+wants to access the current development.
 
 .. _`downloading them from the download page`: download.html
 
 If you choose to use subversion, you must issue the following command on your
 command line, DOS box, or terminal::
 
-    svn co http://codespeak.net/svn/pypy/dist pypy-dist
+    svn co http://codespeak.net/svn/pypy/trunk pypy-trunk
+
+This will check out the subversion head and place it into a directory
+named ``pypy-trunk``, and will get you the PyPy source in
+``pypy-trunk/pypy`` and documentation files in ``pypy-trunk/pypy/doc``.
+We try to ensure that the head is always stable, but it might
+occasionally be broken.  You may want to check out `our nightly tests:`_
+find a revision (5-digit number) that passed at least the
+``{own}`` and ``{applevel}`` tests (corresponding to a ``+`` sign on the
+line ``success``) and then check out using::
+
+    svn co -rXXXXX http://codespeak.net/svn/pypy/trunk pypy-trunk
+
+where XXXXX is the revision number.
+
+.. _`our nightly tests:`: http://codespeak.net:8099/summary?branch=<trunk>
 
-This will check out the most recent stable release from subversion and
-place it into a directory named ``pypy-dist``, and will get you the PyPy
-source in ``pypy-dist/pypy`` and documentation files in
-``pypy-dist/pypy/doc``.  If you would prefer to check out the "cutting edge"
-version of PyPy - which may not always be stable! - then check out
-from ``http://codespeak.net/svn/pypy/trunk`` intead.
 
 Where to go from here
 ----------------------

Modified: pypy/branch/stringbuilder2/pypy/doc/jit/pyjitpl5.txt
==============================================================================
--- pypy/branch/stringbuilder2/pypy/doc/jit/pyjitpl5.txt	(original)
+++ pypy/branch/stringbuilder2/pypy/doc/jit/pyjitpl5.txt	Mon Jan 25 15:25:48 2010
@@ -2,7 +2,7 @@
  PyJitPl5
 ==========
 
-This document describes the fith generation of PyPy's JIT.
+This document describes the fifth generation of PyPy's JIT.
 
 
 Implementation of the JIT

Modified: pypy/branch/stringbuilder2/pypy/interpreter/argument.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/interpreter/argument.py	(original)
+++ pypy/branch/stringbuilder2/pypy/interpreter/argument.py	Mon Jan 25 15:25:48 2010
@@ -4,7 +4,7 @@
 
 from pypy.interpreter.error import OperationError
 from pypy.rlib.debug import make_sure_not_resized
-from pypy.rlib.jit import purefunction, unroll_safe
+from pypy.rlib import jit
 
 
 class Signature(object):
@@ -17,7 +17,7 @@
         self.varargname = varargname
         self.kwargname = kwargname
 
-    @purefunction
+    @jit.purefunction
     def find_argname(self, name):
         try:
             return self.argnames.index(name)
@@ -101,6 +101,11 @@
         make_sure_not_resized(self.arguments_w)
         if w_stararg is not None or w_starstararg is not None:
             self._combine_wrapped(w_stararg, w_starstararg)
+            # if we have a call where * or ** args are used at the callsite
+            # we shouldn't let the JIT see the argument matching
+            self._dont_jit = True
+        else:
+            self._dont_jit = False
         
     def __repr__(self):
         """ NOT_RPYTHON """
@@ -188,13 +193,28 @@
         
     ###  Parsing for function calls  ###
 
-    @unroll_safe # XXX not true always, but for now
     def _match_signature(self, w_firstarg, scope_w, signature, defaults_w=[],
                          blindargs=0):
         """Parse args and kwargs according to the signature of a code object,
         or raise an ArgErr in case of failure.
         Return the number of arguments filled in.
         """
+        if jit.we_are_jitted() and self._dont_jit:
+            return self._match_signature_jit_opaque(w_firstarg, scope_w,
+                                                    signature, defaults_w,
+                                                    blindargs)
+        return self._really_match_signature(w_firstarg, scope_w, signature,
+                                            defaults_w, blindargs)
+
+    @jit.dont_look_inside
+    def _match_signature_jit_opaque(self, w_firstarg, scope_w, signature,
+                                    defaults_w, blindargs):
+        return self._really_match_signature(w_firstarg, scope_w, signature,
+                                            defaults_w, blindargs)
+
+    @jit.unroll_safe
+    def _really_match_signature(self, w_firstarg, scope_w, signature, defaults_w=[],
+                                blindargs=0):
         #
         #   args_w = list of the normal actual parameters, wrapped
         #   kwds_w = real dictionary {'keyword': wrapped parameter}

Modified: pypy/branch/stringbuilder2/pypy/interpreter/baseobjspace.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/interpreter/baseobjspace.py	(original)
+++ pypy/branch/stringbuilder2/pypy/interpreter/baseobjspace.py	Mon Jan 25 15:25:48 2010
@@ -9,7 +9,7 @@
 from pypy.rlib.objectmodel import we_are_translated
 from pypy.rlib.debug import make_sure_not_resized
 from pypy.rlib.timer import DummyTimer, Timer
-from pypy.rlib.jit import we_are_jitted, dont_look_inside, unroll_safe
+from pypy.rlib import jit
 import os, sys
 
 __all__ = ['ObjSpace', 'OperationError', 'Wrappable', 'W_Root']
@@ -239,6 +239,9 @@
             config = get_pypy_config(translating=False)
         self.config = config
 
+        self.builtin_modules = {}
+        self.reloading_modules = {}
+
         # import extra modules for side-effects
         import pypy.interpreter.nestedscope     # register *_DEREF bytecodes
 
@@ -266,16 +269,22 @@
     def startup(self):
         # To be called before using the space
 
-        # Initialize all builtin modules
+        # Initialize already imported builtin modules
         from pypy.interpreter.module import Module
+        w_modules = self.sys.get('modules')
         for w_modname in self.unpackiterable(
                                 self.sys.get('builtin_module_names')):
+            try:
+                w_mod = self.getitem(w_modules, w_modname)
+            except OperationError, e:
+                if e.match(self, self.w_KeyError):
+                    continue
+                raise
             modname = self.str_w(w_modname)
-            mod = self.interpclass_w(self.getbuiltinmodule(modname))
+            mod = self.interpclass_w(w_mod)
             if isinstance(mod, Module):
-                import time
                 self.timer.start("startup " + modname)
-                mod.startup(self)
+                mod.init(self)
                 self.timer.stop("startup " + modname)
 
     def finish(self):
@@ -283,11 +292,9 @@
         if w_exitfunc is not None:
             self.call_function(w_exitfunc)
         from pypy.interpreter.module import Module
-        for w_modname in self.unpackiterable(
-                                self.sys.get('builtin_module_names')):
-            modname = self.str_w(w_modname)
-            mod = self.interpclass_w(self.getbuiltinmodule(modname))
-            if isinstance(mod, Module):
+        for w_mod in self.builtin_modules.values():
+            mod = self.interpclass_w(w_mod)
+            if isinstance(mod, Module) and mod.startup_called:
                 mod.shutdown(self)
         if self.config.objspace.std.withdictmeasurement:
             from pypy.objspace.std.dictmultiobject import report
@@ -339,14 +346,42 @@
 
         w_name = self.wrap(name)
         w_mod = self.wrap(Module(self, w_name))
-        w_modules = self.sys.get('modules')
-        self.setitem(w_modules, w_name, w_mod)
+        self.builtin_modules[name] = w_mod
         return name
 
-    def getbuiltinmodule(self, name):
+    def getbuiltinmodule(self, name, force_init=False):
         w_name = self.wrap(name)
         w_modules = self.sys.get('modules')
-        return self.getitem(w_modules, w_name)
+        try:
+            w_mod = self.getitem(w_modules, w_name)
+        except OperationError, e:
+            if not e.match(self, self.w_KeyError):
+                raise
+        else:
+            if not force_init:
+                return w_mod
+
+        # If the module is a builtin but not yet imported,
+        # retrieve it and initialize it
+        try:
+            w_mod = self.builtin_modules[name]
+        except KeyError:
+            raise OperationError(
+                self.w_SystemError,
+                self.wrap("getbuiltinmodule() called "
+                          "with non-builtin module %s" % name))
+        else:
+            # Add the module to sys.modules
+            self.setitem(w_modules, w_name, w_mod)
+
+            # And initialize it
+            from pypy.interpreter.module import Module
+            mod = self.interpclass_w(w_mod)
+            if isinstance(mod, Module):
+                self.timer.start("startup " + name)
+                mod.init(self)
+                self.timer.stop("startup " + name)
+            return w_mod
 
     def get_builtinmodule_to_install(self):
         """NOT_RPYTHON"""
@@ -390,26 +425,27 @@
         "NOT_RPYTHON: only for initializing the space."
 
         from pypy.module.exceptions import Module
-        w_name_exceptions = self.wrap('exceptions')
-        self.exceptions_module = Module(self, w_name_exceptions)
+        w_name = self.wrap('exceptions')
+        self.exceptions_module = Module(self, w_name)
+        self.builtin_modules['exceptions'] = self.wrap(self.exceptions_module)
 
         from pypy.module.sys import Module
         w_name = self.wrap('sys')
         self.sys = Module(self, w_name)
-        w_modules = self.sys.get('modules')
-        self.setitem(w_modules, w_name, self.wrap(self.sys))
+        self.builtin_modules['sys'] = self.wrap(self.sys)
 
-        self.setitem(w_modules, w_name_exceptions,
-                     self.wrap(self.exceptions_module))
+        from pypy.module.imp import Module
+        w_name = self.wrap('imp')
+        self.builtin_modules['imp'] = self.wrap(Module(self, w_name))
 
         from pypy.module.__builtin__ import Module
         w_name = self.wrap('__builtin__')
         self.builtin = Module(self, w_name)
         w_builtin = self.wrap(self.builtin)
-        self.setitem(w_modules, w_name, w_builtin)
+        self.builtin_modules['__builtin__'] = self.wrap(w_builtin)
         self.setitem(self.builtin.w_dict, self.wrap('__builtins__'), w_builtin)
 
-        bootstrap_modules = ['sys', '__builtin__', 'exceptions']
+        bootstrap_modules = ['sys', 'imp', '__builtin__', 'exceptions']
         installed_builtin_modules = bootstrap_modules[:]
 
         self.export_builtin_exceptions()
@@ -480,12 +516,11 @@
 
     def setup_builtin_modules(self):
         "NOT_RPYTHON: only for initializing the space."
-        from pypy.interpreter.module import Module
-        for w_modname in self.unpackiterable(self.sys.get('builtin_module_names')):
-            modname = self.unwrap(w_modname)
-            mod = self.getbuiltinmodule(modname)
-            if isinstance(mod, Module):
-                mod.setup_after_space_initialization()
+        self.getbuiltinmodule('sys')
+        self.getbuiltinmodule('imp')
+        self.getbuiltinmodule('__builtin__')
+        for mod in self.builtin_modules.values():
+            mod.setup_after_space_initialization()
 
     def initialize(self):
         """NOT_RPYTHON: Abstract method that should put some minimal
@@ -496,6 +531,7 @@
     def leave_cache_building_mode(self, val):
         "hook for the flow object space"
 
+    @jit.loop_invariant
     def getexecutioncontext(self):
         "Return what we consider to be the active execution context."
         # Important: the annotator must not see a prebuilt ExecutionContext:
@@ -700,7 +736,7 @@
         """
         return self.unpackiterable(w_iterable, expected_length)
 
-    @unroll_safe
+    @jit.unroll_safe
     def exception_match(self, w_exc_type, w_check_class):
         """Checks if the given exception type matches 'w_check_class'."""
         if self.is_w(w_exc_type, w_check_class):
@@ -756,8 +792,7 @@
 
     def call_valuestack(self, w_func, nargs, frame):
         from pypy.interpreter.function import Function, Method, is_builtin_code
-        if (not we_are_jitted() and frame.is_being_profiled and
-            is_builtin_code(w_func)):
+        if frame.is_being_profiled and is_builtin_code(w_func):
             # XXX: this code is copied&pasted :-( from the slow path below
             # call_valuestack().
             args = frame.make_arguments(nargs)
@@ -784,7 +819,6 @@
         args = frame.make_arguments(nargs)
         return self.call_args(w_func, args)
 
-    @dont_look_inside 
     def call_args_and_c_profile(self, frame, w_func, args):
         ec = self.getexecutioncontext()
         ec.c_call_trace(frame, w_func)

Modified: pypy/branch/stringbuilder2/pypy/interpreter/error.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/interpreter/error.py	(original)
+++ pypy/branch/stringbuilder2/pypy/interpreter/error.py	Mon Jan 25 15:25:48 2010
@@ -203,8 +203,8 @@
             w_instclass = space.exception_getclass(w_inst)
             if not space.exception_is_valid_class_w(w_instclass):
                 instclassname = w_instclass.getname(space, '?')
-                msg = ("exceptions must be classes, or instances,"
-                       "or strings (deprecated) not %s" % (instclassname,))
+                msg = ("exceptions must be classes, or instances, "
+                       "or strings (deprecated), not %s" % (instclassname,))
                 raise OperationError(space.w_TypeError, space.wrap(msg))
 
             if not space.is_w(w_value, space.w_None):

Modified: pypy/branch/stringbuilder2/pypy/interpreter/executioncontext.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/interpreter/executioncontext.py	(original)
+++ pypy/branch/stringbuilder2/pypy/interpreter/executioncontext.py	Mon Jan 25 15:25:48 2010
@@ -3,7 +3,6 @@
 from pypy.interpreter.error import OperationError
 from pypy.rlib.rarithmetic import LONG_BIT
 from pypy.rlib.unroll import unrolling_iterable
-from pypy.rlib.jit import we_are_jitted
 from pypy.rlib import jit
 
 def app_profile_call(space, w_callable, frame, event, w_arg):
@@ -15,181 +14,61 @@
     """An ExecutionContext holds the state of an execution thread
     in the Python interpreter."""
 
+    # XXX JIT: when tracing (but not when blackholing!), the following
+    # XXX fields should be known to a constant None or False:
+    # XXX   self.w_tracefunc, self.profilefunc
+    # XXX   frame.is_being_profiled
+
     def __init__(self, space):
         self.space = space
-        self._init_frame_chain()
+        self.topframeref = jit.vref_None
+        self.framestackdepth = 0
         # tracing: space.frame_trace_action.fire() must be called to ensure
         # that tracing occurs whenever self.w_tracefunc or self.is_tracing
         # is modified.
-        self.w_tracefunc = None
+        self.w_tracefunc = None        # if not None, no JIT
         self.is_tracing = 0
         self.compiler = space.createcompiler()
-        self.profilefunc = None
+        self.profilefunc = None        # if not None, no JIT
         self.w_profilefuncarg = None
 
+    def gettopframe(self):
+        return self.topframeref()
+
     def gettopframe_nohidden(self):
-        frame = self.gettopframe()
-        # I guess this should just use getnextframe_nohidden XXX
+        frame = self.topframeref()
         while frame and frame.hide():
-            frame = frame.f_back()
+            frame = frame.f_backref()
         return frame
 
     @staticmethod
     def getnextframe_nohidden(frame):
-        frame = frame.f_back()
+        frame = frame.f_backref()
         while frame and frame.hide():
-            frame = frame.f_back()
+            frame = frame.f_backref()
         return frame
 
     def enter(self, frame):
         if self.framestackdepth > self.space.sys.recursionlimit:
             raise OperationError(self.space.w_RuntimeError,
                                  self.space.wrap("maximum recursion depth exceeded"))
-        self._chain(frame)
+        self.framestackdepth += 1
+        frame.f_backref = self.topframeref
+        self.topframeref = jit.virtual_ref(frame)
 
     def leave(self, frame):
-        if self.profilefunc:
-            self._trace(frame, 'leaveframe', self.space.w_None)
+        try:
+            if self.profilefunc:
+                self._trace(frame, 'leaveframe', self.space.w_None)
+        finally:
+            self.topframeref = frame.f_backref
+            self.framestackdepth -= 1
+            jit.virtual_ref_finish(frame)
 
-        self._unchain(frame)
-        
         if self.w_tracefunc is not None and not frame.hide():
             self.space.frame_trace_action.fire()
 
     # ________________________________________________________________
-    # the methods below are used for chaining frames in JIT-friendly way
-    # part of that stuff is obscure
-
-    @jit.unroll_safe
-    def gettopframe(self):
-        frame = self.some_frame
-        if frame is not None:
-            while frame.f_forward is not None:
-                frame = frame.f_forward
-        return frame
-
-    def _init_frame_chain(self):
-        # 'some_frame' points to any frame from this thread's frame stack
-        # (although in general it should point to the top one).
-        # XXX not true: some_frame must point to a frame from which we can
-        # reach the top frame by following the chain of f_forward
-        self.some_frame = None
-        self.framestackdepth = 0
-
-    @staticmethod
-    def _init_chaining_attributes(frame):
-        """
-        explanation of the f_back handling:
-        -----------------------------------
-
-        in the non-JIT case, the frames simply form a doubly linked list via the
-        attributes f_back_some and f_forward.
-
-        When the JIT is used, things become more complex, as functions can be
-        inlined into each other. In this case a frame chain can look like this:
-
-        +---------------+
-        | real_frame    |
-        +---------------+
-           |      
-           | f_back_some
-           |      
-           |      
-           |  +--------------+
-           |  | virtual frame|
-           |  +--------------+
-           |      ^ 
-           |      | f_forward
-           |  +--------------+
-           |  | virtual frame|
-           |  +--------------+
-           |      ^
-           |      |
-           v      | f_forward
-        +---------------+
-        | real_frame    |
-        +---------------+
-           |
-           |
-           v
-          ...
-        
-        This ensures that the virtual frames don't escape via the f_back of the
-        real frames. For the same reason, the executioncontext's some_frame
-        attribute should only point to real frames.
-
-        All places where a frame can become accessed from applevel-code (like
-        sys._getframe and traceback catching) need to call force_f_back to ensure
-        that the intermediate virtual frames are forced to be real ones.
-
-        """ 
-        frame.f_back_some = None
-        frame.f_forward = None
-        frame.f_back_forced = False
-
-    def _chain(self, frame):
-        self.framestackdepth += 1
-        #
-        frame.f_back_some = self.some_frame
-        if self._we_are_jitted():
-            curtopframe = self.gettopframe()
-            assert curtopframe is not None
-            curtopframe.f_forward = frame
-        else:
-            self.some_frame = frame
-
-    def _unchain(self, frame):
-        #assert frame is self.gettopframe() --- slowish
-        if self.some_frame is frame:
-            self.some_frame = frame.f_back_some
-        else:
-            f_back = frame.f_back()
-            if f_back is not None:
-                f_back.f_forward = None
-
-        self.framestackdepth -= 1
-
-    @staticmethod
-    def _jit_rechain_frame(ec, frame):
-        # this method is called after the jit has seen enter (and thus _chain)
-        # of a frame, but then does not actually inline it. This method thus
-        # needs to make sure that the state is as if the _chain method had been
-        # executed outside of the jit. Note that this makes it important that
-        # _unchain does not call we_are_jitted
-        frame.f_back().f_forward = None
-        ec.some_frame = frame
-
-    @staticmethod
-    @jit.unroll_safe
-    def _extract_back_from_frame(frame):
-        back_some = frame.f_back_some
-        if frame.f_back_forced:
-            # don't check back_some.f_forward in this case
-            return back_some
-        if back_some is None:
-            return None
-        while True:
-            f_forward = back_some.f_forward
-            if f_forward is frame or f_forward is None:
-                return back_some
-            back_some = f_forward
-
-    @staticmethod
-    def _force_back_of_frame(frame):
-        orig_frame = frame
-        while frame is not None and not frame.f_back_forced:
-            frame.f_back_some = f_back = ExecutionContext._extract_back_from_frame(frame)
-            frame.f_back_forced = True
-            # now that we force the whole chain, we also have to set the
-            # forward links to None
-            frame.f_forward = None
-            frame = f_back
-        return orig_frame.f_back_some
-
-    _we_are_jitted = staticmethod(we_are_jitted) # indirection for testing
-    
-    # the methods above are used for chaining frames in JIT-friendly way
-    # ________________________________________________________________
 
 
     class Subcontext(object):
@@ -204,7 +83,7 @@
             self.is_tracing = 0
 
         def enter(self, ec):
-            ec.some_frame = self.topframe
+            ec.topframeref = jit.non_virtual_ref(self.topframe)
             ec.framestackdepth = self.framestackdepth
             ec.w_tracefunc = self.w_tracefunc
             ec.profilefunc = self.profilefunc
@@ -247,29 +126,11 @@
             while index > 0:
                 index -= 1
                 lst[index] = f
-                f = f.f_back()
+                f = f.f_backref()
             assert f is None
             return lst
         # coroutine: I think this is all, folks!
 
-
-    def get_builtin(self):
-        frame = self.gettopframe_nohidden()
-        if frame is not None:
-            return frame.builtin
-        else:
-            return self.space.builtin
-
-    # XXX this one should probably be dropped in favor of a module
-    def make_standard_w_globals(self):
-        "Create a new empty 'globals' dictionary."
-        w_key = self.space.wrap("__builtins__")
-        w_value = self.space.wrap(self.get_builtin())
-        w_globals = self.space.newdict()
-        space.setitem(w_globals, w_key, w_value)
-        return w_globals
-
-    @jit.dont_look_inside
     def c_call_trace(self, frame, w_func):
         "Profile the call of a builtin function"
         if self.profilefunc is None:
@@ -277,7 +138,6 @@
         else:
             self._trace(frame, 'c_call', w_func)
 
-    @jit.dont_look_inside
     def c_return_trace(self, frame, w_retval):
         "Profile the return from a builtin function"
         if self.profilefunc is None:
@@ -285,7 +145,6 @@
         else:
             self._trace(frame, 'c_return', w_retval)
 
-    @jit.dont_look_inside
     def c_exception_trace(self, frame, w_exc):
         "Profile function called upon OperationError."
         if self.profilefunc is None:
@@ -293,7 +152,6 @@
         else:
             self._trace(frame, 'c_exception', w_exc)
 
-    @jit.dont_look_inside
     def call_trace(self, frame):
         "Trace the call of a function"
         if self.w_tracefunc is not None or self.profilefunc is not None:
@@ -301,7 +159,6 @@
             if self.profilefunc:
                 frame.is_being_profiled = True
 
-    @jit.dont_look_inside
     def return_trace(self, frame, w_retval):
         "Trace the return from a function"
         if self.w_tracefunc is not None:
@@ -320,7 +177,14 @@
             actionflag.action_dispatcher(self, frame)     # slow path
     bytecode_trace._always_inline_ = True
 
-    @jit.dont_look_inside
+    def bytecode_trace_after_exception(self, frame):
+        "Like bytecode_trace(), but without increasing the ticker."
+        actionflag = self.space.actionflag
+        ticker = actionflag.get()
+        if ticker & actionflag.interesting_bits:  # fast check
+            actionflag.action_dispatcher(self, frame)     # slow path
+    bytecode_trace_after_exception._always_inline_ = True
+
     def exception_trace(self, frame, operationerr):
         "Trace function called upon OperationError."
         operationerr.record_interpreter_traceback()
@@ -343,6 +207,7 @@
         if self.space.is_w(w_func, self.space.w_None):
             self.w_tracefunc = None
         else:
+            self.force_all_frames()
             self.w_tracefunc = w_func
             self.space.frame_trace_action.fire()
 
@@ -355,16 +220,28 @@
             self.setllprofile(app_profile_call, w_func)
 
     def setllprofile(self, func, w_arg):
-        self.profilefunc = func
         if func is not None:
             if w_arg is None:
                 raise ValueError("Cannot call setllprofile with real None")
-            frame = self.gettopframe_nohidden()
-            while frame:
-                frame.is_being_profiled = True
-                frame = self.getnextframe_nohidden(frame)
+            self.force_all_frames(is_being_profiled=True)
+        self.profilefunc = func
         self.w_profilefuncarg = w_arg
 
+    def force_all_frames(self, is_being_profiled=False):
+        # "Force" all frames in the sense of the jit, and optionally
+        # set the flag 'is_being_profiled' on them.  A forced frame is
+        # one out of which the jit will exit: if it is running so far,
+        # in a piece of assembler currently running a CALL_MAY_FORCE,
+        # then being forced means that it will fail the following
+        # GUARD_NOT_FORCED operation, and so fall back to interpreted
+        # execution.  (We get this effect simply by reading the f_back
+        # field of all frames, during the loop below.)
+        frame = self.gettopframe_nohidden()
+        while frame:
+            if is_being_profiled:
+                frame.is_being_profiled = True
+            frame = self.getnextframe_nohidden(frame)
+
     def call_tracing(self, w_func, w_args):
         is_tracing = self.is_tracing
         self.is_tracing = 0
@@ -415,9 +292,8 @@
                              'c_return', 'c_exception']:
                 return
 
-            last_exception = None
+            last_exception = frame.last_exception
             if event == 'leaveframe':
-                last_exception = frame.last_exception
                 event = 'return'
 
             assert self.is_tracing == 0 
@@ -573,7 +449,7 @@
 
     def fire_after_thread_switch(self):
         """Bit of a hack: fire() the action but only the next time the GIL
-        is released and re-acquired (i.e. after a portential thread switch).
+        is released and re-acquired (i.e. after a potential thread switch).
         Don't call this if threads are not enabled.
         """
         from pypy.module.thread.gil import spacestate

Modified: pypy/branch/stringbuilder2/pypy/interpreter/function.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/interpreter/function.py	(original)
+++ pypy/branch/stringbuilder2/pypy/interpreter/function.py	Mon Jan 25 15:25:48 2010
@@ -553,6 +553,7 @@
         
 class StaticMethod(Wrappable):
     """The staticmethod objects."""
+    _immutable_ = True
 
     def __init__(self, w_function):
         self.w_function = w_function
@@ -566,6 +567,7 @@
 
 class ClassMethod(Wrappable):
     """The classmethod objects."""
+    _immutable_ = True
 
     def __init__(self, w_function):
         self.w_function = w_function

Modified: pypy/branch/stringbuilder2/pypy/interpreter/gateway.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/interpreter/gateway.py	(original)
+++ pypy/branch/stringbuilder2/pypy/interpreter/gateway.py	Mon Jan 25 15:25:48 2010
@@ -386,6 +386,15 @@
     else:
         return typ.__name__ + '_w'
 
+
+def unwrap_spec(*spec):
+    """A decorator which attaches the unwrap_spec attribute."""
+    def decorator(func):
+        func.unwrap_spec = spec
+        return func
+    return decorator
+
+
 class BuiltinCode(eval.Code):
     "The code object implementing a built-in (interpreter-level) hook."
     _immutable_ = True
@@ -1075,6 +1084,11 @@
     """ 
     if not isinstance(source, str): 
         source = str(py.code.Source(source).strip())
+        while source.startswith('@py.test.mark.'):
+            # these decorators are known to return the same function
+            # object, we may ignore them
+            assert '\n' in source
+            source = source[source.find('\n') + 1:]
         assert source.startswith("def "), "can only transform functions" 
         source = source[4:]
     p = source.find('(')

Modified: pypy/branch/stringbuilder2/pypy/interpreter/generator.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/interpreter/generator.py	(original)
+++ pypy/branch/stringbuilder2/pypy/interpreter/generator.py	Mon Jan 25 15:25:48 2010
@@ -2,6 +2,7 @@
 from pypy.interpreter.baseobjspace import Wrappable
 from pypy.interpreter.gateway import NoneNotWrapped
 from pypy.rlib.rarithmetic import intmask
+from pypy.rlib import jit
 from pypy.interpreter.pyopcode import LoopBlock
 
 
@@ -64,7 +65,7 @@
             else:
                 return w_result     # YIELDed
         finally:
-            self.frame.f_back_some = None
+            self.frame.f_backref = jit.vref_None
             self.running = False
 
     def descr_throw(self, w_type, w_val=None, w_tb=None):

Modified: pypy/branch/stringbuilder2/pypy/interpreter/mixedmodule.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/interpreter/mixedmodule.py	(original)
+++ pypy/branch/stringbuilder2/pypy/interpreter/mixedmodule.py	Mon Jan 25 15:25:48 2010
@@ -13,7 +13,8 @@
 
     applevel_name = None
     expose__file__attribute = True
-    
+    w_initialdict = None
+
     def __init__(self, space, w_name): 
         """ NOT_RPYTHON """ 
         Module.__init__(self, space, w_name) 
@@ -21,6 +22,13 @@
         self.__class__.buildloaders()
         self.loaders = self.loaders.copy()    # copy from the class to the inst
 
+    def init(self, space):
+        """This is called each time the module is imported or reloaded
+        """
+        if self.w_initialdict is not None:
+            space.call_method(self.w_dict, 'update', self.w_initialdict)
+        Module.init(self, space)
+
     def get_applevel_name(cls):
         """ NOT_RPYTHON """
         if cls.applevel_name is not None:
@@ -82,11 +90,13 @@
             for name in self.loaders: 
                 w_value = self.get(name)  
                 space.setitem(self.w_dict, space.new_interned_str(name), w_value) 
-            self.lazy = False 
+            self.lazy = False
+            self.w_initialdict = space.call_method(self.w_dict, 'items')
         return self.w_dict 
 
     def _freeze_(self):
         self.getdict()
+        self.startup_called = False
         # hint for the annotator: Modules can hold state, so they are
         # not constant
         return False

Modified: pypy/branch/stringbuilder2/pypy/interpreter/module.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/interpreter/module.py	(original)
+++ pypy/branch/stringbuilder2/pypy/interpreter/module.py	Mon Jan 25 15:25:48 2010
@@ -15,22 +15,30 @@
         self.w_dict = w_dict 
         self.w_name = w_name 
         if w_name is not None:
-            space.setitem(w_dict, space.new_interned_str('__name__'), w_name) 
+            space.setitem(w_dict, space.new_interned_str('__name__'), w_name)
+        self.startup_called = False
 
     def setup_after_space_initialization(self):
         """NOT_RPYTHON: to allow built-in modules to do some more setup
         after the space is fully initialized."""
 
+    def init(self, space):
+        """This is called each time the module is imported or reloaded
+        """
+        if not self.startup_called:
+            self.startup_called = True
+            self.startup(space)
+
     def startup(self, space):
-        """This is called at runtime before the space gets uses to allow
-        the module to do initialization at runtime.
+        """This is called at runtime on import to allow the module to
+        do initialization when it is imported for the first time.
         """
 
     def shutdown(self, space):
         """This is called when the space is shut down, just after
-        sys.exitfunc().
+        sys.exitfunc(), if the module has been imported.
         """
-        
+
     def getdict(self):
         return self.w_dict
 

Modified: pypy/branch/stringbuilder2/pypy/interpreter/pycode.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/interpreter/pycode.py	(original)
+++ pypy/branch/stringbuilder2/pypy/interpreter/pycode.py	Mon Jan 25 15:25:48 2010
@@ -27,7 +27,7 @@
 
 
 # Magic numbers for the bytecode version in code objects.
-# See comments in pypy/module/__builtin__/importing.
+# See comments in pypy/module/imp/importing.
 cpython_magic, = struct.unpack("<i", imp.get_magic())   # host magic number
 default_magic = (62131+2) | 0x0a0d0000                  # this PyPy's magic
                                                         # (62131=CPython 2.5.1)
@@ -117,9 +117,6 @@
 
         self._compute_flatcall()
 
-        if self.space.config.objspace.std.withcelldict:
-            from pypy.objspace.std.celldict import init_code
-            init_code(self)
 
     def _init_flags(self):
         co_code = self.co_code

Modified: pypy/branch/stringbuilder2/pypy/interpreter/pyframe.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/interpreter/pyframe.py	(original)
+++ pypy/branch/stringbuilder2/pypy/interpreter/pyframe.py	Mon Jan 25 15:25:48 2010
@@ -9,7 +9,7 @@
 from pypy.interpreter import pytraceback
 import opcode
 from pypy.rlib.objectmodel import we_are_translated, instantiate
-from pypy.rlib.jit import we_are_jitted, hint
+from pypy.rlib.jit import hint
 from pypy.rlib.debug import make_sure_not_resized
 from pypy.rlib import jit
 
@@ -34,13 +34,13 @@
      * 'builtin' is the attached built-in module
      * 'valuestack_w', 'blockstack', control the interpretation
     """
-    
 
     __metaclass__ = extendabletype
 
     frame_finished_execution = False
     last_instr               = -1
     last_exception           = None
+    f_backref                = jit.vref_None
     w_f_trace                = None
     # For tracing
     instr_lb                 = 0
@@ -64,7 +64,6 @@
         self.fastlocals_w = [None]*self.numlocals
         make_sure_not_resized(self.fastlocals_w)
         self.f_lineno = code.co_firstlineno
-        ExecutionContext._init_chaining_attributes(self)
 
     def append_block(self, block):
         block.previous = self.lastblock
@@ -142,8 +141,7 @@
         executioncontext = self.space.getexecutioncontext()
         executioncontext.enter(self)
         try:
-            if not we_are_jitted():
-                executioncontext.call_trace(self)
+            executioncontext.call_trace(self)
             # Execution starts just after the last_instr.  Initially,
             # last_instr is -1.  After a generator suspends it points to
             # the YIELD_VALUE instruction.
@@ -154,11 +152,12 @@
                 rstack.resume_point("execute_frame", self, executioncontext,
                                     returns=w_exitvalue)
             except Exception:
-                if not we_are_jitted():
-                    executioncontext.return_trace(self, self.space.w_None)
+                executioncontext.return_trace(self, self.space.w_None)
                 raise
-            if not we_are_jitted():
-                executioncontext.return_trace(self, w_exitvalue)
+            executioncontext.return_trace(self, w_exitvalue)
+            # clean up the exception, might be useful for not
+            # allocating exception objects in some cases
+            self.last_exception = None
         finally:
             executioncontext.leave(self)
         return w_exitvalue
@@ -308,7 +307,7 @@
             w_tb = w(self.last_exception.application_traceback)
         
         tup_state = [
-            w(self.f_back()),
+            w(self.f_backref()),
             w(self.get_builtin()),
             w(self.pycode),
             w_valuestack,
@@ -360,8 +359,8 @@
         # do not use the instance's __init__ but the base's, because we set
         # everything like cells from here
         PyFrame.__init__(self, space, pycode, w_globals, closure)
-        new_frame.f_back_some = space.interp_w(PyFrame, w_f_back, can_be_None=True)
-        new_frame.f_back_forced = True
+        f_back = space.interp_w(PyFrame, w_f_back, can_be_None=True)
+        new_frame.f_backref = jit.non_virtual_ref(f_back)
 
         new_frame.builtin = space.interp_w(Module, w_builtin)
         new_frame.set_blocklist([unpickle_block(space, w_blk)
@@ -392,6 +391,7 @@
         new_frame.instr_prev = space.int_w(w_instr_prev)
 
         self._setcellvars(cellvars)
+        # XXX what if the frame is in another thread??
         space.frame_trace_action.fire()
 
     def hide(self):
@@ -431,12 +431,6 @@
     def _setcellvars(self, cellvars):
         pass
 
-    def f_back(self):
-        return ExecutionContext._extract_back_from_frame(self)
-
-    def force_f_back(self):
-        return ExecutionContext._force_back_of_frame(self)
-
     ### line numbers ###
 
     # for f*_f_* unwrapping through unwrap_spec in typedef.py
@@ -584,7 +578,7 @@
         return self.get_builtin().getdict()
 
     def fget_f_back(space, self):
-        return self.space.wrap(self.f_back())
+        return self.space.wrap(self.f_backref())
 
     def fget_f_lasti(space, self):
         return self.space.wrap(self.last_instr)
@@ -605,27 +599,27 @@
 
     def fget_f_exc_type(space, self):
         if self.last_exception is not None:
-            f = self.f_back()
+            f = self.f_backref()
             while f is not None and f.last_exception is None:
-                f = f.f_back()
+                f = f.f_backref()
             if f is not None:
                 return f.last_exception.w_type
         return space.w_None
          
     def fget_f_exc_value(space, self):
         if self.last_exception is not None:
-            f = self.f_back()
+            f = self.f_backref()
             while f is not None and f.last_exception is None:
-                f = f.f_back()
+                f = f.f_backref()
             if f is not None:
                 return f.last_exception.w_value
         return space.w_None
 
     def fget_f_exc_traceback(space, self):
         if self.last_exception is not None:
-            f = self.f_back()
+            f = self.f_backref()
             while f is not None and f.last_exception is None:
-                f = f.f_back()
+                f = f.f_backref()
             if f is not None:
                 return space.wrap(f.last_exception.application_traceback)
         return space.w_None

Modified: pypy/branch/stringbuilder2/pypy/interpreter/pyopcode.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/interpreter/pyopcode.py	(original)
+++ pypy/branch/stringbuilder2/pypy/interpreter/pyopcode.py	Mon Jan 25 15:25:48 2010
@@ -130,7 +130,7 @@
 
     def handle_operation_error(self, ec, operr, attach_tb=True):
         if attach_tb:
-            if not jit.we_are_jitted():
+            if 1:
                 # xxx this is a hack.  It allows bytecode_trace() to
                 # call a signal handler which raises, and catch the
                 # raised exception immediately.  See test_alarm_raise in
@@ -146,15 +146,14 @@
                     trace = self.w_f_trace
                     self.w_f_trace = None
                     try:
-                        ec.bytecode_trace(self)
+                        ec.bytecode_trace_after_exception(self)
                     finally:
                         self.w_f_trace = trace
                 except OperationError, e:
                     operr = e
             pytraceback.record_application_traceback(
                 self.space, operr, self, self.last_instr)
-            if not jit.we_are_jitted():
-                ec.exception_trace(self, operr)
+            ec.exception_trace(self, operr)
 
         block = self.unrollstack(SApplicationException.kind)
         if block is None:
@@ -913,13 +912,10 @@
         arguments = f.popvalues(n_arguments)
         args = f.argument_factory(arguments, keywords, keywords_w, w_star, w_starstar)
         w_function  = f.popvalue()
-        if jit.we_are_jitted():
-            w_result = f.space.call_args(w_function, args)
+        if f.is_being_profiled and is_builtin_code(w_function):
+            w_result = f.space.call_args_and_c_profile(f, w_function, args)
         else:
-            if f.is_being_profiled and is_builtin_code(w_function):
-                w_result = f.space.call_args_and_c_profile(f, w_function, args)
-            else:
-                w_result = f.space.call_args(w_function, args)
+            w_result = f.space.call_args(w_function, args)
         rstack.resume_point("call_function", f, returns=w_result)
         f.pushvalue(w_result)
         

Modified: pypy/branch/stringbuilder2/pypy/interpreter/pytraceback.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/interpreter/pytraceback.py	(original)
+++ pypy/branch/stringbuilder2/pypy/interpreter/pytraceback.py	Mon Jan 25 15:25:48 2010
@@ -49,7 +49,6 @@
         self.next = space.interp_w(PyTraceback, w_next, can_be_None=True)
 
 def record_application_traceback(space, operror, frame, last_instruction):
-    frame.force_f_back()
     if frame.pycode.hidden_applevel:
         return
     tb = operror.application_traceback

Modified: pypy/branch/stringbuilder2/pypy/interpreter/test/test_executioncontext.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/interpreter/test/test_executioncontext.py	(original)
+++ pypy/branch/stringbuilder2/pypy/interpreter/test/test_executioncontext.py	Mon Jan 25 15:25:48 2010
@@ -1,6 +1,5 @@
 import py
 from pypy.interpreter import executioncontext
-from pypy.interpreter.executioncontext import ExecutionContext
 from pypy.conftest import gettestobjspace
 
 class Finished(Exception):
@@ -221,541 +220,25 @@
         events = space.unwrap(w_events)
         assert [i[0] for i in events] == ['c_call', 'c_return', 'c_call']
 
-
-
-class TestFrameChaining(object):
-    class EC(ExecutionContext):
-        _some_frame = None
-        def __init__(self, jitted=False):
-            self.jitted = jitted
-            self.virtualizable = None
-            self.framestackdepth = 0
-            self._init_frame_chain()
-
-        def _we_are_jitted(self):
-            return self.jitted
-
-        def _get_some_frame(self):
-            if self._some_frame:
-                self._some_frame.look_at()
-            return self._some_frame
-        def _set_some_frame(self, frame):
-            if frame is not None:
-                frame.force()
-            self._some_frame = frame
-        some_frame = property(_get_some_frame, _set_some_frame)
-
-    class Frame(object):
-        _f_back_some = None
-        _f_forward = None
-
-        def __init__(self, ec, virtual_with_base_frame=None):
-            self.ec = ec
-            self.virtual_with_base_frame = virtual_with_base_frame
-            self.escaped = not virtual_with_base_frame
-            ExecutionContext._init_chaining_attributes(self)
-
-        def f_back(self):
-            return ExecutionContext._extract_back_from_frame(self)
-
-        def force_f_back(self):
-            return ExecutionContext._force_back_of_frame(self)
-
-        def force(self):
-            if not self.escaped:
-                self.virtual_with_base_frame = None
-                self.escaped = True
-                if self._f_back_some:
-                    self._f_back_some.force()
-                if self._f_forward:
-                    self._f_back_some.force()
-
-        def look_at(self):
-            if (not self.ec.jitted or
-                self.ec.virtualizable is not self.virtual_with_base_frame):
-                self.force()
-
-        def store_ref_to(self, other):
-            if (other.virtual_with_base_frame is not self and
-                other.virtual_with_base_frame is not self.virtual_with_base_frame):
-                other.force()
-
-        def _get_f_back_some(self):
-            self.look_at()
-            return self._f_back_some
-        def _set_f_back_some(self, frame):
-            self.look_at()
-            if frame:
-                frame.look_at()
-                self.store_ref_to(frame)
-            self._f_back_some = frame
-        f_back_some = property(_get_f_back_some, _set_f_back_some)
-        
-        def _get_f_forward(self):
-            self.look_at()
-            return self._f_forward
-        def _set_f_forward(self, frame):
-            self.look_at()
-            if frame:
-                frame.look_at()
-                self.store_ref_to(frame)
-            self._f_forward = frame
-        f_forward = property(_get_f_forward, _set_f_forward)
-
-    def test_f_back_no_jit(self):
-        ec = self.EC()
-        frame = self.Frame(ec)
-        frame2 = self.Frame(ec)
-        frame2.f_back_some = frame
-
-        frame3 = self.Frame(ec)
-        frame3.f_back_some = frame2
-
-        assert frame3.f_back() is frame2
-        assert frame2.f_back() is frame
-        assert frame.f_back() is None
-
-    def test_f_back_jit(self):
-        ec = self.EC()
-        frame = self.Frame(ec) # real frame
-        frame2 = self.Frame(ec) # virtual frame
-        frame2.f_back_some = frame
-        frame.f_forward = frame2
-
-        frame3 = self.Frame(ec) # virtual frame
-        frame3.f_back_some = frame
-        frame2.f_forward = frame3
-
-        assert frame3.f_back() is frame2
-        assert frame2.f_back() is frame
-        assert frame.f_back() is None
-
-        frame4 = self.Frame(ec) # real frame again
-        frame4.f_back_some = frame
-        assert frame4.f_back() is frame3
-
-    def test_gettopframe_no_jit(self):
-        ec = self.EC()
-        frame = self.Frame(ec)
-        ec.some_frame = frame
-        assert ec.gettopframe() is frame
-
-    def test_gettopframe_jit(self):
-        ec = self.EC()
-        frame = self.Frame(ec) # real frame
-        ec.some_frame = frame
-        assert ec.gettopframe() is frame
-
-        frame2 = self.Frame(ec) # virtual frame
-        frame2.f_back_some = frame
-        frame.f_forward = frame2
-        assert ec.gettopframe() is frame2
-
-        frame3 = self.Frame(ec) # virtual frame
-        frame3.f_back_some = frame
-        frame2.f_forward = frame3
-        assert ec.gettopframe() is frame3
-
-        frame4 = self.Frame(ec) # real frame again
-        frame4.f_back_some = frame
-        ec.some_frame = frame4
-        assert ec.gettopframe() is frame4
-
-    def test_frame_chain(self):
-
-        ec = self.EC()
-
-        assert ec.some_frame is None
-        assert ec.framestackdepth == 0
-
-        frame = self.Frame(ec)
-        ec._chain(frame)
-        assert ec.some_frame is frame
-        assert ec.framestackdepth == 1
-        assert frame.f_back_some is None
-        assert frame.f_forward is None
-        assert ec.gettopframe() is frame
-        assert ec._extract_back_from_frame(frame) is None
-        
-        frame2 = self.Frame(ec)
-        ec._chain(frame2)
-        assert ec.some_frame is frame2
-        assert ec.framestackdepth == 2
-        assert frame2.f_back_some is frame
-        assert frame.f_forward is None
-        assert frame2.f_forward is None
-        assert ec.gettopframe() is frame2
-        assert ec._extract_back_from_frame(frame2) is frame
-        assert ec._extract_back_from_frame(frame) is None
-       
-        frame3 = self.Frame(ec)
-        ec._chain(frame3)
-        assert ec.some_frame is frame3
-        assert frame3.f_back_some is frame2
-        assert frame2.f_forward is None
-        assert ec.gettopframe() is frame3
-        assert ec._extract_back_from_frame(frame3) is frame2
-        assert ec._extract_back_from_frame(frame2) is frame
-        assert ec._extract_back_from_frame(frame) is None
-
-        assert frame3.f_back() is frame2
-        ec._unchain(frame3)
-        assert ec.some_frame is frame2
-        assert ec.framestackdepth == 2
-        assert frame2.f_forward is None
-        assert frame3.f_back_some is frame2
-        assert ec.gettopframe() is frame2
-        assert ec._extract_back_from_frame(frame2) is frame
-        assert ec._extract_back_from_frame(frame) is None
-        
-        assert frame2.f_back() is frame
-        ec._unchain(frame2)
-        assert ec.some_frame is frame
-        assert ec.framestackdepth == 1
-        assert frame.f_forward is None
-        assert frame2.f_back_some is frame
-        assert ec.gettopframe() is frame
-        assert ec._extract_back_from_frame(frame) is None
-
-        assert frame.f_back() is None
-        ec._unchain(frame)
-        assert ec.some_frame is None
-        assert ec.framestackdepth == 0
-        assert frame.f_back_some is None
-        assert ec.gettopframe() is None
-
-    def test_frame_chain_forced(self):
-
-        ec = self.EC()
-
-        frame = self.Frame(ec)
-        ec._chain(frame)
-        assert ec.gettopframe() is frame
-        assert ec._extract_back_from_frame(frame) is None
-        
-        frame2 = self.Frame(ec)
-        ec._chain(frame2)
-        assert ec.some_frame is frame2
-        assert ec.framestackdepth == 2
-        assert frame2.f_back_some is frame
-        assert frame.f_forward is None
-        assert frame2.f_forward is None
-        res = frame2.force_f_back()
-        assert res is frame
-        assert frame.f_back_forced
-        assert ec.gettopframe() is frame2
-        assert ec._extract_back_from_frame(frame2) is frame
-        assert ec._extract_back_from_frame(frame) is None
-        
-        frame3 = self.Frame(ec)
-        ec._chain(frame3)
-        assert ec.gettopframe() is frame3
-        assert ec._extract_back_from_frame(frame3) is frame2
-        assert ec._extract_back_from_frame(frame2) is frame
-        assert ec._extract_back_from_frame(frame) is None
-
-        assert frame3.f_back() is frame2
-        ec._unchain(frame3)
-        assert ec.some_frame is frame2
-        assert frame3.f_back_some is frame2
-        assert ec.gettopframe() is frame2
-        assert ec._extract_back_from_frame(frame2) is frame
-        assert ec._extract_back_from_frame(frame) is None
-        
-        assert frame2.f_back() is frame
-        ec._unchain(frame2)
-        assert frame2.f_back_some is frame
-        assert ec.gettopframe() is frame
-        assert ec._extract_back_from_frame(frame) is None
-
-        assert frame.f_back() is None
-        ec._unchain(frame)
-        assert ec.some_frame is None
-        assert frame.f_back_some is None
-
-        assert frame2.f_back() is frame
-        assert frame.f_back() is None
-        assert ec.gettopframe() is None
-
-
-    def test_frame_chain_jitted(self):
-
-        ec = self.EC()
-
-        assert ec.some_frame is None
-        assert ec.framestackdepth == 0
-        assert ec.gettopframe() is None
-
-        frame = self.Frame(ec)
-        ec._chain(frame)
-        assert ec.some_frame is frame
-        assert ec.framestackdepth == 1
-        assert frame.f_back_some is None
-        assert frame.f_forward is None
-        assert ec.gettopframe() is frame
-        assert ec._extract_back_from_frame(frame) is None
-        
-        ec.jitted = True
-        ec.virtualizable = frame
-        frame2 = self.Frame(ec, frame)
-        ec._chain(frame2)
-        assert ec.some_frame is frame
-        assert ec.framestackdepth == 2
-        assert frame2.f_back_some is frame
-        assert frame.f_forward is frame2
-        assert frame2.f_forward is None
-        assert ec.gettopframe() is frame2
-        assert ec._extract_back_from_frame(frame2) is frame
-        assert ec._extract_back_from_frame(frame) is None
-
-        # recursive enter/leave seen by the jit
-        frame3 = self.Frame(ec, frame)
-        ec._chain(frame3)
-        assert ec.some_frame is frame
-        assert frame3.f_back_some is frame
-        assert frame2.f_forward is frame3
-        assert ec.gettopframe() is frame3
-        assert ec._extract_back_from_frame(frame3) is frame2
-        assert ec._extract_back_from_frame(frame2) is frame
-        assert ec._extract_back_from_frame(frame) is None
-
-        assert frame3.f_back() is frame2
-        ec._unchain(frame3)
-        assert ec.some_frame is frame
-        assert ec.framestackdepth == 2
-        assert frame2.f_forward is None
-        assert frame3.f_back_some is frame
-        assert not frame3.escaped
-        assert not frame2.escaped
-        assert ec.gettopframe() is frame2
-        assert ec._extract_back_from_frame(frame2) is frame
-        assert ec._extract_back_from_frame(frame) is None
-       
-        # recursive enter/leave not seen by the jit
-        ec.jitted = False
-        ec.virtualizable = None
-        ec._chain(frame3)
-        assert not frame2.escaped
-        assert ec.some_frame is frame3
-        assert frame3.f_back_some is frame
-        assert frame2.f_forward is None
-        assert frame3.escaped
-        assert ec.gettopframe() is frame3
-        assert ec._extract_back_from_frame(frame3) is frame2
-        assert ec._extract_back_from_frame(frame2) is frame
-        assert ec._extract_back_from_frame(frame) is None
-
-        assert frame3.f_back() is frame2
-        ec._unchain(frame3)
-        assert ec.some_frame is frame
-        assert ec.framestackdepth == 2
-        assert frame2.f_forward is None
-        assert frame3.f_back_some is frame
-        assert ec.gettopframe() is frame2
-        assert ec._extract_back_from_frame(frame2) is frame
-        assert ec._extract_back_from_frame(frame) is None
-
-        ec.jitted = True
-        ec.virtualizable = frame
-
-        assert frame2.f_back() is frame
-        ec._unchain(frame2)
-        assert ec.some_frame is frame
-        assert ec.framestackdepth == 1
-        assert frame.f_forward is None
-        assert frame2.f_back_some is frame
-        assert ec.gettopframe() is frame
-        assert ec._extract_back_from_frame(frame) is None
-
-        ec.jitted = False
-        assert frame.f_back() is None
-        ec._unchain(frame)
-        assert ec.some_frame is None
-        assert ec.framestackdepth == 0
-        assert frame.f_back_some is None
-        assert ec.gettopframe() is None
-
-    @py.test.mark.xfail
-    def test_frame_chain_jitted_forced(self):
-
-        ec = self.EC()
-
-        assert ec.some_frame is None
-        assert ec.framestackdepth == 0
-
-        frame = self.Frame(ec)
-        ec._chain(frame)
+    def test_profile_and_exception(self):
+        space = self.space
+        w_res = space.appexec([], """():
+        l = []
         
-        ec.jitted = True
-        frame2 = self.Frame(ec)
-        ec._chain(frame2)
-
-        # recursive enter/leave seen by the jit
-        frame3 = self.Frame(ec)
-        ec._chain(frame3)
-        assert ec.gettopframe() is frame3
-        res = frame3.force_f_back()
-        assert res is frame2
-        assert ec.gettopframe() is frame3
-
-        assert frame3.f_back() is frame2
-        ec._unchain(frame3)
-      
-        assert frame2.f_back() is frame
-        ec._unchain(frame2)
-        ec.jitted = False
-        assert frame.f_back() is None
-        ec._unchain(frame)
-
-        assert frame3.f_back() is frame2
-        assert frame2.f_back() is frame
-        assert frame.f_back() is None
+        def profile(*args):
+            l.append(sys.exc_info()[0])
 
-    def enter_two_jitted_levels(self):
-        ec = self.EC()
-
-        assert ec.some_frame is None
-        assert ec.framestackdepth == 0
+        import sys
+        try:
+            sys.setprofile(profile)
+            try:
+                x
+            except:
+                expected = sys.exc_info()[0]
+                assert expected is NameError
+                for i in l:
+                    assert expected is l[0]
+        finally:
+            sys.setprofile(None)
+        """)
 
-        frame = self.Frame(ec)
-        ec._chain(frame)
-        
-        ec.jitted = True
-        ec.virtualizable = frame
-        frame2 = self.Frame(ec, frame)
-        ec._chain(frame2)
-        assert not frame2.escaped
-        return ec, frame, frame2
-
-    def leave_two_jitted_levels(self, ec, frame, frame2):
-        assert frame2.f_back() is frame
-        ec._unchain(frame2)
-        ec.jitted = False
-        assert frame.f_back() is None
-        ec._unchain(frame)
-
-    
-    def test_check_escaping_all_inlined(self):
-        ec, frame, frame2 = self.enter_two_jitted_levels()
-
-        # recursive enter/leave seen by the jit
-        frame3 = self.Frame(ec, frame)
-        ec._chain(frame3)
-        assert not frame2.escaped
-        assert not frame3.escaped
-
-        assert frame3.f_back() is frame2
-        ec._unchain(frame3)
-        assert not frame2.escaped
-        self.leave_two_jitted_levels(ec, frame, frame2)
-      
-
-    def test_check_escaping_not_all_inlined_enter_leave_not_seen(self):
-        ec, frame, frame2 = self.enter_two_jitted_levels()
-
-        ec.jitted = False
-        # recursive enter/leave not seen by the jit
-        frame3 = self.Frame(ec)
-        ec._chain(frame3)
-
-        assert not frame2.escaped
-        assert frame3.escaped
-
-        ec._unchain(frame3)
-        ec.jitted = True
-        assert not frame2.escaped
-      
-        self.leave_two_jitted_levels(ec, frame, frame2)
-
-    def test_check_escaping_not_all_inlined_enter_leave_seen(self):
-        ec, frame, frame2 = self.enter_two_jitted_levels()
-
-        # recursive enter/leave seen by the jit
-        frame3 = self.Frame(ec, frame)
-        ec._chain(frame3)
-        ExecutionContext._jit_rechain_frame(ec, frame3)
-        ec.jitted = False
-        frame3.look_at()
-        assert not frame2.escaped
-        assert frame3.escaped
-
-        ec.jitted = True
-        assert frame3.f_back() is frame2
-        ec._unchain(frame3)
-        assert not frame2.escaped
-      
-        self.leave_two_jitted_levels(ec, frame, frame2)
-
-
-    def test_check_escaping_multi_non_jitted_levels(self):
-        ec, frame, frame2 = self.enter_two_jitted_levels()
-
-        # recursive enter/leave seen by the jit
-        frame3 = self.Frame(ec, frame)
-        ec._chain(frame3)
-        ExecutionContext._jit_rechain_frame(ec, frame3)
-        ec.jitted = False
-
-        assert frame3.escaped
-        assert not frame2.escaped
-        assert frame3.escaped
-
-        frame4 = self.Frame(ec)
-        ec._chain(frame4)
-        assert ec.framestackdepth == 4
-
-        ec._unchain(frame4)
-        assert frame3.escaped
-        assert not frame2.escaped
-
-        ec.jitted = True
-        assert frame3.f_back() is frame2
-        ec._unchain(frame3)
-        assert not frame2.escaped
-      
-        self.leave_two_jitted_levels(ec, frame, frame2)
-
-    def test_check_escaping_jitted_with_two_different_virtualizables(self):
-        ec, frame, frame2 = self.enter_two_jitted_levels()
-
-        frame3 = self.Frame(ec, frame)
-        ec._chain(frame3)
-        # frame3 is not inlined, but contains a loop itself, for which code has
-        # been generated
-        ExecutionContext._jit_rechain_frame(ec, frame3)
-        ec.virtualizable = frame3
-
-        frame3.look_at()
-        assert not frame2.escaped
-        assert frame3.escaped
-
-        frame4 = self.Frame(ec, frame3)
-        ec._chain(frame4)
-        assert ec.framestackdepth == 4
-        assert not frame4.escaped
-
-        ec._unchain(frame4)
-        assert frame3.escaped
-        assert not frame2.escaped
-
-        ec.virtualizable = frame
-
-        ec._unchain(frame3)
-        assert not frame2.escaped
-
-    def test_frame_top_is_virtualizable(self):
-        ec, frame, frame2 = self.enter_two_jitted_levels()
-        frame3 = self.Frame(ec, frame2)
-        ec.jitted = False
-        ec._chain(frame3)
-        ec.gettopframe()
-        frame3.force_f_back()
-        ec._unchain(frame3)
-        assert not frame2.f_forward
-        assert ec.gettopframe() is frame2
-        ec.jitted = True
-        ec._unchain(frame2)
-        assert not frame.f_forward
-        assert ec.gettopframe() is frame
-        ec._unchain(frame)
-        assert ec.gettopframe() is None

Modified: pypy/branch/stringbuilder2/pypy/interpreter/test/test_gateway.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/interpreter/test/test_gateway.py	(original)
+++ pypy/branch/stringbuilder2/pypy/interpreter/test/test_gateway.py	Mon Jan 25 15:25:48 2010
@@ -520,6 +520,16 @@
         w_res = space.call_obj_args(w_g, w_self, args3)
         assert space.is_true(space.eq(w_res, space.wrap(('g', 'self', 3))))
 
+    def test_unwrap_spec_decorator(self):
+        space = self.space
+        @gateway.unwrap_spec(gateway.ObjSpace, gateway.W_Root, int)
+        def g(space, w_thing, i):
+            return space.newtuple([w_thing, space.wrap(i)])
+        w_g = space.wrap(gateway.interp2app_temp(g))
+        args = argument.Arguments(space, [space.wrap(-1), space.wrap(0)])
+        w_res = space.call_args(w_g, args)
+        assert space.eq_w(w_res, space.wrap((-1, 0)))
+
 
 class TestPassThroughArguments:
     

Modified: pypy/branch/stringbuilder2/pypy/interpreter/test/test_module.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/interpreter/test/test_module.py	(original)
+++ pypy/branch/stringbuilder2/pypy/interpreter/test/test_module.py	Mon Jan 25 15:25:48 2010
@@ -58,7 +58,7 @@
         r = repr(_pypy_interact)
         assert (r.startswith("<module '_pypy_interact' from ") and
                 ('pypy/lib/_pypy_interact.py' in r or
-                 'pypy\\lib\\_pypy_interact.py' in r.lower()) and
+                 r'pypy\\lib\\_pypy_interact.py' in r.lower()) and
                 r.endswith('>'))
         nofile = type(_pypy_interact)('nofile', 'foo')
         assert repr(nofile) == "<module 'nofile' from ?>"

Modified: pypy/branch/stringbuilder2/pypy/interpreter/test/test_zzpickle_and_slow.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/interpreter/test/test_zzpickle_and_slow.py	(original)
+++ pypy/branch/stringbuilder2/pypy/interpreter/test/test_zzpickle_and_slow.py	Mon Jan 25 15:25:48 2010
@@ -2,6 +2,7 @@
 from pypy import conftest
 from pypy.conftest import gettestobjspace
 from pypy.interpreter import gateway
+from pypy.rlib.jit import non_virtual_ref, vref_None
 
 class AppTestSlow:    
     def setup_class(cls):
@@ -30,21 +31,18 @@
     from pypy.interpreter import pytraceback
     def hide_top_frame(space, w_frame):
         w_last = None
-        while w_frame.f_back():
-            # should have been forced by traceback capturing
-            assert w_frame.f_back_forced
+        while w_frame.f_backref():
             w_last = w_frame
-            w_frame = w_frame.f_back()
+            w_frame = w_frame.f_backref()
         assert w_last
-        w_saved = w_last.f_back()
-        w_last.f_back_some = None
+        w_saved = w_last.f_backref()
+        w_last.f_backref = vref_None
         return w_saved
 
     def restore_top_frame(space, w_frame, w_saved):
-        while w_frame.f_back():
-            assert w_frame.f_back_forced
-            w_frame = w_frame.f_back()
-        w_frame.f_back_some = w_saved
+        while w_frame.f_backref():
+            w_frame = w_frame.f_backref()
+        w_frame.f_backref = non_virtual_ref(w_saved)
 
     def read_exc_type(space, w_frame):
         if w_frame.last_exception is None:

Modified: pypy/branch/stringbuilder2/pypy/jit/backend/llgraph/llimpl.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/backend/llgraph/llimpl.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/backend/llgraph/llimpl.py	Mon Jan 25 15:25:48 2010
@@ -125,6 +125,7 @@
     'getarrayitem_gc_pure' : (('ref', 'int'), 'intorptr'),
     'arraylen_gc'     : (('ref',), 'int'),
     'call'            : (('ref', 'varargs'), 'intorptr'),
+    'call_assembler'  : (('ref', 'varargs'), 'intorptr'),
     'call_pure'       : (('ref', 'varargs'), 'intorptr'),
     'cond_call_gc_wb' : (('int', 'int', 'ptr', 'varargs'), None),
     'oosend'          : (('varargs',), 'intorptr'),
@@ -152,7 +153,9 @@
     'debug_merge_point': (('ref',), None),
     'force_token'     : ((), 'int'),
     'call_may_force'  : (('int', 'varargs'), 'intorptr'),
-    'guard_not_forced': ((), None)
+    'guard_not_forced': ((), None),
+    'virtual_ref'     : (('ref', 'int'), 'ref'),
+    'virtual_ref_finish': (('ref', 'ref'), None),
     #'getitem'         : (('void', 'ref', 'int'), 'int'),
     #'setitem'         : (('void', 'ref', 'int', 'int'), None),
     #'newlist'         : (('void', 'varargs'), 'ref'),
@@ -314,6 +317,11 @@
     assert isinstance(type, str) and len(type) == 1
     op.descr = Descr(ofs, type)
 
+def compile_add_loop_token(loop, descr):
+    loop = _from_opaque(loop)
+    op = loop.operations[-1]
+    op.descr = descr
+
 def compile_add_var(loop, intvar):
     loop = _from_opaque(loop)
     op = loop.operations[-1]
@@ -389,8 +397,9 @@
 class Frame(object):
     OPHANDLERS = [None] * (rop._LAST+1)
 
-    def __init__(self, memocast):
+    def __init__(self, memocast, cpu):
         self.verbose = False
+        self.cpu = cpu
         self.memocast = memocast
         self.opindex = 1
         self._forced = False
@@ -807,12 +816,53 @@
         finally:
             self._may_force = -1
 
+    def op_call_assembler(self, loop_token, *args):
+        global _last_exception
+        assert not self._forced
+        self._may_force = self.opindex
+        try:
+            inpargs = _from_opaque(loop_token._llgraph_compiled_version).inputargs
+            for i, inparg in enumerate(inpargs):
+                TYPE = inparg.concretetype
+                if TYPE is lltype.Signed:
+                    set_future_value_int(i, args[i])
+                elif isinstance(TYPE, lltype.Ptr):
+                    set_future_value_ref(i, args[i])
+                elif TYPE is lltype.Float:
+                    set_future_value_float(i, args[i])
+                else:
+                    raise Exception("Nonsense type %s" % TYPE)
+
+            failindex = self.cpu._execute_token(loop_token)
+            try:
+                if self.cpu.index_of_virtualizable != -1:
+                    return self.cpu.assembler_helper_ptr(failindex,
+                        args[self.cpu.index_of_virtualizable])
+                else:
+                    return self.cpu.assembler_helper_ptr(failindex,
+                        lltype.nullptr(llmemory.GCREF.TO))
+            except LLException, lle:
+                assert _last_exception is None, "exception left behind"
+                _last_exception = lle
+                # fish op
+                op = self.loop.operations[self.opindex]
+                if op.result is not None:
+                    return 0
+        finally:
+            self._may_force = -1
+
     def op_guard_not_forced(self, descr):
         forced = self._forced
         self._forced = False
         if forced:
             raise GuardFailed
 
+    def op_virtual_ref(self, _, virtual, index):
+        return virtual
+
+    def op_virtual_ref_finish(self, _, vref, virtual):
+        pass
+
 
 class OOFrame(Frame):
 
@@ -961,11 +1011,11 @@
     return x
 
 
-def new_frame(memocast, is_oo):
+def new_frame(memocast, is_oo, cpu):
     if is_oo:
-        frame = OOFrame(memocast)
+        frame = OOFrame(memocast, cpu)
     else:
-        frame = Frame(memocast)
+        frame = Frame(memocast, cpu)
     return _to_opaque(frame)
 
 _future_values = []
@@ -1059,7 +1109,7 @@
     else:
         # for tests, a random emulated ll_inst will do
         if Class not in _pseudo_exceptions:
-            ll_inst = lltype.malloc(rclass.OBJECT)
+            ll_inst = lltype.malloc(rclass.OBJECT, zero=True)
             ll_inst.typeptr = lltype.malloc(rclass.OBJECT_VTABLE,
                                             immortal=True)
             _pseudo_exceptions[Class] = LLException(ll_inst.typeptr, ll_inst)
@@ -1086,7 +1136,8 @@
     assert frame._may_force >= 0
     call_op = frame.loop.operations[frame._may_force]
     guard_op = frame.loop.operations[frame._may_force+1]
-    assert call_op.opnum == rop.CALL_MAY_FORCE
+    opnum = call_op.opnum
+    assert opnum == rop.CALL_MAY_FORCE or opnum == rop.CALL_ASSEMBLER
     frame._populate_fail_args(guard_op, skip=call_op.result)
     return frame.fail_index
 
@@ -1201,7 +1252,7 @@
 
 def do_new(size):
     TYPE = symbolic.Size2Type[size]
-    x = lltype.malloc(TYPE)
+    x = lltype.malloc(TYPE, zero=True)
     return cast_to_ptr(x)
 
 def do_new_array(arraynum, count):

Modified: pypy/branch/stringbuilder2/pypy/jit/backend/llgraph/runner.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/backend/llgraph/runner.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/backend/llgraph/runner.py	Mon Jan 25 15:25:48 2010
@@ -74,7 +74,8 @@
 class BaseCPU(model.AbstractCPU):
     supports_floats = True
 
-    def __init__(self, rtyper, stats=None, opts=None, translate_support_code=False,
+    def __init__(self, rtyper, stats=None, opts=None,
+                 translate_support_code=False,
                  annmixlevel=None, gcdescr=None):
         assert type(opts) is not bool
         model.AbstractCPU.__init__(self)
@@ -147,6 +148,8 @@
             descr = op.descr
             if isinstance(descr, Descr):
                 llimpl.compile_add_descr(c, descr.ofs, descr.typeinfo)
+            if isinstance(descr, history.LoopToken):
+                llimpl.compile_add_loop_token(c, descr)
             if self.is_oo and isinstance(descr, (OODescr, MethDescr)):
                 # hack hack, not rpython
                 c._obj.externalobj.operations[-1].descr = descr
@@ -207,18 +210,22 @@
         else:
             assert False, "unknown operation"
 
-    def execute_token(self, loop_token):
-        """Calls the assembler generated for the given loop.
-        Returns the ResOperation that failed, of type rop.FAIL.
-        """
+    def _execute_token(self, loop_token):
         compiled_version = loop_token._llgraph_compiled_version
-        frame = llimpl.new_frame(self.memo_cast, self.is_oo)
+        frame = llimpl.new_frame(self.memo_cast, self.is_oo, self)
         # setup the frame
         llimpl.frame_clear(frame, compiled_version)
         # run the loop
         fail_index = llimpl.frame_execute(frame)
         # we hit a FAIL operation.
         self.latest_frame = frame
+        return fail_index
+
+    def execute_token(self, loop_token):
+        """Calls the assembler generated for the given loop.
+        Returns the ResOperation that failed, of type rop.FAIL.
+        """
+        fail_index = self._execute_token(loop_token)
         return self.get_fail_descr_from_number(fail_index)
 
     def set_future_value_int(self, index, intvalue):

Modified: pypy/branch/stringbuilder2/pypy/jit/backend/llgraph/test/test_llgraph.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/backend/llgraph/test/test_llgraph.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/backend/llgraph/test/test_llgraph.py	Mon Jan 25 15:25:48 2010
@@ -7,7 +7,8 @@
      TreeLoop
 from pypy.jit.metainterp.resoperation import ResOperation, rop
 from pypy.jit.metainterp.executor import execute
-from pypy.jit.backend.test.runner_test import LLtypeBackendTest
+from pypy.jit.backend.test.runner_test import LLtypeBackendTest, \
+     BaseAssemblerCallTests
 
 class TestLLTypeLLGraph(LLtypeBackendTest):
     # for individual tests see:

Modified: pypy/branch/stringbuilder2/pypy/jit/backend/llsupport/gc.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/backend/llsupport/gc.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/backend/llsupport/gc.py	Mon Jan 25 15:25:48 2010
@@ -41,7 +41,7 @@
         GcLLDescription.__init__(self, gcdescr, translator)
         # grab a pointer to the Boehm 'malloc' function
         from pypy.rpython.tool import rffi_platform
-        compilation_info = rffi_platform.check_boehm()
+        compilation_info = rffi_platform.configure_boehm()
 
         # Versions 6.x of libgc needs to use GC_local_malloc().
         # Versions 7.x of libgc removed this function; GC_malloc() has

Modified: pypy/branch/stringbuilder2/pypy/jit/backend/model.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/backend/model.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/backend/model.py	Mon Jan 25 15:25:48 2010
@@ -1,8 +1,12 @@
-from pypy.jit.metainterp import history
+from pypy.jit.metainterp import history, compile
 
 
 class AbstractCPU(object):
     supports_floats = False
+    # assembler_helper_ptr - a pointer to helper to call after a direct
+    #                        assembler call
+    portal_calldescr = None
+    done_with_this_frame_int_v = -1
 
     def __init__(self):
         self.fail_descr_list = []
@@ -209,6 +213,12 @@
     def do_call(self, args, calldescr):
         raise NotImplementedError
 
+    def do_call_assembler(self, args, token):
+        raise NotImplementedError
+
+    def do_call_loopinvariant(self, args, calldescr):
+        return self.do_call(args, calldescr)
+
     def do_cond_call_gc_wb(self, args, calldescr):
         if args[0].getint() & args[1].getint():
             self.do_call(args[2:], calldescr)
@@ -219,10 +229,6 @@
     def do_cast_ptr_to_int(self, ptrbox):
         raise NotImplementedError
 
-    def do_force_token(self):
-        # this should not be implemented at all by the backends
-        raise NotImplementedError
-
     def do_call_may_force(self, args, calldescr):
         return self.do_call(args, calldescr)
 

Modified: pypy/branch/stringbuilder2/pypy/jit/backend/test/runner_test.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/backend/test/runner_test.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/backend/test/runner_test.py	Mon Jan 25 15:25:48 2010
@@ -1,5 +1,5 @@
 
-import py, sys, random
+import py, sys, random, os
 from pypy.jit.metainterp.history import (AbstractFailDescr,
                                          BasicFailDescr,
                                          BoxInt, Box, BoxPtr,
@@ -15,6 +15,7 @@
 from pypy.jit.metainterp.test.oparser import parse
 from pypy.rpython.annlowlevel import llhelper
 from pypy.rpython.llinterp import LLException
+from pypy.jit.metainterp.test.oparser import parse
 
 class Runner(object):
 
@@ -464,6 +465,30 @@
                                          [funcbox] + args,
                                          'float', descr=calldescr)
             assert abs(res.value - 4.6) < 0.0001
+        
+    def test_call_stack_alignment(self):
+        # test stack alignment issues, notably for Mac OS/X.
+        # also test the ordering of the arguments.
+
+        def func_ints(*ints):
+            s = str(ints) + '\n'
+            os.write(1, s)   # don't remove -- crash if the stack is misaligned
+            return sum([(10+i)*(5+j) for i, j in enumerate(ints)])
+
+        for nb_args in range(0, 35):
+            cpu = self.cpu
+            TP = lltype.Signed
+            #
+            FPTR = self.Ptr(self.FuncType([TP] * nb_args, TP))
+            func_ptr = llhelper(FPTR, func_ints)
+            FUNC = deref(FPTR)
+            calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
+            funcbox = self.get_funcbox(cpu, func_ptr)
+            args = [280-24*i for i in range(nb_args)]
+            res = self.execute_operation(rop.CALL,
+                                         [funcbox] + map(BoxInt, args),
+                                         'int', descr=calldescr)
+            assert res.value == func_ints(*args)
 
     def test_field_basic(self):
         t_box, T_box = self.alloc_instance(self.T)
@@ -779,6 +804,19 @@
             r = self.execute_operation(rop.SAME_AS, [BoxFloat(5.5)], 'float')
             assert r.value == 5.5
 
+    def test_virtual_ref(self):
+        # if VIRTUAL_REF reaches the backend, it should just be a SAME_AS
+        u_box = self.alloc_unicode(u"hello\u1234")
+        r = self.execute_operation(rop.VIRTUAL_REF, [u_box, ConstInt(2)],
+                                   'ref')
+        assert r.value == u_box.value
+
+    def test_virtual_ref_finish(self):
+        # if VIRTUAL_REF_FINISH reaches the backend, it is a no-op
+        self.execute_operation(rop.VIRTUAL_REF_FINISH,
+                               [BoxInt(123), BoxInt(234)],
+                               'void')
+
     def test_jump(self):
         # this test generates small loops where the JUMP passes many
         # arguments of various types, shuffling them around.
@@ -1572,6 +1610,87 @@
         
         lltype.free(x, flavor='raw')
 
+    def test_assembler_call(self):
+        called = []
+        def assembler_helper(failindex, virtualizable):
+            assert self.cpu.get_latest_value_int(0) == 10
+            called.append(failindex)
+            return 4 + 9
+        self.cpu.index_of_virtualizable = -1
+        self.cpu.assembler_helper_ptr = llhelper(lltype.Ptr(lltype.FuncType
+            ([lltype.Signed, llmemory.GCREF], lltype.Signed)), assembler_helper)
+        
+        ops = '''
+        [i0, i1]
+        i2 = int_add(i0, i1)
+        finish(i2)'''
+        loop = parse(ops)
+        looptoken = LoopToken()
+        self.cpu.compile_loop(loop.inputargs, loop.operations, looptoken)
+        ARGS = [lltype.Signed, lltype.Signed]
+        RES = lltype.Signed
+        self.cpu.portal_calldescr = self.cpu.calldescrof(
+            lltype.Ptr(lltype.FuncType(ARGS, RES)), ARGS, RES)
+        self.cpu.set_future_value_int(0, 1)
+        self.cpu.set_future_value_int(1, 2)
+        res = self.cpu.execute_token(looptoken)
+        assert self.cpu.get_latest_value_int(0) == 3
+        ops = '''
+        [i4, i5]
+        i6 = int_add(i4, 1)
+        i3 = call_assembler(i6, i5, descr=looptoken)
+        guard_not_forced()[]
+        finish(i3)
+        '''
+        loop = parse(ops, namespace=locals())
+        othertoken = LoopToken()
+        self.cpu.compile_loop(loop.inputargs, loop.operations, othertoken)
+        self.cpu.set_future_value_int(0, 4)
+        self.cpu.set_future_value_int(1, 5)
+        res = self.cpu.execute_token(othertoken)
+        assert self.cpu.get_latest_value_int(0) == 13
+        assert called
+
+    def test_assembler_call_float(self):
+        called = []
+        def assembler_helper(failindex, virtualizable):
+            assert self.cpu.get_latest_value_float(0) == 1.2 + 3.2
+            called.append(failindex)
+            return 13.5
+        self.cpu.index_of_virtualizable = -1
+        self.cpu.assembler_helper_ptr = llhelper(lltype.Ptr(lltype.FuncType
+            ([lltype.Signed, llmemory.GCREF], lltype.Float)), assembler_helper)
+        ARGS = [lltype.Float, lltype.Float]
+        RES = lltype.Float
+        self.cpu.portal_calldescr = self.cpu.calldescrof(
+            lltype.Ptr(lltype.FuncType(ARGS, RES)), ARGS, RES)
+        
+        ops = '''
+        [f0, f1]
+        f2 = float_add(f0, f1)
+        finish(f2)'''
+        loop = parse(ops)
+        looptoken = LoopToken()
+        self.cpu.compile_loop(loop.inputargs, loop.operations, looptoken)
+        self.cpu.set_future_value_float(0, 1.2)
+        self.cpu.set_future_value_float(1, 2.3)
+        res = self.cpu.execute_token(looptoken)
+        assert self.cpu.get_latest_value_float(0) == 1.2 + 2.3
+        ops = '''
+        [f4, f5]
+        f3 = call_assembler(f4, f5, descr=looptoken)
+        guard_not_forced()[]
+        finish(f3)
+        '''
+        loop = parse(ops, namespace=locals())
+        othertoken = LoopToken()
+        self.cpu.compile_loop(loop.inputargs, loop.operations, othertoken)
+        self.cpu.set_future_value_float(0, 1.2)
+        self.cpu.set_future_value_float(1, 3.2)
+        res = self.cpu.execute_token(othertoken)
+        assert self.cpu.get_latest_value_float(0) == 13.5
+        assert called
+
 class OOtypeBackendTest(BaseBackendTest):
 
     type_system = 'ootype'
@@ -1609,3 +1728,4 @@
 
     def alloc_unicode(self, unicode):
         py.test.skip("implement me")
+    

Modified: pypy/branch/stringbuilder2/pypy/jit/backend/test/support.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/backend/test/support.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/backend/test/support.py	Mon Jan 25 15:25:48 2010
@@ -100,6 +100,9 @@
     def check_aborted_count(self, *args, **kwds):
         pass
 
+    def check_aborted_count_at_least(self, *args, **kwds):
+        pass
+
     def interp_operations(self, *args, **kwds):
         py.test.skip("interp_operations test skipped")
 
@@ -117,6 +120,7 @@
     def _get_TranslationContext(self):
         t = TranslationContext()
         t.config.translation.gc = 'boehm'
+        t.config.translation.list_comprehension_operations = True
         return t
 
     def _compile_and_run(self, t, entry_point, entry_point_graph, args):

Modified: pypy/branch/stringbuilder2/pypy/jit/backend/test/test_random.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/backend/test/test_random.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/backend/test/test_random.py	Mon Jan 25 15:25:48 2010
@@ -431,10 +431,11 @@
 
 for _op in [rop.FLOAT_NEG,
             rop.FLOAT_ABS,
-            rop.FLOAT_IS_TRUE,
             ]:
     OPERATIONS.append(UnaryFloatOperation(_op))
 
+OPERATIONS.append(UnaryFloatOperation(rop.FLOAT_IS_TRUE, boolres=True))
+
 OPERATIONS.append(CastFloatToIntOperation(rop.CAST_FLOAT_TO_INT))
 OPERATIONS.append(CastIntToFloatOperation(rop.CAST_INT_TO_FLOAT))
 

Modified: pypy/branch/stringbuilder2/pypy/jit/backend/x86/assembler.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/backend/x86/assembler.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/backend/x86/assembler.py	Mon Jan 25 15:25:48 2010
@@ -2,7 +2,8 @@
 import ctypes
 from pypy.jit.backend.llsupport import symbolic
 from pypy.jit.metainterp.history import Const, Box, BoxInt, BoxPtr, BoxFloat
-from pypy.jit.metainterp.history import AbstractFailDescr, INT, REF, FLOAT
+from pypy.jit.metainterp.history import AbstractFailDescr, INT, REF, FLOAT,\
+     LoopToken
 from pypy.rpython.lltypesystem import lltype, rffi, ll2ctypes, rstr, llmemory
 from pypy.rpython.lltypesystem.rclass import OBJECT
 from pypy.rpython.lltypesystem.lloperation import llop
@@ -17,6 +18,7 @@
 from pypy.jit.metainterp.resoperation import rop
 from pypy.jit.backend.x86.support import values_array
 from pypy.rlib.debug import debug_print
+from pypy.rlib import rgc
 
 # our calling convention - we pass first 6 args in registers
 # and the rest stays on the stack
@@ -27,7 +29,6 @@
 else:
     CALL_ALIGN = 1
 
-
 def align_stack_words(words):
     return (words + CALL_ALIGN - 1) & ~(CALL_ALIGN-1)
 
@@ -75,6 +76,7 @@
     mc = None
     mc2 = None
     mc_size = MachineCodeBlockWrapper.MC_DEFAULT_SIZE
+    _float_constants = None
 
     def __init__(self, cpu, translate_support_code=False,
                             failargs_limit=1000):
@@ -85,6 +87,7 @@
         self.malloc_array_func_addr = 0
         self.malloc_str_func_addr = 0
         self.malloc_unicode_func_addr = 0
+        self.assembler_helper_adr = 0
         self.fail_boxes_int = values_array(lltype.Signed, failargs_limit)
         self.fail_boxes_ptr = values_array(llmemory.GCREF, failargs_limit)
         self.fail_boxes_float = values_array(lltype.Float, failargs_limit)
@@ -117,6 +120,14 @@
                 ll_new_unicode = gc_ll_descr.get_funcptr_for_newunicode()
                 self.malloc_unicode_func_addr = rffi.cast(lltype.Signed,
                                                           ll_new_unicode)
+            if we_are_translated():
+                self.assembler_helper_adr = self.cpu.cast_ptr_to_int(
+                    self.cpu.assembler_helper_ptr)
+            else:
+                if getattr(self.cpu, 'assembler_helper_ptr', None):
+                    self.assembler_helper_adr = self.cpu.cast_ptr_to_int(
+                        self.cpu.assembler_helper_ptr)
+        
             # done
             # we generate the loop body in 'mc'
             # 'mc2' is for guard recovery code
@@ -153,6 +164,7 @@
         """adds the following attributes to looptoken:
                _x86_loop_code       (an integer giving an address)
                _x86_bootstrap_code  (an integer giving an address)
+               _x86_direct_bootstrap_code
                _x86_frame_depth
                _x86_param_depth
                _x86_arglocs
@@ -161,15 +173,29 @@
         regalloc = RegAlloc(self, self.cpu.translate_support_code)
         arglocs = regalloc.prepare_loop(inputargs, operations, looptoken)
         looptoken._x86_arglocs = arglocs
+        needed_mem = len(arglocs[0]) * 16 + 16
+        if needed_mem >= self.mc.bytes_free():
+            self.mc.make_new_mc()
         looptoken._x86_bootstrap_code = self.mc.tell()
         adr_stackadjust = self._assemble_bootstrap_code(inputargs, arglocs)
-        looptoken._x86_loop_code = self.mc.tell()
+        curadr = self.mc.tell()
+        looptoken._x86_loop_code = curadr
         looptoken._x86_frame_depth = -1     # temporarily
         looptoken._x86_param_depth = -1     # temporarily        
         frame_depth, param_depth = self._assemble(regalloc, operations)
         self._patch_stackadjust(adr_stackadjust, frame_depth+param_depth)
         looptoken._x86_frame_depth = frame_depth
         looptoken._x86_param_depth = param_depth
+        # we need to make sure here that we don't overload an mc badly.
+        # a safe estimate is that we need at most 16 bytes per arg
+        needed_mem = len(arglocs[0]) * 16 + 16
+        if needed_mem >= self.mc.bytes_free():
+            self.mc.make_new_mc()
+        looptoken._x86_direct_bootstrap_code = self.mc.tell()
+        self._assemble_bootstrap_direct_call(arglocs, curadr,
+                                             frame_depth+param_depth)
+        debug_print("Loop #", looptoken.number, "has address",
+                    looptoken._x86_loop_code, "to", self.mc.tell())
 
     def assemble_bridge(self, faildescr, inputargs, operations):
         self.make_sure_mc_exists()
@@ -192,6 +218,9 @@
             faildescr._x86_bridge_param_depth = param_depth
         # patch the jump from original guard
         self.patch_jump(faildescr, adr_bridge)
+        debug_print("Bridge out of guard",
+                    self.cpu.get_fail_descr_number(faildescr),
+                    "has address", adr_bridge, "to", self.mc.tell())
 
     def patch_jump(self, faildescr, adr_new_target):
         adr_jump_offset = faildescr._x86_adr_jump_offset
@@ -224,17 +253,17 @@
 
     def _patch_stackadjust(self, adr_lea, reserved_depth):
         # patch stack adjustment LEA
-        # possibly align, e.g. for Mac OS X        
         mc = codebuf.InMemoryCodeBuilder(adr_lea, adr_lea + 4)
         # Compute the correct offset for the instruction LEA ESP, [EBP-4*words].
         # Given that [EBP] is where we saved EBP, i.e. in the last word
         # of our fixed frame, then the 'words' value is:
         words = (FRAME_FIXED_SIZE - 1) + reserved_depth
-        mc.write(packimm32(-WORD * words))
+        # align, e.g. for Mac OS X        
+        aligned_words = align_stack_words(words+2)-2 # 2 = EIP+EBP
+        mc.write(packimm32(-WORD * aligned_words))
         mc.done()
 
-    def _assemble_bootstrap_code(self, inputargs, arglocs):
-        nonfloatlocs, floatlocs = arglocs
+    def _call_header(self):
         self.mc.PUSH(ebp)
         self.mc.MOV(ebp, esp)
         self.mc.PUSH(ebx)
@@ -242,7 +271,41 @@
         self.mc.PUSH(edi)
         # NB. the shape of the frame is hard-coded in get_basic_shape() too.
         # Also, make sure this is consistent with FRAME_FIXED_SIZE.
-        adr_stackadjust = self._patchable_stackadjust()
+        return self._patchable_stackadjust()
+
+    def _assemble_bootstrap_direct_call(self, arglocs, jmpadr, stackdepth):
+        # XXX pushing ebx esi and edi is a bit pointless, since we store
+        #     all regsiters anyway, for the case of guard_not_forced
+        # XXX this can be improved greatly. Right now it'll behave like
+        #     a normal call
+        nonfloatlocs, floatlocs = arglocs
+        # XXX not to repeat the logic, a bit around
+        adr_stackadjust = self._call_header()
+        self._patch_stackadjust(adr_stackadjust, stackdepth)
+        for i in range(len(nonfloatlocs)):
+            loc = nonfloatlocs[i]
+            if isinstance(loc, REG):
+                self.mc.MOV(loc, mem(ebp, (2 + i) * WORD))
+            loc = floatlocs[i]
+            if isinstance(loc, XMMREG):
+                self.mc.MOVSD(loc, mem64(ebp, (1 + i) * 2 * WORD))
+        tmp = eax
+        xmmtmp = xmm0
+        for i in range(len(nonfloatlocs)):
+            loc = nonfloatlocs[i]
+            if loc is not None and not isinstance(loc, REG):
+                self.mc.MOV(tmp, mem(ebp, (2 + i) * WORD))
+                self.mc.MOV(loc, tmp)
+            loc = floatlocs[i]
+            if loc is not None and not isinstance(loc, XMMREG):
+                self.mc.MOVSD(xmmtmp, mem64(ebp, (1 + i) * 2 * WORD))
+                self.mc.MOVSD(loc, xmmtmp)
+        self.mc.JMP(rel32(jmpadr))
+        return adr_stackadjust
+
+    def _assemble_bootstrap_code(self, inputargs, arglocs):
+        nonfloatlocs, floatlocs = arglocs
+        adr_stackadjust = self._call_header()
         tmp = X86RegisterManager.all_regs[0]
         xmmtmp = X86XMMRegisterManager.all_regs[0]
         for i in range(len(nonfloatlocs)):
@@ -366,19 +429,18 @@
         def genop_cmp(self, op, arglocs, result_loc):
             if isinstance(op.args[0], Const):
                 self.mc.CMP(arglocs[1], arglocs[0])
-                self.mc.MOV(result_loc, imm8(0))
                 getattr(self.mc, 'SET' + rev_cond)(lower_byte(result_loc))
             else:
                 self.mc.CMP(arglocs[0], arglocs[1])
-                self.mc.MOV(result_loc, imm8(0))
                 getattr(self.mc, 'SET' + cond)(lower_byte(result_loc))
+            self.mc.MOVZX(result_loc, lower_byte(result_loc))
         return genop_cmp
 
     def _cmpop_float(cond):
         def genop_cmp(self, op, arglocs, result_loc):
             self.mc.UCOMISD(arglocs[0], arglocs[1])
-            self.mc.MOV(result_loc, imm8(0))
             getattr(self.mc, 'SET' + cond)(lower_byte(result_loc))
+            self.mc.MOVZX(result_loc, lower_byte(result_loc))
         return genop_cmp
 
     def _cmpop_guard(cond, rev_cond, false_cond, false_rev_cond):
@@ -402,38 +464,47 @@
                     return self.implement_guard(addr, getattr(self.mc, name))
         return genop_cmp_guard
 
-##    XXX redo me
-##    def align_stack_for_call(self, nargs):
-##        # xxx do something when we don't use push anymore for calls
-##        extra_on_stack = align_stack_words(nargs)
-##        for i in range(extra_on_stack-nargs):
-##            self.mc.PUSH(imm(0))   --- or just use a single SUB(esp, imm)
-##        return extra_on_stack
-
-    def _emit_call(self, x, arglocs, start=0, tmp=eax):
+    def _cmpop_guard_float(cond, false_cond):
+        def genop_cmp_guard_float(self, op, guard_op, addr, arglocs,
+                                  result_loc):
+            guard_opnum = guard_op.opnum
+            self.mc.UCOMISD(arglocs[0], arglocs[1])
+            if guard_opnum == rop.GUARD_FALSE:
+                name = 'J' + cond
+                return self.implement_guard(addr, getattr(self.mc, name))
+            else:
+                name = 'J' + false_cond
+                return self.implement_guard(addr, getattr(self.mc, name))
+        return genop_cmp_guard_float
+
+    @specialize.arg(5)
+    def _emit_call(self, x, arglocs, start=0, tmp=eax, force_mc=False,
+                   mc=None):
+        if not force_mc:
+            mc = self.mc
         p = 0
         n = len(arglocs)
         for i in range(start, n):
             loc = arglocs[i]
             if isinstance(loc, REG):
                 if isinstance(loc, XMMREG):
-                    self.mc.MOVSD(mem64(esp, p), loc)
+                    mc.MOVSD(mem64(esp, p), loc)
                 else:
-                    self.mc.MOV(mem(esp, p), loc)
+                    mc.MOV(mem(esp, p), loc)
             p += round_up_to_4(loc.width)
         p = 0
         for i in range(start, n):
             loc = arglocs[i]
             if not isinstance(loc, REG):
                 if isinstance(loc, MODRM64):
-                    self.mc.MOVSD(xmm0, loc)
-                    self.mc.MOVSD(mem64(esp, p), xmm0)
+                    mc.MOVSD(xmm0, loc)
+                    mc.MOVSD(mem64(esp, p), xmm0)
                 else:
-                    self.mc.MOV(tmp, loc)
-                    self.mc.MOV(mem(esp, p), tmp)
+                    mc.MOV(tmp, loc)
+                    mc.MOV(mem(esp, p), tmp)
             p += round_up_to_4(loc.width)
         self._regalloc.reserve_param(p//WORD)
-        self.mc.CALL(x)
+        mc.CALL(x)
         self.mark_gc_roots()
         
     def call(self, addr, args, res):
@@ -460,11 +531,11 @@
     genop_int_lt = _cmpop("L", "G")
     genop_int_le = _cmpop("LE", "GE")
     genop_int_eq = _cmpop("E", "E")
-    genop_oois = genop_int_eq
     genop_int_ne = _cmpop("NE", "NE")
-    genop_ooisnot = genop_int_ne
     genop_int_gt = _cmpop("G", "L")
     genop_int_ge = _cmpop("GE", "LE")
+    genop_oois = genop_int_eq
+    genop_ooisnot = genop_int_ne
 
     genop_float_lt = _cmpop_float('B')
     genop_float_le = _cmpop_float('BE')
@@ -484,12 +555,21 @@
     genop_guard_int_ne = _cmpop_guard("NE", "NE", "E", "E")
     genop_guard_int_gt = _cmpop_guard("G", "L", "LE", "GE")
     genop_guard_int_ge = _cmpop_guard("GE", "LE", "L", "G")
+    genop_guard_oois = genop_guard_int_eq
+    genop_guard_ooisnot = genop_guard_int_ne
 
     genop_guard_uint_gt = _cmpop_guard("A", "B", "BE", "AE")
     genop_guard_uint_lt = _cmpop_guard("B", "A", "AE", "BE")
     genop_guard_uint_le = _cmpop_guard("BE", "AE", "A", "B")
     genop_guard_uint_ge = _cmpop_guard("AE", "BE", "B", "A")
 
+    genop_guard_float_lt = _cmpop_guard_float("B", "AE")
+    genop_guard_float_le = _cmpop_guard_float("BE", "A")
+    genop_guard_float_eq = _cmpop_guard_float("E", "NE")
+    genop_guard_float_ne = _cmpop_guard_float("NE", "E")
+    genop_guard_float_gt = _cmpop_guard_float("A", "BE")
+    genop_guard_float_ge = _cmpop_guard_float("AE", "B")
+
     def genop_float_neg(self, op, arglocs, resloc):
         # Following what gcc does: res = x ^ 0x8000000000000000
         self.mc.XORPD(arglocs[0], self.loc_float_const_neg)
@@ -498,6 +578,16 @@
         # Following what gcc does: res = x & 0x7FFFFFFFFFFFFFFF
         self.mc.ANDPD(arglocs[0], self.loc_float_const_abs)
 
+    def genop_guard_float_is_true(self, op, guard_op, addr, arglocs, resloc):
+        guard_opnum = guard_op.opnum
+        loc0, loc1 = arglocs
+        self.mc.XORPD(loc0, loc0)
+        self.mc.UCOMISD(loc0, loc1)
+        if guard_opnum == rop.GUARD_TRUE:
+            return self.implement_guard(addr, self.mc.JZ)
+        else:
+            return self.implement_guard(addr, self.mc.JNZ)
+
     def genop_float_is_true(self, op, arglocs, resloc):
         loc0, loc1 = arglocs
         self.mc.XORPD(loc0, loc0)
@@ -511,9 +601,6 @@
     def genop_cast_int_to_float(self, op, arglocs, resloc):
         self.mc.CVTSI2SD(resloc, arglocs[0])
 
-    def genop_bool_not(self, op, arglocs, resloc):
-        self.mc.XOR(arglocs[0], imm8(1))
-
     def genop_int_lshift(self, op, arglocs, resloc):
         loc, loc2 = arglocs
         if loc2 is ecx:
@@ -534,8 +621,7 @@
 
     def genop_guard_int_is_true(self, op, guard_op, addr, arglocs, resloc):
         guard_opnum = guard_op.opnum
-        loc = arglocs[0]
-        self.mc.TEST(loc, loc)
+        self.mc.CMP(arglocs[0], imm8(0))
         if guard_opnum == rop.GUARD_TRUE:
             return self.implement_guard(addr, self.mc.JZ)
         else:
@@ -543,12 +629,24 @@
 
     def genop_int_is_true(self, op, arglocs, resloc):
         self.mc.CMP(arglocs[0], imm8(0))
-        self.mc.MOV(resloc, imm8(0))
         self.mc.SETNE(lower_byte(resloc))
+        self.mc.MOVZX(resloc, lower_byte(resloc))
+
+    def genop_guard_bool_not(self, op, guard_op, addr, arglocs, resloc):
+        guard_opnum = guard_op.opnum
+        self.mc.CMP(arglocs[0], imm8(0))
+        if guard_opnum == rop.GUARD_TRUE:
+            return self.implement_guard(addr, self.mc.JNZ)
+        else:
+            return self.implement_guard(addr, self.mc.JZ)
+
+    def genop_bool_not(self, op, arglocs, resloc):
+        self.mc.XOR(arglocs[0], imm8(1))
 
     def genop_same_as(self, op, arglocs, resloc):
         self.mov(arglocs[0], resloc)
     genop_cast_ptr_to_int = genop_same_as
+    genop_virtual_ref = genop_same_as
 
     def genop_int_mod(self, op, arglocs, resloc):
         self.mc.CDQ()
@@ -955,6 +1053,7 @@
             boxes.append(box)
         return boxes
 
+    @rgc.no_collect
     def grab_frame_values(self, bytecode, frame_addr, allregisters):
         # no malloc allowed here!!
         self.fail_ebp = allregisters[16 + ebp.op]
@@ -1019,6 +1118,7 @@
 
     def setup_failure_recovery(self):
 
+        @rgc.no_collect
         def failure_recovery_func(registers):
             # 'registers' is a pointer to a structure containing the
             # original value of the registers, optionally the original
@@ -1153,7 +1253,7 @@
             tmp = ecx
         else:
             tmp = eax
-            
+        
         self._emit_call(x, arglocs, 2, tmp=tmp)
 
         if isinstance(resloc, MODRM64):
@@ -1174,6 +1274,38 @@
         self.mc.CMP(mem(ebp, FORCE_INDEX_OFS), imm(0))
         return self.implement_guard(addr, self.mc.JL)
 
+    def genop_guard_call_assembler(self, op, guard_op, addr,
+                                   arglocs, result_loc):
+        faildescr = guard_op.descr
+        fail_index = self.cpu.get_fail_descr_number(faildescr)
+        self.mc.MOV(mem(ebp, FORCE_INDEX_OFS), imm(fail_index))
+        descr = op.descr
+        assert isinstance(descr, LoopToken)
+        assert len(arglocs) - 2 == len(descr._x86_arglocs[0])
+        self._emit_call(rel32(descr._x86_direct_bootstrap_code), arglocs, 2,
+                        tmp=eax)
+        mc = self.mc._mc
+        mc.CMP(eax, imm(self.cpu.done_with_this_frame_int_v))
+        mc.write(constlistofchars('\x74\x00')) # JE below
+        je_location = mc.get_relative_pos()
+        self._emit_call(rel32(self.assembler_helper_adr), [eax, arglocs[1]], 0,
+                        tmp=ecx, force_mc=True, mc=mc)
+        mc.write(constlistofchars('\xEB\x00')) # JMP below
+        jmp_location = mc.get_relative_pos()
+        offset = jmp_location - je_location
+        assert 0 < offset <= 127
+        mc.overwrite(je_location - 1, [chr(offset)])
+        mc.MOV(eax, heap(self.fail_boxes_int.get_addr_for_num(0)))
+        offset = mc.get_relative_pos() - jmp_location
+        assert 0 < offset <= 127
+        mc.overwrite(jmp_location - 1, [chr(offset)])
+        if isinstance(result_loc, MODRM64):
+            self.mc.FSTP(result_loc)
+        else:
+            assert result_loc is eax or result_loc is None
+        self.mc.CMP(mem(ebp, FORCE_INDEX_OFS), imm(0))
+        return self.implement_guard(addr, self.mc.JL)        
+
     def genop_discard_cond_call_gc_wb(self, op, arglocs):
         # use 'mc._mc' directly instead of 'mc', to avoid
         # bad surprizes if the code buffer is mostly full
@@ -1218,7 +1350,7 @@
 
     def not_implemented_op_guard(self, op, guard_op,
                                  failaddr, arglocs, resloc):
-        msg = "not implemented operation (guard): %s" % guard_op.getopname()
+        msg = "not implemented operation (guard): %s" % op.getopname()
         print msg
         raise NotImplementedError(msg)
 

Modified: pypy/branch/stringbuilder2/pypy/jit/backend/x86/regalloc.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/backend/x86/regalloc.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/backend/x86/regalloc.py	Mon Jan 25 15:25:48 2010
@@ -52,7 +52,28 @@
             print "convert_to_imm: got a %s" % c
             raise AssertionError
 
-BASE_CONSTANT_SIZE = 1000
+
+class FloatConstants(object):
+    BASE_CONSTANT_SIZE = 1000
+
+    def __init__(self):
+        self.cur_array_free = 0
+
+    def _get_new_array(self):
+        n = self.BASE_CONSTANT_SIZE
+        self.cur_array = lltype.malloc(rffi.CArray(lltype.Float), n,
+                                       flavor='raw')
+        self.cur_array_free = n
+    _get_new_array._dont_inline_ = True
+
+    def record_float(self, floatval):
+        if self.cur_array_free == 0:
+            self._get_new_array()
+        arr = self.cur_array
+        n = self.cur_array_free - 1
+        arr[n] = floatval
+        self.cur_array_free = n
+        return rffi.cast(lltype.Signed, arr) + n * 8
 
 
 class X86XMMRegisterManager(RegisterManager):
@@ -63,29 +84,19 @@
     save_around_call_regs = all_regs
     reg_width = 2
 
-    def new_const_array(self):
-        return lltype.malloc(rffi.CArray(lltype.Float), BASE_CONSTANT_SIZE,
-                             flavor='raw')
-
     def __init__(self, longevity, frame_manager=None, assembler=None):
         RegisterManager.__init__(self, longevity, frame_manager=frame_manager,
                                  assembler=assembler)
-        self.constant_arrays = [self.new_const_array()]
-        self.constant_array_counter = 0
+        if assembler is None:
+            self.float_constants = FloatConstants()
+        else:
+            if assembler._float_constants is None:
+                assembler._float_constants = FloatConstants()
+            self.float_constants = assembler._float_constants
 
     def convert_to_imm(self, c):
-        if self.constant_array_counter >= BASE_CONSTANT_SIZE:
-            self.constant_arrays.append(self.new_const_array())
-            self.constant_array_counter = 0
-        res = self.constant_array_counter
-        self.constant_array_counter += 1
-        arr = self.constant_arrays[-1]
-        arr[res] = c.getfloat()
-        return self.get_addr_of_const_float(-1, res)
-
-    def get_addr_of_const_float(self, num_arr, num_pos):
-        arr = self.constant_arrays[num_arr]
-        return heap64(rffi.cast(lltype.Signed, arr) + num_pos * WORD * 2)
+        adr = self.float_constants.record_float(c.getfloat())
+        return heap64(adr)
         
     def after_call(self, v):
         # the result is stored in st0, but we don't have this around,
@@ -307,7 +318,7 @@
         self.assembler.regalloc_perform_discard(op, arglocs)
 
     def can_merge_with_next_guard(self, op, i, operations):
-        if op.opnum == rop.CALL_MAY_FORCE:
+        if op.opnum == rop.CALL_MAY_FORCE or op.opnum == rop.CALL_ASSEMBLER:
             assert operations[i + 1].opnum == rop.GUARD_NOT_FORCED
             return True
         if not op.is_comparison():
@@ -334,10 +345,10 @@
                 self.possibly_free_vars(op.args)
                 continue
             if self.can_merge_with_next_guard(op, i, operations):
-                oplist[op.opnum](self, op, operations[i + 1])
+                oplist_with_guard[op.opnum](self, op, operations[i + 1])
                 i += 1
             else:
-                oplist[op.opnum](self, op, None)
+                oplist[op.opnum](self, op)
             if op.result is not None:
                 self.possibly_free_var(op.result)
             self.rm._check_invariants()
@@ -386,7 +397,7 @@
             return self.xrm.loc(v)
         return self.rm.loc(v)
 
-    def _consider_guard(self, op, ignored):
+    def _consider_guard(self, op):
         loc = self.rm.make_sure_var_in_reg(op.args[0])
         self.perform_guard(op, [loc], None)
         self.rm.possibly_free_var(op.args[0])
@@ -396,7 +407,7 @@
     consider_guard_nonnull = _consider_guard
     consider_guard_isnull = _consider_guard
 
-    def consider_finish(self, op, ignored):
+    def consider_finish(self, op):
         locs = [self.loc(v) for v in op.args]
         locs_are_ref = [v.type == REF for v in op.args]
         fail_index = self.assembler.cpu.get_fail_descr_number(op.descr)
@@ -404,10 +415,10 @@
                                         self.exc, locs_are_ref)
         self.possibly_free_vars(op.args)
 
-    def consider_guard_no_exception(self, op, ignored):
+    def consider_guard_no_exception(self, op):
         self.perform_guard(op, [], None)
 
-    def consider_guard_exception(self, op, ignored):
+    def consider_guard_exception(self, op):
         loc = self.rm.make_sure_var_in_reg(op.args[0])
         box = TempBox()
         loc1 = self.rm.force_allocate_reg(box, op.args)
@@ -423,13 +434,13 @@
     consider_guard_no_overflow = consider_guard_no_exception
     consider_guard_overflow    = consider_guard_no_exception
 
-    def consider_guard_value(self, op, ignored):
+    def consider_guard_value(self, op):
         x = self.make_sure_var_in_reg(op.args[0])
         y = self.loc(op.args[1])
         self.perform_guard(op, [x, y], None)
         self.possibly_free_vars(op.args)
 
-    def consider_guard_class(self, op, ignored):
+    def consider_guard_class(self, op):
         assert isinstance(op.args[0], Box)
         x = self.rm.make_sure_var_in_reg(op.args[0])
         y = self.loc(op.args[1])
@@ -438,15 +449,15 @@
 
     consider_guard_nonnull_class = consider_guard_class
 
-    def _consider_binop_part(self, op, ignored):
+    def _consider_binop_part(self, op):
         x = op.args[0]
         argloc = self.loc(op.args[1])
         loc = self.rm.force_result_in_reg(op.result, x, op.args)
         self.rm.possibly_free_var(op.args[1])
         return loc, argloc
 
-    def _consider_binop(self, op, ignored):
-        loc, argloc = self._consider_binop_part(op, ignored)
+    def _consider_binop(self, op):
+        loc, argloc = self._consider_binop_part(op)
         self.Perform(op, [loc, argloc], loc)
 
     consider_int_add = _consider_binop
@@ -460,14 +471,13 @@
     consider_int_sub_ovf = _consider_binop
     consider_int_add_ovf = _consider_binop
 
-    def consider_int_neg(self, op, ignored):
+    def consider_int_neg(self, op):
         res = self.rm.force_result_in_reg(op.result, op.args[0])
         self.Perform(op, [res], res)
 
     consider_int_invert = consider_int_neg
-    consider_bool_not = consider_int_neg
 
-    def consider_int_lshift(self, op, ignored):
+    def consider_int_lshift(self, op):
         if isinstance(op.args[1], Const):
             loc2 = self.rm.convert_to_imm(op.args[1])
         else:
@@ -493,11 +503,11 @@
         self.rm.possibly_free_vars(op.args)
         self.rm.possibly_free_var(tmpvar)
 
-    def consider_int_mod(self, op, ignored):
+    def consider_int_mod(self, op):
         self._consider_int_div_or_mod(op, edx, eax)
         self.Perform(op, [eax, ecx], edx)
 
-    def consider_int_floordiv(self, op, ignored):
+    def consider_int_floordiv(self, op):
         self._consider_int_div_or_mod(op, eax, edx)
         self.Perform(op, [eax, ecx], eax)
 
@@ -531,7 +541,7 @@
     consider_oois = _consider_compop
     consider_ooisnot = _consider_compop
 
-    def _consider_float_op(self, op, ignored):
+    def _consider_float_op(self, op):
         loc1 = self.xrm.loc(op.args[1])
         loc0 = self.xrm.force_result_in_reg(op.result, op.args[0], op.args)
         self.Perform(op, [loc0, loc1], loc0)
@@ -542,15 +552,17 @@
     consider_float_mul = _consider_float_op
     consider_float_truediv = _consider_float_op
 
-    def _consider_float_cmp(self, op, ignored):
-        assert ignored is None
-        # XXX so far we don't have guards here, but we want them
+    def _consider_float_cmp(self, op, guard_op):
         loc0 = self.xrm.make_sure_var_in_reg(op.args[0], op.args,
                                              imm_fine=False)
         loc1 = self.xrm.loc(op.args[1])
-        res = self.rm.force_allocate_reg(op.result, need_lower_byte=True)
-        self.Perform(op, [loc0, loc1], res)
-        self.xrm.possibly_free_vars(op.args)        
+        arglocs = [loc0, loc1]
+        self.xrm.possibly_free_vars(op.args)
+        if guard_op is None:
+            res = self.rm.force_allocate_reg(op.result, need_lower_byte=True)
+            self.Perform(op, arglocs, res)
+        else:
+            self.perform_with_guard(op, guard_op, arglocs, None)
 
     consider_float_lt = _consider_float_cmp
     consider_float_le = _consider_float_cmp
@@ -559,32 +571,37 @@
     consider_float_gt = _consider_float_cmp
     consider_float_ge = _consider_float_cmp
 
-    def consider_float_neg(self, op, ignored):
+    def consider_float_neg(self, op):
         loc0 = self.xrm.force_result_in_reg(op.result, op.args[0])
         self.Perform(op, [loc0], loc0)
         self.xrm.possibly_free_var(op.args[0])
 
-    def consider_float_abs(self, op, ignored):
+    def consider_float_abs(self, op):
         loc0 = self.xrm.force_result_in_reg(op.result, op.args[0])
         self.Perform(op, [loc0], loc0)
         self.xrm.possibly_free_var(op.args[0])
 
-    def consider_float_is_true(self, op, ignored):
+    def consider_float_is_true(self, op, guard_op):
+        # doesn't need arg to be in a register
         tmpbox0 = TempBox()
         loc0 = self.xrm.force_allocate_reg(tmpbox0)
         loc1 = self.xrm.loc(op.args[0])
-        loc2 = self.rm.force_allocate_reg(op.result, need_lower_byte=True)
-        self.Perform(op, [loc0, loc1], loc2)
+        arglocs = [loc0, loc1]
         self.xrm.possibly_free_var(op.args[0])
         self.xrm.possibly_free_var(tmpbox0)
+        if guard_op is not None:
+            self.perform_with_guard(op, guard_op, arglocs, None)
+        else:
+            loc2 = self.rm.force_allocate_reg(op.result, need_lower_byte=True)
+            self.Perform(op, arglocs, loc2)
 
-    def consider_cast_float_to_int(self, op, ignored):
+    def consider_cast_float_to_int(self, op):
         loc0 = self.xrm.make_sure_var_in_reg(op.args[0], imm_fine=False)
         loc1 = self.rm.force_allocate_reg(op.result)
         self.Perform(op, [loc0], loc1)
         self.xrm.possibly_free_var(op.args[0])
 
-    def consider_cast_int_to_float(self, op, ignored):
+    def consider_cast_int_to_float(self, op):
         loc0 = self.rm.loc(op.args[0])
         loc1 = self.xrm.force_allocate_reg(op.result)
         self.Perform(op, [loc0], loc1)
@@ -614,7 +631,7 @@
         self._call(op, [imm(size)] + [self.loc(arg) for arg in op.args],
                    guard_not_forced_op=guard_not_forced_op)
 
-    def consider_call(self, op, ignored):
+    def consider_call(self, op):
         self._consider_call(op)
     consider_call_pure = consider_call
 
@@ -622,7 +639,21 @@
         assert guard_op is not None
         self._consider_call(op, guard_op)
 
-    def consider_cond_call_gc_wb(self, op, ignored):
+    def consider_call_assembler(self, op, guard_op):
+        descr = op.descr
+        portal_calldescr = self.assembler.cpu.portal_calldescr
+        size = portal_calldescr.get_result_size(self.translate_support_code)
+        vable_index = self.assembler.cpu.index_of_virtualizable
+        if vable_index != -1:
+            self.rm._sync_var(op.args[vable_index])
+            vable = self.fm.loc(op.args[vable_index], 1)
+        else:
+            vable = imm(0)
+        self._call(op, [imm(size), vable] +
+                   [self.loc(arg) for arg in op.args],
+                   guard_not_forced_op=guard_op)
+        
+    def consider_cond_call_gc_wb(self, op):
         assert op.result is None
         arglocs = [self.loc(arg) for arg in op.args]
         # add eax, ecx and edx as extra "arguments" to ensure they are
@@ -664,7 +695,7 @@
             gc_ll_descr.get_malloc_fixedsize_slowpath_addr(),
             )
 
-    def consider_new(self, op, ignored):
+    def consider_new(self, op):
         gc_ll_descr = self.assembler.cpu.gc_ll_descr
         if gc_ll_descr.can_inline_malloc(op.descr):
             self._fastpath_malloc(op, op.descr)
@@ -673,7 +704,7 @@
             arglocs = [imm(x) for x in args]
             return self._call(op, arglocs)
 
-    def consider_new_with_vtable(self, op, ignored):
+    def consider_new_with_vtable(self, op):
         classint = op.args[0].getint()
         descrsize = self.assembler.cpu.class_sizes[classint]
         if self.assembler.cpu.gc_ll_descr.can_inline_malloc(descrsize):
@@ -686,7 +717,7 @@
             arglocs.append(self.loc(op.args[0]))
             return self._call(op, arglocs)
 
-    def consider_newstr(self, op, ignored):
+    def consider_newstr(self, op):
         gc_ll_descr = self.assembler.cpu.gc_ll_descr
         if gc_ll_descr.get_funcptr_for_newstr is not None:
             # framework GC
@@ -698,7 +729,7 @@
         return self._malloc_varsize(ofs_items, ofs, 0, op.args[0],
                                     op.result)
 
-    def consider_newunicode(self, op, ignored):
+    def consider_newunicode(self, op):
         gc_ll_descr = self.assembler.cpu.gc_ll_descr
         if gc_ll_descr.get_funcptr_for_newunicode is not None:
             # framework GC
@@ -736,7 +767,7 @@
         self.PerformDiscard(ResOperation(rop.SETFIELD_GC, [], None),
                             [eax, imm(ofs_length), imm(WORD), loc])
 
-    def consider_new_array(self, op, ignored):
+    def consider_new_array(self, op):
         gc_ll_descr = self.assembler.cpu.gc_ll_descr
         if gc_ll_descr.get_funcptr_for_newarray is not None:
             # framework GC
@@ -767,7 +798,7 @@
         ptr = fielddescr.is_pointer_field()
         return imm(ofs), imm(size), ptr
 
-    def consider_setfield_gc(self, op, ignored):
+    def consider_setfield_gc(self, op):
         ofs_loc, size_loc, ptr = self._unpack_fielddescr(op.descr)
         assert isinstance(size_loc, IMM32)
         if size_loc.value == 1:
@@ -782,7 +813,7 @@
 
     consider_setfield_raw = consider_setfield_gc
 
-    def consider_strsetitem(self, op, ignored):
+    def consider_strsetitem(self, op):
         base_loc = self.rm.make_sure_var_in_reg(op.args[0], op.args)
         ofs_loc = self.rm.make_sure_var_in_reg(op.args[1], op.args)
         value_loc = self.rm.make_sure_var_in_reg(op.args[2], op.args,
@@ -792,7 +823,7 @@
 
     consider_unicodesetitem = consider_strsetitem
 
-    def consider_setarrayitem_gc(self, op, ignored):
+    def consider_setarrayitem_gc(self, op):
         scale, ofs, ptr = self._unpack_arraydescr(op.descr)
         base_loc  = self.rm.make_sure_var_in_reg(op.args[0], op.args)
         if scale == 0:
@@ -808,7 +839,7 @@
 
     consider_setarrayitem_raw = consider_setarrayitem_gc
 
-    def consider_getfield_gc(self, op, ignored):
+    def consider_getfield_gc(self, op):
         ofs_loc, size_loc, _ = self._unpack_fielddescr(op.descr)
         base_loc = self.rm.make_sure_var_in_reg(op.args[0], op.args)
         self.rm.possibly_free_vars(op.args)
@@ -819,7 +850,7 @@
     consider_getfield_raw_pure = consider_getfield_gc
     consider_getfield_gc_pure = consider_getfield_gc
 
-    def consider_getarrayitem_gc(self, op, ignored):
+    def consider_getarrayitem_gc(self, op):
         scale, ofs, _ = self._unpack_arraydescr(op.descr)
         base_loc = self.rm.make_sure_var_in_reg(op.args[0], op.args)
         ofs_loc = self.rm.make_sure_var_in_reg(op.args[1], op.args)
@@ -829,29 +860,34 @@
 
     consider_getarrayitem_gc_pure = consider_getarrayitem_gc
 
-
-    def _consider_nullity(self, op, guard_op):
-        # doesn't need a register in arg
+    def consider_int_is_true(self, op, guard_op):
+        # doesn't need arg to be in a register
+        argloc = self.loc(op.args[0])
+        self.rm.possibly_free_var(op.args[0])
         if guard_op is not None:
-            argloc = self.rm.make_sure_var_in_reg(op.args[0])
-            self.rm.possibly_free_var(op.args[0])
             self.perform_with_guard(op, guard_op, [argloc], None)
         else:
-            argloc = self.loc(op.args[0])
-            self.rm.possibly_free_var(op.args[0])
             resloc = self.rm.force_allocate_reg(op.result, need_lower_byte=True)
             self.Perform(op, [argloc], resloc)
 
-    consider_int_is_true = _consider_nullity
+    def consider_bool_not(self, op, guard_op):
+        if guard_op is not None:
+            # doesn't need arg to be in a register
+            argloc = self.loc(op.args[0])
+            self.rm.possibly_free_var(op.args[0])
+            self.perform_with_guard(op, guard_op, [argloc], None)
+        else:
+            self.consider_int_neg(op)
 
-    def consider_same_as(self, op, ignored):
+    def consider_same_as(self, op):
         argloc = self.loc(op.args[0])
         self.possibly_free_var(op.args[0])
         resloc = self.force_allocate_reg(op.result)
         self.Perform(op, [argloc], resloc)
     consider_cast_ptr_to_int = consider_same_as
+    consider_virtual_ref = consider_same_as
 
-    def consider_strlen(self, op, ignored):
+    def consider_strlen(self, op):
         base_loc = self.rm.make_sure_var_in_reg(op.args[0], op.args)
         self.rm.possibly_free_vars(op.args)
         result_loc = self.rm.force_allocate_reg(op.result)
@@ -859,7 +895,7 @@
 
     consider_unicodelen = consider_strlen
 
-    def consider_arraylen_gc(self, op, ignored):
+    def consider_arraylen_gc(self, op):
         arraydescr = op.descr
         assert isinstance(arraydescr, BaseArrayDescr)
         ofs = arraydescr.get_ofs_length(self.translate_support_code)
@@ -868,7 +904,7 @@
         result_loc = self.rm.force_allocate_reg(op.result)
         self.Perform(op, [base_loc, imm(ofs)], result_loc)
 
-    def consider_strgetitem(self, op, ignored):
+    def consider_strgetitem(self, op):
         base_loc = self.rm.make_sure_var_in_reg(op.args[0], op.args)
         ofs_loc = self.rm.make_sure_var_in_reg(op.args[1], op.args)
         self.rm.possibly_free_vars(op.args)
@@ -877,7 +913,7 @@
 
     consider_unicodegetitem = consider_strgetitem
 
-    def consider_jump(self, op, ignored):
+    def consider_jump(self, op):
         assembler = self.assembler
         assert self.jump_target_descr is None
         descr = op.descr
@@ -905,9 +941,12 @@
         self.possibly_free_vars(op.args)
         assembler.closing_jump(self.jump_target_descr)
 
-    def consider_debug_merge_point(self, op, ignored):
+    def consider_debug_merge_point(self, op):
         pass
 
+    def consider_virtual_ref_finish(self, op):
+        self.possibly_free_vars(op.args)
+
     def get_mark_gc_roots(self, gcrootmap):
         shape = gcrootmap.get_basic_shape()
         for v, val in self.fm.frame_bindings.items():
@@ -926,22 +965,37 @@
                     assert reg is eax     # ok to ignore this one
         return gcrootmap.compress_callshape(shape)
 
-    def consider_force_token(self, op, ignored):
+    def consider_force_token(self, op):
         loc = self.rm.force_allocate_reg(op.result)
         self.Perform(op, [], loc)
 
-    def not_implemented_op(self, op, ignored):
+    def not_implemented_op(self, op):
         msg = "[regalloc] Not implemented operation: %s" % op.getopname()
         print msg
         raise NotImplementedError(msg)
 
+    def not_implemented_op_with_guard(self, op, guard_op):
+        msg = "[regalloc] Not implemented operation with guard: %s" % (
+            op.getopname(),)
+        print msg
+        raise NotImplementedError(msg)
+
 oplist = [RegAlloc.not_implemented_op] * rop._LAST
+oplist_with_guard = [RegAlloc.not_implemented_op_with_guard] * rop._LAST
+
+def add_none_argument(fn):
+    return lambda self, op: fn(self, op, None)
 
 for name, value in RegAlloc.__dict__.iteritems():
     if name.startswith('consider_'):
         name = name[len('consider_'):]
         num = getattr(rop, name.upper())
-        oplist[num] = value
+        if (ResOperation(num, [], None).is_comparison()
+            or num == rop.CALL_MAY_FORCE or num == rop.CALL_ASSEMBLER):
+            oplist_with_guard[num] = value
+            oplist[num] = add_none_argument(value)
+        else:
+            oplist[num] = value
 
 def get_ebp_ofs(position):
     # Argument is a frame position (0, 1, 2...).

Modified: pypy/branch/stringbuilder2/pypy/jit/backend/x86/runner.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/backend/x86/runner.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/backend/x86/runner.py	Mon Jan 25 15:25:48 2010
@@ -18,8 +18,8 @@
 
     def __init__(self, rtyper, stats, opts=None, translate_support_code=False,
                  gcdescr=None):
-        AbstractLLCPU.__init__(self, rtyper, stats, opts, translate_support_code,
-                               gcdescr)
+        AbstractLLCPU.__init__(self, rtyper, stats, opts,
+                               translate_support_code, gcdescr)
 
     def setup(self):
         if self.opts is not None:

Modified: pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_gc_integration.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_gc_integration.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_gc_integration.py	Mon Jan 25 15:25:48 2010
@@ -78,7 +78,7 @@
         box = boxes[0]
         regalloc.position = 0
         regalloc.consider_call(ResOperation(rop.CALL, [box], BoxInt(),
-                                            calldescr), None)
+                                            calldescr))
         assert len(regalloc.assembler.movs) == 3
         #
         mark = regalloc.get_mark_gc_roots(cpu.gc_ll_descr.gcrootmap)

Modified: pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_recursive.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_recursive.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_recursive.py	Mon Jan 25 15:25:48 2010
@@ -3,4 +3,6 @@
 from pypy.jit.backend.x86.test.test_basic import Jit386Mixin
 
 class TestRecursive(Jit386Mixin, RecursiveTests):
+    # for the individual tests see
+    # ====> ../../../metainterp/test/test_recursive.py
     pass

Modified: pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_regalloc.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_regalloc.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_regalloc.py	Mon Jan 25 15:25:48 2010
@@ -9,7 +9,7 @@
 from pypy.jit.backend.llsupport.descr import GcCache
 from pypy.jit.backend.x86.runner import CPU
 from pypy.jit.backend.x86.regalloc import RegAlloc, WORD, X86RegisterManager,\
-     BASE_CONSTANT_SIZE
+     FloatConstants
 from pypy.jit.metainterp.test.oparser import parse
 from pypy.rpython.lltypesystem import lltype, llmemory, rffi
 from pypy.rpython.annlowlevel import llhelper
@@ -28,6 +28,7 @@
 
 class MockAssembler(object):
     gcrefs = None
+    _float_constants = None
 
     def __init__(self, cpu=None, gc_ll_descr=None):
         self.movs = []
@@ -503,6 +504,7 @@
 
     def test_float_overflow_const_list(self):
         ops = ['[f0]']
+        BASE_CONSTANT_SIZE = FloatConstants.BASE_CONSTANT_SIZE
         for i in range(BASE_CONSTANT_SIZE * 2):
             ops.append('f%d = float_add(f%d, 3.5)' % (i + 1, i))
         ops.append('finish(f%d)' % (BASE_CONSTANT_SIZE * 2))

Modified: pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_runner.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_runner.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_runner.py	Mon Jan 25 15:25:48 2010
@@ -25,8 +25,8 @@
     # for the individual tests see
     # ====> ../../test/runner_test.py
     
-    def setup_class(cls):
-        cls.cpu = CPU(rtyper=None, stats=FakeStats())
+    def setup_method(self, meth):
+        self.cpu = CPU(rtyper=None, stats=FakeStats())
 
     def test_execute_ptr_operation(self):
         cpu = self.cpu
@@ -72,45 +72,41 @@
         func = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int)(f)
         addr = ctypes.cast(func, ctypes.c_void_p).value
         
-        try:
-            saved_addr = self.cpu.assembler.malloc_func_addr
-            self.cpu.assembler.malloc_func_addr = addr
-            ofs = symbolic.get_field_token(rstr.STR, 'chars', False)[0]
-
-            res = self.execute_operation(rop.NEWSTR, [ConstInt(7)], 'ref')
-            assert allocs[0] == 7 + ofs + WORD
-            resbuf = self._resbuf(res)
-            assert resbuf[ofs/WORD] == 7
-            
-            # ------------------------------------------------------------
-
-            res = self.execute_operation(rop.NEWSTR, [BoxInt(7)], 'ref')
-            assert allocs[0] == 7 + ofs + WORD
-            resbuf = self._resbuf(res)
-            assert resbuf[ofs/WORD] == 7
-
-            # ------------------------------------------------------------
-
-            TP = lltype.GcArray(lltype.Signed)
-            ofs = symbolic.get_field_token(TP, 'length', False)[0]
-            descr = self.cpu.arraydescrof(TP)
-
-            res = self.execute_operation(rop.NEW_ARRAY, [ConstInt(10)],
-                                             'ref', descr)
-            assert allocs[0] == 10*WORD + ofs + WORD
-            resbuf = self._resbuf(res)            
-            assert resbuf[ofs/WORD] == 10
-
-            # ------------------------------------------------------------
-
-            res = self.execute_operation(rop.NEW_ARRAY, [BoxInt(10)],
-                                             'ref', descr)
-            assert allocs[0] == 10*WORD + ofs + WORD
-            resbuf = self._resbuf(res)                        
-            assert resbuf[ofs/WORD] == 10
-            
-        finally:
-            self.cpu.assembler.malloc_func_addr = saved_addr
+        self.cpu.assembler.make_sure_mc_exists()
+        self.cpu.assembler.malloc_func_addr = addr
+        ofs = symbolic.get_field_token(rstr.STR, 'chars', False)[0]
+
+        res = self.execute_operation(rop.NEWSTR, [ConstInt(7)], 'ref')
+        assert allocs[0] == 7 + ofs + WORD
+        resbuf = self._resbuf(res)
+        assert resbuf[ofs/WORD] == 7
+
+        # ------------------------------------------------------------
+
+        res = self.execute_operation(rop.NEWSTR, [BoxInt(7)], 'ref')
+        assert allocs[0] == 7 + ofs + WORD
+        resbuf = self._resbuf(res)
+        assert resbuf[ofs/WORD] == 7
+
+        # ------------------------------------------------------------
+
+        TP = lltype.GcArray(lltype.Signed)
+        ofs = symbolic.get_field_token(TP, 'length', False)[0]
+        descr = self.cpu.arraydescrof(TP)
+
+        res = self.execute_operation(rop.NEW_ARRAY, [ConstInt(10)],
+                                         'ref', descr)
+        assert allocs[0] == 10*WORD + ofs + WORD
+        resbuf = self._resbuf(res)            
+        assert resbuf[ofs/WORD] == 10
+
+        # ------------------------------------------------------------
+
+        res = self.execute_operation(rop.NEW_ARRAY, [BoxInt(10)],
+                                         'ref', descr)
+        assert allocs[0] == 10*WORD + ofs + WORD
+        resbuf = self._resbuf(res)                        
+        assert resbuf[ofs/WORD] == 10
 
     def test_stringitems(self):
         from pypy.rpython.lltypesystem.rstr import STR
@@ -317,9 +313,9 @@
 
 class TestX86OverflowMC(TestX86):
 
-    def setup_class(cls):
-        cls.cpu = CPU(rtyper=None, stats=FakeStats())
-        cls.cpu.assembler.mc_size = 1024
+    def setup_method(self, meth):
+        self.cpu = CPU(rtyper=None, stats=FakeStats())
+        self.cpu.assembler.mc_size = 1024
 
     def test_overflow_mc(self):
         ops = []
@@ -332,6 +328,7 @@
         ops.append(ResOperation(rop.FINISH, [v], None,
                                 descr=BasicFailDescr()))
         looptoken = LoopToken()
+        self.cpu.assembler.make_sure_mc_exists()
         old_mc_mc = self.cpu.assembler.mc._mc
         self.cpu.compile_loop([base_v], ops, looptoken)
         assert self.cpu.assembler.mc._mc != old_mc_mc   # overflowed

Modified: pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_tlc.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_tlc.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_tlc.py	Mon Jan 25 15:25:48 2010
@@ -1,6 +1,5 @@
 
 import py
-py.test.skip("Widening to trash error")
 from pypy.jit.backend.x86.test.test_basic import Jit386Mixin
 from pypy.jit.metainterp.test.test_tlc import TLCTests
 from pypy.jit.tl import tlc
@@ -10,6 +9,7 @@
     # ====> ../../test/test_tlc.py
     
     def test_accumulator(self):
+        py.test.skip("investigate, maybe")
         path = py.path.local(tlc.__file__).dirpath('accumulator.tlc.src')
         code = path.read()
         res = self.exec_code(code, 20)
@@ -18,6 +18,7 @@
         assert res == 10
 
     def test_fib(self):
+        py.test.skip("investigate, maybe")
         path = py.path.local(tlc.__file__).dirpath('fibo.tlc.src')
         code = path.read()
         res = self.exec_code(code, 7)

Modified: pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_ztranslation.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_ztranslation.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/backend/x86/test/test_ztranslation.py	Mon Jan 25 15:25:48 2010
@@ -4,6 +4,7 @@
 from pypy.jit.metainterp.jitprof import Profiler
 from pypy.jit.backend.x86.runner import CPU386
 from pypy.jit.backend.test.support import CCompiledMixin
+from pypy.jit.metainterp.policy import StopAtXPolicy
 
 class TestTranslationX86(CCompiledMixin):
     CPUClass = CPU386
@@ -94,3 +95,52 @@
             return total * 10
         res = self.meta_interp(main, [40])
         assert res == main(40)
+
+    def test_direct_assembler_call_translates(self):
+        class Thing(object):
+            def __init__(self, val):
+                self.val = val
+        
+        class Frame(object):
+            _virtualizable2_ = ['thing']
+        
+        driver = JitDriver(greens = ['codeno'], reds = ['frame', 'i'],
+                           virtualizables = ['frame'],
+                           get_printable_location = lambda codeno : str(codeno),
+                           can_inline = lambda codeno : False)
+        class SomewhereElse(object):
+            pass
+
+        somewhere_else = SomewhereElse()
+
+        def change(newthing):
+            somewhere_else.frame.thing = newthing
+
+        def main(codeno):
+            frame = Frame()
+            somewhere_else.frame = frame
+            frame.thing = Thing(0)
+            portal(codeno, frame)
+            return frame.thing.val
+
+        def portal(codeno, frame):
+            i = 0
+            while i < 10:
+                driver.can_enter_jit(frame=frame, codeno=codeno, i=i)
+                driver.jit_merge_point(frame=frame, codeno=codeno, i=i)
+                nextval = frame.thing.val
+                if codeno == 0:
+                    subframe = Frame()
+                    subframe.thing = Thing(nextval)
+                    nextval = portal(1, subframe)
+                elif frame.thing.val > 40:
+                    change(Thing(13))
+                    nextval = 13
+                frame.thing = Thing(nextval + 1)
+                i += 1
+            return frame.thing.val
+
+        res = self.meta_interp(main, [0], inline=True,
+                               policy=StopAtXPolicy(change))
+        assert res == main(0)
+

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/codewriter.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/codewriter.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/codewriter.py	Mon Jan 25 15:25:48 2010
@@ -11,7 +11,7 @@
 from pypy.tool.udir import udir
 from pypy.translator.simplify import get_funcobj, get_functype
 from pypy.translator.backendopt.canraise import RaiseAnalyzer
-from pypy.translator.backendopt.writeanalyze import WriteAnalyzer
+from pypy.translator.backendopt.writeanalyze import ReadWriteAnalyzer
 from pypy.jit.metainterp.typesystem import deref, arrayItem, fieldType
 from pypy.jit.metainterp.effectinfo import effectinfo_from_writeanalyze
 from pypy.jit.metainterp.effectinfo import VirtualizableAnalyzer
@@ -86,12 +86,22 @@
         if leave_graph is not None:
             todo.append(leave_graph)        
         self.candidate_graphs = seen = set(todo)
+
+        def callers():
+            graph = top_graph
+            print graph
+            while graph in coming_from:
+                graph = coming_from[graph]
+                print '<-', graph
+        coming_from = {}
+
         while todo:
             top_graph = todo.pop()
             for _, op in top_graph.iterblockops():
                 if op.opname not in ("direct_call", "indirect_call", "oosend"):
                     continue
                 kind = self.guess_call_kind(op, is_candidate)
+                # use callers() to view the calling chain in pdb
                 if kind != "regular":
                     continue
                 for graph in self.graphs_from(op, is_candidate):
@@ -100,6 +110,7 @@
                     assert is_candidate(graph)
                     todo.append(graph)
                     seen.add(graph)
+                    coming_from[graph] = top_graph
         return self.candidate_graphs
 
     def graphs_from(self, op, is_candidate=None):
@@ -185,7 +196,7 @@
         self.portal_runner_ptr = portal_runner_ptr
         translator = self.rtyper.annotator.translator
         self.raise_analyzer = RaiseAnalyzer(translator)
-        self.write_analyzer = WriteAnalyzer(translator)
+        self.readwrite_analyzer = ReadWriteAnalyzer(translator)
         self.virtualizable_analyzer = VirtualizableAnalyzer(translator)
 
     def make_portal_bytecode(self, graph):
@@ -326,7 +337,7 @@
         # ok
         if consider_effects_of is not None:
             effectinfo = effectinfo_from_writeanalyze(
-                    self.write_analyzer.analyze(consider_effects_of),
+                    self.readwrite_analyzer.analyze(consider_effects_of),
                     self.cpu,
                     self.virtualizable_analyzer.analyze(consider_effects_of))
             calldescr = self.cpu.calldescrof(FUNC, tuple(NON_VOID_ARGS), RESULT, effectinfo)
@@ -417,7 +428,6 @@
         """Generate a constant of the given value.
         Returns its index in the list self.positions[].
         """
-        if constvalue is _we_are_jitted: constvalue = True
         const = Const._new(constvalue, self.cpu)
         return self.get_position(const)
 
@@ -496,9 +506,22 @@
             linkfalse, linktrue = block.exits
             if linkfalse.llexitcase == True:
                 linkfalse, linktrue = linktrue, linkfalse
+            vpos = self.var_position(block.exitswitch)
+            #
+            # constant-fold an exitswitch
+            cv = self.get_constant_value(vpos)
+            if cv is not None:
+                if cv.value:
+                    link = linktrue
+                else:
+                    link = linkfalse
+                self.emit(*self.insert_renaming(link))
+                self.make_bytecode_block(link.target)
+                return
+            #
             self.emit("goto_if_not",
                       tlabel(linkfalse),
-                      self.var_position(block.exitswitch))
+                      vpos)
             self.minimize_variables(argument_only=True, exitswitch=False)
             truerenaming = self.insert_renaming(linktrue)
             falserenaming = self.insert_renaming(linkfalse)
@@ -818,15 +841,21 @@
             self.serialize_op_same_as(op)
 
     def serialize_op_int_is_true(self, op):
-        if isinstance(op.args[0], Constant):
-            if op.args[0].value is objectmodel.malloc_zero_filled:
+        vpos = self.var_position(op.args[0])
+        cv = self.get_constant_value(vpos)
+        if cv is not None:
+            if cv.value is objectmodel.malloc_zero_filled:
                 # always True for now
                 warmrunnerdesc = self.codewriter.metainterp_sd.warmrunnerdesc
                 if warmrunnerdesc is not None:
                     assert warmrunnerdesc.gcdescr.malloc_zero_filled
                 self.var_positions[op.result] = self.var_position(Constant(1))
                 return
-        self.emit('int_is_true', self.var_position(op.args[0]))
+            if cv.value is _we_are_jitted:
+                # always True
+                self.var_positions[op.result] = self.var_position(Constant(1))
+                return
+        self.emit('int_is_true', vpos)
         self.register_var(op.result)
 
     serialize_op_uint_is_true = serialize_op_int_is_true
@@ -1158,16 +1187,17 @@
                   self.var_position(op.args[3]))
 
     def serialize_op_jit_marker(self, op):
-        if op.args[0].value == 'jit_merge_point':
-            assert self.portal, "jit_merge_point in non-main graph!"
-            self.emit('jit_merge_point')
-            assert ([self.var_position(i) for i in op.args[2:]] ==
-                    range(0, 2*(len(op.args) - 2), 2))
-            #for i in range(2, len(op.args)):
-            #    arg = op.args[i]
-            #    self._eventualy_builtin(arg)
-        elif op.args[0].value == 'can_enter_jit':
-            self.emit('can_enter_jit')
+        key = op.args[0].value
+        getattr(self, 'handle_jit_marker__%s' % key)(op)
+
+    def handle_jit_marker__jit_merge_point(self, op):
+        assert self.portal, "jit_merge_point in non-main graph!"
+        self.emit('jit_merge_point')
+        assert ([self.var_position(i) for i in op.args[2:]] ==
+                range(0, 2*(len(op.args) - 2), 2))
+
+    def handle_jit_marker__can_enter_jit(self, op):
+        self.emit('can_enter_jit')
 
     def serialize_op_direct_call(self, op):
         kind = self.codewriter.guess_call_kind(op)
@@ -1205,21 +1235,26 @@
         calldescr, non_void_args = self.codewriter.getcalldescr(
             op.args[0], args, op.result, consider_effects_of=op)
         pure = False
+        loopinvariant = False
         if op.opname == "direct_call":
             func = getattr(get_funcobj(op.args[0].value), '_callable', None)
             pure = getattr(func, "_pure_function_", False)
+            loopinvariant = getattr(func, "_jit_loop_invariant_", False)
             all_promoted_args = getattr(func,
                                "_pure_function_with_all_promoted_args_", False)
             if pure and not all_promoted_args:
                 effectinfo = calldescr.get_extra_info()
                 assert (effectinfo is not None and
-                        not effectinfo.promotes_virtualizables)
+                        not effectinfo.forces_virtual_or_virtualizable)
         try:
             canraise = self.codewriter.raise_analyzer.can_raise(op)
         except lltype.DelayedPointer:
             canraise = True  # if we need to look into the delayed ptr that is
                              # the portal, then it's certainly going to raise
-        if pure:
+        if loopinvariant:
+            self.emit("residual_call_loopinvariant")
+            assert not non_void_args, "arguments not supported for loop-invariant function!"
+        elif pure:
             # XXX check what to do about exceptions (also MemoryError?)
             self.emit('residual_call_pure')
         elif canraise:
@@ -1279,6 +1314,9 @@
         return self._do_builtin_call(op, oopspec_name, args)
 
     def _do_builtin_call(self, op, oopspec_name, args):
+        if oopspec_name.startswith('virtual_ref'):
+            self.handle_virtual_ref_call(op, oopspec_name, args)
+            return
         argtypes = [v.concretetype for v in args]
         resulttype = op.result.concretetype
         c_func, TP = support.builtin_func_for_spec(self.codewriter.rtyper,
@@ -1299,6 +1337,15 @@
         self.emit_varargs([c_func] + non_void_args)
         self.register_var(op.result)
 
+    def handle_virtual_ref_call(self, op, oopspec_name, args):
+        self.emit(oopspec_name)     # 'virtual_ref' or 'virtual_ref_finish'
+        self.emit(self.var_position(args[0]))
+        self.register_var(op.result)
+        #
+        vrefinfo = self.codewriter.metainterp_sd.virtualref_info
+        self.codewriter.register_known_gctype(vrefinfo.jit_virtual_ref_vtable,
+                                              vrefinfo.JIT_VIRTUAL_REF)
+
     def _array_of_voids(self, ARRAY):
         if isinstance(ARRAY, ootype.Array):
             return ARRAY.ITEM == ootype.Void
@@ -1557,12 +1604,15 @@
         log.WARNING("found debug_assert in %r; should have be removed" %
                     (self.graph,))
 
-    def serialize_op_promote_virtualizable(self, op):
+    def serialize_op_jit_force_virtualizable(self, op):
         vinfo = self.codewriter.metainterp_sd.virtualizable_info
         assert vinfo is not None
         assert vinfo.is_vtypeptr(op.args[0].concretetype)
         self.vable_flags[op.args[0]] = op.args[2].value
 
+    def serialize_op_jit_force_virtual(self, op):
+        self._do_builtin_call(op, 'jit_force_virtual', op.args)
+
     serialize_op_oostring  = handle_builtin_call
     serialize_op_oounicode = handle_builtin_call
     serialize_op_gc_identityhash = handle_builtin_call
@@ -1596,6 +1646,14 @@
                     raise VirtualizableArrayField(self.graph)
                 raise
 
+    def get_constant_value(self, vpos):
+        """Reverse of var_position().  Returns either None or a Constant."""
+        if vpos & 1:
+            value = self.constants[vpos // 2].value
+            return Constant(value)
+        else:
+            return None
+
     def emit(self, *stuff):
         self.assembler.extend(stuff)
 

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/compile.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/compile.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/compile.py	Mon Jan 25 15:25:48 2010
@@ -54,9 +54,11 @@
     for box in loop.inputargs:
         assert isinstance(box, Box)
     if start > 0:
-        loop.operations = history.operations[start:]
+        ops = history.operations[start:]
     else:
-        loop.operations = history.operations
+        ops = history.operations
+    # make a copy, because optimize_loop can mutate the ops and descrs
+    loop.operations = [op.clone() for op in ops]
     metainterp_sd = metainterp.staticdata
     loop_token = make_loop_token(len(loop.inputargs))
     loop.token = loop_token
@@ -203,10 +205,18 @@
 class ResumeDescr(AbstractFailDescr):
     def __init__(self, original_greenkey):
         self.original_greenkey = original_greenkey
+    def _clone_if_mutable(self):
+        raise NotImplementedError
 
 class ResumeGuardDescr(ResumeDescr):
     counter = 0
-    # this class also gets attributes stored by resume.py code
+    # this class also gets the following attributes stored by resume.py code
+    rd_snapshot = None
+    rd_frame_info_list = None
+    rd_numb = None
+    rd_consts = None
+    rd_virtuals = None
+    rd_pendingfields = None
 
     def __init__(self, metainterp_sd, original_greenkey):
         ResumeDescr.__init__(self, original_greenkey)
@@ -231,29 +241,67 @@
                                new_loop.operations)
 
 
+    def _clone_if_mutable(self):
+        res = self.__class__(self.metainterp_sd, self.original_greenkey)
+        # XXX a bit ugly to have to list them all here
+        res.rd_snapshot = self.rd_snapshot
+        res.rd_frame_info_list = self.rd_frame_info_list
+        res.rd_numb = self.rd_numb
+        res.rd_consts = self.rd_consts
+        res.rd_virtuals = self.rd_virtuals
+        res.rd_pendingfields = self.rd_pendingfields
+        return res
+
 class ResumeGuardForcedDescr(ResumeGuardDescr):
 
     def handle_fail(self, metainterp_sd):
         from pypy.jit.metainterp.pyjitpl import MetaInterp
         metainterp = MetaInterp(metainterp_sd)
         token = metainterp_sd.cpu.get_latest_force_token()
-        data = self.fetch_data(token)
-        if data is None:
-            data = []
-        metainterp._already_allocated_resume_virtuals = data
+        all_virtuals = self.fetch_data(token)
+        if all_virtuals is None:
+            all_virtuals = []
+        metainterp._already_allocated_resume_virtuals = all_virtuals
         self.counter = -2     # never compile
         return metainterp.handle_guard_failure(self)
 
-    def force_virtualizable(self, vinfo, virtualizable, force_token):
+    @staticmethod
+    def force_now(cpu, token):
+        # Called during a residual call from the assembler, if the code
+        # actually needs to force one of the virtualrefs or the virtualizable.
+        # Implemented by forcing *all* virtualrefs and the virtualizable.
+        faildescr = cpu.force(token)
+        assert isinstance(faildescr, ResumeGuardForcedDescr)
+        faildescr.handle_async_forcing(token)
+
+    def handle_async_forcing(self, force_token):
         from pypy.jit.metainterp.pyjitpl import MetaInterp
         from pypy.jit.metainterp.resume import force_from_resumedata
-        metainterp = MetaInterp(self.metainterp_sd)
+        # To handle the forcing itself, we create a temporary MetaInterp
+        # as a convenience to move the various data to its proper place.
+        metainterp_sd = self.metainterp_sd
+        metainterp = MetaInterp(metainterp_sd)
         metainterp.history = None    # blackholing
-        liveboxes = metainterp.cpu.make_boxes_from_latest_values(self)
-        virtualizable_boxes, data = force_from_resumedata(metainterp,
-                                                          liveboxes, self)
-        vinfo.write_boxes(virtualizable, virtualizable_boxes)
-        self.save_data(force_token, data)
+        liveboxes = metainterp_sd.cpu.make_boxes_from_latest_values(self)
+        #
+        expect_virtualizable = metainterp_sd.virtualizable_info is not None
+        forced_data = force_from_resumedata(metainterp, liveboxes, self,
+                                            expect_virtualizable)
+        virtualizable_boxes, virtualref_boxes, all_virtuals = forced_data
+        #
+        # Handle virtualref_boxes: mark each JIT_VIRTUAL_REF as forced
+        vrefinfo = metainterp_sd.virtualref_info
+        for i in range(0, len(virtualref_boxes), 2):
+            virtualbox = virtualref_boxes[i]
+            vrefbox = virtualref_boxes[i+1]
+            vrefinfo.forced_single_vref(vrefbox.getref_base(),
+                                        virtualbox.getref_base())
+        # Handle virtualizable_boxes: store them on the real virtualizable now
+        if expect_virtualizable:
+            metainterp_sd.virtualizable_info.forced_vable(virtualizable_boxes)
+        # Handle all_virtuals: keep them for later blackholing from the
+        # future failure of the GUARD_NOT_FORCED
+        self.save_data(force_token, all_virtuals)
 
     def save_data(self, key, value):
         globaldata = self.metainterp_sd.globaldata
@@ -309,13 +357,16 @@
     # it does not work -- i.e. none of the existing old_loop_tokens match.
     new_loop = create_empty_loop(metainterp)
     new_loop.inputargs = metainterp.history.inputargs
-    new_loop.operations = metainterp.history.operations
+    # clone ops, as optimize_bridge can mutate the ops
+    new_loop.operations = [op.clone() for op in metainterp.history.operations]
     metainterp_sd = metainterp.staticdata
     try:
         target_loop_token = metainterp_sd.state.optimize_bridge(metainterp_sd,
                                                                 old_loop_tokens,
                                                                 new_loop)
     except InvalidLoop:
+        # XXX I am fairly convinced that optimize_bridge cannot actually raise
+        # InvalidLoop
         return None
     # Did it work?
     if target_loop_token is not None:

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/effectinfo.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/effectinfo.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/effectinfo.py	Mon Jan 25 15:25:48 2010
@@ -7,44 +7,62 @@
 class EffectInfo(object):
     _cache = {}
 
-    def __new__(cls, write_descrs_fields, write_descrs_arrays,
-                promotes_virtualizables=False):
-        key = (frozenset(write_descrs_fields), frozenset(write_descrs_arrays),
-               promotes_virtualizables)
+    def __new__(cls, readonly_descrs_fields,
+                write_descrs_fields, write_descrs_arrays,
+                forces_virtual_or_virtualizable=False):
+        key = (frozenset(readonly_descrs_fields),
+               frozenset(write_descrs_fields),
+               frozenset(write_descrs_arrays),
+               forces_virtual_or_virtualizable)
         if key in cls._cache:
             return cls._cache[key]
         result = object.__new__(cls)
+        result.readonly_descrs_fields = readonly_descrs_fields
         result.write_descrs_fields = write_descrs_fields
         result.write_descrs_arrays = write_descrs_arrays
-        result.promotes_virtualizables = promotes_virtualizables
+        result.forces_virtual_or_virtualizable= forces_virtual_or_virtualizable
         cls._cache[key] = result
         return result
 
-def effectinfo_from_writeanalyze(effects, cpu, promotes_virtualizables=False):
+def effectinfo_from_writeanalyze(effects, cpu,
+                                 forces_virtual_or_virtualizable=False):
     from pypy.translator.backendopt.writeanalyze import top_set
     if effects is top_set:
         return None
+    readonly_descrs_fields = []
+    # readonly_descrs_arrays = [] --- not enabled for now
     write_descrs_fields = []
     write_descrs_arrays = []
+
+    def add_struct(descrs_fields, (_, T, fieldname)):
+        T = deref(T)
+        if consider_struct(T, fieldname):
+            descr = cpu.fielddescrof(T, fieldname)
+            descrs_fields.append(descr)
+
+    def add_array(descrs_arrays, (_, T)):
+        ARRAY = deref(T)
+        if consider_array(ARRAY):
+            descr = cpu.arraydescrof(ARRAY)
+            descrs_arrays.append(descr)
+
     for tup in effects:
         if tup[0] == "struct":
-            _, T, fieldname = tup
-            T = deref(T)
-            if not consider_struct(T, fieldname):
-                continue
-            descr = cpu.fielddescrof(T, fieldname)
-            write_descrs_fields.append(descr)
+            add_struct(write_descrs_fields, tup)
+        elif tup[0] == "readstruct":
+            tupw = ("struct",) + tup[1:]
+            if tupw not in effects:
+                add_struct(readonly_descrs_fields, tup)
         elif tup[0] == "array":
-            _, T = tup
-            ARRAY = deref(T)
-            if not consider_array(ARRAY):
-                continue
-            descr = cpu.arraydescrof(ARRAY)
-            write_descrs_arrays.append(descr)
+            add_array(write_descrs_arrays, tup)
+        elif tup[0] == "readarray":
+            pass
         else:
             assert 0
-    return EffectInfo(write_descrs_fields, write_descrs_arrays,
-                      promotes_virtualizables)
+    return EffectInfo(readonly_descrs_fields,
+                      write_descrs_fields,
+                      write_descrs_arrays,
+                      forces_virtual_or_virtualizable)
 
 def consider_struct(TYPE, fieldname):
     if fieldType(TYPE, fieldname) is lltype.Void:
@@ -73,4 +91,5 @@
 
 class VirtualizableAnalyzer(BoolGraphAnalyzer):
     def analyze_simple_operation(self, op):
-        return op.opname == 'promote_virtualizable'
+        return op.opname in ('jit_force_virtualizable',
+                             'jit_force_virtual')

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/executor.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/executor.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/executor.py	Mon Jan 25 15:25:48 2010
@@ -6,7 +6,7 @@
 from pypy.rpython.ootypesystem import ootype
 from pypy.rpython.lltypesystem.lloperation import llop
 from pypy.rlib.rarithmetic import ovfcheck, r_uint, intmask
-from pypy.jit.metainterp.history import BoxInt, ConstInt, check_descr
+from pypy.jit.metainterp.history import BoxInt, BoxPtr, ConstInt, check_descr
 from pypy.jit.metainterp.history import INT, REF, ConstFloat
 from pypy.jit.metainterp import resoperation
 from pypy.jit.metainterp.resoperation import rop
@@ -220,6 +220,15 @@
 
 # ____________________________________________________________
 
+def do_force_token(cpu):
+    raise NotImplementedError
+
+def do_virtual_ref(cpu, box1, box2):
+    raise NotImplementedError
+
+def do_virtual_ref_finish(cpu, box1, box2):
+    raise NotImplementedError
+
 def do_debug_merge_point(cpu, box1):
     from pypy.jit.metainterp.warmspot import get_stats
     loc = box1._get_str()

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/history.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/history.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/history.py	Mon Jan 25 15:25:48 2010
@@ -122,6 +122,9 @@
     def repr_of_descr(self):
         return '%r' % (self,)
 
+    def _clone_if_mutable(self):
+        return self
+
 class AbstractFailDescr(AbstractDescr):
     index = -1
 
@@ -802,15 +805,30 @@
 
 
 class History(object):
-    def __init__(self, cpu):
-        self.cpu = cpu
+    def __init__(self):
         self.inputargs = None
         self.operations = []
+
     def record(self, opnum, argboxes, resbox, descr=None):
         op = ResOperation(opnum, argboxes, resbox, descr)
         self.operations.append(op)
         return op
 
+    def substitute_operation(self, position, opnum, argboxes, descr=None):
+        resbox = self.operations[position].result
+        op = ResOperation(opnum, argboxes, resbox, descr)
+        self.operations[position] = op
+
+    def slice_history_at(self, position):
+        """ a strange function that does this:
+        history : operation_at_position : rest
+        it'll kill operation_at_position, store everything before that
+        in history.operations and return rest
+        """
+        rest = self.operations[position + 1:]
+        del self.operations[position:]
+        return rest
+
 # ____________________________________________________________
 
 
@@ -926,10 +944,6 @@
             loops.append(loop)
         display_loops(loops, errmsg, extraloops)
 
-
-class CrashInJIT(Exception):
-    pass
-
 # ----------------------------------------------------------------
 
 class Options:

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/jitprof.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/jitprof.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/jitprof.py	Mon Jan 25 15:25:48 2010
@@ -20,6 +20,7 @@
 OPT_FORCINGS
 ABORT_TOO_LONG
 ABORT_BRIDGE
+ABORT_ESCAPE
 NVIRTUALS
 NVHOLES
 NVREUSED
@@ -176,8 +177,9 @@
         self._print_intline("opt ops", cnt[OPT_OPS])
         self._print_intline("opt guards", cnt[OPT_GUARDS])
         self._print_intline("forcings", cnt[OPT_FORCINGS])
-        self._print_intline("trace too long", cnt[ABORT_TOO_LONG])
-        self._print_intline("bridge abort", cnt[ABORT_BRIDGE])
+        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("nvirtuals", cnt[NVIRTUALS])
         self._print_intline("nvholes", cnt[NVHOLES])
         self._print_intline("nvreused", cnt[NVREUSED])

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/optimizeopt.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/optimizeopt.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/optimizeopt.py	Mon Jan 25 15:25:48 2010
@@ -11,7 +11,7 @@
 from pypy.jit.metainterp.specnode import VirtualStructSpecNode
 from pypy.jit.metainterp.optimizeutil import _findall, sort_descrs
 from pypy.jit.metainterp.optimizeutil import descrlist_dict
-from pypy.jit.metainterp.optimizeutil import InvalidLoop
+from pypy.jit.metainterp.optimizeutil import InvalidLoop, args_dict
 from pypy.jit.metainterp import resume, compile
 from pypy.jit.metainterp.typesystem import llhelper, oohelper
 from pypy.rlib.objectmodel import we_are_translated
@@ -161,18 +161,18 @@
         return self.box
 
     def make_virtual_info(self, modifier, fieldnums):
-        vinfo = self._cached_vinfo 
-        if vinfo is not None and resume.tagged_list_eq(
-                vinfo.fieldnums, fieldnums):
+        vinfo = self._cached_vinfo
+        if vinfo is not None and vinfo.equals(fieldnums):
             return vinfo
         vinfo = self._make_virtual(modifier)
-        vinfo.fieldnums = fieldnums
+        vinfo.set_content(fieldnums)
         self._cached_vinfo = vinfo
         return vinfo
 
     def _make_virtual(self, modifier):
         raise NotImplementedError("abstract base")
 
+
 def get_fielddescrlist_cache(cpu):
     if not hasattr(cpu, '_optimizeopt_fielddescrlist_cache'):
         result = descrlist_dict()
@@ -207,6 +207,8 @@
             iteritems = list(iteritems)
             iteritems.sort(key = lambda (x,y): x.sort_key())
         for ofs, value in iteritems:
+            if value.is_null():
+                continue
             subbox = value.force_box()
             op = ResOperation(rop.SETFIELD_GC, [box, subbox], None,
                               descr=ofs)
@@ -234,7 +236,6 @@
 
     def get_args_for_fail(self, modifier):
         if self.box is None and not modifier.already_seen_virtual(self.keybox):
-            # modifier.already_seen_virtual()
             # checks for recursion: it is False unless
             # we have already seen the very same keybox
             lst = self._get_field_descr_list()
@@ -294,6 +295,8 @@
         for index in range(len(self._items)):
             subvalue = self._items[index]
             if subvalue is not self.constvalue:
+                if subvalue.is_null():
+                    continue
                 subbox = subvalue.force_box()
                 op = ResOperation(rop.SETARRAYITEM_GC,
                                   [box, ConstInt(index), subbox], None,
@@ -302,11 +305,9 @@
 
     def get_args_for_fail(self, modifier):
         if self.box is None and not modifier.already_seen_virtual(self.keybox):
-            # modifier.already_seen_virtual()
             # checks for recursion: it is False unless
             # we have already seen the very same keybox
             itemboxes = []
-            const = self.optimizer.new_const_item(self.arraydescr)
             for itemvalue in self._items:
                 itemboxes.append(itemvalue.get_key_box())
             modifier.register_virtual_fields(self.keybox, itemboxes)
@@ -387,6 +388,8 @@
         self.resumedata_memo = resume.ResumeDataLoopMemo(metainterp_sd)
         self.heap_op_optimizer = HeapOpOptimizer(self)
         self.bool_boxes = {}
+        self.loop_invariant_results = {}
+        self.pure_operations = args_dict()
 
     def forget_numberings(self, virtualbox):
         self.metainterp_sd.profiler.count(jitprof.OPT_FORCINGS)
@@ -513,17 +516,16 @@
         # accumulate counters
         self.resumedata_memo.update_counters(self.metainterp_sd.profiler)
 
-    def emit_operation(self, op, must_clone=True):
+    def emit_operation(self, op):
         self.heap_op_optimizer.emitting_operation(op)
+        self._emit_operation(op)
+
+    def _emit_operation(self, op):
         for i in range(len(op.args)):
             arg = op.args[i]
             if arg in self.values:
                 box = self.values[arg].force_box()
-                if box is not arg:
-                    if must_clone:
-                        op = op.clone()
-                        must_clone = False
-                    op.args[i] = box
+                op.args[i] = box
         self.metainterp_sd.profiler.count(jitprof.OPT_OPS)
         if op.is_guard():
             self.metainterp_sd.profiler.count(jitprof.OPT_GUARDS)
@@ -535,10 +537,11 @@
         self.newoperations.append(op)
 
     def store_final_boxes_in_guard(self, op):
+        pendingfields = self.heap_op_optimizer.force_lazy_setfields_for_guard()
         descr = op.descr
         assert isinstance(descr, compile.ResumeGuardDescr)
         modifier = resume.ResumeDataVirtualAdder(descr, self.resumedata_memo)
-        newboxes = modifier.finish(self.values)
+        newboxes = modifier.finish(self.values, pendingfields)
         if len(newboxes) > self.metainterp_sd.options.failargs_limit: # XXX be careful here
             raise compile.GiveUp
         descr.store_final_boxes(op, newboxes)
@@ -569,6 +572,23 @@
                 resbox = execute_nonspec(self.cpu, op.opnum, argboxes, op.descr)
                 self.make_constant(op.result, resbox.constbox())
                 return
+
+            if op.descr is None:
+                # did we do the exact same operation already?
+                args = op.args[:]
+                for i in range(len(args)):
+                    arg = args[i]
+                    if arg in self.values:
+                        args[i] = self.values[arg].get_key_box()
+                args.append(ConstInt(op.opnum))
+                oldop = self.pure_operations.get(args, None)
+                if oldop is not None:
+                    assert oldop.opnum == op.opnum
+                    self.make_equal_to(op.result, self.getvalue(oldop.result))
+                    return
+                else:
+                    self.pure_operations[args] = op
+
         # otherwise, the operation remains
         self.emit_operation(op)
 
@@ -582,9 +602,8 @@
         for i in range(len(specnodes)):
             value = self.getvalue(op.args[i])
             specnodes[i].teardown_virtual_node(self, value, exitargs)
-        op2 = op.clone()
-        op2.args = exitargs[:]
-        self.emit_operation(op2, must_clone=False)
+        op.args = exitargs[:]
+        self.emit_operation(op)
 
     def optimize_guard(self, op, constbox, emit_operation=True):
         value = self.getvalue(op.args[0])
@@ -699,7 +718,7 @@
         elif value.is_null():
             self.make_constant_int(op.result, not expect_nonnull)
         else:
-            self.emit_operation(op)
+            self.optimize_default(op)
 
     def optimize_INT_IS_TRUE(self, op):
         self._optimize_nullness(op, op.args[0], True)
@@ -736,6 +755,49 @@
     def optimize_OOIS(self, op):
         self._optimize_oois_ooisnot(op, False)
 
+    def optimize_VIRTUAL_REF(self, op):
+        indexbox = op.args[1]
+        #
+        # get some constants
+        vrefinfo = self.metainterp_sd.virtualref_info
+        c_cls = vrefinfo.jit_virtual_ref_const_class
+        descr_virtual_token = vrefinfo.descr_virtual_token
+        descr_virtualref_index = vrefinfo.descr_virtualref_index
+        #
+        # Replace the VIRTUAL_REF operation with a virtual structure of type
+        # 'jit_virtual_ref'.  The jit_virtual_ref structure may be forced soon,
+        # but the point is that doing so does not force the original structure.
+        op = ResOperation(rop.NEW_WITH_VTABLE, [c_cls], op.result)
+        vrefvalue = self.make_virtual(c_cls, op.result, op)
+        tokenbox = BoxInt()
+        self.emit_operation(ResOperation(rop.FORCE_TOKEN, [], tokenbox))
+        vrefvalue.setfield(descr_virtual_token, self.getvalue(tokenbox))
+        vrefvalue.setfield(descr_virtualref_index, self.getvalue(indexbox))
+
+    def optimize_VIRTUAL_REF_FINISH(self, op):
+        # Set the 'forced' field of the virtual_ref.
+        # In good cases, this is all virtual, so has no effect.
+        # Otherwise, this forces the real object -- but only now, as
+        # opposed to much earlier.  This is important because the object is
+        # typically a PyPy PyFrame, and now is the end of its execution, so
+        # forcing it now does not have catastrophic effects.
+        vrefinfo = self.metainterp_sd.virtualref_info
+        # - set 'forced' to point to the real object
+        op1 = ResOperation(rop.SETFIELD_GC, op.args, None,
+                          descr = vrefinfo.descr_forced)
+        self.optimize_SETFIELD_GC(op1)
+        # - set 'virtual_token' to TOKEN_NONE
+        args = [op.args[0], ConstInt(0)]
+        op1 = ResOperation(rop.SETFIELD_GC, args, None,
+                      descr = vrefinfo.descr_virtual_token)
+        self.optimize_SETFIELD_GC(op1)
+        # Note that in some cases the virtual in op.args[1] has been forced
+        # already.  This is fine.  In that case, and *if* a residual
+        # CALL_MAY_FORCE suddenly turns out to access it, then it will
+        # trigger a ResumeGuardForcedDescr.handle_async_forcing() which
+        # will work too (but just be a little pointless, as the structure
+        # was already forced).
+
     def optimize_GETFIELD_GC(self, op):
         value = self.getvalue(op.args[0])
         if value.is_virtual():
@@ -753,11 +815,11 @@
 
     def optimize_SETFIELD_GC(self, op):
         value = self.getvalue(op.args[0])
+        fieldvalue = self.getvalue(op.args[1])
         if value.is_virtual():
-            value.setfield(op.descr, self.getvalue(op.args[1]))
+            value.setfield(op.descr, fieldvalue)
         else:
             value.ensure_nonnull()
-            fieldvalue = self.getvalue(op.args[1])
             self.heap_op_optimizer.optimize_SETFIELD_GC(op, value, fieldvalue)
 
     def optimize_NEW_WITH_VTABLE(self, op):
@@ -826,6 +888,23 @@
     def optimize_DEBUG_MERGE_POINT(self, op):
         self.emit_operation(op)
 
+    def optimize_CALL_LOOPINVARIANT(self, op):
+        funcvalue = self.getvalue(op.args[0])
+        if not funcvalue.is_constant():
+            self.optimize_default(op)
+            return
+        resvalue = self.loop_invariant_results.get(op.args[0].getint(), None)
+        if resvalue is not None:
+            self.make_equal_to(op.result, resvalue)
+            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
+        op.opnum = rop.CALL
+        self.optimize_default(op)
+        resvalue = self.getvalue(op.result)
+        self.loop_invariant_results[op.args[0].getint()] = resvalue
+            
+
 optimize_ops = _findall(Optimizer, 'optimize_')
 
 
@@ -839,11 +918,13 @@
 class HeapOpOptimizer(object):
     def __init__(self, optimizer):
         self.optimizer = optimizer
-        # cached OptValues for each field descr
+        # cached fields:  {descr: {OptValue_instance: OptValue_fieldvalue}}
         self.cached_fields = {}
-
-        # cached OptValues for each field descr
+        # cached array items:  {descr: CachedArrayItems}
         self.cached_arrayitems = {}
+        # lazily written setfields (at most one per descr):  {descr: op}
+        self.lazy_setfields = {}
+        self.lazy_setfields_descrs = []     # keys (at least) of previous dict
 
     def clean_caches(self):
         self.cached_fields.clear()
@@ -851,15 +932,23 @@
 
     def cache_field_value(self, descr, value, fieldvalue, write=False):
         if write:
+            # when seeing a setfield, we have to clear the cache for the same
+            # field on any other structure, just in case they are aliasing
+            # each other
             d = self.cached_fields[descr] = {}
         else:
             d = self.cached_fields.setdefault(descr, {})
         d[value] = fieldvalue
 
     def read_cached_field(self, descr, value):
+        # XXX self.cached_fields and self.lazy_setfields should probably
+        # be merged somehow
         d = self.cached_fields.get(descr, None)
         if d is None:
-            return None
+            op = self.lazy_setfields.get(descr, None)
+            if op is None:
+                return None
+            return self.optimizer.getvalue(op.args[1])
         return d.get(value, None)
 
     def cache_arrayitem_value(self, descr, value, indexvalue, fieldvalue, write=False):
@@ -908,8 +997,6 @@
         return None
 
     def emitting_operation(self, op):
-        if op.is_always_pure():
-            return
         if op.has_no_side_effect():
             return
         if op.is_ovf():
@@ -921,10 +1008,20 @@
             opnum == rop.SETARRAYITEM_GC or
             opnum == rop.DEBUG_MERGE_POINT):
             return
-        if opnum == rop.CALL:
-            effectinfo = op.descr.get_extra_info()
+        if (opnum == rop.CALL or
+            opnum == rop.CALL_MAY_FORCE or
+            opnum == rop.CALL_ASSEMBLER):
+            if opnum == rop.CALL_ASSEMBLER:
+                effectinfo = None
+            else:
+                effectinfo = op.descr.get_extra_info()
             if effectinfo is not None:
+                # XXX we can get the wrong complexity here, if the lists
+                # XXX stored on effectinfo are large
+                for fielddescr in effectinfo.readonly_descrs_fields:
+                    self.force_lazy_setfield(fielddescr)
                 for fielddescr in effectinfo.write_descrs_fields:
+                    self.force_lazy_setfield(fielddescr)
                     try:
                         del self.cached_fields[fielddescr]
                     except KeyError:
@@ -935,9 +1032,73 @@
                     except KeyError:
                         pass
                 return
+            self.force_all_lazy_setfields()
+        elif op.is_final() or (not we_are_translated() and
+                               op.opnum < 0):   # escape() operations
+            self.force_all_lazy_setfields()
         self.clean_caches()
 
+    def force_lazy_setfield(self, descr, before_guard=False):
+        try:
+            op = self.lazy_setfields[descr]
+        except KeyError:
+            return
+        del self.lazy_setfields[descr]
+        self.optimizer._emit_operation(op)
+        #
+        # hackish: reverse the order of the last two operations if it makes
+        # sense to avoid a situation like "int_eq/setfield_gc/guard_true",
+        # which the backend (at least the x86 backend) does not handle well.
+        newoperations = self.optimizer.newoperations
+        if before_guard and len(newoperations) >= 2:
+            lastop = newoperations[-1]
+            prevop = newoperations[-2]
+            # - is_comparison() for cases like "int_eq/setfield_gc/guard_true"
+            # - CALL_MAY_FORCE: "call_may_force/setfield_gc/guard_not_forced"
+            if ((prevop.is_comparison() or prevop.opnum == rop.CALL_MAY_FORCE)
+                and prevop.result not in lastop.args):
+                newoperations[-2] = lastop
+                newoperations[-1] = prevop
+
+    def force_all_lazy_setfields(self):
+        if len(self.lazy_setfields_descrs) > 0:
+            for descr in self.lazy_setfields_descrs:
+                self.force_lazy_setfield(descr)
+            del self.lazy_setfields_descrs[:]
+
+    def force_lazy_setfields_for_guard(self):
+        pendingfields = []
+        for descr in self.lazy_setfields_descrs:
+            try:
+                op = self.lazy_setfields[descr]
+            except KeyError:
+                continue
+            # the only really interesting case that we need to handle in the
+            # guards' resume data is that of a virtual object that is stored
+            # into a field of a non-virtual object.
+            value = self.optimizer.getvalue(op.args[0])
+            assert not value.is_virtual()      # it must be a non-virtual
+            fieldvalue = self.optimizer.getvalue(op.args[1])
+            if fieldvalue.is_virtual():
+                # this is the case that we leave to resume.py
+                pendingfields.append((descr, value.box,
+                                      fieldvalue.get_key_box()))
+            else:
+                self.force_lazy_setfield(descr, before_guard=True)
+        return pendingfields
+
+    def force_lazy_setfield_if_necessary(self, op, value, write=False):
+        try:
+            op1 = self.lazy_setfields[op.descr]
+        except KeyError:
+            if write:
+                self.lazy_setfields_descrs.append(op.descr)
+        else:
+            if self.optimizer.getvalue(op1.args[0]) is not value:
+                self.force_lazy_setfield(op.descr)
+
     def optimize_GETFIELD_GC(self, op, value):
+        self.force_lazy_setfield_if_necessary(op, value)
         # check if the field was read from another getfield_gc just before
         # or has been written to recently
         fieldvalue = self.read_cached_field(op.descr, value)
@@ -952,7 +1113,8 @@
         self.cache_field_value(op.descr, value, fieldvalue)
 
     def optimize_SETFIELD_GC(self, op, value, fieldvalue):
-        self.optimizer.emit_operation(op)
+        self.force_lazy_setfield_if_necessary(op, value, write=True)
+        self.lazy_setfields[op.descr] = op
         # remember the result of future reads of the field
         self.cache_field_value(op.descr, value, fieldvalue, write=True)
 

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/optimizeutil.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/optimizeutil.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/optimizeutil.py	Mon Jan 25 15:25:48 2010
@@ -1,7 +1,7 @@
 from pypy.rlib.objectmodel import r_dict, compute_identity_hash
 from pypy.rlib.rarithmetic import intmask
 from pypy.rlib.unroll import unrolling_iterable
-from pypy.jit.metainterp import resoperation
+from pypy.jit.metainterp import resoperation, history
 
 class InvalidLoop(Exception):
     """Raised when the optimize*.py detect that the loop that
@@ -59,3 +59,35 @@
 def descrlist_dict():
     return r_dict(descrlist_eq, descrlist_hash)
 
+# ____________________________________________________________
+
+def args_eq(args1, args2):
+    if len(args1) != len(args2):
+        return False
+    for i in range(len(args1)):
+        arg1 = args1[i]
+        arg2 = args2[i]
+        if isinstance(arg1, history.Const):
+            if arg1.__class__ is not arg2.__class__:
+                return False
+            if not arg1.same_constant(arg2):
+                return False
+        else:
+            if not arg1 is arg2:
+                return False
+    return True
+
+def args_hash(args):
+    res = 0x345678
+    for arg in args:
+        if isinstance(arg, history.Const):
+            y = arg._get_hash_()
+        else:
+            y = compute_identity_hash(arg)
+        res = intmask((1000003 * res) ^ y)
+    return res
+
+def args_dict():
+    return r_dict(args_eq, args_hash)
+
+

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/policy.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/policy.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/policy.py	Mon Jan 25 15:25:48 2010
@@ -49,8 +49,6 @@
     def look_inside_graph(self, graph):
         from pypy.translator.backendopt.support import find_backedges
         contains_loop = bool(find_backedges(graph))
-        unsupported = contains_unsupported_variable_type(graph,
-                                                         self.supports_floats)
         try:
             func = graph.func
         except AttributeError:
@@ -61,7 +59,8 @@
             contains_loop = contains_loop and not getattr(
                     func, '_jit_unroll_safe_', False)
 
-        res = see_function and not unsupported
+        res = see_function and not contains_unsupported_variable_type(graph,
+                                                         self.supports_floats)
         if res and contains_loop:
             self.unsafe_loopy_graphs.add(graph)
         return res and not contains_loop

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/pyjitpl.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/pyjitpl.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/pyjitpl.py	Mon Jan 25 15:25:48 2010
@@ -11,7 +11,7 @@
 from pypy.jit.metainterp import codewriter, executor
 from pypy.jit.metainterp.logger import Logger
 from pypy.jit.metainterp.jitprof import BLACKHOLED_OPS, EmptyProfiler
-from pypy.jit.metainterp.jitprof import GUARDS, RECORDED_OPS
+from pypy.jit.metainterp.jitprof import GUARDS, RECORDED_OPS, ABORT_ESCAPE
 from pypy.jit.metainterp.jitprof import ABORT_TOO_LONG, ABORT_BRIDGE
 from pypy.rlib.rarithmetic import intmask
 from pypy.rlib.objectmodel import specialize
@@ -42,16 +42,10 @@
         argtypes = unrolling_iterable(self.argtypes)
         def wrapped(self, orgpc):
             args = (self, )
-            #if DEBUG >= DEBUG_DETAILED:
-            #    s = '%s:%d\t%s' % (self.jitcode.name, orgpc, name)
-            #else:
-            s = ''
             for argspec in argtypes:
                 if argspec == "box":
                     box = self.load_arg()
                     args += (box, )
-                    #if DEBUG >= DEBUG_DETAILED:
-                    #    s += '\t' + box.repr_rpython()
                 elif argspec == "constbox":
                     args += (self.load_const_arg(), )
                 elif argspec == "int":
@@ -82,12 +76,7 @@
                     args += (methdescr, )
                 else:
                     assert 0, "unknown argtype declaration: %r" % (argspec,)
-            #if DEBUG >= DEBUG_DETAILED:
-            #    debug_print(s)
             val = func(*args)
-            #if DEBUG >= DEBUG_DETAILED:
-            #    reprboxes = ' '.join([box.repr_rpython() for box in self.env])
-            #    debug_print('  \x1b[34menv=[%s]\x1b[0m' % (reprboxes,))
             if val is None:
                 val = False
             return val
@@ -654,6 +643,10 @@
     def opimpl_residual_call(self, calldescr, varargs):
         return self.do_residual_call(varargs, descr=calldescr, exc=True)
 
+    @arguments("descr", "varargs")
+    def opimpl_residual_call_loopinvariant(self, calldescr, varargs):
+        return self.execute_varargs(rop.CALL_LOOPINVARIANT, varargs, calldescr, exc=True)
+
     @arguments("varargs")
     def opimpl_recursion_leave_prep(self, varargs):
         warmrunnerstate = self.metainterp.staticdata.state
@@ -667,16 +660,40 @@
             return False
         return self.perform_call(leave_code, varargs)
         
-    @arguments("descr", "varargs")
-    def opimpl_recursive_call(self, calldescr, varargs):
+    @arguments("orgpc", "descr", "varargs")
+    def opimpl_recursive_call(self, pc, calldescr, varargs):
         warmrunnerstate = self.metainterp.staticdata.state
-        if warmrunnerstate.inlining:
+        token = None
+        if not self.metainterp.is_blackholing() and 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 warmrunnerstate.can_inline_callable(greenkey):
                 return self.perform_call(portal_code, varargs[1:], greenkey)
-        return self.do_residual_call(varargs, descr=calldescr, exc=True)
+            token = warmrunnerstate.get_assembler_token(greenkey)
+        call_position = 0
+        if token is not None:
+            call_position = len(self.metainterp.history.operations)
+            # guard value for all green args, needed to make sure
+            # that assembler that we call is still correct
+            greenargs = varargs[1:num_green_args + 1]
+            self.generate_guard_value_for_green_args(pc, greenargs)
+        res = self.do_residual_call(varargs, descr=calldescr, exc=True)
+        if not self.metainterp.is_blackholing() and token is not None:
+            # XXX fix the call position, <UGLY!>
+            found = False
+            while True:
+                op = self.metainterp.history.operations[call_position]
+                if op.opnum == rop.CALL or op.opnum == rop.CALL_MAY_FORCE:
+                    found = True
+                    break
+                call_position += 1
+            assert found
+            # </UGLY!>
+            # this will substitute the residual call with assembler call
+            self.metainterp.direct_assembler_call(pc, varargs, token,
+                                                  call_position)
+        return res
 
     @arguments("descr", "varargs")
     def opimpl_residual_call_noexception(self, calldescr, varargs):
@@ -786,7 +803,7 @@
     def opimpl_keepalive(self, box):
         pass     # xxx?
 
-    def generate_merge_point(self, pc, varargs):
+    def generate_guard_value_for_green_args(self, pc, varargs):
         num_green_args = self.metainterp.staticdata.num_green_args
         for i in range(num_green_args):
             varargs[i] = self.implement_guard_value(pc, varargs[i])
@@ -826,7 +843,7 @@
     @arguments("orgpc")
     def opimpl_jit_merge_point(self, pc):
         if not self.metainterp.is_blackholing():
-            self.generate_merge_point(pc, self.env)
+            self.generate_guard_value_for_green_args(pc, self.env)
             # xxx we may disable the following line in some context later
             self.debug_merge_point()
             if self.metainterp.seen_can_enter_jit:
@@ -834,8 +851,7 @@
                 try:
                     self.metainterp.reached_can_enter_jit(self.env)
                 except GiveUp:
-                    self.metainterp.staticdata.profiler.count(ABORT_BRIDGE)
-                    self.metainterp.switch_to_blackhole()
+                    self.metainterp.switch_to_blackhole(ABORT_BRIDGE)
         if self.metainterp.is_blackholing():
             self.blackhole_reached_merge_point(self.env)
         return True
@@ -846,6 +862,7 @@
         greenkey = self.env[:num_green_args]
         sd = self.metainterp.staticdata
         loc = sd.state.get_location_str(greenkey)
+        debug_print(loc)
         constloc = self.metainterp.cpu.ts.conststr(loc)
         self.metainterp.history.record(rop.DEBUG_MERGE_POINT,
                                        [constloc], None)
@@ -858,9 +875,13 @@
     def opimpl_teardown_exception_block(self):
         self.exception_target = -1
 
-    @arguments("constbox", "jumptarget")
-    def opimpl_goto_if_exception_mismatch(self, vtableref, next_exc_target):
-        assert isinstance(self.exception_box, Const)    # XXX
+    @arguments("constbox", "jumptarget", "orgpc")
+    def opimpl_goto_if_exception_mismatch(self, vtableref, next_exc_target, pc):
+        # XXX used to be:
+        # assert isinstance(self.exception_box, Const)    # XXX
+        # seems this can happen that self.exception_box is not a Const,
+        # but I failed to write a test so far :-(
+        self.exception_box = self.implement_guard_value(pc, self.exception_box)
         cpu = self.metainterp.cpu
         ts = self.metainterp.cpu.ts
         if not ts.subclassOf(cpu, self.exception_box, vtableref):
@@ -886,6 +907,55 @@
         return self.metainterp.finishframe_exception(self.exception_box,
                                                      self.exc_value_box)
 
+    @arguments("box")
+    def opimpl_virtual_ref(self, box):
+        # Details on the content of metainterp.virtualref_boxes:
+        #
+        #  * it's a list whose items go two by two, containing first the
+        #    virtual box (e.g. the PyFrame) and then the vref box (e.g.
+        #    the 'virtual_ref(frame)').
+        #
+        #  * if we detect that the virtual box escapes during tracing
+        #    already (by generating a CALl_MAY_FORCE that marks the flags
+        #    in the vref), then we replace the vref in the list with
+        #    ConstPtr(NULL).
+        #
+        metainterp = self.metainterp
+        if metainterp.is_blackholing():
+            resbox = box      # good enough when blackholing
+        else:
+            vrefinfo = metainterp.staticdata.virtualref_info
+            obj = box.getref_base()
+            vref = vrefinfo.virtual_ref_during_tracing(obj)
+            resbox = history.BoxPtr(vref)
+            cindex = history.ConstInt(len(metainterp.virtualref_boxes) // 2)
+            metainterp.history.record(rop.VIRTUAL_REF, [box, cindex], resbox)
+            # Note: we allocate a JIT_VIRTUAL_REF here
+            # (in virtual_ref_during_tracing()), in order to detect when
+            # the virtual escapes during tracing already.  We record it as a
+            # VIRTUAL_REF operation, although the backend sees this operation
+            # as a no-op.  The point is that the backend should not really see
+            # it in practice, as optimizeopt.py should either kill it or
+            # replace it with a NEW_WITH_VTABLE followed by SETFIELD_GCs.
+        metainterp.virtualref_boxes.append(box)
+        metainterp.virtualref_boxes.append(resbox)
+        self.make_result_box(resbox)
+
+    @arguments("box")
+    def opimpl_virtual_ref_finish(self, box):
+        # virtual_ref_finish() assumes that we have a stack-like, last-in
+        # first-out order.
+        metainterp = self.metainterp
+        vrefbox = metainterp.virtualref_boxes.pop()
+        lastbox = metainterp.virtualref_boxes.pop()
+        assert box.getref_base() == lastbox.getref_base()
+        if not metainterp.is_blackholing():
+            vrefinfo = metainterp.staticdata.virtualref_info
+            vref = vrefbox.getref_base()
+            if vrefinfo.is_virtual_ref(vref):
+                metainterp.history.record(rop.VIRTUAL_REF_FINISH,
+                                          [vrefbox, lastbox], None)
+
     # ------------------------------
 
     def setup_call(self, argboxes):
@@ -947,7 +1017,7 @@
         if metainterp.staticdata.virtualizable_info is not None:
             virtualizable_boxes = metainterp.virtualizable_boxes
         resume.capture_resumedata(metainterp.framestack, virtualizable_boxes,
-                                  resumedescr)
+                                  metainterp.virtualref_boxes, resumedescr)
         self.metainterp.staticdata.profiler.count_ops(opnum, GUARDS)
         # count
         metainterp.attach_debug_info(guard_op)
@@ -988,13 +1058,13 @@
 
     def do_residual_call(self, argboxes, descr, exc):
         effectinfo = descr.get_extra_info()
-        if effectinfo is None or effectinfo.promotes_virtualizables:
+        if effectinfo is None or effectinfo.forces_virtual_or_virtualizable:
             # residual calls require attention to keep virtualizables in-sync
-            self.metainterp.vable_before_residual_call()
+            self.metainterp.vable_and_vrefs_before_residual_call()
             # xxx do something about code duplication
             resbox = self.metainterp.execute_and_record_varargs(
                 rop.CALL_MAY_FORCE, argboxes, descr=descr)
-            self.metainterp.vable_after_residual_call()
+            self.metainterp.vable_and_vrefs_after_residual_call()
             if resbox is not None:
                 self.make_result_box(resbox)
             self.generate_guard(self.pc, rop.GUARD_NOT_FORCED, None, [])
@@ -1047,6 +1117,9 @@
         self._addr2name_values = []
 
         self.__dict__.update(compile.make_done_loop_tokens())
+        # store this information for fastpath of call_assembler
+        d = self.loop_tokens_done_with_this_frame_int[0].finishdescr
+        self.cpu.done_with_this_frame_int_v = self.cpu.get_fail_descr_number(d)
 
     def _freeze_(self):
         return True
@@ -1234,8 +1307,7 @@
                 try:
                     self.compile_done_with_this_frame(resultbox)
                 except GiveUp:
-                    self.staticdata.profiler.count(ABORT_BRIDGE)
-                    self.switch_to_blackhole()
+                    self.switch_to_blackhole(ABORT_BRIDGE)
             sd = self.staticdata
             if sd.result_type == 'void':
                 assert resultbox is None
@@ -1272,8 +1344,7 @@
             try:
                 self.compile_exit_frame_with_exception(excvaluebox)
             except GiveUp:
-                self.staticdata.profiler.count(ABORT_BRIDGE)
-                self.switch_to_blackhole()
+                self.switch_to_blackhole(ABORT_BRIDGE)
         raise self.staticdata.ExitFrameWithExceptionRef(self.cpu, excvaluebox.getref_base())
 
     def check_recursion_invariant(self):
@@ -1308,7 +1379,7 @@
 
     def create_empty_history(self):
         warmrunnerstate = self.staticdata.state
-        self.history = history.History(self.cpu)
+        self.history = history.History()
         self.staticdata.stats.set_history(self.history)
 
     def _all_constants(self, *boxes):
@@ -1391,7 +1462,8 @@
             op.pc = self.framestack[-1].pc
             op.name = self.framestack[-1].jitcode.name
 
-    def switch_to_blackhole(self):
+    def switch_to_blackhole(self, reason):
+        self.staticdata.profiler.count(reason)
         debug_print('~~~ ABORTING TRACING')
         debug_stop('jit-tracing')
         debug_start('jit-blackhole')
@@ -1399,15 +1471,15 @@
         self.staticdata.stats.aborted()
         self.staticdata.profiler.end_tracing()
         self.staticdata.profiler.start_blackhole()
+    switch_to_blackhole._dont_inline_ = True
 
     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.staticdata.profiler.count(ABORT_TOO_LONG)
                 self.greenkey_of_huge_function = self.find_biggest_function()
                 self.portal_trace_positions = None
-                self.switch_to_blackhole()
+                self.switch_to_blackhole(ABORT_TOO_LONG)
 
     def _interpret(self):
         # Execute the frames forward until we raise a DoneWithThisFrame,
@@ -1531,6 +1603,7 @@
                                               len(self.virtualizable_boxes)-1,
                                               duplicates)
             live_arg_boxes += self.virtualizable_boxes[:-1]
+        assert len(self.virtualref_boxes) == 0, "missing virtual_ref_finish()?"
         # Called whenever we reach the 'can_enter_jit' hint.
         # First, attempt to make a bridge:
         # - if self.resumekey is a ResumeGuardDescr, it starts from a guard
@@ -1560,17 +1633,9 @@
                     # we cannot reconstruct the beginning of the proper loop
                     raise GiveUp
 
-                oldops = self.history.operations[:]
                 # raises in case it works -- which is the common case
                 self.compile(original_boxes, live_arg_boxes, start)
-                # creation of the loop was cancelled!  Patch
-                # history.operations so that it contains again
-                # exactly its old list of operations...
-                # xxx maybe we could patch history.operations with
-                # Nones after calling self.compile() instead of
-                # before...  xxx maybe we should just raise GiveUp
-                del self.history.operations[:]
-                self.history.operations.extend(oldops)
+                # creation of the loop was cancelled!
 
         # Otherwise, no loop found so far, so continue tracing.
         start = len(self.history.operations)
@@ -1608,6 +1673,7 @@
                                               greenkey, start)
         if loop_token is not None: # raise if it *worked* correctly
             raise GenerateMergePoint(live_arg_boxes, loop_token)
+        self.history.operations.pop()     # remove the JUMP
 
     def compile_bridge(self, live_arg_boxes):
         num_green_args = self.staticdata.num_green_args
@@ -1686,6 +1752,7 @@
         f = self.newframe(self.staticdata.portal_code)
         f.pc = 0
         f.env = original_boxes[:]
+        self.virtualref_boxes = []
         self.initialize_virtualizable(original_boxes)
         return original_boxes
 
@@ -1694,7 +1761,7 @@
         self.in_recursion = -1 # always one portal around
         inputargs_and_holes = self.cpu.make_boxes_from_latest_values(resumedescr)
         if must_compile:
-            self.history = history.History(self.cpu)
+            self.history = history.History()
             self.history.inputargs = [box for box in inputargs_and_holes if box]
             self.staticdata.profiler.start_tracing()
         else:
@@ -1723,9 +1790,18 @@
         virtualizable = vinfo.unwrap_virtualizable_box(virtualizable_box)
         vinfo.clear_vable_token(virtualizable)
 
-    def vable_before_residual_call(self):
+    def vable_and_vrefs_before_residual_call(self):
         if self.is_blackholing():
             return
+        #
+        vrefinfo = self.staticdata.virtualref_info
+        for i in range(1, len(self.virtualref_boxes), 2):
+            vrefbox = self.virtualref_boxes[i]
+            vref = vrefbox.getref_base()
+            vrefinfo.tracing_before_residual_call(vref)
+            # the FORCE_TOKEN is already set at runtime in each vref when
+            # it is created, by optimizeopt.py.
+        #
         vinfo = self.staticdata.virtualizable_info
         if vinfo is not None:
             virtualizable_box = self.virtualizable_boxes[-1]
@@ -1738,23 +1814,50 @@
                                                   force_token_box],
                                 None, descr=vinfo.vable_token_descr)
 
-    def vable_after_residual_call(self):
+    def vable_and_vrefs_after_residual_call(self):
         if self.is_blackholing():
-            vable_escapes = True
+            escapes = True
         else:
-            vable_escapes = False
+            escapes = False
+            #
+            vrefinfo = self.staticdata.virtualref_info
+            for i in range(0, len(self.virtualref_boxes), 2):
+                virtualbox = self.virtualref_boxes[i]
+                vrefbox = self.virtualref_boxes[i+1]
+                vref = vrefbox.getref_base()
+                if vrefinfo.tracing_after_residual_call(vref):
+                    # this vref was really a virtual_ref, but it escaped
+                    # during this CALL_MAY_FORCE.  Mark this fact by
+                    # generating a VIRTUAL_REF_FINISH on it and replacing
+                    # it by ConstPtr(NULL).
+                    self.stop_tracking_virtualref(i)
+            #
             vinfo = self.staticdata.virtualizable_info
             if vinfo is not None:
                 virtualizable_box = self.virtualizable_boxes[-1]
                 virtualizable = vinfo.unwrap_virtualizable_box(virtualizable_box)
                 if vinfo.tracing_after_residual_call(virtualizable):
-                    # We just did the residual call, and it shows that the
-                    # virtualizable escapes.
-                    self.switch_to_blackhole()
-                    vable_escapes = True
-        if vable_escapes:
+                    # the virtualizable escaped during CALL_MAY_FORCE.
+                    escapes = True
+            #
+            if escapes:
+                self.switch_to_blackhole(ABORT_ESCAPE)
+        #
+        if escapes:
             self.load_fields_from_virtualizable()
 
+    def stop_tracking_virtualref(self, i):
+        virtualbox = self.virtualref_boxes[i]
+        vrefbox = self.virtualref_boxes[i+1]
+        # record VIRTUAL_REF_FINISH just before the current CALL_MAY_FORCE
+        call_may_force_op = self.history.operations.pop()
+        assert call_may_force_op.opnum == rop.CALL_MAY_FORCE
+        self.history.record(rop.VIRTUAL_REF_FINISH,
+                            [vrefbox, virtualbox], None)
+        self.history.operations.append(call_may_force_op)
+        # mark by replacing it with ConstPtr(NULL)
+        self.virtualref_boxes[i+1] = self.cpu.ts.CONST_NULL
+
     def handle_exception(self):
         etype = self.cpu.get_exception()
         evalue = self.cpu.get_exc_value()
@@ -1787,7 +1890,20 @@
         vinfo = self.staticdata.virtualizable_info
         self.framestack = []
         expect_virtualizable = vinfo is not None
-        virtualizable_boxes = resume.rebuild_from_resumedata(self, newboxes, resumedescr, expect_virtualizable)
+        virtualizable_boxes, virtualref_boxes = resume.rebuild_from_resumedata(
+            self, newboxes, resumedescr, expect_virtualizable)
+        #
+        # virtual refs: make the vrefs point to the freshly allocated virtuals
+        self.virtualref_boxes = virtualref_boxes
+        vrefinfo = self.staticdata.virtualref_info
+        for i in range(0, len(virtualref_boxes), 2):
+            virtualbox = virtualref_boxes[i]
+            vrefbox = virtualref_boxes[i+1]
+            vrefinfo.continue_tracing(vrefbox.getref_base(),
+                                      virtualbox.getref_base())
+        #
+        # virtualizable: synchronize the real virtualizable and the local
+        # boxes, in whichever direction is appropriate
         if expect_virtualizable:
             self.virtualizable_boxes = virtualizable_boxes
             if self._already_allocated_resume_virtuals is not None:
@@ -1796,12 +1912,19 @@
                 self.load_fields_from_virtualizable()
                 return
             # just jumped away from assembler (case 4 in the comment in
-            # virtualizable.py) into tracing (case 2); check that vable_rti
-            # is and stays NULL.
+            # virtualizable.py) into tracing (case 2); check that vable_token
+            # is and stays 0.  Note the call to reset_vable_token() in
+            # warmstate.py.
             virtualizable_box = self.virtualizable_boxes[-1]
             virtualizable = vinfo.unwrap_virtualizable_box(virtualizable_box)
             assert not virtualizable.vable_token
-            self.synchronize_virtualizable()
+            if self._already_allocated_resume_virtuals is not None:
+                # resuming from a ResumeGuardForcedDescr: load the new values
+                # currently stored on the virtualizable fields
+                self.load_fields_from_virtualizable()
+            else:
+                # normal case: fill the virtualizable with the local boxes
+                self.synchronize_virtualizable()
 
     def check_synchronized_virtualizable(self):
         if not we_are_translated():
@@ -1850,12 +1973,33 @@
                                             abox, ConstInt(j), itembox)
             assert i + 1 == len(self.virtualizable_boxes)
 
+    def gen_load_from_other_virtualizable(self, vbox):
+        vinfo = self.staticdata.virtualizable_info
+        boxes = []
+        assert vinfo is not None
+        for i in range(vinfo.num_static_extra_boxes):
+            descr = vinfo.static_field_descrs[i]
+            boxes.append(self.execute_and_record(rop.GETFIELD_GC, descr, vbox))
+        virtualizable = vinfo.unwrap_virtualizable_box(vbox)
+        for k in range(vinfo.num_arrays):
+            descr = vinfo.array_field_descrs[k]
+            abox = self.execute_and_record(rop.GETFIELD_GC, descr, vbox)
+            descr = vinfo.array_descrs[k]
+            for j in range(vinfo.get_array_length(virtualizable, k)):
+                boxes.append(self.execute_and_record(rop.GETARRAYITEM_GC, descr,
+                                                     abox, ConstInt(j)))
+        return boxes
+
     def replace_box(self, oldbox, newbox):
         for frame in self.framestack:
             boxes = frame.env
             for i in range(len(boxes)):
                 if boxes[i] is oldbox:
                     boxes[i] = newbox
+        boxes = self.virtualref_boxes
+        for i in range(len(boxes)):
+            if boxes[i] is oldbox:
+                boxes[i] = newbox
         if self.staticdata.virtualizable_info is not None:
             boxes = self.virtualizable_boxes
             for i in range(len(boxes)):
@@ -1886,6 +2030,20 @@
                 max_key = key
         return max_key
 
+    def direct_assembler_call(self, pc, varargs, token, call_position):
+        """ Generate a direct call to assembler for portal entry point.
+        """
+        assert not self.is_blackholing() # XXX
+        num_green_args = self.staticdata.num_green_args
+        args = varargs[num_green_args + 1:]
+        resbox = self.history.operations[call_position].result
+        rest = self.history.slice_history_at(call_position)
+        if self.staticdata.virtualizable_info is not None:
+            vindex = self.staticdata.virtualizable_info.index_of_virtualizable
+            vbox = args[vindex - num_green_args]
+            args += self.gen_load_from_other_virtualizable(vbox)
+        self.history.record(rop.CALL_ASSEMBLER, args[:], resbox, descr=token)
+        self.history.operations += rest
 
 class GenerateMergePoint(Exception):
     def __init__(self, args, target_loop_token):

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/resoperation.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/resoperation.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/resoperation.py	Mon Jan 25 15:25:48 2010
@@ -31,7 +31,11 @@
         self.descr = descr
 
     def clone(self):
-        op = ResOperation(self.opnum, self.args, self.result, self.descr)
+        descr = self.descr
+        if descr is not None:
+            descr = descr._clone_if_mutable()
+        op = ResOperation(self.opnum, self.args, self.result, descr)
+        op.fail_args = self.fail_args
         if not we_are_translated():
             op.name = self.name
             op.pc = self.pc
@@ -90,7 +94,7 @@
         return rop._OVF_FIRST <= self.opnum <= rop._OVF_LAST
 
     def is_comparison(self):
-        return rop._COMPARISON_FIRST <= self.opnum <= rop._COMPARISON_LAST
+        return self.is_always_pure() and self.returns_bool_result()
 
     def is_final(self):
         return rop._FINAL_FIRST <= self.opnum <= rop._FINAL_LAST
@@ -155,7 +159,6 @@
     'CAST_FLOAT_TO_INT/1',
     'CAST_INT_TO_FLOAT/1',
     #
-    '_COMPARISON_FIRST',
     'INT_LT/2b',
     'INT_LE/2b',
     'INT_EQ/2b',
@@ -166,8 +169,7 @@
     'UINT_LE/2b',
     'UINT_GT/2b',
     'UINT_GE/2b',
-    '_COMPARISON_LAST',
-    'FLOAT_LT/2b',          # maybe these ones should be comparisons too
+    'FLOAT_LT/2b',
     'FLOAT_LE/2b',
     'FLOAT_EQ/2b',
     'FLOAT_NE/2b',
@@ -205,6 +207,8 @@
     'NEW/0d',
     'NEW_WITH_VTABLE/1',
     'NEW_ARRAY/1d',
+    'FORCE_TOKEN/0',
+    'VIRTUAL_REF/2',
     '_NOSIDEEFFECT_LAST', # ----- end of no_side_effect operations -----
 
     'SETARRAYITEM_GC/3d',
@@ -221,11 +225,13 @@
     'COND_CALL_GC_MALLOC',  # [a, b, if_(a<=b)_result, if_(a>b)_call, args...]
                             #        => result          (for mallocs)
     'DEBUG_MERGE_POINT/1',      # debugging only
-    'FORCE_TOKEN/0',
+    'VIRTUAL_REF_FINISH/2',
 
     '_CANRAISE_FIRST', # ----- start of can_raise operations -----
     'CALL',
+    'CALL_ASSEMBLER',
     'CALL_MAY_FORCE',
+    'CALL_LOOPINVARIANT',
     'OOSEND',                     # ootype operation
     '_CANRAISE_LAST', # ----- end of can_raise operations -----
 

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/resume.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/resume.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/resume.py	Mon Jan 25 15:25:48 2010
@@ -21,15 +21,10 @@
         self.boxes = boxes
 
 class FrameInfo(object):
-    __slots__ = ('prev', 'jitcode', 'pc', 'exception_target', 'level')
+    __slots__ = ('prev', 'jitcode', 'pc', 'exception_target')
 
     def __init__(self, prev, frame):
         self.prev = prev
-        if prev is None:
-            level = 1
-        else:
-            level = prev.level + 1
-        self.level = level
         self.jitcode = frame.jitcode
         self.pc = frame.pc
         self.exception_target = frame.exception_target
@@ -47,7 +42,8 @@
                                          back.parent_resumedata_snapshot,
                                          back.env[:])
 
-def capture_resumedata(framestack, virtualizable_boxes, storage):
+def capture_resumedata(framestack, virtualizable_boxes, virtualref_boxes,
+                       storage):
     n = len(framestack)-1
     top = framestack[n]
     _ensure_parent_resumedata(framestack, n)
@@ -55,6 +51,7 @@
                                 top)
     storage.rd_frame_info_list = frame_info_list
     snapshot = Snapshot(top.parent_resumedata_snapshot, top.env[:])
+    snapshot = Snapshot(snapshot, virtualref_boxes[:]) # xxx for now
     if virtualizable_boxes is not None:
         snapshot = Snapshot(snapshot, virtualizable_boxes[:]) # xxx for now
     storage.rd_snapshot = snapshot
@@ -98,8 +95,8 @@
 TAGBOX      = 2
 TAGVIRTUAL  = 3
 
-UNASSIGNED = tag(-2 ** 12 - 1, TAGBOX)
-UNASSIGNEDVIRTUAL = tag(-2 ** 12 - 1, TAGVIRTUAL)
+UNASSIGNED = tag(-1<<13, TAGBOX)
+UNASSIGNEDVIRTUAL = tag(-1<<13, TAGVIRTUAL)
 NULLREF = tag(-1, TAGCONST)
 
 
@@ -200,6 +197,7 @@
         return len(self.cached_boxes)
 
     def assign_number_to_box(self, box, boxes):
+        # returns a negative number
         if box in self.cached_boxes:
             num = self.cached_boxes[box]
             boxes[-num-1] = box
@@ -213,6 +211,7 @@
         return len(self.cached_virtuals)
 
     def assign_number_to_virtual(self, box):
+        # returns a negative number
         if box in self.cached_virtuals:
             num = self.cached_virtuals[box]
         else:
@@ -235,8 +234,6 @@
     def __init__(self, storage, memo):
         self.storage = storage
         self.memo = memo
-        #self.virtuals = []
-        #self.vfieldboxes = []
 
     def make_virtual(self, known_class, fielddescrs):
         return VirtualInfo(known_class, fielddescrs)
@@ -257,8 +254,6 @@
         if (isinstance(box, Box) and box not in self.liveboxes_from_env
                                  and box not in self.liveboxes):
             self.liveboxes[box] = UNASSIGNED
-            return True
-        return False
 
     def _register_boxes(self, boxes):
         for box in boxes:
@@ -273,9 +268,11 @@
         _, tagbits = untag(tagged)
         return tagbits == TAGVIRTUAL
 
-    def finish(self, values):
+    def finish(self, values, pending_setfields=[]):
         # compute the numbering
         storage = self.storage
+        # make sure that nobody attached resume data to this guard yet
+        assert storage.rd_numb is None
         numb, liveboxes_from_env, v = self.memo.number(values,
                                                        storage.rd_snapshot)
         self.liveboxes_from_env = liveboxes_from_env
@@ -296,16 +293,29 @@
                 value = values[box]
                 value.get_args_for_fail(self)
 
+        for _, box, fieldbox in pending_setfields:
+            self.register_box(box)
+            self.register_box(fieldbox)
+            value = values[fieldbox]
+            value.get_args_for_fail(self)
+
         self._number_virtuals(liveboxes, values, v)
+        self._add_pending_fields(pending_setfields)
 
         storage.rd_consts = self.memo.consts
         dump_storage(storage, liveboxes)
         return liveboxes[:]
 
     def _number_virtuals(self, liveboxes, values, num_env_virtuals):
+        # !! 'liveboxes' is a list that is extend()ed in-place !!
         memo = self.memo
         new_liveboxes = [None] * memo.num_cached_boxes()
         count = 0
+        # So far, self.liveboxes should contain 'tagged' values that are
+        # either UNASSIGNED, UNASSIGNEDVIRTUAL, or a *non-negative* value
+        # with the TAGVIRTUAL.  The following loop removes the UNASSIGNED
+        # and UNASSIGNEDVIRTUAL entries, and replaces them with real
+        # negative values.
         for box, tagged in self.liveboxes.iteritems():
             i, tagbits = untag(tagged)
             if tagbits == TAGBOX:
@@ -320,6 +330,8 @@
                     assert box not in self.liveboxes_from_env
                     index = memo.assign_number_to_virtual(box)
                     self.liveboxes[box] = tag(index, TAGVIRTUAL)
+                else:
+                    assert i >= 0
         new_liveboxes.reverse()
         liveboxes.extend(new_liveboxes)
         nholes = len(new_liveboxes) - count
@@ -356,6 +368,16 @@
                 return True
         return False
 
+    def _add_pending_fields(self, pending_setfields):
+        rd_pendingfields = None
+        if pending_setfields:
+            rd_pendingfields = []
+            for descr, box, fieldbox in pending_setfields:
+                num = self._gettagged(box)
+                fieldnum = self._gettagged(fieldbox)
+                rd_pendingfields.append((descr, num, fieldnum))
+        self.storage.rd_pendingfields = rd_pendingfields
+
     def _gettagged(self, box):
         if isinstance(box, Const):
             return self.memo.getconst(box)
@@ -364,11 +386,16 @@
                 return self.liveboxes_from_env[box]
             return self.liveboxes[box]
 
+
 class AbstractVirtualInfo(object):
     def allocate(self, metainterp):
         raise NotImplementedError
     def setfields(self, metainterp, box, fn_decode_box):
         raise NotImplementedError
+    def equals(self, fieldnums):
+        return tagged_list_eq(self.fieldnums, fieldnums)
+    def set_content(self, fieldnums):
+        self.fieldnums = fieldnums
 
 
 class AbstractVirtualStructInfo(AbstractVirtualInfo):
@@ -439,11 +466,13 @@
             debug_print("\t\t", str(untag(i)))
 
 
-def rebuild_from_resumedata(metainterp, newboxes, storage, expects_virtualizables):
+def rebuild_from_resumedata(metainterp, newboxes, storage,
+                            expects_virtualizables):
     resumereader = ResumeDataReader(storage, newboxes, metainterp)
     virtualizable_boxes = None
     if expects_virtualizables:
         virtualizable_boxes = resumereader.consume_boxes()
+    virtualref_boxes = resumereader.consume_boxes()
     frameinfo = storage.rd_frame_info_list
     while True:
         env = resumereader.consume_boxes()
@@ -453,11 +482,16 @@
         if frameinfo is None:
             break
     metainterp.framestack.reverse()
-    return virtualizable_boxes
+    return virtualizable_boxes, virtualref_boxes
 
-def force_from_resumedata(metainterp, newboxes, storage):
+def force_from_resumedata(metainterp, newboxes, storage,
+                          expects_virtualizables):
     resumereader = ResumeDataReader(storage, newboxes, metainterp)
-    return resumereader.consume_boxes(), resumereader.virtuals
+    virtualizable_boxes = None
+    if expects_virtualizables:
+        virtualizable_boxes = resumereader.consume_boxes()
+    virtualref_boxes = resumereader.consume_boxes()
+    return virtualizable_boxes, virtualref_boxes, resumereader.virtuals
 
 
 class ResumeDataReader(object):
@@ -469,6 +503,7 @@
         self.liveboxes = liveboxes
         self.cpu = metainterp.cpu
         self._prepare_virtuals(metainterp, storage.rd_virtuals)
+        self._prepare_pendingfields(metainterp, storage.rd_pendingfields)
 
     def _prepare_virtuals(self, metainterp, virtuals):
         if virtuals:
@@ -487,6 +522,16 @@
                     vinfo.setfields(metainterp, self.virtuals[i],
                                     self._decode_box)
 
+    def _prepare_pendingfields(self, metainterp, pendingfields):
+        if pendingfields:
+            if metainterp._already_allocated_resume_virtuals is not None:
+                return
+            for descr, num, fieldnum in pendingfields:
+                box = self._decode_box(num)
+                fieldbox = self._decode_box(fieldnum)
+                metainterp.execute_and_record(rop.SETFIELD_GC,
+                                              descr, box, fieldbox)
+
     def consume_boxes(self):
         numb = self.cur_numb
         assert numb is not None

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/support.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/support.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/support.py	Mon Jan 25 15:25:48 2010
@@ -3,6 +3,7 @@
 from pypy.rpython import rlist
 from pypy.rpython.lltypesystem import rstr as ll_rstr, rdict as ll_rdict
 from pypy.rpython.lltypesystem import rlist as lltypesystem_rlist
+from pypy.rpython.lltypesystem.lloperation import llop
 from pypy.rpython.ootypesystem import rdict as oo_rdict
 from pypy.rpython.llinterp import LLInterpreter
 from pypy.rpython.extregistry import ExtRegistryEntry
@@ -136,6 +137,9 @@
 def _ll_1_gc_identityhash(x):
     return lltype.identityhash(x)
 
+def _ll_1_jit_force_virtual(inst):
+    return llop.jit_force_virtual(lltype.typeOf(inst), inst)
+
 
 class LLtypeHelpers:
 

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_basic.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_basic.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_basic.py	Mon Jan 25 15:25:48 2010
@@ -1,7 +1,7 @@
 import py
 import sys
 from pypy.rlib.jit import JitDriver, we_are_jitted, hint, dont_look_inside
-from pypy.rlib.jit import OPTIMIZER_FULL, OPTIMIZER_SIMPLE
+from pypy.rlib.jit import OPTIMIZER_FULL, OPTIMIZER_SIMPLE, loop_invariant
 from pypy.jit.metainterp.warmspot import ll_meta_interp, get_stats
 from pypy.jit.backend.llgraph import runner
 from pypy.jit.metainterp import support, codewriter, pyjitpl, history
@@ -52,6 +52,8 @@
         assert get_stats().exec_jumps <= maxcount
     def check_aborted_count(self, count):
         assert get_stats().aborted_count == count
+    def check_aborted_count_at_least(self, count):
+        assert get_stats().aborted_count >= count
 
     def meta_interp(self, *args, **kwds):
         kwds['CPUClass'] = self.CPUClass
@@ -84,6 +86,10 @@
         metainterp, rtyper = _get_bare_metainterp(f, args, self.CPUClass,
                                                   self.type_system,
                                                   **kwds)
+        metainterp.staticdata.state = FakeWarmRunnerState()
+        metainterp.staticdata.state.cpu = metainterp.staticdata.cpu
+        if hasattr(self, 'finish_metainterp_for_interp_operations'):
+            self.finish_metainterp_for_interp_operations(metainterp)
         portal_graph = rtyper.annotator.translator.graphs[0]
         cw = codewriter.CodeWriter(rtyper)
         
@@ -95,7 +101,6 @@
         cw.finish_making_bytecodes()
         metainterp.staticdata.portal_code = maingraph
         metainterp.staticdata._class_sizes = cw.class_sizes
-        metainterp.staticdata.state = FakeWarmRunnerState()
         metainterp.staticdata.DoneWithThisFrameInt = DoneWithThisFrame
         metainterp.staticdata.DoneWithThisFrameRef = DoneWithThisFrameRef
         metainterp.staticdata.DoneWithThisFrameFloat = DoneWithThisFrame
@@ -380,6 +385,26 @@
         res = self.meta_interp(f, [55])
         assert res == -1
 
+    def test_confirm_enter_jit(self):
+        def confirm_enter_jit(x, y):
+            return x <= 5
+        myjitdriver = JitDriver(greens = ['x'], reds = ['y'],
+                                confirm_enter_jit = confirm_enter_jit)
+        def f(x, y):
+            while y >= 0:
+                myjitdriver.can_enter_jit(x=x, y=y)
+                myjitdriver.jit_merge_point(x=x, y=y)
+                y -= x
+            return y
+        #
+        res = self.meta_interp(f, [10, 84])
+        assert res == -6
+        self.check_loop_count(0)
+        #
+        res = self.meta_interp(f, [3, 19])
+        assert res == -2
+        self.check_loop_count(1)
+
     def test_format(self):
         def f(n):
             return len("<%d>" % n)
@@ -1196,6 +1221,69 @@
         res = self.meta_interp(f, [21])
         assert res == 42
         self.check_loops(guard_nonnull=1, guard_isnull=1)
+
+    def test_loop_invariant(self):
+        myjitdriver = JitDriver(greens = [], reds = ['x', 'res'])
+        class A(object):
+            pass
+        a = A()
+        a.current_a = A()
+        a.current_a.x = 1
+        @loop_invariant
+        def f():
+            return a.current_a
+
+        def g(x):
+            res = 0
+            while x > 0:
+                myjitdriver.can_enter_jit(x=x, res=res)
+                myjitdriver.jit_merge_point(x=x, res=res)
+                res += f().x
+                res += f().x
+                res += f().x
+                x -= 1
+            a.current_a = A()
+            a.current_a.x = 2
+            return res
+        res = self.meta_interp(g, [21])
+        assert res == 3 * 21
+        self.check_loops(call=1)
+
+    def test_bug_optimizeopt_mutates_ops(self):
+        myjitdriver = JitDriver(greens = [], reds = ['x', 'res', 'a', 'const'])
+        class A(object):
+            pass
+        class B(A):
+            pass
+
+        glob = A()
+        glob.a = None
+        def f(x):
+            res = 0
+            a = A()
+            a.x = 0
+            glob.a = A()
+            const = 2
+            while x > 0:
+                myjitdriver.can_enter_jit(x=x, res=res, a=a, const=const)
+                myjitdriver.jit_merge_point(x=x, res=res, a=a, const=const)
+                if type(glob.a) is B:
+                    res += 1
+                if a is None:
+                    a = A()
+                    a.x = x
+                    glob.a = B()
+                    const = 2
+                else:
+                    const = hint(const, promote=True)
+                    x -= const
+                    res += a.x
+                    a = None
+                    glob.a = A()
+                    const = 1
+            return res
+        res = self.meta_interp(f, [21])
+        assert res == f(21)
         
 
 class TestOOtype(BasicTests, OOJitMixin):

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_codewriter.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_codewriter.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_codewriter.py	Mon Jan 25 15:25:48 2010
@@ -2,6 +2,7 @@
 from pypy.rlib import jit
 from pypy.jit.metainterp import support, typesystem
 from pypy.jit.metainterp.policy import JitPolicy
+from pypy.jit.metainterp.history import ConstInt
 from pypy.jit.metainterp.codewriter import CodeWriter
 from pypy.jit.metainterp.test.test_basic import LLJitMixin, OOJitMixin
 from pypy.translator.translator import graphof
@@ -78,7 +79,27 @@
                              supports_floats=True)
     funcs = set([graph.func for graph in res])
     assert funcs == set([f, h])
-    
+
+def test_unroll_safe_and_inline():
+    @jit.unroll_safe
+    def h(x):
+        i = 0
+        while i < x:
+            i += 1
+        return i
+    h._always_inline_ = True
+
+    def g(x):
+        return h(x)
+
+    rtyper = support.annotate(g, [7])
+    cw = CodeWriter(rtyper)
+    jitpolicy = JitPolicy()
+    translator = rtyper.annotator.translator
+    res = cw.find_all_graphs(translator.graphs[0], None, jitpolicy,
+                             supports_floats=True)
+    funcs = set([graph.func for graph in res])
+    assert funcs == set([g, h])
 
 def test_find_all_graphs_str_join():
     def i(x, y):
@@ -101,6 +122,8 @@
     def make_graphs(self, func, values, type_system='lltype'):
         class FakeMetaInterpSd:
             virtualizable_info = None
+            class options:
+                listops = True
             def find_opcode(self, name):
                 default = len(self.opname_to_index)
                 return self.opname_to_index.setdefault(name, default)
@@ -283,7 +306,7 @@
         assert calldescrs[0][4] is not None
         assert not calldescrs[0][4].write_descrs_fields
         assert not calldescrs[0][4].write_descrs_arrays
-        assert not calldescrs[0][4].promotes_virtualizables
+        assert not calldescrs[0][4].forces_virtual_or_virtualizable
 
     def test_oosend_look_inside_only_one(self):
         class A:
@@ -394,7 +417,7 @@
         assert cw.list_of_addr2name[0][1].endswith('.A1')
         assert cw.list_of_addr2name[1][1] == 'A1.g'
 
-    def test_promote_virtualizable_effectinfo(self):
+    def test_jit_force_virtualizable_effectinfo(self):
         class Frame(object):
             _virtualizable2_ = ['x']
             
@@ -430,9 +453,64 @@
         effectinfo_g1 = calldescrs[1][4]
         effectinfo_g2 = calldescrs[2][4]
         effectinfo_h  = calldescrs[3][4]
-        assert effectinfo_g1.promotes_virtualizables
-        assert effectinfo_g2.promotes_virtualizables
-        assert not effectinfo_h.promotes_virtualizables
+        assert effectinfo_g1.forces_virtual_or_virtualizable
+        assert effectinfo_g2.forces_virtual_or_virtualizable
+        assert not effectinfo_h.forces_virtual_or_virtualizable
+
+    def make_vrefinfo(self):
+        from pypy.jit.metainterp.virtualref import VirtualRefInfo
+        class FakeWarmRunnerDesc:
+            cpu = self.metainterp_sd.cpu
+        self.metainterp_sd.virtualref_info = VirtualRefInfo(FakeWarmRunnerDesc)
+
+    def test_vref_simple(self):
+        class X:
+            pass
+        def f():
+            return jit.virtual_ref(X())
+        graphs = self.make_graphs(f, [])
+        assert graphs[0].func is f
+        assert graphs[1].func is jit.virtual_ref
+        self.make_vrefinfo()
+        cw = CodeWriter(self.rtyper)
+        cw.candidate_graphs = [graphs[0]]
+        cw._start(self.metainterp_sd, None)
+        jitcode = cw.make_one_bytecode((graphs[0], None), False)
+        assert 'virtual_ref' in jitcode._source
+
+    def test_vref_forced(self):
+        class X:
+            pass
+        def f():
+            vref = jit.virtual_ref(X())
+            return vref()
+        graphs = self.make_graphs(f, [])
+        assert graphs[0].func is f
+        assert graphs[1].func is jit.virtual_ref
+        self.make_vrefinfo()
+        cw = CodeWriter(self.rtyper)
+        cw.candidate_graphs = [graphs[0]]
+        cw._start(self.metainterp_sd, None)
+        jitcode = cw.make_one_bytecode((graphs[0], None), False)
+        assert 'virtual_ref' in jitcode._source
+        # the call vref() becomes a residual call to a helper that contains
+        # itself a copy of the call.
+        assert 'residual_call' in jitcode._source
+
+    def test_we_are_jitted(self):
+        def f():
+            if jit.we_are_jitted():
+                return 55
+            else:
+                return 66
+        graphs = self.make_graphs(f, [])
+        cw = CodeWriter(self.rtyper)
+        cw.candidate_graphs = [graphs[0]]
+        cw._start(self.metainterp_sd, None)
+        jitcode = cw.make_one_bytecode((graphs[0], None), False)
+        assert 'goto_if_not' not in jitcode._source
+        assert ConstInt(55) in jitcode.constants
+        assert ConstInt(66) not in jitcode.constants
 
 
 class ImmutableFieldsTests:

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_compile.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_compile.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_compile.py	Mon Jan 25 15:25:48 2010
@@ -77,7 +77,7 @@
     metainterp = FakeMetaInterp()
     metainterp.staticdata = staticdata
     metainterp.cpu = cpu
-    metainterp.history = History(metainterp.cpu)
+    metainterp.history = History()
     metainterp.history.operations = loop.operations[:]
     metainterp.history.inputargs = loop.inputargs[:]
     #
@@ -94,7 +94,7 @@
     metainterp = FakeMetaInterp()
     metainterp.staticdata = staticdata
     metainterp.cpu = cpu
-    metainterp.history = History(metainterp.cpu)
+    metainterp.history = History()
     metainterp.history.operations = loop.operations[:]
     metainterp.history.inputargs = loop.inputargs[:]
     #

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_effectinfo.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_effectinfo.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_effectinfo.py	Mon Jan 25 15:25:48 2010
@@ -3,32 +3,77 @@
 from pypy.rpython.ootypesystem import ootype
 from pypy.jit.metainterp.effectinfo import effectinfo_from_writeanalyze
 
+class FakeCPU:
+    def fielddescrof(self, T, fieldname):
+        return ('fielddescr', T, fieldname)
+    def arraydescrof(self, A):
+        return ('arraydescr', A)
+
+def test_include_read_field():
+    S = lltype.GcStruct("S", ("a", lltype.Signed))
+    effects = frozenset([("readstruct", lltype.Ptr(S), "a")])
+    effectinfo = effectinfo_from_writeanalyze(effects, FakeCPU())
+    assert list(effectinfo.readonly_descrs_fields) == [('fielddescr', S, "a")]
+    assert not effectinfo.write_descrs_fields
+    assert not effectinfo.write_descrs_arrays
+
+def test_include_write_field():
+    S = lltype.GcStruct("S", ("a", lltype.Signed))
+    effects = frozenset([("struct", lltype.Ptr(S), "a")])
+    effectinfo = effectinfo_from_writeanalyze(effects, FakeCPU())
+    assert list(effectinfo.write_descrs_fields) == [('fielddescr', S, "a")]
+    assert not effectinfo.readonly_descrs_fields
+    assert not effectinfo.write_descrs_arrays
+
+def test_include_write_array():
+    A = lltype.GcArray(lltype.Signed)
+    effects = frozenset([("array", lltype.Ptr(A))])
+    effectinfo = effectinfo_from_writeanalyze(effects, FakeCPU())
+    assert not effectinfo.readonly_descrs_fields
+    assert not effectinfo.write_descrs_fields
+    assert list(effectinfo.write_descrs_arrays) == [('arraydescr', A)]
+
+def test_dont_include_read_and_write_field():
+    S = lltype.GcStruct("S", ("a", lltype.Signed))
+    effects = frozenset([("readstruct", lltype.Ptr(S), "a"),
+                         ("struct", lltype.Ptr(S), "a")])
+    effectinfo = effectinfo_from_writeanalyze(effects, FakeCPU())
+    assert not effectinfo.readonly_descrs_fields
+    assert list(effectinfo.write_descrs_fields) == [('fielddescr', S, "a")]
+    assert not effectinfo.write_descrs_arrays
+
+
 def test_filter_out_typeptr():
     effects = frozenset([("struct", lltype.Ptr(OBJECT), "typeptr")])
     effectinfo = effectinfo_from_writeanalyze(effects, None)
+    assert not effectinfo.readonly_descrs_fields
     assert not effectinfo.write_descrs_fields
     assert not effectinfo.write_descrs_arrays
 
 def test_filter_out_array_of_void():
     effects = frozenset([("array", lltype.Ptr(lltype.GcArray(lltype.Void)))])
     effectinfo = effectinfo_from_writeanalyze(effects, None)
+    assert not effectinfo.readonly_descrs_fields
     assert not effectinfo.write_descrs_fields
     assert not effectinfo.write_descrs_arrays
 
 def test_filter_out_struct_with_void():
     effects = frozenset([("struct", lltype.Ptr(lltype.GcStruct("x", ("a", lltype.Void))), "a")])
     effectinfo = effectinfo_from_writeanalyze(effects, None)
+    assert not effectinfo.readonly_descrs_fields
     assert not effectinfo.write_descrs_fields
     assert not effectinfo.write_descrs_arrays
 
 def test_filter_out_ooarray_of_void():
     effects = frozenset([("array", ootype.Array(ootype.Void))])
     effectinfo = effectinfo_from_writeanalyze(effects, None)
+    assert not effectinfo.readonly_descrs_fields
     assert not effectinfo.write_descrs_fields
     assert not effectinfo.write_descrs_arrays
 
 def test_filter_out_instance_with_void():
     effects = frozenset([("struct", ootype.Instance("x", ootype.ROOT, {"a": ootype.Void}), "a")])
     effectinfo = effectinfo_from_writeanalyze(effects, None)
+    assert not effectinfo.readonly_descrs_fields
     assert not effectinfo.write_descrs_fields
     assert not effectinfo.write_descrs_arrays

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_executor.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_executor.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_executor.py	Mon Jan 25 15:25:48 2010
@@ -273,4 +273,4 @@
         elif rettype == 'int':
             assert box.getint() == retvalue
         else:
-            assert retvalue is None
+            assert 0, "rettype is %r" % (rettype,)

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_history.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_history.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_history.py	Mon Jan 25 15:25:48 2010
@@ -9,3 +9,10 @@
     s = lltype.cast_pointer(lltype.Ptr(S), t)
     const = ConstPtr(lltype.cast_opaque_ptr(llmemory.GCREF, s))
     assert const._getrepr_() == "*T"
+
+def test_slicing():
+    h = History()
+    h.operations = [1, 2, 3, 4, 5]
+    rest = h.slice_history_at(2)
+    assert rest == [4, 5]
+    assert h.operations == [1, 2]

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_jitprof.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_jitprof.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_jitprof.py	Mon Jan 25 15:25:48 2010
@@ -64,7 +64,7 @@
         assert profiler.events == expected
         assert profiler.times == [2, 1, 1, 1]
         assert profiler.counters == [1, 1, 1, 1, 4, 3, 1, 1, 7, 1, 0, 0, 0,
-                                     0, 0, 0]
+                                     0, 0, 0, 0]
 
     def test_simple_loop_with_call(self):
         @dont_look_inside

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_optimizefindnode.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_optimizefindnode.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_optimizefindnode.py	Mon Jan 25 15:25:48 2010
@@ -1,6 +1,6 @@
 import py, random
 
-from pypy.rpython.lltypesystem import lltype, llmemory
+from pypy.rpython.lltypesystem import lltype, llmemory, rclass
 from pypy.rpython.ootypesystem import ootype
 from pypy.rpython.lltypesystem.rclass import OBJECT, OBJECT_VTABLE
 
@@ -38,12 +38,13 @@
     type_system = 'lltype'
 
     def get_class_of_box(self, box):
-        from pypy.rpython.lltypesystem import rclass
         return box.getref(rclass.OBJECTPTR).typeptr
 
     node_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True)
+    node_vtable.name = rclass.alloc_array_name('node')
     node_vtable_adr = llmemory.cast_ptr_to_adr(node_vtable)
     node_vtable2 = lltype.malloc(OBJECT_VTABLE, immortal=True)
+    node_vtable2.name = rclass.alloc_array_name('node2')
     node_vtable_adr2 = llmemory.cast_ptr_to_adr(node_vtable2)
     cpu = runner.LLtypeCPU(None)
 
@@ -67,6 +68,12 @@
     nextdescr = cpu.fielddescrof(NODE, 'next')
     otherdescr = cpu.fielddescrof(NODE2, 'other')
 
+    NODEOBJ = lltype.GcStruct('NODEOBJ', ('parent', OBJECT),
+                                         ('ref', lltype.Ptr(OBJECT)))
+    nodeobj = lltype.malloc(NODEOBJ)
+    nodeobjvalue = lltype.cast_opaque_ptr(llmemory.GCREF, nodeobj)
+    refdescr = cpu.fielddescrof(NODEOBJ, 'ref')
+
     arraydescr = cpu.arraydescrof(lltype.GcArray(lltype.Signed))
     floatarraydescr = cpu.arraydescrof(lltype.GcArray(lltype.Float))
 
@@ -95,13 +102,40 @@
     onedescr = cpu.fielddescrof(U, 'one')
 
     FUNC = lltype.FuncType([lltype.Signed], lltype.Signed)
-    nonwritedescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([], []))
-    writeadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([adescr], []))
-    writearraydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, EffectInfo([adescr], [arraydescr]))
-
-    cpu.class_sizes = {cpu.cast_adr_to_int(node_vtable_adr): cpu.sizeof(NODE),
-                      cpu.cast_adr_to_int(node_vtable_adr2): cpu.sizeof(NODE2),
-                       cpu.cast_adr_to_int(u_vtable_adr): cpu.sizeof(U)}
+    plaincalldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
+    nonwritedescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+                                    EffectInfo([], [], []))
+    writeadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+                                  EffectInfo([], [adescr], []))
+    writearraydescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+                                      EffectInfo([], [adescr], [arraydescr]))
+    readadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+                                 EffectInfo([adescr], [], []))
+    mayforcevirtdescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+                 EffectInfo([nextdescr], [], [],
+                            forces_virtual_or_virtualizable=True))
+    class LoopToken(AbstractDescr):
+        pass
+    asmdescr = LoopToken() # it can be whatever, it's not a descr though
+
+    from pypy.jit.metainterp.virtualref import VirtualRefInfo
+    class FakeWarmRunnerDesc:
+        pass
+    FakeWarmRunnerDesc.cpu = cpu
+    vrefinfo = VirtualRefInfo(FakeWarmRunnerDesc)
+    virtualtokendescr = vrefinfo.descr_virtual_token
+    virtualrefindexdescr = vrefinfo.descr_virtualref_index
+    virtualforceddescr = vrefinfo.descr_forced
+    jit_virtual_ref_vtable = vrefinfo.jit_virtual_ref_vtable
+    jvr_vtable_adr = llmemory.cast_ptr_to_adr(jit_virtual_ref_vtable)
+
+    cpu.class_sizes = {
+        cpu.cast_adr_to_int(node_vtable_adr): cpu.sizeof(NODE),
+        cpu.cast_adr_to_int(node_vtable_adr2): cpu.sizeof(NODE2),
+        cpu.cast_adr_to_int(u_vtable_adr): cpu.sizeof(U),
+        cpu.cast_adr_to_int(jvr_vtable_adr): cpu.sizeof(
+                                                   vrefinfo.JIT_VIRTUAL_REF),
+        }
     namespace = locals()
 
 class OOtypeMixin(object):

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_optimizeopt.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_optimizeopt.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_optimizeopt.py	Mon Jan 25 15:25:48 2010
@@ -87,7 +87,10 @@
 
 def test_reuse_vinfo():
     class FakeVInfo(object):
-        pass
+        def set_content(self, fieldnums):
+            self.fieldnums = fieldnums
+        def equals(self, fieldnums):
+            return self.fieldnums == fieldnums
     class FakeVirtualValue(optimizeopt.AbstractVirtualValue):
         def _make_virtual(self, *args):
             return FakeVInfo()
@@ -204,8 +207,9 @@
 
 class Storage(compile.ResumeGuardDescr):
     "for tests."
-    def __init__(self):
-        pass
+    def __init__(self, metainterp_sd=None, original_greenkey=None):
+        self.metainterp_sd = metainterp_sd
+        self.original_greenkey = original_greenkey
     def store_final_boxes(self, op, boxes):
         op.fail_args = boxes
     def __eq__(self, other):
@@ -246,7 +250,10 @@
             loop.token.specnodes = self.unpack_specnodes(spectext)
         #
         self.loop = loop
-        optimize_loop_1(FakeMetaInterpStaticData(self.cpu), loop)
+        metainterp_sd = FakeMetaInterpStaticData(self.cpu)
+        if hasattr(self, 'vrefinfo'):
+            metainterp_sd.virtualref_info = self.vrefinfo
+        optimize_loop_1(metainterp_sd, loop)
         #
         expected = self.parse(optops)
         self.assert_equal(loop, expected)
@@ -606,10 +613,10 @@
         p3sub = getfield_gc(p3, descr=nextdescr)
         i3 = getfield_gc(p3sub, descr=valuedescr)
         escape(i3)
+        p1 = new_with_vtable(ConstClass(node_vtable))
         p2sub = new_with_vtable(ConstClass(node_vtable2))
         setfield_gc(p2sub, i1, descr=valuedescr)
         setfield_gc(p2, p2sub, descr=nextdescr)
-        p1 = new_with_vtable(ConstClass(node_vtable))
         jump(i1, p1, p2)
         """
         # The same as test_p123_simple, but in the end the "old" p2 contains
@@ -642,6 +649,32 @@
 
     # ----------
 
+    def test_call_loopinvariant(self):
+        ops = """
+        [i1]
+        i2 = call_loopinvariant(1, i1, descr=nonwritedescr)
+        guard_no_exception() []
+        guard_value(i2, 1) []
+        i3 = call_loopinvariant(1, i1, descr=nonwritedescr)
+        guard_no_exception() []
+        guard_value(i2, 1) []
+        i4 = call_loopinvariant(1, i1, descr=nonwritedescr)
+        guard_no_exception() []
+        guard_value(i2, 1) []
+        jump(i1)
+        """
+        expected = """
+        [i1]
+        i2 = call(1, i1, descr=nonwritedescr)
+        guard_no_exception() []
+        guard_value(i2, 1) []
+        jump(i1)
+        """
+        self.optimize_loop(ops, 'Not', expected)
+
+
+    # ----------
+
     def test_virtual_1(self):
         ops = """
         [i, p0]
@@ -919,6 +952,26 @@
         """
         self.optimize_loop(ops, 'Not', expected)
 
+    def test_nonvirtual_dont_write_null_fields_on_force(self):
+        ops = """
+        [i]
+        p1 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p1, i, descr=valuedescr)
+        i1 = getfield_gc(p1, descr=valuedescr)
+        setfield_gc(p1, 0, descr=valuedescr)
+        escape(p1)
+        i2 = getfield_gc(p1, descr=valuedescr)
+        jump(i2)
+        """
+        expected = """
+        [i]
+        p1 = new_with_vtable(ConstClass(node_vtable))
+        escape(p1)
+        i2 = getfield_gc(p1, descr=valuedescr)
+        jump(i2)
+        """
+        self.optimize_loop(ops, 'Not', expected)
+
     def test_getfield_gc_pure_1(self):
         ops = """
         [i]
@@ -1019,6 +1072,24 @@
         """
         self.optimize_loop(ops, 'Not, Not', expected)
 
+    def test_nonvirtual_array_dont_write_null_fields_on_force(self):
+        ops = """
+        [i1]
+        p1 = new_array(5, descr=arraydescr)
+        setarrayitem_gc(p1, 0, i1, descr=arraydescr)
+        setarrayitem_gc(p1, 1, 0, descr=arraydescr)
+        escape(p1)
+        jump(i1)
+        """
+        expected = """
+        [i1]
+        p1 = new_array(5, descr=arraydescr)
+        setarrayitem_gc(p1, 0, i1, descr=arraydescr)
+        escape(p1)
+        jump(i1)
+        """
+        self.optimize_loop(ops, 'Not', expected)
+
     def test_varray_2(self):
         ops = """
         [i0, p1]
@@ -1293,6 +1364,204 @@
         """
         self.optimize_loop(ops, 'Not, Not', ops)
 
+    def test_duplicate_setfield_1(self):
+        ops = """
+        [p1, i1, i2]
+        setfield_gc(p1, i1, descr=valuedescr)
+        setfield_gc(p1, i2, descr=valuedescr)
+        jump(p1, i1, i2)
+        """
+        expected = """
+        [p1, i1, i2]
+        setfield_gc(p1, i2, descr=valuedescr)
+        jump(p1, i1, i2)
+        """
+        self.optimize_loop(ops, 'Not, Not, Not', expected)
+
+    def test_duplicate_setfield_2(self):
+        ops = """
+        [p1, i1, i3]
+        setfield_gc(p1, i1, descr=valuedescr)
+        i2 = getfield_gc(p1, descr=valuedescr)
+        setfield_gc(p1, i3, descr=valuedescr)
+        escape(i2)
+        jump(p1, i1, i3)
+        """
+        expected = """
+        [p1, i1, i3]
+        setfield_gc(p1, i3, descr=valuedescr)
+        escape(i1)
+        jump(p1, i1, i3)
+        """
+        self.optimize_loop(ops, 'Not, Not, Not', expected)
+
+    def test_duplicate_setfield_3(self):
+        ops = """
+        [p1, p2, i1, i3]
+        setfield_gc(p1, i1, descr=valuedescr)
+        i2 = getfield_gc(p2, descr=valuedescr)
+        setfield_gc(p1, i3, descr=valuedescr)
+        escape(i2)
+        jump(p1, p2, i1, i3)
+        """
+        # potential aliasing of p1 and p2 means that we cannot kill the
+        # the setfield_gc
+        self.optimize_loop(ops, 'Not, Not, Not, Not', ops)
+
+    def test_duplicate_setfield_4(self):
+        ops = """
+        [p1, i1, i2, p3]
+        setfield_gc(p1, i1, descr=valuedescr)
+        #
+        # some operations on which the above setfield_gc cannot have effect
+        i3 = getarrayitem_gc_pure(p3, 1, descr=arraydescr)
+        i4 = getarrayitem_gc(p3, i3, descr=arraydescr)
+        i5 = int_add(i3, i4)
+        setarrayitem_gc(p3, 0, i5, descr=arraydescr)
+        setfield_gc(p1, i4, descr=nextdescr)
+        #
+        setfield_gc(p1, i2, descr=valuedescr)
+        jump(p1, i1, i2, p3)
+        """
+        expected = """
+        [p1, i1, i2, p3]
+        #
+        i3 = getarrayitem_gc_pure(p3, 1, descr=arraydescr)
+        i4 = getarrayitem_gc(p3, i3, descr=arraydescr)
+        i5 = int_add(i3, i4)
+        setarrayitem_gc(p3, 0, i5, descr=arraydescr)
+        #
+        setfield_gc(p1, i2, descr=valuedescr)
+        setfield_gc(p1, i4, descr=nextdescr)
+        jump(p1, i1, i2, p3)
+        """
+        self.optimize_loop(ops, 'Not, Not, Not, Not', expected)
+
+    def test_duplicate_setfield_5(self):
+        ops = """
+        [p0, i1]
+        p1 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p1, i1, descr=valuedescr)
+        setfield_gc(p0, p1, descr=nextdescr)
+        setfield_raw(i1, i1, descr=valuedescr)    # random op with side-effects
+        p2 = getfield_gc(p0, descr=nextdescr)
+        i2 = getfield_gc(p2, descr=valuedescr)
+        setfield_gc(p0, NULL, descr=nextdescr)
+        escape(i2)
+        jump(p0, i1)
+        """
+        expected = """
+        [p0, i1]
+        setfield_raw(i1, i1, descr=valuedescr)
+        setfield_gc(p0, NULL, descr=nextdescr)
+        escape(i1)
+        jump(p0, i1)
+        """
+        self.optimize_loop(ops, 'Not, Not', expected)
+
+    def test_duplicate_setfield_sideeffects_1(self):
+        ops = """
+        [p1, i1, i2]
+        setfield_gc(p1, i1, descr=valuedescr)
+        escape()
+        setfield_gc(p1, i2, descr=valuedescr)
+        jump(p1, i1, i2)
+        """
+        self.optimize_loop(ops, 'Not, Not, Not', ops)
+
+    def test_duplicate_setfield_residual_guard_1(self):
+        ops = """
+        [p1, i1, i2, i3]
+        setfield_gc(p1, i1, descr=valuedescr)
+        guard_true(i3) []
+        i4 = int_neg(i2)
+        setfield_gc(p1, i2, descr=valuedescr)
+        jump(p1, i1, i2, i4)
+        """
+        self.optimize_loop(ops, 'Not, Not, Not, Not', ops)
+
+    def test_duplicate_setfield_residual_guard_2(self):
+        # the difference with the previous test is that the field value is
+        # a virtual, which we try hard to keep virtual
+        ops = """
+        [p1, i2, i3]
+        p2 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p1, p2, descr=nextdescr)
+        guard_true(i3) []
+        i4 = int_neg(i2)
+        setfield_gc(p1, NULL, descr=nextdescr)
+        jump(p1, i2, i4)
+        """
+        expected = """
+        [p1, i2, i3]
+        guard_true(i3) [p1]
+        i4 = int_neg(i2)
+        setfield_gc(p1, NULL, descr=nextdescr)
+        jump(p1, i2, i4)
+        """
+        self.optimize_loop(ops, 'Not, Not, Not', expected)
+
+    def test_duplicate_setfield_residual_guard_3(self):
+        ops = """
+        [p1, i2, i3]
+        p2 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p2, i2, descr=valuedescr)
+        setfield_gc(p1, p2, descr=nextdescr)
+        guard_true(i3) []
+        i4 = int_neg(i2)
+        setfield_gc(p1, NULL, descr=nextdescr)
+        jump(p1, i2, i4)
+        """
+        expected = """
+        [p1, i2, i3]
+        guard_true(i3) [p1, i2]
+        i4 = int_neg(i2)
+        setfield_gc(p1, NULL, descr=nextdescr)
+        jump(p1, i2, i4)
+        """
+        self.optimize_loop(ops, 'Not, Not, Not', expected)
+
+    def test_duplicate_setfield_residual_guard_4(self):
+        # test that the setfield_gc does not end up between int_eq and
+        # the following guard_true
+        ops = """
+        [p1, i1, i2, i3]
+        setfield_gc(p1, i1, descr=valuedescr)
+        i5 = int_eq(i3, 5)
+        guard_true(i5) []
+        i4 = int_neg(i2)
+        setfield_gc(p1, i2, descr=valuedescr)
+        jump(p1, i1, i2, i4)
+        """
+        self.optimize_loop(ops, 'Not, Not, Not, Not', ops)
+
+    def test_duplicate_setfield_aliasing(self):
+        # a case where aliasing issues (and not enough cleverness) mean
+        # that we fail to remove any setfield_gc
+        ops = """
+        [p1, p2, i1, i2, i3]
+        setfield_gc(p1, i1, descr=valuedescr)
+        setfield_gc(p2, i2, descr=valuedescr)
+        setfield_gc(p1, i3, descr=valuedescr)
+        jump(p1, p2, i1, i2, i3)
+        """
+        self.optimize_loop(ops, 'Not, Not, Not, Not, Not', ops)
+
+    def test_duplicate_setfield_guard_value_const(self):
+        ops = """
+        [p1, i1, i2]
+        guard_value(p1, ConstPtr(myptr)) []
+        setfield_gc(p1, i1, descr=valuedescr)
+        setfield_gc(ConstPtr(myptr), i2, descr=valuedescr)
+        jump(p1, i1, i2)
+        """
+        expected = """
+        [i1, i2]
+        setfield_gc(ConstPtr(myptr), i2, descr=valuedescr)
+        jump(i1, i2)
+        """
+        self.optimize_loop(ops, 'Constant(myptr), Not, Not', expected)
+
     def test_duplicate_getarrayitem_1(self):
         ops = """
         [p1]
@@ -1592,6 +1861,36 @@
         """
         self.optimize_loop(ops, "Not", expected)
 
+    def test_remove_duplicate_pure_op(self):
+        ops = """
+        [p1, p2]
+        i1 = oois(p1, p2)
+        i2 = oois(p1, p2)
+        i3 = int_add(i1, 1)
+        i3b = int_is_true(i3)
+        guard_true(i3b) []
+        i4 = int_add(i2, 1)
+        i4b = int_is_true(i4)
+        guard_true(i4b) []
+        escape(i3)
+        escape(i4)
+        guard_true(i1) []
+        guard_true(i2) []
+        jump(p1, p2)
+        """
+        expected = """
+        [p1, p2]
+        i1 = oois(p1, p2)
+        i3 = int_add(i1, 1)
+        i3b = int_is_true(i3)
+        guard_true(i3b) []
+        escape(i3)
+        escape(i3)
+        guard_true(i1) []
+        jump(p1, p2)
+        """
+        self.optimize_loop(ops, "Not, Not", expected)
+
     # ----------
 
     def make_fail_descr(self):
@@ -1634,6 +1933,14 @@
                 tag = ('virtual', self.namespace[match.group(2)])
             virtuals[pvar] = (tag, None, fieldstext)
         #
+        r2 = re.compile(r"([\w\d()]+)[.](\w+)\s*=\s*([\w\d()]+)")
+        pendingfields = []
+        for match in r2.finditer(text):
+            pvar = match.group(1)
+            pfieldname = match.group(2)
+            pfieldvar = match.group(3)
+            pendingfields.append((pvar, pfieldname, pfieldvar))
+        #
         def _variables_equal(box, varname, strict):
             if varname not in virtuals:
                 if strict:
@@ -1655,11 +1962,21 @@
                 else:
                     virtuals[varname] = tag, box, fieldstext
         #
-        basetext = text[:ends[0]]
+        basetext = text.splitlines()[0]
         varnames = [s.strip() for s in basetext.split(',')]
+        if varnames == ['']:
+            varnames = []
         assert len(boxes) == len(varnames)
         for box, varname in zip(boxes, varnames):
             _variables_equal(box, varname, strict=True)
+        for pvar, pfieldname, pfieldvar in pendingfields:
+            box = oparse.getvar(pvar)
+            fielddescr = self.namespace[pfieldname.strip()]
+            fieldbox = executor.execute(self.cpu,
+                                        rop.GETFIELD_GC,
+                                        fielddescr,
+                                        box)
+            _variables_equal(fieldbox, pfieldvar, strict=True)
         #
         for match in parts:
             pvar = match.group(1)
@@ -1918,6 +2235,57 @@
             where p7v is a node_vtable, valuedescr=iv
             ''')
 
+    def test_expand_fail_lazy_setfield_1(self):
+        self.make_fail_descr()
+        ops = """
+        [p1, i2, i3]
+        p2 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p2, i2, descr=valuedescr)
+        setfield_gc(p1, p2, descr=nextdescr)
+        guard_true(i3, descr=fdescr) []
+        i4 = int_neg(i2)
+        setfield_gc(p1, NULL, descr=nextdescr)
+        jump(p1, i2, i4)
+        """
+        expected = """
+        [p1, i2, i3]
+        guard_true(i3, descr=fdescr) [p1, i2]
+        i4 = int_neg(i2)
+        setfield_gc(p1, NULL, descr=nextdescr)
+        jump(p1, i2, i4)
+        """
+        self.optimize_loop(ops, 'Not, Not, Not', expected)
+        self.loop.inputargs[0].value = self.nodebox.value
+        self.check_expanded_fail_descr('''
+            p1.nextdescr = p2
+            where p2 is a node_vtable, valuedescr=i2
+            ''')
+
+    def test_expand_fail_lazy_setfield_2(self):
+        self.make_fail_descr()
+        ops = """
+        [i2, i3]
+        p2 = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p2, i2, descr=valuedescr)
+        setfield_gc(ConstPtr(myptr), p2, descr=nextdescr)
+        guard_true(i3, descr=fdescr) []
+        i4 = int_neg(i2)
+        setfield_gc(ConstPtr(myptr), NULL, descr=nextdescr)
+        jump(i2, i4)
+        """
+        expected = """
+        [i2, i3]
+        guard_true(i3, descr=fdescr) [i2]
+        i4 = int_neg(i2)
+        setfield_gc(ConstPtr(myptr), NULL, descr=nextdescr)
+        jump(i2, i4)
+        """
+        self.optimize_loop(ops, 'Not, Not', expected)
+        self.check_expanded_fail_descr('''
+            ConstPtr(myptr).nextdescr = p2
+            where p2 is a node_vtable, valuedescr=i2
+            ''')
+
 
 class TestLLtype(BaseTestOptimizeOpt, LLtypeMixin):
 
@@ -2031,6 +2399,233 @@
         """
         self.optimize_loop(ops, 'Not, Not, Not', expected)
 
+    def test_residual_call_invalidates_some_read_caches_1(self):
+        ops = """
+        [p1, i1, p2, i2]
+        setfield_gc(p1, i1, descr=valuedescr)
+        setfield_gc(p2, i2, descr=adescr)
+        i3 = call(i1, descr=readadescr)
+        setfield_gc(p1, i3, descr=valuedescr)
+        setfield_gc(p2, i3, descr=adescr)
+        jump(p1, i1, p2, i2)
+        """
+        expected = """
+        [p1, i1, p2, i2]
+        setfield_gc(p2, i2, descr=adescr)
+        i3 = call(i1, descr=readadescr)
+        setfield_gc(p1, i3, descr=valuedescr)
+        setfield_gc(p2, i3, descr=adescr)
+        jump(p1, i1, p2, i2)
+        """
+        self.optimize_loop(ops, 'Not, Not, Not, Not', expected)
+
+    def test_residual_call_invalidates_some_read_caches_2(self):
+        ops = """
+        [p1, i1, p2, i2]
+        setfield_gc(p1, i1, descr=valuedescr)
+        setfield_gc(p2, i2, descr=adescr)
+        i3 = call(i1, descr=writeadescr)
+        setfield_gc(p1, i3, descr=valuedescr)
+        setfield_gc(p2, i3, descr=adescr)
+        jump(p1, i1, p2, i2)
+        """
+        expected = """
+        [p1, i1, p2, i2]
+        setfield_gc(p2, i2, descr=adescr)
+        i3 = call(i1, descr=writeadescr)
+        setfield_gc(p1, i3, descr=valuedescr)
+        setfield_gc(p2, i3, descr=adescr)
+        jump(p1, i1, p2, i2)
+        """
+        self.optimize_loop(ops, 'Not, Not, Not, Not', expected)
+
+    def test_residual_call_invalidates_some_read_caches_3(self):
+        ops = """
+        [p1, i1, p2, i2]
+        setfield_gc(p1, i1, descr=valuedescr)
+        setfield_gc(p2, i2, descr=adescr)
+        i3 = call(i1, descr=plaincalldescr)
+        setfield_gc(p1, i3, descr=valuedescr)
+        setfield_gc(p2, i3, descr=adescr)
+        jump(p1, i1, p2, i2)
+        """
+        self.optimize_loop(ops, 'Not, Not, Not, Not', ops)
+
+    def test_call_assembler_invalidates_caches(self):
+        ops = '''
+        [p1, i1]
+        setfield_gc(p1, i1, descr=valuedescr)
+        i3 = call_assembler(i1, descr=asmdescr)
+        setfield_gc(p1, i3, descr=valuedescr)
+        jump(p1, i3)
+        '''
+        self.optimize_loop(ops, 'Not, Not', ops)
+
+    def test_vref_nonvirtual_nonescape(self):
+        ops = """
+        [p1]
+        p2 = virtual_ref(p1, 5)
+        virtual_ref_finish(p2, p1)
+        jump(p1)
+        """
+        expected = """
+        [p1]
+        i0 = force_token()
+        jump(p1)
+        """
+        self.optimize_loop(ops, 'Not', expected)
+
+    def test_vref_nonvirtual_escape(self):
+        ops = """
+        [p1]
+        p2 = virtual_ref(p1, 5)
+        escape(p2)
+        virtual_ref_finish(p2, p1)
+        jump(p1)
+        """
+        expected = """
+        [p1]
+        i0 = force_token()
+        p2 = new_with_vtable(ConstClass(jit_virtual_ref_vtable))
+        setfield_gc(p2, i0, descr=virtualtokendescr)
+        setfield_gc(p2, 5, descr=virtualrefindexdescr)
+        escape(p2)
+        setfield_gc(p2, p1, descr=virtualforceddescr)
+        setfield_gc(p2, 0, descr=virtualtokendescr)
+        jump(p1)
+        """
+        # XXX we should optimize a bit more the case of a nonvirtual.
+        # in theory it is enough to just do 'p2 = p1'.
+        self.optimize_loop(ops, 'Not', expected)
+
+    def test_vref_virtual_1(self):
+        ops = """
+        [p0, i1]
+        #
+        p1 = new_with_vtable(ConstClass(node_vtable))
+        p1b = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p1b, 252, descr=valuedescr)
+        setfield_gc(p1, p1b, descr=nextdescr)
+        #
+        p2 = virtual_ref(p1, 3)
+        setfield_gc(p0, p2, descr=nextdescr)
+        call_may_force(i1, descr=mayforcevirtdescr)
+        guard_not_forced() [i1]
+        virtual_ref_finish(p2, p1)
+        setfield_gc(p0, NULL, descr=nextdescr)
+        jump(p0, i1)
+        """
+        expected = """
+        [p0, i1]
+        i3 = force_token()
+        #
+        p2 = new_with_vtable(ConstClass(jit_virtual_ref_vtable))
+        setfield_gc(p2, i3, descr=virtualtokendescr)
+        setfield_gc(p2, 3, descr=virtualrefindexdescr)
+        setfield_gc(p0, p2, descr=nextdescr)
+        #
+        call_may_force(i1, descr=mayforcevirtdescr)
+        guard_not_forced() [i1]
+        setfield_gc(p0, NULL, descr=nextdescr)
+        #
+        p1 = new_with_vtable(ConstClass(node_vtable))
+        p1b = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p1b, 252, descr=valuedescr)
+        setfield_gc(p1, p1b, descr=nextdescr)
+        setfield_gc(p2, p1, descr=virtualforceddescr)
+        setfield_gc(p2, 0, descr=virtualtokendescr)
+        #
+        jump(p0, i1)
+        """
+        self.optimize_loop(ops, 'Not, Not', expected)
+
+    def test_vref_virtual_2(self):
+        self.make_fail_descr()
+        ops = """
+        [p0, i1]
+        #
+        p1 = new_with_vtable(ConstClass(node_vtable))
+        p1b = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p1b, i1, descr=valuedescr)
+        setfield_gc(p1, p1b, descr=nextdescr)
+        #
+        p2 = virtual_ref(p1, 2)
+        setfield_gc(p0, p2, descr=nextdescr)
+        call_may_force(i1, descr=mayforcevirtdescr)
+        guard_not_forced(descr=fdescr) [p2, p1]
+        virtual_ref_finish(p2, p1)
+        setfield_gc(p0, NULL, descr=nextdescr)
+        jump(p0, i1)
+        """
+        expected = """
+        [p0, i1]
+        i3 = force_token()
+        #
+        p2 = new_with_vtable(ConstClass(jit_virtual_ref_vtable))
+        setfield_gc(p2, i3, descr=virtualtokendescr)
+        setfield_gc(p2, 2, descr=virtualrefindexdescr)
+        setfield_gc(p0, p2, descr=nextdescr)
+        #
+        call_may_force(i1, descr=mayforcevirtdescr)
+        guard_not_forced(descr=fdescr) [p2, i1]
+        setfield_gc(p0, NULL, descr=nextdescr)
+        #
+        p1 = new_with_vtable(ConstClass(node_vtable))
+        p1b = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p1b, i1, descr=valuedescr)
+        setfield_gc(p1, p1b, descr=nextdescr)
+        setfield_gc(p2, p1, descr=virtualforceddescr)
+        setfield_gc(p2, 0, descr=virtualtokendescr)
+        #
+        jump(p0, i1)
+        """
+        # the point of this test is that 'i1' should show up in the fail_args
+        # of 'guard_not_forced', because it was stored in the virtual 'p1b'.
+        self.optimize_loop(ops, 'Not, Not', expected)
+        self.check_expanded_fail_descr('''p2, p1
+            where p1 is a node_vtable, nextdescr=p1b
+            where p1b is a node_vtable, valuedescr=i1
+            ''')
+
+    def test_vref_virtual_and_lazy_setfield(self):
+        self.make_fail_descr()
+        ops = """
+        [p0, i1]
+        #
+        p1 = new_with_vtable(ConstClass(node_vtable))
+        p1b = new_with_vtable(ConstClass(node_vtable))
+        setfield_gc(p1b, i1, descr=valuedescr)
+        setfield_gc(p1, p1b, descr=nextdescr)
+        #
+        p2 = virtual_ref(p1, 2)
+        setfield_gc(p0, p2, descr=refdescr)
+        call(i1, descr=nonwritedescr)
+        guard_no_exception(descr=fdescr) [p2, p1]
+        virtual_ref_finish(p2, p1)
+        setfield_gc(p0, NULL, descr=refdescr)
+        jump(p0, i1)
+        """
+        expected = """
+        [p0, i1]
+        i3 = force_token()
+        call(i1, descr=nonwritedescr)
+        guard_no_exception(descr=fdescr) [i3, i1, p0]
+        setfield_gc(p0, NULL, descr=refdescr)
+        jump(p0, i1)
+        """
+        self.optimize_loop(ops, 'Not, Not', expected)
+        # the fail_args contain [i3, i1, p0]:
+        #  - i3 is from the virtual expansion of p2
+        #  - i1 is from the virtual expansion of p1
+        #  - p0 is from the extra pendingfields
+        self.loop.inputargs[0].value = self.nodeobjvalue
+        self.check_expanded_fail_descr('''p2, p1
+            p0.refdescr = p2
+            where p2 is a jit_virtual_ref_vtable, virtualtokendescr=i3, virtualrefindexdescr=2
+            where p1 is a node_vtable, nextdescr=p1b
+            where p1b is a node_vtable, valuedescr=i1
+            ''')
+
 
 class TestOOtype(BaseTestOptimizeOpt, OOtypeMixin):
 

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_pyjitpl.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_pyjitpl.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_pyjitpl.py	Mon Jan 25 15:25:48 2010
@@ -33,7 +33,8 @@
 def test_simple_opimpl_exist():
     rop = resoperation.rop
     for opnum, opname in resoperation.opname.items():
-        if opnum in (rop.SAME_AS, rop.CALL_PURE, rop.OOSEND_PURE):
+        if opnum in (rop.SAME_AS, rop.CALL_PURE, rop.OOSEND_PURE,
+                     rop.FORCE_TOKEN):
             continue
         if rop._NOSIDEEFFECT_FIRST <= opnum <= rop._NOSIDEEFFECT_LAST:
             assert hasattr(pyjitpl.MIFrame, 'opimpl_' + opname.lower()), opname
@@ -88,7 +89,7 @@
         assert box.value == referencebox.value
         return True
     metainterp = pyjitpl.MetaInterp(FakeStaticData())
-    metainterp.history = History(None)
+    metainterp.history = History()
     b1 = BoxInt(1)
     b2 = BoxInt(2)
     c3 = ConstInt(3)

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_recursive.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_recursive.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_recursive.py	Mon Jan 25 15:25:48 2010
@@ -1,5 +1,5 @@
 import py
-from pypy.rlib.jit import JitDriver, we_are_jitted, OPTIMIZER_SIMPLE
+from pypy.rlib.jit import JitDriver, we_are_jitted, OPTIMIZER_SIMPLE, hint
 from pypy.jit.metainterp.test.test_basic import LLJitMixin, OOJitMixin
 from pypy.jit.metainterp.policy import StopAtXPolicy
 from pypy.rpython.annlowlevel import hlstr
@@ -646,9 +646,303 @@
                 result += f('-c-----------l-', i+100)
         self.meta_interp(g, [10], backendopt=True)
         self.check_aborted_count(1)
-        self.check_history(call_may_force=1, call=0)
+        self.check_history(call_assembler=1, call=0)
         self.check_tree_loop_count(3)
+
+    def test_directly_call_assembler(self):
+        driver = JitDriver(greens = ['codeno'], reds = ['i'],
+                           get_printable_location = lambda codeno : str(codeno),
+                           can_inline = lambda codeno : False)
+
+        def portal(codeno):
+            i = 0
+            while i < 10:
+                driver.can_enter_jit(codeno = codeno, i = i)
+                driver.jit_merge_point(codeno = codeno, i = i)
+                if codeno == 2:
+                    portal(1)
+                i += 1
+
+        self.meta_interp(portal, [2], inline=True)
+        self.check_history(call_assembler=1)
+
+    def test_directly_call_assembler_return(self):
+        driver = JitDriver(greens = ['codeno'], reds = ['i', 'k'],
+                           get_printable_location = lambda codeno : str(codeno),
+                           can_inline = lambda codeno : False)
+
+        def portal(codeno):
+            i = 0
+            k = codeno
+            while i < 10:
+                driver.can_enter_jit(codeno = codeno, i = i, k = k)
+                driver.jit_merge_point(codeno = codeno, i = i, k = k)
+                if codeno == 2:
+                    k = portal(1)
+                i += 1
+            return k
+
+        self.meta_interp(portal, [2], inline=True)
+        self.check_history(call_assembler=1)
+
+    def test_directly_call_assembler_raise(self):
+
+        class MyException(Exception):
+            def __init__(self, x):
+                self.x = x
+        
+        driver = JitDriver(greens = ['codeno'], reds = ['i'],
+                           get_printable_location = lambda codeno : str(codeno),
+                           can_inline = lambda codeno : False)
+
+        def portal(codeno):
+            i = 0
+            while i < 10:
+                driver.can_enter_jit(codeno = codeno, i = i)
+                driver.jit_merge_point(codeno = codeno, i = i)
+                if codeno == 2:
+                    try:
+                        portal(1)
+                    except MyException, me:
+                        i += me.x
+                i += 1
+            if codeno == 1:
+                raise MyException(1)
+
+        self.meta_interp(portal, [2], inline=True)
+        self.check_history(call_assembler=1)        
+
+    def test_directly_call_assembler_fail_guard(self):
+        driver = JitDriver(greens = ['codeno'], reds = ['i', 'k'],
+                           get_printable_location = lambda codeno : str(codeno),
+                           can_inline = lambda codeno : False)
+
+        def portal(codeno, k):
+            i = 0
+            while i < 10:
+                driver.can_enter_jit(codeno=codeno, i=i, k=k)
+                driver.jit_merge_point(codeno=codeno, i=i, k=k)
+                if codeno == 2:
+                    k += portal(1, k)
+                elif k > 40:
+                    if i % 2:
+                        k += 1
+                    else:
+                        k += 2
+                k += 1
+                i += 1
+            return k
+
+        res = self.meta_interp(portal, [2, 0], inline=True)
+        assert res == 13542
+
+    def test_directly_call_assembler_virtualizable(self):
+        class Thing(object):
+            def __init__(self, val):
+                self.val = val
+        
+        class Frame(object):
+            _virtualizable2_ = ['thing']
+        
+        driver = JitDriver(greens = ['codeno'], reds = ['frame', 'i'],
+                           virtualizables = ['frame'],
+                           get_printable_location = lambda codeno : str(codeno),
+                           can_inline = lambda codeno : False)
+
+        def main(codeno):
+            frame = Frame()
+            frame.thing = Thing(0)
+            portal(codeno, frame)
+            return frame.thing.val
+
+        def portal(codeno, frame):
+            i = 0
+            while i < 10:
+                driver.can_enter_jit(frame=frame, codeno=codeno, i=i)
+                driver.jit_merge_point(frame=frame, codeno=codeno, i=i)
+                nextval = frame.thing.val
+                if codeno == 0:
+                    subframe = Frame()
+                    subframe.thing = Thing(nextval)
+                    nextval = portal(1, subframe)
+                frame.thing = Thing(nextval + 1)
+                i += 1
+            return frame.thing.val
+
+        res = self.meta_interp(main, [0], inline=True)
+        assert res == main(0)
+
+    def test_directly_call_assembler_virtualizable_force(self):
+        class Thing(object):
+            def __init__(self, val):
+                self.val = val
         
+        class Frame(object):
+            _virtualizable2_ = ['thing']
+        
+        driver = JitDriver(greens = ['codeno'], reds = ['frame', 'i'],
+                           virtualizables = ['frame'],
+                           get_printable_location = lambda codeno : str(codeno),
+                           can_inline = lambda codeno : False)
+        class SomewhereElse(object):
+            pass
+
+        somewhere_else = SomewhereElse()
+
+        def change(newthing):
+            somewhere_else.frame.thing = newthing
+
+        def main(codeno):
+            frame = Frame()
+            somewhere_else.frame = frame
+            frame.thing = Thing(0)
+            portal(codeno, frame)
+            return frame.thing.val
+
+        def portal(codeno, frame):
+            i = 0
+            while i < 10:
+                driver.can_enter_jit(frame=frame, codeno=codeno, i=i)
+                driver.jit_merge_point(frame=frame, codeno=codeno, i=i)
+                nextval = frame.thing.val
+                if codeno == 0:
+                    subframe = Frame()
+                    subframe.thing = Thing(nextval)
+                    nextval = portal(1, subframe)
+                elif frame.thing.val > 40:
+                    change(Thing(13))
+                    nextval = 13
+                frame.thing = Thing(nextval + 1)
+                i += 1
+            return frame.thing.val
+
+        res = self.meta_interp(main, [0], inline=True,
+                               policy=StopAtXPolicy(change))
+        assert res == main(0)
+
+    def test_directly_call_assembler_virtualizable_with_array(self):
+        myjitdriver = JitDriver(greens = ['codeno'], reds = ['n', 'frame', 'x'],
+                                virtualizables = ['frame'],
+                                can_inline = lambda codeno : False)
+
+        class Frame(object):
+            _virtualizable2_ = ['l[*]', 's']
+
+            def __init__(self, l, s):
+                self = hint(self, access_directly=True,
+                            fresh_virtualizable=True)
+                self.l = l
+                self.s = s
+
+        def main(codeno, n, a):
+            frame = Frame([a, a+1, a+2, a+3], 0)
+            return f(codeno, n, a, frame)
+        
+        def f(codeno, n, a, frame):
+            x = 0
+            while n > 0:
+                myjitdriver.can_enter_jit(codeno=codeno, frame=frame, n=n, x=x)
+                myjitdriver.jit_merge_point(codeno=codeno, frame=frame, n=n,
+                                            x=x)
+                frame.s = hint(frame.s, promote=True)
+                n -= 1
+                x += frame.l[frame.s]
+                frame.s += 1
+                if codeno == 0:
+                    subframe = Frame([n, n+1, n+2, n+3], 0)
+                    x += f(1, 10, 1, subframe)
+                x += frame.l[frame.s]
+                x += len(frame.l)
+                frame.s -= 1
+            return x
+
+        res = self.meta_interp(main, [0, 10, 1], listops=True, inline=True)
+        assert res == main(0, 10, 1)
+
+    def test_directly_call_assembler_virtualizable_force_blackhole(self):
+        class Thing(object):
+            def __init__(self, val):
+                self.val = val
+        
+        class Frame(object):
+            _virtualizable2_ = ['thing']
+        
+        driver = JitDriver(greens = ['codeno'], reds = ['frame', 'i'],
+                           virtualizables = ['frame'],
+                           get_printable_location = lambda codeno : str(codeno),
+                           can_inline = lambda codeno : False)
+        class SomewhereElse(object):
+            pass
+
+        somewhere_else = SomewhereElse()
+
+        def change(newthing, arg):
+            print arg
+            if arg > 30:
+                somewhere_else.frame.thing = newthing
+                arg = 13
+            return arg
+
+        def main(codeno):
+            frame = Frame()
+            somewhere_else.frame = frame
+            frame.thing = Thing(0)
+            portal(codeno, frame)
+            return frame.thing.val
+
+        def portal(codeno, frame):
+            i = 0
+            while i < 10:
+                driver.can_enter_jit(frame=frame, codeno=codeno, i=i)
+                driver.jit_merge_point(frame=frame, codeno=codeno, i=i)
+                nextval = frame.thing.val
+                if codeno == 0:
+                    subframe = Frame()
+                    subframe.thing = Thing(nextval)
+                    nextval = portal(1, subframe)
+                else:
+                    nextval = change(Thing(13), frame.thing.val)
+                frame.thing = Thing(nextval + 1)
+                i += 1
+            return frame.thing.val
+
+        res = self.meta_interp(main, [0], inline=True,
+                               policy=StopAtXPolicy(change))
+        assert res == main(0)
+
+    def test_assembler_call_red_args(self):
+        driver = JitDriver(greens = ['codeno'], reds = ['i', 'k'],
+                           get_printable_location = lambda codeno : str(codeno),
+                           can_inline = lambda codeno : False)
+
+        def residual(k):
+            if k > 40:
+                return 0
+            return 1
+
+        def portal(codeno, k):
+            i = 0
+            while i < 10:
+                driver.can_enter_jit(codeno=codeno, i=i, k=k)
+                driver.jit_merge_point(codeno=codeno, i=i, k=k)
+                if codeno == 2:
+                    k += portal(residual(k), k)
+                if codeno == 0:
+                    k += 2
+                elif codeno == 1:
+                    k += 1
+                i += 1
+            return k
+
+        res = self.meta_interp(portal, [2, 0], inline=True,
+                               policy=StopAtXPolicy(residual))
+        assert res == portal(2, 0)
+        self.check_loops(call_assembler=2)
+
+    # There is a test which I fail to write.
+    #   * what happens if we call recursive_call while blackholing
+    #     this seems to be completely corner case and not really happening
+    #     in the wild
 
 class TestLLtype(RecursiveTests, LLJitMixin):
     pass

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_resume.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_resume.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_resume.py	Mon Jan 25 15:25:48 2010
@@ -12,6 +12,8 @@
     rd_frame_info_list = None
     rd_numb = None
     rd_consts = []
+    rd_virtuals = None
+    rd_pendingfields = None
 
 def test_tag():
     assert tag(3, 1) == rffi.r_short(3<<2|1)
@@ -40,6 +42,12 @@
     assert not tagged_list_eq([tag(1, TAGBOX)], [tag(-2, TAGBOX)])
     assert not tagged_list_eq([tag(1, TAGBOX), tag(-2, TAGBOX)], [tag(1, TAGBOX)])
 
+def test_vinfo():
+    v1 = AbstractVirtualInfo()
+    v1.set_content([1, 2, 4])
+    assert v1.equals([1, 2, 4])
+    assert not v1.equals([1, 2, 6])
+
 class MyMetaInterp:
     _already_allocated_resume_virtuals = None
 
@@ -80,7 +88,6 @@
                             tag(0, TAGBOX),
                             tag(1, TAGBOX)])
     storage.rd_numb = numb
-    storage.rd_virtuals = None
 
     b1s, b2s, b3s = [BoxInt(), BoxPtr(), BoxInt()]
     assert b1s != b3s
@@ -103,7 +110,6 @@
                             tag(0, TAGBOX),
                             tag(1, TAGBOX)])
     storage.rd_numb = numb
-    storage.rd_virtuals = None
     b1s, b2s, b3s = [BoxInt(), BoxPtr(), BoxInt()]
     assert b1s != b3s
     reader = ResumeDataReader(storage, [b1s, b2s, b3s], MyMetaInterp())
@@ -125,6 +131,7 @@
         rd_virtuals = [FakeVinfo(), None]
         rd_numb = []
         rd_consts = []
+        rd_pendingfields = None
     class FakeMetainterp(object):
         _already_allocated_resume_virtuals = None
         cpu = None
@@ -175,7 +182,6 @@
     assert fi.jitcode is jitcode
     assert fi.pc == 1
     assert fi.exception_target == 2
-    assert fi.level == 1
 
     jitcode1 = "JITCODE1"
     frame1 = FakeFrame(jitcode, 3, 4)
@@ -184,7 +190,6 @@
     assert fi1.jitcode is jitcode
     assert fi1.pc == 3
     assert fi1.exception_target == 4
-    assert fi1.level == 2
 
 def test_Numbering_create():
     l = [1, 2]
@@ -203,29 +208,32 @@
     fs = [FakeFrame("code0", 0, -1, b1, c1, b2)]
 
     storage = Storage()
-    capture_resumedata(fs, None, storage)
+    capture_resumedata(fs, None, [], storage)
 
     assert fs[0].parent_resumedata_snapshot is None
     assert fs[0].parent_resumedata_frame_info_list is None
 
     assert storage.rd_frame_info_list.prev is None
     assert storage.rd_frame_info_list.jitcode == 'code0'
-    assert storage.rd_snapshot.prev is None
-    assert storage.rd_snapshot.boxes == fs[0].env
-    assert storage.rd_snapshot.boxes is not fs[0].env
+    assert storage.rd_snapshot.boxes == []    # for virtualrefs
+    snapshot = storage.rd_snapshot.prev
+    assert snapshot.prev is None
+    assert snapshot.boxes == fs[0].env
+    assert snapshot.boxes is not fs[0].env
 
     storage = Storage()
     fs = [FakeFrame("code0", 0, -1, b1, c1, b2),
           FakeFrame("code1", 3, 7, b3, c2, b1),
           FakeFrame("code2", 9, -1, c3, b2)]
-    capture_resumedata(fs, None, storage)
+    capture_resumedata(fs, None, [], storage)
 
     frame_info_list = storage.rd_frame_info_list
     assert frame_info_list.prev is fs[2].parent_resumedata_frame_info_list
     assert frame_info_list.jitcode == 'code2'
     assert frame_info_list.pc == 9
 
-    snapshot = storage.rd_snapshot
+    assert storage.rd_snapshot.boxes == []    # for virtualrefs
+    snapshot = storage.rd_snapshot.prev
     assert snapshot.prev is fs[2].parent_resumedata_snapshot
     assert snapshot.boxes == fs[2].env
     assert snapshot.boxes is not fs[2].env
@@ -249,8 +257,9 @@
     fs[2].env = [b2, b3]
     fs[2].pc = 15
     vbs = [b1, b2]
-    capture_resumedata(fs, vbs, storage)
-       
+    vrs = [b3]
+    capture_resumedata(fs, vbs, vrs, storage)
+
     frame_info_list = storage.rd_frame_info_list
     assert frame_info_list.prev is fs[2].parent_resumedata_frame_info_list
     assert frame_info_list.jitcode == 'code2'
@@ -261,6 +270,10 @@
     assert snapshot.boxes is not vbs
 
     snapshot = snapshot.prev
+    assert snapshot.boxes == vrs
+    assert snapshot.boxes is not vrs
+
+    snapshot = snapshot.prev
     assert snapshot.prev is fs[2].parent_resumedata_snapshot
     assert snapshot.boxes == fs[2].env
 
@@ -278,7 +291,7 @@
     fs = [FakeFrame("code0", 0, -1, b1, c1, b2),
           FakeFrame("code1", 3, 7, b3, c2, b1),
           FakeFrame("code2", 9, -1, c3, b2)]
-    capture_resumedata(fs, None, storage)
+    capture_resumedata(fs, None, [], storage)
     memo = ResumeDataLoopMemo(FakeMetaInterpStaticData())
     modifier = ResumeDataVirtualAdder(storage, memo)
     liveboxes = modifier.finish({})
@@ -289,7 +302,7 @@
 
     result = rebuild_from_resumedata(metainterp, newboxes, storage,
                                      False)
-    assert result is None
+    assert result == (None, [])
     fs2 = [FakeFrame("code0", 0, -1, b1t, c1, b2t),
            FakeFrame("code1", 3, 7, b3t, c2, b1t),
            FakeFrame("code2", 9, -1, c3, b2t)]
@@ -302,7 +315,7 @@
     fs = [FakeFrame("code0", 0, -1, b1, c1, b2),
           FakeFrame("code1", 3, 7, b3, c2, b1),
           FakeFrame("code2", 9, -1, c3, b2)]
-    capture_resumedata(fs, [b4], storage)
+    capture_resumedata(fs, [b4], [], storage)
     memo = ResumeDataLoopMemo(FakeMetaInterpStaticData())
     modifier = ResumeDataVirtualAdder(storage, memo)
     liveboxes = modifier.finish({})
@@ -313,7 +326,7 @@
 
     result = rebuild_from_resumedata(metainterp, newboxes, storage,
                                      True)
-    assert result == [b4t]
+    assert result == ([b4t], [])
     fs2 = [FakeFrame("code0", 0, -1, b1t, c1, b2t),
            FakeFrame("code1", 3, 7, b3t, c2, b1t),
            FakeFrame("code2", 9, -1, c3, b2t)]
@@ -326,10 +339,10 @@
     fs = [FakeFrame("code0", 0, -1, b1, c1, b2),
           FakeFrame("code1", 3, 7, b3, c2, b1),
           FakeFrame("code2", 9, -1, c3, b2)]
-    capture_resumedata(fs, None, storage)
+    capture_resumedata(fs, None, [], storage)
     storage2 = Storage()
     fs = fs[:-1] + [FakeFrame("code2", 10, -1, c3, b2, b4)]
-    capture_resumedata(fs, None, storage2)
+    capture_resumedata(fs, None, [], storage2)
     
     memo = ResumeDataLoopMemo(FakeMetaInterpStaticData())
     modifier = ResumeDataVirtualAdder(storage, memo)
@@ -345,7 +358,7 @@
 
     result = rebuild_from_resumedata(metainterp, newboxes, storage,
                                      False)
-    assert result is None
+    assert result == (None, [])
     fs2 = [FakeFrame("code0", 0, -1, b1t, c1, b2t),
            FakeFrame("code1", 3, 7, b3t, c2, b1t),
            FakeFrame("code2", 9, -1, c3, b2t)]
@@ -356,7 +369,7 @@
     metainterp.framestack = []
     result = rebuild_from_resumedata(metainterp, newboxes, storage2,
                                      False)
-    assert result is None
+    assert result == (None, [])
     fs2 = fs2[:-1] + [FakeFrame("code2", 10, -1, c3, b2t, b4t)]
     assert metainterp.framestack == fs2
 
@@ -384,10 +397,10 @@
     fs = [FakeFrame("code0", 0, -1, b1, c1, b2),
           FakeFrame("code1", 3, 7, b3, c2, b1),
           FakeFrame("code2", 9, -1, c3, b2)]
-    capture_resumedata(fs, None, storage)
+    capture_resumedata(fs, None, [], storage)
     storage2 = Storage()
     fs = fs[:-1] + [FakeFrame("code2", 10, -1, c3, b2, b4)]
-    capture_resumedata(fs, None, storage2)
+    capture_resumedata(fs, None, [], storage2)
     
     memo = ResumeDataLoopMemo(FakeMetaInterpStaticData())
     values = {b2: virtual_value(b2, b5, c4)}
@@ -443,7 +456,7 @@
                       LLtypeMixin.nodebox.constbox()]
     storage = Storage()
     fs = [FakeFrame("code0", 0, -1, c1, b2, b3)]
-    capture_resumedata(fs, None, storage)
+    capture_resumedata(fs, None, [], storage)
     
     memo = ResumeDataLoopMemo(FakeMetaInterpStaticData())
     values = {b2: virtual_value(b2, b5, c4)}
@@ -455,7 +468,7 @@
 
     storage2 = Storage()
     fs = [FakeFrame("code0", 0, -1, b1, b4, b2)]
-    capture_resumedata(fs, None, storage2)
+    capture_resumedata(fs, None, [], storage2)
     values[b4] = virtual_value(b4, b6, c4)
     modifier = ResumeDataVirtualAdder(storage2, memo)
     liveboxes = modifier.finish(values)
@@ -468,7 +481,7 @@
     b1, b2, b3 = [BoxPtr(), BoxPtr(), BoxInt()]
     storage = Storage()
     fs = [FakeFrame("code0", 0, -1, b1, b2)]
-    capture_resumedata(fs, None, storage)
+    capture_resumedata(fs, None, [], storage)
     
     memo = ResumeDataLoopMemo(FakeMetaInterpStaticData())
     v1 = virtual_value(b1, b3, None)
@@ -962,6 +975,46 @@
     assert ptr.a == 111
     assert ptr.b == lltype.nullptr(LLtypeMixin.NODE)
 
+
+def test_virtual_adder_pending_fields():
+    b2s, b4s = [BoxPtr(), BoxPtr()]
+    storage = Storage()
+    memo = ResumeDataLoopMemo(FakeMetaInterpStaticData())
+    modifier = ResumeDataVirtualAdder(storage, memo)
+    modifier.liveboxes_from_env = {}
+    modifier.liveboxes = {}
+    modifier.vfieldboxes = {}
+
+    v2 = OptValue(b2s)
+    v4 = OptValue(b4s)
+    modifier.register_box(b2s)
+    modifier.register_box(b4s)
+
+    values = {b4s: v4, b2s: v2}
+    liveboxes = []
+    modifier._number_virtuals(liveboxes, values, 0)
+    assert liveboxes == [b2s, b4s]
+    modifier._add_pending_fields([(LLtypeMixin.nextdescr, b2s, b4s)])
+    storage.rd_consts = memo.consts[:]
+    storage.rd_numb = None
+    # resume
+    demo55.next = lltype.nullptr(LLtypeMixin.NODE)
+    b2t = BoxPtr(demo55o)
+    b4t = BoxPtr(demo66o)
+    newboxes = _resume_remap(liveboxes, [b2s, b4s], b2t, b4t)
+
+    metainterp = MyMetaInterp()
+    reader = ResumeDataReader(storage, newboxes, metainterp)
+    assert reader.virtuals is None
+    trace = metainterp.trace
+    b2set = (rop.SETFIELD_GC, [b2t, b4t], None, LLtypeMixin.nextdescr)
+    expected = [b2set]
+
+    for x, y in zip(expected, trace):
+        assert x == y
+    assert demo55.next == demo66
+
+
 def test_invalidation_needed():
     class options:
         failargs_limit = 10

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_virtualizable.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_virtualizable.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_virtualizable.py	Mon Jan 25 15:25:48 2010
@@ -28,7 +28,7 @@
                   hop.inputconst(lltype.Void, hop.args_v[1].value),
                   hop.inputconst(lltype.Void, {})]
         hop.exception_cannot_occur()
-        return hop.genop('promote_virtualizable',
+        return hop.genop('jit_force_virtualizable',
                          args_v, resulttype=lltype.Void)
 
 debug_print = lloperation.llop.debug_print
@@ -907,6 +907,39 @@
         res = self.meta_interp(f, [123], policy=StopAtXPolicy(g))
         assert res == f(123)
 
+    def test_bridge_forces(self):
+        jitdriver = JitDriver(greens = [], reds = ['frame'],
+                              virtualizables = ['frame'])
+        
+        class Frame(object):
+            _virtualizable2_ = ['x', 'y']
+        class SomewhereElse:
+            pass
+        somewhere_else = SomewhereElse()
+
+        def g():
+            n = somewhere_else.top_frame.y + 700
+            debug_print(lltype.Void, '-+-+-+-+- external write:', n)
+            somewhere_else.top_frame.y = n
+
+        def f(n):
+            frame = Frame()
+            frame.x = n
+            frame.y = 10
+            somewhere_else.counter = 0
+            somewhere_else.top_frame = frame
+            while frame.x > 0:
+                jitdriver.can_enter_jit(frame=frame)
+                jitdriver.jit_merge_point(frame=frame)
+                if frame.y > 17:
+                    g()
+                frame.x -= 5
+                frame.y += 1
+            return frame.y
+
+        res = self.meta_interp(f, [123], policy=StopAtXPolicy(g))
+        assert res == f(123)
+
     def test_promote_index_in_virtualizable_list(self):
         jitdriver = JitDriver(greens = [], reds = ['frame', 'n'],
                               virtualizables = ['frame'])
@@ -980,9 +1013,11 @@
                     for block, op in graph.iterblockops()
                         if op.opname == 'direct_call']
 
-        assert direct_calls(f_graph) == ['__init__', 'force_if_necessary', 'll_portal_runner']
-        assert direct_calls(portal_graph) == ['force_if_necessary', 'maybe_enter_jit']
-
+        assert direct_calls(f_graph) == ['__init__',
+                                         'force_virtualizable_if_necessary',
+                                         'll_portal_runner']
+        assert direct_calls(portal_graph)==['force_virtualizable_if_necessary',
+                                            'maybe_enter_jit']
         assert direct_calls(init_graph) == []
 
     def test_virtual_child_frame(self):
@@ -1158,8 +1193,9 @@
         self.check_loops(getfield_gc=0, setfield_gc=0)
 
     def test_blackhole_should_not_reenter(self):
-        from pypy.jit.backend.test.support import BaseCompiledMixin
-        if isinstance(self, BaseCompiledMixin):
+        # Armin thinks this can occur and does not make interpreters slower
+        # so we don't check for assertionerror, to be discussed
+        if not self.basic:
             py.test.skip("purely frontend test")
 
         myjitdriver = JitDriver(greens = [], reds = ['frame', 'fail'],
@@ -1200,8 +1236,9 @@
             f(10, True)
             return f(10, False)
 
-        einfo = py.test.raises(AssertionError, self.meta_interp, main, [])
-        assert einfo.value.args[0] == "reentering same frame via blackhole"
+        self.meta_interp(main, [])
+        #einfo = py.test.raises(AssertionError, self.meta_interp, main, [])
+        #assert einfo.value.args[0] == "reentering same frame via blackhole"
 
     def test_inlining(self):
         class Frame(object):

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_warmspot.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_warmspot.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_warmspot.py	Mon Jan 25 15:25:48 2010
@@ -4,6 +4,7 @@
 from pypy.rlib.jit import JitDriver, OPTIMIZER_FULL, OPTIMIZER_SIMPLE
 from pypy.rlib.jit import unroll_safe
 from pypy.jit.backend.llgraph import runner
+from pypy.jit.metainterp.history import BoxInt
 
 from pypy.jit.metainterp.test.test_basic import LLJitMixin, OOJitMixin
 
@@ -283,3 +284,80 @@
 class TestOOWarmspot(WarmspotTests, OOJitMixin):
     CPUClass = runner.OOtypeCPU
     type_system = 'ootype'
+
+class TestWarmspotDirect(object):
+    def setup_class(cls):
+        from pypy.jit.metainterp.typesystem import llhelper
+        from pypy.jit.metainterp.support import annotate
+        from pypy.jit.metainterp.warmspot import WarmRunnerDesc
+        from pypy.rpython.lltypesystem.rclass import OBJECT, OBJECT_VTABLE
+        from pypy.rpython.lltypesystem import lltype, llmemory
+        exc_vtable = lltype.malloc(OBJECT_VTABLE, immortal=True)
+        cls.exc_vtable = exc_vtable
+
+        class FakeFailDescr(object):
+            def __init__(self, no):
+                self.no = no
+            
+            def handle_fail(self, metainterp_sd):
+                if self.no == 0:
+                    raise metainterp_sd.warmrunnerdesc.DoneWithThisFrameInt(3)
+                if self.no == 1:
+                    raise metainterp_sd.warmrunnerdesc.ContinueRunningNormally(
+                        [BoxInt(0), BoxInt(1)])
+                if self.no == 3:
+                    exc = lltype.malloc(OBJECT)
+                    exc.typeptr = exc_vtable
+                    raise metainterp_sd.warmrunnerdesc.ExitFrameWithExceptionRef(
+                        metainterp_sd.cpu,
+                        lltype.cast_opaque_ptr(llmemory.GCREF, exc))
+                return self.no
+
+        class FakeCPU(object):
+            supports_floats = False
+            ts = llhelper
+            translate_support_code = False
+
+            def __init__(self, *args, **kwds):
+                pass
+
+            def nodescr(self, *args, **kwds):
+                pass
+            fielddescrof = nodescr
+            calldescrof  = nodescr
+            sizeof       = nodescr
+
+            def get_fail_descr_from_number(self, no):
+                return FakeFailDescr(no)
+
+            def execute_token(self, token):
+                assert token == 2
+                return FakeFailDescr(1)
+
+        driver = JitDriver(reds = ['red'], greens = ['green'])
+        
+        def f(green):
+            red = 0
+            while red < 10:
+                driver.can_enter_jit(red=red, green=green)
+                driver.jit_merge_point(red=red, green=green)
+                red += 1
+            return red
+
+        rtyper = annotate(f, [0])
+        translator = rtyper.annotator.translator
+        translator.config.translation.gc = 'hybrid'
+        cls.desc = WarmRunnerDesc(translator, CPUClass=FakeCPU)
+
+    def test_call_helper(self):
+        from pypy.rpython.llinterp import LLException
+        
+        assert self.desc.assembler_call_helper(0, 0) == 3
+        assert self.desc.assembler_call_helper(1, 0) == 10
+        assert self.desc.assembler_call_helper(2, 0) == 10
+        try:
+            self.desc.assembler_call_helper(3, 0)
+        except LLException, lle:
+            assert lle[0] == self.exc_vtable
+        else:
+            py.test.fail("DID NOT RAISE")

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_warmstate.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_warmstate.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_warmstate.py	Mon Jan 25 15:25:48 2010
@@ -165,6 +165,7 @@
     class FakeWarmRunnerDesc:
         can_inline_ptr = None
         get_printable_location_ptr = None
+        confirm_enter_jit_ptr = None
         green_args_spec = [lltype.Signed, lltype.Float]
     class FakeCell:
         dont_trace_here = False
@@ -192,6 +193,7 @@
         green_args_spec = [lltype.Signed, lltype.Float]
         can_inline_ptr = llhelper(CAN_INLINE, can_inline)
         get_printable_location_ptr = None
+        confirm_enter_jit_ptr = None
     state = WarmEnterState(FakeWarmRunnerDesc())
     def jit_getter(*args):
         return FakeCell()
@@ -212,7 +214,30 @@
         green_args_spec = [lltype.Signed, lltype.Float]
         can_inline_ptr = None
         get_printable_location_ptr = llhelper(GET_LOCATION, get_location)
+        confirm_enter_jit_ptr = None
+        get_jitcell_at_ptr = None
     state = WarmEnterState(FakeWarmRunnerDesc())
     state.make_jitdriver_callbacks()
     res = state.get_location_str([BoxInt(5), BoxFloat(42.5)])
     assert res == "hi there"
+
+def test_make_jitdriver_callbacks_4():
+    def confirm_enter_jit(x, y, z):
+        assert x == 5
+        assert y == 42.5
+        assert z == 3
+        return True
+    ENTER_JIT = lltype.Ptr(lltype.FuncType([lltype.Signed, lltype.Float,
+                                            lltype.Signed], lltype.Bool))
+    class FakeWarmRunnerDesc:
+        rtyper = None
+        green_args_spec = [lltype.Signed, lltype.Float]
+        can_inline_ptr = None
+        get_printable_location_ptr = None
+        confirm_enter_jit_ptr = llhelper(ENTER_JIT, confirm_enter_jit)
+        get_jitcell_at_ptr = None
+
+    state = WarmEnterState(FakeWarmRunnerDesc())
+    state.make_jitdriver_callbacks()
+    res = state.confirm_enter_jit(5, 42.5, 3)
+    assert res is True

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_ztranslation.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_ztranslation.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/test/test_ztranslation.py	Mon Jan 25 15:25:48 2010
@@ -7,6 +7,8 @@
 from pypy.rpython.lltypesystem import lltype, llmemory
 from pypy.rpython.ootypesystem import ootype
 
+py.test.skip("Broken")
+
 class TranslationTest:
 
     CPUClass = None

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/virtualizable.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/virtualizable.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/virtualizable.py	Mon Jan 25 15:25:48 2010
@@ -11,8 +11,8 @@
 
 
 class VirtualizableInfo:
-    token_none    = 0
-    token_tracing = -1
+    TOKEN_NONE            = 0
+    TOKEN_TRACING_RESCALL = -1
 
     def __init__(self, warmrunnerdesc):
         self.warmrunnerdesc = warmrunnerdesc
@@ -153,16 +153,17 @@
 
     def finish(self):
         #
-        def force_if_necessary(virtualizable):
+        def force_virtualizable_if_necessary(virtualizable):
             if virtualizable.vable_token:
                 self.force_now(virtualizable)
-        force_if_necessary._always_inline_ = True
+        force_virtualizable_if_necessary._always_inline_ = True
         #
         all_graphs = self.warmrunnerdesc.translator.graphs
         ts = self.warmrunnerdesc.cpu.ts
         (_, FUNCPTR) = ts.get_FuncType([self.VTYPEPTR], lltype.Void)
-        funcptr = self.warmrunnerdesc.helper_func(FUNCPTR, force_if_necessary)
-        rvirtualizable2.replace_promote_virtualizable_with_call(
+        funcptr = self.warmrunnerdesc.helper_func(
+            FUNCPTR, force_virtualizable_if_necessary)
+        rvirtualizable2.replace_force_virtualizable_with_call(
             all_graphs, self.VTYPEPTR, funcptr)
 
     def unwrap_virtualizable_box(self, virtualizable_box):
@@ -176,7 +177,7 @@
         return rvirtualizable2.match_virtualizable_type(TYPE, self.VTYPEPTR)
 
     def reset_vable_token(self, virtualizable):
-        virtualizable.vable_token = self.token_none
+        virtualizable.vable_token = self.TOKEN_NONE
 
     def clear_vable_token(self, virtualizable):
         if virtualizable.vable_token:
@@ -185,14 +186,14 @@
 
     def tracing_before_residual_call(self, virtualizable):
         assert not virtualizable.vable_token
-        virtualizable.vable_token = self.token_tracing
+        virtualizable.vable_token = self.TOKEN_TRACING_RESCALL
 
     def tracing_after_residual_call(self, virtualizable):
         if virtualizable.vable_token:
             # not modified by the residual call; assert that it is still
-            # set to 'tracing_vable_rti' and clear it.
-            assert virtualizable.vable_token == self.token_tracing
-            virtualizable.vable_token = self.token_none
+            # set to TOKEN_TRACING_RESCALL and clear it.
+            assert virtualizable.vable_token == self.TOKEN_TRACING_RESCALL
+            virtualizable.vable_token = self.TOKEN_NONE
             return False
         else:
             # marker "modified during residual call" set.
@@ -200,32 +201,36 @@
 
     def force_now(self, virtualizable):
         token = virtualizable.vable_token
-        virtualizable.vable_token = self.token_none
-        if token == self.token_tracing:
+        if token == self.TOKEN_TRACING_RESCALL:
             # The values in the virtualizable are always correct during
-            # tracing.  We only need to reset vable_token to token_none
+            # tracing.  We only need to reset vable_token to TOKEN_NONE
             # as a marker for the tracing, to tell it that this
             # virtualizable escapes.
-            pass
+            virtualizable.vable_token = self.TOKEN_NONE
         else:
             from pypy.jit.metainterp.compile import ResumeGuardForcedDescr
-            faildescr = self.cpu.force(token)
-            assert isinstance(faildescr, ResumeGuardForcedDescr)
-            faildescr.force_virtualizable(self, virtualizable, token)
+            ResumeGuardForcedDescr.force_now(self.cpu, token)
+            assert virtualizable.vable_token == self.TOKEN_NONE
     force_now._dont_inline_ = True
 
+    def forced_vable(self, virtualizable_boxes):
+        virtualizable_box = virtualizable_boxes[-1]
+        virtualizable = self.unwrap_virtualizable_box(virtualizable_box)
+        self.write_boxes(virtualizable, virtualizable_boxes)
+        virtualizable.vable_token = self.TOKEN_NONE
+
 # ____________________________________________________________
 #
 # The 'vable_token' field of a virtualizable is either 0, -1, or points
 # into the CPU stack to a particular field in the current frame.  It is:
 #
-#   1. 0 (token_none) if not in the JIT at all, except as described below.
+#   1. 0 (TOKEN_NONE) if not in the JIT at all, except as described below.
 #
 #   2. equal to 0 when tracing is in progress; except:
 #
-#   3. equal to -1 (token_tracing) during tracing when we do a residual call,
-#      calling random unknown other parts of the interpreter; it is
-#      reset to 0 as soon as something occurs to the virtualizable.
+#   3. equal to -1 (TOKEN_TRACING_RESCALL) during tracing when we do a
+#      residual call, calling random unknown other parts of the interpreter;
+#      it is reset to 0 as soon as something occurs to the virtualizable.
 #
 #   4. when running the machine code with a virtualizable, it is set
 #      to the address in the CPU stack by the FORCE_TOKEN operation.

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/warmspot.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/warmspot.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/warmspot.py	Mon Jan 25 15:25:48 2010
@@ -1,4 +1,4 @@
-import sys
+import sys, py
 from pypy.rpython.lltypesystem import lltype, llmemory, rclass, rstr
 from pypy.rpython.ootypesystem import ootype
 from pypy.rpython.annlowlevel import llhelper, MixLevelHelperAnnotator,\
@@ -11,7 +11,7 @@
 from pypy.rlib.objectmodel import we_are_translated
 from pypy.rlib.unroll import unrolling_iterable
 from pypy.rlib.rarithmetic import r_uint, intmask
-from pypy.rlib.debug import debug_print
+from pypy.rlib.debug import debug_print, fatalerror
 from pypy.rpython.lltypesystem.lloperation import llop
 from pypy.translator.simplify import get_funcobj, get_functype
 from pypy.translator.unsimplify import call_final_function
@@ -140,7 +140,7 @@
 
 # ____________________________________________________________
 
-class WarmRunnerDesc:
+class WarmRunnerDesc(object):
 
     def __init__(self, translator, policy=None, backendopt=True, CPUClass=None,
                  optimizer=None, **kwds):
@@ -162,9 +162,13 @@
 
         self.build_meta_interp(CPUClass, **kwds)
         self.make_args_specification()
+        #
+        from pypy.jit.metainterp.virtualref import VirtualRefInfo
+        self.metainterp_sd.virtualref_info = VirtualRefInfo(self)
         if self.jitdriver.virtualizables:
             from pypy.jit.metainterp.virtualizable import VirtualizableInfo
             self.metainterp_sd.virtualizable_info = VirtualizableInfo(self)
+        #
         self.make_exception_classes()
         self.make_driverhook_graphs()
         self.make_enter_function()
@@ -177,6 +181,7 @@
                                           )
         self.rewrite_can_enter_jit()
         self.rewrite_set_param()
+        self.rewrite_force_virtual()
         self.add_profiler_finish()
         self.metainterp_sd.finish_setup(optimizer=optimizer)
 
@@ -339,9 +344,7 @@
                 if sys.stdout == sys.__stdout__:
                     import pdb; pdb.post_mortem(sys.exc_info()[2])
                 raise
-            debug_print('~~~ Crash in JIT!')
-            debug_print('~~~ %s' % (e,))
-            raise history.CrashInJIT("crash in JIT")
+            fatalerror('~~~ Crash in JIT! %s' % (e,), traceback=True)
         crash_in_jit._dont_inline_ = True
 
         if self.translator.rtyper.type_system.name == 'lltypesystem':
@@ -397,9 +400,13 @@
             annhelper, self.jitdriver.can_inline, annmodel.s_Bool)
         self.get_printable_location_ptr = self._make_hook_graph(
             annhelper, self.jitdriver.get_printable_location, s_Str)
+        self.confirm_enter_jit_ptr = self._make_hook_graph(
+            annhelper, self.jitdriver.confirm_enter_jit, annmodel.s_Bool,
+            onlygreens=False)
         annhelper.finish()
 
-    def _make_hook_graph(self, annhelper, func, s_result, s_first_arg=None):
+    def _make_hook_graph(self, annhelper, func, s_result, s_first_arg=None,
+                         onlygreens=True):
         if func is None:
             return None
         #
@@ -407,7 +414,9 @@
         if s_first_arg is not None:
             extra_args_s.append(s_first_arg)
         #
-        args_s = self.portal_args_s[:len(self.green_args_spec)]
+        args_s = self.portal_args_s
+        if onlygreens:
+            args_s = args_s[:len(self.green_args_spec)]
         graph = annhelper.getgraph(func, extra_args_s + args_s, s_result)
         funcptr = annhelper.graph2delayed(graph)
         return funcptr
@@ -432,7 +441,8 @@
          self.PTR_JIT_ENTER_FUNCTYPE) = self.cpu.ts.get_FuncType(ALLARGS, lltype.Void)
         (self.PORTAL_FUNCTYPE,
          self.PTR_PORTAL_FUNCTYPE) = self.cpu.ts.get_FuncType(ALLARGS, RESTYPE)
-        
+        (_, self.PTR_ASSEMBLER_HELPER_FUNCTYPE) = self.cpu.ts.get_FuncType(
+            [lltype.Signed, llmemory.GCREF], RESTYPE)
 
     def rewrite_can_enter_jit(self):
         FUNC = self.JIT_ENTER_FUNCTYPE
@@ -545,9 +555,63 @@
                     else:
                         value = cast_base_ptr_to_instance(Exception, value)
                         raise Exception, value
-        
+
+        self.ll_portal_runner = ll_portal_runner # for debugging
         self.portal_runner_ptr = self.helper_func(self.PTR_PORTAL_FUNCTYPE,
                                                   ll_portal_runner)
+        self.cpu.portal_calldescr = self.cpu.calldescrof(
+            self.PTR_PORTAL_FUNCTYPE.TO,
+            self.PTR_PORTAL_FUNCTYPE.TO.ARGS,
+            self.PTR_PORTAL_FUNCTYPE.TO.RESULT)
+
+        vinfo = self.metainterp_sd.virtualizable_info
+
+        def assembler_call_helper(failindex, virtualizableref):
+            fail_descr = self.cpu.get_fail_descr_from_number(failindex)
+            while True:
+                try:
+                    if vinfo is not None:
+                        virtualizable = lltype.cast_opaque_ptr(
+                            vinfo.VTYPEPTR, virtualizableref)
+                        vinfo.reset_vable_token(virtualizable)
+                    loop_token = fail_descr.handle_fail(self.metainterp_sd)
+                    fail_descr = self.cpu.execute_token(loop_token)
+                except self.ContinueRunningNormally, e:
+                    args = ()
+                    for _, name, _ in portalfunc_ARGS:
+                        v = getattr(e, name)
+                        args = args + (v,)
+                    return ll_portal_runner(*args)
+                except self.DoneWithThisFrameVoid:
+                    assert result_kind == 'void'
+                    return
+                except self.DoneWithThisFrameInt, e:
+                    assert result_kind == 'int'
+                    return lltype.cast_primitive(RESULT, e.result)
+                except self.DoneWithThisFrameRef, e:
+                    assert result_kind == 'ref'
+                    return ts.cast_from_ref(RESULT, e.result)
+                except self.DoneWithThisFrameFloat, e:
+                    assert result_kind == 'float'
+                    return e.result
+                except self.ExitFrameWithExceptionRef, e:
+                    value = ts.cast_to_baseclass(e.value)
+                    if not we_are_translated():
+                        raise LLException(ts.get_typeptr(value), value)
+                    else:
+                        value = cast_base_ptr_to_instance(Exception, value)
+                        raise Exception, value
+
+        self.assembler_call_helper = assembler_call_helper # for debugging
+        self.cpu.assembler_helper_ptr = self.helper_func(
+            self.PTR_ASSEMBLER_HELPER_FUNCTYPE,
+            assembler_call_helper)
+        # XXX a bit ugly sticking
+        if vinfo is not None:
+            self.cpu.index_of_virtualizable = (vinfo.index_of_virtualizable -
+                                               self.num_green_args)
+        else:
+            self.cpu.index_of_virtualizable = -1
 
         # ____________________________________________________________
         # Now mutate origportalgraph to end with a call to portal_runner_ptr
@@ -599,6 +663,13 @@
             op.opname = 'direct_call'
             op.args[:3] = [closures[funcname]]
 
+    def rewrite_force_virtual(self):
+        if self.cpu.ts.name != 'lltype':
+            py.test.skip("rewrite_force_virtual: port it to ootype")
+        all_graphs = self.translator.graphs
+        vrefinfo = self.metainterp_sd.virtualref_info
+        vrefinfo.replace_force_virtual_with_call(all_graphs)
+
 
 def decode_hp_hint_args(op):
     # Returns (list-of-green-vars, list-of-red-vars) without Voids.

Modified: pypy/branch/stringbuilder2/pypy/jit/metainterp/warmstate.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/metainterp/warmstate.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/metainterp/warmstate.py	Mon Jan 25 15:25:48 2010
@@ -196,6 +196,7 @@
         get_jitcell = self.make_jitcell_getter()
         set_future_values = self.make_set_future_values()
         self.make_jitdriver_callbacks()
+        confirm_enter_jit = self.confirm_enter_jit
 
         def maybe_compile_and_run(*args):
             """Entry point to the JIT.  Called at the point with the
@@ -211,8 +212,8 @@
             if vinfo is not None:
                 virtualizable = args[vinfo.index_of_virtualizable]
                 virtualizable = vinfo.cast_to_vtype(virtualizable)
-                assert virtualizable != globaldata.blackhole_virtualizable, (
-                    "reentering same frame via blackhole")
+                if globaldata.blackhole_virtualizable == virtualizable:
+                    return
             else:
                 virtualizable = None
 
@@ -227,6 +228,9 @@
                     cell.counter = n
                     return
                 # bound reached; start tracing
+                if not confirm_enter_jit(*args):
+                    cell.counter = 0
+                    return
                 from pypy.jit.metainterp.pyjitpl import MetaInterp
                 metainterp = MetaInterp(metainterp_sd)
                 try:
@@ -237,6 +241,8 @@
                     self.disable_noninlinable_function(metainterp)
                     raise
             else:
+                if not confirm_enter_jit(*args):
+                    return
                 # machine code was already compiled for these greenargs
                 # get the assembler and fill in the boxes
                 set_future_values(*args[num_green_args:])
@@ -252,7 +258,7 @@
                 if vinfo is not None:
                     vinfo.reset_vable_token(virtualizable)
                 loop_token = fail_descr.handle_fail(metainterp_sd)
-
+       
         maybe_compile_and_run._dont_inline_ = True
         self.maybe_compile_and_run = maybe_compile_and_run
         return maybe_compile_and_run
@@ -448,6 +454,7 @@
         unwrap_greenkey = self.make_unwrap_greenkey()
         if can_inline_ptr is None:
             def can_inline_callable(*greenargs):
+                # XXX shouldn't it be False by default?
                 return True
         else:
             rtyper = self.warmrunnerdesc.rtyper
@@ -465,7 +472,16 @@
             greenargs = unwrap_greenkey(greenkey)
             return can_inline(*greenargs)
         self.can_inline_callable = can_inline_greenkey
-
+        
+        get_jitcell = self.make_jitcell_getter()
+        def get_assembler_token(greenkey):
+            greenargs = unwrap_greenkey(greenkey)
+            cell = get_jitcell(*greenargs)
+            if cell.counter >= 0:
+                return None
+            return cell.entry_loop_token
+        self.get_assembler_token = get_assembler_token
+        
         #
         get_location_ptr = self.warmrunnerdesc.get_printable_location_ptr
         if get_location_ptr is None:
@@ -483,3 +499,16 @@
                     res = hlstr(res)
                 return res
         self.get_location_str = get_location_str
+        #
+        confirm_enter_jit_ptr = self.warmrunnerdesc.confirm_enter_jit_ptr
+        if confirm_enter_jit_ptr is None:
+            def confirm_enter_jit(*args):
+                return True
+        else:
+            rtyper = self.warmrunnerdesc.rtyper
+            #
+            def confirm_enter_jit(*args):
+                fn = support.maybe_on_top_of_llinterp(rtyper,
+                                                      confirm_enter_jit_ptr)
+                return fn(*args)
+        self.confirm_enter_jit = confirm_enter_jit

Modified: pypy/branch/stringbuilder2/pypy/jit/tool/jitoutput.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/tool/jitoutput.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/tool/jitoutput.py	Mon Jan 25 15:25:48 2010
@@ -26,8 +26,12 @@
     (('opt_ops',), '^opt ops:\s+(\d+)$'),
     (('opt_guards',), '^opt guards:\s+(\d+)$'),
     (('forcings',), '^forcings:\s+(\d+)$'),
-    (('trace_too_long',), '^trace too long:\s+(\d+)$'),
-    (('bridge_abort',), '^bridge abort:\s+(\d+)$'),    
+    (('abort.trace_too_long',), '^abort: trace too long:\s+(\d+)$'),
+    (('abort.compiling',), '^abort: compiling:\s+(\d+)$'),
+    (('abort.vable_escape',), '^abort: vable escape:\s+(\d+)$'),
+    (('nvirtuals',), '^nvirtuals:\s+(\d+)$'),
+    (('nvholes',), '^nvholes:\s+(\d+)$'),
+    (('nvreused',), '^nvreused:\s+(\d+)$'),
     ]
 
 class Ops(object):
@@ -35,6 +39,11 @@
     calls = 0
     pure_calls = 0
 
+class Aborts(object):
+    trace_too_long = 0
+    compiling = 0
+    vable_escape = 0
+
 class OutputInfo(object):
     tracing_no = 0
     tracing_time = 0.0
@@ -45,13 +54,16 @@
     guards = 0
     opt_ops = 0
     opt_guards = 0
-    trace_too_long = 0
-    bridge_abort = 0
+    forcings = 0
+    nvirtuals = 0
+    nvholes = 0
+    nvreused = 0
 
     def __init__(self):
         self.ops = Ops()
         self.recorded_ops = Ops()
         self.blackholed_ops = Ops()
+        self.abort = Aborts()
 
 def parse_prof(output):
     lines = output.splitlines()

Modified: pypy/branch/stringbuilder2/pypy/jit/tool/showstats.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/tool/showstats.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/tool/showstats.py	Mon Jan 25 15:25:48 2010
@@ -1,4 +1,6 @@
 #!/usr/bin/env python
+from __future__ import division
+
 import autopath
 import sys, py
 from pypy.tool import logparser
@@ -9,7 +11,7 @@
 def main(argv):
     log = logparser.parse_log_file(argv[0])
     parts = logparser.extract_category(log, "jit-log-opt-")
-    for oplist in parts:
+    for i, oplist in enumerate(parts):
         loop = parse(oplist, no_namespace=True)
         num_ops = 0
         num_dmp = 0
@@ -21,7 +23,7 @@
                 num_ops += 1
             if op.is_guard():
                 num_guards += 1
-        print "Loop, length: %d, opcodes: %d, guards: %d" % (num_ops, num_dmp, num_guards)
+        print "Loop #%d, length: %d, opcodes: %d, guards: %d, %f" % (i, num_ops, num_dmp, num_guards, num_ops/num_dmp)
 
 if __name__ == '__main__':
     main(sys.argv[1:])

Modified: pypy/branch/stringbuilder2/pypy/jit/tool/test/test_jitoutput.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/jit/tool/test/test_jitoutput.py	(original)
+++ pypy/branch/stringbuilder2/pypy/jit/tool/test/test_jitoutput.py	Mon Jan 25 15:25:48 2010
@@ -46,8 +46,6 @@
     assert info.opt_ops == 6
     assert info.opt_guards == 1
     assert info.forcings == 0
-    assert info.trace_too_long == 0
-    assert info.bridge_abort == 0    
 
 DATA = '''Tracing:         1       0.006992
 Backend:        1       0.000525
@@ -66,8 +64,12 @@
 opt ops:                6
 opt guards:             1
 forcings:               1
-trace too long:         2
-bridge abort:           3
+abort: trace too long:  10
+abort: compiling:       11
+abort: vable escape:    12
+nvirtuals:              13
+nvholes:                14
+nvreused:               15
 '''
 
 def test_parse():
@@ -90,5 +92,9 @@
     assert info.opt_ops == 6
     assert info.opt_guards == 1
     assert info.forcings == 1
-    assert info.trace_too_long == 2
-    assert info.bridge_abort == 3  
+    assert info.abort.trace_too_long == 10
+    assert info.abort.compiling == 11
+    assert info.abort.vable_escape == 12
+    assert info.nvirtuals == 13
+    assert info.nvholes == 14
+    assert info.nvreused == 15

Modified: pypy/branch/stringbuilder2/pypy/lib/_ctypes/array.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/lib/_ctypes/array.py	(original)
+++ pypy/branch/stringbuilder2/pypy/lib/_ctypes/array.py	Mon Jan 25 15:25:48 2010
@@ -90,7 +90,7 @@
     def _CData_retval(self, resbuffer):
         raise NotImplementedError
 
-    def _CData_value(self, value):
+    def from_param(self, value):
         # array accepts very strange parameters as part of structure
         # or function argument...
         from ctypes import c_char, c_wchar
@@ -104,7 +104,7 @@
                 if len(value) > self._length_:
                     raise RuntimeError("Invalid length")
                 value = self(*value)
-        return _CDataMeta._CData_value(self, value)
+        return _CDataMeta.from_param(self, value)
 
 def array_get_slice_params(self, index):
     if index.step is not None:

Modified: pypy/branch/stringbuilder2/pypy/lib/_ctypes/primitive.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/lib/_ctypes/primitive.py	(original)
+++ pypy/branch/stringbuilder2/pypy/lib/_ctypes/primitive.py	Mon Jan 25 15:25:48 2010
@@ -290,6 +290,8 @@
             self.value = value
 
     def _ensure_objects(self):
+        if self._type_ in 'zZ':
+            return self._objects
         return None
 
     def _getvalue(self):

Modified: pypy/branch/stringbuilder2/pypy/lib/_ctypes/structure.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/lib/_ctypes/structure.py	(original)
+++ pypy/branch/stringbuilder2/pypy/lib/_ctypes/structure.py	Mon Jan 25 15:25:48 2010
@@ -130,10 +130,10 @@
     def _alignmentofinstances(self):
         return self._ffistruct.alignment
 
-    def _CData_value(self, value):
+    def from_param(self, value):
         if isinstance(value, tuple):
             value = self(*value)
-        return _CDataMeta._CData_value(self, value)
+        return _CDataMeta.from_param(self, value)
 
     def _CData_output(self, resarray, base=None, index=-1):
         res = self.__new__(self)
@@ -183,10 +183,11 @@
             fieldtype = self._fieldtypes[name].ctype
         except KeyError:
             return _CData.__setattr__(self, name, value)
-        if ensure_objects(value) is not None:
+        cobj = fieldtype.from_param(value)
+        if ensure_objects(cobj) is not None:
             key = keepalive_key(getattr(self.__class__, name).num)
-            store_reference(self, key, value._objects)
-        arg = fieldtype._CData_value(value)
+            store_reference(self, key, cobj._objects)
+        arg = cobj._get_buffer_value()
         if fieldtype._fficompositesize is not None:
             from ctypes import memmove
             dest = self._buffer.fieldaddress(name)

Modified: pypy/branch/stringbuilder2/pypy/lib/_ctypes/union.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/lib/_ctypes/union.py	(original)
+++ pypy/branch/stringbuilder2/pypy/lib/_ctypes/union.py	Mon Jan 25 15:25:48 2010
@@ -99,10 +99,11 @@
             fieldtype = self._fieldtypes[name].ctype
         except KeyError:
             raise AttributeError(name)
-        if ensure_objects(value) is not None:
+        cobj = fieldtype.from_param(value)
+        if ensure_objects(cobj) is not None:
             key = keepalive_key(getattr(self.__class__, name).num)
-            store_reference(self, key, value._objects)
-        arg = fieldtype._CData_value(value)
+            store_reference(self, key, cobj._objects)
+        arg = cobj._get_buffer_value()
         if fieldtype._fficompositesize is not None:
             from ctypes import memmove
             dest = self._buffer.buffer

Modified: pypy/branch/stringbuilder2/pypy/lib/app_test/ctypes_tests/test_keepalive.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/lib/app_test/ctypes_tests/test_keepalive.py	(original)
+++ pypy/branch/stringbuilder2/pypy/lib/app_test/ctypes_tests/test_keepalive.py	Mon Jan 25 15:25:48 2010
@@ -205,3 +205,50 @@
         s.r = r
         # obscure        
         assert s._objects == {'1': {}, '0:1': {'1': stuff}}
+
+    def test_c_char_p(self):
+        n = 2
+        xs = "hello" * n
+        x = c_char_p(xs)
+        del xs
+        import gc; gc.collect()
+        print 'x =', repr(x)
+        assert x.value == 'hellohello'
+        assert x._objects.keys() == ['0']
+        #
+        class datum(Structure):
+            _fields_ = [
+            ('dptr', c_char_p),
+            ('dsize', c_int),
+            ]
+        class union(Union):
+            _fields_ = [
+            ('dptr', c_char_p),
+            ('dsize', c_int),
+            ]
+        for wrap in [False, True]:
+            n = 2
+            xs = "hello" * n
+            if wrap:
+                xs = c_char_p(xs)
+            dat = datum()
+            dat.dptr = xs
+            dat.dsize = 15
+            del xs
+            import gc; gc.collect()
+            print 'dat.dptr =', repr(dat.dptr)
+            print 'dat._objects =', repr(dat._objects)
+            assert dat.dptr == "hellohello"
+            assert dat._objects.keys() == ['0']
+
+            xs = "hello" * n
+            if wrap:
+                xs = c_char_p(xs)
+            dat = union()
+            dat.dptr = xs
+            del xs
+            import gc; gc.collect()
+            print 'dat.dptr =', repr(dat.dptr)
+            print 'dat._objects =', repr(dat._objects)
+            assert dat.dptr == "hellohello"
+            assert dat._objects.keys() == ['0']

Modified: pypy/branch/stringbuilder2/pypy/lib/app_test/test_runpy.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/lib/app_test/test_runpy.py	(original)
+++ pypy/branch/stringbuilder2/pypy/lib/app_test/test_runpy.py	Mon Jan 25 15:25:48 2010
@@ -102,7 +102,7 @@
             if verbose: print "  Next level in:", sub_dir
             pkg_fname = os.path.join(sub_dir, init_fname)
             pkg_file = open(pkg_fname, "w")
-            pkg_file.write("__path__ = ['%s']\n" % sub_dir)
+            pkg_file.write("__path__ = [%r]\n" % sub_dir)
             pkg_file.close()
             if verbose: print "  Created:", pkg_fname
         mod_fname = os.path.join(sub_dir, test_fname)
@@ -137,6 +137,12 @@
             d1 = run_module(mod_name) # Read from source
             __import__(mod_name)
             os.remove(mod_fname)
+
+            #--- the block below is to check that "imp.find_module"
+            #--- manages to import the .pyc file alone.  We don't
+            #--- support it in PyPy in the default configuration.
+            return
+
             if verbose: print "Running from compiled:", mod_name
             d2 = run_module(mod_name) # Read from bytecode
         finally:

Modified: pypy/branch/stringbuilder2/pypy/module/__builtin__/__init__.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/module/__builtin__/__init__.py	(original)
+++ pypy/branch/stringbuilder2/pypy/module/__builtin__/__init__.py	Mon Jan 25 15:25:48 2010
@@ -1,6 +1,7 @@
 from pypy.interpreter.error import OperationError
 from pypy.interpreter import module
 from pypy.interpreter.mixedmodule import MixedModule
+import pypy.module.imp.importing
 
 # put builtins here that should be optimized somehow
 
@@ -35,9 +36,6 @@
         'vars'          : 'app_inspect.vars',
         'dir'           : 'app_inspect.dir',
 
-        '_find_module'  : 'app_misc.find_module',
-        'reload'        : 'app_misc.reload',
-
         '__filestub'    : 'app_file_stub.file',
     }
 
@@ -88,7 +86,8 @@
         'compile'       : 'compiling.compile',
         'eval'          : 'compiling.eval',
 
-        '__import__'    : 'importing.importhook',
+        '__import__'    : 'pypy.module.imp.importing.importhook',
+        'reload'        : 'pypy.module.imp.importing.reload',
 
         'range'         : 'functional.range_int',
         'xrange'        : 'functional.W_XRange',
@@ -152,16 +151,3 @@
         space.exception_is_valid_obj_as_class_w = ab.exception_is_valid_obj_as_class_w.__get__(space)
         space.exception_getclass = ab.exception_getclass.__get__(space)
         space.exception_issubclass_w = ab.exception_issubclass_w.__get__(space)
-
-    def startup(self, space):
-        # install zipimport hook if --withmod-zipimport is used
-        if space.config.objspace.usemodules.zipimport:
-            w_import = space.builtin.get('__import__')
-            w_zipimport = space.call(w_import, space.newlist(
-                [space.wrap('zipimport')]))
-            w_sys = space.getbuiltinmodule('sys')
-            w_path_hooks = space.getattr(w_sys, space.wrap('path_hooks'))
-            w_append = space.getattr(w_path_hooks, space.wrap('append'))
-            w_zipimporter = space.getattr(w_zipimport,
-                                          space.wrap('zipimporter'))
-            space.call(w_append, space.newlist([w_zipimporter]))

Modified: pypy/branch/stringbuilder2/pypy/module/__builtin__/descriptor.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/module/__builtin__/descriptor.py	(original)
+++ pypy/branch/stringbuilder2/pypy/module/__builtin__/descriptor.py	Mon Jan 25 15:25:48 2010
@@ -4,7 +4,7 @@
      Arguments
 from pypy.interpreter.gateway import interp2app
 from pypy.interpreter.error import OperationError
-from pypy.objspace.descroperation import object_getattribute
+from pypy.objspace.descroperation import object_getattribute, object_setattr
 from pypy.interpreter.function import StaticMethod, ClassMethod
 from pypy.interpreter.typedef import GetSetProperty, descr_get_dict, \
      descr_set_dict, interp_attrproperty_w
@@ -153,8 +153,11 @@
         # XXX kill me?  This is mostly to make tests happy, raising
         # a TypeError instead of an AttributeError and using "readonly"
         # instead of "read-only" in the error message :-/
-        raise OperationError(space.w_TypeError, space.wrap(
-            "Trying to set readonly attribute %s on property" % (attr,)))
+        if attr in ["__doc__", "fget", "fset", "fdel"]:
+            raise OperationError(space.w_TypeError, space.wrap(
+                "Trying to set readonly attribute %s on property" % (attr,)))
+        return space.call_function(object_setattr(space),
+                                   space.wrap(self), space.wrap(attr), w_value)
     setattr.unwrap_spec = ['self', ObjSpace, str, W_Root]
 
 W_Property.typedef = TypeDef(

Modified: pypy/branch/stringbuilder2/pypy/module/__builtin__/test/test_descriptor.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/module/__builtin__/test/test_descriptor.py	(original)
+++ pypy/branch/stringbuilder2/pypy/module/__builtin__/test/test_descriptor.py	Mon Jan 25 15:25:48 2010
@@ -301,3 +301,13 @@
             pass
         else:
             raise Exception, "expected ZeroDivisionError from bad property"
+
+    def test_property_subclass(self):
+        class P(property):
+            pass
+
+        p = P()
+        p.name = 0
+        assert p.name == 0
+
+

Modified: pypy/branch/stringbuilder2/pypy/module/__pypy__/__init__.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/module/__pypy__/__init__.py	(original)
+++ pypy/branch/stringbuilder2/pypy/module/__pypy__/__init__.py	Mon Jan 25 15:25:48 2010
@@ -1,7 +1,7 @@
 
 # Package initialisation
 from pypy.interpreter.mixedmodule import MixedModule
-from pypy.module.__builtin__.importing import get_pyc_magic
+from pypy.module.imp.importing import get_pyc_magic
 
 class Module(MixedModule):
     appleveldefs = {

Modified: pypy/branch/stringbuilder2/pypy/module/_demo/__init__.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/module/_demo/__init__.py	(original)
+++ pypy/branch/stringbuilder2/pypy/module/_demo/__init__.py	Mon Jan 25 15:25:48 2010
@@ -12,3 +12,13 @@
     appleveldefs = {
         'DemoError'        : 'app_demo.DemoError',
     }
+
+    # Used in tests
+    demo_events = []
+    def setup_after_space_initialization(self):
+        Module.demo_events.append('setup')
+    def startup(self, space):
+        Module.demo_events.append('startup')
+    def shutdown(self, space):
+        Module.demo_events.append('shutdown')
+

Modified: pypy/branch/stringbuilder2/pypy/module/_stackless/interp_coroutine.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/module/_stackless/interp_coroutine.py	(original)
+++ pypy/branch/stringbuilder2/pypy/module/_stackless/interp_coroutine.py	Mon Jan 25 15:25:48 2010
@@ -275,11 +275,10 @@
         return space.newtuple([])
     items = [None] * index
     f = self.subctx.topframe
-    f.force_f_back()
     while index > 0:
         index -= 1
         items[index] = space.wrap(f)
-        f = f.f_back()
+        f = f.f_backref()
     assert f is None
     return space.newtuple(items)
 

Modified: pypy/branch/stringbuilder2/pypy/module/oracle/__init__.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/module/oracle/__init__.py	(original)
+++ pypy/branch/stringbuilder2/pypy/module/oracle/__init__.py	Mon Jan 25 15:25:48 2010
@@ -39,6 +39,7 @@
     def startup(self, space):
         from pypy.module.oracle.interp_error import get
         state = get(space)
+        state.startup(space)
         (state.w_DecimalType,
          state.w_DateTimeType, state.w_DateType, state.w_TimedeltaType,
          ) = space.fixedview(space.appexec([], """():

Modified: pypy/branch/stringbuilder2/pypy/module/oracle/interp_error.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/module/oracle/interp_error.py	(original)
+++ pypy/branch/stringbuilder2/pypy/module/oracle/interp_error.py	Mon Jan 25 15:25:48 2010
@@ -4,32 +4,47 @@
 from pypy.interpreter.typedef import interp_attrproperty, interp_attrproperty_w
 from pypy.interpreter.gateway import interp2app
 from pypy.interpreter.error import OperationError
+
 from pypy.module.oracle import roci, config
+from pypy.rlib.unroll import unrolling_iterable
+
+exported_names = unrolling_iterable("""
+    DatabaseError OperationalError InterfaceError ProgrammingError
+    NotSupportedError IntegrityError InternalError DataError
+    Variable Connection""".split())
 
 class State:
     # XXX move to another file
+
     def __init__(self, space):
-        w_module = space.getbuiltinmodule('cx_Oracle')
-        def get(name):
-            return space.getattr(w_module, space.wrap(name))
+        "NOT_RPYTHON"
+        self.variableTypeByPythonType = {}
+        self.w_DecimalType = None
+        self.w_DateTimeType = None
+        self.w_DateType = None
+        self.w_TimedeltaType = None
 
-        self.w_DatabaseError = get('DatabaseError')
-        self.w_OperationalError = get('OperationalError')
-        self.w_InterfaceError = get('InterfaceError')
-        self.w_ProgrammingError = get('ProgrammingError')
-        self.w_NotSupportedError = get('NotSupportedError')
-        self.w_IntegrityError = get('IntegrityError')
-        self.w_InternalError = get('InternalError')
-        self.w_DataError = get('DataError')
-        self.w_Variable = get('Variable')
-        self.w_Connection = get('Connection')
+        for name in exported_names:
+            setattr(self, 'w_' + name, None)
+
+    def startup(self, space):
+        w_module = space.getbuiltinmodule('cx_Oracle')
+        for name in exported_names:
+            setattr(self, 'w_' + name, space.getattr(w_module, space.wrap(name)))
 
         from pypy.module.oracle.interp_variable import all_variable_types
-        self.variableTypeByPythonType = {}
         for varType in all_variable_types:
             w_type = space.gettypeobject(varType.typedef)
             self.variableTypeByPythonType[w_type] = varType
 
+        (self.w_DecimalType,
+         self.w_DateTimeType, self.w_DateType, self.w_TimedeltaType,
+         ) = space.fixedview(space.appexec([], """():
+             import decimal, datetime
+             return (decimal.Decimal,
+                     datetime.datetime, datetime.date, datetime.timedelta)
+        """))
+
 def get(space): 
     return space.fromcache(State) 
 

Modified: pypy/branch/stringbuilder2/pypy/module/posix/__init__.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/module/posix/__init__.py	(original)
+++ pypy/branch/stringbuilder2/pypy/module/posix/__init__.py	Mon Jan 25 15:25:48 2010
@@ -93,6 +93,8 @@
         interpleveldefs['readlink'] = 'interp_posix.readlink'
     if hasattr(os, 'fork'):
         interpleveldefs['fork'] = 'interp_posix.fork'
+    if hasattr(os, 'openpty'):
+        interpleveldefs['openpty'] = 'interp_posix.openpty'
     if hasattr(os, 'waitpid'):
         interpleveldefs['waitpid'] = 'interp_posix.waitpid'
     if hasattr(os, 'execv'):

Modified: pypy/branch/stringbuilder2/pypy/module/posix/interp_posix.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/module/posix/interp_posix.py	(original)
+++ pypy/branch/stringbuilder2/pypy/module/posix/interp_posix.py	Mon Jan 25 15:25:48 2010
@@ -507,6 +507,14 @@
         raise wrap_oserror(space, e) 
     return space.wrap(pid)
 
+def openpty(space):
+    "Open a pseudo-terminal, returning open fd's for both master and slave end."
+    try:
+        master_fd, slave_fd = os.openpty()
+    except OSError, e:
+        raise wrap_oserror(space, e)
+    return space.newtuple([space.wrap(master_fd), space.wrap(slave_fd)])
+
 def waitpid(space, pid, options):
     """ waitpid(pid, options) -> (pid, status)
     

Modified: pypy/branch/stringbuilder2/pypy/module/posix/test/test_posix2.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/module/posix/test/test_posix2.py	(original)
+++ pypy/branch/stringbuilder2/pypy/module/posix/test/test_posix2.py	Mon Jan 25 15:25:48 2010
@@ -245,6 +245,21 @@
             assert os.WEXITSTATUS(status1) == 4
         pass # <- please, inspect.getsource(), don't crash
 
+
+    if hasattr(__import__(os.name), "openpty"):
+        def test_openpty(self):
+            os = self.posix
+            master_fd, slave_fd = self.posix.openpty()
+            try:
+                assert isinstance(master_fd, int)
+                assert isinstance(slave_fd, int)
+                os.write(slave_fd, 'x')
+                assert os.read(master_fd, 1) == 'x'
+            finally:
+                os.close(master_fd)
+                os.close(slave_fd)
+
+
     if hasattr(__import__(os.name), "execv"):
         def test_execv(self):
             os = self.posix
@@ -312,7 +327,7 @@
         fh.close()
         from time import time, sleep
         t0 = time()
-        sleep(1)
+        sleep(1.1)
         os.utime(path, None)
         assert os.stat(path).st_atime > t0
         os.utime(path, (int(t0), int(t0)))

Modified: pypy/branch/stringbuilder2/pypy/module/pypyjit/interp_jit.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/module/pypyjit/interp_jit.py	(original)
+++ pypy/branch/stringbuilder2/pypy/module/pypyjit/interp_jit.py	Mon Jan 25 15:25:48 2010
@@ -21,7 +21,7 @@
 
 PyFrame._virtualizable2_ = ['last_instr', 'pycode',
                             'valuestackdepth', 'valuestack_w[*]',
-                            'fastlocals_w[*]', 'f_forward',
+                            'fastlocals_w[*]',
                             'last_exception',
                             ]
 
@@ -35,18 +35,17 @@
     name = opcode_method_names[ord(bytecode.co_code[next_instr])]
     return '%s #%d %s' % (bytecode.get_repr(), next_instr, name)
 
-def leave(next_instr, pycode, frame, ec):
-    from pypy.interpreter.executioncontext import ExecutionContext
-    # can't use a method here, since this function is seen later than the main
-    # annotation       XXX no longer true, could be fixed
-    ExecutionContext._jit_rechain_frame(ec, frame)
-
 def get_jitcell_at(next_instr, bytecode):
     return bytecode.jit_cells.get(next_instr, None)
 
 def set_jitcell_at(newcell, next_instr, bytecode):
     bytecode.jit_cells[next_instr] = newcell
 
+def confirm_enter_jit(next_instr, bytecode, frame, ec):
+    return (frame.w_f_trace is None and
+            ec.profilefunc is None and
+            ec.w_tracefunc is None)
+
 
 class PyPyJitDriver(JitDriver):
     reds = ['frame', 'ec']
@@ -63,9 +62,9 @@
 
 pypyjitdriver = PyPyJitDriver(can_inline = can_inline,
                               get_printable_location = get_printable_location,
-                              leave = leave,
                               get_jitcell_at = get_jitcell_at,
-                              set_jitcell_at = set_jitcell_at)
+                              set_jitcell_at = set_jitcell_at,
+                              confirm_enter_jit = confirm_enter_jit)
 
 class __extend__(PyFrame):
 

Modified: pypy/branch/stringbuilder2/pypy/module/pypyjit/test/test_pypy_c.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/module/pypyjit/test/test_pypy_c.py	(original)
+++ pypy/branch/stringbuilder2/pypy/module/pypyjit/test/test_pypy_c.py	Mon Jan 25 15:25:48 2010
@@ -89,8 +89,12 @@
         print >> f, source
         # some support code...
         print >> f, py.code.Source("""
-            import sys, pypyjit
-            pypyjit.set_param(threshold=3)
+            import sys
+            try: # make the file runnable by CPython
+                import pypyjit
+                pypyjit.set_param(threshold=3)
+            except ImportError:
+                pass
 
             def check(args, expected):
                 print >> sys.stderr, 'trying:', args
@@ -113,8 +117,8 @@
         assert result
         assert result.splitlines()[-1].strip() == 'OK :-)'
         self.parse_loops(logfilepath)
+        self.print_loops()
         if self.total_ops > expected_max_ops:
-            self.print_loops()
             assert 0, "too many operations: got %d, expected maximum %d" % (
                 self.total_ops, expected_max_ops)
 
@@ -172,7 +176,7 @@
                         x = x + (i&j)
                     i = i + 1
                 return x
-        ''', 194,
+        ''', 220,
                    ([2117], 1083876708))
 
     def test_factorial(self):
@@ -183,7 +187,7 @@
                     r *= n
                     n -= 1
                 return r
-        ''', 26,
+        ''', 28,
                    ([5], 120),
                     ([20], 2432902008176640000L))
 
@@ -205,30 +209,43 @@
 
             def main():
                 return richards.main(iterations = 1)
-        ''' % (sys.path,), 7000,
+        ''' % (sys.path,), 7200,
                    ([], 42))
 
     def test_simple_call(self):
         self.run_source('''
+            OFFSET = 0
             def f(i):
-                return i + 1
+                return i + 1 + OFFSET
             def main(n):
                 i = 0
-                while i < n:
+                while i < n+OFFSET:
                     i = f(f(i))
                 return i
-        ''', 76,
+        ''', 96,
                    ([20], 20),
                     ([31], 32))
         ops = self.get_by_bytecode("LOAD_GLOBAL")
-        assert len(ops) == 2
-        assert ops[0].get_opnames() == ["getfield_gc", "getarrayitem_gc",
+        assert len(ops) == 5
+        assert ops[0].get_opnames() == ["getfield_gc", "guard_value",
+                                        "getfield_gc", "guard_isnull",
                                         "getfield_gc", "guard_nonnull_class"]
-        assert not ops[1] # second LOAD_GLOBAL folded away
+        # the second getfield on the same globals is quicker
+        assert ops[1].get_opnames() == ["getfield_gc", "guard_nonnull_class"]
+        assert not ops[2] # second LOAD_GLOBAL of the same name folded away
+        # LOAD_GLOBAL of the same name but in different function partially
+        # folded away
+        # XXX could be improved
+        assert ops[3].get_opnames() == ["guard_value",
+                                        "getfield_gc", "guard_isnull"]
+        assert not ops[4]
         ops = self.get_by_bytecode("CALL_FUNCTION")
         assert len(ops) == 2
-        for bytecode in ops:
-            assert not bytecode.get_opnames("call")
+        for i, bytecode in enumerate(ops):
+            if i == 0:
+                assert "call(getexecutioncontext)" in str(bytecode)
+            else:
+                assert not bytecode.get_opnames("call")
             assert not bytecode.get_opnames("new")
             assert len(bytecode.get_opnames("guard")) <= 10
 
@@ -258,8 +275,11 @@
 
         ops = self.get_by_bytecode("CALL_METHOD")
         assert len(ops) == 2
-        for bytecode in ops:
-            assert not bytecode.get_opnames("call")
+        for i, bytecode in enumerate(ops):
+            if i == 0:
+                assert "call(getexecutioncontext)" in str(bytecode)
+            else:
+                assert not bytecode.get_opnames("call")
             assert not bytecode.get_opnames("new")
             assert len(bytecode.get_opnames("guard")) <= 9
         assert len(ops[1]) < len(ops[0])
@@ -270,6 +290,35 @@
                                         "guard_nonnull_class"]
         assert not ops[1] # second LOAD_ATTR folded away
 
+    def test_static_classmethod_call(self):
+        self.run_source('''
+            class A(object):
+                @classmethod
+                def f(cls, i):
+                    return i + (cls is A) + 1
+
+                @staticmethod
+                def g(i):
+                    return i - 1
+
+            def main(n):
+                i = 0
+                a = A()
+                while i < n:
+                    x = a.f(i)
+                    i = a.g(x)
+                return i
+        ''', 105,
+                   ([20], 20),
+                   ([31], 31))
+        ops = self.get_by_bytecode("LOOKUP_METHOD")
+        assert len(ops) == 2
+        assert not ops[0].get_opnames("call")
+        assert not ops[0].get_opnames("new")
+        assert len(ops[0].get_opnames("guard")) <= 7
+        assert len(ops[0].get_opnames("getfield")) < 6
+        assert not ops[1] # second LOOKUP_METHOD folded away
+
     def test_default_and_kw(self):
         self.run_source('''
             def f(i, j=1):
@@ -279,17 +328,45 @@
                 while i < n:
                     i = f(f(i), j=1)
                 return i
-        ''', 98,
+        ''', 100,
                    ([20], 20),
                    ([31], 32))
         ops = self.get_by_bytecode("CALL_FUNCTION")
         assert len(ops) == 2
-        for bytecode in ops:
-            assert not bytecode.get_opnames("call")
+        for i, bytecode in enumerate(ops):
+            if i == 0:
+                assert "call(getexecutioncontext)" in str(bytecode)
+            else:
+                assert not bytecode.get_opnames("call")
             assert not bytecode.get_opnames("new")
         assert len(ops[0].get_opnames("guard")) <= 14
         assert len(ops[1].get_opnames("guard")) <= 3
 
+    def test_kwargs(self):
+        self.run_source('''
+            d = {}
+
+            def g(**args):
+                return len(args)
+
+            def main(x):
+                s = 0
+                d = {}
+                for i in range(x):
+                    s += g(**d)
+                    d[str(i)] = i
+                    if i % 100 == 99:
+                        d = {}
+                return s
+        ''', 100000, ([100], 4950),
+                    ([1000], 49500),
+                    ([10000], 495000),
+                    ([100000], 4950000))
+        assert len(self.loops) == 2
+        op, = self.get_by_bytecode("CALL_FUNCTION_KW")
+        # XXX a bit too many guards, but better than before
+        assert len(op.get_opnames("guard")) <= 10
+
     def test_virtual_instance(self):
         self.run_source('''
             class A(object):
@@ -303,7 +380,7 @@
                     a.x = 2
                     i = i + a.x
                 return i
-        ''', 63,
+        ''', 67,
                    ([20], 20),
                    ([31], 32))
 
@@ -334,7 +411,7 @@
                 while i < n:
                     i = i + a.x
                 return i
-        ''', 39,
+        ''', 41,
                    ([20], 20),
                    ([31], 32))
 
@@ -375,15 +452,40 @@
                     i += 1
                     l.append(i)
                 return i, len(l)
-        ''', 37,
+        ''', 39,
                    ([20], (20, 18)),
                    ([31], (31, 29)))
 
         bytecode, = self.get_by_bytecode("CALL_METHOD")
         assert len(bytecode.get_opnames("new_with_vtable")) == 1 # the forcing of the int
         assert len(bytecode.get_opnames("call")) == 1 # the call to append
-        assert len(bytecode.get_opnames("guard")) == 1 # guard_no_exception after the call
+        assert len(bytecode.get_opnames("guard")) == 2 # guard for profiling disabledness + guard_no_exception after the call
 
+    def test_range_iter(self):
+        self.run_source('''
+            def g(n):
+                return range(n)
+
+            def main(n):
+                s = 0
+                for i in range(n):
+                    s += g(n)[i]
+                return s
+        ''', 143, ([1000], 1000 * 999 / 2))
+        bytecode, = self.get_by_bytecode("BINARY_SUBSCR")
+        assert bytecode.get_opnames("guard") == [
+            "guard_isnull",  # check that the range list is not forced
+            "guard_false",   # check that the index is >= 0
+            "guard_false",   # check that the index is lower than the current length
+            ]
+        bytecode, _ = self.get_by_bytecode("FOR_ITER") # second bytecode is the end of the loop
+        assert bytecode.get_opnames("guard") == [
+            "guard_class",   # check the class of the iterator
+            "guard_nonnull", # check that the iterator is not finished
+            "guard_isnull",  # check that the range list is not forced
+            "guard_false",   # check that the index is lower than the current length
+            ]
+ 
     def test_exception_inside_loop_1(self):
         py.test.skip("exceptions: in-progress")
         self.run_source('''
@@ -445,7 +547,27 @@
     def setup_class(cls):
         if option.pypy_c is None:
             py.test.skip("pass --pypy!")
+        if not has_info(option.pypy_c, 'translation.jit'):
+            py.test.skip("must give a pypy-c with the jit enabled")
         cls.tmpdir = udir.join('pypy-jit')
         cls.tmpdir.ensure(dir=1)
         cls.counter = 0
         cls.pypy_c = option.pypy_c
+
+def has_info(pypy_c, option):
+    g = os.popen('"%s" --info' % pypy_c, 'r')
+    lines = g.readlines()
+    g.close()
+    if not lines:
+        raise ValueError("cannot execute %r" % pypy_c)
+    for line in lines:
+        line = line.strip()
+        if line.startswith(option + ':'):
+            line = line[len(option)+1:].strip()
+            if line == 'True':
+                return True
+            elif line == 'False':
+                return False
+            else:
+                return line
+    raise ValueError(option + ' not found in ' + pypy_c)

Modified: pypy/branch/stringbuilder2/pypy/module/sys/vm.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/module/sys/vm.py	(original)
+++ pypy/branch/stringbuilder2/pypy/module/sys/vm.py	Mon Jan 25 15:25:48 2010
@@ -31,7 +31,6 @@
                              space.wrap("frame index must not be negative"))
     ec = space.getexecutioncontext()
     f = ec.gettopframe_nohidden()
-    f.force_f_back()
     while True:
         if f is None:
             raise OperationError(space.w_ValueError,

Modified: pypy/branch/stringbuilder2/pypy/module/thread/__init__.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/module/thread/__init__.py	(original)
+++ pypy/branch/stringbuilder2/pypy/module/thread/__init__.py	Mon Jan 25 15:25:48 2010
@@ -18,11 +18,6 @@
         'allocate':               'os_lock.allocate_lock',  # obsolete synonym
         'LockType':               'os_lock.getlocktype(space)',
         '_local':                 'os_local.getlocaltype(space)',
-
-        # custom interface for the 'imp' module
-        '_importlock_held':       'importlock.held',
-        '_importlock_acquire':    'importlock.acquire',
-        '_importlock_release':    'importlock.release',
     }
 
     def __init__(self, space, *args):

Modified: pypy/branch/stringbuilder2/pypy/module/zipimport/__init__.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/module/zipimport/__init__.py	(original)
+++ pypy/branch/stringbuilder2/pypy/module/zipimport/__init__.py	Mon Jan 25 15:25:48 2010
@@ -14,4 +14,12 @@
     appleveldefs = {
         'ZipImportError'      : 'app_zipimport.ZipImportError',
     }
-    
+
+    def setup_after_space_initialization(self):
+        """NOT_RPYTHON"""
+        space = self.space
+        # install zipimport hook
+        w_path_hooks = space.sys.get('path_hooks')
+        from pypy.module.zipimport.interp_zipimport import W_ZipImporter
+        w_zipimporter = space.gettypefor(W_ZipImporter)
+        space.call_method(w_path_hooks, 'append', w_zipimporter)

Modified: pypy/branch/stringbuilder2/pypy/module/zipimport/interp_zipimport.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/module/zipimport/interp_zipimport.py	(original)
+++ pypy/branch/stringbuilder2/pypy/module/zipimport/interp_zipimport.py	Mon Jan 25 15:25:48 2010
@@ -5,7 +5,7 @@
 from pypy.interpreter.gateway import interp2app
 from pypy.interpreter.typedef import TypeDef, GetSetProperty
 from pypy.interpreter.module import Module
-from pypy.module.__builtin__ import importing
+from pypy.module.imp import importing
 from pypy.rlib.unroll import unrolling_iterable
 from pypy.rlib.rzipfile import RZipFile, BadZipfile
 import os
@@ -149,9 +149,9 @@
         real_name = self.name + os.path.sep + self.corr_zname(filename)
         space.setattr(w_mod, w('__loader__'), space.wrap(self))
         importing._prepare_module(space, w_mod, real_name, pkgpath)
-        result = importing.load_source_module(space, w(modname), w_mod,
-                                            filename, buf, write_pyc=False)
-        return result
+        code_w = importing.parse_source_module(space, filename, buf)
+        importing.exec_code_module(space, w_mod, code_w)
+        return w_mod
 
     def _parse_mtime(self, space, filename):
         w = space.wrap

Modified: pypy/branch/stringbuilder2/pypy/module/zipimport/test/test_zipimport.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/module/zipimport/test/test_zipimport.py	(original)
+++ pypy/branch/stringbuilder2/pypy/module/zipimport/test/test_zipimport.py	Mon Jan 25 15:25:48 2010
@@ -4,7 +4,7 @@
 import py
 import time
 import struct
-from pypy.module.__builtin__.importing import get_pyc_magic, _w_long
+from pypy.module.imp.importing import get_pyc_magic, _w_long
 from StringIO import StringIO
 
 from pypy.tool.udir import udir
@@ -255,6 +255,11 @@
         l = [i for i in zipimport._zip_directory_cache]
         assert len(l)
 
+    def test_path_hooks(self):
+        import sys
+        import zipimport
+        assert sys.path_hooks.count(zipimport.zipimporter) == 1
+
 class AppTestZipimportDeflated(AppTestZipimport):
     compression = ZIP_DEFLATED
 

Modified: pypy/branch/stringbuilder2/pypy/objspace/descroperation.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/objspace/descroperation.py	(original)
+++ pypy/branch/stringbuilder2/pypy/objspace/descroperation.py	Mon Jan 25 15:25:48 2010
@@ -35,7 +35,13 @@
         w_descr = space.lookup(w_obj, name)
         if w_descr is not None:
             if space.is_data_descr(w_descr):
-                return space.get(w_descr, w_obj)
+                # Only override if __get__ is defined, too, for compatibility
+                # with CPython.
+                w_get = space.lookup(w_descr, "__get__")
+                if w_get is not None:
+                    w_type = space.type(w_obj)
+                    return space.get_and_call_function(w_get, w_descr, w_obj,
+                                                       w_type)
             w_value = w_obj.getdictvalue_attr_is_in_class(space, name)
         else:
             w_value = w_obj.getdictvalue(space, name)
@@ -505,30 +511,42 @@
 slice_max = Temp()[:]
 del Temp
 
+def old_slice_range_getlength(space, w_obj):
+    # NB. the language ref is inconsistent with the new-style class
+    # behavior when w_obj doesn't implement __len__(), so we just
+    # follow cpython. Also note that CPython slots make it easier
+    # to check for object implementing it or not. We just catch errors
+    # so this behavior is slightly different
+    try:
+        return space.len(w_obj)
+    except OperationError, e:
+        if not ((e.match(space, space.w_AttributeError) or
+                 e.match(space, space.w_TypeError))):
+            raise
+    return None
+
 def old_slice_range(space, w_obj, w_start, w_stop):
     """Only for backward compatibility for __getslice__()&co methods."""
+    w_length = None
     if space.is_w(w_start, space.w_None):
         w_start = space.wrap(0)
     else:
-        w_start = space.wrap(space.getindex_w(w_start, None))
-        if space.is_true(space.lt(w_start, space.wrap(0))):
-            try:
-                w_start = space.add(w_start, space.len(w_obj))
-            except OperationError, e:
-                if not ((e.match(space, space.w_AttributeError) or
-                         e.match(space, space.w_TypeError))):
-                    raise
-            # NB. the language ref is inconsistent with the new-style class
-            # behavior when w_obj doesn't implement __len__(), so we just
-            # follow cpython. Also note that CPython slots make it easier
-            # to check for object implementing it or not. We just catch errors
-            # so this behavior is slightly different
+        start = space.getindex_w(w_start, None)
+        w_start = space.wrap(start)
+        if start < 0:
+            w_length = old_slice_range_getlength(space, w_obj)
+            if w_length is not None:
+                w_start = space.add(w_start, w_length)
     if space.is_w(w_stop, space.w_None):
         w_stop = space.wrap(slice_max)
     else:
-        w_stop = space.wrap(space.getindex_w(w_stop, None))
-        if space.is_true(space.lt(w_stop, space.wrap(0))):
-            w_stop = space.add(w_stop, space.len(w_obj))
+        stop = space.getindex_w(w_stop, None)
+        w_stop = space.wrap(stop)
+        if stop < 0:
+            if w_length is None:
+                w_length = old_slice_range_getlength(space, w_obj)
+            if w_length is not None:
+                w_stop = space.add(w_stop, w_length)
     return w_start, w_stop
 
 # regular methods def helpers

Modified: pypy/branch/stringbuilder2/pypy/objspace/flow/flowcontext.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/objspace/flow/flowcontext.py	(original)
+++ pypy/branch/stringbuilder2/pypy/objspace/flow/flowcontext.py	Mon Jan 25 15:25:48 2010
@@ -5,6 +5,7 @@
 from pypy.interpreter.argument import ArgumentsForTranslation
 from pypy.objspace.flow.model import *
 from pypy.objspace.flow.framestate import FrameState
+from pypy.rlib import jit
 
 
 class OperationThatShouldNotBePropagatedError(OperationError):
@@ -260,8 +261,8 @@
             except StopFlowing:
                 continue   # restarting a dead SpamBlock
             try:
-                old_frame = self.some_frame
-                self.some_frame = frame
+                old_frameref = self.topframeref
+                self.topframeref = jit.non_virtual_ref(frame)
                 self.crnt_frame = frame
                 try:
                     w_result = frame.dispatch(frame.pycode,
@@ -269,7 +270,7 @@
                                               self)
                 finally:
                     self.crnt_frame = None
-                    self.some_frame = old_frame
+                    self.topframeref = old_frameref
 
             except OperationThatShouldNotBePropagatedError, e:
                 raise Exception(
@@ -384,6 +385,9 @@
             operr = OperationError(operr.w_type, operr.w_value)
         return operr
 
+    def exception_trace(self, frame, operationerr):
+        pass    # overridden for performance only
+
     # hack for unrolling iterables, don't use this
     def replace_in_stack(self, oldvalue, newvalue):
         w_new = Constant(newvalue)

Modified: pypy/branch/stringbuilder2/pypy/objspace/std/celldict.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/objspace/std/celldict.py	(original)
+++ pypy/branch/stringbuilder2/pypy/objspace/std/celldict.py	Mon Jan 25 15:25:48 2010
@@ -1,4 +1,8 @@
-from pypy.interpreter.pycode import CO_CONTAINSGLOBALS
+""" A very simple cell dict implementation. The dictionary maps keys to cell.
+This ensures that the function (dict, key) -> cell is pure. By itself, this
+optimization is not helping at all, but in conjunction with the JIT it can
+speed up global lookups a lot."""
+
 from pypy.objspace.std.dictmultiobject import IteratorImplementation
 from pypy.objspace.std.dictmultiobject import W_DictMultiObject, _is_sane_hash
 from pypy.rlib import jit
@@ -19,31 +23,22 @@
     def __init__(self, space):
         self.space = space
         self.content = {}
-        self.unshadowed_builtins = {}
 
-    def getcell(self, key, make_new=True):
+    def getcell(self, key, makenew):
+        if makenew or jit.we_are_jitted():
+            # when we are jitting, we always go through the pure function
+            # below, to ensure that we have no residual dict lookup
+            return self._getcell_makenew(key)
+        return self.content.get(key, None)
+
+    @jit.purefunction_promote
+    def _getcell_makenew(self, key):
         res = self.content.get(key, None)
         if res is not None:
             return res
-        if not make_new:
-            return None
         result = self.content[key] = ModuleCell()
         return result
 
-    def add_unshadowed_builtin(self, name, builtin_impl):
-        assert isinstance(builtin_impl, ModuleDictImplementation)
-        self.unshadowed_builtins[name] = builtin_impl
-
-    def invalidate_unshadowed_builtin(self, name):
-        impl = self.unshadowed_builtins[name]
-        try:
-            cell = impl.content[name]
-        except KeyError:
-            pass
-        else:
-            w_value = cell.invalidate()
-            cell = impl.content[name] = ModuleCell(w_value)
-
     def impl_setitem(self, w_key, w_value):
         space = self.space
         if space.is_w(space.type(w_key), space.w_str):
@@ -52,11 +47,7 @@
             self._as_rdict().setitem(w_key, w_value)
 
     def impl_setitem_str(self, name, w_value, shadows_type=True):
-        self.getcell(name).w_value = w_value
-        
-        if name in self.unshadowed_builtins:
-            self.invalidate_unshadowed_builtin(name)
-            del self.unshadowed_builtins[name]
+        self.getcell(name, True).w_value = w_value
 
     def impl_delitem(self, w_key):
         space = self.space
@@ -64,17 +55,25 @@
         if space.is_w(w_key_type, space.w_str):
             key = space.str_w(w_key)
             cell = self.getcell(key, False)
-            if cell is None:
+            if cell is None or cell.w_value is None:
                 raise KeyError
+            # note that we don't remove the cell from self.content, to make
+            # sure that a key that was found at any point in the dict, still
+            # maps to the same cell later (even if this cell no longer
+            # represents a key)
             cell.invalidate()
-            del self.content[key]
         elif _is_sane_hash(space, w_key_type):
             raise KeyError
         else:
             self._as_rdict().delitem(w_key)
         
     def impl_length(self):
-        return len(self.content)
+        # inefficient, but do we care?
+        res = 0
+        for cell in self.content.itervalues():
+            if cell.w_value is not None:
+                res += 1
+        return res
 
     def impl_getitem(self, w_lookup):
         space = self.space
@@ -91,6 +90,7 @@
         res = self.getcell(lookup, False)
         if res is None:
             return None
+        # note that even if the res.w_value is None, the next line is fine
         return res.w_value
 
     def impl_iter(self):
@@ -98,39 +98,34 @@
 
     def impl_keys(self):
         space = self.space
-        return [space.wrap(key) for key in self.content.iterkeys()]
+        return [space.wrap(key) for key, cell in self.content.iteritems()
+                    if cell.w_value is not None]
 
     def impl_values(self):
-        return [cell.w_value for cell in self.content.itervalues()]
+        return [cell.w_value for cell in self.content.itervalues()
+                    if cell.w_value is not None]
 
     def impl_items(self):
         space = self.space
         return [space.newtuple([space.wrap(key), cell.w_value])
-                    for (key, cell) in self.content.iteritems()]
+                    for (key, cell) in self.content.iteritems()
+                        if cell.w_value is not None]
 
     def impl_clear(self):
-        # inefficient, but who cares
         for k, cell in self.content.iteritems():
             cell.invalidate()
-        for k in self.unshadowed_builtins:
-            self.invalidate_unshadowed_builtin(k)
-        self.content.clear()
-        self.unshadowed_builtins.clear()
-
 
     def _as_rdict(self):
         r_dict_content = self.initialize_as_rdict()
         for k, cell in self.content.iteritems():
-            r_dict_content[self.space.wrap(k)] = cell.w_value
+            if cell.w_value is not None:
+                r_dict_content[self.space.wrap(k)] = cell.w_value
             cell.invalidate()
-        for k in self.unshadowed_builtins:
-            self.invalidate_unshadowed_builtin(k)
         self._clear_fields()
         return self
 
     def _clear_fields(self):
         self.content = None
-        self.unshadowed_builtins = None
 
 class ModuleDictIteratorImplementation(IteratorImplementation):
     def __init__(self, space, dictimplementation):
@@ -138,99 +133,8 @@
         self.iterator = dictimplementation.content.iteritems()
 
     def next_entry(self):
-        # note that this 'for' loop only runs once, at most
         for key, cell in self.iterator:
-            return (self.space.wrap(key), cell.w_value)
+            if cell.w_value is not None:
+                return (self.space.wrap(key), cell.w_value)
         else:
             return None, None
-
-
-class State(object):
-    def __init__(self, space):
-        self.space = space
-        self.invalidcell = ModuleCell()
-        self.always_invalid_cache = []
-        self.neverused_dictcontent = {}
-
-class GlobalCacheHolder(object):
-    def __init__(self, space):
-        self.cache = None
-        state = space.fromcache(State)
-        self.dictcontent = state.neverused_dictcontent
-
-    def getcache(self, space, code, w_globals):
-        if type(w_globals) is ModuleDictImplementation:
-            content = w_globals.content
-        else:
-            content = None
-        if self.dictcontent is content:
-            return self.cache
-        return self.getcache_slow(space, code, w_globals, content)
-    getcache._always_inline_ = True
-
-    def getcache_slow(self, space, code, w_globals, content):
-        state = space.fromcache(State)
-        if content is None:
-            cache = state.always_invalid_cache
-            if len(code.co_names_w) > len(cache):
-                cache = [state.invalidcell] * len(code.co_names_w)
-                state.always_invalid_cache = cache
-        else:
-            cache = [state.invalidcell] * len(code.co_names_w)
-        self.cache = cache
-        self.dictcontent = content
-        return cache
-    getcache_slow._dont_inline_ = True
-
-def init_code(code):
-    if code.co_flags & CO_CONTAINSGLOBALS:
-        code.globalcacheholder = GlobalCacheHolder(code.space)
-    else:
-        code.globalcacheholder = None
-
-
-def get_global_cache(space, code, w_globals):
-    from pypy.interpreter.pycode import PyCode
-    assert isinstance(code, PyCode)
-    holder = code.globalcacheholder
-    if holder is not None:
-        return holder.getcache(space, code, w_globals)
-    return None
-
-def getimplementation(w_dict):
-    if type(w_dict) is ModuleDictImplementation and w_dict.r_dict_content is None:
-        return w_dict
-    else:
-        return None
-
-def LOAD_GLOBAL(f, nameindex, *ignored):
-    cell = f.cache_for_globals[nameindex]
-    w_value = cell.w_value
-    if w_value is None:
-        # slow path
-        w_value = load_global_fill_cache(f, nameindex)
-    f.pushvalue(w_value)
-LOAD_GLOBAL._always_inline_ = True
-
-def find_cell_from_dict(implementation, name):
-    if implementation is not None:
-        return implementation.getcell(name, False)
-    return None
-
- at jit.dont_look_inside
-def load_global_fill_cache(f, nameindex):
-    name = f.space.str_w(f.getname_w(nameindex))
-    implementation = getimplementation(f.w_globals)
-    if implementation is not None:
-        cell = implementation.getcell(name, False)
-        if cell is None:
-            builtin_impl = getimplementation(f.get_builtin().getdict())
-            cell = find_cell_from_dict(builtin_impl, name)
-            if cell is not None:
-                implementation.add_unshadowed_builtin(name, builtin_impl)
-            
-        if cell is not None:
-            f.cache_for_globals[nameindex] = cell
-            return cell.w_value
-    return f._load_global(f.getname_u(nameindex))
-load_global_fill_cache._dont_inline_ = True

Modified: pypy/branch/stringbuilder2/pypy/objspace/std/objspace.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/objspace/std/objspace.py	(original)
+++ pypy/branch/stringbuilder2/pypy/objspace/std/objspace.py	Mon Jan 25 15:25:48 2010
@@ -71,16 +71,8 @@
         # Import all the object types and implementations
         self.model = StdTypeModel(self.config)
 
-        from pypy.objspace.std.celldict import get_global_cache
 
         class StdObjSpaceFrame(pyframe.PyFrame):
-            if self.config.objspace.std.withcelldict:
-                def __init__(self, space, code, w_globals, closure):
-                    pyframe.PyFrame.__init__(self, space, code, w_globals, closure)
-                    self.cache_for_globals = get_global_cache(space, code, w_globals)
-
-                from pypy.objspace.std.celldict import LOAD_GLOBAL
-
             if self.config.objspace.std.optimized_int_add:
                 if self.config.objspace.std.withsmallint:
                     def BINARY_ADD(f, oparg, *ignored):
@@ -662,15 +654,23 @@
                 w_value = w_obj.getdictvalue_attr_is_in_class(self, name)
                 if w_value is not None:
                     return w_value
-            try:
-                return self.get(w_descr, w_obj)
-            except OperationError, e:
-                if not e.match(self, self.w_AttributeError):
-                    raise
-        else:
+            w_get = self.lookup(w_descr, "__get__")
+            if w_get is not None:
+                # __get__ is allowed to raise an AttributeError to trigger use
+                # of __getattr__.
+                try:
+                    return self.get_and_call_function(w_get, w_descr, w_obj,
+                                                      w_type)
+                except OperationError, e:
+                    if not e.match(self, self.w_AttributeError):
+                        raise
+        if e is None:
             w_value = w_obj.getdictvalue(self, name)
             if w_value is not None:
                 return w_value
+            # No value in __dict__. Fallback to the descriptor if we have it.
+            if w_descr is not None:
+                return w_descr
 
         w_descr = self.lookup(w_obj, '__getattr__')
         if w_descr is not None:

Modified: pypy/branch/stringbuilder2/pypy/objspace/std/rangeobject.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/objspace/std/rangeobject.py	(original)
+++ pypy/branch/stringbuilder2/pypy/objspace/std/rangeobject.py	Mon Jan 25 15:25:48 2010
@@ -56,7 +56,9 @@
     def getitem(w_self, i):
         if i < 0:
             i += w_self.length
-        if i >= w_self.length or i < 0:
+            if i < 0:
+                raise IndexError
+        elif i >= w_self.length:
             raise IndexError
         return w_self.start + i * w_self.step
 
@@ -194,24 +196,24 @@
     if w_rangelist is None:
         raise OperationError(space.w_StopIteration, space.w_None)
     assert isinstance(w_rangelist, W_RangeListObject)
+    index = w_rangeiter.index
     if w_rangelist.w_list is not None:
         try:
             w_item = space.getitem(w_rangelist.w_list,
-                                   wrapint(space, w_rangeiter.index))
+                                   wrapint(space, index))
         except OperationError, e:
             w_rangeiter.w_seq = None
             if not e.match(space, space.w_IndexError):
                 raise
             raise OperationError(space.w_StopIteration, space.w_None)
     else:
-        try:
-            w_item = wrapint(
-                space,
-                w_rangelist.getitem(w_rangeiter.index))
-        except IndexError:
+        if index >= w_rangelist.length:
             w_rangeiter.w_seq = None
             raise OperationError(space.w_StopIteration, space.w_None)
-    w_rangeiter.index += 1
+        w_item = wrapint(
+            space,
+            w_rangelist.getitem_unchecked(index))
+    w_rangeiter.index = index + 1
     return w_item
 
 # XXX __length_hint__()

Modified: pypy/branch/stringbuilder2/pypy/objspace/std/test/test_celldict.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/objspace/std/test/test_celldict.py	(original)
+++ pypy/branch/stringbuilder2/pypy/objspace/std/test/test_celldict.py	Mon Jan 25 15:25:48 2010
@@ -1,262 +1,31 @@
 import py
 from pypy.conftest import gettestobjspace, option
-from pypy.objspace.std.celldict import get_global_cache, ModuleCell, ModuleDictImplementation
+from pypy.objspace.std.celldict import ModuleCell, ModuleDictImplementation
+from pypy.objspace.std.test.test_dictmultiobject import FakeSpace
 from pypy.interpreter import gateway
 
-# this file tests mostly the effects of caching global lookup. The dict
-# implementation itself is tested in test_dictmultiobject.py
-
-
-class AppTestCellDict(object):
-    def setup_class(cls):
-        if option.runappdirect:
-            py.test.skip("not appdirect tests")
-        cls.space = gettestobjspace(**{"objspace.std.withcelldict": True})
-        cls.w_impl_used = cls.space.appexec([], """():
-            import __pypy__
-            def impl_used(obj):
-                assert "ModuleDictImplementation" in __pypy__.internal_repr(obj)
-            return impl_used
-        """)
-        def is_in_cache(space, w_code, w_globals, w_name):
-            name = space.str_w(w_name)
-            cache = get_global_cache(space, w_code, w_globals)
-            index = [space.str_w(w_n) for w_n in w_code.co_names_w].index(name)
-            return space.wrap(cache[index].w_value is not None)
-        is_in_cache = gateway.interp2app(is_in_cache)
-        cls.w_is_in_cache = cls.space.wrap(is_in_cache) 
-        stored_builtins = []
-        def rescue_builtins(space):
-            w_dict = space.builtin.getdict()
-            content = {}
-            for key, cell in w_dict.content.iteritems():
-                newcell = ModuleCell()
-                newcell.w_value = cell.w_value
-                content[key] = newcell
-            stored_builtins.append(content)
-        rescue_builtins = gateway.interp2app(rescue_builtins)
-        cls.w_rescue_builtins = cls.space.wrap(rescue_builtins) 
-        def restore_builtins(space):
-            w_dict = space.builtin.getdict()
-            assert isinstance(w_dict, ModuleDictImplementation)
-            w_dict.content = stored_builtins.pop()
-            w_dict.fallback = None
-        restore_builtins = gateway.interp2app(restore_builtins)
-        cls.w_restore_builtins = cls.space.wrap(restore_builtins) 
-
-    def test_same_code_in_different_modules(self):
-        import sys
-        mod1 = type(sys)("abc")
-        self.impl_used(mod1.__dict__)
-        glob1 = mod1.__dict__
-        mod2 = type(sys)("abc")
-        self.impl_used(mod2.__dict__)
-        glob2 = mod2.__dict__
-        def f():
-            return x + 1
-        code = f.func_code
-        f1 = type(f)(code, glob1)
-        mod1.x = 1
-        assert not self.is_in_cache(code, glob1, "x")
-        assert f1() == 2
-        assert self.is_in_cache(code, glob1, "x")
-        assert f1() == 2
-        assert self.is_in_cache(code, glob1, "x")
-        mod1.x = 2
-        assert f1() == 3
-        assert self.is_in_cache(code, glob1, "x")
-        assert f1() == 3
-        assert self.is_in_cache(code, glob1, "x")
-        f2 = type(f)(code, glob2)
-        mod2.x = 5
-        assert not self.is_in_cache(code, glob2, "x")
-        assert f2() == 6
-        assert self.is_in_cache(code, glob2, "x")
-        assert f2() == 6
-        assert self.is_in_cache(code, glob2, "x")
-        mod2.x = 7
-        assert f2() == 8
-        assert self.is_in_cache(code, glob2, "x")
-        assert f2() == 8
-        assert self.is_in_cache(code, glob2, "x")
-
-    def test_override_builtins(self):
-        import sys, __builtin__
-        mod1 = type(sys)("abc")
-        glob1 = mod1.__dict__
-        self.impl_used(mod1.__dict__)
-        def f():
-            return len(x)
-        code = f.func_code
-        f1 = type(f)(f.func_code, glob1)
-        mod1.x = []
-        assert not self.is_in_cache(code, glob1, "len")
-        assert not self.is_in_cache(code, glob1, "x")
-        assert f1() == 0
-        assert self.is_in_cache(code, glob1, "len")
-        assert self.is_in_cache(code, glob1, "x")
-        assert f1() == 0
-        mod1.x.append(1)
-        assert f1() == 1
-        assert self.is_in_cache(code, glob1, "len")
-        assert self.is_in_cache(code, glob1, "x")
-        mod1.len = lambda x: 15
-        assert not self.is_in_cache(code, glob1, "len")
-        mod1.x.append(1)
-        assert f1() == 15
-        assert self.is_in_cache(code, glob1, "len")
-        assert f1() == 15
-        assert self.is_in_cache(code, glob1, "len")
-        del mod1.len
-        mod1.x.append(1)
-        assert not self.is_in_cache(code, glob1, "len")
-        assert f1() == 3
-        assert self.is_in_cache(code, glob1, "len")
-        assert f1() == 3
-        assert self.is_in_cache(code, glob1, "len")
-        orig_len = __builtins__.len
-        try:
-            __builtins__.len = lambda x: 12
-            mod1.x.append(1)
-            assert self.is_in_cache(code, glob1, "len")
-            assert f1() == 12
-            assert self.is_in_cache(code, glob1, "len")
-            assert f1() == 12
-            assert self.is_in_cache(code, glob1, "len")
-        finally:
-            __builtins__.len = orig_len
-
-    def test_override_builtins2(self):
-        import sys, __builtin__
-        mod1 = type(sys)("abc")
-        glob1 = mod1.__dict__
-        self.impl_used(mod1.__dict__)
-        def f():
-            return l(x)
-        code = f.func_code
-        f1 = type(f)(f.func_code, glob1)
-        mod1.x = []
-        __builtin__.l = len
-        try:
-            assert not self.is_in_cache(code, glob1, "l")
-            assert not self.is_in_cache(code, glob1, "x")
-            assert f1() == 0
-            assert self.is_in_cache(code, glob1, "l")
-            assert self.is_in_cache(code, glob1, "x")
-            assert f1() == 0
-            mod1.x.append(1)
-            assert f1() == 1
-            assert self.is_in_cache(code, glob1, "l")
-            assert self.is_in_cache(code, glob1, "x")
-            del __builtin__.l
-            mod1.l = len
-            mod1.x.append(1)
-            assert not self.is_in_cache(code, glob1, "l")
-            assert f1() == 2
-            assert self.is_in_cache(code, glob1, "l")
-            assert self.is_in_cache(code, glob1, "x")
-        finally:
-            if hasattr(__builtins__, "l"):
-                del __builtins__.l
-
-    def test_generator(self):
-        import sys, __builtin__
-        mod1 = type(sys)("abc")
-        glob1 = mod1.__dict__
-        self.impl_used(mod1.__dict__)
-        def f():
-            yield 1
-            yield x
-            yield len(x)
-        code = f.func_code
-        f1 = type(f)(f.func_code, glob1)
-        mod1.x = []
-        gen = f1()
-        assert not self.is_in_cache(code, glob1, "len")
-        assert not self.is_in_cache(code, glob1, "x")
-        v = gen.next()
-        assert v == 1
-        assert not self.is_in_cache(code, glob1, "len")
-        assert not self.is_in_cache(code, glob1, "x")
-        v = gen.next()
-        assert v is mod1.x
-        assert not self.is_in_cache(code, glob1, "len")
-        assert self.is_in_cache(code, glob1, "x")
-        v = gen.next()
-        assert v == 0
-        assert self.is_in_cache(code, glob1, "len")
-        assert self.is_in_cache(code, glob1, "x")
-
-    def test_degenerate_to_rdict(self):
-        import sys
-        mod1 = type(sys)("abc")
-        self.impl_used(mod1.__dict__)
-        glob1 = mod1.__dict__
-        def f():
-            return x + 1
-        code = f.func_code
-        f1 = type(f)(code, glob1)
-        mod1.x = 1
-        assert not self.is_in_cache(code, glob1, "x")
-        assert f1() == 2
-        assert self.is_in_cache(code, glob1, "x")
-        glob1[1] = 2
-        assert not self.is_in_cache(code, glob1, "x")
-        assert f1() == 2
-        assert not self.is_in_cache(code, glob1, "x")
-
-    def test_degenerate_builtin_to_rdict(self):
-        import sys, __builtin__
-        mod1 = type(sys)("abc")
-        self.impl_used(mod1.__dict__)
-        glob1 = mod1.__dict__
-        def f():
-            return len(x)
-        code = f.func_code
-        f1 = type(f)(code, glob1)
-        mod1.x = [1, 2]
-        assert not self.is_in_cache(code, glob1, "x")
-        assert not self.is_in_cache(code, glob1, "len")
-        assert f1() == 2
-        assert self.is_in_cache(code, glob1, "x")
-        assert self.is_in_cache(code, glob1, "len")
-        self.rescue_builtins()
-        try:
-            __builtin__.__dict__[1] = 2
-            assert not self.is_in_cache(code, glob1, "len")
-            assert f1() == 2
-            assert not self.is_in_cache(code, glob1, "len")
-        finally:
-            self.restore_builtins()
-
-    def test_mapping_as_locals(self):
-        import sys
-        if sys.version_info < (2,5) or not hasattr(sys, 'pypy_objspaceclass'):
-            skip("need CPython 2.5 or PyPy for non-dictionaries in exec statements")
-        class M(object):
-            def __getitem__(self, key):
-                return key
-            def __setitem__(self, key, value):
-                self.result[key] = value
-        m = M()
-        m.result = {}
-        exec "x=m" in {}, m
-        assert m.result == {'x': 'm'}
-        exec "y=n" in m   # NOTE: this doesn't work in CPython 2.4
-        assert m.result == {'x': 'm', 'y': 'n'}
-
-    def test_subclass_of_dict_as_locals(self):
-        import sys
-        if sys.version_info < (2,5) or not hasattr(sys, 'pypy_objspaceclass'):
-            skip("need CPython 2.5 or PyPy for non-dictionaries in exec statements")
-        class M(dict):
-            def __getitem__(self, key):
-                return key
-            def __setitem__(self, key, value):
-                dict.__setitem__(self, key, value)
-        m = M()
-        exec "x=m" in {}, m
-        assert m == {'x': 'm'}
-        exec "y=n" in m   # NOTE: this doesn't work in CPython 2.4
-        assert m == {'x': 'm', 'y': 'n'}
+space = FakeSpace()
 
+class TestCellDict(object):
+    def test_basic_property(self):
+        d = ModuleDictImplementation(space)
+        d.setitem("a", 1)
+        assert d.getcell("a", False) is d.getcell("a", False)
+        acell = d.getcell("a", False)
+        d.setitem("b", 2)
+        assert d.getcell("b", False) is d.getcell("b", False)
+        assert d.getcell("c", True) is d.getcell("c", True)
+
+        assert d.getitem("a") == 1
+        assert d.getitem("b") == 2
+
+        d.delitem("a")
+        py.test.raises(KeyError, d.delitem, "a")
+        assert d.getitem("a") is None
+        assert d.getcell("a", False) is acell
+        assert d.length() == 1
+
+        d.clear()
+        assert d.getitem("a") is None
+        assert d.getcell("a", False) is acell
+        assert d.length() == 0

Modified: pypy/branch/stringbuilder2/pypy/objspace/std/test/test_userobject.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/objspace/std/test/test_userobject.py	(original)
+++ pypy/branch/stringbuilder2/pypy/objspace/std/test/test_userobject.py	Mon Jan 25 15:25:48 2010
@@ -1,5 +1,6 @@
 import py
 from pypy.interpreter import gateway
+from pypy.objspace.test import test_descriptor
 
 
 class AppTestUserObject:
@@ -297,3 +298,10 @@
 class AppTestWithGetAttributeShortcut(AppTestUserObject):
     OPTIONS = {"objspace.std.getattributeshortcut": True}
 
+
+class AppTestDescriptorWithGetAttributeShortcut(
+    test_descriptor.AppTest_Descriptor):
+    # for the individual tests see
+    # ====> ../../test/test_descriptor.py
+
+    OPTIONS = {"objspace.std.getattributeshortcut": True}

Modified: pypy/branch/stringbuilder2/pypy/objspace/test/test_descriptor.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/objspace/test/test_descriptor.py	(original)
+++ pypy/branch/stringbuilder2/pypy/objspace/test/test_descriptor.py	Mon Jan 25 15:25:48 2010
@@ -1,6 +1,13 @@
+import py
+from pypy.conftest import gettestobjspace
 
 class AppTest_Descriptor:
 
+    OPTIONS = {}
+
+    def setup_class(cls):
+        cls.space = gettestobjspace(**cls.OPTIONS)
+
     def test_non_data_descr(self):
         class X(object):
             def f(self):
@@ -12,6 +19,49 @@
         del x.f
         assert x.f() == 42
 
+    def test_set_without_get(self):
+        class Descr(object):
+
+            def __init__(self, name):
+                self.name = name
+
+            def __set__(self, obj, value):
+                obj.__dict__[self.name] = value
+        descr = Descr("a")
+
+        class X(object):
+            a = descr
+
+        x = X()
+        assert x.a is descr
+        x.a = 42
+        assert x.a == 42
+
+    def test_failing_get(self):
+        # when __get__() raises AttributeError,
+        # __getattr__ is called...
+        class X(object):
+            def get_v(self):
+                raise AttributeError
+            v = property(get_v)
+
+            def __getattr__(self, name):
+                if name == 'v':
+                    return 42
+        x = X()
+        assert x.v == 42
+
+        # ... but the __dict__ is not searched
+        class Y(object):
+            def get_w(self):
+                raise AttributeError
+            def set_w(self, value):
+                raise AttributeError
+            w = property(get_w, set_w)
+        y = Y()
+        y.__dict__['w'] = 42
+        raises(AttributeError, getattr, y, 'w')
+
     def test_member(self):
         class X(object):
             def __init__(self):

Modified: pypy/branch/stringbuilder2/pypy/objspace/test/test_descroperation.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/objspace/test/test_descroperation.py	(original)
+++ pypy/branch/stringbuilder2/pypy/objspace/test/test_descroperation.py	Mon Jan 25 15:25:48 2010
@@ -174,6 +174,29 @@
             (0,   slice_max),
             ]
 
+    def test_getslice_nolength(self):
+        class Sq(object):
+            def __getslice__(self, start, stop):
+                return (start, stop)
+            def __getitem__(self, key):
+                return "booh"
+
+        sq = Sq()
+
+        assert sq[1:3] == (1,3)
+        slice_min, slice_max = sq[:]
+        assert slice_min == 0
+        assert slice_max >= 2**31-1
+        assert sq[1:] == (1, slice_max)
+        assert sq[:3] == (0, 3)
+        assert sq[:] == (0, slice_max)
+        # negative indices, but no __len__
+        assert sq[-1:3] == (-1, 3)
+        assert sq[1:-3] == (1, -3)
+        assert sq[-1:-3] == (-1, -3)
+        # extended slice syntax always uses __getitem__()
+        assert sq[::] == "booh"
+
     def test_ipow(self):
         x = 2
         x **= 5

Modified: pypy/branch/stringbuilder2/pypy/rlib/debug.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/rlib/debug.py	(original)
+++ pypy/branch/stringbuilder2/pypy/rlib/debug.py	Mon Jan 25 15:25:48 2010
@@ -19,6 +19,14 @@
         hop.exception_cannot_occur()
         hop.genop('debug_assert', vlist)
 
+def fatalerror(msg, traceback=False):
+    from pypy.rpython.lltypesystem import lltype
+    from pypy.rpython.lltypesystem.lloperation import llop
+    if traceback:
+        llop.debug_print_traceback(lltype.Void)
+    llop.debug_fatalerror(lltype.Void, msg)
+fatalerror._dont_inline_ = True
+
 
 class DebugLog(list):
     def debug_print(self, *args):

Modified: pypy/branch/stringbuilder2/pypy/rlib/jit.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/rlib/jit.py	(original)
+++ pypy/branch/stringbuilder2/pypy/rlib/jit.py	Mon Jan 25 15:25:48 2010
@@ -2,6 +2,7 @@
 import sys
 from pypy.rpython.extregistry import ExtRegistryEntry
 from pypy.rlib.objectmodel import CDefinedIntSymbolic
+from pypy.rlib.objectmodel import keepalive_until_here
 from pypy.rlib.unroll import unrolling_iterable
 
 def purefunction(func):
@@ -19,6 +20,12 @@
     func._jit_unroll_safe_ = True
     return func
 
+def loop_invariant(func):
+    dont_look_inside(func)
+    func._jit_loop_invariant_ = True
+    return func
+
+
 def purefunction_promote(func):
     import inspect
     purefunction(func)
@@ -80,8 +87,9 @@
 
 
 def we_are_jitted():
+    """ Considered as true during tracing and blackholing,
+    so its consquences are reflected into jitted code """
     return False
-# timeshifts to True
 
 _we_are_jitted = CDefinedIntSymbolic('0 /* we are not jitted here */',
                                      default=0)
@@ -98,6 +106,85 @@
         hop.exception_cannot_occur()
         return hop.inputconst(lltype.Signed, _we_are_jitted)
 
+
+##def force_virtualizable(virtualizable):
+##    pass
+
+##class Entry(ExtRegistryEntry):
+##    _about_ = force_virtualizable
+
+##    def compute_result_annotation(self):
+##        from pypy.annotation import model as annmodel
+##        return annmodel.s_None
+
+##    def specialize_call(self, hop):
+##        [vinst] = hop.inputargs(hop.args_r[0])
+##        cname = inputconst(lltype.Void, None)
+##        cflags = inputconst(lltype.Void, {})
+##        hop.exception_cannot_occur()
+##        return hop.genop('jit_force_virtualizable', [vinst, cname, cflags],
+##                         resulttype=lltype.Void)
+
+# ____________________________________________________________
+# VRefs
+
+def virtual_ref(x):
+    
+    """Creates a 'vref' object that contains a reference to 'x'.  Calls
+    to virtual_ref/virtual_ref_finish must be properly nested.  The idea
+    is that the object 'x' is supposed to be JITted as a virtual between
+    the calls to virtual_ref and virtual_ref_finish, but the 'vref'
+    object can escape at any point in time.  If at runtime it is
+    dereferenced (by the call syntax 'vref()'), it returns 'x', which is
+    then forced."""
+    return DirectJitVRef(x)
+virtual_ref.oopspec = 'virtual_ref(x)'
+
+def virtual_ref_finish(x):
+    """See docstring in virtual_ref(x).  Note that virtual_ref_finish
+    takes as argument the real object, not the vref."""
+    keepalive_until_here(x)   # otherwise the whole function call is removed
+virtual_ref_finish.oopspec = 'virtual_ref_finish(x)'
+
+def non_virtual_ref(x):
+    """Creates a 'vref' that just returns x when called; nothing more special.
+    Used for None or for frames outside JIT scope."""
+    return DirectVRef(x)
+
+# ---------- implementation-specific ----------
+
+class DirectVRef(object):
+    def __init__(self, x):
+        self._x = x
+    def __call__(self):
+        return self._x
+
+class DirectJitVRef(DirectVRef):
+    def __init__(self, x):
+        assert x is not None, "virtual_ref(None) is not allowed"
+        DirectVRef.__init__(self, x)
+
+class Entry(ExtRegistryEntry):
+    _about_ = (non_virtual_ref, DirectJitVRef)
+
+    def compute_result_annotation(self, s_obj):
+        from pypy.rlib import _jit_vref
+        return _jit_vref.SomeVRef(s_obj)
+
+    def specialize_call(self, hop):
+        return hop.r_result.specialize_call(hop)
+
+class Entry(ExtRegistryEntry):
+    _type_ = DirectVRef
+
+    def compute_annotation(self):
+        from pypy.rlib import _jit_vref
+        assert isinstance(self.instance, DirectVRef)
+        s_obj = self.bookkeeper.immutablevalue(self.instance())
+        return _jit_vref.SomeVRef(s_obj)
+
+vref_None = non_virtual_ref(None)
+
 # ____________________________________________________________
 # User interface for the hotpath JIT policy
 
@@ -135,7 +222,8 @@
     def __init__(self, greens=None, reds=None, virtualizables=None,
                  get_jitcell_at=None, set_jitcell_at=None,
                  can_inline=None, get_printable_location=None,
-                 leave=None):
+                 confirm_enter_jit=None,
+                 leave=None):   # XXX 'leave' is deprecated
         if greens is not None:
             self.greens = greens
         if reds is not None:
@@ -152,6 +240,7 @@
         self.set_jitcell_at = set_jitcell_at
         self.get_printable_location = get_printable_location
         self.can_inline = can_inline
+        self.confirm_enter_jit = confirm_enter_jit
         self.leave = leave
 
     def _freeze_(self):

Modified: pypy/branch/stringbuilder2/pypy/rlib/rgc.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/rlib/rgc.py	(original)
+++ pypy/branch/stringbuilder2/pypy/rlib/rgc.py	Mon Jan 25 15:25:48 2010
@@ -1,5 +1,7 @@
 import gc
 from pypy.rpython.extregistry import ExtRegistryEntry
+from pypy.rlib.objectmodel import we_are_translated
+
 # ____________________________________________________________
 # General GC features
 
@@ -334,7 +336,12 @@
     from pypy.rpython.lltypesystem import lltype, llmemory
     from pypy.rlib.objectmodel import keepalive_until_here
 
-    assert source != dest
+    # supports non-overlapping copies only
+    if not we_are_translated():
+        if source == dest:
+            assert (source_start + length <= dest_start or
+                    dest_start + length <= source_start)
+
     TP = lltype.typeOf(source).TO
     assert TP == lltype.typeOf(dest).TO
     if isinstance(TP.OF, lltype.Ptr) and TP.OF.TO._gckind == 'gc':
@@ -357,3 +364,9 @@
     keepalive_until_here(source)
     keepalive_until_here(dest)
 ll_arraycopy._annspecialcase_ = 'specialize:ll'
+ll_arraycopy._jit_look_inside_ = False
+
+def no_collect(func):
+    func._dont_inline_ = True
+    func._gc_no_collect_ = True
+    return func

Modified: pypy/branch/stringbuilder2/pypy/rpython/llinterp.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/rpython/llinterp.py	(original)
+++ pypy/branch/stringbuilder2/pypy/rpython/llinterp.py	Mon Jan 25 15:25:48 2010
@@ -543,10 +543,22 @@
     def op_debug_llinterpcall(self, pythonfunction, *args_ll):
         return pythonfunction(*args_ll)
 
-    def op_jit_marker(self, *args):
-        pass
+    def op_debug_start_traceback(self, *args):
+        pass    # xxx write debugging code here?
+
+    def op_debug_reraise_traceback(self, *args):
+        pass    # xxx write debugging code here?
+
+    def op_debug_record_traceback(self, *args):
+        pass    # xxx write debugging code here?
 
-    def op_promote_virtualizable(self, *args):
+    def op_debug_print_traceback(self, *args):
+        pass    # xxx write debugging code here?
+
+    def op_debug_catch_exception(self, *args):
+        pass    # xxx write debugging code here?
+
+    def op_jit_marker(self, *args):
         pass
 
     def op_get_exception_addr(self, *args):

Modified: pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/ll2ctypes.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/ll2ctypes.py	(original)
+++ pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/ll2ctypes.py	Mon Jan 25 15:25:48 2010
@@ -21,8 +21,7 @@
 from pypy.rlib.rarithmetic import r_uint, r_singlefloat, intmask
 from pypy.annotation import model as annmodel
 from pypy.rpython.llinterp import LLInterpreter, LLException
-from pypy.rpython.lltypesystem.rclass import OBJECT
-from pypy.rpython.annlowlevel import base_ptr_lltype
+from pypy.rpython.lltypesystem.rclass import OBJECT, OBJECT_VTABLE
 from pypy.rpython import raddress
 from pypy.translator.platform import platform
 
@@ -31,7 +30,6 @@
 
 _ctypes_cache = {}
 _eci_cache = {}
-_parent_cache = {}
 
 def _setup_ctypes_cache():
     from pypy.rpython.lltypesystem import rffi
@@ -255,7 +253,6 @@
                 convert_struct(field_value, csubstruct)
                 subcontainer = getattr(container, field_name)
                 substorage = subcontainer._storage
-                update_parent_cache(substorage, subcontainer)
             elif field_name == STRUCT._arrayfld:    # inlined var-sized part
                 csubarray = getattr(cstruct, field_name)
                 convert_array(field_value, csubarray)
@@ -314,7 +311,6 @@
                 struct_storage = getattr(ctypes_storage, field_name)
                 struct_use_ctypes_storage(struct_container, struct_storage)
                 struct_container._setparentstructure(container, field_name)
-                update_parent_cache(ctypes_storage, struct_container)
             elif isinstance(FIELDTYPE, lltype.Array):
                 assert FIELDTYPE._hints.get('nolength', False) == False
                 arraycontainer = _array_of_known_length(FIELDTYPE)
@@ -500,25 +496,6 @@
 _callback2obj = {}
 _callback_exc_info = None
 
-# this is just another hack that passes around references to applevel types
-# disguised as base_ptr_lltype
-class Dummy(object):
-    pass
-
-_opaque_cache = {Dummy():0}
-_opaque_list = [Dummy()]
-
-def new_opaque_object(llobj):
-    try:
-        return _opaque_cache[llobj]
-    except KeyError:
-        assert len(_opaque_cache) == len(_opaque_list)
-        ctypes_type = get_ctypes_type(base_ptr_lltype())
-        val = ctypes.cast(len(_opaque_cache), ctypes_type)
-        _opaque_list.append(llobj)
-        _opaque_cache[llobj] = val
-        return val
-
 def get_rtyper():
     llinterp = LLInterpreter.current_interpreter
     if llinterp is not None:
@@ -542,8 +519,6 @@
                 return ctypes.c_void_p(0)
             return get_ctypes_type(T)()
 
-        if T is base_ptr_lltype():
-            return new_opaque_object(llobj)
         if T == llmemory.GCREF:
             if isinstance(llobj._obj, _llgcopaque):
                 return ctypes.c_void_p(llobj._obj.intval)
@@ -655,8 +630,6 @@
                 raise NotImplementedError(T)
             container._ctypes_storage_was_allocated()
         storage = container._storage
-        if lltype.parentlink(container)[0] is not None:
-            update_parent_cache(storage, container)
         p = ctypes.pointer(storage)
         if index:
             p = ctypes.cast(p, ctypes.c_void_p)
@@ -694,29 +667,37 @@
     if isinstance(T, lltype.Ptr):
         if not cobj:   # NULL pointer
             return lltype.nullptr(T.TO)
-        if T is base_ptr_lltype():
-            return _opaque_list[ctypes.cast(cobj, ctypes.c_void_p).value]
         if isinstance(T.TO, lltype.Struct):
+            REAL_TYPE = T.TO
             if T.TO._arrayfld is not None:
                 carray = getattr(cobj.contents, T.TO._arrayfld)
                 container = lltype._struct(T.TO, carray.length)
             else:
                 # special treatment of 'OBJECT' subclasses
-                if get_rtyper() and lltype._castdepth(T.TO, OBJECT) > 0:
-                    ctypes_object = get_ctypes_type(lltype.Ptr(OBJECT))
-                    as_obj = ctypes2lltype(lltype.Ptr(OBJECT),
-                                           ctypes.cast(cobj, ctypes_object))
-                    TObj = get_rtyper().get_type_for_typeptr(as_obj.typeptr)
-                    if TObj != T.TO:
-                        ctypes_instance = get_ctypes_type(lltype.Ptr(TObj))
-                        return lltype.cast_pointer(T,
-                            ctypes2lltype(lltype.Ptr(TObj),
-                                          ctypes.cast(cobj, ctypes_instance)))
-                container = lltype._struct(T.TO)
+                if get_rtyper() and lltype._castdepth(REAL_TYPE, OBJECT) >= 0:
+                    # figure out the real type of the object
+                    containerheader = lltype._struct(OBJECT)
+                    cobjheader = ctypes.cast(cobj,
+                                       get_ctypes_type(lltype.Ptr(OBJECT)))
+                    struct_use_ctypes_storage(containerheader,
+                                              cobjheader.contents)
+                    REAL_TYPE = get_rtyper().get_type_for_typeptr(
+                        containerheader.typeptr)
+                    REAL_T = lltype.Ptr(REAL_TYPE)
+                    cobj = ctypes.cast(cobj, get_ctypes_type(REAL_T))
+                container = lltype._struct(REAL_TYPE)
             struct_use_ctypes_storage(container, cobj.contents)
-            addr = ctypes.addressof(cobj.contents)
-            if addr in _parent_cache:
-                setparentstructure(container, _parent_cache[addr])
+            if REAL_TYPE != T.TO:
+                p = container._as_ptr()
+                container = lltype.cast_pointer(T, p)._as_obj()
+            # special treatment of 'OBJECT_VTABLE' subclasses
+            if get_rtyper() and lltype._castdepth(REAL_TYPE,
+                                                  OBJECT_VTABLE) >= 0:
+                # figure out the real object that this vtable points to,
+                # and just return that
+                p = get_rtyper().get_real_typeptr_for_typeptr(
+                    container._as_ptr())
+                container = lltype.cast_pointer(T, p)._as_obj()
         elif isinstance(T.TO, lltype.Array):
             if T.TO._hints.get('nolength', False):
                 container = _array_of_unknown_length(T.TO)
@@ -1163,46 +1144,6 @@
         return hop.genop('cast_adr_to_int', [adr],
                          resulttype = lltype.Signed)
 
-# ------------------------------------------------------------
-
-def parentchain(container):
-    current = container
-    links = []
-    while True:
-        link = lltype.parentlink(current)
-        if link[0] is None:
-            try:
-                addr = ctypes.addressof(container._storage)
-                actual = _parent_cache[addr]
-                if len(links) < len(actual):
-                    return actual
-            except KeyError:
-                pass
-            return links
-        links.append(link)
-        current = link[0]
-
-def update_parent_cache(storage, container):
-    chain = parentchain(container)
-    addr = ctypes.addressof(storage)
-    try:
-        current = _parent_cache[addr]
-        if len(chain) > len(current):
-            _parent_cache[addr] = chain
-    except KeyError:
-        _parent_cache[addr] = chain
-
-def setparentstructure(container, chain):
-    TP = lltype.typeOf(container)
-    current = container
-    for i, elem in enumerate(chain):
-        if lltype.typeOf(elem[0]) == TP:
-            chain = chain[i + 1:]
-            break
-    for elem in chain:
-        current._setparentstructure(*elem)
-        current = elem[0]
-
 # ____________________________________________________________
 # errno
 

Modified: pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/lloperation.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/lloperation.py	(original)
+++ pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/lloperation.py	Mon Jan 25 15:25:48 2010
@@ -427,7 +427,8 @@
     # __________ used by the JIT ________
 
     'jit_marker':           LLOp(),
-    'promote_virtualizable':LLOp(canrun=True),
+    'jit_force_virtualizable':LLOp(canrun=True),
+    'jit_force_virtual':    LLOp(canrun=True),
     'get_exception_addr':   LLOp(),
     'get_exc_value_addr':   LLOp(),
     'do_malloc_fixedsize_clear': LLOp(canunwindgc=True),
@@ -535,6 +536,11 @@
     'debug_fatalerror':     LLOp(),
     'debug_llinterpcall':   LLOp(), # Python func call 'res=arg[0](*arg[1:])'
                                     # in backends, abort() or whatever is fine
+    'debug_start_traceback':   LLOp(),
+    'debug_record_traceback':  LLOp(),
+    'debug_catch_exception':   LLOp(),
+    'debug_reraise_traceback': LLOp(),
+    'debug_print_traceback':   LLOp(),
 
     # __________ instrumentation _________
     'instrument_count':     LLOp(),

Modified: pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/opimpl.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/opimpl.py	(original)
+++ pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/opimpl.py	Mon Jan 25 15:25:48 2010
@@ -444,8 +444,11 @@
 def op_gc_stack_bottom():
     pass       # marker for trackgcroot.py
 
-def op_promote_virtualizable(object, fieldname, flags):
-    pass # XXX should do something
+def op_jit_force_virtualizable(*args):
+    pass
+
+def op_jit_force_virtual(x):
+    return x
 
 def op_get_group_member(TYPE, grpptr, memberoffset):
     from pypy.rpython.lltypesystem import llgroup

Modified: pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/rclass.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/rclass.py	(original)
+++ pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/rclass.py	Mon Jan 25 15:25:48 2010
@@ -86,6 +86,13 @@
         vtable = vtable.super
     return vtable
 
+def alloc_array_name(name):
+    p = malloc(Array(Char), len(name)+1, immortal=True)
+    for i in range(len(name)):
+        p[i] = name[i]
+    p[len(name)] = '\x00'
+    return p
+
 
 class ClassRepr(AbstractClassRepr):
     def __init__(self, rtyper, classdef):
@@ -192,10 +199,7 @@
                 name = 'object'
             else:
                 name = rsubcls.classdef.shortname
-            vtable.name = malloc(Array(Char), len(name)+1, immortal=True)
-            for i in range(len(name)):
-                vtable.name[i] = name[i]
-            vtable.name[len(name)] = '\x00'
+            vtable.name = alloc_array_name(name)
             if hasattr(rsubcls.classdef, 'my_instantiate_graph'):
                 graph = rsubcls.classdef.my_instantiate_graph
                 vtable.instantiate = self.rtyper.getcallable(graph)
@@ -379,7 +383,7 @@
                                                   ll_runtime_type_info,
                                                   OBJECT, destrptr)
             vtable = self.rclass.getvtable()
-            self.rtyper.type_for_typeptr[vtable._obj] = self.lowleveltype.TO
+            self.rtyper.set_type_for_typeptr(vtable, self.lowleveltype.TO)
             self.rtyper.lltype2vtable[self.lowleveltype.TO] = vtable
 
     def common_repr(self): # -> object or nongcobject reprs
@@ -689,3 +693,23 @@
             break
     raise AttributeError("%s has no field %s" % (lltype.typeOf(widest),
                                                  name))
+
+def declare_type_for_typeptr(vtable, TYPE):
+    """Hack for custom low-level-only 'subclasses' of OBJECT:
+    call this somewhere annotated, in order to declare that it is
+    of the given TYPE and has got the corresponding vtable."""
+
+class Entry(ExtRegistryEntry):
+    _about_ = declare_type_for_typeptr
+    def compute_result_annotation(self, s_vtable, s_TYPE):
+        assert s_vtable.is_constant()
+        assert s_TYPE.is_constant()
+        return annmodel.s_None
+    def specialize_call(self, hop):
+        vtable = hop.args_v[0].value
+        TYPE   = hop.args_v[1].value
+        assert lltype.typeOf(vtable) == CLASSTYPE
+        assert isinstance(TYPE, GcStruct)
+        assert lltype._castdepth(TYPE, OBJECT) > 0
+        hop.rtyper.set_type_for_typeptr(vtable, TYPE)
+        return hop.inputconst(lltype.Void, None)

Modified: pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/rlist.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/rlist.py	(original)
+++ pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/rlist.py	Mon Jan 25 15:25:48 2010
@@ -368,8 +368,7 @@
     else:
         LIST = typeOf(l).TO
         newitems = malloc(LIST.items.TO, n)
-        for i in range(n):
-            newitems[i] = olditems[i]
+        rgc.ll_arraycopy(olditems, newitems, 0, 0, n)
         return newitems
 ll_list2fixed.oopspec = 'list.list2fixed(l)'
 

Modified: pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/test/test_ll2ctypes.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/test/test_ll2ctypes.py	(original)
+++ pypy/branch/stringbuilder2/pypy/rpython/lltypesystem/test/test_ll2ctypes.py	Mon Jan 25 15:25:48 2010
@@ -928,34 +928,6 @@
         assert op.args[1].value == pypy.rpython.lltypesystem.rstr.LLHelpers
         assert op.args[3].value == -2
 
-    def test_pass_around_t_object(self):
-        from pypy.rpython.annlowlevel import base_ptr_lltype
-        T = base_ptr_lltype()
-        
-        class X(object):
-            _TYPE = T
-            x = 10
-
-        def callback(x):
-            return x.x
-
-        c_source = py.code.Source("""
-        long eating_callback(void *arg, long(*call)(void*))
-        {
-            return call(arg);
-        }
-        """)
-
-        eci = ExternalCompilationInfo(separate_module_sources=[c_source],
-                                      export_symbols=['eating_callback'])
-
-        args = [T, rffi.CCallback([T], rffi.LONG)]
-        eating_callback = rffi.llexternal('eating_callback', args, rffi.LONG,
-                                          compilation_info=eci)
-
-        res = eating_callback(X(), callback)
-        assert res == 10
-
     def test_recursive_struct_more(self):
         NODE = lltype.ForwardReference()
         NODE.become(lltype.Struct('NODE', ('value', lltype.Signed),
@@ -1134,7 +1106,104 @@
         #import pdb; pdb.set_trace()
         assert adr1_2 == adr1
         assert adr1 == adr1_2
-        
+
+    def test_object_subclass(self):
+        from pypy.rpython.lltypesystem import rclass
+        from pypy.rpython.annlowlevel import cast_instance_to_base_ptr
+        from pypy.rpython.annlowlevel import cast_base_ptr_to_instance
+        class S:
+            pass
+        def f(n):
+            s = S()
+            s.x = n
+            ls = cast_instance_to_base_ptr(s)
+            as_num = rffi.cast(lltype.Signed, ls)
+            # --- around this point, only 'as_num' is passed
+            t = rffi.cast(rclass.OBJECTPTR, as_num)
+            u = cast_base_ptr_to_instance(S, t)
+            return u.x
+        res = interpret(f, [123])
+        assert res == 123
+
+    def test_object_subclass_2(self):
+        from pypy.rpython.lltypesystem import rclass
+        SCLASS = lltype.GcStruct('SCLASS',
+                                 ('parent', rclass.OBJECT),
+                                 ('n', lltype.Signed))
+        sclass_vtable = lltype.malloc(rclass.OBJECT_VTABLE, zero=True,
+                                      immortal=True)
+        sclass_vtable.name = rclass.alloc_array_name('SClass')
+        def f(n):
+            rclass.declare_type_for_typeptr(sclass_vtable, SCLASS)
+            s = lltype.malloc(SCLASS)
+            s.parent.typeptr = sclass_vtable
+            s.n = n
+            as_num = rffi.cast(lltype.Signed, s)
+            # --- around this point, only 'as_num' is passed
+            t = rffi.cast(lltype.Ptr(SCLASS), as_num)
+            return t.n
+        res = interpret(f, [123])
+        assert res == 123
+
+    def test_object_subclass_3(self):
+        from pypy.rpython.lltypesystem import rclass
+        from pypy.rpython.annlowlevel import cast_instance_to_base_ptr
+        from pypy.rpython.annlowlevel import cast_base_ptr_to_instance
+        class S:
+            pass
+        def f(n):
+            s = S()
+            s.x = n
+            ls = cast_instance_to_base_ptr(s)
+            as_num = rffi.cast(lltype.Signed, ls)
+            # --- around this point, only 'as_num' is passed
+            r = rffi.cast(llmemory.GCREF, as_num)
+            t = lltype.cast_opaque_ptr(rclass.OBJECTPTR, r)
+            u = cast_base_ptr_to_instance(S, t)
+            return u.x
+        res = interpret(f, [123])
+        assert res == 123
+
+    def test_object_subclass_4(self):
+        from pypy.rpython.lltypesystem import rclass
+        SCLASS = lltype.GcStruct('SCLASS',
+                                 ('parent', rclass.OBJECT),
+                                 ('n', lltype.Signed))
+        sclass_vtable = lltype.malloc(rclass.OBJECT_VTABLE, zero=True,
+                                      immortal=True)
+        sclass_vtable.name = rclass.alloc_array_name('SClass')
+        def f(n):
+            rclass.declare_type_for_typeptr(sclass_vtable, SCLASS)
+            s = lltype.malloc(SCLASS)
+            s.parent.typeptr = sclass_vtable
+            s.n = n
+            as_num = rffi.cast(lltype.Signed, s)
+            # --- around this point, only 'as_num' is passed
+            r = rffi.cast(llmemory.GCREF, as_num)
+            t = lltype.cast_opaque_ptr(lltype.Ptr(SCLASS), r)
+            return t.n
+        res = interpret(f, [123])
+        assert res == 123
+
+    def test_object_subclass_5(self):
+        from pypy.rpython.lltypesystem import rclass
+        from pypy.rpython.annlowlevel import cast_instance_to_base_ptr
+        from pypy.rpython.annlowlevel import cast_base_ptr_to_instance
+        class S:
+            x = 5      # entry in the vtable
+        class T(S):
+            x = 6
+        def f():
+            s = T()
+            ls = cast_instance_to_base_ptr(s)
+            as_num = rffi.cast(lltype.Signed, ls)
+            # --- around this point, only 'as_num' is passed
+            t = rffi.cast(rclass.OBJECTPTR, as_num)
+            u = cast_base_ptr_to_instance(S, t)
+            return u.x
+        res = interpret(f, [])
+        assert res == 6
+
 class TestPlatform(object):
     def test_lib_on_libpaths(self):
         from pypy.translator.platform import platform

Modified: pypy/branch/stringbuilder2/pypy/rpython/memory/gctransform/framework.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/rpython/memory/gctransform/framework.py	(original)
+++ pypy/branch/stringbuilder2/pypy/rpython/memory/gctransform/framework.py	Mon Jan 25 15:25:48 2010
@@ -567,6 +567,12 @@
         f.close()
 
     def transform_graph(self, graph):
+        func = getattr(graph, 'func', None)
+        if func and getattr(func, '_gc_no_collect_', False):
+            if self.collect_analyzer.analyze_direct_call(graph):
+                raise Exception("no_collect function can trigger collection: %s"
+                                % func.__name__)
+            
         if self.write_barrier_ptr:
             self.clean_sets = (
                 find_clean_setarrayitems(self.collect_analyzer, graph).union(

Modified: pypy/branch/stringbuilder2/pypy/rpython/memory/gctransform/test/test_framework.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/rpython/memory/gctransform/test/test_framework.py	(original)
+++ pypy/branch/stringbuilder2/pypy/rpython/memory/gctransform/test/test_framework.py	Mon Jan 25 15:25:48 2010
@@ -1,5 +1,6 @@
 from pypy.objspace.flow.model import Constant, SpaceOperation
 from pypy.annotation.model import SomeInteger
+from pypy.annotation.listdef import s_list_of_strings
 from pypy.rpython.memory.gc.marksweep import MarkSweepGC
 from pypy.rpython.memory.gctransform.test.test_transform import rtype, \
     rtype_and_transform
@@ -34,7 +35,6 @@
     from pypy.rpython.llinterp import LLInterpreter
     from pypy.translator.c.genc import CStandaloneBuilder
     from pypy.translator.c import gc
-    from pypy.annotation.listdef import s_list_of_strings
 
     t = rtype(entrypoint, [s_list_of_strings])
     cbuild = CStandaloneBuilder(t, entrypoint, t.config,
@@ -113,6 +113,50 @@
     gg = graphof(t, g)
     assert CollectAnalyzer(t).analyze_direct_call(gg)
 
+def test_no_collect():
+    from pypy.rlib import rgc
+    from pypy.translator.c.genc import CStandaloneBuilder
+    from pypy.translator.c import gc
+
+    @rgc.no_collect
+    def g():
+        return 1
+
+    assert g._dont_inline_
+    assert g._gc_no_collect_
+
+    def entrypoint(argv):
+        return g() + 2
+    
+    t = rtype(entrypoint, [s_list_of_strings])
+    cbuild = CStandaloneBuilder(t, entrypoint, t.config,
+                                gcpolicy=FrameworkGcPolicy2)
+    db = cbuild.generate_graphs_for_llinterp()
+
+def test_no_collect_detection():
+    from pypy.rlib import rgc
+    from pypy.translator.c.genc import CStandaloneBuilder
+    from pypy.translator.c import gc
+
+    class A(object):
+        def __init__(self, x):
+            self.x = x
+
+    @rgc.no_collect
+    def g():
+        return A(1).x
+
+    assert g._dont_inline_
+    assert g._gc_no_collect_
+
+    def entrypoint(argv):
+        return g() + 2
+    
+    t = rtype(entrypoint, [s_list_of_strings])
+    cbuild = CStandaloneBuilder(t, entrypoint, t.config,
+                                gcpolicy=FrameworkGcPolicy2)
+    f = py.test.raises(Exception, cbuild.generate_graphs_for_llinterp)
+    assert str(f.value) == 'no_collect function can trigger collection: g'
 
 class WriteBarrierTransformer(FrameworkGCTransformer):
     clean_sets = {}

Modified: pypy/branch/stringbuilder2/pypy/rpython/module/ll_os.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/rpython/module/ll_os.py	(original)
+++ pypy/branch/stringbuilder2/pypy/rpython/module/ll_os.py	Mon Jan 25 15:25:48 2010
@@ -1400,6 +1400,30 @@
         return extdef([], int, llimpl=fork_llimpl,
                       export_name="ll_os.ll_os_fork")
 
+    @registering_if(os, 'openpty')
+    def register_os_openpty(self):
+        os_openpty = self.llexternal(
+            'openpty',
+            [rffi.INTP, rffi.INTP, rffi.VOIDP, rffi.VOIDP, rffi.VOIDP],
+            rffi.INT,
+            compilation_info=ExternalCompilationInfo(libraries=['util']))
+        def openpty_llimpl():
+            master_p = lltype.malloc(rffi.INTP.TO, 1, flavor='raw')
+            slave_p = lltype.malloc(rffi.INTP.TO, 1, flavor='raw')
+            result = os_openpty(master_p, slave_p, None, None, None)
+            master_fd = master_p[0]
+            slave_fd = slave_p[0]
+            lltype.free(master_p, flavor='raw')
+            lltype.free(slave_p, flavor='raw')
+            if result == -1:
+                raise OSError(rposix.get_errno(), "os_openpty failed")
+            return (rffi.cast(lltype.Signed, master_fd),
+                    rffi.cast(lltype.Signed, slave_fd))
+
+        return extdef([], (int, int), "ll_os.ll_os_openpty",
+                      llimpl=openpty_llimpl)
+
+
     @registering(os._exit)
     def register_os__exit(self):
         os__exit = self.llexternal('_exit', [rffi.INT], lltype.Void)

Modified: pypy/branch/stringbuilder2/pypy/rpython/rlist.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/rpython/rlist.py	(original)
+++ pypy/branch/stringbuilder2/pypy/rpython/rlist.py	Mon Jan 25 15:25:48 2010
@@ -11,6 +11,7 @@
 from pypy.rlib.debug import ll_assert
 from pypy.rlib.rarithmetic import ovfcheck, widen
 from pypy.rpython.annlowlevel import ADTInterface
+from pypy.rlib import rgc
 
 ADTIFixedList = ADTInterface(None, {
     'll_newlist':      (['SELF', Signed        ], 'self'),
@@ -525,13 +526,24 @@
     return LIST.ITEM
 
 
+def ll_arraycopy(source, dest, source_start, dest_start, length):
+    SRCTYPE = typeOf(source)
+    if isinstance(SRCTYPE, Ptr):
+        # lltype
+        rgc.ll_arraycopy(source.ll_items(), dest.ll_items(),
+                         source_start, dest_start, length)
+    else:
+        # ootype -- XXX improve the case of array->array copy?
+        i = 0
+        while i < length:
+            item = source.ll_getitem_fast(source_start + i)
+            dest.ll_setitem_fast(dest_start + i, item)
+            i += 1
+
 def ll_copy(RESLIST, l):
     length = l.ll_length()
     new_lst = RESLIST.ll_newlist(length)
-    i = 0
-    while i < length:
-        new_lst.ll_setitem_fast(i, l.ll_getitem_fast(i))
-        i += 1
+    ll_arraycopy(l, new_lst, 0, 0, length)
     return new_lst
 
 def ll_len(l):
@@ -574,15 +586,8 @@
     except OverflowError:
         raise MemoryError
     l = RESLIST.ll_newlist(newlength)
-    j = 0
-    while j < len1:
-        l.ll_setitem_fast(j, l1.ll_getitem_fast(j))
-        j += 1
-    i = 0
-    while i < len2:
-        l.ll_setitem_fast(j, l2.ll_getitem_fast(i))
-        i += 1
-        j += 1
+    ll_arraycopy(l1, l, 0, 0, len1)
+    ll_arraycopy(l2, l, 0, len1, len2)
     return l
 
 def ll_insert_nonneg(l, index, newitem):
@@ -769,12 +774,7 @@
     except OverflowError:
         raise MemoryError
     l1._ll_resize_ge(newlength)
-    i = 0
-    j = len1
-    while i < len2:
-        l1.ll_setitem_fast(j, l2.ll_getitem_fast(i))
-        i += 1
-        j += 1
+    ll_arraycopy(l2, l1, 0, len1, len2)
 ll_extend.oopspec = 'list.extend(l1, l2)'
 
 def ll_extend_with_str(lst, s, getstrlen, getstritem):
@@ -867,12 +867,7 @@
     ll_assert(start <= len1, "list slice start larger than list length")
     newlength = len1 - start
     l = RESLIST.ll_newlist(newlength)
-    j = 0
-    i = start
-    while i < len1:
-        l.ll_setitem_fast(j, l1.ll_getitem_fast(i))
-        i += 1
-        j += 1
+    ll_arraycopy(l1, l, start, 0, newlength)
     return l
 ll_listslice_startonly._annenforceargs_ = (None, None, int)
 
@@ -885,22 +880,14 @@
         stop = length
     newlength = stop - start
     l = RESLIST.ll_newlist(newlength)
-    j = 0
-    i = start
-    while i < stop:
-        l.ll_setitem_fast(j, l1.ll_getitem_fast(i))
-        i += 1
-        j += 1
+    ll_arraycopy(l1, l, start, 0, newlength)
     return l
 
 def ll_listslice_minusone(RESLIST, l1):
     newlength = l1.ll_length() - 1
     ll_assert(newlength >= 0, "empty list is sliced with [:-1]")
     l = RESLIST.ll_newlist(newlength)
-    j = 0
-    while j < newlength:
-        l.ll_setitem_fast(j, l1.ll_getitem_fast(j))
-        j += 1
+    ll_arraycopy(l1, l, 0, 0, newlength)
     return l
 
 def ll_listdelslice_startonly(l, start):
@@ -945,13 +932,8 @@
     ll_assert(start <= l1.ll_length(), "l[start:x] = l with start > len(l)")
     ll_assert(count == stop - start,
                  "setslice cannot resize lists in RPython")
-    # XXX but it should be easy enough to support, soon
-    j = start
-    i = 0
-    while i < count:
-        l1.ll_setitem_fast(j, l2.ll_getitem_fast(i))
-        i += 1
-        j += 1
+    # XXX ...but it would be easy enough to support if really needed
+    ll_arraycopy(l2, l1, 0, start, count)
 ll_listsetslice.oopspec = 'list.setslice(l1, start, stop, l2)'
 
 # ____________________________________________________________

Modified: pypy/branch/stringbuilder2/pypy/rpython/rptr.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/rpython/rptr.py	(original)
+++ pypy/branch/stringbuilder2/pypy/rpython/rptr.py	Mon Jan 25 15:25:48 2010
@@ -39,6 +39,14 @@
         attr = hop.args_s[1].const
         if isinstance(hop.s_result, annmodel.SomeLLADTMeth):
             return hop.inputarg(hop.r_result, arg=0)
+        try:
+            self.lowleveltype._example()._lookup_adtmeth(attr)
+        except AttributeError:
+            pass
+        else:
+            assert hop.s_result.is_constant()
+            return hop.inputconst(hop.r_result, hop.s_result.const)
+        assert attr in self.lowleveltype.TO._flds # check that the field exists
         FIELD_TYPE = getattr(self.lowleveltype.TO, attr)
         if isinstance(FIELD_TYPE, lltype.ContainerType):
             if (attr, FIELD_TYPE) == self.lowleveltype.TO._first_struct():

Modified: pypy/branch/stringbuilder2/pypy/rpython/rtyper.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/rpython/rtyper.py	(original)
+++ pypy/branch/stringbuilder2/pypy/rpython/rtyper.py	Mon Jan 25 15:25:48 2010
@@ -133,15 +133,33 @@
         return result
 
     def get_type_for_typeptr(self, typeptr):
+        search = typeptr._obj
         try:
-            return self.type_for_typeptr[typeptr._obj]
+            return self.type_for_typeptr[search]
         except KeyError:
-            # rehash the dictionary
+            # rehash the dictionary, and perform a linear scan
+            # for the case of ll2ctypes typeptr
+            found = None
             type_for_typeptr = {}
             for key, value in self.type_for_typeptr.items():
                 type_for_typeptr[key] = value
+                if key == search:
+                    found = value
             self.type_for_typeptr = type_for_typeptr
-            return self.type_for_typeptr[typeptr._obj]
+            if found is None:
+                raise KeyError(search)
+            return found
+
+    def set_type_for_typeptr(self, typeptr, TYPE):
+        self.type_for_typeptr[typeptr._obj] = TYPE
+
+    def get_real_typeptr_for_typeptr(self, typeptr):
+        # perform a linear scan for the case of ll2ctypes typeptr
+        search = typeptr._obj
+        for key, value in self.type_for_typeptr.items():
+            if key == search:
+                return key._as_ptr()
+        raise KeyError(search)
 
     def makekey(self, s_obj):
         return pair(self.type_system, s_obj).rtyper_makekey(self)

Modified: pypy/branch/stringbuilder2/pypy/rpython/rvirtualizable2.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/rpython/rvirtualizable2.py	(original)
+++ pypy/branch/stringbuilder2/pypy/rpython/rvirtualizable2.py	Mon Jan 25 15:25:48 2010
@@ -52,10 +52,10 @@
         #if not flags.get('access_directly'):
         if cname.value in self.my_redirected_fields:
             cflags = inputconst(lltype.Void, flags)
-            llops.genop('promote_virtualizable', [vinst, cname, cflags])
+            llops.genop('jit_force_virtualizable', [vinst, cname, cflags])
 
 
-def replace_promote_virtualizable_with_call(graphs, VTYPEPTR, funcptr):
+def replace_force_virtualizable_with_call(graphs, VTYPEPTR, funcptr):
     # funcptr should be an ll or oo function pointer with a VTYPEPTR argument
     c_funcptr = inputconst(lltype.typeOf(funcptr), funcptr)
     count = 0
@@ -65,7 +65,7 @@
                 continue
             newoplist = []
             for i, op in enumerate(block.operations):
-                if (op.opname == 'promote_virtualizable' and
+                if (op.opname == 'jit_force_virtualizable' and
                     match_virtualizable_type(op.args[0].concretetype,
                                              VTYPEPTR)):
                     if op.args[-1].value.get('access_directly', False):
@@ -75,7 +75,7 @@
                     count += 1
                 newoplist.append(op)
             block.operations = newoplist
-    log("replaced %d 'promote_virtualizable' with %r" % (count, funcptr))
+    log("replaced %d 'jit_force_virtualizable' with %r" % (count, funcptr))
 
 def match_virtualizable_type(TYPE, VTYPEPTR):
     if isinstance(TYPE, ootype.Instance):

Modified: pypy/branch/stringbuilder2/pypy/rpython/test/test_rlist.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/rpython/test/test_rlist.py	(original)
+++ pypy/branch/stringbuilder2/pypy/rpython/test/test_rlist.py	Mon Jan 25 15:25:48 2010
@@ -410,13 +410,18 @@
         assert res.item2 == 9
 
     def test_bltn_list(self):
-        def dummyfn():
-            l1 = [42]
-            l2 = list(l1)
-            l2[0] = 0
-            return l1[0]
-        res = self.interpret(dummyfn, ())
-        assert res == 42
+        # test for ll_copy()
+        for resize1 in [False, True]:
+            for resize2 in [False, True]:
+                def dummyfn():
+                    l1 = [42]
+                    if resize1: l1.append(43)
+                    l2 = list(l1)
+                    if resize2: l2.append(44)
+                    l2[0] = 0
+                    return l1[0]
+                res = self.interpret(dummyfn, ())
+                assert res == 42
 
     def test_is_true(self):
         def is_true(lst):

Modified: pypy/branch/stringbuilder2/pypy/rpython/test/test_rptr.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/rpython/test/test_rptr.py	(original)
+++ pypy/branch/stringbuilder2/pypy/rpython/test/test_rptr.py	Mon Jan 25 15:25:48 2010
@@ -337,3 +337,13 @@
         return f([1])
     s, t = ll_rtype(lltest, [])
     assert s.is_constant() == False
+
+def test_staticadtmeths():
+    ll_func = staticAdtMethod(lambda x: x + 42)
+    S = GcStruct('S', adtmeths={'ll_func': ll_func})
+    def f():
+        return malloc(S).ll_func(5)
+    s, t = ll_rtype(f, [])
+    graphf = t.graphs[0]
+    for op in graphf.startblock.operations:
+        assert op.opname != 'getfield'

Modified: pypy/branch/stringbuilder2/pypy/rpython/test/test_rvirtualizable2.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/rpython/test/test_rvirtualizable2.py	(original)
+++ pypy/branch/stringbuilder2/pypy/rpython/test/test_rvirtualizable2.py	Mon Jan 25 15:25:48 2010
@@ -1,7 +1,7 @@
 import py
 from pypy.rpython.lltypesystem import lltype
 from pypy.rpython.test.tool import BaseRtypingTest, LLRtypeMixin, OORtypeMixin
-from pypy.rpython.rvirtualizable2 import replace_promote_virtualizable_with_call
+from pypy.rpython.rvirtualizable2 import replace_force_virtualizable_with_call
 from pypy.rlib.jit import hint
 from pypy.objspace.flow.model import summary
 from pypy.rpython.llinterp import LLInterpreter
@@ -33,15 +33,15 @@
     def __init__(self, v0):
         self.v0 = v0
 
-def get_promote_virtualizable_flags(graph):
+def get_force_virtualizable_flags(graph):
     res = []
     for block, op in graph.iterblockops():
-        if op.opname == 'promote_virtualizable':
+        if op.opname == 'jit_force_virtualizable':
             res.append(op.args[-1].value)
     return res
 
 class BaseTest(BaseRtypingTest):
-    def test_generate_promote_virtualizable(self):
+    def test_generate_force_virtualizable(self):
         def fn(n):
             vinst = V(n)
             return vinst.v
@@ -51,11 +51,11 @@
         op_getfield = block.operations[-1]
         assert op_getfield.opname in ('getfield', 'oogetfield')
         v_inst = op_getfield.args[0]
-        assert op_promote.opname == 'promote_virtualizable'
+        assert op_promote.opname == 'jit_force_virtualizable'
         assert op_promote.args[0] is v_inst
         assert op_promote.args[-1].value == {}
 
-    def test_generate_promote_virtualizable_subclass(self):
+    def test_generate_force_virtualizable_subclass(self):
         def fn(n):
             V(n) # to attach v to V
             vinst = SubclassV(n)
@@ -66,11 +66,11 @@
         op_getfield = block.operations[-1]
         assert op_getfield.opname in ('getfield', 'oogetfield')
         v_inst = op_getfield.args[0]
-        assert op_promote.opname == 'promote_virtualizable'
+        assert op_promote.opname == 'jit_force_virtualizable'
         assert op_promote.args[0] is v_inst
         assert op_promote.args[-1].value == {}
 
-    def test_no_promote_virtualizable_for_other_fields(self):
+    def test_no_force_virtualizable_for_other_fields(self):
         def fn(n):
             vinst = V(n)
             return vinst.w
@@ -81,7 +81,7 @@
         assert op_getfield.opname in ('getfield', 'oogetfield')
         assert op_call.opname == 'direct_call'    # to V.__init__
 
-    def test_generate_promote_virtualizable_array(self):
+    def test_generate_force_virtualizable_array(self):
         def fn(n):
             vinst = VArray([n, n+1])
             return vinst.lst[1]
@@ -93,7 +93,7 @@
         assert op_getarrayitem.opname == 'direct_call'  # to ll_getitem_xxx
         assert op_getfield.opname in ('getfield', 'oogetfield')
         v_inst = op_getfield.args[0]
-        assert op_promote.opname == 'promote_virtualizable'
+        assert op_promote.opname == 'jit_force_virtualizable'
         assert op_promote.args[0] is v_inst
         assert op_promote.args[-1].value == {}        
 
@@ -130,13 +130,13 @@
         TYPE = self.gettype(w_inst)
         assert 'virtualizable2_accessor' not in TYPE._hints
 
-    def replace_promote_virtualizable(self, rtyper, graphs):
+    def replace_force_virtualizable(self, rtyper, graphs):
         from pypy.annotation import model as annmodel
         from pypy.rpython.annlowlevel import MixLevelHelperAnnotator
         graph = graphs[0]
 
         for block, op in graph.iterblockops():
-            if op.opname == 'promote_virtualizable':
+            if op.opname == 'jit_force_virtualizable':
                 v_inst_ll_type = op.args[0].concretetype
                 break
             
@@ -150,11 +150,10 @@
             s_vinst = annmodel.SomeOOInstance(v_inst_ll_type)
         funcptr = annhelper.delayedfunction(mycall, [s_vinst], annmodel.s_None)
         annhelper.finish()
-        replace_promote_virtualizable_with_call(graphs, v_inst_ll_type,
-                                                funcptr)
+        replace_force_virtualizable_with_call(graphs, v_inst_ll_type, funcptr)
         return funcptr
 
-    def test_replace_promote_virtualizable_with_call(self):
+    def test_replace_force_virtualizable_with_call(self):
         def fn(n):
             vinst = V(n)
             return vinst.v
@@ -162,7 +161,7 @@
         block = graph.startblock
         op_getfield = block.operations[-1]
         assert op_getfield.opname in ('getfield', 'oogetfield')
-        funcptr = self.replace_promote_virtualizable(rtyper, [graph])
+        funcptr = self.replace_force_virtualizable(rtyper, [graph])
         if getattr(conftest.option, 'view', False):
             graph.show()
         op_promote = block.operations[-2]
@@ -190,9 +189,9 @@
         g_graph = t._graphof(g)
 
         expected =  [{'access_directly': True}] * 3
-        assert get_promote_virtualizable_flags(g_graph) == expected
+        assert get_force_virtualizable_flags(g_graph) == expected
 
-        self.replace_promote_virtualizable(typer, [g_graph])
+        self.replace_force_virtualizable(typer, [g_graph])
         assert summary(g_graph) == {self.GETFIELD: 2, self.SETFIELD: 1, 'int_add': 1}
 
         res = self.interpret(f, [23])
@@ -213,7 +212,7 @@
         f_graph = t._graphof(f)
         g_graph = t._graphof(g)
 
-        self.replace_promote_virtualizable(typer, [f_graph, g_graph])
+        self.replace_force_virtualizable(typer, [f_graph, g_graph])
         t.checkgraphs()
 
         res = self.interpret(f, [23]) 
@@ -236,12 +235,12 @@
         g_graphs.sort()
         assert g_graphs[0][0] is None
 
-        assert get_promote_virtualizable_flags(g_graphs[0][1]) == [{}]
+        assert get_force_virtualizable_flags(g_graphs[0][1]) == [{}]
         expected =  [{'access_directly': True}]        
-        assert get_promote_virtualizable_flags(g_graphs[1][1]) == expected
+        assert get_force_virtualizable_flags(g_graphs[1][1]) == expected
 
-        self.replace_promote_virtualizable(typer, [g_graphs[0][1],
-                                                   g_graphs[1][1]])
+        self.replace_force_virtualizable(typer, [g_graphs[0][1],
+                                                 g_graphs[1][1]])
         
         assert summary(g_graphs[0][1]) == {'direct_call': 1, self.GETFIELD: 1}
         assert summary(g_graphs[1][1]) == {self.GETFIELD: 1}        
@@ -276,8 +275,9 @@
         assert summary(g_graphs[1][1]) == {self.SETFIELD: 1}
         
         h_graph = t._graphof(h)
-        assert summary(h_graph) == {'promote_virtualizable': 1, self.GETFIELD: 1}
-        assert get_promote_virtualizable_flags(h_graph) == [{}]
+        assert summary(h_graph) == {'jit_force_virtualizable': 1,
+                                    self.GETFIELD: 1}
+        assert get_force_virtualizable_flags(h_graph) == [{}]
 
         res = self.interpret(f, [23])
         assert res == 23
@@ -303,7 +303,7 @@
         t, typer, graph = self.gengraph(f, [int])
         g_graph = t._graphof(A.g.im_func)
 
-        self.replace_promote_virtualizable(typer, [g_graph])
+        self.replace_force_virtualizable(typer, [g_graph])
         
         assert summary(g_graph) == {self.GETFIELD: 1, 'int_mul': 1}
 

Modified: pypy/branch/stringbuilder2/pypy/rpython/tool/rffi_platform.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/rpython/tool/rffi_platform.py	(original)
+++ pypy/branch/stringbuilder2/pypy/rpython/tool/rffi_platform.py	Mon Jan 25 15:25:48 2010
@@ -644,7 +644,7 @@
         else:
             raise CompilationError("Library %s is not installed" % (name,))
 
-def check_boehm(platform=None):
+def configure_boehm(platform=None):
     if platform is None:
         from pypy.translator.platform import platform
     if sys.platform == 'win32':
@@ -658,13 +658,10 @@
         includes=includes,
         libraries=['gc'],
         )
-    try:
-        return configure_external_library(
-            'gc', eci,
-            [dict(prefix='gc-', include_dir='include', library_dir=library_dir)],
-            symbol='GC_init')
-    except CompilationError:
-        return None
+    return configure_external_library(
+        'gc', eci,
+        [dict(prefix='gc-', include_dir='include', library_dir=library_dir)],
+        symbol='GC_init')
 
 if __name__ == '__main__':
     doc = """Example:

Modified: pypy/branch/stringbuilder2/pypy/tool/gcc_cache.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/tool/gcc_cache.py	(original)
+++ pypy/branch/stringbuilder2/pypy/tool/gcc_cache.py	Mon Jan 25 15:25:48 2010
@@ -10,7 +10,7 @@
 def cache_file_path(c_files, eci, cachename):
     cache_dir = cache_dir_root.join(cachename).ensure(dir=1)
     filecontents = [c_file.read() for c_file in c_files]
-    key = repr((filecontents, eci))
+    key = repr((filecontents, eci, platform.key()))
     hash = md5(key).hexdigest()
     return cache_dir.join(hash)
 

Modified: pypy/branch/stringbuilder2/pypy/tool/udir.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/tool/udir.py	(original)
+++ pypy/branch/stringbuilder2/pypy/tool/udir.py	Mon Jan 25 15:25:48 2010
@@ -18,7 +18,7 @@
 #
 
 import autopath
-import os
+import os, sys
 import py
 
 from py.path import local 
@@ -30,6 +30,8 @@
     else:
         return basename.split('/')[-2]
 
+PYPY_KEEP = int(os.environ.get('PYPY_USESSION_KEEP', '3'))
+
 def make_udir(dir=None, basename=None):
     if dir is not None:
         dir = local(dir)
@@ -37,6 +39,8 @@
         try:
             p = py.path.local(__file__).dirpath()
             basename = svn_info(py.path.svnwc(p).info().url)
+            if isinstance(basename, unicode):
+                basename = basename.encode(sys.getdefaultencoding())
         except:
             basename = ''
     if not basename.startswith('-'):
@@ -45,7 +49,7 @@
         basename = basename + '-'
     return local.make_numbered_dir(rootdir = dir,
                                    prefix = 'usession' + basename,
-                                   keep = 3)
+                                   keep = PYPY_KEEP)
 
 udir = make_udir(dir      = os.environ.get('PYPY_USESSION_DIR'),
                  basename = os.environ.get('PYPY_USESSION_BASENAME'))

Modified: pypy/branch/stringbuilder2/pypy/translator/backendopt/test/test_writeanalyze.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/backendopt/test/test_writeanalyze.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/backendopt/test/test_writeanalyze.py	Mon Jan 25 15:25:48 2010
@@ -4,14 +4,15 @@
 from pypy.translator.translator import TranslationContext, graphof
 from pypy.translator.simplify import get_funcobj
 from pypy.translator.backendopt.writeanalyze import WriteAnalyzer, top_set
+from pypy.translator.backendopt.writeanalyze import ReadWriteAnalyzer
 from pypy.translator.backendopt.all import backend_optimizations
 from pypy.conftest import option
 
 
-class BaseTestCanRaise(object):
+class BaseTest(object):
 
     type_system = None
-
+    Analyzer = WriteAnalyzer
     
     def translate(self, func, sig):
         t = TranslationContext()
@@ -19,7 +20,10 @@
         t.buildrtyper(type_system=self.type_system).specialize()
         if option.view:
             t.view()
-        return t, WriteAnalyzer(t)
+        return t, self.Analyzer(t)
+
+
+class BaseTestWriteAnalyze(BaseTest):
 
     def test_writes_simple(self):
         def g(x):
@@ -146,7 +150,7 @@
         assert not result
 
 
-class TestLLtype(BaseTestCanRaise):
+class TestLLtype(BaseTestWriteAnalyze):
     type_system = 'lltype'
 
     def test_list(self):
@@ -205,7 +209,7 @@
         assert name.endswith("foobar")
 
 
-class TestOOtype(BaseTestCanRaise):
+class TestOOtype(BaseTestWriteAnalyze):
     type_system = 'ootype'
     
     def test_array(self):
@@ -240,3 +244,88 @@
 
         result = wa.analyze(ggraph.startblock.operations[0])
         assert result is top_set
+
+
+class TestLLtypeReadWriteAnalyze(BaseTest):
+    Analyzer = ReadWriteAnalyzer
+    type_system = 'lltype'
+
+    def test_read_simple(self):
+        def g(x):
+            return True
+
+        def f(x):
+            return g(x - 1)
+        t, wa = self.translate(f, [int])
+        fgraph = graphof(t, f)
+        result = wa.analyze(fgraph.startblock.operations[0])
+        assert not result
+
+    def test_read_really(self):
+        class A(object):
+            def __init__(self, y):
+                self.y = y
+            def f(self):
+                self.x = 1
+                return self.y
+        def h(flag):
+            obj = A(flag)
+            return obj.f()
+        
+        t, wa = self.translate(h, [int])
+        hgraph = graphof(t, h)
+        op_call_f = hgraph.startblock.operations[-1]
+
+        # check that we fished the expected ops
+        assert op_call_f.opname == "direct_call"
+        assert get_funcobj(op_call_f.args[0].value)._name == 'A.f'
+
+        result = wa.analyze(op_call_f)
+        assert len(result) == 2
+        result = list(result)
+        result.sort()
+        [(struct1, T1, name1), (struct2, T2, name2)] = result
+        assert struct1 == "readstruct"
+        assert name1.endswith("y")
+        assert struct2 == "struct"
+        assert name2.endswith("x")
+        assert T1 == T2
+
+    def test_contains(self):
+        def g(x, y, z):
+            l = [x]
+            return f(l, y, z)
+        def f(x, y, z):
+            return y in x
+
+        t, wa = self.translate(g, [int, int, int])
+        ggraph = graphof(t, g)
+        assert ggraph.startblock.operations[-1].opname == 'direct_call'
+
+        result = wa.analyze(ggraph.startblock.operations[-1])
+        ARRAYPTR = list(result)[0][1]
+        assert list(result) == [("readarray", ARRAYPTR)]
+        assert isinstance(ARRAYPTR.TO, lltype.GcArray)
+
+    def test_adt_method(self):
+        def ll_callme(n):
+            return n
+        ll_callme = lltype.staticAdtMethod(ll_callme)
+        S = lltype.GcStruct('S', ('x', lltype.Signed),
+                            adtmeths = {'yep': True,
+                                        'callme': ll_callme})
+        def g(x, y, z):
+            p = lltype.malloc(S)
+            p.x = x
+            if p.yep:
+                z *= p.callme(y)
+            return z
+        def f(x, y, z):
+            return g(x, y, z)
+
+        t, wa = self.translate(f, [int, int, int])
+        fgraph = graphof(t, f)
+        assert fgraph.startblock.operations[-1].opname == 'direct_call'
+
+        result = wa.analyze(fgraph.startblock.operations[-1])
+        assert list(result) == [("struct", lltype.Ptr(S), "x")]

Modified: pypy/branch/stringbuilder2/pypy/translator/backendopt/writeanalyze.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/backendopt/writeanalyze.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/backendopt/writeanalyze.py	Mon Jan 25 15:25:48 2010
@@ -45,3 +45,15 @@
             elif methname in ('ll_getitem_fast', 'll_length'):
                 return self.bottom_result()
         return graphanalyze.GraphAnalyzer.analyze_external_method(self, op, TYPE, meth)
+
+
+class ReadWriteAnalyzer(WriteAnalyzer):
+
+    def analyze_simple_operation(self, op):
+        if op.opname == "getfield":
+            return frozenset([
+                ("readstruct", op.args[0].concretetype, op.args[1].value)])
+        elif op.opname == "getarrayitem":
+            return frozenset([
+                ("readarray", op.args[0].concretetype)])
+        return WriteAnalyzer.analyze_simple_operation(self, op)

Modified: pypy/branch/stringbuilder2/pypy/translator/benchmark/bench-custom.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/benchmark/bench-custom.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/benchmark/bench-custom.py	Mon Jan 25 15:25:48 2010
@@ -34,7 +34,7 @@
                 benchmarks.append(b)
 
     exes = get_executables(args)
-    pythons = 'python2.5 python2.4 python2.3'.split()
+    pythons = 'python2.6 python2.5 python2.4'.split()
     full_pythons = []
     for python in pythons:
         full_python = py.path.local.sysfind(python)
@@ -44,6 +44,7 @@
     sys.stdout.flush()
 
     refs = {}
+    final_error_count = 0
 
     if not options.nocpython:
         exes = full_pythons + exes
@@ -52,7 +53,10 @@
         if i is not None:
             for exe in exes:
                 for b in benchmarks:
-                    benchmark_result.result(exe, allowcreate=True).run_benchmark(b, verbose=options.verbose)
+                    br = benchmark_result.result(exe, allowcreate=True)
+                    result = br.run_benchmark(b, verbose=options.verbose)
+                    if not result:
+                        final_error_count += 1
 
         if options.relto:
             relto = options.relto
@@ -83,6 +87,10 @@
                 print row
             print
 
+    if final_error_count:
+        raise SystemExit("%d benchmark run(s) failed (see -FAILED- above)"
+                         % final_error_count)
+
 if __name__ == '__main__':
     from optparse import OptionParser
     parser = OptionParser()

Modified: pypy/branch/stringbuilder2/pypy/translator/benchmark/benchmarks.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/benchmark/benchmarks.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/benchmark/benchmarks.py	Mon Jan 25 15:25:48 2010
@@ -1,15 +1,16 @@
 import os, sys, time, pickle, re, py
+import yaml
 
 class BenchmarkFailed(Exception):
     pass
 
 PYSTONE_CMD = 'from test import pystone;pystone.main(%s)'
 PYSTONE_PATTERN = 'This machine benchmarks at'
-PYSTONE_ASCENDING_GOOD = True
 
 RICHARDS_CMD = 'from richards import *;main(iterations=%d)'
 RICHARDS_PATTERN = 'Average time per iteration:'
-RICHARDS_ASCENDING_GOOD = False
+
+TIME_FMT = 'max mem used: %Mk\nelapsed time: %e\nsystem time:  %S\nuser time:    %U\nCPU use:      %P'
 
 def get_result(txt, pattern):
     for line in txt.split('\n'):
@@ -36,60 +37,40 @@
         return Benchmark(self._basename, self._run, self.asc_good, self.units,
                          self.check, self.sizefactor * n)
     def run(self, exe):
-        global latest_output
-        latest_output = ''
         try:
-            result = self._run(exe, self.sizefactor)
+            result, latest_output = self._run(exe, self.sizefactor)
+            self.latest_output = latest_output
         except BenchmarkFailed, e:
             result = '-FAILED-'
-        self.latest_output = latest_output
         return result
 
-def external_dependency(dirname, svnurl, revision):
-    """Check out (if necessary) a given fixed revision of a svn url."""
-    dirpath = py.path.local(__file__).dirpath().join(dirname)
-    revtag = dirpath.join('-svn-rev-')
-    if dirpath.check():
-        if not revtag.check() or int(revtag.read()) != revision:
-            print >> sys.stderr, ("Out-of-date benchmark checkout!"
-                                  " I won't update it automatically.")
-            print >> sys.stderr, ("To continue, move away or remove the "
-                                  "%r directory." % (dirname,))
-            sys.exit(1)
-        return True
-    CMD = "svn co -r%d %s@%d %s" % (revision, svnurl, revision, dirpath)
-    print >> sys.stderr, CMD
-    err = os.system(CMD)
-    if err != 0:
-        print >> sys.stderr, "* checkout failed, skipping this benchmark"
-        return False
-    revtag.write(str(revision))
+def external_dependency(dirname, svnurl, revision=None):
+    directory = py.path.local(__file__).dirpath().join(dirname)
+    wc = py.path.svnwc(directory)
+    wc.checkout(svnurl, rev=revision)
     return True
 
 def run_cmd(cmd):
-    global latest_output
-    #print "running", cmd
     pipe = os.popen(cmd + ' 2>&1')
     r = pipe.read()
     status = pipe.close()
-    latest_output = r
     if status:
         raise BenchmarkFailed(status)
     return r
 
-def run_pystone(executable='/usr/local/bin/python', sizefactor=1):
+def run_pystone(executable, sizefactor=1):
     from pypy.tool import autopath
     distdir = py.path.local(autopath.pypydir).dirpath()
     pystone = py.path.local(autopath.libpythondir).join('test', 'pystone.py')
     txt = run_cmd('"%s" "%s" %d' % (executable, pystone, 50000 * sizefactor))
-    return get_result(txt, PYSTONE_PATTERN)
+    return get_result(txt, PYSTONE_PATTERN), txt
 
-def run_richards(executable='/usr/local/bin/python', sizefactor=1):
+def run_richards(executable, sizefactor=1):
     richards = py.path.local(__file__).dirpath().dirpath().join('goal').join('richards.py')
     txt = run_cmd('"%s" %s %d' % (executable, richards, 5 * sizefactor))
-    return get_result(txt, RICHARDS_PATTERN)
+    return get_result(txt, RICHARDS_PATTERN), txt
 
-def run_translate(executable='/usr/local/bin/python'):
+def run_translate(executable):
     translate = py.path.local(__file__).dirpath().dirpath().join('goal').join('translate.py')
     target = py.path.local(__file__).dirpath().dirpath().join('goal').join('targetrpystonedalone.py')
     argstr = '%s %s --batch --backendopt --no-compile %s > /dev/null 2> /dev/null'
@@ -100,39 +81,7 @@
         raise BenchmarkFailed(status)
     return r
 
-def run_docutils(executable='/usr/local/bin/python'):
-    docutilssvnpath = 'docutils'    # subdir of the local dir
-    translatetxt = py.path.local(__file__).dirpath().dirpath().dirpath().join('doc').join('translation.txt')
-    command = """import sys
-sys.modules['unicodedata'] = sys # docutils need 'import unicodedata' to work, but no more...
-sys.path[0:0] = ['%s', '%s/extras']
-from docutils.core import publish_cmdline
-publish_cmdline(writer_name='html')
-"""%(docutilssvnpath, docutilssvnpath)
-    T = time.time()
-    pid = os.fork()
-    if not pid:
-        davenull = os.open('/dev/null', os.O_RDWR)
-        os.dup2(davenull, 0)
-        os.dup2(davenull, 1)
-        os.dup2(davenull, 2)
-        status = os.spawnv(os.P_WAIT, executable, [executable, '-c', command, str(translatetxt)])
-        os._exit(status)
-    else:
-        status = os.waitpid(pid, 0)[1]
-    r = time.time() - T
-    if status:
-        raise BenchmarkFailed(status)
-    return r
-
-def check_docutils():
-    return False     # useless benchmark - I've seen 15% of difference
-                     # between two successive runs on the same machine!
-    #return external_dependency('docutils',
-    #                           'svn://svn.berlios.de/docutils/trunk/docutils',
-    #                           4821)
-
-def run_templess(executable='/usr/local/bin/python', sizefactor=1):
+def run_templess(executable, sizefactor=1):
     """ run some script in the templess package
 
         templess is some simple templating language, to check out use
@@ -149,7 +98,7 @@
     for line in txt.split('\n'):
         if '.' in line:
             try:
-                return float(line) / sizefactor
+                return float(line) / sizefactor, txt
             except ValueError:
                 pass
     else:
@@ -160,7 +109,7 @@
                                'http://johnnydebris.net/templess/trunk',
                                100)
 
-def run_gadfly(executable='/usr/local/bin/python', sizefactor=1):
+def run_gadfly(executable, sizefactor=1):
     """ run some tests in the gadfly pure Python database """
     here = py.path.local(__file__).dirpath()
     gadfly = here.join('gadfly')
@@ -168,14 +117,14 @@
     command = 'PYTHONPATH="%s" "%s" "%s" %d' % (gadfly, executable, testscript,
                                                 sizefactor)
     txt = run_cmd(command)
-    return get_result(txt, 'Total running time:') / sizefactor
+    return get_result(txt, 'Total running time:') / sizefactor, txt
 
 def check_gadfly():
     return external_dependency('gadfly',
               'http://codespeak.net/svn/user/arigo/hack/pypy-hack/gadflyZip',
               70117)
 
-def run_mako(executable='/usr/local/bin/python', sizefactor=1):
+def run_mako(executable, sizefactor=1):
     """ run some tests in the mako templating system """
     here = py.path.local(__file__).dirpath()
     mako = here.join('mako')
@@ -184,29 +133,64 @@
                                                        executable, testscript,
                                                        2000 * sizefactor)
     txt = run_cmd(command)
-    return get_result(txt, 'Mako:')
+    return get_result(txt, 'Mako:'), txt
 
 def check_mako():
     return external_dependency('mako',
               'http://codespeak.net/svn/user/arigo/hack/pypy-hack/mako',
-              70118)
+              70118)    
 
 def check_translate():
     return False   # XXX what should we do about the dependency on ctypes?
 
-BENCHMARKS = [Benchmark('richards', run_richards, RICHARDS_ASCENDING_GOOD, 'ms'),
-              Benchmark('pystone', run_pystone, PYSTONE_ASCENDING_GOOD, ''),
-              Benchmark('translate', run_translate, RICHARDS_ASCENDING_GOOD, 'ms', check_translate),
-              Benchmark('docutils', run_docutils, RICHARDS_ASCENDING_GOOD,
-                        's', check_docutils),
-              Benchmark('templess', run_templess, RICHARDS_ASCENDING_GOOD,
+class LanguageShootoutBenchmark(Benchmark):
+    def __init__(self, name, sizefactor=1, test=False):
+        self.test = test
+        self.basename = name
+        Benchmark.__init__(self, name, self.runner, False, 'ms',
+                           self.check, sizefactor)
+
+    def __mul__(self, i):
+        return LanguageShootoutBenchmark(self.name, self.sizefactor * i,
+                                         self.test)
+
+    def runner(self, executable, sizefactor=1):
+        shootout = py.path.local(__file__).dirpath().join(
+            'shootout_benchmarks')
+        argsfile = shootout.join('tests.yml')
+        if self.test:
+            kind = 'test'
+        else:
+            kind = 'run'
+        args = yaml.load(argsfile.read())[self.basename][kind]['args']
+        progname = str(shootout.join(self.basename)) + '.py'
+        cmd = 'time -f "%s" %s %s %s %d' % (TIME_FMT, executable, progname,
+                                         " ".join(args), sizefactor)
+        txt = run_cmd(cmd)
+        return get_result(txt, 'elapsed time:'), txt
+
+    def check(self):
+        return external_dependency('shootout_benchmarks',
+              'http://codespeak.net/svn/pypy/benchmarks/shootout')
+
+BENCHMARKS = [Benchmark('richards', run_richards, False, 'ms'),
+              Benchmark('pystone', run_pystone, True, ''),
+              Benchmark('translate', run_translate, False, 'ms',
+                        check_translate),
+              Benchmark('templess', run_templess, False,
                         's', check_templess),
-              Benchmark('gadfly2', run_gadfly, RICHARDS_ASCENDING_GOOD,
+              Benchmark('gadfly2', run_gadfly, False,
                         's', check_gadfly),
-              Benchmark('mako', run_mako, RICHARDS_ASCENDING_GOOD,
+              Benchmark('mako', run_mako, False,
                         's', check_mako),
              ]
 
+SHOOTOUT_NAMES = ['binary-trees', 'fannkuch', 'fasta', 'float',
+                  'meteor-contest', 'nbody', 'spectral-norm']
+
+#for name in SHOOTOUT_NAMES:
+#    BENCHMARKS.append(LanguageShootoutBenchmark(name))
+
 BENCHMARKS_BY_NAME = {}
 for _b in BENCHMARKS:
     BENCHMARKS_BY_NAME[_b.name] = _b

Modified: pypy/branch/stringbuilder2/pypy/translator/benchmark/jitbench.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/benchmark/jitbench.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/benchmark/jitbench.py	Mon Jan 25 15:25:48 2010
@@ -4,7 +4,7 @@
 parser = OptionParser()
 parser.add_option(
     '--size-factor-list', dest='sizefactorlist',
-    default='1,2,5,1,2,5,1,2,5',
+    default='1,2,5,20,1,2,5,20,1,2,5,20',
     )
 options, args = parser.parse_args(sys.argv[1:])
 args = args or [sys.executable]
@@ -13,9 +13,18 @@
 
 os.chdir(os.path.dirname(sys.argv[0]) or '.')
 
+errors = []
+
 for sizefactor in sizefactors:
     for executable in executables:
         sys.argv[1:] = [executable, '--pickle=jitbench.benchmark_result',
                         '-v', '--no-cpython',
                         '--size-factor=%d' % sizefactor]
-        execfile('bench-custom.py')
+        try:
+            execfile('bench-custom.py')
+        except SystemExit, e:
+            errors.append('%s:*%s: %s' % (executable, sizefactor, e))
+
+if errors:
+    print '\n'.join(errors)
+    sys.exit(1)

Modified: pypy/branch/stringbuilder2/pypy/translator/benchmark/result.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/benchmark/result.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/benchmark/result.py	Mon Jan 25 15:25:48 2010
@@ -88,7 +88,7 @@
     def run_benchmark(self, benchmark, verbose=False):
         self.asc_goods[benchmark.name] = benchmark.asc_good
         if self.run_counts.get(benchmark.name, 0) > self.max_results:
-            return
+            return -1
         print 'running', benchmark.name, 'for', self.exe_name,
         if verbose and self.pypy_rev > 0:
             print '[rev %d]' % self.pypy_rev,
@@ -97,12 +97,15 @@
         print new_result
         if verbose:
             print '{'
-            for line in benchmark.latest_output.splitlines(False):
+            lines = benchmark.latest_output.splitlines(False)
+            for line in lines[:80]:
                 print '\t' + line
+            if len(lines) > 80:
+                print '\t....'
             print '}'
         self.run_counts[benchmark.name] = self.run_counts.get(benchmark.name, 0) + 1
         if new_result == '-FAILED-':
-            return
+            return 0
         self.benchmarks.setdefault(benchmark.name, []).append(new_result)
         if benchmark.name in self.best_benchmarks:
             old_result = self.best_benchmarks[benchmark.name]
@@ -111,6 +114,7 @@
             else:
                 new_result = min(new_result, old_result)
         self.best_benchmarks[benchmark.name] = new_result
+        return 1
 
     def getstat(self, *args):
         # oh for supplied-p!

Modified: pypy/branch/stringbuilder2/pypy/translator/c/funcgen.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/c/funcgen.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/c/funcgen.py	Mon Jan 25 15:25:48 2010
@@ -793,8 +793,12 @@
     def OP_JIT_MARKER(self, op):
         return '/* JIT_MARKER %s */' % op
 
-    def OP_PROMOTE_VIRTUALIZABLE(self, op):
-        return '/* PROMOTE_VIRTUALIZABLE %s */' % op
+    def OP_JIT_FORCE_VIRTUALIZABLE(self, op):
+        return '/* JIT_FORCE_VIRTUALIZABLE %s */' % op
+
+    def OP_JIT_FORCE_VIRTUAL(self, op):
+        return '%s = %s; /* JIT_FORCE_VIRTUAL */' % (self.expr(op.result),
+                                                     self.expr(op.args[0]))
 
     def OP_GET_GROUP_MEMBER(self, op):
         typename = self.db.gettype(op.result.concretetype)
@@ -813,5 +817,26 @@
             self.expr(op.args[1]),
             self.expr(op.args[2]))
 
+    def getdebugfunctionname(self):
+        name = self.functionname
+        if not name:
+            return "?"
+        if name.startswith('pypy_g_'):
+            name = name[7:]
+        return name
+
+    def OP_DEBUG_RECORD_TRACEBACK(self, op):
+        #if self.functionname is None, we print "?" as the argument */
+        return 'PYPY_DEBUG_RECORD_TRACEBACK("%s");' % (
+            self.getdebugfunctionname(),)
+
+    def OP_DEBUG_CATCH_EXCEPTION(self, op):
+        gottype = self.expr(op.args[0])
+        exprs = []
+        for c_limited_type in op.args[1:]:
+            exprs.append('%s == %s' % (gottype, self.expr(c_limited_type)))
+        return 'PYPY_DEBUG_CATCH_EXCEPTION("%s", %s, %s);' % (
+            self.getdebugfunctionname(), gottype, ' || '.join(exprs))
+
 
 assert not USESLOTS or '__dict__' not in dir(FunctionCodeGenerator)

Modified: pypy/branch/stringbuilder2/pypy/translator/c/gc.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/c/gc.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/c/gc.py	Mon Jan 25 15:25:48 2010
@@ -215,8 +215,8 @@
     def compilation_info(self):
         eci = BasicGcPolicy.compilation_info(self)
 
-        from pypy.rpython.tool.rffi_platform import check_boehm
-        eci = eci.merge(check_boehm())
+        from pypy.rpython.tool.rffi_platform import configure_boehm
+        eci = eci.merge(configure_boehm())
 
         pre_include_bits = []
         if sys.platform == "linux2":
@@ -253,6 +253,9 @@
         nbytes = funcgen.expr(op.args[0])
         return 'GC_set_max_heap_size(%s);' % (nbytes,)
 
+    def GC_KEEPALIVE(self, funcgen, v):
+        return 'pypy_asm_keepalive(%s);' % funcgen.expr(v)
+
 class BoehmGcRuntimeTypeInfo_OpaqueNode(ContainerNode):
     nodekind = 'boehm rtti'
     globalcontainer = True

Modified: pypy/branch/stringbuilder2/pypy/translator/c/genc.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/c/genc.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/c/genc.py	Mon Jan 25 15:25:48 2010
@@ -430,12 +430,16 @@
         bk = self.translator.annotator.bookkeeper
         return getfunctionptr(bk.getdesc(self.entrypoint).getuniquegraph())
 
-    def cmdexec(self, args='', env=None, err=False):
+    def cmdexec(self, args='', env=None, err=False, expect_crash=False):
         assert self._compiled
         res = self.translator.platform.execute(self.executable_name, args,
                                                env=env)
         if res.returncode != 0:
+            if expect_crash:
+                return res.out, res.err
             raise Exception("Returned %d" % (res.returncode,))
+        if expect_crash:
+            raise Exception("Program did not crash!")
         if err:
             return res.out, res.err
         return res.out
@@ -711,6 +715,7 @@
             print >> fc, '/***  Implementations                                    ***/'
             print >> fc
             print >> fc, '#define PYPY_NOT_MAIN_FILE'
+            print >> fc, '#define PYPY_FILE_NAME "%s"' % name
             print >> fc, '#include "common_header.h"'
             print >> fc, '#include "structdef.h"'
             print >> fc, '#include "forwarddecl.h"'
@@ -783,6 +788,7 @@
     print >> f, '/***********************************************************/'
     print >> f, '/***  Implementations                                    ***/'
     print >> f
+    print >> f, '#define PYPY_FILE_NAME "%s"' % os.path.basename(f.name)
     for line in preimplementationlines:
         print >> f, line
     print >> f, '#include "src/g_include.h"'

Modified: pypy/branch/stringbuilder2/pypy/translator/c/src/g_include.h
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/c/src/g_include.h	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/c/src/g_include.h	Mon Jan 25 15:25:48 2010
@@ -17,11 +17,7 @@
 #include "src/mem.h"
 #include "src/exception.h"
 #include "src/support.h"
-#ifndef AVR
-#include "src/trace.h"
-#else
-    #define PY_LONG_LONG long long
-#endif
+#define PY_LONG_LONG long long
 
 #ifndef PYPY_STANDALONE
 #  include "src/pyobj.h"
@@ -51,7 +47,8 @@
 /*** modules ***/
 #ifdef HAVE_RTYPER      /* only if we have an RTyper */
 #  include "src/rtyper.h"
-#  include "src/debug.h"
+#  include "src/debug_print.h"
+#  include "src/debug_traceback.h"
 #ifndef AVR
 #  include "src/ll_os.h"
 #  include "src/ll_strtod.h"

Modified: pypy/branch/stringbuilder2/pypy/translator/c/src/main.h
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/c/src/main.h	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/c/src/main.h	Mon Jan 25 15:25:48 2010
@@ -36,12 +36,8 @@
 
     exitcode = STANDALONE_ENTRY_POINT(list);
     if (RPyExceptionOccurred()) {
-        /* fish for the exception type, at least */
-#ifndef AVR
-        fprintf(stderr, "Fatal RPython error: %s\n",
-                RPyFetchExceptionType()->ov_name->items);
-#endif
-        abort();
+        /* print the RPython traceback */
+        pypy_debug_catch_fatal_exception();
     }
     return exitcode;
 

Modified: pypy/branch/stringbuilder2/pypy/translator/c/test/test_boehm.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/c/test/test_boehm.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/c/test/test_boehm.py	Mon Jan 25 15:25:48 2010
@@ -3,12 +3,16 @@
 from pypy.rpython.lltypesystem import lltype, llmemory
 from pypy.rpython.lltypesystem.lloperation import llop
 from pypy.rpython.memory.test import snippet
-from pypy.rpython.tool.rffi_platform import check_boehm
 from pypy.translator.c.genc import CExtModuleBuilder
+from pypy.rlib.objectmodel import keepalive_until_here
 from pypy import conftest
 
 def setup_module(mod):
-    if not check_boehm():
+    from pypy.rpython.tool.rffi_platform import configure_boehm
+    from pypy.translator.platform import CompilationError
+    try:
+        configure_boehm()
+    except CompilationError:
         py.test.skip("Boehm GC not present")
 
 class AbstractGCTestClass(object):
@@ -287,6 +291,7 @@
                     assert a.index == i & ~1
                 else:
                     count_free += 1
+            keepalive_until_here(keepalive)
             return count_free
         c_fn = self.getcompiled(fn, [int])
         res = c_fn(7000)

Modified: pypy/branch/stringbuilder2/pypy/translator/c/test/test_genc.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/c/test/test_genc.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/c/test/test_genc.py	Mon Jan 25 15:25:48 2010
@@ -334,6 +334,7 @@
     f = compile(prob_with_pyobj, [object])
     from sys import getrefcount as g
     obj = None
+    import gc; gc.collect()
     before = g(obj)
     f(obj)
     after = g(obj)

Modified: pypy/branch/stringbuilder2/pypy/translator/c/test/test_stackless.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/c/test/test_stackless.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/c/test/test_stackless.py	Mon Jan 25 15:25:48 2010
@@ -20,8 +20,11 @@
             import py
             py.test.skip("stackless + refcounting doesn't work any more for now")
         elif cls.gcpolicy == "boehm":
-            from pypy.rpython.tool.rffi_platform import check_boehm
-            if not check_boehm():
+            from pypy.rpython.tool.rffi_platform import configure_boehm
+            from pypy.translator.platform import CompilationError
+            try:
+                configure_boehm()
+            except CompilationError:
                 py.test.skip("Boehm GC not present")
 
     def wrap_stackless_function(self, fn):

Modified: pypy/branch/stringbuilder2/pypy/translator/c/test/test_standalone.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/c/test/test_standalone.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/c/test/test_standalone.py	Mon Jan 25 15:25:48 2010
@@ -10,6 +10,7 @@
 from pypy.annotation.listdef import s_list_of_strings
 from pypy.tool.udir import udir
 from pypy.tool.autopath import pypydir
+from pypy.conftest import option
 
 
 class StandaloneTests(object):
@@ -21,8 +22,10 @@
         t.buildrtyper().specialize()
 
         cbuilder = CStandaloneBuilder(t, entry_point, t.config)
-        cbuilder.generate_source()
+        cbuilder.generate_source(defines=cbuilder.DEBUG_DEFINES)
         cbuilder.compile()
+        if option.view:
+            t.view()
         return t, cbuilder
 
 
@@ -377,6 +380,186 @@
         assert not err
         assert path.check(file=0)
 
+    def test_fatal_error(self):
+        def g(x):
+            if x == 1:
+                raise ValueError
+            else:
+                raise KeyError
+        def entry_point(argv):
+            if len(argv) < 3:
+                g(len(argv))
+            return 0
+        t, cbuilder = self.compile(entry_point)
+        #
+        out, err = cbuilder.cmdexec("", expect_crash=True)
+        assert out.strip() == ''
+        lines = err.strip().splitlines()
+        assert lines[-1] == 'Fatal RPython error: ValueError'
+        assert len(lines) >= 4
+        l0, l1, l2 = lines[-4:-1]
+        assert l0 == 'RPython traceback:'
+        assert re.match(r'  File "\w+.c", line \d+, in entry_point', l1)
+        assert re.match(r'  File "\w+.c", line \d+, in g', l2)
+        #
+        out2, err2 = cbuilder.cmdexec("x", expect_crash=True)
+        assert out2.strip() == ''
+        lines2 = err2.strip().splitlines()
+        assert lines2[-1] == 'Fatal RPython error: KeyError'
+        l0, l1, l2 = lines2[-4:-1]
+        assert l0 == 'RPython traceback:'
+        assert re.match(r'  File "\w+.c", line \d+, in entry_point', l1)
+        assert re.match(r'  File "\w+.c", line \d+, in g', l2)
+        assert lines2[-2] != lines[-2]    # different line number
+        assert lines2[-3] == lines[-3]    # same line number
+
+    def test_fatal_error_finally_1(self):
+        # a simple case of try:finally:
+        def g(x):
+            if x == 1:
+                raise KeyError
+        def h(x):
+            try:
+                g(x)
+            finally:
+                os.write(1, 'done.\n')
+        def entry_point(argv):
+            if len(argv) < 3:
+                h(len(argv))
+            return 0
+        t, cbuilder = self.compile(entry_point)
+        #
+        out, err = cbuilder.cmdexec("", expect_crash=True)
+        assert out.strip() == 'done.'
+        lines = err.strip().splitlines()
+        assert lines[-1] == 'Fatal RPython error: KeyError'
+        assert len(lines) >= 5
+        l0, l1, l2, l3 = lines[-5:-1]
+        assert l0 == 'RPython traceback:'
+        assert re.match(r'  File "\w+.c", line \d+, in entry_point', l1)
+        assert re.match(r'  File "\w+.c", line \d+, in h', l2)
+        assert re.match(r'  File "\w+.c", line \d+, in g', l3)
+
+    def test_fatal_error_finally_2(self):
+        # a try:finally: in which we raise and catch another exception
+        def raiseme(x):
+            if x == 1:
+                raise ValueError
+        def raise_and_catch(x):
+            try:
+                raiseme(x)
+            except ValueError:
+                pass
+        def g(x):
+            if x == 1:
+                raise KeyError
+        def h(x):
+            try:
+                g(x)
+            finally:
+                raise_and_catch(x)
+                os.write(1, 'done.\n')
+        def entry_point(argv):
+            if len(argv) < 3:
+                h(len(argv))
+            return 0
+        t, cbuilder = self.compile(entry_point)
+        #
+        out, err = cbuilder.cmdexec("", expect_crash=True)
+        assert out.strip() == 'done.'
+        lines = err.strip().splitlines()
+        assert lines[-1] == 'Fatal RPython error: KeyError'
+        assert len(lines) >= 5
+        l0, l1, l2, l3 = lines[-5:-1]
+        assert l0 == 'RPython traceback:'
+        assert re.match(r'  File "\w+.c", line \d+, in entry_point', l1)
+        assert re.match(r'  File "\w+.c", line \d+, in h', l2)
+        assert re.match(r'  File "\w+.c", line \d+, in g', l3)
+
+    def test_fatal_error_finally_3(self):
+        py.test.skip("not implemented: "
+                     "a try:finally: in which we raise the *same* exception")
+
+    def test_fatal_error_finally_4(self):
+        # a try:finally: in which we raise (and don't catch) an exception
+        def raiseme(x):
+            if x == 1:
+                raise ValueError
+        def g(x):
+            if x == 1:
+                raise KeyError
+        def h(x):
+            try:
+                g(x)
+            finally:
+                raiseme(x)
+                os.write(1, 'done.\n')
+        def entry_point(argv):
+            if len(argv) < 3:
+                h(len(argv))
+            return 0
+        t, cbuilder = self.compile(entry_point)
+        #
+        out, err = cbuilder.cmdexec("", expect_crash=True)
+        assert out.strip() == ''
+        lines = err.strip().splitlines()
+        assert lines[-1] == 'Fatal RPython error: ValueError'
+        assert len(lines) >= 5
+        l0, l1, l2, l3 = lines[-5:-1]
+        assert l0 == 'RPython traceback:'
+        assert re.match(r'  File "\w+.c", line \d+, in entry_point', l1)
+        assert re.match(r'  File "\w+.c", line \d+, in h', l2)
+        assert re.match(r'  File "\w+.c", line \d+, in raiseme', l3)
+
+    def test_assertion_error(self):
+        def g(x):
+            assert x != 1
+        def f(argv):
+            try:
+                g(len(argv))
+            finally:
+                print 'done'
+        def entry_point(argv):
+            f(argv)
+            return 0
+        t, cbuilder = self.compile(entry_point)
+        out, err = cbuilder.cmdexec("", expect_crash=True)
+        assert out.strip() == ''
+        lines = err.strip().splitlines()
+        assert lines[-1] == 'Fatal RPython error: AssertionError'
+        assert len(lines) >= 4
+        l0, l1, l2 = lines[-4:-1]
+        assert l0 == 'RPython traceback:'
+        assert re.match(r'  File "\w+.c", line \d+, in f', l1)
+        assert re.match(r'  File "\w+.c", line \d+, in g', l2)
+        # The traceback stops at f() because it's the first function that
+        # captures the AssertionError, which makes the program abort.
+
+    def test_ll_assert_error(self):
+        py.test.skip("implement later, maybe: tracebacks even with ll_assert")
+        def g(x):
+            ll_assert(x != 1, "foobar")
+        def f(argv):
+            try:
+                g(len(argv))
+            finally:
+                print 'done'
+        def entry_point(argv):
+            f(argv)
+            return 0
+        t, cbuilder = self.compile(entry_point)
+        out, err = cbuilder.cmdexec("", expect_crash=True)
+        assert out.strip() == ''
+        lines = err.strip().splitlines()
+        assert lines[-1] == 'PyPy assertion failed: foobar'
+        assert len(lines) >= 4
+        l0, l1, l2 = lines[-4:-1]
+        assert l0 == 'RPython traceback:'
+        assert re.match(r'  File "\w+.c", line \d+, in f', l1)
+        assert re.match(r'  File "\w+.c", line \d+, in g', l2)
+        # The traceback stops at f() because it's the first function that
+        # captures the AssertionError, which makes the program abort.
+
 
 class TestMaemo(TestStandalone):
     def setup_class(cls):
@@ -407,7 +590,7 @@
         t.buildrtyper().specialize()
         #
         cbuilder = CStandaloneBuilder(t, entry_point, t.config)
-        cbuilder.generate_source()
+        cbuilder.generate_source(defines=cbuilder.DEBUG_DEFINES)
         cbuilder.compile()
         #
         return t, cbuilder

Modified: pypy/branch/stringbuilder2/pypy/translator/cli/opcodes.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/cli/opcodes.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/cli/opcodes.py	Mon Jan 25 15:25:48 2010
@@ -77,6 +77,11 @@
     'gc_set_max_heap_size':     Ignore,
     'resume_point':             Ignore,
     'debug_assert':             Ignore,
+    'debug_start_traceback':    Ignore,
+    'debug_record_traceback':   Ignore,
+    'debug_catch_exception':    Ignore,
+    'debug_reraise_traceback':  Ignore,
+    'debug_print_traceback':    Ignore,
     'debug_print':              [DebugPrint],
     'debug_start':              [PushAllArgs, 'call void [pypylib]pypy.runtime.DebugPrint::DEBUG_START(string)'],
     'debug_stop':               [PushAllArgs, 'call void [pypylib]pypy.runtime.DebugPrint::DEBUG_STOP(string)'],
@@ -84,7 +89,8 @@
     'debug_fatalerror':         [PushAllArgs, 'call void [pypylib]pypy.runtime.Debug::DEBUG_FATALERROR(string)'],
     'keepalive':                Ignore,
     'jit_marker':               Ignore,
-    'promote_virtualizable':    Ignore,
+    'jit_force_virtualizable':  Ignore,
+    'jit_force_virtual':        DoNothing,
     }
 
 # __________ numeric operations __________

Modified: pypy/branch/stringbuilder2/pypy/translator/driver.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/driver.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/driver.py	Mon Jan 25 15:25:48 2010
@@ -428,10 +428,13 @@
 
     def possibly_check_for_boehm(self):
         if self.config.translation.gc == "boehm":
-            from pypy.rpython.tool.rffi_platform import check_boehm
-            if not check_boehm(self.translator.platform):
+            from pypy.rpython.tool.rffi_platform import configure_boehm
+            from pypy.translator.platform import CompilationError
+            try:
+                configure_boehm(self.translator.platform)
+            except CompilationError, e:
                 i = 'Boehm GC not installed.  Try e.g. "translate.py --gc=hybrid"'
-                raise Exception(i)
+                raise Exception(str(e) + '\n' + i)
 
     def task_database_c(self):
         translator = self.translator

Modified: pypy/branch/stringbuilder2/pypy/translator/exceptiontransform.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/exceptiontransform.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/exceptiontransform.py	Mon Jan 25 15:25:48 2010
@@ -3,11 +3,10 @@
 from pypy.translator.unsimplify import insert_empty_block, split_block
 from pypy.translator.backendopt import canraise, inline, support, removenoops
 from pypy.objspace.flow.model import Block, Constant, Variable, Link, \
-    c_last_exception, SpaceOperation, checkgraph, FunctionGraph
+    c_last_exception, SpaceOperation, checkgraph, FunctionGraph, mkentrymap
 from pypy.rpython.lltypesystem import lltype, llmemory, rffi
 from pypy.rpython.ootypesystem import ootype
 from pypy.rpython.lltypesystem import lloperation
-from pypy.rpython.lltypesystem.llmemory import NULL
 from pypy.rpython import rtyper
 from pypy.rpython import rclass
 from pypy.rpython.rmodel import inputconst
@@ -16,6 +15,7 @@
 from pypy.rlib.debug import ll_assert
 from pypy.annotation import model as annmodel
 from pypy.rpython.annlowlevel import MixLevelHelperAnnotator
+from pypy.tool.sourcetools import func_with_new_name
 
 PrimitiveErrorValue = {lltype.Signed: -1,
                        lltype.Unsigned: r_uint(-1),
@@ -26,7 +26,7 @@
                        lltype.Char: chr(255),
                        lltype.UniChar: unichr(0xFFFF), # XXX is this always right?
                        lltype.Bool: True,
-                       llmemory.Address: NULL,
+                       llmemory.Address: llmemory.NULL,
                        lltype.Void: None}
 
 for TYPE in rffi.NUMBER_TYPES:
@@ -45,6 +45,9 @@
 def error_constant(T):
     return Constant(error_value(T), T)
 
+def constant_value(llvalue):
+    return Constant(llvalue, lltype.typeOf(llvalue))
+
 class BaseExceptionTransformer(object):
 
     def __init__(self, translator):
@@ -64,6 +67,10 @@
         (n_i_error_ll_exc_type,
          n_i_error_ll_exc) = self.get_builtin_exception(NotImplementedError)
 
+        self.c_assertion_error_ll_exc_type = constant_value(
+            assertion_error_ll_exc_type)
+        self.c_n_i_error_ll_exc_type = constant_value(n_i_error_ll_exc_type)
+
         def rpyexc_occured():
             exc_type = exc_data.exc_type
             return bool(exc_type)
@@ -80,10 +87,14 @@
 
         def rpyexc_raise(etype, evalue):
             # assert(!RPyExceptionOccurred());
-            ll_assert(etype != assertion_error_ll_exc_type, "AssertionError!")
-            ll_assert(etype != n_i_error_ll_exc_type, "NotImplementedError!")
             exc_data.exc_type = etype
             exc_data.exc_value = evalue
+            lloperation.llop.debug_start_traceback(lltype.Void, etype)
+
+        def rpyexc_reraise(etype, evalue):
+            exc_data.exc_type = etype
+            exc_data.exc_value = evalue
+            lloperation.llop.debug_reraise_traceback(lltype.Void, etype)
 
         def rpyexc_fetch_exception():
             evalue = rpyexc_fetch_value()
@@ -92,7 +103,8 @@
         
         def rpyexc_restore_exception(evalue):
             if evalue:
-                rpyexc_raise(rclass.ll_inst_type(evalue), evalue)
+                exc_data.exc_type = rclass.ll_inst_type(evalue)
+                exc_data.exc_value = evalue
 
         def rpyexc_raise_runtime_error():
             rpyexc_raise(runtime_error_ll_exc_type, runtime_error_ll_exc)
@@ -119,14 +131,21 @@
 
         self.rpyexc_raise_ptr = self.build_func(
             "RPyRaiseException",
-            rpyexc_raise,
+            self.noinline(rpyexc_raise),
+            [self.lltype_of_exception_type, self.lltype_of_exception_value],
+            lltype.Void,
+            jitcallkind='rpyexc_raise') # for the JIT
+
+        self.rpyexc_reraise_ptr = self.build_func(
+            "RPyReRaiseException",
+            rpyexc_reraise,
             [self.lltype_of_exception_type, self.lltype_of_exception_value],
             lltype.Void,
             jitcallkind='rpyexc_raise') # for the JIT
 
         self.rpyexc_raise_runtime_error_ptr = self.build_func(
             "RPyRaiseRuntimeError",
-            rpyexc_raise_runtime_error,
+            self.noinline(rpyexc_raise_runtime_error),
             [], lltype.Void)
 
         self.rpyexc_fetch_exception_ptr = self.build_func(
@@ -136,7 +155,7 @@
 
         self.rpyexc_restore_exception_ptr = self.build_func(
             "RPyRestoreException",
-            rpyexc_restore_exception,
+            self.noinline(rpyexc_restore_exception),
             [self.lltype_of_exception_value], lltype.Void)
 
         self.build_extra_funcs()
@@ -144,6 +163,11 @@
         self.mixlevelannotator.finish()
         self.lltype_to_classdef = translator.rtyper.lltype_to_classdef_mapping()
 
+    def noinline(self, fn):
+        fn = func_with_new_name(fn, fn.__name__)
+        fn._dont_inline_ = True
+        return fn
+
     def build_func(self, name, fn, inputtypes, rettype, **kwds):
         l2a = annmodel.lltype_to_annotation
         graph = self.mixlevelannotator.getgraph(fn, map(l2a, inputtypes), l2a(rettype))
@@ -184,13 +208,18 @@
         # collect the blocks before changing them
         n_need_exc_matching_blocks = 0
         n_gen_exc_checks           = 0
+        #
+        entrymap = mkentrymap(graph)
+        if graph.exceptblock in entrymap:
+            for link in entrymap[graph.exceptblock]:
+                self.transform_jump_to_except_block(graph, entrymap, link)
+        #
         for block in list(graph.iterblocks()):
             self.replace_stack_unwind(block)
             self.replace_fetch_restore_operations(block)
             need_exc_matching, gen_exc_checks = self.transform_block(graph, block)
             n_need_exc_matching_blocks += need_exc_matching
             n_gen_exc_checks           += gen_exc_checks
-        self.transform_except_block(graph, graph.exceptblock)
         cleanup_graph(graph)
         removenoops.remove_superfluous_keep_alive(graph)
         return n_need_exc_matching_blocks, n_gen_exc_checks
@@ -268,18 +297,54 @@
                 self.insert_matching(lastblock, graph)
         return need_exc_matching, n_gen_exc_checks
 
-    def transform_except_block(self, graph, block):
-        # attach an except block -- let's hope that nobody uses it
-        graph.exceptblock = Block([Variable('etype'),   # exception class
-                                   Variable('evalue')])  # exception value
-        graph.exceptblock.operations = ()
-        graph.exceptblock.closeblock()
-        
+    def comes_from_last_exception(self, entrymap, link):
+        seen = {}
+        pending = [(link, link.args[1])]
+        while pending:
+            link, v = pending.pop()
+            if (link, v) in seen:
+                continue
+            seen[link, v] = True
+            if link.last_exc_value is not None and v is link.last_exc_value:
+                return True
+            block = link.prevblock
+            if block is None:
+                continue
+            for op in block.operations[::-1]:
+                if v is op.result:
+                    if op.opname == 'cast_pointer':
+                        v = op.args[0]
+                    else:
+                        break
+            for link in entrymap.get(block, ()):
+                for v1, v2 in zip(link.args, block.inputargs):
+                    if v2 is v:
+                        pending.append((link, v1))
+        return False
+
+    def transform_jump_to_except_block(self, graph, entrymap, link):
+        reraise = self.comes_from_last_exception(entrymap, link)
         result = Variable()
         result.concretetype = lltype.Void
-        block.operations = [SpaceOperation(
-           "direct_call", [self.rpyexc_raise_ptr] + block.inputargs, result)]
-        l = Link([error_constant(graph.returnblock.inputargs[0].concretetype)], graph.returnblock)
+        block = Block([copyvar(None, v)
+                       for v in graph.exceptblock.inputargs])
+        if reraise:
+            block.operations = [
+                SpaceOperation("direct_call",
+                               [self.rpyexc_reraise_ptr] + block.inputargs,
+                               result),
+                ]
+        else:
+            block.operations = [
+                SpaceOperation("direct_call",
+                               [self.rpyexc_raise_ptr] + block.inputargs,
+                               result),
+                SpaceOperation('debug_record_traceback', [],
+                               varoftype(lltype.Void)),
+                ]
+        link.target = block
+        RETTYPE = graph.returnblock.inputargs[0].concretetype
+        l = Link([error_constant(RETTYPE)], graph.returnblock)
         block.recloseblock(l)
 
     def insert_matching(self, block, graph):
@@ -328,6 +393,11 @@
         llops = rtyper.LowLevelOpList(None)
         var_value = self.gen_getfield('exc_value', llops)
         var_type  = self.gen_getfield('exc_type' , llops)
+        #
+        c_check1 = self.c_assertion_error_ll_exc_type
+        c_check2 = self.c_n_i_error_ll_exc_type
+        llops.genop('debug_catch_exception', [var_type, c_check1, c_check2])
+        #
         self.gen_setfield('exc_value', self.c_null_evalue, llops)
         self.gen_setfield('exc_type',  self.c_null_etype,  llops)
         excblock.operations[:] = llops
@@ -361,7 +431,12 @@
         
         block.exitswitch = var_no_exc
         #exception occurred case
+        b = Block([])
+        b.operations = [SpaceOperation('debug_record_traceback', [],
+                                       varoftype(lltype.Void))]
         l = Link([error_constant(returnblock.inputargs[0].concretetype)], returnblock)
+        b.closeblock(l)
+        l = Link([], b)
         l.exitcase = l.llexitcase = False
 
         #non-exception case

Modified: pypy/branch/stringbuilder2/pypy/translator/goal/app_main.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/goal/app_main.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/goal/app_main.py	Mon Jan 25 15:25:48 2010
@@ -228,7 +228,7 @@
     newpath.insert(0, '')
     # remove duplicates
     _seen = {}
-    sys.path = []
+    del sys.path[:]
     for dir in newpath:
         if dir not in _seen:
             sys.path.append(dir)

Modified: pypy/branch/stringbuilder2/pypy/translator/jvm/opcodes.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/jvm/opcodes.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/jvm/opcodes.py	Mon Jan 25 15:25:48 2010
@@ -97,9 +97,15 @@
     'gc_set_max_heap_size':     Ignore,
     'resume_point':             Ignore,
     'jit_marker':               Ignore,
-    'promote_virtualizable':    Ignore,
+    'jit_force_virtualizable':  Ignore,
+    'jit_force_virtual':        DoNothing,
 
     'debug_assert':              [], # TODO: implement?
+    'debug_start_traceback':    Ignore,
+    'debug_record_traceback':   Ignore,
+    'debug_catch_exception':    Ignore,
+    'debug_reraise_traceback':  Ignore,
+    'debug_print_traceback':    Ignore,
 
     # __________ numeric operations __________
 

Modified: pypy/branch/stringbuilder2/pypy/translator/platform/__init__.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/platform/__init__.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/platform/__init__.py	Mon Jan 25 15:25:48 2010
@@ -52,6 +52,8 @@
     name = "abstract platform"
     c_environ = None
 
+    relevant_environ = []
+
     so_prefixes = ['']
 
     def __init__(self, cc):
@@ -98,6 +100,12 @@
         return (self.__class__ is other.__class__ and
                 self.__dict__ == other.__dict__)
 
+    def key(self):
+        bits = [self.__class__.__name__, 'cc=%s' % self.cc]
+        for varname in self.relevant_environ:
+            bits.append('%s=%s' % (varname, os.environ.get(varname)))
+        return ' '.join(bits)
+
     # some helpers which seem to be cross-platform enough
 
     def _execute_c_compiler(self, cc, args, outname):
@@ -171,8 +179,15 @@
     else:
         host_factory = Linux64
 elif sys.platform == 'darwin':
-    from pypy.translator.platform.darwin import Darwin
-    host_factory = Darwin
+    from pypy.translator.platform.darwin import Darwin_i386, Darwin_x86_64
+    import platform
+    if platform.machine() == 'i386':
+        if sys.maxint <= 2147483647:
+            host_factory = Darwin_i386
+        else:
+            host_factory = Darwin_x86_64
+    else:
+        host_factory = Darwin
 elif sys.platform == 'freebsd7':
     from pypy.translator.platform.freebsd7 import Freebsd7, Freebsd7_64
     import platform

Modified: pypy/branch/stringbuilder2/pypy/translator/platform/darwin.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/platform/darwin.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/platform/darwin.py	Mon Jan 25 15:25:48 2010
@@ -4,7 +4,7 @@
 
 class Darwin(posix.BasePosix):
     name = "darwin"
-    
+
     link_flags = ['-mmacosx-version-min=10.4']
     cflags = ['-O3', '-fomit-frame-pointer', '-mmacosx-version-min=10.4']
     standalone_only = ['-mdynamic-no-pic']
@@ -44,3 +44,13 @@
         include_dirs = self._includedirs(eci.include_dirs)
         return (args + frameworks + include_dirs)
 
+class Darwin_i386(Darwin):
+    name = "darwin_i386"
+    link_flags = ['-arch', 'i386', '-mmacosx-version-min=10.4']
+    cflags = ['-arch', 'i386', '-O3', '-fomit-frame-pointer', '-mmacosx-version-min=10.4']
+
+class Darwin_x86_64(Darwin):
+    name = "darwin_x86_64"
+    link_flags = ['-arch', 'x86_64', '-mmacosx-version-min=10.4']
+    cflags = ['-arch', 'x86_64', '-O3', '-fomit-frame-pointer', '-mmacosx-version-min=10.4']
+

Modified: pypy/branch/stringbuilder2/pypy/translator/platform/linux.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/platform/linux.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/platform/linux.py	Mon Jan 25 15:25:48 2010
@@ -11,7 +11,7 @@
     link_flags = ['-pthread', '-lrt']
     cflags = ['-O3', '-pthread', '-fomit-frame-pointer']
     standalone_only = []
-    shared_only = []
+    shared_only = ['-fPIC']
     so_ext = 'so'
     so_prefixes = ['lib', '']
     

Modified: pypy/branch/stringbuilder2/pypy/translator/platform/posix.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/platform/posix.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/platform/posix.py	Mon Jan 25 15:25:48 2010
@@ -10,6 +10,8 @@
     exe_ext = ''
     make_cmd = 'make'
 
+    relevant_environ=['CPATH', 'LIBRARY_PATH', 'C_INCLUDE_PATH']
+
     def __init__(self, cc=None):
         if cc is None:
             cc = 'gcc'

Modified: pypy/branch/stringbuilder2/pypy/translator/platform/test/test_darwin.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/platform/test/test_darwin.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/platform/test/test_darwin.py	Mon Jan 25 15:25:48 2010
@@ -2,17 +2,25 @@
 """ File containing darwin platform tests
 """
 
-import py, sys
+import py, sys, platform
 if sys.platform != 'darwin':
     py.test.skip("Darwin only")
 
 from pypy.tool.udir import udir
-from pypy.translator.platform.darwin import Darwin
+from pypy.translator.platform.darwin import Darwin_i386, Darwin_x86_64
 from pypy.translator.platform.test.test_platform import TestPlatform as BasicTest
 from pypy.translator.tool.cbuild import ExternalCompilationInfo
 
+if platform.machine() == 'i386':
+    if sys.maxint <= 2147483647:
+        host_factory = Darwin_i386
+    else:
+        host_factory = Darwin_x86_64
+else:
+    host_factory = Darwin
+
 class TestDarwin(BasicTest):
-    platform = Darwin()
+    platform = host_factory()
 
     def test_frameworks(self):
         objcfile = udir.join('test_simple.m')
@@ -39,3 +47,81 @@
         res = self.platform.execute(executable)
         self.check_res(res)
 
+    def test_64_32_results(self):
+        if platform.machine() != 'i386':
+            py.test.skip("i386 only")
+        plat32 = Darwin_i386()
+        plat64 = Darwin_x86_64()
+        cfile = udir.join('test_int_size.c')
+        cfile.write(r'''
+        #include <stdio.h>
+        #include <limits.h>
+
+        int main() {
+                printf("%d\n", INT_MAX < LONG_MAX);
+                return 0;
+        }
+        ''')
+        eci = ExternalCompilationInfo()
+        executable = plat32.compile([cfile], eci)
+        res = plat32.execute(executable)
+        self.check_res(res, '0\n')
+        if host_factory == Darwin_x86_64:
+            executable = plat64.compile([cfile], eci)
+            res = plat64.execute(executable)
+            self.check_res(res, '1\n')
+
+    def test_longsize(self):
+        if platform.machine() != 'i386':
+            py.test.skip("i386 only")
+        cfile = udir.join('test_int_size.c')
+        cfile.write(r'''
+        #include <stdio.h>
+        #include <limits.h>
+
+        int main() {
+                printf("%ld\n", LONG_MAX);
+                return 0;
+        }
+        ''')
+        eci = ExternalCompilationInfo()
+        executable = self.platform.compile([cfile], eci)
+        res = self.platform.execute(executable)
+        self.check_res(res, str(sys.maxint) + '\n')
+        
+    def test_32bit_makefile(self):
+        if platform.machine() != 'i386':
+            py.test.skip("i386 only")
+        plat32 = Darwin_i386()
+        plat64 = Darwin_x86_64()
+        eci = ExternalCompilationInfo()
+        cfile_content =r'''
+        #include <stdio.h>
+        #include <limits.h>
+
+        int main() {
+                printf("%d\n", INT_MAX < LONG_MAX);
+                return 0;
+        }
+        '''
+
+        tmpdir = udir.join('32_makefile' + self.__class__.__name__).ensure(dir=1)
+        cfile = tmpdir.join('test_int_size.c')
+        cfile.write(cfile_content)
+        mk = plat32.gen_makefile([cfile], ExternalCompilationInfo(),
+                               path=tmpdir)
+        mk.write()
+        plat32.execute_makefile(mk)
+        res = plat32.execute(tmpdir.join('test_int_size'))
+        self.check_res(res, '0\n')
+        if host_factory == Darwin_x86_64:
+            tmpdir = udir.join('64_makefile' + self.__class__.__name__).ensure(dir=1)
+            cfile = tmpdir.join('test_int_size.c')
+            cfile.write(cfile_content)
+            mk = plat64.gen_makefile([cfile], ExternalCompilationInfo(),
+                                   path=tmpdir)
+            mk.write()
+            plat64.execute_makefile(mk)
+            res = plat64.execute(tmpdir.join('test_int_size'))
+            self.check_res(res, '1\n')
+

Modified: pypy/branch/stringbuilder2/pypy/translator/platform/test/test_platform.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/platform/test/test_platform.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/platform/test/test_platform.py	Mon Jan 25 15:25:48 2010
@@ -115,6 +115,16 @@
         finally:
             del os.environ['_SOME_VARIABLE_2']
 
+    def test_key(self):
+        class XPlatform(Platform):
+            relevant_environ = ['CPATH']
+            
+            def __init__(self):
+                self.cc = 'xcc'
+        x = XPlatform()
+        res = x.key()
+        assert res.startswith('XPlatform cc=xcc CPATH=')
+
 def test_equality():
     class X(Platform):
         def __init__(self):

Modified: pypy/branch/stringbuilder2/pypy/translator/test/test_exceptiontransform.py
==============================================================================
--- pypy/branch/stringbuilder2/pypy/translator/test/test_exceptiontransform.py	(original)
+++ pypy/branch/stringbuilder2/pypy/translator/test/test_exceptiontransform.py	Mon Jan 25 15:25:48 2010
@@ -95,7 +95,7 @@
                 return 3 + x
             return 4 + x
         t, g = self.transform_func(foo, [int])
-        assert len(list(g.iterblocks())) == 9
+        assert len(list(g.iterblocks())) == 10
         f = self.compile(foo, [int])
         result = interpret(foo, [6])
         assert result == 2
@@ -126,7 +126,7 @@
                 return 1 + x
             return 4 + x
         t, g = self.transform_func(foo, [int])
-        assert len(list(g.iterblocks())) == 5
+        assert len(list(g.iterblocks())) == 6
         f = self.compile(foo, [int])
         result = interpret(foo, [6])
         assert result == 2
@@ -175,6 +175,34 @@
         etrafo.create_exception_handling(g)    
         assert etrafo.raise_analyzer.analyze_direct_call(g)
 
+    def test_reraise_is_not_raise(self):
+        def one(x):
+            if x == 1:
+                raise ValueError()
+            elif x == 2:
+                raise TypeError()
+            return x - 5
+        def foo(x):
+            try:
+                return one(x)
+            except ValueError:
+                return -42
+        t, g = self.transform_func(foo, [int])
+        for block in g.iterblocks():
+            for op in block.operations:
+                # the operation 'debug_record_traceback' should only show up
+                # in a normal raise, not in a reraise
+                assert op.opname != 'debug_record_traceback'
+        f = self.compile(foo, [int])
+        result = interpret(foo, [7])
+        assert result == 2
+        result = f(7)
+        assert result == 2
+        result = interpret(foo, [1])
+        assert result == -42
+        result = f(1)
+        assert result == -42
+
 
 class TestLLType(BaseTestExceptionTransform):
     type_system = 'lltype'



More information about the Pypy-commit mailing list