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

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

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

Name: VMMaker.oscog-eem.2335
Author: eem
Time: 19 February 2018, 6:24:41.264084 pm
UUID: 9525a938-a6aa-43d4-8bdc-e844e8e7e360
Ancestors: VMMaker.oscog-eem.2334

RegisterAllocatingCogit:
Improve (dare I say, fix??) the register permutation engine in the control flow merge logic.  Fix reconciling entries that oproduce an SSRegister entry (the liveRegister must be set to NoReg).  Fux recinciling a constanty with itself (this happens at the end of ifTrue:ifFalse: given that the bytecode compiler preserves the value of ifTrue:ifFalse: until a single trailing pop).  N.B. the merge engine could save effort by peeking ahead for a pop, but Scorch likely won't produyce such bad code so we shouldn't waste effort making the RegisterAllocatingCogit generate better code for non-Scorch input.

Simplify the incomprehensible garbage I wrote for assigning registers on temp var assignment.

Correct isFrameTempVar now that simSelf is niw a normal simStackEntry beneath the temps.  Add isFrameVar to be inclusive of self and temps.

Fix a slip in CogMIPSELCompiler class>>initializeAbstractRegisters.

Simulator:
Correct the size of the border around the display in the simulator windows,

VMMaker:
Add configurations for the mutli-threaded Spur VM.  It would be great to make progress on this yjis year.

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

Item was changed:
  ----- Method: CogMIPSELCompiler class>>initializeAbstractRegisters (in category 'class initialization') -----
  initializeAbstractRegisters
  "Assign the abstract registers with the identities/indices of the relevant concrete registers."
 
  "See MIPSConstants>>initializeRegisters for a description of the C ABI."
 
  "Note we can fit all of the abstract registers in C preserved registers, and
  not need to save or restore them at runtime calls."
  super initializeAbstractRegisters.
 
  self flag: #OABI.
  CallerSavedRegisterMask := self
  registerMaskFor: T0 and: T1 and: T2 and: T3
  and: T4 and: T5 and: T6 and: T7 and: T8 and: T9.
 
  ReceiverResultReg := S0.
  Arg0Reg := S1.
  Arg1Reg := S2.
  ClassReg := S3.
  SendNumArgsReg := S4.
  TempReg := S5.
  VarBaseReg := S6. "Must be callee saved"
  SPReg := SP.
  FPReg := FP.
  RISCTempReg := AT.
  LinkReg := RA.
 
+ NumRegisters := 32.
- NumRegisters := 32
 
  self flag: #todo.
  "Extra0Reg := ??.
  Extra1Reg := ??.
  Extra2Reg := ??.
  Extra3Reg := ??.
  Extra4Reg := ??.
  Extra5Reg := ??.
  Extra6Reg := ??.
  Extra7Reg := ??."
 
  self flag: #todo.
  "DPFPReg0 := ??.
  DPFPReg1 := ??.
  DPFPReg2 := ??.
  DPFPReg3 := ??.
  DPFPReg4 := ??.
  DPFPReg5 := ??.
  DPFPReg6 := ??.
  DPFPReg7 := ??.
  DPFPReg8 := ??.
  DPFPReg9 := ??.
  DPFPReg10 := ??.
  DPFPReg11 := ??.
  DPFPReg12 := ??.
  DPFPReg13 := ??.
  DPFPReg14 := ??.
  DPFPReg15 := ??"!

Item was added:
+ ----- Method: CogRegisterAllocatingSimStackEntry>>popToRegNoAssign: (in category 'compile abstract instructions') -----
+ popToRegNoAssign: reg
+ 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].
+ 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].
+ [SSSpill] -> [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]] }]]!

