Commits

Armin Rigo  committed 94d415e

Backout the merge of stacklet. It seems to give nonsense in non-jitted
translated versions...

  • Participants
  • Parent commits 14f1521

Comments (0)

Files changed (116)

File lib_pypy/greenlet.py

-import _continuation, sys
-
-
-# ____________________________________________________________
-# Exceptions
-
-class GreenletExit(Exception):
-    """This special exception does not propagate to the parent greenlet; it
-can be used to kill a single greenlet."""
-
-error = _continuation.error
-
-# ____________________________________________________________
-# Helper function
-
-def getcurrent():
-    "Returns the current greenlet (i.e. the one which called this function)."
-    try:
-        return _tls.current
-    except AttributeError:
-        # first call in this thread: current == main
-        _green_create_main()
-        return _tls.current
-
-# ____________________________________________________________
-# The 'greenlet' class
-
-_continulet = _continuation.continulet
-
-class greenlet(_continulet):
-    getcurrent = staticmethod(getcurrent)
-    error = error
-    GreenletExit = GreenletExit
-    __main = False
-    __started = False
-
-    def __new__(cls, *args, **kwds):
-        self = _continulet.__new__(cls)
-        self.parent = getcurrent()
-        return self
-
-    def __init__(self, run=None, parent=None):
-        if run is not None:
-            self.run = run
-        if parent is not None:
-            self.parent = parent
-
-    def switch(self, *args):
-        "Switch execution to this greenlet, optionally passing the values "
-        "given as argument(s).  Returns the value passed when switching back."
-        return self.__switch(_continulet.switch, args)
-
-    def throw(self, typ=GreenletExit, val=None, tb=None):
-        "raise exception in greenlet, return value passed when switching back"
-        return self.__switch(_continulet.throw, typ, val, tb)
-
-    def __switch(target, unbound_method, *args):
-        current = getcurrent()
-        #
-        while not target:
-            if not target.__started:
-                _continulet.__init__(target, _greenlet_start, *args)
-                args = ()
-                target.__started = True
-                break
-            # already done, go to the parent instead
-            # (NB. infinite loop possible, but unlikely, unless you mess
-            # up the 'parent' explicitly.  Good enough, because a Ctrl-C
-            # will show that the program is caught in this loop here.)
-            target = target.parent
-        #
-        try:
-            if current.__main:
-                if target.__main:
-                    # switch from main to main
-                    if unbound_method == _continulet.throw:
-                        raise args[0], args[1], args[2]
-                    (args,) = args
-                else:
-                    # enter from main to target
-                    args = unbound_method(target, *args)
-            else:
-                if target.__main:
-                    # leave to go to target=main
-                    args = unbound_method(current, *args)
-                else:
-                    # switch from non-main to non-main
-                    args = unbound_method(current, *args, to=target)
-        except GreenletExit, e:
-            args = (e,)
-        finally:
-            _tls.current = current
-        #
-        if len(args) == 1:
-            return args[0]
-        else:
-            return args
-
-    def __nonzero__(self):
-        return self.__main or _continulet.is_pending(self)
-
-    @property
-    def dead(self):
-        return self.__started and not self
-
-    @property
-    def gr_frame(self):
-        raise NotImplementedError("attribute 'gr_frame' of greenlet objects")
-
-# ____________________________________________________________
-# Internal stuff
-
-try:
-    from thread import _local
-except ImportError:
-    class _local(object):    # assume no threads
-        pass
-
-_tls = _local()
-
-def _green_create_main():
-    # create the main greenlet for this thread
-    _tls.current = None
-    gmain = greenlet.__new__(greenlet)
-    gmain._greenlet__main = True
-    gmain._greenlet__started = True
-    assert gmain.parent is None
-    _tls.main = gmain
-    _tls.current = gmain
-
-def _greenlet_start(greenlet, args):
-    _tls.current = greenlet
-    try:
-        res = greenlet.run(*args)
-    finally:
-        if greenlet.parent is not _tls.main:
-            _continuation.permute(greenlet, greenlet.parent)
-    return (res,)
+from _stackless import greenlet

File pypy/config/pypyoption.py

      "struct", "_hashlib", "_md5", "_sha", "_minimal_curses", "cStringIO",
      "thread", "itertools", "pyexpat", "_ssl", "cpyext", "array",
      "_bisect", "binascii", "_multiprocessing", '_warnings',
-     "_collections", "_multibytecodec", "micronumpy", "_ffi",
-     "_continuation"]
+     "_collections", "_multibytecodec", "micronumpy", "_ffi"]
 ))
 
 translation_modules = default_modules.copy()
     "_ssl"      : ["pypy.module._ssl.interp_ssl"],
     "_hashlib"  : ["pypy.module._ssl.interp_ssl"],
     "_minimal_curses": ["pypy.module._minimal_curses.fficurses"],
-    "_continuation": ["pypy.rlib.rstacklet"],
     }
 
 def get_module_validator(modname):

