[Vm-dev] VM Maker: VMMaker.oscog-eem.2921.mcz
commits at source.squeak.org
commits at source.squeak.org
Mon Jan 4 02:20:15 UTC 2021
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.2921.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.2921
Author: eem
Time: 3 January 2021, 6:20:07.965503 pm
UUID: 43202b09-b01d-4ae5-8014-046b5fcead4f
Ancestors: VMMaker.oscog-eem.2920
CoInterpreterMT Simulation:
Provide a Mutex to lock the processor to ensure that simulation traps are executed atomically. Halt any other thread executing machine code while executing a leaf call so that tryLockVMOwnerTo: can run in its won thread without interference.
Make sure that flush flushes transcript output to the screen even if the TranscriptStream preference says otherwise.
=============== Diff against VMMaker.oscog-eem.2920 ===============
Item was changed:
----- Method: CogThreadManager>>tryLockVMOwnerTo: (in category 'simulation') -----
tryLockVMOwnerTo: threadIndex
"In the real VM this is a direct call of Cogit>>#tryLockVMOwnerTo:/ceTryLockVMOwner.
In the simulation this is where register state is saved and switched, simulaitng a thread switch.
releaseVM also saves register state. The code here and in registerState allow us to avoid the
expensive and complex MultiProcessor hack."
<doNotGenerate>
+ | result |
- | prior processor result |
self deny: threadIndex = 0.
+ cogit withProcessorHaltedDo:
+ [| prior processor |
+ processor := cogit processor.
+ prior := processor registerState.
+ "A thread switch would (have) occur(ed) if it were that the VM were owned other than by threadIndex"
+ vmOwner ~= threadIndex ifTrue:
+ [vmOwner ~= 0 ifTrue:
+ [registerStates at: vmOwner put: prior].
+ processor setRegisterState: (registerStates
+ at: threadIndex
+ ifAbsentPut:
+ [self ensureInitializedProcessor: processor forThreadIndex: threadIndex.
+ processor registerState])].
+ result := cogit tryLockVMOwnerTo: threadIndex.
+ self assert: result = (threadIndex = vmOwner).
+ registerStates at: threadIndex put: processor registerState.
+ threadIndex ~= vmOwner ifTrue: "the lock attempt failed; undo the (processor) thread switch."
+ [processor setRegisterState: prior]].
- processor := cogit processor.
- prior := processor registerState.
- "A thread switch would (have) occur(ed) if it were that the VM were owned other than by threadIndex"
- vmOwner ~= threadIndex ifTrue:
- [vmOwner ~= 0 ifTrue:
- [registerStates at: vmOwner put: prior].
- processor setRegisterState: (registerStates
- at: threadIndex
- ifAbsentPut:
- [self ensureInitializedProcessor: processor forThreadIndex: threadIndex.
- processor registerState])].
- result := cogit tryLockVMOwnerTo: threadIndex.
- self assert: result = (threadIndex = vmOwner).
- registerStates at: threadIndex put: processor registerState.
- threadIndex ~= vmOwner ifTrue: "the lock attempt failed; undo the (processor) thread switch."
- [processor setRegisterState: prior].
^result!
Item was changed:
----- Method: CogVMSimulator>>flush (in category 'debug printing') -----
flush
+ traceOn ifTrue:
+ [transcript flush.
+ "We *always* want to make output visible on flush"
+ TranscriptStream forceUpdate ifFalse:
+ [transcript changed: #appendEntry]]!
- <cmacro: '() fflush(stdout)'>
- traceOn ifTrue: [transcript flush]!
Item was changed:
CogClass subclass: #Cogit
(excessive size, no diff calculated)
Item was changed:
----- Method: Cogit>>handleCallOrJumpSimulationTrap: (in category 'simulation only') -----
handleCallOrJumpSimulationTrap: aProcessorSimulationTrap
<doNotGenerate>
| evaluable function memory result savedFramePointer savedStackPointer savedArgumentCount retpc |
+ "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"
[^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].
function ~~ #ceBaseFrameReturn: ifTrue:
[coInterpreter assertValidExternalStackPointers].
(backEnd wantsNearAddressFor: function) ifTrue:
[^self perform: function with: aProcessorSimulationTrap].
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].
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: [result == nil
or: [result == #continueNoReturn]]]]).
processor cResultRegister: (result
ifNil: [0]
ifNotNil: [result isInteger
ifTrue: [result]
ifFalse: [16rF00BA222]])!
Item was changed:
----- Method: Cogit>>initializeProcessor (in category 'initialization') -----
initializeProcessor
"Initialize the simulation processor, arranging that its initial stack is somewhere on the rump C stack."
<doNotGenerate>
guardPageSize := self class guardPageSize.
lastNInstructions := OrderedCollection new.
processor initializeStackFor: self.
self initializeProcessorStack: coInterpreter rumpCStackAddress.
coInterpreter setCFramePointer: processor fp setCStackPointer: processor sp.
(InitializationOptions at: #UseMultiProcessor ifAbsent: [false]) ifTrue:
+ [processor := MultiProcessor for: processor coInterpreter: coInterpreter].
+ processorLock := Mutex new!
- [processor := MultiProcessor for: processor coInterpreter: coInterpreter]!
Item was changed:
----- Method: Cogit>>simulateCogCodeAt: (in category 'simulation only') -----
simulateCogCodeAt: address "<Integer>"
<doNotGenerate>
| stackZoneBase |
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
- processor
singleStepIn: coInterpreter memory
minimumAddress: guardPageSize
+ readOnlyBelow: methodZone zoneEnd]]
- readOnlyBelow: methodZone zoneEnd]
ifFalse:
+ [processorLock critical:
+ [processor
- [processor
runInMemory: coInterpreter memory
minimumAddress: guardPageSize
+ readOnlyBelow: methodZone zoneEnd]].
- 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 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
- ^[[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]]]]
- processor
- singleStepIn: coInterpreter memory
- minimumAddress: guardPageSize
- readOnlyBelow: methodZone zoneEnd]
- ifFalse: [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 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>>tryLockVMOwnerTo: (in category 'multi-threading') -----
tryLockVMOwnerTo: value
<api>
"ceTryLockVMOwner does an atomic compare-and-swap of the vmOwner
variable with zero and the argument, setting vmOwner to value if it was
zero. It answers if the lock was zero and hence was acquired.
See CogThreadManager>>#tryLockVMOwnerTo: for the simulation of
processor thread switching which surrounds this method."
<cmacro: '(value) ceTryLockVMOwner(value)'>
+ | breakPCWasTrue |
+ (thisContext findContextSuchThat: [:ctxt| ctxt selector == #primitiveRelinquishProcessor]) ifNil:
+ [self halt].
+ (breakPCWasTrue := breakPC == true) ifTrue:
+ [breakPC := nil].
processor abiMarshalArg0: value in: objectMemory memory.
^[ | result |
result := self simulateLeafCallOf: ceTryLockVMOwner.
self assert: (result ~= 0) = (coInterpreter threadManager getVMOwner = value).
result ~= 0] ensure:
+ [processor abiUnmarshal: 1.
+ breakPCWasTrue ifTrue:
+ [breakPC := true]]!
- [processor abiUnmarshal: 1]!
Item was added:
+ ----- Method: Cogit>>withProcessorHaltedDo: (in category 'simulation processor access') -----
+ withProcessorHaltedDo: aBlock
+ ^processorLock critical:
+ [| oldBreakPC oldSingleStep |
+ oldBreakPC := breakPC.
+ oldSingleStep := singleStep.
+ breakPC := singleStep := true.
+ aBlock ensure:
+ [singleStep := oldSingleStep.
+ breakPC := oldBreakPC]]!
Item was changed:
----- Method: StackInterpreterSimulator>>flush (in category 'debug printing') -----
flush
+ traceOn ifTrue:
+ [transcript flush.
+ "We *always* want to make output visible on flush"
+ TranscriptStream forceUpdate ifFalse:
+ [transcript changed: #appendEntry]]!
- traceOn ifTrue: [transcript flush]!
More information about the Vm-dev
mailing list