[Vm-dev] VM Maker: VMMaker.oscog-eem.2200.mcz

commits at source.squeak.org commits at source.squeak.org
Sat Apr 22 00:37:12 UTC 2017


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

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

Name: VMMaker.oscog-eem.2200
Author: eem
Time: 21 April 2017, 5:36:25.552542 pm
UUID: a648efe6-94b9-4ee1-9d72-322751747ef4
Ancestors: VMMaker.oscog-eem.2199

Refactor access to optStatus in StackToRegisterMappingCogit behind receiverIsInReceiverResultReg et al so that it can be and is elided in RegisterAllocatingCogit in favour of simSelf liveRegister.

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

Item was changed:
  StackToRegisterMappingCogit subclass: #RegisterAllocatingCogit
+ 	instanceVariableNames: 'numFixups mergeSimStacksBase nextFixup scratchSimStack scratchSpillBase scratchSimSelf ceSendMustBeBooleanAddTrueLongTrampoline ceSendMustBeBooleanAddFalseLongTrampoline recompileForLoopRegisterAssignments scratchBytecodePC'
- 	instanceVariableNames: 'numFixups mergeSimStacksBase nextFixup scratchSimStack scratchSpillBase scratchOptStatus ceSendMustBeBooleanAddTrueLongTrampoline ceSendMustBeBooleanAddFalseLongTrampoline recompileForLoopRegisterAssignments scratchBytecodePC'
  	classVariableNames: ''
  	poolDictionaries: ''
  	category: 'VMMaker-JIT'!
  
  !RegisterAllocatingCogit commentStamp: 'eem 2/9/2017 10:40' prior: 0!
  RegisterAllocatingCogit is an optimizing code generator that is specialized for register allocation.
  
  On the contrary to StackToRegisterMappingCogit, RegisterAllocatingCogit keeps at each control flow merge point the state of the simulated stack to merge into and not only an integer fixup. Each branch and jump record the current state of the simulated stack, and each fixup is responsible for merging this state into the saved simulated stack.
  
  Instance Variables
  	ceSendMustBeBooleanAddFalseLongTrampoline:		<Integer>
  	ceSendMustBeBooleanAddTrueLongTrampoline:		<Integer>
  	mergeSimStacksBase:									<Integer>
  	nextFixup:												<Integer>
  	numFixups:												<Integer>
  	scratchOptStatus:										<CogSSOptStatus>
  	scratchSimStack:										<Array of CogRegisterAllocatingSimStackEntry>
  	scratchSpillBase:										<Integer>
  
  ceSendMustBeBooleanAddFalseLongTrampoline
  	- the must-be-boolean trampoline for long jump false bytecodes (the existing ceSendMustBeBooleanAddFalseTrampoline is used for short branches)
  
  ceSendMustBeBooleanAddTrueLongTrampoline
  	- the must-be-boolean trampoline for long jump true bytecodes (the existing ceSendMustBeBooleanAddTrueTrampoline is used for short branches)
  
  mergeSimStacksBase
  	- the base address of the alloca'ed memory for merge fixups
  
  nextFixup
  	- the index into mergeSimStacksBase from which the next needed mergeSimStack will be allocated
  
  numFixups
  	- a conservative (over) estimate of the number of merge fixups needed in a method
  
  scratchOptStatus
  	- a scratch variable to hold the state of optStatus while merge code is generated
  
  scratchSimStack
  	- a scratch variable to hold the state of simStack while merge code is generated
  
  scratchSpillBase
  	- a scratch variable to hold the state of spillBase while merge code is generated!

Item was changed:
  ----- Method: RegisterAllocatingCogit class>>declareCVarsIn: (in category 'C translation') -----
  declareCVarsIn: aCodeGen
  	aCodeGen
  		var: #scratchSimStack
  			type: #'SimStackEntry *';