File pypy/config/translationoption.py

 
 translation_optiondescription = OptionDescription(
         "translation", "Translation Options", [
-    BoolOption("continuation", "enable single-shot continuations",
-               default=False, cmdline="--continuation",
-               requires=[("translation.type_system", "lltype")]),
+    BoolOption("stackless", "enable stackless features during compilation",
+               default=False, cmdline="--stackless",
+               requires=[("translation.type_system", "lltype"),
+                         ("translation.gcremovetypeptr", False)]),  # XXX?
     ChoiceOption("type_system", "Type system to use when RTyping",
                  ["lltype", "ootype"], cmdline=None, default="lltype",
                  requires={
                      "statistics": [("translation.gctransformer", "framework")],
                      "generation": [("translation.gctransformer", "framework")],
                      "hybrid": [("translation.gctransformer", "framework")],
-                     "boehm": [("translation.gctransformer", "boehm"),
-                               ("translation.continuation", False)],  # breaks
+                     "boehm": [("translation.gctransformer", "boehm")],
                      "markcompact": [("translation.gctransformer", "framework")],
                      "minimark": [("translation.gctransformer", "framework")],
                      },
             config.translation.suggest(withsmallfuncsets=5)
         elif word == 'jit':
             config.translation.suggest(jit=True)
+            if config.translation.stackless:
+                raise NotImplementedError("JIT conflicts with stackless for now")
         elif word == 'removetypeptr':
             config.translation.suggest(gcremovetypeptr=True)
         else:

File pypy/doc/config/objspace.usemodules._continuation.txt

-Use the '_continuation' module. 
-
-Exposes the `continulet` app-level primitives.
-See also :config:`translation.continuation`.

File pypy/doc/config/objspace.usemodules._stackless.txt

-Deprecated.
+Use the '_stackless' module. 
+
+Exposes the `stackless` primitives, and also implies a stackless build. 
+See also :config:`translation.stackless`.
+
+.. _`stackless`: ../stackless.html

File pypy/doc/config/translation.continuation.txt

-Enable the use of a stackless-like primitive called "stacklet".
-In PyPy, this is exposed at app-level by the "_continuation" module.

File pypy/doc/config/translation.stackless.txt

+Run the `stackless transform`_ on each generated graph, which enables the use
+of coroutines at RPython level and the "stackless" module when translating
+PyPy.
+
+.. _`stackless transform`: ../stackless.html

File pypy/interpreter/test/test_gateway.py

 class TestPassThroughArguments_CALL_METHOD(TestPassThroughArguments):
 
     def setup_class(cls):
-        space = gettestobjspace(usemodules=('itertools',), **{
+        space = gettestobjspace(usemodules=('_stackless',), **{
             "objspace.opcodes.CALL_METHOD": True
             })
         cls.space = space

File pypy/jit/backend/llgraph/runner.py

         token = history.getkind(getattr(S, fieldname))
         return self.getdescr(ofs, token[0], name=fieldname)
 
-    def calldescrof(self, FUNC, ARGS, RESULT, extrainfo):
+    def calldescrof(self, FUNC, ARGS, RESULT, extrainfo=None):
         arg_types = []
         for ARG in ARGS:
             token = history.getkind(ARG)
         return self.getdescr(0, token[0], extrainfo=extrainfo,
                              arg_types=''.join(arg_types))
 
-    def calldescrof_dynamic(self, ffi_args, ffi_result, extrainfo):
+    def calldescrof_dynamic(self, ffi_args, ffi_result, extrainfo=None):
         from pypy.jit.backend.llsupport.ffisupport import get_ffi_type_kind
         from pypy.jit.backend.llsupport.ffisupport import UnsupportedKind
         arg_types = []
         return FieldDescr.new(T1, fieldname)
 
     @staticmethod
-    def calldescrof(FUNC, ARGS, RESULT, extrainfo):
+    def calldescrof(FUNC, ARGS, RESULT, extrainfo=None):
         return StaticMethDescr.new(FUNC, ARGS, RESULT, extrainfo)
 
     @staticmethod

File pypy/jit/backend/llsupport/gc.py

 
     def add_jit2gc_hooks(self, jit2gc):
         #
-        # ---------------
-        # This is used to enumerate the shadowstack in the presence
-        # of the JIT.  It is also used by the stacklet support in
-        # rlib/_stacklet_shadowstack.  That's why it is written as
-        # an iterator that can also be used with a custom_trace.
+        def collect_jit_stack_root(callback, gc, addr):
+            if addr.signed[0] != GcRootMap_shadowstack.MARKER:
+                # common case
+                if gc.points_to_valid_gc_object(addr):
+                    callback(gc, addr)
+                return WORD
+            else:
+                # case of a MARKER followed by an assembler stack frame
+                follow_stack_frame_of_assembler(callback, gc, addr)
+                return 2 * WORD
         #
-        class RootIterator:
-            _alloc_flavor_ = "raw"
-
-            def next(iself, gc, next, range_highest):
-                # Return the "next" valid GC object' address.  This usually
-                # means just returning "next", until we reach "range_highest",
-                # except that we are skipping NULLs.  If "next" contains a
-                # MARKER instead, then we go into JIT-frame-lookup mode.
-                #
-                while True:
-                    #
-                    # If we are not iterating right now in a JIT frame
-                    if iself.frame_addr == 0:
-                        #
-                        # Look for the next shadowstack address that
-                        # contains a valid pointer
-                        while next != range_highest:
-                            if next.signed[0] == self.MARKER:
-                                break
-                            if gc.points_to_valid_gc_object(next):
-                                return next
-                            next += llmemory.sizeof(llmemory.Address)
-                        else:
-                            return llmemory.NULL     # done
-                        #
-                        # It's a JIT frame.  Save away 'next' for later, and
-                        # go into JIT-frame-exploring mode.
-                        next += llmemory.sizeof(llmemory.Address)
-                        frame_addr = next.signed[0]
-                        iself.saved_next = next
-                        iself.frame_addr = frame_addr
-                        addr = llmemory.cast_int_to_adr(frame_addr +
-                                                        self.force_index_ofs)
-                        addr = iself.translateptr(iself.context, addr)
-                        force_index = addr.signed[0]
-                        if force_index < 0:
-                            force_index = ~force_index
-                        # NB: the next line reads a still-alive _callshapes,
-                        # because we ensure that just before we called this
-                        # piece of assembler, we put on the (same) stack a
-                        # pointer to a loop_token that keeps the force_index
-                        # alive.
-                        callshape = self._callshapes[force_index]
-                    else:
-                        # Continuing to explore this JIT frame
-                        callshape = iself.callshape
-                    #
-                    # 'callshape' points to the next INT of the callshape.
-                    # If it's zero we are done with the JIT frame.
-                    while rffi.cast(lltype.Signed, callshape[0]) != 0:
-                        #
-                        # Non-zero: it's an offset inside the JIT frame.
-                        # Read it and increment 'callshape'.
-                        offset = rffi.cast(lltype.Signed, callshape[0])
-                        callshape = lltype.direct_ptradd(callshape, 1)
-                        addr = llmemory.cast_int_to_adr(iself.frame_addr +
-                                                        offset)
-                        addr = iself.translateptr(iself.context, addr)
-                        if gc.points_to_valid_gc_object(addr):
-                            #
-                            # The JIT frame contains a valid GC pointer at
-                            # this address (as opposed to NULL).  Save
-                            # 'callshape' for the next call, and return the
-                            # address.
-                            iself.callshape = callshape
-                            return addr
-                    #
-                    # Restore 'prev' and loop back to the start.
-                    iself.frame_addr = 0
-                    next = iself.saved_next
-                    next += llmemory.sizeof(llmemory.Address)
-
-        # ---------------
+        def follow_stack_frame_of_assembler(callback, gc, addr):
+            frame_addr = addr.signed[1]
+            addr = llmemory.cast_int_to_adr(frame_addr + self.force_index_ofs)
+            force_index = addr.signed[0]
+            if force_index < 0:
+                force_index = ~force_index
+            callshape = self._callshapes[force_index]
+            n = 0
+            while True:
+                offset = rffi.cast(lltype.Signed, callshape[n])
+                if offset == 0:
+                    break
+                addr = llmemory.cast_int_to_adr(frame_addr + offset)
+                if gc.points_to_valid_gc_object(addr):
+                    callback(gc, addr)
+                n += 1
         #
-        root_iterator = RootIterator()
-        root_iterator.frame_addr = 0
-        root_iterator.context = llmemory.NULL
-        root_iterator.translateptr = lambda context, addr: addr
         jit2gc.update({
-            'root_iterator': root_iterator,
+            'rootstackhook': collect_jit_stack_root,
             })
 
     def initialize(self):
             has_finalizer = bool(tid & (1<<llgroup.HALFSHIFT))
             check_typeid(type_id)
             res = llop1.do_malloc_fixedsize_clear(llmemory.GCREF,
-                                                  type_id, size,
+                                                  type_id, size, True,
                                                   has_finalizer, False)
             # In case the operation above failed, we are returning NULL
             # from this function to assembler.  There is also an RPython
             return llop1.do_malloc_varsize_clear(
                 llmemory.GCREF,
                 type_id, num_elem, self.array_basesize, itemsize,
-                self.array_length_ofs)
+                self.array_length_ofs, True)
         self.malloc_array = malloc_array
         self.GC_MALLOC_ARRAY = lltype.Ptr(lltype.FuncType(
             [lltype.Signed] * 3, llmemory.GCREF))
             return llop1.do_malloc_varsize_clear(
                 llmemory.GCREF,
                 str_type_id, length, str_basesize, str_itemsize,
-                str_ofs_length)
+                str_ofs_length, True)
         def malloc_unicode(length):
             return llop1.do_malloc_varsize_clear(
                 llmemory.GCREF,
                 unicode_type_id, length, unicode_basesize,unicode_itemsize,
-                unicode_ofs_length)
+                unicode_ofs_length, True)
         self.malloc_str = malloc_str
         self.malloc_unicode = malloc_unicode
         self.GC_MALLOC_STR_UNICODE = lltype.Ptr(lltype.FuncType(
             # also use it to allocate varsized objects.  The tid
             # and possibly the length are both set afterward.
             gcref = llop1.do_malloc_fixedsize_clear(llmemory.GCREF,
-                                        0, size, False, False)
+                                        0, size, True, False, False)
             return rffi.cast(lltype.Signed, gcref)
         self.malloc_slowpath = malloc_slowpath
         self.MALLOC_SLOWPATH = lltype.FuncType([lltype.Signed], lltype.Signed)

File pypy/jit/backend/llsupport/llmodel.py

         return ofs, size, sign
     unpack_arraydescr_size._always_inline_ = True
 
-    def calldescrof(self, FUNC, ARGS, RESULT, extrainfo):
+    def calldescrof(self, FUNC, ARGS, RESULT, extrainfo=None):
         return get_call_descr(self.gc_ll_descr, ARGS, RESULT, extrainfo)
 
-    def calldescrof_dynamic(self, ffi_args, ffi_result, extrainfo):
+    def calldescrof_dynamic(self, ffi_args, ffi_result, extrainfo=None):
         from pypy.jit.backend.llsupport import ffisupport
         return ffisupport.get_call_descr_dynamic(self, ffi_args, ffi_result,
                                                  extrainfo)

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

     def __init__(self):
         self.record = []
 
-    def do_malloc_fixedsize_clear(self, RESTYPE, type_id, size,
+    def do_malloc_fixedsize_clear(self, RESTYPE, type_id, size, can_collect,
                                   has_finalizer, contains_weakptr):
+        assert can_collect
         assert not contains_weakptr
         p = llmemory.raw_malloc(size)
         p = llmemory.cast_adr_to_ptr(p, RESTYPE)
         return p
 
     def do_malloc_varsize_clear(self, RESTYPE, type_id, length, size,
-                                itemsize, offset_to_length):
+                                itemsize, offset_to_length, can_collect):
+        assert can_collect
         p = llmemory.raw_malloc(size + itemsize * length)
         (p + offset_to_length).signed[0] = length
         p = llmemory.cast_adr_to_ptr(p, RESTYPE)

File pypy/jit/backend/test/calling_convention_test.py

                                          ConstObj, BoxFloat, ConstFloat)
 from pypy.jit.metainterp.resoperation import ResOperation, rop
 from pypy.jit.metainterp.typesystem import deref
-from pypy.jit.codewriter.effectinfo import EffectInfo
 from pypy.jit.tool.oparser import parse
 from pypy.rpython.lltypesystem import lltype, llmemory, rstr, rffi, rclass
 from pypy.rpython.ootypesystem import ootype
             FUNC = self.FuncType(funcargs, F)
             FPTR = self.Ptr(FUNC)
             func_ptr = llhelper(FPTR, func)
-            calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                        EffectInfo.MOST_GENERAL)
+            calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
             funcbox = self.get_funcbox(cpu, func_ptr)
 
             ops = '[%s]\n' % arguments
             FUNC = self.FuncType(args, F)
             FPTR = self.Ptr(FUNC)
             func_ptr = llhelper(FPTR, func)
-            calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                        EffectInfo.MOST_GENERAL)
+            calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
             funcbox = self.get_funcbox(cpu, func_ptr)
 
             res = self.execute_operation(rop.CALL,
             FUNC = self.FuncType(args, F)
             FPTR = self.Ptr(FUNC)
             func_ptr = llhelper(FPTR, func)
-            calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                        EffectInfo.MOST_GENERAL)
+            calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
             funcbox = self.get_funcbox(cpu, func_ptr)
 
             res = self.execute_operation(rop.CALL,
                 else:
                     ARGS.append(lltype.Signed)
             FakeJitDriverSD.portal_calldescr = self.cpu.calldescrof(
-                lltype.Ptr(lltype.FuncType(ARGS, RES)), ARGS, RES,
-                EffectInfo.MOST_GENERAL)
+                lltype.Ptr(lltype.FuncType(ARGS, RES)), ARGS, RES)
             ops = '''
             [%s]
             f99 = call_assembler(%s, descr=called_looptoken)
             FUNC = self.FuncType(args, F)
             FPTR = self.Ptr(FUNC)
             func_ptr = llhelper(FPTR, func)
-            calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                        EffectInfo.MOST_GENERAL)
+            calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
             funcbox = self.get_funcbox(cpu, func_ptr)
 
             res = self.execute_operation(rop.CALL,

File pypy/jit/backend/test/runner_test.py

                                          ConstObj, BoxFloat, ConstFloat)
 from pypy.jit.metainterp.resoperation import ResOperation, rop
 from pypy.jit.metainterp.typesystem import deref
-from pypy.jit.codewriter.effectinfo import EffectInfo
 from pypy.jit.tool.oparser import parse
 from pypy.rpython.lltypesystem import lltype, llmemory, rstr, rffi, rclass
 from pypy.rpython.ootypesystem import ootype
             return chr(ord(c) + 1)
         FPTR = self.Ptr(self.FuncType([lltype.Char], lltype.Char))
         func_ptr = llhelper(FPTR, func)
-        calldescr = cpu.calldescrof(deref(FPTR), (lltype.Char,), lltype.Char,
-                                    EffectInfo.MOST_GENERAL)
+        calldescr = cpu.calldescrof(deref(FPTR), (lltype.Char,), lltype.Char)
         x = cpu.bh_call_i(self.get_funcbox(cpu, func_ptr).value,
                           calldescr, [ord('A')], None, None)
         assert x == ord('B')
                                           lltype.Float))
             func_ptr = llhelper(FPTR, func)
             FTP = deref(FPTR)
-            calldescr = cpu.calldescrof(FTP, FTP.ARGS, FTP.RESULT,
-                                        EffectInfo.MOST_GENERAL)
+            calldescr = cpu.calldescrof(FTP, FTP.ARGS, FTP.RESULT)
             x = cpu.bh_call_f(self.get_funcbox(cpu, func_ptr).value,
                               calldescr,
                               [42], None, [longlong.getfloatstorage(3.5)])
             FUNC = deref(FPTR)
             funcbox = self.get_funcbox(cpu, func_ptr)
             # first, try it with the "normal" calldescr
-            calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                        EffectInfo.MOST_GENERAL)
+            calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
             res = self.execute_operation(rop.CALL,
                                          [funcbox, BoxInt(num), BoxInt(num)],
                                          'int', descr=calldescr)
             assert res.value == 2 * num
             # then, try it with the dynamic calldescr
-            dyn_calldescr = cpu.calldescrof_dynamic([ffi_type, ffi_type], ffi_type,
-                                                    EffectInfo.MOST_GENERAL)
+            dyn_calldescr = cpu.calldescrof_dynamic([ffi_type, ffi_type], ffi_type)
             res = self.execute_operation(rop.CALL,
                                          [funcbox, BoxInt(num), BoxInt(num)],
                                          'int', descr=dyn_calldescr)
             FUNC = self.FuncType([F] * 7 + [I] * 2 + [F] * 3, F)
             FPTR = self.Ptr(FUNC)
             func_ptr = llhelper(FPTR, func)
-            calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                        EffectInfo.MOST_GENERAL)
+            calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
             funcbox = self.get_funcbox(cpu, func_ptr)
             args = ([boxfloat(.1) for i in range(7)] +
                     [BoxInt(1), BoxInt(2), boxfloat(.2), boxfloat(.3),
 
         FUNC = self.FuncType([lltype.Signed]*16, lltype.Signed)
         FPTR = self.Ptr(FUNC)
-        calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                         EffectInfo.MOST_GENERAL)
+        calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
         func_ptr = llhelper(FPTR, func)
         args = range(16)
         funcbox = self.get_funcbox(self.cpu, func_ptr)
             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,
-                                        EffectInfo.MOST_GENERAL)
+            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,
 
         FUNC = self.FuncType([lltype.Float, lltype.Float], lltype.Float)
         FPTR = self.Ptr(FUNC)
-        calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                         EffectInfo.MOST_GENERAL)
+        calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
         func_ptr = llhelper(FPTR, func)
         funcbox = self.get_funcbox(self.cpu, func_ptr)
         res = self.execute_operation(rop.CALL, [funcbox, constfloat(1.5),
         '''
         FPTR = lltype.Ptr(lltype.FuncType([lltype.Signed], lltype.Void))
         fptr = llhelper(FPTR, func)
