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

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

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

Name: VMMaker.oscog-eem.2423
Author: eem
Time: 25 July 2018, 6:10:11.529642 pm
UUID: 235b1bb5-37e3-45e8-9f5f-d38dcb54dc76
Ancestors: VMMaker.oscog-eem.2422

RegisterAllocatingCogit.
ensureReceiverResultRegContainsSelf should use a simple super send, since StackToRegisterMapping's version merely flushes the stack.  This results in poor code when assiging a method result to an inst var; the result comes back in ReceiverResultReg, which must be copied to another register to allow ReceiverResultReg to be loaded with self for the store.

Fix a simulation-only aliasing bug in restoreSimStackFromScratch.

Drop the obsolete noMustBeBoolean support in genJumpIf:to: and make sure to copy the sim stack to sratch /before/ any merge code is generated in the jumps.

Relax an assert in assertCorrectSimStackPtr.

Improve some comments for MNU and store trampolines.

Use the block inlining support to write some neater enumerators over the sim stack.

RegisterAllocatingCogit happily simulates a startreader-64 image and evaluates 3+4 & Smalltalk quit.

N.B. A weakness with the RegisterAllocatingCogit is that it does not currently map from registers to stack entries, hence if two ajoining sequences of bytecodes load the same register, but do so by pushing new desacriptors that are then popped afterwards, the register load will be repeated because there is no record that a particular register holds the value of a newly minted descriptor pushed on the stack.

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

Item was changed:
  ----- Method: CoInterpreter>>ceSendMustBeBooleanTo:interpretingAtDelta: (in category 'trampolines') -----
  ceSendMustBeBooleanTo: aNonBooleanObject interpretingAtDelta: jumpSize
