[pypy-svn] r27522 - in pypy/branch/explicit_resume_pt_experiment: annotation rpython rpython/lltypesystem translator/stackless translator/stackless/test

pedronis at codespeak.net pedronis at codespeak.net
Sun May 21 04:36:54 CEST 2006


Author: pedronis
Date: Sun May 21 04:36:43 2006
New Revision: 27522

Added:
   pypy/branch/explicit_resume_pt_experiment/translator/stackless/test/test_resume_point.py   (contents, props changed)
Modified:
   pypy/branch/explicit_resume_pt_experiment/annotation/builtin.py
   pypy/branch/explicit_resume_pt_experiment/rpython/llinterp.py
   pypy/branch/explicit_resume_pt_experiment/rpython/lltypesystem/lloperation.py
   pypy/branch/explicit_resume_pt_experiment/rpython/lltypesystem/rbuiltin.py
   pypy/branch/explicit_resume_pt_experiment/rpython/objectmodel.py
   pypy/branch/explicit_resume_pt_experiment/translator/stackless/code.py
   pypy/branch/explicit_resume_pt_experiment/translator/stackless/transform.py
Log:
rough experiment on  explicit resume points with stackless transformed code:
primitives:

resume_point(LABEL, values to restore vars, [returs=var])
resume_after(TYP, LABEL, values for var to restore [returns=value for return])

the first test in test_resume_point works on the llinterp.

too unpolished/incomplete for the trunk tough. there are also some open issues. There may be oversights
and some things are quite hackish: I wanted to quickly explore how hard this would be.



Modified: pypy/branch/explicit_resume_pt_experiment/annotation/builtin.py
==============================================================================
--- pypy/branch/explicit_resume_pt_experiment/annotation/builtin.py	(original)
+++ pypy/branch/explicit_resume_pt_experiment/annotation/builtin.py	Sun May 21 04:36:43 2006
@@ -298,6 +298,13 @@
 def robjmodel_hint(s, **kwds_s):
     return s
 
+def robjmodel_resume_point(label, *args_s, **kwds_s):
+    return immutablevalue(None)
+
+def robjmodel_resume_after(s_typ, label, *args_s, **kwds_s):
+    assert s_typ.is_constant()
+    return getbookkeeper().valueoftype(s_typ.const)
+
 def llmemory_cast_ptr_to_adr(s):
     return SomeAddress()
 
@@ -357,6 +364,8 @@
 BUILTIN_ANALYZERS[pypy.rpython.objectmodel.hlinvoke] = robjmodel_hlinvoke
 BUILTIN_ANALYZERS[pypy.rpython.objectmodel.keepalive_until_here] = robjmodel_keepalive_until_here
 BUILTIN_ANALYZERS[pypy.rpython.objectmodel.hint] = robjmodel_hint
+BUILTIN_ANALYZERS[pypy.rpython.objectmodel.resume_point] = robjmodel_resume_point
+BUILTIN_ANALYZERS[pypy.rpython.objectmodel.resume_after] = robjmodel_resume_after
 BUILTIN_ANALYZERS[pypy.rpython.lltypesystem.llmemory.cast_ptr_to_adr] = llmemory_cast_ptr_to_adr
 BUILTIN_ANALYZERS[pypy.rpython.lltypesystem.llmemory.cast_adr_to_ptr] = llmemory_cast_adr_to_ptr
 BUILTIN_ANALYZERS[pypy.rpython.lltypesystem.llmemory.cast_adr_to_int] = llmemory_cast_adr_to_int

Modified: pypy/branch/explicit_resume_pt_experiment/rpython/llinterp.py
==============================================================================
--- pypy/branch/explicit_resume_pt_experiment/rpython/llinterp.py	(original)
+++ pypy/branch/explicit_resume_pt_experiment/rpython/llinterp.py	Sun May 21 04:36:43 2006
@@ -423,6 +423,9 @@
     def op_hint(self, x, hints):
         return x
 