Item was changed:
  ----- Method: CogRegisterAllocatingSimStackEntry>>reconcileWith:spillOffset:onSpillOrUnspill: (in category 'compile abstract instructions') -----
  reconcileWith: targetEntry spillOffset: spillOffset onSpillOrUnspill: spillOrUnspillBlock
  "Make the state of a targetEntry, a stack entry following a non-inlined special selector
  send, the same as the corresponding entry (the receiver) along the inlined path.
  spillOffset is zero for non-spill locations (self & temps), and the offset of the spill for
  volatile stack entries. spillOrUnspillBlock is a block evaluated with the target's
  registerOrNone if the receiver and target have different spilledness.
  Answer if the reconciliation merged a register; merged registers must be deassigned."
  <var: #targetEntry type: #'SimStackEntry *'>
  <inline: true>
  | targetReg mergedRegister |
  spilled = targetEntry spilled ifTrue:
  [self assert: ((self isSameEntryAs: targetEntry)
  or: [(targetEntry spilled not and: [targetEntry registerOrNone ~= NoReg])
  or: [spilled and: [type = SSConstant and: [offset = targetEntry offset]]]]).
  (targetReg := targetEntry registerOrNone) = NoReg ifTrue:
  [liveRegister := NoReg.
  ^false].
  mergedRegister := false.
  type caseOf: {
  [SSBaseOffset] -> [liveRegister ~= targetReg ifTrue:
+ [liveRegister = NoReg
+ ifTrue: [cogit MoveMw: offset r: register R: targetReg]
+ ifFalse: [cogit MoveR: liveRegister R: targetReg].
- [cogit MoveMw: offset r: register R: targetReg.
  mergedRegister := true].
  targetEntry type caseOf: {
  [SSBaseOffset] -> [liveRegister := targetReg.
  (self isSameEntryAs: targetEntry) ifFalse:
  [type := SSSpill.
  offset := spillOffset]].
  [SSSpill] -> [liveRegister := targetReg. type := SSSpill.
  offset := spillOffset].
  [SSConstant] -> [liveRegister := targetReg. type := SSSpill.
  offset := spillOffset].
+ [SSRegister] -> [register := targetReg. type := SSRegister. liveRegister := NoReg] }].
+ [SSSpill] -> [liveRegister = NoReg
+ ifTrue: [cogit MoveMw: offset r: register R: targetReg]
+ ifFalse: [cogit MoveR: liveRegister R: targetReg].
- [SSRegister] -> [register := targetReg. type := SSRegister] }].
- [SSSpill] -> [cogit MoveMw: offset r: register R: targetReg.
  liveRegister := targetReg.
  mergedRegister := true].
+ [SSConstant] -> [(targetEntry type = SSConstant
+ and: [targetEntry constant = constant
+ and: [liveRegister = targetReg]]) ifFalse:
+ [liveRegister = NoReg
+ ifTrue: [cogit genMoveConstant: constant R: targetReg]
+ ifFalse: [cogit MoveR: liveRegister R: targetReg].
+ type := SSRegister. register := targetReg. liveRegister := NoReg.
+ mergedRegister := true]].
- [SSConstant] -> [liveRegister = NoReg
- ifTrue: [cogit genMoveConstant: constant R: targetReg]
- ifFalse: [cogit MoveR: liveRegister R: targetReg].
- type := SSRegister. register := targetReg. liveRegister := NoReg.
- mergedRegister := true].
  [SSRegister] -> [targetReg ~= register ifTrue:
  [cogit MoveR: register R: targetReg.
+ register := targetReg. liveRegister := NoReg.
- register := targetReg.
  mergedRegister := true]] }.
  ^mergedRegister].
  targetReg := targetEntry registerOrNone.
  spillOrUnspillBlock value: targetReg.
  (type = SSConstant
  and: [targetEntry type ~= SSConstant or: [targetEntry constant ~= constant]]) ifTrue:
  [type := SSSpill. offset := spillOffset. register := FPReg].
  (spilled not and: [type = SSSpill]) ifTrue:
  [self assert: targetReg ~= NoReg. type := SSRegister. register := targetReg].
  liveRegister ~= targetReg ifTrue:
  [liveRegister := NoReg.
  ^true].
  ^false!

Item was added:
+ ----- Method: CogRegisterAllocatingSimStackEntry>>storeToRegNoAssign: (in category 'compile abstract instructions') -----
+ storeToRegNoAssign: reg
+ liveRegister ~= NoReg
+ ifTrue:
+ [self deny: (type = SSRegister and: [register ~= liveRegister]).
+ reg ~= liveRegister
+ ifTrue: [cogit MoveR: liveRegister R: reg]
+ ifFalse: [cogit Label]]
+ ifFalse:
+ [type caseOf: {
+ [SSBaseOffset] -> [cogit MoveMw: offset r: register R: reg].
+ [SSSpill] -> [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]] }]!

Item was changed:
  VMStructType subclass: #CogSimStackEntry
  instanceVariableNames: 'cogit objectRepresentation type spilled liveRegister register offset constant bcptr'
  classVariableNames: ''