-        calldescr = self.cpu.calldescrof(FPTR.TO, FPTR.TO.ARGS, FPTR.TO.RESULT,
-                                         EffectInfo.MOST_GENERAL)
+        calldescr = self.cpu.calldescrof(FPTR.TO, FPTR.TO.ARGS, FPTR.TO.RESULT)
 
         xtp = lltype.malloc(rclass.OBJECT_VTABLE, immortal=True)
         xtp.subclassrange_min = 1
         FUNC = self.FuncType([lltype.Signed, lltype.Signed], lltype.Void)
         func_ptr = llhelper(lltype.Ptr(FUNC), maybe_force)
         funcbox = self.get_funcbox(self.cpu, func_ptr).constbox()
-        calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                         EffectInfo.MOST_GENERAL)
+        calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
         cpu = self.cpu
         i0 = BoxInt()
         i1 = BoxInt()
         FUNC = self.FuncType([lltype.Signed, lltype.Signed], lltype.Signed)
         func_ptr = llhelper(lltype.Ptr(FUNC), maybe_force)
         funcbox = self.get_funcbox(self.cpu, func_ptr).constbox()
-        calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                         EffectInfo.MOST_GENERAL)
+        calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
         cpu = self.cpu
         i0 = BoxInt()
         i1 = BoxInt()
         FUNC = self.FuncType([lltype.Signed, lltype.Signed], lltype.Float)
         func_ptr = llhelper(lltype.Ptr(FUNC), maybe_force)
         funcbox = self.get_funcbox(self.cpu, func_ptr).constbox()
-        calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                         EffectInfo.MOST_GENERAL)
+        calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
         cpu = self.cpu
         i0 = BoxInt()
         i1 = BoxInt()
         cpu = self.cpu
         func_adr = llmemory.cast_ptr_to_adr(c_tolower.funcsym)
         funcbox = ConstInt(heaptracker.adr2int(func_adr))
