[pypy-svn] r26969 - in pypy/dist/pypy: rpython rpython/lltypesystem translator/stackless

arigo at codespeak.net arigo at codespeak.net
Mon May 8 17:29:19 CEST 2006


Author: arigo
Date: Mon May  8 17:29:17 2006
New Revision: 26969

Modified:
   pypy/dist/pypy/rpython/llinterp.py
   pypy/dist/pypy/rpython/lltypesystem/lltype.py
   pypy/dist/pypy/rpython/rbuiltin.py
   pypy/dist/pypy/translator/stackless/code.py
   pypy/dist/pypy/translator/stackless/transform.py
Log:
(pedronis, arigo)
Stackless transform: type erasure in a GC-friendly way,
with GcOpaqueType.  Added some comments, changed some strange
usages of numbers into strange usages of globals() in a very
Pythonic way, etc.



Modified: pypy/dist/pypy/rpython/llinterp.py
==============================================================================
--- pypy/dist/pypy/rpython/llinterp.py	(original)
+++ pypy/dist/pypy/rpython/llinterp.py	Mon May  8 17:29:17 2006
@@ -333,10 +333,10 @@
         elif operation.opname == 'indirect_call':
             assert isinstance(operation.args[0], Variable)
         vals = [self.getval(x) for x in operation.args]
-        # if these special cases pile up, do something better here
+        # XXX these special cases DO pile up, do something better here
         if operation.opname in ['cast_pointer', 'ooupcast', 'oodowncast',
                                 'cast_adr_to_ptr', 'cast_int_to_ptr',
-                                'cast_opaque_ptr']:
+                                'cast_opaque_ptr', 'unsafe_call']:
             vals.insert(0, operation.result.concretetype)
         try:
             retval = ophandler(*vals)
@@ -479,7 +479,7 @@
             log.warn("op_indirect_call with graphs=None:", f)
         return self.op_direct_call(f, *args)
 
-    def op_unsafe_call(self, f):
+    def op_unsafe_call(self, TGT, f):
         assert isinstance(f, llmemory.fakeaddress)
         assert f.offset is None
         obj = self.llinterpreter.typer.type_system.deref(f.ob)
@@ -490,9 +490,7 @@
             args.append(arg.concretetype._defl())
         frame = self.__class__(graph, args, self.llinterpreter, self)
         result = frame.eval()
-        if isinstance(lltype.typeOf(result), lltype.Ptr):
-            result = llmemory.cast_ptr_to_adr(result)
-        return result
+        return lltype._cast_whatever(TGT, result)
 
     def op_malloc(self, obj):
         if self.llinterpreter.gc is not None:

Modified: pypy/dist/pypy/rpython/lltypesystem/lltype.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/lltype.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/lltype.py	Mon May  8 17:29:17 2006
@@ -580,7 +580,28 @@
     if cast is None:
         raise TypeError, "unsupported cast"
     return cast(value)
- 
+
+def _cast_whatever(TGT, value):
+    from pypy.rpython.lltypesystem import llmemory
+    ORIG = typeOf(value)
+    if ORIG == TGT:
+        return value
+    if (isinstance(TGT, Primitive) and
+        isinstance(ORIG, Primitive)):
+        return cast_primitive(TGT, value)
+    elif isinstance(TGT, Ptr):
+        if isinstance(ORIG, Ptr):
+            if (isinstance(TGT.TO, OpaqueType) or
+                isinstance(ORIG.TO, OpaqueType)):
+                return cast_opaque_ptr(TGT, value)
+            else:
+                return cast_pointer(TGT, value)
+        elif ORIG == llmemory.Address:
+            return llmemory.cast_adr_to_ptr(value, TGT)
+    elif TGT == llmemory.Address and isinstance(ORIG, Ptr):
+        return llmemory.cast_ptr_to_adr(value)
+    raise TypeError("don't know how to cast from %r to %r" % (ORIG, TGT))
+
 
 class InvalidCast(TypeError):
     pass

