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

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

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

Name: VMMaker.oscog-eem.2137
Author: eem
Time: 22 February 2017, 4:06:39.159606 pm
UUID: 69cb5651-490e-481d-85cf-7bd7ed25f785
Ancestors: VMMaker.oscog-EstebanLorenzano.2136

CoInterpreter:
Earlier assert checking for invalid class on send.

Cogit:
Support for turning frame offsets and variable base offsets into variable names in disassembly.  Support for mapping from offset to temp index and refactor frameOffsetOfTemporary: to frameOffsetOfTemporary:numArgs: to enable this.
Print maybeSelectorForMethod: if selector is nil when disassembling a cogMethod.

StackToRegisterMappingCogit:
Add CogSimStackEntry>>isFrameTempVar and use it swhere possible (for clarity).

RegisterAllocatingCogit:
Again improve register assignment code in genStorePop:TemporaryVariable:; no need to allocate a register for a temp if it already has one.  Must be careful not to assign the same register to two distinct entries, a slip easy to make given that popToReg: and storeToReg: actually do assignments.

Realise that a backward jump merge is exactly the same problem as a jump forwards merge (i.e. a parallel move), hence nuke reconcileRegisterStateForBackwardJoin:, and refactor mergeCurrentSimStackWith: into mergeCurrentSimStackWith:forwards:.

Realise that register assignments in merges should be done once only, so that merges between temp entries with registers should not be done if already done higher up the stack with volatile entries.  

Elide the obscurantism of directionForMergeWith: in favour of pushForMergeWith:.

Add temporary support for aborting a compilation that fails to generate correct merge code.  The idea being to defer solving the somewhat difficult parallel merge problem until after the basics have been verified.

With these changes the RegisterAllocatingCogit can start-up a Squeak image, albeit with a handfull of merge failures: Integer>>#digitMultiply:neg:, Bitmap>>#decompress:fromByteArray:at:, TextStyle>>#nextTabXFrom:leftMargin:rightMargin:, SmallInteger>>#printString, WeakKeyDictionary>>#finalizeValues, Morph>>#balloonText.

Fix a speeling eppop.
Nuke an obsolete method or two.
Revert a change stamp.

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

Item was changed:
  ----- Method: CoInterpreter>>internalFindNewMethodOrdinary (in category 'message sending') -----
  internalFindNewMethodOrdinary
  "Find the compiled method to be run when the current messageSelector is
  sent to the given class, setting the values of newMethod and primitiveIndex."
  | ok |
  <inline: true>
  ok := self inlineLookupInMethodCacheSel: messageSelector classTag: lkupClassTag.
  ok ifTrue:
  [self ifAppropriateCompileToNativeCode: newMethod selector: messageSelector]
  ifFalse:
  [self externalizeIPandSP.
  ((objectMemory isOopForwarded: messageSelector)
   or: [objectMemory isForwardedClassTag: lkupClassTag]) ifTrue:
  [(objectMemory isOopForwarded: messageSelector) ifTrue:
  [messageSelector := self handleForwardedSelectorFaultFor: messageSelector].
  (objectMemory isForwardedClassTag: lkupClassTag) ifTrue:
  [lkupClassTag := self handleForwardedSendFaultForTag: lkupClassTag].
  (self lookupInMethodCacheSel: messageSelector classTag: lkupClassTag) ifTrue:
  [^self ifAppropriateCompileToNativeCode: newMethod selector: messageSelector]].
  lkupClass := objectMemory classForClassTag: lkupClassTag.
+ self assert: (lkupClass notNil and: [self addressCouldBeClassObj: lkupClass]).
  NewspeakVM
  ifTrue: [self lookupOrdinarySend]
  ifFalse: [self lookupMethodInClass: lkupClass].
  self internalizeIPandSP.
  self addNewMethodToCache: lkupClass]!

Item was added:
+ ----- Method: CogRegisterAllocatingSimStackEntry>>complicatedIsMergedWithTargetEntry: (in category 'comparing') -----
+ complicatedIsMergedWithTargetEntry: targetEntry
+ "The receiver is a simStackEntry at a jump to the corresponding simStackEntry at the jump's target.
+ Answer if no merge is required for the jump."
+ <var: 'ssEntry' type: #'CogSimStackEntry *'>
+ spilled ~= targetEntry spilled ifTrue: "push or pop required"
+ [^false].
+ (liveRegister = NoReg and: [targetEntry liveRegister ~= NoReg]) ifTrue: "register load required"
+ [^false].
+ (liveRegister ~= NoReg
+ and: [liveRegister = targetEntry liveRegister
+ and: [type = targetEntry type
+ and: [type = SSConstant or: [type = SSRegister and: [register = targetEntry register]]]]]) ifTrue:
+ [^true].
+ ((type = SSBaseOffset or: [type == SSSpill])
+ and: [(targetEntry type = SSBaseOffset or: [targetEntry type == SSSpill])
+ and: [offset = targetEntry offset and: [register = targetEntry register]]]) ifTrue:
+ [^true].
+ "self: const =1 (16r1) (live: Extra4Reg) {172} vs reg ReceiverResultReg {127}"
+ "self: reg ReceiverResultReg {95} vs reg Extra5Reg {85}"
+ ((type = SSConstant and: [targetEntry type = SSRegister and: [liveRegister ~= targetEntry registerOrNone]])
+ or: [type = SSRegister and: [targetEntry type = SSRegister and: [register ~= targetEntry registerOrNone]]]) ifFalse:
+ [self halt: 'comment the incompatible pair please'].
+ ^false!

Item was changed:
  ----- Method: CogRegisterAllocatingSimStackEntry>>isMergedWithTargetEntry: (in category 'comparing') -----
  isMergedWithTargetEntry: targetEntry
  "The receiver is a simStackEntry at a jump to the corresponding simStackEntry at the jump's target.
  Answer if no merge is required for the jump."
  <var: 'ssEntry' type: #'CogSimStackEntry *'>
  spilled ~= targetEntry spilled ifTrue: "push or pop required"
  [^false].
  (liveRegister = NoReg and: [targetEntry liveRegister ~= NoReg]) ifTrue: "register load required"
  [^false].
+ (self isSameEntryAs: targetEntry) ifTrue:
+ [^liveRegister = targetEntry liveRegister].
+ (type = SSConstant and: [targetEntry type = SSRegister and: [liveRegister = targetEntry register]]) ifTrue:
- (liveRegister ~= NoReg
- and: [liveRegister = targetEntry liveRegister
- and: [type = targetEntry type
- and: [type = SSConstant or: [type = SSRegister and: [register = targetEntry register]]]]]) ifTrue:
  [^true].
