[pypy-commit] lang-smalltalk storage-context-state: Refactored Return-mechanism to make the context state and late sender-chain-manipulation refactoring work.

anton_gulenko noreply at buildbot.pypy.org
Sat Jul 19 14:13:42 CEST 2014


Author: Anton Gulenko <anton.gulenko at googlemail.com>
Branch: storage-context-state
Changeset: r923:a27bd7d3d458
Date: 2014-07-19 13:15 +0200
http://bitbucket.org/pypy/lang-smalltalk/changeset/a27bd7d3d458/

Log:	Refactored Return-mechanism to make the context state and late
	sender-chain-manipulation refactoring work. Added LocalReturn class.
	stack_frame() handles dispatching of Return types, always invoked
	together with loop_bytecodes now.

diff --git a/spyvm/interpreter.py b/spyvm/interpreter.py
--- a/spyvm/interpreter.py
+++ b/spyvm/interpreter.py
@@ -60,25 +60,58 @@
 
     def loop(self, w_active_context):
         # This is the top-level loop and is not invoked recursively.
-        s_new_context = w_active_context.as_context_get_shadow(self.space)
+        s_context = w_active_context.as_context_get_shadow(self.space)
         while True:
-            s_sender = s_new_context.s_sender()
+            s_sender = s_context.s_sender()
             try:
-                self.loop_bytecodes(s_new_context)
+                self.stack_frame(s_context, None)
                 raise Exception("loop_bytecodes left without raising...")
             except ContextSwitchException, e:
                 if self.is_tracing():
-                    e.print_trace(s_new_context)
-                s_new_context = e.s_new_context
-            except Return, nlr:
-                assert nlr.s_target_context or nlr.is_local
-                s_new_context = s_sender
-                if not nlr.is_local:
-                    while s_new_context is not nlr.s_target_context:
-                        s_sender = s_new_context.s_sender()
-                        s_new_context._activate_unwind_context(self)
-                        s_new_context = s_sender
-                s_new_context.push(nlr.value)
+                    e.print_trace()
+                s_context = e.s_new_context
+            except LocalReturn, ret:
+                s_context = self.unwind_context_chain(s_sender, s_sender, ret.value)
+            except Return, ret:
+                s_context = self.unwind_context_chain(s_sender, ret.s_target_context, ret.value)
+            except NonVirtualReturn, ret:
+                if self.is_tracing():
+                    ret.print_trace()
+                s_context = self.unwind_context_chain(ret.s_current_context, ret.s_target_context, ret.value)
+    
+    # This is a wrapper around loop_bytecodes that cleanly enters/leaves the frame,
+    # handles the stack overflow protection mechanism and handles/dispatches Returns.
+    def stack_frame(self, s_frame, s_sender, may_context_switch=True):
+        try:
+            if self.is_tracing():
+                self.stack_depth += 1
+            if s_frame._s_sender is None and s_sender is not None:
+                s_frame.store_s_sender(s_sender)
+            # Now (continue to) execute the context bytecodes
+            assert s_frame.state is InactiveContext
+            s_frame.state = ActiveContext
+            self.loop_bytecodes(s_frame, may_context_switch)
+        except rstackovf.StackOverflow:
+            rstackovf.check_stack_overflow()
+            raise StackOverflow(s_frame)
+        except Return, e:
+            # Do not catch NonVirtualReturn. If there are multiple dirty contexts
+            # on the stack, the inner-most one will count.
+            if s_frame.state is DirtyContext:
+                s_sender = s_frame.s_sender()
+                s_frame._activate_unwind_context(self)
+                target_context = s_sender if e.is_local else e.s_target_context
+                raise NonVirtualReturn(target_context, e.value, s_sender)
+            else:
+                s_frame._activate_unwind_context(self)
+                if e.s_target_context is s_sender or e.is_local:
+                    raise LocalReturn(e.value)
+                else:
+                    raise e
+        finally:
+            if self.is_tracing():
+                self.stack_depth -= 1
+            s_frame.state = InactiveContext
     
     def loop_bytecodes(self, s_context, may_context_switch=True):
         old_pc = 0
@@ -100,39 +133,22 @@
                 s_context=s_context)
             try:
                 self.step(s_context)
