<div dir="ltr">Hi Clément,<div class="gmail_extra"><br><div class="gmail_quote">On Mon, Jun 2, 2014 at 7:40 AM, Clément Bera <span dir="ltr">&lt;<a href="mailto:bera.clement@gmail.com" target="_blank">bera.clement@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 dir="ltr">Eliot,<div><br></div><div>Recent commits are very exciting. Context and closure creations are now inlined in machine code :-)</div>


</div></blockquote><div><br></div><div>I&#39;m glad you think so :-).  What was nice is that once I had inline context, closure and temp vector creation it was motivating to try and do a little peephole optimization, and that turned out to be really easy.  If you look at e.g. Collection&gt;&gt;inject:into: it starts with</div>


<div><br></div><div><div>17 &lt;8A 01&gt; push: (Array new: 1)</div><div>19 &lt;6A&gt; popIntoTemp: 2</div><div>20 &lt;10&gt; pushTemp: 0</div><div>21 &lt;8E 00 02&gt; popIntoTemp: 0 inVectorAt: 2</div></div><div><div>24 &lt;70&gt; self<br>


</div><div>25 &lt;11&gt; pushTemp: 1</div><div>26 &lt;12&gt; pushTemp: 2</div><div>27 &lt;8F 21 00 0A&gt; closureNumCopied: 2 numArgs: 1 bytes 31 to 40</div></div><div><br></div><div>With the current memory manager this generates the following machine code on x86</div>


<div><br></div><div>17:<span style="white-space:pre-wrap">        </span>1057: movl $0x00000001, %ebx : BB 01 00 00 00 </div><div><div><span style="white-space:pre-wrap">        </span>105c: call .+0xfffff927 (0x00000988=ceCreateNewArrayTrampoline) : E8 27 F9 FF FF </div>


<div><span style="white-space:pre-wrap">20:        </span>1061: movl 12(%ebp), %eax : 8B 45 0C </div><div><span style="white-space:pre-wrap">21:        </span>1064: movl %eax, %ds:0x4(%edx) : 89 42 04 </div><div><span style="white-space:pre-wrap">19:        </span>1067: movl %edx, -16(%ebp) : 89 55 F0 </div>


<div><span style="white-space:pre-wrap">24:        </span>106a: movl -12(%ebp), %eax : 8B 45 F4 </div><div><span style="white-space:pre-wrap">        </span>106d: pushl %eax : 50 </div><div><span style="white-space:pre-wrap">25:        </span>106e: movl 8(%ebp), %eax : 8B 45 08 </div>


<div><span style="white-space:pre-wrap">        </span>1071: pushl %eax : 50 </div><div><span style="white-space:pre-wrap">26:        </span>1072: movl -16(%ebp), %eax : 8B 45 F0 </div><div><span style="white-space:pre-wrap">        </span>1075: pushl %eax : 50 </div>


<div><span style="white-space:pre-wrap">27:        </span>1076: movl $0x0001f081, %ebx : BB 81 F0 01 00 </div><div><span style="white-space:pre-wrap">        </span>107b: call .+0xfffff9a0 (0x00000a20=ceClosureCopyTrampoline) : E8 A0 F9 FF FF </div>


<div><span style="white-space:pre-wrap">        </span>1080: addl $0x00000008, %esp : 83 C4 08 </div></div><div><br></div><div>The reordering of bytecodes 20 &amp; 21 before 19 is due to the Cogit&#39;s stack-to-register-mapping code generating strategy which defers certain actions until operands are used.</div>


<div><br></div><div>Both ceCreateNewArrayTrampoline and ceClosureCopyTrampoline switch stacks to the C stack and call routines in the cointerpreter to create an array and create a closure.  Hence, the array created by ceCreateNewArrayTrampoline is initialized with nils before being returned, and the closure&#39;s copied values are pushed to the stack so that the cointerpreter can copy these values into the closure.</div>


<div><br></div><div>With Spur the machine code generated is quite different and even though its longer it is faster because the calls into the cointerpreter are expensive, and because the current memory manager&#39;s allocation routines are slow:</div>


<div><br></div><div><div><span style="white-space:pre-wrap">17:        </span>1463: movl %ds:0x2001c=#freeStart, %edx : 8B 15 1C 00 02 00<span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span>inline temp vector allocation</div>


<div><span style="white-space:pre-wrap">        </span>1469: movl $0x02000033, %eax : B8 33 00 00 02 <span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        high 32-bits of header</span></div>


<div><span style="white-space:pre-wrap">        </span>146e: movl %eax, %ds:(%edx) : 89 02 </div><div><span style="white-space:pre-wrap">        </span>1470: movl $0x01000000, %eax : B8 00 00 00 01  <span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        low 32-bits of header</span></div>


<div><span style="white-space:pre-wrap">        </span>1475: movl %eax, %ds:0x4(%edx) : 89 42 04 </div><div><span style="white-space:pre-wrap">        </span>1478: movl $0x00100000=nil, %eax : B8 00 00 10 00  <span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        initialize slot with nil</span></div>


<div><span style="white-space:pre-wrap">        </span>147d: movl %eax, %ds:0x8(%edx) : 89 42 08 </div><div><span style="white-space:pre-wrap">        </span>1480: movl %edx, %eax : 89 D0 </div><div><span style="white-space:pre-wrap">        </span>1482: addl $0x00000010, %eax : 83 C0 10 </div>


<div><span style="white-space:pre-wrap">        </span>1485: movl %eax, %ds:0x2001c=#freeStart : A3 1C 00 02 00 <span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        write back allocation pointer</span></div>

<div>
<span style="white-space:pre-wrap">        </span>148a: cmpl $0x00024680, %eax : 3D 80 46 02 00 </div><div><span style="white-space:pre-wrap">        </span>148f: jb .+0x00000005 (0x00001496) : 72 05 <span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">check if a scavenge should be scheduled</span></div>


<div><span style="white-space:pre-wrap">        </span>1491: call .+0xfffff632 (0x00000ac8=ceSheduleScavengeTrampoline) : E8 32 F6 FF FF </div><div><span style="white-space:pre-wrap">19:        </span>1496: movl %edx, -16(%ebp) : 89 55 F0 </div>


