[pypy-commit] pypy reflex-support: merge default into branch

wlav noreply at buildbot.pypy.org
Wed Jan 9 04:28:23 CET 2013


Author: Wim Lavrijsen <WLavrijsen at lbl.gov>
Branch: reflex-support
Changeset: r59885:4297455b87c7
Date: 2013-01-08 19:28 -0800
http://bitbucket.org/pypy/pypy/changeset/4297455b87c7/

Log:	merge default into branch

diff --git a/pypy/doc/Makefile b/pypy/doc/Makefile
--- a/pypy/doc/Makefile
+++ b/pypy/doc/Makefile
@@ -32,38 +32,38 @@
 	-rm -rf $(BUILDDIR)/*
 
 html:
-	python config/generate.py
+	# python config/generate.py #readthedocs will not run this Makefile
 	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
 	@echo
 	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
 
 dirhtml:
-	python config/generate.py
+	# python config/generate.py #readthedocs will not run this Makefile
 	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
 	@echo
 	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
 
 pickle:
-	python config/generate.py
+	# python config/generate.py #readthedocs will not run this Makefile
 	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
 	@echo
 	@echo "Build finished; now you can process the pickle files."
 
 json:
-	python config/generate.py
+	# python config/generate.py #readthedocs will not run this Makefile
 	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
 	@echo
 	@echo "Build finished; now you can process the JSON files."
 
 htmlhelp:
-	python config/generate.py
+	# python config/generate.py #readthedocs will not run this Makefile
 	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
 	@echo
 	@echo "Build finished; now you can run HTML Help Workshop with the" \
 	      ".hhp project file in $(BUILDDIR)/htmlhelp."
 
 qthelp:
-	python config/generate.py
+	# python config/generate.py #readthedocs will not run this Makefile
 	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
 	@echo
 	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
@@ -73,7 +73,7 @@
 	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PyPy.qhc"
 
 latex:
-	python config/generate.py
+	# python config/generate.py #readthedocs will not run this Makefile
 	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
 	@echo
 	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@@ -81,26 +81,26 @@
 	      "run these through (pdf)latex."
 
 man:
-	python config/generate.py
+	# python config/generate.py #readthedocs will not run this Makefile
 	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
 	@echo
 	@echo "Build finished. The manual pages are in $(BUILDDIR)/man"
 
 changes:
-	python config/generate.py
+	# python config/generate.py #readthedocs will not run this Makefile
 	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
 	@echo
 	@echo "The overview file is in $(BUILDDIR)/changes."
 
 linkcheck:
-	python config/generate.py
+	# python config/generate.py #readthedocs will not run this Makefile
 	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
 	@echo
 	@echo "Link check complete; look for any errors in the above output " \
 	      "or in $(BUILDDIR)/linkcheck/output.txt."
 
 doctest:
-	python config/generate.py
+	# python config/generate.py #readthedocs will not run this Makefile
 	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
 	@echo "Testing of doctests in the sources finished, look at the " \
 	      "results in $(BUILDDIR)/doctest/output.txt."
diff --git a/pypy/doc/conf.py b/pypy/doc/conf.py
--- a/pypy/doc/conf.py
+++ b/pypy/doc/conf.py
@@ -38,7 +38,7 @@
 
 # General information about the project.
 project = u'PyPy'
-copyright = u'2011, The PyPy Project'
+copyright = u'2013, The PyPy Project'
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
diff --git a/pypy/doc/config/translation.no__thread.txt b/pypy/doc/config/translation.no__thread.txt
--- a/pypy/doc/config/translation.no__thread.txt
+++ b/pypy/doc/config/translation.no__thread.txt
@@ -1,3 +1,3 @@
 Don't use gcc __thread attribute for fast thread local storage
-implementation . Increases the chance that moving the resulting
+implementation. Increases the chance that moving the resulting
 executable to another same processor Linux machine will work.
diff --git a/pypy/doc/pypyconfig.py b/pypy/doc/pypyconfig.py
--- a/pypy/doc/pypyconfig.py
+++ b/pypy/doc/pypyconfig.py
@@ -3,6 +3,10 @@
 def setup(app):
     import sys, os
     sys.path.insert(0, os.path.abspath("../../"))
+
+    #Autmatically calls make_cmdlline_overview
+    from pypy.doc.config import generate 
+
     from pypy.config import makerestdoc
     import py
     role = makerestdoc.register_config_role(py.path.local())
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -5,6 +5,11 @@
 .. this is a revision shortly after release-2.0-beta1
 .. startrev: 0e6161a009c6
 
+.. branch: callback-jit
+Callbacks from C are now better JITted
+
+.. branch: remove-globals-in-jit
+
 .. branch: length-hint
 Implement __lenght_hint__ according to PEP 424
 
diff --git a/pypy/doc/windows.rst b/pypy/doc/windows.rst
--- a/pypy/doc/windows.rst
+++ b/pypy/doc/windows.rst
@@ -56,11 +56,24 @@
 ----------------------------
 
 On Windows, there is no standard place where to download, build and
-install third-party libraries.  We chose to install them in the parent
+install third-party libraries.  We recommend installing them in the parent
 directory of the pypy checkout.  For example, if you installed pypy in
 ``d:\pypy\trunk\`` (This directory contains a README file), the base
-directory is ``d:\pypy``. You may choose different values by setting the
-INCLUDE, LIB and PATH (for DLLs)
+directory is ``d:\pypy``. You must then set the
+INCLUDE, LIB and PATH (for DLLs) environment variables appropriately.
+
+Abridged method (for -Ojit builds using Visual Studio 2008)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Download the versions of all the external packages
+from 
+https://bitbucket.org/pypy/pypy/downloads/local.zip
+Then expand it into the base directory (base_dir) and modify your environment to reflect this::
+
+    set PATH=<base_dir>\bin;%PATH%
+    set INCLUDE=<base_dir>\include;%INCLUDE%
+    set LIB=<base_dir>\lib;%LIB%
+
+Now you should be good to go. Read on for more information.
 
 The Boehm garbage collector
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -105,6 +105,13 @@
 # typical usage (case 1 is the most common kind of app-level subclasses;
 # case 2 is the memory-saving kind defined with __slots__).
 #
+#  +----------------------------------------------------------------+
+#  | NOTE: if withmapdict is enabled, the following doesn't apply!  |
+#  | Map dicts can flexibly allow any slots/__dict__/__weakref__ to |
+#  | show up only when needed.  In particular there is no way with  |
+#  | mapdict to prevent some objects from being weakrefable.        |
+#  +----------------------------------------------------------------+
+#
 #     dict   slots   del   weakrefable
 #
 # 1.    Y      N      N         Y          UserDictWeakref
diff --git a/pypy/jit/backend/arm/test/test_gc_integration.py b/pypy/jit/backend/arm/test/test_gc_integration.py
--- a/pypy/jit/backend/arm/test/test_gc_integration.py
+++ b/pypy/jit/backend/arm/test/test_gc_integration.py
@@ -2,25 +2,13 @@
 """ Tests for register allocation for common constructs
 """
 
-import py
-from pypy.jit.metainterp.history import BoxInt, \
-     BoxPtr, TreeLoop, TargetToken
-from pypy.jit.metainterp.resoperation import rop, ResOperation
-from pypy.jit.codewriter import heaptracker
-from pypy.jit.backend.llsupport.descr import GcCache
-from pypy.jit.backend.llsupport.gc import GcLLDescription
+from pypy.jit.metainterp.history import TargetToken
+from pypy.jit.backend.llsupport.gc import GcLLDescription, GcLLDescr_boehm
 from pypy.jit.backend.detect_cpu import getcpuclass
 from pypy.jit.backend.arm.arch import WORD
 from pypy.rpython.lltypesystem import lltype, llmemory, rffi
-from pypy.rpython.annlowlevel import llhelper
-from pypy.rpython.lltypesystem import rclass
-from pypy.jit.backend.llsupport.gc import GcLLDescr_framework
 
-from pypy.jit.backend.arm.test.test_regalloc import MockAssembler
 from pypy.jit.backend.arm.test.test_regalloc import BaseTestRegalloc
-from pypy.jit.backend.arm.regalloc import ARMFrameManager, VFPRegisterManager
-from pypy.jit.codewriter.effectinfo import EffectInfo
-from pypy.jit.backend.arm.regalloc import Regalloc
 
 CPU = getcpuclass()
 
@@ -44,23 +32,14 @@
         return ['compressed'] + shape[1:]
 
 
-class MockGcDescr(GcCache):
-    get_malloc_slowpath_addr = None
-    write_barrier_descr = None
-    moving_gc = True
+class MockGcDescr(GcLLDescr_boehm):
     gcrootmap = MockGcRootMap()
 
-    def initialize(self):
-        pass
-
-    _record_constptrs = GcLLDescr_framework._record_constptrs.im_func
-    rewrite_assembler = GcLLDescr_framework.rewrite_assembler.im_func
-
 
 class TestRegallocGcIntegration(BaseTestRegalloc):
     
     cpu = CPU(None, None)
-    cpu.gc_ll_descr = MockGcDescr(False)
+    cpu.gc_ll_descr = MockGcDescr(None, None, None)
     cpu.setup_once()
     
     S = lltype.GcForwardReference()
diff --git a/pypy/jit/backend/llgraph/runner.py b/pypy/jit/backend/llgraph/runner.py
--- a/pypy/jit/backend/llgraph/runner.py
+++ b/pypy/jit/backend/llgraph/runner.py
@@ -877,13 +877,16 @@
         #
         assembler_helper_ptr = jd.assembler_helper_adr.ptr  # fish
         try:
-            return assembler_helper_ptr(pframe, vable)
+            result = assembler_helper_ptr(pframe, vable)
         except LLException, lle:
             assert self.last_exception is None, "exception left behind"
             self.last_exception = lle
             # fish op
             op = self.current_op
             return op.result and op.result.value
+        if isinstance(result, float):
+            result = support.cast_to_floatstorage(result)
+        return result
 
     def execute_same_as(self, _, x):
         return x
diff --git a/pypy/jit/metainterp/pyjitpl.py b/pypy/jit/metainterp/pyjitpl.py
--- a/pypy/jit/metainterp/pyjitpl.py
+++ b/pypy/jit/metainterp/pyjitpl.py
@@ -1379,16 +1379,15 @@
         """The 'residual_call' operation is emitted in two cases:
         when we have to generate a residual CALL operation, but also
         to handle an indirect_call that may need to be inlined."""
-        assert isinstance(funcbox, Const)
-        sd = self.metainterp.staticdata
-        key = sd.cpu.ts.getaddr_for_box(funcbox)
-        jitcode = sd.bytecode_for_address(key)
-        if jitcode is not None:
-            # we should follow calls to this graph
-            return self.metainterp.perform_call(jitcode, argboxes)
-        else:
-            # but we should not follow calls to that graph
-            return self.do_residual_call(funcbox, argboxes, calldescr)
+        if isinstance(funcbox, Const):
+            sd = self.metainterp.staticdata
+            key = sd.cpu.ts.getaddr_for_box(funcbox)
+            jitcode = sd.bytecode_for_address(key)
+            if jitcode is not None:
+                # we should follow calls to this graph
+                return self.metainterp.perform_call(jitcode, argboxes)
+        # but we should not follow calls to that graph
+        return self.do_residual_call(funcbox, argboxes, calldescr)
 
 # ____________________________________________________________
 
diff --git a/pypy/jit/metainterp/resume.py b/pypy/jit/metainterp/resume.py
--- a/pypy/jit/metainterp/resume.py
+++ b/pypy/jit/metainterp/resume.py
@@ -1085,6 +1085,9 @@
     def consume_virtualref_info(self, vrefinfo, numb, end):
         # we have to decode a list of references containing pairs
         # [..., virtual, vref, ...]  stopping at 'end'
+        if vrefinfo is None:
+            assert end == 0
+            return
         assert (end & 1) == 0
         for i in range(0, end, 2):
             virtual = self.decode_ref(numb.nums[i])
diff --git a/pypy/jit/metainterp/test/test_ajit.py b/pypy/jit/metainterp/test/test_ajit.py
--- a/pypy/jit/metainterp/test/test_ajit.py
+++ b/pypy/jit/metainterp/test/test_ajit.py
@@ -3979,6 +3979,8 @@
             rgc.add_memory_pressure(1234)
             return 3
 
+        self.interp_operations(f, [])
+
     def test_external_call(self):
         from pypy.rlib.objectmodel import invoke_around_extcall
         
diff --git a/pypy/jit/metainterp/test/test_call.py b/pypy/jit/metainterp/test/test_call.py
new file mode 100644
--- /dev/null
+++ b/pypy/jit/metainterp/test/test_call.py
@@ -0,0 +1,27 @@
+
+from pypy.jit.metainterp.test.support import LLJitMixin
+from pypy.rlib import jit
+
+class TestCall(LLJitMixin):
+    def test_indirect_call(self):
+        @jit.dont_look_inside
+        def f1(x):
+            return x + 1
+
+        @jit.dont_look_inside
+        def f2(x):
+            return x + 2
+
+        @jit.dont_look_inside
+        def choice(i):
+            if i:
+                return f1
+            return f2
+
+        def f(i):
+            func = choice(i)
+            return func(i)
+
+        res = self.interp_operations(f, [3])
+        assert res == f(3)
+    
diff --git a/pypy/jit/metainterp/test/test_warmspot.py b/pypy/jit/metainterp/test/test_warmspot.py
--- a/pypy/jit/metainterp/test/test_warmspot.py
+++ b/pypy/jit/metainterp/test/test_warmspot.py
@@ -519,6 +519,44 @@
         self.check_trace_count(1)
 
 
+    def test_callback_jit_merge_point(self):
+        from pypy.rlib.objectmodel import register_around_callback_hook
+        from pypy.rpython.lltypesystem import lltype, rffi
+        from pypy.translator.tool.cbuild import ExternalCompilationInfo
+        
+        callback_jit_driver = JitDriver(greens = ['name'], reds = 'auto')
+        
+        def callback_merge_point(name):
+            callback_jit_driver.jit_merge_point(name=name)
+    
+        @callback_jit_driver.inline(callback_merge_point)
+        def callback_hook(name):
+            pass
+
+        def callback(a, b):
+            if a > b:
+                return 1
+            return -1
+
+        CB_TP = rffi.CCallback([lltype.Signed, lltype.Signed], lltype.Signed)
+        eci = ExternalCompilationInfo(includes=['stdlib.h'])
+        qsort = rffi.llexternal('qsort',
+                                [rffi.VOIDP, lltype.Signed, lltype.Signed,
+                                 CB_TP], lltype.Void, compilation_info=eci)
+        ARR = rffi.CArray(lltype.Signed)
+
+        def main():
+            register_around_callback_hook(callback_hook)
+            raw = lltype.malloc(ARR, 10, flavor='raw')
+            for i in range(10):
+                raw[i] = 10 - i
+            qsort(raw, 10, rffi.sizeof(lltype.Signed), callback)
+            lltype.free(raw, flavor='raw')
+
+        self.meta_interp(main, [])
+        self.check_trace_count(1)
+
+
 class TestLLWarmspot(WarmspotTests, LLJitMixin):
     CPUClass = runner.LLGraphCPU
     type_system = 'lltype'
diff --git a/pypy/jit/metainterp/warmspot.py b/pypy/jit/metainterp/warmspot.py
--- a/pypy/jit/metainterp/warmspot.py
+++ b/pypy/jit/metainterp/warmspot.py
@@ -290,11 +290,13 @@
         callgraph = inlinable_static_callers(self.translator.graphs, store_calls=True)
         new_callgraph = []
         new_portals = set()
+        inlined_jit_merge_points = set()
         for caller, block, op_call, callee in callgraph:
             func = getattr(callee, 'func', None)
             _inline_jit_merge_point_ = getattr(func, '_inline_jit_merge_point_', None)
             if _inline_jit_merge_point_:
                 _inline_jit_merge_point_._always_inline_ = True
+                inlined_jit_merge_points.add(_inline_jit_merge_point_)
                 op_jmp_call, jmp_graph = get_jmp_call(callee, _inline_jit_merge_point_)
                 #
                 # now we move the op_jmp_call from callee to caller, just
@@ -315,6 +317,9 @@
         # inline them!
         inline_threshold = 0.1 # we rely on the _always_inline_ set above
         auto_inlining(self.translator, inline_threshold, new_callgraph)
+        # clean up _always_inline_ = True, it can explode later
+        for item in inlined_jit_merge_points:
+            del item._always_inline_
 
         # make a fresh copy of the JitDriver in all newly created
         # jit_merge_points
@@ -1011,6 +1016,9 @@
         origblock.operations.append(newop)
         origblock.exitswitch = None
         origblock.recloseblock(Link([v_result], origportalgraph.returnblock))
+        # the origportal now can raise (even if it did not raise before),
+        # which means that we cannot inline it anywhere any more, but that's
+        # fine since any forced inlining has been done before
         #
         checkgraph(origportalgraph)
 
diff --git a/pypy/module/_cffi_backend/ccallback.py b/pypy/module/_cffi_backend/ccallback.py
--- a/pypy/module/_cffi_backend/ccallback.py
+++ b/pypy/module/_cffi_backend/ccallback.py
@@ -3,10 +3,10 @@
 """
 import os
 from pypy.interpreter.error import OperationError, operationerrfmt
-from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+from pypy.rpython.lltypesystem import lltype, rffi
 from pypy.rlib.objectmodel import compute_unique_id, keepalive_until_here
-from pypy.rlib import clibffi, rweakref, rgc
-from pypy.rlib.rarithmetic import r_ulonglong
+from pypy.rlib import clibffi, rweakref
+from pypy.rlib import jit
 
 from pypy.module._cffi_backend.cdataobj import W_CData
 from pypy.module._cffi_backend.ctypefunc import SIZE_OF_FFI_ARG, BIG_ENDIAN
@@ -77,6 +77,7 @@
                                  space.wrap("expected a function ctype"))
         return ctype
 
+    @jit.unroll_safe
     def invoke(self, ll_args):
         space = self.space
         ctype = self.getfunctype()
diff --git a/pypy/module/_weakref/test/test_weakref.py b/pypy/module/_weakref/test/test_weakref.py
--- a/pypy/module/_weakref/test/test_weakref.py
+++ b/pypy/module/_weakref/test/test_weakref.py
@@ -114,19 +114,28 @@
         class A(object):
             def __eq__(self, other):
                 return True
+            def __ne__(self, other):
+                return False
         a1 = A()
         a2 = A()
         ref1 = _weakref.ref(a1)
         ref2 = _weakref.ref(a2)
         assert ref1 == ref2
+        assert not (ref1 != ref2)
+        assert not (ref1 == [])
+        assert ref1 != []
         del a1
         gc.collect()
         assert not ref1 == ref2
         assert ref1 != ref2
+        assert not (ref1 == [])
+        assert ref1 != []
         del a2
         gc.collect()
         assert not ref1 == ref2
         assert ref1 != ref2
+        assert not (ref1 == [])
+        assert ref1 != []
 
     def test_getweakrefs(self):
         import _weakref, gc
@@ -298,6 +307,13 @@
         if seen_callback:
             assert seen_callback == [True, True, True]
 
+    def test_type_weakrefable(self):
+        import _weakref, gc
+        w = _weakref.ref(list)
+        assert w() is list
+        gc.collect()
+        assert w() is list
+
 
 class AppTestProxy(object):
     spaceconfig = dict(usemodules=('_weakref',))
@@ -435,6 +451,8 @@
         class A(object):
             def __eq__(self, other):
                 return True
+            def __ne__(self, other):
+                return False
 
         a = A()
         assert _weakref.ref(a) == a
diff --git a/pypy/module/pyexpat/interp_pyexpat.py b/pypy/module/pyexpat/interp_pyexpat.py
--- a/pypy/module/pyexpat/interp_pyexpat.py
+++ b/pypy/module/pyexpat/interp_pyexpat.py
@@ -182,6 +182,8 @@
 
     def clear(self):
         self.next_id = 0
+        self._last_object_id = -1
+        self._last_object = None
         self.storage = {}
 
     @staticmethod
@@ -194,10 +196,18 @@
 
     @staticmethod
     def get_object(id):
-        return global_storage.storage[id]
+        if id == global_storage._last_object_id:
+            return global_storage._last_object
+        result = global_storage.storage[id]
+        global_storage._last_object_id = id
+        global_storage._last_object = result
+        return result
 
     @staticmethod
     def free_nonmoving_id(id):
+        if id == global_storage._last_object_id:
+            global_storage._last_object = None
+            global_storage._last_object_id = -1
         del global_storage.storage[id]
 
 global_storage = Storage()
diff --git a/pypy/module/pypyjit/interp_jit.py b/pypy/module/pypyjit/interp_jit.py
--- a/pypy/module/pypyjit/interp_jit.py
+++ b/pypy/module/pypyjit/interp_jit.py
@@ -6,7 +6,7 @@
 from pypy.tool.pairtype import extendabletype
 from pypy.rlib.rarithmetic import r_uint, intmask
 from pypy.rlib.jit import JitDriver, hint, we_are_jitted, dont_look_inside
-from pypy.rlib import jit
+from pypy.rlib import jit, objectmodel
 from pypy.rlib.jit import current_trace_length, unroll_parameters
 import pypy.interpreter.pyopcode   # for side-effects
 from pypy.interpreter.error import OperationError, operationerrfmt
@@ -97,6 +97,15 @@
                                     is_being_profiled=self.is_being_profiled)
         return jumpto
 
+callback_jit_driver = JitDriver(greens = ['name'], reds = 'auto')
+
+def callback_merge_point(name):
+    callback_jit_driver.jit_merge_point(name=name)
+
+ at callback_jit_driver.inline(callback_merge_point)
+def callback_hook(name):
+    pass
+
 def _get_adapted_tick_counter():
     # Normally, the tick counter is decremented by 100 for every
     # Python opcode.  Here, to better support JIT compilation of
diff --git a/pypy/module/pypyjit/policy.py b/pypy/module/pypyjit/policy.py
--- a/pypy/module/pypyjit/policy.py
+++ b/pypy/module/pypyjit/policy.py
@@ -106,7 +106,7 @@
                        'posix', '_socket', '_sre', '_lsprof', '_weakref',
                        '__pypy__', 'cStringIO', '_collections', 'struct',
                        'mmap', 'marshal', '_codecs', 'rctime', 'cppyy',
-                       '_cffi_backend']:
+                       '_cffi_backend', 'pyexpat']:
             if modname == 'pypyjit' and 'interp_resop' in rest:
                 return False
             return True
diff --git a/pypy/rlib/objectmodel.py b/pypy/rlib/objectmodel.py
--- a/pypy/rlib/objectmodel.py
+++ b/pypy/rlib/objectmodel.py
@@ -595,6 +595,16 @@
     llhelper(rffi.AroundFnPtr, before)
     llhelper(rffi.AroundFnPtr, after)
 
+def register_around_callback_hook(hook):
+    """ Register a hook that's called before a callback from C calls RPython.
+    Primary usage is for JIT to have 'started from' hook.
+    """
+    from pypy.rpython.lltypesystem import rffi
+    from pypy.rpython.annlowlevel import llhelper
+   
+    rffi.aroundstate.callback_hook = hook
+    llhelper(rffi.CallbackHookPtr, hook)
+
 def is_in_callback():
     from pypy.rpython.lltypesystem import rffi
     return rffi.stackcounter.stacks_counter > 1
diff --git a/pypy/rlib/rgc.py b/pypy/rlib/rgc.py
--- a/pypy/rlib/rgc.py
+++ b/pypy/rlib/rgc.py
@@ -139,6 +139,52 @@
         hop.exception_cannot_occur()
         return hop.genop(opname, vlist, resulttype = hop.r_result.lowleveltype)
 
+def copy_struct_item(source, dest, si, di):
+    TP = lltype.typeOf(source).TO.OF
+    i = 0
+    while i < len(TP._names):
+        setattr(dest[di], TP._names[i], getattr(source[si], TP._names[i]))
+        i += 1
+
+class CopyStructEntry(ExtRegistryEntry):
+    _about_ = copy_struct_item
+
+    def compute_result_annotation(self, s_source, s_dest, si, di):
+        pass
+
+    def specialize_call(self, hop):
+        v_source, v_dest, v_si, v_di = hop.inputargs(hop.args_r[0],
+                                                     hop.args_r[1],
+                                                     lltype.Signed,
+                                                     lltype.Signed)
+        hop.exception_cannot_occur()
+        TP = v_source.concretetype.TO.OF
+        for name, TP in TP._flds.iteritems():
+            c_name = hop.inputconst(lltype.Void, name)
+            v_fld = hop.genop('getinteriorfield', [v_source, v_si, c_name],
+                              resulttype=TP)
+            hop.genop('setinteriorfield', [v_dest, v_di, c_name, v_fld])
+
+
+ at specialize.ll()
+def copy_item(source, dest, si, di):
+    TP = lltype.typeOf(source)
+    if isinstance(TP.TO.OF, lltype.Struct):
+        copy_struct_item(source, dest, si, di)
+    else:
+        dest[di] = source[si]
+
+ at specialize.memo()
+def _contains_gcptr(TP):
+    if not isinstance(TP, lltype.Struct):
+        if isinstance(TP, lltype.Ptr) and TP.TO._gckind == 'gc':
+            return True
+        return False
+    for TP in TP._flds.itervalues():
+        if _contains_gcptr(TP):
+            return True
+    return False
+
 @jit.oopspec('list.ll_arraycopy(source, dest, source_start, dest_start, length)')
 @enforceargs(None, None, int, int, int)
 @specialize.ll()
@@ -150,7 +196,7 @@
     # and also, maybe, speed up very small cases
     if length <= 1:
         if length == 1:
-            dest[dest_start] = source[source_start]
+            copy_item(source, dest, source_start, dest_start)
         return
 
     # supports non-overlapping copies only
@@ -161,7 +207,7 @@
 
     TP = lltype.typeOf(source).TO
     assert TP == lltype.typeOf(dest).TO
-    if isinstance(TP.OF, lltype.Ptr) and TP.OF.TO._gckind == 'gc':
+    if _contains_gcptr(TP.OF):
         # perform a write barrier that copies necessary flags from
         # source to dest
         if not llop.gc_writebarrier_before_copy(lltype.Bool, source, dest,
@@ -170,7 +216,7 @@
             # if the write barrier is not supported, copy by hand
             i = 0
             while i < length:
-                dest[i + dest_start] = source[i + source_start]
+                copy_item(source, dest, i + source_start, i + dest_start)
                 i += 1
             return
     source_addr = llmemory.cast_ptr_to_adr(source)
diff --git a/pypy/rlib/test/test_rgc.py b/pypy/rlib/test/test_rgc.py
--- a/pypy/rlib/test/test_rgc.py
+++ b/pypy/rlib/test/test_rgc.py
@@ -134,6 +134,46 @@
 
     assert check.called
 
+def test_ll_arraycopy_array_of_structs():
+    TP = lltype.GcArray(lltype.Struct('x', ('x', lltype.Signed),
+                                      ('y', lltype.Signed)))
+    def f():
+        a1 = lltype.malloc(TP, 3)
+        a2 = lltype.malloc(TP, 3)
+        for i in range(3):
+            a1[i].x = 2 * i
+            a1[i].y = 2 * i + 1
+        rgc.ll_arraycopy(a1, a2, 0, 0, 3)
+        for i in range(3):
+            assert a2[i].x == 2 * i
+            assert a2[i].y == 2 * i + 1
+
+
+    interpret(f, [])
+    a1 = lltype.malloc(TP, 3)
+    a2 = lltype.malloc(TP, 3)
+    a1[1].x = 3
+    a1[1].y = 15
+    rgc.copy_struct_item(a1, a2, 1, 2)
+    assert a2[2].x == 3
+    assert a2[2].y == 15
+
+def test__contains_gcptr():
+    assert not rgc._contains_gcptr(lltype.Signed)
+    assert not rgc._contains_gcptr(
+        lltype.Struct('x', ('x', lltype.Signed)))
+    assert rgc._contains_gcptr(
+        lltype.Struct('x', ('x', lltype.Signed),
+                      ('y', lltype.Ptr(lltype.GcArray(lltype.Signed)))))
+    assert rgc._contains_gcptr(
+        lltype.Struct('x', ('x', lltype.Signed),
+                      ('y', llmemory.GCREF)))
+    assert rgc._contains_gcptr(lltype.Ptr(lltype.GcStruct('x')))
+    assert not rgc._contains_gcptr(lltype.Ptr(lltype.Struct('x')))
+    GCPTR = lltype.Ptr(lltype.GcStruct('x'))
+    assert rgc._contains_gcptr(
+        lltype.Struct('FOO', ('s', lltype.Struct('BAR', ('y', GCPTR)))))
+
 def test_ll_arraycopy_small():
     TYPE = lltype.GcArray(lltype.Signed)
     for length in range(5):
diff --git a/pypy/rpython/lltypesystem/rffi.py b/pypy/rpython/lltypesystem/rffi.py
--- a/pypy/rpython/lltypesystem/rffi.py
+++ b/pypy/rpython/lltypesystem/rffi.py
@@ -1,6 +1,6 @@
 import py
 from pypy.annotation import model as annmodel
-from pypy.rpython.lltypesystem import lltype
+from pypy.rpython.lltypesystem import lltype, rstr
 from pypy.rpython.lltypesystem import ll2ctypes
 from pypy.rpython.lltypesystem.llmemory import cast_adr_to_ptr, cast_ptr_to_adr
 from pypy.rpython.lltypesystem.llmemory import itemoffsetof, raw_memcopy
@@ -13,7 +13,7 @@
 from pypy.rlib.unroll import unrolling_iterable
 from pypy.rpython.tool.rfficache import platform, sizeof_c_type
 from pypy.translator.tool.cbuild import ExternalCompilationInfo
-from pypy.rpython.annlowlevel import llhelper
+from pypy.rpython.annlowlevel import llhelper, llstr
 from pypy.rlib.objectmodel import we_are_translated
 from pypy.rlib.rstring import StringBuilder, UnicodeBuilder, assert_str0
 from pypy.rlib import jit
@@ -279,9 +279,18 @@
     callable_name = getattr(callable, '__name__', '?')
     if callbackholder is not None:
         callbackholder.callbacks[callable] = True
+    callable_name_descr = str(callable).replace('"', '\\"')
     args = ', '.join(['a%d' % i for i in range(len(TP.TO.ARGS))])
     source = py.code.Source(r"""
-        def wrapper(%s):    # no *args - no GIL for mallocing the tuple
+        def inner_wrapper(%(args)s):
+            if aroundstate is not None:
+                callback_hook = aroundstate.callback_hook
+                if callback_hook:
+                    callback_hook(llstr("%(callable_name_descr)s"))
+            return callable(%(args)s)
+        inner_wrapper._never_inline_ = True
+        
+        def wrapper(%(args)s):    # no *args - no GIL for mallocing the tuple
             llop.gc_stack_bottom(lltype.Void)   # marker for trackgcroot.py
             if aroundstate is not None:
                 after = aroundstate.after
@@ -290,7 +299,7 @@
             # from now on we hold the GIL
             stackcounter.stacks_counter += 1
             try:
-                result = callable(%s)
+                result = inner_wrapper(%(args)s)
             except Exception, e:
                 os.write(2,
                     "Warning: uncaught exception in callback: %%s %%s\n" %%
@@ -308,10 +317,11 @@
             # by llexternal, it is essential that no exception checking occurs
             # after the call to before().
             return result
-    """ % (args, args))
+    """ % locals())
     miniglobals = locals().copy()
     miniglobals['Exception'] = Exception
     miniglobals['os'] = os
+    miniglobals['llstr'] = llstr
     miniglobals['we_are_translated'] = we_are_translated
     miniglobals['stackcounter'] = stackcounter
     exec source.compile() in miniglobals
@@ -319,10 +329,14 @@
 _make_wrapper_for._annspecialcase_ = 'specialize:memo'
 
 AroundFnPtr = lltype.Ptr(lltype.FuncType([], lltype.Void))
+CallbackHookPtr = lltype.Ptr(lltype.FuncType([lltype.Ptr(rstr.STR)], lltype.Void))
+
 class AroundState:
+    callback_hook = None
+    
     def _cleanup_(self):
-        self.before = None    # or a regular RPython function
-        self.after = None     # or a regular RPython function
+        self.before = None        # or a regular RPython function
+        self.after = None         # or a regular RPython function
 aroundstate = AroundState()
 aroundstate._cleanup_()
 
diff --git a/pypy/rpython/lltypesystem/test/test_lltype.py b/pypy/rpython/lltypesystem/test/test_lltype.py
--- a/pypy/rpython/lltypesystem/test/test_lltype.py
+++ b/pypy/rpython/lltypesystem/test/test_lltype.py
@@ -808,7 +808,6 @@
     assert F.RESULT == Signed
     assert F.ARGS == (Signed,)
 
-
 class TestTrackAllocation:
     def test_automatic_tracking(self):
         # calls to start_tracking_allocations/stop_tracking_allocations
diff --git a/pypy/rpython/memory/gc/minimark.py b/pypy/rpython/memory/gc/minimark.py
--- a/pypy/rpython/memory/gc/minimark.py
+++ b/pypy/rpython/memory/gc/minimark.py
@@ -2000,6 +2000,17 @@
                     (obj + offset).address[0] = llmemory.NULL
                     continue    # no need to remember this weakref any longer
             #
+            elif self.header(pointing_to).tid & GCFLAG_NO_HEAP_PTRS:
+                # see test_weakref_to_prebuilt: it's not useful to put
+                # weakrefs into 'old_objects_with_weakrefs' if they point
+                # to a prebuilt object (they are immortal).  If moreover
+                # the 'pointing_to' prebuilt object still has the
+                # GCFLAG_NO_HEAP_PTRS flag, then it's even wrong, because
+                # 'pointing_to' will not get the GCFLAG_VISITED during
+                # the next major collection.  Solve this by not registering
+                # the weakref into 'old_objects_with_weakrefs'.
+                continue
+            #
             self.old_objects_with_weakrefs.append(obj)
 
     def invalidate_old_weakrefs(self):
@@ -2013,6 +2024,9 @@
                 continue # weakref itself dies
             offset = self.weakpointer_offset(self.get_type_id(obj))
             pointing_to = (obj + offset).address[0]
+            ll_assert((self.header(pointing_to).tid & GCFLAG_NO_HEAP_PTRS)
+                      == 0, "registered old weakref should not "
+                            "point to a NO_HEAP_PTRS obj")
             if self.header(pointing_to).tid & GCFLAG_VISITED:
                 new_with_weakref.append(obj)
             else:
diff --git a/pypy/rpython/memory/test/test_gc.py b/pypy/rpython/memory/test/test_gc.py
--- a/pypy/rpython/memory/test/test_gc.py
+++ b/pypy/rpython/memory/test/test_gc.py
@@ -557,6 +557,19 @@
         res = self.interpret(f, [20])  # for GenerationGC, enough for a minor collection
         assert res == True
 
+    def test_weakref_to_prebuilt(self):
+        import weakref
+        class A:
+            pass
+        a = A()
+        def f(x):
+            ref = weakref.ref(a)
+            assert ref() is a
+            llop.gc__collect(lltype.Void)
+            return ref() is a
+        res = self.interpret(f, [20])  # for GenerationGC, enough for a minor collection
+        assert res == True
+
     def test_many_weakrefs(self):
         # test for the case where allocating the weakref itself triggers
         # a collection
diff --git a/pypy/translator/goal/app_main.py b/pypy/translator/goal/app_main.py
--- a/pypy/translator/goal/app_main.py
+++ b/pypy/translator/goal/app_main.py
@@ -584,7 +584,12 @@
                                                         python_startup,
                                                         'exec')
                             exec co_python_startup in mainmodule.__dict__
+                        mainmodule.__file__ = python_startup
                         run_toplevel(run_it)
+                        try:
+                            del mainmodule.__file__
+                        except (AttributeError, TypeError):
+                            pass
                 # Then we need a prompt.
                 inspect = True
             else:
diff --git a/pypy/translator/goal/targetpypystandalone.py b/pypy/translator/goal/targetpypystandalone.py
--- a/pypy/translator/goal/targetpypystandalone.py
+++ b/pypy/translator/goal/targetpypystandalone.py
@@ -28,6 +28,11 @@
     w_call_startup_gateway = space.wrap(gateway.interp2app(call_startup))
     withjit = space.config.objspace.usemodules.pypyjit
 
+    if withjit:
+        from pypy.module.pypyjit.interp_jit import callback_hook
+        from pypy.rlib import objectmodel
+        objectmodel.register_around_callback_hook(callback_hook)
+
     def entry_point(argv):
         if withjit:
             from pypy.jit.backend.hlinfo import highleveljitinfo
diff --git a/pypy/translator/goal/test2/test_app_main.py b/pypy/translator/goal/test2/test_app_main.py
--- a/pypy/translator/goal/test2/test_app_main.py
+++ b/pypy/translator/goal/test2/test_app_main.py
@@ -376,6 +376,30 @@
         child.expect('Traceback')
         child.expect('NameError')
 
+    def test_pythonstartup_file1(self, monkeypatch):
+        monkeypatch.setenv('PYTHONPATH', None)
+        monkeypatch.setenv('PYTHONSTARTUP', demo_script)
+        child = self.spawn([])
+        child.expect('File: [^\n]+\.py')
+        child.expect('goodbye')
+        child.expect('>>> ')
+        child.sendline('[myvalue]')
+        child.expect(re.escape('[42]'))
+        child.expect('>>> ')
+        child.sendline('__file__')
+        child.expect('Traceback')
+        child.expect('NameError')
+
+    def test_pythonstartup_file2(self, monkeypatch):
+        monkeypatch.setenv('PYTHONPATH', None)
+        monkeypatch.setenv('PYTHONSTARTUP', crashing_demo_script)
+        child = self.spawn([])
+        child.expect('Traceback')
+        child.expect('>>> ')
+        child.sendline('__file__')
+        child.expect('Traceback')
+        child.expect('NameError')
+
     def test_ignore_python_startup(self):
         old = os.environ.get('PYTHONSTARTUP', '')
         try:
diff --git a/pypy/translator/platform/linux.py b/pypy/translator/platform/linux.py
--- a/pypy/translator/platform/linux.py
+++ b/pypy/translator/platform/linux.py
@@ -36,7 +36,13 @@
         # places where we need to look for libffi.a
         # XXX obscuuure!  only look for libffi.a if run with translate.py
         if 'translate' in sys.modules:
-            return self.library_dirs_for_libffi() + ['/usr/lib']
+            if sys.maxint > 2**32:
+                host = 'x86_64'
+            else:
+                host = 'x86'
+            return self.library_dirs_for_libffi() + [
+                '/usr/lib',
+                '/usr/lib/%s-linux-gnu/' % host]
         else:
             return []
 
diff --git a/pypy/translator/platform/test/test_distutils.py b/pypy/translator/platform/test/test_distutils.py
--- a/pypy/translator/platform/test/test_distutils.py
+++ b/pypy/translator/platform/test/test_distutils.py
@@ -8,3 +8,6 @@
 
     def test_nice_errors(self):
         py.test.skip("Unsupported")
+
+    def test_900_files(self):
+        py.test.skip('Makefiles not suppoerted')
diff --git a/pypy/translator/platform/test/test_platform.py b/pypy/translator/platform/test/test_platform.py
--- a/pypy/translator/platform/test/test_platform.py
+++ b/pypy/translator/platform/test/test_platform.py
@@ -59,6 +59,34 @@
         res = self.platform.execute(executable)
         self.check_res(res)
 
+    def test_900_files(self):
+        txt = '#include <stdio.h>\n'
+        for i in range(900):
+            txt += 'int func%03d();\n' % i
+        txt += 'int main() {\n    int j=0;'    
+        for i in range(900):
+            txt += '    j += func%03d();\n' % i
+        txt += '    printf("%d\\n", j);\n'
+        txt += '    return 0;};\n'
+        cfile = udir.join('test_900_files.c')
+        cfile.write(txt)
+        cfiles = [cfile]
+        for i in range(900):
+            cfile2 = udir.join('implement%03d.c' %i)
+            cfile2.write('''
+                int func%03d()
+            {
+                return %d;
+            }
+            ''' % (i, i))
+            cfiles.append(cfile2)
+        mk = self.platform.gen_makefile(cfiles, ExternalCompilationInfo(), path=udir)
+        mk.write()
+        self.platform.execute_makefile(mk)
+        res = self.platform.execute(udir.join('test_900_files'))
+        self.check_res(res, '%d\n' %sum(range(900)))
+
+
     def test_nice_errors(self):
         cfile = udir.join('test_nice_errors.c')
         cfile.write('')
diff --git a/pypy/translator/platform/windows.py b/pypy/translator/platform/windows.py
--- a/pypy/translator/platform/windows.py
+++ b/pypy/translator/platform/windows.py
@@ -326,17 +326,33 @@
 
         for rule in rules:
             m.rule(*rule)
-
+        
+        objects = ' $(OBJECTS)'
+        create_obj_response_file = []
+        if len(' '.join(rel_ofiles)) > 4000:
+            # cmd.exe has a limit of ~4000 characters before a command line is too long.
+            # Use a response file instead, at the cost of making the Makefile very ugly.
+            for i in range(len(rel_ofiles) - 1):
+                create_obj_response_file.append('echo %s >> obj_names.rsp' % \
+                                                rel_ofiles[i])
+            # use cmd /c for the last one so that the file is flushed 
+            create_obj_response_file.append('cmd /c echo %s >> obj_names.rsp' % \
+                                            rel_ofiles[-1])
+            objects = ' @obj_names.rsp'
         if self.version < 80:
             m.rule('$(TARGET)', '$(OBJECTS)',
-                   '$(CC_LINK) /nologo $(LDFLAGS) $(LDFLAGSEXTRA) $(OBJECTS) /out:$@ $(LIBDIRS) $(LIBS)')
+                    create_obj_response_file + [\
+                   '$(CC_LINK) /nologo $(LDFLAGS) $(LDFLAGSEXTRA)' + objects + ' /out:$@ $(LIBDIRS) $(LIBS)',
+                   ])
         else:
             m.rule('$(TARGET)', '$(OBJECTS)',
-                   ['$(CC_LINK) /nologo $(LDFLAGS) $(LDFLAGSEXTRA) $(OBJECTS) $(LINKFILES) /out:$@ $(LIBDIRS) $(LIBS) /MANIFEST /MANIFESTFILE:$*.manifest',
+                    create_obj_response_file + [\
+                    '$(CC_LINK) /nologo $(LDFLAGS) $(LDFLAGSEXTRA)' + objects + ' $(LINKFILES) /out:$@ $(LIBDIRS) $(LIBS) /MANIFEST /MANIFESTFILE:$*.manifest',
                     'mt.exe -nologo -manifest $*.manifest -outputresource:$@;1',
                     ])
         m.rule('debugmode_$(TARGET)', '$(OBJECTS)',
-               ['$(CC_LINK) /nologo /DEBUG $(LDFLAGS) $(LDFLAGSEXTRA) $(OBJECTS) $(LINKFILES) /out:$@ $(LIBDIRS) $(LIBS)',
+                create_obj_response_file + [\
+               '$(CC_LINK) /nologo /DEBUG $(LDFLAGS) $(LDFLAGSEXTRA)' + objects + ' $(LINKFILES) /out:$@ $(LIBDIRS) $(LIBS)',
                 ])
 
         if shared:


More information about the pypy-commit mailing list