+ poolDictionaries: 'CogAbstractRegisters CogCompilationConstants CogRTLOpcodes VMStackFrameOffsets'
- poolDictionaries: 'CogAbstractRegisters CogCompilationConstants CogRTLOpcodes'
  category: 'VMMaker-JIT'!
 
  !CogSimStackEntry commentStamp: 'eem 1/22/2018 17:13' prior: 0!
  A CogSimStackEntry represents an object pushed on the stack, but during the partial evaluation that occurs as part of the StackToRegisterMappingCogit's compilation.  Bytecodes that produce operands (push items onto the stack) push suitably descriptive instances of CogSimStackEntry onto the simStack (simulation stack).  Bytecodes that consume operands (sends, assignments, returns, etc) take items off the simStack.  Hence the generated code avoids pushing items onto the real stack, and the StackToRegisterMappngCogit can put the operands found on the simStack in registers, etc.  Hence actual stack traffic is much reduced, a much more efficient calling convention is enabled, and so overall performance is increased.  This scheme is due to L. Peter Deutsch and extended here.
 
  Instance Variables
  bcptr: <Integer>
  cogit: <StackToRegisterMappingCogit>
  constant: <Oop>
  liveRegister: <Integer>
  objectRepresentation: <CogObjectRepresentation>
  offset: <Integer>
  register: <Integer>
  spilled: <Boolean>
  type: <Integer from SSBaseOffset, SSConstant, SSRegister or SSSpill>
 
  bcptr
  - the bytecode PC at which this particular entry was created (pushed onto the stack).
 
  cogit
  - the StackToRegisterMappingCogit using this instance
 
  constant
  - if type = SSConstant then this is the constant's oop
 
  liveRegister
  - unused other than for simSelf.  This is here for simSelf and for the subclass CogRegisterAllocatingSimStackEntry
 
  objectRepresentation
  - the CogObjectRepresentation in use for the current object model
 
  offset
  - if type = SSBaseOffset or type = SSSpill then this is the offset from register
 
  register
  - type = SSBaseOffset or type = SSSpill or type = SSRegister then this is the register's code (NoReg, TempReg, ReceiverResultReg et al)
 
  spilled
  - if true, then this entry has been spilled onto the actual stack (or rather code has been generated to push the entry onto the real stack)
 
  type
  - SSBaseOffset, SSConstant, SSRegister or SSSpill!

Item was added:
+ ----- Method: CogSimStackEntry>>isFrameSimSelf (in category 'comparing') -----
+ isFrameSimSelf
+ "Answer if the receiver is self.  This only works in a frameful method, hence the weird name."
+ <inline: true>
+ ^type = SSBaseOffset and: [register = FPReg and: [offset = FoxMFReceiver]]!

Item was changed:
  ----- Method: CogSimStackEntry>>isFrameTempVar (in category 'comparing') -----
  isFrameTempVar
  "Answer if the receiver is a temporary variable.  This
  only works in a frameful method, hence the weird name."
  <inline: true>
+ ^type = SSBaseOffset and: [register = FPReg and: [offset ~= FoxMFReceiver]]!
- ^type = SSBaseOffset and: [register = FPReg]!

Item was added:
+ ----- Method: CogSimStackEntry>>isFrameVar (in category 'comparing') -----
+ isFrameVar
+ "Answer if the receiver is a temporary variable or self.  This
+ only works in a frameful method, hence the weird name."
+ <inline: true>
+ ^type = SSBaseOffset and: [register = FPReg]!

