VM Maker: VMMaker.oscog-eem.1446.mcz

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

VM Maker: VMMaker.oscog-eem.1446.mcz

commits-2
 
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.1446.mcz

==================== Summary ====================

Name: VMMaker.oscog-eem.1446
Author: eem
Time: 31 August 2015, 11:59:47.399 am
UUID: 9ab44a24-1f54-4c21-b99a-3977d5ecc0a1
Ancestors: VMMaker.oscog-EstebanLorenzano.1445

Implement ephemeron queue primitive.  Add an image flag that controls the finalization support (StackInterpreter instVar newFinalization).  If unset, finalization is as currently expected; WeakArray finalizes all WeakArrays in its registries on each finalization (which does not scale!).  If set, then both fired ephemerons and bereaved weak arrays are added to the finalization queue and the finalization semaphore is signalled.  In this regime the image is expected to send mourn to each element of the queue, and weak arrays will send themselves finalize in mourn, side-stepping the finalizeValues steps and rendering WeakArray's FinalizationDependents and WeakRegistry obsolete.  The bit is set via Smalltalk vmParameterAt: 48 put: ((Smalltalk vmParameterAt: 48) bitOr: 64).

Rename ephemeronQueue to mournQueue to reflect the more general use of the queue.

Reorganize the getting/setting of the image header VM flags so that processHasThreadId & noThreadingOfGUIThread are now inst vars of CoInterpreterMT, not CoInterpreter.

=============== Diff against VMMaker.oscog-EstebanLorenzano.1445 ===============

Item was changed:
  StackInterpreterPrimitives subclass: #CoInterpreter
+ instanceVariableNames: 'cogit cogMethodZone gcMode cogCodeSize desiredCogCodeSize heapBase lastCoggableInterpretedBlockMethod reenterInterpreter deferSmash deferredSmash primTraceLog primTraceLogIndex traceLog traceLogIndex traceSources cogCompiledCodeCompactionCalledFor statCodeCompactionCount statCodeCompactionUsecs lastUncoggableInterpretedBlockMethod flagInterpretedMethods maxLiteralCountForCompile minBackwardJumpCountForCompile'
- instanceVariableNames: 'cogit cogMethodZone gcMode cogCodeSize desiredCogCodeSize heapBase lastCoggableInterpretedBlockMethod reenterInterpreter deferSmash deferredSmash primTraceLog primTraceLogIndex traceLog traceLogIndex traceSources cogCompiledCodeCompactionCalledFor statCodeCompactionCount statCodeCompactionUsecs lastUncoggableInterpretedBlockMethod processHasThreadId flagInterpretedMethods maxLiteralCountForCompile minBackwardJumpCountForCompile noThreadingOfGUIThread'
  classVariableNames: 'CSCallbackEnter CSCallbackLeave CSCheckEvents CSEnterCriticalSection CSExitCriticalSection CSOwnVM CSResume CSSignal CSSuspend CSSwitchIfNeccessary CSThreadBind CSThreadSchedulingLoop CSWait CSYield HasBeenReturnedFromMCPC MFMethodFlagFrameIsMarkedFlag MinBackwardJumpCountForCompile PrimTraceLogSize ReturnToInterpreter RumpCStackSize TraceBlockActivation TraceBlockCreation TraceBufferSize TraceCodeCompaction TraceContextSwitch TraceDisownVM TraceFullGC TraceIncrementalGC TraceIsFromInterpreter TraceIsFromMachineCode TraceOwnVM TracePreemptDisowningThread TracePrimitiveFailure TracePrimitiveRetry TraceSources TraceStackOverflow TraceThreadSwitch TraceVMCallback TraceVMCallbackReturn'
  poolDictionaries: 'CogMethodConstants VMStackFrameOffsets'
  category: 'VMMaker-JIT'!
 
  !CoInterpreter commentStamp: '<historical>' prior: 0!
  I am a variant of the StackInterpreter that can co-exist with the Cog JIT.  I interpret unjitted methods, either because they have been found for the first time or because they are judged to be too big to JIT.  See CogMethod class's comment for method interoperability.!

Item was changed:
  ----- Method: CoInterpreter>>getCogVMFlags (in category 'internal interpreter access') -----
  getCogVMFlags
  "Answer an array of flags indicating various properties of the Cog VM.
+ These are the same as the image header flags shifted right two bits (excluding float order and full screen flags).
+ Bit 0: specific to CoInterpreterMT
- Bit 0: implies the image's Process class has threadId as its 3rd inst var (zero relative)
  Bit 1: if set, methods that are interpreted will have the flag bit set in their header