- ((type = SSBaseOffset or: [type == SSSpill])
- and: [(targetEntry type = SSBaseOffset or: [targetEntry type == SSSpill])
- and: [offset = targetEntry offset and: [register = targetEntry register]]]) ifTrue:
- [^true].
  "self: const =1 (16r1) (live: Extra4Reg) {172} vs reg ReceiverResultReg {127}"
  "self: reg ReceiverResultReg {95} vs reg Extra5Reg {85}"
+ "(bo ReceiverResultReg+296 (live: Extra5Reg) {88} vs reg ReceiverResultReg {84}"
  ((type = SSConstant and: [targetEntry type = SSRegister and: [liveRegister ~= targetEntry registerOrNone]])
+ or: [(type = SSRegister and: [targetEntry type = SSRegister and: [register ~= targetEntry registerOrNone]])
+ or: [type = SSBaseOffset and: [register = ReceiverResultReg and: [targetEntry type = SSRegister]]]]) ifFalse:
- or: [type = SSRegister and: [targetEntry type = SSRegister and: [register ~= targetEntry registerOrNone]]]) ifFalse:
  [self halt: 'comment the incompatible pair please'].
  ^false!

Item was changed:
  ----- Method: CogRegisterAllocatingSimStackEntry>>reconcileForwardsWith: (in category 'compile abstract instructions') -----
  reconcileForwardsWith: targetEntry
  "Make the state of the receiver, a stack entry at the end of a basic block,
  the same as the corresponding simStackEntry at the target of a preceding
  jump to the beginning of the next basic block.  Make sure targetEntry
  reflects the state of the merged simStack; it will be installed as the current
  entry by restoreSimStackAtMergePoint: in mergeWithFixupIfRequired:.
 
  Answer if the liveRegister for the targetEntry (if any) should be deassigned;
  this is because if merging a non-temp with a temp that has a live register we
  can assign to the register, but must unassign the register from the temp,
  otherwise the temp will acquire the merged value without an assignment."
  <var: #targetEntry type: #'SimStackEntry *'>
  | targetReg |
  (targetReg := targetEntry registerOrNone) = NoReg ifTrue:
  [| reg |
  self assert: targetEntry spilled.
  (self isSameEntryAs: targetEntry) ifTrue:
  [self assert: spilled.
  ^false].
  (reg := self registerOrNone) = NoReg ifTrue: [reg := TempReg].
  self storeToReg: reg.
  spilled
  ifTrue: [cogit MoveR: reg Mw: targetEntry offset r: targetEntry register]
  ifFalse: [cogit PushR: reg].
  ^false].
  liveRegister ~= NoReg ifTrue:
  [liveRegister ~= targetReg ifTrue:
  [cogit MoveR: liveRegister R: targetReg].
  (spilled and: [targetEntry spilled not]) ifTrue:
  [cogit AddCq: objectRepresentation wordSize R: SPReg].
  ^false].
  spilled
  ifTrue:
  [targetEntry spilled ifFalse:
  [cogit PopR: targetReg. "KISS; generate the least number of instructions..."
  ^false]]
  ifFalse:
  [targetEntry spilled ifTrue:
  [cogit SubCq: objectRepresentation wordSize R: SPReg]].
  type caseOf: {
  [SSBaseOffset] -> [cogit MoveMw: offset r: register R: targetReg].
  [SSSpill] -> [cogit MoveMw: offset r: register R: targetReg].
  [SSConstant] -> [cogit genMoveConstant: constant R: targetReg].
  [SSRegister] -> [register ~= targetReg ifTrue:
  [cogit MoveR: register R: targetReg]] }.
  (targetEntry type = SSConstant
  and: [type ~= SSConstant or: [constant ~= targetEntry constant]]) ifTrue:
  [targetEntry
  register: targetReg;
  type: SSRegister].
  "If merging a constant with a constant assigned to a register, then the register must be deassigned from any temps."
  ^targetEntry type = SSConstant
  "If merging a non-temp with a temp that has a live register we can assign
  to the register, but must unassign the register from the temp, otherwise
  the temp will acquire the merged value without an assignment."
+ or: [targetEntry isFrameTempVar and: [(self isSameEntryAs: targetEntry) not]]!
- or: [targetEntry type = SSBaseOffset
-  and: [targetEntry register = FPReg
-  and: [(self isSameEntryAs: targetEntry) not]]]!

Item was changed:
  ----- Method: CogRegisterAllocatingSimStackEntry>>reconcilePoppingWith: (in category 'compile abstract instructions') -----
  reconcilePoppingWith: targetEntry
  "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."
  <var: #targetEntry type: #'SimStackEntry *'>
  | targetReg |
+ spilled = targetEntry spilled ifTrue:
+ [self assert: (targetEntry spilled
+ ifTrue: [self isSameEntryAs: targetEntry]
+ ifFalse: [targetEntry registerOrNone ~= NoReg]).
- targetEntry spilled ifTrue:
- [self assert: (self isSameEntryAs: targetEntry).
  (targetReg := targetEntry registerOrNone) = NoReg ifTrue:
  [^self].
  type caseOf: {
  [SSBaseOffset] -> [cogit MoveMw: offset r: register R: targetReg].
  [SSSpill] -> [cogit MoveMw: offset r: register R: targetReg].
  [SSConstant] -> [cogit genMoveConstant: constant R: targetReg].
  [SSRegister] -> [targetReg ~= register ifTrue:
  [cogit MoveR: register R: targetReg]] }.
  ^self].
+ self assert: spilled.
  (targetEntry type ~= SSConstant
  and: [(targetReg := targetEntry registerOrNone) ~= NoReg])
  ifTrue: [cogit PopR: targetReg]
  ifFalse: [cogit AddCq: objectRepresentation wordSize R: SPReg]!

Item was added:
+ ----- Method: CogRegisterAllocatingSimStackEntry>>simplifiedIsMergedWithTargetEntry: (in category 'comparing') -----
+ simplifiedIsMergedWithTargetEntry: targetEntry
+ "The receiver is a simStackEntry at a jump to the corresponding simStackEntry at the jump's target.
+ Answer if no merge is required for the jump."
+ <var: 'ssEntry' type: #'CogSimStackEntry *'>
+ spilled ~= targetEntry spilled ifTrue: "push or pop required"
+ [^false].
+ (liveRegister = NoReg and: [targetEntry liveRegister ~= NoReg]) ifTrue: "register load required"
+ [^false].
+ (self isSameEntryAs: targetEntry) ifTrue:
+ [^liveRegister = targetEntry liveRegister].
+ (type = SSConstant and: [targetEntry type = SSRegister and: [liveRegister = targetEntry register]]) ifTrue:
+ [^true].
+ "self: const =1 (16r1) (live: Extra4Reg) {172} vs reg ReceiverResultReg {127}"
+ "self: reg ReceiverResultReg {95} vs reg Extra5Reg {85}"
+ "(bo ReceiverResultReg+296 (live: Extra5Reg) {88} vs reg ReceiverResultReg {84}"
+ ((type = SSConstant and: [targetEntry type = SSRegister and: [liveRegister ~= targetEntry registerOrNone]])
+ or: [(type = SSRegister and: [targetEntry type = SSRegister and: [register ~= targetEntry registerOrNone]])
+ or: [type = SSBaseOffset and: [register = ReceiverResultReg and: [targetEntry type = SSRegister]]]]) ifFalse:
+ [self halt: 'comment the incompatible pair please'].
+ ^false!

Item was added:
+ ----- 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]!

