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

commits at source.squeak.org commits at source.squeak.org
Thu Jan 9 04:00:22 UTC 2020


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

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

Name: VMMaker.oscog-eem.2652
Author: eem
Time: 8 January 2020, 8:00:09.03729 pm
UUID: 44036dbb-7b06-454e-9d83-65dadf5b146b
Ancestors: VMMaker.oscog-eem.2651

Cogit:
Fix the regression in genGetActiveContextLarge:inBlock: due to reversing the comparison of the stack pointer with the pointer into the stack.

Improve the IMMUTABILITY store check/immutability trampolines by sharing the stack switch code between the two c ases and oinly taking the branch in the immutable (uncommon) case. Hence extract trampoline return in to its own routine.

Refactor  genWriteCResult[High]IntoReg: to do all the necessary checking.
Move genLoadStackPointers to Cogit from the backEnd.

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

Item was removed:
- ----- Method: CogAbstractInstruction>>genLoadStackPointers (in category 'smalltalk calling convention') -----
- genLoadStackPointers
- 	"Switch back to the Smalltalk stack. Assign SPReg first
- 	 because typically it is used immediately afterwards."
- 	cogit MoveAw: cogit stackPointerAddress R: SPReg.
- 	cogit MoveAw: cogit framePointerAddress R: FPReg.
- 	^0!

Item was changed:
  ----- Method: CogAbstractInstruction>>genWriteCResultIntoReg: (in category 'abi') -----
  genWriteCResultIntoReg: abstractRegister
+ 	(abstractRegister ~= NoReg
+ 	 and: [abstractRegister ~= self cResultRegister]) ifTrue:
+ 		[cogit gen: MoveRR operand: self cResultRegister operand: abstractRegister]!
- 	| cResultReg |
- 	cResultReg := self cResultRegister.
- 	abstractRegister ~= cResultReg ifTrue:
- 		[cogit gen: MoveRR operand: cResultReg operand: abstractRegister]!

Item was changed:
  ----- Method: CogAbstractInstruction>>genWriteCSecondResultIntoReg: (in category 'abi') -----
  genWriteCSecondResultIntoReg: abstractRegister
  	<option: #LowcodeVM>
+ 	(abstractRegister ~= NoReg
+ 	 and: [abstractRegister ~= self cResultRegisterHigh]) ifTrue:
+ 		[cogit gen: MoveRR operand: self cResultRegisterHigh operand: abstractRegister]!
- 	| cResultReg |
- 	cResultReg := self cResultRegisterHigh.
- 	abstractRegister ~= cResultReg ifTrue:
- 		[cogit gen: MoveRR operand: cResultReg operand: abstractRegister]!

Item was changed:
  ----- Method: CogObjectRepresentationForSpur>>genGetActiveContextLarge:inBlock: (in category 'initialization') -----
  genGetActiveContextLarge: isLarge inBlock: isInBlock
  	"Create a trampoline to answer the active context that will
  	 answer it if a frame is already married, and create it otherwise.
  	 Assume numArgs is in SendNumArgsReg and ClassReg is free."
  	| header slotSize jumpSingle loopHead jumpNeedScavenge continuation exit |
  	<var: #jumpNeedScavenge type: #'AbstractInstruction *'>
  	<var: #continuation type: #'AbstractInstruction *'>
  	<var: #jumpSingle type: #'AbstractInstruction *'>
  	<var: #loopHead type: #'AbstractInstruction *'>
  	<var: #exit type: #'AbstractInstruction *'>
  	cogit
  		MoveMw: FoxMethod r: FPReg R: ClassReg;
  		TstCq: MFMethodFlagHasContextFlag R: ClassReg.
  	jumpSingle := cogit JumpZero: 0. "jump if flag bit not set"
  	cogit "since the flag bit was set, get the context in the receiver reg and return"
  		MoveMw: FoxThisContext r: FPReg R: ReceiverResultReg;
  		RetN: 0.
  	jumpSingle jmpTarget: cogit Label.
  
  	"OK, it doesn't exist; instantiate and initialize it"
  	"set the hasContext flag; See CoInterpreter class>>initializeFrameIndices"
  	cogit
  		OrCq: MFMethodFlagHasContextFlag R: ClassReg;
  		MoveR: ClassReg Mw: FoxMethod r: FPReg.
  	"now get the home CogMethod into ClassReg and save for post-instantiation."
  	isInBlock caseOf: {
  	[InFullBlock]	-> [cogit SubCq: 3 R: ClassReg]. "-3 is -(hasContext+isBlock) flags"
  	[InVanillaBlock]	-> [cogit
  							SubCq: 3 R: ClassReg; "-3 is -(hasContext+isBlock) flags"
  							MoveM16: 0 r: ClassReg R: TempReg;
  							SubR: TempReg R: ClassReg].
  	[0]				-> [cogit SubCq: 1 R: ClassReg]. "-1 is hasContext flag" }.
  
  	"instantiate the context..."
  	slotSize := isLarge ifTrue: [LargeContextSlots] ifFalse: [SmallContextSlots].
  	header := objectMemory
  					headerForSlots: slotSize
  					format: objectMemory indexablePointersFormat
  					classIndex: ClassMethodContextCompactIndex.
  	self flag: #endianness.
  	cogit MoveAw: objectMemory freeStartAddress R: ReceiverResultReg.
  	self genStoreHeader: header intoNewInstance: ReceiverResultReg using: TempReg.
  	cogit
  		LoadEffectiveAddressMw: (objectMemory smallObjectBytesForSlots: slotSize) r: ReceiverResultReg R: TempReg;
  		MoveR: TempReg Aw: objectMemory freeStartAddress;
  		CmpCq: objectMemory getScavengeThreshold R: TempReg.
  	jumpNeedScavenge := cogit JumpAboveOrEqual: 0.
  
  	"Now initialize the fields of the context.  See CoInterpreter>>marryFrame:SP:copyTemps:"
  	"sender gets frame pointer as a SmallInteger"
  	continuation :=
  	cogit MoveR: FPReg R: TempReg.
  	self genSetSmallIntegerTagsIn: TempReg.
  	cogit MoveR: TempReg Mw: objectMemory baseHeaderSize + (SenderIndex * objectMemory bytesPerOop) r: ReceiverResultReg.
  
  	"pc gets frame caller as a SmallInteger"
  	cogit MoveMw: FoxSavedFP r: FPReg R: TempReg.
  	self genSetSmallIntegerTagsIn: TempReg.
  	cogit MoveR: TempReg Mw: objectMemory baseHeaderSize + (InstructionPointerIndex * objectMemory bytesPerOop) r: ReceiverResultReg.
  
  	"Set the method field, freeing up ClassReg again, and frame's context field,"
  	cogit
  		MoveMw: (cogit offset: CogMethod of: #methodObject) r: ClassReg R: TempReg;
  		MoveR: TempReg Mw: objectMemory baseHeaderSize + (MethodIndex * objectMemory wordSize) r: ReceiverResultReg;
  		MoveR: ReceiverResultReg Mw: FoxThisContext r: FPReg.
  
  	"Now compute stack pointer; this is stackPointer (- 1 for return pc if a CISC) - framePointer - wordSize (1 each for saved pc, method, context, receiver) + 1 (1-relative) + numArgs"
  	"TPR note - the code here is actually doing
  	context stackPointer := ((((fp - sp) / wordSize) - [3|4]) + num args) asSmallInteger"
  	cogit
  		SubR: SPReg R: FPReg R: TempReg; "TempReg := FPReg - SPReg"
  		LogicalShiftRightCq: self log2BytesPerWord R: TempReg;
  		SubCq: (cogit backEnd hasLinkRegister ifTrue: [3] ifFalse: [4]) R: TempReg;
  		AddR: SendNumArgsReg R: TempReg.
  	self genConvertIntegerToSmallIntegerInReg: TempReg.
  	cogit MoveR: TempReg Mw: objectMemory baseHeaderSize + (StackPointerIndex * objectMemory bytesPerOop) r: ReceiverResultReg.
  
  	"Set closureOrNil to either the stacked receiver or nil"
  	isInBlock > 0
  		ifTrue:
  			[cogit
  				MoveR: SendNumArgsReg R: TempReg;
  				AddCq: 2 R: TempReg; "+2 for saved fp and saved pc"
  				MoveXwr: TempReg R: FPReg R: TempReg]
  		ifFalse:
  			[cogit genMoveNilR: TempReg].
  	cogit MoveR: TempReg Mw: objectMemory baseHeaderSize + (ClosureIndex * objectMemory bytesPerOop) r: ReceiverResultReg.
  
  	"Set the receiver"
  	cogit
  		MoveMw: FoxMFReceiver r: FPReg R: TempReg;
  		MoveR: TempReg Mw: objectMemory baseHeaderSize + (ReceiverIndex * objectMemory bytesPerOop) r: ReceiverResultReg.
  
  	"Now copy the arguments.  This is tricky because of the shortage of registers,.  ClassReg ranges
  	 from 1 to numArgs (SendNumArgsReg), and from ReceiverIndex + 1 to ReceiverIndex + numArgs.
  	 1 to: numArgs do:
  		[:i|
  		temp := longAt(FPReg + ((SendNumArgs - i + 2) * wordSize)). +2 for saved pc and savedfp
  		longAtput(FPReg + FoxMFReceiver + (i * wordSize), temp)]"
  	"TPR note: this is a prime candidate for passing off to the backend to do at least faintly optimal code"
  	cogit MoveCq: 1 R: ClassReg.
  	loopHead := cogit CmpR: SendNumArgsReg R: ClassReg.
  	exit := cogit JumpGreater: 0.
  	cogit
  		MoveR: SendNumArgsReg R: TempReg;
  		SubR: ClassReg R: TempReg;
  		AddCq: 2 R: TempReg; "+2 for saved fp and saved pc"
  		MoveXwr: TempReg R: FPReg R: TempReg;
  		AddCq: ReceiverIndex + (objectMemory baseHeaderSize / objectMemory wordSize) R: ClassReg; "Now convert ClassReg from frame index to context index"
  		MoveR: TempReg Xwr: ClassReg R: ReceiverResultReg;
  		SubCq: ReceiverIndex + (objectMemory baseHeaderSize / objectMemory wordSize) - 1 R: ClassReg; "convert back adding 1 ;-)"
  		Jump: loopHead.
  	exit jmpTarget: cogit Label.
  
  	"Finally nil or copy the non-argument temps.
  	 ClassReg := FPReg + FoxMFReceiver.
  	 SendNumArgsReg := SendNumArgsReg+ReceiverIndex.
  	 [ClassReg := ClassReg - wordSize.
  	  backEnd hasLinkRegister
+ 			ifTrue: [SPReg <= ClassReg]
+ 			ifFalse: [SPReg < ClassReg]] whileTrue:
- 			ifTrue: [ClassReg > SPReg]
- 			ifFalse: [ClassReg >= SPReg]] whileTrue:
  		[receiver[SendNumArgsReg] := *ClassReg.
  		 SendNumArgsReg := SendNumArgsReg + 1]]"
  	coInterpreter marryFrameCopiesTemps ifFalse:
  		[cogit MoveCq: objectMemory nilObject R: TempReg].
  	cogit
  		MoveR: FPReg R: ClassReg;
