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

commits at source.squeak.org commits at source.squeak.org
Mon Jan 22 05:27:29 UTC 2018


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

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

Name: VMMaker.oscog-eem.2321
Author: eem
Time: 21 January 2018, 9:26:57.702661 pm
UUID: 6709a6af-5bb3-4d9f-9b0b-683ff77fdfd9
Ancestors: VMMaker.oscog-eem.2320

Implement following of pushConstant: aBoolean; jumpTo: target; ...target: jump: target2 if: cond in genJumpTo:.

Add generatorForPC: and use it as appropriate.

Fix some senders of #== that should be using #=.

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

Item was added:
+ ----- Method: CogBytecodeDescriptor>>isConditionalBranch (in category 'accessing') -----
+ isConditionalBranch
+ 	<inline: true>
+ 	^isBranchTrue or: [isBranchFalse]!

Item was changed:
  ----- Method: CogBytecodeDescriptor>>isUnconditionalBranch (in category 'accessing') -----
  isUnconditionalBranch
+ 	<inline: true>
- 
  	^self isBranch and: [(isBranchTrue or: [isBranchFalse]) not]!

Item was added:
+ ----- Method: Cogit>>generatorForPC: (in category 'compile abstract instructions') -----
+ generatorForPC: pc
+ 	<returnTypeC: #'BytecodeDescriptor *'>
+ 	<inline: true>
+ 	^self generatorAt: bytecodeSetOffset + (objectMemory fetchByte: pc ofObject: methodObj)!

Item was changed:
  ----- Method: Cogit>>spanForCleanBlockStartingAt: (in category 'compile abstract instructions') -----
  spanForCleanBlockStartingAt: startPC
  	<var: #descriptor type: #'BytecodeDescriptor *'>
  	| pc end descriptor |
  	pc := startPC.
  	end := objectMemory numBytesOf: methodObj.
  	[pc <= end] whileTrue:
+ 		[descriptor := self generatorForPC: pc.
- 		[descriptor := self generatorAt: (objectMemory fetchByte: pc ofObject: methodObj) + bytecodeSetOffset.
  		 pc := pc + descriptor numBytes.
  		 descriptor isReturn ifTrue:
  			[^pc - startPC]].
  	self error: 'couldn''t locate end of clean block'.
  	^0!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>ensureFixupAt: (in category 'bytecode generator support') -----
  ensureFixupAt: targetPC
  	"Make sure there's a flagged fixup at the target pc in fixups.
  	 Initially a fixup's target is just a flag.  Later on it is replaced with a proper instruction.
+ 	 Override to generate stack merging code if required."
- 	 Override to enerate stack merging code if required."
  	| fixup |	
  	<var: #fixup type: #'BytecodeFixup *'>
  	self assert: targetPC > bytecodePC.
  	fixup := self fixupAt: targetPC.
+ 	"If a non-merge fixup has already been defined then where-ever that was done didn't
+ 	 realise there needed to be a merge and forgot to save the stack state for that merge."
+ 	self deny: fixup isNonMergeFixup.
  	fixup needsFixup 
  		ifTrue:
  			[fixup mergeSimStack
  				ifNil: [self setMergeSimStackOf: fixup]
  				ifNotNil:
  					[self copySimStackToScratch: simSpillBase.
  					 self mergeCurrentSimStackWith: fixup forwards: true.
  					 self restoreSimStackFromScratch]]
  		ifFalse: 
  			[self assert: (fixup mergeSimStack isNil or: [compilationPass = 2]).
+ 			 self moveVolatileSimStackEntriesToRegisters. "Is this needed here?  It is sent immediately in setMergeSimStackOf:; maybe it should be in the ifNotNil: branch only?"
- 			 self moveVolatileSimStackEntriesToRegisters.
  			 fixup mergeSimStack
  				ifNil: [self setMergeSimStackOf: fixup]
  				ifNotNil: [self assert: (self simStack: simStack isIdenticalTo: fixup mergeSimStack)]].
  	^super ensureFixupAt: targetPC!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>existsInstVarRefBeforeSendOrReturn (in category 'bytecode generator support') -----
  existsInstVarRefBeforeSendOrReturn
  	"Answer if the current bytecode is followed by an inst var ref before the next full send."
+ 	| pc nExts descriptor |
- 	| pc nExts byte descriptor |
  	pc := bytecodePC.
  	nExts := 0.
  	[pc <= endPC] whileTrue:
+ 		[descriptor := self generatorForPC: pc.
- 		[byte := (objectMemory fetchByte: pc ofObject: methodObj) + bytecodeSetOffset.
- 		 descriptor := self generatorAt: byte.
  		 (descriptor isMapped
+ 		  or: [descriptor isConditionalBranch
+ 		  or: [descriptor spanFunction notNil]]) ifTrue:
- 		  or: [descriptor isBranchTrue
- 		  or: [descriptor isBranchFalse
- 		  or: [descriptor spanFunction notNil]]]) ifTrue:
  			[^false].
  		 descriptor isInstVarRef ifTrue:
  			[^true].
  		 nExts := descriptor isExtension ifTrue: [nExts + 1] ifFalse: [0].
  		 pc := self nextBytecodePCFor: descriptor at: pc exts: nExts in: methodObj].
  	^false!

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.
  
+ 	(self stackEntryIsBoolean: desc) ifTrue:
- 	(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])]])
  				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 changed:
  ----- Method: SimpleStackBasedCogit>>genJumpIf:to: (in category 'bytecode generator support') -----
  genJumpIf: boolean to: targetBytecodePC
  	<inline: false>
  	"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."
  	| ok |
  	<var: #ok type: #'AbstractInstruction *'>
  	extA := 0.
  	self assert: (objectMemory objectAfter: objectMemory falseObject) = objectMemory trueObject.
  	self PopR: TempReg.
  	self genSubConstant: boolean R: TempReg.
  	self JumpZero: (self ensureFixupAt: targetBytecodePC).