Item was changed:
  ----- Method: CogVMSimulator>>openAsMorph (in category 'UI') -----
  openAsMorph
  "Open a morphic view on this simulation."
  | localImageName borderWidth window |
  localImageName := imageName
  ifNotNil: [FileDirectory default localNameFor: imageName]
  ifNil: [' synthetic image'].
  window := (SystemWindow labelled: 'Simulation of ', localImageName) model: self.
  window paneColor: self windowColorToUse.
 
  window addMorph: (displayView := SimulatorImageMorph new image: displayForm)
  frame: (0@0 corner: 1@0.8).
  displayView activeHand addEventListener: self.
  eventTransformer := SimulatorEventTransformer new.
 
  transcript := TranscriptStream on: (String new: 10000).
  window addMorph: (PluggableTextMorph
  on: transcript text: nil accept: nil
  readSelection: nil menu: #codePaneMenu:shifted:)
  frame: (0@0.8 corner: 0.7@1).
  window addMorph: (PluggableTextMorph on: self
  text: #byteCountText accept: nil
  readSelection: nil menu: #utilitiesMenu:) hideScrollBarsIndefinitely
  frame: (0.7@0.8 corner: 1@1).
 
  borderWidth := [SystemWindow borderWidth] "Squeak 4.1"
  on: MessageNotUnderstood
  do: [:ex| 0]. "3.8"
  borderWidth := borderWidth + window borderWidth.
  window openInWorldExtent: (self desiredDisplayExtent
+ + (2 * borderWidth @ borderWidth)
- + (2 * borderWidth)
  + (0@window labelHeight)
  * (1@(1/0.8))) rounded.
  ^window!

Item was changed:
  ----- Method: CogVMSimulator>>openAsMorphNoTranscript (in category 'UI') -----
  openAsMorphNoTranscript
  "Open a morphic view on this simulation."
  | localImageName borderWidth window |
  localImageName := imageName
  ifNotNil: [FileDirectory default localNameFor: imageName]
  ifNil: [' synthetic image'].
  window := (SystemWindow labelled: 'Simulation of ', localImageName) model: self.
  window paneColor: self windowColorToUse.
 
  window addMorph: (displayView := SimulatorImageMorph new image: displayForm)
  frame: (0@0 corner: 1@0.95).
  displayView activeHand addEventListener: self.
  eventTransformer := SimulatorEventTransformer new.
 
  window addMorph: (PluggableTextMorph on: self
  text: #byteCountText accept: nil
  readSelection: nil menu: #utilitiesMenu:) hideScrollBarsIndefinitely
  frame: (0@0.95 corner: 1@1).
 
  borderWidth := [SystemWindow borderWidth] "Squeak 4.1"
  on: MessageNotUnderstood
  do: [:ex| 0]. "3.8"
  borderWidth := borderWidth + window borderWidth.
  window openInWorldExtent: (self desiredDisplayExtent
+ + (2 * borderWidth@borderWidth)
- + (2 * borderWidth)
  + (0@window labelHeight)
  * (1@(1/0.95))) rounded!

Item was changed:
  ----- Method: Cogit>>methodFoundInvalidPostScan (in category 'testing') -----
  methodFoundInvalidPostScan
  "This is a hook for subclasses to filter out methods they can't deal with."
+ <inline: true>
  ^false!

Item was added:
+ ----- Method: RegisterAllocatingCogit>>genPushReceiverBytecode (in category 'bytecode generators') -----
+ genPushReceiverBytecode
+ ^self ssPushDesc: self simSelf!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>genStorePop:TemporaryVariable: (in category 'bytecode generator support') -----
  genStorePop: popBoolean TemporaryVariable: tempIndex
  <inline: false>
+ | top targetTemp srcRegOrNone destReg |
- | top srcRegOrNone destReg |
  self deny: self duplicateRegisterAssignmentsInTemporaries.
  self ssFlushUpThroughTemporaryVariable: tempIndex.
  "To avoid a stall writing through destReg, remember srcReg before the potential ssPop: 1 in ssStorePop:toReg:"
  top := self ssTop.
+ targetTemp := self simStackAt: tempIndex + 1.
  srcRegOrNone := top registerOrNone.
+ "Take care to avoid duplicating registers in different temps and/or self."
+ destReg := targetTemp liveRegister.
+ destReg = NoReg ifTrue:
+ [destReg := self availableRegOrNoneNotConflictingWith: (self registerMaskUndesirableForTempVars bitOr: self liveRegisters)].
+ destReg = NoReg ifTrue:
+ [destReg := TempReg].
+ self deny: srcRegOrNone = destReg.
+ srcRegOrNone = NoReg
+ ifTrue: "Avoid duplicating destReg into top; this would only be ok if top
+ is the same entry as the target, which is not worth optimizing."
+ [self ssStorePopNoAssign: popBoolean toReg: destReg]
- "ssStorePop:toPreferredReg: will allocate a register, and indeed may allocate ReceiverResultReg
- if, for example, the ssEntry to be popped is already in ReceiverResultReg (as the result of a send).
- ReceiverResultReg is not a good choice for a temporary variable; it has other uses.  So if the ssEntry
- at top of stack has ReceiverResultReg as its live variable, try and allocate an alternative."
- destReg := (self simStackAt: tempIndex + 1) liveRegister.
- destReg ~= NoReg
- ifTrue:
- [self ssStorePop: popBoolean toReg: destReg]
  ifFalse:
+ [self ssStorePop: popBoolean toReg: destReg].
+ (destReg ~= TempReg and: [targetTemp liveRegister = NoReg]) ifTrue:
+ [targetTemp liveRegister: destReg.
+ self copyLiveRegisterToCopiesOf: targetTemp].
- [((top type = SSConstant
-    or: [srcRegOrNone = NoReg
-    or: [self register: srcRegOrNone isInMask: self registerMaskUndesirableForTempVars]])
-  and: [(destReg := self availableRegOrNoneNotConflictingWith: (self registerMaskUndesirableForTempVars bitOr: self liveRegisters)) ~= NoReg])
- ifTrue: [self ssStorePop: popBoolean toReg: destReg]
- ifFalse: [destReg := self ssStorePop: popBoolean toPreferredReg: TempReg].
- "The ssStorePop: may end up assigning a register to ssTop, and if it is also a temp then a new
-  register must be found for the destination temp, sicne two temp vars can't share a register."
- (top isFrameTempVar and: [top liveRegister = destReg]) ifTrue:
- [srcRegOrNone := destReg.
- destReg := self availableRegOrNoneNotConflictingWith: (self registerMaskUndesirableForTempVars bitOr: self liveRegisters).
- destReg ~= NoReg ifTrue:
- [self MoveR: srcRegOrNone R: destReg]].
- (destReg ~= NoReg and: [destReg ~= TempReg]) ifTrue:
- [(self simStackAt: tempIndex + 1) liveRegister: destReg.
- self copyLiveRegisterToCopiesOf: (self simStackAt: tempIndex + 1)]].
  self MoveR: (srcRegOrNone ~= NoReg ifTrue: [srcRegOrNone] ifFalse: [destReg])
  Mw: (self frameOffsetOfTemporary: tempIndex)
  r: FPReg.
+ targetTemp bcptr: bytecodePC. "for debugging"
- (self simStackAt: tempIndex + 1) bcptr: bytecodePC. "for debugging"
  self deny: self duplicateRegisterAssignmentsInTemporaries.
  ^0!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>mergeCurrentSimStackWith: (in category 'bytecode generator support') -----
  mergeCurrentSimStackWith: fixup
  "At a merge point the cogit expects the stack to be in the same state as fixup's mergeSimStack.
  mergeSimStack is the state as of some jump forward or backward to this point.  So make
  simStack agree with mergeSimStack (it is, um, problematic to plant code at the jump). Values
  may have to be assigned to registers.  Registers may have to be swapped.
  Generate code to merge the current simStack with that of the target fixup, the goal being to
  keep as many registers live as possible."
  "self printSimStack; printSimStack: fixup mergeSimStack"
  "self simStackPrintString-> fixup simStackPrintString"
  "abstractOpcodes object copyFrom: startIndex to: opcodeIndex"
  <var: #fixup type: #'BytecodeFixup *'>
+ | currentRegisters targetRegisters mergeSimStack current target spillOffset them |
- | currentRegisters targetRegisters mergeSimStack current target spillOffset |
  (mergeSimStack := fixup mergeSimStack) ifNil: [^self].
+ self cCode: '' inSmalltalk: [them := {self simStackPrintString. fixup simStackPrintString}].
  self assert: simStackPtr = fixup simStackPtr.
  currentRegisters := self liveRegistersFrom: 0 to: simStackPtr in: simStack.
  targetRegisters := self liveRegistersFrom: 0 to: simStackPtr in: mergeSimStack.
  self resolveConflicts: (currentRegisters bitAnd: targetRegisters) with: fixup mergeSimStack to: fixup simStackPtr.
  self assert: (self conflictsResolvedBetweenSimStackAnd: mergeSimStack).
  (self pushForMergeWith: mergeSimStack)
  ifTrue:
  [0 to: simStackPtr do:
  [:i|
  spillOffset := i > methodOrBlockNumTemps
  ifTrue: [self frameOffsetOfTemporary: i - 1]
  ifFalse: [0].
  ((current := self simStack: simStack at: i)
  reconcileWith: (target := self simStack: mergeSimStack at: i)
  spillOffset: spillOffset
  onSpillOrUnspill:
  [:targetReg|
  self deny: current spilled.
  self assert: spillOffset ~= 0.
  current ensureSpilledAt: spillOffset from: FPReg.
  simSpillBase <= i ifTrue:
  [simSpillBase := i + 1]]) ifTrue:
  [| targetReg |
  (i > methodOrBlockNumTemps and: [(targetReg := target registerOrNone) ~= NoReg]) ifTrue:
  [self deassignRegister: targetReg in: simStack.
  self deassignRegister: targetReg in: mergeSimStack.
  self deny: (self register: targetReg isInMask: self liveRegistersInSelfAndTemps)]]]]
  ifFalse:
  [simStackPtr to: 0 by: -1 do:
  [:i|
  spillOffset := i > methodOrBlockNumTemps
  ifTrue: [self frameOffsetOfTemporary: i - 1]
  ifFalse: [0].
  ((current := self simStack: simStack at: i)
  reconcileWith: (target := self simStack: mergeSimStack at: i)
  spillOffset: spillOffset
  onSpillOrUnspill:
  [:targetReg|
  self assert: current spilled.
  self assert: spillOffset ~= 0.
  targetReg  ~= NoReg
  ifTrue: [self PopR: targetReg]
  ifFalse: [self AddCq: objectRepresentation wordSize R: SPReg].
  current spilled: false.
  simSpillBase > i ifTrue:
  [simSpillBase := i]]) ifTrue:
  [| targetReg |
  (i > methodOrBlockNumTemps and: [(targetReg := target registerOrNone) ~= NoReg]) ifTrue:
  [self deassignRegister: targetReg in: simStack.
  self deassignRegister: targetReg in: mergeSimStack.
  self deny: (self register: targetReg isInMask: self liveRegistersInSelfAndTemps)]]]].
  self updateSimSpillBase!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>mergeWithFixupIfRequired: (in category 'simulation stack') -----
  mergeWithFixupIfRequired: fixup
  "If this bytecode has a fixup, some kind of merge needs to be done. There are 4 cases:
  1) the bytecode has no fixup (fixup isNotAFixup)
  do nothing
  2) the bytecode has a non merge fixup
  the fixup has needsNonMergeFixup.
  The code generating non merge fixup (currently only special selector code) is responsible
  for the merge so no need to do it.
  We set deadCode to false as the instruction can be reached from jumps.
  3) the bytecode has a merge fixup, but execution flow *cannot* fall through to the merge point.
  the fixup has needsMergeFixup and deadCode = true.
  ignores the current simStack as it does not mean anything
  restores the simStack to the state the jumps to the merge point expects it to be.
  4) the bytecode has a merge fixup and execution flow *can* fall through to the merge point.
  the fixup has needsMergeFixup and deadCode = false.
  Merge the state into the fixup's state via mergeCurrentSimStackWith:forwards:.
 
  In addition, if this is a backjump merge point, we patch the fixup to hold the current simStackPtr
  for later assertions. self printSimStack: fixup mergeSimStack"
 
  <var: #fixup type: #'BytecodeFixup *'>
  deadCode ifFalse:
  [self assertCorrectSimStackPtr].
 
  "case 1"
  fixup notAFixup ifTrue:
  [^0].
 
  "case 2"
  fixup isNonMergeFixup ifTrue:
  [deadCode
  ifTrue:
  [self deny: fixup simStackPtr isNil.
  simStackPtr := fixup simStackPtr.
  self restoreSimStackAtMergePoint: fixup.
  deadCode := false.
  self assertCorrectSimStackPtr]
  ifFalse:
  [self flushRegistersOnlyLiveOnFallThrough: fixup].
  ^0].
 
  "cases 3 and 4"
  self assert: fixup isMergeFixup.
  self traceMerge: fixup.
  deadCode
  ifTrue: "case 3"
