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

commits at source.squeak.org commits at source.squeak.org
Sun Oct 17 19:10:43 UTC 2021


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

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

Name: VMMaker.oscog-eem.3092
Author: eem
Time: 17 October 2021, 12:10:30.907626 pm
UUID: ab7ad37d-48ab-47f0-a48d-04e926c7684c
Ancestors: VMMaker.oscog-eem.3091

Simulation:
Adapt assertValidExternalStackPointers & assertProcessorStackPointersBelongToCurrentThread to FastCPrimitive calls, and hence simplify handleCallOrJumpSimulationTrap:.
Use printf for neater bytecode printing, moved to the common superclass.

Save & restore pc around "simulateLeafCallOf: ceCaptureCStackPointers" so inMachineCode isn;t confised by a stale value.

Fix a speeling rorre (funciton)

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

Item was changed:
  ----- Method: CCodeGenerator class>>initialize (in category 'class initialization') -----
  initialize
  	"CCodeGenerator initialize"
  
  	NoRegParmsInAssertVMs := true
+ 		"If NoRegParmsInAssertVMs is true the generator spits out an attribute turning off register parameters for static functions in the Assert and Debug VMs which makes debugging easier, since all functions can be safely called from gdb.  One might hope that -mregparm=0 would work but at least on Mac OS X's gcc 4.2.1 it does not and hence we have to use a per function attribute.  Sigh..."!
- 		"If NoRegParmsInAssertVMs is true the generator spits out an attribute turning off register parameters for static functions in the Assert and Debug VMs which makes debugging easier, since all functions can be safely called from gdb.  One might hope that -mregparm=0 would work but at least on Mac OS X's gcc 4.2.1 it does not and hence we have to use a per funciton attribute.  Sigh..."!

Item was changed:
  ----- Method: CoInterpreter>>assertValidExternalStackPointers (in category 'debug support') -----
  assertValidExternalStackPointers
  	<doNotGenerate>
  	"For use *ONLY* by routines coming in to the VM,
  	 i.e. handleCallOrJumpSimulationTrap:.  This is because it nils localFP as a side-effect,
  	 and it does so so that the head frame can be determined reliably."
+ 	((stackPage addressIsInPage: cogit processor sp)
+ 	 and: [stackPage addressIsInPage: cogit processor fp])
+ 		ifTrue: "This is for FastCPrimitive calls.  Assume we're in such a call on the Smalltalk stack"
+ 			[self assert: (stackPointer - cogit processor sp between: 0 and: 64).
+ 			 self assert: cogit processor fp < stackPage baseAddress.
+ 			 self assert: cogit processor sp <= cogit processor fp.
+ 			 self assert: cogit processor sp >= (stackPage realStackLimit - self stackLimitOffset)]
+ 		ifFalse:
+ 			[self assert: framePointer < stackPage baseAddress.
+ 			 self assert: stackPointer < framePointer.
+ 			 self assert: framePointer > stackPointer.
+ 			 self assert: stackPointer >= (stackPage realStackLimit - self stackLimitOffset)].
- 	self assert: framePointer < stackPage baseAddress.
- 	self assert: stackPointer < framePointer.
- 	self assert: framePointer > stackPointer.
- 	self assert: stackPointer >= (stackPage realStackLimit - self stackLimitOffset).
  	self nilLocalFP!

Item was changed:
  ----- Method: CoInterpreter>>enterSmalltalkExecutiveImplementation (in category 'initialization') -----
  enterSmalltalkExecutiveImplementation
  	"Main entry-point into the interpreter at each execution level, where an execution
  	 level is either the start of execution or reentry for a callback.  Capture the C stack
  	 pointers so that calls from machine-code into the C run-time occur at this level.
  	 This is the actual implementation, separated from enterSmalltalkExecutive so the
  	 simulator can wrap it in an exception handler and hence simulate the Cogit's jump
  	 back into C code on interpreting; see ceInvokeInterpret.
  
  	 Conceptually, an invocation of interpret exists at each level of execution from the
  	 initial invocation through each callback.  Entry to each execution level is through
  	 this function.  It captures the C stack & frame pointers for this level of execution
  	 and then either invokes machine code or interpret, depending on whether the
  	 current frame (the effective entry-point into Smalltalk execution) is a machine code
  	 or interpreted frame. In addition, interpret captures the return address of its caller
