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

commits at source.squeak.org commits at source.squeak.org
Thu Jul 26 01:11:06 UTC 2018

Eliot Miranda uploaded a new version of VMMaker to project VM Maker:

==================== 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

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."
  	| 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])
  			[methodHeader := (self cCoerceSimple: cogMethod cmHomeMethod to: #'CogMethod *') methodHeader.
  			 methodObj := (self cCoerceSimple: cogMethod cmHomeMethod to: #'CogMethod *') methodObject.
  			 startBcpc := cogMethod startpc]
  			[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.
  			longAt: p - objectMemory wordSize - objectMemory wordSize
  			put: (objectMemory longAt: p)].
  	stackPointer := stackPointer - objectMemory wordSize - objectMemory wordSize.
  	self push: aNonBooleanObject.
  	"Fill in the fields"
  		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') -----
  	<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') -----
  	"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
  						[boolean = objectMemory falseObject
  							ifTrue: [ceSendMustBeBooleanAddFalseTrampoline]
  							ifFalse: [ceSendMustBeBooleanAddTrueTrampoline]]
  						[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
  				[deadCode := true. "Can't fall through."
  				 fixup := self ensureFixupAt: eventualTarget.
  				 self annotateBytecode: (self Jump: fixup)]
  				[self annotateBytecode: (self prevInstIsPCAnnotated
  												ifTrue: [self Nop]
  												ifFalse: [self Label])].
  	"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)
  			[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]
  			[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.
  	ok jmpTarget: (self annotateBytecode: self Label).
  	self restoreSimStackFromScratch.

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

Item was changed:
  ----- Method: RegisterAllocatingCogit>>restoreSimStackFromScratch (in category 'bytecode generator support') -----
  	<inline: true>
  	self cCode: [self mem: simStack cp: scratchSimStack y: self simStackSlots * (self sizeof: CogSimStackEntry)]
  		inSmalltalk: [0 to: simStackPtr do:
+ 						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!

More information about the Vm-dev mailing list