<div dir="ltr"><br><br><div class="gmail_quote"><div dir="ltr">On Wed, 29 Aug 2018 at 11:08, Eliot Miranda <<a href="mailto:eliot.miranda@gmail.com">eliot.miranda@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> <div dir="ltr"><div class="gmail_quote"><div>Well, the first thing to say is that this is a magnificent diagram; thank you.  </div></div></div></blockquote><div><br></div><div>Your welcome.  Actually I tried to avoid doing it because I've got some other work to go on with ;)</div><div>but I got sucked in :).   Its a side effect of how I *need* to approach understanding complex problems.</div><div>My eyes were glazing over getting lost with the tree of senders and creating such a chart makes me pay attention to details.</div><div>btw, I created it with cross platform <a href="https://www.yworks.com/products/yed">https://www.yworks.com/products/yed</a>  </div><div>(although I should be using Roassal to ensure completeness, doing it manually provides time to aborb details)</div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><div>The problem is that the VM, and hence the diagram, is much more complex than the blue book specification, essentially because the VM is a highly optimized interpreter, whereas the specification is bare bones.  So I would ask you, and anyone else who wants to understand a Smalltalk-80 VM (a VM that provides Context objects for method activations, rather than a Smalltalk that uses a more conventional stack model) to read the Blue Book Specification:<a href="http://www.mirandabanda.org/bluebook/bluebook_chapter28.html" target="_blank"> http://www.mirandabanda.org/bluebook/bluebook_chapter28.html</a> carefully and fully. This is the last section of Smalltalk-80: The Language and its Implementation, by Adele Goldberg and David Robson.  The specification is well-written and clear and once digested serves as essential reference for understanding a more complex production VM.</div></div></div></blockquote><div><br></div><div>I've been meaning to do this for a while, so took the opportunity just now to read chapters 28 & 29.  </div><div>I'm split my insights from that into a few posts.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><div><br></div><div>Now to your question: "I'm not at all clear about how internalExecuteNewMethod selects between internalQuickPrimitiveResponse and slowPrimitiveResponse, and what is the difference between them?".</div><div><br></div><div>First, in a system that notionally allocates a context object to hold every activation, leaf routines are extremely expensive if all they do is answer a constant or an instance variable.  Dan Ingall's optimization is to avoid activations by providing a set of quick primitives that answer an instance variable whose slot index is from 0 to 255, or self, nil, true, false, -1, 0, 1 & 2.  The self, nil, true, false, -1, 0, 1 & 2 constants are derived from a static frequency analysis of literals and variable references in Smalltalk code and are echoed in the original bytecode set, byte codes 112-119 bering 01110iii Push (receiver, true, false, nil, -1, 0, 1, 2) [iii].  internalQuickPrimitiveResponse handles precisely these primitives, and these primitives only.  These primitives have the property that they can never fail, so they are invoked along a path that does not reset the flag used to identify primitive failure, nor test it.</div></div></div></blockquote><div><br></div><div>I see BlueBook p605 & p620 code these primitives like this...</div><div><br></div><div>BlueBook Interpreter >> executeNewMethod    "no separate quick/slow primitive handling here"</div><div>    self primitiveResponse</div><div>        ifFalse: [self activateNewMethod] </div><div><br></div><div>BlueBook Interpreter >> primitiveResponse </div><div>    | flagValue thisReceiver offset | </div><div>    primitiveIndex = 0 </div><div>        ifTrue: [     "quick primitives"</div><div>            flagValue := self flagValueOf: newMethod. </div><div>            flagValue = 5 ifTrue: [self quickReturnSelf.   ^true]. </div><div>            flagValue = 6 ifTrue: [self quickInstanceLoad. ^true].  "Quick return inst vars"</div><div>            ^false] </div><div>        ifFalse: [    "slow primitives"</div><div><span style="white-space:pre">                </span>    self initPrimitive. </div><div>            self dispatchPrimitives. </div><div>            ^ self success]  </div><div><br></div><div><div>BlueBook Interpreter >> quicklnstanceLoad  "Quick return inst vars, push instance variable whose slot index is from 0 to 255"</div><div>    | thisReceiver fieldIndex |</div><div>    thisReceiver := self popStack. </div><div>    fieldlndex := self fieldlndexOf: newMethod. </div><div>    self push: (memory fetchPointer: fieldlndex ofObject: thisReceiver) </div></div><div><br></div><div><div>So it looks like {nil, true, false, -1, 0, 1 & 2} were not handled. Although I see them handled by StackInterpreter.</div><div>I noted that StackInterpreter dispensed with "flagValue" and just used primitive number to identify individual quick variables.  <br></div><div><br></div><div><div>StackInterpreter >> externalQuickPrimitiveResponse</div><div><span style="white-space:pre">  </span>"Invoke a quick primitive.</div><div><span style="white-space:pre">       </span> Called under the assumption that primFunctionPtr has been preloaded"</div><div><span style="white-space:pre">    </span></div><div><span style="white-space:pre">      </span>| localPrimIndex |</div><div><span style="white-space:pre">    </span>self assert: self isPrimitiveFunctionPointerAnIndex.</div><div><span style="white-space:pre">  </span>localPrimIndex := self cCoerceSimple: primitiveFunctionPointer to: #sqInt.</div><div><span style="white-space:pre">    </span>self assert: (localPrimIndex > 255 and: [localPrimIndex < 520]).</div><div><span style="white-space:pre">        </span>"Quick return inst vars"</div><div><span style="white-space:pre">    </span>localPrimIndex >= 264 ifTrue:</div><div><span style="white-space:pre">              </span>[self pop: 1 thenPush: (objectMemory fetchPointer: localPrimIndex - 264 ofObject: self stackTop).</div><div><span style="white-space:pre">             </span> ^true].</div><div><span style="white-space:pre">      </span>"Quick return constants"</div><div><span style="white-space:pre">    </span>localPrimIndex = 256 ifTrue: [^true "return self"].</div><div><span style="white-space:pre"> </span>localPrimIndex = 257 ifTrue: [self pop: 1 thenPush: objectMemory trueObject. ^true].</div><div><span style="white-space:pre">  </span>localPrimIndex = 258 ifTrue: [self pop: 1 thenPush: objectMemory falseObject. ^true].</div><div><span style="white-space:pre"> </span>localPrimIndex = 259 ifTrue: [self pop: 1 thenPush: objectMemory nilObject. ^true].</div><div><span style="white-space:pre">   </span>self pop: 1 thenPush: (objectMemory integerObjectOf: localPrimIndex - 261).</div><div><span style="white-space:pre">   </span>^true</div></div><div><br></div><div>And distinguishing between quick/slow primitives moved out to executeNewMethod...</div><div><br></div></div><div><div>StackInterpreter >> executeNewMethod<br></div><div><span style="white-space:pre"> </span>"Execute newMethod - either primitiveFunctionPointer must be set directly</div><div><span style="white-space:pre">        </span> (i.e. from primitiveExecuteMethod et al), or it would have been set probing</div><div><span style="white-space:pre">  </span> the method cache (i.e. primitivePerform et al)."</div><div><span style="white-space:pre">        </span>primitiveFunctionPointer ~= 0 ifTrue:</div><div><span style="white-space:pre">         </span>[self isPrimitiveFunctionPointerAnIndex ifTrue:</div><div><span style="white-space:pre">                       </span>[self externalQuickPrimitiveResponse.</div><div><span style="white-space:pre">                 </span> ^nil].</div><div><span style="white-space:pre">               </span> self slowPrimitiveResponse.</div><div><span style="white-space:pre">          </span> self successful ifTrue: [^nil]].</div><div><span style="white-space:pre">     </span>"if not primitive, or primitive failed, activate the method"</div><div><span style="white-space:pre">        </span>self activateNewMethod</div><br class="gmail-Apple-interchange-newline"></div><div><br></div><div>Having soaked all that up, I've now better identified my difficulty understanding </div><div>"how executeNewMethod distinguishes between quick and slow primitives"</div><div><br></div><div>I was looking at isPrimitiveFunctionPointerAnIndex (called from internalExecuteMethod)</div><div>seeing it only checked   ```primitiveFunctionPointer <= 520```   to identify quick primitives,</div><div>while quick primitives were between 256 to 520.  i.e. not checking ```primitiveFunctionPointer >=256```</div><div><br></div><div><div>StackInterpreter class >> initializePrimitiveTable<span style="white-space:pre">                 </span></div><div><span style="white-space:pre">              </span>"Quick Push Const Methods primitiveIndex=0"</div><div><span style="white-space:pre">         </span>(256 nil) "primitivePushSelf"</div><div><span style="white-space:pre">               </span>(257 nil) "primitivePushTrue"</div><div><span style="white-space:pre">               </span>(258 nil) "primitivePushFalse"</div><div><span style="white-space:pre">              </span>(259 nil) "primitivePushNil"</div><div><span style="white-space:pre">                </span>(260 nil) "primitivePushMinusOne"</div><div><span style="white-space:pre">           </span>(261 nil) "primitivePushZero"</div><div><span style="white-space:pre">               </span>(262 nil) "primitivePushOne"</div><div><span style="white-space:pre">                </span>(263 nil) "primitivePushTwo"</div><div><span style="white-space:pre">                </span>"Quick Push Inst Var Methods"</div><div><span style="white-space:pre">               </span>(264 519 nil) "primitiveLoadInstVar"</div></div><div><br></div><div>But I now I realise that I didn't properly read/understand the comments marked *** here...</div><div><br></div><div>StackInterpreter >> isPrimitiveFunctionPointerAnIndex</div><div><span style="white-space:pre">   </span>"We save slots in the method cache by using the primitiveFunctionPointer</div><div><span style="white-space:pre"> </span> to hold  ***either*** a function pointer or the index of a quick primitive. <br>         ***Since quick primitive indices are small they can't be confused with function addresses*** ." </div><div><span style="white-space:pre">      </span>^(self cCoerce: primitiveFunctionPointer to: #'usqIntptr_t') <= MaxQuickPrimitiveIndex</div><div><br></div><div>I haven't dug into where primitiveFunctionPointer is set, but I now get the general idea.</div><div>cheers -ben</div></div></div>