+    def op_resume_point(self, *args):
+        pass
+
     def op_decode_arg(self, fname, i, name, vargs, vkwds):
         raise NotImplementedError("decode_arg")
 

Modified: pypy/branch/explicit_resume_pt_experiment/rpython/lltypesystem/lloperation.py
==============================================================================
--- pypy/branch/explicit_resume_pt_experiment/rpython/lltypesystem/lloperation.py	(original)
+++ pypy/branch/explicit_resume_pt_experiment/rpython/lltypesystem/lloperation.py	Sun May 21 04:36:43 2006
@@ -343,6 +343,9 @@
     'yield_current_frame_to_caller': LLOp(canraise=(StackException,)),
     #                               can always unwind, not just if stackless gc
 
+    'resume_point':  LLOp(canraise=(StackException,)),
+    'resume_after':  LLOp(canraise=(Exception, StackException)),
+
     # __________ misc operations __________
 
     'keepalive':            LLOp(),

Modified: pypy/branch/explicit_resume_pt_experiment/rpython/lltypesystem/rbuiltin.py
==============================================================================
--- pypy/branch/explicit_resume_pt_experiment/rpython/lltypesystem/rbuiltin.py	(original)
+++ pypy/branch/explicit_resume_pt_experiment/rpython/lltypesystem/rbuiltin.py	Sun May 21 04:36:43 2006
@@ -59,8 +59,46 @@
     c = hop.inputconst(pyobj_repr, __import__)
     return hop.genop('simple_call', [c] + args_v, resulttype = pyobj_repr)
 
+def rtype_resume_point(hop, **kwds_i):
+    assert hop.args_s[0].is_constant()
+    c_label = hop.inputconst(lltype.Void, hop.args_s[0].const)
+    args_v = hop.args_v[1:]
+    if 'i_returns' in kwds_i:
+        assert len(kwds_i) == 1
+        returns_index = kwds_i['i_returns']
+        v_return = args_v.pop(returns_index-1)
+    else:
+        assert not kwds_i
+        v_return = hop.inputconst(lltype.Void, None)
+
+    hop.exception_is_here()
+    return hop.genop('resume_point', [c_label, v_return] + args_v,
+                     hop.r_result)
+
+def rtype_resume_after(hop, **kwds_i):
+    # xxx hack, can do this in transform
+    assert hop.args_s[1].is_constant()
+    c_label = hop.inputconst(lltype.Void, hop.args_s[1].const)
+    args_v = hop.args_v[2:]
+    if 'i_returns' in kwds_i:
+        assert len(kwds_i) == 1
+        returns_index = kwds_i['i_returns']
+        v_return = args_v.pop(returns_index-1)
+    else:
+        assert not kwds_i
+        v_return = hop.inputconst(lltype.Void, None)
+
+    from pypy.rpython.lltypesystem import llmemory
+    v_state = hop.genop('resume_after_prepare', [c_label] + args_v,
+                        llmemory.GCREF)
+    hop.exception_is_here()
+    return hop.genop('resume_after', [v_state, v_return],
+                     hop.r_result)
+
 BUILTIN_TYPER = {}
 BUILTIN_TYPER[objectmodel.instantiate] = rtype_instantiate
 BUILTIN_TYPER[isinstance] = rtype_builtin_isinstance
 BUILTIN_TYPER[hasattr] = rtype_builtin_hasattr
 BUILTIN_TYPER[__import__] = rtype_builtin___import__
+BUILTIN_TYPER[objectmodel.resume_point] = rtype_resume_point
+BUILTIN_TYPER[objectmodel.resume_after] = rtype_resume_after

Modified: pypy/branch/explicit_resume_pt_experiment/rpython/objectmodel.py
==============================================================================
--- pypy/branch/explicit_resume_pt_experiment/rpython/objectmodel.py	(original)
+++ pypy/branch/explicit_resume_pt_experiment/rpython/objectmodel.py	Sun May 21 04:36:43 2006
@@ -53,6 +53,11 @@
 def hint(x, **kwds):
     return x
 
