[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