<div dir="ltr">Is the bleeding edge of the VM stable ? <div><br></div><div>If so I will look at it and find a solution and have a working simulator.<div><br></div><div>To fix:</div><div>- error code</div></div><div>- interpreter inst var access</div><div><br></div><div>I can do it today fortunately.</div><div><br></div><div>This is a shame I have not really understood yet how the interpreter is translated efficiently to C...</div><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">2016-01-06 9:25 GMT+01:00 Eliot Miranda <span dir="ltr">&lt;<a href="mailto:eliot.miranda@gmail.com" target="_blank">eliot.miranda@gmail.com</a>&gt;</span>:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"> <br><div dir="ltr">Hi Clément,<div><br></div><div>    also, before I forget, the primitive failure code for immutability violations should be PrimErrNoModification, not PrimErrInappropriate.<br><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Jan 6, 2016 at 12:00 AM, Eliot Miranda <span dir="ltr">&lt;<a href="mailto:eliot.miranda@gmail.com" target="_blank">eliot.miranda@gmail.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">Hi Clément,<br><div class="gmail_extra"><br><div class="gmail_quote">On Tue, Dec 29, 2015 at 1:31 AM,  <span dir="ltr">&lt;<a href="mailto:commits@source.squeak.org" target="_blank">commits@source.squeak.org</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>
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:<br>
<a href="http://source.squeak.org/VMMaker/VMMaker.oscog-cb.1615.mcz" rel="noreferrer" target="_blank">http://source.squeak.org/VMMaker/VMMaker.oscog-cb.1615.mcz</a><br>
<br>
==================== Summary ====================<br>
<br>
Name: VMMaker.oscog-cb.1615<br>
Author: cb<br>
Time: 29 December 2015, 10:30:37.779 am<br>
UUID: 712a0115-a780-4d2c-b4f8-c0bc78031889<br>
Ancestors: VMMaker.oscog-eem.1614, VMMaker.oscog-cb.1579<br>
<br>
Made a new version of immutability with Eliot&#39;s remarks:<br>
- control flow maintained for the simulator<br>
- option pragma everywhere needed<br>
- removed duplication in extStore bytecodes<br>
- removed useless instruction in primitiveClone.<br>
<br>
I tried to use it, it does not work, but it seems the bleeding edge is not stable.<br></blockquote><div><br></div><div>I see one reason why it doesn&#39;t work.  Let me take you through storeAndPopReceiverVariableBytecode</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">
<br>
The only thing I noticed is that some assertion may fail in #maybeCheckStackDepth:sp:pc: .<br>
<br>
=============== Diff against VMMaker.oscog-eem.1614 ===============<br>
<br>
Item was added:<br>
+ ----- Method: CoInterpreter&gt;&gt;ceCannotAssignTo:withIndex:valueToAssign: (in category &#39;trampolines&#39;) -----<br>
+ ceCannotAssignTo: immutableObject withIndex: index valueToAssign: valueToAssign<br>
+       &quot;index is unboxed.&quot;<br>
+       &lt;api&gt;<br>
+       &lt;option: #IMMUTABILITY&gt;<br>
+       instructionPointer := self popStack.<br>
+       self push: immutableObject.<br>
+       self push: valueToAssign.<br>
+       self push: (objectMemory integerObjectOf: index).<br>
+       self push: instructionPointer.<br>
+       ^ self<br>
+               ceSendAbort: (objectMemory splObj: SelectorAttemptToAssign)<br>
+               to: immutableObject<br>
+               numArgs: 2!<br>
<br>
Item was removed:<br>
- ----- Method: CoInterpreter&gt;&gt;extendedStoreBytecode (in category &#39;stack bytecodes&#39;) -----<br>
- extendedStoreBytecode<br>
-       &quot;Override to use itemporary:in:put:&quot;<br>
-       | descriptor variableType variableIndex |<br>
-       &lt;inline: true&gt;<br>
-       descriptor := self fetchByte.<br>
-       self fetchNextBytecode.<br>
-       variableType := descriptor &gt;&gt; 6 bitAnd: 3.<br>
-       variableIndex := descriptor bitAnd: 63.<br>
-       variableType = 0 ifTrue:<br>
-               [^objectMemory storePointer: variableIndex ofObject: self receiver withValue: self internalStackTop].<br>
-       variableType = 1 ifTrue:<br>
-               [^self itemporary: variableIndex in: localFP put: self internalStackTop].<br>
-       variableType = 3 ifTrue:<br>
-               [^self storeLiteralVariable: variableIndex withValue: self internalStackTop].<br>
-       self error: &#39;illegal store&#39;!<br>
<br>
Item was added:<br>
+ ----- Method: CoInterpreter&gt;&gt;extendedStoreBytecodePop: (in category &#39;stack bytecodes&#39;) -----<br>
+ extendedStoreBytecodePop: popBoolean<br>
+       &quot;Override to use itemporary:in:put:&quot;<br>
+       | descriptor variableType variableIndex value |<br>
+       &lt;inline: true&gt;<br>
+       descriptor := self fetchByte.<br>
+       self fetchNextBytecode.<br>
+       variableType := descriptor &gt;&gt; 6 bitAnd: 3.<br>
+       variableIndex := descriptor bitAnd: 63.<br>
+       value := self internalStackTop.<br>
+       popBoolean ifTrue: [ self internalPop: 1 ].<br>
+       variableType = 0 ifTrue:<br>
+               [^objectMemory storePointerImmutabilityCheck: variableIndex ofObject: self receiver withValue: value].<br>
+       variableType = 1 ifTrue:<br>
+               [^self itemporary: variableIndex in: localFP put: value].<br>
+       variableType = 3 ifTrue:<br>
+               [^self storeLiteralVariable: variableIndex withValue: value].<br>
+       self error: &#39;illegal store&#39;!<br>
<br>
Item was changed:<br>
  ----- Method: CoInterpreterPrimitives&gt;&gt;primitiveObjectAtPut (in category &#39;object access primitives&#39;) -----<br>
  primitiveObjectAtPut<br>
        &quot;Store a literal into a CompiledMethod at the given index. Defined for CompiledMethods only.&quot;<br>
        | thisReceiver rawHeader realHeader index newValue |<br>
        newValue := self stackValue: 0.<br>
        index := self stackValue: 1.<br>
        (objectMemory isNonIntegerObject: index) ifTrue:<br>
                [^self primitiveFailFor: PrimErrBadArgument].<br>
        index := objectMemory integerValueOf: index.<br>
        thisReceiver := self stackValue: 2.<br>
+       self cppIf: IMMUTABILITY<br>
+               ifTrue: [ (objectMemory isImmutable: thisReceiver) ifTrue: [ ^self primitiveFailFor: PrimErrInappropriate ] ].<br>
        rawHeader := self rawHeaderOf: thisReceiver.<br>
        realHeader := (self isCogMethodReference: rawHeader)<br>
                                        ifTrue: [(self cCoerceSimple: rawHeader to: #&#39;CogMethod *&#39;) methodHeader]<br>
                                        ifFalse: [rawHeader].<br>
        (index &gt; 0<br>
         and: [index &lt;= ((objectMemory literalCountOfMethodHeader: realHeader) + LiteralStart)]) ifFalse:<br>
                [^self primitiveFailFor: PrimErrBadIndex].<br>
        index = 1<br>
                ifTrue:<br>
                        [((objectMemory isNonIntegerObject: newValue)<br>
                         or: [(objectMemory literalCountOfMethodHeader: newValue) ~= (objectMemory literalCountOfMethodHeader: realHeader)]) ifTrue:<br>
                                [^self primitiveFailFor: PrimErrBadArgument].<br>
                         (self isCogMethodReference: rawHeader)<br>
                                ifTrue: [(self cCoerceSimple: rawHeader to: #&#39;CogMethod *&#39;) methodHeader: newValue]<br>
                                ifFalse: [objectMemory storePointerUnchecked: 0 ofObject: thisReceiver withValue: newValue]]<br>
                ifFalse:<br>
                        [objectMemory storePointer: index - 1 ofObject: thisReceiver withValue: newValue].<br>
        self pop: 3 thenPush: newValue!<br>
<br>
Item was added:<br>
+ ----- Method: CogObjectRepresentation&gt;&gt;genStoreSourceReg:slotIndex:destReg:scratchReg:inFrame: (in category &#39;compile abstract instructions&#39;) -----<br>
+ genStoreSourceReg: sourceReg slotIndex: index destReg: destReg scratchReg: scratchReg inFrame: inFrame<br>
+       ^ self<br>
+               genStoreSourceReg: sourceReg<br>
+               slotIndex: index<br>
+               destReg: destReg<br>
+               scratchReg: scratchReg<br>
+               inFrame: inFrame<br>
+               needsStoreCheck: true!<br>
<br>
Item was changed:<br>
  ----- Method: CogObjectRepresentationFor32BitSpur&gt;&gt;genInnerPrimitiveAtPut: (in category &#39;primitive generators&#39;) -----<br>
  genInnerPrimitiveAtPut: retNoffset<br>
        &quot;Implement the guts of primitiveAtPut&quot;<br>
+       | formatReg jumpImmediate jumpBadIndex jumpImmutable<br>
-       | formatReg jumpImmediate jumpBadIndex<br>
          jumpNotIndexablePointers jumpNotIndexableBits<br>
          jumpIsContext jumpIsCompiledMethod jumpIsBytes jumpHasFixedFields<br>
          jumpArrayOutOfBounds jumpFixedFieldsOutOfBounds<br>
          jumpWordsOutOfBounds jumpBytesOutOfBounds jumpBytesOutOfRange<br>
          jumpNonSmallIntegerValue jumpNegative jumpShortsUnsupported jumpNotPointers<br>
          |<br>
        &lt;inline: true&gt;<br>
        &quot;c.f. StackInterpreter&gt;&gt;stSizeOf: SpurMemoryManager&gt;&gt;lengthOf:format: fixedFieldsOf:format:length:&quot;<br>
        &lt;var: #jumpIsBytes type: #&#39;AbstractInstruction *&#39;&gt;<br>
        &lt;var: #jumpNegative type: #&#39;AbstractInstruction *&#39;&gt;<br>
        &lt;var: #jumpBadIndex type: #&#39;AbstractInstruction *&#39;&gt;<br>
        &lt;var: #jumpIsContext type: #&#39;AbstractInstruction *&#39;&gt;<br>
        &lt;var: #jumpImmediate type: #&#39;AbstractInstruction *&#39;&gt;<br>
        &lt;var: #jumpHasFixedFields type: #&#39;AbstractInstruction *&#39;&gt;<br>
        &lt;var: #jumpNotIndexableBits type: #&#39;AbstractInstruction *&#39;&gt;<br>
        &lt;var: #jumpArrayOutOfBounds type: #&#39;AbstractInstruction *&#39;&gt;<br>
        &lt;var: #jumpBytesOutOfBounds type: #&#39;AbstractInstruction *&#39;&gt;<br>
        &lt;var: #jumpShortsUnsupported type: #&#39;AbstractInstruction *&#39;&gt;<br>
        &lt;var: #jumpWordsOutOfBounds type: #&#39;AbstractInstruction *&#39;&gt;<br>
        &lt;var: #jumpNotIndexablePointers type: #&#39;AbstractInstruction *&#39;&gt;<br>
<br>
        jumpImmediate := self genJumpImmediate: ReceiverResultReg.<br>
        jumpBadIndex := self genJumpNotSmallInteger: Arg0Reg scratchReg: TempReg.<br>
        self genConvertSmallIntegerToIntegerInReg: Arg0Reg.<br>
        cogit SubCq: 1 R: Arg0Reg. &quot;1-rel =&gt; 0-rel&quot;<br>
<br>
        &quot;formatReg := self formatOf: ReceiverResultReg&quot;<br>
+       self cppIf: IMMUTABILITY<br>
+               ifTrue:<br>
+               [ self genGetFormatOf: ReceiverResultReg<br>
+                       into: (formatReg := SendNumArgsReg)<br>
+                       leastSignificantHalfOfBaseHeaderIntoScratch: TempReg.<br>
+               jumpImmutable := self genJumpBaseHeaderImmutable: TempReg ]<br>
+               ifFalse:<br>
+               [ self genGetFormatOf: ReceiverResultReg<br>
+                       into: (formatReg := SendNumArgsReg)<br>
+                       leastSignificantHalfOfBaseHeaderIntoScratch: NoReg ].<br>
-       self genGetFormatOf: ReceiverResultReg<br>
-               into: (formatReg := SendNumArgsReg)<br>
-               leastSignificantHalfOfBaseHeaderIntoScratch: NoReg.<br>
<br>
        self genGetNumSlotsOf: ReceiverResultReg into: ClassReg.<br>
<br>
        &quot;dispatch on format in a combination of highest dynamic frequency order first and convenience.<br>
                  0 = 0 sized objects (UndefinedObject True False et al)<br>
                  1 = non-indexable objects with inst vars (Point et al)<br>
                  2 = indexable objects with no inst vars (Array et al)<br>
                  3 = indexable objects with inst vars (MethodContext AdditionalMethodState et al)<br>
                  4 = weak indexable objects with inst vars (WeakArray et al)<br>
                  5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron)<br>
                  6 unused, reserved for exotic pointer objects?<br>
                  7 Forwarded Object, 1st field is pointer, rest of fields are ignored<br>
                  8 unused, reserved for exotic non-pointer objects?<br>
                  9 (?) 64-bit indexable<br>
                10 - 11 32-bit indexable<br>
                12 - 15 16-bit indexable<br>
                16 - 23 byte indexable<br>
                24 - 31 compiled method&quot;<br>
        cogit CmpCq: objectMemory weakArrayFormat R: formatReg.<br>
        jumpNotPointers := cogit JumpAbove: 0.<br>
        &quot;optimistic store check; assume index in range (almost always is).&quot;<br>
        self genStoreCheckReceiverReg: ReceiverResultReg<br>
                valueReg: Arg1Reg<br>
                scratchReg: TempReg<br>
                inFrame: false.<br>
<br>
        cogit CmpCq: objectMemory arrayFormat R: formatReg.<br>
        jumpNotIndexablePointers := cogit JumpBelow: 0.<br>
        jumpHasFixedFields := cogit JumpNonZero: 0.<br>
        cogit CmpR: Arg0Reg R: ClassReg.<br>
        jumpArrayOutOfBounds := cogit JumpBelowOrEqual: 0.<br>
        cogit AddCq: objectMemory baseHeaderSize &gt;&gt; objectMemory shiftForWord R: Arg0Reg.<br>
        cogit MoveR: Arg1Reg Xwr: Arg0Reg R: ReceiverResultReg.<br>
        cogit MoveR: Arg1Reg R: ReceiverResultReg.<br>
        cogit RetN: retNoffset.<br>
<br>
        jumpHasFixedFields jmpTarget: cogit Label.<br>
        self genGetClassIndexOfNonImm: ReceiverResultReg into: formatReg.<br>
        cogit CmpCq: ClassMethodContextCompactIndex R: formatReg.<br>
        jumpIsContext := cogit JumpZero: 0.<br>
        &quot;get # fixed fields in formatReg&quot;<br>
        cogit PushR: ClassReg.<br>
        self genGetClassObjectOfClassIndex: formatReg into: ClassReg scratchReg: TempReg.<br>
        self genLoadSlot: InstanceSpecificationIndex sourceReg: ClassReg destReg: formatReg.<br>
        cogit PopR: ClassReg.<br>
        self genConvertSmallIntegerToIntegerInReg: formatReg.<br>
        cogit AndCq: objectMemory fixedFieldsOfClassFormatMask R: formatReg.<br>
        cogit SubR: formatReg R: ClassReg.<br>
        cogit AddCq: objectMemory baseHeaderSize &gt;&gt; objectMemory shiftForWord R: formatReg.<br>
        cogit CmpR: Arg0Reg R: ClassReg.<br>
        jumpFixedFieldsOutOfBounds := cogit JumpBelowOrEqual: 0.<br>
        cogit AddR: formatReg R: Arg0Reg.<br>
        cogit MoveR: Arg1Reg Xwr: Arg0Reg R: ReceiverResultReg.<br>
        cogit MoveR: Arg1Reg R: ReceiverResultReg.<br>
        cogit RetN: retNoffset.<br>
<br>
        jumpNotPointers jmpTarget:<br>
                (cogit CmpCq: objectMemory firstCompiledMethodFormat R: formatReg).<br>
        jumpIsCompiledMethod := cogit JumpAboveOrEqual: 0.<br>
        jumpNonSmallIntegerValue := self genJumpNotSmallInteger: Arg1Reg scratchReg: TempReg.<br>
                                        cogit CmpCq: objectMemory firstByteFormat R: formatReg.<br>
        jumpIsBytes := cogit JumpAboveOrEqual: 0.<br>
                                        cogit CmpCq: objectMemory firstShortFormat R: formatReg.<br>
        jumpShortsUnsupported := cogit JumpAboveOrEqual: 0.<br>
                                        cogit CmpCq: objectMemory firstLongFormat R: formatReg.<br>
        &quot;For now ignore 64-bit indexability.&quot;<br>
        jumpNotIndexableBits := cogit JumpBelow: 0.<br>
<br>
        cogit CmpR: Arg0Reg R: ClassReg.<br>
        jumpWordsOutOfBounds := cogit JumpBelowOrEqual: 0.<br>
        cogit MoveR: Arg1Reg R: TempReg.<br>
        self genConvertSmallIntegerToIntegerInReg: TempReg.<br>
        (cogit lastOpcode setsConditionCodesFor: JumpNegative) ifFalse:<br>
                [self CmpCq: 0 R: ClassReg]. &quot;N.B. FLAGS := ClassReg - 0&quot;<br>
        jumpNegative := cogit JumpNegative: 0.<br>
        cogit AddCq: objectMemory baseHeaderSize &gt;&gt; objectMemory shiftForWord R: Arg0Reg.<br>
        cogit MoveR: TempReg Xwr: Arg0Reg R: ReceiverResultReg.<br>
        cogit MoveR: Arg1Reg R: ReceiverResultReg.<br>
        cogit RetN: retNoffset.<br>
<br>
        jumpIsBytes jmpTarget:<br>
                (cogit CmpCq: (objectMemory integerObjectOf: 255) R: Arg1Reg).<br>
        jumpBytesOutOfRange := cogit JumpAbove: 0.<br>
        cogit LogicalShiftLeftCq: objectMemory shiftForWord R: ClassReg.<br>
        cogit AndCq: objectMemory wordSize - 1 R: formatReg.<br>
        cogit SubR: formatReg R: ClassReg;<br>
        CmpR: Arg0Reg R: ClassReg.<br>
        jumpBytesOutOfBounds := cogit JumpBelowOrEqual: 0.<br>
        cogit MoveR: Arg1Reg R: TempReg.<br>
        self genConvertSmallIntegerToIntegerInReg: TempReg.<br>
        cogit AddCq: objectMemory baseHeaderSize R: Arg0Reg.<br>
        cogit MoveR: TempReg Xbr: Arg0Reg R: ReceiverResultReg.<br>
        cogit MoveR: Arg1Reg R: ReceiverResultReg.<br>
        cogit RetN: retNoffset.<br>
<br>
        &quot;there are no shorts as yet.  so this is dead code:<br>
        jumpIsShorts jmpTarget:<br>
                (cogit CmpCq: (objectMemory integerObjectOf: 65535) R: Arg1Reg).<br>
        jumpShortsOutOfRange := cogit JumpAbove: 0.<br>
        cogit LogicalShiftLeftCq: objectMemory shiftForWord - 1 R: ClassReg.<br>
        cogit AndCq: 1 R: formatReg.<br>
        cogit SubR: formatReg R: ClassReg;<br>
        CmpR: Arg0Reg R: ClassReg.<br>
        jumpShortsOutOfBounds := cogit JumpBelowOrEqual: 0.<br>
        cogit MoveR: Arg1Reg R: TempReg.<br>
        cogit genConvertSmallIntegerToIntegerInReg: TempReg.<br>
        cogit AddR: Arg0Reg R: ReceiverResultReg.<br>
        cogit MoveR: TempReg M16: objectMemory baseHeaderSize r: ReceiverResultReg.<br>
        cogit MoveR: Arg1Reg R: ReceiverResultReg.<br>
        jumpShortsDone := cogit Jump: 0.&quot;<br>
<br>
        jumpIsContext jmpTarget:<br>
        (jumpNegative jmpTarget:<br>
        (jumpNotIndexableBits jmpTarget:<br>
        (jumpBytesOutOfRange jmpTarget:<br>
        (jumpIsCompiledMethod jmpTarget:<br>
        (jumpArrayOutOfBounds jmpTarget:<br>
        (jumpBytesOutOfBounds jmpTarget:<br>
        (jumpShortsUnsupported jmpTarget:<br>
        (jumpWordsOutOfBounds jmpTarget:<br>
        (jumpNotIndexablePointers jmpTarget:<br>
        (jumpNonSmallIntegerValue jmpTarget:<br>
        (jumpFixedFieldsOutOfBounds jmpTarget: cogit Label))))))))))).<br>
+<br>
+       self cppIf: IMMUTABILITY ifTrue: [ jumpImmutable jmpTarget: cogit Label ].<br>
<br>
        cogit AddCq: 1 R: Arg0Reg. &quot;0-rel =&gt; 1-rel&quot;<br>
        self genConvertIntegerToSmallIntegerInReg: Arg0Reg.<br>
<br>
        jumpBadIndex jmpTarget: (jumpImmediate jmpTarget: cogit Label).<br>
<br>
        ^0!<br>
<br>
Item was changed:<br>
  ----- Method: CogObjectRepresentationFor32BitSpur&gt;&gt;genInnerPrimitiveStringAtPut: (in category &#39;primitive generators&#39;) -----<br>
  genInnerPrimitiveStringAtPut: retNoffset<br>
        &quot;Implement the guts of primitiveStringAtPut&quot;<br>
        | formatReg jumpBadIndex jumpBadArg jumpWordsDone jumpBytesOutOfRange<br>
+         jumpIsBytes jumpNotString jumpIsCompiledMethod jumpImmutable<br>
-         jumpIsBytes jumpNotString jumpIsCompiledMethod<br>
          jumpBytesOutOfBounds jumpWordsOutOfBounds jumpShortsUnsupported |<br>
        &lt;inline: true&gt;<br>
        &quot;c.f. StackInterpreter&gt;&gt;stSizeOf: SpurMemoryManager&gt;&gt;lengthOf:format: fixedFieldsOf:format:length:&quot;<br>
        &lt;var: #jumpBadArg type: #&#39;AbstractInstruction *&#39;&gt;<br>
        &lt;var: #jumpIsBytes type: #&#39;AbstractInstruction *&#39;&gt;<br>
        &lt;var: #jumpBadIndex type: #&#39;AbstractInstruction *&#39;&gt;<br>
        &lt;var: #jumpWordsDone type: #&#39;AbstractInstruction *&#39;&gt;<br>
        &lt;var: #jumpBytesOutOfBounds type: #&#39;AbstractInstruction *&#39;&gt;<br>
        &lt;var: #jumpShortsUnsupported type: #&#39;AbstractInstruction *&#39;&gt;<br>
        &lt;var: #jumpWordsOutOfBounds type: #&#39;AbstractInstruction *&#39;&gt;<br>
<br>
        jumpBadIndex := self genJumpNotSmallInteger: Arg0Reg.<br>
        cogit MoveR: Arg1Reg R: TempReg.<br>
        jumpBadArg := self genJumpNotCharacterInScratchReg: TempReg.<br>
        self genConvertSmallIntegerToIntegerInReg: Arg0Reg.<br>
        cogit SubCq: 1 R: Arg0Reg. &quot;1-rel =&gt; 0-rel&quot;<br>
<br>
        &quot;formatReg := self formatOf: ReceiverResultReg&quot;<br>
+       self cppIf: IMMUTABILITY<br>
+               ifTrue:<br>
+               [ self genGetFormatOf: ReceiverResultReg<br>
+                       into: (formatReg := SendNumArgsReg)<br>
+                       leastSignificantHalfOfBaseHeaderIntoScratch: TempReg.<br>
+               jumpImmutable := self genJumpBaseHeaderImmutable: TempReg ]<br>
+               ifFalse:<br>
+               [ self genGetFormatOf: ReceiverResultReg<br>
+                       into: (formatReg := SendNumArgsReg)<br>
+                       leastSignificantHalfOfBaseHeaderIntoScratch: NoReg ].<br>
-       self genGetFormatOf: ReceiverResultReg<br>
-               into: (formatReg := SendNumArgsReg)<br>
-               leastSignificantHalfOfBaseHeaderIntoScratch: NoReg.<br>
<br>
        self genGetNumSlotsOf: ReceiverResultReg into: ClassReg.<br>
<br>
        &quot;dispatch on format; words and/or bytes.<br>
                  0 to 8 = pointer objects, forwarders, reserved.<br>
                  9 (?) 64-bit indexable<br>
                10 - 11 32-bit indexable<br>
                12 - 15 16-bit indexable (but unused)<br>
                16 - 23 byte indexable<br>
                24 - 31 compiled method&quot;<br>
        cogit CmpCq: objectMemory firstLongFormat R: formatReg.<br>
        jumpNotString := cogit JumpBelowOrEqual: 0.<br>
                                        cogit CmpCq: objectMemory firstCompiledMethodFormat R: formatReg.<br>
        jumpIsCompiledMethod := cogit JumpAboveOrEqual: 0.<br>
                                        cogit CmpCq: objectMemory firstByteFormat R: formatReg.<br>
        jumpIsBytes := cogit JumpGreaterOrEqual: 0.<br>
                                        cogit CmpCq: objectMemory firstShortFormat R: formatReg.<br>
        jumpShortsUnsupported := cogit JumpGreaterOrEqual: 0.<br>
<br>
        cogit CmpR: Arg0Reg R: ClassReg.<br>
        jumpWordsOutOfBounds := cogit JumpBelowOrEqual: 0.<br>
        cogit MoveR: Arg1Reg R: TempReg.<br>
        self genConvertSmallIntegerToIntegerInReg: TempReg.<br>
        cogit AddCq: objectMemory baseHeaderSize &gt;&gt; objectMemory shiftForWord R: Arg0Reg.<br>
        cogit MoveR: TempReg Xwr: Arg0Reg R: ReceiverResultReg.<br>
        cogit MoveR: Arg1Reg R: ReceiverResultReg.<br>
        jumpWordsDone := cogit Jump: 0.<br>
<br>
        &quot;there are no shorts as yet.  so this is dead code:<br>
        jumpIsShorts jmpTarget:<br>
                (cogit CmpCq: (objectMemory integerObjectOf: 65535) R: Arg1Reg).<br>
        jumpShortsOutOfRange := cogit JumpAbove: 0.<br>
        cogit LogicalShiftLeftCq: objectMemory shiftForWord - 1 R: ClassReg.<br>
        cogit AndCq: 1 R: formatReg.<br>
        cogit SubR: formatReg R: ClassReg;<br>
        CmpR: Arg0Reg R: ClassReg.<br>
        jumpShortsOutOfBounds := cogit JumpBelowOrEqual: 0.<br>
        cogit MoveR: Arg1Reg R: TempReg.<br>
        cogit genConvertSmallIntegerToIntegerInReg: TempReg.<br>
        cogit AddR: Arg0Reg R: ReceiverResultReg.<br>
        cogit MoveR: TempReg M16: objectMemory baseHeaderSize r: ReceiverResultReg.<br>
        cogit MoveR: Arg1Reg R: ReceiverResultReg.<br>
        jumpShortsDone := cogit Jump: 0.&quot;<br>
<br>
        jumpIsBytes jmpTarget:<br>
                (cogit CmpCq: (objectMemory characterObjectOf: 255) R: Arg1Reg).<br>
        jumpBytesOutOfRange := cogit JumpAbove: 0.<br>
        cogit LogicalShiftLeftCq: objectMemory shiftForWord R: ClassReg.<br>
        cogit AndCq: objectMemory wordSize - 1 R: formatReg.<br>
        cogit SubR: formatReg R: ClassReg;<br>
        CmpR: Arg0Reg R: ClassReg.<br>
        jumpBytesOutOfBounds := cogit JumpBelowOrEqual: 0.<br>
        cogit MoveR: Arg1Reg R: TempReg.<br>
        self genConvertCharacterToCodeInReg: TempReg.<br>
        cogit AddCq: objectMemory baseHeaderSize R: Arg0Reg.<br>
        cogit MoveR: TempReg Xbr: Arg0Reg R: ReceiverResultReg.<br>
        cogit MoveR: Arg1Reg R: ReceiverResultReg.<br>
<br>
        jumpWordsDone jmpTarget:<br>
                (cogit RetN: retNoffset).<br>
<br>
        jumpBadArg jmpTarget:<br>
        (jumpNotString jmpTarget:<br>
        (jumpBytesOutOfRange jmpTarget:<br>
        (jumpIsCompiledMethod jmpTarget:<br>
        (jumpBytesOutOfBounds jmpTarget:<br>
        (jumpShortsUnsupported jmpTarget:<br>
        (jumpWordsOutOfBounds jmpTarget: cogit Label)))))).<br>
<br>
+       self cppIf: IMMUTABILITY ifTrue: [ jumpImmutable jmpTarget: cogit Label ].<br>
+<br>
        cogit AddCq: 1 R: Arg0Reg. &quot;0-rel =&gt; 1-rel&quot;<br>
        self genConvertIntegerToSmallIntegerInReg: Arg0Reg.<br>
<br>
        jumpBadIndex jmpTarget: cogit Label.<br>
<br>
        ^0!<br>
<br>
Item was changed:<br>
  CogObjectRepresentation subclass: #CogObjectRepresentationForSpur<br>
+       instanceVariableNames: &#39;ceScheduleScavengeTrampoline ceSmallActiveContextInMethodTrampoline ceSmallActiveContextInBlockTrampoline ceLargeActiveContextInMethodTrampoline ceLargeActiveContextInBlockTrampoline ceStoreCheckContextReceiverTrampoline ceCannotAssignToWithIndexTrampoline&#39;<br>
-       instanceVariableNames: &#39;ceScheduleScavengeTrampoline ceSmallActiveContextInMethodTrampoline ceSmallActiveContextInBlockTrampoline ceLargeActiveContextInMethodTrampoline ceLargeActiveContextInBlockTrampoline ceStoreCheckContextReceiverTrampoline&#39;<br>
        classVariableNames: &#39;&#39;<br>
        poolDictionaries: &#39;VMBytecodeConstants VMSqueakClassIndices&#39;<br>
        category: &#39;VMMaker-JIT&#39;!<br>
<br>
Item was changed:<br>
  ----- Method: CogObjectRepresentationForSpur class&gt;&gt;numTrampolines (in category &#39;accessing&#39;) -----<br>
  numTrampolines<br>
+       (initializationOptions at: #IMMUTABILITY ifAbsent: [false])<br>
+               ifTrue: [ ^ super numTrampolines + 7 ]<br>
+               ifFalse: [ ^ super numTrampolines + 6 ]!<br>
-       ^super numTrampolines + 6!<br>
<br>
Item was added:<br>
+ ----- Method: CogObjectRepresentationForSpur&gt;&gt;genImmutableCheck:slotIndex:sourceReg:scratchReg:popBoolean:needRestoreRcvr: (in category &#39;compile abstract instructions&#39;) -----<br>
+ genImmutableCheck: regHoldingObjectMutated slotIndex: index sourceReg: regHoldingValueToStore scratchReg: scratchReg popBoolean: popBoolean needRestoreRcvr: needRestoreRcvr<br>
+       | mutableJump fail |<br>
+       &lt;var: #mutableJump type: #&#39;AbstractInstruction *&#39;&gt;<br>
+       &lt;var: #fail type: #&#39;AbstractInstruction *&#39;&gt;<br>
+       &lt;inline: true&gt;<br>
+       &lt;option: #IMMUTABILITY&gt;<br>
+       &quot;Trampoline convention:<br>
+       - objectMutated passed in RcvrResultReg<br>
+       - index (unboxed) passed in TempReg<br>
+       - valueToStore passed in Arg1Reg.<br>
+       Simulated stack is flushed until simulatedStackPointer - 1, which implies full flush<br>
+       if popBoolean is true, else top value may not be flushed.<br>
+       We spill the top value (the value to store) for the trampoline if needed.&quot;<br>
+       self assert: regHoldingObjectMutated == ReceiverResultReg.<br>
+       self assert: scratchReg == TempReg.<br>
+       self assert: regHoldingValueToStore == ClassReg.<br>
+       mutableJump := self genJumpMutable: ClassReg scratchReg: TempReg.<br>
+<br>
+       &quot;We reach this code if the object mutated is immutable.&quot;<br>
+       &quot;simulatedStack state altered for the trampoline, spill top value if needed&quot;<br>
+       (popBoolean or: [ cogit ssTop spilled ]) ifFalse:<br>
+               [ self assert: (cogit ssTop type = SSRegister and: [cogit ssTop register = ClassReg]).<br>
+                 cogit PushR: ClassReg ].<br>
+       &quot;pass the unboxed index using TempReg&quot;<br>
+       cogit MoveCq: index R: TempReg.<br>
+       &quot;trampoline call and mcpc to bcpc annotation.&quot;<br>
+       cogit CallRT: ceCannotAssignToWithIndexTrampoline.<br>
+       cogit annotateBytecode: cogit Label.<br>
+       &quot;Top of stack is consumed by the trampoline. In case of store with non spilled value,<br>
+       restore ClassReg to match simulated stack state&quot;<br>
+       (popBoolean or: [ cogit ssTop spilled ]) ifFalse:<br>
+               [cogit popR: ClassReg].<br>
+       &quot;restore ReceiverResultReg state if needed&quot;<br>
+       needRestoreRcvr ifTrue: [ self putSelfInReceiverResultReg ].<br>
+       fail := cogit Jump: 0.<br>
+<br>
+       &quot;We reach this code is the object mutated is mutable&quot;<br>
+       mutableJump jmpTarget: cogit Label.<br>
+<br>
+       ^ fail!<br>
<br>
Item was added:<br>
+ ----- Method: CogObjectRepresentationForSpur&gt;&gt;genJumpBaseHeaderImmutable: (in category &#39;compile abstract instructions&#39;) -----<br>
+ genJumpBaseHeaderImmutable: baseHeaderReg<br>
+       &quot;baseHeader holds at least the least significant 32 bits of the object&quot;<br>
+       &lt;returnTypeC: #&#39;AbstractInstruction *&#39;&gt;<br>
+       &lt;option: #IMMUTABILITY&gt;<br>
+       cogit TstCq: objectMemory immutableBitMask R: baseHeaderReg.<br>
+       ^ cogit JumpNonZero: 0!<br>
<br>
Item was added:<br>
+ ----- Method: CogObjectRepresentationForSpur&gt;&gt;genJumpBaseHeaderMutable: (in category &#39;compile abstract instructions&#39;) -----<br>
+ genJumpBaseHeaderMutable: baseHeaderReg<br>
+       &quot;baseHeader holds at least the least significant 32 bits of the object&quot;<br>
+       &lt;returnTypeC: #&#39;AbstractInstruction *&#39;&gt;<br>
+       &lt;option: #IMMUTABILITY&gt;<br>
+       cogit TstCq: objectMemory immutableBitMask R: baseHeaderReg.<br>
+       ^ cogit JumpZero: 0!<br>
<br>
Item was added:<br>
+ ----- Method: CogObjectRepresentationForSpur&gt;&gt;genJumpImmutable:scratchReg: (in category &#39;compile abstract instructions&#39;) -----<br>
+ genJumpImmutable: sourceReg scratchReg: scratchReg<br>
+       &lt;returnTypeC: #&#39;AbstractInstruction *&#39;&gt;<br>
+       &lt;option: #IMMUTABILITY&gt;<br>
+       cogit MoveMw: 0 r: sourceReg R: scratchReg.<br>
+       ^ self genJumpBaseHeaderImmutable: scratchReg!<br>
<br>
Item was added:<br>
+ ----- Method: CogObjectRepresentationForSpur&gt;&gt;genJumpMutable:scratchReg: (in category &#39;compile abstract instructions&#39;) -----<br>
+ genJumpMutable: sourceReg scratchReg: scratchReg<br>
+       &lt;returnTypeC: #&#39;AbstractInstruction *&#39;&gt;<br>
+       &lt;option: #IMMUTABILITY&gt;<br>
+       cogit MoveMw: 0 r: sourceReg R: scratchReg.<br>
+       ^ self genJumpBaseHeaderMutable: scratchReg!<br>
<br>
Item was changed:<br>
  ----- Method: CogObjectRepresentationForSpur&gt;&gt;genStoreCheckReceiverReg:valueReg:scratchReg:inFrame: (in category &#39;compile abstract instructions&#39;) -----<br>
  genStoreCheckReceiverReg: destReg valueReg: valueReg scratchReg: scratchReg inFrame: inFrame<br>
        &quot;Generate the code for a store check of valueReg into destReg.&quot;<br>
        | jmpImmediate jmpDestYoung jmpSourceOld jmpAlreadyRemembered mask rememberedBitByteOffset |<br>
        &lt;var: #jmpImmediate type: #&#39;AbstractInstruction *&#39;&gt;<br>
        &lt;var: #jmpDestYoung type: #&#39;AbstractInstruction *&#39;&gt;<br>
        &lt;var: #jmpSourceOld type: #&#39;AbstractInstruction *&#39;&gt;<br>
        &lt;var: #jmpAlreadyRemembered type: #&#39;AbstractInstruction *&#39;&gt;<br>
        &quot;Is value stored an integer?  If so we&#39;re done&quot;<br>
        cogit MoveR: valueReg R: scratchReg.<br>
        cogit AndCq: objectMemory tagMask R: scratchReg.<br>
        jmpImmediate := cogit JumpNonZero: 0.<br>
        &quot;Get the old/new boundary in scratchReg&quot;<br>
        cogit MoveCw: objectMemory storeCheckBoundary R: scratchReg.<br>
        &quot;Is target young?  If so we&#39;re done&quot;<br>
        cogit CmpR: scratchReg R: destReg. &quot;N.B. FLAGS := destReg - scratchReg&quot;<br>
        jmpDestYoung := cogit JumpBelow: 0.<br>
        &quot;Is value stored old?  If so we&#39;re done.&quot;<br>
        cogit CmpR: scratchReg R: valueReg. &quot;N.B. FLAGS := valueReg - scratchReg&quot;<br>
        jmpSourceOld := cogit JumpAboveOrEqual: 0.<br>
        &quot;value is young and target is old.<br>
         Need to remember this only if the remembered bit is not already set.<br>
         Test the remembered bit.  Only need to fetch the byte containing it,<br>
         which reduces the size of the mask constant.&quot;<br>
        rememberedBitByteOffset := jmpSourceOld isBigEndian<br>
                                                                        ifTrue: [objectMemory baseHeaderSize - 1 - (objectMemory rememberedBitShift // 8)]<br>
                                                                        ifFalse:[objectMemory rememberedBitShift // 8].<br>
        mask := 1 &lt;&lt; (objectMemory rememberedBitShift \\ 8).<br>
        cogit MoveMb: rememberedBitByteOffset r: destReg R: scratchReg.<br>
        cogit AndCq: mask R: scratchReg.<br>
        jmpAlreadyRemembered := cogit JumpNonZero: 0.<br>
        &quot;Remembered bit is not set.  Call store check to insert dest into remembered table.&quot;<br>
        self assert: destReg == ReceiverResultReg.<br>
+       cogit<br>
+               evaluateTrampolineCallBlock:<br>
-       inFrame<br>
-               ifTrue:<br>
                        [cogit<br>
                                CallRT: ceStoreCheckTrampoline<br>
                                registersToBeSavedMask: (((cogit registerMaskFor: valueReg)<br>
                                                                                                bitOr: cogit callerSavedRegMask)<br>
                                                                                                bitClear: (cogit registerMaskFor: ReceiverResultReg))]<br>
+               protectLinkRegIfNot: inFrame.<br>
-               ifFalse:<br>
-                       [cogit backEnd saveAndRestoreLinkRegAround:<br>
-                               [cogit<br>
-                                       CallRT: ceStoreCheckTrampoline<br>
-                                       registersToBeSavedMask: (((cogit registerMaskFor: valueReg)<br>
-                                                                                                       bitOr: cogit callerSavedRegMask)<br>
-                                                                                                       bitClear: (cogit registerMaskFor: ReceiverResultReg))]].<br>
        jmpImmediate jmpTarget:<br>
        (jmpDestYoung jmpTarget:<br>
        (jmpSourceOld jmpTarget:<br>
        (jmpAlreadyRemembered jmpTarget:<br>
                cogit Label))).<br>
        ^0!<br>
<br>
Item was removed:<br>
- ----- Method: CogObjectRepresentationForSpur&gt;&gt;genStoreImmediateInSourceReg:slotIndex:destReg: (in category &#39;compile abstract instructions&#39;) -----<br>
- genStoreImmediateInSourceReg: sourceReg slotIndex: index destReg: destReg<br>
-       cogit MoveR: sourceReg<br>
-                  Mw: index * objectMemory wordSize + objectMemory baseHeaderSize<br>
-                  r: destReg.<br>
-       ^0!<br>
<br>
Item was removed:<br>
- ----- Method: CogObjectRepresentationForSpur&gt;&gt;genStoreSourceReg:slotIndex:destReg:scratchReg: (in category &#39;compile abstract instructions&#39;) -----<br>
- genStoreSourceReg: sourceReg slotIndex: index destReg: destReg scratchReg: scratchReg<br>
-       &quot;do the store&quot;<br>
-       cogit MoveR: sourceReg<br>
-                  Mw: index * objectMemory wordSize + objectMemory baseHeaderSize<br>
-                  r: destReg.<br>
-       &quot;now the check&quot;<br>
-       ^self genStoreCheckReceiverReg: destReg valueReg: sourceReg scratchReg: scratchReg inFrame: true!<br>
<br>
Item was removed:<br>
- ----- Method: CogObjectRepresentationForSpur&gt;&gt;genStoreSourceReg:slotIndex:destReg:scratchReg:inFrame: (in category &#39;compile abstract instructions&#39;) -----<br>
- genStoreSourceReg: sourceReg slotIndex: index destReg: destReg scratchReg: scratchReg inFrame: inFrame<br>
-       &quot;do the store&quot;<br>
-       cogit MoveR: sourceReg<br>
-                  Mw: index * objectMemory wordSize + objectMemory baseHeaderSize<br>
-                  r: destReg.<br>
-       &quot;now the check&quot;<br>
-       ^self genStoreCheckReceiverReg: destReg valueReg: sourceReg scratchReg: scratchReg inFrame: inFrame!<br>
<br>
Item was added:<br>
+ ----- Method: CogObjectRepresentationForSpur&gt;&gt;genStoreSourceReg:slotIndex:destReg:scratchReg:inFrame:needsStoreCheck: (in category &#39;compile abstract instructions&#39;) -----<br>
+ genStoreSourceReg: sourceReg slotIndex: index destReg: destReg scratchReg: scratchReg inFrame: inFrame needsStoreCheck: needsStoreCheck<br>
+       &quot;do the store&quot;<br>
+       cogit MoveR: sourceReg<br>
+                  Mw: index * objectMemory wordSize + objectMemory baseHeaderSize<br>
+                  r: destReg.<br>
+       &quot;now the check. needStoreCheck is false if the JIT has figured out that the value stored does not need the check (immediate, nil, true, false)&quot;<br>
+       needsStoreCheck ifTrue:<br>
+               [ ^ self<br>
+                       genStoreCheckReceiverReg: destReg<br>
+                       valueReg: sourceReg<br>
+                       scratchReg: scratchReg<br>
+                       inFrame: inFrame ].<br>
+       ^ 0!<br>
<br>
Item was changed:<br>
  ----- Method: CogObjectRepresentationForSpur&gt;&gt;genStoreSourceReg:slotIndex:intoNewObjectInDestReg: (in category &#39;compile abstract instructions&#39;) -----<br>
  genStoreSourceReg: sourceReg slotIndex: index intoNewObjectInDestReg: destReg<br>
+       &quot;This method is used for unchecked stores in objects after their creation (typically, inlined creation of Array, closures and some temp vectors).<br>
+       Currently there is no need to do the immutability check here&quot;<br>
        cogit MoveR: sourceReg<br>
                   Mw: index * objectMemory wordSize + objectMemory baseHeaderSize<br>
                   r: destReg.<br>
        ^0!<br>
<br>
Item was changed:<br>
  ----- Method: CogObjectRepresentationForSpur&gt;&gt;generateObjectRepresentationTrampolines (in category &#39;initialization&#39;) -----<br>
  generateObjectRepresentationTrampolines<br>
        &quot;Do the store check.  Answer the argument for the benefit of the code generator;<br>
         ReceiverResultReg may be caller-saved and hence smashed by this call.  Answering<br>
         it allows the code generator to reload ReceiverResultReg cheaply.<br>
         In Spur the only thing we leave to the run-time is adding the receiver to the<br>
         remembered set and setting its isRemembered bit.&quot;<br>
+       self<br>
+               cppIf: IMMUTABILITY<br>
+               ifTrue:<br>
+                       [ &quot;Arg1Reg is used as resultReg because cogit needs to restore the valueToStore<br>
+                       in the reg expected. The reg for the valueToStore is dynamically allocated, but<br>
+                       in most case, in the non-sista VM, it ends up being Arg1Reg.&quot;<br>
+                         ceCannotAssignToWithIndexTrampoline := cogit<br>
+                                                                       genTrampolineFor: #ceCannotAssignTo:withIndex:valueToAssign:<br>
+                                                                       called: &#39;ceCannotAssignToWithIndexTrampoline&#39;<br>
+                                                                       arg: ReceiverResultReg<br>
+                                                                       arg: TempReg<br>
+                                                                       arg: ClassReg ].<br>
        ceStoreCheckTrampoline := cogit<br>
                                                                        genTrampolineFor: #remember:<br>
                                                                        called: &#39;ceStoreCheckTrampoline&#39;<br>
                                                                        arg: ReceiverResultReg<br>
                                                                        result: cogit returnRegForStoreCheck.<br>
        ceStoreCheckContextReceiverTrampoline := self genStoreCheckContextReceiverTrampoline.<br>
        ceScheduleScavengeTrampoline := cogit<br>
                                                                                        genSafeTrampolineFor: #ceScheduleScavenge<br>
                                                                                        called: &#39;ceScheduleScavengeTrampoline&#39;.<br>
        ceSmallActiveContextInMethodTrampoline := self genActiveContextTrampolineLarge: false inBlock: false called: &#39;ceSmallMethodContext&#39;.<br>
        ceSmallActiveContextInBlockTrampoline := self genActiveContextTrampolineLarge: false inBlock: true called: &#39;ceSmallBlockContext&#39;.<br>
        ceLargeActiveContextInMethodTrampoline := self genActiveContextTrampolineLarge: true inBlock: false called: &#39;ceLargeMethodContext&#39;.<br>
        ceLargeActiveContextInBlockTrampoline := self genActiveContextTrampolineLarge: true inBlock: true called: &#39;ceLargeBlockContext&#39;!<br>
<br>
Item was removed:<br>
- ----- Method: CogObjectRepresentationForSqueakV3&gt;&gt;genStoreImmediateInSourceReg:slotIndex:destReg: (in category &#39;compile abstract instructions&#39;) -----<br>
- genStoreImmediateInSourceReg: sourceReg slotIndex: index destReg: destReg<br>
-       cogit MoveR: sourceReg Mw: index * objectMemory wordSize + objectMemory baseHeaderSize r: destReg.<br>
-       ^0!<br>
<br>
Item was removed:<br>
- ----- Method: CogObjectRepresentationForSqueakV3&gt;&gt;genStoreSourceReg:slotIndex:destReg:scratchReg:inFrame: (in category &#39;compile abstract instructions&#39;) -----<br>
- genStoreSourceReg: sourceReg slotIndex: index destReg: destReg scratchReg: scratchReg inFrame: inFrame<br>
-       | jmpImmediate jmpDestYoung jmpSourceOld jmpAlreadyRoot mask rootBitByteOffset |<br>
-       &lt;var: #jmpImmediate type: #&#39;AbstractInstruction *&#39;&gt;<br>
-       &lt;var: #jmpDestYoung type: #&#39;AbstractInstruction *&#39;&gt;<br>
-       &lt;var: #jmpSourceOld type: #&#39;AbstractInstruction *&#39;&gt;<br>
-       &lt;var: #jmpAlreadyRoot type: #&#39;AbstractInstruction *&#39;&gt;<br>
-       &quot;do the store&quot;<br>
-       cogit MoveR: sourceReg Mw: index * objectMemory wordSize + objectMemory baseHeaderSize r: destReg.<br>
-       &quot;now the check.  Is value stored an integer?  If so we&#39;re done&quot;<br>
-       cogit MoveR: sourceReg R: scratchReg.<br>
-       cogit AndCq: 1 R: scratchReg.<br>
-       jmpImmediate := cogit JumpNonZero: 0.<br>
-       &quot;Get the old/new boundary in scratchReg&quot;<br>
-       cogit MoveAw: objectMemory youngStartAddress R: scratchReg.<br>
-       &quot;Is target young?  If so we&#39;re done&quot;<br>
-       cogit CmpR: scratchReg R: destReg. &quot;N.B. FLAGS := destReg - scratchReg&quot;<br>
-       jmpDestYoung := cogit JumpAboveOrEqual: 0.<br>
-       &quot;Is value stored old?  If so we&#39;re done.&quot;<br>
-       cogit CmpR: scratchReg R: sourceReg. &quot;N.B. FLAGS := sourceReg - scratchReg&quot;<br>
-       jmpSourceOld := cogit JumpBelow: 0.<br>
-       &quot;value is young and target is old.<br>
-        Need to make this a root if the root bit is not already set.<br>
-        Test the root bit.  Only need to fetch the byte containing it,<br>
-        which reduces the size of the mask constant.&quot;<br>
-       rootBitByteOffset := jmpSourceOld isBigEndian<br>
-                                                       ifTrue: [objectMemory wordSize - RootBitDigitLength]<br>
-                                                       ifFalse:[RootBitDigitLength - 1].<br>
-       mask := RootBitDigitLength &gt; 1<br>
-                               ifTrue: [RootBit &gt;&gt; (RootBitDigitLength - 1 * 8)]<br>
-                               ifFalse: [RootBit].<br>
-       cogit MoveMb: rootBitByteOffset r: destReg R: scratchReg.<br>
-       cogit AndCq: mask R: scratchReg.<br>
-       jmpAlreadyRoot := cogit JumpNonZero: 0.<br>
-       &quot;Root bit is not set.  Call store check to insert dest into root table.&quot;<br>
-       self assert: destReg == ReceiverResultReg.<br>
-       inFrame<br>
-               ifTrue:<br>
-                       [cogit<br>
-                               CallRT: ceStoreCheckTrampoline<br>
-                               registersToBeSavedMask: (((cogit registerMaskFor: sourceReg)<br>
-                                                                                               bitOr: cogit callerSavedRegMask)<br>
-                                                                                               bitClear: (cogit registerMaskFor: ReceiverResultReg))]<br>
-               ifFalse:<br>
-                       [cogit backEnd saveAndRestoreLinkRegAround:<br>
-                               [cogit<br>
-                                       CallRT: ceStoreCheckTrampoline<br>
-                                       registersToBeSavedMask: (((cogit registerMaskFor: sourceReg)<br>
-                                                                                                       bitOr: cogit callerSavedRegMask)<br>
-                                                                                                       bitClear: (cogit registerMaskFor: ReceiverResultReg))]].<br>
-       jmpImmediate jmpTarget:<br>
-       (jmpDestYoung jmpTarget:<br>
-       (jmpSourceOld jmpTarget:<br>
-       (jmpAlreadyRoot jmpTarget:<br>
-               cogit Label))).<br>
-       ^0!<br>
<br>
Item was added:<br>
+ ----- Method: CogObjectRepresentationForSqueakV3&gt;&gt;genStoreSourceReg:slotIndex:destReg:scratchReg:inFrame:needsStoreCheck: (in category &#39;compile abstract instructions&#39;) -----<br>
+ genStoreSourceReg: sourceReg slotIndex: index destReg: destReg scratchReg: scratchReg inFrame: inFrame needsStoreCheck: needsStoreCheck<br>
+       | jmpImmediate jmpDestYoung jmpSourceOld jmpAlreadyRoot mask rootBitByteOffset |<br>
+       &lt;var: #jmpImmediate type: #&#39;AbstractInstruction *&#39;&gt;<br>
+       &lt;var: #jmpDestYoung type: #&#39;AbstractInstruction *&#39;&gt;<br>
+       &lt;var: #jmpSourceOld type: #&#39;AbstractInstruction *&#39;&gt;<br>
+       &lt;var: #jmpAlreadyRoot type: #&#39;AbstractInstruction *&#39;&gt;<br>
+       &quot;do the store&quot;<br>
+       cogit MoveR: sourceReg Mw: index * objectMemory wordSize + objectMemory baseHeaderSize r: destReg.<br>
+       &quot;if no need for the store check then returns&quot;<br>
+       needsStoreCheck ifFalse: [ ^ 0 ].<br>
+       &quot;now the check.  Is value stored an integer?  If so we&#39;re done&quot;<br>
+       cogit MoveR: sourceReg R: scratchReg.<br>
+       cogit AndCq: 1 R: scratchReg.<br>
+       jmpImmediate := cogit JumpNonZero: 0.<br>
+       &quot;Get the old/new boundary in scratchReg&quot;<br>
+       cogit MoveAw: objectMemory youngStartAddress R: scratchReg.<br>
+       &quot;Is target young?  If so we&#39;re done&quot;<br>
+       cogit CmpR: scratchReg R: destReg. &quot;N.B. FLAGS := destReg - scratchReg&quot;<br>
+       jmpDestYoung := cogit JumpAboveOrEqual: 0.<br>
+       &quot;Is value stored old?  If so we&#39;re done.&quot;<br>
+       cogit CmpR: scratchReg R: sourceReg. &quot;N.B. FLAGS := sourceReg - scratchReg&quot;<br>
+       jmpSourceOld := cogit JumpBelow: 0.<br>
+       &quot;value is young and target is old.<br>
+        Need to make this a root if the root bit is not already set.<br>
+        Test the root bit.  Only need to fetch the byte containing it,<br>
+        which reduces the size of the mask constant.&quot;<br>
+       rootBitByteOffset := jmpSourceOld isBigEndian<br>
+                                                       ifTrue: [objectMemory wordSize - RootBitDigitLength]<br>
+                                                       ifFalse:[RootBitDigitLength - 1].<br>
+       mask := RootBitDigitLength &gt; 1<br>
+                               ifTrue: [RootBit &gt;&gt; (RootBitDigitLength - 1 * 8)]<br>
+                               ifFalse: [RootBit].<br>
+       cogit MoveMb: rootBitByteOffset r: destReg R: scratchReg.<br>
+       cogit AndCq: mask R: scratchReg.<br>
+       jmpAlreadyRoot := cogit JumpNonZero: 0.<br>
+       &quot;Root bit is not set.  Call store check to insert dest into root table.&quot;<br>
+       self assert: destReg == ReceiverResultReg.<br>
+       cogit<br>
+               evaluateTrampolineCallBlock:<br>
+                       [cogit<br>
+                               CallRT: ceStoreCheckTrampoline<br>
+                               registersToBeSavedMask: (((cogit registerMaskFor: sourceReg)<br>
+                                                                                               bitOr: cogit callerSavedRegMask)<br>
+                                                                                               bitClear: (cogit registerMaskFor: ReceiverResultReg))]<br>
+               protectLinkRegIfNot: inFrame.<br>
+       jmpImmediate jmpTarget:<br>
+       (jmpDestYoung jmpTarget:<br>
+       (jmpSourceOld jmpTarget:<br>
+       (jmpAlreadyRoot jmpTarget:<br>
+               cogit Label))).<br>
+       ^0!<br>
<br>
Item was changed:<br>
  ----- Method: CogVMSimulator&gt;&gt;maybeCheckStackDepth:sp:pc: (in category &#39;debug support&#39;) -----<br>
  maybeCheckStackDepth: delta sp: sp pc: mcpc<br>
        | asp bcpc startbcpc cogHomeMethod cogBlockMethod csp debugStackPointers |<br>
        debugStackDepthDictionary ifNil: [^self].<br>
        (self isMachineCodeFrame: framePointer) ifFalse: [^self].<br>
        cogBlockMethod := self mframeCogMethod: framePointer.<br>
        cogHomeMethod := self asCogHomeMethod: cogBlockMethod.<br>
        debugStackPointers := debugStackDepthDictionary<br>
                                                                at: cogHomeMethod methodObject<br>
                                                                ifAbsentPut: [self debugStackPointersFor: cogHomeMethod methodObject].<br>
        startbcpc := cogHomeMethod = cogBlockMethod<br>
                                        ifTrue: [self startPCOfMethod: cogHomeMethod methodObject]<br>
                                        ifFalse: [self startPCOfClosure: (self pushedReceiverOrClosureOfFrame: framePointer)].<br>
        bcpc := cogit bytecodePCFor: mcpc startBcpc: startbcpc in: cogBlockMethod.<br>
        self assert: bcpc ~= 0.<br>
        cogBlockMethod ~= cogHomeMethod ifTrue:<br>
                [| lastbcpc |<br>
                 lastbcpc := cogit lastBytecodePCForBlockAt: startbcpc in: cogHomeMethod methodObject.<br>
                 bcpc &gt; lastbcpc ifTrue:<br>
                        [bcpc := lastbcpc]].<br>
        asp := self stackPointerIndexForFrame: framePointer WithSP: sp + objectMemory wordSize.<br>
        csp := debugStackPointers at: bcpc.<br>
        &quot;Compensate lazily for absent receiver sends.&quot;<br>
        (NewspeakVM<br>
         and: [asp - delta = csp<br>
         and: [cogit isAbsentReceiverSendAt: mcpc in: cogHomeMethod]]) ifTrue:<br>
                [csp := debugStackPointers at: bcpc put: csp + 1].<br>
        self assert: asp - delta + 1 = csp!<br>
<br>
Item was added:<br>
+ ----- Method: CurrentImageCoInterpreterFacadeForSpurObjectRepresentation&gt;&gt;immutableBitMask (in category &#39;accessing&#39;) -----<br>
+ immutableBitMask<br>
+       ^objectMemory immutableBitMask!<br>
<br>
Item was added:<br>
+ ----- Method: InterpreterPrimitives&gt;&gt;canBeImmutable: (in category &#39;object access primitives&#39;) -----<br>
+ canBeImmutable: oop<br>
+       &lt;option: #IMMUTABILITY&gt;<br>
+       | scheduler processLists |<br>
+<br>
+       self assert: (objectMemory isNonImmediate: oop).<br>
+<br>
+       &quot;For now we fail the primitive for contexts to we ensure there are no immutable contexts.<br>
+       Later we can consider having immutable contexts and send cannotReturn callback<br>
+       when returning to an immutable context. That would mean that setting a context<br>
+       to immutable would require a divorce and returns to immutable context are<br>
+       necessarily across stack pages&quot;<br>
+       (objectMemory isContext: oop) ifTrue: [ ^ false ].<br>
+<br>
+       &quot;I don&#39;t get it for semaphores so they can&#39;t be immutable&quot;<br>
+       (objectMemory isSemaphoreObj: oop) ifTrue: [^ false].<br>
+<br>
+       &quot;simple version of process management: we forbid Process and LinkedList instances to be immutable<br>
+       as well as the Processor and the array of activeProcess&quot;<br>
+       scheduler := self fetchPointer: ValueIndex ofObject: (self splObj: SchedulerAssociation).<br>
+       processLists := objectMemory fetchPointer: ProcessListsIndex ofObject: scheduler.<br>
+       oop = scheduler ifTrue: [ ^ false ].<br>
+       oop = processLists ifTrue: [ ^ false ].<br>
+       &quot;Is it a linkedList ?&quot;<br>
+       (objectMemory classIndexOf: (processLists at: 1)) = (objectMemory classIndexOf: oop) ifTrue: [ ^ false ].<br>
+       &quot;is it a Process ?&quot;<br>
+       (objectMemory classIndexOf: (objectMemory fetchPointer: ActiveProcessIndex ofObject: scheduler)) =  (objectMemory classIndexOf: oop) ifTrue: [ ^ false ].<br>
+<br>
+       &quot;The rest of the code is relative to process management: the Processor (the active<br>
+       process scheduler) can&#39;t be immutable, as well as all the objects relative to Process management &quot;<br>
+       &quot;scheduler := self fetchPointer: ValueIndex ofObject: (self splObj: SchedulerAssociation).<br>
+       processLists := objectMemory fetchPointer: ProcessListsIndex ofObject: scheduler.<br>
+       ((objectMemory formatOf: oop) = objectMemory nonIndexablePointerFormat)<br>
+               ifFalse:<br>
+                       [ (objectMemory isArrayNonImm: oop) ifFalse: [ ^ true ].<br>
+                         ^ (oop = processLists) not ].<br>
+       (objectMemory numSlotsOf: oop) &gt;= 2 ifFalse: [ ^ true ].<br>
+       &quot;&quot;is the oop the scheduler itself ?&quot;&quot;<br>
+       oop = scheduler ifTrue: [ ^ false ].<br>
+       1 to: (objectMemory numSlotsOf: processLists) do: [ :i |<br>
+               &quot;&quot;is the oop one of the linked lists ?&quot;&quot;<br>
+               (list := processLists at: i) = oop ifTrue: [^ false].<br>
+               &quot;&quot;is the oop one of the runnable process ?&quot;&quot;<br>
+               first := objectMemory fetchPointer: FirstLinkIndex ofObject: list.<br>
+               first = objectMemory nilObject ifFalse:<br>
+                       [ last := objectMemory fetchPointer: LastLinkIndex ofObject: list.<br>
+                         link := first.<br>
+                         [ link = last ] whileFalse:<br>
+                               [ link = oop ifTrue: [ ^ false ].<br>
+                                 link := objectMemory fetchPointer: NextLinkIndex ofObject: link. ] ] ].&quot;<br>
+       ^ true!<br>
<br>
Item was changed:<br>
  ----- Method: InterpreterPrimitives&gt;&gt;primitiveFloatAtPut (in category &#39;indexing primitives&#39;) -----<br>
  primitiveFloatAtPut<br>
        &quot;Provide platform-independent access to 32-bit words comprising<br>
         a Float.  Map index 1 onto the most significant word and index 2<br>
         onto the least significant word.&quot;<br>
        | rcvr index oopToStore valueToStore |<br>
        &lt;var: #valueToStore type: #usqInt&gt;<br>
        oopToStore := self stackTop.<br>
        valueToStore := self positive32BitValueOf: oopToStore.<br>
        self successful ifFalse:<br>
                [^self primitiveFailFor: PrimErrBadArgument].<br>
        rcvr := self stackValue: 2.<br>
        index := self stackValue: 1.<br>
        (objectMemory isImmediateFloat: rcvr) ifTrue:<br>
                [^self primitiveFailFor: PrimErrBadReceiver].<br>
+       self cppIf: IMMUTABILITY<br>
+               ifTrue: [ (objectMemory isImmutable: rcvr) ifTrue: [^self primitiveFailFor: PrimErrBadReceiver] ].<br>
        index = ConstOne ifTrue:<br>
                [objectMemory storeLong32: (VMBIGENDIAN ifTrue: [0] ifFalse: [1])<br>
                        ofObject: rcvr<br>
                        withValue: valueToStore.<br>
                ^self pop: 3 thenPush: oopToStore].<br>
        index = ConstTwo ifTrue:<br>
                [objectMemory storeLong32: (VMBIGENDIAN ifTrue: [1] ifFalse: [0])<br>
                        ofObject: rcvr<br>
                        withValue: valueToStore.<br>
                ^self pop: 3 thenPush: oopToStore].<br>
        self primitiveFailFor: ((objectMemory isIntegerObject: index)<br>
                                                        ifTrue: [PrimErrBadIndex]<br>
                                                        ifFalse: [PrimErrBadArgument])!<br>
<br>
Item was added:<br>
+ ----- Method: InterpreterPrimitives&gt;&gt;primitiveGetImmutability (in category &#39;object access primitives&#39;) -----<br>
+ primitiveGetImmutability<br>
+       &lt;option: #IMMUTABILITY&gt;<br>
+       | rcvr bool |<br>
+       rcvr := self stackValue: 0.<br>
+       bool := (objectMemory isOopImmutable: rcvr)<br>
+               ifTrue: [ TrueObject ]<br>
+               ifFalse: [ FalseObject ].<br>
+       self pop: argumentCount thenPush: (self splObj: bool)!<br>
<br>
Item was changed:<br>
  ----- Method: InterpreterPrimitives&gt;&gt;primitiveIntegerAtPut (in category &#39;sound primitives&#39;) -----<br>
  primitiveIntegerAtPut<br>
        &quot;Return the 32bit signed integer contents of a words receiver&quot;<br>
        | index rcvr sz addr value valueOop |<br>
        &lt;var: &#39;value&#39; type: &#39;int&#39;&gt;<br>
        valueOop := self stackValue: 0.<br>
        index := self stackIntegerValue: 1.<br>
        value := self signed32BitValueOf: valueOop.<br>
        self successful ifFalse:<br>
                [^self primitiveFailFor: PrimErrBadArgument].<br>
        rcvr := self stackValue: 2.<br>
        (objectMemory isWords: rcvr) ifFalse:<br>
                [^self primitiveFailFor: PrimErrInappropriate].<br>
+       self cppIf: IMMUTABILITY &quot;isWords: ensure non immediate&quot;<br>
+               ifTrue: [ (objectMemory isImmutable: rcvr) ifTrue: [ ^self primitiveFailFor: PrimErrInappropriate ] ].<br>
        sz := objectMemory lengthOf: rcvr.  &quot;number of fields&quot;<br>
        (index &gt;= 1 and: [index &lt;= sz]) ifFalse:<br>
                [^self primitiveFailFor: PrimErrBadIndex].<br>
        &quot;4 = 32 bits / 8&quot;<br>
        addr := rcvr + objectMemory baseHeaderSize + (index - 1 * 4). &quot;for zero indexing&quot;<br>
        value := objectMemory intAt: addr put: value.<br>
        self pop: 3 thenPush: valueOop &quot;pop all; return value&quot;<br>
  !<br>
<br>
Item was changed:<br>
  ----- Method: InterpreterPrimitives&gt;&gt;primitiveObjectAtPut (in category &#39;object access primitives&#39;) -----<br>
  primitiveObjectAtPut<br>
        &quot;Store a literal into a CompiledMethod at the given index. Defined for CompiledMethods only.&quot;<br>
        | thisReceiver index newValue |<br>
        newValue := self stackValue: 0.<br>
        index := self stackValue: 1.<br>
        ((objectMemory isNonIntegerObject: index)<br>
         or: [index = ConstOne and: [(objectMemory isNonIntegerObject: newValue)]]) ifTrue:<br>
                [^self primitiveFailFor: PrimErrBadArgument].<br>
        index := objectMemory integerValueOf: index.<br>
        thisReceiver := self stackValue: 2.<br>
+       self cppIf: IMMUTABILITY<br>
+               ifTrue: [ (objectMemory isImmutable: thisReceiver) ifTrue: [ ^self primitiveFailFor: PrimErrInappropriate ] ].<br>
        (index &gt; 0 and: [index &lt;= ((objectMemory literalCountOf: thisReceiver) + LiteralStart)]) ifFalse:<br>
                [^self primitiveFailFor: PrimErrBadIndex].<br>
        objectMemory storePointer: index - 1 ofObject: thisReceiver withValue: newValue.<br>
        self pop: 3 thenPush: newValue!<br>
<br>
Item was added:<br>
+ ----- Method: InterpreterPrimitives&gt;&gt;primitiveSetImmutability (in category &#39;object access primitives&#39;) -----<br>
+ primitiveSetImmutability<br>
+       &lt;option: #IMMUTABILITY&gt;<br>
+       | rcvr boolean wasImmutable |<br>
+       rcvr := self stackValue: 1.<br>
+       (objectMemory isImmediate: rcvr) ifTrue: [ ^ self primitiveFailFor: PrimErrInappropriate ].<br>
+       boolean := self booleanValueOf: self stackTop.<br>
+       self successful ifFalse:<br>
+               [^self primitiveFailFor: PrimErrBadArgument].<br>
+       boolean ifTrue:<br>
+               [ (self canBeImmutable: rcvr) ifFalse: [ ^ self primitiveFailFor: PrimErrInappropriate ] ].<br>
+       wasImmutable := (objectMemory isOopImmutable: rcvr)<br>
+               ifTrue: [ TrueObject ]<br>
+               ifFalse: [ FalseObject ].<br>
+       objectMemory setIsImmutableOf: rcvr to: boolean.<br>
+       self pop: argumentCount thenPush: (self splObj: wasImmutable)!<br>
<br>
Item was changed:<br>
  ----- Method: InterpreterPrimitives&gt;&gt;primitiveShortAtPut (in category &#39;sound primitives&#39;) -----<br>
  primitiveShortAtPut<br>
        &quot;Treat the receiver, which can be indexible by either bytes or words, as an array<br>
         of signed 16-bit values. Set the contents of the given index to the given value.<br>
         Note that the index specifies the i-th 16-bit entry, not the i-th byte or word.&quot;<br>
<br>
        | index rcvr value |<br>
        value := self stackTop.<br>
        index := self stackValue: 1.<br>
        ((objectMemory isIntegerObject: value)<br>
         and: [(objectMemory isIntegerObject: index)<br>
         and: [value := objectMemory integerValueOf: value.<br>
                  (value &gt;= -32768) and: [value &lt;= 32767]]]) ifFalse:<br>
                [^self primitiveFailFor: PrimErrBadArgument].<br>
        rcvr := self stackValue: 2.<br>
        (objectMemory isWordsOrBytes: rcvr) ifFalse:<br>
                [^self primitiveFailFor: PrimErrInappropriate].<br>
+       self cppIf: IMMUTABILITY &quot;isWordsOrBytes ensure non immediate&quot;<br>
+               ifTrue: [ (objectMemory isImmutable: rcvr) ifTrue: [ ^self primitiveFailFor: PrimErrInappropriate ] ].<br>
        index := objectMemory integerValueOf: index.<br>
        (index &gt;= 1 and: [index &lt;= (objectMemory num16BitUnitsOf: rcvr)]) ifFalse:<br>
                [^self primitiveFailFor: PrimErrBadIndex].<br>
        objectMemory storeShort16: index - 1 ofObject: rcvr withValue: value.<br>
        self pop: 3 thenPush: (objectMemory integerObjectOf: value)!<br>
<br>
Item was added:<br>
+ ----- Method: ObjectMemory&gt;&gt;storePointerImmutabilityCheck:ofObject:withValue: (in category &#39;object access&#39;) -----<br>
+ storePointerImmutabilityCheck: index ofObject: rcvr withValue: top<br>
+       &lt;inline: true&gt;<br>
+       ^ self storePointer: index ofObject: rcvr withValue: top!<br>
<br>
Item was changed:<br>
  ----- Method: SpurMemoryManager&gt;&gt;clone: (in category &#39;allocation&#39;) -----<br>
  clone: objOop<br>
        | numSlots fmt newObj |<br>
        numSlots := self numSlotsOf: objOop.<br>
        fmt := self formatOf: objOop.<br>
        numSlots &gt; self maxSlotsForNewSpaceAlloc<br>
                ifTrue:<br>
                        [newObj := self allocateSlotsInOldSpace: numSlots<br>
                                                        format: fmt<br>
                                                        classIndex: (self classIndexOf: objOop)]<br>
                ifFalse:<br>
                        [newObj := self allocateSlots: numSlots<br>
                                                        format: fmt<br>
                                                        classIndex: (self classIndexOf: objOop)].<br>
        newObj ifNil:<br>
                [^0].<br>
        (self isPointersFormat: fmt)<br>
                ifTrue:<br>
                        [| hasYoung |<br>
                         hasYoung := false.<br>
                         0 to: numSlots - 1 do:<br>
                                [:i| | oop |<br>
                                oop := self fetchPointer: i ofObject: objOop.<br>
                                (self isNonImmediate: oop) ifTrue:<br>
                                        [(self isForwarded: oop) ifTrue:<br>
                                                [oop := self followForwarded: oop].<br>
                                        ((self isNonImmediate: oop)<br>
                                         and: [self isYoungObject: oop]) ifTrue:<br>
                                                [hasYoung := true]].<br>
                                self storePointerUnchecked: i<br>
                                        ofObject: newObj<br>
                                        withValue: oop].<br>
                        (hasYoung<br>
                         and: [(self isYoungObject: newObj) not]) ifTrue:<br>
                                [scavenger remember: newObj]]<br>
                ifFalse:<br>
                        [0 to: numSlots - 1 do:<br>
                                [:i|<br>
                                self storePointerUnchecked: i<br>
                                        ofObject: newObj<br>
                                        withValue: (self fetchPointer: i ofObject: objOop)].<br>
                         fmt &gt;= self firstCompiledMethodFormat ifTrue:<br>
                                [coInterpreter maybeFixClonedCompiledMethod: newObj.<br>
                                 ((self isOldObject: newObj)<br>
                                  and: [(self isYoungObject: objOop) or: [self isRemembered: objOop]]) ifTrue:<br>
                                        [scavenger remember: newObj]]].<br>
        ^newObj!<br>
<br>
Item was changed:<br>
  ----- Method: SpurMemoryManager&gt;&gt;containsOnlyValidBecomeObjects: (in category &#39;become implementation&#39;) -----<br>
  containsOnlyValidBecomeObjects: array<br>
        &quot;Answer 0 if the array contains only unpinned non-immediates.<br>
         Otherwise answer an informative error code.<br>
         Can&#39;t become: immediates!!  Shouldn&#39;t become pinned objects.&quot;<br>
+       | fieldOffset effectsFlags oop errCode |<br>
-       | fieldOffset effectsFlags oop |<br>
        fieldOffset := self lastPointerOfArray: array.<br>
        effectsFlags := 0.<br>
        &quot;same size as array2&quot;<br>
        [fieldOffset &gt;= self baseHeaderSize] whileTrue:<br>
                [oop := self longAt: array + fieldOffset.<br>
                 (self isOopForwarded: oop) ifTrue:<br>
                        [oop := self followForwarded: oop.<br>
                         self longAt: array + fieldOffset put: oop].<br>
+                (errCode := self isOopValidBecome: oop) = 0 ifFalse: [^ errCode].<br>
-                (self isImmediate: oop) ifTrue: [^PrimErrInappropriate].<br>
-                (self isPinned: oop) ifTrue: [^PrimErrObjectIsPinned].<br>
                 effectsFlags := effectsFlags bitOr: (self becomeEffectFlagsFor: oop).<br>
                 fieldOffset := fieldOffset - self bytesPerOop].<br>
        &quot;only set flags after checking all args.&quot;<br>
        becomeEffectsFlags := effectsFlags.<br>
        ^0!<br>
<br>
Item was changed:<br>
  ----- Method: SpurMemoryManager&gt;&gt;forward:to: (in category &#39;become implementation&#39;) -----<br>
  forward: obj1 to: obj2<br>
        self set: obj1 classIndexTo: self isForwardedObjectClassIndexPun formatTo: self forwardedFormat.<br>
+       self cppIf: IMMUTABILITY ifTrue: [ self setIsImmutableOf: obj1 to: false ].<br>
        self storePointer: 0 ofForwarder: obj1 withValue: obj2.<br>
        &quot;For safety make sure the forwarder has a slot count that includes its contents.&quot;<br>
        (self rawNumSlotsOf: obj1) = 0 ifTrue:<br>
                [self rawNumSlotsOf: obj1 put: 1]!<br>
<br>
Item was added:<br>
+ ----- Method: SpurMemoryManager&gt;&gt;immutableBitMask (in category &#39;header format&#39;) -----<br>
+ immutableBitMask<br>
+       &quot;mask the immutable bit in the base header word&quot;<br>
+       &lt;option: #IMMUTABILITY&gt;<br>
+       ^ 1 &lt;&lt; self immutableBitShift!<br>
<br>
Item was added:<br>
+ ----- Method: SpurMemoryManager&gt;&gt;isOopValidBecome: (in category &#39;become implementation&#39;) -----<br>
+ isOopValidBecome: oop<br>
+       &quot;Answers 0 if the oop can be become.<br>
+       Answers an error code in the other case&quot;<br>
+       (self isImmediate: oop) ifTrue: [^PrimErrInappropriate].<br>
+       (self isPinned: oop) ifTrue: [^PrimErrObjectIsPinned].<br>
+       self<br>
+               cppIf: IMMUTABILITY<br>
+               ifTrue: [ (self isImmutable: oop) ifTrue: [^PrimErrInappropriate] ].<br>
+       ^ 0!<br>
<br>
Item was added:<br>
+ ----- Method: SpurMemoryManager&gt;&gt;storePointerImmutabilityCheck:ofObject:withValue: (in category &#39;object access&#39;) -----<br>
+ storePointerImmutabilityCheck: fieldIndex ofObject: objOop withValue: valuePointer<br>
+       &quot;Note must check here for stores of young objects into old ones.&quot;<br>
+       &lt;inline: true&gt; &quot;normal send in cannotAssign&quot;<br>
+<br>
+       self cppIf: IMMUTABILITY ifTrue:<br>
+               [ self assert: (self isImmediate: objOop) not.<br>
+               (self isImmutable: objOop) ifTrue:<br>
+                       [ ^ coInterpreter cannotAssign: valuePointer to: objOop withIndex: fieldIndex ] ].<br>
+<br>
+       ^ self storePointer: fieldIndex ofObject: objOop withValue: valuePointer!<br>
<br>
Item was changed:<br>
  ----- Method: StackInterpreter class&gt;&gt;initializePrimitiveTable (in category &#39;initialization&#39;) -----<br>
(excessive size, no diff calculated)<br>
<br>
Item was changed:<br>
  ----- Method: StackInterpreter&gt;&gt;bytecodePrimAtPut (in category &#39;common selector sends&#39;) -----<br>
  bytecodePrimAtPut<br>
        &quot;BytecodePrimAtPut will only succeed if the receiver is in the atCache.<br>
        Otherwise it will fail so that the more general primitiveAtPut will put it in the<br>
        cache after validating that message lookup results in a primitive response.<br>
         Override to insert in the atCache here.  This is necessary since once there<br>
         is a compiled at:[put:] primitive method (which doesn&#39;t use the at: cache) the<br>
         only way something can get installed in the atCache is here.&quot;<br>
+       | index rcvr atIx value correctRcvr |<br>
-       | index rcvr atIx value |<br>
        value := self internalStackTop.<br>
        index := self internalStackValue: 1.<br>
        rcvr := self internalStackValue: 2.<br>
+       self cppIf: IMMUTABILITY<br>
+               ifTrue: [ correctRcvr := objectMemory isOopMutable: rcvr ]<br>
+               ifFalse: [ correctRcvr := objectMemory isNonImmediate: rcvr ].<br>
+       (correctRcvr<br>
-       ((objectMemory isNonImmediate: rcvr)<br>
         and: [objectMemory isIntegerObject: index]) ifTrue:<br>
                [atIx := (rcvr bitAnd: AtCacheMask) + AtPutBase.  &quot;Index into atPutCache&quot;<br>
                 (atCache at: atIx+AtCacheOop) ~= rcvr ifTrue:<br>
                        [lkupClassTag := objectMemory fetchClassTagOfNonImm: rcvr.<br>
                         messageSelector := self specialSelector: 17.<br>
                         (self lookupInMethodCacheSel: messageSelector classTag: lkupClassTag) ifFalse:<br>
                                [argumentCount := 2.<br>
                                 ^self commonSendOrdinary].<br>
                         primitiveFunctionPointer == #primitiveAtPut<br>
                                ifTrue: [self install: rcvr inAtCache: atCache at: atIx string: false]<br>
                                ifFalse:<br>
                                        [primitiveFunctionPointer == #primitiveStringAtPut<br>
                                                ifTrue: [self install: rcvr inAtCache: atCache at: atIx string: true]<br>
                                                ifFalse:<br>
                                                        [argumentCount := 2.<br>
                                                         ^self commonSendOrdinary]]].<br>
                 self successful ifTrue:<br>
                        [self commonVariable: rcvr at: (objectMemory integerValueOf: index) put: value cacheIndex: atIx].<br>
                 self successful ifTrue:<br>
                        [self fetchNextBytecode.<br>
                         ^self internalPop: 3 thenPush: value].<br>
                 self initPrimCall].<br>
<br>
        messageSelector := self specialSelector: 17.<br>
        argumentCount := 2.<br>
        self normalSend!<br>
<br>
Item was added:<br>
+ ----- Method: StackInterpreter&gt;&gt;cannotAssign:to:withIndex: (in category &#39;stack bytecodes&#39;) -----<br>
+ cannotAssign: resultObj to: targetObj withIndex: index<br>
+       &lt;option: #IMMUTABILITY&gt;<br>
+       &lt;inline: true&gt; &quot;because of use of normalSend...&quot;<br>
+       self internalPush: targetObj.<br>
+       self internalPush: resultObj.<br>
+       self internalPush: (self integerObjectOf: index + 1).<br>
+       messageSelector := self splObj: SelectorAttemptToAssign.<br>
+       argumentCount := 2.<br>
+       ^ self normalSend!<br>
<br>
Item was changed:<br>
  ----- Method: StackInterpreter&gt;&gt;commonAtPut: (in category &#39;indexing primitive support&#39;) -----<br>
  commonAtPut: stringy<br>
        &quot;This code is called if the receiver responds primitively to at:Put:.<br>
         N.B. this does *not* use the at cache, instead inlining stObject:at:put:.<br>
         Using the at cache here would require that callers set messageSelector<br>
         and lkupClass and that is onerous and error-prone, and in any case,<br>
         inlining produces much better performance than using the at cache here.&quot;<br>
+       | value index rcvr badRcvr |<br>
-       | value index rcvr |<br>
        &lt;inline: true&gt; &quot;to get it inlined in primitiveAtPut and primitiveStringAtPut&quot;<br>
        self initPrimCall.<br>
        rcvr := self stackValue: 2.<br>
        index := self stackValue: 1.<br>
        value := self stackTop.<br>
+       self cppIf: IMMUTABILITY<br>
+               ifTrue: [ badRcvr := objectMemory isOopImmutable: rcvr ]<br>
+               ifFalse: [ badRcvr := objectMemory isImmediate: rcvr ].<br>
+       badRcvr ifTrue:<br>
-       (objectMemory isImmediate: rcvr) ifTrue:<br>
                [^self primitiveFailFor: PrimErrInappropriate].<br>
        &quot;No need to test for large positive integers here.  No object has 1g elements&quot;<br>
        ((objectMemory isNonIntegerObject: index)<br>
         or: [argumentCount &gt; 2 &quot;e.g. object:basicAt:put:&quot;<br>
                 and: [objectMemory isForwarded: rcvr]]) ifTrue:<br>
                [^self primitiveFailFor: PrimErrBadArgument].<br>
        index := objectMemory integerValueOf: index.<br>
        stringy<br>
                ifTrue: [self stObject: rcvr at: index put: (self asciiOfCharacter: value)]<br>
                ifFalse: [self stObject: rcvr at: index put: value].<br>
        self successful ifTrue:<br>
                [self pop: argumentCount+1 thenPush: value]!<br>
<br>
Item was changed:<br>
  ----- Method: StackInterpreter&gt;&gt;extStoreAndPopLiteralVariableBytecode (in category &#39;stack bytecodes&#39;) -----<br>
  extStoreAndPopLiteralVariableBytecode<br>
        &quot;236            11101100        i i i i i i i i Pop and Store Literal Variable #iiiiiiii (+ Extend A * 256)&quot;<br>
+       | variableIndex value |<br>
+       variableIndex := self fetchByte + (extA &lt;&lt; 8).<br>
+       self fetchNextBytecode.<br>
+       value := self internalStackTop.<br>
+       self internalPop: 1.<br>
+       extA := 0.<br>
+       self storeLiteralVariable: variableIndex withValue: value!<br>
-       self extStoreLiteralVariableBytecode.<br>
-       self internalPop: 1!<br>
<br>
Item was changed:<br>
  ----- Method: StackInterpreter&gt;&gt;extStoreAndPopReceiverVariableBytecode (in category &#39;stack bytecodes&#39;) -----<br>
  extStoreAndPopReceiverVariableBytecode<br>
        &quot;235            11101011        i i i i i i i i Pop and Store Receiver Variable #iiiiiii (+ Extend A * 256)&quot;<br>
+       | variableIndex value |<br>
+       variableIndex := self fetchByte + (extA &lt;&lt; 8).<br>
+       self fetchNextBytecode.<br>
+       extA := 0.<br>
+       value := self internalStackTop.<br>
+       self internalPop: 1.<br>
+       self storeMaybeContextReceiverVariable: variableIndex withValue: value!<br>
-       self extStoreReceiverVariableBytecode.<br>
-       self internalPop: 1!<br></blockquote><div><br></div><div><br></div><div>This used to read</div><div><br></div><div>storeAndPopReceiverVariableBytecode</div><div><span style="white-space:pre-wrap">        </span>| rcvr top |</div><div><span style="white-space:pre-wrap">        </span>rcvr := self receiver.</div><div><span style="white-space:pre-wrap">        </span>top := self internalStackTop.</div><div><span style="white-space:pre-wrap">        </span>objectMemory storePointer: (currentBytecode bitAnd: 7) ofObject: rcvr withValue: top.</div><div><span style="white-space:pre-wrap">        </span>self fetchNextBytecode.</div><div><span style="white-space:pre-wrap">        </span>self internalPop: 1</div><div><br></div><div>Note how currentBytecode is used before fetchNextBytecode.  fetchNextBytecode assigns currentBytecode:</div><div><br></div><div><div>fetchNextBytecode</div><div><span style="white-space:pre-wrap">        </span>&quot;This method fetches the next instruction (bytecode). Each bytecode method is responsible for fetching the next bytecode, preferably as early as possible to allow the memory system time to process the request before the next dispatch.&quot;</div><div><br></div><div><span style="white-space:pre-wrap">        </span>self cppIf: MULTIPLEBYTECODESETS</div><div><span style="white-space:pre-wrap">                </span>ifTrue: [currentBytecode := self fetchByte + bytecodeSetSelector]</div><div><span style="white-space:pre-wrap">                </span>ifFalse: [currentBytecode := self fetchByte]</div></div><div><br></div><div>So your rewrite to</div><div><br></div><div><div>storeAndPopReceiverVariableBytecode</div><div><span style="white-space:pre-wrap">        </span>| rcvr top |</div><div><span style="white-space:pre-wrap">        </span>rcvr := self receiver.</div><div><span style="white-space:pre-wrap">        </span>top := self internalStackTop.</div><div><span style="white-space:pre-wrap">        </span>self internalPop: 1.</div><div><span style="white-space:pre-wrap">        </span>self fetchNextBytecode.</div><div><span style="white-space:pre-wrap">        </span>objectMemory storePointerImmutabilityCheck: (currentBytecode bitAnd: 7) ofObject: rcvr withValue: top</div></div><div><br></div><div>breaks things since currentBytecode is now that of the next bytecode and the wrong inst var will probably be assigned.  So you need to rewrite, e.g. like this:</div><div><br></div><div><div>storeAndPopReceiverVariableBytecode</div><div><span style="white-space:pre-wrap">        </span>| rcvr top |</div><div><span style="white-space:pre-wrap">        </span>rcvr := self receiver.</div><div><span style="white-space:pre-wrap">        </span>top := self internalStackTop.</div><div><span style="white-space:pre-wrap">        </span>self internalPop: 1.</div><div><span style="white-space:pre-wrap">        </span>self</div><div><span style="white-space:pre-wrap">                </span>cCode: &quot;Slang will inline currentBytecode to a constant so this will work in C&quot;</div><div><span style="white-space:pre-wrap">                        </span>[self fetchNextBytecode.</div><div><span style="white-space:pre-wrap">                        </span> objectMemory</div><div><span style="white-space:pre-wrap">                                </span>storePointerImmutabilityCheck: (currentBytecode bitAnd: 7)</div><div><span style="white-space:pre-wrap">                                </span>ofObject: rcvr</div><div><span style="white-space:pre-wrap">                                </span>withValue: top]</div><div><span style="white-space:pre-wrap">                </span>inSmalltalk: &quot;But in Smalltalk we must use the currentBytecode&#39;s value, not the next&quot;</div><div><span style="white-space:pre-wrap">                        </span>[| instVarIndex |</div><div><span style="white-space:pre-wrap">                        </span> instVarIndex := currentBytecode bitAnd: 7.</div><div><span style="white-space:pre-wrap">                        </span> self fetchNextBytecode.</div><div><span style="white-space:pre-wrap">                        </span> objectMemory</div><div><span style="white-space:pre-wrap">                                </span>storePointerImmutabilityCheck: instVarIndex</div><div><span style="white-space:pre-wrap">                                </span>ofObject: rcvr</div><div><span style="white-space:pre-wrap">                                </span>withValue: top]</div></div><div><br></div><div>This needs to happen anywhere you use currentBytecode in the new immutability code.  We /don&#39;t/ want to use a variable to hold currentBytecode because that won&#39;t get inlined quite as nicely by Slang.</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">
Item was changed:<br>
  ----- Method: StackInterpreter&gt;&gt;extendedStoreAndPopBytecode (in category &#39;stack bytecodes&#39;) -----<br>
  extendedStoreAndPopBytecode<br>
+       &lt;inline: true&gt;<br>
+       self extendedStoreBytecodePop: true<br>
-<br>
-       self extendedStoreBytecode.<br>
-       self internalPop: 1.<br>
  !<br>
<br>
Item was changed:<br>
  ----- Method: StackInterpreter&gt;&gt;extendedStoreBytecode (in category &#39;stack bytecodes&#39;) -----<br>
  extendedStoreBytecode<br>
-       | descriptor variableType variableIndex |<br>
        &lt;inline: true&gt;<br>
+       self extendedStoreBytecodePop: false!<br>
-       descriptor := self fetchByte.<br>
-       self fetchNextBytecode.<br>
-       variableType := descriptor &gt;&gt; 6 bitAnd: 3.<br>
-       variableIndex := descriptor bitAnd: 63.<br>
-       variableType = 0 ifTrue:<br>
-               [^objectMemory storePointer: variableIndex ofObject: self receiver withValue: self internalStackTop].<br>
-       variableType = 1 ifTrue:<br>
-               [^self temporary: variableIndex in: localFP put: self internalStackTop].<br>
-       variableType = 3 ifTrue:<br>
-               [^self storeLiteralVariable: variableIndex withValue: self internalStackTop].<br>
-       self error: &#39;illegal store&#39;!<br>
<br>
Item was added:<br>
+ ----- Method: StackInterpreter&gt;&gt;extendedStoreBytecodePop: (in category &#39;stack bytecodes&#39;) -----<br>
+ extendedStoreBytecodePop: popBoolean<br>
+       | descriptor variableType variableIndex value |<br>
+       &lt;inline: true&gt;<br>
+       descriptor := self fetchByte.<br>
+       self fetchNextBytecode.<br>
+       variableType := descriptor &gt;&gt; 6 bitAnd: 3.<br>
+       variableIndex := descriptor bitAnd: 63.<br>
+       value := self internalStackTop.<br>
+       popBoolean ifTrue: [ self internalPop: 1 ].<br>
+       variableType = 0 ifTrue:<br>
+               [^objectMemory storePointerImmutabilityCheck: variableIndex ofObject: self receiver withValue: value].<br>
+       variableType = 1 ifTrue:<br>
+               [^self temporary: variableIndex in: localFP put: value].<br>
+       variableType = 3 ifTrue:<br>
+               [^self storeLiteralVariable: variableIndex withValue: value].<br>
+       self error: &#39;illegal store&#39;<br>
+ !<br>
<br>
Item was changed:<br>
  ----- Method: StackInterpreter&gt;&gt;storeAndPopReceiverVariableBytecode (in category &#39;stack bytecodes&#39;) -----<br>
  storeAndPopReceiverVariableBytecode<br>
        &quot;Note: This code uses<br>
        storePointerUnchecked:ofObject:withValue: and does the<br>
        store check explicitely in order to help the translator<br>
        produce better code.&quot;<br>
        | rcvr top |<br>
        rcvr := self receiver.<br>
        top := self internalStackTop.<br>
+       self internalPop: 1.<br>
-       objectMemory storePointer: (currentBytecode bitAnd: 7) ofObject: rcvr withValue: top.<br>
        self fetchNextBytecode.<br>
+       objectMemory storePointerImmutabilityCheck: (currentBytecode bitAnd: 7) ofObject: rcvr withValue: top.!<br>
-       self internalPop: 1!<br>
<br>
Item was changed:<br>
  ----- Method: StackInterpreter&gt;&gt;storeLiteralVariable:withValue: (in category &#39;stack bytecodes&#39;) -----<br>
  storeLiteralVariable: literalIndex withValue: anObject<br>
        | litVar |<br>
        litVar := self literal: literalIndex.<br>
        &quot;push/store/popLiteralVariable all fetch a literal, and either read or write the literal&#39;s value field.<br>
         The fetch of the literal needs an explicit check (otherwise we would have to scan all literals in<br>
         all methods in the stack zone, and the entire method on return, and global variables are relatively<br>
         rare; in my work image 8.7% of literals are globals).&quot;<br>
<br>
        (objectMemory isForwarded: litVar) ifTrue:<br>
                [litVar := objectMemory followForwarded: litVar].<br>
+       ^objectMemory storePointerImmutabilityCheck: ValueIndex ofObject: litVar withValue: anObject!<br>
-       ^objectMemory storePointer: ValueIndex ofObject: litVar withValue: anObject!<br>
<br>
Item was changed:<br>
  ----- Method: StackInterpreter&gt;&gt;storeMaybeContextReceiverVariable:withValue: (in category &#39;stack bytecodes&#39;) -----<br>
  storeMaybeContextReceiverVariable: fieldIndex withValue: anObject<br>
        &quot;Must trap accesses to married and widowed contexts.<br>
         But don&#39;t want to check on all inst var accesses.  This<br>
         method is only used by the long-form bytecodes, evading the cost.&quot;<br>
        | rcvr |<br>
        rcvr := self receiver.<br>
        ((self isWriteMediatedContextInstVarIndex: fieldIndex)<br>
        and: [(objectMemory isContextNonImm: rcvr)<br>
        and: [self isMarriedOrWidowedContext: rcvr]])<br>
                ifTrue:<br>
                        [self instVar: fieldIndex ofContext: rcvr put: anObject]<br>
                ifFalse:<br>
+                       [objectMemory storePointerImmutabilityCheck: fieldIndex ofObject: rcvr withValue: anObject]<br>
-                       [objectMemory storePointer: fieldIndex ofObject: rcvr withValue: anObject]<br>
  !<br>
<br>
Item was changed:<br>
  ----- Method: StackInterpreter&gt;&gt;trinaryInlinePrimitive: (in category &#39;miscellaneous bytecodes&#39;) -----<br>
  trinaryInlinePrimitive: primIndex<br>
        &quot;SistaV1:       248             11111000        iiiiiiii                mjjjjjjj                Call Primitive #iiiiiiii + (jjjjjjj * 256) m=1 means inlined primitive, no hard return after execution.&quot;<br>
        &lt;option: #SistaVM&gt;<br>
        | result |<br>
        primIndex caseOf: {<br>
<br>
                &quot;3000   unchecked Pointer Object&gt;&gt;at:put:.                      The receiver is guaranteed to be a pointer object.  The 0-relative (1-relative?) index is an in-range SmallInteger&quot;<br>
                [0]     -&gt;      [result := self internalStackTop.<br>
                                 objectMemory<br>
                                        storePointer: (objectMemory integerValueOf: (self internalStackValue: 1)) - 1<br>
                                        ofObject: (self internalStackValue: 2)<br>
                                        withValue: result.<br>
                                 self internalPop: 2; internalStackTopPut: result].<br>
                &quot;3001   unchecked Byte Object&gt;&gt;at:put:.                 The receiver is guaranteed to be a non-pointer object.  The 0-relative (1-relative?) index is an in-range SmallInteger.  The argument is a SmallInteger.  The primitive stores the least significant 8 bits.&quot;<br>
                [1]     -&gt;      [result := self internalStackTop.<br>
                                 objectMemory<br>
                                        storeByte: (objectMemory integerValueOf: (self internalStackValue: 1)) - 1<br>
                                        ofObject: (self internalStackValue: 2)<br>
                                        withValue: (objectMemory integerValueOf: result).<br>
                                 self internalPop: 2; internalStackTopPut: result].<br>
                &quot;3002   unchecked Word Object&gt;&gt;at:put:.                 The receiver is guaranteed to be a non-pointer object.  The 0-relative (1-relative?) index is an in-range SmallInteger.  The argument is a SmallInteger.  The primitive stores the least significant 16 bits.&quot;<br>
                [2]     -&gt;      [result := self internalStackTop.<br>
                                 objectMemory<br>
                                        storeShort16: (objectMemory integerValueOf: (self internalStackValue: 1)) - 1<br>
                                        ofObject: (self internalStackValue: 2)<br>
                                        withValue: (objectMemory integerValueOf: result).<br>
                                 self internalPop: 2; internalStackTopPut: result].<br>
                &quot;3003   unchecked DoubleWord Object&gt;&gt;at:put:.   The receiver is guaranteed to be a non-pointer object.  The 0-relative (1-relative?) index is an in-range SmallInteger.  The argument is a SmallInteger.  The primitive stores the least significant 32 bits.&quot;<br>
                [3]     -&gt;      [result := self internalStackTop.<br>
                                 objectMemory<br>
                                        storeLong32: (objectMemory integerValueOf: (self internalStackValue: 1)) - 1<br>
                                        ofObject: (self internalStackValue: 2)<br>
                                        withValue: (objectMemory integerValueOf: result).<br>
                                 self internalPop: 2; internalStackTopPut: result].<br>
                &quot;3004   unchecked QuadWord Object&gt;&gt;at:put:.             The receiver is guaranteed to be a non-pointer object.  The 0-relative (1-relative?) index is an in-range SmallInteger.  The argument is a SmallInteger.  The primitive stores the least significant 64 bits.&quot;<br>
                [4]     -&gt;      [result := self internalStackTop.<br>
                                 objectMemory<br>
                                        storeLong64: (objectMemory integerValueOf: (self internalStackValue: 1)) - 1<br>
                                        ofObject: (self internalStackValue: 2)<br>
                                        withValue: (objectMemory integerValueOf: result).<br>
                                 self internalPop: 2; internalStackTopPut: result] }<br>
        otherwise:<br>
                [localIP := localIP - 3.<br>
                 self respondToUnknownBytecode]!<br>
<br>
Item was changed:<br>
  ----- Method: StackInterpreterPrimitives&gt;&gt;primitiveInstVarAt (in category &#39;object access primitives&#39;) -----<br>
  primitiveInstVarAt<br>
        | index rcvr hdr fmt totalLength fixedFields value |<br>
        index := self stackTop.<br>
        rcvr := self stackValue: 1.<br>
        ((objectMemory isNonIntegerObject: index)<br>
         or: [argumentCount &gt; 1 &quot;e.g. object:instVarAt:&quot;<br>
                and: [objectMemory isOopForwarded: rcvr]]) ifTrue:<br>
                [^self primitiveFailFor: PrimErrBadArgument].<br>
+       (objectMemory isImmediate: rcvr) ifTrue: [^self primitiveFailFor: PrimErrInappropriate].<br>
        index := objectMemory integerValueOf: index.<br>
        hdr := objectMemory baseHeader: rcvr.<br>
        fmt := objectMemory formatOfHeader: hdr.<br>
        totalLength := objectMemory lengthOf: rcvr baseHeader: hdr format: fmt.<br>
        fixedFields := objectMemory fixedFieldsOf: rcvr format: fmt length: totalLength.<br>
        (index &gt;= 1 and: [index &lt;= fixedFields]) ifFalse:<br>
                [^self primitiveFailFor: PrimErrBadIndex].<br>
        (fmt = objectMemory indexablePointersFormat<br>
         and: [objectMemory isContextHeader: hdr])<br>
                ifTrue: [value := self externalInstVar: index - 1 ofContext: rcvr]<br>
                ifFalse: [value := self subscript: rcvr with: index format: fmt].<br>
        self pop: argumentCount + 1 thenPush: value!<br>
<br>
Item was changed:<br>
  ----- Method: StackInterpreterPrimitives&gt;&gt;primitiveInstVarAtPut (in category &#39;object access primitives&#39;) -----<br>
  primitiveInstVarAtPut<br>
        | newValue index rcvr hdr fmt totalLength fixedFields |<br>
        newValue := self stackTop.<br>
        index := self stackValue: 1.<br>
        rcvr := self stackValue: 2.<br>
        ((objectMemory isNonIntegerObject: index)<br>
         or: [argumentCount &gt; 2 &quot;e.g. object:instVarAt:put:&quot;<br>
  </blockquote></div></div></div></blockquote></div></div></div></div><br>...<br><br>[Message tronqué]  </blockquote></div><br></div>