[pypy-commit] lang-smalltalk storage: WORK IN PROGRESS. This compiles, but segfaults.

anton_gulenko noreply at buildbot.pypy.org
Mon Jul 7 13:17:09 CEST 2014


Author: Anton Gulenko <anton.gulenko at googlemail.com>
Branch: storage
Changeset: r868:3fe7264ab317
Date: 2014-07-07 13:15 +0200
http://bitbucket.org/pypy/lang-smalltalk/changeset/3fe7264ab317/

Log:	WORK IN PROGRESS. This compiles, but segfaults.
	- Refactoring: maintaining 2 backreferences to the sender in
	ContextPartObjects (direct_sender and virtual_sender). As many
	contexts as possible should use virtual_sender; when it becomes
	necessary, direct_sender will be set instead (should break
	performance). Context objects are now always created WITHOUT a
	sender reference; it is set and deleted by the interpreter. Required
	small changes in lots of places.
	- Made ProcessWrapper more consistent and removed 2 duplicated
	methods.
	- Fixed tests.

diff --git a/spyvm/interpreter.py b/spyvm/interpreter.py
--- a/spyvm/interpreter.py
+++ b/spyvm/interpreter.py
@@ -68,7 +68,9 @@
         while True:
             assert self.current_stack_depth == 0
             # Need to save s_sender, loop_bytecodes will nil this on return
-            s_sender = s_new_context.s_sender()
+            # Virtual references are not allowed here, and neither are "fresh" contexts (except for the toplevel one).
+            assert s_new_context.virtual_sender is jit.vref_None
+            s_sender = s_new_context.direct_sender
             try:
                 self.loop_bytecodes(s_new_context)
                 raise Exception("loop_bytecodes left without raising...")
@@ -79,7 +81,7 @@
             except Return, nlr:
                 s_new_context = s_sender
                 while s_new_context is not nlr.s_target_context:
-                    s_sender = s_new_context.s_sender()
+                    s_sender = s_new_context.direct_sender
                     s_new_context._activate_unwind_context(self)
                     s_new_context = s_sender
                 s_new_context.push(nlr.value)
@@ -88,7 +90,7 @@
                     print "====== Switched process from: %s" % s_new_context.short_str()
                     print "====== to: %s " % p.s_new_context.short_str()
                 s_new_context = p.s_new_context
-
+        
     def loop_bytecodes(self, s_context, may_context_switch=True):
         old_pc = 0
         if not jit.we_are_jitted() and may_context_switch:
@@ -98,6 +100,7 @@
             pc = s_context.pc()
             if pc < old_pc:
                 if jit.we_are_jitted():
