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

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

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

Name: VMMaker.oscog-eem.2022
Author: eem
Time: 2 December 2016, 5:01:48.877113 pm
UUID: 0f8040b3-62e2-4f22-af99-5ba2cfb4974f
Ancestors: VMMaker.oscog-eem.2021

RegisterAllocatingCogit:
Abstract away voiding the optStatus into voidReceiverOptStatus so that RegisterAllocatingCogit can override to clear the liveRegister on simSelf.

Add voidReceiverResultRegContainsSelf that scans the stack for occurrences of simSelf in a register on stack and to either flush (StackToRegisterMappingCogit) or void liveRegister (RegisterAllocatingCogit).  Except that I suspect RegisterAllocatingCogit may have to flush in certain curcumstances too.

Add asserts to check that optStatus and simSelf are in sync w.r.t. ReceiverResultReg.

Fix a bad slip in existsInstVarRefBeforeSendOrReturn.

Nuke an obsolete NewspeakV3 bytecode.

Very close to working properly.  System no longer crashes in JIT or assert failure but runs, albeit looping on an MNU.

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

Item was changed:
  ----- Method: CogRegisterAllocatingSimStackEntry>>popToReg: (in category 'compile abstract instructions') -----
  popToReg: reg
  <var: #inst type: #'AbstractInstruction *'>
  liveRegister ~= NoReg
  ifTrue:
+ [self deny: (type = SSRegister and: [register ~= liveRegister and: [cogit needsFrame]]).
+ spilled ifTrue: "This is rare, and in some cases it isn't even needed (e.g. frameful return) but we can't tell as yet."
+ [cogit AddCq: objectRepresentation wordSize R: SPReg].
- [self deny: spilled.
- self deny: (type = SSRegister and: [register ~= liveRegister]).
  reg ~= liveRegister
  ifTrue: [cogit MoveR: liveRegister R: reg]
  ifFalse: [cogit Label]]
  ifFalse:
  [spilled
  ifTrue:
  [cogit PopR: reg]
  ifFalse:
  [type caseOf: {
  [SSBaseOffset] -> [cogit MoveMw: offset r: register R: reg].
  [SSConstant] -> [cogit genMoveConstant: constant R: reg].
  [SSRegister] -> [reg ~= register
  ifTrue: [cogit MoveR: register R: reg]
  ifFalse: [cogit Label]] }]].
 
  (reg ~= TempReg and: [reg ~= liveRegister and: [type ~= SSRegister]]) ifTrue:
  [liveRegister := reg.
  cogit copyLiveRegisterToCopiesOf: self]!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>captureUnspilledSpillsForSpecialSelectorSend: (in category 'bytecode generator support') -----
  captureUnspilledSpillsForSpecialSelectorSend: liveRegisterMask
  "Since we're allocating values in registers we would like to keep those registers live on the inlined path
  and reload registers along the non-inlined send path.  But any values that would need to be spilled
  along the non-inlined path must be captured before the split so that both paths can join.  If we don't
  capture the values on the non-inlined path we could access stale values.  So for all stack entries that
  would be spilled along the non-inlined path, assign them to registers, or spill if none are available."
  | i liveRegs reg |
  liveRegs := liveRegisterMask.
+ self assert: (simSelf liveRegister = ReceiverResultReg) = optStatus isReceiverResultRegLive.
  optStatus isReceiverResultRegLive ifTrue:
  [liveRegs := liveRegs + (self registerMaskFor: ReceiverResultReg)].
  reg := TempReg. "Anything but NoReg"
  i := simStackPtr + 1. "We must spill a contiguous range at the hot top of stack, so we assign coldest first :-("
  [reg ~= NoReg and: [i > simSpillBase and: [i > 0]]] whileTrue:
  [i := i - 1.
  ((self simStackAt: i) spilled not
   and: [(self simStackAt: i) type = SSBaseOffset]) ifTrue:
  [reg := self allocateRegNotConflictingWith: liveRegs.
  reg ~= NoReg ifTrue:
  [(self simStackAt: i) storeToReg: reg.
  liveRegs := liveRegs bitOr: (self registerMaskFor: reg)]]].
  reg = NoReg ifTrue:
  [self ssFlushTo: i]!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>ensureReceiverResultRegContainsSelf (in category 'bytecode generator support') -----
  ensureReceiverResultRegContainsSelf
  super ensureReceiverResultRegContainsSelf.
+ methodOrBlockNumTemps to: simStackPtr do:
- 0 to: simStackPtr do:
  [:i|
  (simSelf isSameEntryAs: (self simStackAt: i))
  ifTrue: [(self simStackAt: i) liveRegister: ReceiverResultReg]
  ifFalse:
  [(self simStackAt: i) liveRegister = ReceiverResultReg ifTrue:
  [(self simStackAt: i) liveRegister: NoReg]]].
  simSelf liveRegister: ReceiverResultReg!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>existsInstVarRefBeforeSendOrReturn (in category 'bytecode generator support') -----
  existsInstVarRefBeforeSendOrReturn
  "Answer if the current bytecode is followed by an inst var ref before the next full send."
+ | pc nExts byte descriptor |
- | pc nExts descriptor |
  pc := bytecodePC.
  nExts := 0.
  [pc <= endPC] whileTrue:
+ [byte := (objectMemory fetchByte: pc ofObject: methodObj) + bytecodeSetOffset.
+ descriptor := self generatorAt: byte.
- [descriptor := self generatorAt: pc.
  (descriptor isMapped
   or: [descriptor isBranchTrue
   or: [descriptor isBranchFalse
   or: [descriptor spanFunction notNil]]]) ifTrue:
  [^false].
  descriptor isInstVarRef ifTrue:
  [^true].
  nExts := descriptor isExtension ifTrue: [nExts + 1] ifFalse: [0].
  pc := self nextBytecodePCFor: descriptor at: pc exts: nExts in: methodObj].
  ^false!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>moveVolatileSimStackEntriesToRegisters (in category 'bytecode generator support') -----
  moveVolatileSimStackEntriesToRegisters
  "When jumping forward to a merge point the stack mst be reconcilable with the state that falls through to the merge point.
  We cannot easily arrange that later we add code to the branch, e.g. to spill values.  Instead, any volatile contents must be
  moved to registers.  [In fact, that's not exactly true, consider these two code sequences:
  self at: (expr ifTrue: [1] ifFalse: [2]) put: a
  self at: 1 put: (expr ifTrue: [a] ifFalse: [b])
  The first one needs 1 saving to a register to reconcile with 2.
  The second one has 1 on both paths, but we're not clever enough to spot this case yet.]
+ Volatile contents are anything not spilled to the stack, because as yet we can only merge registers."
- Volatile contents are constants and base-offset references other than temporaries and spills (regsier ~= FPReg)"
  <inline: true>
  <var: #desc type: #'SimStackEntry *'>
  (simSpillBase max: 0) to: simStackPtr do:
  [:i| | desc reg |
  desc := self simStackAt: i.
+ desc spilled
+ ifTrue: [simSpillBase := i]
+ ifFalse:
+ [desc registerOrNone = NoReg ifTrue:
+ [reg := self allocateRegNotConflictingWith: 0.
+ reg = NoReg
+ ifTrue: [self halt] "have to spill"
+ ifFalse: [desc storeToReg: reg]]]]!
- ((desc type = SSConstant or: [desc type = SSBaseOffset and: [desc register ~= FPReg]])
-  and: [desc liveRegister = NoReg]) ifTrue:
- [reg := self allocateRegNotConflictingWith: 0.
- reg = NoReg
- ifTrue: [self halt] "have to spill"
- ifFalse: [desc storeToReg: reg]]]!

Item was added:
+ ----- Method: RegisterAllocatingCogit>>needsFrame (in category 'accessing') -----
+ needsFrame
+ "for asserts"
+ <cmacro: '() needsFrame'>
+ ^needsFrame!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>reconcileRegisterStateForJoinAfterSpecialSelectorSend (in category 'bytecode generator support') -----
  reconcileRegisterStateForJoinAfterSpecialSelectorSend
  "When the control flow from the inlined special selector code (e.g. add or comparison)
  joins the control flow from the send, taken when the inlined code fails, we should decide
  whether to reload any registers known to contain useful values or mark them as dead."
 
  "If ReceiverResultReg is live along the inlined path, and is used before the next full send,
  reload it on the uncommon path."
  scratchOptStatus isReceiverResultRegLive ifTrue:
  [(self existsInstVarRefBeforeSendOrReturn
   or: [self receiverRefOnScratchSimStack])
  ifTrue:
  [optStatus isReceiverResultRegLive: true.
  optStatus ssEntry storeToReg: ReceiverResultReg]
+ ifFalse: [self voidReceiverOptStatus]].
- ifFalse:
- [optStatus isReceiverResultRegLive: false.
- optStatus ssEntry liveRegister: NoReg]].
 
  "Restore the simStack to that in scratchSimStack,
  popping any spilled state back into allocated registers."
  simSpillBase := scratchSpillBase.
  simStackPtr to: 0 by: -1 do:
  [:i|
  self assert: (i = simStackPtr
  ifTrue: [(self simStackAt: i) type = SSRegister]
  ifFalse: [(self simStackAt: i) spilled]).
  (self simStackAt: i) reconcilePoppingWith: (self simStack: scratchSimStack at: i).
  simStack
  at: i
  put: (self
  cCode: [scratchSimStack at: i]
  inSmalltalk: [(scratchSimStack at: i) copy])]!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>restoreSimStackAtMergePoint: (in category 'simulation stack') -----
  restoreSimStackAtMergePoint: fixup
  <inline: true>
  "All the execution paths reaching a merge point expect everything to be spilled
  on stack and the optStatus is unknown.  If the merge point follows a return, it
  isn't a merge, but a skip past a return.  If it is a real merge point then throw
  away all simStack and optStatus optimization state."
+ simSelf liveRegister: ((optStatus isReceiverResultRegLive: fixup isReceiverResultRegSelf)
+ ifTrue: [ReceiverResultReg]
+ ifFalse: [NoReg]).
- (optStatus isReceiverResultRegLive: fixup isReceiverResultRegSelf) ifFalse:
- [simSelf liveRegister: NoReg].
  fixup mergeSimStack ifNotNil:
  [simSpillBase := methodOrBlockNumTemps.
  0 to: simStackPtr do:
  [:i|
  self cCode: [simStack at: i put: (fixup mergeSimStack at: i)]
  inSmalltalk: [(simStack at: i) copyFrom: (fixup mergeSimStack at: i)]]].
  ^0!

Item was added:
+ ----- Method: RegisterAllocatingCogit>>simSelfOnStackInReceiverResultReg (in category 'bytecode generator support') -----
+ simSelfOnStackInReceiverResultReg
+ "For assert checking only."
+ methodOrBlockNumArgs to: simStackPtr do:
+ [:i|
+ ((simSelf isSameEntryAs: (self simStackAt: i))
+  and: [(self simStackAt: i) registerOrNone = ReceiverResultReg]) ifTrue:
+ [^true]].
+ ^false!

Item was added:
+ ----- Method: RegisterAllocatingCogit>>voidReceiverOptStatus (in category 'bytecode generator support') -----
+ voidReceiverOptStatus
+ "Used to mark ReceiverResultReg as dead or not containing simSelf.
+ Used when the simStack has already been flushed, e.g. for sends."
+ <inline: true>
+ super voidReceiverOptStatus.
+ simSelf liveRegister: NoReg!

Item was added:
+ ----- Method: RegisterAllocatingCogit>>voidReceiverResultRegContainsSelf (in category 'bytecode generator support') -----
+ voidReceiverResultRegContainsSelf
+ "Used when ReceiverResultReg is allocated for other than simSelf, and
+ there may be references to ReceiverResultReg which need to be spilled."
+ self assert: (simSelf liveRegister = ReceiverResultReg) = optStatus isReceiverResultRegLive.
+ optStatus isReceiverResultRegLive ifFalse:
+ [self deny: self simSelfOnStackInReceiverResultReg.
+ ^self].
+ optStatus isReceiverResultRegLive: false.
+ methodOrBlockNumTemps to: simStackPtr do:
+ [:i|
+ (simSelf isSameEntryAs: (self simStackAt: i)) ifTrue:
+ [(self simStackAt: i) liveRegister: NoReg]].
+ simSelf liveRegister: NoReg!

Item was removed:
- ----- Method: SimpleStackBasedCogit>>genPushEnclosingObjectBytecode (in category 'bytecode generators') -----
- genPushEnclosingObjectBytecode
- "Uncached push enclosing object"
- | levelOop |
- levelOop := self getLiteral: byte1.
- self assert: (objectMemory isIntegerObject: levelOop).
- ^self genPushEnclosingObjectAt: (objectMemory integerValueOf: levelOop)!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>allocateRegForStackTopThreeEntriesInto:thirdIsReceiver: (in category 'simulation stack') -----
  allocateRegForStackTopThreeEntriesInto: trinaryBlock thirdIsReceiver: thirdIsReceiver
  "Answers registers for the 3 top values on stack. If the values are already in registers, answers
  these registers, else allocate registers not conflicting with each others.
  If thirdIsReceiver is true, allocate ReceiverResultReg for stackTop - 2 (for ceStoreCheck)."
  <inline: true>
  | topRegistersMask rTop rNext rThird |
 
  topRegistersMask := 0.
  rTop := rNext := rThird := NoReg.
 
  (self ssTop registerOrNone ~= NoReg and: [ thirdIsReceiver not or: [ self ssTop registerOrNone ~= ReceiverResultReg ] ]) ifTrue:
  [ topRegistersMask := self registerMaskFor: (rTop := self ssTop registerOrNone)].
  ((self ssValue: 1) registerOrNone ~= NoReg and: [ thirdIsReceiver not or: [ (self ssValue: 1) registerOrNone ~= ReceiverResultReg ] ]) ifTrue:
  [ topRegistersMask := topRegistersMask bitOr: (self registerMaskFor: (rNext := (self ssValue: 1) registerOrNone))].
  ((self ssValue: 2) registerOrNone ~= NoReg and: [thirdIsReceiver not or: [ (self ssValue: 2) registerOrNone = ReceiverResultReg ] ]) ifTrue:
  [ topRegistersMask := topRegistersMask bitOr: (self registerMaskFor: (rThird := (self ssValue: 2) registerOrNone))].
 
  rThird = NoReg ifTrue:
  [ thirdIsReceiver
  ifTrue:
  [ rThird := ReceiverResultReg.  "Free ReceiverResultReg if it was not free"
  self ssAllocateRequiredReg: ReceiverResultReg.
+ self voidReceiverResultRegContainsSelf ]
- optStatus isReceiverResultRegLive: false ]
  ifFalse: [ rThird := self allocateRegNotConflictingWith: topRegistersMask ].
  topRegistersMask := topRegistersMask bitOr: (self registerMaskFor: rThird) ].
 
  rTop = NoReg ifTrue:
  [ rTop := self allocateRegNotConflictingWith: topRegistersMask.
   topRegistersMask := topRegistersMask bitOr: (self registerMaskFor: rTop) ].
 
  rNext = NoReg ifTrue:
  [ rNext := self allocateRegNotConflictingWith: topRegistersMask ].
 
  self deny: (rTop = NoReg or: [rNext = NoReg or: [rThird = NoReg]]).
 
  ^ trinaryBlock value: rTop value: rNext value: rThird!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>allocateRegNotConflictingWith: (in category 'simulation stack') -----
  allocateRegNotConflictingWith: regMask
  | reg |
  "if there's a free register, use it"
  reg := backEnd availableRegisterOrNoneFor: (self liveRegisters bitOr: regMask).
  reg = NoReg ifTrue: "No free register, choose one that does not conflict with regMask"
  [reg := self freeAnyRegNotConflictingWith: regMask].
  reg = ReceiverResultReg ifTrue: "If we've allocated RcvrResultReg, it's not live anymore"