+ [(fixup isBackwardBranchFixup and: [fixup mergeSimStack isNil]) "e.g. a loop within false ifTrue: []"
+ ifTrue: [self assert: fixup simStackPtr isNil]
+ ifFalse:
+ [simStackPtr := fixup simStackPtr.
+ self restoreSimStackAtMergePoint: fixup.
+ deadCode := false]]
- [simStackPtr := fixup simStackPtr.
- self restoreSimStackAtMergePoint: fixup.
- deadCode := false]
  ifFalse: "case 4"
  [(fixup isBackwardBranchFixup and: [compilationPass > 1])
  ifTrue:
  [fixup simStackPtr: simStackPtr.
  self mergeCurrentSimStackWith: fixup.
  self copySimStackToFixup: fixup]
  ifFalse:
  [self mergeCurrentSimStackWith: fixup]].
  "cases 3 and 4"
  fixup isBackwardBranchFixup ifTrue:
  [fixup mergeSimStack ifNil:
  [self assert: compilationPass = 1.
  self setMergeSimStackOf: fixup]].
  fixup targetInstruction: self Label.
  self assertCorrectSimStackPtr.
  self assert: (self simStackMergeCompatibleWith: fixup).
  "self simStackPrintString, fixup simStackPrintString"
  ^0!