+ Bit 2: if set, implies preempting a process does not put it to the back of its run queue
+ Bit 3: specific to CoInterpreterMT
+ Bit 4: if set, implies preempting a process does not put it to the back of its run queue"
+ ^objectMemory integerObjectOf: (flagInterpretedMethods ifTrue: [2] ifFalse: [0])
+ + (preemptionYields ifTrue: [0] ifFalse: [4])
+ + (newFinalization ifTrue: [16] ifFalse: [0])
+ + (imageHeaderFlags >> 2 bitClear: 2 + 4 + 16)!
- Bit 2: if set, implies preempting a process does not put it to the back of its run queue"
- ^objectMemory integerObjectOf: (processHasThreadId ifTrue: [1] ifFalse: [0])
- + (flagInterpretedMethods ifTrue: [2] ifFalse: [0])
- + (preemptionYields ifTrue: [0] ifFalse: [4])
- + (noThreadingOfGUIThread ifTrue: [8] ifFalse: [0])!

Item was changed:
  ----- Method: CoInterpreter>>getImageHeaderFlags (in category 'image save/restore') -----
  getImageHeaderFlags
  "Answer the flags that are contained in the 7th long of the image header."
  ^fullScreenFlag "0 or 1"
  + (VMBIGENDIAN ifTrue: [0] ifFalse: [2]) "this is the imageFloatsLittleEndian flag"
- + (processHasThreadId ifTrue: [4] ifFalse: [0])
  + (flagInterpretedMethods ifTrue: [8] ifFalse: [0])
+ + (preemptionYields ifTrue: [0] ifFalse: [16r10])
+ + (newFinalization ifTrue: [0] ifFalse: [16r40])
+ + (imageHeaderFlags bitClear: 16r5B) "these are any flags we do not recognize"!
- + (preemptionYields ifTrue: [0] ifFalse: [16])
- + (noThreadingOfGUIThread ifTrue: [32] ifFalse: [0])
- + (imageHeaderFlags bitClear: 63) "these are any flags we do not recognize"!

Item was changed:
  ----- Method: CoInterpreter>>setCogVMFlags: (in category 'internal interpreter access') -----
  setCogVMFlags: flags
  "Set an array of flags indicating various properties of the Cog VM.
  Bit 0: if set, implies the image's Process class has threadId as its 3rd inst var (zero relative)
  Bit 1: if set, methods that are interpreted will have the flag bit set in their header
  Bit 2: if set, implies preempting a process does not put it to the back of its run queue
  Bit 3: if set, implies a threaded VM will not dosown the VM if owned by the GUI thread."
+ flags asUnsignedInteger > 31 ifTrue:
- flags asUnsignedInteger > 15 ifTrue:
  [^self primitiveFailFor: PrimErrUnsupported].
- processHasThreadId := (flags bitAnd: 1) ~= 0.
  flagInterpretedMethods := (flags bitAnd: 2) ~= 0.
  preemptionYields := (flags bitAnd: 4) = 0.
+ newFinalization := (flags bitAnd: 16) ~= 0!
- noThreadingOfGUIThread := (flags bitAnd: 8) ~= 0!

Item was changed:
  ----- Method: CoInterpreter>>setImageHeaderFlagsFrom: (in category 'image save/restore') -----
  setImageHeaderFlagsFrom: headerFlags
  "Set the flags that are contained in the 7th long of the image header."
  imageHeaderFlags := headerFlags. "so as to preserve unrecognised flags."
  fullScreenFlag := headerFlags bitAnd: 1.
  imageFloatsBigEndian := (headerFlags bitAnd: 2) = 0 ifTrue: [1] ifFalse: [0].
- processHasThreadId := (headerFlags bitAnd: 4) ~= 0.
  flagInterpretedMethods := (headerFlags bitAnd: 8) ~= 0.
  preemptionYields := (headerFlags bitAnd: 16) = 0.
+ newFinalization := (headerFlags bitAnd: 64) ~= 0.
- noThreadingOfGUIThread := (headerFlags bitAnd: 32) ~= 0.
 
  (SistaVM and: [flagInterpretedMethods]) ifTrue:
  [self print: 'warning, flagInterpretedMethods inoperable in Sista VMs.'; cr]!

Item was changed:
  CoInterpreterPrimitives subclass: #CoInterpreterMT
+ instanceVariableNames: 'cogThreadManager checkThreadActivation maxWaitingPriority foreignCallbackPriority deferThreadSwitch disowningVMThread disownCount foreignCallbackProcessSlot willNotThreadWarnCount activeProcessAffined relinquishing processHasThreadId noThreadingOfGUIThread'
- instanceVariableNames: 'cogThreadManager checkThreadActivation maxWaitingPriority foreignCallbackPriority deferThreadSwitch disowningVMThread disownCount foreignCallbackProcessSlot willNotThreadWarnCount activeProcessAffined relinquishing'
  classVariableNames: 'DisownFlagsShift DisownVMForProcessorRelinquish LockGUIThreadFlag LockGUIThreadShift OwnVMForeignThreadFlag ProcessUnaffinedOnDisown ReturnToThreadSchedulingLoop VMAlreadyOwnedHenceDoNotDisown'
  poolDictionaries: 'VMThreadingConstants'
  category: 'VMMaker-Multithreading'!