+ 	 (this function).  The Cogit then uses the captured C stack pointers and return
- 	 (this funciton).  The Cogit then uses the captured C stack pointers and return
  	 address to invoke interpret as if it had been called from this function."
  	<inline: false>
  	cogit assertCStackWellAligned.
  	cogit ceCaptureCStackPointers.
  	(self isMachineCodeFrame: framePointer) ifTrue:
  		[self returnToExecutive: false postContextSwitch: true
  		 "NOTREACHED"].
  	self interpret.
  	^0!

Item was changed:
  ----- Method: CoInterpreterMT>>assertProcessorStackPointersBelongToCurrentThread (in category 'simulation') -----
  assertProcessorStackPointersBelongToCurrentThread
  	<cmacro: '(ignored) 0'> "simulation only"
  	| ownerIndex range |
  	self assert: (ownerIndex := cogThreadManager getVMOwner) > 0.
+ 	self assert: (((stackPage addressIsInPage: cogit processor fp) and: [stackPage addressIsInPage: cogit processor sp])
+ 			or: [range := self cStackRangeForThreadIndex: ownerIndex.
+ 				(range includes: cogit processor fp) and: [range includes: cogit processor sp]])!
- 	range := self cStackRangeForThreadIndex: ownerIndex.
- 	self assert: ((range includes: cogit processor fp) and: [range includes: cogit processor sp])!

Item was changed:
  ----- Method: CogVMSimulator>>logSend: (in category 'debugging traps') -----
  logSend: oop
  	sendCount := sendCount + 1.
  	(printSends or: [printBytecodeAtEachStep]) ifTrue:
+ 		[transcript ensureCr.
+ 		'%d/%d %.*s\n'
+ 			f: transcript
+ 			printf: { byteCount. sendCount. objectMemory numBytesOfBytes: oop. objectMemory firstIndexableField: oop }]!
- 		[transcript print: byteCount; nextPut: $/; print: sendCount; space.
- 		 self printStringOf: oop.
- 		 transcript cr; flush]!

Item was changed:
  ----- Method: CogVMSimulator>>maybeMapPrimitiveFunctionPointerBackToSomethingEvaluable (in category 'primitive support') -----
  maybeMapPrimitiveFunctionPointerBackToSomethingEvaluable
  	"In the real VM primitiveFunctionPointer is either an index (for quick primitives)
  	 or a proper function pointer to a primitive.  In the simulator it may be a small
  	 index (corresponding to a quick primitive index), a symbol (corresponding to
  	 a function pointer) or an index into the externalPrimitiveTable, or an invalid
  	 address that references an evaluable in the simulatedTrampolines dictionary
  	 of the Cogit.  The simulator expects dispatchFunctionPointer to be called with
  	 primitiveFunctionPointer being a symbol only for internal primitives.  External
+ 	 primitives must have their function pointer mapped back to an index.  This
- 	 primitives must have their funciton pointer mapped back to an index.  This
  	 method does the mapping back from fake addresses."
  	<doNotGenerate>
  	(primitiveFunctionPointer isInteger
  	 and: [self isExternalPrimitiveCall: newMethod]) ifTrue: "External prims must be evaluated by the right plugin..."
  		[(cogit simulatedTrampolines at: primitiveFunctionPointer ifAbsent: nil) ifNotNil:
  			[:evaluable| | pfp index externalIndex |
  						"primitiveFunctionPointer := pfp"
  						"(1 to: self mappedPluginEntries size) select: [:index| (self mappedPluginEntries at: index) third == evaluable]"
  			 pfp := primitiveFunctionPointer.
  			 index := self mappedPluginEntries findFirst: [:entry| entry third == evaluable].
  			 self assert: index ~= 0.
  			 externalIndex := 1000 + (externalPrimitiveTable object
  										indexOf: index
  										ifAbsent: [self error: 'entry not found']).
  			 self assert: ((self pluginEntryFor: externalIndex) notNil
  						   and: [(self pluginEntryFor: externalIndex) third == evaluable]).
  			 primitiveFunctionPointer := externalIndex.
  			 ^self]].
  	^super maybeMapPrimitiveFunctionPointerBackToSomethingEvaluable!

