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

commits at source.squeak.org commits at source.squeak.org
Tue Aug 3 21:21:36 UTC 2021

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

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

Name: VMMaker.oscog-eem.3021
Author: eem
Time: 3 August 2021, 2:21:16.144209 pm
UUID: 2c777da9-4008-4da6-969c-9a92ef4d977f
Ancestors: VMMaker.oscog-eem.3020

Cogit: FastCPrimitiveCall. Save and restore LinkReg and/or SPReg around the primitive and checkForAndFollowForwardedPrimitiveState calls.  At least do so on ARMv8.  ARMv6 et al still needs testing.

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

Item was changed:
  ----- Method: SimpleStackBasedCogit>>compileOnStackExternalPrimitive:flags: (in category 'primitive generators') -----
  compileOnStackExternalPrimitive: primitiveRoutine flags: flags
  	"Compile a fast call of a C primitive using the current stack page, avoiding the stack switch except on failure.
  	 This convention still uses stackPointer and argumentCount to access operands.  Push all operands to the stack,
  	 assign stackPointer, argumentCount, and zero primFailCode.  Make the call (saving a LinkReg if required).
  	 Test for failure and return.  On failure on Spur, if there is an accessor depth, assign framePointer and newMethod,
  	 do the stack switch, call checkForAndFollowForwardedPrimitiveState, and loop back if forwarders are found.
  	 Fall through to frame build."
  	<var: #primitiveRoutine declareC: 'void (*primitiveRoutine)(void)'>
+ 	| calleeSavedRegisterMask linkRegSaveRegister spRegSaveRegister jmp retry |
- 	| jmp retry calleeSavedReg |
  	self assert: (objectRepresentation hasSpurMemoryManagerAPI and: [flags anyMask: PrimCallOnSmalltalkStack]).
  	self deny: (backEnd hasVarBaseRegister
  				and: [self register: VarBaseReg isInMask: ABICallerSavedRegisterMask]).
  	"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.
  	self genExternalizeStackPointerForFastPrimitiveCall.
+ 	"We may need to save LinkReg and/or SPReg, and given the stack machinations
+ 	  it is much easier to save them in callee saved registers than on the stack itself."
+ 	calleeSavedRegisterMask := ABICalleeSavedRegisterMask bitClear: (self registerMaskFor: ClassReg).
  	backEnd hasLinkRegister ifTrue:
+ 		[linkRegSaveRegister := self availableRegisterOrNoneIn: calleeSavedRegisterMask.
+ 		 self deny: linkRegSaveRegister = NoReg.
+ 		 self MoveR: LinkReg R: linkRegSaveRegister.
+ 		 calleeSavedRegisterMask := calleeSavedRegisterMask bitClear: (self registerMaskFor: linkRegSaveRegister)].
+ 	spRegSaveRegister := NoReg.
- 		[self PushR: LinkReg].
- 	retry := self Label.
- 	calleeSavedReg := NoReg.
  	(SPReg ~= NativeSPReg
  	 and: [(self isCalleeSavedReg: SPReg) not]) ifTrue:
+ 		[spRegSaveRegister := self availableRegisterOrNoneIn: calleeSavedRegisterMask.
+ 		 self deny: spRegSaveRegister = NoReg.
+ 		 self MoveR: SPReg R: spRegSaveRegister].
+ 	retry := self Label.
- 		[calleeSavedReg := self availableRegisterOrNoneIn: ABICalleeSavedRegisterMask.
- 		 self deny: calleeSavedReg = NoReg.
- 		 self MoveR: SPReg R: calleeSavedReg].
  	(flags anyMask: PrimCallOnSmalltalkStackAlign2x)
  		ifTrue: [self AndCq: (objectMemory wordSize * 2 - 1) bitInvert R: SPReg R: NativeSPReg]
  			[SPReg ~= NativeSPReg ifTrue:
  				[backEnd genLoadNativeSPRegWithAlignedSPReg]].
  	self CallFullRT: primitiveRoutine.
  	self MoveAw: coInterpreter primFailCodeAddress R: TempReg.