<div><span style="white-space:pre-wrap">20:        </span>1499: movl 12(%ebp), %ecx : 8B 4D 0C </div><div><span style="white-space:pre-wrap">21:        </span>149c: movl -16(%ebp), %edx : 8B 55 F0 <span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        alas an inline store check is generated</span></div>


<div><span style="white-space:pre-wrap">        </span>149f: movl %ecx, %ds:0x8(%edx) : 89 4A 08 </div><div><span style="white-space:pre-wrap">        </span>14a2: movl %ecx, %eax : 89 C8 </div><div><span style="white-space:pre-wrap">        </span>14a4: andl $0x00000003, %eax : 83 E0 03 </div>


<div><span style="white-space:pre-wrap">        </span>14a7: jnz .+0x0000001c (0x000014c5) : 75 1C </div><div><span style="white-space:pre-wrap">        </span>14a9: movl $0x12345678, %eax : B8 78 56 34 12 </div><div><span style="white-space:pre-wrap">        </span>14ae: cmpl %eax, %edx : 39 C2 </div>


<div><span style="white-space:pre-wrap">        </span>14b0: jb .+0x00000013 (0x000014c5) : 72 13 </div><div><span style="white-space:pre-wrap">        </span>14b2: cmpl %eax, %ecx : 39 C1 </div><div><span style="white-space:pre-wrap">        </span>14b4: jnb .+0x0000000f (0x000014c5) : 73 0F </div>


<div><span style="white-space:pre-wrap">        </span>14b6: movb %ds:0x3(%edx), %al : 8A 42 03 </div><div><span style="white-space:pre-wrap">        </span>14b9: andl $0x00000020, %eax : 83 E0 20 </div><div><span style="white-space:pre-wrap">        </span>14bc: jnz .+0x00000007 (0x000014c5) : 75 07 </div>


<div><span style="white-space:pre-wrap">        </span>14be: pushl %ecx : 51 </div><div><span style="white-space:pre-wrap">        </span>14bf: call .+0xfffff5d4 (0x00000a98=ceStoreCheckTrampoline) : E8 D4 F5 FF FF </div><div><span style="white-space:pre-wrap">        </span>14c4: popl %ecx : 59 </div>


<div><span style="white-space:pre-wrap">27:        </span>14c5: movl $0x00000002, %ebx : BB 02 00 00 00 <span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        set numArgs for </span>ceSmallMethodContext</div>


<div><span style="white-space:pre-wrap">        </span>14ca: call .+0xfffff639 (0x00000b08=ceSmallMethodContext) : E8 39 F6 FF FF<span style="white-space:pre-wrap">        </span>call to machine code context creation</div><div><span style="white-space:pre-wrap">        </span>14cf: movl %edx, %ecx : 89 D1 </div>


<div><span style="white-space:pre-wrap">        </span>14d1: movl %ds:0x2001c=#freeStart, %edx : 8B 15 1C 00 02 00 <span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span>inline closure allocation</div>


<div><span style="white-space:pre-wrap">        </span>14d7: movl $0x03000025, %eax : B8 25 00 00 03 </div><div><span style="white-space:pre-wrap">        </span>14dc: movl %eax, %ds:(%edx) : 89 02 </div><div><span style="white-space:pre-wrap">        </span>14de: movl $0x05000000, %eax : B8 00 00 00 05 </div>


<div><span style="white-space:pre-wrap">        </span>14e3: movl %eax, %ds:0x4(%edx) : 89 42 04 </div><div><span style="white-space:pre-wrap">        </span>14e6: movl %edx, %eax : 89 D0 </div><div><span style="white-space:pre-wrap">        </span>14e8: addl $0x00000020, %eax : 83 C0 20 </div>


<div><span style="white-space:pre-wrap">        </span>14eb: movl %eax, %ds:0x2001c=#freeStart : A3 1C 00 02 00 </div><div><span style="white-space:pre-wrap">        </span>14f0: cmpl $0x00024680, %eax : 3D 80 46 02 00 </div><div>
<span style="white-space:pre-wrap">        </span>14f5: jb .+0x00000005 (0x000014fc) : 72 05 </div><div><span style="white-space:pre-wrap">        </span>14f7: call .+0xfffff5cc (0x00000ac8=ceSheduleScavengeTrampoline) : E8 CC F5 FF FF </div>


<div><span style="white-space:pre-wrap">        </span>14fc: movl %ecx, %ds:0x8(%edx) : 89 4A 08 </div><div><span style="white-space:pre-wrap">        </span>14ff: movl $0x0000003f, %eax : B8 3F 00 00 00  <span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span> <span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        set closure&#39;s startpc</span></div>


<div><span style="white-space:pre-wrap">        </span>1504: movl %eax, %ds:0xc(%edx) : 89 42 0C </div><div><span style="white-space:pre-wrap">        </span>1507: movl $0x00000003, %eax : B8 03 00 00 00   <span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span> <span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">set closure&#39;s numArgs (1 as a SmallInteger)</span></div>


<div><span style="white-space:pre-wrap">        </span>150c: movl %eax, %ds:0x10(%edx) : 89 42 10 </div><div><span style="white-space:pre-wrap">        </span>150f: movl -16(%ebp), %eax : 8B 45 F0    <span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span> <span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span> <span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">set closure&#39;s outerContext</span></div>


<div><span style="white-space:pre-wrap">        </span>1512: movl %eax, %ds:0x18(%edx) : 89 42 18 </div><div><span style="white-space:pre-wrap">        </span>1515: movl 8(%ebp), %eax : 8B 45 08     <span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span> <span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span> <span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">set closure&#39;s copied value</span></div>


<div><span style="white-space:pre-wrap">        </span>1518: movl %eax, %ds:0x14(%edx) : 89 42 14 </div></div><div><br></div><div>What&#39;s cool is that the closure creation and initialization is all inlined, pushing nothing to the stack at all.  However, the indirect temp vector initialization is clumsy.  The store check is unnecessary and long.  And we&#39;re still initializing the slot with nil before assigning the temp from &quot;pushTemp: 0; popIntoTemp: 0 inVectorAt: 2&quot;.</div>