Item was added:
+ ----- Method: CoInterpreterMT>>getCogVMFlags (in category 'internal interpreter access') -----
+ getCogVMFlags
+ "Answer an array of flags indicating various properties of the Cog VM.
+ These are the same as the image header flags shifted right two bits (excluding float order and full screen flags).
+ Bit 0: implies the image's Process class has threadId as its 3rd inst var (zero relative)
+ Bit 1: if set, methods that are interpreted will have the flag bit set in their header
+ Bit 2: if set, implies preempting a process does not put it to the back of its run queue
+ Bit 3: if set, implies the GUI will run on the first thread and event queues will not be accessed from other threads
+ Bit 4: if set, implies the new finalizartion scheme where WeakArrays are queued"
+ ^objectMemory integerObjectOf: (processHasThreadId ifTrue: [1] ifFalse: [0])
+ + (flagInterpretedMethods ifTrue: [2] ifFalse: [0])
+ + (preemptionYields ifTrue: [0] ifFalse: [4])
+ + (noThreadingOfGUIThread ifTrue: [8] ifFalse: [0])
+ + (newFinalization ifTrue: [16] ifFalse: [0])
+ + (imageHeaderFlags >> 2 bitClear: 1 + 2 + 4 + 8 + 16)!

Item was added:
+ ----- Method: CoInterpreterMT>>getImageHeaderFlags (in category 'image save/restore') -----
+ getImageHeaderFlags
+ "Answer the flags that are contained in the 7th long of the image header."
+ ^fullScreenFlag "0 or 1"
+ + (VMBIGENDIAN ifTrue: [0] ifFalse: [2]) "this is the imageFloatsLittleEndian flag"
+ + (processHasThreadId ifTrue: [0] ifFalse: [4])
+ + (flagInterpretedMethods ifTrue: [8] ifFalse: [0])
+ + (preemptionYields ifTrue: [0] ifFalse: [16r10])
+ + (noThreadingOfGUIThread ifTrue: [16r20] ifFalse: [0])
+ + (newFinalization ifTrue: [0] ifFalse: [16r40])
+ + (imageHeaderFlags bitClear: 16r7F) "these are any flags we do not recognize"!

Item was added:
+ ----- Method: CoInterpreterMT>>setCogVMFlags: (in category 'internal interpreter access') -----
+ setCogVMFlags: flags
+ "Set an array of flags indicating various properties of the Cog VM.
+ Bit 0: if set, implies the image's Process class has threadId as its 3rd inst var (zero relative)
+ Bit 1: if set, methods that are interpreted will have the flag bit set in their header
+ Bit 2: if set, implies preempting a process does not put it to the back of its run queue
+ Bit 3: if set, implies a threaded VM will not dosown the VM if owned by the GUI thread."
+ flags asUnsignedInteger > 31 ifTrue:
+ [^self primitiveFailFor: PrimErrUnsupported].
+ processHasThreadId := (flags bitAnd: 1) ~= 0.
+ flagInterpretedMethods := (flags bitAnd: 2) ~= 0.
+ preemptionYields := (flags bitAnd: 4) = 0.
+ noThreadingOfGUIThread := (flags bitAnd: 8) ~= 0.
+ newFinalization := (flags bitAnd: 16) ~= 0!

Item was changed:
  ----- Method: CoInterpreterMT>>setImageHeaderFlagsFrom: (in category 'image save/restore') -----
  setImageHeaderFlagsFrom: headerFlags
  "Set the flags that are contained in the 7th long of the image header."
  imageHeaderFlags := headerFlags. "so as to preserve unrecognised flags."
  fullScreenFlag := headerFlags bitAnd: 1.
  imageFloatsBigEndian := (headerFlags bitAnd: 2) = 0 ifTrue: [1] ifFalse: [0].
  processHasThreadId := (headerFlags bitAnd: 4) ~= 0.
  flagInterpretedMethods := (headerFlags bitAnd: 8) ~= 0.
  preemptionYields := (headerFlags bitAnd: 16) = 0.
  noThreadingOfGUIThread := (headerFlags bitAnd: 32) ~= 0.
+ newFinalization := (headerFlags bitAnd: 64) ~= 0.
 
  processHasThreadId ifFalse:
  [self print: 'warning, processHasThreadId flag is unset; cannot function as a threaded VM if so.'; cr]!

Item was changed:
  VMClass subclass: #InterpreterPrimitives
