<div dir="ltr">Hi Chris,<br><div class="gmail_extra"><br><div class="gmail_quote">On Sun, Mar 13, 2016 at 11:42 AM, Chris Cunnington <span dir="ltr">&lt;<a href="mailto:brasspen@gmail.com" target="_blank">brasspen@gmail.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"> <br><div style="word-wrap:break-word"><br><div><blockquote type="cite"><div>On Mar 13, 2016, at 2:14 PM, tim Rowledge &lt;<a href="mailto:tim@rowledge.org" target="_blank">tim@rowledge.org</a>&gt; wrote:</div><br><div><span style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;float:none;display:inline!important">The clever bit is that the first part of the machine code we built for BoringClass&gt;theRubberPullets does a check that the class of the receiver is the same as the boringClassTag we loaded. If it is, no problem, carry on. If it is not then we abort to a routine that builds a PIC - a Polymorphic Inline Cache, see the 2nd URL above - moves things around a bit (not quite randomly) and once more rewrites the call so that now it jumps to the beginning of the PIC. And then we carry on again (isn’t that a neat title for a movie?) with  our processing.</span></div></blockquote></div><br><div>Right. This is the interesting part. But here’s the question: what’s different in an image from 5-7 years ago to an image now? Who is carrying this information? The receiver or the CompiledMethod? </div></div></blockquote><div><br></div><div>Nothing is different in the image.  The state is all in the VM.  At start-up an image contains only unjitted bytecoded methods, hence it can run on wither an Interpreter or a JIT VM; the initial state is the same; there is no machine code, not PICs, no inline caches, no nothing.</div><div><br></div><div>If you&#39;re running the image on an interpreter then the VM maintains its first-level method lookup cache (FLMLC) which records class x selectror -&gt; {method, primitiveFunctionOrNil} quads, which avoids ~ 97% of all class hierarchy lookups.  This state could be exposed through a primitive.  It would reveal something about the current working set of active receiver classes and selectors.  It&#39;s information would not be completely reliable.  The FLMLC is three-way set associative but small (1k entries in Cog VMs, 512 entries in the Interpreter (!!)) so there are frequent conflicts.</div><div><br></div><div>If you&#39;re running the image on a Cog JIT VM then the VM creates a machine-code twin for a bytecode method the second time it is used.  Actually, the criterion is that if a method is in the FLMLC and it has 60 literals or less then it is jitted into machine code.  Invisibly to the image, the header of the bytecoded method is changed to point to the machine code method, and the bytecoded method&#39;s header and the bytecoded method and its selector are stored in the machine coded method&#39;s header.  Let&#39;s call these methods BCM and MCM.</div><div><br></div><div>Each send bytecode in the BCM has a corresponding machine code send sequence in the MCM.  Ignoring the inlining of code like #+, the machine code send sequence in the MCM looks like</div><div><br></div><div>    classReg := #selector.</div><div>    call lookupRoutineForSendWith[0,1,2]Args</div><div><br></div><div>or, if there are 3 or more arguments</div><div><br></div><div>    sendNumArgsReg := N.</div><div>    classReg := #selector.</div><div>    call lookupRoutineForSendWithNArgs</div><div><br></div><div>This state is called an unlinked send.</div><div><br></div><div>When the lookup routine is run it looks up the method in the FLMLC, and if there, it JITs it. [ If not, it does a normal lookup, enters the method in the FLMLC and interprets it.  This way we don&#39;t waste time and space jitting methods we only run once, e.g. at start-up.  It&#39;s much faster to interpret once; jitting is like making a very slow single pass through the method interpreting it. ]. Once the new jitted method is available, the send site is rewritten to call it directly, and the selector load is replaced with a load of the class of the receiver of the send</div><div><br></div><div><div><br class="">    classReg := #ReceiversClassIndex.                 (or class, or compact class index in the pre-Spur VMs)</div><div>    call jittedMethodForClassAndSelector.entryPoint</div></div><div><br></div><div>, and then jumps into the newly jitted method to run it.  The next time the send is executed the VM will call jittedMethodForSelector.entryPoint directly. The entry-point gets the class of the _current_ receiver and compare it with that in the classReg.  If they&#39;re the same the method executes. </div><div><br></div><div>This state is called a monomorphic inline cache.  There is one class in the cache.</div><div><br></div><div><br></div><div> If the receiver class is different, when a monomorphic send is executed, a lookup occurs, and the send site is updated to a PIC with two entries, one for the first class and one for the new class.</div><div><br></div><div>A PIC grabs the class of the receiver and then compares it against constants, for the various classes.  It looks like</div><div><br></div><div><div><br class="">    classReg := #ReceiversClassIndex.                 (or class, or compact class index in the pre-Spur VMs)</div><div>    call PICSelector.entryPoint</div></div><div><br></div><div>PICForSelector.entryPoint</div><div>    receiverClass := receiver class.</div><div>    receiverClass == #Class1 ifTrue: [jump jittedMethodForClass1AndSelector.uncheckedEntryPoint].</div>    receiverClass == #Class2 ifTrue: [jump jittedMethodForClass2AndSelector.uncheckedEntryPoint].</div><div class="gmail_quote">    call extendClosedPIC</div><div class="gmail_quote"><br></div><div class="gmail_quote">Note that jittedMethodForClass1AndSelector and jittedMethodForClass2AndSelector might actually be the same method.</div><div class="gmail_quote"><br></div><div class="gmail_quote">This is a polymorphic send.</div><div class="gmail_quote"><br></div><div class="gmail_quote">I call these PICs &quot;closed PICs&quot; because they have a finite number of cases.  In Cog, the max size is 6.  Subsequent sends to receivers with different classes will extend the PIC up to 6 cases.  On encountering the 7th class the VM creates an &quot;open&quot; PIC.  This is machine code that does a FLMLC probe for the selector,class pair (and the selector is now a constant because this machine code is just for this selector) and either jumps to the machine code or invokes the interpreter for an unjitted method.<br><div><br></div><div>This is a megamorphic send; there are more than 6 classes at the send site, but the VM no longer records what they are, other than the unreliable information in the FLMLC.</div><div><br></div><div>As an optimization the VM keeps the set of open PICs on a linked list, and if a monomorphic send misses and the open PIC list contains an open PIC with the monomorphic send&#39;s selector, the VM binds directly to the open IC, rather than creating a closed PIC, because in most cases a polymorphic send of a megamorphic selector will soon become megamorphic.</div><div><br></div><div>Now, all the above optimization ends up recording class information in the inline caches which are in send sites in MCMs and each send in an MCM corresponds to a send bytecode in a BCM.  So what we do in Sista is add a primitive that answers the state of the inline caches in a BCM, in a form that hides the MCM reality.  Let&#39;s look at an example:</div><div><br></div><div><br></div><div>Here&#39;s a little doit:</div><div><br></div><div>1 to: 4 do: [:i| (#(1 1.0 #one &#39;one&#39;) at: i) class].<br></div><div><br></div><div>It sends #&lt;= and #+ hidden in the inlining of the to:do:.  It sends #at: to the array, and it sends #species to a SmallInteger, a BoxedFloat64, a ByteSymbol and a ByteString (I use #species, not #class because the VM inlines #class so there is no send if we use it).  It contains a conditional branch that tests the result of #&lt;=.  Here&#39;s the bytecode:</div><div><br></div><div><div>57 &lt;76&gt; pushConstant: 1</div><div>58 &lt;68&gt; popIntoTemp: 0</div><div>59 &lt;10&gt; pushTemp: 0</div><div>60 &lt;22&gt; pushConstant: 4</div><div>61 &lt;B4&gt; send: &lt;=</div><div>62 &lt;AC 0B&gt; jumpFalse: 75</div><div>64 &lt;21&gt; pushConstant: #(1 1.0 #one &#39;&#39;one&#39;&#39;)</div><div>65 &lt;10&gt; pushTemp: 0</div><div>66 &lt;C0&gt; send: at:</div><div>67 &lt;D0&gt; send: species</div><div>68 &lt;87&gt; pop</div><div>69 &lt;10&gt; pushTemp: 0</div><div>70 &lt;76&gt; pushConstant: 1</div><div>71 &lt;B0&gt; send: +</div><div>72 &lt;68&gt; popIntoTemp: 0</div><div>73 &lt;A3 F0&gt; jumpTo: 59</div><div>75 &lt;78&gt; returnSelf</div></div><div><br></div><div>Here&#39;s the actual object the send and branch data primitive answers for an execution of the above:</div><div><br></div><div>{#(62 5 4).</div><div>   {66 . Array . (Object&gt;&gt;#at: &quot;a CompiledMethod(1789611)&quot;)} .</div><div>   {67 . SmallInteger . (Object&gt;&gt;#species &quot;a CompiledMethod(3005395)&quot;) .</div><div>            BoxedFloat64 . (Object&gt;&gt;#species &quot;a CompiledMethod(3005395)&quot;) .</div><div>            ByteSymbol . (ByteSymbol&gt;&gt;#species &quot;a CompiledMethod(3876413)&quot;) .</div><div>            ByteString . (Object&gt;&gt;#species &quot;a CompiledMethod(3005395)&quot;)} } . &#39;<br></div><div><br></div><div>So what does this say?  The entry for bytecode 62 &lt;AC 0B&gt; jumpFalse: 75 has been executed 5 times and taken 4 times.  We count conditional branches so that ofetn executed code calls back into Smalltalk to let Sista run, do its analysis, optimize, and continue in optimized code.</div><div><br></div><div>The entry for bytecode 66 &lt;C0&gt; send: at: has one class, Array, and for that class the method invoked is Object&gt;&gt;#at:.</div><div>The entry for bytecode 67 &lt;D0&gt; send: species as four classes, SmallInteger, BoxedFloat64, ByteSymbol and ByteString, and all of them happen to invoke Object&gt;&gt;#species.</div><div><br></div><div>So the realities of machine code are completely hidden form the image.  it sees only bytecodes, and indeed it optimizes to bytecodes.  Only the JIT converts those bytecodes to machine code, and it hides the details, mapping back all the infrmation into the portable machine-independent bytecode representation.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><div>The answer (as I understand it) is that the CompiledMethod is carrying a cue for the JIT. Something is going on there. The receiver doesn’t know anything, I don’t think. (I appreciate the process is going to ping it. But the process is not starting there.)<br></div></div></blockquote><div><br></div><div>Right.  The information is in send sites.  And it is reified through a primitive that maps the information back to the bytecoded representation of methods.  </div><div><br></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><div></div><div>There’s been lots of talk about byte codes and new byte code set, but that’s not it. If I explore a CompiledMethod in an old image or a new one, it’s not going to show me when a PIC is activating. The header gets incinerated and replaced with machine code. And then there’s a check to see if the header is now machine code ash called #isCogMethodReference:. That is the bifurcation between the old way and the new. If Yes, then go to JIT related code. If No, then it looks like it always has. <br></div></div></blockquote><div><br></div><div>Yes.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><div></div><div>Thank you for the links. I’ll check them out in a bit. I think I’m reaching saturation for now. <br></div><div><br></div><div>Chris </div></div></blockquote></div><br><div 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>