<div><br></div><div>So I thought I would try to implement a peephole that would look for</div><div><div><span style="white-space:pre-wrap">                </span>push: (Array new: 1)</div><div><span style="white-space:pre-wrap">                </span>popIntoTemp: tempIndex</div>


<div><span style="white-space:pre-wrap">                </span>pushConstant: const or pushTemp: n</div><div><span style="white-space:pre-wrap">                </span>popIntoTemp: 0 inVectorAt: tempIndex</div><div><span style="white-space:pre-wrap">and </span>collapse this into</div>


<div><span style="white-space:pre-wrap">                </span>tempAt: tempIndex put: {const or temp}</div></div><div><br></div><div>The peephole&#39;s checking for the sequence is a bit long-winded but it boils down to</div><div><br>


</div><div><div><span style="white-space:pre-wrap">        </span>objectRepresentation genNewArrayOfSize: 1 initialized: false.</div><div><span style="white-space:pre-wrap">        </span>self evaluate: pushValueDesc at: bytecodePC + pushArrayDesc numBytes + storeArrayDesc numBytes.</div>


<div><span style="white-space:pre-wrap">        </span>reg := self ssStorePop: true toPreferredReg: TempReg.</div><div><span style="white-space:pre-wrap">        </span>objectRepresentation</div><div><span style="white-space:pre-wrap">                </span>genStoreSourceReg: reg</div>


<div><span style="white-space:pre-wrap">                </span>slotIndex: 0</div><div><span style="white-space:pre-wrap">                </span>intoNewObjectInDestReg: ReceiverResultReg.</div><div><span style="white-space:pre-wrap">        </span>self ssPushRegister: ReceiverResultReg.</div>


<div><span style="white-space:pre-wrap">        </span>self evaluate: storeArrayDesc at: bytecodePC + pushArrayDesc numBytes.</div></div><div><br></div><div>and now the code generated is quite a bit shorter:</div><div><br></div>


<div><div><span style="white-space:pre-wrap">17:        </span>1463: movl %ds:0x2001c=#freeStart, %edx : 8B 15 1C 00 02 00 </div><div><span style="white-space:pre-wrap">        </span>1469: movl $0x02000033, %eax : B8 33 00 00 02 </div>


<div><span style="white-space:pre-wrap">        </span>146e: movl %eax, %ds:(%edx) : 89 02 </div><div><span style="white-space:pre-wrap">        </span>1470: movl $0x01000000, %eax : B8 00 00 00 01 </div><div><span style="white-space:pre-wrap">        </span>1475: movl %eax, %ds:0x4(%edx) : 89 42 04 </div>


<div><span style="white-space:pre-wrap">        </span>1478: movl %edx, %eax : 89 D0 </div><div><span style="white-space:pre-wrap">        </span>147a: addl $0x00000010, %eax : 83 C0 10 </div><div><span style="white-space:pre-wrap">        </span>147d: movl %eax, %ds:0x2001c=#freeStart : A3 1C 00 02 00 </div>


<div><span style="white-space:pre-wrap">        </span>1482: cmpl $0x00024680, %eax : 3D 80 46 02 00 </div><div><span style="white-space:pre-wrap">        </span>1487: jb .+0x00000005 (0x0000148e) : 72 05 </div><div><span style="white-space:pre-wrap">        </span>1489: call .+0xfffff63a (0x00000ac8=ceSheduleScavengeTrampoline) : E8 3A F6 FF FF </div>


<div><span style="white-space:pre-wrap">20:        </span>148e: movl 12(%ebp), %eax : 8B 45 0C </div><div><span style="white-space:pre-wrap">21:        </span>1491: movl %eax, %ds:0x8(%edx) : 89 42 08 </div><div><span style="white-space:pre-wrap">19:        </span>1494: movl %edx, -16(%ebp) : 89 55 F0 </div>


<div><span style="white-space:pre-wrap">27:        </span>1497: movl $0x00000002, %ebx : BB 02 00 00 00 </div><div><span style="white-space:pre-wrap">        </span>149c: call .+0xfffff667 (0x00000b08=ceSmallMethodContext) : E8 67 F6 FF FF </div>


<div><span style="white-space:pre-wrap">        </span>14a1: movl %edx, %ecx : 89 D1 </div><div><span style="white-space:pre-wrap">        </span>14a3: movl %ds:0x2001c=#freeStart, %edx : 8B 15 1C 00 02 00 </div><div><span style="white-space:pre-wrap">        </span>14a9: movl $0x03000025, %eax : B8 25 00 00 03 </div>


<div><span style="white-space:pre-wrap">        </span>14ae: movl %eax, %ds:(%edx) : 89 02 </div><div><span style="white-space:pre-wrap">        </span>14b0: movl $0x05000000, %eax : B8 00 00 00 05 </div><div><span style="white-space:pre-wrap">        </span>14b5: movl %eax, %ds:0x4(%edx) : 89 42 04 </div>


<div><span style="white-space:pre-wrap">        </span>14b8: movl %edx, %eax : 89 D0 </div><div><span style="white-space:pre-wrap">        </span>14ba: addl $0x00000020, %eax : 83 C0 20 </div><div><span style="white-space:pre-wrap">        </span>14bd: movl %eax, %ds:0x2001c=#freeStart : A3 1C 00 02 00 </div>


<div><span style="white-space:pre-wrap">        </span>14c2: cmpl $0x00024680, %eax : 3D 80 46 02 00 </div><div><span style="white-space:pre-wrap">        </span>14c7: jb .+0x00000005 (0x000014ce) : 72 05 </div><div><span style="white-space:pre-wrap">        </span>14c9: call .+0xfffff5fa (0x00000ac8=ceSheduleScavengeTrampoline) : E8 FA F5 FF FF </div>


<div><span style="white-space:pre-wrap">        </span>14ce: movl %ecx, %ds:0x8(%edx) : 89 4A 08 </div><div><span style="white-space:pre-wrap">        </span>14d1: movl $0x0000003f, %eax : B8 3F 00 00 00 </div><div><span style="white-space:pre-wrap">        </span>14d6: movl %eax, %ds:0xc(%edx) : 89 42 0C </div>