-        calldescr = cpu.calldescrof_dynamic([types.uchar], types.sint,
-                                            EffectInfo.MOST_GENERAL)
+        calldescr = cpu.calldescrof_dynamic([types.uchar], types.sint)
         i1 = BoxInt()
         i2 = BoxInt()
         tok = BoxInt()
         funcbox = ConstInt(heaptracker.adr2int(func_adr))
         calldescr = cpu.calldescrof_dynamic([types.pointer, types_size_t,
                                              types_size_t, types.pointer],
-                                            types.void,
-                                            EffectInfo.MOST_GENERAL)
+                                            types.void)
         i0 = BoxInt()
         i1 = BoxInt()
         i2 = BoxInt()
         ARGS = [lltype.Signed] * 10
         RES = lltype.Signed
         FakeJitDriverSD.portal_calldescr = self.cpu.calldescrof(
-            lltype.Ptr(lltype.FuncType(ARGS, RES)), ARGS, RES,
-            EffectInfo.MOST_GENERAL)
+            lltype.Ptr(lltype.FuncType(ARGS, RES)), ARGS, RES)
         for i in range(10):
             self.cpu.set_future_value_int(i, i+1)
         res = self.cpu.execute_token(looptoken)
         ARGS = [lltype.Float, lltype.Float]
         RES = lltype.Float
         FakeJitDriverSD.portal_calldescr = self.cpu.calldescrof(
-            lltype.Ptr(lltype.FuncType(ARGS, RES)), ARGS, RES,
-            EffectInfo.MOST_GENERAL)
+            lltype.Ptr(lltype.FuncType(ARGS, RES)), ARGS, RES)
         
         ops = '''
         [f0, f1]
         ARGS = [lltype.Float, lltype.Float]
         RES = lltype.Float
         FakeJitDriverSD.portal_calldescr = self.cpu.calldescrof(
-            lltype.Ptr(lltype.FuncType(ARGS, RES)), ARGS, RES,
-            EffectInfo.MOST_GENERAL)
+            lltype.Ptr(lltype.FuncType(ARGS, RES)), ARGS, RES)
         
         ops = '''
         [f0, f1]
             #
             FUNC = self.FuncType([lltype.Signed], RESTYPE)
             FPTR = self.Ptr(FUNC)
-            calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                             EffectInfo.MOST_GENERAL)
+            calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
             x = self.cpu.bh_call_i(self.get_funcbox(self.cpu, f).value,
                                    calldescr, [value], None, None)
             assert x == expected, (
             #
             FUNC = self.FuncType([lltype.Signed], RESTYPE)
             FPTR = self.Ptr(FUNC)
-            calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                             EffectInfo.MOST_GENERAL)
+            calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
             funcbox = self.get_funcbox(self.cpu, f)
             res = self.execute_operation(rop.CALL, [funcbox, BoxInt(value)],
                                          'int', descr=calldescr)
         #
         FUNC = self.FuncType([lltype.SignedLongLong], lltype.SignedLongLong)
         FPTR = self.Ptr(FUNC)
-        calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                         EffectInfo.MOST_GENERAL)
+        calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
         x = self.cpu.bh_call_f(self.get_funcbox(self.cpu, f).value,
                                calldescr, None, None, [value])
         assert x == expected
         #
         FUNC = self.FuncType([lltype.SignedLongLong], lltype.SignedLongLong)
         FPTR = self.Ptr(FUNC)
-        calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                         EffectInfo.MOST_GENERAL)
+        calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
         funcbox = self.get_funcbox(self.cpu, f)
         res = self.execute_operation(rop.CALL, [funcbox, BoxFloat(value)],
                                      'float', descr=calldescr)
         #
         FUNC = self.FuncType([lltype.SingleFloat], lltype.SingleFloat)
         FPTR = self.Ptr(FUNC)
-        calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                         EffectInfo.MOST_GENERAL)
+        calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
         ivalue = longlong.singlefloat2int(value)
         iexpected = longlong.singlefloat2int(expected)
         x = self.cpu.bh_call_i(self.get_funcbox(self.cpu, f).value,
         #
         FUNC = self.FuncType([lltype.SingleFloat], lltype.SingleFloat)
         FPTR = self.Ptr(FUNC)
-        calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                         EffectInfo.MOST_GENERAL)
+        calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
         funcbox = self.get_funcbox(self.cpu, f)
         ivalue = longlong.singlefloat2int(value)
         iexpected = longlong.singlefloat2int(expected)

File pypy/jit/backend/test/test_ll_random.py

 from pypy.jit.metainterp.history import BoxPtr, BoxInt
 from pypy.jit.metainterp.history import BasicFailDescr
 from pypy.jit.codewriter import heaptracker
-from pypy.jit.codewriter.effectinfo import EffectInfo
 from pypy.rpython.annlowlevel import llhelper
 from pypy.rlib.rarithmetic import intmask
 from pypy.rpython.llinterp import LLException
         exec code in d
         return subset, d['f'], vtableptr
 
-    def getcalldescr(self, builder, TP):
-        ef = EffectInfo.MOST_GENERAL
-        return builder.cpu.calldescrof(TP, TP.ARGS, TP.RESULT, ef)
-
 # 1. non raising call and guard_no_exception
 class CallOperation(BaseCallOperation):
     def produce_into(self, builder, r):
         ptr = llhelper(lltype.Ptr(TP), f)
         c_addr = ConstAddr(llmemory.cast_ptr_to_adr(ptr), builder.cpu)
         args = [c_addr] + subset
-        descr = self.getcalldescr(builder, TP)
+        descr = builder.cpu.calldescrof(TP, TP.ARGS, TP.RESULT)
         self.put(builder, args, descr)
         op = ResOperation(rop.GUARD_NO_EXCEPTION, [], None,
                           descr=BasicFailDescr())
         ptr = llhelper(lltype.Ptr(TP), f)
         c_addr = ConstAddr(llmemory.cast_ptr_to_adr(ptr), builder.cpu)
         args = [c_addr] + subset
-        descr = self.getcalldescr(builder, TP)
+        descr = builder.cpu.calldescrof(TP, TP.ARGS, TP.RESULT)
         self.put(builder, args, descr)
         _, vtableptr = builder.get_random_structure_type_and_vtable(r)
         exc_box = ConstAddr(llmemory.cast_ptr_to_adr(vtableptr), builder.cpu)
         ptr = llhelper(lltype.Ptr(TP), f)
         c_addr = ConstAddr(llmemory.cast_ptr_to_adr(ptr), builder.cpu)
         args = [c_addr] + subset
