<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Mar 31, 2016 at 2:27 PM, <span dir="ltr"><<a href="mailto:commits@source.squeak.org" target="_blank">commits@source.squeak.org</a>></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-eem.1755.mcz" rel="noreferrer" target="_blank">http://source.squeak.org/VMMaker/VMMaker.oscog-eem.1755.mcz</a><br>
<br>
==================== Summary ====================<br>
<br>
Name: VMMaker.oscog-eem.1755<br>
Author: eem<br>
Time: 1 January 1970, 2:26:33.237515 pm<br></blockquote><div><br></div><div>^^Ouch^^ !! That's what one gets for living on the bleeding edge :-/</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
UUID: adcfc67f-a856-48b8-aef2-71ff6275a903<br>
Ancestors: VMMaker.oscog-nice.1754<br>
<br>
Spur Cogit:<br>
Add a compile-time flag, CheckRememberedInTrampoline, that controls whether the remembered check is generated in-line or in the trampoline. If checked in the trampoline and on 64-bits use one memory access to get the header and share the access between the IMMUTABILITY check and the rememberedBit check. Factor out the rememberedBit check into its own method.<br>
<br>
The default leaves the code unchanged. I doubt that the option will improve anything significantly; measuring the generated code in the simulator the option saves only 0.6% of generated code. But it's so lovely being able to explore the question in a couple of hours instead of at least a day or two in C.<br>
<br>
Here's the data: CRIT=CheckRememberedInTrampoline; this methid is the last method generated in a listener image before it prompts the user:<br>
IMMTABILITY: 16r93F80 <-> 16r940E8: method: 16rC582F8 prim 117 selector: 16r6D12C8 primRead:into:startingAt:count:<br>
IMMTABILICRIT: 16r93250 <-> 16r933B8: method: 16rC582F8 prim 117 selector: 16r6D12C8 primRead:into:startingAt:count:<br>
VANILLA: 16r91C00 <-> 16r91D68: method: 16rC582F8 prim 117 selector: 16r6D12C8 primRead:into:startingAt:count:<br>
VANILLACRIT: 16r90D80 <-> 16r90EE8: method: 16rC582F8 prim 117 selector: 16r6D12C8 primRead:into:startingAt:count:<br>
16r93F80 - 16r93250 / 16r93250 * 100.0 0.5601422920704027<br>
16r91C00 - 16r90D80 / 16r90D80 * 100.0 0.6256742179072277<br>
<br>
And interestingly IMMUTABILITY adds only 1.5% to the code bulk; writes are rare.<br>
16r93F80 - 16r91C00 / 16r91C00 * 100.0 1.5222984562607205<br>
16r93250 - 16r90D80 / 16r90D80 * 100.0 1.5884573894282634<br>
<br>
=============== Diff against VMMaker.oscog-nice.1754 ===============<br>
<br>
Item was added:<br>
+ ----- Method: CogObjectRepresentation class>>initializeMiscConstants (in category 'initialization') -----<br>
+ initializeMiscConstants<br>
+ "Override to avoid inheriting (and hence repeating) VMClass class>>initializeMiscConstants.<br>
+ Subclasses that have misc constants to initialize will override further."!<br>
<br>
Item was changed:<br>
CogObjectRepresentation subclass: #CogObjectRepresentationForSpur<br>
instanceVariableNames: 'ceScheduleScavengeTrampoline ceSmallActiveContextInMethodTrampoline ceSmallActiveContextInBlockTrampoline ceLargeActiveContextInMethodTrampoline ceLargeActiveContextInBlockTrampoline ceStoreCheckContextReceiverTrampoline ceStoreTrampolines'<br>
+ classVariableNames: 'CheckRememberedInTrampoline NumStoreTrampolines'<br>
- classVariableNames: 'NumStoreTrampolines'<br>
poolDictionaries: 'VMBytecodeConstants VMSqueakClassIndices'<br>
category: 'VMMaker-JIT'!<br>
<br>
Item was added:<br>
+ ----- Method: CogObjectRepresentationForSpur class>>initializeMiscConstants (in category 'initialization') -----<br>
+ initializeMiscConstants<br>
+ CheckRememberedInTrampoline := initializationOptions at: #CheckRememberedInTrampoline ifAbsent: [false]!<br>
<br>
Item was added:<br>
+ ----- Method: CogObjectRepresentationForSpur>>genCheckRememberedBitOf:scratch: (in category 'compile abstract instructions') -----<br>
+ genCheckRememberedBitOf: objReg scratch: scratchReg<br>
+ "Check the remembered bit of the object in objReg; answer the jump taken if the bit is already set.<br>
+ Only need to fetch the byte containing it, which reduces the size of the mask constant."<br>
+ | rememberedBitByteOffset mask |<br>
+ rememberedBitByteOffset := cogit backEnd isBigEndian<br>
+ ifTrue: [objectMemory baseHeaderSize - 1 - (objectMemory rememberedBitShift // 8)]<br>
+ ifFalse:[objectMemory rememberedBitShift // 8].<br>
+ mask := 1 << (objectMemory rememberedBitShift \\ 8).<br>
+ cogit MoveMb: rememberedBitByteOffset r: objReg R: scratchReg.<br>
+ cogit TstCq: mask R: scratchReg.<br>
+ ^cogit JumpNonZero: 0!<br>
<br>
Item was changed:<br>
----- Method: CogObjectRepresentationForSpur>>genStoreCheckReceiverReg:valueReg:scratchReg:inFrame: (in category 'compile abstract instructions') -----<br>
genStoreCheckReceiverReg: destReg valueReg: valueReg scratchReg: scratchReg inFrame: inFrame<br>
"Generate the code for a store check of valueReg into destReg."<br>
+ | jmpImmediate jmpDestYoung jmpSourceOld jmpAlreadyRemembered |<br>
- | jmpImmediate jmpDestYoung jmpSourceOld jmpAlreadyRemembered mask rememberedBitByteOffset |<br>
<var: #jmpImmediate type: #'AbstractInstruction *'><br>
<var: #jmpDestYoung type: #'AbstractInstruction *'><br>
<var: #jmpSourceOld type: #'AbstractInstruction *'><br>
<var: #jmpAlreadyRemembered type: #'AbstractInstruction *'><br>
"Is value stored an immediate? If so we're done"<br>
jmpImmediate := self genJumpImmediate: valueReg.<br>
"Get the old/new boundary in scratchReg"<br>
cogit MoveCw: objectMemory storeCheckBoundary R: scratchReg.<br>
"Is target young? If so we're done"<br>
cogit CmpR: scratchReg R: destReg. "N.B. FLAGS := destReg - scratchReg"<br>
jmpDestYoung := cogit JumpBelow: 0.<br>
"Is value stored old? If so we're done."<br>
cogit CmpR: scratchReg R: valueReg. "N.B. FLAGS := valueReg - scratchReg"<br>
jmpSourceOld := cogit JumpAboveOrEqual: 0.<br>
"value is young and target is old.<br>
+ Need to remember this only if the remembered bit is not already set."<br>
+ CheckRememberedInTrampoline ifFalse:<br>
+ [jmpAlreadyRemembered := self genCheckRememberedBitOf: destReg scratch: scratchReg].<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."<br>
- rememberedBitByteOffset := jmpSourceOld isBigEndian<br>
- ifTrue: [objectMemory baseHeaderSize - 1 - (objectMemory rememberedBitShift // 8)]<br>
- ifFalse:[objectMemory rememberedBitShift // 8].<br>
- mask := 1 << (objectMemory rememberedBitShift \\ 8).<br>
- cogit MoveMb: rememberedBitByteOffset r: destReg R: scratchReg.<br>
- cogit AndCq: mask R: scratchReg.<br>
- jmpAlreadyRemembered := cogit JumpNonZero: 0.<br>
"Remembered bit is not set. Call store check to insert dest into remembered table."<br>
self assert: destReg = ReceiverResultReg.<br>
cogit<br>
evaluateTrampolineCallBlock: [cogit CallRT: ceStoreCheckTrampoline]<br>
protectLinkRegIfNot: inFrame.<br>
jmpImmediate jmpTarget:<br>
(jmpDestYoung jmpTarget:<br>
(jmpSourceOld jmpTarget:<br>
+ cogit Label)).<br>
+ CheckRememberedInTrampoline ifFalse:<br>
+ [jmpAlreadyRemembered jmpTarget: jmpSourceOld getJmpTarget].<br>
- (jmpAlreadyRemembered jmpTarget:<br>
- cogit Label))).<br>
^0!<br>
<br>
Item was added:<br>
+ ----- Method: CogObjectRepresentationForSpur>>genStoreCheckTrampoline (in category 'initialization') -----<br>
+ genStoreCheckTrampoline<br>
+ | jumpSC |<br>
+ <var: #jumpSC type: #'AbstractInstruction *'><br>
+ <inline: true><br>
+ CheckRememberedInTrampoline ifTrue:<br>
+ [cogit zeroOpcodeIndex.<br>
+ jumpSC := self genCheckRememberedBitOf: ReceiverResultReg scratch: cogit backEnd cResultRegister.<br>
+ self assert: jumpSC opcode = JumpNonZero.<br>
+ jumpSC opcode: JumpZero.<br>
+ cogit RetN: 0.<br>
+ jumpSC jmpTarget: cogit Label].<br>
+ ^cogit<br>
+ genTrampolineFor: #remember:<br>
+ called: 'ceStoreCheckTrampoline'<br>
+ numArgs: 1<br>
+ arg: ReceiverResultReg<br>
+ arg: nil<br>
+ arg: nil<br>
+ arg: nil<br>
+ regsToSave: (cogit callerSavedRegMask bitClear: (cogit registerMaskFor: ReceiverResultReg))<br>
+ pushLinkReg: true<br>
+ resultReg: cogit returnRegForStoreCheck<br>
+ appendOpcodes: CheckRememberedInTrampoline!<br>
<br>
Item was changed:<br>
----- Method: CogObjectRepresentationForSpur>>genStoreTrampolineCalled:instVarIndex: (in category 'initialization') -----<br>
genStoreTrampolineCalled: trampolineName instVarIndex: instVarIndex<br>
"Convention:<br>
- RcvrResultReg holds the object mutated.<br>
If immutability failure:<br>
- TempReg holds the instance variable index mutated<br>
if instVarIndex > numDedicatedStoreTrampoline<br>
- ClassReg holds the value to store<br>
Registers are not lived across this trampoline as the<br>
immutability failure may need new stack frames."<br>
<br>
+ | jumpSC jumpRC |<br>
- | jumpSC |<br>
<option: #IMMUTABILITY><br>
<var: #trampolineName type: #'char *'><br>
<var: #jumpSC type: #'AbstractInstruction *'><br>
+ <var: #jumpRC type: #'AbstractInstruction *'><br>
<inline: false><br>
cogit zeroOpcodeIndex.<br>
"SendNumArgsReg is mutated but we don't care as register are not live across the trampoline.<br>
There is no reason why registers cannot be saved over the remember: call, but since the<br>
immutability check is a suspension point, registers cannot remain live."<br>
jumpSC := self genJumpMutable: ReceiverResultReg scratchReg: SendNumArgsReg.<br>
cogit<br>
compileTrampolineFor: #ceCannotAssignTo:withIndex:valueToAssign:<br>
numArgs: 3<br>
arg: ReceiverResultReg<br>
arg: (instVarIndex < (NumStoreTrampolines - 1)<br>
ifTrue: [cogit trampolineArgConstant: instVarIndex]<br>
ifFalse: [TempReg])<br>
arg: ClassReg<br>
arg: nil<br>
regsToSave: cogit emptyRegisterMask<br>
pushLinkReg: true<br>
resultReg: NoReg.<br>
<br>
"Store check"<br>
jumpSC jmpTarget: cogit Label.<br>
+ "If on 64-bits and doing the remembered bit test here, we can combine the tests to fetch the header once."<br>
+ CheckRememberedInTrampoline ifTrue:<br>
+ [objectMemory wordSize = 8<br>
+ ifTrue:<br>
+ [cogit TstCq: 1 << objectMemory rememberedBitShift R: SendNumArgsReg.<br>
+ jumpRC := cogit JumpZero: 0.<br>
+ cogit RetN: 0]<br>
+ ifFalse:<br>
+ [jumpRC := self genCheckRememberedBitOf: ReceiverResultReg scratch: SendNumArgsReg.<br>
+ self assert: jumpRC opcode = JumpNonZero.<br>
+ jumpRC opcode: JumpZero.<br>
+ cogit RetN: 0].<br>
+ jumpRC jmpTarget: cogit Label].<br>
^ cogit genTrampolineFor: #remember:<br>
called: trampolineName<br>
numArgs: 1<br>
arg: ReceiverResultReg<br>
arg: nil<br>
arg: nil<br>
arg: nil<br>
regsToSave: cogit emptyRegisterMask<br>
pushLinkReg: true<br>
resultReg: NoReg<br>
appendOpcodes: true!<br>
<br>
Item was changed:<br>
----- Method: CogObjectRepresentationForSpur>>genStoreWithImmutabilityAndStoreCheckSourceReg:slotIndex:destReg:scratchReg:needRestoreRcvr: (in category 'compile abstract instructions') -----<br>
genStoreWithImmutabilityAndStoreCheckSourceReg: sourceReg slotIndex: index destReg: destReg scratchReg: scratchReg needRestoreRcvr: needRestoreRcvr<br>
"Store check code is duplicated to use a single trampoline"<br>
+ | immutableJump jmpImmediate jmpDestYoung jmpSourceOld jmpAlreadyRemembered |<br>
- | immutableJump jmpImmediate jmpDestYoung jmpSourceOld rememberedBitByteOffset jmpAlreadyRemembered mask |<br>
<var: #immutableJump type: #'AbstractInstruction *'><br>
<var: #jmpImmediate type: #'AbstractInstruction *'><br>
<var: #jmpDestYoung type: #'AbstractInstruction *'><br>
<var: #jmpSourceOld type: #'AbstractInstruction *'><br>
<var: #jmpAlreadyRemembered type: #'AbstractInstruction *'><br>
<br>
immutableJump := self genJumpImmutable: destReg scratchReg: scratchReg.<br>
<br>
cogit genTraceStores.<br>
<br>
"do the store"<br>
cogit MoveR: sourceReg<br>
Mw: index * objectMemory wordSize + objectMemory baseHeaderSize<br>
r: destReg.<br>
<br>
"store check"<br>
jmpImmediate := self genJumpImmediate: sourceReg.<br>
"Get the old/new boundary in scratchReg"<br>
cogit MoveCw: objectMemory storeCheckBoundary R: scratchReg.<br>
"Is target young? If so we're done"<br>
cogit CmpR: scratchReg R: destReg. "N.B. FLAGS := destReg - scratchReg"<br>
jmpDestYoung := cogit JumpBelow: 0.<br>
"Is value stored old? If so we're done."<br>
cogit CmpR: scratchReg R: sourceReg. "N.B. FLAGS := valueReg - scratchReg"<br>
jmpSourceOld := cogit JumpAboveOrEqual: 0.<br>
"value is young and target is old.<br>
+ Need to remember this only if the remembered bit is not already set."<br>
+ CheckRememberedInTrampoline ifFalse:<br>
+ [jmpAlreadyRemembered := self genCheckRememberedBitOf: destReg scratch: scratchReg].<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."<br>
- rememberedBitByteOffset := jmpSourceOld isBigEndian<br>
- ifTrue: [objectMemory baseHeaderSize - 1 - (objectMemory rememberedBitShift // 8)]<br>
- ifFalse:[objectMemory rememberedBitShift // 8].<br>
- mask := 1 << (objectMemory rememberedBitShift \\ 8).<br>
- cogit MoveMb: rememberedBitByteOffset r: destReg R: scratchReg.<br>
- cogit AndCq: mask R: scratchReg.<br>
- jmpAlreadyRemembered := cogit JumpNonZero: 0.<br>
"Set the inst var index for the benefit of the immutability check. The trampoline will<br>
repeat the check to choose between the immutbality violation and the store check."<br>
immutableJump jmpTarget: cogit Label.<br>
self genStoreTrampolineCall: index.<br>
needRestoreRcvr ifTrue: [ cogit putSelfInReceiverResultReg ].<br>
<br>
jmpImmediate jmpTarget:<br>
(jmpDestYoung jmpTarget:<br>
(jmpSourceOld jmpTarget:<br>
+ cogit Label)).<br>
+ CheckRememberedInTrampoline ifFalse:<br>
+ [jmpAlreadyRemembered jmpTarget: jmpSourceOld getJmpTarget].<br>
- (jmpAlreadyRemembered jmpTarget:<br>
- cogit Label))).<br>
-<br>
^ 0!<br>
<br>
Item was changed:<br>
----- Method: CogObjectRepresentationForSpur>>generateObjectRepresentationTrampolines (in category 'initialization') -----<br>
generateObjectRepresentationTrampolines<br>
"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."<br>
self<br>
cppIf: IMMUTABILITY<br>
ifTrue:<br>
[self cCode: [] inSmalltalk:<br>
[ceStoreTrampolines := CArrayAccessor on: (Array new: NumStoreTrampolines)].<br>
0 to: NumStoreTrampolines - 1 do:<br>
[:instVarIndex |<br>
ceStoreTrampolines<br>
at: instVarIndex<br>
put: (self<br>
genStoreTrampolineCalled: (cogit<br>
trampolineName: 'ceStoreTrampoline'<br>
numArgs: instVarIndex<br>
limit: NumStoreTrampolines - 2)<br>
instVarIndex: instVarIndex)]].<br>
+ ceStoreCheckTrampoline := self genStoreCheckTrampoline.<br>
- ceStoreCheckTrampoline := cogit<br>
- genTrampolineFor: #remember:<br>
- called: 'ceStoreCheckTrampoline'<br>
- arg: ReceiverResultReg<br>
- regsToSave: (cogit callerSavedRegMask bitClear: (cogit registerMaskFor: ReceiverResultReg))<br>
- result: cogit returnRegForStoreCheck.<br>
ceStoreCheckContextReceiverTrampoline := self genStoreCheckContextReceiverTrampoline.<br>
ceScheduleScavengeTrampoline := cogit<br>
genTrampolineFor: #ceScheduleScavenge<br>
called: 'ceScheduleScavengeTrampoline'<br>
regsToSave: cogit callerSavedRegMask.<br>
ceSmallActiveContextInMethodTrampoline := self genActiveContextTrampolineLarge: false inBlock: false called: 'ceSmallMethodContext'.<br>
ceSmallActiveContextInBlockTrampoline := self genActiveContextTrampolineLarge: false inBlock: true called: 'ceSmallBlockContext'.<br>
ceLargeActiveContextInMethodTrampoline := self genActiveContextTrampolineLarge: true inBlock: false called: 'ceLargeMethodContext'.<br>
ceLargeActiveContextInBlockTrampoline := self genActiveContextTrampolineLarge: true inBlock: true called: 'ceLargeBlockContext'!<br>
<br>
Item was changed:<br>
----- Method: Cogit class>>initializeMiscConstants (in category 'class initialization') -----<br>
initializeMiscConstants<br>
super initializeMiscConstants.<br>
Debug := initializationOptions at: #Debug ifAbsent: [false].<br>
(initializationOptions includesKey: #EagerInstructionDecoration)<br>
ifTrue:<br>
[EagerInstructionDecoration := initializationOptions at: #EagerInstructionDecoration]<br>
ifFalse:<br>
[EagerInstructionDecoration ifNil:<br>
[EagerInstructionDecoration := false]]. "speeds up single stepping but could lose fidelity"<br>
<br>
ProcessorClass := (initializationOptions at: #ISA ifAbsentPut: [self objectMemoryClass defaultISA]) caseOf: {<br>
[#X64] -> [BochsX64Alien].<br>
[#IA32] -> [BochsIA32Alien].<br>
[#ARMv5] -> [GdbARMAlien].<br>
[#MIPSEL] -> [MIPSELSimulator] }.<br>
CogCompilerClass := self activeCompilerClass.<br>
(CogCompilerClass withAllSuperclasses copyUpTo: CogAbstractInstruction) reverseDo:<br>
[:compilerClass| compilerClass initialize; initializeAbstractRegisters].<br>
+ self objectMemoryClass objectRepresentationClass initializeMiscConstants.<br>
"Our criterion for which methods to JIT is literal count. The default value is 60 literals or less."<br>
MaxLiteralCountForCompile := initializationOptions at: #MaxLiteralCountForCompile ifAbsent: [60].<br>
"we special-case 0, 1 & 2 argument sends, N is numArgs >= 3"<br>
NumSendTrampolines := 4.<br>
"Currently not even the ceImplicitReceiverTrampoline contains object references."<br>
NumObjRefsInRuntime := 0.<br>
<br>
NSCSelectorIndex := (NSSendCache instVarNames indexOf: #selector) - 1.<br>
NSCNumArgsIndex := (NSSendCache instVarNames indexOf: #numArgs) - 1.<br>
NSCClassTagIndex := (NSSendCache instVarNames indexOf: #classTag) - 1.<br>
NSCEnclosingObjectIndex := (NSSendCache instVarNames indexOf: #enclosingObject) - 1.<br>
NSCTargetIndex := (NSSendCache instVarNames indexOf: #target) - 1.<br>
NumOopsPerNSC := NSSendCache instVarNames size.<br>
<br>
"Max size to alloca when compiling.<br>
Mac OS X 10.6.8 segfaults approaching 8Mb.<br>
Linux 2.6.9 segfaults above 11Mb.<br>
WIndows XP segfaults approaching 2Mb."<br>
MaxStackAllocSize := 1024 * 1024 * 3 / 2 !<br>
<br>
Item was changed:<br>
----- Method: VMBasicConstants class>>namesDefinedAtCompileTime (in category 'C translation') -----<br>
namesDefinedAtCompileTime<br>
"Answer the set of names for variables that should be defined at compile time.<br>
Some of these get default values during simulation, and hence get defaulted in<br>
the various initializeMiscConstants methods. But that they have values should<br>
/not/ cause the code generator to do dead code elimination based on their<br>
default values."<br>
^#( VMBIGENDIAN<br>
IMMUTABILITY<br>
STACKVM COGVM COGMTVM SPURVM<br>
+ PharoVM "Pharo vs Squeak"<br>
+ EnforceAccessControl "Newspeak"<br>
+ CheckRememberedInTrampoline) "IMMUTABILITY"!<br>
- "Pharo vs Squeak" PharoVM<br>
- "Newspeak" EnforceAccessControl)!<br>
<br>
</blockquote></div><br><br clear="all"><div><br></div>-- <br><div class="gmail_signature"><div dir="ltr"><div><span style="font-size:small;border-collapse:separate"><div>_,,,^..^,,,_<br></div><div>best, Eliot</div></span></div></div></div>
</div></div>