+ instanceVariableNames: 'objectMemory messageSelector argumentCount newMethod primFailCode profileMethod profileProcess profileSemaphore nextProfileTick preemptionYields newFinalization'
- instanceVariableNames: 'objectMemory messageSelector argumentCount newMethod primFailCode profileMethod profileProcess profileSemaphore nextProfileTick preemptionYields'
  classVariableNames: 'CrossedX EndOfRun MillisecondClockMask'
  poolDictionaries: 'VMBasicConstants VMBytecodeConstants VMMethodCacheConstants VMObjectIndices VMSqueakClassIndices VMStackFrameOffsets'
  category: 'VMMaker-Interpreter'!
 
  !InterpreterPrimitives commentStamp: 'eem 12/11/2012 17:11' prior: 0!
  InterpreterPrimitives implements most of the VM's core primitives.  It is the root of the interpreter hierarchy so as to share the core primitives amongst the varioius interpreters.
 
  Instance Variables
  argumentCount: <Integer>
  messageSelector: <Integer>
  newMethod: <Integer>
  nextProfileTick: <Integer>
  objectMemory: <ObjectMemory> (simulation only)
  preemptionYields: <Boolean>
  primFailCode: <Integer>
  profileMethod: <Integer>
  profileProcess: <Integer>
  profileSemaphore: <Integer>
 
  argumentCount
  - the number of arguments of the current message
 
  messageSelector
  - the oop of the selector of the current message
 
  newMethod
  - the oop of the result of looking up the current message
 
  nextProfileTick
  - the millisecond clock value of the next profile tick (if profiling is in effect)
 
  objectMemory
  - the memory manager and garbage collector that manages the heap
 
  preemptionYields
  - a boolean controlling the process primitives.  If true (old, incorrect, blue-book semantics) a preempted process is sent to the back of its run-queue.  If false, a process preempted by a higher-priority process is put back at the head of its run queue, hence preserving cooperative scheduling within priorities.
 
  primFailCode
  - primtiive success/failure flag, 0 for success, otherwise the reason code for failure
 
  profileMethod
  - the oop of the method at the time nextProfileTick was reached
 
  profileProcess
  - the oop of the activeProcess at the time nextProfileTick was reached
 
  profileSemaphore
  - the oop of the semaphore to signal when nextProfileTick is reached
  !

Item was added:
+ ----- Method: InterpreterPrimitives>>primitiveFetchNextMourner (in category 'memory space primitives') -----
+ primitiveFetchNextMourner
+ <option: #SpurObjectMemory>
+ objectMemory dequeueMourner
+ ifNil: [self primitiveFailFor: PrimErrNotFound]
+ ifNotNil: [:mourner| self pop: 1 thenPush: mourner]!

Item was changed:
  ----- Method: SpurGenerationScavenger>>processWeakSurvivor: (in category 'weakness and ephemerality') -----
  processWeakSurvivor: weakObj
  "Process a weak survivor on the weakList.  Those of its fields
  which have not survived the scavenge should be nilled, and if any
+ are, the coInterpreter should be informed via fireFinalization:.
- are, the coInterpreter should be informed via signalFinalization:.
  Answer if the weakObj has any young referents."
  | weakObjShouldMourn hasYoungReferents numStrongSlots  |
  weakObjShouldMourn := hasYoungReferents := false.
  "N.B. generateToByDoLimitExpression:negative:on: guards against (unsigned)0 - 1 going +ve"
  numStrongSlots := manager numFixedSlotsOf: weakObj.
  0 to: numStrongSlots - 1 do:
  [:i| | referent |
  referent := manager fetchPointer: i ofObject: weakObj.
  ((manager isNonImmediate: referent)
   and: [manager isYoungObject: referent]) ifTrue:
  [hasYoungReferents := true]].
  numStrongSlots
  to: (manager numSlotsOf: weakObj) - 1
  do: [:i| | referent |
  referent := manager fetchPointer: i ofObject: weakObj.
  "Referent could be forwarded due to scavenging or a become:, don't assume."
  (manager isNonImmediate: referent) ifTrue:
  [(manager isForwarded: referent) ifTrue:
  [referent := manager followForwarded: referent.
  "weakObj is either young or already in remembered table; no need to check"
  self assert: ((manager isReallyYoungObject: weakObj)
  or: [manager isRemembered: weakObj]).
  manager storePointerUnchecked: i ofObject: weakObj withValue: referent].
  (self isMaybeOldScavengeSurvivor: referent)
  ifTrue:
  [(manager isYoungObject: referent) ifTrue:
  [hasYoungReferents := true]]
  ifFalse:
  [weakObjShouldMourn := true.
  manager
  storePointerUnchecked: i
  ofObject: weakObj
  withValue: manager nilObject]]].
  weakObjShouldMourn ifTrue:
+ [coInterpreter fireFinalization: weakObj].
- [coInterpreter signalFinalization: weakObj].
  ^hasYoungReferents!

Item was changed:
  CogClass subclass: #SpurMemoryManager
(excessive size, no diff calculated)

