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

commits at source.squeak.org commits at source.squeak.org
Sun Dec 25 20:56:48 UTC 2022


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

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

Name: VMMaker.oscog-eem.3284
Author: eem
Time: 25 December 2022, 12:56:31.883528 pm
UUID: 99050939-5e02-4371-aa33-f716c24648f6
Ancestors: VMMaker.oscog-eem.3283

Slang: refactor macro generation to make it ewasier to check for validity in the slang test workspace.

Cog ARMv8 Simulator: make the DUAL_MAPPED_CODE_ZONE regime simulate again, fixing an assert fail due to overlap of the guard page and the end of the code zone when mapped.  We need an additional guard poage's delta to make sure that the simulated hi-rez timer code is generated outside of the code zone.  And the asserts for executability in simulateCogCodeAt:/simulateLeafCallOf: only apply to the non-dual-mapped regime.

Fix some excess var:type: in old code.

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

Item was added:
+ ----- Method: CCodeGenerator>>emitAsCMacro:on: (in category 'C code generator') -----
+ emitAsCMacro: tMethod on: aStream
+ 	"Store the global variable declarations on the given stream.  Answer any constants used in the macros."
+ 	aStream
+ 		nextPutAll: '#define ';
+ 		nextPutAll:(self cFunctionNameFor: tMethod selector);
+ 		nextPutAll: (macros at: tMethod selector); cr!

Item was changed:
  ----- Method: CCodeGenerator>>emitCMacros:on: (in category 'C code generator') -----