+def resume_point(label, *args, **kwds):
+    pass
+
+def resume_after(typ, label, *args, **kwds):
+    pass
 
 class FREED_OBJECT(object):
     def __getattribute__(self, attr):

Modified: pypy/branch/explicit_resume_pt_experiment/translator/stackless/code.py
==============================================================================
--- pypy/branch/explicit_resume_pt_experiment/translator/stackless/code.py	(original)
+++ pypy/branch/explicit_resume_pt_experiment/translator/stackless/code.py	Sun May 21 04:36:43 2006
@@ -190,6 +190,48 @@
 
 # ____________________________________________________________
 
+# xxx combination mess
+
+def resume_after_long(state, retvalue):
+    return 0
+
+resume_after_long.stackless_explicit = True
+INDEX_RESUME_AFTER_LONG = frame.RestartInfo.add_prebuilt(resume_after_long,
+                                                         [EMPTY_STATE])
+
+RESUME_AFTER_STATE = frame.make_state_header_type('resume_after_state',
+                                                  ('c', SAVED_REFERENCE),)
+
+def resume_after_void(state, retvalue):
+    if global_state.restart_substate == -1:
+        # normal entry point for a call to state.switch()
+        # first unwind the stack
+        u = UnwindException()
+        s = lltype.malloc(RESUME_AFTER_STATE)
+        s.header.f_restart = INDEX_RESUME_AFTER_VOID
+        s.c = state
+        add_frame_state(u, s.header)
+        raise u
+    elif global_state.restart_substate == 0:
+        # STATE 0: we didn't do anything so far, but the stack is unwound
+        global_state.restart_substate = -1
+        # grab the frame corresponding to ourself
+        # the 'targetstate' local is garbage here, it must be read back from
+        # 's.c' where we saved it by the normal entry point above
+        mystate = global_state.top
+        s = lltype.cast_pointer(lltype.Ptr(RESUME_AFTER_STATE), mystate)
+        targetstate = lltype.cast_opaque_ptr(lltype.Ptr(STATE_HEADER), s.c)
+        targetstate.f_back = mystate.f_back
+        global_state.top = targetstate
+        raise UnwindException()
+    else:
+        return 0
+
+resume_after_void.stackless_explicit = True
+INDEX_RESUME_AFTER_VOID = frame.RestartInfo.add_prebuilt(resume_after_void,
+                                                         [RESUME_AFTER_STATE,
+                                                          EMPTY_STATE])
+
 class StacklessData:
     def __init__(self):
         self.top = frame.null_state

Added: pypy/branch/explicit_resume_pt_experiment/translator/stackless/test/test_resume_point.py
==============================================================================
--- (empty file)
+++ pypy/branch/explicit_resume_pt_experiment/translator/stackless/test/test_resume_point.py	Sun May 21 04:36:43 2006
@@ -0,0 +1,72 @@
+from pypy.rpython.objectmodel import resume_point, resume_after
+from pypy.translator.stackless.transform import StacklessTransformer
+from pypy.translator.stackless.test.test_transform import llinterp_stackless_function, rtype_stackless_function, one
+from pypy import conftest
+import py
+from pypy.rpython import rstack
+
+def transform_stackless_function(fn):
+    def wrapper(argv):
+        return fn()
+    t = rtype_stackless_function(wrapper)
+    st = StacklessTransformer(t, wrapper, False)
+    st.transform_all()
+    if conftest.option.view:
+        t.view()
+
+def test_no_call():
+    def f(x, y):
+        x = x-1
+        resume_point("rp0", x, y) 
+        r = x+y
+        rstack.stack_unwind()
+        return r
+    def example():
+        v1 = f(one(),one()+one())
+        v2 = resume_after(int, "rp0", one(), one()+one()+one())
+        return v1*10 + v2
+    res = llinterp_stackless_function(example, assert_unwind=False)
+    assert res == 24
+
+def test_call():
+    def g(x,y):
+        return x*y
+    def f(x, y):
+        z = g(x,y)
+        resume_point("rp1", y, returns=z) 
+        return z+y
+    def example():
+        f(one(),one()+one())
+        return 0
+    transform_stackless_function(example)
+
+def test_call_exception_handling():
+    def g(x,y):
+        if x == 0:
+            raise KeyError
+        return x*y
+    def f(x, y):
+        try:
+            z = g(x,y)
+            resume_point("rp1", y, returns=z)
+        except KeyError:
+            return 0
+        return z+y
+    def example():
+        f(one(),one()+one())
+        return 0
+    transform_stackless_function(example)
+
+def test_call_uncovered():
+    def g(x,y):
+        return x*y
+    def f(x, y):
+        z = g(x,y)
+        resume_point("rp1", y, returns=z) 
+        return z+y+x
+    def example():
+        f(one(),one()+one())
+        return 0
+    e = py.test.raises(Exception, transform_stackless_function, example)
+    assert e.value.args == ('not covered needed value at resume_point',)
+