Item was changed:
  ----- Method: SpurMemoryManager class>>initialize (in category 'class initialization') -----
  initialize
  "SpurMemoryManager initialize"
  BitsPerByte := 8.
 
  "Initialize at least the become constants for the Spur bootstrap where the
  old ObjectMemory simulator is used before a Spur simulator is created.."
  self initializeSpurObjectRepresentationConstants.
 
  "Pig compact can be repeated to compact better.  Experience shows that 3 times
  compacts very well, desirable for snapshots.  But this is overkill for normal GCs."
  CompactionPassesForGC := 2.
  CompactionPassesForSnapshot := 3.
 
  "An obj stack is a stack of objects stored in a hidden root slot, such as
  the markStack or the ephemeronQueue.  It is a linked list of segments,
  with the hot end at the head of the list.  It is a word object.  The stack
  pointer is in ObjStackTopx and 0 means empty.  The list goes through
  ObjStackNextx. We don't want to shrink objStacks, since they're used
  in GC and its good to keep their memory around.  So unused pages
  created by popping emptying pages are kept on the ObjStackFreex list.
  ObjStackNextx must be the last field for swizzleObjStackAt:."
  ObjStackPageSlots := 4092. "+ double header = 16k bytes per page in 32-bits"
  ObjStackTopx := 0.
  ObjStackMyx := 1.
  ObjStackFreex := 2.
  ObjStackNextx := 3.
  ObjStackFixedSlots := 4.
  ObjStackLimit := ObjStackPageSlots - ObjStackFixedSlots.
  "The hiddenHootsObject contains the classTable pages and up to 8 additional objects.
  Currently we use four; the three objStacks, the mark stack, the weaklings and the
+ mourn queue, and the rememberedSet."
- ephemeron queue, and the rememberedSet."
  MarkStackRootIndex := self basicNew classTableRootSlots.
  WeaklingStackRootIndex := MarkStackRootIndex + 1.
+ MournQueueRootIndex := MarkStackRootIndex + 2.
- EphemeronQueueRootIndex := MarkStackRootIndex + 2.
  RememberedSetRootIndex := MarkStackRootIndex + 3.
 
  MarkObjectsForEnumerationPrimitives := false.
 
  "The remap buffer support is for compatibility; Spur doesn't GC during allocation.
  Eventually this should die."
  RemapBufferSize := 25.
 
  "Extra roots are for plugin support."
  ExtraRootsSize := 2048 "max. # of external roots"!

Item was added:
+ ----- Method: SpurMemoryManager>>dequeueMourner (in category 'weakness and ephemerality') -----
+ dequeueMourner
+ "Answer the top mourner (ephemeron or weak array) from the queue or
+ nil if the queue is empty. We don't care about order; ephemerons are
+ fired in an arbitrary order based on where they are in the heap."
+ ^self popObjStack: mournQueue!

Item was removed:
- ----- Method: SpurMemoryManager>>ephemeronQueue (in category 'spur bootstrap') -----
- ephemeronQueue
- ^ephemeronQueue!

Item was changed:
  ----- Method: SpurMemoryManager>>fireAllUnscannedEphemerons (in category 'weakness and ephemerality') -----
  fireAllUnscannedEphemerons
  self assert: (self noUnscannedEphemerons) not.
  self assert: self allUnscannedEphemeronsAreActive.
  unscannedEphemerons start to: unscannedEphemerons top - self wordSize do:
  [:p|
+ coInterpreter fireEphemeron: (self longAt: p)]!
- self queueEphemeron: (self longAt: p)].
- coInterpreter forceInterruptCheck!

Item was changed:
  ----- Method: SpurMemoryManager>>followForwardedObjStacks (in category 'compaction') -----
  followForwardedObjStacks
  "Compaction will move objStack pages as well as ordinary objects.
  So they need their slots followed."
  self followForwardedInObjStack: markStack atIndex: MarkStackRootIndex.
  self followForwardedInObjStack: weaklingStack atIndex: WeaklingStackRootIndex.
+ self followForwardedInObjStack: mournQueue atIndex: MournQueueRootIndex!
- self followForwardedInObjStack: ephemeronQueue atIndex: EphemeronQueueRootIndex!