Item was removed:
- ----- Method: CogVMSimulator>>printCurrentBytecodeOn: (in category 'debug printing') -----
- printCurrentBytecodeOn: aStream
- 	| code |
- 	code := currentBytecode radix: 16.
- 	aStream ensureCr; print: localIP - method - 3; tab.
- 	bytecodeSetSelector > 0 ifTrue:
- 		[aStream nextPutAll: 'ALT '].
- 	aStream
- 		nextPut: (code size < 2
- 					ifTrue: [$0]
- 					ifFalse: [code at: 1]);
- 		nextPut: code last; space;
- 		nextPutAll: (BytecodeTable at: currentBytecode + 1);
- 		space;
- 		nextPut: $(; print: byteCount + 1; nextPut: $)!

Item was changed:
  ----- Method: Cogit>>ceCaptureCStackPointers (in category 'jit - api') -----
  ceCaptureCStackPointers
  	<api: 'extern void (*ceCaptureCStackPointers)()'>
  	<doNotGenerate>
+ 	| savedpc |
  	coInterpreter isCurrentImageFacade ifTrue:
  		[^self].
+ 	savedpc := processor pc. "this is so as not to mislead inMachineCode"
  	self simulateLeafCallOf: ceCaptureCStackPointers.
+ 	processor pc: savedpc.
  	thisContext sender selector == #generateStackPointerCapture ifTrue:
  		[^self].
  	coInterpreter isThreadedVM ifTrue:
  		[coInterpreter assertProcessorStackPointersBelongToCurrentThread]!

Item was changed:
  ----- Method: Cogit>>handleCallOrJumpSimulationTrap: (in category 'simulation only') -----
  handleCallOrJumpSimulationTrap: aProcessorSimulationTrap
  	<doNotGenerate>
  	| evaluable function memory result savedFramePointer savedStackPointer savedArgumentCount retpc invalidStackPointersExpected index |
  	"Execution of a single instruction must be within the processorLock critical section to ensure
  	 simulation traps are executed atomically.  However, at this point control is leaving machine
  	 code and entering the run-time and hence the lock must be released."
  	processorLock primitiveExitCriticalSection.
  	"This is a hack fix before we revise the simulators.  When a jump call is made, the next
  	 pc is effectively the return address on the stack, not the instruction following the jump."
  	aProcessorSimulationTrap type == #jump ifTrue:
  		[processor hackFixNextPCOfJumpFor: aProcessorSimulationTrap using: objectMemory].
  
  	evaluable := simulatedTrampolines
  					at: aProcessorSimulationTrap address
  					ifAbsent: [self errorProcessingSimulationTrap: aProcessorSimulationTrap
  								in: simulatedTrampolines].
  	function := evaluable isBlock
  					ifTrue: ['aBlock; probably some plugin primitive']
  					ifFalse:
  						[evaluable receiver == backEnd ifTrue: "this is for invoking ARMv5 floating-point intrinsics, and for the short-cut tracing trampolines"
  							[^self handleABICallOrJumpSimulationTrap: aProcessorSimulationTrap evaluable: evaluable].
  						 evaluable selector].
  	memory := coInterpreter memory.
  	function == #interpret ifTrue: "i.e. we're here via ceInvokeInterpret/ceReturnToInterpreterTrampoline and should discard all state back to enterSmalltalkExecutiveImplementation"
  		[self recordInstruction: {'(simulated jump call of '. aProcessorSimulationTrap address. '/'. function. ')'}.
  		 "self halt: evaluable selector."
  	   	 clickConfirm ifTrue:
  		 	[(self confirm: 'skip jump to interpret?') ifFalse:
  				[clickConfirm := false. self halt]].
  		 processor simulateJumpCallOf: aProcessorSimulationTrap address memory: memory.
  		 coInterpreter reenterInterpreter.
  		 "NOTREACHED"
  		 self halt].