+ [self voidReceiverResultRegContainsSelf].
- [optStatus isReceiverResultRegLive: false].
  ^ reg!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>genExtPushFullClosureBytecode (in category 'bytecode generators') -----
  genExtPushFullClosureBytecode
  "Full Block creation compilation. The block's actual code will be compiled separatedly."
  "* 255 11111111 xxxxxxxx siyyyyyy push Closure Compiled block literal index xxxxxxxx (+ Extend A * 256) numCopied yyyyyy receiverOnStack: s = 1 ignoreOuterContext: i = 1"
  | numCopied ignoreContext receiverIsOnStack compiledBlock reg |
  self assert: needsFrame.
  compiledBlock := self getLiteral: byte1 + (extA << 8).
  extA := 0.
  numCopied := byte2 bitAnd: 1<< 6 - 1.
  receiverIsOnStack := byte2 anyMask: 1 << 7.
  ignoreContext := byte2 anyMask: 1 << 6.
+ self voidReceiverResultRegContainsSelf.
- optStatus isReceiverResultRegLive: false.
  self ssAllocateCallReg: ReceiverResultReg
  and: SendNumArgsReg
  and: ClassReg.
  objectRepresentation
  genCreateFullClosure: compiledBlock
  numArgs: (coInterpreter argumentCountOf: compiledBlock)
  numCopied: numCopied
  ignoreContext: ignoreContext
  contextNumArgs: methodOrBlockNumArgs
  large: (coInterpreter methodNeedsLargeContext: methodObj)
  inBlock: inBlock.
  "Closure in ReceiverResultReg"
  1 to: numCopied do:
  [:i|
  reg := self ssStorePop: true toPreferredReg: TempReg.
  objectRepresentation
  genStoreSourceReg: reg
  slotIndex: FullClosureFirstCopiedValueIndex + numCopied - i
  intoNewObjectInDestReg: ReceiverResultReg].
  receiverIsOnStack
  ifTrue: [reg := self ssStorePop: true toPreferredReg: TempReg]
  ifFalse: [(self addressOf: simSelf) storeToReg: (reg := TempReg)].
  objectRepresentation
  genStoreSourceReg: reg
  slotIndex: FullClosureReceiverIndex
  intoNewObjectInDestReg: ReceiverResultReg.
  self ssPushRegister: ReceiverResultReg.
  ^0!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>genInlineClosure:numArgs:numCopied: (in category 'bytecode generator support') -----
  genInlineClosure: startpc numArgs: numArgs numCopied: numCopied
  <inline: true>
  self assert: objectRepresentation getActiveContextAllocatesInMachineCode.