Item was changed:
  ----- Method: SpurMemoryManager>>initializeObjectMemory: (in category 'initialization') -----
  initializeObjectMemory: bytesToShift
  "Initialize object memory variables at startup time. Assume endOfMemory at al are
  initialised by the image-reading code via setHeapBase:memoryLimit:endOfMemory:.
  endOfMemory is assumed to point to the end of the last object in the image.
  Assume: image reader also initializes the following variables:
  specialObjectsOop
  lastHash"
  <inline: false>
  | freeListObj |
  "Catch mis-initializations leading to bad translations to C"
  self assert: self baseHeaderSize = self baseHeaderSize.
  self assert: (self maxSlotsForAlloc * self wordSize) asInteger > 0.
  self bootstrapping ifFalse:
  [self
  initSegmentBridgeWithBytes: self bridgeSize
  at: endOfMemory - self bridgeSize].
  segmentManager adjustSegmentSwizzlesBy: bytesToShift.
  "image may be at a different address; adjust oops for new location"
  self adjustAllOopsBy: bytesToShift.
  specialObjectsOop := segmentManager swizzleObj: specialObjectsOop.
 
  "heavily used special objects"
  nilObj := self splObj: NilObject.
  falseObj := self splObj: FalseObject.
  trueObj := self splObj: TrueObject.
 
  "In Cog we insist that nil, true & false are next to each other (Cogit generates tighter
  conditional branch code as a result).  In addition, Spur places the free lists and
  class table root page immediately following them."
  self assert: nilObj = oldSpaceStart.
  self assert: falseObj = (self objectAfter: nilObj).
  self assert: trueObj = (self objectAfter: falseObj).
  freeListObj := self objectAfter: trueObj.
  self setHiddenRootsObj: (self objectAfter: freeListObj).
  markStack := self swizzleObjStackAt: MarkStackRootIndex.
  weaklingStack := self swizzleObjStackAt: WeaklingStackRootIndex.
+ mournQueue := self swizzleObjStackAt: MournQueueRootIndex.
- ephemeronQueue := self swizzleObjStackAt: EphemeronQueueRootIndex.
  self assert: self validObjStacks.
  self assert: (self isEmptyObjStack: markStack).
  self assert: (self isEmptyObjStack: weaklingStack).
 
  self initializeFreeSpacePostLoad: freeListObj.
  segmentManager collapseSegmentsPostSwizzle.
  self computeFreeSpacePostSwizzle.
  self initializeOldSpaceFirstFree: freeOldSpaceStart. "initializes endOfMemory, freeStart, free space"
  self initializeNewSpaceVariables.
  scavenger initializeRememberedSet.
  segmentManager checkSegments.
 
  numCompactionPasses := CompactionPassesForGC.
 
  "These defaults should depend on machine size; e.g. too small on a powerful laptop, too big on a Pi."
  growHeadroom := 16*1024*1024. "headroom when growing"
  shrinkThreshold := 32*1024*1024. "free space before shrinking"
  self setHeapSizeAtPreviousGC.
  heapGrowthToSizeGCRatio := 0.333333. "By default GC after scavenge if heap has grown by a third since the last GC"!

Item was changed:
  ----- Method: SpurMemoryManager>>markAndTraceHiddenRoots (in category 'gc - global') -----
  markAndTraceHiddenRoots
  "The hidden roots hold both the class table pages and the obj stacks,
+ and hence need special treatment.  The obj stacks must be marked
+ specially; their pages must be marked, but only the contents of the
+ mournQueue should be marked.
+
- and hence need special treatment.
- The obj stacks must be marked specially; their pages must be marked,
- but only the contents of the ephemeronQueue should be marked.
  If a class table page is weak we can mark and trace the hiddenRoots,
  which will not trace through class table pages because they are weak.
  But if class table pages are strong, we must mark the pages and *not*
  trace them so that only classes reachable from the true roots will be
  marked, and unreachable classes will be left unmarked."
 
  self markAndTraceObjStack: markStack andContents: false.
  self markAndTraceObjStack: weaklingStack andContents: false.
+ self markAndTraceObjStack: mournQueue andContents: true.
- self markAndTraceObjStack: ephemeronQueue andContents: true.
 
  self setIsMarkedOf: self rememberedSetObj to: true.
  self setIsMarkedOf: self freeListsObj to: true.
 
  (self isWeakNonImm: classTableFirstPage) ifTrue:
  [^self markAndTrace: hiddenRootsObj].
 
  self setIsMarkedOf: hiddenRootsObj to: true.
  self markAndTrace: classTableFirstPage.
  1 to: numClassTablePages - 1 do:
  [:i| self setIsMarkedOf: (self fetchPointer: i ofObject: hiddenRootsObj)
  to: true]!

Item was added:
+ ----- Method: SpurMemoryManager>>mournQueue (in category 'spur bootstrap') -----
+ mournQueue
+ ^mournQueue!

Item was changed:
  ----- Method: SpurMemoryManager>>nilUnmarkedWeaklingSlots (in category 'weakness and ephemerality') -----
  nilUnmarkedWeaklingSlots
  "Nil the unmarked slots in the weaklings on the
  weakling stack, finalizing those that lost references.
  Finally, empty the weaklingStack."
  <inline: #never> "for profiling"
  self cCode: '' inSmalltalk: [coInterpreter transcript nextPutAll: 'nilling...'; flush].
  self eassert: [self allOldMarkedWeakObjectsOnWeaklingStack].
  weaklingStack = nilObj ifTrue:
  [^self].
  self objStack: weaklingStack from: 0 do:
  [:weakling| | anyUnmarked |
  anyUnmarked := self nilUnmarkedWeaklingSlotsIn: weakling.
  anyUnmarked ifTrue:
+ [coInterpreter fireFinalization: weakling]].
- [coInterpreter signalFinalization: weakling]].
  self emptyObjStack: weaklingStack!