-        descr = self.getcalldescr(builder, TP)
+        descr = builder.cpu.calldescrof(TP, TP.ARGS, TP.RESULT)
         self.put(builder, args, descr)
         exc_box = ConstAddr(llmemory.cast_ptr_to_adr(exc), builder.cpu)
         op = ResOperation(rop.GUARD_EXCEPTION, [exc_box], BoxPtr(),
         ptr = llhelper(lltype.Ptr(TP), f)
         c_addr = ConstAddr(llmemory.cast_ptr_to_adr(ptr), builder.cpu)
         args = [c_addr] + subset
-        descr = self.getcalldescr(builder, TP)
+        descr = builder.cpu.calldescrof(TP, TP.ARGS, TP.RESULT)
         self.put(builder, args, descr)
         op = ResOperation(rop.GUARD_NO_EXCEPTION, [], BoxPtr(),
                           descr=BasicFailDescr())
         ptr = llhelper(lltype.Ptr(TP), f)
         c_addr = ConstAddr(llmemory.cast_ptr_to_adr(ptr), builder.cpu)
         args = [c_addr] + subset
-        descr = self.getcalldescr(builder, TP)
+        descr = builder.cpu.calldescrof(TP, TP.ARGS, TP.RESULT)
         self.put(builder, args, descr)
         while True:
             _, vtableptr = builder.get_random_structure_type_and_vtable(r)

File pypy/jit/backend/x86/regalloc.py

 
     def consider_call(self, op):
         effectinfo = op.getdescr().get_extra_info()
-        oopspecindex = effectinfo.oopspecindex
-        if oopspecindex != EffectInfo.OS_NONE:
+        if effectinfo is not None:
+            oopspecindex = effectinfo.oopspecindex
             if IS_X86_32:
                 # support for some of the llong operations,
                 # which only exist on x86-32

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

      BoxPtr, ConstPtr, TreeLoop
 from pypy.jit.metainterp.resoperation import rop, ResOperation
 from pypy.jit.codewriter import heaptracker
-from pypy.jit.codewriter.effectinfo import EffectInfo
 from pypy.jit.backend.llsupport.descr import GcCache
 from pypy.jit.backend.llsupport.gc import GcLLDescription
 from pypy.jit.backend.detect_cpu import getcpuclass
         for box in boxes:
             regalloc.rm.try_allocate_reg(box)
         TP = lltype.FuncType([], lltype.Signed)
-        calldescr = cpu.calldescrof(TP, TP.ARGS, TP.RESULT,
-                                    EffectInfo.MOST_GENERAL)
+        calldescr = cpu.calldescrof(TP, TP.ARGS, TP.RESULT)
         regalloc.rm._check_invariants()
         box = boxes[0]
         regalloc.position = 0

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

 from pypy.rpython.annlowlevel import llhelper
 from pypy.rpython.lltypesystem import rclass, rstr
 from pypy.jit.codewriter import longlong
-from pypy.jit.codewriter.effectinfo import EffectInfo
 from pypy.jit.backend.x86.rx86 import *
 
 def test_is_comparison_or_ovf_op():
     zd_addr = cpu.cast_int_to_adr(zero_division_tp)
     zero_division_error = llmemory.cast_adr_to_ptr(zd_addr,
                                             lltype.Ptr(rclass.OBJECT_VTABLE))
-    raising_calldescr = cpu.calldescrof(FPTR.TO, FPTR.TO.ARGS, FPTR.TO.RESULT,
-                                        EffectInfo.MOST_GENERAL)
+    raising_calldescr = cpu.calldescrof(FPTR.TO, FPTR.TO.ARGS, FPTR.TO.RESULT)
 
     fdescr1 = BasicFailDescr(1)
     fdescr2 = BasicFailDescr(2)
     f2ptr = llhelper(F2PTR, f2)
     f10ptr = llhelper(F10PTR, f10)
 
-    f1_calldescr = cpu.calldescrof(F1PTR.TO, F1PTR.TO.ARGS, F1PTR.TO.RESULT,
-                                   EffectInfo.MOST_GENERAL)
-    f2_calldescr = cpu.calldescrof(F2PTR.TO, F2PTR.TO.ARGS, F2PTR.TO.RESULT,
-                                   EffectInfo.MOST_GENERAL)
-    f10_calldescr= cpu.calldescrof(F10PTR.TO, F10PTR.TO.ARGS, F10PTR.TO.RESULT,
-                                   EffectInfo.MOST_GENERAL)
+    f1_calldescr = cpu.calldescrof(F1PTR.TO, F1PTR.TO.ARGS, F1PTR.TO.RESULT)
+    f2_calldescr = cpu.calldescrof(F2PTR.TO, F2PTR.TO.ARGS, F2PTR.TO.RESULT)
+    f10_calldescr = cpu.calldescrof(F10PTR.TO, F10PTR.TO.ARGS, F10PTR.TO.RESULT)
 
     namespace = locals().copy()
     type_system = 'lltype'

File pypy/jit/codewriter/call.py

 from pypy.jit.codewriter import support
 from pypy.jit.codewriter.jitcode import JitCode
 from pypy.jit.codewriter.effectinfo import (VirtualizableAnalyzer,
-    QuasiImmutAnalyzer, RandomEffectsAnalyzer, effectinfo_from_writeanalyze,
+    QuasiImmutAnalyzer, CanReleaseGILAnalyzer, effectinfo_from_writeanalyze,
     EffectInfo, CallInfoCollection)
 from pypy.translator.simplify import get_funcobj, get_functype
 from pypy.rpython.lltypesystem import lltype, llmemory
             self.readwrite_analyzer = ReadWriteAnalyzer(translator)
             self.virtualizable_analyzer = VirtualizableAnalyzer(translator)
             self.quasiimmut_analyzer = QuasiImmutAnalyzer(translator)
-            self.randomeffects_analyzer = RandomEffectsAnalyzer(translator)
+            self.canreleasegil_analyzer = CanReleaseGILAnalyzer(translator)
         #
         for index, jd in enumerate(jitdrivers_sd):
             jd.index = index
             fnaddr = llmemory.cast_ptr_to_adr(fnptr)
         NON_VOID_ARGS = [ARG for ARG in FUNC.ARGS if ARG is not lltype.Void]
         calldescr = self.cpu.calldescrof(FUNC, tuple(NON_VOID_ARGS),
-                                         FUNC.RESULT, EffectInfo.MOST_GENERAL)
+                                         FUNC.RESULT)
         return (fnaddr, calldescr)
 
     def getcalldescr(self, op, oopspecindex=EffectInfo.OS_NONE,
                 assert not NON_VOID_ARGS, ("arguments not supported for "
                                            "loop-invariant function!")
         # build the extraeffect
-        random_effects = self.randomeffects_analyzer.analyze(op)
-        if random_effects:
-            extraeffect = EffectInfo.EF_RANDOM_EFFECTS
-        # random_effects implies can_invalidate
-        can_invalidate = random_effects or self.quasiimmut_analyzer.analyze(op)
+        can_release_gil = self.canreleasegil_analyzer.analyze(op)
+        # can_release_gil implies can_invalidate
+        can_invalidate = can_release_gil or self.quasiimmut_analyzer.analyze(op)
         if extraeffect is None:
             if self.virtualizable_analyzer.analyze(op):
                 extraeffect = EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE
         #
         effectinfo = effectinfo_from_writeanalyze(
             self.readwrite_analyzer.analyze(op), self.cpu, extraeffect,
-            oopspecindex, can_invalidate)
+            oopspecindex, can_invalidate, can_release_gil)
         #
-        assert effectinfo is not None
+        if oopspecindex != EffectInfo.OS_NONE:
+            assert effectinfo is not None
         if elidable or loopinvariant:
+            assert effectinfo is not None
             assert extraeffect != EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE
             # XXX this should also say assert not can_invalidate, but
             #     it can't because our analyzer is not good enough for now
 
     def calldescr_canraise(self, calldescr):
         effectinfo = calldescr.get_extra_info()
-        return effectinfo.check_can_raise()
+        return (effectinfo is None or
+                effectinfo.extraeffect > EffectInfo.EF_CANNOT_RAISE)
 
     def jitdriver_sd_from_portal_graph(self, graph):
         for jd in self.jitdrivers_sd:

File pypy/jit/codewriter/effectinfo.py

     EF_ELIDABLE_CAN_RAISE              = 3 #elidable function (but can raise)
     EF_CAN_RAISE                       = 4 #normal function (can raise)
     EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE = 5 #can raise and force virtualizables
-    EF_RANDOM_EFFECTS                  = 6 #can do whatever
 
     # the 'oopspecindex' field is one of the following values:
     OS_NONE                     = 0    # normal case, no oopspec
                 write_descrs_fields, write_descrs_arrays,
                 extraeffect=EF_CAN_RAISE,
                 oopspecindex=OS_NONE,
-                can_invalidate=False):
-        key = (frozenset_or_none(readonly_descrs_fields),
-               frozenset_or_none(readonly_descrs_arrays),
-               frozenset_or_none(write_descrs_fields),
-               frozenset_or_none(write_descrs_arrays),
+                can_invalidate=False, can_release_gil=False):
+        key = (frozenset(readonly_descrs_fields),
+               frozenset(readonly_descrs_arrays),
+               frozenset(write_descrs_fields),
+               frozenset(write_descrs_arrays),
                extraeffect,
                oopspecindex,
-               can_invalidate)
+               can_invalidate,
+               can_release_gil)
         if key in cls._cache:
             return cls._cache[key]
-        if extraeffect == EffectInfo.EF_RANDOM_EFFECTS:
-            assert readonly_descrs_fields is None
-            assert readonly_descrs_arrays is None
-            assert write_descrs_fields is None
-            assert write_descrs_arrays is None
-        else:
-            assert readonly_descrs_fields is not None
-            assert readonly_descrs_arrays is not None
-            assert write_descrs_fields is not None
-            assert write_descrs_arrays is not None
         result = object.__new__(cls)
         result.readonly_descrs_fields = readonly_descrs_fields
         result.readonly_descrs_arrays = readonly_descrs_arrays
             result.write_descrs_arrays = write_descrs_arrays
         result.extraeffect = extraeffect
         result.can_invalidate = can_invalidate