Modified: pypy/dist/pypy/rpython/rbuiltin.py
==============================================================================
--- pypy/dist/pypy/rpython/rbuiltin.py	(original)
+++ pypy/dist/pypy/rpython/rbuiltin.py	Mon May  8 17:29:17 2006
@@ -399,7 +399,12 @@
         return v_value
     elif isinstance(TGT, lltype.Ptr):
         if isinstance(ORIG, lltype.Ptr):
-            return llops.genop('cast_pointer', [v_value], resulttype = TGT)
+            if (isinstance(TGT.TO, lltype.OpaqueType) or
+                isinstance(ORIG.TO, lltype.OpaqueType)):
+                return llops.genop('cast_opaque_ptr', [v_value],
+                                                              resulttype = TGT)
+            else:
+                return llops.genop('cast_pointer', [v_value], resulttype = TGT)
         elif ORIG == llmemory.Address:
             return llops.genop('cast_adr_to_ptr', [v_value], resulttype = TGT)
     elif TGT == llmemory.Address and isinstance(ORIG, lltype.Ptr):

Modified: pypy/dist/pypy/translator/stackless/code.py
==============================================================================
--- pypy/dist/pypy/translator/stackless/code.py	(original)
+++ pypy/dist/pypy/translator/stackless/code.py	Mon May  8 17:29:17 2006
@@ -2,9 +2,29 @@
 from pypy.rpython import rarithmetic
 from pypy.rpython import extfunctable
 
+SAVED_REFERENCE = lltype.Ptr(lltype.GcOpaqueType('stackless.saved_ref'))
+null_saved_ref = lltype.nullptr(SAVED_REFERENCE.TO)
 
-def ll_frame_switch(state):
+STORAGE_TYPES = [lltype.Void, SAVED_REFERENCE, llmemory.Address,
+                 lltype.Signed, lltype.Float, lltype.SignedLongLong]
+
+STORAGE_FIELDS = {SAVED_REFERENCE: 'ref',
+                  llmemory.Address: 'addr',
+                  lltype.Signed: 'long',
+                  lltype.Float: 'float',
+                  lltype.SignedLongLong: 'longlong',
+                  }
+
+RETVAL_VOID = 0
+for _key, _value in STORAGE_FIELDS.items():
+    globals()['RETVAL_' + _value.upper()] = STORAGE_TYPES.index(_key)
+
+# ____________________________________________________________
+
+def ll_frame_switch(targetstate):
     if global_state.restart_substate == 0:
+        # normal entry point for a call to state.switch()
+        # first unwind the stack
         u = UnwindException()
         s = lltype.malloc(SWITCH_STATE)
         s.header.restartstate = 1
@@ -12,26 +32,35 @@
         f = ll_frame_switch
         if global_state.restart_substate:
             f = None
-        s.c = llmemory.cast_ptr_to_adr(state)
+        s.c = lltype.cast_opaque_ptr(SAVED_REFERENCE, targetstate)
         s.header.function = llmemory.cast_ptr_to_adr(f)
+        s.header.retval_type = RETVAL_REF
         add_frame_state(u, s.header)
         raise u
     elif global_state.restart_substate == 1:
+        # STATE 1: we didn't do anything so far, but the stack is unwound
         global_state.restart_substate = 0
-        top = global_state.top
-        s = lltype.cast_pointer(lltype.Ptr(SWITCH_STATE), top)
-        top.restartstate = 2
-        state = llmemory.cast_adr_to_ptr(s.c, lltype.Ptr(STATE_HEADER))
-        global_state.top = state
-        global_state.retval_void_p = llmemory.cast_ptr_to_adr(top)
-        raise UnwindException()
+        # grab the frame corresponding to ourself, and prepare it for
+        # the future switch() back, which will go to STATE 2 below
+        sourcestate = global_state.top
+        sourcestate.restartstate = 2
+        # the 'targetstate' local is garbage here, it must be read back from
+        # 's.c' where we saved it by STATE 0 above
+        s = lltype.cast_pointer(lltype.Ptr(SWITCH_STATE), sourcestate)
+        targetstate = lltype.cast_opaque_ptr(lltype.Ptr(STATE_HEADER), s.c)
+        global_state.top = targetstate
+        global_state.retval_ref = lltype.cast_opaque_ptr(SAVED_REFERENCE,
+                                                         sourcestate)
+        raise UnwindException()   # this jumps to targetstate
     else:
-        top = global_state.top
+        # STATE 2: switching back into a tasklet suspended by
+        # a call to switch()
         global_state.top = null_state
         global_state.restart_substate = 0
-        origin_state = llmemory.cast_adr_to_ptr(fetch_retval_void_p(),
-                                                OPAQUE_STATE_HEADER_PTR)
-        return origin_state
+        origin_state = lltype.cast_opaque_ptr(OPAQUE_STATE_HEADER_PTR,
+                                              fetch_retval_ref())
+        return origin_state    # a normal return into the current tasklet,
+                               # with the source state as return value
 ll_frame_switch.stackless_explicit = True
 
 STATE_HEADER = lltype.GcStruct('state_header',
@@ -55,10 +84,12 @@
 
 SWITCH_STATE = lltype.GcStruct('state_switch',
                                ('header', STATE_HEADER),
-                               ('c', llmemory.Address))
+                               ('c', SAVED_REFERENCE))
 
 def yield_current_frame_to_caller():
     if global_state.restart_substate == 0:
+        # normal entry point for yield_current_frame_to_caller()
+        # first unwind the stack
         u = UnwindException()
         s = lltype.malloc(STATE_HEADER)
         s.restartstate = 1
@@ -67,45 +98,50 @@
         if global_state.restart_substate:
             f = None
         s.function = llmemory.cast_ptr_to_adr(f)
-        s.retval_type = RETVAL_VOID_P
+        s.retval_type = RETVAL_REF
         add_frame_state(u, s)
-        raise u
+        raise u   # this goes to 'STATE 1' below
+
     elif global_state.restart_substate == 1:
+        # STATE 1: we didn't do anything so far, but the stack is unwound
         global_state.restart_substate = 0
         ycftc_state = global_state.top
-        ycftc_state.restartstate = 2
         our_caller_state = ycftc_state.f_back
         caller_state = our_caller_state.f_back
         # the next three lines are pure rtyper-pleasing hacks
         f = yield_current_frame_to_caller
         if global_state.restart_substate:
             f = None
+        # when our immediate caller finishes (which is later, when the
+        # tasklet finishes), then we will jump to 'STATE 2' below
         endstate = lltype.malloc(STATE_HEADER)
-        endstate.restartstate = 3
+        endstate.restartstate = 2
         endstate.function = llmemory.cast_ptr_to_adr(f)
         our_caller_state.f_back = endstate
         global_state.top = caller_state
-        global_state.retval_void_p = llmemory.cast_ptr_to_adr(ycftc_state)
-        raise UnwindException()
-    elif global_state.restart_substate == 2:
-        top = global_state.top
-        global_state.top = null_state
-        global_state.restart_substate = 0
-        origin_state = llmemory.cast_adr_to_ptr(fetch_retval_void_p(),
-                                                OPAQUE_STATE_HEADER_PTR)
-        return origin_state
+        global_state.retval_ref = lltype.cast_opaque_ptr(SAVED_REFERENCE,
+                                                         our_caller_state)
+        raise UnwindException()  # this goes to the caller's caller
+
     else:
+        # STATE 2: this is a slight abuse of yield_current_frame_to_caller(),
+        # as we return here when our immediate caller returns (and thus the
+        # new tasklet finishes).
         global_state.restart_substate = 0
-        next_state = llmemory.cast_adr_to_ptr(fetch_retval_void_p(),
-                                              lltype.Ptr(STATE_HEADER))
+        next_state = lltype.cast_opaque_ptr(lltype.Ptr(STATE_HEADER),
+                                            fetch_retval_ref())
+        # return a NULL state pointer to the target of the implicit switch
         global_state.top = next_state
-        global_state.retval_void_p = llmemory.NULL
-        raise UnwindException()
+        global_state.retval_ref = null_saved_ref
+        raise UnwindException()  # this goes to the switch target given by
+                                 # the 'return' at the end of our caller
 
 yield_current_frame_to_caller.stackless_explicit = True
 
 def stack_frames_depth():
     if not global_state.restart_substate:
+        # normal entry point for stack_frames_depth()
+        # first unwind the stack
         u = UnwindException()
         s = lltype.malloc(STATE_HEADER)
         s.restartstate = 1
