[Vm-dev] When Cog should deoptimize CM and when its not?

Eliot Miranda eliot.miranda at gmail.com
Mon Mar 11 22:19:54 UTC 2013


On Fri, Mar 8, 2013 at 2:34 PM, Igor Stasenko <siguctua at gmail.com> wrote:
>
> Hi, Eliot & all
>
> I was looking at #become: code
> and did not found the place where it deoptimizing compiled methods to
> throw away the jited version of it (if it has one),
> before even trying to do anything with it.
>
> i know it pricey, because it should:
>
>  - remove CogMethod from JIT zone
>  - walk the stack and look for all activations of it, and replace
> native code instruction pointer with bytecode instruction pointer
>  - fix the compiled method's header
> (another alternative, if method has activations, i would fail
> primitive actually, because this is too hard to do things right and
> very dangerous
> to play with, as well as it makes little sense )
>
> IMO, if we add that, then we could minimize the impact of JIT
> complexity on #become: and make things work
> in same way as in stack vm (which is stable ;).

Yes, but I don't want to do this, precisely because it's so expensive
and complex, /and/ shouldn't happen.  I mean, if you become: a method
in the interpreter and you change the code there's a good chance it's
going to break, because the VM can't do anything to map the execution
state in a context from old method to new.  In other words, it's not
worth doing anything other than exiting with a useful error message,
and that's what the VM does now.


So I came to the conclusion that it's just not worth the effort to
deoptimize.  Instead, the VM checks that the code hasn;t changed (e.g.
only trailer bytes have changed) and if so does the become, and if not
exits with an error message.  If you really want to do become or
destructive update on a method (as I do with my breakpoint
implementation at Cadence) you can use CompiledMethod>voidVMState.

Does that make sense?

> And since become scans part/whole heap.. deoptimizing the CM would not
> add too much on top :)
>
> I don't know if it is there, maybe it is.. then sorry for first part..
> but not the second one:
>
>
> objectAt: index put: value
>         "Primitive. Store the value argument into a literal in the receiver. An
>         index of 2 corresponds to the first literal. Fails if the index is less than 2
>         or greater than the number of literals. Answer the value as the result.
>         Normally only the compiler sends this message, because only the
>         compiler stores values in CompiledMethods. Essential. See Object
>         documentation whatIsAPrimitive."
>
>         <primitive: 69>
>         self primitiveFailed
>
> i was playing with metaprogramming , using
> #literalAt:put:
> to store/change some value directly in method's literal (yeah.. you
> can kill the messenger and stop reading further ;) .
>
> And i found that the change is not immediately visible..
>
> My idea is plain simple:
>  - you have a template method, like:
>
>  foo
>    ^ 'ssssss'
>
> and then i simply doing things like:
>
>  copy := foo copy literalAt:1 put: 'bar'.
>  methodDict at: #bar put: copy
>
> and when i wanna change the #bar value next time, i don't creating new
> method again, but just modifying
> its literal.
> Things worked perfectly on old interpreted VMs and i used this
> trickery to implement prototypes (where object's properties
> are held by correspoding CM )
>
> But with Cog, because of its aggressive caching, it no longer working:
>
> at: aName put: value
>         | method |
>
>         (getter methodDict includesKey: aName) ifTrue: [
>
>                 method := getter methodDict at: aName.
>                 method literalAt: 1 put: value.
>                 method flushCache.
>
>         ] ifFalse: [
>
>                 method := getter addReadAccessorFor: aName value: value.
>                 setter addWriteAccessorFor: aName readAccessor: method.
>         ].
>         aName flushCache.
>
>         ^ value
>
> here neither "method flushCache. " nor "aName flushCache." helps with it..
> my test still fails.
> But of course, when i click to debug , it magically passes when i
> evaluating very same expression in debugger :)
>
> Things working only when i always replacing things with new method(s)
> (as i doing under under ifFalse: clause).
>
> I strongly suggest that <primitive: 69> should always deoptimize CM
> (throw away Cogged method , even if it has high price to pay)
> as well as #flushCache (<primitive: 116>) should also do the same, because
>   - JITed code is a cache
>   - we should have a means to throw it away when asked so
>
> The #voidCogVMState flushes all machine code.. but is it possible to
> make more could we have one which flushing only particular one?
>
> (...in alternative universe, i'd rather look for a way how to
> "deoptimize Jited method in smart way, so when you deoptimizing the
> CM,
> it won't require scanning stacks to patch instruction pointers, and
> instead mark/make jited code obsolete by:
>
> - patching the machine code at entry point to immediately switch back
> to interpreter mode of corresponding method (to prevent spawning new
> activations with native code)
>  - patching the machine code at every possible return address (call
> sites) to immediately switch to interpreter (to gracefully handle
> existing activations so, there is no need to fix instruction pointers)
>  - keep that obsolete CogMethod till next GC.
>  ;)
>
>
>
> --
> Best regards,
> Igor Stasenko.



-- 
best,
Eliot


More information about the Vm-dev mailing list