Item was added:
+ ----- Method: RegisterAllocatingCogit>>putSelfInReceiverResultReg (in category 'bytecode generator support') -----
+ putSelfInReceiverResultReg
+ "Override to force copying the register to duplicates on stack."
+ <inline: true>
+ | simSelfHasRegister |
+ simSelfHasRegister := self simSelf liveRegister ~= NoReg.
+ super putSelfInReceiverResultReg.
+ simSelfHasRegister ifTrue:
+ [self simSelf liveRegister: ReceiverResultReg.
+ self copyLiveRegisterToCopiesOf: self simSelf]!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>resolveConflicts:with:to: (in category 'bytecode generator support') -----
  resolveConflicts: registersInCommon with: mergeSimStack to: simStackPtr
  "registersInCommon is the register mask of registers in use in both the current
  simStack and the target mergeSimStack. Swap any and all conflicting register uses
  in registersInCommon, until register uses in simStack agree with mergeSimStack."
+ | registerExchanges registerLocations agreements visited initialIndex |
- | registerLocations visited |
  "registerLocations records where a register has moved to
  during an exchange. This allows a single pass of the stack
  to rename registers, instead of 1/2 N^2; max stack ~ 56"
+ <var: 'registerExchanges' declareC: 'int registerExchanges[NumRegisters]'>
  <var: 'registerLocations' declareC: 'int registerLocations[NumRegisters]'>