+ 	(invalidStackPointersExpected := function == #ceBaseFrameReturn:) ifFalse:
+ 		[evaluable isBlock
+ 			ifTrue: "external primitives..."
+ 				["The only acceptable exception to the rule are fast C primitive calls..."
+ 				 (methodZone cogMethodContaining: (self mostLikelyPrimInvocationPC: processor pc or: (processor leafRetpcIn: memory)))
+ 					ifNil: [self assertf: 'call to block evaluable from non-external method']
+ 					ifNotNil: [:cogMethod|
+ 							coInterpreter assertValidExternalStackPointers]]
+ 			ifFalse:
+ 				[coInterpreter assertValidExternalStackPointers]].
- 	(function == #ceBaseFrameReturn:
- 	or: [function == #ceTakeProfileSample:
- 	or: [function == #primitiveObjectAtPut]])
- 		ifTrue: [invalidStackPointersExpected := true]
- 		ifFalse:
- 			[invalidStackPointersExpected := false.
- 			 evaluable isBlock
- 				ifTrue: "external primitives..."
- 					["The only acceptable exception to the rule are fast C primitive calls..."
- 					 (methodZone cogMethodContaining: (self mostLikelyPrimInvocationPC: processor pc or: (processor leafRetpcIn: memory)))
- 						ifNil: [self assertf: 'call to block evaluable from non-external method']
- 						ifNotNil: [:cogMethod|
- 								self assert: (self cogMethodHasExternalPrim: cogMethod).
- 								(coInterpreter hasFastCLinkage: cogMethod methodObject)
- 									ifTrue: [invalidStackPointersExpected := true. coInterpreter nilLocalFP]
- 									ifFalse: [coInterpreter assertValidExternalStackPointers]]]
- 				ifFalse:
- 					[coInterpreter assertValidExternalStackPointers]].
  	processor
  		simulateCallOf: aProcessorSimulationTrap address
  		nextpc: aProcessorSimulationTrap nextpc
  		memory: memory.
  	retpc := processor retpcIn: memory.
  	self recordInstruction: {'(simulated call of '. aProcessorSimulationTrap address. '/'. function. ')'}.
  	savedFramePointer := coInterpreter framePointer.
  	savedStackPointer := coInterpreter stackPointer.
  	savedArgumentCount := coInterpreter argumentCount.
  	result := ["self halt: evaluable selector."
  		   	   clickConfirm ifTrue:
  			 	[(self confirm: 'skip run-time call?') ifFalse:
  					[clickConfirm := false. self halt]].
  			   evaluable valueWithArguments: (processor
  												postCallArgumentsNumArgs: evaluable numArgs
  												in: memory)]
  				on: ReenterMachineCode
  				do: [:ex| ex return: #continueNoReturn].
  			
  	invalidStackPointersExpected ifFalse:
  		[coInterpreter assertValidExternalStackPointers].
  	"Verify the stack layout assumption compileInterpreterPrimitive: makes, provided we've
  	 not called something that has built a frame, such as closure value or evaluate method, or
  	 switched frames, such as primitiveSignal, primitiveWait, primitiveResume, primitiveSuspend et al."
  	(function beginsWith: 'primitive') ifTrue:
  		[coInterpreter primFailCode = 0
  			ifTrue: [(CogVMSimulator stackAlteringPrimitives includes: function) ifFalse:
  						["This is a rare case (e.g. in Scorch where a married context's sender is set to nil on trapTrpped and hence the stack layout is altered."
  						 (function == #primitiveSlotAtPut and: [objectMemory isContext: (coInterpreter frameReceiver: coInterpreter framePointer)]) ifFalse:
  							[self assert: savedFramePointer = coInterpreter framePointer.
  							 self assert: savedStackPointer + (savedArgumentCount * objectMemory wordSize)
  									= coInterpreter stackPointer]]]
  			ifFalse:
  				[self assert: savedFramePointer = coInterpreter framePointer.
  				 self assert: savedStackPointer = coInterpreter stackPointer]].
  	result ~~ #continueNoReturn ifTrue:
  		[self recordInstruction: {'(simulated return to '. processor retpcIn: memory. ')'}.
  		 processor simulateReturnIn: memory.
  		 self assert: processor pc = retpc.
  		 processor smashCallerSavedRegistersWithValuesFrom: 16r80000000 by: objectMemory wordSize].
  	self assert: (result isInteger "an oop result"
  			or: [result == coInterpreter
  			or: [result == objectMemory
  			or: [(index := #(nil true false continueNoReturn) indexOf: result) > 0
  				and: [result := #(0 1 0 16rF00BA4) at: index. true]]]]).
  	processor cResultRegister: (result
  								ifNil: [0]
  								ifNotNil: [result isInteger
  											ifTrue: [result]
  											ifFalse: [16rF00BA222]])!

Item was changed:
  ----- Method: Integer>>coerceTo:sim: (in category '*VMMaker-interpreter simulator') -----
  coerceTo: cTypeString sim: interpreter
  
  	| bits unitSize |
  	cTypeString last == $* ifTrue:  "C pointer"
  		[unitSize := cTypeString caseOf: {
  		[#'char *'] -> [1].
  		[#'short *'] -> [2].
  		[#'int *'] -> [4].
  		[#'long long *'] -> [8].
  		[#'float *'] -> [^CFloatArray basicNew interpreter: interpreter address: self unitSize: 4; yourself].
  		[#'double *'] -> [^CFloatArray basicNew interpreter: interpreter address: self unitSize: 8; yourself].
  		[#'unsigned *'] -> [4].
  		[#'unsigned int *'] -> [4].
  		[#'unsigned char *'] -> [1].
  		[#'signed char *'] -> [1].
  		[#'unsigned short *'] -> [2].
  		[#'unsigned long long *'] -> [8].
  		[#'oop *'] -> [interpreter objectMemory bytesPerOop].
  		}
  		otherwise: [interpreter objectMemory wordSize].
  		^CArray basicNew
  			interpreter: interpreter address: self unitSize: unitSize;
  			yourself].
  	cTypeString first == $u ifTrue:
  		[bits := cTypeString caseOf: {
  		[#usqInt] -> [interpreter objectMemory wordSize * 8].
  		[#usqLong] -> [64].
  		[#unsigned] -> [32].
  		[#'unsigned char'] -> [8].
  		[#'unsigned int'] -> [8].
  		[#'unsigned long'] -> [48]. "LLP64 on Windows :-("
  		[#'unsigned long long'] -> [64].
  		[#'unsigned short'] -> [16].
  		}
  		otherwise: [self error: 'unknown unsigned integer type name'].
  		^self bitAnd: 1 << bits - 1].
  	bits := cTypeString caseOf: {
  		[#'sqIntptr_t'] -> [interpreter objectMemory wordSize * 8].
  		[#sqLong] -> [64].
  		[#char] -> [^self bitAnd: 255]. "char may be signed, may be unsigned; interpret as unsigned by default"
  		[#'signed char'] -> [8].
  		[#'short'] -> [16].
  		[#int] -> [32].
  		[#long] -> [48]. "LLP64 on Windows :-("
  		[#'long long'] -> [64].
+ 		[#'wint_t'] -> [24]. "unsigned short on Windows; int elsewhere"
  		}
  		otherwise: [self error: 'unknown signed integer type name'].
  	^(self bitAnd: (1 bitShift: bits) - 1) - ((self bitAnd: (1 bitShift: bits - 1)) bitShift: 1)!

Item was changed:
  ----- Method: InterpreterPlugin>>expandDereferenceInterpreterProxyFunctionTable (in category 'initialize') -----
  expandDereferenceInterpreterProxyFunctionTable
  	"This is a dummy function that the VMPluginCodeGenerator expands into a
+ 	 sequence of assignments from interpreterProxy functions to local function pointers."
- 	 sequence of assignments from interpreterProxy funcitons to local function pointers."
  	<doNotGenerate>!

Item was changed:
  ----- Method: SpurMemoryManager>>printImmediateObject:on: (in category 'debug printing interpreter support') -----
  printImmediateObject: oop on: aStream
  	<var: 'aStream' type: #'FILE *'>
  	self assert: (self isImmediate: oop).
  	(self isIntegerObject: oop) ifTrue:
  		['16r%lx=%ld\n' f: aStream printf: {oop. (self integerValueOf: oop) asInteger}].
  	(self isImmediateCharacter: oop) ifTrue:
  		['16r%lx=$%ld ($%lc)\n' f: aStream printf: {oop.
  													(self characterValueOf: oop) asLong.
+ 													self cCoerce: (self characterValueOf: oop) to:  #'wint_t'}].
- 													self cCoerce: (self characterValueOf: oop) to: 'wint_t'}].
  	(self isImmediateFloat: oop) ifTrue:
  		['16r%lx=%g\n' f: aStream printf: {oop. self floatValueOf: oop}]!

Item was changed:
  ----- Method: StackInterpreter>>maybeMapPrimitiveFunctionPointerBackToSomethingEvaluable (in category 'primitive support') -----
  maybeMapPrimitiveFunctionPointerBackToSomethingEvaluable
  	"In the real VM primitiveFunctionPointer is either an index (for quick primitives)
  	 or a proper function pointer to a primitive.  In the simulator it may be a small
  	 index (corresponding to a quick primitive index), a symbol (corresponding to
  	 a function pointer) or an index into the externalPrimitiveTable, or an invalid
  	 address that references an evaluable in the simulatedTrampolines dictionary
  	 of the Cogit.  The simulator expects dispatchFunctionPointer to be called with
  	 primitiveFunctionPointer being a symbol only for internal primitives.  External
+ 	 primitives must have their function pointer mapped back to an index.  This
- 	 primitives must have their funciton pointer mapped back to an index.  This
  	 method does the reverse mapping."
  	<doNotGenerate>
  	(self isExternalPrimitiveCall: newMethod) ifTrue: "External prims must be evaluated by the right plugin..."
  		[| pfp index externalIndex |
  		 pfp := primitiveFunctionPointer.
  		 index := self mappedPluginEntries findFirst: [:entry| entry second == primitiveFunctionPointer].
  		 self assert: index ~= 0.
  		 externalIndex := 1000 + (externalPrimitiveTable object
  												indexOf: index
  												ifAbsent: [self error: 'entry not found']).
  		 self assert: ((self pluginEntryFor: externalIndex) notNil
  					   and: [(self pluginEntryFor: externalIndex) second == primitiveFunctionPointer]).
  		 primitiveFunctionPointer := externalIndex]!

Item was added:
+ ----- Method: StackInterpreter>>printCurrentBytecodeOn: (in category 'debug printing') -----
+ printCurrentBytecodeOn: aStream
+ 	<doNotGenerate>
+ 	aStream ensureCr.
+ 	(bytecodeSetSelector >= 256
+ 		ifTrue: ['%d\tALT %02X %s (%d)']
+ 		ifFalse: ['%d\t %02X %s (%d)'])
+ 			f: transcript
+ 			printf: {localIP - method - objectMemory bytesPerOop + 1.
+ 					currentBytecode.
+ 					BytecodeTable at: currentBytecode + 1.
+ 					self byteCount + 1 }.
+ 	transcript flush!

Item was removed:
- ----- Method: StackInterpreterSimulator>>printCurrentBytecodeOn: (in category 'debug printing') -----
- printCurrentBytecodeOn: aStream
- 	| code |
- 	code := currentBytecode radix: 16.
- 	aStream ensureCr; print: localIP - method - 3; tab.
- 	bytecodeSetSelector > 0 ifTrue:
- 		[aStream nextPutAll: 'ALT '].
- 	aStream
- 		nextPut: (code size < 2
- 					ifTrue: [$0]
- 					ifFalse: [code at: 1]);
- 		nextPut: code last; space;
- 		nextPutAll: (BytecodeTable at: currentBytecode + 1);
- 		space;
- 		nextPut: $(; print: byteCount + 1; nextPut: $);
- 		cr;
- 		flush!



More information about the Vm-dev mailing list