+ 		AddCq: FoxMFReceiver R: ClassReg.
+ 	cogit AddCq: ReceiverIndex + 1 + (objectMemory baseHeaderSize / objectMemory wordSize) R: SendNumArgsReg.
+ 	"The receiver has already been set.
+ 	 If on a CISC there's a retpc for the trampoline call on top of stack; if on a RISC there isn't."
+ 	loopHead := cogit SubCq: objectMemory wordSize R: ClassReg.
- 		AddCq: FoxMFReceiver R: ClassReg;
- 		AddCq: ReceiverIndex + 1 + (objectMemory baseHeaderSize / objectMemory wordSize) R: SendNumArgsReg.
- 	loopHead :=
- 	cogit SubCq: objectMemory wordSize R: ClassReg.
  	cogit CmpR: ClassReg R: SPReg.
+ 	cogit backEnd hasLinkRegister
+ 		ifTrue: [exit := cogit JumpAbove: 0]
+ 		ifFalse: [exit := cogit JumpAboveOrEqual: 0].
+ 
- 	"If on a CISC there's a retpc for the trampoline call on top of stack; if on a RISC there isn't."
- 	exit := cogit backEnd hasLinkRegister
- 				ifTrue: [cogit JumpAboveOrEqual: 0]
- 				ifFalse: [cogit JumpBelow: 0].
  	coInterpreter marryFrameCopiesTemps ifTrue:
  		[cogit MoveMw: 0 r: ClassReg R: TempReg].
  	cogit
  		MoveR: TempReg Xwr: SendNumArgsReg R: ReceiverResultReg;
  		AddCq: 1 R: SendNumArgsReg;
  		Jump: loopHead.
  	exit jmpTarget: cogit Label.
  
  	cogit RetN: 0.
  	
  	jumpNeedScavenge jmpTarget: cogit Label.
  	cogit backEnd saveAndRestoreLinkRegAround:
  		[cogit
  			CallRT: ceScheduleScavengeTrampoline
  			registersToBeSavedMask: (cogit registerMaskFor: ReceiverResultReg and: SendNumArgsReg and: ClassReg)].
  	cogit Jump: continuation.
  	^0!

Item was changed:
  ----- Method: CogObjectRepresentationForSpur>>genStoreTrampolineCalled:instVarIndex: (in category 'initialization') -----
  genStoreTrampolineCalled: trampolineName instVarIndex: instVarIndex
  	"Convention:
  	- RcvrResultReg holds the object mutated.
  	If immutability failure:
  	- TempReg holds the instance variable index mutated 
  		if instVarIndex > numDedicatedStoreTrampoline
  	- ClassReg holds the value to store
  	Registers are not lived across this trampoline as the 
  	immutability failure may need new stack frames."
  	
- 	| jumpSC jumpRC |
  	<option: #IMMUTABILITY>
- 	<var: #trampolineName type: #'char *'>
- 	<var: #jumpSC type: #'AbstractInstruction *'>
- 	<var: #jumpRC type: #'AbstractInstruction *'>
  	<inline: false>
+ 	| startAddress |
+ 	startAddress := cogit methodZoneBase.
  	cogit zeroOpcodeIndex.
+ 	CheckRememberedInTrampoline
+ 		ifTrue:
+ 			[self genStoreTrampolineCheckingRememberedCalled: trampolineName instVarIndex: instVarIndex]
+ 		ifFalse:
+ 			[self genStoreTrampolineNotCheckingRememberedCalled: trampolineName instVarIndex: instVarIndex].
- 	"SendNumArgsReg is mutated but we don't care as register are not live across the trampoline.
- 	 There is no reason why registers cannot be saved over the remember: call, but since the
- 	 immutability check is a suspension point, registers cannot remain live."
- 	jumpSC := self genJumpMutable: ReceiverResultReg scratchReg: SendNumArgsReg.
- 	cogit
- 		compileTrampolineFor: #ceCannotAssignTo:withIndex:valueToAssign:
- 		numArgs: 3
- 		arg: ReceiverResultReg
- 		arg: (instVarIndex < (NumStoreTrampolines - 1)
- 				ifTrue: [cogit trampolineArgConstant: instVarIndex]
- 				ifFalse: [TempReg])
- 		arg: ClassReg
- 		arg: nil
- 		regsToSave: cogit emptyRegisterMask
- 		pushLinkReg: true
- 		resultReg: NoReg.
  		
+ 	cogit outputInstructionsForGeneratedRuntimeAt: startAddress.
+ 	cogit recordGeneratedRunTime: trampolineName address: startAddress.
+ 	cogit recordRunTimeObjectReferences.
+ 	^startAddress!
- 	"Store check"
- 	jumpSC jmpTarget: cogit Label.
- 	"If on 64-bits and doing the remembered bit test here, we can combine the tests to fetch the header once."
- 	CheckRememberedInTrampoline ifTrue:
- 		[objectMemory wordSize = 8
- 			ifTrue:
- 				[cogit TstCq: 1 << objectMemory rememberedBitShift R: SendNumArgsReg.
- 				 jumpRC := cogit JumpZero: 0.
- 				 cogit RetN: 0]
- 			ifFalse:
- 				[jumpRC := self genCheckRememberedBitOf: ReceiverResultReg scratch: SendNumArgsReg.
- 				 self assert: jumpRC opcode = JumpNonZero.
- 				 jumpRC opcode: JumpZero.
- 				 cogit RetN: 0].
- 		 jumpRC jmpTarget: cogit Label].
- 	^ cogit genTrampolineFor: #remember:
- 		called: trampolineName
- 		numArgs: 1
- 		arg: ReceiverResultReg
- 		arg: nil
- 		arg: nil
- 		arg: nil
- 		regsToSave: cogit emptyRegisterMask
- 		pushLinkReg: true
- 		resultReg: NoReg
- 		appendOpcodes: true!