+ self voidReceiverResultRegContainsSelf.
- optStatus isReceiverResultRegLive: false.
  self ssAllocateCallReg: ReceiverResultReg
  and: SendNumArgsReg
  and: ClassReg.
  objectRepresentation
  genNoPopCreateClosureAt: startpc + 1 "1 relative"
  numArgs: numArgs
  numCopied: numCopied
  contextNumArgs: methodOrBlockNumArgs
  large: (coInterpreter methodNeedsLargeContext: methodObj)
  inBlock: inBlock.
  1 to: numCopied do:
  [:i| | reg |
  reg := self ssStorePop: true toPreferredReg: TempReg.
  objectRepresentation
  genStoreSourceReg: reg
  slotIndex: ClosureFirstCopiedValueIndex + numCopied - i
  intoNewObjectInDestReg: ReceiverResultReg].
  self ssPushRegister: ReceiverResultReg
  !

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>genLoadLiteralVariable:in: (in category 'bytecode generator support') -----
  genLoadLiteralVariable: litVarIndex in: destReg
  <inline: true>
  | association |
  association := self getLiteral: litVarIndex.
+ destReg = ReceiverResultReg ifTrue: [self voidReceiverResultRegContainsSelf].
- destReg = ReceiverResultReg ifTrue: [ optStatus isReceiverResultRegLive: false ].
  self ssAllocateRequiredReg: destReg.
  self genMoveConstant: association R: destReg.
  objectRepresentation genEnsureObjInRegNotForwarded: destReg scratchReg: TempReg.!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>genLoadTemp:in: (in category 'bytecode generator support') -----
  genLoadTemp: objectIndex in: destReg
