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

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

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

Name: VMMaker.oscog-eem.2329
Author: eem
Time: 4 February 2018, 11:52:42.260503 am
UUID: 74077530-8ed4-4601-a446-062057eeb3d3
Ancestors: VMMaker.oscog-eem.2328

RegisterAllocatingCogit:
Revise merge code generation (& uke the now unneeded failed merge catcher).  Have the merge update simStack reflecting reality as the merge proceeds.  This update occurs in reconcilePoppingWith: & reconcilePushingWith:.  N.B. reconcilePoppingWith: & reconcilePushingWith: are now essentially identical apart from whether they pusgh or pop.  They will be merged and the push/pop moved to the client mergeCurrentSimStackWith: very soon.

On backward jumps copy the simStackState to the loop head on the first pass and on the second pass have the merge at the loop head preserve as many live registers as possible, not just the live registers arriving from the back jump.  Consequently generate two sets of merge code, one from regsiters live at back-jump, one from no live registers after the evebt poll.

Make the merge code exchange all registers in the conflict set (in common on both sides, and a pair of current,target exists with live registers that differ).  For this, add NumIntegerRegs to the back ends, along with NumFloatRegisters for symmetry.

Fix temp offsets in genStorePop:TemporaryVariable: given self now at 0 in simStack.

Execution on a suitably interesting boot imnage gets as far as
        CMMethod 395 CMClosedPIC 36 CMOpenPIC 2 CMFree 0 total 433
until assert fails stop progress.

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

Item was changed:
  ----- Method: CogARMCompiler class>>initializeAbstractRegisters (in category 'class initialization') -----
  initializeAbstractRegisters
  "Assign the abstract registers with the identities/indices of the relevant concrete registers."
 
  super initializeAbstractRegisters.
 
  "According to IHI0042E ARM Architecture Procedure Calling Standard, in section 5.1.1:
  A subroutine must preserve the contents of the registers r4-r8, r10, r11 and SP (and r9 in PCS variants that designate r9 as v6).
  SP = r13, so the callee-saved regs are r4-r8 & r10-r12.
  The caller-saved registers are those that are not callee-saved and not reserved for hardware/abi uses,
  i..e r0-r3, r9 & r12.
  We exclude registers 0 & 1 (TempReg/CArg0Reg & CArg1Reg) from the CallerSavedRegisterMask because we only
  use them for argument passing and so never want to save and restore them.  In fact restoring TempReg/CArg0Reg
  would overwrite function results, so it shouldn't be included under any circumstances."
 
  CallerSavedRegisterMask := self registerMaskFor: "0 and: 1 and:" 2 and: 3 and: 9 and: 12.
 
  TempReg := R0.
  ClassReg := R2.
  ReceiverResultReg := R5.
  SendNumArgsReg := R6.
  SPReg := SP. "a.k.a. R13" self assert: SP = 13.
  FPReg := R11.
  Arg0Reg := R3. "overlaps with last C arg reg"
  Arg1Reg := R4.
  Extra0Reg := R7.
  Extra1Reg := R8.
  Extra2Reg := R9.
  VarBaseReg := R10. "Must be callee saved" self assert: ConcreteVarBaseReg = R10.
  RISCTempReg := R12. "a.k.a. IP" self assert: ConcreteIPReg = R12.
  LinkReg := LR. "R14"
  PCReg := PC. "R15"
 
+ NumRegisters := 16.
+
  DPFPReg0 := D0.
  DPFPReg1 := D1.
  DPFPReg2 := D2.
  DPFPReg3 := D3.
  DPFPReg4 := D4.
  DPFPReg5 := D5.
  DPFPReg6 := D6.
+ DPFPReg7 := D7.
+
+ NumFloatRegisters := 8!
- DPFPReg7 := D7!