+ 	self CmpCq: (boolean = objectMemory falseObject
- 	self CmpCq: (boolean == objectMemory falseObject
  					ifTrue: [objectMemory trueObject - objectMemory falseObject]
  					ifFalse: [objectMemory falseObject - objectMemory trueObject])
  		R: TempReg.
  	ok := self JumpZero: 0.
  	self genCallMustBeBooleanFor: boolean.
  	ok jmpTarget: (self annotateBytecode: self Label).
  	^0!

Item was changed:
  ----- Method: SistaCogit>>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 |
  	<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 generatorForPC: nextPC.
- 		  nextDescriptor := self generatorAt: (objectMemory fetchByte: nextPC ofObject: methodObj) + bytecodeSetOffset.
  		  nextDescriptor generator ==  #genPushConstantTrueBytecode ifTrue: [ ^ super genJumpIf: boolean to: targetBytecodePC ].
+ 		  nextDescriptor := self generatorForPC: 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.
+ 	(self stackEntryIsBoolean: desc) ifTrue:
- 	(desc type == SSConstant
- 	 and: [desc constant = objectMemory trueObject or: [desc constant = objectMemory falseObject]]) ifTrue:
  		[ ^ super genJumpIf: boolean to: targetBytecodePC ].
  
  	eventualTarget := self eventualTargetOf: targetBytecodePC.
  
  	self ssFlushTo: simStackPtr - 1.
  	desc popToReg: TempReg.
  	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: TempReg.
  	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: TempReg.
  	ok := self JumpZero: 0.
  	self MoveCq: 0 R: SendNumArgsReg. "if counterReg is 0 this is a mustBeBoolean, not a counter trip."
  	
  	countTripped jmpTarget: (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 makes sure that TempReg has been reloaded."
  	
  	"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.
  	self Jump: retry.
  	
  	ok jmpTarget: self Label.
  	^0!

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 generatorForPC: nextPC.
- 		  nextDescriptor := self generatorAt: (objectMemory fetchByte: nextPC ofObject: methodObj) + bytecodeSetOffset.
  		  nextDescriptor generator ==  #genPushConstantTrueBytecode ifTrue: [ ^ super genJumpIf: boolean to: targetBytecodePC ].
+ 		  nextDescriptor := self generatorForPC: 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.
+ 	(self stackEntryIsBoolean: desc) ifTrue:
- 	(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])]])
  				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>>ensureFixupAt: (in category 'compile abstract instructions') -----
  ensureFixupAt: targetPC
  	"Make sure there's a flagged fixup at the target pc in fixups.
  	 Initially a fixup's target is just a flag.  Later on it is replaced with a proper instruction."
  	<returnTypeC: #'BytecodeFixup *'>
  	| fixup |
  	<var: #fixup type: #'BytecodeFixup *'>
  	fixup := self fixupAt:  targetPC.
  	self traceFixup: fixup merge: true.
  	self cCode: '' inSmalltalk:
  		[self assert: simStackPtr = (self debugStackPointerFor: targetPC).
  		 (fixup isMergeFixupOrIsFixedUp
  		  and: [fixup isBackwardBranchFixup not]) ifTrue: "ignore backward branch targets"
  			[self assert: fixup simStackPtr = simStackPtr]].
  	fixup isNonMergeFixupOrNotAFixup
  		ifTrue: "convert a non-merge into a merge"
  			[fixup becomeMergeFixup.
  			 fixup simStackPtr: simStackPtr.
  			 LowcodeVM ifTrue: [
  				 fixup simNativeStackPtr: simNativeStackPtr.
  				 fixup simNativeStackSize: simNativeStackSize]]
  		ifFalse:
  			[fixup isBackwardBranchFixup
  				ifTrue: "this is the target of a backward branch and
  						 so doesn't have a simStackPtr assigned yet."
+ 					[fixup simStackPtr: simStackPtr.
+ 		 			 LowcodeVM ifTrue:
+ 			 			[fixup simNativeStackPtr: simNativeStackPtr.
+ 			 			 fixup simNativeStackSize: simNativeStackSize]]
- 						[fixup simStackPtr: simStackPtr.
- 			 			 LowcodeVM ifTrue:
- 				 			[fixup simNativeStackPtr: simNativeStackPtr.
- 				 			 fixup simNativeStackSize: simNativeStackSize]]
  				ifFalse:
  					[self assert: fixup simStackPtr = simStackPtr.
  					 LowcodeVM ifTrue:
  				 		[self assert: fixup simNativeStackPtr = simNativeStackPtr.
  		 			 	 self assert: fixup simNativeStackSize = simNativeStackSize]]].
  	fixup recordBcpc: bytecodePC.
  	^fixup!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>eventualTargetOf: (in category 'peephole optimizations') -----
  eventualTargetOf: targetBytecodePC