+ destReg = ReceiverResultReg ifTrue: [self voidReceiverResultRegContainsSelf].
- destReg = ReceiverResultReg ifTrue: [ optStatus isReceiverResultRegLive: false ].
  self ssAllocateRequiredReg: destReg.
  self MoveMw: (self frameOffsetOfTemporary: objectIndex) r: FPReg R: destReg.!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>genMarshalledSend:numArgs:sendTable: (in category 'bytecode generator support') -----
  genMarshalledSend: selectorIndex numArgs: numArgs sendTable: sendTable
  <inline: false>
  <var: #sendTable type: #'sqInt *'>
  | annotation |
  self assert: needsFrame.
  annotation := self annotationForSendTable: sendTable.
  "Deal with stale super sends; see SpurMemoryManager's class comment."
  (self annotationIsForUncheckedEntryPoint: annotation) ifTrue:
  [objectRepresentation genEnsureOopInRegNotForwarded: ReceiverResultReg scratchReg: TempReg].
  "0 through (NumSendTrampolines - 2) numArgs sends have the arg count implciti in the trampoline.
  The last send trampoline (NumSendTrampolines - 1) passes numArgs in SendNumArgsReg."
  numArgs >= (NumSendTrampolines - 1) ifTrue:
  [self MoveCq: numArgs R: SendNumArgsReg].
  (BytecodeSetHasDirectedSuperSend
  and: [annotation = IsDirectedSuperSend]) ifTrue:
  [self genMoveConstant: tempOop R: TempReg].
  self genLoadInlineCacheWithSelector: selectorIndex.
  (self Call: (sendTable at: (numArgs min: NumSendTrampolines - 1))) annotation: annotation.