Item was changed:
  ----- Method: CogAbstractRegisters class>>initialize (in category 'class initialization') -----
  initialize
  "Define a fixed set of abstract register names used in code generation for Smalltalk code.
  These are given concrete values by the currently in-use back end, which is a subclass of
  CogAbstractInstruction; see implementors of initializeAbstractRegisters.
 
  We assume concrete registers defined by the back ends are in the range 0 to N, possibly
  with integer registers and floating-point registers using overlapping ranges of indices.
  Here we initialize all registers to #undefined, leaving it to initializeAbstractRegisters to
  initialize the subset of the abstract registers that a platform actually uses."
 
  "NoReg encodes no register, e.g. for parameters that supply an optional register.
  Being negative it is distinct from abstract and concrete registers in the 0 to N range."
  NoReg := -1.
 
  "The core set of abstract registers that define the Cogit's model of Smalltalk code
  provide for a register-based calling convention oriented towards inline cacheing and
  executing a core set of machine code primitives in registers.  The set is composed of
  8 registers, dictated by the available registers on IA32."
  "Smalltalk machine code executes on stack pages in the stack zone, requiring frame and stack pointers."
  FPReg := #undefined. "A frame pointer is used for Smalltalk frames."
  SPReg := #undefined.
  ReceiverResultReg := #undefined. "The receiver at point of send, and return value of a send"
  ClassReg := #undefined. "The inline send cache class tag is in this register, loaded at the send site"
  SendNumArgsReg := #undefined. "Sends > 2 args set the arg count in this reg"
  Arg0Reg := #undefined. "In the StackToRegisterMappingCogit 1 & 2 arg sends marshall into these registers."
  Arg1Reg := #undefined.
  TempReg := #undefined.
 
  "A small fixed set of abstract scratch registers for register-rich machines (ARM can use 1, x64 can use 6 or 7)."
  Extra0Reg := #undefined.
  Extra1Reg := #undefined.
  Extra2Reg := #undefined.
  Extra3Reg := #undefined.
  Extra4Reg := #undefined.
  Extra5Reg := #undefined.
  Extra6Reg := #undefined.
  Extra7Reg := #undefined.
 
  "RISC-specific registers"
  LinkReg := #undefined.
  RISCTempReg := #undefined. "Used to synthesize CISC instructions from multiple RISC instructions."
  PCReg := #undefined. "If the processor has an assignable pc, e.g. ARM"
  VarBaseReg := #undefined. "If useful, points to base of interpreter variables."
 
+ NumRegisters := #undefined. "Number of basic/integer regsiters (we don't do M68k ;-) )"
+
  "Up to 16 floating-point registers. e.g. IA32+SSE2 can use 8, x64 can use 16."
  DPFPReg0 := #undefined.
  DPFPReg1 := #undefined.
  DPFPReg2 := #undefined.
  DPFPReg3 := #undefined.
  DPFPReg4 := #undefined.
  DPFPReg5 := #undefined.
  DPFPReg6 := #undefined.
  DPFPReg7 := #undefined.
  DPFPReg8 := #undefined.
  DPFPReg9 := #undefined.
  DPFPReg10 := #undefined.
  DPFPReg11 := #undefined.
  DPFPReg12 := #undefined.
  DPFPReg13 := #undefined.
  DPFPReg14 := #undefined.
+ DPFPReg15 := #undefined.
+
+ NumFloatRegisters := #undefined. "Number of floating-point registers (we don;t do single-precision; we don't do xmm high (yet)."!
- DPFPReg15 := #undefined!

Item was changed:
  ----- Method: CogIA32Compiler class>>initializeAbstractRegisters (in category 'class initialization') -----
  initializeAbstractRegisters
  "Assign the abstract registers with the identities/indices of the relevant concrete registers."
 
  super initializeAbstractRegisters.
 
  "N.B. EAX ECX & EDX are caller-save (scratch) registers.  Hence we use ECX for class and EDX for
  receiver/result since these are written in all normal sends.  EBX ESI & EDI are callee-save."
 
  CallerSavedRegisterMask := self registerMaskFor: EAX and: ECX and: EDX.
 
  TempReg := EAX.
  ClassReg := ECX.
  ReceiverResultReg := EDX.
  SendNumArgsReg := EBX.
  SPReg := ESP.
  FPReg := EBP.
  Arg0Reg := ESI.
  Arg1Reg := EDI.
 
+ NumRegisters := 8.
+
  DPFPReg0 := XMM0L.
  DPFPReg1 := XMM1L.
  DPFPReg2 := XMM2L.
  DPFPReg3 := XMM3L.
  DPFPReg4 := XMM4L.
  DPFPReg5 := XMM5L.
  DPFPReg6 := XMM6L.