+        result.can_release_gil = can_release_gil
         result.oopspecindex = oopspecindex
         cls._cache[key] = result
         return result
 
-    def check_can_raise(self):
-        return self.extraeffect > self.EF_CANNOT_RAISE
-
     def check_can_invalidate(self):
         return self.can_invalidate
 
         return self.extraeffect >= self.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE
 
     def has_random_effects(self):
-        return self.extraeffect >= self.EF_RANDOM_EFFECTS
-
-
-def frozenset_or_none(x):
-    if x is None:
-        return None
-    return frozenset(x)
-
-EffectInfo.MOST_GENERAL = EffectInfo(None, None, None, None,
-                                     EffectInfo.EF_RANDOM_EFFECTS,
-                                     can_invalidate=True)
-
+        return self.oopspecindex == self.OS_LIBFFI_CALL or self.can_release_gil
 
 def effectinfo_from_writeanalyze(effects, cpu,
                                  extraeffect=EffectInfo.EF_CAN_RAISE,
                                  oopspecindex=EffectInfo.OS_NONE,
-                                 can_invalidate=False):
+                                 can_invalidate=False,
+                                 can_release_gil=False):
     from pypy.translator.backendopt.writeanalyze import top_set
-    if effects is top_set or extraeffect == EffectInfo.EF_RANDOM_EFFECTS:
-        readonly_descrs_fields = None
-        readonly_descrs_arrays = None
-        write_descrs_fields = None
-        write_descrs_arrays = None
-        extraeffect = EffectInfo.EF_RANDOM_EFFECTS
-    else:
-        readonly_descrs_fields = []
-        readonly_descrs_arrays = []
-        write_descrs_fields = []
-        write_descrs_arrays = []
+    if effects is top_set:
+        return None
+    readonly_descrs_fields = []
+    readonly_descrs_arrays = []
+    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_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)
+    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":
-                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":
-                add_array(write_descrs_arrays, tup)
-            elif tup[0] == "readarray":
-                tupw = ("array",) + tup[1:]
-                if tupw not in effects:
-                    add_array(readonly_descrs_arrays, tup)
-            else:
-                assert 0
-    #
+    for tup in effects:
+        if tup[0] == "struct":
+            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":
+            add_array(write_descrs_arrays, tup)
+        elif tup[0] == "readarray":
+            tupw = ("array",) + tup[1:]
+            if tupw not in effects:
+                add_array(readonly_descrs_arrays, tup)
+        else:
+            assert 0
     return EffectInfo(readonly_descrs_fields,
                       readonly_descrs_arrays,
                       write_descrs_fields,
                       write_descrs_arrays,
                       extraeffect,
                       oopspecindex,
-                      can_invalidate)
+                      can_invalidate,
+                      can_release_gil)
 
 def consider_struct(TYPE, fieldname):
     if fieldType(TYPE, fieldname) is lltype.Void:
     def analyze_simple_operation(self, op, graphinfo):
         return op.opname == 'jit_force_quasi_immutable'
 
-class RandomEffectsAnalyzer(BoolGraphAnalyzer):
+class CanReleaseGILAnalyzer(BoolGraphAnalyzer):
     def analyze_direct_call(self, graph, seen=None):
+        releases_gil = False
         if hasattr(graph, "func") and hasattr(graph.func, "_ptr"):
-            if graph.func._ptr._obj.random_effects_on_gcobjs:
-                return True
-        return super(RandomEffectsAnalyzer, self).analyze_direct_call(graph,
-                                                                      seen)
+            releases_gil = graph.func._ptr._obj.releases_gil
+        return releases_gil or super(CanReleaseGILAnalyzer, self).analyze_direct_call(graph, seen)
 
     def analyze_simple_operation(self, op, graphinfo):
         return False

File pypy/jit/codewriter/jtransform.py

             extraeffect = EffectInfo.EF_CANNOT_RAISE
         elif oopspec_name.startswith('libffi_call_'):
             oopspecindex = EffectInfo.OS_LIBFFI_CALL
-            extraeffect = EffectInfo.EF_RANDOM_EFFECTS
+            extraeffect = EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE
         else:
             assert False, 'unsupported oopspec: %s' % oopspec_name
         return self._handle_oopspec_call(op, args, oopspecindex, extraeffect)

File pypy/jit/codewriter/test/test_call.py

     [block, _] = list(f_graph.iterblocks())
     [op] = block.operations
     call_descr = cc.getcalldescr(op)
-    assert call_descr.extrainfo.has_random_effects()
+    assert call_descr.extrainfo.can_release_gil

File pypy/jit/codewriter/test/test_codewriter.py

 from pypy.rpython.lltypesystem import lltype, llmemory, rffi
 
 class FakeCallDescr(AbstractDescr):
-    def __init__(self, FUNC, ARGS, RESULT, effectinfo):
+    def __init__(self, FUNC, ARGS, RESULT, effectinfo=None):
         self.FUNC = FUNC
         self.ARGS = ARGS
         self.RESULT = RESULT

File pypy/jit/codewriter/test/test_flatten.py

     def __init__(self, rtyper):
         rtyper._builtin_func_for_spec_cache = FakeDict()
         self.rtyper = rtyper
-    def calldescrof(self, FUNC, ARGS, RESULT, effectinfo):
+    def calldescrof(self, FUNC, ARGS, RESULT):
         return FakeDescr()
     def fielddescrof(self, STRUCT, name):
         return FakeDescr()

File pypy/jit/metainterp/optimizeopt/fficall.py

         self.funcval = funcval
         self.opargs = []
         argtypes, restype = self._get_signature(funcval)
-        self.descr = cpu.calldescrof_dynamic(argtypes, restype,
-                                             EffectInfo.MOST_GENERAL)
+        self.descr = cpu.calldescrof_dynamic(argtypes, restype)
         # ^^^ may be None if unsupported
         self.prepare_op = prepare_op
         self.delayed_ops = []
 
     def _get_oopspec(self, op):
         effectinfo = op.getdescr().get_extra_info()
-        return effectinfo.oopspecindex
+        if effectinfo is not None:
+            return effectinfo.oopspecindex
+        return EffectInfo.OS_NONE
 
     def _get_funcval(self, op):
         return self.getvalue(op.getarg(1))

File pypy/jit/metainterp/optimizeopt/heap.py

             opnum == rop.CALL_RELEASE_GIL or
             opnum == rop.CALL_ASSEMBLER):
             if opnum == rop.CALL_ASSEMBLER:
-                self._seen_guard_not_invalidated = False
+                effectinfo = None
             else:
                 effectinfo = op.getdescr().get_extra_info()
-                if effectinfo.check_can_invalidate():
-                    self._seen_guard_not_invalidated = False
-                if not effectinfo.has_random_effects():
-                    self.force_from_effectinfo(effectinfo)
-                    return
+            if effectinfo is None or effectinfo.check_can_invalidate():
+                self._seen_guard_not_invalidated = False
+            if effectinfo is not None and not effectinfo.has_random_effects():
+                # 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 arraydescr in effectinfo.readonly_descrs_arrays:
+                    self.force_lazy_setarrayitem(arraydescr)
+                for fielddescr in effectinfo.write_descrs_fields:
+                    self.force_lazy_setfield(fielddescr, can_cache=False)
+                for arraydescr in effectinfo.write_descrs_arrays:
+                    self.force_lazy_setarrayitem(arraydescr, can_cache=False)
+                if effectinfo.check_forces_virtual_or_virtualizable():
+                    vrefinfo = self.optimizer.metainterp_sd.virtualref_info
+                    self.force_lazy_setfield(vrefinfo.descr_forced)
+                    # ^^^ we only need to force this field; the other fields
+                    # of virtualref_info and virtualizable_info are not gcptrs.
+                return
         self.force_all_lazy_setfields_and_arrayitems()
         self.clean_caches()
 
-    def force_from_effectinfo(self, effectinfo):
-        # 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 arraydescr in effectinfo.readonly_descrs_arrays:
-            self.force_lazy_setarrayitem(arraydescr)
-        for fielddescr in effectinfo.write_descrs_fields:
-            self.force_lazy_setfield(fielddescr, can_cache=False)
-        for arraydescr in effectinfo.write_descrs_arrays:
-            self.force_lazy_setarrayitem(arraydescr, can_cache=False)
-        if effectinfo.check_forces_virtual_or_virtualizable():
-            vrefinfo = self.optimizer.metainterp_sd.virtualref_info
-            self.force_lazy_setfield(vrefinfo.descr_forced)
-            # ^^^ we only need to force this field; the other fields
-            # of virtualref_info and virtualizable_info are not gcptrs.
 
     def turned_constant(self, value):
         assert value.is_constant()

