Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.2023.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.2023
Author: eem
Time: 3 December 2016, 10:48:57.324652 am
UUID: ee0a00bb-160d-4efe-a960-f81681c4012a
Ancestors: VMMaker.oscog-eem.2022
RegisterAllocatingCogit:
Fix a slip in reconcileForwardsWith:; if a constant is merged with something other than the same constant then the resulting stack entry cannot be constant. This fixes the bug compiling the or: in UnixFileDirectory>>#setPathName:.
Refactor mergeWithFixupIfRequired: & mergeCurrentSimStackWith: to pass fixup so that mergeCurrentSimStackWith: can update isReceiverResultRegSelf correctly. This fixes the bug accessing array for the second send of #size in WeakSet>>scanFor:.
=============== Diff against VMMaker.oscog-eem.2022 ===============
Item was changed:
----- Method: CogRegisterAllocatingSimStackEntry>>reconcileForwardsWith: (in category 'compile abstract instructions') -----
reconcileForwardsWith: targetEntry
"Make the state of the receiver, a stack entry at the end of a basic block,
the same as the corresponding simStackEntry at the target of a preceding
+ jump to the beginning of the next basic block. Make sure targetEntry
+ reflects the state of the merged simStack; it will be installed as the current
+ entry by restoreSimStackAtMergePoint: in mergeWithFixupIfRequired:."
- jump to the beginning of the next basic block."
<var: #targetEntry type: #'targetEntry *'>
| targetReg |
(targetReg := targetEntry registerOrNone) = NoReg ifTrue:
[self assert: (self isSameEntryAs: targetEntry).
^self].
liveRegister ~= NoReg ifTrue:
[liveRegister ~= targetReg ifTrue:
[cogit MoveR: liveRegister R: targetReg].
(spilled and: [targetEntry spilled not]) ifTrue:
[cogit AddCq: objectRepresentation wordSize R: SPReg].
^self].
spilled
ifTrue:
[targetEntry spilled ifFalse:
[cogit PopR: targetReg. "KISS; generate the least number of instructions..."
^self]]
ifFalse:
[self deny: targetEntry spilled].
type caseOf: {
[SSBaseOffset] -> [cogit MoveMw: offset r: register R: targetReg].
[SSSpill] -> [cogit MoveMw: offset r: register R: targetReg].
[SSConstant] -> [cogit genMoveConstant: constant R: targetReg].
[SSRegister] -> [register ~= targetReg ifTrue:
+ [cogit MoveR: register R: targetReg]] }.
+ (targetEntry type = SSConstant
+ and: [type ~= SSConstant or: [constant ~= targetEntry constant]]) ifTrue:
+ [targetEntry
+ register: targetReg;
+ type: SSRegister]!
- [cogit MoveR: register R: targetReg]] }!
Item was changed:
CogClass subclass: #Cogit
instanceVariableNames: 'coInterpreter objectMemory objectRepresentation processor threadManager methodZone methodZoneBase codeBase minValidCallAddress lastNInstructions simulatedAddresses simulatedTrampolines simulatedVariableGetters simulatedVariableSetters printRegisters printInstructions compilationTrace clickConfirm breakPC breakBlock singleStep guardPageSize traceFlags traceStores breakMethod methodObj enumeratingCogMethod methodHeader initialPC endPC methodOrBlockNumArgs inBlock needsFrame hasYoungReferent primitiveIndex backEnd literalsManager postCompileHook methodLabel stackCheckLabel blockEntryLabel blockEntryNoContextSwitch blockNoContextSwitchOffset stackOverflowCall sendMiss missOffset entryPointMask checkedEntryAlignment uncheckedEntryAlignment cmEntryOffset entry cmNoCheckEntryOffset noCheckEntry fullBlockEntry cbEntryOffset fullBlockNoContextSwitchEntry cbNoSwitchEntryOffset picMNUAbort picInterpretAbort endCPICCase0 endCPICCase1 firstCPICCaseOffset cPICCaseSize cP
ICEndSize closedPICSize openPICSize fixups abstractOpcodes generatorTable byte0 byte1 byte2 byte3 bytecodePC bytecodeSetOffset opcodeIndex numAbstractOpcodes blockStarts blockCount labelCounter cStackAlignment expectedSPAlignment expectedFPAlignment codeModified maxLitIndex ceMethodAbortTrampoline cePICAbortTrampoline ceCheckForInterruptTrampoline ceCPICMissTrampoline ceReturnToInterpreterTrampoline ceBaseFrameReturnTrampoline ceSendMustBeBooleanAddTrueTrampoline ceSendMustBeBooleanAddFalseTrampoline ceCannotResumeTrampoline ceEnterCogCodePopReceiverReg ceCallCogCodePopReceiverReg ceCallCogCodePopReceiverAndClassRegs cePrimReturnEnterCogCode cePrimReturnEnterCogCodeProfiling ceNonLocalReturnTrampoline ceFetchContextInstVarTrampoline ceStoreContextInstVarTrampoline ceEnclosingObjectTrampoline ceFlushICache ceCheckFeaturesFunction ceTraceLinkedSendTrampoline ceTraceBlockActivationTrampoline ceTraceStoreTrampoline ceGetFP ceGetSP ceCaptureCStackPointers ordinarySendTrampolines superSen
dTrampolines directedSuperSendTrampolines dynamicSuperSendTrampolines outerSendTrampolines selfSendTrampolines firstSend lastSend realCEEnterCogCodePopReceiverReg realCECallCogCodePopReceiverReg realCECallCogCodePopReceiverAndClassRegs trampolineTableIndex trampolineAddresses objectReferencesInRuntime runtimeObjectRefIndex cFramePointerInUse debugPrimCallStackOffset ceTryLockVMOwner ceUnlockVMOwner extA extB tempOop numIRCs indexOfIRC theIRCs implicitReceiverSendTrampolines cogMethodSurrogateClass cogBlockMethodSurrogateClass nsSendCacheSurrogateClass CStackPointer CFramePointer cPICPrototype cPICEndOfCodeOffset cPICEndOfCodeLabel maxCPICCases debugBytecodePointers debugOpcodeIndices disassemblingMethod ceMallocTrampoline ceFreeTrampoline ceFFICalloutTrampoline'
+ classVariableNames: 'AltBlockCreationBytecodeSize AltFirstSpecialSelector AltNSSendIsPCAnnotated AltNumSpecialSelectors AnnotationConstantNames AnnotationShift AnnotationsWithBytecodePCs BlockCreationBytecodeSize Debug DisplacementMask DisplacementX2N EagerInstructionDecoration FirstAnnotation FirstSpecialSelector HasBytecodePC IsAbsPCReference IsAnnotationExtension IsDirectedSuperSend IsDisplacementX2N IsNSDynamicSuperSend IsNSImplicitReceiverSend IsNSSelfSend IsNSSendCall IsObjectReference IsRelativeCall IsSendCall IsSuperSend MapEnd MaxCPICCases MaxCompiledPrimitiveIndex MaxStackAllocSize MaxX2NDisplacement NSCClassTagIndex NSCEnclosingObjectIndex NSCNumArgsIndex NSCSelectorIndex NSCTargetIndex NSSendIsPCAnnotated NeedsFixupFlag NumObjRefsInRuntime NumOopsPerNSC NumSpecialSelectors NumTrampolines ProcessorClass RRRName'
- classVariableNames: 'AltBlockCreationBytecodeSize AltFirstSpecialSelector AltNSSendIsPCAnnotated AltNumSpecialSelectors AnnotationConstantNames AnnotationShift AnnotationsWithBytecodePCs BlockCreationBytecodeSize Debug DisplacementMask DisplacementX2N EagerInstructionDecoration FirstAnnotation FirstSpecialSelector HasBytecodePC IsAbsPCReference IsAnnotationExtension IsDirectedSuperSend IsDisplacementX2N IsNSDynamicSuperSend IsNSImplicitReceiverSend IsNSSelfSend IsNSSendCall IsObjectReference IsRelativeCall IsSendCall IsSuperSend MapEnd MaxCPICCases MaxCompiledPrimitiveIndex MaxStackAllocSize MaxX2NDisplacement NSCClassTagIndex NSCEnclosingObjectIndex NSCNumArgsIndex NSCSelectorIndex NSCTargetIndex NSSendIsPCAnnotated NeedsFixupFlag NumObjRefsInRuntime NumOopsPerNSC NumSpecialSelectors NumTrampolines ProcessorClass'
poolDictionaries: 'CogAbstractRegisters CogCompilationConstants CogMethodConstants CogRTLOpcodes VMBasicConstants VMBytecodeConstants VMObjectIndices VMStackFrameOffsets'
category: 'VMMaker-JIT'!
Cogit class
instanceVariableNames: 'generatorTable primitiveTable'!
!Cogit commentStamp: 'eem 4/6/2015 15:56' prior: 0!
I am the code generator for the Cog VM. My job is to produce machine code versions of methods for faster execution and to manage inline caches for faster send performance. I can be tested in the current image using my class-side in-image compilation facilities. e.g. try
StackToRegisterMappingCogit genAndDis: (Integer >> #benchFib)
I have concrete subclasses that implement different levels of optimization:
SimpleStackBasedCogit is the simplest code generator.
StackToRegisterMappingCogit is the current production code generator It defers pushing operands
to the stack until necessary and implements a register-based calling convention for low-arity sends.
StackToRegisterMappingCogit is an experimental code generator with support for counting
conditional branches, intended to support adaptive optimization.
coInterpreter <CoInterpreterSimulator>
the VM's interpreter with which I cooperate
methodZoneManager <CogMethodZoneManager>
the manager of the machine code zone
objectRepresentation <CogObjectRepresentation>
the object used to generate object accesses
processor <BochsIA32Alien|?>
the simulator that executes the IA32/x86 machine code I generate when simulating execution in Smalltalk
simulatedTrampolines <Dictionary of Integer -> MessageSend>
the dictionary mapping trap jump addresses to run-time routines used to warp from simulated machine code in to the Smalltalk run-time.
simulatedVariableGetters <Dictionary of Integer -> MessageSend>
the dictionary mapping trap read addresses to variables in run-time objects used to allow simulated machine code to read variables in the Smalltalk run-time.
simulatedVariableSetters <Dictionary of Integer -> MessageSend>
the dictionary mapping trap write addresses to variables in run-time objects used to allow simulated machine code to write variables in the Smalltalk run-time.
printRegisters printInstructions clickConfirm <Boolean>
flags controlling debug printing and code simulation
breakPC <Integer>
machine code pc breakpoint
cFramePointer cStackPointer <Integer>
the variables representing the C stack & frame pointers, which must change on FFI callback and return
selectorOop <sqInt>
the oop of the methodObj being compiled
methodObj <sqInt>
the bytecode method being compiled
initialPC endPC <Integer>
the start and end pcs of the methodObj being compiled
methodOrBlockNumArgs <Integer>
argument count of current method or block being compiled
needsFrame <Boolean>
whether methodObj or block needs a frame to execute
primitiveIndex <Integer>
primitive index of current method being compiled
methodLabel <CogAbstractOpcode>
label for the method header
blockEntryLabel <CogAbstractOpcode>
label for the start of the block dispatch code
stackOverflowCall <CogAbstractOpcode>
label for the call of ceStackOverflow in the method prolog
sendMissCall <CogAbstractOpcode>
label for the call of ceSICMiss in the method prolog
entryOffset <Integer>
offset of method entry code from start (header) of method
entry <CogAbstractOpcode>
label for the first instruction of the method entry code
noCheckEntryOffset <Integer>
offset of the start of a method proper (after the method entry code) from start (header) of method
noCheckEntry <CogAbstractOpcode>
label for the first instruction of start of a method proper
fixups <Array of <AbstractOpcode Label | nil>>
the labels for forward jumps that will be fixed up when reaching the relevant bytecode. fixup shas one element per byte in methodObj's bytecode
abstractOpcodes <Array of <AbstractOpcode>>
the code generated when compiling methodObj
byte0 byte1 byte2 byte3 <Integer>
individual bytes of current bytecode being compiled in methodObj
bytecodePointer <Integer>
bytecode pc (same as Smalltalk) of the current bytecode being compiled
opcodeIndex <Integer>
the index of the next free entry in abstractOpcodes (this code is translated into C where OrderedCollection et al do not exist)
numAbstractOpcodes <Integer>
the number of elements in abstractOpcocdes
blockStarts <Array of <BlockStart>>
the starts of blocks in the current method
blockCount
the index into blockStarts as they are being noted, and hence eventually the total number of blocks in the current method
labelCounter <Integer>
a nicety for numbering labels not needed in the production system but probably not expensive enough to worry about
ceStackOverflowTrampoline <Integer>
ceSend0ArgsTrampoline <Integer>
ceSend1ArgsTrampoline <Integer>
ceSend2ArgsTrampoline <Integer>
ceSendNArgsTrampoline <Integer>
ceSendSuper0ArgsTrampoline <Integer>
ceSendSuper1ArgsTrampoline <Integer>
ceSendSuper2ArgsTrampoline <Integer>
ceSendSuperNArgsTrampoline <Integer>
ceSICMissTrampoline <Integer>
ceCPICMissTrampoline <Integer>
ceStoreCheckTrampoline <Integer>
ceReturnToInterpreterTrampoline <Integer>
ceBaseFrameReturnTrampoline <Integer>
ceSendMustBeBooleanTrampoline <Integer>
ceClosureCopyTrampoline <Integer>
the various trampolines (system-call-like jumps from machine code to the run-time).
See Cogit>>generateTrampolines for the mapping from trampoline to run-time
routine and then read the run-time routine for a funcitonal description.
ceEnterCogCodePopReceiverReg <Integer>
the enilopmart (jump from run-time to machine-code)
methodZoneBase <Integer>
!
Cogit class
instanceVariableNames: 'generatorTable primitiveTable'!
Item was changed:
----- Method: Cogit>>recordProcessing (in category 'simulation only') -----
recordProcessing
| inst |
self recordRegisters.
inst := self recordLastInstruction.
+ "Set RRRName ito the selector that accesses ReceiverResultReg (RRR) to alter instruction printing to add the value of RRR as a suffix
+ (RRRName := #rdx)
+ (RRRName := #edx)
+ (RRRName := #nil)"
printRegisters ifTrue:
+ [RRRName ifNil: [processor printRegistersOn: coInterpreter transcript].
- [processor printRegistersOn: coInterpreter transcript.
printInstructions ifFalse:
+ [coInterpreter transcript cr]].
- [coInterpreter transcript cr].].
printInstructions ifTrue:
[printRegisters ifTrue:
[coInterpreter transcript cr].
+ coInterpreter transcript nextPutAll: inst.
+ RRRName ifNotNil:
+ [coInterpreter transcript space; nextPutAll: RRRName; space.
+ (processor perform: RRRName) printOn: coInterpreter transcript base: 16 length: 8 padded: false].
+ coInterpreter transcript cr; flush]!
- coInterpreter transcript nextPutAll: inst; cr; flush]!
Item was changed:
----- Method: RegisterAllocatingCogit>>ensureFixupAt: (in category 'bytecode generator support') -----
ensureFixupAt: targetIndex
| fixup |
<var: #fixup type: #'BytecodeFixup *'>
fixup := self fixupAt: targetIndex.
fixup needsFixup
ifTrue:
[fixup mergeSimStack
ifNil: [self setMergeSimStackOf: fixup]
+ ifNotNil: [self mergeCurrentSimStackWith: fixup]]
- ifNotNil: [self mergeCurrentSimStackWith: fixup mergeSimStack]]
ifFalse:
[self assert: fixup mergeSimStack isNil.
self moveVolatileSimStackEntriesToRegisters.
self setMergeSimStackOf: fixup].
^super ensureFixupAt: targetIndex!
Item was changed:
----- Method: RegisterAllocatingCogit>>mergeCurrentSimStackWith: (in category 'bytecode generator support') -----
+ mergeCurrentSimStackWith: fixup
+ <var: #fixup type: #'BytecodeFixup *'>
+ | mergeSimStack currentEntry targetEntry |
- mergeCurrentSimStackWith: mergeSimStack
<var: #mergeSimStack type: #'SimStackEntry *'>
"At a merge point the cogit expects the stack to be in the same state as mergeSimStack.
mergeSimStack is the state as of some jump forward to this point. So make simStack agree
with mergeSimStack (it is, um, problematic to plant code at the jump).
Values may have to be assigned to registers. Registers may have to be swapped.
The state of optStatus must agree."
- | currentEntry targetEntry |
<var: #currentEntry type: #'SimStackEntry *'>
<var: #targetEntry type: #'SimStackEntry *'>
+ (mergeSimStack := fixup mergeSimStack) ifNil: [^self].
- mergeSimStack ifNil: [^self].
"Assignments amongst the registers must be made in order to avoid overwriting.
If necessary exchange registers amongst simStack's entries to resolve any conflicts."
self resolveRegisterOrderConflictsBetweenCurrentSimStackAnd: mergeSimStack.
self assert: (self conflcitsResolvedBetweenSimStackAnd: mergeSimStack).
simStackPtr to: 0 by: -1 do:
[:i|
currentEntry := self simStack: simStack at: i.
targetEntry := self simStack: mergeSimStack at: i.
currentEntry reconcileForwardsWith: targetEntry.
"Note, we could update the simStack and spillBase here but that is done in restoreSimStackAtMergePoint:
spilled ifFalse:
[simSpillBase := i - 1].
simStack
at: i
put: (self
cCode: [mergeSimStack at: i]
+ inSmalltalk: [(mergeSimStack at: i) copy])"].
+
+ "a.k.a. fixup isReceiverResultRegSelf: (fixup isReceiverResultRegSelf and: [optStatus isReceiverResultRegLive])"
+ optStatus isReceiverResultRegLive ifFalse:
+ [fixup isReceiverResultRegSelf: false]!
- inSmalltalk: [(mergeSimStack at: i) copy])"]!
Item was changed:
----- Method: RegisterAllocatingCogit>>mergeWithFixupIfRequired: (in category 'simulation stack') -----
mergeWithFixupIfRequired: fixup
"If this bytecode has a fixup, some kind of merge needs to be done. There are 4 cases:
1) the bytecode has no fixup (fixup isNotAFixup)
do nothing
2) the bytecode has a non merge fixup
the fixup has needsNonMergeFixup.
The code generating non merge fixup (currently only special selector code) is responsible
for the merge so no need to do it.
We set deadCode to false as the instruction can be reached from jumps.
3) the bytecode has a merge fixup, but execution flow *cannot* fall through to the merge point.
the fixup has needsMergeFixup and deadCode = true.
ignores the current simStack as it does not mean anything
restores the simStack to the state the jumps to the merge point expects it to be.
4) the bytecode has a merge fixup and execution flow *can* fall through to the merge point.
the fixup has needsMergeFixup and deadCode = false.
flushes the stack to the stack pointer so the fall through execution path simStack is
in the state the merge point expects it to be.
restores the simStack to the state the jumps to the merge point expects it to be.
In addition, if this is a backjump merge point, we patch the fixup to hold the current simStackPtr
for later assertions."
<var: #fixup type: #'BytecodeFixup *'>
"case 1"
fixup notAFixup ifTrue: [^ 0].
"case 2"
fixup isNonMergeFixup ifTrue: [deadCode := false. ^ 0 ].
"cases 3 and 4"
self assert: fixup isMergeFixup.
self traceMerge: fixup.
deadCode
ifTrue: [simStackPtr := fixup simStackPtr] "case 3"
+ ifFalse: [self mergeCurrentSimStackWith: fixup]. "case 4"
- ifFalse: [self mergeCurrentSimStackWith: fixup mergeSimStack]. "case 4"
"cases 3 and 4"
deadCode := false.
fixup isBackwardBranchFixup ifTrue:
[self assert: fixup mergeSimStack isNil.
self setMergeSimStackOf: fixup].
fixup targetInstruction: self Label.
self assert: simStackPtr = fixup simStackPtr.
self cCode: '' inSmalltalk:
[self assert: fixup simStackPtr = (self debugStackPointerFor: bytecodePC)].
self restoreSimStackAtMergePoint: fixup.
^0!
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.2020.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.2020
Author: eem
Time: 2 December 2016, 8:59:35.572375 am
UUID: f53bedb4-3a2d-42ed-bf04-56ed996e7aa0
Ancestors: VMMaker.oscog-eem.2019
RegisterAllocatingCogit:
Give sensible names to the three reconciliation methods in CogRegisterAllocatingSimStackEntry. Be sure to use registerOrNone, not just liveRegister.
Refactor mergeCurrentSimStackWith: for clarity.
Have restoreSimStackAtMergePoint: maintain optStatus.
Execution gets as far as a stack imbalance in a send from Dictionary>noCheckNoGrowFillFrom: during EventSensor startUp:.
=============== Diff against VMMaker.oscog-eem.2019 ===============
Item was added:
+ ----- Method: CogRegisterAllocatingSimStackEntry>>reconcileBackwardsWith: (in category 'compile abstract instructions') -----
+ reconcileBackwardsWith: targetEntry
+ "Make the state of the receiver, a stack entry at a backward jump,
+ the same as the corresponding simStackEntry at the target of the jump"
+ <var: #targetEntry type: #'targetEntry *'>
+ | targetReg |
+ (targetReg := targetEntry registerOrNone) = NoReg ifTrue:
+ [^self].
+ targetEntry type = SSConstant ifTrue:
+ [self assert: (type = SSConstant and: [constant = targetEntry constant]).
+ ^self].
+ liveRegister ~= NoReg ifTrue:
+ [liveRegister ~= targetReg ifTrue:
+ [cogit MoveR: liveRegister R: targetReg].
+ ^self].
+ type caseOf: {
+ [SSBaseOffset] -> [cogit MoveMw: offset r: register R: targetReg].
+ [SSSpill] -> [cogit MoveMw: offset r: register R: targetReg].
+ [SSConstant] -> [cogit genMoveConstant: constant R: targetReg].
+ [SSRegister] -> [register ~= targetReg ifTrue:
+ [cogit MoveR: register R: targetReg]] }!
Item was added:
+ ----- Method: CogRegisterAllocatingSimStackEntry>>reconcileForwardsWith: (in category 'compile abstract instructions') -----
+ reconcileForwardsWith: targetEntry
+ "Make the state of the receiver, a stack entry at the end of a basic block,
+ the same as the corresponding simStackEntry at the target of a preceding
+ jump to the beginning of the next basic block."
+ <var: #targetEntry type: #'targetEntry *'>
+ | targetReg |
+ (targetReg := targetEntry registerOrNone) = NoReg ifTrue:
+ [self assert: (self isSameEntryAs: targetEntry).
+ ^self].
+ liveRegister ~= NoReg ifTrue:
+ [liveRegister ~= targetReg ifTrue:
+ [cogit MoveR: liveRegister R: targetReg].
+ (spilled and: [targetEntry spilled not]) ifTrue:
+ [cogit AddCq: objectRepresentation wordSize R: SPReg].
+ ^self].
+ spilled
+ ifTrue:
+ [targetEntry spilled ifTrue:
+ [cogit PopR: targetReg. "KISS; generate the least number of instructions..."
+ ^self]]
+ ifFalse:
+ [self deny: targetEntry spilled].
+ type caseOf: {
+ [SSBaseOffset] -> [cogit MoveMw: offset r: register R: targetReg].
+ [SSSpill] -> [cogit MoveMw: offset r: register R: targetReg].
+ [SSConstant] -> [cogit genMoveConstant: constant R: targetReg].
+ [SSRegister] -> [register ~= targetReg ifTrue:
+ [cogit MoveR: register R: targetReg]] }!
Item was changed:
----- Method: CogRegisterAllocatingSimStackEntry>>reconcilePoppingWith: (in category 'compile abstract instructions') -----
reconcilePoppingWith: targetEntry
"Make the state of a targetEntry, a stack entry following a non-inlined special selector
send, the same as the corresponding entry (the receiver) along the inlined path."
<var: #targetEntry type: #'targetEntry *'>
+ | targetReg |
targetEntry spilled ifTrue:
[self assert: (self isSameEntryAs: targetEntry).
+ (targetReg := targetEntry registerOrNone) = NoReg ifTrue:
- targetEntry liveRegister = NoReg ifTrue:
[^self].
type caseOf: {
+ [SSBaseOffset] -> [cogit MoveMw: offset r: register R: targetReg].
+ [SSSpill] -> [cogit MoveMw: offset r: register R: targetReg].
+ [SSConstant] -> [cogit genMoveConstant: constant R: targetReg].
+ [SSRegister] -> [targetReg ~= register ifTrue:
+ [cogit MoveR: register R: targetReg]] }.
- [SSBaseOffset] -> [cogit MoveMw: offset r: register R: targetEntry liveRegister].
- [SSSpill] -> [cogit MoveMw: offset r: register R: targetEntry liveRegister].
- [SSConstant] -> [cogit genMoveConstant: constant R: targetEntry liveRegister].
- [SSRegister] -> [targetEntry liveRegister ~= register ifTrue:
- [cogit MoveR: register R: targetEntry liveRegister]] }.
^self].
targetEntry type = SSConstant ifTrue:
+ [self assert: (targetEntry registerOrNone) = NoReg.
+ cogit AddCq: objectRepresentation wordSize R: SPReg.
- [cogit AddCw: BytesPerWord R: SPReg.
^self].
+ (targetReg := targetEntry registerOrNone) ~= NoReg ifTrue:
+ [cogit PopR: targetReg.
- targetEntry registerOrNone ~= NoReg ifTrue:
- [cogit PopR: targetEntry registerOrNone.
^self].
self halt!
Item was removed:
- ----- Method: CogRegisterAllocatingSimStackEntry>>reconcileWith: (in category 'compile abstract instructions') -----
- reconcileWith: targetEntry
- "Make the state of the receiver, a stack entry at a backward jump,
- the same as the corresponding simStackEntry at the target of the jump"
- <var: #targetEntry type: #'targetEntry *'>
- targetEntry liveRegister = NoReg ifTrue:
- [^self].
- targetEntry type = SSConstant ifTrue:
- [self assert: (type = SSConstant and: [constant = targetEntry constant]).
- ^self].
- liveRegister ~= NoReg ifTrue:
- [liveRegister ~= targetEntry liveRegister ifTrue:
- [cogit MoveR: liveRegister R: targetEntry liveRegister].
- ^self].
- type caseOf: {
- [SSBaseOffset] -> [cogit MoveMw: offset r: register R: targetEntry liveRegister].
- [SSSpill] -> [cogit MoveMw: offset r: register R: targetEntry liveRegister].
- [SSConstant] -> [cogit genMoveConstant: constant R: targetEntry liveRegister].
- [SSRegister] -> [register ~= targetEntry liveRegister ifTrue:
- [cogit MoveR: register R: targetEntry liveRegister]] }!
Item was changed:
----- Method: RegisterAllocatingCogit>>mergeCurrentSimStackWith: (in category 'bytecode generator support') -----
mergeCurrentSimStackWith: mergeSimStack
<var: #mergeSimStack type: #'SimStackEntry *'>
"At a merge point the cogit expects the stack to be in the same state as mergeSimStack.
mergeSimStack is the state as of some jump forward to this point. So make simStack agree
with mergeSimStack (it is, um, problematic to plant code at the jump).
Values may have to be assigned to registers. Registers may have to be swapped.
The state of optStatus must agree."
+ | currentEntry targetEntry |
- | currentRegsMask mergeRegsMask potentialConflictRegMask conflictingRegsMask
- currentRegMask mergeRegMask currentEntry targetEntry |
<var: #currentEntry type: #'SimStackEntry *'>
<var: #targetEntry type: #'SimStackEntry *'>
mergeSimStack ifNil: [^self].
+ "Assignments amongst the registers must be made in order to avoid overwriting.
+ If necessary exchange registers amongst simStack's entries to resolve any conflicts."
+ self resolveRegisterOrderConflictsBetweenCurrentSimStackAnd: mergeSimStack.
- currentRegsMask := mergeRegsMask := potentialConflictRegMask := 0.
- 0 to: simStackPtr do:
- [:i|
- currentRegMask := (currentEntry := self simStack: simStack at: i) registerMaskOrNone.
- mergeRegMask := (targetEntry := self simStack: mergeSimStack at: i) registerMaskOrNone.
- currentRegMask = mergeRegMask
- ifTrue:
- [self assert: (currentEntry isSameEntryAs: targetEntry)]
- ifFalse: "This checks for a potential conflict..."
- [(currentRegMask ~= 0 or: [mergeRegMask ~= 0]) ifTrue:
- [potentialConflictRegMask := potentialConflictRegMask bitOr: (currentRegMask bitOr: mergeRegMask)]].
- currentRegsMask := currentRegsMask bitOr: currentRegMask.
- mergeRegsMask := mergeRegsMask bitOr: mergeRegMask].
- conflictingRegsMask := potentialConflictRegMask bitAnd: (currentRegsMask bitAnd: mergeRegsMask).
- "One simple algorithm is to spill everything if there are any conflicts and then pop back.
- But this is terrible :-( Can we do better? Yes... Consider the following two simStacks
- target: 0: | rA | __ | rB | rC | rD | <- sp
- current: 0: | __ | __ | rD | rA | rC | <- sp
- If we were to assign in a naive order, 0 through sp rA would be overwritten before its value in current[3] is written to rC,
- and rC would be overwritten before its value in current[4] is written to rD. But if we swap the registers in current so that
- they respect the reverse ordering in target we can assign directly:
- swap current[3] & current[4]
- 0: | __ | __ | rD | rC | rA | <- sp
- now do the assignment in the order target[0] := current[0], target[1] := current[1], ... target[4] := current[4],
- i.e. rA := current[0]; rB := rD; (rC := rC); (rD := rD)."
- conflictingRegsMask ~= 0 ifTrue:
- [(self isAPowerOfTwo: conflictingRegsMask) "Multiple conflicts mean we have to sort"
- ifFalse: [self swapCurrentRegistersInMask: currentRegsMask accordingToRegisterOrderIn: mergeSimStack]
- ifTrue: [self assignToTempRegConflictingRegisterIn: conflictingRegsMask]].
self assert: (self conflcitsResolvedBetweenSimStackAnd: mergeSimStack).
simStackPtr to: 0 by: -1 do:
[:i|
+ currentEntry := self simStack: simStack at: i.
+ targetEntry := self simStack: mergeSimStack at: i.
+ currentEntry reconcileForwardsWith: targetEntry.
+ "Note, we could update the simStack and spillBase here but that is done in restoreSimStackAtMergePoint:
+ spilled ifFalse:
+ [simSpillBase := i - 1].
+ simStack
+ at: i
+ put: (self
+ cCode: [mergeSimStack at: i]
+ inSmalltalk: [(mergeSimStack at: i) copy])"]!
- currentRegMask := (currentEntry := self simStack: simStack at: i) registerMaskOrNone.
- mergeRegMask := (targetEntry := self simStack: mergeSimStack at: i) registerMaskOrNone.
- mergeRegMask ~= 0
- ifTrue:
- [currentRegMask ~= mergeRegMask
- ifTrue:
- [currentEntry reconcilePoppingWith: targetEntry.
- simStack
- at: i
- put: (self
- cCode: [mergeSimStack at: i]
- inSmalltalk: [(mergeSimStack at: i) copy])]
- ifFalse:
- [self assert: (currentEntry isSameEntryAs: targetEntry)]]
- ifFalse:
- [self assert: (currentRegMask = 0 or: [currentEntry spilled]).
- self assert: (currentEntry isSameEntryAs: targetEntry) "really? here to check"]]!
Item was changed:
----- Method: RegisterAllocatingCogit>>reconcileRegisterStateForBackwardJoin: (in category 'bytecode generator support') -----
reconcileRegisterStateForBackwardJoin: fixup
<var: #fixup type: #'SSBytecodeFixup *'>
| fixupSimStack |
<var: #fixupSimStack type: #'SimStackEntry *'>
self assert: (optStatus ssEntry isSameEntryAs: simSelf).
fixup isReceiverResultRegSelf ifTrue:
[optStatus isReceiverResultRegLive ifFalse:
[optStatus ssEntry storeToReg: ReceiverResultReg]].
fixupSimStack := fixup mergeSimStack.
simStackPtr to: 0 by: -1 do:
[:i|
self assert: (self simStackAt: i) spilled.
+ (self simStackAt: i) reconcileBackwardsWith: (fixupSimStack at: i)]!
- (self simStackAt: i) reconcileWith: (fixupSimStack at: i)]!
Item was added:
+ ----- Method: RegisterAllocatingCogit>>resolveRegisterOrderConflictsBetweenCurrentSimStackAnd: (in category 'bytecode generator support') -----
+ resolveRegisterOrderConflictsBetweenCurrentSimStackAnd: mergeSimStack
+ <var: #mergeSimStack type: #'SimStackEntry *'>
+ "One simple algorithm is to spill everything if there are any conflicts and then pop back.
+ But this is terrible :-( Can we do better? Yes... Consider the following two simStacks
+ target: 0: | rA | __ | rB | rC | rD | <- sp
+ current: 0: | __ | __ | rD | rA | rC | <- sp
+ If we were to assign in a naive order, 0 through sp rA would be overwritten before its value in current[3] is written to rC,
+ and rC would be overwritten before its value in current[4] is written to rD. But if we swap the registers in current so that
+ they respect the reverse ordering in target we can assign directly:
+ swap current[3] & current[4]
+ 0: | __ | __ | rD | rC | rA | <- sp
+ now do the assignment in the order target[0] := current[0], target[1] := current[1], ... target[4] := current[4],
+ i.e. rA := current[0]; rB := rD; (rC := rC); (rD := rD).
+
+ So find any conflicts, and if there are any, swap registers in the simStack to resolve them.
+ The trivial case of a single conflict is resolved by assigning that conflict to TempReg.
+ "
+ | currentRegsMask mergeRegsMask potentialConflictRegMask conflictingRegsMask
+ currentRegMask mergeRegMask currentEntry targetEntry |
+ <var: #currentEntry type: #'SimStackEntry *'>
+ <var: #targetEntry type: #'SimStackEntry *'>
+ currentRegsMask := mergeRegsMask := potentialConflictRegMask := 0.
+ 0 to: simStackPtr do:
+ [:i|
+ currentRegMask := (currentEntry := self simStack: simStack at: i) registerMaskOrNone.
+ mergeRegMask := (targetEntry := self simStack: mergeSimStack at: i) registerMaskOrNone.
+ (currentRegMask ~= mergeRegMask
+ and: [currentRegMask ~= 0 or: [mergeRegMask ~= 0]]) ifTrue:
+ [potentialConflictRegMask := potentialConflictRegMask bitOr: (currentRegMask bitOr: mergeRegMask)].
+ currentRegsMask := currentRegsMask bitOr: currentRegMask.
+ mergeRegsMask := mergeRegsMask bitOr: mergeRegMask].
+ conflictingRegsMask := potentialConflictRegMask bitAnd: (currentRegsMask bitAnd: mergeRegsMask).
+ conflictingRegsMask ~= 0 ifTrue:
+ [(self isAPowerOfTwo: conflictingRegsMask) "Multiple conflicts mean we have to sort"
+ ifFalse: [self swapCurrentRegistersInMask: currentRegsMask accordingToRegisterOrderIn: mergeSimStack]
+ ifTrue: [self assignToTempRegConflictingRegisterIn: conflictingRegsMask]].!
Item was changed:
----- Method: RegisterAllocatingCogit>>restoreSimStackAtMergePoint: (in category 'simulation stack') -----
restoreSimStackAtMergePoint: fixup
<inline: true>
"All the execution paths reaching a merge point expect everything to be spilled
on stack and the optStatus is unknown. If the merge point follows a return, it
+ isn't a merge, but a skip past a return. If it is a real merge point then throw
- isn't a merge, but a sdkip past a return. If it is a real merge point then throw
away all simStack and optStatus optimization state."
+ optStatus isReceiverResultRegLive: fixup isReceiverResultRegSelf.
fixup mergeSimStack ifNotNil:
[simSpillBase := methodOrBlockNumTemps.
- self flag: 'try and maintain this through the merge'.
- optStatus isReceiverResultRegLive: false.
0 to: simStackPtr do:
[:i|
self cCode: [simStack at: i put: (fixup mergeSimStack at: i)]
inSmalltalk: [(simStack at: i) copyFrom: (fixup mergeSimStack at: i)]]].
^0!
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.2021.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.2021
Author: eem
Time: 2 December 2016, 12:52:57.838901 pm
UUID: 79a1fe01-f27f-4839-b390-3b952042f672
Ancestors: VMMaker.oscog-eem.2020
RegisterAllocatingCogit:
Rename moveSimStackConstantsToRegisters to moveVolatileSimStackEntriesToRegisters sicne we also have to capture inst var refs (anything not relative to FPReg).
Fix slip in reconcileForwardsWith:.
ensureNonMergeFixupAt: shoudl only set the mergeSimStack of the fixup if it doesn't yet have one.
When merging we have tio make sure that simSelf's liveRegister is up-to-date, not just optStatus. Oh how nice it would be to only have simSelf and be able to nuke optStatus. [the issue is having two defs for a simStackEntry in StackToRegisterMappingCogit, one with liveRegister, needed for simSelf, one not, for normal entries].
Get fixups to remember the bytecode pc that set them, and print the bcpc(s). Add printing simSelf to traceSimStack.
Execution now gets to Process>>popTo: during SmalltalkImage>installLowSpaceWatcher, 187 methods compiled and executed.
=============== Diff against VMMaker.oscog-eem.2020 ===============
Item was changed:
VMStructType subclass: #CogBytecodeFixup
+ instanceVariableNames: 'targetInstruction instructionIndex bcpc'
- instanceVariableNames: 'targetInstruction instructionIndex'
classVariableNames: 'NeedsFixupFlag'
poolDictionaries: ''
category: 'VMMaker-JIT'!
!CogBytecodeFixup commentStamp: 'eem 1/20/2011 12:41' prior: 0!
I am a fixup for a bytecode in the Cogit. Currently fixups are for labels only. To fixup jumps the cogit places fixups in the fixups array at indices that correspond to bytecodes that are the targets of jumps. When the cogit encounters a bytecode with a fixup it assigns the fixup's target field to the first generated instruction for the bytecode. Later when AbstractInstruction Jump* instructions attempt to compute their target they follow the indirection through the fixup to the actual target.!
Item was changed:
----- Method: CogBytecodeFixup class>>instVarNamesAndTypesForTranslationDo: (in category 'translation') -----
instVarNamesAndTypesForTranslationDo: aBinaryBlock
"enumerate aBinaryBlock with the names and C type strings for the inst vars to include in a BytecodeFixup struct."
self allInstVarNames do:
[:ivn|
+ ivn ~= 'bcpc' ifTrue:
+ [aBinaryBlock
+ value: ivn
+ value: (ivn = 'targetInstruction'
+ ifTrue: [#'AbstractInstruction *']
+ ifFalse:
+ [#sqInt])]]!
- aBinaryBlock
- value: ivn
- value: (ivn = 'targetInstruction'
- ifTrue: [#'AbstractInstruction *']
- ifFalse:
- [#sqInt])]!
Item was added:
+ ----- Method: CogBytecodeFixup>>bcpc (in category 'accessing') -----
+ bcpc
+
+ ^ bcpc!
Item was added:
+ ----- Method: CogBytecodeFixup>>bcpc: (in category 'accessing') -----
+ bcpc: anObject
+
+ ^bcpc := anObject!
Item was changed:
----- Method: CogBytecodeFixup>>printStateOn: (in category 'debug printing') -----
printStateOn: aStream
<doNotGenerate>
targetInstruction ifNotNil:
+ [aStream space; nextPut: $(; print: targetInstruction; nextPutAll: ' bc '; print: bcpc; nextPut: $)]!
- [aStream space; nextPut: $(; print: targetInstruction; nextPut: $)]!
Item was added:
+ ----- Method: CogBytecodeFixup>>recordBcpc: (in category 'simulation') -----
+ recordBcpc: bytecodePC
+ <inline: true>
+ self cCode: '' inSmalltalk:
+ [bcpc
+ ifNil: [bcpc := bytecodePC]
+ ifNotNil:
+ [bcpc := bcpc isInteger
+ ifTrue: [{bcpc. bytecodePC}]
+ ifFalse: [bcpc, {bytecodePC}]]]!
Item was added:
+ ----- Method: CogObjectRepresentation>>wordSize (in category 'accessing') -----
+ wordSize
+ <doNotGenerate>
+ ^objectMemory wordSize!
Item was changed:
----- Method: CogRegisterAllocatingSimStackEntry>>reconcileForwardsWith: (in category 'compile abstract instructions') -----
reconcileForwardsWith: targetEntry
"Make the state of the receiver, a stack entry at the end of a basic block,
the same as the corresponding simStackEntry at the target of a preceding
jump to the beginning of the next basic block."
<var: #targetEntry type: #'targetEntry *'>
| targetReg |
(targetReg := targetEntry registerOrNone) = NoReg ifTrue:
[self assert: (self isSameEntryAs: targetEntry).
^self].
liveRegister ~= NoReg ifTrue:
[liveRegister ~= targetReg ifTrue:
[cogit MoveR: liveRegister R: targetReg].
(spilled and: [targetEntry spilled not]) ifTrue:
[cogit AddCq: objectRepresentation wordSize R: SPReg].
^self].
spilled
ifTrue:
+ [targetEntry spilled ifFalse:
- [targetEntry spilled ifTrue:
[cogit PopR: targetReg. "KISS; generate the least number of instructions..."
^self]]
ifFalse:
[self deny: targetEntry spilled].
type caseOf: {
[SSBaseOffset] -> [cogit MoveMw: offset r: register R: targetReg].
[SSSpill] -> [cogit MoveMw: offset r: register R: targetReg].
[SSConstant] -> [cogit genMoveConstant: constant R: targetReg].
[SSRegister] -> [register ~= targetReg ifTrue:
[cogit MoveR: register R: targetReg]] }!
Item was changed:
----- Method: CogSSBytecodeFixup>>printStateOn: (in category 'debug printing') -----
printStateOn: aStream
<doNotGenerate>
(targetInstruction isNil and: [simStackPtr isNil]) ifTrue:
[^self].
aStream space; nextPut: $(.
targetInstruction ifNotNil:
[aStream space; print: targetInstruction].
simStackPtr ifNotNil:
+ [aStream nextPutAll: ' sp '; print: simStackPtr].
+ bcpc ifNotNil:
+ [aStream nextPutAll: ' bc '; print: bcpc].
- [aStream space; print: simStackPtr].
aStream nextPut: $)!
Item was changed:
----- Method: Cogit>>ensureFixupAt: (in category 'compile abstract instructions') -----
ensureFixupAt: targetIndex
"Make sure there's a flagged fixup at the targetIndex (pc relative to first pc) in fixups.
Initially a fixup's target is just a flag. Later on it is replaced with a proper instruction."
<returnTypeC: #'BytecodeFixup *'>
| fixup |
<var: #fixup type: #'BytecodeFixup *'>
fixup := self fixupAt: targetIndex.
fixup notAFixup ifTrue:
[fixup becomeFixup].
+ fixup recordBcpc: bytecodePC.
^fixup!
Item was changed:
----- Method: RegisterAllocatingCogit>>ensureFixupAt: (in category 'bytecode generator support') -----
ensureFixupAt: targetIndex
| fixup |
<var: #fixup type: #'BytecodeFixup *'>
fixup := self fixupAt: targetIndex.
fixup needsFixup
ifTrue:
[fixup mergeSimStack
ifNil: [self setMergeSimStackOf: fixup]
ifNotNil: [self mergeCurrentSimStackWith: fixup mergeSimStack]]
ifFalse:
[self assert: fixup mergeSimStack isNil.
+ self moveVolatileSimStackEntriesToRegisters.
+ self setMergeSimStackOf: fixup].
+ ^super ensureFixupAt: targetIndex!
- self moveSimStackConstantsToRegisters.
- self setMergeSimStackOf: fixup ].
- ^super ensureFixupAt: targetIndex.
- !
Item was changed:
----- Method: RegisterAllocatingCogit>>ensureNonMergeFixupAt: (in category 'compile abstract instructions') -----
ensureNonMergeFixupAt: targetIndex
"Make sure there's a flagged fixup at the targetIndex (pc relative to first pc) in fixups.
Initially a fixup's target is just a flag. Later on it is replaced with a proper instruction."
| fixup |
fixup := super ensureNonMergeFixupAt: targetIndex.
+ fixup mergeSimStack ifNil: [self setMergeSimStackOf: fixup].
- self setMergeSimStackOf: fixup.
^fixup!
Item was removed:
- ----- Method: RegisterAllocatingCogit>>moveSimStackConstantsToRegisters (in category 'bytecode generator support') -----
- moveSimStackConstantsToRegisters
- <inline: true>
- <var: #desc type: #'SimStackEntry *'>
- self flag: 'I think this should be done at the merge point if required. e.g. self at: 1 put: (expr ifTrue: [a] ifFalse: [b]) does not need to assign'.
- (simSpillBase max: 0) to: simStackPtr do:
- [:i| | desc reg |
- desc := self simStackAt: i.
- (desc type = SSConstant and: [desc liveRegister = NoReg]) ifTrue:
- [reg := self allocateRegNotConflictingWith: 0.
- reg ~= NoReg ifTrue:
- [desc storeToReg: reg]]]!
Item was added:
+ ----- Method: RegisterAllocatingCogit>>moveVolatileSimStackEntriesToRegisters (in category 'bytecode generator support') -----
+ moveVolatileSimStackEntriesToRegisters
+ "When jumping forward to a merge point the stack mst be reconcilable with the state that falls through to the merge point.
+ We cannot easily arrange that later we add code to the branch, e.g. to spill values. Instead, any volatile contents must be
+ moved to registers. [In fact, that's not exactly true, consider these two code sequences:
+ self at: (expr ifTrue: [1] ifFalse: [2]) put: a
+ self at: 1 put: (expr ifTrue: [a] ifFalse: [b])
+ The first one needs 1 saving to a register to reconcile with 2.
+ The second one has 1 on both paths, but we're not clever enough to spot this case yet.]
+ Volatile contents are constants and base-offset references other than temporaries and spills (regsier ~= FPReg)"
+ <inline: true>
+ <var: #desc type: #'SimStackEntry *'>
+ (simSpillBase max: 0) to: simStackPtr do:
+ [:i| | desc reg |
+ desc := self simStackAt: i.
+ ((desc type = SSConstant or: [desc type = SSBaseOffset and: [desc register ~= FPReg]])
+ and: [desc liveRegister = NoReg]) ifTrue:
+ [reg := self allocateRegNotConflictingWith: 0.
+ reg = NoReg
+ ifTrue: [self halt] "have to spill"
+ ifFalse: [desc storeToReg: reg]]]!
Item was changed:
----- Method: RegisterAllocatingCogit>>reconcileRegisterStateForJoinAfterSpecialSelectorSend (in category 'bytecode generator support') -----
reconcileRegisterStateForJoinAfterSpecialSelectorSend
"When the control flow from the inlined special selector code (e.g. add or comparison)
joins the control flow from the send, taken when the inlined code fails, we should decide
whether to reload any registers known to contain useful values or mark them as dead."
"If ReceiverResultReg is live along the inlined path, and is used before the next full send,
reload it on the uncommon path."
scratchOptStatus isReceiverResultRegLive ifTrue:
[(self existsInstVarRefBeforeSendOrReturn
or: [self receiverRefOnScratchSimStack])
ifTrue:
[optStatus isReceiverResultRegLive: true.
optStatus ssEntry storeToReg: ReceiverResultReg]
+ ifFalse:
+ [optStatus isReceiverResultRegLive: false.
+ optStatus ssEntry liveRegister: NoReg]].
- ifFalse: [optStatus isReceiverResultRegLive: false]].
"Restore the simStack to that in scratchSimStack,
popping any spilled state back into allocated registers."
simSpillBase := scratchSpillBase.
simStackPtr to: 0 by: -1 do:
[:i|
self assert: (i = simStackPtr
ifTrue: [(self simStackAt: i) type = SSRegister]
ifFalse: [(self simStackAt: i) spilled]).
(self simStackAt: i) reconcilePoppingWith: (self simStack: scratchSimStack at: i).
simStack
at: i
put: (self
cCode: [scratchSimStack at: i]
inSmalltalk: [(scratchSimStack at: i) copy])]!
Item was changed:
----- Method: RegisterAllocatingCogit>>restoreSimStackAtMergePoint: (in category 'simulation stack') -----
restoreSimStackAtMergePoint: fixup
<inline: true>
"All the execution paths reaching a merge point expect everything to be spilled
on stack and the optStatus is unknown. If the merge point follows a return, it
isn't a merge, but a skip past a return. If it is a real merge point then throw
away all simStack and optStatus optimization state."
+ (optStatus isReceiverResultRegLive: fixup isReceiverResultRegSelf) ifFalse:
+ [simSelf liveRegister: NoReg].
- optStatus isReceiverResultRegLive: fixup isReceiverResultRegSelf.
fixup mergeSimStack ifNotNil:
[simSpillBase := methodOrBlockNumTemps.
0 to: simStackPtr do:
[:i|
self cCode: [simStack at: i put: (fixup mergeSimStack at: i)]
inSmalltalk: [(simStack at: i) copyFrom: (fixup mergeSimStack at: i)]]].
^0!
Item was changed:
----- Method: RegisterAllocatingCogit>>setMergeSimStackOf: (in category 'bytecode generator support') -----
setMergeSimStackOf: fixup
<var: #fixup type: #'BytecodeFixup *'>
+ self moveVolatileSimStackEntriesToRegisters.
- self moveSimStackConstantsToRegisters.
fixup mergeSimStack
ifNil:
[self assert: nextFixup <= numFixups.
self cCode: [fixup mergeSimStack: mergeSimStacksBase + (nextFixup * self simStackSlots * (self sizeof: CogSimStackEntry))].
nextFixup := nextFixup + 1]
ifNotNil:
[self assert: fixup simStackPtr = simStackPtr.
0 to: simStackPtr do:
[:i|
self assert: ((self simStackAt: i) isSameEntryAs: (fixup mergeSimStack at: i)).
(self simStackAt: i) liveRegister ~= (fixup mergeSimStack at: i) liveRegister ifTrue:
[(self simStackAt: i) liveRegister: NoReg]]].
fixup
simStackPtr: simStackPtr;
isReceiverResultRegSelf: optStatus isReceiverResultRegLive.
self cCode: [self mem: fixup mergeSimStack cp: simStack y: self simStackSlots * (self sizeof: CogSimStackEntry)]
inSmalltalk: [fixup mergeSimStack: self copySimStack]!
Item was changed:
----- Method: StackToRegisterMappingCogit>>ensureFixupAt: (in category 'compile abstract instructions') -----
ensureFixupAt: targetIndex
"Make sure there's a flagged fixup at the targetIndex (pc relative to first pc) in fixups.
Initially a fixup's target is just a flag. Later on it is replaced with a proper instruction."
<returnTypeC: #'BytecodeFixup *'>
| fixup |
<var: #fixup type: #'BytecodeFixup *'>
fixup := self fixupAt: targetIndex.
self traceFixup: fixup.
self cCode: '' inSmalltalk:
[self assert: simStackPtr = (self debugStackPointerFor: targetIndex + initialPC).
(fixup isMergeFixupOrIsFixedUp
and: [fixup isBackwardBranchFixup not]) ifTrue: "ignore backward branch targets"
[self assert: fixup simStackPtr = simStackPtr]].
fixup isNonMergeFixupOrNotAFixup
ifTrue: "convert a non-merge into a merge"
[fixup becomeMergeFixup.
fixup simStackPtr: simStackPtr.
LowcodeVM ifTrue: [
fixup simNativeStackPtr: simNativeStackPtr.
+ fixup simNativeStackSize: simNativeStackSize]]
- fixup simNativeStackSize: simNativeStackSize.]]
ifFalse:
[fixup isBackwardBranchFixup
+ ifTrue: "this is the target of a backward branch and
- ifTrue: ["this is the target of a backward branch and
so doesn't have a simStackPtr assigned yet."
+ [fixup simStackPtr: simStackPtr.
+ LowcodeVM ifTrue:
+ [fixup simNativeStackPtr: simNativeStackPtr.
+ fixup simNativeStackSize: simNativeStackSize]]
+ ifFalse:
+ [self assert: fixup simStackPtr = simStackPtr.
+ LowcodeVM ifTrue:
+ [self assert: fixup simNativeStackPtr = simNativeStackPtr.
+ self assert: fixup simNativeStackSize = simNativeStackSize]]].
+ fixup recordBcpc: bytecodePC.
- fixup simStackPtr: simStackPtr.
- LowcodeVM ifTrue: [
- fixup simNativeStackPtr: simNativeStackPtr.
- fixup simNativeStackSize: simNativeStackSize.]]
- ifFalse: [
- self assert: fixup simStackPtr = simStackPtr.
- LowcodeVM ifTrue: [
- self assert: fixup simNativeStackPtr = simNativeStackPtr.
- self assert: fixup simNativeStackSize = simNativeStackSize.]]].
^fixup!
Item was changed:
----- Method: StackToRegisterMappingCogit>>genPushReceiverBytecode (in category 'bytecode generators') -----
genPushReceiverBytecode
optStatus isReceiverResultRegLive ifTrue:
[^self ssPushRegister: ReceiverResultReg].
+ self assert: simSelf liveRegister = NoReg.
^self ssPushDesc: simSelf!
Item was added:
+ ----- Method: StackToRegisterMappingCogit>>printSimSelf (in category 'simulation only') -----
+ printSimSelf
+ <doNotGenerate>
+ coInterpreter transcript ensureCr; nextPutAll: 'self: '.
+ simSelf printStateOn: coInterpreter transcript.
+ coInterpreter transcript cr; flush!
Item was changed:
----- Method: StackToRegisterMappingCogit>>traceSimStack (in category 'simulation only') -----
traceSimStack
<cmacro: '() 0'>
(compilationTrace anyMask: 4) ifTrue:
+ [self printOptStatus; printSimSelf; printSimStack]!
- [self printOptStatus; printSimStack]!
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.2019.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.2019
Author: eem
Time: 1 December 2016, 12:20:35.048344 pm
UUID: 364ea882-9181-4ed0-8495-3044fac4318b
Ancestors: VMMaker.oscog-eem.2018
Improve debugging of stack imbalances on primitive invocation by restoring the stack pointer after calling failUnbalancedPrimitive.
=============== Diff against VMMaker.oscog-eem.2018 ===============
Item was changed:
----- Method: StackInterpreter>>slowPrimitiveResponse (in category 'primitive support') -----
slowPrimitiveResponse
"Invoke a normal (non-quick) primitive.
Called under the assumption that primFunctionPointer has been preloaded."
| nArgs savedFramePointer savedStackPointer |
<inline: true>
<var: #savedFramePointer type: #'char *'>
<var: #savedStackPointer type: #'char *'>
self assert: (objectMemory isOopForwarded: (self stackValue: argumentCount)) not.
self assert: objectMemory remapBufferCount = 0.
FailImbalancedPrimitives ifTrue:
[nArgs := argumentCount.
savedStackPointer := stackPointer.
savedFramePointer := framePointer].
self initPrimCall.
self dispatchFunctionPointer: primitiveFunctionPointer.
self assert: (self maybeLeakCheckExternalPrimCall: newMethod).
self maybeRetryPrimitiveOnFailure.
self maybeFailForLastObjectOverwrite.
(FailImbalancedPrimitives
and: [self successful
and: [framePointer = savedFramePointer
and: [(self isMachineCodeFrame: framePointer) not]]]) ifTrue:"Don't fail if primitive has done something radical, e.g. perform:"
[stackPointer ~= (savedStackPointer + (nArgs * objectMemory wordSize)) ifTrue:
[self flag: 'Would be nice to make this a message send of e.g. unbalancedPrimitive to the current process or context'.
"This is necessary but insufficient; the result may still have been written to the stack.
At least we'll know something is wrong."
+ self failUnbalancedPrimitive.
+ stackPointer := savedStackPointer]].
- stackPointer := savedStackPointer.
- self failUnbalancedPrimitive]].
"If we are profiling, take accurate primitive measures"
nextProfileTick > 0 ifTrue:
[self checkProfileTick: newMethod].
^self successful!
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.2018.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.2018
Author: eem
Time: 1 December 2016, 10:47:33.781504 am
UUID: 2ca1b9f3-8f95-4dc0-9111-acfd6f7fe95f
Ancestors: VMMaker.oscog-EstebanLorenzano.2017
RegisterAllocatingCogit:
Reimplement mergeCurrentSimStackWith: to use a better algorithm. Implement all but the register sort/swap code.
Esteban, amen!
=============== Diff against VMMaker.oscog-EstebanLorenzano.2017 ===============
Item was added:
+ ----- Method: RegisterAllocatingCogit>>assignToTempRegConflictingRegisterIn: (in category 'bytecode generator support') -----
+ assignToTempRegConflictingRegisterIn: conflictingRegisterMask
+ "Find the stackEntry in simStack whose liveRegister matches conflictingRegisterMask
+ and assign it to TempReg."
+ self assert: (self isAPowerOfTwo: conflictingRegisterMask).
+ 0 to: simStackPtr do:
+ [:i|
+ (self simStackAt: i) registerMaskOrNone = conflictingRegisterMask ifTrue:
+ [(self simStackAt: i)
+ storeToReg: TempReg;
+ liveRegister: TempReg.
+ ^self]].
+ self error: 'conflict entry not found'!
Item was added:
+ ----- Method: RegisterAllocatingCogit>>conflcitsResolvedBetweenSimStackAnd: (in category 'bytecode generator support') -----
+ conflcitsResolvedBetweenSimStackAnd: mergeSimStack
+ "There are no register conflicts between simStack and mergeSimStack if
+ traversing both stacks from hot end (simStackPtr) to cold end (0) no register
+ exists in simStack that has previously existed in mergeSimStack. This is because
+ the resolution assigns values from simStack to registers in mergeSimStack and so
+ must not assign to a register yet to be read."
+ | regsWrittenToMask |
+ regsWrittenToMask := 0.
+ simStackPtr to: 0 by: -1 do:
+ [:i| | mergeMask currentMask |
+ mergeMask := (self simStack: mergeSimStack at: i) registerMaskOrNone.
+ currentMask := (self simStack: simStack at: i) registerMaskOrNone.
+ mergeMask ~= currentMask ifTrue:
+ [(currentMask anyMask: regsWrittenToMask) ifTrue:
+ [^false]].
+ regsWrittenToMask := regsWrittenToMask bitOr: mergeMask].
+ ^true!
Item was added:
+ ----- Method: RegisterAllocatingCogit>>isAPowerOfTwo: (in category 'bytecode generator support') -----
+ isAPowerOfTwo: anInteger
+ <inline: true>
+ ^(anInteger bitAnd: anInteger - 1) = 0!
Item was removed:
- ----- Method: RegisterAllocatingCogit>>liveRegistersExceptingTopNItems: (in category 'simulation stack') -----
- liveRegistersExceptingTopNItems: n
- | regsSet |
- regsSet := 0.
- 0 to: simStackPtr - n do:
- [:i|
- regsSet := regsSet bitOr: (self simStackAt: i) registerMask].
- LowcodeVM ifTrue:
- [(simNativeSpillBase max: 0) to: simNativeStackPtr - n do:
- [:i|
- regsSet := regsSet bitOr: (self simNativeStackAt: i) nativeRegisterMask]].
- ^regsSet!
Item was added:
+ ----- Method: RegisterAllocatingCogit>>liveRegistersExceptingTopNItems:in: (in category 'simulation stack') -----
+ liveRegistersExceptingTopNItems: n in: aSimStack
+ <var: 'aSimStack' type: #'SimStackEntry *'>
+ | regsSet |
+ regsSet := 0.
+ 0 to: simStackPtr - n do:
+ [:i|
+ regsSet := regsSet bitOr: (self simStack: aSimStack at: i) registerMask].
+ LowcodeVM ifTrue:
+ [self shouldBeImplemented.
+ (simNativeSpillBase max: 0) to: simNativeStackPtr - n do:
+ [:i|
+ regsSet := regsSet bitOr: (self simNativeStackAt: i) nativeRegisterMask]].
+ ^regsSet!
Item was changed:
----- Method: RegisterAllocatingCogit>>mergeCurrentSimStackWith: (in category 'bytecode generator support') -----
mergeCurrentSimStackWith: mergeSimStack
<var: #mergeSimStack type: #'SimStackEntry *'>
+ "At a merge point the cogit expects the stack to be in the same state as mergeSimStack.
+ mergeSimStack is the state as of some jump forward to this point. So make simStack agree
+ with mergeSimStack (it is, um, problematic to plant code at the jump).
+ Values may have to be assigned to registers. Registers may have to be swapped.
+ The state of optStatus must agree."
+ | currentRegsMask mergeRegsMask potentialConflictRegMask conflictingRegsMask
+ currentRegMask mergeRegMask currentEntry targetEntry |
+ <var: #currentEntry type: #'SimStackEntry *'>
+ <var: #targetEntry type: #'SimStackEntry *'>
+ mergeSimStack ifNil: [^self].
+ currentRegsMask := mergeRegsMask := potentialConflictRegMask := 0.
+ 0 to: simStackPtr do:
- <var: #currentSSEntry type: #'SimStackEntry *'>
- <var: #expectedSSEntry type: #'SimStackEntry *'>
- "At merge point the cogit expects the stack to be in the same state as mergeSimStack.
- The logic is very naive, we align the existing state from the current stack to the merge stack
- from simStackPtr to methodOrBlockNumTemps, and if a conflict happen, we flush what remains
- to be merged."
- self flag: #TODO. "we could have a better algorithm with the current set of live registers to avoid flushing"
- simStackPtr to: methodOrBlockNumTemps by: -1 do:
[:i|
+ currentRegMask := (currentEntry := self simStack: simStack at: i) registerMaskOrNone.
+ mergeRegMask := (targetEntry := self simStack: mergeSimStack at: i) registerMaskOrNone.
+ currentRegMask = mergeRegMask
+ ifTrue:
+ [self assert: (currentEntry isSameEntryAs: targetEntry)]
+ ifFalse: "This checks for a potential conflict..."
+ [(currentRegMask ~= 0 or: [mergeRegMask ~= 0]) ifTrue:
+ [potentialConflictRegMask := potentialConflictRegMask bitOr: (currentRegMask bitOr: mergeRegMask)]].
+ currentRegsMask := currentRegsMask bitOr: currentRegMask.
+ mergeRegsMask := mergeRegsMask bitOr: mergeRegMask].
+ conflictingRegsMask := potentialConflictRegMask bitAnd: (currentRegsMask bitAnd: mergeRegsMask).
+ "One simple algorithm is to spill everything if there are any conflicts and then pop back.
+ But this is terrible :-( Can we do better? Yes... Consider the following two simStacks
+ target: 0: | rA | __ | rB | rC | rD | <- sp
+ current: 0: | __ | __ | rD | rA | rC | <- sp
+ If we were to assign in a naive order, 0 through sp rA would be overwritten before its value in current[3] is written to rC,
+ and rC would be overwritten before its value in current[4] is written to rD. But if we swap the registers in current so that
+ they respect the reverse ordering in target we can assign directly:
+ swap current[3] & current[4]
+ 0: | __ | __ | rD | rC | rA | <- sp
+ now do the assignment in the order target[0] := current[0], target[1] := current[1], ... target[4] := current[4],
+ i.e. rA := current[0]; rB := rD; (rC := rC); (rD := rD)."
+ conflictingRegsMask ~= 0 ifTrue:
+ [(self isAPowerOfTwo: conflictingRegsMask) "Multiple conflicts mean we have to sort"
+ ifFalse: [self swapCurrentRegistersInMask: currentRegsMask accordingToRegisterOrderIn: mergeSimStack]
+ ifTrue: [self assignToTempRegConflictingRegisterIn: conflictingRegsMask]].
+ self assert: (self conflcitsResolvedBetweenSimStackAnd: mergeSimStack).
+ simStackPtr to: 0 by: -1 do:
+ [:i|
+ currentRegMask := (currentEntry := self simStack: simStack at: i) registerMaskOrNone.
+ mergeRegMask := (targetEntry := self simStack: mergeSimStack at: i) registerMaskOrNone.
+ mergeRegMask ~= 0
+ ifTrue:
+ [currentRegMask ~= mergeRegMask
+ ifTrue:
+ [currentEntry reconcilePoppingWith: targetEntry.
+ simStack
+ at: i
+ put: (self
+ cCode: [mergeSimStack at: i]
+ inSmalltalk: [(mergeSimStack at: i) copy])]
+ ifFalse:
+ [self assert: (currentEntry isSameEntryAs: targetEntry)]]
+ ifFalse:
+ [self assert: (currentRegMask = 0 or: [currentEntry spilled]).
+ self assert: (currentEntry isSameEntryAs: targetEntry) "really? here to check"]]!
- | currentSSEntry expectedSSEntry |
- currentSSEntry := self simStackAt: i.
- expectedSSEntry := self simStack: mergeSimStack at: i.
- expectedSSEntry type
- caseOf: {
- [SSBaseOffset] -> [ self assert: (expectedSSEntry register = ReceiverResultReg or: [ expectedSSEntry register = FPReg ]).
- (expectedSSEntry register = ReceiverResultReg and: [needsFrame]) ifTrue:
- [optStatus isReceiverResultRegLive ifFalse:
- [self ssFlushFrom: i - 1 upThroughRegister: ReceiverResultReg.
- self putSelfInReceiverResultReg ].
- optStatus isReceiverResultRegLive: true]. ].
- [SSSpill] -> [currentSSEntry ensureSpilledAt: (self frameOffsetOfTemporary: i) from: FPReg].
- [SSConstant] -> [self assert: expectedSSEntry liveRegister notNil.
- currentSSEntry storeToReg: expectedSSEntry liveRegister ].
- [SSRegister] -> [(currentSSEntry type = SSRegister and: [currentSSEntry register = expectedSSEntry register])
- ifFalse:
- [ self ssFlushFrom: i - 1 upThroughRegister: expectedSSEntry register.
- currentSSEntry storeToReg: expectedSSEntry register ] ]}.
- ]!
Item was changed:
----- Method: RegisterAllocatingCogit>>ssStorePop:toPreferredReg: (in category 'simulation stack') -----
ssStorePop: popBoolean toPreferredReg: preferredReg
"Store or pop the top simulated stack entry to a register.
Use preferredReg if the entry is not itself a register.
Answer the actual register the result ends up in."
| actualReg liveRegisters |
actualReg := preferredReg.
self ssTop type = SSRegister ifTrue:
[self assert: (self ssTop liveRegister = NoReg
or: [self ssTop liveRegister = self ssTop register]).
self assert: self ssTop spilled not.
actualReg := self ssTop register].
self ssTop liveRegister ~= NoReg ifTrue:
[actualReg := self ssTop liveRegister].
+ liveRegisters := self liveRegistersExceptingTopNItems: 1 in: simStack.
- liveRegisters := self liveRegistersExceptingTopNItems: 1.
(self register: actualReg isInMask: liveRegisters) ifTrue:
[actualReg := self allocateRegNotConflictingWith: (self registerMaskFor: preferredReg).
actualReg = NoReg ifTrue:
[actualReg := preferredReg]].
self deny: (self register: actualReg isInMask: liveRegisters).
self ssStorePop: popBoolean toReg: actualReg. "generates nothing if ssTop is already in actualReg"
^actualReg!
Item was added:
+ ----- Method: StackToRegisterMappingCogit>>printSimStack: (in category 'simulation only') -----
+ printSimStack: aSimStack
+ <doNotGenerate>
+ self printSimStack: aSimStack toDepth: simStackPtr spillBase: simSpillBase on: coInterpreter transcript!
ClementBera uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-cb.2016.mcz
==================== Summary ====================
Name: VMMaker.oscog-cb.2016
Author: cb
Time: 1 December 2016, 10:33:24.307114 am
UUID: 00547c08-bf88-4160-9dd8-baf1f367a96e
Ancestors: VMMaker.oscog-EstebanLorenzano.2015
Added a setting so that subclasses of cogit can choose to refuse early openPIC promotion.
This leads to much, much, *much* better type information on PICs in the Sista VM. No changes in the normal VM.
=============== Diff against VMMaker.oscog-EstebanLorenzano.2015 ===============
Item was added:
+ ----- Method: Cogit>>allowEarlyOpenPICPromotion (in category 'in-line cacheing') -----
+ allowEarlyOpenPICPromotion
+ <inline: true>
+ ^ true!
Item was changed:
----- Method: Cogit>>ceSICMiss: (in category 'in-line cacheing') -----
ceSICMiss: receiver
"An in-line cache check in a method has failed. The failing entry check has jumped
to the ceMethodAbort abort call at the start of the method which has called this routine.
If possible allocate a closed PIC for the current and existing classes.
The stack looks like:
receiver
args
sender return address
sp=> ceMethodAbort call return address
So we can find the method that did the failing entry check at
ceMethodAbort call return address - missOffset
and we can find the send site from the outer return address."
<api>
| pic innerReturn outerReturn entryPoint targetMethod newTargetMethodOrNil errorSelectorOrNil cacheTag extent result |
<var: #pic type: #'CogMethod *'>
<var: #targetMethod type: #'CogMethod *'>
"Whether we can relink to a PIC or not we need to pop off the inner return and identify the target method."
innerReturn := coInterpreter popStack asUnsignedInteger.
targetMethod := self cCoerceSimple: innerReturn - missOffset to: #'CogMethod *'.
(objectMemory isOopForwarded: receiver) ifTrue:
[^coInterpreter ceSendFromInLineCacheMiss: targetMethod].
outerReturn := coInterpreter stackTop asUnsignedInteger.
self assert: (outerReturn between: methodZoneBase and: methodZone freeStart).
entryPoint := backEnd callTargetFromReturnAddress: outerReturn.
self assert: targetMethod selector ~= objectMemory nilObject.
self assert: targetMethod asInteger + cmEntryOffset = entryPoint.
self lookup: targetMethod selector
for: receiver
methodAndErrorSelectorInto:
[:method :errsel|
newTargetMethodOrNil := method.
errorSelectorOrNil := errsel].
"We assume lookupAndCog:for: will *not* reclaim the method zone"
self assert: outerReturn = coInterpreter stackTop.
cacheTag := objectRepresentation inlineCacheTagForInstance: receiver.
((errorSelectorOrNil notNil and: [errorSelectorOrNil ~= SelectorDoesNotUnderstand])
or: [(objectRepresentation inlineCacheTagIsYoung: cacheTag)
or: [(backEnd inlineCacheTagAt: outerReturn) = self picAbortDiscriminatorValue
or: [newTargetMethodOrNil isNil
or: [objectMemory isYoung: newTargetMethodOrNil]]]]) ifTrue:
[result := self patchToOpenPICFor: targetMethod selector
numArgs: targetMethod cmNumArgs
receiver: receiver.
self assert: result not. "If patchToOpenPICFor:.. returns we're out of code memory"
^coInterpreter ceSendFromInLineCacheMiss: targetMethod].
"See if an Open PIC is already available."
pic := methodZone openPICWithSelector: targetMethod selector.
+ (pic isNil or: [self allowEarlyOpenPICPromotion not]) ifTrue:
- pic ifNil:
["otherwise attempt to create a closed PIC for the two cases."
pic := self cogPICSelector: targetMethod selector
numArgs: targetMethod cmNumArgs
Case0Method: targetMethod
Case1Method: newTargetMethodOrNil
tag: cacheTag
isMNUCase: errorSelectorOrNil = SelectorDoesNotUnderstand.
(pic asInteger between: MaxNegativeErrorCode and: -1) ifTrue:
["For some reason the PIC couldn't be generated, most likely a lack of code memory.
Continue as if this is an unlinked send."
pic asInteger = InsufficientCodeSpace ifTrue:
[coInterpreter callForCogCompiledCodeCompaction].
^coInterpreter ceSendFromInLineCacheMiss: targetMethod].
processor flushICacheFrom: pic asUnsignedInteger to: pic asUnsignedInteger + closedPICSize].
"Relink the send site to the pic. If to an open PIC then reset the cache tag to the selector,
for the benefit of the cacheTag assert check in checkIfValidOopRef:pc:cogMethod: et al."
extent := pic cmType = CMOpenPIC
ifTrue:
[backEnd
rewriteInlineCacheAt: outerReturn
tag: (self inlineCacheValueForSelector: targetMethod selector
in: coInterpreter mframeHomeMethodExport
at: outerReturn)
target: pic asInteger + cmEntryOffset]
ifFalse:
[backEnd
rewriteCallAt: outerReturn
target: pic asInteger + cmEntryOffset].
processor flushICacheFrom: outerReturn asUnsignedInteger - extent to: outerReturn asUnsignedInteger.
"Jump back into the pic at its entry in case this is an MNU (newTargetMethodOrNil is nil)"
coInterpreter
executeCogPIC: pic
fromLinkedSendWithReceiver: receiver
andCacheTag: (backEnd inlineCacheTagAt: outerReturn).
"NOTREACHED"
^nil!
Item was added:
+ ----- Method: SistaCogit>>allowEarlyOpenPICPromotion (in category 'in-line cacheing') -----
+ allowEarlyOpenPICPromotion
+ <inline: true>
+ ^ false!