+ 		var: #scratchSimSelf
+ 			type: #SimStackEntry!
- 		var: #scratchOptStatus
- 			type: #CogSSOptStatus!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>copySimStackToScratch: (in category 'bytecode generator support') -----
  copySimStackToScratch: spillBase
  	<inline: true>
  	scratchBytecodePC = bytecodePC ifTrue:
  		[^self].
  	scratchBytecodePC := bytecodePC.
  	self cCode: [self mem: scratchSimStack cp: simStack y: self simStackSlots * (self sizeof: CogSimStackEntry)]
  		inSmalltalk: [0 to: simStackPtr do:
  						[:i|
  						scratchSimStack at: i put: (simStack at: i) copy]].
  	scratchSpillBase := spillBase.
+ 	scratchSimSelf := self cCode: [simSelf] inSmalltalk: [simSelf copy]!
- 	scratchOptStatus := self cCode: [optStatus] inSmalltalk: [optStatus copy]!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>ensureNonMergeFixupAt: (in category 'compile abstract instructions') -----
  ensureNonMergeFixupAt: 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 remember the simStack state at the target, if not already there."
  	"self printSimStack; printSimStack: fixup mergeSimStack"
+ 	true
+ 		ifTrue: [self shouldNotImplement]
+ 		ifFalse:
+ 			[| fixup |
+ 			fixup := super ensureNonMergeFixupAt: targetPC.
+ 			fixup mergeSimStack
+ 				ifNil: [self setMergeSimStackOf: fixup]
+ 				ifNotNil: [self assert: simStackPtr = fixup simStackPtr.
+ 						self deny: (self mergeRequiredToTarget: fixup mergeSimStack)].
+ 			self receiverIsInReceiverResultReg ifFalse:
+ 				[fixup isReceiverResultRegSelf: false].
+ 			^fixup]!
- 	| fixup |
- 	fixup := super ensureNonMergeFixupAt: targetPC.
- 	fixup mergeSimStack
- 		ifNil: [self setMergeSimStackOf: fixup]
- 		ifNotNil: [self assert: simStackPtr = fixup simStackPtr.
- 				self deny: (self mergeRequiredToTarget: fixup mergeSimStack)].
- 	optStatus isReceiverResultRegLive ifFalse:
- 		[fixup isReceiverResultRegSelf: false].
- 	^fixup!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>ensureReceiverResultRegContainsSelf (in category 'bytecode generator support') -----
  ensureReceiverResultRegContainsSelf
- 	super ensureReceiverResultRegContainsSelf.
  	methodOrBlockNumTemps to: simStackPtr do:
  		[:i|
  		((self addressOf: simSelf) isSameEntryAs: (self simStackAt: i))
  			ifTrue: [(self simStackAt: i) liveRegister: ReceiverResultReg]
  			ifFalse:
  				[(self simStackAt: i) liveRegister = ReceiverResultReg ifTrue:
  					[(self simStackAt: i) liveRegister: NoReg]]].
+ 	needsFrame
+ 		ifTrue:
+ 			[self receiverIsInReceiverResultReg ifFalse:
+ 				[self ssAllocateRequiredReg: ReceiverResultReg.
+ 				 self putSelfInReceiverResultReg]]
+ 		ifFalse:
+ 			[self assert: (simSelf type = SSRegister
+ 						  and: [simSelf liveRegister = ReceiverResultReg])].
+ 	self assert: self receiverIsInReceiverResultReg!
- 	simSelf liveRegister: ReceiverResultReg!

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 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].
+ 	self receiverIsInReceiverResultReg ifTrue:
- 	optStatus isReceiverResultRegLive ifTrue:
  		[target isReceiverResultRegSelf: true]!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>genJumpIf:to: (in category 'bytecode generator support') -----
  genJumpIf: boolean to: targetBytecodePC
  	<inline: false>
  	| 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.
  
  	(desc type == SSConstant
  	 and: [desc constant = objectMemory trueObject or: [desc constant = objectMemory falseObject]]) 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])]])