+ DPFPReg7 := XMM7L.
+
+ NumFloatRegisters := 8!
- DPFPReg7 := XMM7L!

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
+
  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: CogRASSBytecodeFixup>>simStackPrintString (in category 'debug printing') -----
+ simStackPrintString
+ <doNotGenerate>
+ ^String streamContents:
+ [:s|
+ self notAFixup
+ ifTrue: [s nextPutAll: 'notAFixup']
+ ifFalse: [cogit printSimStack: mergeSimStack toDepth: simStackPtr spillBase: -1 on: s]]!

Item was changed:
  ----- Method: CogRegisterAllocatingSimStackEntry>>ensureSpilledAt:from: (in category 'compile abstract instructions') -----
  ensureSpilledAt: baseOffset from: baseRegister
  spilled ifTrue:
  [type = SSSpill ifTrue:
  [self assert: ((offset = baseOffset and: [register = baseRegister]) or: [cogit violatesEnsureSpilledSpillAssert]).
  liveRegister := NoReg.
  ^self]].
  self assert: type ~= SSSpill.
  cogit traceSpill: self.
+ "N.B. Keep the type of SSConstant spills as SSConstant so that when joins occur
+ as a result of expressions with constants on the stack, the constant on stack
+ can be recovered.  e.g. as in
+ self at: 1 put: (self foo ifTrue: [self bar] ifFalse: [self baz])."
  type = SSConstant
  ifTrue:
  [cogit genPushConstant: constant]
  ifFalse:
  [type = SSBaseOffset
  ifTrue:
  [liveRegister = NoReg
  ifTrue:
  [cogit MoveMw: offset r: register R: TempReg.
  cogit PushR: TempReg]
  ifFalse: [cogit PushR: liveRegister]]
  ifFalse:
  [self assert: type = SSRegister.
  cogit PushR: register].
+ type := SSSpill].
- type := SSSpill.
- offset := baseOffset.
- register := baseRegister].
  liveRegister := NoReg.
+ spilled := true.
+ offset := baseOffset.
+ register := baseRegister!
- spilled := true!

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:
  [^true].
  "self: const =1 (16r1) (live: Extra4Reg) {172} vs reg ReceiverResultReg {127}"
  "self: reg ReceiverResultReg {95} vs reg Extra5Reg {85}"
+ "self: (bo ReceiverResultReg+296 (live: Extra5Reg) {88} vs reg ReceiverResultReg {84}"
+ "self: const =1 (16r1) (spilled) {167} vs spill @ FPReg-48 {122}"
- "(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]])
+ or: [(type = SSConstant and: [targetEntry type = SSSpill])]]]) ifFalse:
- or: [type = SSBaseOffset and: [register = ReceiverResultReg and: [targetEntry type = SSRegister]]]]) ifFalse:
  [self halt: 'comment the incompatible pair please'].
  ^false!

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: ((self isSameEntryAs: targetEntry)
+ or: [(targetEntry spilled not and: [targetEntry registerOrNone ~= NoReg])
+ or: [spilled and: [type = SSConstant and: [offset = targetEntry offset]]]]).
- or: [targetEntry spilled not and: [targetEntry registerOrNone ~= NoReg]]).
  (targetReg := targetEntry registerOrNone) = NoReg ifTrue:
+ [liveRegister := NoReg.
+ ^self].
- [^self].
  type caseOf: {
+ [SSBaseOffset] -> [liveRegister ~= targetReg ifTrue:
+ [cogit MoveMw: offset r: register R: targetReg].
+ targetEntry type caseOf: {
+ [SSBaseOffset] -> [liveRegister := targetReg.
+ (self isSameEntryAs: targetEntry) ifFalse:
+ [type := SSSpill]].
+ [SSSpill] -> [liveRegister := targetReg. type := SSSpill].
+ [SSConstant] -> [liveRegister := targetReg. type := SSSpill].
+ [SSRegister] -> [register := targetReg. type := SSRegister] }].
+ [SSSpill] -> [cogit MoveMw: offset r: register R: targetReg.
+ liveRegister := targetReg].
+ [SSConstant] -> [cogit genMoveConstant: constant R: targetReg.
+ type := SSRegister. register := targetReg].
+ [SSRegister] -> [targetReg ~= register ifTrue:
+ [cogit MoveR: register R: targetReg.
+ register := targetReg]] }.
- [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.
+ (targetReg := targetEntry registerOrNone) ~= NoReg
- (targetEntry type ~= SSConstant
- and: [(targetReg := targetEntry registerOrNone) ~= NoReg])
  ifTrue: [cogit PopR: targetReg]
+ ifFalse: [cogit AddCq: objectRepresentation wordSize R: SPReg].
+ liveRegister ~= targetReg ifTrue:
+ [liveRegister := NoReg]!
- ifFalse: [cogit AddCq: objectRepresentation wordSize R: SPReg]!

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

Item was changed:
  ----- Method: CogX64Compiler class>>initializeAbstractRegisters (in category 'class initialization') -----
  initializeAbstractRegisters
  "Assign the abstract registers with the identities/indices of the relevant concrete registers."
  "[1] Figure 3.4 Register Usage in
  System V Application Binary Interface
  AMD64 Architecture Processor Supplement"
 
  super initializeAbstractRegisters.
 
  "N.B. RAX RCX & RDX are caller-save (scratch) registers.  Hence we use RCX for class and RDX for
  receiver/result since these are written in all normal sends."
 
  SysV
  ifTrue: [self initializeAbstractRegistersSysV]
  ifFalse: [self initializeAbstractRegistersWin64].
 
+ NumRegisters := 16.
+
  DPFPReg0 := XMM0L.
  DPFPReg1 := XMM1L.
  DPFPReg2 := XMM2L.
  DPFPReg3 := XMM3L.
  DPFPReg4 := XMM4L.
  DPFPReg5 := XMM5L.
  DPFPReg6 := XMM6L.
  DPFPReg7 := XMM7L.
  DPFPReg8 := XMM8L.
  DPFPReg9 := XMM9L.
  DPFPReg10 := XMM10L.
  DPFPReg11 := XMM11L.
  DPFPReg12 := XMM12L.
  DPFPReg13 := XMM13L.
  DPFPReg14 := XMM14L.
+ DPFPReg15 := XMM15L.
+
+ NumFloatRegisters := 16!
- DPFPReg15 := XMM15L!

Item was changed:
  ----- Method: Cogit>>maybeHaltIfDebugPC (in category 'compile abstract instructions') -----
  maybeHaltIfDebugPC
  <cmacro: '() 0'> "Simulation only; void in C"
  ((debugBytecodePointers includes: bytecodePC)
  and: [breakMethod isNil or: [methodObj = breakMethod]]) ifTrue:
+ [self halt: ' at bcpc ', bytecodePC printString, '/', (bytecodePC + 1) printString]!
- [self halt]!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>assertCorrectSimStackPtr (in category 'compile abstract instructions') -----
  assertCorrectSimStackPtr
+ <inline: true> "generates nothing anyway" "self simStackPrintString"
- <inline: true> "generates nothing anyway"
  self cCode: '' inSmalltalk:
  [deadCode ifFalse:
  [self assert: simStackPtr + (needsFrame ifTrue: [0] ifFalse: [1])
  = (self debugStackPointerFor: bytecodePC)].
+ self deny: self duplicateRegisterAssignmentsInTemporaries]!
- self deny: self duplicateRegisterAssignmentsInTemporaries].
- !

Item was changed:
  ----- 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.  Override to recompile after a loop requiring a merge is detected."