+ self voidReceiverOptStatus.
- optStatus isReceiverResultRegLive: false.
  ^self ssPushRegister: ReceiverResultReg!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>genNSSend:numArgs:depth:sendTable: (in category 'bytecode generators') -----
  genNSSend: selectorIndex numArgs: numArgs depth: depth sendTable: sendTable
  <var: #sendTable type: #'sqInt *'>
  | selector nsSendCache |
  self assert: (selectorIndex between: 0 and: (objectMemory literalCountOf: methodObj) - 1).
  selector := self getLiteral: selectorIndex.
  self assert: (objectMemory addressCouldBeOop: selector).
  (objectMemory isYoung: selector) ifTrue:
  [hasYoungReferent := true].
 
  nsSendCache := theIRCs + (NumOopsPerNSC * objectMemory bytesPerOop * indexOfIRC).
  indexOfIRC := indexOfIRC + 1.
  self assert: (objectMemory isInOldSpace: nsSendCache).
  self initializeNSSendCache: nsSendCache selector: selector numArgs: numArgs depth: depth.
 
  self ssAllocateCallReg: SendNumArgsReg.
 
  "This may leave the method receiver on the stack, which might not be the implicit receiver.
  But the lookup trampoline will establish an on-stack receiver once it locates it."
  self marshallAbsentReceiverSendArguments: numArgs.
 
  "Load the cache last so it is a fixed distance from the call."
  self MoveUniqueCw: nsSendCache R: SendNumArgsReg.
  self CallNewspeakSend: (sendTable at: (numArgs min: NumSendTrampolines - 1)).
 
+ self voidReceiverOptStatus.
- optStatus isReceiverResultRegLive: false.
  self ssPushRegister: ReceiverResultReg.
  ^0!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>genOutlineClosure:numArgs:numCopied: (in category 'bytecode generator support') -----
  genOutlineClosure: startpc numArgs: numArgs numCopied: numCopied
  <inline: true>
  numCopied > 0 ifTrue:
  [self ssFlushTo: simStackPtr].
+ self voidReceiverResultRegContainsSelf.
- optStatus isReceiverResultRegLive: false.
  objectRepresentation getActiveContextAllocatesInMachineCode
  ifTrue: [self ssAllocateCallReg: ReceiverResultReg
  and: SendNumArgsReg
  and: ClassReg]
  ifFalse: [self ssAllocateCallReg: SendNumArgsReg
  and: ReceiverResultReg].
  objectRepresentation
  genCreateClosureAt: startpc + 1 "1 relative"
  numArgs: numArgs
  numCopied: numCopied
  contextNumArgs: methodOrBlockNumArgs
  large: (coInterpreter methodNeedsLargeContext: methodObj)
  inBlock: inBlock.
  numCopied > 0 ifTrue:
  [self ssPop: numCopied].
  self ssPushRegister: ReceiverResultReg
  !

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>genPushActiveContextBytecode (in category 'bytecode generators') -----
  genPushActiveContextBytecode
  self assert: needsFrame.