Modified: pypy/branch/explicit_resume_pt_experiment/translator/stackless/transform.py
==============================================================================
--- pypy/branch/explicit_resume_pt_experiment/translator/stackless/transform.py	(original)
+++ pypy/branch/explicit_resume_pt_experiment/translator/stackless/transform.py	Sun May 21 04:36:43 2006
@@ -100,6 +100,10 @@
     def operation_is_true(self, op):
         if op.opname == 'yield_current_frame_to_caller':
             return True
+        if op.opname == 'resume_point':
+            return True
+        if op.opname.startswith('resume_after'):
+            return True
         return self.stackless_gc and LL_OPERATIONS[op.opname].canunwindgc
 
     def analyze_external_call(self, op):
@@ -210,6 +214,15 @@
 
         self.yield_current_frame_to_caller_ptr = mixlevelannotator.constfunc(
             code.yield_current_frame_to_caller, [], s_StatePtr)
+        # resume_after
+        self.resume_after_long_ptr = mixlevelannotator.constfunc(
+            code.resume_after_long, [annmodel.SomePtr(llmemory.GCREF),
+                                     annmodel.SomeInteger()],
+                                    annmodel.SomeInteger())
+        self.resume_after_void_ptr = mixlevelannotator.constfunc(
+            code.resume_after_void, [annmodel.SomePtr(llmemory.GCREF),
+                                     annmodel.s_None],
+                                    annmodel.SomeInteger())
 
         mixlevelannotator.finish()
 
@@ -232,6 +245,10 @@
 
         self.reccopyannotator = MixLevelHelperAnnotator(translator.rtyper)
 
+        self.explicit_resume_points = {}
+
+        self.explicit_resume_after = []
+
         # register the prebuilt restartinfos
         for restartinfo in frame.RestartInfo.prebuilt:
             self.register_restart_info(restartinfo)
@@ -407,9 +424,75 @@
             if op.opname == 'yield_current_frame_to_caller':
                 op = replace_with_call(self.yield_current_frame_to_caller_ptr)
                 stackless_op = True
+            if op.opname == 'resume_after': # xxx
+                if op.result.concretetype != lltype.Signed:
+                    raise NotImplementedError
+                v_returns = op.args[1]
+                if v_returns.concretetype == lltype.Signed:
+                    raise NotImplementedError
+                elif v_returns.concretetype == lltype.Void:
+                    args = [self.resume_after_void_ptr] + op.args
+                    newop = model.SpaceOperation('direct_call', args, op.result)
+                    block.operations[i] = newop
+                else:
+                    raise NotImplementedError
+                stackeless_op = True
                 
             if (op.opname in ('direct_call', 'indirect_call')
                 or self.analyzer.operation_is_true(op)):