+ | result initialOpcodeIndex initialCounterIndex initialIndexOfIRC |
+ compilationPass := 1.
+ scratchBytecodePC := nil.
+ initialOpcodeIndex := opcodeIndex.
+ initialCounterIndex := self maybeCounterIndex."for SistaCogit"
+ literalsManager saveForRecompile.
+ NewspeakVM ifTrue:
+ [initialIndexOfIRC := indexOfIRC].
+ [recompileForLoopRegisterAssignments := false.
+ result := super compileAbstractInstructionsFrom: start through: end.
+ result = 0 and: [recompileForLoopRegisterAssignments]]
- ^[| result initialOpcodeIndex initialCounterIndex initialIndexOfIRC |
-   compilationPass := 1.
-   scratchBytecodePC := nil.
-   initialOpcodeIndex := opcodeIndex.
-   initialCounterIndex := self maybeCounterIndex."for SistaCogit"
-   literalsManager saveForRecompile.
-   NewspeakVM ifTrue:
- [initialIndexOfIRC := indexOfIRC].
-   [recompileForLoopRegisterAssignments := false.
-    result := super compileAbstractInstructionsFrom: start through: end.
-    result = 0 and: [recompileForLoopRegisterAssignments]]
  whileTrue:
  [self assert: compilationPass <= 2.
  self reinitializeAllButBackwardFixupsFrom: start through: end.
  self resetSimStack: start.
  self reinitializeOpcodesFrom: initialOpcodeIndex to: opcodeIndex - 1.
  compilationPass := compilationPass + 1.
  nextFixup := 0.
  opcodeIndex := initialOpcodeIndex.
  self maybeSetCounterIndex: initialCounterIndex. "For SistaCogit"
  literalsManager resetForRecompile.
  NewspeakVM ifTrue:
  [indexOfIRC := initialIndexOfIRC]].
+ ^result!
-    result]
- 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 changed:
  ----- 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.
+ "Ignore targets with no registers.  Current's register can be deassigned and so there's no conflict."
+ (mergeMask ~= 0 and: [mergeMask ~= currentMask]) ifTrue:
- mergeMask ~= currentMask ifTrue:
  [(currentMask anyMask: regsWrittenToMask) ifTrue:
  [^false]].
  regsWrittenToMask := regsWrittenToMask bitOr: mergeMask].
  ^true!

Item was added:
+ ----- Method: RegisterAllocatingCogit>>copySimStackToFixup: (in category 'bytecode generator support') -----
+ copySimStackToFixup: fixup
+ <var: #fixup type: #'BytecodeFixup *'>
+ <inline: true>
+ self cCode: [self mem: fixup mergeSimStack cp: simStack y: simStackPtr + 1 * (self sizeof: CogSimStackEntry)]
+ inSmalltalk: [0 to: simStackPtr do:
+ [:i|
+ fixup mergeSimStack at: i put: (simStack at: i) copy]]!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>ensureRegisterAssignmentsAreAtHeadOfLoop: (in category 'bytecode generator support') -----
  ensureRegisterAssignmentsAreAtHeadOfLoop: target
  "Compiling a loop body will compute a set of live registers.  The backward branch must merge
  with the head of the loop.  So it is preferrable to make the register assignments at the end of
  the loop available at the head.  To do this, simply copy the register assignments to the loop
  head's fixup in the first compilation pass and schedule a second compilation pass.  On the
  second pass the merge will occur when encountering the fixup for the loop head, using
  exactly the same code as for a merge at the end of an if."
  | conflictingRegsMask |
  compilationPass > 1 ifTrue:
+ [self assert: (target mergeSimStack notNil).
- ["self deny: (self mergeRequiredToTarget: target mergeSimStack)."
- self assert: (target mergeSimStack isNil or: [self simStack: simStack isIdenticalTo: target mergeSimStack]).
  ^self].
  (self mergeRequiredToTarget: target mergeSimStack) ifFalse:
  [^self].
  "Schedule a recompile and merge the end-of-loop assignments into the head of the loop,
  replacing any and all register assignments with the state as of the back jump.  Because
  typically the back jump will be taken much more often than the loop entered, favouring
  the assignments here is more efficient than trying to merge."
  recompileForLoopRegisterAssignments := true.
  conflictingRegsMask := self conflictingRegistersBetweenSimStackAnd: target mergeSimStack.
  self deny: (self register: FPReg isInMask: conflictingRegsMask).
  0 to: simStackPtr do:
  [:i| | currentEntry targetEntry |
  currentEntry := self simStack: simStack at: i.
  targetEntry := self simStack: target mergeSimStack at: i.
  targetEntry liveRegister: currentEntry liveRegister]!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>genJumpBackTo: (in category 'bytecode generator support') -----
  genJumpBackTo: targetPC
  | target |
  "On first pass install register allocations (if any) as of the end of the loop and back up to recompile.
  One the second pass generate
  (any merge other than self elided because register assignments copied to loop head in first pass)
  cmp stackLimit
  maybe reload self
  jumpAboveOrEqual target
  flush
  checkForInterrupts
  merge from flushed (N.B. If stack was flushed before loop we could conceivably jump to the pre-loop merge code)
  jmp target
  self printSimStack; printSimStack: target mergeSimStack"
  self assert: targetPC < bytecodePC.
  target := self fixupAt: targetPC.
  self ensureRegisterAssignmentsAreAtHeadOfLoop: target.
+ self copySimStackToScratch: simSpillBase.
+ self mergeCurrentSimStackWith: target.
  self MoveAw: coInterpreter stackLimitAddress R: TempReg.
  self CmpR: TempReg R: SPReg. "N.B. FLAGS := SPReg - TempReg"
  self JumpAboveOrEqual: target.
 
  self ssFlushTo: simStackPtr.
  self CallRT: ceCheckForInterruptTrampoline.
  self annotateBytecode: self Label.
+ self restoreSimStackFromScratch.
  self flushLiveRegistersForSuspensionPoint.
+ self mergeCurrentSimStackWith: target.
- self mergeCurrentSimStackWith: target forwards: false.
  self Jump: target.
  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.
  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.
  "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 := (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 + 1) liveRegister: destReg.
+ self copyLiveRegisterToCopiesOf: (self simStackAt: tempIndex + 1)]].
- [(self simStackAt: tempIndex) liveRegister: destReg.
- self copyLiveRegisterToCopiesOf: (self simStackAt: tempIndex)]].
  self MoveR: (srcRegOrNone ~= NoReg ifTrue: [srcRegOrNone] ifFalse: [destReg])
  Mw: (self frameOffsetOfTemporary: tempIndex)
  r: FPReg.