Item was added:
+ ----- Method: CogObjectRepresentationForSpur>>genStoreTrampolineCheckingRememberedCalled:instVarIndex: (in category 'initialization') -----
+ genStoreTrampolineCheckingRememberedCalled: trampolineName instVarIndex: instVarIndex
+ 	"Convention:
+ 	- RcvrResultReg holds the object to be mutated.
+ 	If immutability failure:
+ 	- TempReg holds the instance variable index mutated 
+ 		if instVarIndex > numDedicatedStoreTrampoline
+ 	- ClassReg holds the value to store
+ 	Registers are not live across this trampoline as the 
+ 	immutability failure may need new stack frames."
+ 	
+ 	| jumpImmutable jumpRC |
+ 	<option: #IMMUTABILITY>
+ 	<var: #trampolineName type: #'char *'>
+ 	<inline: true>
+ 	"SendNumArgsReg is mutated but we don't care as registers are not live across the trampoline.
+ 	 There is no reason why registers cannot be saved over the remember: call, but since the
+ 	 immutability check is a suspension point, registers cannot remain live."
+ 	
+ 	"Jump in the uncommon case"
+ 	jumpImmutable := self genJumpImmutable: ReceiverResultReg scratchReg: SendNumArgsReg.
+ 	"Store check"
+ 	"If on 64-bits and doing the remembered bit test here, we can combine the tests to fetch the header once."
+ 	objectMemory wordSize = 8
+ 		ifTrue:
+ 			[cogit TstCq: 1 << objectMemory rememberedBitShift R: SendNumArgsReg.
+ 			 jumpRC := cogit JumpZero: 0]
+ 		ifFalse:
+ 			[jumpRC := self genCheckRememberedBitOf: ReceiverResultReg scratch: SendNumArgsReg.
+ 			 self assert: jumpRC opcode = JumpNonZero.
+ 			 jumpRC opcode: JumpZero].
+ 	cogit RetN: 0.
+ 	jumpRC jmpTarget: cogit Label.
+ 	cogit
+ 		compileTrampolineFor: #remember:
+ 		numArgs: 1
+ 		arg: ReceiverResultReg
+ 		arg: nil
+ 		arg: nil
+ 		arg: nil
+ 		regsToSave: cogit emptyRegisterMask
+ 		pushLinkReg: true
+ 		resultReg: cogit returnRegForStoreCheck.
+ 	jumpImmutable jmpTarget: cogit Label.
+ 	cogit
+ 		compileTrampolineFor: #ceCannotAssignTo:withIndex:valueToAssign:
+ 		numArgs: 3
+ 		arg: ReceiverResultReg
+ 		arg: (instVarIndex < (NumStoreTrampolines - 1)
+ 				ifTrue: [cogit trampolineArgConstant: instVarIndex]
+ 				ifFalse: [TempReg])
+ 		arg: ClassReg
+ 		arg: nil
+ 		regsToSave: cogit emptyRegisterMask
+ 		pushLinkReg: true
+ 		resultReg: NoReg.
+ 	^0
+ !

Item was added:
+ ----- Method: CogObjectRepresentationForSpur>>genStoreTrampolineNotCheckingRememberedCalled:instVarIndex: (in category 'initialization') -----
+ genStoreTrampolineNotCheckingRememberedCalled: trampolineName instVarIndex: instVarIndex
+ 	"Convention:
+ 	- RcvrResultReg holds the object to be mutated.
+ 	If immutability failure:
+ 	- TempReg holds the instance variable index mutated 
+ 		if instVarIndex > numDedicatedStoreTrampoline
+ 	- ClassReg holds the value to store
+ 	Registers are not live across this trampoline as the 
+ 	immutability failure may need new stack frames."
+ 	
+ 	| jumpImmutable pushLinkReg |
+ 	<option: #IMMUTABILITY>
+ 	<var: #trampolineName type: #'char *'>
+ 	<inline: true>
+ 	"We are  going to call something, either remember: in the common case,
+ 	 or much more rarely ceCannotAssignTo:withIndex:valueToAssign:.  So
+ 	 share the stack switch between the two."
+ 	cogit genSmalltalkToCStackSwitch: (pushLinkReg := cogit backEnd hasLinkRegister).
+ 	"SendNumArgsReg is mutated but we don't care as registers are not live across the trampoline.
+ 	 There is no reason why registers cannot be saved over the remember: call, but since the
+ 	 immutability check is a suspension point, registers cannot remain live."
+ 	jumpImmutable := self genJumpImmutable: ReceiverResultReg scratchReg: SendNumArgsReg.
+ 	"Store check"
+ 	cogit
+ 		compileCallFor: #remember:
+ 		numArgs: 1
+ 		arg: ReceiverResultReg
+ 		arg: nil
+ 		arg: nil
+ 		arg: nil
+ 		resultReg: cogit returnRegForStoreCheck
+ 		regsToSave: cogit emptyRegisterMask.
+ 	cogit genLoadStackPointers.
+ 	cogit genTrampolineReturn: pushLinkReg.
+ 	jumpImmutable jmpTarget: cogit Label.
+ 	cogit
+ 		compileCallFor: #ceCannotAssignTo:withIndex:valueToAssign:
+ 		numArgs: 3
+ 		arg: ReceiverResultReg
+ 		arg: (instVarIndex < (NumStoreTrampolines - 1)
+ 				ifTrue: [cogit trampolineArgConstant: instVarIndex]
+ 				ifFalse: [TempReg])
+ 		arg: ClassReg
+ 		arg: nil
+ 		resultReg: NoReg
+ 		regsToSave: cogit emptyRegisterMask.
+ 	cogit genLoadStackPointers.
+ 	cogit genTrampolineReturn: pushLinkReg.
+ 	^0!

Item was added:
+ ----- Method: CogVMSimulator>>printHeadFrame (in category 'debug printing') -----
+ printHeadFrame
+ 	self printFrame: cogit headFramePointer WithSP: cogit headStackPointer!

Item was changed:
  ----- Method: Cogit>>compileCallFor:numArgs:arg:arg:arg:arg:resultReg:regsToSave: (in category 'initialization') -----
  compileCallFor: aRoutine numArgs: numArgs arg: regOrConst0 arg: regOrConst1 arg: regOrConst2 arg: regOrConst3 resultReg: resultRegOrNone regsToSave: regMask
  	"Generate a call to aRoutine with up to 4 arguments.  If resultRegOrNone is not
  	 NoReg assign the C result to resultRegOrNone.  If saveRegs, save all registers.
  	 Hack: a negative arg value indicates an abstract register, a non-negative value
  	 indicates a constant."
  	<var: #aRoutine type: #'void *'>
  	<inline: false>
  	| regsToSave |
  	regsToSave := resultRegOrNone = NoReg
  						ifTrue: [regMask]
  						ifFalse: [regMask bitClear: (self registerMaskFor: resultRegOrNone)].
  	cStackAlignment > objectMemory wordSize ifTrue:
  		[backEnd
  			genAlignCStackSavingRegisters: regsToSave
  			numArgs: numArgs
  			wordAlignment: cStackAlignment / objectMemory wordSize].
  	backEnd
  		genSaveRegs: regsToSave;
  		genMarshallNArgs: numArgs arg: regOrConst0 arg: regOrConst1 arg: regOrConst2 arg: regOrConst3.
  	self CallFullRT: (self cCode: [aRoutine asUnsignedInteger]
  						inSmalltalk: [self simulatedTrampolineFor: aRoutine]).
+ 	backEnd
+ 		genWriteCResultIntoReg: resultRegOrNone;
+ 		genRemoveNArgsFromStack: numArgs;
+ 		genRestoreRegs: regsToSave!
- 	resultRegOrNone ~= NoReg ifTrue:
- 		[backEnd genWriteCResultIntoReg: resultRegOrNone].
- 	backEnd genRemoveNArgsFromStack: numArgs.
- 	backEnd genRestoreRegs: regsToSave!

