<div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><span style="color:rgb(0,0,0)">Hi Fabio,  Hi All,</span><br></div><br><div class="gmail_quote"><div dir="ltr">On Fri, Jan 4, 2019 at 9:11 AM Fabio Niephaus <<a href="mailto:lists@fniephaus.com">lists@fniephaus.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"> <div dir="ltr">Hi all,<div><br></div><div>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.</div></div></blockquote><div><br></div><div style="color:rgb(0,0,0)">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.</div><div style="color:rgb(0,0,0)"><br></div><div style="color:rgb(0,0,0)">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.</div><div style="color:rgb(0,0,0)"><br></div><div style="color:rgb(0,0,0)">Some rationale:</div><div style="color:rgb(0,0,0)">In VW there is no adaptive optimization and so no closure4 inlining.  Instead there are three different kinds of blocks:</div><div style="color:rgb(0,0,0)"><br></div><div style="color:rgb(0,0,0)">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.</div><div style="color:rgb(0,0,0)">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.</div><div style="color:rgb(0,0,0)">full: created at run-time; requires the outerContext to be materialized</div><div style="color:rgb(0,0,0)"><br></div><div style="color:rgb(0,0,0)">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.</div><div style="color:rgb(0,0,0)"><br></div><div style="color:rgb(0,0,0)">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.</div><div style="color:rgb(0,0,0)"><br></div><div><span style="color:rgb(0,0,0)">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).</span></div><div><span style="color:rgb(0,0,0)"></span> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div>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?<br></div></div></blockquote><div><br></div><div>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).</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div></div><div>Cheers,<br></div><div>Fabio</div></div>
</blockquote></div><div><br></div><div dir="ltr" class="gmail_signature"><div dir="ltr"><div><span style="font-size:small;border-collapse:separate"><div>_,,,^..^,,,_<br></div><div>best, Eliot</div></span></div></div></div></div></div></div>