+ (self simStackAt: tempIndex + 1) bcptr: bytecodePC. "for debugging"
- (self simStackAt: tempIndex) bcptr: bytecodePC. "for debugging"
  self deny: self duplicateRegisterAssignmentsInTemporaries.
  ^0!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>liveRegistersExceptingTopNItems:in: (in category 'simulation stack') -----
  liveRegistersExceptingTopNItems: n in: aSimStack
  <var: 'aSimStack' type: #'SimStackEntry *'>
+ ^self liveRegistersFrom: 0 to: simStackPtr - n in: aSimStack!
- | regsSet |
- regsSet := 0.
- 0 to: simStackPtr - n do:
- [:i|
- regsSet := regsSet bitOr: (self simStack: aSimStack at: i) registerMask].
- LowcodeVM ifTrue:
- [self shouldBeImplemented.
- (simNativeSpillBase max: 0) to: simNativeStackPtr - n do:
- [:i|
- regsSet := regsSet bitOr: (self simNativeStackAt: i) nativeRegisterMask]].
- ^regsSet!

Item was added:
+ ----- Method: RegisterAllocatingCogit>>liveRegistersFrom:to:in: (in category 'simulation stack') -----
+ liveRegistersFrom: low to: high in: aSimStack
+ <var: 'aSimStack' type: #'SimStackEntry *'>
+ <inline: true>
+ | regsSet |
+ regsSet := 0.
+ low to: high do:
+ [:i|
+ regsSet := regsSet bitOr: (self simStack: aSimStack at: i) registerMask].
+ ^regsSet!

Item was added:
+ ----- 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 |
+ (mergeSimStack := fixup mergeSimStack) ifNil: [^self].
+ 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).
+ "if we simply compare spill bases it is trivial here to decide whether to push or pop.
+ Otherwise we have to determine the spill base in fixup's mergeSimStack."
+ (self pushForMergeWith: mergeSimStack)
+ ifTrue:
+ [0 to: simStackPtr do:
+ [:i|
+ current := self simStack: simStack at: i.
+ target := self simStack: mergeSimStack at: i.
+ current reconcilePushingWith: target]]
+ ifFalse:
+ [simStackPtr to: 0 by: -1 do:
+ [:i|
+ current := self simStack: simStack at: i.
+ target := self simStack: mergeSimStack at: i.
+ current reconcilePoppingWith: target]].
+ self assertCorrectSimStackPtr!

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].
- self deny: self duplicateRegisterAssignmentsInTemporaries.
 
  "case 1"
+ fixup notAFixup ifTrue:
+ [^0].
- 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]
- deadCode := false]
  ifFalse:
  [self flushRegistersOnlyLiveOnFallThrough: fixup].
  ^0].
 
  "cases 3 and 4"
  self assert: fixup isMergeFixup.
  self traceMerge: fixup.
  deadCode
