Tom Braun uploaded a new version of VMMaker to project VM Maker: http://source.squeak.org/VMMaker/VMMaker.oscog.seperateMarking-WoC.3335.mcz
==================== Summary ====================
Name: VMMaker.oscog.seperateMarking-WoC.3335 Author: WoC Time: 12 July 2023, 12:03:02.02244 am UUID: ce3cd6a3-1732-4656-a1de-f8e318f90642 Ancestors: VMMaker.oscog.seperateMarking-WoC.3334
- fix bug with incorrect type declaration in mapStackPages, which caused tenured objects during sweeping to not get marked
- added trampolines with the correct registers as arguments for the tricolor barrier
- began to write class comments (not much until now)
- removed SpurHybridGarbageCollector (the class was just an unused idea)
- handle forwarders created by the mutator during compacting different. Do not unremember them, but remove remembered forwarders after marking ended (they are not marked -> forgetUnmarkedRememberedObjects removes them)
- some minor changes to make the simulator run with less issues when incremental GC is enabled
=============== Diff against VMMaker.oscog.seperateMarking-WoC.3334 ===============
Item was changed: ----- Method: CoInterpreter>>mapStackPages (in category 'object memory support') ----- mapStackPages - <declareTypeForStaticPolymorphism: #SpurStopTheWorldGarbageCollector> <inline: #never> <var: #thePage type: #'StackPage *'> <var: #theSP type: #'char *'> <var: #theFP type: #'char *'> <var: #frameRcvrOffset type: #'char *'> <var: #callerFP type: #'char *'> <var: #theIPPtr type: #'char *'> | numLivePages | numLivePages := 0. 0 to: numStackPages - 1 do: [:i| | thePage theSP theFP frameRcvrOffset callerFP theIPPtr theIP oop | thePage := stackPages stackPageAt: i. thePage isFree ifFalse: [self assert: (self ifCurrentStackPageHasValidHeadPointers: thePage). numLivePages := numLivePages + 1. theSP := thePage headSP. theFP := thePage headFP. "Skip the instruction pointer on top of stack of inactive pages." thePage = stackPage ifTrue: [theIPPtr := ((self isMachineCodeFrame: theFP) or: [(self iframeSavedIP: theFP) = 0]) ifTrue: [0] ifFalse: [theFP + FoxIFSavedIP]] ifFalse: [theIPPtr := theSP. theSP := theSP + objectMemory wordSize]. [self assert: (thePage addressIsInPage: theFP). self assert: (thePage addressIsInPage: theSP). self assert: (theIPPtr = 0 or: [thePage addressIsInPage: theIPPtr]). frameRcvrOffset := self frameReceiverLocation: theFP. [theSP <= frameRcvrOffset] whileTrue: [oop := stackPages longAt: theSP. (objectMemory shouldRemapOop: oop) ifTrue: [stackPages longAt: theSP put: (objectMemory remapObj: oop)]. theSP := theSP + objectMemory wordSize]. (self frameHasContext: theFP) ifTrue: [(objectMemory shouldRemapObj: (self frameContext: theFP)) ifTrue: [stackPages longAt: theFP + FoxThisContext put: (objectMemory remapObj: (self frameContext: theFP))]. "With SqueakV3 objectMemory or SpurPlanningCompactor can't assert since object body is yet to move." (objectMemory hasSpurMemoryManagerAPI and: [(objectMemory slidingCompactionInProgress or: [objectMemory scavengeInProgress]) not]) ifTrue: [self assert: ((self isMarriedOrWidowedContext: (self frameContext: theFP)) and: [(self frameOfMarriedContext: (self frameContext: theFP)) = theFP])]]. (self isMachineCodeFrame: theFP) ifFalse: [(objectMemory shouldRemapObj: (self iframeMethod: theFP)) ifTrue: [theIPPtr ~= 0 ifTrue: [theIP := stackPages longAt: theIPPtr. theIP = cogit ceReturnToInterpreterPC ifTrue: [self assert: (self iframeSavedIP: theFP) > (self iframeMethod: theFP). theIPPtr := theFP + FoxIFSavedIP. theIP := stackPages longAt: theIPPtr] ifFalse: [self assert: theIP > (self iframeMethod: theFP)]. theIP := theIP - (self iframeMethod: theFP)]. stackPages longAt: theFP + FoxMethod put: (objectMemory remapObj: (self iframeMethod: theFP)). theIPPtr ~= 0 ifTrue: [stackPages longAt: theIPPtr put: theIP + (self iframeMethod: theFP)]]]. (callerFP := self frameCallerFP: theFP) ~= 0] whileTrue: [theSP := (theIPPtr := theFP + FoxCallerSavedIP) + objectMemory wordSize. theFP := callerFP]. theSP := theFP + FoxCallerSavedIP + objectMemory wordSize. [theSP <= thePage baseAddress] whileTrue: [oop := stackPages longAt: theSP. (objectMemory shouldRemapOop: oop) ifTrue: [stackPages longAt: theSP put: (objectMemory remapObj: oop)]. theSP := theSP + objectMemory wordSize]]]. stackPages recordLivePagesOnMapping: numLivePages!
Item was changed: CogObjectRepresentation subclass: #CogObjectRepresentationForSpur + instanceVariableNames: 'ceRetractWavefrontTrampoline ceTricolorCheckArg0Trampoline ceTricolorCheckArg1Trampoline ceScheduleScavengeTrampoline ceSmallActiveContextInMethodTrampoline ceSmallActiveContextInBlockTrampoline ceSmallActiveContextInFullBlockTrampoline ceLargeActiveContextInMethodTrampoline ceLargeActiveContextInBlockTrampoline ceLargeActiveContextInFullBlockTrampoline ceStoreCheckContextReceiverTrampoline ceStoreTrampolines ceNewHashTrampoline ceInlineNewHashTrampoline' - instanceVariableNames: 'ceRetractWavefrontTrampoline ceTricolorCheckTrampoline ceScheduleScavengeTrampoline ceSmallActiveContextInMethodTrampoline ceSmallActiveContextInBlockTrampoline ceSmallActiveContextInFullBlockTrampoline ceLargeActiveContextInMethodTrampoline ceLargeActiveContextInBlockTrampoline ceLargeActiveContextInFullBlockTrampoline ceStoreCheckContextReceiverTrampoline ceStoreTrampolines ceNewHashTrampoline ceInlineNewHashTrampoline' classVariableNames: 'CheckRememberedInTrampoline NumStoreTrampolines' poolDictionaries: 'VMBytecodeConstants VMSqueakClassIndices' category: 'VMMaker-JIT'!
Item was changed: ----- Method: CogObjectRepresentationForSpur class>>numTrampolines (in category 'trampoline support') ----- numTrampolines ^super numTrampolines + (SistaV1BytecodeSet ifTrue: [9] "(small,large)x(method,block,fullBlock) context creation, ceNewHashTrampoline, ceStoreCheckContextReceiverTrampoline and ceScheduleScavengeTrampoline" ifFalse: [7] "(small,large)x(method,block) context creation, ceNewHashTrampoline, ceStoreCheckContextReceiverTrampoline and ceScheduleScavengeTrampoline") + (IMMUTABILITY ifTrue: [NumStoreTrampolines] ifFalse: [0]) + (SistaVM ifTrue: [1] "inline newHash" ifFalse: [0]) + (SpurMemoryManager wantsIncrementalGC + ifTrue: [3] - ifTrue: [1] ifFalse: [0])!
Item was changed: ----- Method: CogObjectRepresentationForSpur>>genStoreCheckReceiverReg:valueReg:scratchReg:inFrame: (in category 'compile abstract instructions') ----- genStoreCheckReceiverReg: destReg valueReg: valueReg scratchReg: scratchReg inFrame: inFrame "Generate the code for a store check of valueReg into destReg." | jmpImmediate jmpDestYoung jmpSourceOld jmpAlreadyRemembered jmpYoungBranch jmpNotMarked jmpNotMarking | <var: #jmpImmediate type: #'AbstractInstruction *'> <var: #jmpDestYoung type: #'AbstractInstruction *'> <var: #jmpSourceOld type: #'AbstractInstruction *'> <var: #jmpAlreadyRemembered type: #'AbstractInstruction *'> <staticallyResolveReceiver: 'objectMemory gc' to: #SpurIncrementalGarbageCollector> "Is value stored an immediate? If so we're done" jmpImmediate := self genJumpImmediate: valueReg. "Get the old/new boundary in scratchReg" cogit MoveCw: objectMemory storeCheckBoundary R: scratchReg. "Is target young? If so we're done" cogit CmpR: scratchReg R: destReg. "N.B. FLAGS := destReg - scratchReg" jmpDestYoung := cogit JumpBelow: 0. "Is value stored old? If so we're done." cogit CmpR: scratchReg R: valueReg. "N.B. FLAGS := valueReg - scratchReg" jmpSourceOld := cogit JumpAboveOrEqual: 0. "value is young and target is old. Need to remember this only if the remembered bit is not already set." CheckRememberedInTrampoline ifFalse: [jmpAlreadyRemembered := self genCheckRememberedBitOf: destReg scratch: scratchReg]. "Remembered bit is not set. Call store check to insert dest into remembered table." self assert: destReg = ReceiverResultReg. cogit evaluateTrampolineCallBlock: [cogit CallRT: ceStoreCheckTrampoline] protectLinkRegIfNot: inFrame.
objectMemory gc isIncremental ifTrue: [ jmpYoungBranch := cogit Jump: 0. jmpSourceOld jmpTarget: cogit Label. "is the gc currently marking?" cogit MoveAw: objectMemory marker currentlyActive R: scratchReg. cogit CmpCq: 0 R: scratchReg. jmpNotMarking := cogit JumpZero: 0. "is the mark bit set? If yes trigger the tricolor barrier" jmpNotMarked := self genCheckMarkBitOf: destReg scratch: scratchReg.
+ self assert: (valueReg = Arg0Reg or: [valueReg = Arg1Reg]). + valueReg = Arg0Reg + ifTrue: [cogit + evaluateTrampolineCallBlock: [cogit CallRT: ceTricolorCheckArg0Trampoline] + protectLinkRegIfNot: inFrame]. + valueReg = Arg1Reg + ifTrue: [cogit + evaluateTrampolineCallBlock: [cogit CallRT: ceTricolorCheckArg1Trampoline] + protectLinkRegIfNot: inFrame]]. - self assert: destReg = ReceiverResultReg. - cogit - evaluateTrampolineCallBlock: [cogit CallRT: ceTricolorCheckTrampoline] - protectLinkRegIfNot: inFrame]. (jmpImmediate jmpTarget: (jmpDestYoung jmpTarget: cogit Label)). CheckRememberedInTrampoline ifFalse: [jmpAlreadyRemembered jmpTarget: jmpDestYoung getJmpTarget]. objectMemory gc isIncremental ifTrue: [ jmpYoungBranch jmpTarget: (jmpNotMarked jmpTarget: (jmpNotMarking jmpTarget: jmpDestYoung getJmpTarget))] ifFalse: [jmpSourceOld jmpTarget: jmpDestYoung getJmpTarget]. ^0!
Item was changed: ----- Method: CogObjectRepresentationForSpur>>genStoreTrampolineCheckingRememberedCalled:instVarIndex: (in category 'initialization') ----- genStoreTrampolineCheckingRememberedCalled: trampolineName instVarIndex: instVarIndex "Convention: - RcvrResultReg holds the object to be mutated. If immutability failure: - TempReg holds the instance variable index mutated if instVarIndex > numDedicatedStoreTrampoline - ClassReg holds the value to store Registers are not live across this trampoline as the immutability failure may need new stack frames." | jumpImmutable jumpRC checkTricolor | <option: #IMMUTABILITY> <staticallyResolveReceiver: 'objectMemory gc' to: #SpurIncrementalGarbageCollector> <var: #trampolineName type: #'char *'> <inline: true> "SendNumArgsReg is mutated but we don't care as registers are not live across the trampoline. There is no reason why registers cannot be saved over the remember: call, but since the immutability check is a suspension point, registers cannot remain live." "Jump in the uncommon case" jumpImmutable := self genJumpImmutable: ReceiverResultReg scratchReg: SendNumArgsReg. objectMemory gc isIncremental ifTrue: [ cogit CmpCw: objectMemory storeCheckBoundary R: ClassReg. checkTricolor := cogit JumpAboveOrEqual: 0]. "Store check" "If on 64-bits and doing the remembered bit test here, we can combine the tests to fetch the header once." objectMemory wordSize = 8 ifTrue: [cogit TstCq: 1 << objectMemory rememberedBitShift R: SendNumArgsReg. jumpRC := cogit JumpZero: 0] ifFalse: [jumpRC := self genCheckRememberedBitOf: ReceiverResultReg scratch: SendNumArgsReg. self assert: jumpRC opcode = JumpNonZero. jumpRC opcode: JumpZero]. cogit RetN: 0. jumpRC jmpTarget: cogit Label. cogit compileTrampolineFor: #remember: numArgs: 1 arg: ReceiverResultReg arg: nil arg: nil arg: nil regsToSave: cogit emptyRegisterMask pushLinkReg: true resultReg: cogit returnRegForStoreCheck. objectMemory gc isIncremental ifTrue: [ checkTricolor jmpTarget: cogit Label. cogit compileTrampolineFor: #SIM_markAndShouldScan: numArgs: 1 + arg: ClassReg - arg: ReceiverResultReg arg: nil arg: nil arg: nil regsToSave: cogit emptyRegisterMask pushLinkReg: true + resultReg: cogit returnRegForStoreCheck]. - resultReg: cogit returnRegForStoreCheck.]. jumpImmutable jmpTarget: cogit Label. cogit compileTrampolineFor: #ceCannotAssignTo:withIndex:valueToAssign: numArgs: 3 arg: ReceiverResultReg arg: (instVarIndex < (NumStoreTrampolines - 1) ifTrue: [cogit trampolineArgConstant: instVarIndex] ifFalse: [TempReg]) arg: ClassReg arg: nil regsToSave: cogit emptyRegisterMask pushLinkReg: true resultReg: NoReg. ^0 !
Item was changed: ----- Method: CogObjectRepresentationForSpur>>genStoreTrampolineNotCheckingRememberedCalled:instVarIndex: (in category 'initialization') ----- genStoreTrampolineNotCheckingRememberedCalled: trampolineName instVarIndex: instVarIndex "Convention: - RcvrResultReg holds the object to be mutated. If immutability failure: - TempReg holds the instance variable index mutated if instVarIndex > numDedicatedStoreTrampoline - ClassReg holds the value to store Registers are not live across this trampoline as the immutability failure may need new stack frames." | jumpImmutable pushLinkReg checkTricolor | <option: #IMMUTABILITY> <staticallyResolveReceiver: 'objectMemory gc' to: #SpurIncrementalGarbageCollector> <var: #trampolineName type: #'char *'> <inline: true> "We are going to call something, either remember: in the common case, or much more rarely ceCannotAssignTo:withIndex:valueToAssign:. So share the stack switch between the two." cogit genSmalltalkToCStackSwitch: (pushLinkReg := cogit backEnd hasLinkRegister). "SendNumArgsReg is mutated but we don't care as registers are not live across the trampoline. There is no reason why registers cannot be saved over the remember: call, but since the immutability check is a suspension point, registers cannot remain live." jumpImmutable := self genJumpImmutable: ReceiverResultReg scratchReg: SendNumArgsReg. objectMemory gc isIncremental ifTrue: [ cogit CmpCw: objectMemory storeCheckBoundary R: ClassReg. checkTricolor := cogit JumpAboveOrEqual: 0]. "Store check" cogit compileCallFor: #remember: numArgs: 1 arg: ReceiverResultReg arg: nil arg: nil arg: nil resultReg: cogit returnRegForStoreCheck regsToSave: cogit emptyRegisterMask. cogit backEnd genLoadStackPointers. cogit genTrampolineReturn: pushLinkReg. objectMemory gc isIncremental ifTrue: [ checkTricolor jmpTarget: cogit Label. cogit compileCallFor: #SIM_markAndShouldScan: numArgs: 1 + arg: ClassReg - arg: ReceiverResultReg arg: nil arg: nil arg: nil resultReg: cogit returnRegForStoreCheck regsToSave: cogit emptyRegisterMask. cogit backEnd genLoadStackPointers. cogit genTrampolineReturn: pushLinkReg]. jumpImmutable jmpTarget: cogit Label. cogit compileCallFor: #ceCannotAssignTo:withIndex:valueToAssign: numArgs: 3 arg: ReceiverResultReg arg: (instVarIndex < (NumStoreTrampolines - 1) ifTrue: [cogit trampolineArgConstant: instVarIndex] ifFalse: [TempReg]) arg: ClassReg arg: nil resultReg: NoReg regsToSave: cogit emptyRegisterMask. cogit backEnd genLoadStackPointers. cogit genTrampolineReturn: pushLinkReg. ^0!
Item was added: + ----- Method: CogObjectRepresentationForSpur>>genTricolorCheckArg0Trampoline (in category 'initialization') ----- + genTricolorCheckArg0Trampoline + + <inline: true> + "SIM_ prefix comes from static polymorphism. unfortunately it is necessary :(" + ^cogit + genTrampolineFor: #SIM_markAndShouldScan: + called: 'ceTricolorCheckTrampoline' + numArgs: 1 + arg: Arg0Reg + arg: nil + arg: nil + arg: nil + regsToSave: CallerSavedRegisterMask + pushLinkReg: true + resultReg: NoReg + appendOpcodes: false!
Item was added: + ----- Method: CogObjectRepresentationForSpur>>genTricolorCheckArg1Trampoline (in category 'initialization') ----- + genTricolorCheckArg1Trampoline + + <inline: true> + "SIM_ prefix comes from static polymorphism. unfortunately it is necessary :(" + ^cogit + genTrampolineFor: #SIM_markAndShouldScan: + called: 'ceTricolorCheckTrampoline' + numArgs: 1 + arg: Arg1Reg + arg: nil + arg: nil + arg: nil + regsToSave: CallerSavedRegisterMask + pushLinkReg: true + resultReg: NoReg + appendOpcodes: false!
Item was removed: - ----- Method: CogObjectRepresentationForSpur>>genTricolorCheckTrampoline (in category 'initialization') ----- - genTricolorCheckTrampoline - - <inline: true> - "SIM_ prefix comes from static polymorphism. unfortunately it is necessary :(" - ^cogit - genTrampolineFor: #SIM_markAndShouldScan: - called: 'ceTricolorCheckTrampoline' - numArgs: 1 - arg: ReceiverResultReg - arg: nil - arg: nil - arg: nil - regsToSave: CallerSavedRegisterMask - pushLinkReg: true - resultReg: NoReg - appendOpcodes: false!
Item was changed: ----- Method: CogObjectRepresentationForSpur>>generateObjectRepresentationTrampolines (in category 'initialization') ----- generateObjectRepresentationTrampolines "Do the store check. Answer the argument for the benefit of the code generator; ReceiverResultReg may be caller-saved and hence smashed by this call. Answering it allows the code generator to reload ReceiverResultReg cheaply. In Spur the only thing we leave to the run-time is adding the receiver to the remembered set and setting its isRemembered bit." <staticallyResolveReceiver: 'objectMemory gc' to: #SpurIncrementalGarbageCollector> self cppIf: IMMUTABILITY ifTrue: [self cCode: [] inSmalltalk: [ceStoreTrampolines := CArrayAccessor on: (Array new: NumStoreTrampolines)]. 0 to: NumStoreTrampolines - 1 do: [:instVarIndex | ceStoreTrampolines at: instVarIndex put: (self genStoreTrampolineCalled: (cogit trampolineName: 'ceStoreTrampoline' numArgs: instVarIndex limit: NumStoreTrampolines - 2) instVarIndex: instVarIndex)]]. ceNewHashTrampoline := self genNewHashTrampoline: false called: 'ceNewHash'. SistaVM ifTrue: [ceInlineNewHashTrampoline := self genNewHashTrampoline: true called: 'ceInlineNewHash']. ceStoreCheckTrampoline := self genStoreCheckTrampoline. objectMemory gc isIncremental + ifTrue: [ceTricolorCheckArg0Trampoline := self genTricolorCheckArg0Trampoline. + ceTricolorCheckArg1Trampoline := self genTricolorCheckArg1Trampoline. + - ifTrue: [ceTricolorCheckTrampoline := self genTricolorCheckTrampoline. ceRetractWavefrontTrampoline := self genRetractWavefrontTrampoline]. ceStoreCheckContextReceiverTrampoline := self genStoreCheckContextReceiverTrampoline. ceScheduleScavengeTrampoline := cogit genTrampolineFor: #ceScheduleScavenge called: 'ceScheduleScavengeTrampoline' regsToSave: CallerSavedRegisterMask. ceSmallActiveContextInMethodTrampoline := self genActiveContextTrampolineLarge: false inBlock: 0 called: 'ceSmallMethodContext'. ceSmallActiveContextInBlockTrampoline := self genActiveContextTrampolineLarge: false inBlock: InVanillaBlock called: 'ceSmallBlockContext'. SistaV1BytecodeSet ifTrue: [ceSmallActiveContextInFullBlockTrampoline := self genActiveContextTrampolineLarge: false inBlock: InFullBlock called: 'ceSmallFullBlockContext']. ceLargeActiveContextInMethodTrampoline := self genActiveContextTrampolineLarge: true inBlock: 0 called: 'ceLargeMethodContext'. ceLargeActiveContextInBlockTrampoline := self genActiveContextTrampolineLarge: true inBlock: InVanillaBlock called: 'ceLargeBlockContext'. SistaV1BytecodeSet ifTrue: [ceLargeActiveContextInFullBlockTrampoline := self genActiveContextTrampolineLarge: true inBlock: InFullBlock called: 'ceLargeFullBlockContext']. LowcodeVM ifTrue: [ self generateLowcodeObjectTrampolines ]!
Item was changed: CogClass subclass: #SpurGarbageCollector + instanceVariableNames: 'marker scavenger compactor manager coInterpreter' - instanceVariableNames: 'marker scavenger compactor manager coInterpreter allocatorShouldAllocateBlack' classVariableNames: '' poolDictionaries: 'SpurMemoryManagementConstants VMSpurObjectRepresentationConstants' category: 'VMMaker-SpurGarbageCollector'! + + !SpurGarbageCollector commentStamp: 'WoC 7/11/2023 13:33' prior: 0! + A SpurGarbageCollector is an abstract superclass for concrete garbage collector implementations. Because all currently implemented garbage collectors are + based on Generation Scavenging, they have an young space collection. Currently the young space collection is the same for all implementations, therefore + the interaction with the SpurGenerationScavenger is implemented in this class. The concrete interactions and how to trigger old space collection is + implemented in the subclasses. + + Provides a common interface for the different concrete implementations. If a method is abstract is currently has to be at least implemented in direct subclasses + of SpurGarbageCollector (because the current (7/11/2023) implementation of static polymorphism cannot handle it otherwise) + + Instance Variables + coInterpreter: <StackInterpreter|CoInterpreter> + compactor: <SpurPlanningCompactor|SpurIncrementalSweepAndCompact> + manager: <SpurMemoryManager> + marker: <SpurMarker> + scavenger: <SpurGenerationScavenger> + + coInterpreter + - A reference to the coInterpreter + + compactor + - A reference to the compactor + + manager + - A reference to the manager + + marker + - A reference to the marker + + scavenger + - A reference to the scavenger + !
Item was changed: ----- Method: SpurGarbageCollector class>>declareCVarsIn: (in category 'as yet unclassified') ----- declareCVarsIn: aCCodeGenerator
super declareCVarsIn: aCCodeGenerator. - aCCodeGenerator - var: #allocatorShouldAllocateBlack type: #usqInt. "do not generate polymorphic methods for abstract baseclass SpurGarbageCollector" self = SpurGarbageCollector ifTrue: [^ self]. SpurMemoryManager wantsIncrementalGC ifFalse: [^ self]. aCCodeGenerator staticallyResolvedPolymorphicReceiver: 'marker' to: self markerClass in: self; staticallyResolvedPolymorphicReceiver: 'compactor' to: self compactorClass in: self. self selectorsInIncrementalAndStopTheWorldGC do: [:key | aCCodeGenerator staticallyResolveMethodNamed: key forClass: self to: (self staticallyResolvePolymorphicSelector: key)].!
Item was removed: - ----- Method: SpurGarbageCollector>>allocatorShouldAllocateBlack (in category 'accessing') ----- - allocatorShouldAllocateBlack - - <cmacro: '() GIV(allocatorShouldAllocateBlack)'> - ^ allocatorShouldAllocateBlack!
Item was removed: - ----- Method: SpurGarbageCollector>>allocatorShouldAllocateBlack: (in category 'accessing') ----- - allocatorShouldAllocateBlack: anObject - - allocatorShouldAllocateBlack := anObject.!
Item was removed: - ----- Method: SpurGarbageCollector>>initialize (in category 'initialize-release') ----- - initialize - - allocatorShouldAllocateBlack := false!
Item was removed: - SpurGarbageCollector subclass: #SpurHybridGarbageCollector - instanceVariableNames: 'isInIncrementalMode incrementalGC stopTheWorldGC' - classVariableNames: '' - poolDictionaries: '' - category: 'VMMaker-SpurGarbageCollector'!
Item was removed: - ----- Method: SpurHybridGarbageCollector class>>classesForTranslation (in category 'translation') ----- - classesForTranslation - - ^ super classesForTranslation , {SpurMarker . SpurIncrementalMarker . SpurAllAtOnceMarker . SpurPlanningCompactor . SpurIncrementalSweeper . SpurIncrementalCompactor . SpurIncrementalSweepAndCompact }!
Item was removed: - ----- Method: SpurHybridGarbageCollector class>>declareCVarsIn: (in category 'translation') ----- - declareCVarsIn: aCCodeGenerator - - aCCodeGenerator - staticallyResolvedPolymorphicReceiver: 'incrementalGC' to: SpurIncrementalGarbageCollector in: self; - staticallyResolvedPolymorphicReceiver: 'stopTheWorldGC' to: SpurStopTheWorldGarbageCollector in: self!
Item was removed: - ----- Method: SpurHybridGarbageCollector>>initialize (in category 'as yet unclassified') ----- - initialize - - isInIncrementalMode := true. - incrementalGC := SpurIncrementalGarbageCollector new. - stopTheWorldGC := SpurStopTheWorldGarbageCollector new.!
Item was removed: - ----- Method: SpurHybridGarbageCollector>>writeBarrierFor:at:with: (in category 'as yet unclassified') ----- - writeBarrierFor: anObject at: index with: value - - self isInIncrementalMode - ifTrue: [incrementalGC writeBarrierFor: anObject at: index with: value] - ifFalse: ["does nothing"]!
Item was changed: ----- Method: SpurIncrementalCompactingSweeper>>reserveSegmentToFill (in category 'segment access') ----- reserveSegmentToFill "remove the free space from the freeLists so the mutator cannot allocate in this segment" | freeChunk | self assert: segmentToFill notNil. self assert: (self segmentIsEmpty: segmentToFill). + (self segmentIsEmpty: segmentToFill) ifFalse: [manager debugger]. freeChunk := manager objectStartingAt: segmentToFill segStart. manager detachFreeObject: freeChunk!
Item was changed: ----- Method: SpurIncrementalCompactor>>compactSegment:freeStart:segIndex: (in category 'incremental compaction') ----- compactSegment: segInfo freeStart: initialFreeStart segIndex: segIndex <var: 'segInfo' type: #'SpurSegmentInfo *'>
| fillStart | fillStart := initialFreeStart. self deny: segIndex = 0. "Cannot compact seg 0" manager segmentManager allEntitiesInSegment: segInfo exceptTheLastBridgeDo: [:entity| (manager isFreeObject: entity) ifTrue: [reservedObjects := reservedObjects + 1. manager detachFreeObject: entity. "To avoid confusing too much Spur (especially the leak/free checks), we mark the free chunk as a word object." manager set: entity classIndexTo: manager wordSizeClassIndexPun formatTo: manager wordIndexableFormat] ifFalse: + [ + "Ignore forwarders. + - We do not want to copy them because they may be bigger than they need to (they need 16 bytes, but are as bit as the original object) + - we are not allowed to remove them from the remembered set here, because they could point to a young objects and the forwarder's reference + still needs to be updated (which it would not if we remove it from the remembered set) + - we have to make sure to remove unmarked objects from the remembered set after the next phase to remove the forwarder from the set. Otherwise + it would remain in the set, as we won't visit individual pointers when freeing the segment after compaction" + (manager isForwarded: entity) - [(manager isForwarded: entity) - ifTrue: ["do not simply copy forwarder. (1) They get resolved during the next marking without us having to do anything - (2) they preserved the original objects size they got created from and would just waste space unless we copied - only the header and the first slot" - (manager isRemembered: entity) - ifTrue: [scavenger forgetObject: entity]] ifFalse:[| bytesToCopy | "I really hate that this can happen :( . The marker will keep track of which segments contain pinned objects. If the pinned object is created during sweeping and compacting, we cannot know about it during planning" (manager isPinned: entity) ifTrue: [self abortCompactionAt: entity in: segInfo. ^ fillStart].
"Copy the object in segmentToFill and replace it by a forwarder." bytesToCopy := manager bytesInBody: entity. (self canTolerateObjStartingAt: fillStart sized: bytesToCopy inSegment: segmentToFill) ifFalse: [self abortCompactionAt: entity in: segInfo. currentSegmentIndex := currentSegmentIndex + 1. ^ fillStart]. compactedObjects := compactedObjects + 1. compactedBytes := compactedBytes + bytesToCopy.
self migrate: entity sized: bytesToCopy to: fillStart.
fillStart := fillStart + bytesToCopy. self assert: (self oop: fillStart isLessThan: (segmentToFill segLimit - manager bridgeSize))]]].
currentSegmentIndex := currentSegmentIndex + 1. ^ fillStart!
Item was changed: SpurGarbageCollector subclass: #SpurIncrementalGarbageCollector + instanceVariableNames: 'phase allAtOnceMarker checkSetGCFlags stopTheWorldGC fullGCWanted phaseCounter gcPauseGoal shouldCollectCounter markTime markCount sweepTime sweepCount compactTime compactCount allocatorShouldAllocateBlack' - instanceVariableNames: 'phase allAtOnceMarker checkSetGCFlags stopTheWorldGC fullGCWanted phaseCounter gcPauseGoal shouldCollectCounter markTime markCount sweepTime sweepCount compactTime compactCount' classVariableNames: 'InCompactingPhase InMarkingPhase InSweepingPhase' poolDictionaries: '' category: 'VMMaker-SpurGarbageCollector'!
!SpurIncrementalGarbageCollector commentStamp: 'WoC 1/5/2023 21:36' prior: 0! A SpurIncrementalGarbageCollector is a garbage collection algorithm. The GC is a mark and sweep with an additional compaction if certain conditions are fulfilled. This class manages SpurIncrementalMarker and SpurIncrementalSweepAndCompact (which in turn manages SpurIncrementalCompactor and SpurIncrementalSweeper). The 3 classes implementing the GC are therefore SpurIncrementalMarker, SpurIncrementalSweeper and SpurIncrementalCompactor.
Instance Variables allAtOnceMarker: <SpurAllAtOnceMarker> checkSetGCFlags: <Bool> phase: <Number (InMarkingPhase|InSweepingPhase|InCompactingPhase)>
allAtOnceMarker - an instance of SpurAllAtOnceMarker. We sometimes need parts of the old (stop-the-world) gc algorithm. This is the marking algorithm we can use through static polymorphism
checkSetGCFlags - should we check if it ok to set gc flags or not
phase - in which phase is the gc algorithm at the moment. Is either InMarkingPhase, InSweepingPhase or InCompactingPhase !
Item was changed: ----- Method: SpurIncrementalGarbageCollector class>>declareCVarsIn: (in category 'translation') ----- declareCVarsIn: aCCodeGenerator super declareCVarsIn: aCCodeGenerator. aCCodeGenerator var: 'phase' declareC: 'sqInt phase = 0'; var: 'checkSetGCFlags' declareC: 'sqInt checkSetGCFlags = 1'; var: 'shouldCollectCounter' declareC: 'sqInt shouldCollectCounter = 0'; + var: 'trigger' declareC: 'sqInt trigger = 0'; + var: #allocatorShouldAllocateBlack type: #usqInt. - var: 'trigger' declareC: 'sqInt trigger = 0'. #(markTime markCount sweepTime sweepCount compactTime compactCount) do: [:var | aCCodeGenerator var: var declareC: 'sqInt ' , var , ' = 0'.]. aCCodeGenerator staticallyResolvedPolymorphicReceiver: 'allAtOnceMarker' to: SpurAllAtOnceMarker in: self; staticallyResolvedPolymorphicReceiver: 'stopTheWorldGC' to: SpurStopTheWorldGarbageCollector in: self.
SpurMemoryManager gcClass = self ifTrue: [ self declareRecursivePolymorphismMappingForIncrementalClasses: aCCodeGenerator. "just important when doiing incremental compaction, therefore doin it here" aCCodeGenerator forRecursivePolymorphismResolve: SpurAllAtOnceMarker as: SpurStopTheWorldGarbageCollector; forRecursivePolymorphismResolve: SpurPlanningCompactor as: SpurStopTheWorldGarbageCollector]. !
Item was added: + ----- Method: SpurIncrementalGarbageCollector>>allocatorShouldAllocateBlack (in category 'accessing') ----- + allocatorShouldAllocateBlack + + <cmacro: '() GIV(allocatorShouldAllocateBlack)'> + ^ allocatorShouldAllocateBlack!
Item was added: + ----- Method: SpurIncrementalGarbageCollector>>allocatorShouldAllocateBlack: (in category 'accessing') ----- + allocatorShouldAllocateBlack: anObject + + allocatorShouldAllocateBlack := anObject.!
Item was changed: ----- Method: SpurIncrementalGarbageCollector>>canReactToShiftSegment:to: (in category 'compactor support') ----- canReactToShiftSegment: segmentAddress to: anIndex "because we have a pointer to segmentToFill changes in the segments array (where segmentToFill is pointing to) can invalidate our pointer (it now points to an incorrect segment). Therefore react to this change and set the segmentToFill to the new address" segmentAddress = compactor segmentToFill ifTrue: [ + self + cCode: [compactor setSegmentToFillToAddress: (manager segInfoAt: anIndex)] + inSmalltalk: ["in C we store a pointer we need to update. In Smalltalk we have a reference"]]! - compactor setSegmentToFillToAddress: (manager segInfoAt: anIndex)]!
Item was changed: ----- Method: SpurIncrementalGarbageCollector>>doIncrementalCollect (in category 'as yet unclassified') ----- doIncrementalCollect | startTime | phase = InMarkingPhase ifTrue: [ | finishedMarking | marker isCurrentlyMarking + ifFalse: [self assert: manager allObjectsWhite. - ifFalse: [self assert: manager allObjectsUnmarked. markCount := markCount + 1.]. coInterpreter cr; print: 'start marking '; printNum: (phaseCounter := phaseCounter + 1); tab; flush. startTime := coInterpreter ioUTCMicrosecondsNow. finishedMarking := marker incrementalMarkObjects. markTime := markTime + (coInterpreter ioUTCMicrosecondsNow - startTime). "self assert: manager validObjectColors." finishedMarking ifTrue: [ "manager allPastSpaceObjectsDo: [:obj | self assert: (manager isWhite: obj)]." "when sweeping the mutator needs to allocate new objects black as we do not have any information about them. We only know if they should get swept after the next marking -> keep them alive for this cycle" self allocatorShouldAllocateBlack: true. compactor setInitialSweepingEntity. self phase: InSweepingPhase. "marking is done and thus all forwarding from the last compaction references are resolved -> we can use the now free segments that were compacted during the last cycle" compactor freePastSegmentsAndSetSegmentToFill. manager segmentManager prepareForGlobalSweep. self assert: manager noObjectGrey. coInterpreter cr; print: 'finish marking '; tab; flush.
startTime := coInterpreter ioUTCMicrosecondsNow. manager setCheckForLeaks: GCCheckFreeSpace + GCModeFull; runLeakCheckerFor: GCModeFull excludeUnmarkedObjs: true classIndicesShouldBeValid: true; checkFreeSpace: GCModeFull. coInterpreter cr; print: 'time for internal check: '; printNum: coInterpreter ioUTCMicrosecondsNow - startTime; tab; flush. markCount := markCount + 1. coInterpreter cr; print: 'mark ------> '; printNum: markTime; print: ' '; printNum: manager oldSpaceSize - manager totalFreeOldSpace; tab; flush. + self assert: manager validObjectColors. - self assert: manager validObjectColors. ^ self] ifFalse: [coInterpreter cr; print: 'finish marking pass'; tab; flush. manager runLeakCheckerFor: GCModeIncremental]]. phase = InSweepingPhase ifTrue: [ | finishedSweeping | coInterpreter cr; print: 'start sweeping '; tab; flush. manager compactionStartUsecs: coInterpreter ioUTCMicrosecondsNow. startTime := coInterpreter ioUTCMicrosecondsNow. finishedSweeping := compactor incrementalSweep. sweepTime := sweepTime + (coInterpreter ioUTCMicrosecondsNow - startTime).
self flag: #Todo. "if not segmentToFill is reserved do so here first before attemp to shrink" manager attemptToShrink. finishedSweeping ifTrue: [ self allocatorShouldAllocateBlack: false. "manager allOldSpaceObjectsDo: [:ea | self assert: (manager isWhite: ea) ]." "self assert: manager allObjectsUnmarked." coInterpreter cr; print: 'finish sweeping '; tab; flush. startTime := coInterpreter ioUTCMicrosecondsNow. manager setCheckForLeaks: GCCheckFreeSpace + GCModeFull; runLeakCheckerFor: GCModeFull; checkFreeSpace: GCModeFull. coInterpreter cr; print: 'time for internal check: '; printNum: coInterpreter ioUTCMicrosecondsNow - startTime; tab; flush. compactor assertNoSegmentBeingCompacted. sweepCount := sweepCount + 1. coInterpreter cr; print: 'sweep ------> '; printNum: sweepTime; print: ' '; printNum: manager oldSpaceSize - manager totalFreeOldSpace; print: ' '; printNum: compactor sweeper sweptEntities; tab; flush. self phase: InCompactingPhase. ^ self]]. phase = InCompactingPhase ifTrue: [ | compactionFinished | "self cCode: 'raise(SIGINT)'." coInterpreter cr; print: 'start compacting '; tab; flush. manager compactionStartUsecs: coInterpreter ioUTCMicrosecondsNow. "compactor isCurrentlyCompacting ifFalse: [manager printFreeSpaceStatistics]." startTime := coInterpreter ioUTCMicrosecondsNow. compactionFinished := compactor incrementalCompact. compactTime := compactTime + (coInterpreter ioUTCMicrosecondsNow - startTime). compactionFinished ifTrue: [ coInterpreter cr; print: 'finish compacting '; tab; flush. startTime := coInterpreter ioUTCMicrosecondsNow. manager setCheckForLeaks: GCCheckFreeSpace + GCModeFull; runLeakCheckerFor: GCModeFull; checkFreeSpace: GCModeFull. coInterpreter cr; print: 'time for internal check: '; printNum: coInterpreter ioUTCMicrosecondsNow - startTime; tab; flush. self phase: InMarkingPhase. manager attemptToShrink. coInterpreter postGCAction: GCModeIncremental. manager setHeapSizeAtPreviousGC. compactCount := compactCount + 1. coInterpreter cr; print: 'compact ------> '; printNum: compactTime; print: ' '; printNum: manager oldSpaceSize - manager totalFreeOldSpace; print: ' '; printNum: compactor compactor compactedObjects; print: ' '; printNum: compactor compactor compactedBytes; print: ' '; printNum: compactor compactor reservedObjects; tab; flush. fullGCWanted := false. ^ self]]!
Item was changed: ----- Method: SpurIncrementalGarbageCollector>>initialize (in category 'initialize-release') ----- initialize
super initialize. + + allocatorShouldAllocateBlack := false. checkSetGCFlags := true. phase := InMarkingPhase. phaseCounter := 0. fullGCWanted := false. gcPauseGoal := 5000. shouldCollectCounter := 0. markTime := 0. markCount := 0. sweepTime := 0. sweepCount := 0. compactTime := 0. compactCount := 0. allAtOnceMarker := SpurAllAtOnceMarker new. stopTheWorldGC := SpurStopTheWorldGarbageCollector new. stopTheWorldGC marker: allAtOnceMarker. stopTheWorldGC compactor: SpurPlanningCompactor new!
Item was changed: ----- Method: SpurIncrementalGarbageCollector>>internalPrintGCState (in category 'as yet unclassified') ----- internalPrintGCState
coInterpreter cr; print: 'IGC'; tab; flush. phase = InMarkingPhase ifTrue: [ marker isCurrentlyMarking ifFalse: [coInterpreter print: 'Idle'; cr; flush. ^ self]. coInterpreter print: 'marking'; cr; flush.]. phase = InSweepingPhase ifTrue: [coInterpreter print: 'sweeping'; cr; flush. + coInterpreter print: 'position: '; printHexnp: compactor currentSweepingEntity. + compactor currentSweepingEntity = manager oldSpaceStart + ifTrue: [coInterpreter print: ' (not started)']. + coInterpreter cr; flush.]. - coInterpreter print: 'position: '; printHexnp: compactor currentSweepingEntity; cr; flush.]. phase = InCompactingPhase ifTrue: [coInterpreter print: 'compacting'; cr; flush.] !
Item was changed: ----- Method: SpurIncrementalMarker>>finishMarking (in category 'as yet unclassified') ----- finishMarking "marks the structures needed during GC" <inline: #never> "lets assume there are not too many for now" self markWeaklingsAndMarkAndFireEphemerons. [(manager isEmptyObjStack: manager markStack) not] whileTrue: [self incrementalMark]. self flag: #Todo. "now that all reachable objects got marked we could prune the rememberd set here of unmarked objects" self assert: (manager isEmptyObjStack: manager markStack). "self assert: self allReferencedClassesAreMarked." "self allReferencedClassesAreMarked not ifTrue: [self cCode: 'raise(SIGINT)']." manager expungeDuplicateAndUnmarkedClasses: true ignoringClassesInYoungSpace: true. "Young space weaklings are not included in the weak set here. If weaklings from young space contain references to old space and the object behind it gets freed during sweeping a scavenge can try to access such an object. Therefore collect all young space weaklings here and nil their references (do it in the end to not include not existing weak objects from previous marking passes" self collectWeaklingsFromYoungSpaceInWeakSet. manager nilUnmarkedWeaklingSlotsExcludingYoungObjects: true. + manager gc scavenger forgetUnmarkedRememberedObjects. + self assert: (manager isEmptyObjStack: manager markStack). isCurrentlyMarking := false. marking := false!
Item was changed: ----- Method: SpurIncrementalMarker>>markAndShouldScan: (in category 'marking - incremental') ----- markAndShouldScan: objOop "marks the object (grey or black as neccessary) and returns if the object should be scanned Objects that get handled later on get marked as black, as they are practically a leaf in the object tree (we scan them later on, so we cannot lose objects and do not need to adhere to the tricolor invariant)"
| format | <api> <inline: true> + self isCurrentlyMarking not ifTrue: [manager debugger]. - self isCurrentlyMarking not ifTrue: [self halt. manager debugger]. ((manager isImmediate: objOop) or: [manager isYoungObject: objOop]) ifTrue: [^ false]. self assert: (manager isForwarded: objOop) not.
"if it is marked we already did everything we needed to do and if is grey we already saw it and do not have to do anything here" (manager isWhite: objOop) ifFalse: [^false]. format := manager formatOf: objOop. (manager isPureBitsFormat: format) ifTrue: "avoid pushing non-pointer objects on the markStack." ["Avoid tracing classes of non-objects on the heap, e.g. IRC caches, Sista counters." (manager classIndexOf: objOop) > manager lastClassIndexPun ifTrue: [self markAndTraceClassOf: objOop]. "the object does not need to enter the marking stack as there are no pointer to visit -> it is already finished and we can make it black" self blackenObject: objOop. ^false]. (manager isWeakFormat: format) ifTrue: "push weaklings on the weakling stack to scan later" [manager push: objOop onObjStack: manager weaklingStack. "do not follow weak references. They get scanned at the end of marking -> it should be ok to not follow the tricolor invariant" self blackenObject: objOop. ^false]. ((manager isEphemeronFormat: format) and: [manager activeAndDeferredScan: objOop]) ifTrue: [self blackenObject: objOop. ^false]. "we know it is an object that can contain we have to follow" self pushOnMarkingStackAndMakeGrey: objOop. ^ true!
Item was added: + ----- Method: SpurIncrementalSweepAndCompact>>setSegmentToFillToAddress: (in category 'as yet unclassified') ----- + setSegmentToFillToAddress: segInfo + + <doNotGenerate> + compactor setSegmentToFillToAddress: segInfo!
Item was changed: SpurGarbageCollector subclass: #SpurStopTheWorldGarbageCollector instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'VMMaker-SpurGarbageCollector'! + + !SpurStopTheWorldGarbageCollector commentStamp: 'WoC 7/11/2023 13:30' prior: 0! + A SpurStopTheWorldGarbageCollector is a garbage collector following the stop the world scheme, i.e. the mutator stops and garbage collection + completes before the mutator can resume. Uses the SpurPlanningCompactor for mark-and-compact of the old space. + + + !
vm-dev@lists.squeakfoundation.org