+                    # Do the interrupt-check at the end of a loop, don't interrupt loops midway.
                     self.jitted_check_for_interrupt(s_context)
                 self.jit_driver.can_enter_jit(
                     pc=pc, self=self, method=method,
@@ -115,17 +118,30 @@
                 else:
                     s_context.push(nlr.value)
     
-    # This is just a wrapper around loop_bytecodes that handles the stack overflow protection mechanism
-    def stack_frame(self, s_new_frame, may_context_switch=True):
-        if self.max_stack_depth > 0:
-            if self.current_stack_depth >= self.max_stack_depth:
-                raise StackOverflow(s_new_frame)
-        
-        self.current_stack_depth += 1
+    # 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):
+        assert s_frame.virtual_sender is jit.vref_None
         try:
-            self.loop_bytecodes(s_new_frame, may_context_switch)
+            # Enter the context - store a virtual reference back to the sender
+            # Non-fresh contexts can happen, e.g. when activating a stored BlockContext.
+            # The same frame object must not pass through here recursively!
+            if s_frame.is_fresh():
+                s_frame.virtual_sender = jit.virtual_ref(s_sender)
+            
+            self.current_stack_depth += 1
+            if self.max_stack_depth > 0:
+                if self.current_stack_depth >= self.max_stack_depth:
+                    raise StackOverflow(s_frame)
+            
+            # Now (continue to) execute the context bytecodes
+            self.loop_bytecodes(s_frame, may_context_switch)
         finally:
             self.current_stack_depth -= 1
+            # Cleanly leave the context. This will finish the virtual sender-reference, if
+            # it is still there, which can happen in case of ProcessSwitch or StackOverflow;
+            # in case of a Return, this will already be handled while unwinding the stack.
+            s_frame.finish_virtual_sender()
     
     def step(self, context):
         bytecode = context.fetch_next_bytecode()
@@ -177,7 +193,7 @@
             self.next_wakeup_tick = 0
             semaphore = self.space.objtable["w_timerSemaphore"]
             if not semaphore.is_nil(self.space):
-                wrapper.SemaphoreWrapper(self.space, semaphore).signal(s_frame.w_self())
+                wrapper.SemaphoreWrapper(self.space, semaphore).signal(s_frame)
         # We have no finalization process, so far.
         # We do not support external semaphores.
             # In cog, the method to add such a semaphore is only called in GC.
@@ -195,7 +211,12 @@
         except ReturnFromTopLevel, e:
             return e.object
 
-    def perform(self, w_receiver, selector, *arguments_w):
+    def perform(self, w_receiver, selector, *w_arguments):
+        s_frame = self.create_toplevel_context(w_receiver, selector, *w_arguments)
+        self.interrupt_check_counter = self.interrupt_counter_size
+        return self.interpret_toplevel(s_frame.w_self())
+    
+    def create_toplevel_context(self, w_receiver, selector, *w_arguments):
         if isinstance(selector, str):
             if selector == "asSymbol":
                 w_selector = self.image.w_asSymbol
@@ -207,15 +228,13 @@
         
         w_method = model.W_CompiledMethod(self.space, header=512)
         w_method.literalatput0(self.space, 1, w_selector)
-        assert len(arguments_w) <= 7
-        w_method.setbytes([chr(131), chr(len(arguments_w) << 5 + 0), chr(124)]) #returnTopFromMethodBytecode
+        assert len(w_arguments) <= 7
+        w_method.setbytes([chr(131), chr(len(w_arguments) << 5 + 0), chr(124)]) #returnTopFromMethodBytecode
         w_method.set_lookup_class_and_name(w_receiver.getclass(self.space), "Interpreter.perform")
-        s_frame = MethodContextShadow(self.space, None, w_method, w_receiver, [])
+        s_frame = MethodContextShadow(self.space, w_method=w_method, w_receiver=w_receiver)
         s_frame.push(w_receiver)
-        s_frame.push_all(list(arguments_w))
-        
-        self.interrupt_check_counter = self.interrupt_counter_size
-        return self.interpret_toplevel(s_frame.w_self())
+        s_frame.push_all(list(w_arguments))
+        return s_frame
     
     def padding(self, symbol=' '):
         return symbol * self.current_stack_depth
@@ -233,8 +252,7 @@
 
 class ContextSwitchException(Exception):
     """General Exception that causes the interpreter to leave
-    the current context. The current pc is required in order to update
-    the context object that we are leaving."""
+    the current context."""
     _attrs_ = ["s_new_context"]
     def __init__(self, s_new_context):
         self.s_new_context = s_new_context
@@ -528,7 +546,7 @@
                                   s_compiledin.s_superclass())
 
     def _sendSelector(self, w_selector, argcount, interp,
-                      receiver, receiverclassshadow):
+                      receiver, receiverclassshadow, w_arguments=None):
         assert argcount >= 0
         try:
             w_method = receiverclassshadow.lookup(w_selector)
@@ -537,19 +555,22 @@
         
         code = w_method.primitive()
         if code:
+            if w_arguments:
+                self.push_all(w_arguments)
             try:
                 return self._call_primitive(code, interp, argcount, w_method, w_selector)
             except primitives.PrimitiveFailedError:
                 pass # ignore this error and fall back to the Smalltalk version
-        arguments = self.pop_and_return_n(argcount)
-        s_frame = w_method.create_frame(interp.space, receiver, arguments, self)
+        if not w_arguments:
+            w_arguments = self.pop_and_return_n(argcount)
+        s_frame = w_method.create_frame(interp.space, receiver, w_arguments)
         self.pop() # receiver
 
         # ######################################################################
         if interp.trace:
             print interp.padding() + s_frame.short_str()
 
-        return interp.stack_frame(s_frame)
+        return interp.stack_frame(s_frame, self)
 
     @objectmodel.specialize.arg(1)
     def _sendSelfSelectorSpecial(self, selector, numargs, interp):
@@ -560,7 +581,7 @@
         w_special_selector = self.space.objtable["w_" + special_selector]
         s_class = receiver.class_shadow(self.space)
         w_method = s_class.lookup(w_special_selector)
-        s_frame = w_method.create_frame(interp.space, receiver, w_args, self)
+        s_frame = w_method.create_frame(interp.space, receiver, w_args)
         
         # ######################################################################
         if interp.trace:
@@ -568,7 +589,7 @@
             if not objectmodel.we_are_translated():
                 import pdb; pdb.set_trace()
         
-        return interp.stack_frame(s_frame)
+        return interp.stack_frame(s_frame, self)
     
     def _doesNotUnderstand(self, w_selector, argcount, interp, receiver):
         arguments = self.pop_and_return_n(argcount)
diff --git a/spyvm/model.py b/spyvm/model.py
--- a/spyvm/model.py
+++ b/spyvm/model.py
@@ -1411,15 +1411,15 @@
         
     def is_array_object(self):
         return True
-
-    def create_frame(self, space, receiver, arguments, sender = None):
+        
+    def create_frame(self, space, receiver, arguments=[]):
         from spyvm.shadow import MethodContextShadow
         assert len(arguments) == self.argsize
-        return MethodContextShadow(space, None, self, receiver, arguments, sender)
+        return MethodContextShadow(space, w_method=self, w_receiver=receiver, arguments=arguments)
         
     # === Printing ===
 
-    def guess_classname (self):
+    def guess_classname(self):
         return "CompiledMethod"
         
     def str_content(self):
diff --git a/spyvm/objspace.py b/spyvm/objspace.py
--- a/spyvm/objspace.py
+++ b/spyvm/objspace.py
@@ -197,11 +197,6 @@
         elif isinstance(w_v, model.W_SmallInteger): return float(w_v.value)
         raise UnwrappingError()
 
-    def unwrap_pointersobject(self, w_v):
-        if not isinstance(w_v, model.W_PointersObject):
-            raise UnwrappingError()
-        return w_v
-
     @jit.look_inside_iff(lambda self, w_array: jit.isconstant(w_array.size()))
     def unwrap_array(self, w_array):
         # Check that our argument has pointers format and the class:
diff --git a/spyvm/primitives.py b/spyvm/primitives.py
--- a/spyvm/primitives.py
+++ b/spyvm/primitives.py
@@ -10,6 +10,10 @@
 
 from rpython.rlib import rarithmetic, rfloat, unroll, jit
 
+def assert_class(interp, w_obj, w_class):
+    if not w_obj.getclass(interp.space).is_same_object(w_class):
+        raise PrimitiveFailedError()
+
 def assert_bounds(n0, minimum, maximum):
     if not minimum <= n0 < maximum:
         raise PrimitiveFailedError()
@@ -100,17 +104,17 @@
         if unwrap_spec is None:
             def wrapped(interp, s_frame, argument_count_m1, w_method=None):
                 if compiled_method:
-                    w_result = func(interp, s_frame, argument_count_m1, w_method)
+                    result = func(interp, s_frame, argument_count_m1, w_method)
                 else:
-                    w_result = func(interp, s_frame, argument_count_m1)
+                    result = func(interp, s_frame, argument_count_m1)
                 if result_is_new_frame:
-                    return interp.stack_frame(w_result, may_context_switch)
+                    return interp.stack_frame(result, s_frame, may_context_switch)
                 if not no_result:
-                    assert w_result is not None
-                    s_frame.push(w_result)
+                    assert result is not None
+                    s_frame.push(result)
         else:
             len_unwrap_spec = len(unwrap_spec)
-            assert (len_unwrap_spec + 2 == len(inspect.getargspec(func)[0])), "wrong number of arguments"
+            assert len_unwrap_spec + 2 == len(inspect.getargspec(func)[0]), "wrong number of arguments"
             unrolling_unwrap_spec = unrolling_iterable(enumerate(unwrap_spec))
             def wrapped(interp, s_frame, argument_count_m1, w_method=None):
                 argument_count = argument_count_m1 + 1 # to account for the rcvr
@@ -153,7 +157,7 @@
                     if clean_stack:
                         # happens only if no exception occurs!
                         s_frame.pop_n(len_unwrap_spec)
-                    return interp.stack_frame(s_new_frame, may_context_switch)
+                    return interp.stack_frame(s_new_frame, s_frame, may_context_switch)
                 else:
                     w_result = func(interp, s_frame, *args)
                     # After calling primitive, reload context-shadow in case it
@@ -379,7 +383,6 @@
         print ("%s" % s_frame.peek(1)).replace('\r', '\n')
         if isinstance(w_message, model.W_PointersObject):
             print ('%s' % w_message.fetch_all(s_frame.space)).replace('\r', '\n')
-        # raise Exit('Probably Debugger called...')
     raise PrimitiveFailedError()
 
 # ___________________________________________________________________________
@@ -593,8 +596,7 @@
 @expose_primitive(NEW_METHOD, unwrap_spec=[object, int, int])
 def func(interp, s_frame, w_class, bytecount, header):
     # We ignore w_class because W_CompiledMethod is special
-    w_method = model.W_CompiledMethod(s_frame.space, bytecount, header)
-    return w_method
+    return model.W_CompiledMethod(interp.space, bytecount, header)
 
 # ___________________________________________________________________________
 # I/O Primitives
@@ -965,15 +967,14 @@
     raise PrimitiveFailedError
 
 @expose_primitive(LOW_SPACE_SEMAPHORE, unwrap_spec=[object, object])
-def func(interp, s_frame, w_reciver, i):
+def func(interp, s_frame, w_receiver, i):
     # dont know when the space runs out
-    return w_reciver
-
+    return w_receiver
 
 @expose_primitive(SIGNAL_AT_BYTES_LEFT, unwrap_spec=[object, int])
-def func(interp, s_frame, w_reciver, i):
+def func(interp, s_frame, w_receiver, i):
     # dont know when the space runs out
-    return w_reciver
+    return w_receiver
 
 @expose_primitive(DEFER_UPDATES, unwrap_spec=[object, bool])
 def func(interp, s_frame, w_receiver, flag):
@@ -1287,19 +1288,8 @@
     # The block bytecodes are stored inline: so we skip past the
     # byteodes to invoke this primitive to find them (hence +2)
     initialip = s_frame.pc() + 2
-    s_new_context = shadow.BlockContextShadow(
-                        interp.space, None, w_method_context, argcnt, initialip)
+    s_new_context = shadow.BlockContextShadow(interp.space, None, w_method_context, argcnt, initialip)
     return s_new_context.w_self()
-    
-def finalize_block_ctx(interp, s_block_ctx, s_frame):
-    from spyvm.error import SenderChainManipulation
-    # Set some fields
-    s_block_ctx.store_pc(s_block_ctx.initialip())
-    try:
-        s_block_ctx.store_s_sender(s_frame)
-    except SenderChainManipulation, e:
-        assert e.s_context == s_block_ctx
-    return s_block_ctx
 
 @expose_primitive(VALUE, result_is_new_frame=True)
 def func(interp, s_frame, argument_count):
@@ -1333,7 +1323,8 @@
     s_block_ctx.push_all(block_args)
 
     s_frame.pop()
-    return finalize_block_ctx(interp, s_block_ctx, s_frame)
+    s_block_ctx.reset_pc()
+    return s_block_ctx
 
 @expose_primitive(VALUE_WITH_ARGS, unwrap_spec=[object, list],
                   result_is_new_frame=True)
@@ -1352,7 +1343,8 @@
 
     # XXX Check original logic. Image does not test this anyway
     # because falls back to value + internal implementation
-    return finalize_block_ctx(interp, s_block_ctx, s_frame)
+    s_block_ctx.reset_pc()
+    return s_block_ctx
 
 @expose_primitive(PERFORM)
 def func(interp, s_frame, argcount):
@@ -1361,72 +1353,49 @@
 @expose_primitive(PERFORM_WITH_ARGS,
                   unwrap_spec=[object, object, list],
                   no_result=True, clean_stack=False)
-def func(interp, s_frame, w_rcvr, w_selector, args_w):
+def func(interp, s_frame, w_rcvr, w_selector, w_arguments):
     from spyvm.shadow import MethodNotFound
-    argcount = len(args_w)
     s_frame.pop_n(2) # removing our arguments
+    
+    return s_frame._sendSelector(w_selector, len(w_arguments), interp, w_rcvr,
+                        w_rcvr.class_shadow(interp.space), w_arguments=w_arguments)
 
-    try:
-        w_method = w_rcvr.class_shadow(interp.space).lookup(w_selector)
-    except MethodNotFound:
-        return s_frame._doesNotUnderstand(w_selector, argcount, interp, w_rcvr)
-
-    code = w_method.primitive()
-    if code:
-        s_frame.push_all(args_w)
-        try:
-            return s_frame._call_primitive(code, interp, argcount, w_method, w_selector)
-        except PrimitiveFailedError:
-            pass # ignore this error and fall back to the Smalltalk version
-    s_new_frame = w_method.create_frame(interp.space, w_rcvr, args_w, s_frame)
-    s_frame.pop()
-    return interp.stack_frame(s_new_frame)
-
- at expose_primitive(WITH_ARGS_EXECUTE_METHOD, unwrap_spec=[object, list, object], no_result=True)
+ at expose_primitive(WITH_ARGS_EXECUTE_METHOD,
+    result_is_new_frame=True, unwrap_spec=[object, list, object])
 def func(interp, s_frame, w_rcvr, args_w, w_cm):
     if not isinstance(w_cm, model.W_CompiledMethod):
         raise PrimitiveFailedError()
     code = w_cm.primitive()
     if code:
         raise PrimitiveFailedError("withArgs:executeMethod: not support with primitive method")
-    s_new_frame = w_cm.create_frame(interp.space, w_rcvr, args_w, s_frame)
-    return interp.stack_frame(s_new_frame)
+    return w_cm.create_frame(interp.space, w_rcvr, args_w)
+
+
+# XXX we might want to disable the assert_class checks in the 4 primitives below
 
 @expose_primitive(SIGNAL, unwrap_spec=[object], clean_stack=False, no_result=True)
 def func(interp, s_frame, w_rcvr):
-    # XXX we might want to disable this check
-    if not w_rcvr.getclass(interp.space).is_same_object(
-        interp.space.w_Semaphore):
-        raise PrimitiveFailedError()
-    wrapper.SemaphoreWrapper(interp.space, w_rcvr).signal(s_frame.w_self())
+    assert_class(interp, w_rcvr, interp.space.w_Semaphore)
+    wrapper.SemaphoreWrapper(interp.space, w_rcvr).signal(s_frame)
 
 @expose_primitive(WAIT, unwrap_spec=[object], clean_stack=False, no_result=True)
 def func(interp, s_frame, w_rcvr):
-    # XXX we might want to disable this check
-    if not w_rcvr.getclass(interp.space).is_same_object(
-        interp.space.w_Semaphore):
-        raise PrimitiveFailedError()
-    wrapper.SemaphoreWrapper(interp.space, w_rcvr).wait(s_frame.w_self())
+    assert_class(interp, w_rcvr, interp.space.w_Semaphore)
+    wrapper.SemaphoreWrapper(interp.space, w_rcvr).wait(s_frame)
 
- at expose_primitive(RESUME, unwrap_spec=[object], result_is_new_frame=True, clean_stack=False)
+ at expose_primitive(RESUME, unwrap_spec=[object], no_result=True, clean_stack=False)
 def func(interp, s_frame, w_rcvr):
-    # XXX we might want to disable this check
-    if not w_rcvr.getclass(interp.space).is_same_object(
-        interp.space.w_Process):
-        raise PrimitiveFailedError()
-    w_frame = wrapper.ProcessWrapper(interp.space, w_rcvr).resume(s_frame.w_self())
-    w_frame = interp.space.unwrap_pointersobject(w_frame)
-    return w_frame.as_context_get_shadow(interp.space)
+    import pdb; pdb.set_trace()
+    assert_class(interp, w_rcvr, interp.space.w_Process)
+    wrapper.ProcessWrapper(interp.space, w_rcvr).resume(s_frame)
 
- at expose_primitive(SUSPEND, unwrap_spec=[object], result_is_new_frame=True, clean_stack=False)
+ at expose_primitive(SUSPEND, unwrap_spec=[object], no_result=True, clean_stack=False)
 def func(interp, s_frame, w_rcvr):
-    # XXX we might want to disable this check
-    if not w_rcvr.getclass(interp.space).is_same_object(
-        interp.space.w_Process):
-        raise PrimitiveFailedError()
-    w_frame = wrapper.ProcessWrapper(interp.space, w_rcvr).suspend(s_frame.w_self())
-    w_frame = interp.space.unwrap_pointersobject(w_frame)
-    return w_frame.as_context_get_shadow(interp.space)
+    import pdb; pdb.set_trace()
+    assert_class(interp, w_rcvr, interp.space.w_Process)
+    wrapper.ProcessWrapper(interp.space, w_rcvr).suspend(s_frame)
+    
+    
 
 @expose_primitive(FLUSH_CACHE, unwrap_spec=[object])
 def func(interp, s_frame, w_rcvr):
@@ -1455,11 +1424,9 @@
     return w_context
 
 
-def activateClosure(interp, s_frame, w_block, args_w):
+def activateClosure(interp, w_block, args_w):
     space = interp.space
-    if not w_block.getclass(space).is_same_object(
-            space.w_BlockClosure):
-        raise PrimitiveFailedError()
+    assert_class(interp, w_block, space.w_BlockClosure)
     block = wrapper.BlockClosureWrapper(space, w_block)
     if not block.numArgs() == len(args_w):
         raise PrimitiveFailedError()
@@ -1470,7 +1437,7 @@
 
     # additionally to the smalltalk implementation, this also pushes
     # args and copiedValues
-    s_new_frame = block.asContextWithSender(s_frame.w_self(), args_w)
+    s_new_frame = block.create_frame(args_w)
     w_closureMethod = s_new_frame.w_method()
 
     assert isinstance(w_closureMethod, model.W_CompiledMethod)
@@ -1481,35 +1448,35 @@
 
 @expose_primitive(CLOSURE_VALUE, unwrap_spec=[object], result_is_new_frame=True)
 def func(interp, s_frame, w_block_closure):
-    return activateClosure(interp, s_frame, w_block_closure, [])
+    return activateClosure(interp, w_block_closure, [])
 
 @expose_primitive(CLOSURE_VALUE_, unwrap_spec=[object, object], result_is_new_frame=True)
 def func(interp, s_frame, w_block_closure, w_a0):
-    return activateClosure(interp, s_frame, w_block_closure, [w_a0])
+    return activateClosure(interp, w_block_closure, [w_a0])
 
 @expose_primitive(CLOSURE_VALUE_VALUE, unwrap_spec=[object, object, object], result_is_new_frame=True)
 def func(interp, s_frame, w_block_closure, w_a0, w_a1):
-    return activateClosure(interp, s_frame, w_block_closure, [w_a0, w_a1])
+    return activateClosure(interp, w_block_closure, [w_a0, w_a1])
 
 @expose_primitive(CLOSURE_VALUE_VALUE_VALUE, unwrap_spec=[object, object, object, object], result_is_new_frame=True)
 def func(interp, s_frame, w_block_closure, w_a0, w_a1, w_a2):
-    return activateClosure(interp, s_frame, w_block_closure, [w_a0, w_a1, w_a2])
+    return activateClosure(interp, w_block_closure, [w_a0, w_a1, w_a2])
 
 @expose_primitive(CLOSURE_VALUE_VALUE_VALUE_VALUE, unwrap_spec=[object, object, object, object, object], result_is_new_frame=True)
 def func(interp, s_frame, w_block_closure, w_a0, w_a1, w_a2, w_a3):
-    return activateClosure(interp, s_frame, w_block_closure, [w_a0, w_a1, w_a2, w_a3])
+    return activateClosure(interp, w_block_closure, [w_a0, w_a1, w_a2, w_a3])
 
 @expose_primitive(CLOSURE_VALUE_WITH_ARGS, unwrap_spec=[object, list], result_is_new_frame=True)
 def func(interp, s_frame, w_block_closure, args_w):
-    return activateClosure(interp, s_frame, w_block_closure, args_w)
+    return activateClosure(interp, w_block_closure, args_w)
 
 @expose_primitive(CLOSURE_VALUE_NO_CONTEXT_SWITCH, unwrap_spec=[object], result_is_new_frame=True, may_context_switch=False)
 def func(interp, s_frame, w_block_closure):
-    return activateClosure(interp, s_frame, w_block_closure, [])
+    return activateClosure(interp, w_block_closure, [])
 
 @expose_primitive(CLOSURE_VALUE_NO_CONTEXT_SWITCH_, unwrap_spec=[object, object], result_is_new_frame=True, may_context_switch=False)
 def func(interp, s_frame, w_block_closure, w_a0):
-    return activateClosure(interp, s_frame, w_block_closure, [w_a0])
+    return activateClosure(interp, w_block_closure, [w_a0])
 
 # ___________________________________________________________________________
 # Override the default primitive to give latitude to the VM in context management.
diff --git a/spyvm/shadow.py b/spyvm/shadow.py
--- a/spyvm/shadow.py
+++ b/spyvm/shadow.py
@@ -607,13 +607,14 @@
 class ContextPartShadow(AbstractRedirectingShadow):
 
     __metaclass__ = extendabletype
-    _attrs_ = ['_s_sender', '_pc', '_temps_and_stack',
+    _attrs_ = ['direct_sender', 'virtual_sender',
+            '_pc', '_temps_and_stack',
             '_stack_ptr', 'instances_w']
     repr_classname = "ContextPartShadow"
     
     _virtualizable_ = [
-        "_s_sender", "_pc",
-        "_temps_and_stack[*]", "_stack_ptr",
+        'direct_sender', 'virtual_sender',
+        "_pc", "_temps_and_stack[*]", "_stack_ptr",
         "_w_self", "_w_self_size"
     ]
 
@@ -621,7 +622,8 @@
     # Initialization
     
     def __init__(self, space, w_self):
-        self._s_sender = None
+        self.direct_sender = None
+        self.virtual_sender = jit.vref_None
         AbstractRedirectingShadow.__init__(self, space, w_self)
         self.instances_w = {}
 
@@ -671,7 +673,7 @@
         if n0 == constants.CTXPART_SENDER_INDEX:
             assert isinstance(w_value, model.W_PointersObject)
             if w_value.is_nil(self.space):
-                self._s_sender = None
+                self.store_s_sender(None, raise_error=False)
             else:
                 self.store_s_sender(w_value.as_context_get_shadow(self.space))
             return
@@ -690,19 +692,40 @@
             raise error.WrapperException("Index in context out of bounds")
     
     # === Sender ===
+    # There are two fields for the sender (virtual and direct). Only one of them is can be set at a time.
+    # As long as the frame object is virtualized, using the virtual reference should increase performance.
+    # As soon as a frame object is forced to the heap, the direct reference must be used.
     
-    def store_s_sender(self, s_sender):
-        assert s_sender is None or isinstance(s_sender, ContextPartShadow)
-        self._s_sender = s_sender
-        raise error.SenderChainManipulation(self)
+    def is_fresh(self):
+        return self.direct_sender is None and self.virtual_sender is jit.vref_None
+    
+    def finish_virtual_sender(self, save_direct_sender=True):
+        if self.virtual_sender is not jit.vref_None:
+            sender = self.virtual_sender()
+            jit.virtual_ref_finish(self.virtual_sender, sender)
+            self.virtual_sender = jit.vref_None
+            if save_direct_sender:
+                self.direct_sender = sender
+    
+    def store_s_sender(self, s_sender, raise_error=True):
+        # If we have a virtual back reference, we must finish it before storing the direct reference.
+        self.finish_virtual_sender(save_direct_sender=False)
+        self.direct_sender = s_sender
+        if raise_error:
+            raise error.SenderChainManipulation(self)
     
     def w_sender(self):
-        if self._s_sender is None:
+        sender = self.s_sender()
+        if sender is None:
             return self.space.w_nil
-        return self._s_sender.w_self()
+        return sender.w_self()
     
     def s_sender(self):
-        return self._s_sender
+        if self.direct_sender:
+            return self.direct_sender
+        else:
+            result = self.virtual_sender()
+            return result
     
     # === Stack Pointer ===
     
@@ -779,10 +802,7 @@
     
     def mark_returned(self):
         self.store_pc(-1)
-        try:
-            self.store_s_sender(None)
-        except error.SenderChainManipulation, e:
-            assert self == e.s_context
+        self.store_s_sender(None, raise_error=False)
 
     def is_returned(self):
         return self.pc() == -1 and self.w_sender().is_nil(self.space)
@@ -1042,7 +1062,10 @@
         initialip = self.initialip()
         initialip += 1 + self.w_method().literalsize
         return self.space.wrap_int(initialip)
-
+    
+    def reset_pc(self):
+        self.store_pc(self.initialip())
+    
     def initialip(self):
         return self._initialip
         
@@ -1079,7 +1102,7 @@
     
     @jit.unroll_safe
     def __init__(self, space, w_self=None, w_method=None, w_receiver=None,
-                              arguments=None, s_sender=None, closure=None, pc=0):
+                              arguments=[], closure=None, pc=0):
         self = jit.hint(self, access_directly=True, fresh_virtualizable=True)
         ContextPartShadow.__init__(self, space, w_self)
         self.store_w_receiver(w_receiver)
@@ -1095,18 +1118,9 @@
         else:
             self._w_method = None
         
-        if s_sender:
-            try:
-                self.store_s_sender(s_sender)
-            except error.SenderChainManipulation, e:
-                assert self == e.s_context
-        
-        if arguments:
-            argc = len(arguments)
-            for i0 in range(argc):
-                self.settemp(i0, arguments[i0])
-        else:
-            argc = 0
+        argc = len(arguments)
+        for i0 in range(argc):
+            self.settemp(i0, arguments[i0])
         
         if closure:
             for i0 in range(closure.size()):
diff --git a/spyvm/test/jit.py b/spyvm/test/jit.py
--- a/spyvm/test/jit.py
+++ b/spyvm/test/jit.py
@@ -49,7 +49,7 @@
     w_method.literals = literals
     w_method.setbytes(bytes)
     w_receiver = stack[0]
-    s_frame = shadow.MethodContextShadow(space, None, w_method, w_receiver, [])
+    s_frame = shadow.MethodContextShadow(space, w_method=w_method, w_receiver=w_receiver)
     w_frame = s_frame.w_self()
     def interp_execute_frame():
         return interp.interpret_toplevel(w_frame)
diff --git a/spyvm/test/test_interpreter.py b/spyvm/test/test_interpreter.py
--- a/spyvm/test/test_interpreter.py
+++ b/spyvm/test/test_interpreter.py
@@ -1013,12 +1013,12 @@
         assert False
 
 class StackTestInterpreter(TestInterpreter):
-    def stack_frame(self, w_frame, may_interrupt=True):
+    def stack_frame(self, s_frame, s_sender, may_interrupt=True):
         stack_depth = self.current_stack_depth
         for i in range(stack_depth + 1):
             assert sys._getframe(5 + i * 7).f_code.co_name == 'loop_bytecodes'
         assert sys._getframe(6 + stack_depth * 7).f_code.co_name == 'loop'
-        return interpreter.Interpreter.stack_frame(self, w_frame)
+        return interpreter.Interpreter.stack_frame(self, s_frame, s_sender, may_interrupt)
 
 def test_actual_stackdepth():
     # | testBlock |
diff --git a/spyvm/test/test_shadow.py b/spyvm/test/test_shadow.py
--- a/spyvm/test/test_shadow.py
+++ b/spyvm/test/test_shadow.py
@@ -281,9 +281,7 @@
 def test_methodcontext_s_home():
     w_context = methodcontext()
     s_context = w_context.as_methodcontext_get_shadow(space)
-    w_middle_context = methodcontext(w_sender=w_context)
-    s_middle_context = w_middle_context.as_methodcontext_get_shadow(space)
 
     w_closure = space.newClosure(w_context, 3, 0, [])
-    s_closure_context = wrapper.BlockClosureWrapper(space, w_closure).asContextWithSender(w_middle_context, [])
+    s_closure_context = wrapper.BlockClosureWrapper(space, w_closure).create_frame()
     assert s_closure_context.s_home() is s_context
diff --git a/spyvm/test/test_wrapper.py b/spyvm/test/test_wrapper.py
--- a/spyvm/test/test_wrapper.py
+++ b/spyvm/test/test_wrapper.py
@@ -12,7 +12,7 @@
     cleanup_module(__name__)
 
 def new_frame():
-    return _new_frame(space, "")[0]
+    return _new_frame(space, "")[0].as_context_get_shadow(space)
 
 def test_simpleread():
     w_o = model.W_PointersObject(space, None, 2)
@@ -152,7 +152,7 @@
 
     def test_suspend_asleep(self):
         process, old_process = self.make_processes(4, 2, space.w_false)
-        w_frame = process.suspend(space.w_true)
+        process.suspend(space.w_true)
         process_list = wrapper.scheduler(space).get_process_list(process.priority())
         assert process_list.first_link() is process_list.last_link()
         assert process_list.first_link().is_nil(space)
@@ -168,7 +168,7 @@
         assert process_list.first_link() is process_list.last_link()
         assert process_list.first_link().is_nil(space)
         assert old_process.my_list().is_nil(space)
-        assert old_process.suspended_context() is current_context
+        assert old_process.suspended_context() is current_context.w_self()
         assert wrapper.scheduler(space).active_process() is process._w_self
 
     def new_process_consistency(self, process, old_process, w_active_context):
@@ -181,15 +181,16 @@
         assert priority_list.first_link() is process._w_self
 
     def old_process_consistency(self, old_process, old_process_context):
-        assert old_process.suspended_context() is old_process_context
+        assert old_process.suspended_context() is old_process_context.w_self()
         priority_list = wrapper.scheduler(space).get_process_list(old_process.priority())
         assert priority_list.first_link() is old_process._w_self
 
     def make_processes(self, sleepingpriority, runningpriority,
                              sleepingcontext):
+        if not isinstance(sleepingcontext, model.W_Object):
+            sleepingcontext = sleepingcontext.w_self()
         scheduler = wrapper.scheduler(space)
-        sleeping = new_process(priority=sleepingpriority,
-                               w_suspended_context=sleepingcontext)
+        sleeping = new_process(priority=sleepingpriority, w_suspended_context=sleepingcontext)
         sleeping.put_to_sleep()
         running = new_process(priority=runningpriority)
         scheduler.store_active_process(running._w_self)
diff --git a/spyvm/test/test_zin_squeak_4_5_image.py b/spyvm/test/test_zin_squeak_4_5_image.py
--- a/spyvm/test/test_zin_squeak_4_5_image.py
+++ b/spyvm/test/test_zin_squeak_4_5_image.py
@@ -42,10 +42,11 @@
 
     # create a frame for our newly crafted method with a valid sender (to avoid raising returnFromTop to early)
     s_initial_frame = create_method(chr(0x7c)).create_frame(space, w(0), [])
-    w_frame = w_method.create_frame(space, w(0), [], sender=s_initial_frame).w_self()
-
+    s_frame = w_method.create_frame(space, w(0))
+    s_frame.store_s_sender(s_initial_frame, raise_error=False)
+    
     try:
-        interp.loop(w_frame)
+        interp.loop(s_frame.w_self())
     except interpreter.ReturnFromTopLevel, e:
         assert e.object.as_string() == 'b2'
     except interpreter.StackOverflow, e:
@@ -67,11 +68,12 @@
                                             w('ensure'), space.w_BlockClosure])
 
     # create a frame for our newly crafted method with a valid sender (to avoid raising returnFromTop to early)
-    s_initial_frame = create_method(chr(0x7c)).create_frame(space, w(0), [])
-    w_frame = w_method.create_frame(space, w(0), [], sender=s_initial_frame).w_self()
-
+    s_initial_frame = create_method(chr(0x7c)).create_frame(space, w(0))
+    s_frame = w_method.create_frame(space, w(0))
+    s_frame.store_s_sender(s_initial_frame, raise_error=False)
+    
     try:
-        interp.loop(w_frame)
+        interp.loop(s_frame.w_self())
     except interpreter.ReturnFromTopLevel, e:
         assert e.object.as_string() == 'b1'
     except interpreter.StackOverflow, e:
diff --git a/spyvm/test/util.py b/spyvm/test/util.py
--- a/spyvm/test/util.py
+++ b/spyvm/test/util.py
@@ -81,11 +81,13 @@
         self._loop = True
         return interpreter.Interpreter.loop(self, w_active_context)
     
-    def stack_frame(self, s_new_frame, may_context_switch=True):
+    def stack_frame(self, s_new_frame, s_sender, may_context_switch=True):
         if not self._loop:
-            return s_new_frame # this test is done to not loop in test,
-                               # but rather step just once where wanted
-        return interpreter.Interpreter.stack_frame(self, s_new_frame, may_context_switch)
+            # this test is done to not loop in test, but rather step just once where wanted
+            # Unfortunately, we have to mimick some of the original behaviour.
+            s_new_frame.store_s_sender(s_sender, raise_error=False)
+            return s_new_frame
+        return interpreter.Interpreter.stack_frame(self, s_new_frame, s_sender, may_context_switch)
 
 class BootstrappedObjSpace(objspace.ObjSpace):
     
diff --git a/spyvm/tool/analyseimage.py b/spyvm/tool/analyseimage.py
--- a/spyvm/tool/analyseimage.py
+++ b/spyvm/tool/analyseimage.py
@@ -56,7 +56,7 @@
     w_method = s_class.lookup("tinyBenchmarks")
 
     assert w_method
-    w_frame = w_method.create_frame(interp.space, w_object, [])
+    w_frame = w_method.create_frame(interp.space, w_object)
     interp.store_w_active_context(w_frame)
 
     from spyvm.interpreter import BYTECODE_TABLE
diff --git a/spyvm/wrapper.py b/spyvm/wrapper.py
--- a/spyvm/wrapper.py
+++ b/spyvm/wrapper.py
@@ -82,37 +82,36 @@
         assert isinstance(w_frame, model.W_PointersObject)
         raise ProcessSwitch(w_frame.as_context_get_shadow(self.space))
 
-    def deactivate(self, w_current_frame):
-        self.put_to_sleep()
-        self.store_suspended_context(w_current_frame)
+    def deactivate(self, s_current_frame, put_to_sleep=True):
+        if put_to_sleep:
+            self.put_to_sleep()
+        self.store_suspended_context(s_current_frame.w_self())
 
-    def resume(self, w_current_frame):
+    def resume(self, s_current_frame):
         sched = scheduler(self.space)
         active_process = ProcessWrapper(self.space, sched.active_process())
         active_priority = active_process.priority()
         priority = self.priority()
         if priority > active_priority:
-            active_process.deactivate(w_current_frame)
-            return self.activate()
+            active_process.deactivate(s_current_frame)
+            self.activate()
         else:
             self.put_to_sleep()
-            return w_current_frame
 
     def is_active_process(self):
         return self._w_self.is_same_object(scheduler(self.space).active_process())
 
-    def suspend(self, w_current_frame):
+    def suspend(self, s_current_frame):
         if self.is_active_process():
             assert self.my_list().is_nil(self.space)
             w_process = scheduler(self.space).pop_highest_priority_process()
-            self.store_suspended_context(w_current_frame)
-            return ProcessWrapper(self.space, w_process).activate()
+            self.deactivate(s_current_frame, put_to_sleep=False)
+            ProcessWrapper(self.space, w_process).activate()
         else:
             if not self.my_list().is_nil(self.space):
                 process_list = ProcessListWrapper(self.space, self.my_list())
                 process_list.remove(self._w_self)
                 self.store_my_list(self.space.w_nil)
-            return w_current_frame
 
 class LinkedListWrapper(Wrapper):
     first_link, store_first_link = make_getter_setter(0)
@@ -212,24 +211,22 @@
 
     excess_signals, store_excess_signals = make_int_getter_setter(2)
 
-    def signal(self, w_current_frame):
+    def signal(self, s_current_frame):
         if self.is_empty_list():
             value = self.excess_signals()
             self.store_excess_signals(value + 1)
-            return w_current_frame
         else:
             process = self.remove_first_link_of_list()
-            return ProcessWrapper(self.space, process).resume(w_current_frame)
+            ProcessWrapper(self.space, process).resume(s_current_frame)
 
-    def wait(self, w_current_frame):
+    def wait(self, s_current_frame):
         excess = self.excess_signals()
         w_process = scheduler(self.space).active_process()
         if excess > 0:
             self.store_excess_signals(excess - 1)
-            return w_current_frame
         else:
             self.add_last_link(w_process)
-            return ProcessWrapper(self.space, w_process).suspend(w_current_frame)
+            ProcessWrapper(self.space, w_process).suspend(s_current_frame)
 
 class PointWrapper(Wrapper):
     x, store_x = make_int_getter_setter(0)
@@ -241,7 +238,7 @@
     startpc, store_startpc = make_int_getter_setter(constants.BLKCLSR_STARTPC)
     numArgs, store_numArgs = make_int_getter_setter(constants.BLKCLSR_NUMARGS)
 
-    def asContextWithSender(self, w_context, arguments):
+    def create_frame(self, arguments=[]):
         from spyvm import shadow
         w_outerContext = self.outerContext()
         if not isinstance(w_outerContext, model.W_PointersObject):
@@ -250,10 +247,8 @@
         w_method = s_outerContext.w_method()
         w_receiver = s_outerContext.w_receiver()
         pc = self.startpc() - w_method.bytecodeoffset() - 1
-        s_new_frame = shadow.MethodContextShadow(self.space, None, w_method, w_receiver,
-                     arguments, s_sender=w_context.get_shadow(self.space),
-                     closure=self, pc=pc)
-        return s_new_frame
+        return shadow.MethodContextShadow(self.space, w_method=w_method, w_receiver=w_receiver,
+                     arguments=arguments, closure=self, pc=pc)
 
     def tempsize(self):
         # We ignore the number of temps a block has, because the first
diff --git a/targetimageloadingsmalltalk.py b/targetimageloadingsmalltalk.py
--- a/targetimageloadingsmalltalk.py
+++ b/targetimageloadingsmalltalk.py
@@ -104,19 +104,11 @@
         return _run_benchmark(interp, 0, selector, "")
 
 def context_for(interp, number, benchmark, stringarg):
-    # XXX: Copied from interpreter >> perform
-    space = interp.space
-    argcount = 0 if stringarg == "" else 1
-    w_receiver = space.wrap_int(number)
-    w_selector = interp.perform(space.wrap_string(benchmark), "asSymbol")
-    w_method = model.W_CompiledMethod(space, header=512)
-    w_method.literalatput0(space, 1, w_selector)
-    w_method.setbytes([chr(131), chr(argcount << 5), chr(124)]) #returnTopFromMethodBytecodeBytecode
-    s_frame = shadow.MethodContextShadow(space, None, w_method, w_receiver, [])
-    s_frame.push(w_receiver)
-    if not stringarg == "":
-        s_frame.push(space.wrap_string(stringarg))
-    return s_frame
+    w_receiver = interp.space.wrap_int(number)
+    if stringarg:
+        return interp.create_toplevel_context(w_receiver, benchmark, interp.space.wrap_string(stringarg))
+    else:
+        return interp.create_toplevel_context(w_receiver, benchmark)
 
 def _usage(argv):
     print """
diff --git a/targettinybenchsmalltalk.py b/targettinybenchsmalltalk.py
--- a/targettinybenchsmalltalk.py
+++ b/targettinybenchsmalltalk.py
@@ -25,7 +25,7 @@
     w_object = model.W_SmallInteger(0)
     s_class = w_object.class_shadow(space)
     w_method = s_class.lookup(w_selector)
-    s_frame = w_method.create_frame(space, w_object, [])
+    s_frame = w_method.create_frame(space, w_object)
     return interp, s_frame
 
 interp, s_frame = setup()


More information about the pypy-commit mailing list