VM Maker: VMMaker.oscog-eem.2729.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.2729.mcz

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

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

Name: VMMaker.oscog-eem.2729
Author: eem
Time: 18 March 2020, 8:11:20.738684 pm
UUID: b57f3688-aef5-4b14-9b59-ee1fd0fd6d53
Ancestors: VMMaker.oscog-eem.2728

Interpreters: rip out the check alloc filler support in the interests of simplicity.  Though useful in theory, this faclity has never been used successfully to find a bug in all the years it's been available (and we can always put it back).
Rip out the debugPrimCallStackOffset support.  I can't remember what this was supposed to do.  It is confusing.

Spur: Don't update become effect flags for identical oops that will later be filtered out in the loops oiver the arrays.  Don't queue a WeakArray for finalization more than once (i.e. if its already in the queue there's no point adding it again).

Cogit: fix genExternalizePointersForPrimitiveCall & genLoadCStackPointersForPrimCall for the SPReg ~= NativeSPReg regime.
Localise code to the primitive generator invocation block in compilePrimitive

Slang: rewrite TAssignmentNode>>emitValueExpansionOn:level:generator: in the same style as the recent rewrite of TReturnNode>>emitValueExpansionOn:level:generator:

Simulation:
Fix simulating leaf calls in teh context of a primitive invocation.  ARMv8 has generated cache flushing code which is executed potentilly during a become primitive.  Making simulateLeafCallOf: restore stack,frame,and link registers prevents this breaking asserts in handleCallOrJumpSimulationTrap:.

Don't bother to print endless zeros on the rump C stack.

Allow settingt a break block either in teh coInterpreter or the cogit (so processor et al are in scope).

Fix receiver in canBeImmutable:.

Fix strncpy:_:_: for CArray receivers.  Correctly simulate getModuleName in e.g. LargeIntegersPlugin.

Most of the above advance productizing the ARMv8 JIT.

=============== Diff against VMMaker.oscog-eem.2728 ===============

Item was changed:
  ----- Method: CoInterpreter class>>initializeMiscConstants (in category 'initialization') -----
  initializeMiscConstants
 
  super initializeMiscConstants.
  COGVM := true.
 
  MinBackwardJumpCountForCompile := 40.
 
  MaxNumArgs := 15.
  PrimCallNeedsNewMethod := 1.
  PrimCallNeedsPrimitiveFunction := 2.
  PrimCallMayCallBack := 4.
  PrimCallOnSmalltalkStack := 8.
  PrimCallCollectsProfileSamples := 16.
+ "CheckAllocationFillerAfterPrimCall := 32. this has never been successfully used in all the years we've had it; nuking it"
- CheckAllocationFillerAfterPrimCall := 32.
  PrimCallDoNotJIT := 64.
 
  PrimTraceLogSize := 256. "Room for 256 selectors.  Must be 256 because we use a byte to hold the index"
  TraceBufferSize := 256 * 3. "Room for 256 events"
  TraceContextSwitch := self objectMemoryClass basicNew integerObjectOf: 1.
  TraceBlockActivation := self objectMemoryClass basicNew integerObjectOf: 2.
  TraceBlockCreation := self objectMemoryClass basicNew integerObjectOf: 3.
  TraceIncrementalGC := self objectMemoryClass basicNew integerObjectOf: 4.
  TraceFullGC := self objectMemoryClass basicNew integerObjectOf: 5.
  TraceCodeCompaction := self objectMemoryClass basicNew integerObjectOf: 6.
  TraceOwnVM := self objectMemoryClass basicNew integerObjectOf: 7.
  TraceDisownVM := self objectMemoryClass basicNew integerObjectOf: 8.
  TraceThreadSwitch := self objectMemoryClass basicNew integerObjectOf: 9.
  TracePreemptDisowningThread := self objectMemoryClass basicNew integerObjectOf: 10.
  TraceVMCallback := self objectMemoryClass basicNew integerObjectOf: 11.
  TraceVMCallbackReturn := self objectMemoryClass basicNew integerObjectOf: 12.
  TraceStackOverflow := self objectMemoryClass basicNew integerObjectOf: 13.
  TracePrimitiveFailure := self objectMemoryClass basicNew integerObjectOf: 14.
  TracePrimitiveRetry := self objectMemoryClass basicNew integerObjectOf: 15.
 
  TraceIsFromMachineCode := 1.
  TraceIsFromInterpreter := 2.
  CSCallbackEnter := 3.
  CSCallbackLeave := 4.
  CSEnterCriticalSection := 5.
  CSExitCriticalSection := 6.
  CSResume := 7.
  CSSignal := 8.
  CSSuspend := 9.
  CSWait := 10.
  CSYield := 11.
  CSCheckEvents := 12.
  CSThreadSchedulingLoop := 13.
  CSOwnVM := 14.
  CSThreadBind := 15.
  CSSwitchIfNeccessary := 16.
 
  TraceSources := CArrayAccessor on: #('?' 'm' 'i' 'callbackEnter' 'callbackLeave' 'enterCritical' 'exitCritical' 'resume' 'signal'  'suspend' 'wait' 'yield' 'eventcheck' 'threadsched' 'ownVM' 'bindToThread' 'switchIfNecessary').
 
  "this is simulation only"
  RumpCStackSize := 4096!

Item was removed:
- ----- Method: CoInterpreter>>getCheckAllocFiller (in category 'cog jit support') -----
- getCheckAllocFiller
- <api>
- ^checkAllocFiller!

Item was changed:
  ----- Method: CoInterpreter>>primitivePropertyFlagsForSpur: (in category 'cog jit support') -----
  primitivePropertyFlagsForSpur: primIndex
  <inline: true>
  "Answer any special requirements of the given primitive.  Spur always needs to set
  primitiveFunctionPointer and newMethod so primitives can retry on failure due to forwarders."
  | baseFlags |
  self cCode: [] inSmalltalk: [#(mcprimHashMultiply: primitiveExternalCall primitiveCalloutToFFI)]. "For senders..."
  primIndex = PrimNumberHashMultiply ifTrue:
  [^PrimCallOnSmalltalkStack].
  baseFlags := PrimCallNeedsPrimitiveFunction + PrimCallNeedsNewMethod.
  profileSemaphore ~= objectMemory nilObject ifTrue:
  [baseFlags := baseFlags bitOr: PrimCallCollectsProfileSamples].
 
  (primIndex = PrimNumberExternalCall "#primitiveExternalCall"
  or: [primIndex = PrimNumberFFICall "#primitiveCalloutToFFI"]) ifTrue: "For callbacks"
+ [baseFlags := baseFlags bitOr: PrimCallMayCallBack].
- [baseFlags := baseFlags bitOr: PrimCallMayCallBack.
- checkAllocFiller ifTrue:
- [baseFlags := baseFlags bitOr: CheckAllocationFillerAfterPrimCall]].
 
  ^baseFlags!

Item was changed:
  SharedPool subclass: #CogMethodConstants
  instanceVariableNames: ''
+ classVariableNames: 'CMBlock CMClosedPIC CMFree CMMaxUsageCount CMMethod CMOpenPIC CompletePrimitive EncounteredUnknownBytecode InsufficientCodeSpace MaxLiteralCountForCompile MaxMethodSize MaxNegativeErrorCode MaxNumArgs MaxStackCheckOffset MethodTooBig NotFullyInitialized PrimCallCollectsProfileSamples PrimCallDoNotJIT PrimCallMayCallBack PrimCallNeedsNewMethod PrimCallNeedsPrimitiveFunction PrimCallOnSmalltalkStack ShouldNotJIT UnfailingPrimitive UnimplementedPrimitive YoungSelectorInPIC'
- classVariableNames: 'CMBlock CMClosedPIC CMFree CMMaxUsageCount CMMethod CMOpenPIC CheckAllocationFillerAfterPrimCall CompletePrimitive EncounteredUnknownBytecode InsufficientCodeSpace MaxLiteralCountForCompile MaxMethodSize MaxNegativeErrorCode MaxNumArgs MaxStackCheckOffset MethodTooBig NotFullyInitialized PrimCallCollectsProfileSamples PrimCallDoNotJIT PrimCallMayCallBack PrimCallNeedsNewMethod PrimCallNeedsPrimitiveFunction PrimCallOnSmalltalkStack ShouldNotJIT UnfailingPrimitive UnimplementedPrimitive YoungSelectorInPIC'
  poolDictionaries: ''
  category: 'VMMaker-JIT'!

Item was changed:
  ----- Method: CogVMSimulator>>primitiveExecuteMethodArgsArray (in category 'control primitives') -----
  primitiveExecuteMethodArgsArray
+ (InitializationOptions at: #haltOnExecuteMethod ifAbsent: [true]) ifTrue:
+ [self halt: thisContext selector].
- self halt: thisContext selector.
  ^super primitiveExecuteMethodArgsArray!

Item was changed:
  ----- Method: CogVMSimulator>>printRumpCStackTo: (in category 'rump c stack') -----
  printRumpCStackTo: address
+ | start |
  self assert: (self isOnRumpCStack: address).
+ start := heapBase - self rumpCStackSize.
+ [start < address and: [(objectMemory longAt: start) = 0]] whileTrue:
+ [start := start + objectMemory wordSize].
+ start := start - (3 * objectMemory wordSize) max: heapBase - self rumpCStackSize.
  address
+ to: start
- to: heapBase - self rumpCStackSize
  by: objectMemory wordSize negated
  do: [:addr| | label |
  self printHex: addr.
  addr = cogit processor sp ifTrue: [label := ' sp'].
  addr = cogit processor fp ifTrue: [label := (label ifNil: [''] ifNotNil: [label, ',']), 'fp'].
  addr = CStackPointer ifTrue: [label := (label ifNil: [''] ifNotNil: [label, ',']), 'CSP'].
  addr = CFramePointer ifTrue: [label := (label ifNil: [''] ifNotNil: [label, ',']), 'CFP'].
  label ifNil: [self tab] ifNotNil: [self print: ' ', label, '->'].
+ self tab; printHex: (objectMemory longAt: addr); cr].
+ heapBase - self rumpCStackSize < start ifTrue:
+ [self print: 'zeros...'; cr]!
- self tab; printHex: (objectMemory longAt: addr); cr]!

Item was removed:
- ----- Method: CogVMSimulator>>setBreakBlockFromString: (in category 'UI') -----
- setBreakBlockFromString: aString
- | bString block |
- bString := aString withBlanksTrimmed.
- bString first = $- ifTrue:
- [^cogit breakBlock: nil].
- bString first ~= $[ ifTrue:
- [bString := '[:_address|', bString, ']'].
- block := [Compiler evaluate: bString for: self logged: false]
- on: Error
- do: [:ex|
- UIManager default warn: ex messageText.
- ^self].
- cogit breakBlock: block!

Item was added:
+ ----- Method: CogVMSimulator>>setBreakBlockFromString:for: (in category 'UI') -----
+ setBreakBlockFromString: aString for: coInterpreterOrCogit
+ | bString block |
+ bString := aString withBlanksTrimmed.
+ bString first = $- ifTrue:
+ [^cogit breakBlock: nil].
+ bString first ~= $[ ifTrue:
+ [bString := '[:_address|', bString, ']'].
+ block := [Compiler evaluate: bString for: coInterpreterOrCogit logged: false]
+ on: Error
+ do: [:ex|
+ UIManager default warn: ex messageText.
+ ^self].
+ cogit breakBlock: block.
+ (atEachStepBlock notNil and: [atEachStepBlock home selector == #useCogitBreakBlockIfNone]) ifTrue:
+ [atEachStepBlock := nil]!

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'
  'simulatedTrampolines' 'simulatedVariableGetters' 'simulatedVariableSetters'
  'processorFrameValid' 'printRegisters' 'printInstructions' 'clickConfirm' 'singleStep') do:
  [:simulationVariableNotNeededForRealVM|
  aCCodeGenerator removeVariable: simulationVariableNotNeededForRealVM].
  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"
  aCCodeGenerator
  addHeaderFile:'<stddef.h>'; "for e.g. offsetof"
  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: #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)(void)';
  var: #ceUnlockVMOwner
  declareC: 'void (*ceUnlockVMOwner)(void)\#endif /* COGMTVM */'.
 
  backEnd numCheckLZCNTOpcodes > 0 ifTrue:
  [aCCodeGenerator
  var: #ceCheckLZCNTFunction
  declareC: 'static usqIntptr_t (*ceCheckLZCNTFunction)(void)'].
  backEnd numCheckFeaturesOpcodes > 0 ifTrue:
  [aCCodeGenerator
  var: #ceCheckFeaturesFunction
  declareC: 'static usqIntptr_t (*ceCheckFeaturesFunction)(void)'].
  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'.
 
  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
  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 */';
  var: #cStackAlignment
  declareC: 'const int cStackAlignment = STACK_ALIGN_BYTES'.
  aCCodeGenerator
+ declareVar: #minValidCallAddress type: #'usqIntptr_t'.
- declareVar: #minValidCallAddress type: #'usqIntptr_t';
- declareVar: #debugPrimCallStackOffset 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!

Item was changed:
  ----- Method: Cogit class>>mustBeGlobal: (in category 'translation') -----
  mustBeGlobal: var
  "Answer if a variable must be global and exported.  Used for inst vars that are
  accessed from VM support code."
  ^#('ceBaseFrameReturnTrampoline' 'ceCaptureCStackPointers' 'ceCheckForInterruptTrampoline'
  ceEnterCogCodePopReceiverReg realCEEnterCogCodePopReceiverReg
  ceCallCogCodePopReceiverReg realCECallCogCodePopReceiverReg
  ceCallCogCodePopReceiverAndClassRegs realCECallCogCodePopReceiverAndClassRegs
  'ceReturnToInterpreterTrampoline' 'ceCannotResumeTrampoline'
  ceTryLockVMOwner ceUnlockVMOwner
  'cmEntryOffset' 'cmNoCheckEntryOffset' 'cmDynSuperEntryOffset' 'cmSelfSendEntryOffset'
  'missOffset' 'cbEntryOffset' 'cbNoSwitchEntryOffset' 'blockNoContextSwitchOffset' breakPC
  ceGetFP ceGetSP cFramePointerInUse
+ traceFlags traceStores)
- traceFlags traceStores debugPrimCallStackOffset)
  includes: var!

Item was changed:
  ----- Method: Cogit>>codeMemmove:_:_: (in category 'generate machine code - dual mapped zone support') -----
  codeMemmove: dest _: src _: bytes
  "Move memory (copy allowing for overlap), applying the codeToDataDelta.
  This is used for code compaction and so works on readable addresses (delta is required)."
  "production uses the macro..."
+ <cmacro: '(dest,src,bytes) memmove((char *)(dest)+codeToDataDelta,src,bytes)'>
- <cmacro: '(dest,src,bytes) memmove((dest)+codeToDataDelta,src,bytes)'>
  self codeWriteBreakpoint: dest.
  "simulation writes twice if simulating dual mapping..."
  codeToDataDelta ~= 0 ifTrue:
  [objectMemory memmove: dest asUnsignedInteger + codeToDataDelta _: src _: bytes].
+ objectMemory memmove: dest _: src _: bytes.
+ "and as a convenience, relocate the breakPC if it is in this method"
+ (breakPC notNil and: [breakPC between: src and: src + bytes - 1]) ifTrue:
+ [breakPC := breakPC - (src - dest)]!
- objectMemory memmove: dest _: src _: bytes!

Item was changed:
  ----- Method: Cogit>>genExternalizePointersForPrimitiveCall (in category 'trampoline support') -----
  genExternalizePointersForPrimitiveCall
- self MoveR: FPReg Aw: coInterpreter framePointerAddress.
  backEnd hasLinkRegister
  ifTrue:
+ [self MoveR: LinkReg Aw: coInterpreter instructionPointerAddress]
+ ifFalse: "Set coInterpreter stackPointer to the topmost argument, skipping the return address."
- ["Set coInterpreter stackPointer to the topmost argument, skipping the return address."
- self MoveR: SPReg Aw: coInterpreter stackPointerAddress.
- self MoveR: LinkReg Aw: coInterpreter instructionPointerAddress]
- ifFalse:
  [self PopR: TempReg. "get retpc"
+ self MoveR: TempReg Aw: coInterpreter instructionPointerAddress].
+ ^backEnd genSaveStackPointers!
- self MoveR: TempReg Aw: coInterpreter instructionPointerAddress.
- "Set coInterpreter stackPointer to the topmost argument, skipping the return address."
- self MoveR: SPReg Aw: coInterpreter stackPointerAddress].
- ^0!

Item was changed:
  ----- Method: Cogit>>genLoadCStackPointersForPrimCall (in category 'trampoline support') -----
  genLoadCStackPointersForPrimCall
+ <inline: #always>
+ cFramePointerInUse
+ ifTrue: [backEnd genLoadCStackPointers]
+ ifFalse: [backEnd genLoadCStackPointer]!
- debugPrimCallStackOffset = 0
- ifTrue:
- [self MoveAw: self cStackPointerAddress R: SPReg]
- ifFalse:
- [self MoveAw: self cStackPointerAddress R: TempReg.
- self SubCq: debugPrimCallStackOffset R: TempReg.
- self MoveR: TempReg R: SPReg].
- cFramePointerInUse ifTrue:
- [self MoveAw: self cFramePointerAddress R: FPReg].
- ^0!

Item was changed:
  ----- Method: Cogit>>handleCallOrJumpSimulationTrap: (in category 'simulation only') -----
  handleCallOrJumpSimulationTrap: aProcessorSimulationTrap
  <doNotGenerate>
  | evaluable function memory result savedFramePointer savedStackPointer savedArgumentCount leaf retpc |
  evaluable := simulatedTrampolines
  at: aProcessorSimulationTrap address
  ifAbsent: [self errorProcessingSimulationTrap: aProcessorSimulationTrap
  in: simulatedTrampolines].
  function := evaluable isBlock
  ifTrue: ['aBlock; probably some plugin primitive']
  ifFalse:
  [evaluable receiver == backEnd ifTrue:
  [^self handleABICallOrJumpSimulationTrap: aProcessorSimulationTrap evaluable: evaluable].
  evaluable selector].
  function ~~ #ceBaseFrameReturn: ifTrue:
  [coInterpreter assertValidExternalStackPointers].
  (backEnd wantsNearAddressFor: function) ifTrue:
  [^self perform: function with: aProcessorSimulationTrap].
  memory := coInterpreter memory.
  aProcessorSimulationTrap type == #call
  ifTrue:
  [(leaf := coInterpreter mcprims includes: function)
  ifTrue:
  [processor
  simulateLeafCallOf: aProcessorSimulationTrap address
  nextpc: aProcessorSimulationTrap nextpc
  memory: memory.
  retpc := processor leafRetpcIn: memory]
  ifFalse:
  [processor
  simulateCallOf: aProcessorSimulationTrap address
  nextpc: aProcessorSimulationTrap nextpc
  memory: memory.
  retpc := processor retpcIn: memory].
  self recordInstruction: {'(simulated call of '. aProcessorSimulationTrap address. '/'. function. ')'}]
  ifFalse:
  [leaf := false.
  processor
  simulateJumpCallOf: aProcessorSimulationTrap address
  memory: memory.
  retpc := processor retpcIn: memory. "sideways call; the primitive has pushed a return address."
  self recordInstruction: {'(simulated jump to '. aProcessorSimulationTrap address. '/'. function. ')'}].
  savedFramePointer := coInterpreter framePointer.
  savedStackPointer := coInterpreter stackPointer.
  savedArgumentCount := coInterpreter argumentCount.
  result := ["self halt: evaluable selector."
      clickConfirm ifTrue:
  [(self confirm: 'skip run-time call?') ifFalse:
  [clickConfirm := false. self halt]].
    evaluable valueWithArguments: (processor
  postCallArgumentsNumArgs: evaluable numArgs
  in: memory)]
  on: ReenterMachineCode
  do: [:ex| ex return: ex returnValue].
 
  coInterpreter assertValidExternalStackPointers.
  "Verify the stack layout assumption compileInterpreterPrimitive: makes, provided we've
  not called something that has built a frame, such as closure value or evaluate method, or
  switched frames, such as primitiveSignal, primitiveWait, primitiveResume, primitiveSuspend et al."
  (function beginsWith: 'primitive') ifTrue:
+ [coInterpreter primFailCode = 0
- [coInterpreter checkForLastObjectOverwrite.
- coInterpreter primFailCode = 0
  ifTrue: [(#( primitiveClosureValue primitiveClosureValueWithArgs primitiveClosureValueNoContextSwitch
  primitiveFullClosureValue primitiveFullClosureValueWithArgs primitiveFullClosureValueNoContextSwitch
  primitiveSignal primitiveWait primitiveResume primitiveSuspend primitiveYield
  primitiveExecuteMethodArgsArray primitiveExecuteMethod
  primitivePerform primitivePerformWithArgs primitivePerformInSuperclass
  primitiveTerminateTo primitiveStoreStackp primitiveDoPrimitiveWithArgs)
  includes: function) ifFalse:
  ["This is a rare case (e.g. in Scorch where a married context's sender is set to nil on trapTrpped and hence the stack layout is altered."
  (function == #primitiveSlotAtPut and: [objectMemory isContext: (coInterpreter frameReceiver: coInterpreter framePointer)]) ifFalse:
  [self assert: savedFramePointer = coInterpreter framePointer.
  self assert: savedStackPointer + (savedArgumentCount * objectMemory wordSize)
  = coInterpreter stackPointer]]]
  ifFalse:
  [self assert: savedFramePointer = coInterpreter framePointer.
  self assert: savedStackPointer = coInterpreter stackPointer]].
  result ~~ #continueNoReturn ifTrue:
  [self recordInstruction: {'(simulated return to '. processor retpcIn: memory. ')'}.
  leaf
  ifTrue: [processor simulateLeafReturnIn: memory]
  ifFalse: [processor simulateReturnIn: memory].
  self assert: processor pc = retpc.
  processor smashCallerSavedRegistersWithValuesFrom: 16r80000000 by: objectMemory wordSize in: memory].
  self assert: (result isInteger "an oop result"
  or: [result == coInterpreter
  or: [result == objectMemory
  or: [#(nil continue continueNoReturn) includes: result]]]).
  processor cResultRegister: (result
  ifNil: [0]
  ifNotNil: [result isInteger
  ifTrue: [result]
  ifFalse: [16rF00BA222]])
 
  "coInterpreter cr.
  processor sp + 32 to: processor sp - 32 by: -4 do:
  [:sp|
  sp = processor sp
  ifTrue: [coInterpreter print: 'sp->'; tab]
  ifFalse: [coInterpreter printHex: sp].
  coInterpreter tab; printHex: (coInterpreter longAt: sp); cr]"!

Item was changed:
  ----- Method: Cogit>>setInterpreter: (in category 'initialization') -----
  setInterpreter: aCoInterpreter
  "Initialization of the code generator in the simulator.
  These objects already exist in the generated C VM
  or are used only in the simulation."
  <doNotGenerate>
  coInterpreter := aCoInterpreter.
  objectMemory := aCoInterpreter objectMemory.
  threadManager := aCoInterpreter threadManager. "N.B. may be nil"
  methodZone := self class methodZoneClass new.
  objectRepresentation := objectMemory objectRepresentationClass
  forCogit: self methodZone: methodZone.
  methodZone setInterpreter: aCoInterpreter
  objectRepresentation: objectRepresentation
  cogit: self.
  generatorTable := self class generatorTable.
  processor := ProcessorClass new.
  simulatedAddresses := Dictionary new.
  coInterpreter class clusteredVariableNames do:
  [:cvn| self simulatedAddressFor: (cvn first = $C ifTrue: ['get', cvn] ifFalse: [cvn]) asSymbol].
  simulatedTrampolines := Dictionary new.
  simulatedVariableGetters := Dictionary new.
  simulatedVariableSetters := Dictionary new.
  traceStores := 0.
  traceFlags := (self class initializationOptions at: #recordPrimTrace ifAbsent: [true])
  ifTrue: [8] "record prim trace on by default (see Cogit class>>decareCVarsIn:)"
  ifFalse: [0].
- debugPrimCallStackOffset := 0.
  singleStep := printRegisters := printInstructions := clickConfirm := false.
  backEnd := CogCompilerClass for: self.
  methodLabel := CogCompilerClass for: self.
  (literalsManager := backEnd class literalsManagerClass new) cogit: self.
  ordinarySendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines).
  superSendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines).
  BytecodeSetHasDirectedSuperSend ifTrue:
  [directedSuperSendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines).
  directedSuperBindingSendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines).
  directedSendUsesBinding := false].
  NewspeakVM ifTrue:
  [selfSendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines).
  dynamicSuperSendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines).
  implicitReceiverSendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines).
  outerSendTrampolines := CArrayAccessor on: (Array new: NumSendTrampolines)].
  "debug metadata"
  objectReferencesInRuntime := CArrayAccessor on: (Array new: NumObjRefsInRuntime).
  runtimeObjectRefIndex := 0.
  "debug metadata"
  trampolineAddresses := CArrayAccessor on: (Array new: NumTrampolines * 2).
  trampolineTableIndex := 0.
 
  extA := numExtB := extB := 0.
 
  compilationTrace ifNil: [compilationTrace := self class initializationOptions at: #compilationTrace ifAbsent: [0]].
  debugOpcodeIndices := self class initializationOptions at: #debugOpcodeIndices ifAbsent: [Set new].
  debugBytecodePointers := self class initializationOptions at: #debugBytecodePointers ifAbsent: [Set new].
  self class initializationOptions at: #breakPC ifPresent: [:pc| breakPC := pc]!

Item was changed:
  ----- Method: Cogit>>simulateLeafCallOf: (in category 'simulation only') -----
  simulateLeafCallOf: someFunction
  "Simulate execution of machine code that leaf-calls someFunction,
  answering the result returned by someFunction."
  "CogProcessorAlienInspector openFor: coInterpreter"
  <doNotGenerate>
+ | priorSP priorPC priorLR spOnEntry bogusRetPC |
- | priorSP priorPC spOnEntry bogusRetPC |
  self recordRegisters.
  priorSP := processor sp.
  priorPC := processor pc.
+ priorLR := backEnd hasLinkRegister ifTrue: [processor lr].
  processor
  setFramePointer: coInterpreter getCFramePointer stackPointer: coInterpreter getCStackPointer;
  simulateLeafCallOf: someFunction
  nextpc: (bogusRetPC := 16rBADF00D5 roundTo: backEnd codeGranularity)
  memory: coInterpreter memory.
  spOnEntry := processor sp.
  self recordInstruction: {'(simulated call of '. someFunction. ')'}.
+ [[processor pc between: 0 and: methodZone zoneEnd] whileTrue:
- [processor pc between: 0 and: methodZone zoneEnd] whileTrue:
  [[singleStep
  ifTrue: [self recordProcessing.
  self maybeBreakAt: processor pc.
  processor
  singleStepIn: coInterpreter memory
  minimumAddress: guardPageSize
  readOnlyBelow: methodZone zoneEnd]
  ifFalse: [processor
  runInMemory: coInterpreter memory
  minimumAddress: guardPageSize
  readOnlyBelow: methodZone zoneEnd]]
  on: ProcessorSimulationTrap, Error
  do: [:ex| | retpc |
  processor pc = bogusRetPC ifTrue:
  [self recordInstruction: {'(simulated (real) return to '. processor pc. ')'}.
  ^processor cResultRegister].
  ex class == ProcessorSimulationTrap ifTrue:
  [ex type == #read ifTrue:
  [self handleReadSimulationTrap: ex. ex resume: processor].
  ex type == #write ifTrue:
  [self handleWriteSimulationTrap: ex. ex resume: processor].
  ex type == #return ifTrue:
  [retpc := processor leafRetpcIn: coInterpreter memory.
  self assert: retpc = bogusRetPC.
  processor simulateLeafReturnIn: coInterpreter memory.
  self recordInstruction: {'(simulated return to '. retpc. ')'}.
  ^processor cResultRegister]].
  ex pass]].
  processor pc = bogusRetPC ifTrue:
  [self recordInstruction: {'(simulated (real) return to '. processor pc. ')'}].
+ ^processor cResultRegister]
+ ensure:
+ [processor sp: priorSP.
+ processor pc: priorPC.
+ priorLR ifNotNil: [:lr| processor lr: lr]]!
- ^processor cResultRegister!

Item was removed:
- ----- Method: CurrentImageCoInterpreterFacade>>getCheckAllocFiller (in category 'cog jit support') -----
- getCheckAllocFiller
- ^coInterpreter getCheckAllocFiller ifNil: [false]!

Item was removed:
- ----- Method: ImageLeakChecker>>checkAllocFiller (in category 'no-op overrides') -----
- checkAllocFiller
- <inline: #always>
- ^false!

Item was changed:
  ----- Method: InterpreterPlugin>>getModuleName (in category 'initialize') -----
  getModuleName
  "Note: This is hardcoded so it can be run from Squeak.
  The module name is used for validating a module *after*
  it is loaded to check if it does really contain the module
  we're thinking it contains. This is important!!"
  <returnTypeC:'const char*'>
  <export: true>
  ^self cCode: [moduleName]
  inSmalltalk:
+ [| string index |
+ string := ((self class codeGeneratorClass new pluginClass: self class) variableDeclarationStringsForVariable: 'moduleName') first.
+ index := (string indexOfSubCollection: 'moduleName = "') + 14.
+ (string copyFrom: index to: (string indexOf: $" startingAt: index + 1) - 1), '(i)']!
- [self class codeGeneratorClass new pluginClass: self class]!

Item was changed:
  ----- Method: InterpreterPrimitives>>canBeImmutable: (in category 'object access primitives') -----
  canBeImmutable: oop
  <option: #IMMUTABILITY>
  | scheduler processLists |
 
  self assert: (objectMemory isNonImmediate: oop).
 
  "For now we fail the primitive for contexts to we ensure there are no immutable contexts.
  Later we can consider having immutable contexts and send cannotReturn callback
  when returning to an immutable context. That would mean that setting a context
  to immutable would require a divorce and returns to immutable context are
  necessarily across stack pages"
  (objectMemory isContext: oop) ifTrue: [ ^ false ].
 
  "Weak structures can't be immutable"
  (objectMemory isEphemeron: oop) ifTrue: [^ false].
  (objectMemory isWeakNonImm: oop) ifTrue: [^ false].
 
  "No clue what is going on for semaphores so they can't be immutable"
  (objectMemory isSemaphoreObj: oop) ifTrue: [^ false].
 
  "Simple version of process management: we forbid Process and LinkedList instances to be immutable
  as well as the Processor and the array of activeProcess"
+ scheduler := self fetchPointer: ValueIndex ofObject: (objectMemory splObj: SchedulerAssociation).
- scheduler := self fetchPointer: ValueIndex ofObject: (self splObj: SchedulerAssociation).
  processLists := objectMemory fetchPointer: ProcessListsIndex ofObject: scheduler.
  oop = scheduler ifTrue: [ ^ false ].
  oop = processLists ifTrue: [ ^ false ].
  "Is it a linkedList ?"
  (objectMemory classIndexOf: (objectMemory fetchPointer: 1 ofObject: processLists)) = (objectMemory classIndexOf: oop) ifTrue: [ ^ false ].
  "is it a Process ?"
  (objectMemory classIndexOf: (objectMemory fetchPointer: ActiveProcessIndex ofObject: scheduler)) =  (objectMemory classIndexOf: oop) ifTrue: [ ^ false ].
 
  "The rest of the code is relative to process management: the Processor (the active
  process scheduler) can't be immutable, as well as all the objects relative to Process management "
  "scheduler := self fetchPointer: ValueIndex ofObject: (self splObj: SchedulerAssociation).
  processLists := objectMemory fetchPointer: ProcessListsIndex ofObject: scheduler.
  ((objectMemory formatOf: oop) = objectMemory nonIndexablePointerFormat)
  ifFalse:
  [ (objectMemory isArrayNonImm: oop) ifFalse: [ ^ true ].
   ^ (oop = processLists) not ].
  (objectMemory numSlotsOf: oop) >= 2 ifFalse: [ ^ true ].
  ""is the oop the scheduler itself ?""
  oop = scheduler ifTrue: [ ^ false ].
  1 to: (objectMemory numSlotsOf: processLists) do: [ :i |
  ""is the oop one of the linked lists ?""
  (list := processLists at: i) = oop ifTrue: [^ false].
  ""is the oop one of the runnable process ?""
  first := objectMemory fetchPointer: FirstLinkIndex ofObject: list.
  first = objectMemory nilObject ifFalse:
  [ last := objectMemory fetchPointer: LastLinkIndex ofObject: list.
   link := first.
   [ link = last ] whileFalse:
  [ link = oop ifTrue: [ ^ false ].
   link := objectMemory fetchPointer: NextLinkIndex ofObject: link. ] ] ]."
  ^ true!

Item was changed:
  ----- Method: NewObjectMemory>>allInstancesOf: (in category 'primitive support') -----
  allInstancesOf: aBehavior
  "Attempt to answer all instances of aBehavior, failing if there is not enough room."
  | count container fillPointer obj byteSize afterPreAllocatedObject |
  "Allocate a large header Array of sufficient size to require a large header.
  Reset its size later."
  container := self instantiateClass: (self splObj: ClassArray) indexableSize: self minLargeHeaderSize.
  self sizeHeader: container putBodySize: 0.
  afterPreAllocatedObject := freeStart.
  freeStart := fillPointer := (self firstFixedField: container) asInteger.
  count := 0.
  obj := self firstObject.
  [self oop: obj isLessThan: container] whileTrue:
  [(self isFreeObject: obj) ifFalse:
  [(self fetchClassOfNonImm: obj) = aBehavior ifTrue:
  [count := count + 1.
  fillPointer < reserveStart ifTrue:
  [self longAt: fillPointer put: obj.
  fillPointer := fillPointer + self bytesPerOop]]].
  obj := self accessibleObjectAfter: obj].
+ fillPointer >= reserveStart ifTrue: "didn't fit; answer instance count so large enough container can be allocated."
+ [^self integerObjectOf: count].
- fillPointer >= reserveStart ifTrue: "didn't fit.  refill with allocation check pattern and answer count."
- [self maybeFillWithAllocationCheckFillerFrom: freeStart to: fillPointer.
- ^self integerObjectOf: count].
  byteSize := fillPointer - (self firstFixedField: container) asInteger.
  self sizeHeader: container putBodySize: byteSize.
- "Need to refill with the allocation check pattern if we shortened the object."
- fillPointer < afterPreAllocatedObject ifTrue:
- [self maybeFillWithAllocationCheckFillerFrom: fillPointer to: afterPreAllocatedObject].
  freeStart := fillPointer.
  ^container!

Item was removed:
- ----- Method: NewObjectMemory>>checkAllocFiller (in category 'allocation') -----
- checkAllocFiller
- <doNotGenerate>
- "in the Spur bootstrap coInterpreter may not be initialized..."
- ^coInterpreter notNil and: [coInterpreter checkAllocFiller]!

Item was changed:
  ----- Method: NewObjectMemory>>initializeMemoryFirstFree: (in category 'initialization') -----
  initializeMemoryFirstFree: firstFree
  "Initialize endOfMemory to the top of oop storage space, reserving some space
  for forwarding blocks, and set freeStart from which space is allocated."
  "Note: The amount of space reserved for forwarding blocks should be chosen to
   ensure that incremental compactions can usually be done in a single pass.
   However, there should be enough forwarding blocks so a full compaction can be done
   in a reasonable number of passes, say ten. (A full compaction requires N object-moving
   passes, where N = number of non-garbage objects / number of forwarding blocks).
 
  di 11/18/2000 Re totalObjectCount: Provide a margin of one byte per object to be
  used for forwarding pointers at GC time. Since fwd blocks are 8 bytes, this means
  an absolute worst case of 8 passes to compact memory. In most cases it will be
  adequate to do compaction in a single pass. "
  | fwdBlockBytes totalReserve |
  "reserve space for forwarding blocks and the interpreter.  We can sacrifice
  forwarding block space at the cost of slower compactions but we cannot
  safely sacrifice interpreter allocation headroom."
  fwdBlockBytes := totalObjectCount bitAnd: WordMask - self wordSize + 1.
  totalReserve := fwdBlockBytes + coInterpreter interpreterAllocationReserveBytes.
  (self oop: memoryLimit - totalReserve isLessThan: firstFree + self baseHeaderSize) ifTrue:
  ["reserve enough space for a minimal free block of BaseHeaderSize bytes.
   We are apparently in an emergency situation here because we have no space
   for reserve and forwarding blocks.  But a full GC will occur immediately in
   sufficientSpaceAfterGC: which will grow memory and restore the reserve."
  fwdBlockBytes := memoryLimit - (firstFree  + self baseHeaderSize)].
 
  "set endOfMemory reserveStart and freeStart"
  self setEndOfMemory: memoryLimit - fwdBlockBytes.
  reserveStart := endOfMemory - coInterpreter interpreterAllocationReserveBytes.
  freeStart := firstFree. "bytes available for oops"
  scavengeThreshold := freeStart + edenBytes min: reserveStart.
- self maybeFillWithAllocationCheckFillerFrom: freeStart to: scavengeThreshold.
 
  self assert: (self oop: freeStart isLessThan: reserveStart).
  "We would like to assert this but can't because in GC situations it may be false.  It is
  established by sufficientSpaceToAllocate: and sufficientSpaceAfterGC:"
  false ifTrue: [self assert: (self oop: reserveStart isLessThan: endOfMemory)].
  self assert: (self oop: endOfMemory isLessThan: memoryLimit)!

Item was removed:
- ----- Method: NewObjectMemory>>maybeFillWithAllocationCheckFillerFrom:to: (in category 'allocation') -----
- maybeFillWithAllocationCheckFillerFrom: start to: end
- "Fill free memory with a bit pattern for checking if the last object has been overwritten."
- <inline: true>
- <var: 'start' type: #usqInt>
- <var: 'end' type: #usqInt>
- <var: 'p' type: #usqInt>
- self checkAllocFiller ifTrue:
- [start to: end by: self wordSize do:
- [:p| self longAt: p put: p]]!

Item was changed:
  ----- Method: NewObjectMemory>>shorten:toIndexableSize: (in category 'allocation') -----
  shorten: obj toIndexableSize: nSlots
  "Reduce the number of indexable fields in obj, a pointer object, to nSlots. Convert the
  unused residual to a free chunk. Word and byte indexable objects are not changed.
  Answer the number of bytes returned to free memory, which may be zero if no change
  was possible."
  | deltaBytes desiredLength fixedFields fmt hdr totalLength
  indexableFields |
  <api>
  (self isPointersNonImm: obj) ifFalse: [^0].
  nSlots >  0
  ifFalse: [^0]. "no change if nSlots is zero, error if nSlots is negative"
  hdr := self baseHeader: obj.
  fmt := self formatOfHeader: hdr.
  totalLength := self lengthOf: obj baseHeader: hdr format: fmt.
  fixedFields := self fixedFieldsOf: obj format: fmt length: totalLength.
  indexableFields := totalLength - fixedFields.
  nSlots >= indexableFields
  ifTrue: [^0]. "no change, or error if attempting to increase size into next chunk"
  desiredLength := fixedFields + nSlots.
  deltaBytes := (totalLength - desiredLength) * self wordSize.
  obj + self baseHeaderSize + (totalLength * self wordSize) = freeStart
  ifTrue: "Shortening the last object.  Need to reduce freeStart."
+ [freeStart := obj + self baseHeaderSize + (desiredLength * self wordSize)]
- [self maybeFillWithAllocationCheckFillerFrom: obj + self baseHeaderSize + (desiredLength * self wordSize) to: freeStart.
- freeStart := obj + self baseHeaderSize + (desiredLength * self wordSize)]
  ifFalse: "Shortening some interior object.  Need to create a free block."
  [self setSizeOfFree: obj + self baseHeaderSize + (desiredLength * self wordSize)
  to: deltaBytes].
  (self headerType: obj) caseOf: {
  [HeaderTypeSizeAndClass] ->
  [self longAt: (obj - (self baseHeaderSize * 2)) put: (self sizeHeader: obj) - deltaBytes].
  [HeaderTypeClass] ->
  [self longAt: obj put: ((hdr bitClear: SizeMask) bitOr: (hdr bitAnd: SizeMask) - deltaBytes)].
  [HeaderTypeShort] ->
  [self longAt: obj put: ((hdr bitClear: SizeMask) bitOr: (hdr bitAnd: SizeMask) - deltaBytes)] }.
  ^deltaBytes!

Item was changed:
  ----- Method: SimpleStackBasedCogit>>compileInterpreterPrimitive:flags: (in category 'primitive generators') -----
  compileInterpreterPrimitive: primitiveRoutine flags: flags
  "Compile a call to an interpreter primitive.  Call the C routine with the
  usual stack-switching dance, test the primFailCode and then either
  return on success or continue to the method body."
  <var: #primitiveRoutine declareC: 'void (*primitiveRoutine)(void)'>
  | jmp jmpSamplePrim continuePostSamplePrim jmpSampleNonPrim continuePostSampleNonPrim |
  <var: #jmp type: #'AbstractInstruction *'>
  <var: #jmpSamplePrim type: #'AbstractInstruction *'>
  <var: #jmpSampleNonPrim type: #'AbstractInstruction *'>
  <var: #continuePostSamplePrim type: #'AbstractInstruction *'>
  <var: #continuePostSampleNonPrim type: #'AbstractInstruction *'>
 
  "Save processor fp, sp and return pc in the interpreter's frame stack and instruction pointers"
  self genExternalizePointersForPrimitiveCall.
  "Switch to the C stack."
  self genLoadCStackPointersForPrimCall.
 
  (flags anyMask: PrimCallCollectsProfileSamples) ifTrue:
  ["Test nextProfileTick for being non-zero and call checkProfileTick if so"
  objectMemory wordSize = 4
  ifTrue:
  [self MoveAw: coInterpreter nextProfileTickAddress R: TempReg.
  self MoveAw: coInterpreter nextProfileTickAddress + objectMemory wordSize R: ClassReg.
  self OrR: TempReg R: ClassReg]
  ifFalse:
  [self MoveAw: coInterpreter nextProfileTickAddress R: TempReg.
  self CmpCq: 0 R: TempReg].
  "If set, jump to record sample call."
  jmpSampleNonPrim := self JumpNonZero: 0.
  continuePostSampleNonPrim := self Label].
 
  "Old full prim trace is in VMMaker-eem.550 and prior"
  self recordPrimTrace ifTrue:
  [self genFastPrimTraceUsing: ClassReg and: SendNumArgsReg].
 
  "Clear the primFailCode and set argumentCount"
  self MoveCq: 0 R: TempReg.
  self MoveR: TempReg Aw: coInterpreter primFailCodeAddress.
  methodOrBlockNumArgs ~= 0 ifTrue:
  [self MoveCq: methodOrBlockNumArgs R: TempReg].
  self MoveR: TempReg Aw: coInterpreter argumentCountAddress.
 
  "If required, set primitiveFunctionPointer and newMethod"
  (flags anyMask: PrimCallNeedsPrimitiveFunction) ifTrue:
  [self MoveCw: primitiveRoutine asInteger R: TempReg.
  primSetFunctionLabel :=
  self MoveR: TempReg Aw: coInterpreter primitiveFunctionPointerAddress].
  (flags anyMask: PrimCallNeedsNewMethod+PrimCallMayCallBack) ifTrue:
  ["The ceActivateFailingPrimitiveMethod: machinery can't handle framelessness."
  (flags anyMask: PrimCallMayCallBack) ifTrue:
  [needsFrame := true].
  methodLabel addDependent:
  (self annotateAbsolutePCRef:
  (self MoveCw: methodLabel asInteger R: ClassReg)).
  self MoveMw: (self offset: CogMethod of: #methodObject) r: ClassReg R: TempReg.
  self MoveR: TempReg Aw: coInterpreter newMethodAddress].
 
  "Invoke the primitive"
  self PrefetchAw: coInterpreter primFailCodeAddress.
  (flags anyMask: PrimCallMayCallBack)
  ifTrue: "Sideways call the C primitive routine so that we return through cePrimReturnEnterCogCode."
  ["On Spur ceActivateFailingPrimitiveMethod: would like to retry if forwarders
   are found. So insist on PrimCallNeedsPrimitiveFunction being set too."
  self assert: (flags anyMask: PrimCallNeedsPrimitiveFunction).
  backEnd
+ genMarshallNArgs: 0 arg: nil arg: nil arg: nil arg: nil;
- genMarshallNArgs: 0 arg: 0 arg: 0 arg: 0 arg: 0;
  genSubstituteReturnAddress:
  ((flags anyMask: PrimCallCollectsProfileSamples)
  ifTrue: [cePrimReturnEnterCogCodeProfiling]
  ifFalse: [cePrimReturnEnterCogCode]).
  primInvokeInstruction := self JumpFullRT: primitiveRoutine asInteger.
  jmp := jmpSamplePrim := continuePostSamplePrim := nil]
  ifFalse:
  ["Call the C primitive routine."
  backEnd genMarshallNArgs: 0 arg: 0 arg: 0 arg: 0 arg: 0.
  primInvokeInstruction := self CallFullRT: primitiveRoutine asInteger.
  backEnd genRemoveNArgsFromStack: 0.
  (flags anyMask: PrimCallCollectsProfileSamples) ifTrue:
  [self assert: (flags anyMask: PrimCallNeedsNewMethod).
  "Test nextProfileTick for being non-zero and call checkProfileTick if so"
  objectMemory wordSize = 4
  ifTrue:
  [self MoveAw: coInterpreter nextProfileTickAddress R: TempReg.
  self MoveAw: coInterpreter nextProfileTickAddress + objectMemory wordSize R: ClassReg.
  self OrR: TempReg R: ClassReg]
  ifFalse:
  [self MoveAw: coInterpreter nextProfileTickAddress R: TempReg.
  self CmpCq: 0 R: TempReg].
  "If set, jump to record sample call."
  jmpSamplePrim := self JumpNonZero: 0.
  continuePostSamplePrim := self Label].
  objectRepresentation maybeCompileRetryOnPrimitiveFail: primitiveIndex.
- self maybeCompileAllocFillerCheck.
  "Switch back to the Smalltalk stack.  Stack better be in either of these two states:
  success: stackPointer -> result (was receiver)
  arg1
  ...
  argN
  return pc
  failure: receiver
  arg1
  ...
  stackPointer -> argN
  return pc
  In either case we can push the instructionPointer or load it into the LinkRegister to reestablish the return pc"
  self MoveAw: coInterpreter instructionPointerAddress
  R: (backEnd hasLinkRegister ifTrue: [LinkReg] ifFalse: [ClassReg]).
  backEnd genLoadStackPointers.
  "Test primitive failure"
  self MoveAw: coInterpreter primFailCodeAddress R: TempReg.
  backEnd hasLinkRegister ifFalse: [self PushR: ClassReg]. "Restore return pc on CISCs"
  self flag: 'ask concrete code gen if move sets condition codes?'.
  self CmpCq: 0 R: TempReg.
  jmp := self JumpNonZero: 0.
  "Fetch result from stack"
  self MoveMw: (backEnd hasLinkRegister ifTrue: [0] ifFalse: [objectMemory wordSize])
  r: SPReg
  R: ReceiverResultReg.
  self RetN: objectMemory wordSize]. "return to caller, popping receiver"
 
  (flags anyMask: PrimCallCollectsProfileSamples) ifTrue:
  ["The sample is collected by cePrimReturnEnterCogCode for external calls"
  jmpSamplePrim ifNotNil:
  ["Call ceCheckProfileTick: to record sample and then continue."
  jmpSamplePrim jmpTarget: self Label.
  self assert: (flags anyMask: PrimCallNeedsNewMethod).
  self CallFullRT: (self cCode: [#ceCheckProfileTick asUnsignedIntegerPtr]
    inSmalltalk: [self simulatedTrampolineFor: #ceCheckProfileTick]).
  "reenter the post-primitive call flow"
  self Jump: continuePostSamplePrim].
  "Null newMethod and call ceCheckProfileTick: to record sample and then continue.
  ceCheckProfileTick will map null/0 to coInterpreter nilObject"
  jmpSampleNonPrim jmpTarget: self Label.
  self MoveCq: 0 R: TempReg.
  self MoveR: TempReg Aw: coInterpreter newMethodAddress.
  self CallFullRT: (self cCode: [#ceCheckProfileTick asUnsignedIntegerPtr]
    inSmalltalk: [self simulatedTrampolineFor: #ceCheckProfileTick]).
  "reenter the post-primitive call flow"
  self Jump: continuePostSampleNonPrim].
 
  jmp ifNotNil:
  ["Jump to restore of receiver reg and proceed to frame build for failure."
  jmp jmpTarget: self Label.
  "Restore receiver reg from stack.  If on RISCs ret pc is in LinkReg, if on CISCs ret pc is on stack."
  self MoveMw: objectMemory wordSize * (methodOrBlockNumArgs + (backEnd hasLinkRegister ifTrue: [0] ifFalse: [1]))
  r: SPReg
  R: ReceiverResultReg].
  ^0!

Item was changed:
  ----- Method: SimpleStackBasedCogit>>compilePrimitive (in category 'primitive generators') -----
  compilePrimitive
  "Compile a primitive.  If possible, performance-critical primtiives will
  be generated by their own routines (primitiveGenerator).  Otherwise,
  if there is a primitive at all, we call the C routine with the usual
  stack-switching dance, test the primFailCode and then either return
  on success or continue to the method body."
  <inline: false>
+ | primitiveDescriptor primitiveRoutine flags |
- | code opcodeIndexAtPrimitive primitiveDescriptor primitiveRoutine flags |
  <var: #primitiveDescriptor type: #'PrimitiveDescriptor *'>
  <var: #primitiveRoutine declareC: 'void (*primitiveRoutine)(void)'>
  primitiveIndex = 0 ifTrue: [^0].
- code := 0.
- "Note opcodeIndex so that compileFallbackToInterpreterPrimitive:
- can discard arg load instructions for unimplemented primitives."
- opcodeIndexAtPrimitive := opcodeIndex.
  "If a descriptor specifies an argument count (by numArgs >= 0) then it must match
  for the generated code to be correct.  For example for speed many primitives use
  ResultReceiverReg instead of accessing the stack, so the receiver better be at
  numArgs down the stack.  Use the interpreter version if not."
  ((primitiveDescriptor := self primitiveGeneratorOrNil) notNil
  and: [primitiveDescriptor primitiveGenerator notNil
+ and: [(primitiveDescriptor primNumArgs < 0 "means generator doesn't care"
- and: [(primitiveDescriptor primNumArgs < 0 "means don't care"
    or: [primitiveDescriptor primNumArgs = (coInterpreter argumentCountOf: methodObj)])]]) ifTrue:
+ [| opcodeIndexAtPrimitive code |
+ "Note opcodeIndex so that any arg load instructions
+ for unimplemented primitives can be discarded."
+ opcodeIndexAtPrimitive := opcodeIndex.
+ code := objectRepresentation perform: primitiveDescriptor primitiveGenerator.
- [code := objectRepresentation perform: primitiveDescriptor primitiveGenerator].
- (code < 0 and: [code ~= UnimplementedPrimitive]) ifTrue: "Generator failed, so no point continuing..."
- [^code].
- code = UnfailingPrimitive ifTrue:
- [^0].
- "If the machine code verison handles all cases the only reason to call the interpreter
- primitive is to reap the primitive error code.  Don't bother if it isn't used."
- (code = CompletePrimitive
- and: [(self methodUsesPrimitiveErrorCode: methodObj header: methodHeader) not]) ifTrue:
- [^0].
- "Discard any arg load code generated by the primitive generator."
- code = UnimplementedPrimitive ifTrue:
- [opcodeIndex := opcodeIndexAtPrimitive].
 
+ (code < 0 and: [code ~= UnimplementedPrimitive]) ifTrue: "Generator failed, so no point continuing..."
+ [^code].
+ "If the primitive can never fail then there is nothing more that needs to be done."
+ code = UnfailingPrimitive ifTrue:
+ [^0].
+ "If the machine code version handles all cases the only reason to call the interpreter
+ primitive is to reap the primitive error code.  Don't bother if it isn't used."
+ (code = CompletePrimitive
+ and: [(self methodUsesPrimitiveErrorCode: methodObj header: methodHeader) not]) ifTrue:
+ [^0].
+ "Discard any arg load code generated by the primitive generator."
+ code = UnimplementedPrimitive ifTrue:
+ [opcodeIndex := opcodeIndexAtPrimitive]].
+
  flags := coInterpreter primitivePropertyFlags: primitiveIndex.
  (flags anyMask: PrimCallDoNotJIT) ifTrue:
  [^ShouldNotJIT].
 
  (flags anyMask: PrimCallOnSmalltalkStack) ifTrue:
  [self assert: flags = PrimCallOnSmalltalkStack.
  ^self compileMachineCodeInterpreterPrimitive: (self cCoerceSimple: (coInterpreter mcprimFunctionForPrimitiveIndex: primitiveIndex)
  to: 'void (*)(void)')].
 
  ((primitiveRoutine := coInterpreter
  functionPointerForCompiledMethod: methodObj
  primitiveIndex: primitiveIndex) = 0 "no primitive"
  or: [primitiveRoutine = (self cCoerceSimple: #primitiveFail to: 'void (*)(void)')]) ifTrue:
  [^self genFastPrimFail].
  minValidCallAddress := minValidCallAddress min: primitiveRoutine asUnsignedInteger.
  ^self compileInterpreterPrimitive: primitiveRoutine flags: flags!

Item was changed:
  ----- Method: SimpleStackBasedCogit>>genPrimReturnEnterCogCodeEnilopmart: (in category 'initialization') -----
  genPrimReturnEnterCogCodeEnilopmart: profiling
  "Generate the substitute return code for an external or FFI primitive call.
  On success simply return, extracting numArgs from newMethod.
  On primitive failure call ceActivateFailingPrimitiveMethod: newMethod."
  | jmpSample continuePostSample jmpFail |
  <var: #jmpSample type: #'AbstractInstruction *'>
  <var: #continuePostSample type: #'AbstractInstruction *'>
  <var: #jmpFail type: #'AbstractInstruction *'>
  self zeroOpcodeIndex.
  backEnd hasVarBaseRegister ifTrue:
  [self MoveCq: self varBaseAddress R: VarBaseReg]. "Must happen sometime"
 
  profiling ifTrue:
  ["Test nextProfileTick for being non-zero and call checkProfileTick: if so.
   N.B. nextProfileTick is 64-bits so 32-bit systems need to test both halves."
  objectMemory wordSize = 4
  ifTrue:
  [self MoveAw: coInterpreter nextProfileTickAddress R: TempReg.
  self MoveAw: coInterpreter nextProfileTickAddress + objectMemory wordSize R: ClassReg.
  self OrR: TempReg R: ClassReg]
  ifFalse:
  [self MoveAw: coInterpreter nextProfileTickAddress R: TempReg.
  self CmpCq: 0 R: TempReg].
  "If set, jump to record sample call."
  jmpSample := self JumpNonZero: 0.
  continuePostSample := self Label].
 
- self maybeCompileAllocFillerCheck.
-
  "Test primitive failure"
  self MoveAw: coInterpreter primFailCodeAddress R: TempReg.
  self flag: 'ask concrete code gen if move sets condition codes?'.
  self CmpCq: 0 R: TempReg.
  jmpFail := self JumpNonZero: 0.
 
  "Switch back to the Smalltalk stack.  Stack better be in either of these two states:
  success: stackPointer -> result (was receiver)
  arg1
  ...
  argN
  return pc
  failure: receiver
  arg1
  ...
  stackPointer -> argN
  return pc
  We push the instructionPointer to reestablish the return pc in the success case,
  but leave it to ceActivateFailingPrimitiveMethod: to do so in the failure case."
 
  backEnd hasLinkRegister
  ifTrue:
  [backEnd genLoadStackPointers. "Switch back to Smalltalk stack."
  backEnd hasPCRegister
  ifTrue:
  [self PopR: ReceiverResultReg. "Pop result from stack"
  self MoveAw: coInterpreter instructionPointerAddress R: PCReg] "Return"
  ifFalse:
  [self MoveMw: 0 r: SPReg R: ReceiverResultReg. "Fetch result from stack"
  self MoveAw: coInterpreter instructionPointerAddress R: LinkReg. "Get ret pc"
  self RetN: objectMemory wordSize]] "Return, popping result from stack"
  ifFalse:
  [self MoveAw: coInterpreter instructionPointerAddress R: ClassReg. "Get return pc"
  backEnd genLoadStackPointers. "Switch back to Smalltalk stack."
  self MoveMw: 0 r: SPReg R: ReceiverResultReg. "Fetch result from stack"
  self MoveR: ClassReg Mw: 0 r: SPReg. "Restore return pc"
  self RetN: 0]. "Return, popping result from stack"
 
  "Primitive failed.  Invoke C code to build the frame and continue."
  jmpFail jmpTarget: (self MoveAw: coInterpreter newMethodAddress R: SendNumArgsReg).
  "Reload sp with CStackPointer; easier than popping args of checkProfileTick."
  self MoveAw: self cStackPointerAddress R: SPReg.
  self
  compileCallFor: #ceActivateFailingPrimitiveMethod:
  numArgs: 1
  arg: SendNumArgsReg
  arg: nil
  arg: nil
  arg: nil
  resultReg: NoReg
  regsToSave: self emptyRegisterMask.
 
  "On Spur ceActivateFailingPrimitiveMethod: may retry the primitive and return if successful.
  So continue by returning to the caller.
  Switch back to the Smalltalk stack.  Stack should be in this state:
  success: stackPointer -> result (was receiver)
  arg1
  ...
  argN
  return pc
  We can push the instructionPointer or load it into the LinkRegister to reestablish the return pc"
  self MoveAw: coInterpreter instructionPointerAddress
  R: (backEnd hasLinkRegister ifTrue: [LinkReg] ifFalse: [ClassReg]).
  backEnd genLoadStackPointers.
  backEnd hasLinkRegister
  ifTrue:
  [self MoveMw: 0 r: SPReg R: ReceiverResultReg] "Fetch result from stack"
  ifFalse:
  [self MoveMw: objectMemory wordSize r: SPReg R: ReceiverResultReg. "Fetch result from stack"
  self PushR: ClassReg]. "Restore return pc on CISCs"
  self RetN: objectMemory wordSize. "return to caller, popping receiver"
 
  profiling ifTrue:
  ["Call ceCheckProfileTick: to record sample and then continue.  newMethod
  should be up-to-date.  Need to save and restore the link reg around this call."
  jmpSample jmpTarget: self Label.
  backEnd saveAndRestoreLinkRegAround:
  [self CallFullRT: (self cCode: '(usqIntptr_t)ceCheckProfileTick'
  inSmalltalk: [self simulatedTrampolineFor: #ceCheckProfileTick])].
  self Jump: continuePostSample]!

Item was removed:
- ----- Method: SimpleStackBasedCogit>>maybeCompileAllocFillerCheck (in category 'primitive generators') -----
- maybeCompileAllocFillerCheck
- "If allocCheckFiller is true, words in newSpace from freeStart to scavengeThreshold
- are filled with their address, and after each call of a plugin primitive, the VM checks
- that freeStart points to a word containing the value of freeStart.  This is a simple
- check for primitives overwriting the ends of an object."
- | jmpOk |
- <var: #jmpOk type: #'AbstractInstruction *'>
- coInterpreter getCheckAllocFiller ifTrue:
- [self MoveAw: objectMemory freeStartAddress R: ClassReg.
- self MoveMw: 0 r: ClassReg R: TempReg.
- self CmpR: ClassReg R: TempReg.
- jmpOk := self JumpZero: 0.
- self MoveCq: PrimErrWritePastObject R: TempReg.
- self MoveR: TempReg Aw: coInterpreter primFailCodeAddress.
- jmpOk jmpTarget: self Label]!

Item was changed:
  ----- Method: SpurGenerationScavenger>>newSpaceStart:newSpaceBytes:survivorBytes: (in category 'initialization') -----
  newSpaceStart: startAddress newSpaceBytes: totalBytes survivorBytes: requestedSurvivorBytes
  | actualEdenBytes survivorBytes |
  survivorBytes := requestedSurvivorBytes truncateTo: manager allocationUnit.
  actualEdenBytes := totalBytes - survivorBytes - survivorBytes truncateTo: manager allocationUnit.
  self assert: totalBytes - actualEdenBytes - survivorBytes - survivorBytes < manager allocationUnit.
 
  "for tenuring we require older objects below younger objects.  since allocation
  grows up this means that the survivor spaces must precede eden."
 
  pastSpace start: startAddress; limit: startAddress + survivorBytes.
  futureSpace start: pastSpace limit; limit: pastSpace limit + survivorBytes.
  eden start: futureSpace limit; limit: startAddress + totalBytes.
 
  self assert: self futureSpace limit <= (startAddress + totalBytes).
  self assert: self eden start \\ manager allocationUnit
  + (self eden limit \\ manager allocationUnit) = 0.
  self assert: self pastSpace start \\ manager allocationUnit
  + (self pastSpace limit \\ manager allocationUnit) = 0.
  self assert: self futureSpace start \\ manager allocationUnit
  + (self futureSpace limit \\ manager allocationUnit) = 0.
 
  self initFutureSpaceStart.
- manager initSpaceForAllocationCheck: (self addressOf: eden) limit: eden limit.
 
  tenuringProportion := 0.9!

Item was removed:
- ----- Method: SpurMemoryManager>>checkAllocFiller (in category 'allocation') -----
- checkAllocFiller
- <doNotGenerate>
- "in the Spur bootstrap coInterpreter may not be initialized..."
- ^coInterpreter notNil and: [coInterpreter checkAllocFiller]!

Item was changed:
  ----- Method: SpurMemoryManager>>containsOnlyValidBecomeObjects:and:twoWay:copyHash: (in category 'become implementation') -----
  containsOnlyValidBecomeObjects: array1 and: array2 twoWay: isTwoWay copyHash: copyHash
  "Answer 0 if neither array contains an object inappropriate for the become operation.
  Otherwise answer an informative error code for the first offending object found:
  Can't become: immediates => PrimErrInappropriate
  Shouldn't become pinned objects => PrimErrObjectIsPinned.
  Shouldn't become immutable objects => PrimErrNoModification.
  Can't copy hash into immediates => PrimErrInappropriate.
  Two-way become may require memory to create copies => PrimErrNoMemory.
  As a side-effect unforward any forwarders in the two arrays if answering 0."
  <inline: true>
  | fieldOffset effectsFlags oop1 oop2 size |
  fieldOffset := self lastPointerOf: array1.
  effectsFlags := size := 0.
  "array1 is known to be the same size as array2"
  [fieldOffset >= self baseHeaderSize] whileTrue:
  [oop1 := self longAt: array1 + fieldOffset.
  (self isOopForwarded: oop1) ifTrue:
  [oop1 := self followForwarded: oop1.
  self longAt: array1 + fieldOffset put: oop1].
  self ifOopInvalidForBecome: oop1 errorCodeInto: [:errCode| ^errCode].
- effectsFlags := effectsFlags bitOr: (self becomeEffectFlagsFor: oop1).
  oop2 := self longAt: array2 + fieldOffset.
  (self isOopForwarded: oop2) ifTrue:
  [oop2 := self followForwarded: oop2.
  self longAt: array2 + fieldOffset put: oop2].
  isTwoWay
  ifTrue:
  [self ifOopInvalidForBecome: oop2 errorCodeInto: [:errCode| ^errCode].
+ oop1 ~= oop2 ifTrue:
+ [size := size + (self bytesInObject: oop1) + (self bytesInObject: oop2).
+ effectsFlags := (effectsFlags
+ bitOr: (self becomeEffectFlagsFor: oop1))
+ bitOr: (self becomeEffectFlagsFor: oop2)]]
- size := size + (self bytesInObject: oop1) + (self bytesInObject: oop2).
- effectsFlags := effectsFlags bitOr: (self becomeEffectFlagsFor: oop2)]
  ifFalse:
  [(copyHash and: [(self isImmediate: oop2) or: [self isImmutable: oop2]]) ifTrue:
+ [^PrimErrInappropriate].
+ oop1 ~= oop2 ifTrue:
+ [effectsFlags := effectsFlags bitOr: (self becomeEffectFlagsFor: oop1)]].
- [^PrimErrInappropriate]].
  fieldOffset := fieldOffset - self bytesPerOop].
  size >= (totalFreeOldSpace + (scavengeThreshold - freeStart)) ifTrue:
  [^PrimErrNoMemory].
  "only set flags after checking all args."
  becomeEffectsFlags := effectsFlags.
  ^0!

Item was changed:
  ----- Method: SpurMemoryManager>>doScavenge: (in category 'gc - scavenging') -----
  doScavenge: tenuringCriterion
  "The inner shell for scavenge, abstrascted out so globalGarbageCollect can use it."
  <inline: false>
  self doAllocationAccountingForScavenge.
  gcPhaseInProgress := ScavengeInProgress.
  pastSpaceStart := scavenger scavenge: tenuringCriterion.
  self assert: (self
  oop: pastSpaceStart
  isGreaterThanOrEqualTo: scavenger pastSpace start
  andLessThanOrEqualTo: scavenger pastSpace limit).
  freeStart := scavenger eden start.
- self initSpaceForAllocationCheck: (self addressOf: scavenger eden) limit: scavengeThreshold.
  gcPhaseInProgress := 0.
  self resetAllocationAccountingAfterGC!

Item was removed:
- ----- Method: SpurMemoryManager>>initSpaceForAllocationCheck:limit: (in category 'allocation') -----
- initSpaceForAllocationCheck: aNewSpace limit: limit
- <var: 'aNewSpace' type: #'SpurNewSpaceSpace *'>
- <var: 'limit' type: #usqInt>
- memory ifNotNil:
- [self checkAllocFiller ifTrue:
- [aNewSpace start
- to: limit - 1
- by: self wordSize
- do: [:p| self longAt: p put: p]]]!

Item was changed:
  ----- Method: SpurMemoryManager>>initializeNewSpaceVariables (in category 'gc - scavenging') -----
  initializeNewSpaceVariables
  <inline: #never>
  freeStart := scavenger eden start.
  pastSpaceStart := scavenger pastSpace start.
  scavengeThreshold := scavenger eden limit
  - (scavenger edenBytes // 64)
  - coInterpreter interpreterAllocationReserveBytes.
  newSpaceStart := scavenger pastSpace start min: scavenger futureSpace start.
+ self assert: newSpaceStart < scavenger eden start!
- self assert: newSpaceStart < scavenger eden start.
- self initSpaceForAllocationCheck: (self addressOf: scavenger eden) limit: scavengeThreshold!

Item was changed:
  ----- Method: SpurMemoryManager>>queueMourner: (in category 'weakness and ephemerality') -----
  queueMourner: anEphemeronOrWeakArray
+ "Add the ephemeron or weak array to the queue."
- "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 formatOf: anEphemeronOrWeakArray) = self ephemeronFormat
  and: [self is: anEphemeronOrWeakArray onObjStack: mournQueue]).
  self ensureRoomOnObjStackAt: MournQueueRootIndex.
+ "There is no point queueing weak arrays more than once.  Note that it should be impossible
+ for ephemerons to be enqueued more than once since they are turned into non-ephemerons
+ in the relevant sender.  Alas this means that other ephemerons on the same object not
+ identified in this scavenge or GC will not fire until later.  But that's life."
+ (self isEphemeron: anEphemeronOrWeakArray) ifFalse:
+ [(self is: anEphemeronOrWeakArray onObjStack: mournQueue) ifTrue:
+ [^self]].
  self push: anEphemeronOrWeakArray onObjStack: mournQueue!

Item was changed:
  InterpreterPrimitives subclass: #StackInterpreter
(excessive size, no diff calculated)

Item was changed:
  ----- Method: StackInterpreter>>callExternalPrimitive: (in category 'plugin primitive support') -----
  callExternalPrimitive: functionID
  "Call the external plugin function identified. In the VM this is an address;
  see StackInterpreterSimulator for its version."
 
  <var: #functionID declareC: 'void (*functionID)()'>
  "Spur needs the primitiveFunctionPointer to be set correctly
  for accurate following of forwarders on primitive failure."
  objectMemory hasSpurMemoryManagerAPI ifTrue:
  [primitiveFunctionPointer := functionID].
+ self dispatchFunctionPointer: functionID!
- self dispatchFunctionPointer: functionID.
- self maybeFailForLastObjectOverwrite.!

Item was removed:
- ----- Method: StackInterpreter>>checkAllocFiller (in category 'primitive support') -----
- checkAllocFiller
- "If allocCheckFiller is true, words in newSpace from freeStart to scavengeThreshold
- are filled with their address, and after each call of a plugin primitive, the VM checks
- that freeStart points to a word containing the value of freeStart.  This is a simple
- check for primitives overwriting the ends of an object."
- <cmacro: '() GIV(checkAllocFiller)'>
- ^checkAllocFiller!

Item was removed:
- ----- Method: StackInterpreter>>checkForLastObjectOverwrite (in category 'simulation') -----
- checkForLastObjectOverwrite
- <doNotGenerate>
- | freeStart |
- checkAllocFiller ifTrue:
- [self assert: ((freeStart := objectMemory freeStart) >= objectMemory scavengeThreshold
-  or: [(objectMemory longAt: freeStart) = freeStart])]!

Item was changed:
  ----- Method: StackInterpreter>>initialize (in category 'initialization') -----
  initialize
  "Here we can initialize the variables C initializes to zero.  #initialize methods do /not/ get translated."
  super initialize.
  primitiveDoMixedArithmetic := true. "whether we authorize primitives to perform mixed arithmetic or not"
- checkAllocFiller := false. "must precede initializeObjectMemory:"
  stackLimit := 0. "This is also the initialization flag for the stack system."
  stackPage := overflowedPage := 0.
  extraFramesToMoveOnOverflow := 0.
  bytecodeSetSelector := 0.
  highestRunnableProcessPriority := 0.
  nextPollUsecs := 0.
  nextWakeupUsecs := 0.
  tempOop := tempOop2 := theUnknownShort := 0.
  interruptPending := false.
  inIOProcessEvents := 0.
  fullScreenFlag := 0.
  sendWheelEvents := deferDisplayUpdates := false.
  displayBits := displayWidth := displayHeight := displayDepth := 0.
  pendingFinalizationSignals := statPendingFinalizationSignals := 0.
  globalSessionID := 0.
  jmpDepth := 0.
  longRunningPrimitiveStartUsecs := longRunningPrimitiveStopUsecs := 0.
  maxExtSemTabSizeSet := false.
  debugCallbackInvokes := debugCallbackPath := debugCallbackReturns := 0.
  statForceInterruptCheck := statStackOverflow := statCheckForEvents :=
  statProcessSwitch := statIOProcessEvents := statStackPageDivorce :=
  statIdleUsecs := 0!

Item was removed:
- ----- Method: StackInterpreter>>maybeFailForLastObjectOverwrite (in category 'primitive support') -----
- maybeFailForLastObjectOverwrite
- <inline: true>
- checkAllocFiller ifTrue:
- [(objectMemory freeStart < objectMemory scavengeThreshold
-  and: [(objectMemory longAt: objectMemory freeStart) ~= objectMemory freeStart]) ifTrue:
- [self primitiveFailFor: PrimErrWritePastObject]]!

Item was removed:
- ----- Method: StackInterpreter>>setCheckAllocFiller: (in category 'primitive support') -----
- setCheckAllocFiller: aBool
- "If allocCheckFiller is true, words in newSpace from freeStart to scavengeThreshold
- are filled with their address, and after each call of a plugin primitive, the VM checks
- that freeStart points to a word containing the value of freeStart.  This is a simple
- check for primitives overwriting the ends of an object."
- checkAllocFiller := aBool!

Item was changed:
  ----- Method: StackInterpreter>>slowPrimitiveResponse (in category 'primitive support') -----
  slowPrimitiveResponse
  "Invoke a normal (non-quick) primitive.
  Called under the assumption that primFunctionPointer has been preloaded."
  | nArgs savedFramePointer savedStackPointer |
  <inline: true>
  <var: #savedFramePointer type: #'char *'>
  <var: #savedStackPointer type: #'char *'>
  self assert: (objectMemory isOopForwarded: (self stackValue: argumentCount)) not.
  self assert: objectMemory remapBufferCount = 0.
  FailImbalancedPrimitives ifTrue:
  [nArgs := argumentCount.
  savedStackPointer := stackPointer.
  savedFramePointer := framePointer].
  self initPrimCall.
  self dispatchFunctionPointer: primitiveFunctionPointer.
  self assert: (self maybeLeakCheckExternalPrimCall: newMethod).
  self maybeRetryPrimitiveOnFailure.
- self maybeFailForLastObjectOverwrite.
  (FailImbalancedPrimitives
  and: [self successful
  and: [framePointer = savedFramePointer
  and: [(self isMachineCodeFrame: framePointer) not]]]) ifTrue:"Don't fail if primitive has done something radical, e.g. perform:"
  [stackPointer ~= (savedStackPointer + (nArgs * objectMemory wordSize)) ifTrue:
  [self flag: 'Would be nice to make this a message send of e.g. unbalancedPrimitive to the current process or context'.
  "This is necessary but insufficient; the result may still have been written to the stack.
    At least we'll know something is wrong."
  self failUnbalancedPrimitive.
  stackPointer := savedStackPointer]].
  "If we are profiling, take accurate primitive measures"
  nextProfileTick > 0 ifTrue:
  [self checkProfileTick: newMethod].
  ^self successful!

Item was changed:
  ----- Method: TAssignmentNode>>emitValueExpansionOn:level:generator: (in category 'C code generation') -----
  emitValueExpansionOn: aStream level: level generator: aCodeGen
+ | stmtList lastStmt copiedStatements |
- | stmtList lastStmt copy |
  self assert: (expression isSend and: [expression isValueExpansion]).
  stmtList := expression receiver.
  lastStmt := stmtList statements last.
  lastStmt = variable ifTrue:
  [^expression emitCCodeOn: aStream level: level generator: aCodeGen].
+ copiedStatements := stmtList copy.
+ copiedStatements statements
- copy := stmtList copy.
- copy statements
  at: stmtList statements size
  put: (TAssignmentNode new
  setVariable: variable
  expression: lastStmt).
+ expression copy
+ receiver: copiedStatements;
+ emitCCodeOn: aStream level: level generator: aCodeGen!
- (TSendNode new
- setSelector: expression selector
- receiver: copy
- arguments: expression args)
- emitCCodeOn: aStream level: level generator: aCodeGen.!

Item was changed:
  ----- Method: VMClass>>strncpy:_:_: (in category 'C library simulation') -----
  strncpy: dest _: src _: n
  <doNotGenerate>
  "implementation of strncpy(3).
  See e.g. https://manpages.debian.org/stretch/manpages-dev/strncpy.3.en.html
  The C version always takes an address; the simulation allows a String, ByteArray,
  CArray or address within the simulation object memory (Positive Integer)"
  | getBlock setBlock count |
  count := n.
  "Determine the source and destination access blocks based on the parameter type"
  getBlock := src isCollection
  ifTrue:
  [count := count min: src size.
  src isString
  ifTrue: [[ :idx | src basicAt: idx]] "basicAt: answers integers"
  ifFalse:
  [src class == ByteArray ifTrue:
  [[ :idx | src at: idx]]]]
  ifFalse:
  [src isInteger
  ifTrue: [[ :idx | self byteAt: src + idx - 1]]
  ifFalse:
  [src isCArray ifTrue:
  [[ :idx | src at: idx - 1]]]].
  getBlock ifNil: [self error: 'unhandled type of source string'].
  setBlock := dest isCollection
  ifTrue:
  [dest isString
  ifTrue: [[ :idx | dest basicAt: idx put: (getBlock value: idx)]] "basicAt:put: stores integers"
  ifFalse:
  [dest class == ByteArray ifTrue:
  [[ :idx | dest at: idx put: (getBlock value: idx)]]]]
  ifFalse:
  [dest isInteger ifTrue:
+ [[ :idx | self byteAt: dest + idx - 1 put: (getBlock value: idx)]]
+ ifFalse:
+ [dest isCArray ifTrue:
+ [[ :idx | dest at: idx - 1 put: (getBlock value: idx)]]]].
- [[ :idx | self byteAt: dest + idx - 1 put: (getBlock value: idx)]]].
  setBlock ifNil: [self error: 'unhandled type of destination string'].
  1 to: count do: setBlock.
  "SVr4, 4.3BSD, C89, C99 require the remainder of the buffer be filled with nulls"
  getBlock := [:idx| 0].
  count + 1 to: n do: setBlock.
  ^dest!