<div><span style="white-space:pre-wrap">        </span>14d9: movl $0x00000003, %eax : B8 03 00 00 00 </div><div><span style="white-space:pre-wrap">        </span>14de: movl %eax, %ds:0x10(%edx) : 89 42 10 </div><div><span style="white-space:pre-wrap">        </span>14e1: movl -16(%ebp), %eax : 8B 45 F0 </div>


<div><span style="white-space:pre-wrap">        </span>14e4: movl %eax, %ds:0x18(%edx) : 89 42 18 </div><div><span style="white-space:pre-wrap">        </span>14e7: movl 8(%ebp), %eax : 8B 45 08 </div><div><span style="white-space:pre-wrap">        </span>14ea: movl %eax, %ds:0x14(%edx) : 89 42 14 </div>


</div><div><br></div><div>Very nice.  I could also move the allocations into trampolines.  There&#39;s an advantage to this because the test for ceSheduleScavengeTrampoline ends up only jumping when a scavenge is required, returning if not.  And it will save even more space.  This at the cost of a leaf call to get to the trampoline plus setting a register with either the number of slots in the array or the number of copied values.</div>


<div><br></div><div>But what&#39;s most satisfying is the use of the entire VMMaker framework with the simulator and Bochs plugin allowing me to test the code generation and the generated code immediately with no long edit-compile cycles.  I was able to implement the entire refactoring of temp vector and closure creation (moving it from the Cogit to the object representations) and implement machine code allocation over a weekend in which I still took my son fishing, went shopping, played gran turismo and watched a couple of movies.</div>


<div><br></div><div>This is the first time I&#39;ve ever implemented context creation in machine code in a Smalltalk VM.  Context creation is complex because the context has to be allocated and married with its stack frame.  In the VW VM and in Cog up until now that has been done in C code, requiring an expensive switch from the Smalltalk machine code stack to the C stack, and using a generic instantiation routine that marries any kind of context, large or small x method or block.  Now I have four machine code routines, large or small x method or block, that are extremely tight and take a single register parameter which is the argument count, which is known at compile time.  That&#39;s possible both because Spur makes allocation simple /and/ because the whole Cog VMMaker framework makes developing and debugging machine code so much easier than in a C VM.</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 dir="ltr"><div>Have you already done at:put: and stringAt:put: or is it your next step ?<br>


</div></div></blockquote><div><br></div><div>That&#39;s been there for quite a while.</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 dir="ltr">

<div>Please tell us about the new bench results with these features.<br></div></div></blockquote><div><br></div><div>So in the current Cog the following, which is mostly Interval&gt;&gt;#do: and SmallInteger&gt;&gt;#+, but involves lots of closure creation where all but the outer closure has copied values:</div>


<div><br></div><div><span style="white-space:pre-wrap">        </span>[(1 to: 10) do: [:i| (1 to: 10) do: [:j| (1 to: 10) do: [:k| (1 to: 10) do: [:l| (1 to: 10) do: [:m| (1 to: 10) do: [:n| (1 to: 10) do: [:o| (1 to: 10) do: [:p| i + j + k + l + m + n + o + p]]]]]]]]] timeToRun</div>


<div><br></div><div>evaluates to 9002, 9 seconds.</div><div><br></div><div>In the new Spur VM it just evaluated to 4829, 4.8 seconds, for a -46% speedup.</div><div><br></div><div>Currently the Newspeak bootstrap, which loads a number of Monticello packages, including the Newspeak compiler, and then compiles a number of Newspeak packages, about 80k lines of Newspeak, now takes about -51% of the time using Spur.</div>