Item was changed:
  ----- Method: Cogit>>compileCallFor:numArgs:arg:arg:arg:arg:resultReg:resultReg:regsToSave: (in category 'initialization') -----
  compileCallFor: aRoutine numArgs: numArgs arg: regOrConst0 arg: regOrConst1 arg: regOrConst2 arg: regOrConst3 resultReg: resultRegOrNone resultReg: resultReg2OrNone regsToSave: regMask
  	"Generate a call to aRoutine with up to 4 arguments.  If resultRegOrNone is not
  	 NoReg assign the C result to resultRegOrNone.  If saveRegs, save all registers.
  	 Hack: a negative arg value indicates an abstract register, a non-negative value
  	 indicates a constant."
  	<var: #aRoutine type: #'void *'>
  	<inline: false>
  	| regsToSave |
  	regsToSave := resultRegOrNone = NoReg
  						ifTrue: [regMask]
  						ifFalse: [regMask bitClear: (self registerMaskFor: resultRegOrNone)].
  	cStackAlignment > objectMemory wordSize ifTrue:
  		[backEnd
  			genAlignCStackSavingRegisters: regsToSave
  			numArgs: numArgs
  			wordAlignment: cStackAlignment / objectMemory wordSize].
  	backEnd
  		genSaveRegs: regsToSave;
  		genMarshallNArgs: numArgs arg: regOrConst0 arg: regOrConst1 arg: regOrConst2 arg: regOrConst3.
  	self CallFullRT: (self cCode: [aRoutine asUnsignedInteger]
  						inSmalltalk: [self simulatedTrampolineFor: aRoutine]).
+ 	backEnd
+ 		genWriteCResultIntoReg: resultRegOrNone;
+ 		genWriteCSecondResultIntoReg: resultReg2OrNone;
+ 		genRemoveNArgsFromStack: numArgs;
+ 		genRestoreRegs: regsToSave!
- 	resultRegOrNone ~= NoReg ifTrue:
- 		[backEnd genWriteCResultIntoReg: resultRegOrNone].
- 	resultReg2OrNone ~= NoReg ifTrue:
- 		[backEnd genWriteCSecondResultIntoReg: resultReg2OrNone].
- 	backEnd genRemoveNArgsFromStack: numArgs.
- 	backEnd genRestoreRegs: regsToSave!

Item was changed:
  ----- Method: Cogit>>compileCallFor:numArgs:floatArg:floatArg:floatArg:floatArg:resultReg:regsToSave: (in category 'initialization') -----
  compileCallFor: aRoutine numArgs: numArgs floatArg: regOrConst0 floatArg: regOrConst1 floatArg: regOrConst2 floatArg: regOrConst3 resultReg: resultRegOrNone regsToSave: regMask
  	"Generate a call to aRoutine with up to 4 arguments.  If resultRegOrNone is not
  	 NoReg assign the C result to resultRegOrNone.  If saveRegs, save all registers.
  	 Hack: a negative arg value indicates an abstract register, a non-negative value
  	 indicates a constant."
  	<option: #LowcodeVM>
  	<var: #aRoutine type: #'void *'>
  	<inline: false>
  	| regsToSave |
  	regsToSave := resultRegOrNone = NoReg
  						ifTrue: [regMask]
  						ifFalse: [regMask bitClear: (self registerMaskFor: resultRegOrNone)].
  	cStackAlignment > objectMemory wordSize ifTrue:
  		[backEnd
  			genAlignCStackSavingRegisters: regsToSave
  			numArgs: numArgs
  			wordAlignment: cStackAlignment / objectMemory wordSize].
  	backEnd
  		genSaveRegs: regsToSave;
  		genMarshallNArgs: numArgs floatArg: regOrConst0 floatArg: regOrConst1 floatArg: regOrConst2 floatArg: regOrConst3.
  	self CallFullRT: (self cCode: [aRoutine asUnsignedInteger]
  						inSmalltalk: [self simulatedTrampolineFor: aRoutine]).
+ 	backEnd
+ 		genWriteCResultIntoReg: resultRegOrNone;
+ 		genRemoveNFloatArgsFromStack: numArgs;
+ 		genRestoreRegs: regsToSave!
- 	resultRegOrNone ~= NoReg ifTrue:
- 		[backEnd genWriteCResultIntoReg: resultRegOrNone].
- 	backEnd genRemoveNFloatArgsFromStack: numArgs.
- 	backEnd genRestoreRegs: regsToSave!

Item was changed:
  ----- Method: Cogit>>compileTrampolineFor:numArgs:arg:arg:arg:arg:regsToSave:pushLinkReg:floatResultReg: (in category 'initialization') -----
  compileTrampolineFor: aRoutine numArgs: numArgs arg: regOrConst0 arg: regOrConst1 arg: regOrConst2 arg: regOrConst3 regsToSave: regMask pushLinkReg: pushLinkReg floatResultReg: resultRegOrNone
  	"Generate a trampoline with up to four arguments.  Generate either a call or a jump to aRoutine
  	 as requested by callJumpBar.  If generating a call and resultRegOrNone is not NoReg pass the C
  	 result back in resultRegOrNone.
  	 Hack: a negative value indicates an abstract register, a non-negative value indicates a constant."
  	<var: #aRoutine type: #'void *'>
  	<inline: false>
  	self genSmalltalkToCStackSwitch: pushLinkReg.
  	self
  		compileCallFor: aRoutine
  		numArgs: numArgs
  		arg: regOrConst0
  		arg: regOrConst1
  		arg: regOrConst2
  		arg: regOrConst3
  		floatResultReg: resultRegOrNone
  		regsToSave: regMask.
+ 	self genLoadStackPointers;
+ 		genTrampolineReturn: pushLinkReg!
- 	backEnd genLoadStackPointers.
- 	(pushLinkReg and: [backEnd hasLinkRegister])
- 		ifTrue:
- 			[backEnd hasPCRegister
- 				ifTrue: [self PopR: PCReg]
- 				ifFalse: [self PopR: LinkReg. 
- 						self RetN: 0]]
- 		ifFalse: [self RetN: 0]!

Item was changed:
  ----- Method: Cogit>>compileTrampolineFor:numArgs:arg:arg:arg:arg:regsToSave:pushLinkReg:resultReg: (in category 'initialization') -----
  compileTrampolineFor: aRoutine numArgs: numArgs arg: regOrConst0 arg: regOrConst1 arg: regOrConst2 arg: regOrConst3 regsToSave: regMask pushLinkReg: pushLinkReg resultReg: resultRegOrNone
  	"Generate a trampoline with up to four arguments.  Generate either a call or a jump to aRoutine
  	 as requested by callJumpBar.  If generating a call and resultRegOrNone is not NoReg pass the C
  	 result back in resultRegOrNone.
  	 Hack: a negative value indicates an abstract register, a non-negative value indicates a constant."
  	<var: #aRoutine type: #'void *'>
  	<inline: false>
  	self genSmalltalkToCStackSwitch: pushLinkReg.
  	self
  		compileCallFor: aRoutine
  		numArgs: numArgs
  		arg: regOrConst0
  		arg: regOrConst1
  		arg: regOrConst2
  		arg: regOrConst3
  		resultReg: resultRegOrNone
  		regsToSave: regMask.
+ 	self genLoadStackPointers;
+ 		genTrampolineReturn: pushLinkReg!
- 	backEnd genLoadStackPointers.
- 	(pushLinkReg and: [backEnd hasLinkRegister])
- 		ifTrue:
- 			[backEnd hasPCRegister
- 				ifTrue: [self PopR: PCReg]
- 				ifFalse: [self PopR: LinkReg. 
- 						self RetN: 0]]
- 		ifFalse: [self RetN: 0]!

Item was changed:
  ----- Method: Cogit>>compileTrampolineFor:numArgs:arg:arg:arg:arg:regsToSave:pushLinkReg:resultReg:resultReg: (in category 'initialization') -----
  compileTrampolineFor: aRoutine numArgs: numArgs arg: regOrConst0 arg: regOrConst1 arg: regOrConst2 arg: regOrConst3 regsToSave: regMask pushLinkReg: pushLinkReg resultReg: resultRegOrNone resultReg: resultReg2OrNone
  	"Generate a trampoline with up to four arguments.  Generate either a call or a jump to aRoutine
  	 as requested by callJumpBar.  If generating a call and resultRegOrNone is not NoReg pass the C
  	 result back in resultRegOrNone.
  	 Hack: a negative value indicates an abstract register, a non-negative value indicates a constant."
  	<var: #aRoutine type: #'void *'>
  	<inline: false>
  	self genSmalltalkToCStackSwitch: pushLinkReg.
  	self
  		compileCallFor: aRoutine
  		numArgs: numArgs
  		arg: regOrConst0
  		arg: regOrConst1
  		arg: regOrConst2
  		arg: regOrConst3
  		resultReg: resultRegOrNone
  		resultReg: resultReg2OrNone
  		regsToSave: regMask.