+                # XXX special casing with duplication for now
+                if op.opname == 'resume_point':
+                    if i == len(block.operations) - 1:
+                        link = block.exits[0]
+                    else:
+                        link = support.split_block_with_keepalive(block, i+1)
+                    parms = op.args[1:]
+                    if not isinstance(parms[0], model.Variable):
+                        assert parms[0].value is None
+                        parms[0] = None
+                    
+                    args = []
+                    for l in block.exits:
+                        for arg in l.args:
+                            if isinstance(arg, model.Variable) \
+                                   and arg.concretetype is not lltype.Void \
+                                   and arg is not op.result \
+                                   and arg not in args \
+                                   and arg not in [l.last_exception, l.last_exc_value]:
+                                args.append(arg)
+                                if arg not in parms:
+                                    raise Exception, "not covered needed value at resume_point"
+                    if parms[0] is not None: # returns= case
+                        res = parms[0]
+                        args = [arg for arg in args if arg is not res]
+                    else:
+                        args = args
+                        res = op.result
+
+                    (frame_type,
+                     fieldnames) = self.frametyper.frame_type_for_vars(args)
+
+                    self.resume_points.append(
+                        ResumePoint(res, args, tuple(block.exits),
+                                    frame_type, fieldnames))
+
+                    field2parm = {}
+                    for arg, fieldname in zip(args, fieldnames):
+                        p = parms.index(arg)
+                        field2parm[fieldname] = p-1 # ignore parm[0]
+                        
+                    label = op.args[0].value
+                    self.explicit_resume_points[label] = {
+                        'restart': len(self.masterarray1) + len(self.resume_points)-1,
+                        'frame_type': frame_type,
+                        'restype': res.concretetype,
+                        'field2parm': field2parm,
+                    }
+                    block = link.target
+                    i = 0
+                    continue
+
+                
                 # trap calls to stackless-related suggested primitives
                 if op.opname == 'direct_call':
                     func = getattr(op.args[0].value._obj, '_callable', None)
@@ -487,6 +570,9 @@
                 block.recloseblock(*newexits)
                 self.translator.rtyper._convert_link(block, newlink)
 
+                if op.opname == "resume_after_prepare":
+                    self.explicit_resume_after.append((block, i))
+
                 block = link.target
                 i = 0
             else:
@@ -594,6 +680,34 @@
         # (shorter, though)
         ll_global_state = self.ll_global_state.value
         ll_global_state.inst_masterarray = masterarray
+
+        # XXX when to do this properly with genc is a bit open
+        # xxx attach exception handling properly to malloc
+        for block, i in self.explicit_resume_after:
+            assert len(block.operations) == i+1
+            op = block.operations[i]
+            llops = LowLevelOpList()
+            label = op.args[0].value
+            data = self.explicit_resume_points[label]
+            FRAME = data['frame_type']
+            c_FRAME = model.Constant(FRAME, lltype.Void)
+            v_state = llops.genop('malloc', [c_FRAME],
+                                  resulttype = lltype.Ptr(FRAME))
+            for fieldname, p in data['field2parm'].items():
+                llops.genop('setfield', [v_state,
+                                model.Constant(fieldname, lltype.Void),
+                                op.args[1+p]])            
+            v_state = llops.genop('cast_pointer', [v_state],
+                                 resulttype = lltype.Ptr(frame.STATE_HEADER))
+            llops.genop('setfield', [v_state,
+                            model.Constant('f_restart', lltype.Void),
+                            model.Constant(data['restart'], lltype.Signed)])
+            v_state = llops.genop('cast_opaque_ptr', [v_state],
+                                 resulttype = llmemory.GCREF)
+            llops.append(model.SpaceOperation('same_as', [v_state], op.result))
+            block.operations = block.operations[:-1] + llops
+
+        
         return [masterarray]
 
     def transform_gc_nocollect(self, graph):



More information about the Pypy-commit mailing list