+ ifTrue: "case 3"
+ [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]].
- ifTrue: [simStackPtr := fixup simStackPtr] "case 3"
- ifFalse: [(fixup isBackwardBranchFixup and: [compilationPass > 1]) ifTrue:
- [fixup simStackPtr: simStackPtr].
- self mergeCurrentSimStackWith: fixup forwards: true]. "case 4"
  "cases 3 and 4"
- deadCode := false.
  fixup isBackwardBranchFixup ifTrue:
+ [fixup mergeSimStack ifNil:
+ [self assert: compilationPass = 1.
+ self setMergeSimStackOf: fixup]].
- [self assert: fixup mergeSimStack isNil == (compilationPass = 1).
- fixup mergeSimStack ifNil:
- [self setMergeSimStackOf: fixup]].
  fixup targetInstruction: self Label.
+ self assertCorrectSimStackPtr.
+ self assert: (self simStackMergeCompatibleWith: fixup).
+ "self simStackPrintString""fixup simStackPrintString"
- self assert: simStackPtr = fixup simStackPtr.
- self cCode: '' inSmalltalk:
- [self assert: fixup simStackPtr = (self debugStackPointerFor: bytecodePC)].
- self restoreSimStackAtMergePoint: fixup.
-
  ^0!

Item was added:
+ ----- 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."
+ | 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: 'registerLocations' declareC: 'int registerLocations[NumRegisters]'>
+ registersInCommon = (self registerMaskFor: FPReg) ifTrue:
+ [^self].
+ self cCode: '' inSmalltalk:
+ [registerLocations := CArrayAccessor on: (Array new: NumRegisters)].
+ 0 to: NumRegisters - 1 do:
+ [: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 := 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)]]]!

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 optimization state."
- away all simStack and optStatus optimization state."
 
  fixup mergeSimStack ifNotNil:
  [simSpillBase := methodOrBlockNumTemps + 1.
  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>>simStackMergeCompatibleWith: (in category 'bytecode generator support') -----
+ simStackMergeCompatibleWith: fixup
+ simStackPtr = fixup simStackPtr ifFalse:
+ [^false].
+ simStackPtr to: 0 by: -1 do:
+ [:i| | target current |
+ target := self simStack: fixup mergeSimStack at: i.
+ current := self simStack: simStack at: i.
+ (target isSameEntryAs: current) ifFalse:
+ [^current type caseOf: {
+ [SSBaseOffset] -> [target type caseOf: {
+ [SSBaseOffset] -> [false].
+ [SSSpill] -> [false].
+ [SSConstant] -> [false].
+ [SSRegister] -> [false] }].
+ [SSSpill] -> [target type caseOf: {
+ [SSBaseOffset] -> [true].
+ [SSSpill] -> [true].
+ [SSConstant] -> [true].
+ [SSRegister] -> [true] }].
+ [SSConstant] -> [target type caseOf: {
+ [SSBaseOffset] -> [false].
+ [SSSpill] -> [false].
+ [SSConstant] -> [current constant = target constant].
+ [SSRegister] -> [false] }].
+ [SSRegister] -> [target type caseOf: {
+ [SSBaseOffset] -> [current register = target liveRegister].
+ [SSSpill] -> [current register = target liveRegister].
+ [SSConstant] -> [current register = target liveRegister].
+ [SSRegister] -> [current register = target register] }] }]].
+ ^true!

Item was added:
+ ----- Method: RegisterAllocatingCogit>>simStackSameAs: (in category 'bytecode generator support') -----
+ simStackSameAs: fixup
+ "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."
+ | |
+ simStackPtr = fixup simStackPtr ifFalse:
+ [^false].
+ simStackPtr to: 0 by: -1 do:
+ [:i| | target |
+ target := self simStack: fixup mergeSimStack at: i.
+ (target isSameEntryAs: (self simStack: simStack at: i)) ifFalse:
+ [^false]].
+ ^true!

Item was added:
+ ----- Method: SimpleStackBasedCogit>>register:isNotInMask: (in category 'simulation stack') -----
+ register: reg isNotInMask: mask
+ <inline: true>
+ ^ mask noMask: (self registerMaskFor: reg)!