- 			or: [(desc register = ReceiverResultReg and: [optStatus isReceiverResultRegLive])]])
  				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.
  
  	"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].
  
  	reg ~= TempReg ifTrue:
  		[self MoveR: reg R: TempReg].
  	self copySimStackToScratch: simSpillBase.
  	self ssFlushTo: simStackPtr.
  	self genCallMustBeBooleanFor: boolean.
  	"NOTREACHED"
  	ok jmpTarget: (self annotateBytecode: self Label).
  	self restoreSimStackFromScratch.
  	^0!

Item was added:
+ ----- Method: RegisterAllocatingCogit>>initOptStatus: (in category 'simulation stack') -----
+ initOptStatus: receiverResultRegLive
+ 	<inline: true>!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>initSimStackForFramelessBlock: (in category 'simulation stack') -----
  initSimStackForFramelessBlock: startpc
  	super initSimStackForFramelessBlock: startpc.
+ 	simSelf liveRegister: ReceiverResultReg.
- 	simSelf liveRegister: simSelf register.
  	0 to: simStackPtr do:
  		[:i| (self simStackAt: i) liveRegister: NoReg]!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>initSimStackForFramelessMethod: (in category 'simulation stack') -----
  initSimStackForFramelessMethod: startpc
  	super initSimStackForFramelessMethod: startpc.
+ 	simSelf liveRegister: ReceiverResultReg.
- 	simSelf liveRegister: NoReg.
  	0 to: simStackPtr do:
  		[:i| | desc |
  		desc := self simStackAt: i.
+ 		desc liveRegister: (desc type = SSRegister ifTrue: [desc register] ifFalse: [NoReg])]!
- 		desc liveRegister: (desc type == SSRegister ifTrue: [desc register] ifFalse: [NoReg])]!

Item was changed:
  ----- 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"
  	"abstractOpcodes object copyFrom: startIndex to: opcodeIndex"
  	<var: #fixup type: #'BytecodeFixup *'>
  	| startIndex mergeSimStack |
  	<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 reconcileRegistersInTempVarsInCurrentSimStackWithThoseIn: mergeSimStack.
  	(self asserta: (self conflictsResolvedBetweenSimStackAnd: mergeSimStack)) ifFalse:
  		[Notification new tag: #failedMerge; signal].
  	(self pushForMergeWith: mergeSimStack)
  		ifTrue:
  			[methodOrBlockNumTemps to: simStackPtr do:
  				[:i| self mergePushingWithEntryInTargetSimStack: mergeSimStack at: i]]
  		ifFalse:
  			[simStackPtr to: methodOrBlockNumTemps by: -1 do:
  				[:i| self mergePoppingWithEntryInTargetSimStack: mergeSimStack at: i]].
  	"Still haven't handled simSpillBase."
  	self assert: (simSpillBase > simStackPtr
  				or: [simSpillBase < (methodOrBlockNumTemps max: 1)
  				or: [(self simStack: mergeSimStack at: simSpillBase - 1) spilled]]).
  	fixup isReceiverResultRegSelf ifTrue:
+ 		[self receiverIsInReceiverResultReg ifFalse:
- 		[optStatus isReceiverResultRegLive ifFalse:
  			[self putSelfInReceiverResultReg]]!

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

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

Item was changed:
  ----- Method: RegisterAllocatingCogit>>resetSimStack: (in category 'bytecode generator support') -----
  resetSimStack: startPC
  	<inline: true>
  	simSpillBase := methodOrBlockNumTemps.
  	simStackPtr := methodOrBlockNumTemps - 1.
- 	optStatus isReceiverResultRegLive: false.
  	self flushLiveRegistersForSend.
  	self cCode: '' inSmalltalk:
  		[0 to: methodOrBlockNumTemps - 1 do:
  			[:i|
  			(self simStackAt: i) bcptr: startPC]]!

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

Item was 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)]].
  	simSpillBase := scratchSpillBase.
+ 	simSelf := scratchSimSelf!
- 	optStatus := scratchOptStatus!