File pypy/jit/metainterp/optimizeopt/rewrite.py

         # specifically the given oopspec call.  For non-oopspec calls,
         # oopspecindex is just zero.
         effectinfo = op.getdescr().get_extra_info()
-        oopspecindex = effectinfo.oopspecindex
-        if oopspecindex == EffectInfo.OS_ARRAYCOPY:
-            if self._optimize_CALL_ARRAYCOPY(op):
-                return
+        if effectinfo is not None:
+            oopspecindex = effectinfo.oopspecindex
+            if oopspecindex == EffectInfo.OS_ARRAYCOPY:
+                if self._optimize_CALL_ARRAYCOPY(op):
+                    return
         self.emit_operation(op)
 
     def _optimize_CALL_ARRAYCOPY(self, op):

File pypy/jit/metainterp/optimizeopt/test/test_optimizefficall.py

                              restype=types.sint)
         #
         def calldescr(cpu, FUNC, oopspecindex, extraeffect=None):
-            if extraeffect == EffectInfo.EF_RANDOM_EFFECTS:
-                f = None   # means "can force all" really
-            else:
-                f = []
-            einfo = EffectInfo(f, f, f, f, oopspecindex=oopspecindex,
+            einfo = EffectInfo([], [], [], [], oopspecindex=oopspecindex,
                                extraeffect=extraeffect)
             return cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, einfo)
         #
         libffi_prepare =  calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_PREPARE)
         libffi_push_arg = calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_PUSH_ARG)
         libffi_call =     calldescr(cpu, FUNC, EffectInfo.OS_LIBFFI_CALL,
-                                    EffectInfo.EF_RANDOM_EFFECTS)
+                                 EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE)
     
     namespace = namespace.__dict__
 

File pypy/jit/metainterp/optimizeopt/test/test_util.py

     onedescr = cpu.fielddescrof(U, 'one')
 
     FUNC = lltype.FuncType([lltype.Signed], lltype.Signed)
-    plaincalldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                                     EffectInfo.MOST_GENERAL)
+    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,

File pypy/jit/metainterp/optimizeopt/vstring.py

         # specifically the given oopspec call.  For non-oopspec calls,
         # oopspecindex is just zero.
         effectinfo = op.getdescr().get_extra_info()
-        oopspecindex = effectinfo.oopspecindex
-        if oopspecindex != EffectInfo.OS_NONE:
+        if effectinfo is not None:
+            oopspecindex = effectinfo.oopspecindex
             for value, meth in opt_call_oopspec_ops:
                 if oopspecindex == value:      # a match with the OS_STR_xxx
                     if meth(self, op, mode_string):

File pypy/jit/metainterp/pyjitpl.py

         assert i == len(allboxes)
         #
         effectinfo = descr.get_extra_info()
-        if (assembler_call or
-                effectinfo.check_forces_virtual_or_virtualizable()):
+        if (effectinfo is None or
+                effectinfo.extraeffect ==
+                             effectinfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE or
+                assembler_call):
             # residual calls require attention to keep virtualizables in-sync
             self.metainterp.clear_exception()
             self.metainterp.vable_and_vrefs_before_residual_call()
             return
         if opnum == rop.CALL:
             effectinfo = descr.get_extra_info()
-            ef = effectinfo.extraeffect
-            if ef == effectinfo.EF_LOOPINVARIANT or \
-               ef == effectinfo.EF_ELIDABLE_CANNOT_RAISE or \
-               ef == effectinfo.EF_ELIDABLE_CAN_RAISE:
-                return
+            if effectinfo is not None:
+                ef = effectinfo.extraeffect
+                if ef == effectinfo.EF_LOOPINVARIANT or \
+                   ef == effectinfo.EF_ELIDABLE_CANNOT_RAISE or \
+                   ef == effectinfo.EF_ELIDABLE_CAN_RAISE:
+                    return
         if self.heap_cache:
             self.heap_cache.clear()
         if self.heap_array_cache:

File pypy/jit/metainterp/test/test_compile.py

     class FakeJitDriverSD:
         portal_runner_ptr = llhelper(lltype.Ptr(FUNC), ll_portal_runner)
         portal_runner_adr = llmemory.cast_ptr_to_adr(portal_runner_ptr)
-        portal_calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT, None)
+        portal_calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT)
         portal_finishtoken = compile.DoneWithThisFrameDescrInt()
         num_red_args = 2
         result_type = INT

File pypy/jit/metainterp/test/test_string.py

 import py
 from pypy.rlib.jit import JitDriver, dont_look_inside, we_are_jitted
-from pypy.rlib.debug import debug_print
 from pypy.jit.codewriter.policy import StopAtXPolicy
 from pypy.rpython.ootypesystem import ootype
 from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
         jitdriver = JitDriver(greens = ['g'], reds = ['m'])
         @dont_look_inside
         def escape(x):
-            # a plain "print" would call os.write() and release the gil
-            debug_print(str(x))
+            print str(x)
         def f(g, m):
             g = str(g)
             while m >= 0:

File pypy/jit/metainterp/warmspot.py

 from pypy.jit.metainterp.jitdriver import JitDriverStaticData
 from pypy.jit.codewriter import support, codewriter, longlong
 from pypy.jit.codewriter.policy import JitPolicy
-from pypy.jit.codewriter.effectinfo import EffectInfo
 from pypy.jit.metainterp.optimizeopt import ALL_OPTS_NAMES
 
 # ____________________________________________________________
         jd.portal_calldescr = self.cpu.calldescrof(
             jd._PTR_PORTAL_FUNCTYPE.TO,
             jd._PTR_PORTAL_FUNCTYPE.TO.ARGS,
-            jd._PTR_PORTAL_FUNCTYPE.TO.RESULT,
-            EffectInfo.MOST_GENERAL)
+            jd._PTR_PORTAL_FUNCTYPE.TO.RESULT)
 
         vinfo = jd.virtualizable_info
 

File pypy/module/_continuation/__init__.py

-from pypy.interpreter.mixedmodule import MixedModule
-
-
-class Module(MixedModule):
-    """This module exposes 'one-shot continuation containers'.
-
-A 'continulet' object from this module is a container that stores a
-one-shot continuation.  It is similar in purpose to the 'f_back'
-attribute of frames, which points to where execution should continue
-after this frame finishes.  The difference is that it will be changed
-(often repeatedly) before the frame actually returns.
-
-To make a continulet object, call 'continulet' with a callable and
-optional extra arguments.  Later, the first time you switch() to the
-continulet, the callable is invoked wih the same continulet object as
-the extra first argument.
-
-At this point, the one-shot continuation stored in the continulet points
-to the caller of switch().  When switch() is called again, this one-shot
-continuation is exchanged with the current one; it means that the caller
-of switch() is suspended, its continuation stored in the container, and
-the old continuation from the continulet object is resumed.
-
-Continulets are internally implemented using stacklets.  Stacklets
-are a bit more primitive (they are really one-shot continuations), but
-that idea only works in C, not in Python, notably because of exceptions.
-
-The most primitive API is actually 'permute()', which just permutes the
-one-shot continuation stored in two (or more) continulets.
-"""
-
-    appleveldefs = {
-        'error': 'app_continuation.error',
-        'generator': 'app_continuation.generator',
-    }
-
-    interpleveldefs = {
-        'continulet': 'interp_continuation.W_Continulet',
-        'permute': 'interp_continuation.permute',
-    }

File pypy/module/_continuation/app_continuation.py

-
-class error(Exception):
-    "Usage error of the _continuation module."
-
-
-import _continuation
-
-
-class generator(object):
-
-    def __init__(self, callable):
-        self.__func__ = callable
-
-    def __get__(self, obj, type=None):
-        return generator(self.__func__.__get__(obj, type))
-
-    def __call__(self, *args, **kwds):
-        return genlet(self.__func__, *args, **kwds)
-
-
-class genlet(_continuation.continulet):
-
-    def __iter__(self):
-        return self
-
-    def next(self, value=None):
-        res = self.switch(value)
-        if self.is_pending():
-            return res
-        else:
-            if res is not None:
-                raise TypeError("_continuation.generator must return None")
-            raise StopIteration
-
-    send = next

