Tom Braun uploaded a new version of VMMaker to project VM Maker: http://source.squeak.org/VMMaker/VMMaker.oscog.seperateMarking-WoC.3332.mcz
==================== Summary ====================
Name: VMMaker.oscog.seperateMarking-WoC.3332 Author: WoC Time: 7 July 2023, 12:22:39.968848 am UUID: 53964fe4-01dc-48e5-8fe5-7b28e075a284 Ancestors: VMMaker.oscog.seperateMarking-eem.3331
- write some more metadata for callgraph export (useful for analysis with external tools)
- added incremental methods for tracing the Cog parts
- added tricolor check trampoline and checks in jitted code (not sure I found all stores)
- added neccessay pragmas for static polymorphism
- fixed bug in compaction (unremember forwarder)
- fixed bug when running gcForSnapshot in incremental collector (remove compaction bits from segments, because after stop-the-world gc these segments are not to be compacted anymore and compaction would cause incorrect freeing)
- helper method for printing gc state during debugging in the C debugger
- fix performance bug in marking (when marking zero sized objects we would not count them towards the count of objects that can be visited. This results in problems if the marker handles a big number of zero sized objects)
=============== Diff against VMMaker.oscog.seperateMarking-eem.3331 ===============
Item was changed: Object subclass: #CCodeGenerator instanceVariableNames: 'vmClass structClasses translationDict asArgumentTranslationDict inlineList constants variables variableDeclarations scopeStack methods macros apiMethods apiVariables kernelReturnTypes currentMethod headerFiles globalVariableUsage useSymbolicConstants generateDeadCode requiredSelectors previousCommentMarksInlining previousCommenter logger suppressAsmLabels asmLabelCounts pools selectorTranslations staticallyResolvedPolymorphicReceivers recursivelyResolvedPolymorphicReceivers optionsDictionary breakSrcInlineSelectors breakDestInlineSelectors breakOnInline vmMaker accessorDepthCache beganInlining mappingForRecursivePolymophism removedForPolymorphism recursivePolymorphicMethodsMap toGenerate classesToBeGenerated polymorphicResolver' + classVariableNames: 'CallGraphExportPath NoRegParmsInAssertVMs VerbosePolymorphismResolution' - classVariableNames: 'NoRegParmsInAssertVMs VerbosePolymorphismResolution' poolDictionaries: 'VMBasicConstants' category: 'VMMaker-Translation to C'!
!CCodeGenerator commentStamp: 'tpr 5/2/2003 14:30' prior: 0! This class oversees the translation of a subset of Smalltalk to C, allowing the comforts of Smalltalk during development and the efficiency and portability of C for the resulting interpreter. See VMMaker for more useful info!
Item was changed: ----- Method: CCodeGenerator>>buildForwardCallGraphFrom:and: (in category 'call graph') ----- buildForwardCallGraphFrom: stackCC and: cogCC
| graph methods cogMethods stackMethods | graph := Dictionary new. self assert: (stackCC methods keys intersection: stackCC apiMethods keys) isEmpty. self assert: (cogCC methods keys intersection: cogCC apiMethods keys) isEmpty. stackMethods := stackCC methods , stackCC apiMethods. cogMethods := cogCC methods , cogCC apiMethods. "there is some overlap between the two. As I theorize in this case the cog variants get generated we take these ones" methods := stackMethods , cogMethods.
stackMethods do: [:m | m parseTree nodesWithParentsDo: [:node :parent| node isSend ifTrue: [ stackMethods at: node selector ifPresent: [:innerMethod | graph + at: m + ifPresent: [:set | set add: innerMethod] + ifAbsentPut: [Set with: innerMethod]]]]]. - at: m definingClass -> m selector - ifPresent: [:set | set add: innerMethod definingClass -> innerMethod selector] - ifAbsentPut: [Set with: innerMethod definingClass -> innerMethod selector]]]]]. cogMethods do: [:m | m parseTree nodesWithParentsDo: [:node :parent| node isSend ifTrue: [ methods at: node selector ifPresent: [:innerMethod | graph + at: m + ifPresent: [:set | set add: innerMethod] + ifAbsentPut: [Set with: innerMethod]]]]]. - at: m definingClass -> m selector - ifPresent: [:set | set add: innerMethod definingClass -> innerMethod selector] - ifAbsentPut: [Set with: innerMethod definingClass -> innerMethod selector]]]]].
^ graph!
Item was changed: ----- Method: CCodeGenerator>>createAndWriteCallGraph (in category 'call graph') ----- createAndWriteCallGraph
+ | vmm cg cogCg callGraph cogCallGraph sharedKeys graph | - | vmm cg | vmm := (VMMaker forPlatform: 'Cross') + interpreterClass: CoInterpreter; - interpreterClass: StackInterpreter; options: #(ObjectMemory Spur64BitCoMemoryManager + TempVectReadBarrier false + Cogit StackToRegisterMappingCogit + gcClass SpurIncrementalGarbageCollector). - TempVectReadBarrier false). cg := [vmm buildCodeGeneratorForInterpreter] on: Notification do: [:ex| ex tag == #getVMMaker ifTrue: [ex resume: vmm] ifFalse: [ex pass]].
cg vmClass preGenerationHook: cg. cg inferTypesForImplicitlyTypedVariablesAndMethods. cg prepareMethods. + + cogCg := (VMMaker forPlatform: 'Cross') + interpreterClass: CoInterpreter; + options: #(ObjectMemory Spur64BitCoMemoryManager + ISA ARMv8 + TempVectReadBarrier true + MULTIPLEBYTECODESETS true) , {#Cogit. #StackToRegisterMappingCogit}; + buildCodeGeneratorForCogit. + + cogCg vmClass preGenerationHook: cogCg. + cogCg inferTypesForImplicitlyTypedVariablesAndMethods. + cogCg retainMethods: { #compactCogCompiledCode }. + cogCg prepareMethods.
+ callGraph := cg buildForwardCallGraph. + cogCallGraph := cogCg buildForwardCallGraph. + + + graph := Dictionary new. + sharedKeys := callGraph keys intersection: cogCallGraph keys. + + callGraph keysAndValuesDo: [:key :value | (sharedKeys includes: key) ifFalse: [graph at: key put: value]]. + cogCallGraph keysAndValuesDo: [:key :value | (sharedKeys includes: key) ifFalse: [graph at: key put: value]]. + + sharedKeys do: [:ea | graph at: ea put: ((callGraph at: ea) , (cogCallGraph at: ea))]. + + self halt. + + cg writeCallGraphCSV: graph! - cg writeCallGraphCSV: cg buildCallGraphForGC!
Item was changed: ----- Method: CCodeGenerator>>createAndWriteCogCallGraph (in category 'call graph') ----- createAndWriteCogCallGraph
| vmm cg cogCg graph | vmm := (VMMaker forPlatform: 'Cross') interpreterClass: CoInterpreter; options: #(ObjectMemory Spur64BitCoMemoryManager + TempVectReadBarrier false - TempVectReadBarrier true Cogit StackToRegisterMappingCogit gcClass SpurIncrementalGarbageCollector). cg := [vmm buildCodeGeneratorForInterpreter] on: Notification do: [:ex| ex tag == #getVMMaker ifTrue: [ex resume: vmm] ifFalse: [ex pass]].
cg vmClass preGenerationHook: cg. cg inferTypesForImplicitlyTypedVariablesAndMethods. cg prepareMethods. cogCg := (VMMaker forPlatform: 'Cross') interpreterClass: CoInterpreter; options: #(ObjectMemory Spur64BitCoMemoryManager ISA ARMv8 + TempVectReadBarrier false + gcClass SpurIncrementalGarbageCollector - TempVectReadBarrier true MULTIPLEBYTECODESETS true) , {#Cogit. #StackToRegisterMappingCogit}; instVarNamed: #sharedResolver put: cg polymorphicResolverForAdoption; buildCodeGeneratorForCogit. cogCg vmClass preGenerationHook: cogCg. cogCg inferTypesForImplicitlyTypedVariablesAndMethods. cogCg retainMethods: { #compactCogCompiledCode }. cogCg prepareMethods. graph := self buildForwardCallGraphFrom: cg and: cogCg. cg writeCallGraphCSV: graph!
Item was changed: ----- Method: CCodeGenerator>>ifStaticallyResolvedPolymorphicReceiverThenUpdateSelectorIn:fromMethod:in: (in category 'public') ----- ifStaticallyResolvedPolymorphicReceiverThenUpdateSelectorIn: aSendNode fromMethod: aTMethod in: aClass "We allow a limited amount of polymorphism; if a class chooses, its selectors can be prefixed with a given string to disambiguate. This hack allows us to use two different compaction algorithms with the same API at the same time; the selection being done by a class which holds the flag stating which algorithm is in effect at the current time." + polymorphicResolver isNeeded ifFalse: [^self]. - polymorphicResolver isNeeded ifNil: [^self]. polymorphicResolver staticallyResolvePolymorphicReceiverThenUpdateSelectorIn: aSendNode fromMethod: aTMethod in: aClass!
Item was changed: ----- Method: CCodeGenerator>>writeCallGraphCSV: (in category 'call graph') ----- writeCallGraphCSV: connections
FileStream + fileNamed: (CallGraphExportPath ifNil: [CallGraphExportPath := (UIManager default + request: 'Please enter the path you want to store the output file' + initialAnswer: FileDirectory default fullName + onCancelReturn: nil) ifNil: [UIManager default inform: 'Cancelled storing call graph. Please provide a path'.^ self]]) , '/output.csv' - fileNamed: 'output.csv' do: [:file| file nextPutAll: 'caller'; tab; nextPutAll: 'callerClass'; tab; nextPutAll: 'callerMethod'; tab; nextPutAll: 'callerCategory'; tab; + nextPutAll: 'resolvedFor'; + tab; + nextPutAll: 'isPolymorphicBase'; + tab; + nextPutAll: 'isPolymorphic'; + tab; + nextPutAll: 'receiverClass'; + tab; + nextPutAll: 'touchedByPolymorphism'; + tab; nextPutAll: 'callee'; tab; nextPutAll: 'calleeClass'; tab; nextPutAll: 'calleeMethod'; tab; nextPutAll: 'calleeCategory'; cr; lf.
connections keysAndValuesDo: [:caller :callees | "self halt." callees do: [:callee | file + nextPutAll: (caller definingClass -> caller selector) asString; - nextPutAll: caller asString; tab; + nextPutAll: caller definingClass asString; - nextPutAll: caller key asString; tab; + nextPutAll: caller selector asString; - nextPutAll: caller value asString; tab; + nextPutAll: caller definingClass category asString; - nextPutAll: caller key category asString; tab; + nextPutAll: (caller resolvedFor ifNil: ['none'] ifNotNil: [caller resolvedFor asString]); - nextPutAll: callee asString; tab; + nextPutAll: caller isPolymorphicBase asString; - nextPutAll: callee key asString; tab; + nextPutAll: caller isPolymorphic asString; - nextPutAll: callee value asString; tab; + nextPutAll: (caller isPolymorphic ifTrue: [caller receiverClass asString] ifFalse: ['none']); + tab; + nextPutAll: ((caller isPolymorphic or: [caller isPolymorphicBase or: [caller resolvedFor notNil]] )ifTrue: ['true'] ifFalse: ['false']); + tab; + nextPutAll: (callee definingClass -> callee selector) asString; + tab; + nextPutAll: callee definingClass asString; + tab; + nextPutAll: callee selector asString; + tab; + nextPutAll: callee definingClass category asString; - nextPutAll: callee key category asString; cr; lf]]].
^ connections!
Item was changed: ----- Method: CoInterpreter class>>declareCVarsIn: (in category 'translation') ----- declareCVarsIn: aCCodeGenerator "Override to avoid repeating StackInterpreter's declarations and add our own extensions" self class == thisContext methodClass ifFalse: [^self]. "Don't duplicate decls in subclasses" aCCodeGenerator addHeaderFile:'"sqCogStackAlignment.h"'; addHeaderFile:'"cogmethod.h"'. NewspeakVM ifTrue: [aCCodeGenerator addHeaderFile:'"nssendcache.h"']. aCCodeGenerator addHeaderFile: (aCCodeGenerator vmClass isThreadedVM ifTrue: ['"cointerpmt.h"'] ifFalse: ['"cointerp.h"']); addHeaderFile:'"cogit.h"'. aCCodeGenerator vmClass declareInterpreterVersionIn: aCCodeGenerator defaultName: aCCodeGenerator interpreterVersion. aCCodeGenerator var: #cogCodeSize type: #usqInt; var: #heapBase type: #usqInt; var: #statCodeCompactionUsecs type: #usqLong; var: #maxLiteralCountForCompile declareC: 'sqInt maxLiteralCountForCompile = MaxLiteralCountForCompile /* ', MaxLiteralCountForCompile printString, ' */'; var: #minBackwardJumpCountForCompile declareC: 'sqInt minBackwardJumpCountForCompile = MinBackwardJumpCountForCompile /* ', MinBackwardJumpCountForCompile printString, ' */'. aCCodeGenerator removeVariable: 'atCache'; "Way too much trouble than it's worth in the Cog VM" removeVariable: 'reenterInterpreter'. "We can use the JIT and CFrame/StrackPointer for a lighter-weight solution." aCCodeGenerator removeVariable: #primitiveAccessorDepthTable. aCCodeGenerator vmClass objectMemoryClass hasSpurMemoryManagerAPI ifTrue: [aCCodeGenerator var: #primitiveMetadataTable type: 'signed short' sizeString: 'MaxPrimitiveIndex + 2 /* ', (MaxPrimitiveIndex + 2) printString, ' */' array: (aCCodeGenerator vmClass primitiveMetadataTableUsing: aCCodeGenerator)] ifFalse: [aCCodeGenerator removeVariable: #primitiveMetadataTable]. aCCodeGenerator var: #primTraceLogIndex type: #'unsigned char'; var: #primTraceLog declareC: 'sqInt primTraceLog[256]'; var: #traceLog declareC: 'sqInt traceLog[TraceBufferSize /* ', TraceBufferSize printString, ' */]'; var: 'primTracePluginName' type: #'char *'; var: #traceSources type: #'char *' array: TraceSources. aCCodeGenerator var: #CFramePointer type: #'volatile usqIntptr_t'; var: #CStackPointer type: #'volatile usqIntptr_t'; var: #CReturnAddress type: #'volatile usqIntptr_t'. SpurMemoryManager wantsIncrementalGC ifTrue: [ aCCodeGenerator + recursivelyResolvePolymorpicReceiver: 'objectMemory' toVariants: {SpurIncrementalGarbageCollector. SpurStopTheWorldGarbageCollector} in: self default: SpurIncrementalGarbageCollector forMethodList: #(mapInterpreterOops mapStackPages mapVMRegisters mapProfileState postBecomeAction:) - recursivelyResolvePolymorpicReceiver: 'objectMemory' toVariants: {SpurIncrementalGarbageCollector. SpurStopTheWorldGarbageCollector} in: self default: SpurIncrementalGarbageCollector forMethodList: #(mapInterpreterOops mapStackPages mapVMRegisters mapProfileState) ]!
Item was added: + ----- Method: CoInterpreter>>freeUnmarkedMachineCodeForIncrementalGC (in category 'object memory support') ----- + freeUnmarkedMachineCodeForIncrementalGC + "Free machine-code methods whose compiled methods are unmarked + (or open PICs whose selectors are not marked). + The stack pages have already been traced so any methods + of live stack activations have already been marked and traced." + <doNotGenerate> + cogit freeUnmarkedMachineCodeForIncrementalGC!
Item was changed: ----- Method: CoInterpreter>>incrementalMarkAndTraceMachineCodeMethod: (in category 'gc -- mark and sweep') ----- incrementalMarkAndTraceMachineCodeMethod: aCogMethod <var: #aCogMethod type: #'CogBlockMethod *'> + <declareTypeForStaticPolymorphism: #SpurIncrementalGarbageCollector> - <declareTypeForStaticPolymorphism: #SpurStopTheWorldGarbageCollector> | homeMethod | <var: #homeMethod type: #'CogMethod *'> homeMethod := self asCogHomeMethod: aCogMethod. + objectMemory marker markAndScan: homeMethod methodObject! - objectMemory pushOnMarkingStackAndMakeGrey: homeMethod methodObject!
Item was added: + ----- Method: CoInterpreter>>incrementalMarkAndTraceMachineCodeOfMarkedMethods (in category 'object memory support') ----- + incrementalMarkAndTraceMachineCodeOfMarkedMethods + "Deal with a fulGC's effects on machine code. Mark and + trace oops in marked machine code methods. The stack + pages have already been traced so any methods of live + stack activations have already been marked and traced." + <doNotGenerate> + cogit incrementalMarkAndTraceMachineCodeOfMarkedMethods!
Item was changed: ----- Method: CoInterpreter>>incrementalMarkAndTraceStackPage: (in category 'object memory support') ----- incrementalMarkAndTraceStackPage: thePage <var: #thePage type: #'StackPage *'> | theSP theFP frameRcvrOffset callerFP oop marker | <staticallyResolveReceiver: 'marker' to: #SpurIncrementalMarker> <inline: false> "do not remove. Necessary for resolving polymorphic receiver" marker := objectMemory marker.
self assert: (stackPages isFree: thePage) not. self assert: (self ifCurrentStackPageHasValidHeadPointers: thePage). self assert: thePage trace ~= StackPageTraced. thePage trace: StackPageTraced.
theSP := thePage headSP. theFP := thePage headFP. "Skip the instruction pointer on top of stack of inactive pages." thePage = stackPage ifFalse: [theSP := theSP + objectMemory wordSize]. [frameRcvrOffset := self frameReceiverLocation: theFP. [theSP <= frameRcvrOffset] whileTrue: [oop := stackPages longAt: theSP. (objectMemory isOopForwarded: oop) ifTrue: [oop := objectMemory followForwarded: oop. stackPages longAt: theSP put: oop]. marker markAndScan: oop. theSP := theSP + objectMemory wordSize]. (self frameHasContext: theFP) ifTrue: [self assert: (objectMemory isContext: (self frameContext: theFP)). marker markAndScan: (self frameContext: theFP)]. (self isMachineCodeFrame: theFP) + ifTrue: [self incrementalMarkAndTraceMachineCodeMethod: (self mframeCogMethod: theFP)] - ifTrue: [self markAndTraceMachineCodeMethod: (self mframeCogMethod: theFP)] ifFalse: [marker markAndScan: (self iframeMethod: theFP)]. (callerFP := self frameCallerFP: theFP) ~= 0] whileTrue: [theSP := theFP + FoxCallerSavedIP + objectMemory wordSize. theFP := callerFP]. theSP := theFP + FoxCallerSavedIP + objectMemory wordSize. "caller ip is ceBaseReturnPC" [theSP <= thePage baseAddress] whileTrue: [oop := stackPages longAt: theSP. (objectMemory isOopForwarded: oop) ifTrue: [oop := objectMemory followForwarded: oop. stackPages longAt: theSP put: oop]. marker markAndScan: oop. theSP := theSP + objectMemory wordSize]. self deny: (self whiteOrBogusGreyObjectsOnStackPage: thePage)!
Item was changed: ----- Method: CoInterpreter>>incrementalMarkAndTraceTraceLog (in category 'object memory support') ----- incrementalMarkAndTraceTraceLog "The trace log is a circular buffer of pairs of entries. If there is an entry at traceLogIndex - 3 \ TraceBufferSize it has entries. If there is something at traceLogIndex it has wrapped." <inline: false> + <staticallyResolveReceiver: 'objectMemory marker' to: #SpurIncrementalMarker> | limit | limit := self safe: traceLogIndex - 3 mod: TraceBufferSize. (traceLog at: limit) = 0 ifTrue: [^self]. (traceLog at: traceLogIndex) ~= 0 ifTrue: [limit := TraceBufferSize - 3]. 0 to: limit by: 3 do: [:i| | oop | oop := traceLog at: i. (objectMemory isImmediate: oop) ifFalse: + [objectMemory marker markAndScan: oop]. - [objectMemory markAndScan: oop]. oop := traceLog at: i + 1. (objectMemory isImmediate: oop) ifFalse: + [objectMemory marker markAndScan: oop]]! - [objectMemory markAndScan: oop]]!
Item was changed: ----- Method: CoInterpreter>>mapVMRegisters (in category 'object memory support') ----- mapVMRegisters "Map the oops in the interpreter's vm ``registers'' to their new values during garbage collection or a become: operation." "Assume: All traced variables contain valid oops. N.B. Don't trace messageSelector and lkupClass; these are ephemeral, live only during message lookup and because createActualMessageTo will not cause a GC these cannot change during message lookup. c.f. followMethodNewMethodAndInstructionPointer. Override to relocate the instructionPointer relative to newMethod, if required. In the Cogit the VM may be in a machine code primitive when the GC is invoked. However, because compaction moves several objects, it is possible for the compacted method to overlap the pre-compacted newMethod and hence there's a danger of updating instructionPointer twice." - <declareTypeForStaticPolymorphism: #SpurStopTheWorldGarbageCollector> | ipdelta | (objectMemory shouldRemapObj: method) ifTrue: [ipdelta := (self method: method includesAddress: instructionPointer) ifTrue: [instructionPointer - method]. method := objectMemory remapObj: method. ipdelta ifNotNil: [instructionPointer := method + ipdelta]]. (objectMemory shouldRemapOop: newMethod) ifTrue: "maybe oop due to object-as-method" [ipdelta := (ipdelta isNil "don't relocate twice!!!!" and: [self method: newMethod includesAddress: instructionPointer]) ifTrue: [instructionPointer - newMethod]. newMethod := objectMemory remapObj: newMethod. ipdelta ifNotNil: [instructionPointer := newMethod + ipdelta]]!
Item was added: + ----- Method: CoInterpreter>>markAndTraceMachineCodeOfMarkedMethodsForIncrementalGC (in category 'object memory support') ----- + markAndTraceMachineCodeOfMarkedMethodsForIncrementalGC + "Deal with a fulGC's effects on machine code. Mark and + trace oops in marked machine code methods. The stack + pages have already been traced so any methods of live + stack activations have already been marked and traced." + <doNotGenerate> + cogit markAndTraceMachineCodeOfMarkedMethodsForIncrementalGC!
Item was changed: ----- Method: CoInterpreterMT>>incrementalMarkAndTraceInterpreterOops (in category 'object memory support') ----- incrementalMarkAndTraceInterpreterOops "Override to mark the awolProcesses" <var: #vmThread type: #'CogVMThread *'> "do not remove. Necessary for resolving polymorphic receiver" | marker | marker := objectMemory marker.
super incrementalMarkAndTraceInterpreterOops.
"Per-thread state; trace each thread's own newMethod and stack of awol processes." 1 to: cogThreadManager getNumThreads do: [:i| | vmThread | vmThread := cogThreadManager vmThreadAt: i. vmThread state ifNotNil: [vmThread newMethodOrNull ifNotNil: + [marker markAndScan: vmThread newMethodOrNull]. - [marker pushOnMarkingStackAndMakeGrey: vmThread newMethodOrNull]. 0 to: vmThread awolProcIndex - 1 do: + [:j| marker markAndScan: (vmThread awolProcesses at: j)]]]! - [:j| marker pushOnMarkingStackAndMakeGrey: (vmThread awolProcesses at: j)]]]!
Item was changed: ----- Method: CogObjectRepresentationFor32BitSpur>>incrementalMarkAndTraceCacheTagLiteral:in:atpc: (in category 'garbage collection') ----- incrementalMarkAndTraceCacheTagLiteral: literal in: cogMethodOrNil atpc: address "Mark and trace a literal in an inline cache preceding address in cogMethodOrNil. Answer if code was modified." <var: #cogMethodOrNil type: #'CogMethod *'> <var: #address type: #usqInt> | objOop | (self couldBeObject: literal) ifFalse: [^false]. self assert: (objectMemory addressCouldBeObj: literal). (objectMemory isForwarded: literal) ifFalse: + [objectMemory markAndScan: literal. - [objectMemory pushOnMarkingStackAndMakeGrey: literal. ^false]. objOop := objectMemory followForwarded: literal. self setCodeModified. cogit backEnd rewriteInlineCacheTag: objOop at: address. self incrementalMarkAndTraceUpdatedLiteral: objOop in: cogMethodOrNil. ^true!
Item was added: + ----- Method: CogObjectRepresentationFor64BitSpur>>genCheckMarkBitOf:scratch: (in category 'as yet unclassified') ----- + genCheckMarkBitOf: objReg scratch: scratchReg + "Check the remembered bit of the object in objReg; answer the jump taken if the bit is already set. + Only need to fetch the byte containing it, which reduces the size of the mask constant." + | markBitByteOffset mask | + markBitByteOffset := cogit backEnd isBigEndian + ifTrue: [objectMemory baseHeaderSize - 1 - (objectMemory markedBitFullShift // 8)] + ifFalse:[objectMemory markedBitFullShift // 8]. + mask := 1 << (objectMemory markedBitFullShift \ 8). + cogit MoveMb: markBitByteOffset r: objReg R: scratchReg. + cogit TstCq: mask R: scratchReg. + ^cogit JumpZero: 0!
Item was added: + ----- Method: CogObjectRepresentationFor64BitSpur>>incrementalMarkAndTraceCacheTagLiteral:in:atpc: (in category 'garbage collection') ----- + incrementalMarkAndTraceCacheTagLiteral: literal in: cogMethodOrNil atpc: address + "Mark and trace a literal in an inline cache preceding address in cogMethodOrNil. + Answer if code was modified. In 64-bit Spur, cache tags are either selector + indices or class indices and so this is a noop." + <var: #cogMethodOrNil type: #'CogMethod *'> + <var: #address type: #usqInt> + <inline: true> + ^false!
Item was changed: CogObjectRepresentation subclass: #CogObjectRepresentationForSpur + instanceVariableNames: 'ceTricolorCheckTrampoline ceScheduleScavengeTrampoline ceSmallActiveContextInMethodTrampoline ceSmallActiveContextInBlockTrampoline ceSmallActiveContextInFullBlockTrampoline ceLargeActiveContextInMethodTrampoline ceLargeActiveContextInBlockTrampoline ceLargeActiveContextInFullBlockTrampoline ceStoreCheckContextReceiverTrampoline ceStoreTrampolines ceNewHashTrampoline ceInlineNewHashTrampoline' - instanceVariableNames: '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>>declareCVarsIn: (in category 'translation') ----- declareCVarsIn: aCodeGen aCodeGen var: #ceStoreTrampolines declareC: '#if IMMUTABILITY\sqInt ceStoreTrampolines[', NumStoreTrampolines printString, '];#endif'. SpurMemoryManager wantsIncrementalGC ifTrue: [ - "0 halt." aCodeGen recursivelyResolvePolymorpicReceiver: 'objectMemory' toVariants: {SpurIncrementalGarbageCollector. SpurStopTheWorldGarbageCollector} in: self default: SpurIncrementalGarbageCollector forMethodList: #(remapObject: remapOop:)]!
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: [1] ifFalse: [0])!
Item was added: + ----- Method: CogObjectRepresentationForSpur>>genCheckMarkBitOf:scratch: (in category 'compile abstract instructions') ----- + genCheckMarkBitOf: objReg scratch: scratchReg + + ^ self shouldBeImplemented!
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 | - | jmpImmediate jmpDestYoung jmpSourceOld jmpAlreadyRemembered | <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: destReg = ReceiverResultReg. + cogit + evaluateTrampolineCallBlock: [cogit CallRT: ceTricolorCheckTrampoline] + protectLinkRegIfNot: inFrame]. + + (jmpImmediate jmpTarget: + (jmpDestYoung jmpTarget: cogit Label)). - jmpImmediate jmpTarget: - (jmpDestYoung jmpTarget: - (jmpSourceOld 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]. - [jmpAlreadyRemembered jmpTarget: jmpSourceOld 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 | - | jumpImmutable jumpRC | <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: ReceiverResultReg + arg: nil + arg: nil + arg: nil + regsToSave: cogit emptyRegisterMask + pushLinkReg: true + 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 | - | jumpImmutable pushLinkReg | <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: 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 changed: ----- Method: CogObjectRepresentationForSpur>>genStoreWithImmutabilityAndStoreCheckSourceReg:slotIndex:destReg:scratchReg:needRestoreRcvr: (in category 'compile abstract instructions') ----- genStoreWithImmutabilityAndStoreCheckSourceReg: sourceReg slotIndex: index destReg: destReg scratchReg: scratchReg needRestoreRcvr: needRestoreRcvr "Store check code is duplicated to use a single trampoline" <option: #IMMUTABILITY> + | immutableJump jmpImmediate jmpDestYoung jmpSourceOld jmpAlreadyRemembered jmpNotMarked jmpNotMarking jmpYoungBranch | - | immutableJump jmpImmediate jmpDestYoung jmpSourceOld jmpAlreadyRemembered | <var: #immutableJump type: #'AbstractInstruction *'> <var: #jmpImmediate type: #'AbstractInstruction *'> <var: #jmpDestYoung type: #'AbstractInstruction *'> <var: #jmpSourceOld type: #'AbstractInstruction *'> <var: #jmpAlreadyRemembered type: #'AbstractInstruction *'> + <staticallyResolveReceiver: 'objectMemory gc' to: #SpurIncrementalGarbageCollector>
immutableJump := self genJumpImmutable: destReg scratchReg: scratchReg. cogit genTraceStores. "do the store" cogit MoveR: sourceReg Mw: index * objectMemory wordSize + objectMemory baseHeaderSize r: destReg. "store check" jmpImmediate := self genJumpImmediate: sourceReg. "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: sourceReg. "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]. + + 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]. + + "Set the inst var index for the benefit of the immutability check. The trampoline will repeat the check to choose between the immutbality violation and the store check." immutableJump jmpTarget: cogit Label. + objectMemory gc isIncremental + ifTrue: [jmpYoungBranch jmpTarget: immutableJump getJmpTarget]. self genStoreTrampolineCall: index. cogit voidReceiverOptStatus. needRestoreRcvr ifTrue: [cogit putSelfInReceiverResultReg].
jmpImmediate jmpTarget: (jmpDestYoung jmpTarget: + cogit Label). - (jmpSourceOld jmpTarget: - cogit Label)). CheckRememberedInTrampoline ifFalse: + [jmpAlreadyRemembered jmpTarget: jmpDestYoung getJmpTarget]. + objectMemory gc isIncremental + ifTrue: [ + jmpNotMarked jmpTarget: + (jmpNotMarking jmpTarget: jmpDestYoung getJmpTarget)] + ifFalse: [jmpSourceOld jmpTarget: jmpDestYoung getJmpTarget]. - [jmpAlreadyRemembered jmpTarget: jmpSourceOld getJmpTarget]. ^ 0!
Item was added: + ----- 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 bitClear: (cogit registerMaskFor: ReceiverResultReg)) + 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: [ceTricolorCheckTrampoline := self genTricolorCheckTrampoline]. 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: ----- Method: CogObjectRepresentationForSpur>>incrementalMarkAndTraceLiteral: (in category 'incremental garbage collection') ----- incrementalMarkAndTraceLiteral: literal + + <staticallyResolveReceiver: 'objectMemory marker' to: #SpurIncrementalMarker> (self couldBeObject: literal) ifTrue: [self assert: (objectMemory addressCouldBeObj: literal). + objectMemory marker markAndScan: literal]! - objectMemory gc pushOnMarkingStackAndMakeGrey: literal]!
Item was changed: ----- Method: CogObjectRepresentationForSpur>>incrementalMarkAndTraceLiteral:in:at: (in category 'incremental garbage collection') ----- incrementalMarkAndTraceLiteral: literal in: cogMethod at: address "Mark and trace a literal in a sqInt variable of cogMethod." <var: #cogMethod type: #'CogMethod *'> <var: #address type: #'sqInt *'> + <staticallyResolveReceiver: 'objectMemory marker' to: #SpurIncrementalMarker> | objOop | (self couldBeObject: literal) ifFalse: [^self]. self assert: (objectMemory addressCouldBeObj: literal). (objectMemory isForwarded: literal) ifFalse: + [objectMemory marker markAndScan: literal. - [objectMemory gc pushOnMarkingStackAndMakeGrey: literal. ^self]. objOop := objectMemory followForwarded: literal. address at: 0 put: objOop. self incrementalMarkAndTraceUpdatedLiteral: objOop in: cogMethod!
Item was changed: ----- Method: CogObjectRepresentationForSpur>>incrementalMarkAndTraceLiteral:in:atpc: (in category 'incremental garbage collection') ----- incrementalMarkAndTraceLiteral: literal in: cogMethodOrNil atpc: address "Mark and trace a literal in a machine code instruction preceding address in cogMethodOrNil. Answer if code was modified." <var: #cogMethodOrNil type: #'CogMethod *'> <var: #address type: #usqInt> + <staticallyResolveReceiver: 'objectMemory marker' to: #SpurIncrementalMarker> | objOop | (self couldBeObject: literal) ifFalse: [^false]. self assert: (objectMemory addressCouldBeObj: literal). (objectMemory isForwarded: literal) ifFalse: + [objectMemory marker markAndScan: literal. - [objectMemory pushOnMarkingStackAndMakeGrey: literal. ^false]. cogit setCodeModified. objOop := objectMemory followForwarded: literal. cogit backEnd storeLiteral: objOop beforeFollowingAddress: address. self incrementalMarkAndTraceUpdatedLiteral: objOop in: cogMethodOrNil. ^true!
Item was changed: ----- Method: CogObjectRepresentationForSpur>>incrementalMarkAndTraceLiteralIfYoung: (in category 'incremental garbage collection') ----- incrementalMarkAndTraceLiteralIfYoung: literal + + <staticallyResolveReceiver: 'objectMemory marker' to: #SpurIncrementalMarker> ((self couldBeObject: literal) and: [objectMemory isYoungObject: literal]) ifTrue: [self assert: (objectMemory addressCouldBeObj: literal). + objectMemory marker markAndScan: literal]! - objectMemory pushOnMarkingStackAndMakeGrey: literal]!
Item was changed: ----- Method: CogObjectRepresentationForSpur>>incrementalMarkAndTraceUpdatedLiteral:in: (in category 'incremental garbage collection') ----- incrementalMarkAndTraceUpdatedLiteral: objOop in: cogMethodOrNil "Common code to mark a literal in cogMethod and add the cogMethod to youngReferrers if the literal is young." + <staticallyResolveReceiver: 'objectMemory marker' to: #SpurIncrementalMarker> <var: #cogMethodOrNil type: #'CogMethod *'> (objectMemory isNonImmediate: objOop) ifTrue: [(cogMethodOrNil notNil and: [objectMemory isYoungObject: objOop]) ifTrue: [methodZone ensureInYoungReferrers: cogMethodOrNil]. + objectMemory marker markAndScan: objOop]! - objectMemory pushOnMarkingStackAndMakeGrey: objOop]!
Item was changed: ----- Method: Cogit class>>declareCVarsIn: (in category 'translation') ----- declareCVarsIn: aCCodeGenerator | backEnd | backEnd := CogCompilerClass basicNew. #( 'coInterpreter' 'objectMemory' 'methodZone' 'objectRepresentation' 'cogBlockMethodSurrogateClass' 'cogMethodSurrogateClass' 'nsSendCacheSurrogateClass' 'threadManager' 'processor' 'lastNInstructions' 'simulatedAddresses' 'ioHighResClock' 'simulatedTrampolines' 'simulatedVariableGetters' 'simulatedVariableSetters' 'processorFrameValid' 'printRegisters' 'printInstructions' 'clickConfirm' 'clickStepping' 'singleStep' 'codeZoneIsExecutableNotWritable' 'debugAPISelector' 'shortCutTrampolineBlocks' 'perMethodProfile' 'instructionProfile') do: [:simulationVariableUnusedByRealVM| aCCodeGenerator removeVariable: simulationVariableUnusedByRealVM]. NewspeakVM ifFalse: [#( 'selfSendTrampolines' 'dynamicSuperSendTrampolines' 'implicitReceiverSendTrampolines' 'outerSendTrampolines' 'ceEnclosingObjectTrampoline' 'numIRCs' 'indexOfIRC' 'theIRCs') do: [:variableNotNeededInNormalVM| aCCodeGenerator removeVariable: variableNotNeededInNormalVM]]. aCCodeGenerator removeConstant: #COGMTVM. "this should be defined at compile time" "N.B. We *do not* include sq.h; it pulls in conflicting definitions now that sqVirtualMachine.h declares cointerp's functions, and declares some of them inaccurately for histrical reasons. We pull in CoInterpreter's api via cointerp.h which is accurate." aCCodeGenerator addHeaderFile:'"sqConfig.h"'; "config.h must be first on linux" addHeaderFile:'<stddef.h>'; "for e.g. offsetof" addHeaderFile:'<stdio.h>'; addHeaderFile:'<stdlib.h>'; addHeaderFile:'<string.h>'; addHeaderFile:'"sqPlatformSpecific.h"'; "e.g. solaris overrides things for sqCogStackAlignment.h" addHeaderFile:'"sqMemoryAccess.h"'; addHeaderFile:'"sqCogStackAlignment.h"'; addHeaderFile:'"dispdbg.h"'; "must precede cointerp.h & cogit.h otherwise NoDbgRegParms gets screwed up" addHeaderFile:'"cogmethod.h"'. NewspeakVM ifTrue: [aCCodeGenerator addHeaderFile:'"nssendcache.h"']. aCCodeGenerator addHeaderFile:'#if COGMTVM'; addHeaderFile:'"cointerpmt.h"'; addHeaderFile:'#else'; addHeaderFile:'"cointerp.h"'; addHeaderFile:'#endif'; addHeaderFile:'"cogit.h"'. aCCodeGenerator var: #ceGetFP declareC: 'usqIntptr_t (*ceGetFP)(void)'; var: #ceGetSP declareC: 'usqIntptr_t (*ceGetSP)(void)'; var: #ceCaptureCStackPointers declareC: 'void (*ceCaptureCStackPointers)(void)'; var: #ceInvokeInterpret declareC: 'void (*ceInvokeInterpret)(void)'; var: #ceEnterCogCodePopReceiverReg declareC: 'void (*ceEnterCogCodePopReceiverReg)(void)'; var: #realCEEnterCogCodePopReceiverReg declareC: 'void (*realCEEnterCogCodePopReceiverReg)(void)'; var: #ceCallCogCodePopReceiverReg declareC: 'void (*ceCallCogCodePopReceiverReg)(void)'; var: #realCECallCogCodePopReceiverReg declareC: 'void (*realCECallCogCodePopReceiverReg)(void)'; var: #ceCallCogCodePopReceiverAndClassRegs declareC: 'void (*ceCallCogCodePopReceiverAndClassRegs)(void)'; var: #realCECallCogCodePopReceiverAndClassRegs declareC: 'void (*realCECallCogCodePopReceiverAndClassRegs)(void)'; var: #postCompileHook declareC: 'void (*postCompileHook)(CogMethod *)'; var: #openPICList declareC: 'CogMethod *openPICList = 0'; var: #maxMethodBefore type: #'CogBlockMethod *'; var: 'enumeratingCogMethod' type: #'CogMethod *'. aCCodeGenerator var: #ceTryLockVMOwner declareC: '#if COGMTVM\usqIntptr_t (*ceTryLockVMOwner)(usqIntptr_t)#endif'.
backEnd numICacheFlushOpcodes > 0 ifTrue: [aCCodeGenerator var: #ceFlushICache declareC: 'static void (*ceFlushICache)(usqIntptr_t from, usqIntptr_t to)']. aCCodeGenerator var: #ceFlushDCache declareC: '#if DUAL_MAPPED_CODE_ZONE\static void (*ceFlushDCache)(usqIntptr_t from, usqIntptr_t to)#endif'; var: #codeToDataDelta declareC: '#if DUAL_MAPPED_CODE_ZONE\static sqInt codeToDataDelta#else# define codeToDataDelta 0#endif'; var: #cFramePointerInUse declareC: '#if !!defined(cFramePointerInUse)\sqInt cFramePointerInUse#endif'.
aCCodeGenerator declareVar: 'aMethodLabel' type: #'AbstractInstruction'; "Has to come lexicographically before backEnd & methodLabel" var: #backEnd declareC: 'AbstractInstruction * const backEnd = &aMethodLabel'; var: #methodLabel declareC: 'AbstractInstruction * const methodLabel = &aMethodLabel'. self declareC: #(abstractOpcodes stackCheckLabel blockEntryLabel blockEntryNoContextSwitch stackOverflowCall sendMiss entry noCheckEntry selfSendEntry dynSuperEntry fullBlockNoContextSwitchEntry fullBlockEntry picMNUAbort picInterpretAbort endCPICCase0 endCPICCase1 cPICEndOfCodeLabel) as: #'AbstractInstruction *' in: aCCodeGenerator. aCCodeGenerator declareVar: #cPICPrototype type: #'CogMethod *'; declareVar: #blockStarts type: #'BlockStart *'; declareVar: #fixups type: #'BytecodeFixup *'; declareVar: #methodZoneBase type: #usqInt. aCCodeGenerator var: #ordinarySendTrampolines declareC: 'sqInt ordinarySendTrampolines[NumSendTrampolines]'; var: #superSendTrampolines declareC: 'sqInt superSendTrampolines[NumSendTrampolines]'. BytecodeSetHasDirectedSuperSend ifTrue: [aCCodeGenerator var: #directedSuperSendTrampolines declareC: 'sqInt directedSuperSendTrampolines[NumSendTrampolines]'; var: #directedSuperBindingSendTrampolines declareC: 'sqInt directedSuperBindingSendTrampolines[NumSendTrampolines]']. NewspeakVM ifTrue: [aCCodeGenerator var: #selfSendTrampolines declareC: 'sqInt selfSendTrampolines[NumSendTrampolines]'; var: #dynamicSuperSendTrampolines declareC: 'sqInt dynamicSuperSendTrampolines[NumSendTrampolines]'; var: #implicitReceiverSendTrampolines declareC: 'sqInt implicitReceiverSendTrampolines[NumSendTrampolines]'; var: #outerSendTrampolines declareC: 'sqInt outerSendTrampolines[NumSendTrampolines]']. aCCodeGenerator addConstantForBinding: self bindingForNumTrampolines; var: #trampolineAddresses declareC: 'static char *trampolineAddresses[NumTrampolines*2]'; var: #objectReferencesInRuntime declareC: 'static usqInt objectReferencesInRuntime[NumObjRefsInRuntime+1]'; var: #labelCounter type: #int; var: #traceFlags declareC: 'int traceFlags = 8 /* prim trace log on by default */'; declareVar: #traceFlagsMeanings type: #'const char *'; var: #traceFlagsMeanings declareC: (CCodeGenerator new arrayInitializerCalled: #traceFlagsMeanings for: ((Cogit basicNew sendTrace: -1)) sizeString: nil type: #'const char *'); var: #cStackAlignment declareC: 'const int cStackAlignment = STACK_ALIGN_BYTES'. aCCodeGenerator declareVar: #minValidCallAddress type: #'usqIntptr_t'. aCCodeGenerator vmClass generatorTable ifNotNil: [:bytecodeGenTable| aCCodeGenerator var: #generatorTable declareC: 'static BytecodeDescriptor generatorTable[', bytecodeGenTable size printString, ']', (self tableInitializerFor: bytecodeGenTable in: aCCodeGenerator)]. "In C the abstract opcode names clash with the Smalltalk generator syntactic sugar. Most of the syntactic sugar is inlined, but alas some remains. Rename the syntactic sugar to avoid the clash." (self organization listAtCategoryNamed: #'abstract instructions') do: [:s| aCCodeGenerator addSelectorTranslation: s to: 'g', (aCCodeGenerator cFunctionNameFor: s)]. aCCodeGenerator addSelectorTranslation: #halt: to: 'haltmsg'. + self declareFlagVarsAsByteIn: aCCodeGenerator. + + SpurMemoryManager wantsIncrementalGC + ifTrue: [ + aCCodeGenerator + recursivelyResolvePolymorpicReceiver: 'objectMemory' toVariants: {SpurIncrementalGarbageCollector. SpurStopTheWorldGarbageCollector} in: self default: SpurIncrementalGarbageCollector forMethodList: #(followForwardedLiteralsImplementationIn:)]! - self declareFlagVarsAsByteIn: aCCodeGenerator.!
Item was changed: ----- Method: Cogit class>>isNonArgumentImplicitReceiverVariableName: (in category 'translation') ----- isNonArgumentImplicitReceiverVariableName: aString ^#('cogit' 'coInterpreter' 'methodZone' 'literalsManager' + 'objectMemory' 'objectRepresentation' 'manager' 'gc' 'marker') includes: aString! - 'objectMemory' 'objectRepresentation' 'manager') includes: aString!
Item was added: + ----- Method: Cogit>>closedPICRefersToUnmarkedObjectForIncrementalGC: (in category 'garbage collection') ----- + closedPICRefersToUnmarkedObjectForIncrementalGC: cPIC + "Answer if the ClosedPIC refers to any unmarked objects or freed/freeable target methods, + applying markAndTraceOrFreeCogMethod:firstVisit: to those targets to determine if freed/freeable." + <var: #cPIC type: #'CogMethod *'> + | pc object | + ((objectMemory isImmediate: cPIC selector) + or: [objectMemory isMarkedOrYoung: cPIC selector]) ifFalse: + [^true]. + + "First jump is unconditional; subsequent ones are conditional." + "Check the potential method oop for the first case only. + Inline cache tags for the 1st case are at the send site." + pc := self addressOfEndOfCase: 1 inCPIC: cPIC. + (objectRepresentation couldBeObject: (object := backEnd literalBeforeFollowingAddress: pc - backEnd jumpLongByteSize)) ifTrue: + [(objectMemory isMarkedOrYoung: object) ifFalse: + [^true]]. + + "Check the first target" + (self markAndTraceOrFreePICTargetForIncrementalGC: (backEnd jumpLongTargetBeforeFollowingAddress: pc) in: cPIC) ifTrue: + [^true]. + + 2 to: cPIC cPICNumCases do: + [:i| + pc := self addressOfEndOfCase: i inCPIC: cPIC. + (self inlineCacheTagsAreIndexes not + and: [objectRepresentation inlineCacheTagsMayBeObjects + and: [objectRepresentation couldBeObject: (object := backEnd literal32BeforeFollowingAddress: pc - backEnd jumpLongConditionalByteSize)]]) ifTrue: + [(objectMemory isMarkedOrYoung: object) ifFalse: + [^true]]. + "Check the potential method oop for subsequent cases." + (objectRepresentation couldBeObject: (object := backEnd literalBeforeFollowingAddress: pc - backEnd jumpLongConditionalByteSize - backEnd cmpC32RTempByteSize)) ifTrue: + [(objectMemory isMarkedOrYoung: object) ifFalse: + [^true]]. + "Check subsequent targets" + (self markAndTraceOrFreePICTargetForIncrementalGC: (backEnd jumpLongConditionalTargetBeforeFollowingAddress: pc) in: cPIC) ifTrue: + [^true]]. + + ^false!
Item was changed: ----- Method: Cogit>>ensureExecutableCodeZone (in category 'memory access') ----- ensureExecutableCodeZone "On some platforms run-time calls may be required to enable execution and disable write-protect of the code zone. This is sequenced by ensuring that the code zone is executable most of the time. Note that any code space modification requires an icache flush (on processors with such an icache). Hence the least invasive time to ensure code is executable is post icache flush. Making sure code is writable can be done either before any bulk edit (e.g. code zone reclamation) or as part of any fine- grained code modification (e.g. setting an anonymous method's selector)." <inline: #always> self cppIf: #DUAL_MAPPED_CODE_ZONE ifFalse: [backEnd needsCodeZoneExecuteWriteSwitch ifTrue: [self cCode: nil inSmalltalk: [| currentAPISelector | "What's all this crap? We're trying to catch cases where ensureExecutableCodeZone is called without first calling ensureWritableCodeZone, and vice verse. But there are lots of exceptions where code is not modified but executability is turned on unnecessarily. The list of exceptions follows." currentAPISelector := self debugAPISelector. self assert: (codeZoneIsExecutableNotWritable not or: [currentAPISelector == #mapObjectReferencesInMachineCode: or: [currentAPISelector == #freeUnmarkedMachineCode + or: [currentAPISelector == #freeUnmarkedMachineCodeForIncrementalGC or: [currentAPISelector == #cogitPostGCAction: or: [currentAPISelector == #ceSICMiss: or: [currentAPISelector == #ceCPICMiss:receiver: or: [currentAPISelector == #unlinkSendsOf:isMNUSelector: or: [currentAPISelector == #unlinkSendsTo:andFreeIf: or: [currentAPISelector == #unlinkSendsToMethodsSuchThat:AndFreeIf: + or: [currentAPISelector == #followMovableLiteralsAndUpdateYoungReferrers]]]]]]]]]])]. - or: [currentAPISelector == #followMovableLiteralsAndUpdateYoungReferrers]]]]]]]]])]. backEnd makeCodeZoneExecutable. self cCode: nil inSmalltalk: [codeZoneIsExecutableNotWritable := true. debugAPISelector := self debugAPISelector]]]!
Item was changed: ----- Method: Cogit>>followForwardedLiteralsImplementationIn: (in category 'garbage collection & become') ----- followForwardedLiteralsImplementationIn: cogMethod <option: #SpurObjectMemory> - <declareTypeForStaticPolymorphism: #SpurStopTheWorldGarbageCollector> <var: #cogMethod type: #'CogMethod *'> | writableCogMethod hasYoungObj hasYoungObjPtr | self assert: (cogMethod isCMMethodEtAl not or: [(objectMemory isForwarded: cogMethod methodObject) not]). writableCogMethod := self writableMethodFor: cogMethod. hasYoungObj := objectMemory isYoung: cogMethod methodObject. (objectMemory shouldRemapOop: cogMethod selector) ifTrue: [writableCogMethod selector: (objectMemory remapObj: cogMethod selector). (objectMemory isYoung: cogMethod selector) ifTrue: [hasYoungObj := true]]. hasYoungObjPtr := (self addressOf: hasYoungObj put: [:val| hasYoungObj := val]) asInteger. self mapFor: cogMethod performUntil: #remapIfObjectRef:pc:hasYoung: arg: hasYoungObjPtr asVoidPointer. hasYoungObj ifTrue: [methodZone ensureInYoungReferrers: cogMethod] ifFalse: [writableCogMethod cmRefersToYoung: false]!
Item was added: + ----- Method: Cogit>>freeUnmarkedMachineCodeForIncrementalGC (in category 'jit - api') ----- + freeUnmarkedMachineCodeForIncrementalGC + "Free machine-code methods whose compiled methods are unmarked + and open PICs whose selectors are not marked, and closed PICs that + refer to unmarked objects." + <api> + <option: #SpurObjectMemory> + | cogMethod freedMethod | + self moveProfileToMethods. "simulation only..." + + freedMethod := false. + cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'. + [cogMethod < methodZone limitZony] whileTrue: + [(cogMethod isCMMethodEtAl + and: [(objectMemory isMarked: cogMethod methodObject) not + and: [(objectMemory isYoungObject: cogMethod methodObject) not]]) ifTrue: + [freedMethod := true. + methodZone freeMethod: cogMethod]. + (cogMethod isCMOpenPIC + and: [(objectMemory isImmediate: cogMethod selector) not + and: [(objectMemory isMarked: cogMethod selector) not + and: [(objectMemory isYoungObject: cogMethod methodObject) not]]]) ifTrue: + [freedMethod := true. + methodZone freeMethod: cogMethod]. + (cogMethod isCMClosedPIC + and: [(self closedPICRefersToUnmarkedObjectForIncrementalGC: cogMethod) + and: [(objectMemory isYoungObject: cogMethod methodObject) not]]) ifTrue: + [freedMethod := true. + methodZone freeMethod: cogMethod]. + cogMethod := methodZone methodAfter: cogMethod]. + freedMethod ifTrue: + [self unlinkSendsToFree]. + self ensureExecutableCodeZone!
Item was removed: - ----- Method: Cogit>>incrementakMarkYoungObjectsIn: (in category 'garbage collection & become') ----- - incrementakMarkYoungObjectsIn: cogMethod - "Mark young literals in the method." - <var: #cogMethod type: #'CogMethod *'> - <inline: true> - self assert: (cogMethod isCMMethodEtAl - or: [cogMethod isCMOpenPIC]). - (objectMemory isYoung: cogMethod selector) ifTrue: - [objectMemory markAndScan: cogMethod selector]. - (cogMethod isCMMethodEtAl - and: [objectMemory isYoung: cogMethod methodObject]) ifTrue: - [objectMemory markAndScan: cogMethod methodObject]. - self mapFor: cogMethod - performUntil: #incrementalMarkYoungObjects:pc:method: - arg: cogMethod!
Item was added: + ----- Method: Cogit>>incrementalMarkAndTraceLiteralsIn: (in category 'garbage collection & become') ----- + incrementalMarkAndTraceLiteralsIn: cogMethod + <option: #SpurObjectMemory> + "Unlink sends that have unmarked classes in inline caches or freed/freeable targets. + Nil-out inline caches linked to open PICs. + Assert that any selectors are marked. We can do this since + this is only run on marked methods and thus any selectors they + reference should already be marked." + <var: #cogMethod type: #'CogMethod *'> + <inline: true> + self assert: ((cogMethod isCMMethodEtAl + and: [objectMemory isMarkedOrYoung: cogMethod methodObject]) + or: [cogMethod isCMOpenPIC + and: [(objectMemory isImmediate: cogMethod selector) + or: [objectMemory isMarkedOrYoung: cogMethod selector]]]). + objectRepresentation + incrementalMarkAndTraceLiteral: cogMethod selector + in: cogMethod + at: (self addressOf: cogMethod selector put: [:val| cogMethod selector: val]). + self maybeMarkCountersIn: cogMethod. + self maybeMarkIRCsIn: cogMethod. + self mapFor: cogMethod + performUntil: #incrementalMarkLiterals:pc:method: + arg: cogMethod!
Item was changed: ----- Method: Cogit>>incrementalMarkAndTraceMachineCodeForNewSpaceGC (in category 'jit - api') ----- incrementalMarkAndTraceMachineCodeForNewSpaceGC "Free any methods that refer to unmarked objects, unlinking sends to freed methods." | pointer cogMethod | <var: #cogMethod type: #'CogMethod *'> + <staticallyResolveReceiver: 'objectMemory marker' to: #SpurIncrementalMarker> objectMemory leakCheckNewSpaceGC ifTrue: [self asserta: self allMachineCodeObjectReferencesValid]. codeModified := false. pointer := methodZone youngReferrers. [pointer < methodZone zoneEnd] whileTrue: [cogMethod := coInterpreter cCoerceSimple: (objectMemory longAt: pointer) to: #'CogMethod *'. cogMethod cmRefersToYoung ifTrue: [self assert: (self cogMethodDoesntLookKosher: cogMethod) = 0. self assert: (cogMethod isCMMethodEtAl or: [cogMethod isCMOpenPIC]). (objectMemory isYoung: cogMethod selector) ifTrue: + [objectMemory marker markAndScan: cogMethod selector]. - [objectMemory markAndScan: cogMethod selector]. cogMethod isCMMethodEtAl ifTrue: [(objectMemory isYoung: cogMethod methodObject) ifTrue: + [objectMemory marker markAndScan: cogMethod methodObject]. + self incrementalMarkYoungObjectsIn: cogMethod]]. - [objectMemory markAndScan: cogMethod methodObject]. - self markYoungObjectsIn: cogMethod]]. pointer := pointer + objectMemory wordSize]. objectMemory leakCheckNewSpaceGC ifTrue: [self asserta: self allMachineCodeObjectReferencesValid]. codeModified ifTrue: "After updating oops in inline caches we need to flush the icache." [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone freeStart]!
Item was added: + ----- Method: Cogit>>incrementalMarkAndTraceMachineCodeOfMarkedMethods (in category 'jit - api') ----- + incrementalMarkAndTraceMachineCodeOfMarkedMethods + "Mark objects in machine-code of marked methods (or open PICs with marked selectors)." + <api> + <option: #SpurObjectMemory> + | cogMethod | + <var: #cogMethod type: #'CogMethod *'> + objectMemory leakCheckFullGC ifTrue: + [self asserta: self allMachineCodeObjectReferencesValid]. + codeModified := false. + self incrementalMarkAndTraceObjectReferencesInGeneratedRuntime. + cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'. + [cogMethod < methodZone limitZony] whileTrue: + [(cogMethod isCMMethodEtAl + and: [objectMemory isMarkedOrYoung: cogMethod methodObject]) ifTrue: + [self incrementalMarkAndTraceLiteralsIn: cogMethod]. + (cogMethod isCMOpenPIC + and: [(objectMemory isImmediate: cogMethod selector) + or: [objectMemory isMarkedOrYoung: cogMethod selector]]) ifTrue: + [self incrementalMarkAndTraceLiteralsIn: cogMethod]. + cogMethod := methodZone methodAfter: cogMethod]. + objectMemory leakCheckFullGC ifTrue: + [self asserta: self allMachineCodeObjectReferencesValid]. + codeModified ifTrue: "After updating oops in inline caches we need to flush the icache." + [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone freeStart]!
Item was changed: ----- Method: Cogit>>incrementalMarkLiterals:pc:method: (in category 'garbage collection') ----- incrementalMarkLiterals: annotation pc: mcpc method: cogMethod "Mark and trace literals. Additionally in Newspeak, void push implicits that have unmarked classes." <var: #mcpc type: #'char *'> <var: #cogMethod type: #'CogMethod *'> <var: #nsSendCache type: #'NSSendCache *'> + <staticallyResolveReceiver: 'objectMemory marker' to: #SpurIncrementalMarker> | literal | annotation = IsObjectReference ifTrue: [literal := literalsManager fetchLiteralAtAnnotatedAddress: mcpc asUnsignedInteger using: backEnd. (objectRepresentation incrementalMarkAndTraceLiteral: literal in: cogMethod atpc: mcpc asUnsignedInteger) ifTrue: [codeModified := true]].
NewspeakVM ifTrue: [annotation = IsNSSendCall ifTrue: [| nsSendCache sel eo | nsSendCache := self nsSendCacheFromReturnAddress: mcpc asInteger. sel := nsSendCache selector. (objectMemory isForwarded: sel) + ifFalse: [objectMemory marker markAndScan: sel] - ifFalse: [objectMemory markAndScan: sel] ifTrue: [sel := objectMemory followForwarded: literal. nsSendCache selector: sel. + self incrementalMarkAndTraceUpdatedLiteral: sel in: (self cCoerceSimple: cogMethod to: #'CogMethod *')]. - self markAndTraceUpdatedLiteral: sel in: (self cCoerceSimple: cogMethod to: #'CogMethod *')]. eo := nsSendCache enclosingObject. eo ~= 0 ifTrue: [(objectMemory isForwarded: eo) + ifFalse: [objectMemory marker markAndScan: eo] - ifFalse: [objectMemory markAndScan: eo] ifTrue: [eo := objectMemory followForwarded: literal. nsSendCache enclosingObject: eo. self incrementalMarkAndTraceUpdatedLiteral: eo in: (self cCoerceSimple: cogMethod to: #'CogMethod *')]]]].
(self isPureSendAnnotation: annotation) ifTrue: [self entryCacheTagAndCouldBeObjectAt: mcpc annotation: annotation into: [:entryPoint :cacheTag :tagCouldBeObj | (tagCouldBeObj and: [objectRepresentation incrementalMarkAndTraceCacheTagLiteral: cacheTag in: cogMethod atpc: mcpc asUnsignedInteger]) ifTrue: ["cacheTag is selector" codeModified := true]]].
^0 "keep scanning"!
Item was changed: ----- Method: Cogit>>incrementalMarkYoungObjectsIn: (in category 'incremental garbage collection') ----- incrementalMarkYoungObjectsIn: cogMethod "Mark young literals in the method." + <declareTypeForStaticPolymorphism: #SpurIncrementalGarbageCollector> <var: #cogMethod type: #'CogMethod *'> <inline: true> self assert: (cogMethod isCMMethodEtAl or: [cogMethod isCMOpenPIC]). (objectMemory isYoung: cogMethod selector) ifTrue: + [objectMemory markAndTrace: cogMethod selector]. - [objectMemory pushOnMarkingStackAndMakeGrey: cogMethod selector]. (cogMethod isCMMethodEtAl and: [objectMemory isYoung: cogMethod methodObject]) ifTrue: + [objectMemory markAndTrace: cogMethod methodObject]. - [objectMemory pushOnMarkingStackAndMakeGrey: cogMethod methodObject]. self mapFor: cogMethod performUntil: #markYoungObjects:pc:method: arg: cogMethod!
Item was changed: ----- Method: Cogit>>mapObjectReferencesInMachineCode: (in category 'jit - api') ----- mapObjectReferencesInMachineCode: gcMode <api> "Update all references to objects in machine code." gcMode caseOf: { [GCModeNewSpace] -> ["N.B. do *not* ensureWritableCodeZone for every scavenge." self mapObjectReferencesInMachineCodeForYoungGC]. [GCModeFull] -> [self ensureWritableCodeZone. self mapObjectReferencesInMachineCodeForFullGC]. [GCModeBecome] -> [self ensureWritableCodeZone. + self mapObjectReferencesInMachineCodeForBecome]. + [GCModeIncremental] -> [self ensureWritableCodeZone. + self flag: #Todo. "can we optimize something here?" + self mapObjectReferencesInMachineCodeForFullGC] }. - self mapObjectReferencesInMachineCodeForBecome] }.
self mapPerMethodProfile. "simulation only..."
(self asserta: methodZone freeStart <= methodZone youngReferrers) ifFalse: [self error: 'youngReferrers list overflowed']!
Item was changed: ----- Method: Cogit>>markAndTraceMachineCodeOfMarkedMethods (in category 'jit - api') ----- markAndTraceMachineCodeOfMarkedMethods "Mark objects in machine-code of marked methods (or open PICs with marked selectors)." <api> <option: #SpurObjectMemory> | cogMethod | <var: #cogMethod type: #'CogMethod *'> + "objectMemory leakCheckFullGC ifTrue: + [objectMemory clearLeakMapAndMapMarkedOrYoungObjects. + self asserta: self allMachineCodeObjectReferencesValid]." - objectMemory leakCheckFullGC ifTrue: - [self asserta: self allMachineCodeObjectReferencesValid]. codeModified := false. self markAndTraceObjectReferencesInGeneratedRuntime. cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'. [cogMethod < methodZone limitZony] whileTrue: [(cogMethod isCMMethodEtAl and: [objectMemory isMarked: cogMethod methodObject]) ifTrue: [self markAndTraceLiteralsIn: cogMethod]. (cogMethod isCMOpenPIC and: [(objectMemory isImmediate: cogMethod selector) or: [objectMemory isMarked: cogMethod selector]]) ifTrue: [self markAndTraceLiteralsIn: cogMethod]. cogMethod := methodZone methodAfter: cogMethod]. + "objectMemory leakCheckFullGC ifTrue: + [self asserta: self allMachineCodeObjectReferencesValid]." - objectMemory leakCheckFullGC ifTrue: - [self asserta: self allMachineCodeObjectReferencesValid]. codeModified ifTrue: "After updating oops in inline caches we need to flush the icache." [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone freeStart]!
Item was added: + ----- Method: Cogit>>markAndTraceMachineCodeOfMarkedMethodsForIncrementalGC (in category 'jit - api') ----- + markAndTraceMachineCodeOfMarkedMethodsForIncrementalGC + "Mark objects in machine-code of marked methods (or open PICs with marked selectors)." + <api> + <option: #SpurObjectMemory> + | cogMethod | + <var: #cogMethod type: #'CogMethod *'> + "objectMemory leakCheckFullGC ifTrue: + [objectMemory clearLeakMapAndMapMarkedOrYoungObjects. + self asserta: self allMachineCodeObjectReferencesValid]." + codeModified := false. + self markAndTraceObjectReferencesInGeneratedRuntime. + cogMethod := self cCoerceSimple: methodZoneBase to: #'CogMethod *'. + [cogMethod < methodZone limitZony] whileTrue: + [(cogMethod isCMMethodEtAl + and: [objectMemory isMarkedOrYoung: cogMethod methodObject]) ifTrue: + [self incrementalMarkAndTraceLiteralsIn: cogMethod]. + (cogMethod isCMOpenPIC + and: [(objectMemory isImmediate: cogMethod selector) + or: [objectMemory isMarkedOrYoung: cogMethod selector]]) ifTrue: + [self incrementalMarkAndTraceLiteralsIn: cogMethod]. + cogMethod := methodZone methodAfter: cogMethod]. + "objectMemory leakCheckFullGC ifTrue: + [self asserta: self allMachineCodeObjectReferencesValid]." + codeModified ifTrue: "After updating oops in inline caches we need to flush the icache." + [backEnd flushICacheFrom: methodZoneBase asUnsignedInteger to: methodZone freeStart]!
Item was added: + ----- Method: Cogit>>markAndTraceOrFreeCogMethodForIncrementalGC:firstVisit: (in category 'garbage collection & become') ----- + markAndTraceOrFreeCogMethodForIncrementalGC: cogMethod firstVisit: firstVisit + "Mark and trace objects in the argument and free if it is appropriate. + Answer if the method has been freed. firstVisit is a hint used to avoid + scanning methods we've already seen. False positives are fine. + For a CMMethod this + frees if the bytecode method isnt marked, + marks and traces object literals and selectors, + unlinks sends to targets that should be freed. + For a CMClosedPIC this + frees if it refers to anything that should be freed or isn't marked. + For a CMOpenPIC this + frees if the selector isn't marked." + <var: #cogMethod type: #'CogMethod *'> + <inline: false> "this recurses at most one level down" + cogMethod isCMFree ifTrue: + [^true]. + self assert: (self cogMethodDoesntLookKosher: cogMethod) = 0. + cogMethod isCMMethodEtAl ifTrue: + [(objectMemory isMarkedOrYoung: cogMethod methodObject) ifFalse: + [self ensureWritableCodeZone. + methodZone freeMethod: cogMethod. + ^true]. + firstVisit ifTrue: + [self markLiteralsAndUnlinkUnmarkedSendsForIncrementalGCIn: cogMethod]. + ^false]. + cogMethod isCMClosedPIC ifTrue: + [(self closedPICRefersToUnmarkedObjectForIncrementalGC: cogMethod) ifFalse: + [^false]. + self ensureWritableCodeZone. + methodZone freeMethod: cogMethod. + ^true]. + cogMethod isCMOpenPIC ifTrue: + [(objectMemory isMarkedOrYoung: cogMethod selector) ifTrue: + [^false]. + self ensureWritableCodeZone. + methodZone freeMethod: cogMethod. + ^true]. + self assert: (cogMethod isCMMethodEtAl + or: [cogMethod isCMClosedPIC + or: [cogMethod isCMOpenPIC]]). + ^false!
Item was added: + ----- Method: Cogit>>markAndTraceOrFreePICTargetForIncrementalGC:in: (in category 'garbage collection & become') ----- + markAndTraceOrFreePICTargetForIncrementalGC: entryPoint in: cPIC + "If entryPoint is that of some method, then mark and trace objects in it and free if it is appropriate. + Answer if the method has been freed." + <var: #cPIC type: #'CogMethod *'> + | targetMethod | + <var: #targetMethod type: #'CogMethod *'> + self assert: (entryPoint > methodZoneBase and: [entryPoint < methodZone freeStart]). + (cPIC containsAddress: entryPoint) ifTrue: + [^false]. + targetMethod := self cCoerceSimple: entryPoint - cmNoCheckEntryOffset to: #'CogMethod *'. + self assert: (targetMethod isCMMethodEtAl or: [targetMethod isCMFree]). + ^self markAndTraceOrFreeCogMethodForIncrementalGC: targetMethod + firstVisit: targetMethod asUnsignedInteger > cPIC asUnsignedInteger!
Item was added: + ----- Method: Cogit>>markLiteralsAndUnlinkIfUnmarkedSendForIncrementalGC:pc:method: (in category 'garbage collection') ----- + markLiteralsAndUnlinkIfUnmarkedSendForIncrementalGC: annotation pc: mcpc method: cogMethod + "Mark and trace literals. Unlink sends that have unmarked cache tags or targets." + <var: #mcpc type: #'char *'> + <var: #cogMethod type: #'CogMethod *'> + <declareTypeForStaticPolymorphism: #SpurStopTheWorldGarbageCollector> + | literal | + <var: #nsSendCache type: #'NSSendCache *'> + annotation = IsObjectReference ifTrue: + [literal := literalsManager fetchLiteralAtAnnotatedAddress: mcpc asUnsignedInteger using: backEnd. + (objectRepresentation + incrementalMarkAndTraceLiteral: literal + in: (self cCoerceSimple: cogMethod to: #'CogMethod *') + atpc: mcpc asUnsignedInteger) ifTrue: + [codeModified := true]]. + + NewspeakVM ifTrue: + [annotation = IsNSSendCall ifTrue: + [| nsSendCache entryPoint targetMethod sel eo | + nsSendCache := self nsSendCacheFromReturnAddress: mcpc asInteger. + entryPoint := nsSendCache target. + entryPoint ~= 0 ifTrue: "Send is linked" + [targetMethod := self cCoerceSimple: entryPoint - cmNoCheckEntryOffset to: #'CogMethod *'. + (self markAndTraceOrFreeCogMethodForIncrementalGC: targetMethod + firstVisit: targetMethod asUnsignedInteger > mcpc asUnsignedInteger) ifTrue: + [self voidNSSendCache: nsSendCache]]. + sel := nsSendCache selector. + (objectMemory isForwarded: sel) + ifFalse: [objectMemory markAndTrace: sel] + ifTrue: [sel := objectMemory followForwarded: literal. + nsSendCache selector: sel. + self incrementalMarkAndTraceUpdatedLiteral: sel in: (self cCoerceSimple: cogMethod to: #'CogMethod *')]. + eo := nsSendCache enclosingObject. + eo ~= 0 ifTrue: + [(objectMemory isForwarded: eo) + ifFalse: [objectMemory markAndTrace: eo] + ifTrue: [eo := objectMemory followForwarded: literal. + nsSendCache enclosingObject: eo. + self incrementalMarkAndTraceUpdatedLiteral: eo in: (self cCoerceSimple: cogMethod to: #'CogMethod *')]]]]. + + (self isPureSendAnnotation: annotation) ifTrue: + [self entryCacheTagAndCouldBeObjectAt: mcpc annotation: annotation into: + [:entryPoint :cacheTag :tagCouldBeObj | | cacheTagMarked | + cacheTagMarked := tagCouldBeObj and: [objectRepresentation cacheTagIsMarked: cacheTag]. + entryPoint > methodZoneBase + ifTrue: "It's a linked send." + [self targetMethodAndSendTableFor: entryPoint annotation: annotation into: + [:targetMethod :sendTable| + (cacheTagMarked not + or: [self markAndTraceOrFreeCogMethodForIncrementalGC: targetMethod + firstVisit: targetMethod asUnsignedInteger > mcpc asUnsignedInteger]) ifTrue: + ["Either the cacheTag is unmarked (e.g. new class) or the target + has been freed (because it is unmarked), so unlink the send." + self unlinkSendAt: mcpc targetMethod: targetMethod sendTable: sendTable. + objectRepresentation + incrementalMarkAndTraceLiteral: targetMethod selector + in: targetMethod + at: (self addressOf: targetMethod selector put: [:val| targetMethod selector: val])]]] + ifFalse: "cacheTag is selector" + [(objectRepresentation + incrementalMarkAndTraceCacheTagLiteral: cacheTag + in: (self cCoerceSimple: cogMethod to: #'CogMethod *') + atpc: mcpc asUnsignedInteger) ifTrue: + [codeModified := true]]]]. + + ^0 "keep scanning"!
Item was added: + ----- Method: Cogit>>markLiteralsAndUnlinkUnmarkedSendsForIncrementalGCIn: (in category 'garbage collection & become') ----- + markLiteralsAndUnlinkUnmarkedSendsForIncrementalGCIn: cogMethod + "Unlink sends that have unmarked classes in inline caches or freed/freeable targets. + Nil-out inline caches linked to open PICs. + Assert that any selectors are marked. We can do this since + this is only run on marked methods and thus any selectors they + reference should already be marked." + <var: #cogMethod type: #'CogMethod *'> + <inline: true> + self assert: cogMethod isCMMethodEtAl. + self assert: (objectMemory isMarkedOrYoung: cogMethod methodObject). + objectRepresentation + incrementalMarkAndTraceLiteral: cogMethod selector + in: cogMethod + at: (self addressOf: cogMethod selector put: [:val| cogMethod selector: val]). + self maybeMarkCountersIn: cogMethod. + self maybeMarkIRCsIn: cogMethod. + self mapFor: cogMethod + performUntil: #markLiteralsAndUnlinkIfUnmarkedSendForIncrementalGC:pc:method: + arg: cogMethod!
Item was added: + ----- Method: CurrentImageCoInterpreterFacadeFor64BitSpurObjectRepresentation>>gc (in category 'accessing') ----- + gc + + ^ objectMemory gc!
Item was added: + ----- Method: CurrentImageCoInterpreterFacadeFor64BitSpurObjectRepresentation>>markedBitFullShift (in category 'accessing') ----- + markedBitFullShift + + ^ objectMemory markedBitFullShift!
Item was added: + ----- Method: CurrentImageCoInterpreterFacadeFor64BitSpurObjectRepresentation>>marker (in category 'accessing') ----- + marker + + ^ objectMemory marker!
Item was changed: Object subclass: #PolymorphicResolver + instanceVariableNames: 'generator staticallyResolvedPolymorphicReceivers recursivelyResolvedPolymorphicReceivers mappingForRecursivePolymophism removedForPolymorphism recursivePolymorphicMethodsMap additionalMethods isNeeded' - instanceVariableNames: 'generator staticallyResolvedPolymorphicReceivers recursivelyResolvedPolymorphicReceivers mappingForRecursivePolymophism removedForPolymorphism recursivePolymorphicMethodsMap additionalMethods' classVariableNames: 'VerbosePolymorphismResolution' poolDictionaries: '' category: 'VMMaker-Translation to C'!
Item was changed: + ----- Method: PolymorphicResolver>>adopt: (in category 'helper merge resolvers') ----- - ----- Method: PolymorphicResolver>>adopt: (in category 'as yet unclassified') ----- adopt: aPolymorphicResolver
| properties | properties := #(removedForPolymorphism staticallyResolvedPolymorphicReceivers recursivelyResolvedPolymorphicReceivers recursivePolymorphicMethodsMap mappingForRecursivePolymophism). properties do: [:property | self instVarNamed: property put: (self mergeIfPresent: (self instVarNamed: property) and: (aPolymorphicResolver instVarNamed: property))].
self additionalMethods: self additionalMethods , aPolymorphicResolver additionalMethods!
Item was changed: + ----- Method: PolymorphicResolver>>forRecursivePolymorphismResolve:as: (in category 'register polymorphism') ----- - ----- Method: PolymorphicResolver>>forRecursivePolymorphismResolve:as: (in category 'as yet unclassified') ----- forRecursivePolymorphismResolve: aClass as: anotherClass
(mappingForRecursivePolymophism ifNil: [mappingForRecursivePolymophism := Dictionary new]) at: aClass put: anotherClass!
Item was changed: ----- Method: PolymorphicResolver>>initialize (in category 'initialize-release') ----- initialize
+ additionalMethods := Dictionary new. + staticallyResolvedPolymorphicReceivers := Dictionary new. + recursivelyResolvedPolymorphicReceivers := Dictionary new. + mappingForRecursivePolymophism := Dictionary new. + removedForPolymorphism := Dictionary new. + recursivePolymorphicMethodsMap := Dictionary new.! - additionalMethods := Dictionary new!
Item was changed: ----- Method: PolymorphicResolver>>isNeeded (in category 'testing') ----- isNeeded
+ ^ isNeeded ifNil: [isNeeded := {staticallyResolvedPolymorphicReceivers . recursivelyResolvedPolymorphicReceivers . mappingForRecursivePolymophism . removedForPolymorphism . recursivePolymorphicMethodsMap } anySatisfy: [:ea | ea notEmpty]]! - "quite an easy an inaccurate check. Could be better, but works good enough for now" - - ^ staticallyResolvedPolymorphicReceivers notNil!
Item was changed: + ----- Method: PolymorphicResolver>>mergeIfPresent:and: (in category 'helper merge resolvers') ----- - ----- Method: PolymorphicResolver>>mergeIfPresent:and: (in category 'as yet unclassified') ----- mergeIfPresent: aCollection and: anotherCollection
aCollection ifNil: [anotherCollection ifNotNil: [^ anotherCollection]]. anotherCollection ifNil: [^ aCollection]. ^ aCollection , anotherCollection!
Item was changed: + ----- Method: PolymorphicResolver>>recursivelyResolvePolymorpicReceiver:toVariants:in:default: (in category 'register polymorphism') ----- - ----- Method: PolymorphicResolver>>recursivelyResolvePolymorpicReceiver:toVariants:in:default: (in category 'as yet unclassified') ----- recursivelyResolvePolymorpicReceiver: variableName toVariants: classArray in: aClass default: defaultClass "We allow a limited amount of polymorphism; if a class chooses, its selectors can be prefixed with a given string to disambiguate. This hack allows us to use two different compaction algorithms with the same API at the same time; the selection being done by a class which holds the flag stating which algorithm is in effect at the current time." | methodsInClass methodsReferencingReceiver missingSelectors notGeneratedMethods oldMissingSelectorsSize | ((recursivelyResolvedPolymorphicReceivers ifNil: [recursivelyResolvedPolymorphicReceivers := Dictionary new]) at: aClass ifAbsentPut: [Dictionary new]) at: variableName put: classArray. "not generated methods can forward to the polymophic target. Scan them for use, so we can methods that use them include into the transitice closure of referencing methods" notGeneratedMethods := Dictionary newFrom: (((Pragma allNamed: #doNotGenerate in: aClass) collect: [:ea | | selector | selector := ea method selector. "some methods cannot be converted. Ignore them and print them for the developer to do something about it" [selector -> (ea method asTranslationMethodOfClass: TMethod)] on: Error do: [Transcript showln: selector , ' of notGenerated methods could not be translated to a TMethod. Should it be relevant for polymorphism please fix it'. selector]]) select: [:ea | ea isSymbol not]). methodsInClass := self methodsSelect: [:each | each definingClass = aClass]. methodsReferencingReceiver := methodsInClass select: [:method | (method allReferencedVariablesUsing: generator) includes: variableName].
missingSelectors := self transitiveClosureOfMethods: methodsReferencingReceiver , notGeneratedMethods in: aClass. oldMissingSelectorsSize := missingSelectors size. "do not start to generate not generated methods now. We just wanted to get their transistive closure" missingSelectors := missingSelectors copyWithoutAll: notGeneratedMethods keys. self assert: missingSelectors size = (oldMissingSelectorsSize - notGeneratedMethods size). missingSelectors do: [:selector | self methodNamed: selector ifPresent: [:method | (recursivePolymorphicMethodsMap ifNil: [recursivePolymorphicMethodsMap := Dictionary new]) at: aClass ifPresent: [:set | set add: method selector] ifAbsentPut: [Set with: method selector]. self addPolymorphicVariantsFor: method referencing: variableName with: classArray default: defaultClass] ifAbsent: []].!
Item was changed: + ----- Method: PolymorphicResolver>>recursivelyResolvePolymorpicReceiver:toVariants:in:default:forMethodList: (in category 'register polymorphism') ----- - ----- Method: PolymorphicResolver>>recursivelyResolvePolymorpicReceiver:toVariants:in:default:forMethodList: (in category 'as yet unclassified') ----- recursivelyResolvePolymorpicReceiver: variableName toVariants: classArray in: aClass default: defaultClass forMethodList: aMethodCollection "We allow a limited amount of polymorphism; if a class chooses, its selectors can be prefixed with a given string to disambiguate. This hack allows us to use two different compaction algorithms with the same API at the same time; the selection being done by a class which holds the flag stating which algorithm is in effect at the current time." ((recursivelyResolvedPolymorphicReceivers ifNil: [recursivelyResolvedPolymorphicReceivers := Dictionary new]) at: aClass ifAbsentPut: [Dictionary new]) at: variableName put: classArray. aMethodCollection do: [:selector | self methodNamed: selector ifPresent: [:method | (recursivePolymorphicMethodsMap ifNil: [recursivePolymorphicMethodsMap := Dictionary new]) at: aClass ifPresent: [:set | set add: method selector] ifAbsentPut: [Set with: method selector]. self addPolymorphicVariantsFor: method referencing: variableName with: classArray default: defaultClass]].!
Item was changed: ----- Method: PolymorphicResolver>>staticallyResolveMethodNamed:forClass:to: (in category 'register polymorphism') ----- staticallyResolveMethodNamed: selector forClass: aClass to: staticallyResolvedSelector "We allow a limited amount of polymorphism; if a class chooses, its selectors can be prefixed with a given string to disambiguate. This hack allows us to use two different compaction algorithms with the same API at the same time; the selection being done by a class which holds the flag stating which algorithm is in effect at the current time." | method | method := generator methods removeKey: selector ifAbsent: ["self halt. "generator logger cr; nextPutAll: 'warning: did not find ', selector, ' to be able to map to ', staticallyResolvedSelector. ^self]. method selector: staticallyResolvedSelector. + method resolvedFor: aClass. + method oldSelector: selector. generator methods at: staticallyResolvedSelector put: method. (removedForPolymorphism ifNil: [removedForPolymorphism := Dictionary new]) at: selector ifPresent: [:set | set at: aClass put: staticallyResolvedSelector ] ifAbsentPut: [Dictionary with: aClass -> staticallyResolvedSelector] !
Item was changed: ----- Method: PolymorphicResolver>>staticallyResolvePolymorphicReceiverThenUpdateSelectorIn:fromMethod:in: (in category 'as yet unclassified') ----- staticallyResolvePolymorphicReceiverThenUpdateSelectorIn: aSendNode fromMethod: aTMethod in: aClass
| class receiverSymbol | "for debugging. Please do not remove!!" "(aTMethod selector = #checkForEventsMayContextSwitch: and: [aSendNode selector = #sufficientSpaceAfterGC:]) ifTrue: [self halt]." (aSendNode receiver isVariable or: [(self hasPolymorphicMethod: aSendNode selector in: aClass ) or: [removedForPolymorphism includesKey: aSendNode selector]]) ifFalse: [^self]. receiverSymbol := aSendNode receiver name. class := (aTMethod pragmasAt: #staticallyResolveMethod:to:) ifNotNil: [:pragmas | pragmas detect: [:pragma | aSendNode selector = (pragma argumentAt: 1)] ifFound: [:pragma | "self halt." self resolve: aSendNode to: (Smalltalk at: (pragma argumentAt: 2)) ifFound: [^self]] ifNone: []]. class := (aTMethod pragmaAt: #declareTypeForStaticPolymorphism:) ifNotNil: [:pragma | | typeHint classFromHint | typeHint := pragma argumentAt: 1. classFromHint := Smalltalk at: (pragma argumentAt: 1). "if we look at a polymorphic base method do not resolve it to its default but the type hint if it knows it" self methodNamed: aSendNode selector ifPresent: [:method | method isPolymorphicBase ifTrue: [(method classes includes: classFromHint) ifTrue: [ | newSelector | newSelector := method polymorphicSelectorForClass: classFromHint. aSendNode setSelectorForPolymorphism: newSelector. ^ self]]. method isPolymorphic ifTrue: [self error: 'Should not happen']]. removedForPolymorphism at: aSendNode selector ifPresent: [:dictionary | dictionary at: classFromHint ifPresent: [:selector | aSendNode setSelectorForPolymorphism: selector. ^ self]. (mappingForRecursivePolymophism associationsSelect: [:assoc | assoc value = classFromHint]) keys detect: [:key | dictionary includesKey: key] ifFound: [:clazz | aSendNode setSelectorForPolymorphism: (dictionary at: clazz). ^ self] ]]. class ifNil: [self resolveRecursivePolymorphism: receiverSymbol in: aSendNode fromMethod: aTMethod in: aClass ifMatch: [^ self]]. class := class ifNil: [self getClassFromPragmasIn: aTMethod ifMatching: receiverSymbol]. class := class ifNil: [self getClassFor: receiverSymbol in: aClass]. class := class ifNil: [ removedForPolymorphism at: aSendNode selector ifPresent: [: dict | "you probably ask yourself: why am I here? This halt is triggered if we were unable to resolve your method, although it is polymorphic with a very high probability. You either have to declare to which type the method has to be resolved, you did not implement to method in the class you would it expect to be in (inspect dict and see if the class you would expect is listed there as a key. If not you did not call staticallyResolveMethodNamed:forClass:to: on the selector in the missing class, please investigate) or I forgot to include one case if the type should already be known Please have a look what aTMethod to know in which method the problem occured and aSendNode to know the call in aTMethod that is not enough defined. Probably you want to include a pragma #staticallyResolveReceiver:to: to define of which type the receiver is. Should the current method be a Polymorphic(Base)TMethod it is probably interesting why resolveRecursivePolymorphism:in:fromMethod:in:ifMatch: above does not resolve it." " For easier debugging: Browser fullOnClass: aTMethod definingClass selector: aTMethod selector. " self error: 'Could not resolve: ' , aSendNode asString , ' in: ' , aTMethod asString , '. Possible variants of the methods exist in: ' , dict associations asString]]. "we have to find a class to resolve the selector" class ifNotNil: [ aSendNode setSelectorForPolymorphism: (class staticallyResolvePolymorphicSelector: aSendNode selector)] !
Item was changed: + ----- Method: PolymorphicResolver>>staticallyResolvedPolymorphicReceiver:to:in: (in category 'register polymorphism') ----- - ----- Method: PolymorphicResolver>>staticallyResolvedPolymorphicReceiver:to:in: (in category 'as yet unclassified') ----- staticallyResolvedPolymorphicReceiver: variableName to: aClass in: theClassWithTheVariable "We allow a limited amount of polymorphism; if a class chooses, its selectors can be prefixed with a given string to disambiguate. This hack allows us to use two different compaction algorithms with the same API at the same time; the selection being done by a class which holds the flag stating which algorithm is in effect at the current time." ((staticallyResolvedPolymorphicReceivers ifNil: [staticallyResolvedPolymorphicReceivers := Dictionary new]) at: theClassWithTheVariable ifAbsentPut: [Dictionary new]) at: variableName put: aClass!
Item was changed: TMethod subclass: #PolymorphicTMethod + instanceVariableNames: 'receiverToResolve receiverClass' - instanceVariableNames: 'receiverToResolve receiverClass oldSelector' classVariableNames: '' poolDictionaries: '' category: 'VMMaker-Translation to C'!
Item was removed: - ----- Method: PolymorphicTMethod>>oldSelector (in category 'accessing') ----- - oldSelector - - ^ oldSelector!
Item was removed: - ----- Method: PolymorphicTMethod>>oldSelector: (in category 'accessing') ----- - oldSelector: anObject - - oldSelector := anObject.!
Item was added: + ----- Method: Spur32BitCoMemoryManager>>cogit (in category 'as yet unclassified') ----- + cogit + + ^ cogit!
Item was added: + ----- Method: Spur64BitCoMemoryManager>>cogit (in category 'accessing') ----- + cogit + + ^ cogit!
Item was added: + ----- Method: SpurAllAtOnceMarker>>markAndScan: (in category 'as yet unclassified') ----- + markAndScan: objOop + + <api> + manager debugger!
Item was added: + ----- Method: SpurGarbageCollector>>generationalBarrierFor:at:with: (in category 'as yet unclassified') ----- + generationalBarrierFor: anObject at: index with: value + "generational barrier" + + (manager isOldObject: anObject) ifTrue: "most stores into young objects" + [(manager isYoung: value) ifTrue: + [manager possibleRootStoreInto: anObject]].!
Item was added: + ----- Method: SpurGarbageCollector>>internalPrintGCState (in category 'as yet unclassified') ----- + internalPrintGCState + + self subclassResponsibility!
Item was changed: ----- Method: SpurGenerationScavenger>>remember:insteadOf: (in category 'gc - global') ----- remember: newObjOop insteadOf: oldObjOop "Replace a forwarder by the object it is pointing to in the remembered set. This method is designed to work with migrate:sized:to: from the SpurIncrementalCompactor family" + | index | self assert: rememberedSetSize > 0. self assert: (manager isRemembered: oldObjOop). self assert: (manager isRemembered: newObjOop). manager setIsRememberedOf: oldObjOop to: false. + index := 0. + [index < rememberedSetSize] whileTrue: - [| index | - index := 0. - [index < rememberedSetSize] whileTrue: [oldObjOop = (rememberedSet at: index) ifTrue: [rememberedSet at: index put: newObjOop. + ^ self] + ifFalse: [index := index + 1]]. + + coInterpreter cr; print: 'could not replace: '; printHex: oldObjOop; tab; flush. + manager debugger! - index := rememberedSetSize] - ifFalse: [index := index + 1]]].!
Item was changed: ----- Method: SpurIncrementalCompactor>>cannotBeCompacted: (in category 'as yet unclassified') ----- cannotBeCompacted: segInfo
^ (self isSegmentBeingCompacted: segInfo) or: [segInfo containsPinned + or: [(manager segmentManager isEmptySegment: segInfo) + or: [segInfo = segmentToFill]]]! - or: [manager segmentManager isEmptySegment: segInfo]]!
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: + [(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]. - ["During the mutator runs new forwarding references can be created. Ignore them as they get resolved with the other forwarders in this segment in the next marking pass" - self flag: #Todo. "if forwarded we still need to unremember" - (manager isForwarded: 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. - "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]. + (self canTolerateObjStartingAt: fillStart sized: bytesToCopy inSegment: segmentToFill) + ifFalse: [self abortCompactionAt: entity in: segInfo. + + currentSegmentIndex := currentSegmentIndex + 1. + ^ fillStart]. + + compactedObjects := compactedObjects + 1. + compactedBytes := compactedBytes + bytesToCopy. - compactedObjects := compactedObjects + 1. - compactedBytes := compactedBytes + bytesToCopy.
+ self migrate: entity sized: bytesToCopy to: fillStart. - self migrate: entity sized: bytesToCopy to: fillStart.
+ fillStart := fillStart + bytesToCopy. + self assert: (self oop: fillStart isLessThan: (segmentToFill segLimit - manager bridgeSize))]]]. - fillStart := fillStart + bytesToCopy. - self assert: (self oop: fillStart isLessThan: (segmentToFill segLimit - manager bridgeSize))]]].
currentSegmentIndex := currentSegmentIndex + 1. ^ fillStart!
Item was changed: ----- Method: SpurIncrementalCompactor>>postCompactionAction (in category 'as yet unclassified') ----- postCompactionAction | allFlags | "For now we don't optimize and just follow everything everywhere on stack and in caches, let's see in the profiler if we need to optimize with those cases. My guess is that this is < 100 microSecond" manager followSpecialObjectsOop. allFlags := BecamePointerObjectFlag + BecameActiveClassFlag bitOr: BecameCompiledMethodFlag. "Note: there is not the OldBecameNewFlag" "gcMode flag is cleared after postBecomeAction, reset it." manager coInterpreter postBecomeAction: allFlags. + manager coInterpreter setGCMode: GCModeIncremental. - manager coInterpreter setGCMode: GCModeFull. "Special to selective, crazy objects can be forwarded..." "manager postBecomeScanClassTable: allFlags. => Done in followClassTable" manager followClassTable. manager followProcessList. manager followForwardedObjStacks. + manager coInterpreter mapMachineCode: GCModeIncremental. + "Not sure the following are needed... coInterpreter mapInterpreterOops. manager mapExtraRoots." self assert: manager validClassTableHashes.!
Item was added: + ----- Method: SpurIncrementalCompactor>>removeAllCompactionBits (in category 'segment access') ----- + removeAllCompactionBits + <var: 'segInfo' type: #'SpurSegmentInfo *'> + + 0 to: manager numSegments - 1 do: + [:i| | segInfo | + segInfo := manager segInfoAt: i. + self unmarkSegmentAsBeingCompacted: segInfo] + !
Item was changed: SpurGarbageCollector subclass: #SpurIncrementalGarbageCollector + instanceVariableNames: 'phase allAtOnceMarker checkSetGCFlags stopTheWorldGC fullGCWanted phaseCounter gcPauseGoal shouldCollectCounter markTime markCount sweepTime sweepCount compactTime compactCount' - instanceVariableNames: 'phase allAtOnceMarker checkSetGCFlags stopTheWorldGC fullGCWanted phaseCounter gcPauseGoal shouldCollectCounter markTime markCount sweepTime sweepCount compactTime compactCount trigger' 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>>coInterpreter: (in category 'accessing') ----- coInterpreter: anObject
<doNotGenerate> coInterpreter := anObject. + allAtOnceMarker coInterpreter: anObject. + stopTheWorldGC coInterpreter: anObject! - allAtOnceMarker coInterpreter: anObject!
Item was changed: ----- Method: SpurIncrementalGarbageCollector>>doIncrementalCollect (in category 'as yet unclassified') ----- doIncrementalCollect | startTime | phase = InMarkingPhase ifTrue: [ | finishedMarking | marker isCurrentlyMarking 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. - "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. - 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] 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. - "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. - 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. - "startTime := coInterpreter ioUTCMicrosecondsNow. manager setCheckForLeaks: GCCheckFreeSpace + GCModeFull; runLeakCheckerFor: GCModeFull; checkFreeSpace: GCModeFull. + coInterpreter cr; print: 'time for internal check: '; printNum: coInterpreter ioUTCMicrosecondsNow - startTime; tab; flush. - coInterpreter cr; print: 'time for internal check: '; printNum: coInterpreter ioUTCMicrosecondsNow - startTime; tab; flush." self phase: InMarkingPhase. manager attemptToShrink. + coInterpreter postGCAction: GCModeIncremental. - coInterpreter postGCAction: GCModeFull. 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>>doScavenge: (in category 'scavenge') ----- doScavenge: tenuringCriterion
"The inner shell for scavenge, abstrascted out so globalGarbageCollect can use it." <inline: false> | start | start := coInterpreter ioUTCMicrosecondsNow. manager doAllocationAccountingForScavenge. manager gcPhaseInProgress: ScavengeInProgress. manager pastSpaceStart: (scavenger scavenge: tenuringCriterion). self assert: (self oop: manager pastSpaceStart isGreaterThanOrEqualTo: scavenger pastSpace start andLessThanOrEqualTo: scavenger pastSpace limit). manager freeStart: scavenger eden start. manager gcPhaseInProgress: 0. manager resetAllocationAccountingAfterGC. coInterpreter cr; print: 'scavenge time: '; printNum: coInterpreter ioUTCMicrosecondsNow - start; tab; flush. + self eassert: manager allNecessaryRememberedBitsSet. manager statGCEndUsecs: coInterpreter ioUTCMicrosecondsNow. manager statSGCDeltaUsecs: manager statGCEndUsecs - manager gcStartUsecs. (fullGCWanted or: [self numSegmentsAboutToBeFreed > 0]) ifTrue: [ shouldCollectCounter = 0 ifTrue: [self incrementalCollect]. + shouldCollectCounter := (shouldCollectCounter + 1) \ 5]. - shouldCollectCounter := (shouldCollectCounter + 1) \ 5] !
Item was changed: ----- Method: SpurIncrementalGarbageCollector>>gcForSnapshot (in category 'as yet unclassified') ----- gcForSnapshot
self finishGCPass. coInterpreter cr; print: 'finished incremental gc pass'; tab; flush. compactor giveSegmentToFillBackToMemoryManager. + "The stop the world collector starts with marking and therefore resolves all forwarding pointers from potential previous compactions. + The collector will not remove the compaction bits and after the next phase the incremental collector will free the segments, although + the stop the world collector could have moved reachable objects here" + compactor removeAllCompactionBits. stopTheWorldGC fullGC. coInterpreter cr; print: 'finished stop the world gc'; tab; flush. !
Item was added: + ----- Method: SpurIncrementalGarbageCollector>>gcPhase (in category 'accessing') ----- + gcPhase + + "helper for accessing phase from the JIT" + <api> + ^ phase!
Item was added: + ----- 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; cr; flush.]. + + phase = InCompactingPhase + ifTrue: [coInterpreter print: 'compacting'; cr; flush.] + !
Item was changed: ----- Method: SpurIncrementalGarbageCollector>>isIncremental (in category 'testing') ----- isIncremental
+ <api> ^ true!
Item was changed: ----- Method: SpurIncrementalGarbageCollector>>manager: (in category 'accessing') ----- manager: anObject
<doNotGenerate> manager := anObject. + allAtOnceMarker manager: anObject. + stopTheWorldGC manager: anObject! - allAtOnceMarker manager: anObject !
Item was changed: ----- Method: SpurIncrementalGarbageCollector>>markObjectsCompletely (in category 'as yet unclassified') ----- markObjectsCompletely "use the all at once marker to mark objects in young space too (normal incremental marking would not do so) make sure to unmark objects in young space afterwards!! The correctness of the algorithm depends on it"
checkSetGCFlags := false. + manager withGC: stopTheWorldGC do: [allAtOnceMarker markersMarkObjects: false]. - allAtOnceMarker markersMarkObjects: false. checkSetGCFlags := true!
Item was changed: ----- Method: SpurIncrementalGarbageCollector>>phase (in category 'accessing') ----- phase
^ phase!
Item was changed: ----- Method: SpurIncrementalGarbageCollector>>writeBarrierFor:at:with: (in category 'barrier') ----- writeBarrierFor: anObject at: index with: value "combination of the usual generational barrier (see SpurStopTheWorldGarbageCollector>>writeBarrierFor:at:with:) and a dijkstra style write barrier" <inline: true> (manager isOldObject: anObject) ifTrue: "most stores into young objects" [(manager isYoung: value) ifTrue: [manager possibleRootStoreInto: anObject] ifFalse: [ "don't make the check for the phase using the phase variable of the GC. Phase will indicate marking before the next GC cycle even started. The marker knows exactly if marking already started" (marker isCurrentlyMarking and: [manager isMarked: anObject]) + ifTrue: [marker markAndShouldScan: value]]]! - ifTrue: [trigger := trigger + 1. marker markAndShouldScan: value]]]!
Item was added: + ----- Method: SpurIncrementalMarker>>currentlyActive (in category 'as yet unclassified') ----- + currentlyActive + + <api> + ^self cCode: [(self addressOf: isCurrentlyMarking) asUnsignedInteger] + inSmalltalk: [manager cogit simulatedReadWriteVariableAddress: #isCurrentlyMarking in: self]!
Item was changed: ----- Method: SpurIncrementalMarker>>incrementalMark (in category 'marking - incremental') ----- incrementalMark "does one marking cycle. Breaks after a certain amount of slots is marked and the last object, that amount is crossed in, is completely scanned" <inline: #never> "for profiling"
| currentObj slotsLeft | "manager objStack: manager markStack do: [:index :page | Transcript showln: (manager fetchPointer: index ofObject: page)]. manager sizeOfObjStack: manager markStack" currentObj := manager popObjStack: manager markStack. "skip young objects. They get already scanned as they are part of the roots" [(currentObj notNil) and: [manager isYoung: currentObj]] whileTrue: [currentObj := manager popObjStack: manager markStack]. currentObj ifNil: [^ true]. "there is nothing more on the stack and we are done" "arbitrary number. We probably should take a number which correlates with the current allocation behaviour, so we can guarantee marking finished (although this did worked just fine for me until now)" slotsLeft := 1024. [ | slotNumber slotsToVisit startIndex | "after passing the limit we push the current index on the stack. Is the currentObj only an index? " (manager isImmediate: currentObj) ifTrue: [startIndex := manager integerValueOf: currentObj. currentObj := manager popObjStack: manager markStack.] ifFalse: [startIndex := 0. self assert: (manager isFreeObject: currentObj) not. (manager isForwarded: currentObj) ifTrue: [currentObj := manager followForwarded: currentObj]. self flag: #Todo. "unify isYoung with shouldScan: later on. " (manager isYoung: currentObj) ifTrue: ["trick to skip young object" startIndex := manager numStrongSlotsOfInephemeral: currentObj] ifFalse: [self markAndTraceClassOf: currentObj. "eager color the object black. Either it will get scanned completely and the color is correct or we have at least scanned some of the slots. In the second case the mutator could modify one of the slots of the object that already were scanned and we would could lose this object. Therefore color the object early to trigger the write barrier on writes. There will be some overhead (trigger the barrier always although only the already scanned slots are technically black) but it seems we need to do this for correctness" self blackenObject: currentObj]. (self shouldScan: currentObj) not ifTrue: [startIndex := manager numStrongSlotsOfInephemeral: currentObj]]. slotNumber := manager numStrongSlotsOfInephemeral: currentObj. slotsToVisit := slotNumber - startIndex. slotsLeft - slotsToVisit < 0 ifTrue: [ self markFrom: startIndex nSlots: slotsLeft of: currentObj. "If we need to abort earlier we push the index and the currently scanned object on the marking stack. Otherwise it is not possible for immediates to be on the stack (they have no fields to be scanned) -> we can use the immediated to detect this pattern" (manager topOfObjStack: manager markStack) ~= currentObj ifTrue: [manager push: currentObj onObjStack: manager markStack]. manager push: (manager integerObjectOf: startIndex + slotsLeft) onObjStack: manager markStack. "we need to abort early to not run into some extreme corner cases (giant objects) that would explode our mark time assumptions" coInterpreter ioUTCMicrosecondsNow - mStartTime > 5000 ifTrue: [^ false] ifFalse: [slotsLeft := 1024]] ifFalse: ["we can mark all" + slotsLeft := (slotsLeft - slotsToVisit - 1) max: 0. - slotsLeft := slotsLeft - slotsToVisit. + self markFrom: startIndex nSlots: slotsToVisit of: currentObj. + self assert: (manager allReferencesMarkedFor: currentObj)]. - self markFrom: startIndex nSlots: slotsToVisit of: currentObj].
currentObj := manager popObjStack: manager markStack. [(currentObj notNil) and: [manager isYoung: currentObj]] whileTrue: [currentObj := manager popObjStack: manager markStack]. "repeat while there still are objects" currentObj notNil] whileTrue.
^ true!
Item was changed: ----- Method: SpurIncrementalMarker>>markAndScan: (in category 'marking - incremental') ----- markAndScan: objOop "marks the object (grey or black as neccessary) and returns if the object should be scanned This is simply the non-inlined version of markAndShouldScan:"
+ <api> <inline: #never> + ((manager isNonImmediate: objOop) and: [(manager isGrey: objOop) and: [(manager is: objOop onObjStack: manager markStack) not]]) ifTrue: [manager debugger]. - ((manager isNonImmediate: objOop) and: [(self isGrey: objOop) and: [(self is: objOop onObjStack: manager markStack) not]]) ifTrue: [manager debugger]. ^self markAndShouldScan: objOop!
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: [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 changed: ----- Method: SpurIncrementalMarker>>markAndTrace: (in category 'marking - incremental') ----- markAndTrace: objOop
"we do not want to call this method" + self halt. self cCode: 'raise(SIGINT)'!
Item was changed: ----- Method: SpurIncrementalMarker>>markWeaklingsAndMarkAndFireEphemerons (in category 'weaklings and ephemerons') ----- markWeaklingsAndMarkAndFireEphemerons "After the initial scan-mark is complete ephemerons can be processed. Weaklings have accumulated on the weaklingStack, but more may be uncovered during ephemeron processing. So trace the strong slots of the weaklings, and as ephemerons are processed ensure any newly reached weaklings are also traced." | numTracedWeaklings | <inline: false> numTracedWeaklings := 0. + [coInterpreter markAndTraceUntracedReachableStackPagesForIncrementalGC. + coInterpreter markAndTraceMachineCodeOfMarkedMethodsForIncrementalGC. - [coInterpreter markAndTraceUntracedReachableStackPages. - coInterpreter markAndTraceMachineCodeOfMarkedMethods. "Make sure all reached weaklings have their strong slots traced before firing ephemerons..." [numTracedWeaklings := self markAndTraceWeaklingsFrom: numTracedWeaklings. (manager sizeOfObjStack: manager weaklingStack) > numTracedWeaklings] whileTrue. manager noUnscannedEphemerons ifTrue: [coInterpreter + markAndTraceUntracedReachableStackPagesForIncrementalGC; + markAndTraceMachineCodeOfMarkedMethodsForIncrementalGC; - markAndTraceUntracedReachableStackPages; - markAndTraceMachineCodeOfMarkedMethods; freeUntracedStackPages; + freeUnmarkedMachineCodeForIncrementalGC. - freeUnmarkedMachineCode. ^self]. self markInactiveEphemerons ifFalse: [manager fireAllUnscannedEphemerons]. self markAllUnscannedEphemerons] repeat!
Item was changed: ----- Method: SpurIncrementalMarker>>shouldScan: (in category 'testing') ----- shouldScan: objOop
| format | <inline: true> (manager isImmediate: objOop) ifTrue: [^false]. "if markAndTrace: is to follow and eliminate forwarding pointers in its scan it cannot be handed an r-value which is forwarded." self assert: (manager isForwarded: objOop) not.
format := manager formatOf: objOop. (manager isPureBitsFormat: format) ifTrue: "avoid pushing non-pointer objects on the markStack." + [(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]. - [ ^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]. - [^false]. ((manager isEphemeronFormat: format) and: [manager activeAndDeferredScan: objOop]) ifTrue: + [self blackenObject: objOop. + + ^false]. - [^false]. ^true!
Item was added: + ----- Method: SpurIncrementalSweepAndCompact>>postSwizzleAction (in category 'as yet unclassified') ----- + postSwizzleAction + + <doNotGenerate> + compactor postSwizzleAction!
Item was added: + ----- Method: SpurIncrementalSweepAndCompact>>removeAllCompactionBits (in category 'as yet unclassified') ----- + removeAllCompactionBits + + <doNotGenerate> + + compactor removeAllCompactionBits!
Item was changed: ----- Method: SpurMemoryManager class>>declareCVarsIn: (in category 'translation') ----- declareCVarsIn: aCCodeGenerator
self wantsIncrementalGC ifTrue: [aCCodeGenerator recursivelyResolvePolymorpicReceiver: 'gc' toVariants: {SpurIncrementalGarbageCollector. SpurStopTheWorldGarbageCollector} in: self default: SpurIncrementalGarbageCollector; staticallyResolvedPolymorphicReceiver: 'gc' to: self markerClass in: SpurIncrementalGarbageCollector; staticallyResolvedPolymorphicReceiver: 'compactor' to: self compactorClass in: self; staticallyResolvedPolymorphicReceiver: 'marker' to: self markerClass in: self].
self class == thisContext methodClass ifFalse: [^self]. "Don't duplicate decls in subclasses" aCCodeGenerator removeVariable: 'memory'. "memory is a simulation time thing only" self declareCAsOop: #( freeStart scavengeThreshold newSpaceStart pastSpaceStart oldSpaceStart lowSpaceThreshold freeOldSpaceStart endOfMemory) in: aCCodeGenerator. self declareCAsUSqLong: (self allInstVarNames select: [:ivn| ivn endsWith: 'Usecs']), #(statAllocatedBytes) in: aCCodeGenerator. aCCodeGenerator var: #lastHash type: #usqInt; var: #freeListsMask type: #usqInt; var: #freeLists type: #'sqInt *'; var: #objStackInvalidBecause type: #'char *'; var: #unscannedEphemerons type: #SpurContiguousObjStack; var: #heapGrowthToSizeGCRatio type: #float; var: #heapSizeAtPreviousGC type: #usqInt; var: #totalFreeOldSpace type: #usqInt; var: #maxOldSpaceSize type: #usqInt. aCCodeGenerator var: #oldSpaceUsePriorToScavenge type: #sqLong. aCCodeGenerator var: #remapBuffer declareC: 'sqInt remapBuffer[RemapBufferSize + 1 /* ', (RemapBufferSize + 1) printString, ' */]'. aCCodeGenerator var: #extraRoots declareC: 'sqInt *extraRoots[ExtraRootsSize + 1 /* ', (ExtraRootsSize + 1) printString, ' */]'. self wantsIncrementalGC ifTrue: [ aCCodeGenerator staticallyResolvedPolymorphicReceiver: 'manager gc' to: SpurIncrementalGarbageCollector in: SpurSegmentManager; staticallyResolvedPolymorphicReceiver: 'objectMemory gc' to: self gcClass in: StackInterpreter; + "ugly hack to generate polymorphism for fireAllUnscannedEphemerons (is not polymorphic because the polymorhpism hides behind a method on the coInterpreter) " + recursivelyResolvePolymorpicReceiver: 'objectMemory' toVariants: {SpurIncrementalGarbageCollector. SpurStopTheWorldGarbageCollector} in: self default: SpurIncrementalGarbageCollector forMethodList: #( fireAllUnscannedEphemerons); "the vm needs (from handwritten C code) the method fullGC. Generate it later on" generate: #fullGC from: #SIGC_fullGC]!
Item was added: + ----- Method: SpurMemoryManager>>SIM_markAndShouldScan: (in category 'cog jit support') ----- + SIM_markAndShouldScan: objOop + + <doNotGenerate> + marker markAndShouldScan: objOop!
Item was added: + ----- Method: SpurMemoryManager>>allNecessaryRememberedBitsSet (in category 'debug support') ----- + allNecessaryRememberedBitsSet + + self allOldSpaceObjectsDo: [:obj | | shouldBeRem | + shouldBeRem := self shouldBeRemembered: obj. + + (shouldBeRem not or: [shouldBeRem = (self isRemembered: obj)]) + ifFalse: [bogon := obj. self debugger. ^ false]]. + + ^ true + + !
Item was changed: ----- Method: SpurMemoryManager>>allObjectsUnmarked (in category 'gc - global') ----- allObjectsUnmarked self allObjectsDo: + [:o| ((self isMarked: o) and: [(self gc isInSegmentToFill: o) not]) ifTrue: [bogon := o. self cCode: 'raise(SIGINT)'. ^false]]. - [:o| (self isMarked: o) ifTrue: [bogon := o. self cCode: 'raise(SIGINT)'. ^false]]. ^true!
Item was changed: ----- Method: SpurMemoryManager>>allObjectsWhite (in category 'gc - global') ----- allObjectsWhite self allObjectsDo: + [:o| ((self isWhite: o) not and: [(self gc isInSegmentToFill: o) not]) ifTrue: [self cCode: 'raise(SIGINT)'. bogon := o. ^false]]. - [:o| ((self isMarked: o) or: [self isGrey: o]) ifTrue: [self cCode: 'raise(SIGINT)'. bogon := o. ^false]]. ^true!
Item was added: + ----- Method: SpurMemoryManager>>allReferencesMarkedFor: (in category 'debug support') ----- + allReferencesMarkedFor: objOop + + ((self isPointers: objOop) and: [(self isContext: objOop) not]) + ifTrue: [| slotCount | + slotCount := self numSlotsOf: objOop. + + 0 to: slotCount - 1 + do: [:index | | slot | + slot := self fetchPointer: index ofObject: objOop. + + ((self isNonImmediate: slot) and: [(self isOldObject: slot) and: [(self isForwarded: slot) not]]) + ifTrue: [(self isMarkedOrYoung: slot) not + ifTrue: [self debugger. + ^ false]]]]. + + + ^ true!
Item was added: + ----- Method: SpurMemoryManager>>allRememberedBitsCorrect (in category 'debug support') ----- + allRememberedBitsCorrect + + self allOldSpaceObjectsDo: [:obj | (self shouldBeRemembered: obj) = (self isRemembered: obj) + ifFalse: [bogon := obj. self debugger. ^ false]]. + + ^ true + + !
Item was changed: ----- Method: SpurMemoryManager>>clearLeakMapAndMapMarkedOrYoungObjects (in category 'debug support - leak/mark map') ----- clearLeakMapAndMapMarkedOrYoungObjects "Perform an integrity/leak check using the heapMap. Set a bit at each object's header." + <api> <inline: false> heapMap clearHeapMap. self allObjectsDo: [:oop|( (self isMarked: oop) or: [self isYoung: oop] )ifTrue: [heapMap heapMapAtWord: (self pointerForOop: oop) Put: 1]]!
Item was changed: ----- Method: SpurMemoryManager>>debugger (in category 'plugin support') ----- debugger
+ <api> <inline: #never> + self halt. self cCode: 'raise(SIGINT)'!
Item was added: + ----- Method: SpurMemoryManager>>isMarkedOrYoung: (in category 'header access') ----- + isMarkedOrYoung: objOop + + <api> + ^ (self isMarked: objOop) or: [self isYoungObject: objOop]!
Item was changed: ----- Method: SpurMemoryManager>>markedBitFullShift (in category 'header format') ----- markedBitFullShift + + <api> <cmacro> "bit 1 of 2-bit field above identityHash (little endian)" ^55!
Item was changed: ----- Method: SpurMemoryManager>>noElementOfFreeSpaceIsInSegment: (in category 'debug support') ----- noElementOfFreeSpaceIsInSegment: segInfo "Check that no free space on teh system's free lists is in the segment. N.B. This is slightly different to there is no free space in the segment." <var: 'segInfo' type: #'SpurSegmentInfo *'> self allFreeObjectsDo: + [:freeBird| (segmentManager is: freeBird inSegment: segInfo) ifTrue: [ self halt.self cCode: 'raise(SIGINT)'.^false]]. - [:freeBird| (segmentManager is: freeBird inSegment: segInfo) ifTrue: [ self cCode: 'raise(SIGINT)'.^false]]. ^true!
Item was added: + ----- Method: SpurMemoryManager>>printGCState (in category 'debug printing') ----- + printGCState + + <export: true> + gc internalPrintGCState!
Item was changed: ----- Method: SpurMemoryManager>>printInfo: (in category 'debug printing') ----- printInfo: objOop
<export: true> coInterpreter print: 'the object '; print: (coInterpreter whereIs: objOop); tab; flush. (self isOldObject: objOop) ifTrue: [ | segmentIndex segInfo | segmentIndex := self segmentManager segmentIndexContainingObj: objOop. segInfo := self segInfoAt: segmentIndex. coInterpreter cr; print: 'in segment '; printNum: segmentIndex; tab; print: '('; printHexnp: segInfo segStart; print: ' - '; printHexnp: segInfo segStart + segInfo segSize; print: ')'; cr; flush.]. (self isImmediate: objOop) ifTrue: [coInterpreter cr; print: 'immediate '; tab; flush. ^ self]. + + (self isFreeObject: objOop) + ifTrue: [coInterpreter cr; printHexnp: objOop; print: ' is a free chunk of size: '; printNum: (self bytesInBody: objOop); print: ' byte']. (self isForwarded: objOop) + ifTrue: [coInterpreter cr; printHexnp: objOop; print: ' is a forwarder to: '; printHexnp: (self fetchPointer: 0 ofObject: objOop)] - ifTrue: [coInterpreter cr; print: 'forwarder to: '; printHexnp: (self fetchPointer: 0 ofObject: objOop)] ifFalse: [| class className length | class := self fetchClassOfNonImm: objOop. className := coInterpreter nameOfClass: class lengthInto: (self addressOf: length put: [:v| length := v]). coInterpreter cr; printHexnp: objOop; tab; print: className; tab; print: 'format: '; printNum: (self formatOf: objOop); tab; + print: 'size: '; printNum: (self bytesInBody: objOop); print: ' byte']. - print: 'size: '; printNum: (self bytesInBody: objOop)]. coInterpreter cr; print: 'marked '; printNum: (self isMarked: objOop); tab; print: 'grey '; printNum: (self isGrey: objOop); tab; print: 'remembered '; printNum: (self isRemembered: objOop); tab; print: 'pinned '; printNum: (self isPinned: objOop); cr; flush.!
Item was added: + ----- Method: SpurMemoryManager>>shouldBeRemembered: (in category 'debug support') ----- + shouldBeRemembered: objOop + + <export: true> + (self isImmediate: objOop) + ifTrue: [^ false]. + + (self isPointers: objOop) not + ifTrue: [^ false]. + + ((gc inSweepingAheadOfSweepersPosition: objOop) and: [(self isMarked: objOop) not]) + ifTrue: [^ false]. + + (self isContext: objOop) + ifTrue: [ | sp sender | + sender := self fetchPointer: SenderIndex ofObject: objOop. + (self isIntegerObject: sender) + ifFalse: [(self isYoung: sender) ifTrue: [^ true]]. + + sp := self fetchPointer: StackPointerIndex ofObject: objOop. + sp := self integerValueOf: sp. + sp := sp min: (self lengthOf: objOop) - ReceiverIndex. + MethodIndex to: sp + do: [:i | | slot | + slot := self fetchPointer: i ofObject: objOop. + ((self isNonImmediate: slot) and: [self isYoung: slot]) + ifTrue: [^ true]]. + ^ false ]. + + 0 to: (self numSlotsOf: objOop) - 1 + do: [:i | | slot | + slot := self fetchPointer: i ofObject: objOop. + ((self isNonImmediate: slot) and: [self isYoung: slot]) + ifTrue: [^ true]]. + + ^ false + + !
Item was changed: ----- Method: SpurMemoryManager>>validObjectColors (in category 'debug support') ----- validObjectColors
| currentSweepingEntityT | currentSweepingEntityT := gc sweepersCurrentSweepingEntity ifNil: [self firstObject].
self allOldSpaceEntitiesFrom: currentSweepingEntityT do: [:obj | ((self isMarked: obj) and: [(self isPointers: obj) and: [(self isContext: obj) not]]) ifTrue: [| slotCount | slotCount := self numSlotsOf: obj. 0 to: slotCount - 1 do: [:index | | slot | slot := self fetchPointer: index ofObject: obj. ((self isNonImmediate: slot) and: [(self isOldObject: slot) and: [(self isForwarded: slot) not]]) ifTrue: [(slot >= currentSweepingEntityT and: [(self isMarked: slot) not]) ifTrue: [ "for the 2 phase incremental gc. When we call this method while already having compacted some objects they are not marked anymore as we unmark them and do not sweep the segmentToFill" (gc isInSegmentToFill: slot) not ifTrue: [self halt. coInterpreter longPrintOop: (self firstReferenceTo:(self firstReferenceTo: obj)). self printReferencesTo: (self firstReferenceTo: obj). self printReferencesTo: obj. self printRelativePositionOf: obj. self printRelativePositionOf: slot. coInterpreter longPrintOop: obj. coInterpreter longPrintOop: slot. + self debugger. - self cCode: 'raise(SIGINT)'. ^ false]]]]]]. ^ true!
Item was added: + ----- Method: SpurMemoryManager>>withGC:do: (in category 'gc - incremental') ----- + withGC: aGarbageCollector do: aBlock + + <inline: true> + | oldGC oldMarker oldCompactor | + self cCode: [] inSmalltalk: [ + oldGC := gc. + oldMarker := marker. + oldCompactor := compactor. + + gc := aGarbageCollector. + marker := aGarbageCollector marker. + compactor := aGarbageCollector compactor]. + + aBlock value. + + self cCode: [] inSmalltalk: [ + gc := oldGC. + marker := oldMarker. + compactor := oldCompactor] + + + !
Item was changed: ----- Method: SpurSegmentManager>>allObjectsAreForwardedInSegment:includingFreeSpace: (in category 'testing') ----- allObjectsAreForwardedInSegment: segInfo includingFreeSpace: includeFreeSpace "Answer if all objects in the segment are forwarded to somewhere outside the segment. If includeFreeSpace is true, answer false if there is any unforwarded free space in the segment."
<var: 'segInfo' type: #'SpurSegmentInfo *'> self allEntitiesInSegment: segInfo exceptTheLastBridgeDo: [:thing| (manager isFreeObject: thing) ifTrue: [includeFreeSpace ifTrue: [^false]] ifFalse: [self flag: #Todo. "does not work because free objects seem to be collections" (manager isForwarded: thing) ifFalse: + ["we make an exception for object with the wordSizeClassIndexPun. We do not want to confuse internal checks too much + with free space in compacted segments (because they are not in the free lists and internal checks assume them rightfully to be)" + (manager classIndexOf: thing) = manager wordSizeClassIndexPun + ifFalse: [^false]]. - [^false]. (self is: (manager fetchPointer: 0 ofMaybeForwardedObject: thing) inSegment: segInfo) ifTrue: [^false]]]. ^true!
Item was added: + ----- Method: SpurStopTheWorldGarbageCollector>>gcCounters (in category 'as yet unclassified') ----- + gcCounters + + | result | + result := manager instantiateClass: manager classArray indexableSize: 0. + + manager beRootIfOld: result. + + ^ result!
Item was added: + ----- Method: SpurStopTheWorldGarbageCollector>>gcCounters: (in category 'as yet unclassified') ----- + gcCounters: result + + manager beRootIfOld: result. + + ^ result!
Item was removed: - ----- Method: SpurStopTheWorldGarbageCollector>>generationalBarrierFor:at:with: (in category 'barrier') ----- - generationalBarrierFor: anObject at: index with: value - "generational barrier" - - (manager isOldObject: anObject) ifTrue: "most stores into young objects" - [(manager isYoung: value) ifTrue: - [manager possibleRootStoreInto: anObject]].!
Item was added: + ----- Method: SpurStopTheWorldGarbageCollector>>internalPrintGCState (in category 'as yet unclassified') ----- + internalPrintGCState + + <export: true> + coInterpreter cr; print: 'STWGC'; tab; flush.!
Item was changed: ----- Method: SpurStopTheWorldGarbageCollector>>isIncremental (in category 'testing') ----- isIncremental
+ <api> ^ false!
Item was changed: ----- Method: StackInterpreter class>>declareCVarsIn: (in category 'translation') ----- declareCVarsIn: aCCodeGenerator | vmClass | self class == thisContext methodClass ifFalse: [^self]. "Don't duplicate decls in subclasses" vmClass := aCCodeGenerator vmClass. "Generate primitiveTable etc based on vmClass, not just StackInterpreter" aCCodeGenerator addHeaderFile: '<stdio.h> /* for printf */'; addHeaderFile: '<stdlib.h> /* for e.g. alloca */'; addHeaderFile: '<setjmp.h>'; addHeaderFile: '<wchar.h> /* for wint_t */'; addHeaderFile: '<signal.h>'; addHeaderFile: '"vmCallback.h"'; addHeaderFile: '"sqMemoryFence.h"'; addHeaderFile: '"sqImageFileAccess.h"'; addHeaderFile: '"sqSetjmpShim.h"'; addHeaderFile: '"dispdbg.h"'. LowcodeVM ifTrue: [aCCodeGenerator addHeaderFile: '"sqLowcodeFFI.h"'].
vmClass declareInterpreterVersionIn: aCCodeGenerator defaultName: 'Stack'. aCCodeGenerator var: #interpreterProxy type: #'struct VirtualMachine*'. aCCodeGenerator declareVar: #sendTrace type: 'volatile int'; declareVar: #byteCount type: #usqLong. "see dispdbg.h" "These need to be pointers or unsigned." self declareC: #(instructionPointer method newMethod) as: #usqInt in: aCCodeGenerator. "These are all pointers; char * because Slang has no support for C pointer arithmetic." self declareC: #(localIP localSP localFP stackPointer framePointer stackLimit breakSelector) as: #'char *' in: aCCodeGenerator. aCCodeGenerator var: #breakSelectorLength declareC: 'sqInt breakSelectorLength = MinSmallInteger'. self declareC: #(stackPage overflowedPage) as: #'StackPage *' in: aCCodeGenerator. aCCodeGenerator var: #transcript type: #'FILE *'. aCCodeGenerator removeVariable: 'stackPages'. "this is an implicit receiver in the translated code." "This defines bytecodeSetSelector as 0 if MULTIPLEBYTECODESETS is not defined, for the benefit of the interpreter on slow machines." aCCodeGenerator addConstantForBinding: (self bindingOf: #MULTIPLEBYTECODESETS). MULTIPLEBYTECODESETS == false ifTrue: [aCCodeGenerator removeVariable: 'bytecodeSetSelector']. BytecodeSetHasExtensions == false ifTrue: [aCCodeGenerator removeVariable: 'extA'; removeVariable: 'extB']. aCCodeGenerator var: #methodCache declareC: 'sqIntptr_t methodCache[MethodCacheSize + 1 /* ', (MethodCacheSize + 1) printString, ' */]'. NewspeakVM ifTrue: [aCCodeGenerator var: #nsMethodCache declareC: 'sqIntptr_t nsMethodCache[NSMethodCacheSize + 1 /* ', (NSMethodCacheSize + 1) printString, ' */]'] ifFalse: [aCCodeGenerator removeVariable: #nsMethodCache; removeVariable: 'localAbsentReceiver'; removeVariable: 'localAbsentReceiverOrZero']. AtCacheTotalSize isInteger ifTrue: [aCCodeGenerator var: #atCache declareC: 'sqInt atCache[AtCacheTotalSize + 1 /* ', (AtCacheTotalSize + 1) printString, ' */]']. aCCodeGenerator var: #primitiveTable declareC: 'void (*primitiveTable[MaxPrimitiveIndex + 2 /* ', (MaxPrimitiveIndex + 2) printString, ' */])(void) = ', vmClass primitiveTableString. vmClass primitiveTable do: [:symbolOrNot| (symbolOrNot isSymbol and: [symbolOrNot ~~ #primitiveFail]) ifTrue: [(aCCodeGenerator methodNamed: symbolOrNot) ifNotNil: [:tMethod| tMethod returnType: #void]]].
vmClass objectMemoryClass hasSpurMemoryManagerAPI ifTrue: [aCCodeGenerator var: #primitiveAccessorDepthTable type: 'signed char' sizeString: 'MaxPrimitiveIndex + 2 /* ', (MaxPrimitiveIndex + 2) printString, ' */' array: (vmClass primitiveAccessorDepthTableUsing: aCCodeGenerator). aCCodeGenerator removeConstant: #PrimNumberInstVarAt; removeConstant: #PrimNumberPerform; removeConstant: # PrimNumberPerformWithArgs; removeConstant: #PrimNumberShallowCopy; removeConstant: #PrimNumberSlotAt; removeConstant: #PrimNumberFlushExternalPrimitives; removeConstant: #PrimNumberUnloadModule] ifFalse: [aCCodeGenerator removeVariable: #primitiveAccessorDepthTable; removeConstant: #PrimNumberVMParameter].
aCCodeGenerator var: #displayBits type: #'void *'; var: #primitiveCalloutPointer declareC: 'void *primitiveCalloutPointer = (void *)-1'. #('primitiveDoMixedArithmetic' 'upscaleDisplayIfHighDPI' ) do: [:var| aCCodeGenerator var: var declareC: 'sqInt ', var, ' = -1']. self declareC: #(displayWidth displayHeight displayDepth) as: #int in: aCCodeGenerator. aCCodeGenerator var: #primitiveFunctionPointer declareC: 'void (*primitiveFunctionPointer)()'; var: 'pcPreviousToFunction' declareC: 'sqInt (* const pcPreviousToFunction)(sqInt,sqInt) = ', (aCCodeGenerator cFunctionNameFor: PCPreviousToFunction); var: #externalPrimitiveTable declareC: 'void (*externalPrimitiveTable[MaxExternalPrimitiveTableSize + 1 /* ', (MaxExternalPrimitiveTableSize + 1) printString, ' */])(void)'; var: #interruptCheckChain declareC: 'void (*interruptCheckChain)(void) = 0'; var: #showSurfaceFn declareC: 'int (*showSurfaceFn)(sqIntptr_t, int, int, int, int)'.
self declareCAsUSqLong: #(nextPollUsecs nextWakeupUsecs "these are high-frequency enough that they're overflowing quite quickly on modern hardware" statProcessSwitch statIOProcessEvents statForceInterruptCheck statCheckForEvents statStackOverflow statStackPageDivorce statIdleUsecs) in: aCCodeGenerator. aCCodeGenerator var: #nextProfileTick type: #sqLong. aCCodeGenerator var: #reenterInterpreter type: 'jmp_buf'. LowcodeVM ifTrue: [aCCodeGenerator var: #lowcodeCalloutState type: #'sqLowcodeCalloutState*'. self declareC: #(nativeSP nativeStackPointer shadowCallStackPointer) as: #'char *' in: aCCodeGenerator] ifFalse: [#(lowcodeCalloutState nativeSP nativeStackPointer shadowCallStackPointer) do: [:var| aCCodeGenerator removeVariable: var]]. (self instVarNames select: [:ivn| ivn beginsWith: 'longRunningPrimitive']) do: [:lrpmVar| aCCodeGenerator var: lrpmVar declareC: '#if LRPCheck', ((lrpmVar endsWith: 'Usecs') ifTrue: [#usqLong] ifFalse: [#sqInt]), ' ', lrpmVar, '#endif']. SpurMemoryManager wantsIncrementalGC ifTrue: [ aCCodeGenerator + recursivelyResolvePolymorpicReceiver: 'objectMemory' toVariants: {SpurIncrementalGarbageCollector. SpurStopTheWorldGarbageCollector} in: self default: SpurIncrementalGarbageCollector forMethodList: #(mapInterpreterOops mapStackPages mapVMRegisters mapProfileState postBecomeAction: fireEphemeron: reverseDisplayFrom:to: + postGCUpdateDisplayBits fireFinalization: spurPostBecomeAction:) - recursivelyResolvePolymorpicReceiver: 'objectMemory' toVariants: {SpurIncrementalGarbageCollector. SpurStopTheWorldGarbageCollector} in: self default: SpurIncrementalGarbageCollector forMethodList: #(mapInterpreterOops mapStackPages mapVMRegisters mapProfileState) ]!
Item was added: + ----- Method: StackInterpreter>>freeUnmarkedMachineCodeForIncrementalGC (in category 'object memory support') ----- + freeUnmarkedMachineCodeForIncrementalGC + "This is a no-op in the StackInterpreter"!
Item was added: + ----- Method: StackInterpreter>>incrementalMarkAndTraceMachineCodeOfMarkedMethods (in category 'object memory support') ----- + incrementalMarkAndTraceMachineCodeOfMarkedMethods + "This is a no-op in the StackVM"!
Item was added: + ----- Method: StackInterpreter>>incrementalMarkAndTraceUntracedReachableStackPages (in category 'object memory support') ----- + incrementalMarkAndTraceUntracedReachableStackPages + "Trace any untraced pages" + | thePage foundToBeTracedPage | + <var: #thePage type: #'StackPage *'> + <inline: false> + + [foundToBeTracedPage := false. + 0 to: numStackPages - 1 do: + [:i| + thePage := stackPages stackPageAt: i. + ((stackPages isFree: thePage) not + and: [thePage trace = StackPageReachedButUntraced]) ifTrue: + [foundToBeTracedPage := true. + self incrementalMarkAndTraceStackPage: thePage]]. + foundToBeTracedPage] whileTrue!
Item was added: + ----- Method: StackInterpreter>>markAndTraceMachineCodeOfMarkedMethodsForIncrementalGC (in category 'object memory support') ----- + markAndTraceMachineCodeOfMarkedMethodsForIncrementalGC + "This is a no-op in the StackVM"!
Item was added: + ----- Method: StackInterpreter>>markAndTraceUntracedReachableStackPagesForIncrementalGC (in category 'object memory support') ----- + markAndTraceUntracedReachableStackPagesForIncrementalGC + "Trace any untraced pages" + | thePage foundToBeTracedPage | + <var: #thePage type: #'StackPage *'> + <inline: false> + + [foundToBeTracedPage := false. + 0 to: numStackPages - 1 do: + [:i| + thePage := stackPages stackPageAt: i. + ((stackPages isFree: thePage) not + and: [thePage trace = StackPageReachedButUntraced]) ifTrue: + [foundToBeTracedPage := true. + self incrementalMarkAndTraceStackPage: thePage]]. + foundToBeTracedPage] whileTrue!
Item was changed: ----- Method: StackInterpreter>>updateStateOfSpouseContextForFrame:WithSP: (in category 'frame access') ----- updateStateOfSpouseContextForFrame: theFP WithSP: theSP "Update the frame's spouse context with the frame's current state except for the sender and instruction pointer, which are used to mark the context as married." | theContext tempIndex pointer | <inline: false> <var: #theFP type: #'char *'> <var: #theSP type: #'char *'> <var: #pointer type: #'char *'> self assert: (self frameHasContext: theFP). theContext := self frameContext: theFP. self assert: (self frameReceiver: theFP) = (objectMemory fetchPointer: ReceiverIndex ofObject: theContext). tempIndex := self frameNumArgs: theFP. "update the arguments. this would appear not to be strictly necessary, but is for two reasons. First, the fact that arguments are read-only is only as convention in the Smalltalk compiler; other languages may choose to modify arguments. Second, the Squeak runUntilErrorOrReturnFrom: nightmare pops the stack top, which may, in certain circumstances, be the last argument, and hence the last argument may not have been stored into the context." pointer := theFP + (self frameStackedReceiverOffsetNumArgs: tempIndex). 1 to: tempIndex do: [:i| pointer := pointer - objectMemory wordSize. self assert: (objectMemory addressCouldBeOop: (stackPages longAt: pointer)). objectMemory storePointer: ReceiverIndex + i ofObject: theContext withValue: (stackPages longAt: pointer)]. "now update the non-argument stack contents." pointer := theFP + FoxReceiver - objectMemory wordSize. [pointer >= theSP] whileTrue: [self assert: (objectMemory addressCouldBeOop: (stackPages longAt: pointer)). tempIndex := tempIndex + 1. objectMemory storePointer: ReceiverIndex + tempIndex ofObject: theContext withValue: (stackPages longAt: pointer). pointer := pointer - objectMemory wordSize]. self assert: ReceiverIndex + tempIndex < (objectMemory lengthOf: theContext). objectMemory storePointerUnchecked: StackPointerIndex ofObject: theContext withValue: (objectMemory integerObjectOf: tempIndex)!
Item was changed: Object subclass: #TMethod + instanceVariableNames: 'args comment complete declarations definingClass export extraVariableNumber globalStructureBuildMethodHasFoo inline labels locals parseTree primitive properties returnType selector sharedCase sharedLabel static writtenToGlobalVarsCache functionAttributes usedVariablesCache resolvedFor oldSelector' - instanceVariableNames: 'args comment complete declarations definingClass export extraVariableNumber globalStructureBuildMethodHasFoo inline labels locals parseTree primitive properties returnType selector sharedCase sharedLabel static writtenToGlobalVarsCache functionAttributes usedVariablesCache' classVariableNames: 'CaseStatements' poolDictionaries: '' category: 'VMMaker-Translation to C'!
!TMethod commentStamp: 'dtl 9/15/2008 09:06' prior: 0! A TMethod is a translation method, representing a MethodNode that is to be translated to C source. It has a parseTree of translation nodes that mirrors the parse tree of the corresponding Smalltalk method.!
Item was added: + ----- Method: TMethod>>oldSelector (in category 'accessing') ----- + oldSelector + + ^ oldSelector!
Item was added: + ----- Method: TMethod>>oldSelector: (in category 'accessing') ----- + oldSelector: aSymbol + + oldSelector := aSymbol!
Item was added: + ----- Method: TMethod>>resolvedFor (in category 'accessing') ----- + resolvedFor + + ^ resolvedFor!
Item was added: + ----- Method: TMethod>>resolvedFor: (in category 'accessing') ----- + resolvedFor: aClass + + resolvedFor := aClass!
Item was changed: ----- Method: TMethod>>smalltalkSelector (in category 'accessing') ----- smalltalkSelector "Answer the selector of the original Smalltalk method, not any mangled one." + ^self oldSelector ifNil: [selector]! - ^selector!
vm-dev@lists.squeakfoundation.org