Item was changed:
  ----- Method: Cogit>>blockStartAt: (in category 'compile abstract instructions') -----
  blockStartAt: index
  <cmacro: '(index) (&blockStarts[index])'>
  "hack set startpc for printSimStack:toDepth:spillBase:on:"
+ self maybeNoteStartpcFor: (blockStarts at: index).
- (self class initializationOptions at: #tempNames ifAbsent: nil) ifNotNil:
- [self class initializationOptions at: #startpc put: (blockStarts at: index) startpc].
  ^blockStarts at: index!

Item was changed:
  ----- Method: Cogit>>disassembleMethod:on: (in category 'disassembly') -----
  disassembleMethod: surrogateOrAddress on: aStream
  <doNotGenerate>
  | cogMethod mapEntries codeRanges |
  cogMethod := surrogateOrAddress isInteger
  ifTrue: [self cogMethodSurrogateAt: surrogateOrAddress]
  ifFalse: [surrogateOrAddress].
  cogMethod cmType = CMBlock ifTrue:
  [^self disassembleMethod: cogMethod cmHomeMethod on: aStream].
  (disassemblingMethod isNil
  and: [self class initializationOptions at: #relativeAddressDisassembly ifAbsent: [false]]) ifTrue:
  [^[disassemblingMethod := cogMethod.
     self disassembleMethod: surrogateOrAddress on: aStream] ensure:
  [disassemblingMethod := nil]].
  self printMethodHeader: cogMethod on: aStream.
 
  mapEntries := Dictionary new.
  (cogMethod cmType = CMMethod and: [cogMethod cmIsFullBlock]) ifFalse:
  [mapEntries at: cogMethod asInteger + cmEntryOffset put: 'entry'].
 
  cogMethod cmType = CMMethod ifTrue:
  [cogMethod cmIsFullBlock
  ifTrue: [mapEntries at: cogMethod asInteger + cbNoSwitchEntryOffset put: 'noSwitchEntry']
  ifFalse: [mapEntries at: cogMethod asInteger + cmNoCheckEntryOffset put: 'noCheckEntry']].
 
  cogMethod cmType = CMClosedPIC
  ifTrue:
  [mapEntries at: cogMethod asInteger + firstCPICCaseOffset put: 'ClosedPICCase0'.
  1 to: MaxCPICCases - 1 do:
  [:i|
  mapEntries
  at: cogMethod asInteger + firstCPICCaseOffset + (i * cPICCaseSize)
  put: 'ClosedPICCase', i printString]]
  ifFalse:
  [self mapFor: cogMethod
  performUntil: #collectMapEntry:address:into:
  arg: mapEntries].
 
  NewspeakVM ifTrue:
  [objectRepresentation canPinObjects ifFalse:
  [mapEntries keys do:
  [:a|
  (mapEntries at: a) = #IsNSSendCall ifTrue:
  [mapEntries
  at: a + backEnd jumpShortByteSize
  put: {'Class'. #disassembleCachedOop:. (objectMemory wordSize)};
  at: a + backEnd jumpShortByteSize + objectMemory bytesPerOop
  put: {'ImplicitReceiver'. #disassembleCachedOop:. (objectMemory wordSize)}]]]].
 
  "This would all be far more elegant and simple if we used blocks.
  But there are no blocks in C and the basic enumerators here need
  to be used in the real VM.  Apologies."
  (codeRanges := self codeRangesFor: cogMethod) do:
  [:range|
  (cogMethod cmType = CMMethod) ifTrue:
  [mapEntries keysAndValuesDo:
  [:mcpc :label| | bcpc selectorOrNone |
  (((range includes: mcpc) or: [range last + 1 = mcpc])
  and: [(AnnotationsWithBytecodePCs includes: label)
  and: [range cogMethod stackCheckOffset > 0]]) ifTrue:
  [bcpc := self bytecodePCFor: mcpc startBcpc: range startpc in: range cogMethod.
  bcpc ~= 0 ifTrue:
  [label = #IsSendCall
  ifTrue:
  [selectorOrNone := (self selectorForSendAt: mcpc annotation: IsSendCall in: cogMethod methodObject).
  (selectorOrNone isInteger and: [objectMemory addressCouldBeOop: selectorOrNone]) ifTrue:
  [selectorOrNone := objectMemory stringOf: selectorOrNone].
  selectorOrNone := ' ', selectorOrNone]
  ifFalse: [selectorOrNone := ''].
  mapEntries
  at: mcpc
  put: label, selectorOrNone, ' bc ', bcpc printString, '/', (bcpc + 1) printString]]]].
  (cogMethod blockEntryOffset ~= 0
  and: [range first = (cogMethod blockEntryOffset + cogMethod asInteger)])
  ifTrue:
  [aStream nextPutAll: 'blockEntry:'; cr.
  self blockDispatchFor: cogMethod
  perform: #disassemble:from:to:arg:
  arg: aStream]
  ifFalse:
  [range first > (cogMethod address + cmNoCheckEntryOffset) ifTrue:
  [self printMethodHeader: range cogMethod
  on: aStream].
+ self maybeNoteStartpcFor: range.
  self disassembleFrom: range first to: range last labels: mapEntries on: aStream]].
  aStream nextPutAll: 'startpc: '; print: codeRanges first startpc; cr.
  (cogMethod cmType = CMMethod
  or: [cogMethod cmType = CMOpenPIC]) ifTrue:
  [[self mapFor: cogMethod
  performUntil: #printMapEntry:mcpc:args:
  arg: { aStream. codeRanges. cogMethod }]
  on: AssertionFailure
  do: [:ex|
  ex primitiveChangeClassTo: ResumableVMError basicNew. ":) :) :)"
  ex resume: nil]].
  ^cogMethod!

Item was added:
+ ----- Method: Cogit>>lookupFrameOffset: (in category 'disassembly') -----
+ lookupFrameOffset: anInteger
+ (self class initializationOptions at: #tempNames ifAbsent: nil) ifNotNil:
+ [:dict|
+ (self class initializationOptions at: #startpc ifAbsent: nil) ifNotNil:
+ [:startpc|
+ (dict at: startpc + 1 ifAbsent: nil) ifNotNil:
+ [:tempNames| | numArgs |
+ anInteger = FoxMFReceiver ifTrue:
+ [^'self'].
+ numArgs := self class initializationOptions at: #numArgs.
+ 1 to: tempNames size do:
+ [:i|
+ anInteger = (self frameOffsetOfTemporary: i - 1 numArgs: numArgs) ifTrue:
+ [^tempNames at: i]]]]].
+ ^nil!

Item was added:
+ ----- Method: Cogit>>maybeNoteStartpcFor: (in category 'disassembly') -----
+ maybeNoteStartpcFor: thing "<CogBlockStart|CogCodeRange>"
+ <doNotGenerate>
+ (self class initializationOptions at: #tempNames ifAbsent: nil) ifNotNil:
+ [self class initializationOptions
+ at: #startpc put: thing startpc;
+ at: #numArgs put: ([thing cogMethod ifNil: [-1] ifNotNil: [:cm| cm cmNumArgs]] "CogCodeRange"
+ on: MessageNotUnderstood
+ do: [:ex| thing numArgs])] "CogBlockStart"!

Item was changed:
  ----- Method: Cogit>>printMethodHeader:on: (in category 'disassembly') -----
  printMethodHeader: cogMethod on: aStream
  <doNotGenerate>
  self cCode: ''
  inSmalltalk:
  [cogMethod isInteger ifTrue:
  [^self printMethodHeader: (self cogMethodOrBlockSurrogateAt: cogMethod) on: aStream]].
  aStream ensureCr.
  cogMethod asInteger printOn: aStream base: 16.
  cogMethod cmType = CMMethod ifTrue:
  [aStream crtab; nextPutAll: 'objhdr: '.
  cogMethod objectHeader printOn: aStream base: 16].
  cogMethod cmType = CMBlock ifTrue:
  [aStream crtab; nextPutAll: 'homemth: '.
  cogMethod cmHomeMethod asUnsignedInteger printOn: aStream base: 16.
  aStream
  nextPutAll: ' (offset '; print: cogMethod homeOffset; nextPut: $);
  crtab; nextPutAll: 'startpc: '; print: cogMethod startpc].
  aStream
  crtab; nextPutAll: 'nArgs: '; print: cogMethod cmNumArgs;
  tab;    nextPutAll: 'type: '; print: cogMethod cmType.
  (cogMethod cmType ~= 0 and: [cogMethod cmType ~= CMBlock]) ifTrue:
  [aStream crtab; nextPutAll: 'blksiz: '.
  cogMethod blockSize printOn: aStream base: 16.
  cogMethod cmType = CMMethod ifTrue:
  [aStream crtab; nextPutAll: 'method: '.
  cogMethod methodObject printOn: aStream base: 16.
  aStream crtab; nextPutAll: 'mthhdr: '.
  cogMethod methodHeader printOn: aStream base: 16].
  aStream crtab; nextPutAll: 'selctr: '.
  cogMethod selector printOn: aStream base: 16.
  (coInterpreter lookupAddress: cogMethod selector) ifNotNil:
  [:string| aStream nextPut: $=; nextPutAll: string].
+ cogMethod selector = objectMemory nilObject ifTrue:
+ [aStream space; nextPut: $(; nextPutAll: (coInterpreter stringOf: (coInterpreter maybeSelectorOfMethod: cogMethod methodObject)); nextPut: $)].
  cogMethod cmType = CMMethod ifTrue:
  [aStream crtab; nextPutAll: 'blkentry: '.
  cogMethod blockEntryOffset printOn: aStream base: 16.
  cogMethod blockEntryOffset ~= 0 ifTrue:
  [aStream nextPutAll: ' => '.
  cogMethod asInteger + cogMethod blockEntryOffset printOn: aStream base: 16]]].
  cogMethod cmType = CMClosedPIC
  ifTrue:
  [aStream crtab; nextPutAll: 'cPICNumCases: '.
  cogMethod cPICNumCases printOn: aStream base: 16.
  aStream tab; nextPutAll: 'cpicHasMNUCase: ';
  nextPutAll: (cogMethod cpicHasMNUCase ifTrue: ['yes'] ifFalse: ['no'])]
  ifFalse:
  [aStream crtab; nextPutAll: 'stackCheckOffset: '.
  cogMethod stackCheckOffset printOn: aStream base: 16.
  cogMethod stackCheckOffset > 0 ifTrue:
  [aStream nextPut: $/.
  cogMethod asInteger + cogMethod stackCheckOffset printOn: aStream base: 16].
  cogMethod cmType = CMBlock
  ifTrue:
  [aStream
  crtab;
  nextPutAll: 'cbUsesInstVars ';
  nextPutAll: (cogMethod cbUsesInstVars ifTrue: ['yes'] ifFalse: ['no'])]
  ifFalse:
  [aStream
  crtab;
  nextPutAll: 'cmRefersToYoung: ';
  nextPutAll: (cogMethod cmRefersToYoung ifTrue: ['yes'] ifFalse: ['no']);
  tab;
  nextPutAll: 'cmIsFullBlock: ';
  nextPutAll: (cogMethod cmIsFullBlock ifTrue: ['yes'] ifFalse: ['no'])].
  cogMethod cmType = CMMethod ifTrue:
  [([cogMethod nextMethodOrIRCs] on: MessageNotUnderstood do: [:ex| nil]) ifNotNil:
  [:nmoircs| aStream crtab; nextPutAll: 'nextMethodOrIRCs: '.
  nmoircs = 0 ifTrue: [aStream print: nmoircs] ifFalse: [coInterpreter printHex: nmoircs]].
  ([cogMethod counters] on: MessageNotUnderstood do: [:ex| nil]) ifNotNil:
  [:cntrs| aStream crtab; nextPutAll: 'counters: '.
  cntrs = 0 ifTrue: [aStream print: cntrs] ifFalse: [coInterpreter printHex: cntrs]]]].
  aStream cr; flush!

Item was changed:
  ----- Method: MiscPrimitivePlugin class>>translatedPrimitivesForPharo (in category 'translation') -----
  translatedPrimitivesForPharo
  ^#(
  (Bitmap compress:toByteArray:)
  (Bitmap decompress:fromByteArray:at:)
  (Bitmap encodeBytesOf:in:at:)
  (Bitmap encodeInt:in:at:)
  (ByteString compare:with:collated:)
  (ByteString translate:from:to:table:)
  (ByteString findFirstInString:inSet:startingAt:)
  (ByteString indexOfAscii:inString:startingAt:)
  (String findSubstringViaPrimitive:in:startingAt:matchTable:)
  (ByteArray hashBytes:startingWith:)
  (SampledSound convert8bitSignedFrom:to16Bit:)
  )!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>assignToTempRegConflictingRegisterIn: (in category 'bytecode generator support') -----
  assignToTempRegConflictingRegisterIn: conflictingRegisterMask
  "Find the stackEntry in simStack whose liveRegister matches conflictingRegisterMask
  and assign it to TempReg."
  self assert: (self isAPowerOfTwo: conflictingRegisterMask).
  0 to: simStackPtr do:
  [:i|
  (self simStackAt: i) registerMaskOrNone = conflictingRegisterMask ifTrue:
  [(self simStackAt: i)
  storeToReg: TempReg;
  liveRegister: TempReg.
+ i+1 to: simStackPtr do:
+ [:j|
+ self deny: (self simStackAt: i) registerMaskOrNone = conflictingRegisterMask].
  ^self]].
  self error: 'conflict entry not found'!

Item was added:
+ ----- Method: RegisterAllocatingCogit>>compileAbstractInstructionsFrom:through: (in category 'compile abstract instructions') -----
+ compileAbstractInstructionsFrom: start through: end
+ "Loop over bytecodes, dispatching to the generator for each bytecode, handling fixups in due course.
+ Override to provide a development-time only escape for failed merges due to partially implemented
+ parallel move."
+ ^[super compileAbstractInstructionsFrom: start through: end]
+ on: Notification
+ do: [:ex|
+ ex tag == #failedMerge ifTrue:
+ [coInterpreter transcript
+ ensureCr; nextPutAll: 'FAILED MERGE IN ';
+ nextPutAll: (coInterpreter nameOfClass: (coInterpreter methodClassOf: methodObj));
+ nextPutAll: '>>#'; nextPutAll: (coInterpreter stringOf: (coInterpreter maybeSelectorOfMethod: methodObj));
+ flush.
+ ^ShouldNotJIT].
+ ex pass]!

Item was removed:
- ----- Method: RegisterAllocatingCogit>>conflcitsResolvedBetweenSimStackAnd: (in category 'bytecode generator support') -----
- conflcitsResolvedBetweenSimStackAnd: mergeSimStack
- "There are no register conflicts between simStack and mergeSimStack if
- traversing both stacks from hot end (simStackPtr) to cold end (0) no register
- exists in simStack that has previously existed in mergeSimStack.  This is because
- the resolution assigns values from simStack to registers in mergeSimStack and so
- must not assign to a register yet to be read."
- | regsWrittenToMask |
- regsWrittenToMask := 0.
- simStackPtr to: 0 by: -1 do:
- [:i| | mergeMask currentMask |
- mergeMask := (self simStack: mergeSimStack at: i) registerMaskOrNone.
- currentMask := (self simStack: simStack at: i) registerMaskOrNone.
- mergeMask ~= currentMask ifTrue:
- [(currentMask anyMask: regsWrittenToMask) ifTrue:
- [^false]].
- regsWrittenToMask := regsWrittenToMask bitOr: mergeMask].
- ^true!

Item was added:
+ ----- Method: RegisterAllocatingCogit>>conflictsResolvedBetweenSimStackAnd: (in category 'bytecode generator support') -----
+ conflictsResolvedBetweenSimStackAnd: mergeSimStack
+ "There are no register conflicts between simStack and mergeSimStack if
+ traversing both stacks from hot end (simStackPtr) to cold end (0) no register
+ exists in simStack that has previously existed in mergeSimStack.  This is because
+ the resolution assigns values from simStack to registers in mergeSimStack and so
+ must not assign to a register yet to be read."
+ | regsWrittenToMask |
+ regsWrittenToMask := 0.
+ simStackPtr to: 0 by: -1 do:
+ [:i| | mergeMask currentMask |
+ mergeMask := (self simStack: mergeSimStack at: i) registerMaskOrNone.
+ currentMask := (self simStack: simStack at: i) registerMaskOrNone.
+ mergeMask ~= currentMask ifTrue:
+ [(currentMask anyMask: regsWrittenToMask) ifTrue:
+ [^false]].
+ regsWrittenToMask := regsWrittenToMask bitOr: mergeMask].
+ ^true!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>deassignRegisterForTempVar:in: (in category 'bytecode generator support') -----
  deassignRegisterForTempVar: targetEntry in: mergeSimStack
  "If merging a non-temp with a temp that has a live register we can assign
  to the register, but must unassign the register from the temp, otherwise
  the temp will acquire the merged value without an assignment.  The targetEntry
  must also be transmogrified into an SSRegister entry."
  <var: #targetEntry type: #'SimStackEntry *'>
  <var: #duplicateEntry type: #'SimStackEntry *'>
  <var: #mergeSimStack type: #'SimStackEntry *'>
  <inline: true>
  | reg |
  reg := targetEntry liveRegister.
+ self assert: (reg ~= NoReg and: [targetEntry type = SSConstant or: [targetEntry isFrameTempVar]]).
- self assert: ((targetEntry type = SSConstant and: [reg ~= NoReg])
- or: [targetEntry type = SSBaseOffset and: [targetEntry register = FPReg]]).
  targetEntry type = SSConstant
  ifTrue:
  [simStackPtr to: 0 by: -1 do:
  [:j| | duplicateEntry |
  duplicateEntry := self simStack: mergeSimStack at: j.
  (duplicateEntry registerOrNone = reg
   and: [duplicateEntry type = SSBaseOffset or: [duplicateEntry type = SSSpill]]) ifTrue:
  [duplicateEntry liveRegister: NoReg]]]
  ifFalse:
  [simStackPtr to: 0 by: -1 do:
  [:j| | duplicateEntry |
  duplicateEntry := self simStack: mergeSimStack at: j.
  (targetEntry isSameEntryAs: duplicateEntry) ifTrue:
+ [j < methodOrBlockNumTemps
+ ifTrue: [duplicateEntry liveRegister: NoReg]
+ ifFalse: [duplicateEntry type: SSRegister; register: reg]]]].
- [duplicateEntry liveRegister: NoReg]]].
  targetEntry
  type: SSRegister;
  register: reg!

Item was removed:
- ----- Method: RegisterAllocatingCogit>>directionForMergeWith: (in category 'bytecode generator support') -----
- directionForMergeWith: mergeSimStack
- "Answer > 0 if values must be pushed from simStack to merge with mergeSimStack, otherwise < 0 (the default)."
- <var: #mergeSimStack type: #'SimStackEntry *'>
- <inline: true>
- simStackPtr to: methodOrBlockNumArgs by: -1 do:
- [:i|
- (self simStack: mergeSimStack at: i) spilled ifTrue:
- [(self simStack: simStack at: i) spilled ifFalse:
- [^1]]].
- ^-1!

Item was added:
+ ----- Method: RegisterAllocatingCogit>>duplicateRegisterAssignmentsInTemporaries (in category 'debugging') -----
+ duplicateRegisterAssignmentsInTemporaries
+ | liveRegisters |
+ liveRegisters := 0.
+ 0 to: methodOrBlockNumTemps - 1 do:
+ [:i| | liveRegister |
+ liveRegister := (self simStackAt: i) liveRegister.
+ liveRegister ~= NoReg ifTrue:
+ [(self register: liveRegister isInMask: liveRegisters) ifTrue:
+ [^true].
+ liveRegisters := liveRegisters bitOr: 1 << liveRegister]].
+ ^false!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>ensureFixupAt: (in category 'bytecode generator support') -----
  ensureFixupAt: targetPC
  "Make sure there's a flagged fixup at the target pc in fixups.
  Initially a fixup's target is just a flag.  Later on it is replaced with a proper instruction.
  Override to enerate stack merging code if required."
  | fixup |
  <var: #fixup type: #'BytecodeFixup *'>
+ self assert: targetPC > bytecodePC.
+ fixup := self fixupAt: targetPC.
- fixup := self fixupAt:  targetPC.
  fixup needsFixup
  ifTrue:
  [fixup mergeSimStack
  ifNil: [self setMergeSimStackOf: fixup]
+ ifNotNil: [self mergeCurrentSimStackWith: fixup forwards: true]]
- ifNotNil: [self mergeCurrentSimStackWith: fixup]]
  ifFalse:
  [self assert: fixup mergeSimStack isNil.
  self moveVolatileSimStackEntriesToRegisters.
  self setMergeSimStackOf: fixup].
  ^super ensureFixupAt: targetPC!

Item was removed:
- ----- Method: RegisterAllocatingCogit>>frameOffsetOfLastTemp (in category 'bytecode generator support') -----
- frameOffsetOfLastTemp
- ^self frameOffsetOfTemporary: methodOrBlockNumTemps!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>genJumpBackTo: (in category 'bytecode generator support') -----
+ genJumpBackTo: targetPC
+ | target |
+ "We generate
+ merge
+ cmp stackLimit
+ jumpAboveOrEqual target
+ flush
+ checkForInterrupts
+ marge from flushed
+ jmp target
+ self printSimStack; printSimStack: target mergeSimStack"
+ self assert: targetPC < bytecodePC.
+ target := self fixupAt: targetPC.
+ self mergeCurrentSimStackWith: target forwards: false.
- genJumpBackTo: targetBytecodePC
- | nothingToFlush label |
- <var: #label type: #'AbstractInstruction *'>
- "If there's nothing to flush then the stack state at this point is the same as that after
- the check for interrupts and we can avoid generating the register reload code twice."
- nothingToFlush := (self liveRegisters bitClear: (self registerMaskFor: FPReg)) = 0.
- nothingToFlush ifTrue:
- [label := self Label].
- self reconcileRegisterStateForBackwardJoin: (self fixupAt: targetBytecodePC).
  self MoveAw: coInterpreter stackLimitAddress R: TempReg.
  self CmpR: TempReg R: SPReg. "N.B. FLAGS := SPReg - TempReg"
+ self JumpAboveOrEqual: target.
- self JumpAboveOrEqual: (self fixupAt: targetBytecodePC).
 
  self ssFlushTo: simStackPtr.
  self CallRT: ceCheckForInterruptTrampoline.
  self annotateBytecode: self Label.
  self flushLiveRegistersForSuspensionPoint.
+ self mergeCurrentSimStackWith: target forwards: false.
+ self Jump: target.
- nothingToFlush
- ifTrue:
- [self Jump: label]
- ifFalse:
- [self reconcileRegisterStateForBackwardJoin: (self fixupAt: targetBytecodePC).
- self Jump: (self fixupAt: targetBytecodePC)].
  deadCode := true. "can't fall through"
  ^0!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>genStorePop:TemporaryVariable: (in category 'bytecode generator support') -----
  genStorePop: popBoolean TemporaryVariable: tempIndex
  <inline: false>
+ | top srcRegOrNone destReg |
+ self deny: self duplicateRegisterAssignmentsInTemporaries.
- | srcRegOrNone destReg |
  self ssFlushUpThroughTemporaryVariable: tempIndex.
  "To avoid a stall writing through destReg, remember srcReg before the potential ssPop: 1 in ssStorePop:toReg:"
+ top := self ssTop.
+ srcRegOrNone := top registerOrNone.
- srcRegOrNone := self ssTop registerOrNone.
  "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) liveRegister.
+ destReg ~= NoReg
+ ifTrue:
+ [self ssStorePop: popBoolean toReg: destReg]
+ ifFalse:
+ [((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) liveRegister: destReg.
+ self copyLiveRegisterToCopiesOf: (self simStackAt: tempIndex)]].
- ((self ssTop type = SSConstant
-  or: [self ssTop registerMaskOrNone anyMask: 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].
  self MoveR: (srcRegOrNone ~= NoReg ifTrue: [srcRegOrNone] ifFalse: [destReg])
  Mw: (self frameOffsetOfTemporary: tempIndex)
  r: FPReg.
+ self deny: self duplicateRegisterAssignmentsInTemporaries.
- destReg ~= TempReg ifTrue:
- [(self simStackAt: tempIndex) liveRegister: destReg.
- self copyLiveRegisterToCopiesOf: (self simStackAt: tempIndex)].
  ^0!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>mergeCurrentSimStackWith: (in category 'bytecode generator support') -----
  mergeCurrentSimStackWith: fixup
  <var: #fixup type: #'BytecodeFixup *'>
+ | mergeSimStack currentEntry targetEntry |
- | mergeSimStack direction currentEntry targetEntry |
  <var: #mergeSimStack type: #'SimStackEntry *'>
  "At a merge point the cogit expects the stack to be in the same state as mergeSimStack.
  mergeSimStack is the state as of some jump forward 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.
  The state of optStatus must agree."
  <var: #targetEntry type: #'SimStackEntry *'>
  <var: #currentEntry type: #'SimStackEntry *'>
  (mergeSimStack := fixup mergeSimStack) ifNil: [^self].
  "Assignments amongst the registers must be made in order to avoid overwriting.
  If necessary exchange registers amongst simStack's entries to resolve any conflicts."
  self resolveRegisterOrderConflictsBetweenCurrentSimStackAnd: mergeSimStack.
+ self assert: (self conflictsResolvedBetweenSimStackAnd: mergeSimStack).
+ (self pushForMergeWith: mergeSimStack)
- self assert: (self conflcitsResolvedBetweenSimStackAnd: mergeSimStack).
- "Must determine if we will push or pop values.
- If pushing values must enumerate from bottom to top.
- If popping, must enumerate from top to bottom."
- direction := self directionForMergeWith: mergeSimStack.
- direction > 0
  ifTrue:
  [0 to: simStackPtr do:
  [:i|
  currentEntry := self simStack: simStack at: i.
  targetEntry := self simStack: mergeSimStack at: i.
  (currentEntry reconcileForwardsWith: targetEntry) ifTrue:
  [self assert: i >= methodOrBlockNumArgs.
  self deassignRegisterForTempVar: targetEntry in: mergeSimStack].
  "Note, we could update the simStack and spillBase here but that is done in restoreSimStackAtMergePoint:
  spilled ifFalse:
  [simSpillBase := i - 1].
  simStack
  at: i
  put: (self
  cCode: [mergeSimStack at: i]
  inSmalltalk: [(mergeSimStack at: i) copy])"]]
  ifFalse:
  [simStackPtr to: 0 by: -1 do:
  [:i|
  currentEntry := self simStack: simStack at: i.
  targetEntry := self simStack: mergeSimStack at: i.
  (currentEntry reconcileForwardsWith: targetEntry) ifTrue:
  [self assert: i >= methodOrBlockNumArgs.
  self deassignRegisterForTempVar: targetEntry in: mergeSimStack].
  "Note, we could update the simStack and spillBase here but that is done in restoreSimStackAtMergePoint:
  spilled ifFalse:
  [simSpillBase := i - 1].
  simStack
  at: i
  put: (self
  cCode: [mergeSimStack at: i]
  inSmalltalk: [(mergeSimStack at: i) copy])"]].
 
  "a.k.a. fixup isReceiverResultRegSelf: (fixup isReceiverResultRegSelf and: [optStatus isReceiverResultRegLive])"
  optStatus isReceiverResultRegLive ifFalse:
  [fixup isReceiverResultRegSelf: false]!

Item was added:
+ ----- Method: RegisterAllocatingCogit>>mergeCurrentSimStackWith:forwards: (in category 'bytecode generator support') -----
+ mergeCurrentSimStackWith: fixup forwards: forwards
+ "At a merge point the cogit expects the stack to be in the same state as 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.
+ The state of optStatus must agree.
+ Generate code to merge the current simStack with that of the target fixup,
+ the goal being to keep as many registers live as possible.  If the merge is forwards
+ registers can be deassigned (since registers are always written to temp vars).
+ But if backwards, nothing can be deassigned, and the state /must/ reflect the target."
+ "self printSimStack; printSimStack: fixup mergeSimStack"
+ <var: #fixup type: #'BytecodeFixup *'>
+ | startIndex mergeSimStack currentEntry targetEntry writtenToRegisters |
+ <var: #mergeSimStack type: #'SimStackEntry *'>
+ <var: #targetEntry type: #'SimStackEntry *'>
+ <var: #currentEntry type: #'SimStackEntry *'>
+ (mergeSimStack := fixup mergeSimStack) ifNil: [^self].
+ startIndex := opcodeIndex. "for debugging"
+ "Assignments amongst the registers must be made in order to avoid overwriting.
+ If necessary exchange registers amongst simStack's entries to resolve any conflicts."
+ self resolveRegisterOrderConflictsBetweenCurrentSimStackAnd: mergeSimStack.
+ (self asserta: (self conflictsResolvedBetweenSimStackAnd: mergeSimStack)) ifFalse:
+ [Notification new tag: #failedMerge; signal].
+ writtenToRegisters := 0.
+ (self pushForMergeWith: mergeSimStack)
+ ifTrue:
+ [methodOrBlockNumArgs to: simStackPtr do:
+ [:i|
+ currentEntry := self simStack: simStack at: i.
+ targetEntry := self simStack: mergeSimStack at: i.
+ writtenToRegisters := writtenToRegisters bitOr: targetEntry registerMask.
+ (currentEntry reconcileForwardsWith: targetEntry) ifTrue:
+ [self assert: i >= methodOrBlockNumArgs.
+ self deassignRegisterForTempVar: targetEntry in: mergeSimStack].
+ "Note, we could update the simStack and spillBase here but that is done in restoreSimStackAtMergePoint:
+ spilled ifFalse:
+ [simSpillBase := i - 1].
+ simStack
+ at: i
+ put: (self
+ cCode: [mergeSimStack at: i]
+ inSmalltalk: [(mergeSimStack at: i) copy])"]]
+ ifFalse:
+ [simStackPtr to: methodOrBlockNumArgs by: -1 do:
+ [:i|
+ currentEntry := self simStack: simStack at: i.
+ targetEntry := self simStack: mergeSimStack at: i.
+ writtenToRegisters := writtenToRegisters bitOr: targetEntry registerMask.
+ (currentEntry reconcileForwardsWith: targetEntry) ifTrue:
+ [self assert: i >= methodOrBlockNumArgs.
+ self deassignRegisterForTempVar: targetEntry in: mergeSimStack].
+ "Note, we could update the simStack and spillBase here but that is done in restoreSimStackAtMergePoint:
+ spilled ifFalse:
+ [simSpillBase := i - 1].
+ simStack
+ at: i
+ put: (self
+ cCode: [mergeSimStack at: i]
+ inSmalltalk: [(mergeSimStack at: i) copy])"]].
+ methodOrBlockNumArgs - 1 to: 0 by: -1 do:
+ [:i|
+ targetEntry := self simStack: mergeSimStack at: i.
+ (targetEntry registerMask noMask: writtenToRegisters) ifTrue:
+ [currentEntry := self simStack: simStack at: i.
+ writtenToRegisters := writtenToRegisters bitOr: targetEntry registerMask.
+ (currentEntry reconcileForwardsWith: targetEntry) ifTrue:
+ [self assert: i >= methodOrBlockNumArgs.
+ self deassignRegisterForTempVar: targetEntry in: mergeSimStack]]].
+ optStatus isReceiverResultRegLive ifFalse:
+ [forwards
+ ifTrue: "a.k.a. fixup isReceiverResultRegSelf: (fixup isReceiverResultRegSelf and: [optStatus isReceiverResultRegLive])"
+ [fixup isReceiverResultRegSelf: false]
+ ifFalse:
+ [fixup isReceiverResultRegSelf ifTrue:
+ [self putSelfInReceiverResultReg]]]!

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:.
- flushes the stack to the stack pointer so the fall through execution path simStack is
- in the state the merge point expects it to be.
- restores the simStack to the state the jumps to the merge point expects it to be.
 
  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 *'>
  "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]
  ifFalse:
  [self flushRegistersOnlyLiveOnFallThrough: fixup].
  ^0].
 
  "cases 3 and 4"
  self assert: fixup isMergeFixup.
  self traceMerge: fixup.
  deadCode
  ifTrue: [simStackPtr := fixup simStackPtr] "case 3"
+ ifFalse: [self mergeCurrentSimStackWith: fixup forwards: true]. "case 4"
- ifFalse: [self mergeCurrentSimStackWith: fixup]. "case 4"
  "cases 3 and 4"
  deadCode := false.
  fixup isBackwardBranchFixup ifTrue:
  [self assert: fixup mergeSimStack isNil.
  self setMergeSimStackOf: fixup].
  fixup targetInstruction: self Label.
  self assert: simStackPtr = fixup simStackPtr.
  self cCode: '' inSmalltalk:
  [self assert: fixup simStackPtr = (self debugStackPointerFor: bytecodePC)].
  self restoreSimStackAtMergePoint: fixup.
 
  ^0!

Item was added:
+ ----- Method: RegisterAllocatingCogit>>pushForMergeWith: (in category 'bytecode generator support') -----
+ pushForMergeWith: mergeSimStack
+ "Answer if values must be pushed from simStack to merge with mergeSimStack, otherwise < 0 (the default)."
+ <var: #mergeSimStack type: #'SimStackEntry *'>
+ <inline: true>
+ simStackPtr to: methodOrBlockNumArgs by: -1 do:
+ [:i|
+ (self simStack: mergeSimStack at: i) spilled ~=(self simStack: simStack at: i) spilled ifTrue:
+ [^(self simStack: mergeSimStack at: i) spilled]].
+ ^false!

Item was removed:
- ----- Method: RegisterAllocatingCogit>>reconcileRegisterStateForBackwardJoin: (in category 'bytecode generator support') -----
- reconcileRegisterStateForBackwardJoin: fixup
- <var: #fixup type: #'BytecodeFixup *'>
- | fixupSimStack |
- <var: #fixupSimStack type: #'SimStackEntry *'>
- self assert: (optStatus ssEntry isSameEntryAs: (self addressOf: simSelf)).
- fixup isReceiverResultRegSelf ifTrue:
- [optStatus isReceiverResultRegLive ifFalse:
- [optStatus ssEntry storeToReg: ReceiverResultReg]].
- fixupSimStack := fixup mergeSimStack.
- simStackPtr to: 0 by: -1 do:
- [:i|
- self assert: (self simStackAt: i) spilled.
- (self simStackAt: i) reconcileBackwardsWith: (self addressOf: (fixupSimStack at: i))]!

Item was changed:
  ----- Method: SimpleStackBasedCogit>>frameOffsetOfTemporary: (in category 'bytecode generator support') -----
  frameOffsetOfTemporary: index
+ ^self frameOffsetOfTemporary: index numArgs: methodOrBlockNumArgs!
- ^index < methodOrBlockNumArgs
- ifTrue: [FoxCallerSavedIP + ((methodOrBlockNumArgs - index) * objectMemory wordSize)]
- ifFalse: [FoxMFReceiver - objectMemory wordSize + ((methodOrBlockNumArgs - index) * objectMemory wordSize)]!

Item was added:
+ ----- Method: SimpleStackBasedCogit>>frameOffsetOfTemporary:numArgs: (in category 'bytecode generator support') -----
+ frameOffsetOfTemporary: index numArgs: numArgs
+ <inline: true>
+ ^index < numArgs
+ ifTrue: [FoxCallerSavedIP + ((numArgs - index) * objectMemory wordSize)]
+ ifFalse: [FoxMFReceiver - objectMemory wordSize + ((numArgs - index) * objectMemory wordSize)]!

Item was added:
+ ----- Method: SimpleStackBasedCogit>>temporaryIndexOfFrameOffset: (in category 'bytecode generator support') -----
+ temporaryIndexOfFrameOffset: offset
+ ^self temporaryIndexOfFrameOffset: offset numArgs: methodOrBlockNumArgs!

Item was added:
+ ----- Method: SimpleStackBasedCogit>>temporaryIndexOfFrameOffset:numArgs: (in category 'bytecode generator support') -----
+ temporaryIndexOfFrameOffset: offset numArgs: numArgs
+ "For assert checking..."
+ ^numArgs - ((offset < 0 "args are > 0"
+ ifTrue: [offset - FoxMFReceiver + objectMemory wordSize]
+ ifFalse: [offset - FoxCallerSavedIP]) / objectMemory wordSize)!