Item was changed:
  ----- Method: SpurMemoryManager>>queueEphemeron: (in category 'weakness and ephemerality') -----
  queueEphemeron: anEphemeron
+ "Add the ephemeron to the queue and make it non-ephemeral, to avoid subsequent firing.
+ Alas this means that other ephemerons on the same object not identified in this sccavenge
+ or GC will not fire until later.  But that's life."
  self assert: ((self isNonImmediate: anEphemeron)
  and: [(self formatOf: anEphemeron) = self ephemeronFormat]).
+ self deny: (self is: anEphemeron onObjStack: mournQueue).
+ self push: anEphemeron onObjStack: mournQueue.
+ self setFormatOf: anEphemeron to: self nonIndexablePointerFormat!
- self push: anEphemeron onObjStack: ephemeronQueue!

Item was added:
+ ----- Method: SpurMemoryManager>>queueMourner: (in category 'weakness and ephemerality') -----
+ queueMourner: anEphemeronOrWeakArray
+ "Add the ephemeron to the queue and make it non-ephemeral, to avoid subsequent firing.
+ Alas this means that other ephemerons on the same object not identified in this sccavenge
+ or GC will not fire until later.  But that's life."
+ self assert: ((self isNonImmediate: anEphemeronOrWeakArray)
+ and: [(self formatOf: anEphemeronOrWeakArray) = self ephemeronFormat
+   or: [(self formatOf: anEphemeronOrWeakArray) = self weakArrayFormat]]).
+ self deny: (self is: anEphemeronOrWeakArray onObjStack: mournQueue).
+ self push: anEphemeronOrWeakArray onObjStack: mournQueue.
+ (self formatOf: anEphemeronOrWeakArray) = self ephemeronFormat ifTrue:
+ [self setFormatOf: anEphemeronOrWeakArray to: self nonIndexablePointerFormat]!

Item was changed:
  ----- Method: SpurMemoryManager>>storePointer:ofObjStack:withValue: (in category 'object access') -----
  storePointer: fieldIndex ofObjStack: objStackPage withValue: thang
  self assert: (self formatOf: objStackPage) = self wordIndexableFormat.
  self cCode: []
  inSmalltalk:
  [fieldIndex caseOf: {
  [ObjStackTopx] -> [self assert: (thang between: 0 and: ObjStackLimit)].
+ [ObjStackMyx] -> [self assert: (thang between: MarkStackRootIndex and: MournQueueRootIndex)].
- [ObjStackMyx] -> [self assert: (thang between: MarkStackRootIndex and: EphemeronQueueRootIndex)].
  [ObjStackFreex] -> [self assert: (thang = 0
  or: [(self addressCouldBeObj: thang)
  and: [(self numSlotsOfAny: thang) = ObjStackPageSlots
  and: [(self formatOf: thang) = self wordIndexableFormat]]])].
  [ObjStackNextx] -> [self assert: (thang = 0
  or: [(self addressCouldBeObj: thang)
  and: [(self numSlotsOfAny: thang) = ObjStackPageSlots
  and: [(self formatOf: thang) = self wordIndexableFormat]]])]. }
  otherwise: []].
  ^self
  longAt: objStackPage + self baseHeaderSize + (fieldIndex << self shiftForWord)
  put: thang!

Item was changed:
  ----- Method: SpurMemoryManager>>updateRootOfObjStackAt:with: (in category 'obj stacks') -----
  updateRootOfObjStackAt: objStackRootIndex with: newRootPage
  self storePointer: objStackRootIndex
  ofObject: hiddenRootsObj
  withValue: newRootPage.
  objStackRootIndex caseOf: {
+ [MarkStackRootIndex] -> [markStack := newRootPage].
+ [WeaklingStackRootIndex] -> [weaklingStack := newRootPage].
+ [MournQueueRootIndex] -> [mournQueue := newRootPage] }.
- [MarkStackRootIndex] -> [markStack := newRootPage].
- [WeaklingStackRootIndex] -> [weaklingStack := newRootPage].
- [EphemeronQueueRootIndex] -> [ephemeronQueue := newRootPage] }.
  ^newRootPage!

Item was changed:
  ----- Method: SpurMemoryManager>>validObjStacks (in category 'obj stacks') -----
  validObjStacks
  ^(markStack = nilObj or: [self isValidObjStack: markStack])
   and: [(weaklingStack = nilObj or: [self isValidObjStack: weaklingStack])
+  and: [mournQueue = nilObj or: [self isValidObjStack: mournQueue]]]!
-  and: [ephemeronQueue = nilObj or: [self isValidObjStack: ephemeronQueue]]]!

Item was changed:
  ----- Method: StackInterpreter class>>initializePrimitiveTable (in category 'initialization') -----
(excessive size, no diff calculated)