@@ -116,8 +152,10 @@
         s.function = llmemory.cast_ptr_to_adr(f)
         s.retval_type = RETVAL_LONG
         add_frame_state(u, s)
-        raise u
+        raise u    # goes to STATE 1 below
     else:
+        # STATE 1: now the stack is unwound, and we can count the frames
+        # in the heap
         cur = global_state.top
         global_state.restart_substate = 0
         depth = 0
@@ -129,6 +167,8 @@
 
 def ll_stack_unwind():
     if not global_state.restart_substate:
+        # normal entry point for stack_frames_depth()
+        # first unwind the stack in the usual way
         u = UnwindException()
         s = lltype.malloc(STATE_HEADER)
         s.restartstate = 1
@@ -139,8 +179,10 @@
         s.function = llmemory.cast_ptr_to_adr(f)
         s.retval_type = RETVAL_VOID
         add_frame_state(u, s)
-        raise u
+        raise u    # goes to STATE 1 below
     else:
+        # STATE 1: now the stack is unwound.  That was the goal.
+        # Return to caller.
         global_state.restart_substate = 0
 ll_stack_unwind.stackless_explicit = True
 
@@ -151,29 +193,30 @@
         self.retval_long = 0
         self.retval_longlong = rarithmetic.r_longlong(0)
         self.retval_float = 0.0
-        self.retval_void_p = llmemory.NULL
+        self.retval_addr = llmemory.NULL
+        self.retval_ref = null_saved_ref
         self.exception = None
 
 global_state = StacklessData()
 
-RETVAL_VOID, RETVAL_LONG, RETVAL_LONGLONG, RETVAL_FLOAT, RETVAL_VOID_P = \
-             range(5)
-
 def call_function(fn, retval_code):
     if retval_code == RETVAL_VOID:
         lloperation.llop.unsafe_call(lltype.Void, fn)
+    elif retval_code == RETVAL_REF:
+        global_state.retval_ref = lloperation.llop.unsafe_call(
+            SAVED_REFERENCE, fn)
+    elif retval_code == RETVAL_ADDR:
+        global_state.retval_addr = lloperation.llop.unsafe_call(
+            llmemory.Address, fn)
     elif retval_code == RETVAL_LONG:
         global_state.retval_long = lloperation.llop.unsafe_call(
             lltype.Signed, fn)
-    elif retval_code == RETVAL_LONGLONG:
-        global_state.retval_longlong = lloperation.llop.unsafe_call(
-            lltype.SignedLongLong, fn)
     elif retval_code == RETVAL_FLOAT:
         global_state.retval_float = lloperation.llop.unsafe_call(
             lltype.Float, fn)
-    elif retval_code == RETVAL_VOID_P:
-        global_state.retval_void_p = lloperation.llop.unsafe_call(
-            llmemory.Address, fn)
+    elif retval_code == RETVAL_LONGLONG:
+        global_state.retval_longlong = lloperation.llop.unsafe_call(
+            lltype.SignedLongLong, fn)
 call_function.stackless_explicit = True
 
 class UnwindException(lloperation.StackException):
@@ -274,11 +317,24 @@
         return global_state.retval_float
 fetch_retval_float.stackless_explicit = True
 
-def fetch_retval_void_p():
+def fetch_retval_addr():
+    e = global_state.exception
+    if e:
+        global_state.exception = None
+        raise e
+    else:
+        res = global_state.retval_addr
+        global_state.retval_addr = llmemory.NULL
+        return res
+fetch_retval_addr.stackless_explicit = True
+
+def fetch_retval_ref():
     e = global_state.exception
     if e:
         global_state.exception = None
         raise e
     else:
-        return global_state.retval_void_p
-fetch_retval_void_p.stackless_explicit = True
+        res = global_state.retval_ref
+        global_state.retval_ref = null_saved_ref
+        return res
+fetch_retval_ref.stackless_explicit = True

Modified: pypy/dist/pypy/translator/stackless/transform.py
==============================================================================
--- pypy/dist/pypy/translator/stackless/transform.py	(original)
+++ pypy/dist/pypy/translator/stackless/transform.py	Mon May  8 17:29:17 2006
@@ -6,7 +6,9 @@
 from pypy.translator import unsimplify
 from pypy.annotation import model as annmodel
 from pypy.rpython.annlowlevel import MixLevelHelperAnnotator
