[Vm-dev] Materializing BlockClosure's outerContext?

Eliot Miranda eliot.miranda at gmail.com
Fri Jan 4 19:07:57 UTC 2019


Hi Fabio,  Hi All,

On Fri, Jan 4, 2019 at 9:11 AM Fabio Niephaus <lists at fniephaus.com> wrote:

>
> Hi all,
>
> I'm trying to understand when the outerContext of a BlockClosure is
> materialized in Cog. I'm assuming Cog does much better than materializing
> the context at allocation time of the BlockClosure, but I wasn't able to
> find that out by browsing VMMaker code.
>

Unless Sista is being used then the outer context is always materialized
whenever a closure is created.  In the StackInterpreter the two methods
called by the actual bytecode routines
are pushClosureNumArgs:copiedValues:blockSize:
and pushFullClosureNumArgs:copiedValues:compiledBlock:receiverIsOnStack:ignoreContext:.
You'll see that the first thing pushClosureNumArgs:copiedValues:blockSize:
does is ensure the current frame is married, i.e. it materializes the
context if it isn't already.  In
pushFullClosureNumArgs:copiedValues:compiledBlock:receiverIsOnStack:ignoreContext:
the first thing that happens is the same unless the ignoreContext flag has
been set, in which case the outerContext will be nil.

In Cog the JIT generates code to materialize as quickly as possible.
See CogObjectRepresentationForSpur>>#genGetActiveContextLarge:inBlock:
which is sent four times to create four trampolines ceSmallMethodContext,
ceSmallBlockContext, ceSmallFullBlockContext, and ceLargeMethodContext.
These are used in turn by
CogObjectRepresentationForSpur>>#genGetActiveContextNumArgs:large:inBlock:,
which is used by
CogObjectRepresentationForSpur>>#genNoPopCreateClosureAt:numArgs:numCopied:contextNumArgs:large:inBlock:
&
CogObjectRepresentationForSpur>>#genCreateFullClosure:numArgs:numCopied:ignoreContext:contextNumArgs:large:inBlock:,
which generates the JIT code that parallels the StackInterpreter bytecodes.

Some rationale:
In VW there is no adaptive optimization and so no closure4 inlining.
Instead there are three different kinds of blocks:

clean: created at compile time, no copied values, only arguments, and hence
no need for an outer context.  If in a clean closure in a debugger there is
no information as to where the closure was activated; only its static
method name is known.
copying: created at run-time, but since there is no up-arrow return there
is no need to create an outer context.  If in a copying closure in a
debugger there is no information as to where the closure was activated;
only its static method name is known.
full: created at run-time; requires the outerContext to be materialized

When I came to add closures to Squeak (good in itself but also essential in
implementing a sane context-to-stack mapping scheme) there were only 8
unused bytecodes, and two of these were already being used in a Newspeak
implementation I had affection for.  Qwaq wanted to keep things as simple
as possible, as did I.  Therefore the logical thing to do was to only
provide closures that always had an outerContext; KISS.  Implementing
closures used 5 bytecodes (create closure, create indirection vector,
push/store/pop indirect), leaving three to spare (at this time I had no
idea about a Sista bytecode set or about multiple bytecode sets). Knowing
that at some stage adaptive optimization would be a much better approach
than simply micro-optimizing closure creation, with all the extra
complexity and infidelity in the debugger, and that I could optimize
materialization aggressively, I think I made the right decision.  We can
still implement the equivalent of clean blocks in the compiler (and indeed
someone has done this in Pharo, and with full blocks it would be easy to do
in Squeak).  But much more interesting is to finish Sista/Scorch.  To this
end I'm currently frustrated by not being able to load Clément's tonel
Scorch repository into Squeak.

Why do I want too be able to load Sista in Squeak right now? (Forgive me if
I've already told you this).  Clément has been developing Scorch (Sista is
the name for the overall architecture, Scorch is the name of the
image-level optimizing compiler) in a live Pharo image.  That means any
bugs crash his system and progress is slow.  But the simulator can be
modified to intercept the counter callback and instead of delivering it
into the image being simulated can deliver it to Scorch in the current
image.  The simulator can provide "facade" proxy context objects for
contexts in the simulation.  These appear to be contexts, but are actually
proxies for objects in the simulation (we do the same for methods so we can
use InstructionPrinter, StackDepthFinder et al on methods in the
simulation).  And we can rewrite the back end of Scorch, the part that
installs methods into method dictionaries, to use a mirror object.  Then we
can substitute a mirror that materializes a method in the simulation and
invokes code in the simulation to install the method there-in.

So this allows Scorch to be developed as intended, in the current image,
but have it affect only the simulation, and therefore be immune from
crashing the current system.  This should speed up productization a lot,
and will allow Sophie and I to work on register allocation in the Sista
JIT.  So any energy anyone can put into getting Scorch to load into Squeak
would be much appreciated.  I shall return to it as soon as I've finished
assuring myself I can determine conditional branch dominators with a simple
FSM in bytecode (this is part of the register allocation problem).


> Example: In `BlockClosureTest>>#setUp`, a BlockClosure is stored in an
> inst var and then accessed later in some tests. Assuming contexts are only
> materialized when necessary, how does Cog determine that the context of
> this closure has escaped? In the previous example, thisContext is also
> stored in an inst var. Whenever a context is stored like this, we must
> consider it escaped. Does the same apply for closures? As in, must we
> consider their home context escaped as soon as closures are stored
> somewhere?
>

As Clément stated, there is no escape analysis at the standard bytecode
compiler/JIT level.  Escape analysis is done in the Scorch optimizer (and
if it doesn't yet, that's where we will do it when we get to it).


> Cheers,
> Fabio
>

_,,,^..^,,,_
best, Eliot
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.squeakfoundation.org/pipermail/vm-dev/attachments/20190104/8034af71/attachment-0001.html>


More information about the Vm-dev mailing list