Item was changed:
  ----- Method: StackInterpreter>>fireEphemeron: (in category 'finalization') -----
  fireEphemeron: ephemeron
  <option: #SpurObjectMemory>
+ objectMemory queueMourner: ephemeron.
+ self signalFinalization: ephemeron!
- objectMemory queueEphemeron: ephemeron.
- self forceInterruptCheck!

Item was added:
+ ----- Method: StackInterpreter>>fireFinalization: (in category 'finalization') -----
+ fireFinalization: weakling
+ <option: #SpurObjectMemory>
+ newFinalization ifTrue:
+ [objectMemory queueMourner: weakling].
+ self signalFinalization: weakling!

Item was changed:
  ----- Method: StackInterpreter>>getCogVMFlags (in category 'internal interpreter access') -----
  getCogVMFlags
  "Answer an array of flags indicating various properties of the Cog VM.
+ These are the same as the image header flags shifted right two bits (excluding float order and full screen flags).
+ Bit 0: specific to CoInterpreterMT
+ Bit 1: specific to CoInterpreter
+ Bit 2: if set, implies preempting a process does not put it to the back of its run queue
+ Bit 3: specific to CoInterpreterMT
+ Bit 4: if set, implies the new finalizartion scheme where WeakArrays are queued"
+ ^objectMemory integerObjectOf: (preemptionYields ifTrue: [0] ifFalse: [4])
+ + (newFinalization ifTrue: [16] ifFalse: [0])
+ + (imageHeaderFlags >> 2 bitClear: 4 + 16)!
- Bit 2: if set, implies preempting a process does not put it to the back of its run queue"
- ^objectMemory integerObjectOf: (preemptionYields ifTrue: [0] ifFalse: [4])!

Item was changed:
  ----- Method: StackInterpreter>>getImageHeaderFlags (in category 'image save/restore') -----
  getImageHeaderFlags
  "Answer the flags that are contained in the 7th long of the image header."
  ^fullScreenFlag "0 or 1"
  + (VMBIGENDIAN ifTrue: [0] ifFalse: [2]) "this is the imageFloatsLittleEndian flag"
  + (preemptionYields ifTrue: [0] ifFalse: [16r10])
+ + (newFinalization ifTrue: [0] ifFalse: [16r40])
+ + (imageHeaderFlags bitClear: 16r53) "these are any flags we do not recognize"!
- + (imageHeaderFlags bitClear: 16r13) "these are any flags we do not recognize"!

Item was changed:
  ----- Method: StackInterpreter>>setCogVMFlags: (in category 'internal interpreter access') -----
  setCogVMFlags: flags
  "Set an array of flags indicating various properties of the Cog VM.
+ Bit 2: if set, implies preempting a process does not put it to the back of its run queue
+ Bit 4: if set, implies the new finalizartion scheme where WeakArrays are queued"
+ flags asUnsignedInteger > 31 ifTrue:
- Bit 2: if set, implies preempting a process does not put it to the back of its run queue"
- flags asUnsignedInteger > 7 ifTrue:
  [^self primitiveFailFor: PrimErrUnsupported].
+ preemptionYields := (flags bitAnd: 4) = 0.
+ newFinalization := (flags bitAnd: 16) ~= 0!
- preemptionYields := (flags bitAnd: 4) = 0!

Item was changed:
  ----- Method: StackInterpreter>>setImageHeaderFlagsFrom: (in category 'image save/restore') -----
  setImageHeaderFlagsFrom: headerFlags
  "Set the flags that are contained in the 7th long of the image header."
  imageHeaderFlags := headerFlags. "so as to preserve unrecognised flags."
  fullScreenFlag := headerFlags bitAnd: 1.
  imageFloatsBigEndian := (headerFlags bitAnd: 2) = 0 ifTrue: [1] ifFalse: [0].
+ preemptionYields := (headerFlags bitAnd: 16) = 0.
+ newFinalization := (headerFlags bitAnd: 64) ~= 0.!
- preemptionYields := (headerFlags bitAnd: 16) = 0!

Item was removed:
- ----- Method: StackInterpreterPrimitives>>primitiveMakeEphemeron (in category 'system control primitives') -----
- primitiveMakeEphemeron
- "Turn the receiver into an ephemeron.
- TEMPORARY. For testing ephemeron handling in the VM only.
- Ephemerons should be instantiated from a suitable class."
- <export: true>
- <option: #SpurObjectMemory>
- ((objectMemory isNonImmediate: self stackTop)
- and: [objectMemory isFixedSizePointerFormat: (objectMemory formatOf: self stackTop)]) ifFalse:
- [^self primitiveFailFor: (argumentCount = 0
- ifTrue: [PrimErrBadReceiver]
- ifFalse: [PrimErrBadArgument])].
- objectMemory
- setFormatOf: self stackTop
- to: objectMemory ephemeronFormat.
- self pop: argumentCount!