+ emitCMacros: methodList on: aStream
- emitCMacros: methodList on: aStream 
  	"Store the global variable declarations on the given stream.  Answer any constants used in the macros."
  	| usedConstants |
  	macros isEmpty ifTrue: [^#()].
  	aStream cr; nextPutAll: '/*** Macros ***/'; cr.
  	usedConstants := Set new.
  	(methodList reject: [:m| m isRealMethod]) do:
  		[:m |
  		m definedAsMacro ifTrue:
+ 			[self emitAsCMacro: m on: aStream.
- 			[aStream
- 				nextPutAll: '#define ';
- 				nextPutAll:(self cFunctionNameFor: m selector);
- 				nextPutAll: (macros at: m selector); cr.
  			 m compiledMethod literalsDo:
  				[:lit|
  				(lit isVariableBinding and: [(macros at: m selector) includesSubstring: lit key]) ifTrue:
  					[usedConstants add: lit key]]]].
  	aStream cr.
  	^usedConstants!

Item was changed:
  ----- Method: CogRegisterAllocatingSimStackEntry>>isMergedWithTargetEntry: (in category 'comparing') -----
  isMergedWithTargetEntry: targetEntry
  	"The receiver is a simStackEntry at a jump to the corresponding simStackEntry at the jump's target.
  	 Answer if no merge is required for the jump."
+ 	<var: 'targetEntry' type: #'CogSimStackEntry *'>
- 	<var: 'ssEntry' type: #'CogSimStackEntry *'>
  	spilled ~= targetEntry spilled ifTrue: "push or pop required"
  		[^false].
  	(liveRegister = NoReg and: [targetEntry liveRegister ~= NoReg]) ifTrue: "register load required"
  		[^false].
  	(self isSameEntryAs: targetEntry) ifTrue:
  		[^liveRegister = targetEntry liveRegister].
  	(type = SSConstant and: [targetEntry type = SSRegister and: [liveRegister = targetEntry register]]) ifTrue:
  		[^true].
  	"self: const =1 (16r1) (live: Extra4Reg) {172} vs reg ReceiverResultReg {127}"
  	"self: reg ReceiverResultReg {95} vs reg Extra5Reg {85}"
  	"self: (bo ReceiverResultReg+296 (live: Extra5Reg) {88} vs reg ReceiverResultReg {84}"
  	"self: const =1 (16r1) (spilled) {167} vs spill @ FPReg-48 {122}"
  	((type = SSConstant and: [targetEntry type = SSRegister and: [liveRegister ~= targetEntry registerOrNone]])
  	 or: [(type = SSRegister and: [targetEntry type = SSRegister and: [register ~= targetEntry registerOrNone]])
  	 or: [(type = SSBaseOffset and: [register = ReceiverResultReg and: [targetEntry type = SSRegister]])
  	 or: [(type = SSConstant and: [targetEntry type = SSSpill])]]]) ifFalse:
  		[self halt: 'comment the incompatible pair please'].
  	^false!

Item was changed:
  ----- Method: CogVMSimulator>>openOn:extraMemory: (in category 'initialize-release') -----
  openOn: fileName extraMemory: extraBytes
  	"CogVMSimulator new openOn: 'clone.im' extraMemory: 100000"
  
  	| f version headerSize dataSize count oldBaseAddr bytesToShift swapBytes
  	  headerFlags firstSegSize heapSize
  	  hdrNumStackPages hdrEdenBytes hdrMaxExtSemTabSize hdrCogCodeSize
  	  stackZoneSize methodCacheSize primTraceLogSize allocationReserve |
  	"open image file and read the header"
  
  	(f := self openImageFileNamed: fileName) ifNil: [^self].
  
  	"Set the image name and the first argument; there are
  	 no arguments during simulation unless set explicitly."
  	systemAttributes at: 1 put: fileName.
  
  	["begin ensure block..."
  	imageName := f fullName.
  	f binary.
  
  	version := self getWord32FromFile: f swap: false.  "current version: 16r1968 (=6504) vive la revolucion!!"
  	(self readableFormat: version)
  		ifTrue: [swapBytes := false]
  		ifFalse: [(version := version byteSwap32) = self imageFormatVersion
  					ifTrue: [swapBytes := true]
  					ifFalse: [self error: 'incomaptible image format']].
  	headerSize := self getWord32FromFile: f swap: swapBytes.
  	dataSize := self getLongFromFile: f swap: swapBytes.  "length of heap in file"
  	oldBaseAddr := self getLongFromFile: f swap: swapBytes.  "object memory base address of image"
  	objectMemory specialObjectsOop: (self getLongFromFile: f swap: swapBytes).
  	objectMemory lastHash: (self getLongFromFile: f swap: swapBytes).  "Should be loaded from, and saved to the image header"
  
  	savedWindowSize	:= self getLongFromFile: f swap: swapBytes.
  	headerFlags		:= self getLongFromFile: f swap: swapBytes.
  	self setImageHeaderFlagsFrom: headerFlags.
  	extraVMMemory	:= self getWord32FromFile: f swap: swapBytes.
  	hdrNumStackPages	:= self getShortFromFile: f swap: swapBytes.
  	"4 stack pages is small.  Should be able to run with as few as
  	 three. 4 should be comfortable but slow.  8 is a reasonable
  	 default. Can be changed via vmParameterAt: 43 put: n"
  	numStackPages := desiredNumStackPages ~= 0
  						ifTrue: [desiredNumStackPages]
  						ifFalse: [hdrNumStackPages = 0
  									ifTrue: [self defaultNumStackPages]
  									ifFalse: [hdrNumStackPages]].
  	desiredNumStackPages := hdrNumStackPages.
  	stackZoneSize := self computeStackZoneSize.
  	"This slot holds the size of the native method zone in 1k units. (pad to word boundary)."
  	hdrCogCodeSize := (self getShortFromFile: f swap: swapBytes) * 1024.
  	cogCodeSize := desiredCogCodeSize ~= 0
  						ifTrue: [desiredCogCodeSize]
  						ifFalse:
  							[hdrCogCodeSize = 0
  									ifTrue: [cogit defaultCogCodeSize]
  									ifFalse: [hdrCogCodeSize]].
  	desiredCogCodeSize := hdrCogCodeSize.
  	self assert: f position = (objectMemory wordSize = 4 ifTrue: [40] ifFalse: [64]).
  	hdrEdenBytes	:= self getWord32FromFile: f swap: swapBytes.
  	objectMemory edenBytes: (desiredEdenBytes ~= 0
  						ifTrue: [desiredEdenBytes]
  						ifFalse:
  							[hdrEdenBytes = 0
  									ifTrue: [objectMemory defaultEdenBytes]
  									ifFalse: [hdrEdenBytes]]).
  	desiredEdenBytes := hdrEdenBytes.
  	hdrMaxExtSemTabSize := self getShortFromFile: f swap: swapBytes.
  	hdrMaxExtSemTabSize ~= 0 ifTrue:
  		[self setMaxExtSemSizeTo: hdrMaxExtSemTabSize].
  	"pad to word boundary.  This slot can be used for anything else that will fit in 16 bits.
  	 Preserve it to be polite to other VMs."
  	the2ndUnknownShort	:= self getShortFromFile: f swap: swapBytes.
  	self assert: f position = (objectMemory wordSize = 4 ifTrue: [48] ifFalse: [72]).
  	firstSegSize := self getLongFromFile: f swap: swapBytes.
  	objectMemory firstSegmentSize: firstSegSize.
  	"For Open PICs to be able to probe the method cache during
  	 simulation the methodCache must be relocated to memory."
  	methodCacheSize := methodCache size * objectMemory wordSize.
  	primTraceLogSize := primTraceLog size * objectMemory wordSize.
  
  	"To cope with modern OSs that disallow executing code in writable memory we dual-map
  	 the code zone, one mapping with read/write permissions and the other with read/execute
  	 permissions. In simulation all we can do is use memory, so if we're simulating dual mapping
  	 we use double the memory and simulate the memory sharing in the Cogit's backEnd."
  	effectiveCogCodeSize := (InitializationOptions at: #DUAL_MAPPED_CODE_ZONE ifAbsent: [false])
+ 								ifTrue: [cogCodeSize * 2 + Cogit guardPageSize]
- 								ifTrue: [cogCodeSize * 2]
  								ifFalse: [cogCodeSize].
  
  	"allocate interpreter memory. This list is in address order, low to high.
  	 In the actual VM the stack zone exists on the C stack."
  	heapBase := (Cogit guardPageSize
  				+ effectiveCogCodeSize
  				+ stackZoneSize
  				+ methodCacheSize
  				+ primTraceLogSize
  				+ self rumpCStackSize) roundUpTo: objectMemory allocationUnit.
  	"compare memory requirements with availability"
  	allocationReserve := self interpreterAllocationReserveBytes.
  	objectMemory hasSpurMemoryManagerAPI
  		ifTrue:
  			[| freeOldSpaceInImage headroom |
  			 freeOldSpaceInImage := self getLongFromFile: f swap: swapBytes.
  			 headroom := objectMemory
  							initialHeadroom: extraVMMemory
  							givenFreeOldSpaceInImage: freeOldSpaceInImage.
  			 heapSize := objectMemory roundUpHeapSize:
  						   dataSize
  						+ headroom
  						+ objectMemory newSpaceBytes
  						+ (headroom > allocationReserve
  							ifTrue: [0]
  							ifFalse: [allocationReserve])]
  		ifFalse:
  			[heapSize :=  dataSize
  						+ extraBytes
  						+ objectMemory newSpaceBytes
  						+ (extraBytes > allocationReserve
  							ifTrue: [0]
  							ifFalse: [allocationReserve])].
  	"allocate interpreter memory"
  	heapBase := objectMemory
  					setHeapBase: heapBase
  					memoryLimit: heapBase + heapSize
  					endOfMemory: heapBase + dataSize. "bogus for Spur"
  
  	self assert: cogCodeSize \\ 4 = 0.
  	self assert: objectMemory memoryLimit \\ 4 = 0.
  	self assert: self rumpCStackSize \\ 4 = 0.
  	objectMemory allocateMemoryOfSize: objectMemory memoryLimit.
  	"read in the image in bulk, then swap the bytes if necessary"
  	f position: headerSize.
  	count := objectMemory readHeapFromImageFile: f dataBytes: dataSize.
  	count ~= dataSize ifTrue: [self halt]]
  		ensure: [f close].
  	self moveMethodCacheToMemoryAt: objectMemory cogCodeBase + effectiveCogCodeSize + stackZoneSize.
  	self movePrimTraceLogToMemoryAt: objectMemory cogCodeBase + effectiveCogCodeSize + stackZoneSize + methodCacheSize.
  
  	self ensureImageFormatIsUpToDate: swapBytes.
  
  	bytesToShift := objectMemory memoryBaseForImageRead - oldBaseAddr.  "adjust pointers for zero base address"
  	UIManager default
  		informUser: 'Relocating object pointers...'
  		during: [self initializeInterpreter: bytesToShift].
  	cogit
  		initializeCodeZoneFrom: Cogit guardPageSize
  		upTo: Cogit guardPageSize + cogCodeSize!

Item was changed:
  ----- Method: Cogit>>assertValidDualZone (in category 'debugging') -----
  assertValidDualZone
  	"{self firstInvalidDualZoneAddress. self firstInvalidDualZoneAddress + codeToDataDelta }"
  	"{self firstInvalidDualZoneAddress hex. (self firstInvalidDualZoneAddress + codeToDataDelta) hex }"
+ 	"[:fidza| {fidza. objectMemory longAt: fidza. objectMemory longAt: fidza + codeToDataDelta } collect: #hex] value: self firstInvalidDualZoneAddress"
- 	"{(objectMemory longAt: self firstInvalidDualZoneAddress) hex. (objectMemory longAt: self firstInvalidDualZoneAddress + codeToDataDelta) hex }"
  	"self armDisassembleDualZoneAnomalies"
  	"self armPrintDualZoneAnomalies"
+ 	<cmacro: '() true'>
+ 	self assert: self firstInvalidDualZoneAddress isNil!
- 	self cCode: ''
- 		inSmalltalk: [self assert: self firstInvalidDualZoneAddress isNil]!

Item was changed:
  ----- Method: Cogit>>ensureExecutableCodeZone (in category 'memory access') -----
  ensureExecutableCodeZone
  	"On some platforms run-time calls may be required to enable execution and disable
  	 write-protect of the code zone. This is sequenced by ensuring that the code zone is
  	 executable most of the time.  Note that any code space modification requires an
  	 icache flush (on processors with such an icache). Hence the least invasive time to
  	 ensure code is executable is post icache flush.  Making sure code is writable can be
  	 done either before any bulk edit (e.g. code zone reclamation) or as part of any fine-
  	 grained code modification (e.g. setting an anonymous method's selector)."
  	<inline: #always>
  	
  	self cppIf: #DUAL_MAPPED_CODE_ZONE
  		ifFalse:
  			[backEnd needsCodeZoneExecuteWriteSwitch ifTrue:
  				[self cCode: nil inSmalltalk: [| currentAPISelector |
  											"What's all this crap?  We're trying to catch cases where ensureExecutableCodeZone
  											 is called without first calling ensureWritableCodeZone, and vice verse.  But there are
  											 lots of exceptions where code is not modified but executability is turned on unnecessarily.
  											 The list of exceptions follows."
  											 currentAPISelector := self debugAPISelector.
  											self assert: (codeZoneIsExecutableNotWritable not
  														or: [currentAPISelector == #mapObjectReferencesInMachineCode:
+ 														or: [currentAPISelector == #freeUnmarkedMachineCode
+ 														or: [currentAPISelector == #cogitPostGCAction:
+ 														or: [currentAPISelector == #ceSICMiss:
+ 														or: [currentAPISelector == #ceCPICMiss:receiver:
+ 														or: [currentAPISelector == #unlinkSendsOf:isMNUSelector:
+ 														or: [currentAPISelector == #unlinkSendsTo:andFreeIf:
+ 														or: [currentAPISelector == #unlinkSendsToMethodsSuchThat:AndFreeIf:
+ 														or: [currentAPISelector == #followMovableLiteralsAndUpdateYoungReferrers]]]]]]]]])].
- 															or: [currentAPISelector == #cogitPostGCAction:
- 															or: [currentAPISelector == #ceSICMiss:
- 															or: [currentAPISelector == #ceCPICMiss:receiver:
- 															or: [currentAPISelector == #unlinkSendsOf:isMNUSelector:
- 															or: [currentAPISelector == #unlinkSendsTo:andFreeIf:
- 															or: [currentAPISelector == #unlinkSendsToMethodsSuchThat:AndFreeIf:
- 															or: [currentAPISelector == #followMovableLiteralsAndUpdateYoungReferrers]]]]]]]])].
  				 backEnd makeCodeZoneExecutable.
  				 self cCode: nil inSmalltalk: [codeZoneIsExecutableNotWritable := true. debugAPISelector := self debugAPISelector]]]!

Item was changed:
  ----- Method: Cogit>>simulateCogCodeAt: (in category 'simulation only') -----
  simulateCogCodeAt: address "<Integer>"
  	<doNotGenerate>
  	| stackZoneBase |
+ 	(InitializationOptions at: #DUAL_MAPPED_CODE_ZONE ifAbsent: [false]) ifFalse:
+ 		[backEnd needsCodeZoneExecuteWriteSwitch ifTrue:
+ 			[self assert: codeZoneIsExecutableNotWritable]].
- 	backEnd needsCodeZoneExecuteWriteSwitch ifTrue:
- 		[self assert: codeZoneIsExecutableNotWritable].
  	stackZoneBase := coInterpreter stackZoneBase.
  	processor pc: address.
  	[[[singleStep
  		ifTrue:
  			[[processor sp < stackZoneBase ifTrue: [self halt].
  			  self recordProcessing.
  			  self maybeBreakAt: processor pc] value. "So that the Debugger's Over steps over all this"
  			  processorLock critical:
  				[processor
  					singleStepIn: coInterpreter memory
  					minimumAddress: guardPageSize
  					readOnlyBelow: methodZone zoneEnd]]
  		ifFalse:
  			[processorLock critical:
  				[processor
  					runInMemory: coInterpreter memory
  					minimumAddress: guardPageSize
  					readOnlyBelow: methodZone zoneEnd]].
  	   "((printRegisters or: [printInstructions]) and: [clickConfirm]) ifTrue:
  	 	[(self confirm: 'continue?') ifFalse:
  			[clickConfirm := false. self halt]]."
  	   true] whileTrue]
  		on: ProcessorSimulationTrap
  		do: [:ex| ex applyTo: self].
  	 true] whileTrue!

Item was changed:
  ----- Method: Cogit>>simulateLeafCallOf: (in category 'simulation only') -----
  simulateLeafCallOf: someFunction
  	"Simulate execution of machine code that leaf-calls someFunction,
  	 answering the result returned by someFunction."
  	"CogProcessorAlienInspector openFor: coInterpreter"
  	<doNotGenerate>
  	| priorSP priorPC priorLR spOnEntry bogusRetPC |
+ 	self cppIf: #DUAL_MAPPED_CODE_ZONE ifFalse:
+ 		[backEnd needsCodeZoneExecuteWriteSwitch ifTrue:
+ 			[self assert: codeZoneIsExecutableNotWritable]].
- 	backEnd needsCodeZoneExecuteWriteSwitch ifTrue:
- 		[self assert: codeZoneIsExecutableNotWritable].
  	self recordRegisters.
  	priorSP := processor sp.
  	priorPC := processor pc.
  	priorLR := backEnd hasLinkRegister ifTrue: [processor lr].
  	processor
  		simulateLeafCallOf: someFunction
  		nextpc: (bogusRetPC := 16rBADF00D5 roundTo: backEnd codeGranularity)
  		memory: coInterpreter memory.
  	spOnEntry := processor sp.
  	self recordInstruction: {'(simulated call of '. someFunction. ')'}.
  	^[[[processor pc between: self class guardPageSize and: methodZone zoneEnd] whileTrue:
  		[singleStep
  			ifTrue: [self recordProcessing.
  					self maybeBreakAt: processor pc.
  					processorLock critical:
  						[processor
  							singleStepIn: coInterpreter memory
  							minimumAddress: guardPageSize
  							readOnlyBelow: methodZone zoneEnd]]
  			ifFalse: [processorLock critical:
  						[processor
  							runInMemory: coInterpreter memory
  							minimumAddress: guardPageSize
  							readOnlyBelow: methodZone zoneEnd]]]]
  			on: ProcessorSimulationTrap, Error
  			do: [:ex|
  				"Again this is a hack for the processor simulators not properly simulating returns to bogus addresses.
  				 In this case BochsX64Alien doesn't do the right thing."
  				processor pc = bogusRetPC ifTrue:
  					[self recordInstruction: {'(simulated (real) return to '. processor pc. ')'}.
  					 ^processor cResultRegister].
  				ex isProcessorSimulationTrap ifFalse:
  					[ex pass].
  				ex applyTo: self.
  				ex type == #return ifTrue:
  					[^processor cResultRegister]].
  	processor pc = bogusRetPC ifTrue:
  		[self recordInstruction: {'(simulated (real) return to '. processor pc. ')'}].
  	processor cResultRegister]
  		ensure:
  			[processor sp: priorSP.
  			 processor pc: priorPC.
  			 priorLR ifNotNil: [:lr| processor lr: lr]]!

Item was changed:
  ----- Method: Cogit>>sqMakeMemoryExecutableFrom:To:CodeToDataDelta: (in category 'initialization') -----
  sqMakeMemoryExecutableFrom: startAddress To: endAddress CodeToDataDelta: codeToDataDeltaPtr
  	<doNotGenerate>
  	"Simulate setting executable permissions on the code zone.  In production this will apply execute permission
  	 to startAddress throguh endAddress - 1.  If starting up in the DUAL_MAPPED_CODE_ZONE regime then it
  	 will also create a writable mapping for the code zone and assign the distance from executable zone to the
+ 	 writable zone through codeToDataDeltaPtr.  If in this regime when simulating, the CogVMSimulator will
- 	 writable zone throguh codeToDataDeltaPtr.  If in this regime when simulating, the CogVMSimulator will
  	 have allocated twice as much code memory as asked for (see CogVMSimulator openOn:extraMemory:) and
  	 so simply set the delta to the code size."
  	(InitializationOptions at: #DUAL_MAPPED_CODE_ZONE ifAbsent: [false]) ifTrue:
+ 		[codeToDataDeltaPtr at: 0 put: coInterpreter cogCodeSize + Cogit guardPageSize]!
- 		[codeToDataDeltaPtr at: 0 put: coInterpreter cogCodeSize]!

Item was changed:
  ----- Method: RegisterAllocatingCogit>>genForwardersInlinedIdenticalOrNotIf: (in category 'bytecode generators') -----
  genForwardersInlinedIdenticalOrNotIf: orNot
  	| nextPC branchDescriptor unforwardRcvr argReg targetPC
  	  unforwardArg  rcvrReg postBranchPC retry fixup
  	  comparison rcvrConstant argConstant
  	  needMergeToTarget needMergeToContinue |
  	<var: #branchDescriptor type: #'BytecodeDescriptor *'>
- 	<var: #toContinueLabel type: #'AbstractInstruction *'>
- 	<var: #toTargetLabel type: #'AbstractInstruction *'>
  	<var: #comparison type: #'AbstractInstruction *'>
  	<var: #retry type: #'AbstractInstruction *'>
  	
  	self extractMaybeBranchDescriptorInto: [ :descr :next :postBranch :target | 
  		branchDescriptor := descr. nextPC := next. postBranchPC := postBranch. targetPC := target ].
  
  	"If an operand is an annotable constant, it may be forwarded, so we need to store it into a 
  	register so the forwarder check can jump back to the comparison after unforwarding the constant.
  	However, if one of the operand is an unnanotable constant, does not allocate a register for it 
  	(machine code will use operations on constants) and does not generate forwarder checks."
  	unforwardRcvr := (self ssValue: 1) mayBeAForwarder.
  	unforwardArg := self ssTop mayBeAForwarder.
  	(unforwardRcvr not and: [unforwardArg not])
  		ifTrue: [^self genVanillaInlinedIdenticalOrNotIf: orNot].
  	self assert: (unforwardArg or: [unforwardRcvr]).
  	"We use reg for non annotable constants to avoid duplicating objRef."
  	rcvrConstant := objectRepresentation isUnannotatableConstant: (self ssValue: 1).
  	argConstant := objectRepresentation isUnannotatableConstant: self ssTop.
  
  	self 
  		allocateEqualsEqualsRegistersArgNeedsReg: argConstant not 
  		rcvrNeedsReg: rcvrConstant not 
  		into: [ :rcvr :arg | rcvrReg:= rcvr. argReg := arg ].
  
  	"If not followed by a branch, resolve to true or false."
  	(branchDescriptor isBranchTrue or: [branchDescriptor isBranchFalse]) ifFalse:
  		[^self 
  			genIdenticalNoBranchArgIsConstant: argConstant
  			rcvrIsConstant: rcvrConstant
  			argReg: argReg 
  			rcvrReg: rcvrReg 
  			orNotIf: orNot].
  	
  	self assert: (unforwardArg or: [unforwardRcvr]).
  	self ssPop: 2. "If we had moveAllButTop: 2 volatileSimStackEntriesToRegistersPreserving: we could avoid the extra ssPop:s"
  	self moveVolatileSimStackEntriesToRegistersPreserving:
  		(self allocatedRegisters bitOr: (argReg = NoReg
  										ifTrue: [self registerMaskFor: rcvrReg]
  										ifFalse:
  											[rcvrReg = NoReg
  												ifTrue: [self registerMaskFor: argReg]
  												ifFalse: [self registerMaskFor: rcvrReg and: argReg]])).
  	retry := self Label.
  	self ssPop: -2.
  	self genCmpArgIsConstant: argConstant rcvrIsConstant: rcvrConstant argReg: argReg rcvrReg: rcvrReg.
  	self ssPop: 2.
  
  	(self fixupAt: nextPC) notAFixup "The next instruction is dead.  we can skip it."
  		ifTrue:  [deadCode := true]
  		ifFalse: [self deny: deadCode]. "push dummy value below"
  
  	"self printSimStack; printSimStack: (self fixupAt: postBranchPC) mergeSimStack"
  	"If there are merges to be performed on the forward branches we have to execute
  	 the merge code only along the path requiring that merge, and exactly once."
  	needMergeToTarget := self mergeRequiredForJumpTo: targetPC.
  	needMergeToContinue := self mergeRequiredForJumpTo: postBranchPC.
  	orNot == branchDescriptor isBranchTrue
  		ifFalse: "a == b ifTrue: ... or a ~~ b ifFalse: ... jump on equal to target pc"
  			[fixup := needMergeToContinue
  						ifTrue: [0] "jumps will fall-through to to-continue merge code"
  						ifFalse: [self ensureFixupAt: postBranchPC].
  			 comparison := self JumpZero: (needMergeToTarget
  												ifTrue: [0] "comparison will be fixed up to to-target merge code"
  												ifFalse: [self ensureFixupAt: targetPC])]
  		ifTrue: "a == b ifFalse: ... or a ~~ b ifTrue: ... jump on equal to post-branch pc"
  			[fixup := needMergeToTarget
  						ifTrue: [0] "jumps will fall-through to to-target merge code"
  						ifFalse: [self ensureFixupAt: targetPC].
  			 comparison := self JumpZero: (needMergeToContinue
  												ifTrue: [0] "comparison will be fixed up to to-continue merge code"
  												ifFalse: [self ensureFixupAt: postBranchPC])].
  
  	"The forwarders check(s) need(s) to jump back to the comparison (retry) if a forwarder is found,
  	 else jump forward either to the next forwarder check or to the postBranch or branch target (fixup).
  	 But if there is merge code along a path, the jump must be to the merge code."
  	(unforwardArg and: [unforwardRcvr]) ifTrue:
  		[objectRepresentation genEnsureOopInRegNotForwarded: argReg scratchReg: TempReg jumpBackTo: retry].
  	objectRepresentation 
  		genEnsureOopInRegNotForwarded: (unforwardRcvr ifTrue: [rcvrReg] ifFalse: [argReg]) 
  		scratchReg: TempReg 
  		ifForwarder: retry
  		ifNotForwarder: fixup.
  	"If fixup is zero then the ifNotForwarder path falls through to a Label which is interpreted
  	 as either to-continue or to-target, depending on orNot == branchDescriptor isBranchTrue."
  	orNot == branchDescriptor isBranchTrue
  		ifFalse: "a == b ifTrue: ... or a ~~ b ifFalse: ... jump on equal to target pc"
  			[needMergeToContinue ifTrue: "fall-through to to-continue merge code"
  				[self Jump: (self ensureFixupAt: postBranchPC)].
  			 needMergeToTarget ifTrue: "fixup comparison to to-target merge code"
  				[comparison jmpTarget: self Label.
  				 self Jump: (self ensureFixupAt: targetPC)]]
  		ifTrue: "a == b ifFalse: ... or a ~~ b ifTrue: ... jump on equal to post-branch pc"
  			[needMergeToTarget ifTrue: "fall-through to to-target merge code"
  				[self Jump: (self ensureFixupAt: targetPC)].
  			 needMergeToContinue ifTrue: "fixup comparison to to-continue merge code"
  				[comparison jmpTarget: self Label.
  				 self Jump: (self ensureFixupAt: postBranchPC)]].
  
  	deadCode ifFalse: "duplicate the merge fixup's top of stack so as to avoid a false confict."
  		[self ssPushDesc: ((self fixupAt: nextPC) mergeSimStack at: simStackPtr + 1)].
  	^0!

Item was changed:
  ----- Method: SistaCogitClone>>genByteEqualsInlinePrimitiveResult:returnReg: (in category 'inline primitive generators') -----
  genByteEqualsInlinePrimitiveResult: jmp returnReg: reg
  	"Byte equal is falling through if the result is true, or jumping using jmp if the result is false.
  	 The method is required to set the jump target of jmp.
  	 We look ahead for a branch and pipeline the jumps if possible..
  	 ReturnReg is used only if not followed immediately by a branch."
  	| branchDescriptor nextPC postBranchPC targetBytecodePC localJump canElide |
+ 	<var: #jmp type: #'AbstractInstruction *'>
  	<var: #localJump type: #'AbstractInstruction *'>
  	<var: #branchDescriptor type: #'BytecodeDescriptor *'>
  	self extractMaybeBranchDescriptorInto: [ :descr :next :postBranch :target | 
  		branchDescriptor := descr. nextPC := next. postBranchPC := postBranch. targetBytecodePC := target ].
  	
  	"Case 1 - not followed by a branch"
  	(branchDescriptor isBranchTrue or: [branchDescriptor isBranchFalse])
  		ifFalse: 
  			[self genMoveTrueR: reg.
  			 localJump := self Jump: 0.
  			 jmp jmpTarget: (self genMoveFalseR: reg).
  			 localJump jmpTarget: self Label.
  			 self ssPushRegister: reg.
  			^ 0].
  
  	"Case 2 - followed by a branch"
  	(self fixupAt: nextPC) notAFixup
  		ifTrue: "The next instruction is dead.  we can skip it."
  			[deadCode := true.
  		 	 self ensureFixupAt: targetBytecodePC.
  			 self ensureFixupAt: postBranchPC ]
  		ifFalse:
  			[self ssPushConstant: objectMemory trueObject]. "dummy value"
  	"We can only elide the jump if the pc after nextPC is the same as postBranchPC.
  	 Branch following means it may not be."
  	self nextDescriptorExtensionsAndNextPCInto:
  		[:iguana1 :iguana2 :iguana3 :followingPC| nextPC := followingPC].
  	canElide := deadCode and: [nextPC = postBranchPC].
  	 branchDescriptor isBranchTrue
  		ifTrue: 
  			[ self Jump: (self ensureNonMergeFixupAt: targetBytecodePC).
  			  canElide 
  					ifFalse: [ jmp jmpTarget: (self ensureNonMergeFixupAt: postBranchPC) ]
  					ifTrue: [ jmp jmpTarget: self Label ] ]
  		ifFalse: [ canElide ifFalse: [ self Jump: (self ensureNonMergeFixupAt: postBranchPC).
  				 jmp jmpTarget: (self ensureNonMergeFixupAt: targetBytecodePC) ] ].
  	^0!



More information about the Vm-dev mailing list