-            except Return, nlr:
-                if nlr.s_target_context is s_context or nlr.is_local:
-                    s_context.push(nlr.value)
-                else:
-                    if nlr.s_target_context is None:
-                        # This is the case where we are returning to our sender.
-                        # Mark the return as local, so our sender will take it
-                        nlr.is_local = True
-                    s_context._activate_unwind_context(self)
-                    raise nlr
-
-    # This is a wrapper around loop_bytecodes that cleanly enters/leaves the frame
-    # and handles the stack overflow protection mechanism.
-    def stack_frame(self, s_frame, s_sender, may_context_switch=True):
-        try:
-            if self.is_tracing():
-                self.stack_depth += 1
-            if s_frame._s_sender is None and s_sender is not None:
-                s_frame.store_s_sender(s_sender)
-            # Now (continue to) execute the context bytecodes
-            assert s_frame.state is InactiveContext
-            s_frame.state = ActiveContext
-            self.loop_bytecodes(s_frame, may_context_switch)
-        except rstackovf.StackOverflow:
-            rstackovf.check_stack_overflow()
-            raise StackOverflow(s_frame)
-        finally:
-            if self.is_tracing():
-                self.stack_depth -= 1
-            dirty_frame = s_frame.state is DirtyContext
-            s_frame.state = InactiveContext
-            if dirty_frame:
-                raise SenderChainManipulation(s_frame)
+            except LocalReturn, ret:
+                s_context.push(ret.value)
+    
+    def unwind_context_chain(self, start_context, target_context, return_value):
+        if start_context is None:
+            # This is the toplevel frame. Execution ended.
+            raise ReturnFromTopLevel(return_value)
+        assert target_context
+        context = start_context
+        while context is not target_context:
+            assert context, "Sender chain ended without finding return-context."
+            s_sender = context.s_sender()
+            context._activate_unwind_context(self)
+            context = s_sender
+        context.push(return_value)
+        return context
     
     def step(self, context):
         bytecode = context.fetch_next_bytecode()
@@ -253,44 +269,60 @@
     def __init__(self, object):
         self.object = object
 
-class Return(Exception):
-    _attrs_ = ["value", "s_target_context", "is_local"]
+class LocalReturn(Exception):
+    _attrs_ = ["value"]
+    def __init__(self, value):
+        self.value = value
+
+class AbstractReturn(Exception):
+    _attrs_ = ["value", "s_target_context"]
     def __init__(self, s_target_context, w_result):
         self.value = w_result
         self.s_target_context = s_target_context
-        self.is_local = False
+
+class Return(AbstractReturn):
+    """This is the basic Return, handled on the C-stack,
+    without forcing all contexts to the heap."""
+    _attrs_ = ["is_local"]
+    def __init__(self, s_target_context, w_result, is_local):
+        AbstractReturn.__init__(self, s_target_context, w_result)
+        self.is_local = is_local
+    
+class NonVirtualReturn(AbstractReturn):
+    """This Return will be passed through the entire C-stack built from stack_frame()
+    invokations. Used when the sender-chain has been manipulated.
+    s_current_context is the context where the outermost loop()
+    will start unrolling the context-chain and looking for s_target_context."""
+    
+    _attrs_ = ["s_current_context"]
+    def __init__(self, s_target_context, w_result, s_current_context):
+        AbstractReturn.__init__(self, s_target_context, w_result)
+        self.s_current_context = s_current_context
+    
+    def print_trace(self):
+        print "====== Sender Chain Manipulation, contexts forced to heap at: %s" % self.s_current_context.short_str()
 
 class ContextSwitchException(Exception):
     """General Exception that causes the interpreter to leave
     the current context."""
-    
     _attrs_ = ["s_new_context"]
-    type = "ContextSwitch"
     def __init__(self, s_new_context):
         self.s_new_context = s_new_context
-    
-    def print_trace(self, old_context):
-        print "====== %s, contexts forced to heap at: %s" % (self.type, self.s_new_context.short_str())
-        
+
 class StackOverflow(ContextSwitchException):
     """This causes the current jit-loop to be left, dumping all virtualized objects to the heap.
     This breaks performance, so it should rarely happen.
     In case of severe performance problems, execute with -t and check if this occurrs."""
-    type = "Stack Overflow"
     
+    def print_trace(self):
+        print "====== Stack Overflow, contexts forced to heap at: %s" % self.s_new_context.short_str()
+
 class ProcessSwitch(ContextSwitchException):
     """This causes the interpreter to switch the executed context.
     Triggered when switching the process."""
     
-    def print_trace(self, old_context):
-        print "====== Switched process from: %s" % old_context.short_str()
-        print "====== to: %s " % self.s_new_context.short_str()
-    
-class SenderChainManipulation(ContextSwitchException):
-    """Manipulation of the sender chain can invalidate the jitted C stack.
-    We have to dump all virtual objects and rebuild the stack.
-    We try to raise this as rarely as possible and as late as possible."""
-    type = "Sender Manipulation"
+    def print_trace(self):
+        print "====== Switched Process to: %s" % self.s_new_context.short_str()
 
 import rpython.rlib.unroll
 if hasattr(unroll, "unrolling_zero"):
@@ -306,7 +338,6 @@
             return unrolling_int(int.__rsub__(self, other))
     unrolling_zero = unrolling_int(0)
 
-
 # This is a decorator for bytecode implementation methods.
 # parameter_bytes=N means N additional bytes are fetched as parameters.
 def bytecode_implementation(parameter_bytes=0):
@@ -685,16 +716,12 @@
             # it will find the sender as a local, and we don't have to
             # force the reference
             s_return_to = None
-            return_from_top = self.s_sender() is None
+            is_local = True
         else:
             s_return_to = self.s_home().s_sender()
-            return_from_top = s_return_to is None
+            is_local = False
         
-        if return_from_top:
-            # This should never happen while executing a normal image.
-            raise ReturnFromTopLevel(return_value)
-        else:
-            raise Return(s_return_to, return_value)
+        raise Return(s_return_to, return_value, is_local)
 
     # ====== Send/Return bytecodes ======
 


More information about the pypy-commit mailing list