+ "For RegisterAllocatingCogit we want the pc following a conditional branch not to be reachable, so
+ we don't have to generate code to reload registers.  But notionally the pc following a conditional
+ branch is reached when continuing from a mustBeBoolean error.  Instead of supporting this in the
+ JIT, simply convert to an interpreter frame, backup the pc to the branch, reenter the interpreter
+ and hence retry the mustBeBoolean send therein.  N.B. We could do this for immutability violations
+ too, but immutability is used in actual applications and so should be performant, whereas
+ mustBeBoolean errors are extremely rare and so we choose brevity over performance in this case."
- "For RegisterAllocatingCogit we want the address following a conditional branch not to be reachable, so we
- don't have to generate code to reload registers.  Instead simply convert to an interpreter frame,
- backup the pc to the branch, reenter the interpreter and hence retry the mustBeBoolean send therein."
  <api>
  | cogMethod methodObj methodHeader startBcpc |
  <var: 'cogMethod' type: #'CogBlockMethod *'>
  <var: 'p' type: #'char *'>
  self assert: (objectMemory addressCouldBeOop: aNonBooleanObject).
  cogMethod := self mframeCogMethod: framePointer.
  ((self mframeIsBlockActivation: framePointer)
  and: [cogMethod cmIsFullBlock not])
  ifTrue:
  [methodHeader := (self cCoerceSimple: cogMethod cmHomeMethod to: #'CogMethod *') methodHeader.
  methodObj := (self cCoerceSimple: cogMethod cmHomeMethod to: #'CogMethod *') methodObject.
  startBcpc := cogMethod startpc]
  ifFalse:
  [methodHeader := (self cCoerceSimple: cogMethod to: #'CogMethod *') methodHeader.
  methodObj := (self cCoerceSimple: cogMethod to: #'CogMethod *') methodObject.
  startBcpc := self startPCOfMethod: methodObj].
 
  "Map the machine code instructionPointer to the interpreter instructionPointer of the branch."
  instructionPointer := self popStack.
  instructionPointer := cogit bytecodePCFor: instructionPointer startBcpc: startBcpc in: cogMethod.
  instructionPointer := methodObj + objectMemory baseHeaderSize + instructionPointer - jumpSize - 1. "pre-decrement"
 
  "Make space for the two extra fields in an interpreter frame"
  stackPointer to: framePointer + FoxMFReceiver by: objectMemory wordSize do:
  [:p| | oop |
  oop := objectMemory longAt: p.
  objectMemory
  longAt: p - objectMemory wordSize - objectMemory wordSize
  put: (objectMemory longAt: p)].
  stackPointer := stackPointer - objectMemory wordSize - objectMemory wordSize.
  self push: aNonBooleanObject.
  "Fill in the fields"
  objectMemory
  longAt: framePointer + FoxIFrameFlags
  put: (self
  encodeFrameFieldHasContext: (self mframeHasContext: framePointer)
  isBlock: (self mframeIsBlockActivation: framePointer)
  numArgs: cogMethod cmNumArgs);
  longAt: framePointer + FoxIFSavedIP
  put: 0;
  longAt: framePointer + FoxMethod
  put: methodObj.
 
  "and now reenter the interpreter..."
  self setMethod: methodObj methodHeader: methodHeader.
  self siglong: reenterInterpreter jmp: ReturnToInterpreter.!

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

Item was changed:
  ----- Method: RegisterAllocatingCogit>>ensureReceiverResultRegContainsSelf (in category 'bytecode generator support') -----
  ensureReceiverResultRegContainsSelf
  "First ensure that ReceiverResultReg is allocated to self,
  which may cause spills, etc.  Then copy the register into
  any instances of simSelf on the stack."
+
+ | freeReg live ssEntry |
+ needsFrame
+ ifTrue:
+ [self receiverIsInReceiverResultReg ifFalse: "Avoid spilling if possible"
+ [live := self liveRegisters.
+ ssEntry := self ssEntrySuchThat: [:e| e registerOrNone = ReceiverResultReg].
+ (ssEntry notNil
+  and: [(freeReg := backEnd availableRegisterOrNoneFor: live) ~= NoReg])
+ ifTrue: "ReceiverResultReg is being used, but we have a free register.  So avoid spill by assigining."
+ [ssEntry storeToRegNoAssign: freeReg.
+ self ssEntriesDo:
+ [:e|
+ e registerOrNone = ReceiverResultReg ifTrue:
+ [e type = SSRegister
+ ifTrue: [e register: freeReg]
+ ifFalse: [e liveRegister: freeReg]]]]
+ ifFalse:
+ [self ssAllocateRequiredReg: ReceiverResultReg].
+ self putSelfInReceiverResultReg.
+ self simSelf liveRegister: ReceiverResultReg]]
+ ifFalse:
+ [self assert: (self simSelf type = SSRegister
+  and: [self simSelf register = ReceiverResultReg
+  and: [self receiverIsInReceiverResultReg]])].
- super ensureReceiverResultRegContainsSelf.
  "the storeToReg: in putSelfInReceiverResultReg in
  ensureReceiverResultRegContainsSelf copies the register to all copies.
  So simply check that the stack agrees with this."
  self assert: self receiverResultRegIsAssignedToSelfAndNothingElse!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>genCallMustBeBooleanFor: (in category 'trampoline support') -----
  genCallMustBeBooleanFor: boolean
+ "Call ceSendMustBeBooleanTo:interpretingAtDelta: via the relevant trampoline."
  self assert: ((self generatorAt: byte0) numBytes between: 1 and: 2).
  ^self CallRT: ((self generatorAt: byte0) numBytes = 1
  ifTrue:
  [boolean = objectMemory falseObject
  ifTrue: [ceSendMustBeBooleanAddFalseTrampoline]
  ifFalse: [ceSendMustBeBooleanAddTrueTrampoline]]
  ifFalse:
  [boolean = objectMemory falseObject
  ifTrue: [ceSendMustBeBooleanAddFalseLongTrampoline]
  ifFalse: [ceSendMustBeBooleanAddTrueLongTrampoline]])!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>genJumpIf:to: (in category 'bytecode generator support') -----
  genJumpIf: boolean to: targetBytecodePC
  <inline: false>
+ | eventualTarget desc reg fixup ok mbb |
- | eventualTarget desc reg fixup ok mbb noMustBeBoolean |
  <var: #fixup type: #'BytecodeFixup *'>
  <var: #ok type: #'AbstractInstruction *'>
  <var: #desc type: #'CogSimStackEntry *'>
  <var: #mbb type: #'AbstractInstruction *'>
  eventualTarget := self eventualTargetOf: targetBytecodePC.
  desc := self ssTop.
  self ssPop: 1.
 
- noMustBeBoolean := self extASpecifiesNoMustBeBoolean.
  extA := 0.
 
  self moveVolatileSimStackEntriesToRegisters.
 
  (self stackEntryIsBoolean: desc) ifTrue:
  ["Must annotate the bytecode for correct pc mapping."
  desc constant = boolean
  ifTrue:
  [deadCode := true. "Can't fall through."
  fixup := self ensureFixupAt: eventualTarget.
  self annotateBytecode: (self Jump: fixup)]
  ifFalse:
  [self annotateBytecode: (self prevInstIsPCAnnotated
  ifTrue: [self Nop]
  ifFalse: [self Label])].
  ^0].
 
  "try and use the top entry's register if any, but only if it can be destroyed."
  reg := (desc type ~= SSRegister
  or: [(self anyReferencesToRegister: desc register inAllButTopNItems: 0)
  or: [(desc register = ReceiverResultReg and: [self receiverIsInReceiverResultReg])]])
  ifTrue: [TempReg]
  ifFalse: [desc register].
  desc popToReg: reg.
  "Cunning trick by LPD.  If true and false are contiguous subtract the smaller.
  Correct result is either 0 or the distance between them.  If result is not 0 or
  their distance send mustBeBoolean."
  self assert: (objectMemory objectAfter: objectMemory falseObject) = objectMemory trueObject.
 
+ "Copy simStack before any branching.  If no branch then the stack is as it is here, not after the merge for eventualTarget."
+ self copySimStackToScratch: simSpillBase.
  "Merge required; must not generate merge code along untaken branch, so flip the order."
  (self mergeRequiredForJumpTo: eventualTarget)
  ifTrue:
  [self genSubConstant: (boolean = objectMemory trueObject
  ifTrue: [objectMemory falseObject]
  ifFalse: [objectMemory trueObject])
  R: reg.
  ok := self JumpZero: 0.
  self CmpCq: (boolean = objectMemory trueObject
  ifTrue: [objectMemory trueObject - objectMemory falseObject]
  ifFalse: [objectMemory falseObject - objectMemory trueObject])
  R: reg.
- noMustBeBoolean ifTrue:
- [self JumpZero: (self ensureFixupAt: eventualTarget). "generates merge code"
- ok jmpTarget: (self annotateBytecode: self lastOpcode).
- ^0].
  mbb := self JumpNonZero: 0.
  self Jump: (self ensureFixupAt: eventualTarget). "generates merge code"
  mbb jmpTarget: self Label]
  ifFalse:
  [self genSubConstant: boolean R: reg.
  self JumpZero: (self ensureFixupAt: eventualTarget).
- noMustBeBoolean ifTrue:
- [self annotateBytecode: self lastOpcode.
- ^0].
  self CmpCq: (boolean = objectMemory falseObject
  ifTrue: [objectMemory trueObject - objectMemory falseObject]
  ifFalse: [objectMemory falseObject - objectMemory trueObject])
  R: reg.
  ok := self JumpZero: 0].
+ "Restore simStack after the merges in ensureFixupAt: above."
+ self restoreSimStackFromScratch.
-
  reg ~= TempReg ifTrue:
  [self MoveR: reg R: TempReg].
- self copySimStackToScratch: simSpillBase.
  self ssFlushTo: simStackPtr.
+ "In the RegisterAllocatingCogit we map to an interpreter frame in ceSendMustBeBooleanTo:interpretingAtDelta:"
  self genCallMustBeBooleanFor: boolean.
  "NOTREACHED"
  ok jmpTarget: (self annotateBytecode: self Label).
  self restoreSimStackFromScratch.
  ^0!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>moveVolatileSimStackEntriesToRegisters (in category 'bytecode generator support') -----
  moveVolatileSimStackEntriesToRegisters
+ <inline: true>
  self moveVolatileSimStackEntriesToRegistersPreserving: self allocatedRegisters!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>restoreSimStackFromScratch (in category 'bytecode generator support') -----
  restoreSimStackFromScratch
  <inline: true>
  self cCode: [self mem: simStack cp: scratchSimStack y: self simStackSlots * (self sizeof: CogSimStackEntry)]
  inSmalltalk: [0 to: simStackPtr do:
  [:i|
+ simStack at: i put: (scratchSimStack at: i) copy]].
- simStack at: i put: (scratchSimStack at: i)]].
  simSpillBase := scratchSpillBase!

Item was added:
+ ----- Method: RegisterAllocatingCogit>>ssEntriesDo: (in category 'simulation stack') -----
+ ssEntriesDo: aBlock
+ "Evaluate aBlock with all simStackEntries"
+ <inline: true>
+ 0 to: simStackPtr do:
+ [:i| aBlock value: (self simStackAt: i)]!

Item was added:
+ ----- Method: RegisterAllocatingCogit>>ssEntrySuchThat: (in category 'simulation stack') -----
+ ssEntrySuchThat: aBlock
+ "Answer the SimStackEntry for which aBlock answers true, or nil if none."
+ <inline: true>
+ 0 to: simStackPtr do:
+ [:i|
+ (aBlock value: (self simStackAt: i)) ifTrue:
+ [^self simStackAt: i]].
+ ^nil!