+ self deny: self duplicateRegisterAssignmentsInTemporaries.
  registersInCommon = (self registerMaskFor: FPReg) ifTrue:
+ [self assert: (self conflictsResolvedBetweenSimStackAnd: mergeSimStack).
+ ^self].
- [^self].
  self cCode: '' inSmalltalk:
+ [registerExchanges := CArrayAccessor on: (Array new: NumRegisters).
+ registerLocations := CArrayAccessor on: (Array new: NumRegisters)].
- [registerLocations := CArrayAccessor on: (Array new: NumRegisters)].
  0 to: NumRegisters - 1 do:
+ [:i| registerExchanges at: i put: i].
+ initialIndex := opcodeIndex. "for debugging"
+ agreements := visited := 0.
- [:i| registerLocations at: i put: i].
- visited := 0.
  0 to: simStackPtr do:
  [:i| | currentReg targetReg |
  currentReg := (self simStackAt: i) registerOrNone.
  targetReg := (self simStack: mergeSimStack at: i) registerOrNone.
  (currentReg ~= NoReg
  and: [targetReg ~= NoReg]) ifTrue:
+ [currentReg := registerExchanges at: currentReg.
+ currentReg = targetReg
+ ifTrue:
+ [(self register: currentReg isInMask: visited) ifFalse:
+ [visited := visited bitOr: (self registerMaskFor: currentReg).
+ agreements := agreements bitOr: (self registerMaskFor: currentReg)]]
+ ifFalse:
+ [((self register: currentReg isInMask: registersInCommon)
+  and: [(self register: currentReg isNotInMask: visited)
+ or: [self register: targetReg isNotInMask: visited]]) ifTrue:
+ [| this that |
+ visited := visited bitOr: (self registerMaskFor: currentReg and: targetReg).
+ self SwapR: targetReg R: currentReg Scratch: RISCTempReg.
+ this := registerExchanges at: currentReg.
+ that := registerExchanges at: targetReg.
+ registerExchanges
+ at: currentReg put: that;
+ at: targetReg put: this]]]].
+ (visited := visited bitClear: agreements) = 0 ifTrue:
+ [self assert: (self conflictsResolvedBetweenSimStackAnd: mergeSimStack).
+ ^self].
+ 0 to: NumRegisters - 1 do:
+ [:i| registerLocations at: (registerExchanges at: i) put: i].
- [currentReg := registerLocations at: currentReg.
- (currentReg ~= targetReg
-  and: [(self register: currentReg isInMask: registersInCommon)
-  and: [(self register: currentReg isNotInMask: visited)]]) ifTrue:
- [visited := visited + (self registerMaskFor: currentReg).
- self SwapR: targetReg R: currentReg Scratch: RISCTempReg.
- registerLocations
- at: currentReg put: targetReg;
- at: targetReg put: currentReg]]].
- visited = 0 ifTrue:
- [^self].
  0 to: simStackPtr do:
  [:i| | ssEntry reg |
  ssEntry := self simStackAt: i.
  reg := ssEntry registerOrNone.
  (reg ~= NoReg
  and: [(self register: reg isInMask: registersInCommon)
  and: [reg ~= (self simStack: mergeSimStack at: i) registerOrNone]]) ifTrue:
  [ssEntry type = SSRegister
  ifTrue: [ssEntry register: (registerLocations at: reg)]
+ ifFalse: [ssEntry liveRegister: (registerLocations at: reg)]]].
+ self deny: self duplicateRegisterAssignmentsInTemporaries.
+ self assert: (self conflictsResolvedBetweenSimStackAnd: mergeSimStack)
+ "(initialIndex to: opcodeIndex - 1) collect: [:x| abstractOpcodes at: x]"!
- ifFalse: [ssEntry liveRegister: (registerLocations at: reg)]]]!

Item was added:
+ ----- Method: RegisterAllocatingCogit>>ssStorePopNoAssign:toReg: (in category 'simulation stack') -----
+ ssStorePopNoAssign: popBoolean toReg: reg
+ "Store or pop the top simulated stack entry to a register.
+ Do /not/ assign reg to ssTop.
+ N.B.: popToReg: and storeToReg: does not generate anything if
+ it moves a register to the same register."
+ popBoolean
+ ifTrue: [self ssTop popToRegNoAssign: reg.
+ self ssPop: 1]
+ ifFalse: [self ssTop storeToRegNoAssign: reg].!

