[Vm-dev] Incomprehendable behaviour of ContextPart class
>>#contextOn:do:
Eliot Miranda
eliot.miranda at gmail.com
Sun May 19 17:29:13 UTC 2013
On Sat, May 18, 2013 at 3:49 PM, Lars <lars.wassermann at googlemail.com>wrote:
>
> Hi Eliot,
> thank you for the analysis.
> The question is why the code works with the VM, but not if we step through
> it. What does the VM do that makes this behave correctly, although the
> temps-array is removed from the stack prior to returning from
> #contextOn:do:?
>
the vm doesn't do anything special (* with one exception) because even
though the image code pops off the temps array the return pushes it back
almost immediately, before the temps array is used.
* the exception is that when a context is created for a stack frame the
code miust handle the fact that the stack may be empty and so the proxy
context must have a nil where the missing stack entry is, not an
uninitialized field. See the comment
in updateStateOfSpouseContextForFrame:WithSP:.
I ask because I need to recreate that behaviour in case it also appears in
> other places. The general project is creating a squeak research VM. That
> is also why I try to refrain from changing the image, retaining
> compatibility with Pharo/Squeak trunk.
>
> Thank you, Lars
>
> 2013/05/17 8:08 pm Eliot Miranda <eliot.miranda at gmail.com><eliot.miranda at gmail.com>
> :
>
>
>
> Hi Lars,
>
> On Fri, May 17, 2013 at 9:36 AM, Lars Wassermann <
> lars.wassermann at googlemail.com> wrote:
>
>>
>> Hello VM-dev,
>> I habe problems understanding what exactly happens when I step through:
>> (ContextPart contextOn: Error do: []) halt
>> in a workspace.
>> When I step over the parenthesis in the debugger, everything works kind
>> of like expected (the context of contextOn:do: misses one temporary but the
>> returned value is what the methods description promised). When I step into
>> or through the code, an error is raised when executing bytecode 54 of
>> #contextOn:do:, because the temps-array illegally has been poped (by
>> bytecode 53).
>>
>> My question now is:
>> Why does the normal vm not only break when runing this code, but also
>> return the correct value (the first element of the last temporary).
>> I appended a graphic trying to visualize what happens up to the miracle
>> of three skipped bytecodes.
>>
>
> Ugh. I hate this method. Actually I hate ContextPart>>jump, which is
> the really evil method.
>
> So let's take a look at the methods:
>
> ContextPasrt class>>contextOn: exceptionClass do: block
> "Create an #on:do: context that is ready to return from executing its
> receiver"
>
> | ctxt chain |
> ctxt := thisContext.
> [chain := thisContext sender cut: ctxt. ctxt jump]
> on: exceptionClass
> do: block.
> "jump above will resume here without unwinding chain"
> ^ chain
>
> answers a context that is an activation of on:do: ready to catch the
> supplied exception. It is used by ContextPart>>runUntilErrorOrReturnFrom:
> to insert an exception handler for UnhandledError immediately beneath the
> current context being executed by the debugger.
>
> The on:do: argument is a little unclear. It is easier to understand if
> it reads
>
> ContextPasrt class>>contextOn: exceptionClass do: block
> "Create an #on:do: context that is ready to return from executing its
> receiver"
>
> | ctxt onDoContext |
> ctxt := thisContext.
> [onDoContext := thisContext sender.
> onDoContext cut: ctxt.
> ctxt jump]
> on: exceptionClass
> do: block.
> "jump above will resume here without unwinding chain"
> ^ onDoContext
>
> So the idea is to run until in the on:do:'s receiver (the [onDoContext
> := ...] block), grab the sender (the on:do: activation), remove intervening
> contexts (onDoContext cut: ctxt) so that on:do:'s handlerActive doesn't get
> set, resume executing in the contextOn:do: context (ctxt jump) and answer
> the onDoContext.
>
> The horror is in ContextPart>>jump:
>
> ContextPart>>jump
> "Abandon thisContext and resume self instead (using the same current
> process). You may want to save thisContext's sender before calling this so
> you can jump back to it.
> Self MUST BE a top context (ie. a suspended context or a abandoned
> context that was jumped out of). A top context already has its return
> value on its stack (see Interpreter>>primitiveSuspend and other suspending
> primitives).
> thisContext's sender is converted to a top context (by pushing a nil
> return value on its stack) so it can be jump back to."
>
> | top |
> "Make abandoned context a top context (has return value (nil)) so it can
> be jumped back to"
> thisContext sender push: nil.
>
> "Pop self return value then return it to self (since we jump to self by
> returning to it)"
> stackp = 0 ifTrue: [self stepToSendOrReturn].
> stackp = 0 ifTrue: [self push: nil]. "must be quick return
> self/constant"
> top := self pop.
> thisContext privSender: self.
> ^ top
>
> This works by returning to self (a MethodContext), but return pushes a
> result. So the code pops the top thing off the stack and returns it. This
> is evil. If the stack is empty then this will pop the receiver, return it
> and push it back in the return (!!). If the stack contains only temps then
> the last temp is popped and pushed by the return (!!). This is what you're
> seeing. The stack's last temp is an indirection vector. It gets popped
> off the stack by
> top := self pop.
> at which point any attempt to look at self's temps in a debugger will
> cause an error because the indirection vector is no longer there. It then
> gets pushed back by=
> ^ top
>
> Horrible. It all depends on being able to create a temporary invalid
> execution state in a context.
>
> A better way to do this is by process switch, avoiding the return. For
> example, in my basic block profiler I handle an unknownBytecode error
> caused by executing the unknownBytecode that overwrites the bytecode at the
> beginning of each basic block, replaces the unknownBytecode with the
> correct bytecode, and continues:
>
> ContextPart>>unusedBytecode
> "Handle unusedBytecode by replacing the bytecode with the
> correct one found in the coverage property and continuing.
> Continue via wait/signal since return would push a result."
> | coverage semaphore process |
> self assert: (method at: pc) = method encoderClass unusedBytecode.
> coverage := method propertyValueAt: #coverage.
> self assert: coverage notNil.
> self assert: (coverage includesKey: pc).
> semaphore := Semaphore new.
> process := Processor activeProcess.
>
> [method
> at: pc
> put: (coverage removeKey: pc).
> process suspendedContext unwindTo: self.
> process suspendedContext: self.
> semaphore signal] fork.
>
> semaphore wait
>
> So that could become something like
>
> jump
> "Abandon thisContext and resume self instead (using the same current
> process). You may want to save thisContext's sender before calling this so
> you can jump back to it.
> Self MUST BE a top context (ie. a suspended context or a abandoned
> context that was jumped out of). A top context already has its return
> value on its stack (see Interpreter>>primitiveSuspend and other suspending
> primitives)."
> | semaphore process |
> semaphore := Semaphore new.
> process := Processor activeProcess.
>
> [process suspendedContext unwindTo: self.
> process suspendedContext: self.
> semaphore signal] fork.
>
> semaphore wait
>
> but perhaps the process switch will introduce other problems. I don't
> know.
>
> Hope this helps.
>
>
>> I replicated the behavior in Squeak 4.0 and 4.4 images on interpreter VMs
>> 3.7.7, 4.10, and a newer cog VM.
>>
>>
>> A fix to the problem of stepping and comprehension as well as the missing
>> temp in the frame would be changing the method to:
>>
>> contextOn: exceptionClass do: block
>> "Create an #on:do: context that is ready to return from executing
>> its receiver"
>>
>> | ctxt chain |
>> ctxt := thisContext.
>> [chain := thisContext sender cut: ctxt.
>> ctxt push: nil. ctxt jump] on: exceptionClass do: block.
>> "jump above will resume here without unwinding chain"
>> ^ chain
>>
>> The difference is that after cutting off ctxt, we ensure that it is a top
>> context by pushing a meaningless value. But this still doesn't help me
>> understanding the original behaviour.
>>
>> Thank you and all the best
>> Lars
>>
>>
> --
> best,
> Eliot
>
> --
best,
Eliot
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.squeakfoundation.org/pipermail/vm-dev/attachments/20130519/4d3d6559/attachment.htm
More information about the Vm-dev
mailing list