+ 	self genLoadStackPointers;
+ 		genTrampolineReturn: pushLinkReg!
- 	backEnd genLoadStackPointers.
- 	(pushLinkReg and: [backEnd hasLinkRegister])
- 		ifTrue:
- 			[backEnd hasPCRegister
- 				ifTrue: [self PopR: PCReg]
- 				ifFalse: [self PopR: LinkReg. 
- 						self RetN: 0]]
- 		ifFalse: [self RetN: 0]!

Item was changed:
  ----- Method: Cogit>>compileTrampolineFor:numArgs:floatArg:floatArg:floatArg:floatArg:regsToSave:pushLinkReg:resultReg: (in category 'initialization') -----
  compileTrampolineFor: aRoutine numArgs: numArgs floatArg: regOrConst0 floatArg: regOrConst1 floatArg: regOrConst2 floatArg: regOrConst3 regsToSave: regMask pushLinkReg: pushLinkReg resultReg: resultRegOrNone
  	"Generate a trampoline with up to four arguments.  Generate either a call or a jump to aRoutine
  	 as requested by callJumpBar.  If generating a call and resultRegOrNone is not NoReg pass the C
  	 result back in resultRegOrNone.
  	 Hack: a negative value indicates an abstract register, a non-negative value indicates a constant."
  	<option: #LowcodeVM>
  	<var: #aRoutine type: #'void *'>
  	<inline: false>
  	self genSmalltalkToCStackSwitch: pushLinkReg.
  	self
  		compileCallFor: aRoutine
  		numArgs: numArgs
  		floatArg: regOrConst0
  		floatArg: regOrConst1
  		floatArg: regOrConst2
  		floatArg: regOrConst3
  		resultReg: resultRegOrNone
  		regsToSave: regMask.
+ 	self genLoadStackPointers;
+ 		genTrampolineReturn: pushLinkReg!
- 	backEnd genLoadStackPointers.
- 	(pushLinkReg and: [backEnd hasLinkRegister])
- 		ifTrue:
- 			[backEnd hasPCRegister
- 				ifTrue: [self PopR: PCReg]
- 				ifFalse: [self PopR: LinkReg. 
- 						self RetN: 0]]
- 		ifFalse: [self RetN: 0]!

Item was changed:
  ----- Method: Cogit>>genEnilopmartFor:and:and:forCall:called: (in category 'initialization') -----
  genEnilopmartFor: regArg1 and: regArg2OrNone and: regArg3OrNone forCall: forCall called: trampolineName
  	"An enilopmart (the reverse of a trampoline) is a piece of code that makes
  	 the system-call-like transition from the C runtime into generated machine
  	 code.  The desired arguments and entry-point are pushed on a stackPage's
  	 stack.  The enilopmart pops off the values to be loaded into registers and
  	 then executes a return instruction to pop off the entry-point and jump to it.
  
  						BEFORE				AFTER			(stacks grow down)
  						whatever			stackPointer ->	whatever
  						target address =>	reg1 = reg1val, etc
  						reg1val				pc = target address
  						reg2val
  		stackPointer ->	reg3val"
  
  	<var: #trampolineName type: #'char *'>
  	<returnTypeC: #'void (*genEnilopmartForandandforCallcalled(sqInt regArg1, sqInt regArg2OrNone, sqInt regArg3OrNone, sqInt forCall, char *trampolineName))(void)'>
  
  	| size endAddress enilopmart |
  	self zeroOpcodeIndex.
  	backEnd hasVarBaseRegister ifTrue:
  		[self MoveCq: self varBaseAddress R: VarBaseReg]. "Must happen first; value may be used in genLoadStackPointers"
+ 	self genLoadStackPointers.
- 	backEnd genLoadStackPointers.
  	regArg3OrNone ~= NoReg ifTrue: [self PopR: regArg3OrNone].
  	regArg2OrNone ~= NoReg ifTrue: [self PopR: regArg2OrNone].
  	self PopR: regArg1.
  	self genEnilopmartReturn: forCall.
  	self computeMaximumSizes.
  	size := self generateInstructionsAt: methodZoneBase.
  	endAddress := self outputInstructionsAt: methodZoneBase.
  	self assert: methodZoneBase + size = endAddress.
  	enilopmart := methodZoneBase.
  	methodZoneBase := self alignUptoRoutineBoundary: endAddress.
  	backEnd stopsFrom: endAddress to: methodZoneBase - 1.
  	self recordGeneratedRunTime: trampolineName address: enilopmart.
  	^self cCoerceSimple: enilopmart to: #'void (*)(void)'!

Item was added:
+ ----- Method: Cogit>>genLoadStackPointers (in category 'trampoline support') -----
+ genLoadStackPointers
+ 	"Switch back to the Smalltalk stack. Assign SPReg first
+ 	 because typically it is used immediately afterwards."
+ 	self MoveAw: coInterpreter stackPointerAddress R: SPReg.
+ 	self MoveAw: coInterpreter framePointerAddress R: FPReg.
+ 	^0!

Item was added:
+ ----- Method: Cogit>>genTrampolineReturn: (in category 'initialization') -----
+ genTrampolineReturn: lnkRegWasPushed
+ 	"To return from a trampoline call we have to take the return address off the stack,
+ 	 iof it has been saved"
+ 
+ 	(lnkRegWasPushed and: [backEnd hasLinkRegister])
+ 		ifTrue:
+ 			[backEnd hasPCRegister
+ 				ifTrue: [self PopR: PCReg]
+ 				ifFalse: [self PopR: LinkReg. 
+ 						self RetN: 0]]
+ 		ifFalse: [self RetN: 0]!