+ 	spRegSaveRegister ~= NoReg ifTrue:
+ 		[self MoveR: spRegSaveRegister R: SPReg].
- 	calleeSavedReg ~= NoReg ifTrue:
- 		[self MoveR: calleeSavedReg R: SPReg].
  	self CmpCq: 0 R: TempReg.
  	jmp := self JumpNonZero: 0.
  	"At this point the primitive has cut back stackPointer to point to the result.
  	 The original retpc is (argumentCount + 1) words beneath it."
  	self MoveAw: coInterpreter stackPointerAddress R: TempReg.
- 	self MoveMw: (methodOrBlockNumArgs + 1 * objectMemory wordSize) negated
- 		r: TempReg
- 		R: (backEnd hasLinkRegister ifTrue: [LinkReg] ifFalse: [ClassReg]).
  	self MoveR: TempReg R: SPReg.
+ 	"get result and restore retpc"
  	backEnd hasLinkRegister
+ 		ifTrue:
+ 			[self PopR: ReceiverResultReg.
+ 			 self MoveR: linkRegSaveRegister R: LinkReg]
- 		ifTrue: [self PopR: ReceiverResultReg] "i.e. get result"
+ 			[| retpcOffset |
+ 			 retpcOffset := (methodOrBlockNumArgs + 1 * objectMemory wordSize) negated.
+ 			 self MoveMw: retpcOffset negated r: TempReg R: ClassReg; "get retpc"
+ 			 	MoveMw: 0 r: TempReg R: ReceiverResultReg;
+ 				MoveR: ClassReg Mw: 0 r: TempReg "put it back on stack for the return..."].
- 			[self MoveMw: 0 r: TempReg R: ReceiverResultReg;
- 				MoveR: ClassReg Mw: 0 r: TempReg]. "i.e. get result and restore retpc"
  	self RetN: 0.
  	jmp jmpTarget: self Label.
  	(coInterpreter accessorDepthForExternalPrimitiveMethod: methodObj) >= 0
  			[| skip |
  			 "Given that following primitive state to the accessor depth is recursive, we're asking for
  			  trouble if we run the fixup on the Smalltalk stack page.  Run it on the full C stack instead.
  			 This won't be a performance issue since primitive failure should be very rare."
  			self MoveR: FPReg Aw: coInterpreter framePointerAddress.
  			self MoveCw: primitiveRoutine asInteger R: TempReg.
  			self MoveR: TempReg Aw: coInterpreter primitiveFunctionPointerAddress.
  			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.
  			self genLoadCStackPointersForPrimCall.
  			self CallFullRT: (self cCode: [#checkForAndFollowForwardedPrimitiveState asUnsignedIntegerPtr]
  								   inSmalltalk: [self simulatedTrampolineFor: #checkForAndFollowForwardedPrimitiveState]).
  			backEnd genLoadStackPointersForFastPrimCall: ClassReg.
  			self CmpCq: 0 R: ABIResultReg.
  			skip := self JumpZero: 0.
  			self MoveCq: 0 R: TempReg.
  			self MoveR: TempReg Aw: coInterpreter primFailCodeAddress.
  			self Jump: retry.
  			skip jmpTarget: self Label]
  		ifFalse: "must reload SPReg to undo any alignment change,"
  			[(flags anyMask: PrimCallOnSmalltalkStackAlign2x) ifTrue:
  				[backEnd genLoadStackPointersForFastPrimCall: ClassReg]].
+ 	"The LinkRegister now contains the return address either of the primitive call or of checkForAndFollowForwardedPrimitiveState.
+ 	 It must be restored to the return address of the send invoking this primtiive method."
+ 	backEnd hasLinkRegister ifTrue:
+ 		[self MoveR: linkRegSaveRegister R: LinkReg].
  	"Finally remember to reload ReceiverResultReg if required.  Even if
  	 arguments have been pushed, the prolog sequence assumes it is live."
  	(self register: ReceiverResultReg isInMask: ABICallerSavedRegisterMask) ifTrue:
+ 		[self MoveMw: (methodOrBlockNumArgs + (backEnd hasLinkRegister ifTrue: [0] ifFalse: [1])) * objectMemory wordSize
- 		[self MoveMw: (backEnd hasLinkRegister ifTrue: [methodOrBlockNumArgs] ifFalse: [methodOrBlockNumArgs + 1]) * objectMemory wordSize
  			r: SPReg
  			R: ReceiverResultReg].
+ 	"continue to frame build..."

Item was changed:
  ----- Method: StackInterpreter>>checkForAndFollowForwardedPrimitiveState (in category 'primitive support') -----
  	"In Spur a primitive may fail due to encountering a forwarder. On failure, check the accessorDepth for the
  	 primitive and if non-negative scan the args to the depth, following any forwarders.  Answer if any are found
  	 so the prim can be retried.  The primitive index is derived from newMethod.
  	 See http://www.mirandabanda.org/cogblog/2014/02/08/primitives-and-the-partial-read-barrier/
  	 and SpurMemoryManager's class comment."
  	<option: #SpurObjectMemory>
  	| primIndex accessorDepth found |
  	self assert: self failed.
  	found := false.
  	primIndex := self primitiveIndexOf: newMethod.
  	self assert: (argumentCount = (self argumentCountOf: newMethod) or: [self isMetaPrimitiveIndex: primIndex]).
  	"First things first; make sure the metadata has been followed before it is accessed to derive accessorDepth..."
+ 	(((self isCalloutPrimitiveIndex: primIndex) or: [primIndex = PrimNumberDoExternalCall])
- 	((self isCalloutPrimitiveIndex: primIndex)
  	 and: [self unfollowFirstLiteralOfMaybeCalloutMethod: newMethod primitiveIndex: primIndex]) ifTrue:
  		[found := true].
  	"If the primitive is one of the meta primitives PrimNumberDoPrimitive or PrimNumberDoExternalCall, then
  	 metaAccessorDepth will have been set to nil at the start of the primitive, and to the accessor depth of the
  	 called primitive (or external call) immediately before dispatch.  Hence if primIndex is that of a meta primitive
  	 then if metaAccessorDepth is -2, the accessor depth is that of the meta primitive, and if > -2, then
  	 metaAccessorDepth is the accessor depth of the primitive (or external call).  Similarly, if the primitive is
  	 primitiveExternalCall then the accessor depth is that of primitiveExternalCall until primitiveFunctionPointer
  	 is assigned, at which point the accessor depth is taken from the slot in newMethod's first literal."
  	accessorDepth := ((self isMetaPrimitiveIndex: primIndex)
  						 and: [metaAccessorDepth > -2])
  							ifTrue: [metaAccessorDepth]
  								[(primIndex = PrimNumberExternalCall
  								  and: [primitiveFunctionPointer ~~ #primitiveExternalCall])
  									ifTrue: [self primitiveAccessorDepthForExternalPrimitiveMethod: newMethod]
  									ifFalse: [primitiveAccessorDepthTable at: primIndex]].
  	self assert: (self saneFunctionPointerForFailureOfPrimIndex: primIndex).
  	self assert: (accessorDepth between: -1 and: 5).
  	accessorDepth >= 0 ifTrue:
  		[| scannedStackFrame |
  		 scannedStackFrame := false.
  		 0 to: argumentCount do:
  			[:index| | oop |
  			oop := self stackValue: index.
  			(objectMemory isNonImmediate: oop) ifTrue:
  				[(objectMemory isForwarded: oop) ifTrue:
  					[self assert: index < argumentCount. "receiver should have been caught at send time."
  					 found := true.
  					 oop := objectMemory followForwarded: oop.
  					 self stackValue: index put: oop.
  					 scannedStackFrame ifFalse:
  						[scannedStackFrame := true.
  						 self	"Avoid repeated primitive failures by following all state in the current stack frame."
  							followForwardedFrameContents: framePointer
  							stackPointer: stackPointer + (argumentCount + 1 * objectMemory wordSize)]].
  				(accessorDepth > 0
  			 	 and: [(objectMemory hasPointerFields: oop)
  				 and: [objectMemory followForwardedObjectFields: oop toDepth: accessorDepth]]) ifTrue:
  					[found := true]]]].

More information about the Vm-dev mailing list