File pypy/module/_continuation/interp_continuation.py

-from pypy.rlib.rstacklet import StackletThread
-from pypy.rlib import jit
-from pypy.interpreter.error import OperationError
-from pypy.interpreter.executioncontext import ExecutionContext
-from pypy.interpreter.baseobjspace import Wrappable
-from pypy.interpreter.typedef import TypeDef
-from pypy.interpreter.gateway import interp2app
-
-
-class W_Continulet(Wrappable):
-    sthread = None
-
-    def __init__(self, space):
-        self.space = space
-        # states:
-        #  - not init'ed: self.sthread == None
-        #  - normal:      self.sthread != None, not is_empty_handle(self.h)
-        #  - finished:    self.sthread != None, is_empty_handle(self.h)
-
-    def check_sthread(self):
-        ec = self.space.getexecutioncontext()
-        if ec.stacklet_thread is not self.sthread:
-            start_state.clear()
-            raise geterror(self.space, "inter-thread support is missing")
-        return ec
-
-    def descr_init(self, w_callable, __args__):
-        if self.sthread is not None:
-            raise geterror(self.space, "continulet already __init__ialized")
-        start_state.origin = self
-        start_state.w_callable = w_callable
-        start_state.args = __args__
-        self.sthread = build_sthread(self.space)
-        try:
-            self.h = self.sthread.new(new_stacklet_callback)
-            if self.sthread.is_empty_handle(self.h):    # early return
-                raise MemoryError
-        except MemoryError:
-            self.sthread = None
-            start_state.clear()
-            raise getmemoryerror(self.space)
-
-    def switch(self, w_to):
-        to = self.space.interp_w(W_Continulet, w_to, can_be_None=True)
-        if to is not None:
-            if self is to:    # double-switch to myself: no-op
-                return get_result()
-            if to.sthread is None:
-                start_state.clear()
-                raise geterror(self.space, "continulet not initialized yet")
-        if self.sthread is None:
-            start_state.clear()
-            raise geterror(self.space, "continulet not initialized yet")
-        ec = self.check_sthread()
-        saved_topframeref = ec.topframeref
-        #
-        start_state.origin = self
-        if to is None:
-            # simple switch: going to self.h
-            start_state.destination = self
-        else:
-            # double switch: the final destination is to.h
-            start_state.destination = to
-        #
-        h = start_state.destination.h
-        sthread = self.sthread
-        if sthread.is_empty_handle(h):
-            start_state.clear()
-            raise geterror(self.space, "continulet already finished")
-        #
-        try:
-            do_switch(sthread, h)
-        except MemoryError:
-            start_state.clear()
-            raise getmemoryerror(self.space)
-        #
-        ec = sthread.ec
-        ec.topframeref = saved_topframeref
-        return get_result()
-
-    def descr_switch(self, w_value=None, w_to=None):
-        start_state.w_value = w_value
-        return self.switch(w_to)
-
-    def descr_throw(self, w_type, w_val=None, w_tb=None, w_to=None):
-        from pypy.interpreter.pytraceback import check_traceback
-        space = self.space
-        #
-        msg = "throw() third argument must be a traceback object"
-        if space.is_w(w_tb, space.w_None):
-            tb = None
-        else:
-            tb = check_traceback(space, w_tb, msg)
-        #
-        operr = OperationError(w_type, w_val, tb)
-        operr.normalize_exception(space)
-        start_state.w_value = None
-        start_state.propagate_exception = operr
-        return self.switch(w_to)
-
-    def descr_is_pending(self):
-        valid = (self.sthread is not None
-                 and not self.sthread.is_empty_handle(self.h))
-        return self.space.newbool(valid)
-
-
-def W_Continulet___new__(space, w_subtype, __args__):
-    r = space.allocate_instance(W_Continulet, w_subtype)
-    r.__init__(space)
-    return space.wrap(r)
-
-
-W_Continulet.typedef = TypeDef(
-    'continulet',
-    __module__ = '_continuation',
-    __new__     = interp2app(W_Continulet___new__),
-    __init__    = interp2app(W_Continulet.descr_init),
-    switch      = interp2app(W_Continulet.descr_switch),
-    throw       = interp2app(W_Continulet.descr_throw),
-    is_pending  = interp2app(W_Continulet.descr_is_pending),
-    )
-
-
-# ____________________________________________________________
-
-
-class State:
-    def __init__(self, space):
-        self.space = space 
-        w_module = space.getbuiltinmodule('_continuation')
-        self.w_error = space.getattr(w_module, space.wrap('error'))
-        self.w_memoryerror = OperationError(space.w_MemoryError, space.w_None)
-
-def geterror(space, message):
-    cs = space.fromcache(State)
-    return OperationError(cs.w_error, space.wrap(message))
-
-def getmemoryerror(space):
-    cs = space.fromcache(State)
-    return cs.w_memoryerror
-
-# ____________________________________________________________
-
-
-class SThread(StackletThread):
-
-    def __init__(self, space, ec):
-        StackletThread.__init__(self, space.config)
-        self.space = space
-        self.ec = ec
-
-ExecutionContext.stacklet_thread = None
-
-# ____________________________________________________________
-
-
-class StartState:   # xxx a single global to pass around the function to start
-    def clear(self):
-        self.origin = None
-        self.destination = None
-        self.w_callable = None
-        self.args = None
-        self.w_value = None
-        self.propagate_exception = None
-start_state = StartState()
-start_state.clear()
-
-
-def new_stacklet_callback(h, arg):
-    self       = start_state.origin
-    w_callable = start_state.w_callable
-    args       = start_state.args
-    start_state.clear()
-    try:
-        do_switch(self.sthread, h)
-    except MemoryError:
-        return h       # oups!  do an early return in this case
-    #
-    space = self.space
-    try:
-        ec = self.sthread.ec
-        ec.topframeref = jit.vref_None
-
-        if start_state.propagate_exception is not None:
-            raise start_state.propagate_exception   # just propagate it further
-        if start_state.w_value is not space.w_None:
-            raise OperationError(space.w_TypeError, space.wrap(
-                "can't send non-None value to a just-started continulet"))
-
-        args = args.prepend(self.space.wrap(self))
-        w_result = space.call_args(w_callable, args)
-    except Exception, e:
-        start_state.propagate_exception = e
-    else:
-        start_state.w_value = w_result
-    start_state.origin = self
-    start_state.destination = self
-    return self.h
-
-
-def do_switch(sthread, h):
-    h = sthread.switch(h)
-    origin = start_state.origin
-    self = start_state.destination
-    start_state.origin = None
-    start_state.destination = None
-    self.h, origin.h = origin.h, h
-
-def get_result():
-    if start_state.propagate_exception:
-        e = start_state.propagate_exception
-        start_state.propagate_exception = None
-        raise e
-    w_value = start_state.w_value
-    start_state.w_value = None
-    return w_value
-
-def build_sthread(space):
-    ec = space.getexecutioncontext()
-    sthread = ec.stacklet_thread
-    if not sthread:
-        sthread = ec.stacklet_thread = SThread(space, ec)
-    return sthread
-
-# ____________________________________________________________
-
-def permute(space, args_w):
-    sthread = build_sthread(space)
-    #
-    contlist = []
-    for w_cont in args_w:
-        cont = space.interp_w(W_Continulet, w_cont)
-        if cont.sthread is not sthread:
-            if cont.sthread is None:
-                raise geterror(space, "got a non-initialized continulet")
-            else:
-                raise geterror(space, "inter-thread support is missing")
-        elif sthread.is_empty_handle(cont.h):
-            raise geterror(space, "got an already-finished continulet")
-        contlist.append(cont)
-    #
-    if len(contlist) > 1:
-        other = contlist[-1].h
-        for cont in contlist:
-            other, cont.h = cont.h, other

File pypy/module/_continuation/test/__init__.py

Empty file removed.

File pypy/module/_continuation/test/support.py

-import py
-from pypy.conftest import gettestobjspace
-from pypy.rpython.tool.rffi_platform import CompilationError
-
-
-class BaseAppTest:
-    def setup_class(cls):
-        try:
-            import pypy.rlib.rstacklet
-        except CompilationError, e:
-            py.test.skip("cannot import rstacklet: %s" % e)
-        cls.space = gettestobjspace(usemodules=['_continuation'])

File pypy/module/_continuation/test/test_generator.py