+ self voidReceiverResultRegContainsSelf.
- optStatus isReceiverResultRegLive: false.
  objectRepresentation getActiveContextAllocatesInMachineCode
  ifTrue: [self ssAllocateCallReg: ReceiverResultReg
  and: SendNumArgsReg
  and: ClassReg]
  ifFalse: [self ssAllocateCallReg: ReceiverResultReg].
  objectRepresentation
  genGetActiveContextNumArgs: methodOrBlockNumArgs
  large: (coInterpreter methodNeedsLargeContext: methodObj)
  inBlock: inBlock.
  ^self ssPushRegister: ReceiverResultReg!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>genPushEnclosingObjectAt: (in category 'bytecode generator support') -----
  genPushEnclosingObjectAt: level
  "Uncached push enclosing object"
+ self voidReceiverResultRegContainsSelf.
- optStatus isReceiverResultRegLive: false.
  self ssAllocateCallReg: SendNumArgsReg and: ReceiverResultReg.
  self MoveCq: level R: SendNumArgsReg.
  self CallRT: ceEnclosingObjectTrampoline.
  ^self ssPushRegister: ReceiverResultReg!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>genPushMaybeContextSlotIndex: (in category 'bytecode generator support') -----
  genPushMaybeContextSlotIndex: slotIndex
  <inline: true>
  "This method expects ReceiverResultReg to hold the object read"
  | jmpSingle jmpDone |
  <var: #jmpSingle type: #'AbstractInstruction *'>
  <var: #jmpDone type: #'AbstractInstruction *'>
  self assert: needsFrame.
  (self isCallerSavedReg: ReceiverResultReg) ifTrue:
  ["We have no way of reloading ReceiverResultReg since we need the inst var value as the result."
+ self voidReceiverResultRegContainsSelf].
- optStatus isReceiverResultRegLive: false].
  "See CoInterpreter>>contextInstructionPointer:frame: for an explanation
  of the instruction pointer slot handling."
  slotIndex = InstructionPointerIndex ifTrue:
  [self MoveCq: slotIndex R: SendNumArgsReg.
  self CallRT: ceFetchContextInstVarTrampoline.
  ^self ssPushRegister: SendNumArgsReg].
  objectRepresentation
  genLoadSlot: SenderIndex
  sourceReg: ReceiverResultReg
  destReg: TempReg.
  jmpSingle := objectRepresentation genJumpNotSmallIntegerInScratchReg: TempReg.
  self MoveCq: slotIndex R: SendNumArgsReg.
  self CallRT: ceFetchContextInstVarTrampoline.
  jmpDone := self Jump: 0.
  jmpSingle jmpTarget: self Label.
  objectRepresentation
  genLoadSlot: slotIndex
  sourceReg: ReceiverResultReg
  destReg: SendNumArgsReg.
  jmpDone jmpTarget: self Label.
  ^self ssPushRegister: SendNumArgsReg!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>genPushNewArrayBytecode (in category 'bytecode generators') -----
  genPushNewArrayBytecode
  | size popValues |
  self assert: needsFrame.
+ self voidReceiverResultRegContainsSelf.
- optStatus isReceiverResultRegLive: false.
  (popValues := byte1 > 127)
  ifTrue: [self ssFlushTo: simStackPtr]
  ifFalse: [self ssAllocateCallReg: SendNumArgsReg and: ReceiverResultReg].
  size := byte1 bitAnd: 127.
  popValues ifFalse:
  [(self tryCollapseTempVectorInitializationOfSize: size) ifTrue:
  [^0]].
  objectRepresentation genNewArrayOfSize: size initialized: popValues not.
  popValues ifTrue:
  [size - 1 to: 0 by: -1 do:
  [:i|
  self PopR: TempReg.
  objectRepresentation
  genStoreSourceReg: TempReg
  slotIndex: i
  intoNewObjectInDestReg: ReceiverResultReg].
  self ssPop: size].
  ^self ssPushRegister: ReceiverResultReg!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>genPushReceiverBytecode (in category 'bytecode generators') -----
  genPushReceiverBytecode
  optStatus isReceiverResultRegLive ifTrue:
  [^self ssPushRegister: ReceiverResultReg].