Item was added:
+ ----- Method: RegisterAllocatingCogit>>setInterpreter: (in category 'initialization') -----
+ setInterpreter: aCoInterpreter
+ 	"Override to elide optStatus which shoudl be unused since simSelf has a liveRegister
+ 	 and hence more elegantly accomplishes the same purpose."
+ 	<doNotGenerate>
+ 	super setInterpreter: aCoInterpreter.
+ 	optStatus := nil!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>setMergeSimStackOf: (in category 'bytecode generator support') -----
  setMergeSimStackOf: fixup
  	<var: #fixup type: #'BytecodeFixup *'>
  	self moveVolatileSimStackEntriesToRegisters.
  	fixup mergeSimStack
  		ifNil:
  			[self assert: nextFixup <= numFixups.
  			 self cCode: [fixup mergeSimStack: mergeSimStacksBase + (nextFixup * self simStackSlots * (self sizeof: CogSimStackEntry))].
  			 nextFixup := nextFixup + 1]
  		ifNotNil:
  			[self assert: fixup simStackPtr = simStackPtr.
  			 0 to: simStackPtr do:
  				[:i|
  				self assert: ((self simStackAt: i) isSameEntryAs: (self addressOf: (fixup mergeSimStack at: i))).
  				(self simStackAt: i) liveRegister ~= (self addressOf: (fixup mergeSimStack at: i)) liveRegister ifTrue:
  					[(self simStackAt: i) liveRegister: NoReg]]].
  	fixup
  		simStackPtr: simStackPtr;
+ 		isReceiverResultRegSelf: self receiverIsInReceiverResultReg.
- 		isReceiverResultRegSelf: optStatus isReceiverResultRegLive.
  	self cCode: [self mem: fixup mergeSimStack cp: simStack y: self simStackSlots * (self sizeof: CogSimStackEntry)]
  		inSmalltalk: [fixup mergeSimStack: self copySimStack]!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>ssAllocateRequiredRegMask:upThrough:upThroughNative: (in category 'simulation stack') -----
  ssAllocateRequiredRegMask: requiredRegsMask upThrough: stackPtr upThroughNative: nativeStackPtr
  	"Override to void any required registers in temp vars."
  	(requiredRegsMask anyMask: (self registerMaskFor: ReceiverResultReg)) ifTrue:
+ 		[self voidReceiverOptStatus].
- 		[optStatus isReceiverResultRegLive: false.
- 		 optStatus ssEntry liveRegister: NoReg].
  	0 to: methodOrBlockNumTemps - 1 do:
  		[:i|
  		((self simStackAt: i) registerMask anyMask: requiredRegsMask) ifTrue:
  			[(self simStackAt: i) liveRegister: NoReg]].
  	super ssAllocateRequiredRegMask: requiredRegsMask upThrough: stackPtr upThroughNative: nativeStackPtr!

Item was added:
+ ----- Method: RegisterAllocatingCogit>>traceSimStack (in category 'simulation only') -----
+ traceSimStack
+ 	<cmacro: '() 0'>
+ 	(compilationTrace anyMask: 4) ifTrue:
+ 		[self printSimSelf; printSimStack]!

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

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