-from pypy.translator.stackless import code 
+from pypy.translator.stackless import code
+from pypy.translator.stackless.code import SAVED_REFERENCE, STORAGE_TYPES
+from pypy.translator.stackless.code import STORAGE_FIELDS
 from pypy.rpython.rclass import getinstancerepr
 from pypy.rpython.rbuiltin import gen_cast
 from pypy.rpython.rtyper import LowLevelOpList
@@ -14,28 +16,24 @@
 
 from pypy.translator.stackless.code import STATE_HEADER, null_state
 
-STORAGE_TYPES = [llmemory.Address,
-                 lltype.Signed,
-                 lltype.Float,
-                 lltype.SignedLongLong]
-STORAGE_FIELDS = ['addr',
-                  'long',
-                  'float',
-                  'longlong']
-
 def storage_type(T):
-    """Return the index into STORAGE_TYPES 
+    """Return the 'erased' storage type corresponding to T.
     """
     if T is lltype.Void:
-        return None
+        return lltype.Void
+    elif isinstance(T, lltype.Ptr):
+        if T._needsgc():
+            return SAVED_REFERENCE
+        else:
+            return llmemory.Address
     elif T is lltype.Float:
-        return 2
+        return lltype.Float
     elif T in [lltype.SignedLongLong, lltype.UnsignedLongLong]:
-        return 3
-    elif T is llmemory.Address or isinstance(T, lltype.Ptr):
-        return 0
+        return lltype.SignedLongLong
+    elif T is llmemory.Address:
+        return lltype.Address
     elif isinstance(T, lltype.Primitive):
-        return 1
+        return lltype.Signed
     else:
         raise Exception("don't know about %r" % (T,))
 
@@ -107,16 +105,20 @@
         self.resume_state_ptr = mixlevelannotator.constfunc(
             code.resume_state, [], annmodel.SomeInteger())
 
-        self.fetch_retval_void_ptr = mixlevelannotator.constfunc(
-            code.fetch_retval_void, [], annmodel.s_None)
-        self.fetch_retval_long_ptr = mixlevelannotator.constfunc(
-            code.fetch_retval_long, [], annmodel.SomeInteger())
-        self.fetch_retval_longlong_ptr = mixlevelannotator.constfunc(
-            code.fetch_retval_longlong, [], annmodel.SomeInteger(size=2))
-        self.fetch_retval_float_ptr = mixlevelannotator.constfunc(
-            code.fetch_retval_float, [], annmodel.SomeFloat())
-        self.fetch_retval_void_p_ptr = mixlevelannotator.constfunc(
-            code.fetch_retval_void_p, [], annmodel.SomeAddress())
+        self.fetch_retvals = {
+            lltype.Void: mixlevelannotator.constfunc(
+                code.fetch_retval_void, [], annmodel.s_None),
+            lltype.Signed: mixlevelannotator.constfunc(
+                code.fetch_retval_long, [], annmodel.SomeInteger()),
+            lltype.SignedLongLong: mixlevelannotator.constfunc(
+                code.fetch_retval_longlong, [], annmodel.SomeInteger(size=2)),
+            lltype.Float: mixlevelannotator.constfunc(
+                code.fetch_retval_float, [], annmodel.SomeFloat()),
+            llmemory.Address: mixlevelannotator.constfunc(
+                code.fetch_retval_addr, [], annmodel.SomeAddress()),
+            SAVED_REFERENCE: mixlevelannotator.constfunc(
+                code.fetch_retval_ref, [], annmodel.SomePtr(SAVED_REFERENCE)),
+            }
 
         s_StatePtr = annmodel.SomePtr(code.OPAQUE_STATE_HEADER_PTR)
         self.suggested_primitives = {
@@ -145,25 +147,24 @@
 
     def frame_type_for_vars(self, vars):
         fieldnames = []
-        counts = [0] * len(STORAGE_TYPES)
+        counts = {}
         for v in vars:
             t = storage_type(v.concretetype)
-            if t is None:
+            if t is lltype.Void:
                 fieldnames.append(None)
             else:
-                fieldnames.append('state_%s_%d' % (STORAGE_FIELDS[t],
-                                                   counts[t]))
-                counts[t] = counts[t] + 1
-        key = tuple(counts)
+                n = counts.get(t, 0)
+                fieldnames.append('state_%s_%d' % (STORAGE_FIELDS[t], n))
+                counts[t] = n + 1
+        key = lltype.frozendict(counts)
         if key in self.frametypes:
             T = self.frametypes[key]
         else:
             fields = []
-            for t in range(len(STORAGE_TYPES)):
-                for j in range(counts[t]):
-                    fields.append(('state_%s_%d'%(STORAGE_FIELDS[t], j),
-                                   STORAGE_TYPES[t]))
-            T = lltype.GcStruct("FrameState_%d_%d_%d_%d" % tuple(key),
+            for t in STORAGE_TYPES:
+                for j in range(counts.get(t, 0)):
+                    fields.append(('state_%s_%d' % (STORAGE_FIELDS[t], j), t))
+            T = lltype.GcStruct("FrameState",
                                 ('header', STATE_HEADER),
                                 *fields)
             self.frametypes[key] = T
@@ -244,36 +245,16 @@
             for i, arg in enumerate(resume_point.args):
                 assert arg is not resume_point.var_result
                 t = storage_type(arg.concretetype)
-                if t is None:
+                if t is lltype.Void:
                     continue
                 fname = model.Constant(resume_point.fieldnames[i], lltype.Void)
                 v_newarg = llops.genop('getfield', [frame_top, fname],
-                                       resulttype = STORAGE_TYPES[t])
+                                       resulttype = t)
                 v_newarg = gen_cast(llops, arg.concretetype, v_newarg)
                 varmap[arg] = v_newarg
 
-            r = storage_type(resume_point.var_result.concretetype)
-            if r is not None:
-                rettype = STORAGE_TYPES[r]
-            else:
-                rettype = lltype.Void
-
-            if rettype == lltype.Signed:
-                getretval = self.fetch_retval_long_ptr
-            if rettype == lltype.SignedLongLong:
-                getretval = self.fetch_retval_longlong_ptr
-            elif rettype == lltype.Void:
-                getretval = self.fetch_retval_void_ptr
-            elif rettype == lltype.Float:
-                getretval = self.fetch_retval_float_ptr
-            elif rettype == llmemory.Address:
-##                if resume_point.var_result.concretetype is not \
-##                       llmemory.Address:
-##                    if resume_point.var_result in \
-##                           resume_point.links_to_resumption[0].args:
-##                        need_address_conversion = True
-                getretval = self.fetch_retval_void_p_ptr
-
+            rettype = storage_type(resume_point.var_result.concretetype)
+            getretval = self.fetch_retvals[rettype]
             retval = llops.genop("direct_call", [getretval],
                                  resulttype = rettype)
             varmap[resume_point.var_result] = retval
@@ -476,12 +457,8 @@
              model.Constant(llmemory.fakeaddress(funcptr), llmemory.Address)],
             varoftype(lltype.Void)))
         rettype = lltype.typeOf(funcptr).TO.RESULT
-        retval_type = {None: code.RETVAL_VOID,
-                       0: code.RETVAL_VOID_P,
-                       1: code.RETVAL_LONG,
-                       2: code.RETVAL_FLOAT,
-                       3: code.RETVAL_LONGLONG}[storage_type(rettype)]
-        
+        retval_type = STORAGE_TYPES.index(storage_type(rettype))
+
         saveops.append(model.SpaceOperation(
             "setfield", [var_header, model.Constant("retval_type", lltype.Void), 
                          model.Constant(retval_type, lltype.Signed)],
@@ -509,6 +486,6 @@
             if t is lltype.Void:
                 continue
             fname = model.Constant(fieldnames[i], lltype.Void)
-            v_typeerased = gen_cast(llops, STORAGE_TYPES[t], var)
+            v_typeerased = gen_cast(llops, t, var)
             llops.genop('setfield', [frame_state_var, fname, v_typeerased])
         return llops



More information about the Pypy-commit mailing list