+ self assert: simSelf registerOrNone = NoReg.
- self assert: simSelf liveRegister = NoReg.
  ^self ssPushDesc: simSelf!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>genStorePop:RemoteTemp:At:needsStoreCheck: (in category 'bytecode generator stores') -----
  genStorePop: popBoolean RemoteTemp: slotIndex At: remoteTempIndex needsStoreCheck: needsStoreCheck
  <inline: false>
  "The only reason we assert needsFrame here is that in a frameless method
  ReceiverResultReg must and does contain only self, but the ceStoreCheck
  trampoline expects the target of the store to be in ReceiverResultReg.  So
  in a frameless method we would have a conflict between the receiver and
  the temote temp store, unless we we smart enough to realise that
  ReceiverResultReg was unused after the literal variable store, unlikely given
  that methods return self by default."
  self assert: needsFrame.
  "N.B.  No need to check the stack for references because we generate code for
  remote temp loads that stores the result in a register, deferring only the register push."
  self ssAllocateRequiredReg: ReceiverResultReg.
+ self voidReceiverResultRegContainsSelf.
- optStatus isReceiverResultRegLive: false.
  self MoveMw: (self frameOffsetOfTemporary: remoteTempIndex) r: FPReg R: ReceiverResultReg.
  ^self
  genGenericStorePop: popBoolean
  slotIndex: slotIndex
  destReg: ReceiverResultReg
  needsStoreCheck: needsStoreCheck
  needsRestoreRcvr: false "We don't keep ReceiverResultReg live with the receiver across this operation"
  needsImmutabilityCheck: false "never do immutability check on temp vectors"!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>restoreSimStackAtMergePoint: (in category 'simulation stack') -----
  restoreSimStackAtMergePoint: fixup
  <inline: true>
  "All the execution paths reaching a merge point expect everything to be
  spilled on stack and the optStatus is unknown. Throw away all simStack and
  optStatus optimization state."
  simSpillBase := methodOrBlockNumTemps.
+ self voidReceiverOptStatus.
- optStatus isReceiverResultRegLive: false.
  methodOrBlockNumTemps to: simStackPtr do:
  [:i|
+ (self simStackAt: i)
+ type: SSSpill;
+ offset: FoxMFReceiver - (i - methodOrBlockNumArgs + 1 * objectMemory bytesPerOop);
+ register: FPReg;
+ spilled: true].
+ LowcodeVM ifTrue:
+ [0 to: simNativeStackPtr do:
+ [ :i |
- (self simStackAt: i)
- type: SSSpill;
- offset: FoxMFReceiver - (i - methodOrBlockNumArgs + 1 * objectMemory bytesPerOop);
- register: FPReg;
- spilled: true].
- LowcodeVM ifTrue: [
- 0 to: simNativeStackPtr do: [ :i |
  (self simNativeStackAt: i)
+ ensureIsMarkedAsSpilled].
+ simNativeSpillBase := simNativeStackPtr + 1].
- ensureIsMarkedAsSpilled
- ].
- simNativeSpillBase := simNativeStackPtr + 1
- ].
  ^ 0!

Item was added:
+ ----- Method: StackToRegisterMappingCogit>>voidReceiverOptStatus (in category 'bytecode generator support') -----
+ voidReceiverOptStatus
+ "Used to mark ReceiverResultReg as dead or not containing simSelf.
+ Used when the simStack has already been flushed, e.g. for sends."
+ <inline: true>
+ optStatus isReceiverResultRegLive: false!

Item was added:
+ ----- Method: StackToRegisterMappingCogit>>voidReceiverResultRegContainsSelf (in category 'bytecode generator support') -----
+ voidReceiverResultRegContainsSelf
+ "Used when ReceiverResultReg is allocated for other than simSelf, and
+ there may be references to ReceiverResultReg which need to be spilled."
+ | spillIndex |
+ optStatus isReceiverResultRegLive: false.
+ spillIndex := -1.
+ (methodOrBlockNumTemps max: simSpillBase) to: simStackPtr do:
+ [:i|
+ (self simStackAt: i) registerOrNone = ReceiverResultReg ifTrue:
+ [spillIndex := i]].
+ spillIndex > 0 ifTrue:
+ [self ssFlushTo: simStackPtr - spillIndex]!