Item was changed:
  ----- Method: StackInterpreterSimulator>>openAsMorph (in category 'UI') -----
  openAsMorph
  "Open a morphic view on this simulation."
  | localImageName borderWidth window |
  localImageName := imageName
  ifNotNil: [FileDirectory default localNameFor: imageName]
  ifNil: [' synthetic image'].
  window := (SystemWindow labelled: 'Simulation of ', localImageName) model: self.
  window paneColor: self windowColorToUse.
 
  window addMorph: (displayView := SimulatorImageMorph new image: displayForm)
  frame: (0@0 corner: 1@0.8).
  displayView activeHand addEventListener: self.
  eventTransformer := SimulatorEventTransformer new.
 
  transcript := TranscriptStream on: (String new: 10000).
  window addMorph: (PluggableTextMorph
  on: transcript text: nil accept: nil
  readSelection: nil menu: #codePaneMenu:shifted:)
  frame: (0@0.8 corner: 0.7@1).
  window addMorph: (PluggableTextMorph on: self
  text: #byteCountText accept: nil
  readSelection: nil menu: #utilitiesMenu:) hideScrollBarsIndefinitely
  frame: (0.7@0.8 corner: 1@1).
 
  borderWidth := [SystemWindow borderWidth] "Squeak 4.1"
  on: MessageNotUnderstood
  do: [:ex| 0]. "3.8"
  borderWidth := borderWidth + window borderWidth.
  window openInWorldExtent: (self desiredDisplayExtent
+ + (2 * borderWidth@borderWidth)
- + (2 * borderWidth)
  + (0@window labelHeight)
  * (1@(1/0.8))) rounded.
  ^window!

Item was changed:
  ----- Method: StackInterpreterSimulator>>openAsMorphNoTranscript (in category 'UI') -----
  openAsMorphNoTranscript
  "Open a morphic view on this simulation."
  | localImageName borderWidth window |
  localImageName := imageName
  ifNotNil: [FileDirectory default localNameFor: imageName]
  ifNil: [' synthetic image'].
  window := (SystemWindow labelled: 'Simulation of ', localImageName) model: self.
  window paneColor: self windowColorToUse.
 
  window addMorph: (displayView := SimulatorImageMorph new image: displayForm)
  frame: (0@0 corner: 1@0.95).
  displayView activeHand addEventListener: self.
  eventTransformer := SimulatorEventTransformer new.
 
  window addMorph: (PluggableTextMorph on: self
  text: #byteCountText accept: nil
  readSelection: nil menu: #utilitiesMenu:) hideScrollBarsIndefinitely
  frame: (0@0.95 corner: 1@1).
 
  borderWidth := [SystemWindow borderWidth] "Squeak 4.1"
  on: MessageNotUnderstood
  do: [:ex| 0]. "3.8"
  borderWidth := borderWidth + window borderWidth.
  window openInWorldExtent: (self desiredDisplayExtent
+ + (2 * borderWidth@borderWidth)
- + (2 * borderWidth)
  + (0@window labelHeight)
  * (1@(1/0.95))) rounded!

Item was added:
+ ----- Method: VMMaker class>>generateSqueakSpurCog64MTVM (in category 'configurations') -----
+ generateSqueakSpurCog64MTVM
+ "No primitives since we can use those for the Cog VM"
+ ^VMMaker
+ generate: CoInterpreterMT
+ and: StackToRegisterMappingCogit
+ with: #(COGMTVM true
+ ObjectMemory Spur64BitCoMemoryManager
+ MULTIPLEBYTECODESETS true
+ bytecodeTableInitializer initializeBytecodeTableForSqueakV3PlusClosuresSistaV1Hybrid)
+ to: (FileDirectory default pathFromURI: self sourceTree, '/spur64src')
+ platformDir: (FileDirectory default pathFromURI: self sourceTree, '/platforms')
+ including:#()!

Item was added:
+ ----- Method: VMMaker class>>generateSqueakSpurCogMTVM (in category 'configurations') -----
+ generateSqueakSpurCogMTVM
+ "No primitives since we can use those for the Cog VM"
+ ^VMMaker
+ generate: CoInterpreterMT
+ and: StackToRegisterMappingCogit
+ with: #(COGMTVM true
+ ObjectMemory Spur32BitCoMemoryManager
+ MULTIPLEBYTECODESETS true
+ bytecodeTableInitializer initializeBytecodeTableForSqueakV3PlusClosuresSistaV1Hybrid)
+ to: (FileDirectory default pathFromURI: self sourceTree, '/spursrc')
+ platformDir: (FileDirectory default pathFromURI: self sourceTree, '/platforms')
+ including:#()!