<div><br></div><div>Alas I now need two computers.  I tend to keep lots of tabs open in my web browser and unless I quit Chrome my benchmark figures slow down by about a factor of two :-( [ ;-) ].</div><div><br></div><div>
Of course, this:</div><div><br></div><div>[1 to: 10 do: [:i| 1 to: 10 do: [:j| 1 to: 10 do: [:k| 1 to: 10 do: [:l| 1 to: 10 do: [:m| 1 to: 10 do: [:n| 1 to: 10 do: [:o| 1 to: 10 do: [:p| i + j + k + l + m + n + o + p]]]]]]]]] timeToRun</div>
<div><br></div><div>is even faster, 2320, for a -52% speedup w.r.t. Spur and a -74% speedup w.r.t. the current VM.  And that&#39;s what you&#39;re working on Clément.  (Clément is working on adaptive optimization which will optimize the Interval&gt;&gt;#do: code into the inlined to:do: code on the fly).  And thats going to be really exciting!<br>
</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 dir="ltr"><div>Clément<br></div></div><div class="gmail_extra"><br><br><div class="gmail_quote">2014-06-02 16:14 GMT+02:00  <span dir="ltr">&lt;<a href="mailto:commits@source.squeak.org" target="_blank">commits@source.squeak.org</a>&gt;</span>:<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>
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:<br>
<a href="http://source.squeak.org/VMMaker/VMMaker.oscog-eem.746.mcz" target="_blank">http://source.squeak.org/VMMaker/VMMaker.oscog-eem.746.mcz</a><br>
<br>
==================== Summary ====================<br>
<br>
Name: VMMaker.oscog-eem.746<br>
Author: eem<br>
Time: 1 June 2014, 6:05:30.694 pm<br>
UUID: cc4961d3-e629-4e28-b308-88eab314a8c9<br>
Ancestors: VMMaker.oscog-eem.745<br>
<br>
Implement a peephole in the Spur Cogit for an indirection<br>
vector initialized with a single value  Avoid initializing the<br>
slot in the array to nil and instead initialize it with the value.<br>
<br>
Refactor setting byte1, byte2 &amp; byte3 into<br>
loadSubsequentBytesForDescriptor:at: for the peephole<br>
tryCollapseTempVectorInitializationOfSize:.<br>
<br>
No loner inline CoInterpreter&gt;&gt;pre/postGCAction: for VM profiling.<br>
<br>
Increase the number of trampoline table slots.<br>
<br>
Simulator:<br>
Fix CurrentImageCoInterpreterFacade for the new Spur<br>
inline instantiation code.<br>
<br>
=============== Diff against VMMaker.oscog-eem.745 ===============<br>
<br>
Item was changed:<br>
  ----- Method: CoInterpreter&gt;&gt;postGCAction: (in category &#39;object memory support&#39;) -----<br>
  postGCAction: gcModeArg<br>
        &quot;Attempt to shrink free memory, signal the gc semaphore and let the Cogit do its post GC thang&quot;<br>
+       &lt;inline: false&gt;<br>
        self assert: gcModeArg = gcMode.<br>
        super postGCAction: gcModeArg.<br>
        cogit cogitPostGCAction: gcModeArg.<br>
        lastCoggableInterpretedBlockMethod := lastUncoggableInterpretedBlockMethod := nil.<br>
        gcMode := 0!<br>
<br>
Item was changed:<br>
  ----- Method: CoInterpreter&gt;&gt;preGCAction: (in category &#39;object memory support&#39;) -----<br>
  preGCAction: gcModeArg<br>
+       &lt;inline: false&gt;<br>
-       &lt;inline: true&gt;<br>
        &quot;Need to write back the frame pointers unless all pages are free (as in snapshot).<br>
         Need to set gcMode var (to avoid passing the flag through a lot of the updating code)&quot;<br>
        super preGCAction: gcModeArg.<br>
<br>
        gcMode := gcModeArg.<br>
<br>
        cogit recordEventTrace ifTrue:<br>
                [| traceType |<br>
                traceType := gcModeArg == GCModeFull ifTrue: [TraceFullGC] ifFalse: [TraceIncrementalGC].<br>
                self recordTrace: traceType thing: traceType source: 0].<br>
<br>
        cogit recordPrimTrace ifTrue:<br>
                [| traceType |<br>
                traceType := gcModeArg == GCModeFull ifTrue: [TraceFullGC] ifFalse: [TraceIncrementalGC].<br>
                self fastLogPrim: traceType]!<br>
<br>
Item was added:<br>
+ ----- Method: CogObjectRepresentation&gt;&gt;createsArraysInline (in category &#39;bytecode generator support&#39;) -----<br>
+ createsArraysInline<br>
+       &quot;Answer if the object representation allocates arrays inline.  By<br>
+        default answer false. Better code can be generated when creating<br>
+        arrays inline if values are /not/ flushed to the stack.&quot;<br>
+       ^false!<br>
<br>
Item was removed:<br>
- ----- Method: CogObjectRepresentationFor32BitSpur&gt;&gt;createsClosuresInline (in category &#39;bytecode generator support&#39;) -----<br>
- createsClosuresInline<br>
-       &quot;Answer if the object representation allocates closures inline.  By<br>
-        default answer false. Better code can be generated when creating<br>
-        closures inline if copied values are /not/ flushed to the stack.&quot;<br>
-       ^true!<br>
<br>
Item was added:<br>
+ ----- Method: CogObjectRepresentationForSpur&gt;&gt;createsArraysInline (in category &#39;bytecode generator support&#39;) -----<br>
+ createsArraysInline<br>
+       &quot;Answer if the object representation allocates arrays inline.  By<br>
+        default answer false. Better code can be generated when creating<br>
+        arrays inline if values are /not/ flushed to the stack.&quot;<br>
+       ^true!<br>
<br>
Item was added:<br>
+ ----- Method: CogObjectRepresentationForSpur&gt;&gt;createsClosuresInline (in category &#39;bytecode generator support&#39;) -----<br>
+ createsClosuresInline<br>
+       &quot;Answer if the object representation allocates closures inline.  By<br>
+        default answer false. Better code can be generated when creating<br>
+        closures inline if copied values are /not/ flushed to the stack.&quot;<br>
+       ^true!<br>
<br>
Item was changed:<br>
  ----- Method: Cogit&gt;&gt;compileAbstractInstructionsFrom:through: (in category &#39;compile abstract instructions&#39;) -----<br>
  compileAbstractInstructionsFrom: start through: end<br>
        &quot;Loop over bytecodes, dispatching to the generator for each bytecode, handling fixups in due course.&quot;<br>
        | nextOpcodeIndex descriptor fixup result nExts |<br>
        &lt;var: #descriptor type: #&#39;BytecodeDescriptor *&#39;&gt;<br>
        &lt;var: #fixup type: #&#39;BytecodeFixup *&#39;&gt;<br>
        bytecodePC := start.<br>
        nExts := 0.<br>
        [byte0 := (objectMemory fetchByte: bytecodePC ofObject: methodObj)  + bytecodeSetOffset.<br>
         descriptor := self generatorAt: byte0.<br>
+        self loadSubsequentBytesForDescriptor: descriptor at: bytecodePC.<br>
-        descriptor numBytes &gt; 1 ifTrue:<br>
-               [byte1 := objectMemory fetchByte: bytecodePC + 1 ofObject: methodObj.<br>
-                descriptor numBytes &gt; 2 ifTrue:<br>
-                       [byte2 := objectMemory fetchByte: bytecodePC + 2 ofObject: methodObj.<br>
-                        descriptor numBytes &gt; 3 ifTrue:<br>
-                               [byte3 := objectMemory fetchByte: bytecodePC + 3 ofObject: methodObj.<br>
-                                descriptor numBytes &gt; 4 ifTrue:<br>
-                                       [self notYetImplemented]]]].<br>
         nextOpcodeIndex := opcodeIndex.<br>
         result := self perform: descriptor generator.<br>
         descriptor isExtension ifFalse: &quot;extended bytecodes must consume their extensions&quot;<br>
                [self assert: (extA = 0 and: [extB = 0])].<br>
         fixup := self fixupAt: bytecodePC - initialPC.<br>
         fixup targetInstruction ~= 0 ifTrue:<br>
                [&quot;There is a fixup for this bytecode.  It must point to the first generated<br>
                   instruction for this bytecode.  If there isn&#39;t one we need to add a label.&quot;<br>
                 opcodeIndex = nextOpcodeIndex ifTrue:<br>
                        [self Label].<br>
                 fixup targetInstruction: (self abstractInstructionAt: nextOpcodeIndex)].<br>
         bytecodePC := self nextBytecodePCFor: descriptor at: bytecodePC exts: nExts in: methodObj.<br>
         result = 0 and: [bytecodePC &lt;= end]]<br>
                whileTrue:<br>
                        [nExts := descriptor isExtension ifTrue: [nExts + 1] ifFalse: [0]].<br>
        self checkEnoughOpcodes.<br>
        ^result!<br>
<br>
Item was added:<br>
+ ----- Method: Cogit&gt;&gt;loadSubsequentBytesForDescriptor:at: (in category &#39;compile abstract instructions&#39;) -----<br>
+ loadSubsequentBytesForDescriptor: descriptor at: pc<br>
+       &lt;var: #descriptor type: #&#39;BytecodeDescriptor *&#39;&gt;<br>
+       descriptor numBytes &gt; 1 ifTrue:<br>
+               [byte1 := objectMemory fetchByte: pc + 1 ofObject: methodObj.<br>
+                descriptor numBytes &gt; 2 ifTrue:<br>
+                       [byte2 := objectMemory fetchByte: pc + 2 ofObject: methodObj.<br>
+                        descriptor numBytes &gt; 3 ifTrue:<br>
+                               [byte3 := objectMemory fetchByte: pc + 3 ofObject: methodObj.<br>
+                                descriptor numBytes &gt; 4 ifTrue:<br>
+                                       [self notYetImplemented]]]]!<br>
<br>
Item was added:<br>
+ ----- Method: CurrentImageCoInterpreterFacade class&gt;&gt;objectMemoryClass (in category &#39;accessing&#39;) -----<br>
+ objectMemoryClass<br>
+       ^self subclassResponsibility!<br>
<br>
Item was changed:<br>
  ----- Method: CurrentImageCoInterpreterFacade&gt;&gt;cogit: (in category &#39;initialize-release&#39;) -----<br>
  cogit: aCogit<br>
        cogit := aCogit.<br>
        coInterpreter cogit: aCogit.<br>
+       (objectMemory respondsTo: #cogit:) ifTrue:<br>
+               [objectMemory cogit: aCogit]!<br>
-       objectMemory cogit: aCogit!<br>
<br>
Item was added:<br>
+ ----- Method: CurrentImageCoInterpreterFacade&gt;&gt;indexablePointersFormat (in category &#39;accessing&#39;) -----<br>
+ indexablePointersFormat<br>
+       ^objectMemory indexablePointersFormat!<br>
<br>
Item was changed:<br>
  ----- Method: CurrentImageCoInterpreterFacade&gt;&gt;initialize (in category &#39;initialize-release&#39;) -----<br>
  initialize<br>
        memory := ByteArray new: 262144.<br>
+       objectMemory := self class objectMemoryClass new.<br>
-       objectMemory := NewCoObjectMemory new.<br>
        coInterpreter := CoInterpreter new.<br>
        coInterpreter<br>
                instVarNamed: &#39;objectMemory&#39;<br>
                        put: objectMemory;<br>
                instVarNamed: &#39;primitiveTable&#39;<br>
                        put: (CArrayAccessor on: CoInterpreter primitiveTable copy).<br>
        variables := Dictionary new.<br>
        #(&#39;stackLimit&#39;) do:<br>
                [:l| self addressForLabel: l].<br>
        self initializeObjectMap!<br>
<br>
Item was added:<br>
+ ----- Method: CurrentImageCoInterpreterFacade&gt;&gt;methodNeedsLargeContext: (in category &#39;accessing&#39;) -----<br>
+ methodNeedsLargeContext: aMethodOop<br>
+       ^(self objectForOop: aMethodOop) frameSize &gt; CompiledMethod smallFrameSize!<br>
<br>
Item was added:<br>
+ ----- Method: CurrentImageCoInterpreterFacadeForSpurObjectRepresentation class&gt;&gt;objectMemoryClass (in category &#39;accessing&#39;) -----<br>
+ objectMemoryClass<br>
+       ^Spur32BitCoMemoryManager!<br>
<br>
Item was added:<br>
+ ----- Method: CurrentImageCoInterpreterFacadeForSpurObjectRepresentation&gt;&gt;arrayFormat (in category &#39;accessing&#39;) -----<br>
+ arrayFormat<br>
+       ^objectMemory arrayFormat!<br>
<br>
Item was added:<br>
+ ----- Method: CurrentImageCoInterpreterFacadeForSpurObjectRepresentation&gt;&gt;getScavengeThreshold (in category &#39;accessing&#39;) -----<br>
+ getScavengeThreshold<br>
+       ^objectMemory getScavengeThreshold ifNil: [16r24680]!<br>
<br>
Item was added:<br>
+ ----- Method: CurrentImageCoInterpreterFacadeForSpurObjectRepresentation&gt;&gt;headerForSlots:format:classIndex: (in category &#39;accessing&#39;) -----<br>
+ headerForSlots: numSlots format: formatField classIndex: classIndex<br>
+       ^objectMemory headerForSlots: numSlots format: formatField classIndex: classIndex!<br>
<br>
Item was added:<br>
+ ----- Method: CurrentImageCoInterpreterFacadeForSpurObjectRepresentation&gt;&gt;numSlotsMask (in category &#39;accessing&#39;) -----<br>
+ numSlotsMask<br>
+       ^objectMemory numSlotsMask!<br>
<br>
Item was added:<br>
+ ----- Method: CurrentImageCoInterpreterFacadeForSpurObjectRepresentation&gt;&gt;rememberedBitShift (in category &#39;accessing&#39;) -----<br>
+ rememberedBitShift<br>
+       ^objectMemory rememberedBitShift!<br>
<br>
Item was added:<br>
+ ----- Method: CurrentImageCoInterpreterFacadeForSpurObjectRepresentation&gt;&gt;smallObjectBytesForSlots: (in category &#39;accessing&#39;) -----<br>
+ smallObjectBytesForSlots: numSlots<br>
+       ^objectMemory smallObjectBytesForSlots: numSlots!<br>
<br>
Item was added:<br>
+ ----- Method: CurrentImageCoInterpreterFacadeForSpurObjectRepresentation&gt;&gt;storeCheckBoundary (in category &#39;accessing&#39;) -----<br>
+ storeCheckBoundary<br>
+       ^objectMemory storeCheckBoundary ifNil: [16r12345678]!<br>
<br>
Item was added:<br>
+ ----- Method: CurrentImageCoInterpreterFacadeForSqueakV3ObjectRepresentation class&gt;&gt;objectMemoryClass (in category &#39;accessing&#39;) -----<br>
+ objectMemoryClass<br>
+       ^NewObjectMemory!<br>
<br>
Item was changed:<br>
  ----- Method: SimpleStackBasedCogit class&gt;&gt;initializeMiscConstants (in category &#39;class initialization&#39;) -----<br>
  initializeMiscConstants<br>
        super initializeMiscConstants.<br>
        MaxLiteralCountForCompile := initializationOptions at: #MaxLiteralCountForCompile ifAbsent: [60].<br>
        NumTrampolines := NewspeakVM<br>
+                                                       ifTrue: [50]<br>
+                                                       ifFalse: [42]!<br>
-                                                       ifTrue: [46]<br>
-                                                       ifFalse: [38]!<br>
<br>
Item was changed:<br>
  ----- Method: StackToRegisterMappingCogit class&gt;&gt;initializeMiscConstants (in category &#39;class initialization&#39;) -----<br>
  initializeMiscConstants<br>
        super initializeMiscConstants.<br>
        NumTrampolines := NewspeakVM<br>
+                                                       ifTrue: [60]<br>
+                                                       ifFalse: [52]!<br>
-                                                       ifTrue: [58]<br>
-                                                       ifFalse: [50]!<br>
<br>
Item was changed:<br>
  ----- Method: StackToRegisterMappingCogit&gt;&gt;compileAbstractInstructionsFrom:through: (in category &#39;compile abstract instructions&#39;) -----<br>
  compileAbstractInstructionsFrom: start through: end<br>
        &quot;Loop over bytecodes, dispatching to the generator for each bytecode, handling fixups in due course.&quot;<br>
        | nextOpcodeIndex descriptor nExts fixup result |<br>
        &lt;var: #descriptor type: #&#39;BytecodeDescriptor *&#39;&gt;<br>
        &lt;var: #fixup type: #&#39;BytecodeFixup *&#39;&gt;<br>
        self traceSimStack.<br>
        bytecodePC := start.<br>
        nExts := 0.<br>
        descriptor := nil.<br>
        deadCode := false.<br>
        [self cCode: &#39;&#39; inSmalltalk:<br>
                [(debugBytecodePointers includes: bytecodePC) ifTrue: [self halt]].<br>
        fixup := self fixupAt: bytecodePC - initialPC.<br>
        fixup targetInstruction asUnsignedInteger &gt; 0<br>
                ifTrue:<br>
                        [deadCode := false.<br>
                         fixup targetInstruction asUnsignedInteger &gt;= 2 ifTrue:<br>
                                [self merge: fixup<br>
                                        afterContinuation: (descriptor notNil<br>
                                                                                and: [descriptor isUnconditionalBranch<br>
                                                                                        or: [descriptor isReturn]]) not]]<br>
                ifFalse: &quot;If there&#39;s no fixup following a return there&#39;s no jump to that code and it is dead.&quot;<br>
                        [(descriptor notNil and: [descriptor isReturn]) ifTrue:<br>
                                [deadCode := true]].<br>
         self cCode: &#39;&#39; inSmalltalk:<br>
                [deadCode ifFalse:<br>
                        [self assert: simStackPtr + (needsFrame ifTrue: [0] ifFalse: [1])<br>
                                                = (self debugStackPointerFor: bytecodePC)]].<br>
         byte0 := (objectMemory fetchByte: bytecodePC ofObject: methodObj) + bytecodeSetOffset.<br>
         descriptor := self generatorAt: byte0.<br>
+        self loadSubsequentBytesForDescriptor: descriptor at: bytecodePC.<br>
-        descriptor numBytes &gt; 1 ifTrue:<br>
-               [byte1 := objectMemory fetchByte: bytecodePC + 1 ofObject: methodObj.<br>
-                descriptor numBytes &gt; 2 ifTrue:<br>
-                       [byte2 := objectMemory fetchByte: bytecodePC + 2 ofObject: methodObj.<br>
-                        descriptor numBytes &gt; 3 ifTrue:<br>
-                               [byte3 := objectMemory fetchByte: bytecodePC + 3 ofObject: methodObj.<br>
-                                descriptor numBytes &gt; 4 ifTrue:<br>
-                                       [self notYetImplemented]]]].<br>
         nextOpcodeIndex := opcodeIndex.<br>
         result := deadCode<br>
                                ifTrue: &quot;insert nops for dead code that is mapped so that bc to mc mapping is not many to one&quot;<br>
                                        [(descriptor isMapped<br>
                                          or: [inBlock and: [descriptor isMappedInBlock]]) ifTrue:<br>
                                                [self annotateBytecode: self Nop].<br>
                                                0]<br>
                                ifFalse:<br>
                                        [self perform: descriptor generator].<br>
         descriptor isExtension ifFalse: &quot;extended bytecodes must consume their extensions&quot;<br>
                [self assert: (extA = 0 and: [extB = 0])].<br>
         self traceDescriptor: descriptor; traceSimStack.<br>
         (fixup targetInstruction asUnsignedInteger between: 1 and: 2) ifTrue:<br>
                [&quot;There is a fixup for this bytecode.  It must point to the first generated<br>
                   instruction for this bytecode.  If there isn&#39;t one we need to add a label.&quot;<br>
                 opcodeIndex = nextOpcodeIndex ifTrue:<br>
                        [self Label].<br>
                 fixup targetInstruction: (self abstractInstructionAt: nextOpcodeIndex)].<br>
         bytecodePC := self nextBytecodePCFor: descriptor at: bytecodePC exts: nExts in: methodObj.<br>
         result = 0 and: [bytecodePC &lt;= end]] whileTrue:<br>
                [nExts := descriptor isExtension ifTrue: [nExts + 1] ifFalse: [0]].<br>
        self checkEnoughOpcodes.<br>
        ^result!<br>
<br>
Item was added:<br>
+ ----- Method: StackToRegisterMappingCogit&gt;&gt;evaluate:at: (in category &#39;peephole optimizations&#39;) -----<br>
+ evaluate: descriptor at: pc<br>
+       &lt;var: #descriptor type: #&#39;BytecodeDescriptor *&#39;&gt;<br>
+       byte0 := objectMemory fetchByte: pc ofObject: methodObj.<br>
+       self assert: descriptor = (self generatorAt: bytecodeSetOffset + byte0).<br>
+       self loadSubsequentBytesForDescriptor: descriptor at: pc.<br>
+       self perform: descriptor generator!<br>
<br>
Item was changed:<br>
  ----- Method: StackToRegisterMappingCogit&gt;&gt;genPushNewArrayBytecode (in category &#39;bytecode generators&#39;) -----<br>
  genPushNewArrayBytecode<br>
        | size popValues |<br>
        self assert: needsFrame.<br>
        optStatus isReceiverResultRegLive: false.<br>
        (popValues := byte1 &gt; 127)<br>
                ifTrue: [self ssFlushTo: simStackPtr]<br>
                ifFalse: [self ssAllocateCallReg: SendNumArgsReg and: ReceiverResultReg].<br>
        size := byte1 bitAnd: 127.<br>
+       popValues ifFalse:<br>
+               [(self tryCollapseTempVectorInitializationOfSize: size) ifTrue:<br>
+                       [^0]].<br>
        objectRepresentation genNewArrayOfSize: size initialized: popValues not.<br>
        popValues ifTrue:<br>
                [size - 1 to: 0 by: -1 do:<br>
                        [:i|<br>
                        self PopR: TempReg.<br>
                        objectRepresentation<br>
                                genStoreSourceReg: TempReg<br>
                                slotIndex: i<br>
                                intoNewObjectInDestReg: ReceiverResultReg].<br>
                 self ssPop: size].<br>
        ^self ssPushRegister: ReceiverResultReg!<br>
<br>
Item was added:<br>
+ ----- Method: StackToRegisterMappingCogit&gt;&gt;tryCollapseTempVectorInitializationOfSize: (in category &#39;peephole optimizations&#39;) -----<br>
+ tryCollapseTempVectorInitializationOfSize: slots<br>
+       &quot;Try and collapse<br>
+               push: (Array new: 1)<br>
+               popIntoTemp: tempIndex<br>
+               pushConstant: const or pushTemp: n<br>
+               popIntoTemp: 0 inVectorAt: tempIndex<br>
+        into<br>
+               tempAt: tempIndex put: {const}.<br>
+        One might think that we should look for a sequence of more than<br>
+        one pushes and pops but this is extremely rare.&quot;<br>
+       | pushArrayDesc storeArrayDesc pushValueDesc storeValueDesc reg |<br>
+       &lt;var: #pushArrayDesc type: #&#39;BytecodeDescriptor *&#39;&gt;<br>
+       &lt;var: #pushValueDesc type: #&#39;BytecodeDescriptor *&#39;&gt;<br>
+       &lt;var: #storeArrayDesc type: #&#39;BytecodeDescriptor *&#39;&gt;<br>
+       &lt;var: #storeValueDesc type: #&#39;BytecodeDescriptor *&#39;&gt;<br>
+       slots ~= 1 ifTrue:<br>
+               [^false].<br>
+       pushArrayDesc := self generatorAt: bytecodeSetOffset<br>
+                                                                               + (objectMemory<br>
+                                                                                               fetchByte: bytecodePC<br>
+                                                                                               ofObject: methodObj).<br>
+       self assert: pushArrayDesc generator == #genPushNewArrayBytecode.<br>
+       storeArrayDesc := self generatorAt: bytecodeSetOffset<br>
+                                                                               + (objectMemory<br>
+                                                                                               fetchByte: bytecodePC<br>
+                                                                                                               + pushArrayDesc numBytes<br>
+                                                                                               ofObject: methodObj).<br>
+       storeArrayDesc generator ~~ #genStoreAndPopTemporaryVariableBytecode ifTrue:<br>
+               [^false].<br>
+       pushValueDesc := self generatorAt: bytecodeSetOffset<br>
+                                                                               + (objectMemory<br>
+                                                                                               fetchByte: bytecodePC<br>
+                                                                                                               + pushArrayDesc numBytes<br>
+                                                                                                               + storeArrayDesc numBytes<br>
+                                                                                               ofObject: methodObj).<br>
+       (pushValueDesc generator ~~ #genPushLiteralConstantBytecode<br>
+        and: [pushValueDesc generator ~~ #genPushQuickIntegerConstantBytecode<br>
+        and: [pushValueDesc generator ~~ #genPushTemporaryVariableBytecode]]) ifTrue:<br>
+               [^false].<br>
+       storeValueDesc := self generatorAt: bytecodeSetOffset<br>
+                                                                               + (objectMemory<br>
+                                                                                               fetchByte: bytecodePC<br>
+                                                                                                               + pushArrayDesc numBytes<br>
+                                                                                                               + storeArrayDesc numBytes<br>
+                                                                                                               + pushValueDesc numBytes<br>
+                                                                                               ofObject: methodObj).<br>
+       storeValueDesc generator ~~ #genStoreAndPopRemoteTempLongBytecode ifTrue:<br>
+               [^false].<br>
+<br>
+       objectRepresentation genNewArrayOfSize: 1 initialized: false.<br>
+       self evaluate: pushValueDesc at: bytecodePC + pushArrayDesc numBytes + storeArrayDesc numBytes.<br>
+       reg := self ssStorePop: true toPreferredReg: TempReg.<br>
+       objectRepresentation<br>
+               genStoreSourceReg: reg<br>
+               slotIndex: 0<br>
+               intoNewObjectInDestReg: ReceiverResultReg.<br>
+       self ssPushRegister: ReceiverResultReg.<br>
+       self evaluate: storeArrayDesc at: bytecodePC + pushArrayDesc numBytes.<br>
+       bytecodePC := bytecodePC<br>
+                                       &quot;+ pushArrayDesc numBytes this gets added by nextBytecodePCFor:at:exts:in:&quot;<br>
+                                       + storeArrayDesc numBytes<br>
+                                       + pushValueDesc numBytes<br>
+                                       + storeValueDesc numBytes.<br>
+       ^true!<br>
<br>
</blockquote></div><br></div>
<br></blockquote></div><br><br clear="all"><div><br></div>-- <br>best,<div>Eliot</div>
</div></div>