Item was changed:
  ----- Method: SimpleStackBasedCogit>>compileInterpreterPrimitive:flags: (in category 'primitive generators') -----
  compileInterpreterPrimitive: primitiveRoutine flags: flags
  	"Compile a call to an interpreter primitive.  Call the C routine with the
  	 usual stack-switching dance, test the primFailCode and then either
  	 return on success or continue to the method body."
  	<var: #primitiveRoutine declareC: 'void (*primitiveRoutine)(void)'>
  	| jmp jmpSamplePrim continuePostSamplePrim jmpSampleNonPrim continuePostSampleNonPrim |
  	<var: #jmp type: #'AbstractInstruction *'>
  	<var: #jmpSamplePrim type: #'AbstractInstruction *'>
  	<var: #jmpSampleNonPrim type: #'AbstractInstruction *'>
  	<var: #continuePostSamplePrim type: #'AbstractInstruction *'>
  	<var: #continuePostSampleNonPrim type: #'AbstractInstruction *'>
  
  	"Save processor fp, sp and return pc in the interpreter's frame stack and instruction pointers"
  	self genExternalizePointersForPrimitiveCall.
  	"Switch to the C stack."
  	self genLoadCStackPointersForPrimCall.
  
  	(flags anyMask: PrimCallCollectsProfileSamples) ifTrue:
  		["Test nextProfileTick for being non-zero and call checkProfileTick if so"
  		objectMemory wordSize = 4
  			ifTrue:
  				[self MoveAw: coInterpreter nextProfileTickAddress R: TempReg.
  				 self MoveAw: coInterpreter nextProfileTickAddress + objectMemory wordSize R: ClassReg.
  				 self OrR: TempReg R: ClassReg]
  			ifFalse:
  				[self MoveAw: coInterpreter nextProfileTickAddress R: TempReg.
  				 self CmpCq: 0 R: TempReg].
  		"If set, jump to record sample call."
  		jmpSampleNonPrim := self JumpNonZero: 0.
  		continuePostSampleNonPrim := self Label].
  
  	"Old full prim trace is in VMMaker-eem.550 and prior"
  	self recordPrimTrace ifTrue:
  		[self genFastPrimTraceUsing: ClassReg and: SendNumArgsReg].
  
  	"Clear the primFailCode and set argumentCount"
  	self MoveCq: 0 R: TempReg.
  	self MoveR: TempReg Aw: coInterpreter primFailCodeAddress.
  	methodOrBlockNumArgs ~= 0 ifTrue:
  		[self MoveCq: methodOrBlockNumArgs R: TempReg].
  	self MoveR: TempReg Aw: coInterpreter argumentCountAddress.
  
  	"If required, set primitiveFunctionPointer and newMethod"
  	(flags anyMask: PrimCallNeedsPrimitiveFunction) ifTrue:
  		[self MoveCw: primitiveRoutine asInteger R: TempReg.
  		 primSetFunctionLabel :=
  		 self MoveR: TempReg Aw: coInterpreter primitiveFunctionPointerAddress].
  	(flags anyMask: PrimCallNeedsNewMethod+PrimCallMayCallBack) ifTrue:
  		["The ceActivateFailingPrimitiveMethod: machinery can't handle framelessness."
  		 (flags anyMask: PrimCallMayCallBack) ifTrue:
  			[needsFrame := true].
  		 methodLabel addDependent:
  			(self annotateAbsolutePCRef:
  				(self MoveCw: methodLabel asInteger R: ClassReg)).
  		 self MoveMw: (self offset: CogMethod of: #methodObject) r: ClassReg R: TempReg.
  		 self MoveR: TempReg Aw: coInterpreter newMethodAddress].
  
  	"Invoke the primitive"
  	self PrefetchAw: coInterpreter primFailCodeAddress.
  	(flags anyMask: PrimCallMayCallBack)
  		ifTrue: "Sideways call the C primitive routine so that we return through cePrimReturnEnterCogCode."
  			["On Spur ceActivateFailingPrimitiveMethod: would like to retry if forwarders
  			  are found. So insist on PrimCallNeedsPrimitiveFunction being set too."
  			 self assert: (flags anyMask: PrimCallNeedsPrimitiveFunction).
  			 backEnd
  				genMarshallNArgs: 0 arg: 0 arg: 0 arg: 0 arg: 0;
  				genSubstituteReturnAddress:
  					((flags anyMask: PrimCallCollectsProfileSamples)
  						ifTrue: [cePrimReturnEnterCogCodeProfiling]
  						ifFalse: [cePrimReturnEnterCogCode]).
  			 primInvokeInstruction := self JumpFullRT: primitiveRoutine asInteger.
  			 jmp := jmpSamplePrim := continuePostSamplePrim := nil]
  		ifFalse:
  			["Call the C primitive routine."
  			backEnd genMarshallNArgs: 0 arg: 0 arg: 0 arg: 0 arg: 0.
  			primInvokeInstruction := self CallFullRT: primitiveRoutine asInteger.
  			backEnd genRemoveNArgsFromStack: 0.
  			(flags anyMask: PrimCallCollectsProfileSamples) ifTrue:
  				[self assert: (flags anyMask: PrimCallNeedsNewMethod).
  				"Test nextProfileTick for being non-zero and call checkProfileTick if so"
  				objectMemory wordSize = 4
  					ifTrue:
  						[self MoveAw: coInterpreter nextProfileTickAddress R: TempReg.
  						 self MoveAw: coInterpreter nextProfileTickAddress + objectMemory wordSize R: ClassReg.
  						 self OrR: TempReg R: ClassReg]
  					ifFalse:
  						[self MoveAw: coInterpreter nextProfileTickAddress R: TempReg.
  						 self CmpCq: 0 R: TempReg].
  				"If set, jump to record sample call."
  				jmpSamplePrim := self JumpNonZero: 0.
  				continuePostSamplePrim := self Label].
  			objectRepresentation maybeCompileRetryOnPrimitiveFail: primitiveIndex.
  			self maybeCompileAllocFillerCheck.
  			"Switch back to the Smalltalk stack.  Stack better be in either of these two states:
  				success:	stackPointer ->	result (was receiver)
  											arg1
  											...
  											argN
  											return pc
  				failure:						receiver
  											arg1
  											...
  							stackPointer ->	argN
  											return pc
  			In either case we can push the instructionPointer or load it into the LinkRegister to reestablish the return pc"
  			self MoveAw: coInterpreter instructionPointerAddress
  				R: (backEnd hasLinkRegister ifTrue: [LinkReg] ifFalse: [ClassReg]).
+ 			self genLoadStackPointers.
- 			backEnd genLoadStackPointers.
  			"Test primitive failure"
  			self MoveAw: coInterpreter primFailCodeAddress R: TempReg.
  			backEnd hasLinkRegister ifFalse: [self PushR: ClassReg]. "Restore return pc on CISCs"
  			self flag: 'ask concrete code gen if move sets condition codes?'.
  			self CmpCq: 0 R: TempReg.
  			jmp := self JumpNonZero: 0.
  			"Fetch result from stack"
  			self MoveMw: (backEnd hasLinkRegister ifTrue: [0] ifFalse: [objectMemory wordSize])
  				r: SPReg
  				R: ReceiverResultReg.
  			self RetN: objectMemory wordSize].	"return to caller, popping receiver"
  
  	(flags anyMask: PrimCallCollectsProfileSamples) ifTrue:
  		["The sample is collected by cePrimReturnEnterCogCode for external calls"
  		jmpSamplePrim ifNotNil:
  			["Call ceCheckProfileTick: to record sample and then continue."
  			jmpSamplePrim jmpTarget: self Label.
  			self assert: (flags anyMask: PrimCallNeedsNewMethod).
  			self CallFullRT: (self cCode: [#ceCheckProfileTick asUnsignedIntegerPtr]
  							   inSmalltalk: [self simulatedTrampolineFor: #ceCheckProfileTick]).
  			"reenter the post-primitive call flow"
  			self Jump: continuePostSamplePrim].
  		"Null newMethod and call ceCheckProfileTick: to record sample and then continue.
  		 ceCheckProfileTick will map null/0 to coInterpreter nilObject"
  		jmpSampleNonPrim jmpTarget: self Label.
  		self MoveCq: 0 R: TempReg.
  		self MoveR: TempReg Aw: coInterpreter newMethodAddress.
  		self CallFullRT: (self cCode: [#ceCheckProfileTick asUnsignedIntegerPtr]
  						   inSmalltalk: [self simulatedTrampolineFor: #ceCheckProfileTick]).
  		"reenter the post-primitive call flow"
  		self Jump: continuePostSampleNonPrim].
  
  	jmp ifNotNil:
  		["Jump to restore of receiver reg and proceed to frame build for failure."
  		 jmp jmpTarget: self Label.
  		 "Restore receiver reg from stack.  If on RISCs ret pc is in LinkReg, if on CISCs ret pc is on stack."
  		 self MoveMw: objectMemory wordSize * (methodOrBlockNumArgs + (backEnd hasLinkRegister ifTrue: [0] ifFalse: [1]))
  			r: SPReg
  			R: ReceiverResultReg].
  	^0!

Item was changed:
  ----- Method: SimpleStackBasedCogit>>compileMachineCodeInterpreterPrimitive: (in category 'primitive generators') -----
  compileMachineCodeInterpreterPrimitive: primitiveRoutine
  	"Compile a call to a machine-code convention interpreter primitive.  Call the C routine
  	 on the Smalltalk stack, assuming it consumes little or no stack space."
  	<var: #primitiveRoutine declareC: 'void (*primitiveRoutine)(void)'>
  	| jmpFail liveRegsMask |
  	"for now handle functions with less than 4 arguments; our C call marshalling machinery
  	 extends up to 4 arguments only, and the first argument of an mcprim is the receiver."
  	self assert: methodOrBlockNumArgs <= 3.
  	liveRegsMask := (methodOrBlockNumArgs > self numRegArgs
  					   or: [methodOrBlockNumArgs = 0])
  						ifTrue:
  							[self registerMaskFor: ReceiverResultReg]
  						ifFalse:
  							[(self numRegArgs > 1 and: [methodOrBlockNumArgs > 1])
  								ifFalse: [self registerMaskFor: ReceiverResultReg and: Arg0Reg]
  								ifTrue: [self registerMaskFor: ReceiverResultReg and: Arg0Reg and: Arg1Reg]].
  	backEnd genSaveRegs: (liveRegsMask bitAnd: CallerSavedRegisterMask).
  	methodOrBlockNumArgs > self numRegArgs ifTrue:
  		["Wrangle args into Arg0Reg, Arg1Reg, SendNumArgsReg & ClassReg"
  		 "offset := self bitCountOf: (liveRegsMask bitAnd: CallerSavedRegisterMask)."
  		 self shouldBeImplemented].
  	backEnd
  		genMarshallNArgs: methodOrBlockNumArgs + 1
  		arg: ReceiverResultReg
  		arg: Arg0Reg
  		arg: Arg1Reg
  		arg: SendNumArgsReg
  		"arg: ClassReg (when we extend C call marshalling to support 5 args for replaceFrom:to:with:startingAt:".
  	backEnd saveAndRestoreLinkRegUsingCalleeSavedRegNotLiveAtPointOfSendAround:
  		[self CallFullRT: primitiveRoutine asInteger].
  	backEnd
  		genRemoveNArgsFromStack: methodOrBlockNumArgs + 1;
  		genRestoreRegs: (liveRegsMask bitAnd: CallerSavedRegisterMask).
  	self CmpCq: 0 R: backEnd cResultRegister.
  	jmpFail := self JumpZero: 0.
+ 	backEnd genWriteCResultIntoReg: ReceiverResultReg.
- 	backEnd cResultRegister ~= ReceiverResultReg ifTrue:
- 		[self MoveR: backEnd cResultRegister R: ReceiverResultReg].
  	self RetN: (methodOrBlockNumArgs > self numRegArgs
  				ifTrue: [methodOrBlockNumArgs + 1 * objectMemory wordSize]
  				ifFalse: [0]).
  	jmpFail jmpTarget: self Label.
  	^0!

Item was changed:
  ----- Method: SimpleStackBasedCogit>>genPrimReturnEnterCogCodeEnilopmart: (in category 'initialization') -----
  genPrimReturnEnterCogCodeEnilopmart: profiling
  	"Generate the substitute return code for an external or FFI primitive call.
  	 On success simply return, extracting numArgs from newMethod.
  	 On primitive failure call ceActivateFailingPrimitiveMethod: newMethod."
  	| jmpSample continuePostSample jmpFail |
  	<var: #jmpSample type: #'AbstractInstruction *'>
  	<var: #continuePostSample type: #'AbstractInstruction *'>
  	<var: #jmpFail type: #'AbstractInstruction *'>
  	self zeroOpcodeIndex.
  	backEnd hasVarBaseRegister ifTrue:
  		[self MoveCq: self varBaseAddress R: VarBaseReg]. "Must happen sometime"
  
  	profiling ifTrue:
  		["Test nextProfileTick for being non-zero and call checkProfileTick: if so.
  		  N.B. nextProfileTick is 64-bits so 32-bit systems need to test both halves."
  		objectMemory wordSize = 4
  			ifTrue:
  				[self MoveAw: coInterpreter nextProfileTickAddress R: TempReg.
  				 self MoveAw: coInterpreter nextProfileTickAddress + objectMemory wordSize R: ClassReg.
  				 self OrR: TempReg R: ClassReg]
  			ifFalse:
  				[self MoveAw: coInterpreter nextProfileTickAddress R: TempReg.
  				 self CmpCq: 0 R: TempReg].
  		"If set, jump to record sample call."
  		jmpSample := self JumpNonZero: 0.
  		continuePostSample := self Label].
  
  	self maybeCompileAllocFillerCheck.
  
  	"Test primitive failure"
  	self MoveAw: coInterpreter primFailCodeAddress R: TempReg.
  	self flag: 'ask concrete code gen if move sets condition codes?'.
  	self CmpCq: 0 R: TempReg.
  	jmpFail := self JumpNonZero: 0.
  
  	"Switch back to the Smalltalk stack.  Stack better be in either of these two states:
  		success:	stackPointer	->	result (was receiver)
  										arg1
  										...
  										argN
  										return pc
  		failure:							receiver
  										arg1
  										...
  					stackPointer	->	argN
  										return pc
  	We push the instructionPointer to reestablish the return pc in the success case,
  	but leave it to ceActivateFailingPrimitiveMethod: to do so in the failure case."
  
  	backEnd hasLinkRegister
  		ifTrue:
+ 			[self genLoadStackPointers.											"Switch back to Smalltalk stack."
- 			[backEnd genLoadStackPointers.											"Switch back to Smalltalk stack."
  			 backEnd hasPCRegister
  				ifTrue:
  					[self PopR: ReceiverResultReg.										"Pop result from stack"
  					 self MoveAw: coInterpreter instructionPointerAddress R: PCReg]	"Return"
  				ifFalse:
  					[self MoveMw: 0 r: SPReg R: ReceiverResultReg.						"Fetch result from stack"
  					 self MoveAw: coInterpreter instructionPointerAddress R: LinkReg.	"Get ret pc"
  					 self RetN: objectMemory wordSize]]								"Return, popping result from stack"
  		ifFalse:
  			[self MoveAw: coInterpreter instructionPointerAddress R: ClassReg.	"Get return pc"
+ 			 self genLoadStackPointers.									"Switch back to Smalltalk stack."
- 			 backEnd genLoadStackPointers.									"Switch back to Smalltalk stack."
  			 self MoveMw: 0 r: SPReg R: ReceiverResultReg.						"Fetch result from stack"
  			 self MoveR: ClassReg Mw: 0 r: SPReg.								"Restore return pc"
  			 self RetN: 0].														"Return, popping result from stack"
  
  	"Primitive failed.  Invoke C code to build the frame and continue."
  	jmpFail jmpTarget: (self MoveAw: coInterpreter newMethodAddress R: SendNumArgsReg).
  	"Reload sp with CStackPointer; easier than popping args of checkProfileTick."
  	self MoveAw: self cStackPointerAddress R: SPReg.
  	self 
  		compileCallFor: #ceActivateFailingPrimitiveMethod:
  		numArgs: 1
  		arg: SendNumArgsReg
  		arg: nil
  		arg: nil
  		arg: nil
  		resultReg: NoReg
  		regsToSave: self emptyRegisterMask.
  
  	"On Spur ceActivateFailingPrimitiveMethod: may retry the primitive and return if successful.
  	 So continue by returning to the caller.
  	 Switch back to the Smalltalk stack.  Stack should be in this state:
  				success:	stackPointer ->	result (was receiver)
  											arg1
  											...
  											argN
  											return pc
  	 We can push the instructionPointer or load it into the LinkRegister to reestablish the return pc"
  	self MoveAw: coInterpreter instructionPointerAddress
  		R: (backEnd hasLinkRegister ifTrue: [LinkReg] ifFalse: [ClassReg]).
+ 	self genLoadStackPointers.
- 	backEnd genLoadStackPointers.
  	backEnd hasLinkRegister
  		ifTrue:
  			[self MoveMw: 0 r: SPReg R: ReceiverResultReg]	"Fetch result from stack"
  		ifFalse:
  			[self MoveMw: objectMemory wordSize r: SPReg R: ReceiverResultReg.	"Fetch result from stack"
  			 self PushR: ClassReg].											"Restore return pc on CISCs"
  	self RetN: objectMemory wordSize.	"return to caller, popping receiver"
  
  	profiling ifTrue:
  		["Call ceCheckProfileTick: to record sample and then continue.  newMethod
  		 should be up-to-date.  Need to save and restore the link reg around this call."
  		 jmpSample jmpTarget: self Label.
  		 backEnd saveAndRestoreLinkRegAround:
  			[self CallFullRT: (self cCode: '(usqIntptr_t)ceCheckProfileTick'
  						inSmalltalk: [self simulatedTrampolineFor: #ceCheckProfileTick])].
  		 self Jump: continuePostSample]!

Item was changed:
  ----- Method: SimpleStackBasedCogit>>returnRegForStoreCheck (in category 'trampolines') -----
  returnRegForStoreCheck
  	"We must ensure the ReceiverResultReg is live across the store check so that
+ 	 receiver inst vars can be stored into in a frameless method since self exists only
+ 	 in ReceiverResultReg in a frameless method.  So if ReceiverResultReg is caller-saved
+ 	 we use the fact that ceStoreCheck:/remember: answer their argument to reload
+ 	 ReceiverResultReg cheaply.  Otherwise we don't care about the result and use the
+ 	 cResultRegister, effectively a no-op (see compileTrampoline...)."
- 	 we can store into receiver inst vars in a frameless method since self exists
- 	 only in ReceiverResultReg in a frameless method.  So if ReceiverResultReg is
- 	 caller-saved we use the fact that ceStoreCheck: answers its argument to
- 	 reload ReceiverResultReg cheaply.  Otherwise we don't care about the result
- 	 and use the cResultRegister, effectively a no-op (see compileTrampoline...)"
  	<inline: true>
  	^(self isCallerSavedReg: ReceiverResultReg)
  		ifTrue: [ReceiverResultReg]
  		ifFalse: [backEnd cResultRegister]!

Item was changed:
  ----- Method: SistaCogit>>genMustBeBooleanTrampolineFor:called: (in category 'initialization') -----
  genMustBeBooleanTrampolineFor: boolean called: trampolineName
  	"This can be entered in one of two states, depending on SendNumArgsReg. See
  	 e.g. genJumpIf:to:.  If SendNumArgsReg is non-zero then this has been entered via
  	 the initial test of the counter in the jump executed count (i.e. the counter has
  	 tripped).  In this case TempReg contains the boolean to be tested and should not
  	 be offset, and ceCounterTripped should be invoked with the unoffset TempReg.
  	 If SendNumArgsReg is zero then this has been entered for must-be-boolean
  	 processing. TempReg has been offset by boolean and must be corrected and
  	 ceSendMustBeBoolean: invoked with the corrected value."
  	<var: #trampolineName type: #'char *'>
  	| jumpMBB |
  	<var: #jumpMBB type: #'AbstractInstruction *'>
  	<inline: false>
  	self zeroOpcodeIndex.
  	self CmpCq: 0 R: SendNumArgsReg.
  	jumpMBB := self JumpZero: 0.
  	"Open-code self compileTrampolineFor: #ceCounterTripped: numArgs: 1 arg: TempReg ...
  	 so we can restore ResultReceiverReg."
  	self genSmalltalkToCStackSwitch: true.
  	self
  		compileCallFor: #ceCounterTripped:
  		numArgs: 1
  		arg: TempReg
  		arg: nil
  		arg: nil
  		arg: nil
  		resultReg: TempReg "(*)"
  		regsToSave: self emptyRegisterMask.
  	"(*) For the case where the ceCounterTripped: call returns (e.g. because there's no callback selector
  	 installed), the call to the ceSendMustBeBooleanAddTrue/FalseTrampoline is followed by a jump
  	 back to the start of the counter/condition test sequence.  For this case copy the C result to
  	 TempReg (the register that is tested), to reload it with the boolean to be tested."
+ 	self genLoadStackPointers.
- 	backEnd genLoadStackPointers.
  	backEnd hasLinkRegister ifTrue:
  		[self PopR: LinkReg].
  	"To keep ResultReceiverReg live if optStatus thought it was, simply reload it
  	 from the frame pointer.  This avoids having to reload it in the common case
  	 (counter does not trip) if it was live.  Note we can't use putSelfInReceiverResultReg
  	 when generating trampolines because simSelf has not yet been initialized."
  	self MoveMw: FoxMFReceiver r: FPReg R: ReceiverResultReg.
  	self RetN: 0.
  	"If the objectRepresentation does want true & false to be mobile then we need to record these addresses."
  	self assert: (objectRepresentation shouldAnnotateObjectReference: boolean) not.
  	jumpMBB jmpTarget: (self AddCq: boolean R: TempReg).
  	^self genTrampolineFor: #ceSendMustBeBoolean:
  		called: trampolineName
  		numArgs: 1
  		arg: TempReg
  		arg: nil
  		arg: nil
  		arg: nil
  		regsToSave: self emptyRegisterMask
  		pushLinkReg: true
  		resultReg: NoReg
  		appendOpcodes: true!

Item was changed:
  ----- Method: SistaCogitClone>>genMustBeBooleanTrampolineFor:called: (in category 'initialization') -----
  genMustBeBooleanTrampolineFor: boolean called: trampolineName
  	"This can be entered in one of two states, depending on SendNumArgsReg. See
  	 e.g. genJumpIf:to:.  If SendNumArgsReg is non-zero then this has been entered via
  	 the initial test of the counter in the jump executed count (i.e. the counter has
  	 tripped).  In this case TempReg contains the boolean to be tested and should not
  	 be offset, and ceCounterTripped should be invoked with the unoffset TempReg.
  	 If SendNumArgsReg is zero then this has been entered for must-be-boolean
  	 processing. TempReg has been offset by boolean and must be corrected and
  	 ceSendMustBeBoolean: invoked with the corrected value."
  	<var: #trampolineName type: #'char *'>
  	| jumpMBB |
  	<var: #jumpMBB type: #'AbstractInstruction *'>
  	<inline: false>
  	self zeroOpcodeIndex.
  	self CmpCq: 0 R: SendNumArgsReg.
  	jumpMBB := self JumpZero: 0.
  	"Open-code self compileTrampolineFor: #ceCounterTripped: numArgs: 1 arg: TempReg ...
  	 so we can restore ResultReceiverReg."
  	self genSmalltalkToCStackSwitch: true.
  	self
  		compileCallFor: #ceCounterTripped:
  		numArgs: 1
  		arg: TempReg
  		arg: nil
  		arg: nil
  		arg: nil
  		resultReg: TempReg "(*)"
  		regsToSave: self emptyRegisterMask.
  	"(*) For the case where the ceCounterTripped: call returns (e.g. because there's no callback selector
  	 installed), the call to the ceSendMustBeBooleanAddTrue/FalseTrampoline is followed by a jump
  	 back to the start of the counter/condition test sequence.  For this case copy the C result to
  	 TempReg (the register that is tested), to reload it with the boolean to be tested."
+ 	self genLoadStackPointers.
- 	backEnd genLoadStackPointers.
  	backEnd hasLinkRegister ifTrue:
  		[self PopR: LinkReg].
  	"To keep ResultReceiverReg live if optStatus thought it was, simply reload it
  	 from the frame pointer.  This avoids having to reload it in the common case
  	 (counter does not trip) if it was live.  Note we can't use putSelfInReceiverResultReg
  	 when generating trampolines because simSelf has not yet been initialized."
  	self MoveMw: FoxMFReceiver r: FPReg R: ReceiverResultReg.
  	self RetN: 0.
  	"If the objectRepresentation does want true & false to be mobile then we need to record these addresses."
  	self assert: (objectRepresentation shouldAnnotateObjectReference: boolean) not.
  	jumpMBB jmpTarget: (self AddCq: boolean R: TempReg).
  	^self genTrampolineFor: #ceSendMustBeBoolean:
  		called: trampolineName
  		numArgs: 1
  		arg: TempReg
  		arg: nil
  		arg: nil
  		arg: nil
  		regsToSave: self emptyRegisterMask
  		pushLinkReg: true
  		resultReg: NoReg
  		appendOpcodes: true!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>callSwitchToSmalltalkStack (in category 'inline ffi') -----
  callSwitchToSmalltalkStack
  	<option: #LowcodeVM>
  	"Restore the link register"
  	backEnd hasVarBaseRegister ifTrue:
  		[self MoveCq: self varBaseAddress R: VarBaseReg].
+ 	backEnd hasLinkRegister ifTrue:
+ 		[self MoveAw: coInterpreter instructionPointerAddress R: LinkReg].
+ 	self genLoadStackPointers!
- 	backEnd hasLinkRegister ifTrue: [
- 		self MoveAw: coInterpreter instructionPointerAddress R: LinkReg
- 	].
- 	backEnd genLoadStackPointers.!

Item was changed:
  ----- Method: StackToRegisterMappingCogit>>genCallPICEnilopmartNumArgs: (in category 'initialization') -----
  genCallPICEnilopmartNumArgs: numArgs
  	"Generate special versions of the ceCallCogCodePopReceiverAndClassRegs
  	 enilopmart that also pop register args from the stack to undo the pushing of
  	 register args in the abort/miss trampolines."
  	<returnTypeC: 'void (*genCallPICEnilopmartNumArgs(sqInt numArgs))(void)'>
  	| size endAddress enilopmart |
  	self zeroOpcodeIndex.
  	backEnd hasVarBaseRegister ifTrue:
  		[self MoveCq: self varBaseAddress R: VarBaseReg]. "Must happen first; value may be used in genLoadStackPointers"
+ 	self genLoadStackPointers.
- 	backEnd genLoadStackPointers.
  	self PopR: ClassReg. "cacheTag"
  	self PopR: TempReg. "entry-point"
  	self PopR: (backEnd hasLinkRegister ifTrue: [LinkReg] ifFalse: [SendNumArgsReg]). "retpc"
  	numArgs > 0 ifTrue:
  		[numArgs > 1 ifTrue:
  			[self PopR: Arg1Reg.
  			 self assert: self numRegArgs = 2].
  		 self PopR: Arg0Reg].
  	self PopR: ReceiverResultReg.
  	backEnd hasLinkRegister ifFalse: [self PushR: SendNumArgsReg]. "retpc"
  	self JumpR: TempReg.
  	self computeMaximumSizes.
  	size := self generateInstructionsAt: methodZoneBase.
  	endAddress := self outputInstructionsAt: methodZoneBase.
  	self assert: methodZoneBase + size = endAddress.
  	enilopmart := methodZoneBase.
  	methodZoneBase := self alignUptoRoutineBoundary: endAddress.
  	backEnd stopsFrom: endAddress to: methodZoneBase - 1.
  	self recordGeneratedRunTime: (self trampolineName: 'ceCallPIC' numRegArgs: numArgs) address: enilopmart.
  	^self cCoerceSimple: enilopmart to: #'void (*)(void)'!



More information about the Vm-dev mailing list