Item was changed:
  ----- Method: SistaRegisterAllocatingCogit>>genJumpIf:to: (in category 'bytecode generator support') -----
  genJumpIf: boolean to: targetBytecodePC
  	"The heart of performance counting in Sista.  Conditional branches are 6 times less
  	 frequent than sends and can provide basic block frequencies (send counters can't).
  	 Each conditional has a 32-bit counter split into an upper 16 bits counting executions
  	 and a lower half counting untaken executions of the branch.  Executing the branch
  	 decrements the upper half, tripping if the count goes negative.  Not taking the branch
  	 decrements the lower half.  N.B. We *do not* eliminate dead branches (true ifTrue:/true ifFalse:)
  	 so that scanning for send and branch data is simplified and that branch data is correct."
  	<inline: false>
  	| ok counterAddress countTripped retry nextPC nextDescriptor desc eventualTarget reg |
  	<var: #ok type: #'AbstractInstruction *'>
  	<var: #desc type: #'CogSimStackEntry *'>
  	<var: #retry type: #'AbstractInstruction *'>
  	<var: #countTripped type: #'AbstractInstruction *'>
  	<var: #nextDescriptor type: #'BytecodeDescriptor *'>
  
  	"In optimized code we don't generate counters to improve performance"
  	(coInterpreter isOptimizedMethod: methodObj) ifTrue:
  		[^super genJumpIf: boolean to: targetBytecodePC].
  	
  	"If the branch is reached only for the counter trip trampoline 
  	(typically, var1 == var2 ifTrue: falls through to the branch only for the trampoline)
  	we generate a specific path to drastically reduce the number of machine instructions"
  	branchReachedOnlyForCounterTrip ifTrue: 
  		[branchReachedOnlyForCounterTrip := false.
  		 ^self genCounterTripOnlyJumpIf: boolean to: targetBytecodePC].
  
  	"We detect and: / or:, if found, we don't generate the counters to avoid pathological counter slow down"
  	boolean = objectMemory falseObject ifTrue:
  		[ nextPC := bytecodePC + (self generatorAt: byte0) numBytes.
  		  nextDescriptor := self generatorAt: (objectMemory fetchByte: nextPC ofObject: methodObj) + bytecodeSetOffset.
  		  nextDescriptor generator ==  #genPushConstantTrueBytecode ifTrue: [ ^ super genJumpIf: boolean to: targetBytecodePC ].
  		  nextDescriptor := self generatorAt: (objectMemory fetchByte: targetBytecodePC ofObject: methodObj) + bytecodeSetOffset.
  		  nextDescriptor generator == #genPushConstantFalseBytecode ifTrue: [ ^ super genJumpIf: boolean to: targetBytecodePC ]. ].
  
  	extA := 0. "We ignore the noMustBeBoolean flag. It should not be present in methods with counters, and if it is we don't care."
  
  	"We don't generate counters on branches on true/false, the basicblock usage can be inferred"
  	desc := self ssTop.
  	(desc type == SSConstant
  	 and: [desc constant = objectMemory trueObject or: [desc constant = objectMemory falseObject]]) ifTrue:
  		[ ^ super genJumpIf: boolean to: targetBytecodePC ].
  
  	eventualTarget := self eventualTargetOf: targetBytecodePC.
  
  	self flag: 'Because of the restriction on x64 that absolute loads must target %rax, it would perhaps be a better choice to use TempReg (%rax) for the counter reg and SendNumArgsReg for the boolean.'.
  	"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])]])
- 			or: [(desc register = ReceiverResultReg and: [optStatus isReceiverResultRegLive])]])
  				ifTrue: [TempReg]
  				ifFalse: [desc register].
  	desc popToReg: reg.
  	self ssPop: 1.
  
  	"We need SendNumArgsReg because of the mustBeBooleanTrampoline"
  	self ssAllocateRequiredReg: SendNumArgsReg.
  
  	retry := self Label.
  	self 
  		genExecutionCountLogicInto: [ :cAddress :countTripBranch | 
  			counterAddress := cAddress. 
  			countTripped := countTripBranch ] 
  		counterReg: SendNumArgsReg.
  	counterIndex := counterIndex + 1.
  
  	"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.
  	self genSubConstant: boolean R: reg.
  	self JumpZero: (self ensureFixupAt: eventualTarget).
  
  	self genFallsThroughCountLogicCounterReg: SendNumArgsReg counterAddress: counterAddress.
  
  	self CmpCq: (boolean = objectMemory falseObject
  					ifTrue: [objectMemory trueObject - objectMemory falseObject]
  					ifFalse: [objectMemory falseObject - objectMemory trueObject])
  		R: reg.
  	ok := self JumpZero: 0.
  	self MoveCq: 0 R: SendNumArgsReg. "if counterReg is 0 this is a mustBeBoolean, not a counter trip."
  	reg ~= TempReg ifTrue:
  		[self MoveR: reg R: TempReg].
  	countTripped jmpTarget: self Label.
  	self copySimStackToScratch: simSpillBase.
  	self ssFlushTo: simStackPtr.
  	self genCallMustBeBooleanFor: boolean.
  						
  	"If we're in an image which hasn't got the Sista code loaded then the ceCounterTripped: trampoline
  	 will return directly to machine code, returning the boolean.  So the code should jump back to the
  	 retry point. The trampoline preserves register state when taking the ceCounterTripped: path."
  	"Clément: For some reason if I write self annotateBytecode: (self Jump: retry) the annotation is not at the correct place."
  	"Eliot: Annotations apply the the address following an instruction, and the annotation must be for the return address
  	 of the call (since this is the address the run-time sees), so it must be on a label before the jump, not after the jump."
  	self annotateBytecode: self Label.
  	simSpillBase ~= scratchSpillBase ifTrue:
  		[self assert: simSpillBase > scratchSpillBase.
  		 self AddCq: simSpillBase - scratchSpillBase * objectMemory wordSize R: SPReg].
  	self Jump: retry.
  
  	ok jmpTarget: self Label.
  	self restoreSimStackFromScratch.
  	^0!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>ensureReceiverResultRegContainsSelf (in category 'bytecode generator support') -----
  ensureReceiverResultRegContainsSelf
  	needsFrame
  		ifTrue:
+ 			[self receiverIsInReceiverResultReg ifFalse:
- 			[optStatus isReceiverResultRegLive ifFalse:
  				[self ssAllocateRequiredReg: ReceiverResultReg.
  				self putSelfInReceiverResultReg ].
  			optStatus isReceiverResultRegLive: true]
  		ifFalse:
  			[self assert: (simSelf type = SSRegister
  						  and: [simSelf register = ReceiverResultReg]).
  			self assert: (optStatus isReceiverResultRegLive
  						  and: [optStatus ssEntry = (self addressOf: simSelf)])]!

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

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>genReturnReceiver (in category 'bytecode generators') -----
  genReturnReceiver
  	"In a frameless method ReceiverResultReg already contains self.
  	 In a frameful method, ReceiverResultReg /may/ contain self."
  	needsFrame ifTrue:
+ 		[self receiverIsInReceiverResultReg ifFalse:
- 		[ optStatus isReceiverResultRegLive ifFalse:
  			[self putSelfInReceiverResultReg]].
  	^self genUpArrowReturn!

Item was added:
+ ----- Method: StackToRegisterMappingCogit>>initOptStatus: (in category 'simulation stack') -----
+ initOptStatus: receiverResultRegLive
+ 	<inline: true>
+ 	optStatus 
+ 		isReceiverResultRegLive: receiverResultRegLive;
+ 		ssEntry: (self addressOf: simSelf)!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>initSimStackForFramefulMethod: (in category 'simulation stack') -----
  initSimStackForFramefulMethod: startpc
  	<var: #desc type: #'CogSimStackEntry *'>
  	simSelf
  		type: SSBaseOffset;
  		spilled: true;
  		register: FPReg;
  		offset: FoxMFReceiver.
+ 	self initOptStatus: false.
- 	optStatus 
- 		isReceiverResultRegLive: false;
- 		ssEntry: (self addressOf: simSelf).
  	simSpillBase := methodOrBlockNumTemps. "N.B. Includes num args"
  	simStackPtr := simSpillBase - 1.
  	LowcodeVM ifTrue: [
  		simNativeSpillBase := simNativeStackPtr := -1.
  		simNativeStackSize := 0.
  	].
  	"args"
  	0 to: methodOrBlockNumArgs - 1 do:
  		[:i| | desc |
  		desc := self simStackAt: i.
  		desc
  			type: SSBaseOffset;
  			spilled: true;
  			register: FPReg;
  			offset: FoxCallerSavedIP + ((methodOrBlockNumArgs - i) * objectMemory wordSize);
  			bcptr: startpc].
  	"temps"
  	methodOrBlockNumArgs to: simStackPtr do:
  		[:i| | desc |
  		desc := self simStackAt: i.
  		desc
  			type: SSBaseOffset;
  			spilled: true;
  			register: FPReg;
  			offset: FoxMFReceiver - (i - methodOrBlockNumArgs + 1 * objectMemory wordSize);
  			bcptr: startpc]!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>initSimStackForFramelessBlock: (in category 'simulation stack') -----
  initSimStackForFramelessBlock: startpc
  	"The register receiver (the closure itself) and args are pushed by the closure value primitive(s)
  	 and hence a frameless block has all arguments and copied values pushed to the stack.  However,
  	 the method receiver (self) is put in the ReceiverResultRegister by the block entry."
  	| desc |
  	<var: #desc type: #'CogSimStackEntry *'>
  	simSelf
  		type: SSRegister;
  		spilled: false;
  		register: ReceiverResultReg.
+ 	self initOptStatus: true.
- 	optStatus
- 		isReceiverResultRegLive: true;
- 		ssEntry: (self addressOf: simSelf).
  	self assert: methodOrBlockNumTemps >= methodOrBlockNumArgs.
  	0 to: methodOrBlockNumTemps - 1 do:
  		[:i|
  		desc := self simStackAt: i.
  		desc
  			type: SSBaseOffset;
  			spilled: true;
  			register: SPReg;
  			offset: ((backEnd hasLinkRegister
  								ifTrue: [methodOrBlockNumArgs - 1- i]
  								ifFalse: [methodOrBlockNumArgs - i]) * objectMemory wordSize);
  			bcptr: startpc].
  	simSpillBase := simStackPtr := methodOrBlockNumTemps - 1.
  	LowcodeVM ifTrue: [
  		simNativeSpillBase := simNativeStackPtr := -1.
  		simNativeStackSize := 0.
  	].!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>initSimStackForFramelessMethod: (in category 'simulation stack') -----
  initSimStackForFramelessMethod: startpc
  	| desc |
  	<var: #desc type: #'CogSimStackEntry *'>
  	simSelf
  		type: SSRegister;
  		spilled: false;
  		register: ReceiverResultReg.
+ 	self initOptStatus: true.
- 	optStatus
- 		isReceiverResultRegLive: true;
- 		ssEntry: (self addressOf: simSelf).
  	self assert: methodOrBlockNumTemps = methodOrBlockNumArgs.
  	self assert: self numRegArgs <= 2.
  	(methodOrBlockNumArgs between: 1 and: self numRegArgs)
  		ifTrue:
  			[desc := self simStackAt: 0.
  			 desc
  				type: SSRegister;
  				spilled: false;
  				register: Arg0Reg;
  				bcptr: startpc.
  			 methodOrBlockNumArgs > 1 ifTrue:
  				[desc := self simStackAt: 1.
  				 desc
  					type: SSRegister;
  					spilled: false;
  					register: Arg1Reg;
  					bcptr: startpc]]
  		ifFalse:
  			[0 to: methodOrBlockNumArgs - 1 do:
  				[:i|
  				desc := self simStackAt: i.
  				desc
  					type: SSBaseOffset;
  					register: SPReg;
  					spilled: true;
  					offset: ((backEnd hasLinkRegister
  								ifTrue: [methodOrBlockNumArgs - 1- i]
  								ifFalse: [methodOrBlockNumArgs - i]) * objectMemory wordSize);
  					bcptr: startpc]].
  	simSpillBase := simStackPtr := methodOrBlockNumArgs - 1.
  	LowcodeVM ifTrue: [
  		simNativeSpillBase := simNativeStackPtr := -1.
  		simNativeStackSize := 0.
  	].!

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

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



More information about the Vm-dev mailing list