+ 	"Attempt to follow a branch to a pc.  Handle branches to unconditional jumps and
+ 	 branches to push: aBoolean; conditional branch pairs.  If the branch cannot be
+ 	 followed answer targetBytecodePC. It is not possible to follow jumps to conditional
+ 	 branches because the stack changes depth.  That following is left to the genJumpIf:to:
+ 	 clients."
- 	"Attempt to follow a branch to a pc.  Handle branches to unconditional jumps
- 	 and branches to push: aBoolean; conditional branch pairs.  If the branch cannot
- 	 be followed answer targetBytecodePC."
  
  	| currentTarget nextPC nExts descriptor span cond |
  	<var: #descriptor type: #'BytecodeDescriptor *'>
  	nextPC := currentTarget := targetBytecodePC.
  	[ nExts := 0.
+ 	 [descriptor := self generatorForPC: nextPC.
- 	 [descriptor := self generatorAt: bytecodeSetOffset
- 								+ (objectMemory fetchByte: nextPC ofObject: methodObj).
  	  descriptor isReturn ifTrue: [^currentTarget]. "avoid stepping off the end of methods"
  	  descriptor isExtension]
  		whileTrue:
  			[nExts := nExts + 1.
  			 nextPC := nextPC + descriptor numBytes].
  	 descriptor isUnconditionalBranch
  		ifTrue:
  			[span := self spanFor: descriptor at: nextPC exts: nExts in: methodObj.
  			 span < 0 ifTrue: "Do *not* follow backward branches; these are interrupt points and should not be elided."
  				[^currentTarget].
  			 nextPC := nextPC + descriptor numBytes + span]
  		ifFalse:
  			[descriptor generator == #genPushConstantTrueBytecode ifTrue: [ cond := true ]
  			 ifFalse: [ descriptor generator == #genPushConstantFalseBytecode ifTrue: [ cond := false ]
  			 ifFalse: [ ^currentTarget ] ].
  			 "Don't step into loops across a pushTrue; jump:if: boundary, so as not to confuse stack depth fixup."
  			 (self fixupAt: nextPC) isBackwardBranchFixup ifTrue:
  				[^currentTarget].
  			 nextPC := self eventualTargetOf: nextPC + descriptor numBytes.
  			 nExts := 0.
+ 			 [descriptor := self generatorForPC: nextPC.
- 			 [descriptor := self generatorAt: bytecodeSetOffset
- 								+ (objectMemory fetchByte: nextPC ofObject: methodObj).
  			  descriptor isReturn ifTrue: [^currentTarget]. "avoid stepping off the end of methods"
  			  descriptor isExtension]
  				whileTrue:
  					[nExts := nExts + 1.
  					 nextPC := nextPC + descriptor numBytes].
  			 descriptor isBranch ifFalse:
  				[^currentTarget].
  			 descriptor isUnconditionalBranch ifTrue:
  				[^currentTarget].
  			 nextPC := cond == descriptor isBranchTrue
  									ifTrue: [nextPC
  											+ descriptor numBytes
  											+ (self spanFor: descriptor at: nextPC exts: nExts in: methodObj)]
  									ifFalse: [nextPC + descriptor numBytes]].
  	 currentTarget := nextPC]
  		repeat!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>extractMaybeBranchDescriptorInto: (in category 'bytecode generator support') -----
  extractMaybeBranchDescriptorInto: fourArgBlock
  	"Looks one instruction ahead of the current bytecodePC and answers its bytecode descriptor and its pc.
  	 If the instruction found is a branch, also answers the pc after the branch and the pc targeted by the branch.
  	 For convenience, avoiding duplication in the senders, it follows those two pcs to their eventual targets."
  	| primDescriptor nextPC nExts branchDescriptor targetBytecodePC postBranchPC |
  	<inline: true>
  	<var: #primDescriptor type: #'BytecodeDescriptor *'>
  	<var: #branchDescriptor type: #'BytecodeDescriptor *'>
  	
  	primDescriptor := self generatorAt: byte0.
  
  	nextPC := bytecodePC + primDescriptor numBytes.
  	nExts := 0.
+ 	[[branchDescriptor := self generatorForPC: nextPC.
- 	[[branchDescriptor := self generatorAt: (objectMemory fetchByte: nextPC ofObject: methodObj) + bytecodeSetOffset.
  	  branchDescriptor isExtension] whileTrue:
  		[nExts := nExts + 1.
  		 nextPC := nextPC + branchDescriptor numBytes].
  	 branchDescriptor isUnconditionalBranch]
  		whileTrue:
  			[nextPC := self eventualTargetOf: nextPC
  											+ branchDescriptor numBytes
  											+ (self spanFor: branchDescriptor at: nextPC exts: nExts in: methodObj)].
  
  	targetBytecodePC := postBranchPC := 0.
  
  	(branchDescriptor isBranchTrue or: [branchDescriptor isBranchFalse])
  		ifTrue: 
  			[targetBytecodePC := self eventualTargetOf: nextPC
  														+ branchDescriptor numBytes
  														+ (self spanFor: branchDescriptor at: nextPC exts: nExts in: methodObj).
  			 postBranchPC := self eventualTargetOf: nextPC + branchDescriptor numBytes]
  		ifFalse:
  			[nextPC := bytecodePC + primDescriptor numBytes].
  
  	fourArgBlock value: branchDescriptor value: nextPC value: postBranchPC value: targetBytecodePC!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>genJumpIf:to: (in category 'bytecode generator support') -----
  genJumpIf: boolean to: targetBytecodePC
  	<inline: false>
  	| desc fixup ok eventualTarget |
  	<var: #desc type: #'CogSimStackEntry *'>
  	<var: #fixup type: #'BytecodeFixup *'>
  	<var: #ok type: #'AbstractInstruction *'>
  	eventualTarget := self eventualTargetOf: targetBytecodePC.
  	self ssFlushTo: simStackPtr - 1.
  	desc := self ssTop.
  	self ssPop: 1.
+ 	(self stackEntryIsBoolean: desc) ifTrue:
- 	(desc type == SSConstant
- 	 and: [desc constant = objectMemory trueObject or: [desc constant = objectMemory falseObject]]) ifTrue:
  		["Must arrange there's a fixup at the target whether it is jumped to or
  		  not so that the simStackPtr can be kept correct."
  		 fixup := self ensureFixupAt: eventualTarget.
  		 "Must annotate the bytecode for correct pc mapping."
  		 self annotateBytecode: (desc constant = boolean
  									ifTrue: [self Jump: fixup]
  									ifFalse: [self prevInstIsPCAnnotated
  												ifTrue: [self Nop]
  												ifFalse: [self Label]]).
  		 extA := 0.
  		 ^0].
  	desc popToReg: TempReg.
  	"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: TempReg.
  	self JumpZero: (self ensureFixupAt: eventualTarget).
  	
  	self extASpecifiesNoMustBeBoolean ifTrue: 
  		[ extA := 0. 
  		self annotateBytecode: self lastOpcode.
  		^ 0].
  	extA := 0.
  	
  	self CmpCq: (boolean = objectMemory falseObject
  					ifTrue: [objectMemory trueObject - objectMemory falseObject]
  					ifFalse: [objectMemory falseObject - objectMemory trueObject])
  		R: TempReg.
  	ok := self JumpZero: 0.
  	self genCallMustBeBooleanFor: boolean.
  	ok jmpTarget: (self annotateBytecode: self Label).
  	^0!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>genJumpTo: (in category 'bytecode generator support') -----
  genJumpTo: targetBytecodePC
+ 	| eventualTarget generator fixup |
+ 	eventualTarget := self eventualTargetOf: targetBytecodePC.
+ 	(eventualTarget > bytecodePC
+ 	 and: [self stackTopIsBoolean
+ 	 and: [(generator := self generatorForPC: eventualTarget) isConditionalBranch]])
+ 		ifTrue:
+ 			[eventualTarget := eventualTarget
+ 							  + generator numBytes
+ 							  + (generator isBranchTrue == (self ssTop constant = objectMemory trueObject)
+ 									ifTrue: [self spanFor: generator at: eventualTarget exts: 0 in: methodObj]
+ 									ifFalse: [0]).
+ 			self ssPop: 1.
+ 			self ssFlushTo: simStackPtr.
+ 			fixup := self ensureFixupAt: eventualTarget.
+ 			self ssPop: -1]
+ 		ifFalse:
+ 			[self ssFlushTo: simStackPtr.
+ 			fixup := self ensureFixupAt: eventualTarget].
- 	self ssFlushTo: simStackPtr.
  	deadCode := true. "can't fall through"
+ 	self Jump: fixup.
- 	self Jump: (self ensureFixupAt: (self eventualTargetOf: targetBytecodePC)).
  	^0!

Item was added:
+ ----- Method: StackToRegisterMappingCogit>>stackEntryIsBoolean: (in category 'testing') -----
+ stackEntryIsBoolean: simStackEntry
+ 	<var: #simStackEntry type: #'CogSimStackEntry *'>
+ 	<inline: true>
+ 	^simStackEntry type = SSConstant
+ 	  and: [simStackEntry constant = objectMemory trueObject
+ 			or: [simStackEntry constant = objectMemory falseObject]]!

Item was added:
+ ----- Method: StackToRegisterMappingCogit>>stackTopIsBoolean (in category 'testing') -----
+ stackTopIsBoolean
+ 	<inline: true>
+ 	^simStackPtr > 0 and: [self stackEntryIsBoolean: self ssTop]!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>tryCollapseTempVectorInitializationOfSize: (in category 'peephole optimizations') -----
  tryCollapseTempVectorInitializationOfSize: slots
  	"If the sequence of bytecodes is
  		push: (Array new: 1)
  		popIntoTemp: tempIndex
  		pushConstant: const or pushTemp: n
  		popIntoTemp: 0 inVectorAt: tempIndex
  	 collapse this into
  		tempAt: tempIndex put: {const or temp}
  	 and answer true, otherwise answer false.
  	 One might think that we should look for a sequence of more than
  	 one pushes and pops but this is extremely rare.
  	 Exclude pushRcvr: n to avoid potential complications with context inst vars."
  	| pushArrayDesc storeArrayDesc pushValueDesc storeValueDesc tempIndex remoteTempIndex reg |
  	<var: #pushArrayDesc type: #'BytecodeDescriptor *'>
  	<var: #pushValueDesc type: #'BytecodeDescriptor *'>
  	<var: #storeArrayDesc type: #'BytecodeDescriptor *'>
  	<var: #storeValueDesc type: #'BytecodeDescriptor *'>
  	slots ~= 1 ifTrue:
  		[^false].
+ 	pushArrayDesc := self generatorForPC: bytecodePC.
- 	pushArrayDesc := self generatorAt: bytecodeSetOffset
- 										+ (objectMemory
- 												fetchByte: bytecodePC
- 												ofObject: methodObj).
  	self assert: pushArrayDesc generator == #genPushNewArrayBytecode.
+ 	storeArrayDesc := self generatorForPC: bytecodePC + pushArrayDesc numBytes.
- 	storeArrayDesc := self generatorAt: bytecodeSetOffset
- 										+ (objectMemory
- 												fetchByte: bytecodePC
- 														+ pushArrayDesc numBytes
- 												ofObject: methodObj).
  	storeArrayDesc generator == #genStoreAndPopTemporaryVariableBytecode
  		ifTrue:
  			[tempIndex := (objectMemory
  								fetchByte: bytecodePC + pushArrayDesc numBytes
  								ofObject: methodObj) bitAnd: 16r7]
  		ifFalse:
  			[storeArrayDesc generator == #genLongStoreAndPopTemporaryVariableBytecode ifFalse:
  				[^false].
  			 tempIndex := objectMemory
  								fetchByte: bytecodePC + pushArrayDesc numBytes + 1
  								ofObject: methodObj].
+ 	pushValueDesc := self generatorForPC: bytecodePC
+ 										+ pushArrayDesc numBytes
+ 										+ storeArrayDesc numBytes.
- 	pushValueDesc := self generatorAt: bytecodeSetOffset
- 										+ (objectMemory
- 												fetchByte: bytecodePC
- 														+ pushArrayDesc numBytes
- 														+ storeArrayDesc numBytes
- 												ofObject: methodObj).
  	(pushValueDesc generator == #genPushLiteralConstantBytecode
  	 or: [pushValueDesc generator == #genPushQuickIntegerConstantBytecode
  	 or: [pushValueDesc generator == #genPushTemporaryVariableBytecode]]) ifFalse:
  		[^false].
+ 	storeValueDesc := self generatorForPC: bytecodePC
+ 											+ pushArrayDesc numBytes
+ 											+ storeArrayDesc numBytes
+ 											+ pushValueDesc numBytes.
- 	storeValueDesc := self generatorAt: bytecodeSetOffset
- 										+ (objectMemory
- 												fetchByte: bytecodePC
- 														+ pushArrayDesc numBytes
- 														+ storeArrayDesc numBytes
- 														+ pushValueDesc numBytes
- 												ofObject: methodObj).
  	remoteTempIndex := objectMemory
+ 									fetchByte: bytecodePC
+ 											+ pushArrayDesc numBytes
+ 											+ storeArrayDesc numBytes
+ 											+ pushValueDesc numBytes
+ 											+ 2
+ 									ofObject: methodObj.
- 												fetchByte: bytecodePC
- 														+ pushArrayDesc numBytes
- 														+ storeArrayDesc numBytes
- 														+ pushValueDesc numBytes
- 														+ 2
- 												ofObject: methodObj.
  	(storeValueDesc generator == #genStoreAndPopRemoteTempLongBytecode
  	 and: [tempIndex = remoteTempIndex]) ifFalse:
  		[^false].
  
  	objectRepresentation genNewArrayOfSize: 1 initialized: false.
  	self evaluate: pushValueDesc at: bytecodePC + pushArrayDesc numBytes + storeArrayDesc numBytes.
  	reg := self ssStorePop: true toPreferredReg: TempReg.
  	objectRepresentation
  		genStoreSourceReg: reg
  		slotIndex: 0
  		intoNewObjectInDestReg: ReceiverResultReg.
  	self ssPushRegister: ReceiverResultReg.
  	self evaluate: storeArrayDesc at: bytecodePC + pushArrayDesc numBytes.
  	bytecodePC := bytecodePC
  					"+ pushArrayDesc numBytes this gets added by nextBytecodePCFor:at:exts:in:"
  					+ storeArrayDesc numBytes
  					+ pushValueDesc numBytes
  					+ storeValueDesc numBytes.
  	^true!



More information about the Vm-dev mailing list