Branch: refs/heads/Cog
Home: https://github.com/OpenSmalltalk/opensmalltalk-vm
Commit: ac6ca33352554179b80a04283ce3de91d41261c6
https://github.com/OpenSmalltalk/opensmalltalk-vm/commit/ac6ca33352554179b8…
Author: Eliot Miranda <eliot.miranda(a)gmail.com>
Date: 2024-03-21 (Thu, 21 Mar 2024)
Changed paths:
M src/spur32.cog.lowcode/cointerp.c
M src/spur32.cog.lowcode/cointerp.h
M src/spur32.cog.lowcode/gcc3x-cointerp.c
M src/spur32.cog/cointerp.c
M src/spur32.cog/cointerp.h
M src/spur32.cog/cointerpmt.c
M src/spur32.cog/cointerpmt.h
M src/spur32.cog/gcc3x-cointerp.c
M src/spur32.cog/gcc3x-cointerpmt.c
M src/spur32.sista/cointerp.c
M src/spur32.sista/cointerp.h
M src/spur32.sista/gcc3x-cointerp.c
M src/spur32.stack.lowcode/gcc3x-interp.c
M src/spur32.stack.lowcode/interp.c
M src/spur32.stack/gcc3x-interp.c
M src/spur32.stack/interp.c
M src/spur32.stack/validImage.c
M src/spur64.cog.lowcode/cointerp.c
M src/spur64.cog.lowcode/cointerp.h
M src/spur64.cog.lowcode/gcc3x-cointerp.c
M src/spur64.cog/cointerp.c
M src/spur64.cog/cointerp.h
M src/spur64.cog/cointerpmt.c
M src/spur64.cog/cointerpmt.h
M src/spur64.cog/gcc3x-cointerp.c
M src/spur64.cog/gcc3x-cointerpmt.c
M src/spur64.sista/cointerp.c
M src/spur64.sista/cointerp.h
M src/spur64.sista/gcc3x-cointerp.c
M src/spur64.stack.lowcode/gcc3x-interp.c
M src/spur64.stack.lowcode/interp.c
M src/spur64.stack/gcc3x-interp.c
M src/spur64.stack/interp.c
M src/spur64.stack/validImage.c
M src/v3.cog/cointerp.c
M src/v3.cog/cointerp.h
M src/v3.cog/gcc3x-cointerp.c
M src/v3.stack/gcc3x-interp.c
M src/v3.stack/interp.c
Log Message:
-----------
CogVM source as per VMMaker.oscog-eem.3355
Fix checkImageHeaderFromBytes:AndSize:; the headerSize field in a header
is 32 bits, not wordSize.
To unsubscribe from these emails, change your notification settings at https://github.com/OpenSmalltalk/opensmalltalk-vm/settings/notifications
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.3355.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.3355
Author: eem
Time: 21 March 2024, 9:49:48.008968 pm
UUID: c384a960-5652-4118-b6ab-563e95c1bc74
Ancestors: VMMaker.oscog-eem.3354
Fix checkImageHeaderFromBytes:AndSize:; the headerSize field in a header is 32 bits, not wordSize.
=============== Diff against VMMaker.oscog-eem.3354 ===============
Item was changed:
----- Method: StackInterpreter>>checkImageHeaderFromBytes:AndSize: (in category 'image save/restore') -----
checkImageHeaderFromBytes: bytes AndSize: totalSize
"Support for embedded images. Check that the first few bytes of a potential header and answer if it
looks like something the VM can load,
The method checks the first three fields of the header (magic, header size & data size) & the total size.
The magic number should be correct.
The header size should be correct.
The size of the data should be at least as long as the headerSize plus the data size in the header"
<var: 'bytes' type: #'char *'>
<public>
| version headerSize dataSize |
"Need at least headerSize bytes; no point going further if not..."
totalSize < (objectMemory wordSize * 16) ifTrue:
[^false].
version := self long32At: bytes.
+ headerSize := self long32At: bytes + 4.
+ dataSize := self longAt: bytes + 8.
- headerSize := self longAt: bytes + 4.
- dataSize := self longAt: bytes + 4 + objectMemory wordSize.
(self readableFormat: version) ifFalse:
[(self readableFormat: version byteSwap32) ifFalse:
[^false].
headerSize := objectMemory byteSwapped: headerSize.
dataSize := objectMemory byteSwapped: dataSize].
^headerSize = objectMemory wordSize * 16
and: [totalSize >= (headerSize + dataSize)]!
Branch: refs/heads/Cog
Home: https://github.com/OpenSmalltalk/opensmalltalk-vm
Commit: 6641c9d5a25f94874b81efd267df123e56fc3d2c
https://github.com/OpenSmalltalk/opensmalltalk-vm/commit/6641c9d5a25f94874b…
Author: Eliot Miranda <eliot.miranda(a)gmail.com>
Date: 2024-03-21 (Thu, 21 Mar 2024)
Changed paths:
M src/spur32.cog.lowcode/cointerp.c
M src/spur32.cog.lowcode/cointerp.h
M src/spur32.cog.lowcode/gcc3x-cointerp.c
M src/spur32.cog/cointerp.c
M src/spur32.cog/cointerp.h
M src/spur32.cog/cointerpmt.c
M src/spur32.cog/cointerpmt.h
M src/spur32.cog/gcc3x-cointerp.c
M src/spur32.cog/gcc3x-cointerpmt.c
M src/spur32.sista/cointerp.c
M src/spur32.sista/cointerp.h
M src/spur32.sista/gcc3x-cointerp.c
M src/spur32.stack.lowcode/gcc3x-interp.c
M src/spur32.stack.lowcode/interp.c
M src/spur32.stack/gcc3x-interp.c
M src/spur32.stack/interp.c
M src/spur32.stack/validImage.c
M src/spur64.cog.lowcode/cointerp.c
M src/spur64.cog.lowcode/cointerp.h
M src/spur64.cog.lowcode/gcc3x-cointerp.c
M src/spur64.cog/cointerp.c
M src/spur64.cog/cointerp.h
M src/spur64.cog/cointerpmt.c
M src/spur64.cog/cointerpmt.h
M src/spur64.cog/gcc3x-cointerp.c
M src/spur64.cog/gcc3x-cointerpmt.c
M src/spur64.sista/cointerp.c
M src/spur64.sista/cointerp.h
M src/spur64.sista/gcc3x-cointerp.c
M src/spur64.stack.lowcode/gcc3x-interp.c
M src/spur64.stack.lowcode/interp.c
M src/spur64.stack/gcc3x-interp.c
M src/spur64.stack/interp.c
M src/spur64.stack/validImage.c
M src/v3.cog/cointerp.c
M src/v3.cog/cointerp.h
M src/v3.cog/gcc3x-cointerp.c
M src/v3.stack/gcc3x-interp.c
M src/v3.stack/interp.c
Log Message:
-----------
CogVM source as per VMMaker.oscog-eem.3354
Get rid of the vile hack that assigned multipleBytecodeSetsActive in the
function imageFormatVersionFromSnapshot: used from the testing readableImage:
(!!, this is really bad pratice ;-) ).
Refactor checkImageVersionFrom:startingAt: into
checkImageVersionFrom:startingAt:assignRawVersion: & bailOutOfImageLoad:,
decoupling the abort exit message from the image load check (the former
assumes a console VM, waiting for a carriage return, which is dubious).
To unsubscribe from these emails, change your notification settings at https://github.com/OpenSmalltalk/opensmalltalk-vm/settings/notifications
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.3354.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.3354
Author: eem
Time: 21 March 2024, 7:56:37.355946 pm
UUID: 67b625b4-ae15-4b1d-a144-7fd52510caf2
Ancestors: VMMaker.oscog-eem.3353
Get rid of the vile hack that assigned multipleBytecodeSetsActive in the function imageFormatVersionFromSnapshot: used from the testing readableImage: (!!, this is really bad pratice ;-) ).
Refactor checkImageVersionFrom:startingAt: into checkImageVersionFrom:startingAt:assignRawVersion: & bailOutOfImageLoad:, decoupling the abort exit message from the image load check (the former assumes a console VM, waiting for a carriage return, which is dubious).
Also lose RawBitsArray>>byteAt: which is now in trunk. I trust the implementation there-in is correct.
=============== Diff against VMMaker.oscog-eem.3353 ===============
Item was changed:
----- Method: CoInterpreter>>readImageFromFile:HeapSize:StartingAt: (in category 'image save/restore') -----
readImageFromFile: f HeapSize: desiredHeapSize StartingAt: imageOffset
"Read an image from the given file stream, allocating an amount of memory to its object heap.
V3: desiredHeapSize is the total size of the heap. Fail if the image has an unknown format or
requires more than the specified amount of memory.
Spur: desiredHeapSize is ignored; this routine will attempt to provide at least extraVMMemory's
ammount of free space after the image is loaded, taking any free space in the image into account.
extraVMMemory is stored in the image header and is accessible as vmParameterAt: 23. If
extraVMMemory is 0, the value defaults to the default grow headroom. Fail if the image has an
unknown format or if sufficient memory cannot be allocated.
Details: This method detects when the image was stored on a machine with the opposite byte
ordering from this machine and swaps the bytes automatically. Furthermore, it allows the header
information to start 512 bytes into the file, since some file transfer programs for the Macintosh
apparently prepend a Mac-specific header of this size. Note that this same 512 bytes of prefix
area could also be used to store an exec command on Unix systems, allowing one to launch
Smalltalk by invoking the image name as a command."
<var: 'f' type: #sqImageFile>
<var: 'desiredHeapSize' type: #usqInt>
<var: 'imageOffset' type: #squeakFileOffsetType>
+ | version rawVersion swapBytes headerStart headerSize headerFlags dataSize bytesRead bytesToShift heapSize
- | swapBytes headerStart headerSize headerFlags dataSize bytesRead bytesToShift heapSize
minimumMemory allocationReserve cogCodeBase firstSegSize
hdrNumStackPages hdrEdenBytes hdrCogCodeSize hdrMaxExtSemTabSize |
<var: 'heapSize' type: #usqInt>
<var: 'dataSize' type: #'size_t'>
<var: 'minimumMemory' type: #usqInt>
<var: 'allocationReserve' type: #usqInt>
<var: 'headerStart' type: #squeakFileOffsetType>
transcript := #stdout. "stdout is not available at compile time. this is the earliest available point."
metaclassNumSlots := 6. "guess Metaclass instSize"
classNameIndex := 6. "guess (Class instVarIndexFor: 'name' ifAbsent: []) - 1"
+ version := self checkImageVersionFrom: f startingAt: imageOffset assignRawVersion: (self addressOf: rawVersion put: [:v| rawVersion := v]).
+ version = 0 ifTrue:
+ [self bailOutOfImageLoad: rawVersion].
+ swapBytes := rawVersion ~= version.
+ multipleBytecodeSetsActive := version anyMask: MultipleBytecodeSetsBitmask.
- swapBytes := self checkImageVersionFrom: f startingAt: imageOffset.
headerStart := (self sqImageFilePosition: f) - 4. "record header start position"
headerSize := self getWord32FromFile: f swap: swapBytes.
dataSize := self getLongFromFile: f swap: swapBytes.
oldImageBaseAddress := self getLongFromFile: f swap: swapBytes.
objectMemory specialObjectsOop: (self getLongFromFile: f swap: swapBytes).
objectMemory lastHash: (self getLongFromFile: f swap: swapBytes). "N.B. not used by NewObjectMemory."
savedWindowSize := self getLongFromFile: f swap: swapBytes.
headerFlags := self getLongFromFile: f swap: swapBytes.
self setImageHeaderFlagsFrom: headerFlags.
extraVMMemory := self getWord32FromFile: f swap: swapBytes. "N.B. ignored in V3."
hdrNumStackPages := self getShortFromFile: f swap: swapBytes.
"4 stack pages is small. Should be able to run with as few as
three. 4 should be comfortable but slow. 8 is a reasonable
default. Can be changed via vmParameterAt: 43 put: n.
Can be set as a preference (Info.plist, VM.ini, command line etc).
If desiredNumStackPages is already non-zero then it has been
set as a preference. Ignore (but preserve) the header's default."
numStackPages := desiredNumStackPages ~= 0
ifTrue: [desiredNumStackPages]
ifFalse: [hdrNumStackPages = 0
ifTrue: [self defaultNumStackPages]
ifFalse: [hdrNumStackPages]].
desiredNumStackPages := hdrNumStackPages.
"This slot holds the size of the native method zone in 1k units. (pad to word boundary)."
hdrCogCodeSize := (self getShortFromFile: f swap: swapBytes) * 1024.
cogCodeSize := desiredCogCodeSize ~= 0
ifTrue: [desiredCogCodeSize]
ifFalse:
[hdrCogCodeSize = 0
ifTrue: [cogit defaultCogCodeSize]
ifFalse: [desiredCogCodeSize := hdrCogCodeSize]]. "set for vmParameter 47"
cogCodeSize > cogit maxCogCodeSize ifTrue:
[cogCodeSize := cogit maxCogCodeSize].
hdrEdenBytes := self getWord32FromFile: f swap: swapBytes.
objectMemory edenBytes: (desiredEdenBytes ~= 0
ifTrue: [desiredEdenBytes]
ifFalse:
[hdrEdenBytes = 0
ifTrue: [objectMemory defaultEdenBytes]
ifFalse: [hdrEdenBytes]]).
desiredEdenBytes := hdrEdenBytes.
hdrMaxExtSemTabSize := self getShortFromFile: f swap: swapBytes.
hdrMaxExtSemTabSize ~= 0 ifTrue:
[self setMaxExtSemSizeTo: hdrMaxExtSemTabSize].
the2ndUnknownShort := self getShortFromFile: f swap: swapBytes.
"Set maxLiteralCountForCompile unless it has already been set on the command line."
maxLiteralCountForCompile < 0 ifTrue:
[maxLiteralCountForCompile := the2ndUnknownShort ~= 0
ifTrue: [the2ndUnknownShort]
ifFalse: [MaxLiteralCountForCompile]].
firstSegSize := self getLongFromFile: f swap: swapBytes.
objectMemory firstSegmentSize: firstSegSize.
"compare memory requirements with availability"
allocationReserve := self interpreterAllocationReserveBytes.
minimumMemory := cogCodeSize "no need to include the stackZone; this is alloca'ed"
+ dataSize
+ objectMemory newSpaceBytes
+ allocationReserve.
"Compute how much space is needed for the initial heap allocation.
no need to include the stackZone; this is alloca'ed.
no need to include the JIT code zone size; this is allocated separately."
objectMemory hasSpurMemoryManagerAPI
ifTrue:
[| freeOldSpaceInImage headroom |
freeOldSpaceInImage := self getLongFromFile: f swap: swapBytes.
headroom := objectMemory
initialHeadroom: extraVMMemory
givenFreeOldSpaceInImage: freeOldSpaceInImage.
heapSize := objectMemory roundUpHeapSize:
dataSize
+ headroom
+ objectMemory newSpaceBytes
+ (headroom > allocationReserve
ifTrue: [0]
ifFalse: [allocationReserve])]
ifFalse:
[heapSize := desiredHeapSize
+ objectMemory newSpaceBytes
+ (desiredHeapSize - dataSize > allocationReserve
ifTrue: [0]
ifFalse: [allocationReserve]).
heapSize < minimumMemory ifTrue:
[self insufficientMemorySpecifiedError]].
"allocateJITMemory will assign the actual size allocated, which is rounded up to a page boundary."
cogCodeBase := (self allocateJITMemory: (self addressOf: cogCodeSize)) asInteger.
"allocate a contiguous block of memory for the Squeak heap and ancilliary data structures"
(self
allocateMemory: heapSize
minimum: minimumMemory
imageFile: f
headerSize: headerSize) asUnsignedInteger
ifNil: [self insufficientMemoryAvailableError]
ifNotNil:
[:mem| "cannot clash with the variable memory still in use in NewCoObjectMemory and superclasses"
objectMemory
setHeapBase: (heapBase := mem)
memoryLimit: mem + heapSize
endOfMemory: mem + dataSize].
"position file after the header"
self sqImageFile: f Seek: headerStart + headerSize.
"read in the image in bulk, then swap the bytes if necessary"
bytesRead := objectMemory readHeapFromImageFile: f dataBytes: dataSize.
bytesRead ~= dataSize ifTrue: [self unableToReadImageError].
self ensureImageFormatIsUpToDate: swapBytes.
"compute difference between old and new memory base addresses"
bytesToShift := objectMemory memoryBaseForImageRead - oldImageBaseAddress.
self initializeInterpreter: bytesToShift. "adjusts all oops to new location"
cogit initializeCodeZoneFrom: cogCodeBase upTo: cogCodeBase + cogCodeSize.
^dataSize!
Item was changed:
----- Method: CogVMSimulator>>openOn:extraMemory: (in category 'initialize-release') -----
openOn: fileName extraMemory: extraBytes
"CogVMSimulator new openOn: 'clone.im' extraMemory: 100000"
| f version headerSize dataSize count bytesToShift swapBytes
headerFlags firstSegSize heapSize
hdrNumStackPages hdrEdenBytes hdrMaxExtSemTabSize hdrCogCodeSize
stackZoneSize methodCacheSize primTraceLogSize allocationReserve |
"open image file and read the header"
(f := self openImageFileNamed: fileName) ifNil: [^self].
"Set the image name and the first argument; there are
no arguments during simulation unless set explicitly."
systemAttributes at: 1 put: fileName.
["begin ensure block..."
imageName := f fullName.
f binary.
+ version := self getWord32FromFile: f swap: false.
- version := self getWord32FromFile: f swap: false. "current version: 16r1968 (=6504) vive la revolucion!!"
(self readableFormat: version)
ifTrue: [swapBytes := false]
ifFalse: [(version := version byteSwap32) = self imageFormatVersion
ifTrue: [swapBytes := true]
ifFalse: [self error: 'incomaptible image format']].
+ multipleBytecodeSetsActive := version anyMask: MultipleBytecodeSetsBitmask.
headerSize := self getWord32FromFile: f swap: swapBytes.
dataSize := self getLongFromFile: f swap: swapBytes. "length of heap in file"
oldImageBaseAddress := self getLongFromFile: f swap: swapBytes. "object memory base address of image"
objectMemory specialObjectsOop: (self getLongFromFile: f swap: swapBytes).
objectMemory lastHash: (self getLongFromFile: f swap: swapBytes). "Should be loaded from, and saved to the image header"
savedWindowSize := self getLongFromFile: f swap: swapBytes.
headerFlags := self getLongFromFile: f swap: swapBytes.
self setImageHeaderFlagsFrom: headerFlags.
extraVMMemory := self getWord32FromFile: f swap: swapBytes.
hdrNumStackPages := self getShortFromFile: f swap: swapBytes.
"4 stack pages is small. Should be able to run with as few as
three. 4 should be comfortable but slow. 8 is a reasonable
default. Can be changed via vmParameterAt: 43 put: n"
numStackPages := desiredNumStackPages ~= 0
ifTrue: [desiredNumStackPages]
ifFalse: [hdrNumStackPages = 0
ifTrue: [self defaultNumStackPages]
ifFalse: [hdrNumStackPages]].
desiredNumStackPages := hdrNumStackPages.
stackZoneSize := self computeStackZoneSize.
"This slot holds the size of the native method zone in 1k units. (pad to word boundary)."
hdrCogCodeSize := (self getShortFromFile: f swap: swapBytes) * 1024.
cogCodeSize := desiredCogCodeSize ~= 0
ifTrue: [desiredCogCodeSize]
ifFalse:
[hdrCogCodeSize = 0
ifTrue: [cogit defaultCogCodeSize]
ifFalse: [hdrCogCodeSize]].
desiredCogCodeSize := hdrCogCodeSize.
self assert: f position = (objectMemory wordSize = 4 ifTrue: [40] ifFalse: [64]).
hdrEdenBytes := self getWord32FromFile: f swap: swapBytes.
objectMemory edenBytes: (desiredEdenBytes ~= 0
ifTrue: [desiredEdenBytes]
ifFalse:
[hdrEdenBytes = 0
ifTrue: [objectMemory defaultEdenBytes]
ifFalse: [hdrEdenBytes]]).
desiredEdenBytes := hdrEdenBytes.
hdrMaxExtSemTabSize := self getShortFromFile: f swap: swapBytes.
hdrMaxExtSemTabSize ~= 0 ifTrue:
[self setMaxExtSemSizeTo: hdrMaxExtSemTabSize].
the2ndUnknownShort := self getShortFromFile: f swap: swapBytes.
"Set maxLiteralCountForCompile unless it has already been set on the command line."
maxLiteralCountForCompile < 0 ifTrue:
[maxLiteralCountForCompile := the2ndUnknownShort ~= 0
ifTrue: [the2ndUnknownShort]
ifFalse: [MaxLiteralCountForCompile]].
self assert: f position = (objectMemory wordSize = 4 ifTrue: [48] ifFalse: [72]).
firstSegSize := self getLongFromFile: f swap: swapBytes.
objectMemory firstSegmentSize: firstSegSize.
"For Open PICs to be able to probe the method cache during
simulation the methodCache must be relocated to memory."
methodCacheSize := methodCache size * objectMemory wordSize.
primTraceLogSize := primTraceLog size * objectMemory wordSize.
"To cope with modern OSs that disallow executing code in writable memory we dual-map
the code zone, one mapping with read/write permissions and the other with read/execute
permissions. In simulation all we can do is use memory, so if we're simulating dual mapping
we use double the memory and simulate the memory sharing in the Cogit's backEnd."
effectiveCogCodeSize := (InitializationOptions at: #DUAL_MAPPED_CODE_ZONE ifAbsent: [false])
ifTrue: [cogCodeSize * 2 + Cogit guardPageSize]
ifFalse: [cogCodeSize].
"allocate interpreter memory. This list is in address order, low to high.
In the actual VM the stack zone exists on the C stack."
heapBase := (Cogit guardPageSize
+ effectiveCogCodeSize
+ stackZoneSize
+ methodCacheSize
+ primTraceLogSize
+ self rumpCStackSize) roundUpTo: objectMemory allocationUnit.
"compare memory requirements with availability"
allocationReserve := self interpreterAllocationReserveBytes.
objectMemory hasSpurMemoryManagerAPI
ifTrue:
[| freeOldSpaceInImage headroom |
freeOldSpaceInImage := self getLongFromFile: f swap: swapBytes.
headroom := objectMemory
initialHeadroom: extraVMMemory
givenFreeOldSpaceInImage: freeOldSpaceInImage.
heapSize := objectMemory roundUpHeapSize:
dataSize
+ headroom
+ objectMemory newSpaceBytes
+ (headroom > allocationReserve
ifTrue: [0]
ifFalse: [allocationReserve])]
ifFalse:
[heapSize := dataSize
+ extraBytes
+ objectMemory newSpaceBytes
+ (extraBytes > allocationReserve
ifTrue: [0]
ifFalse: [allocationReserve])].
"allocate interpreter memory"
heapBase := objectMemory
setHeapBase: heapBase
memoryLimit: heapBase + heapSize
endOfMemory: heapBase + dataSize. "bogus for Spur"
self assert: cogCodeSize \\ 4 = 0.
self assert: objectMemory memoryLimit \\ 4 = 0.
self assert: self rumpCStackSize \\ 4 = 0.
objectMemory allocateMemoryOfSize: objectMemory memoryLimit.
"read in the image in bulk, then swap the bytes if necessary"
f position: headerSize.
count := objectMemory readHeapFromImageFile: f dataBytes: dataSize.
count ~= dataSize ifTrue: [self halt]]
ensure: [f close].
self moveMethodCacheToMemoryAt: objectMemory cogCodeBase + effectiveCogCodeSize + stackZoneSize.
self movePrimTraceLogToMemoryAt: objectMemory cogCodeBase + effectiveCogCodeSize + stackZoneSize + methodCacheSize.
self ensureImageFormatIsUpToDate: swapBytes.
bytesToShift := objectMemory memoryBaseForImageRead - oldImageBaseAddress. "adjust pointers for zero base address"
UIManager default
informUser: 'Relocating object pointers...'
during: [self initializeInterpreter: bytesToShift].
cogit
initializeCodeZoneFrom: Cogit guardPageSize
upTo: Cogit guardPageSize + cogCodeSize!
Item was removed:
- ----- Method: RawBitsArray>>byteAt: (in category '*VMMaker-simulation') -----
- byteAt: anInteger
- "emulate an access to raw (unsigned) bytes, as if the receiver was a ByteArray"
-
- | element p |
- p := self bytesPerBasicElement.
- p = 1 ifTrue: [^self basicAt: 1].
- element := self basicAt: anInteger + p - 1 // p.
- ^Smalltalk isLittleEndian
- ifTrue: [element digitAt: anInteger - 1 \\ p + 1]
- ifFalse: [element digitAt: p - (anInteger \\ p)]
- !
Item was added:
+ ----- Method: StackDepthFinder>>methodReturnSpecialConstant: (in category 'instruction decoding implicit literals') -----
+ methodReturnSpecialConstant: aConstant
+ ^self methodReturnConstant: aConstant!
Item was added:
+ ----- Method: StackInterpreter>>bailOutOfImageLoad: (in category 'image save/restore') -----
+ bailOutOfImageLoad: rawVersion
+ "Abort on image load, because of invalid version info, etc, with an error message"
+ self print: 'This interpreter (vers. '.
+ self printNum: self imageFormatVersion.
+ self print: ') cannot read image file (vers. '.
+ self printNum: rawVersion.
+ self print: ').'.
+ self cr.
+ self print: 'Press CR to quit...'.
+ self getchar.
+ self ioExitWithErrorCode: 1.
+ ^false!
Item was removed:
- ----- Method: StackInterpreter>>checkImageVersionFrom:startingAt: (in category 'image save/restore') -----
- checkImageVersionFrom: f startingAt: imageOffset
- "Read and verify the image file version number and return true if the the given image file needs to be byte-swapped. As a side effect, position the file stream just after the version number of the image header. This code prints a warning and does a hard-exit if it cannot find a valid version number."
- "This code is based on C code by Ian Piumarta."
-
- <inline: false>
- | version firstVersion |
- <var: #f type: #sqImageFile>
- <var: #imageOffset type: #squeakFileOffsetType>
-
- "check the version number"
- self sqImageFile: f Seek: imageOffset.
- version := firstVersion := self getWord32FromFile: f swap: false.
- (self readableFormat: version) ifTrue: [^ false].
-
- "try with bytes reversed"
- self sqImageFile: f Seek: imageOffset.
- version := self getWord32FromFile: f swap: true.
- (self readableFormat: version) ifTrue: [^ true].
-
- "Note: The following is only meaningful if not reading an embedded image"
- imageOffset = 0 ifTrue:[
- "try skipping the first 512 bytes (prepended by certain Mac file transfer utilities)"
- self sqImageFile: f Seek: 512.
- version := self getWord32FromFile: f swap: false.
- (self readableFormat: version) ifTrue: [^ false].
-
- "try skipping the first 512 bytes with bytes reversed"
- self sqImageFile: f Seek: 512.
- version := self getWord32FromFile: f swap: true.
- (self readableFormat: version) ifTrue: [^ true]].
-
- "hard failure; abort"
- self print: 'This interpreter (vers. '.
- self printNum: self imageFormatVersion.
- self print: ') cannot read image file (vers. '.
- self printNum: firstVersion.
- self print: ').'.
- self cr.
- self print: 'Press CR to quit...'.
- self getchar.
- self ioExitWithErrorCode: 1.
- ^false!
Item was added:
+ ----- Method: StackInterpreter>>checkImageVersionFrom:startingAt:assignRawVersion: (in category 'image save/restore') -----
+ checkImageVersionFrom: f startingAt: imageOffset assignRawVersion: rawVersionPtr
+ "Read and verify the image file version number and answer it.
+ Assign through rawVersionPtr the unswapped version number. The caller can then
+ infer if the given image file needs to be byte-swapped by seeing if the returned value
+ equals tha assigned through rawVersionPtr.
+ 0 is answered if no valid version could be found."
+
+ <inline: false>
+ | version |
+ <var: 'f' type: #sqImageFile>
+ <var: 'imageOffset' type: #squeakFileOffsetType>
+ <var: 'rawVersionPtr' type: #'sqInt *'>
+
+ "check the version number"
+ self sqImageFile: f Seek: imageOffset.
+ version := self getWord32FromFile: f swap: false.
+ rawVersionPtr at: 0 put: version.
+ (self readableFormat: version) ifTrue:
+ [^version].
+
+ "try with bytes reversed"
+ self sqImageFile: f Seek: imageOffset.
+ version := self getWord32FromFile: f swap: true.
+ (self readableFormat: version) ifTrue:
+ [^version].
+
+ "Note: The following is only meaningful if not reading an embedded image"
+ imageOffset = 0 ifTrue:
+ "try skipping the first 512 bytes (prepended by certain Mac file transfer utilities)"
+ [self sqImageFile: f Seek: 512.
+ version := self getWord32FromFile: f swap: false.
+ rawVersionPtr at: 0 put: version.
+ (self readableFormat: version) ifTrue:
+ [^version].
+
+ "try skipping the first 512 bytes with bytes reversed"
+ self sqImageFile: f Seek: 512.
+ version := self getWord32FromFile: f swap: true.
+ (self readableFormat: version) ifTrue:
+ [^version]].
+
+ ^0!
Item was changed:
----- Method: StackInterpreter>>imageFormatVersionFromSnapshot: (in category 'image save/restore') -----
imageFormatVersionFromSnapshot: imageVersion
+ "Snapshot image format can include additional state flags.
+ Currently that is the state of multipleBytecodeSetsActive.
+ Mask it out when checking compatibility with this interpreter."
- "Snapshot image format includes the state of multipleBytecodeSetsActive,
- mask it out when checking compatibility with this interpreter"
+ ^imageVersion bitClear: MultipleBytecodeSetsBitmask!
- multipleBytecodeSetsActive := (imageVersion bitAnd: MultipleBytecodeSetsBitmask) ~= 0.
- ^imageVersion bitAnd: ( -1 - MultipleBytecodeSetsBitmask)
- !
Item was changed:
----- Method: StackInterpreter>>readImageFromFile:HeapSize:StartingAt: (in category 'image save/restore') -----
readImageFromFile: f HeapSize: desiredHeapSize StartingAt: imageOffset
"Read an image from the given file stream, allocating an amount of memory to its object heap.
V3: desiredHeapSize is the total size of the heap. Fail if the image has an unknown format or
requires more than the specified amount of memory.
Spur: desiredHeapSize is ignored; this routine will attempt to provide at least extraVMMemory's
ammount of free space after the image is loaded, taking any free space in teh image into account.
extraVMMemory is stored in the image header and is accessible as vmParameterAt: 23. If
extraVMMemory is 0, the value defaults to the default grow headroom. Fail if the image has an
unknown format or if sufficient memory cannot be allocated.
Details: This method detects when the image was stored on a machine with the opposite byte
ordering from this machine and swaps the bytes automatically. Furthermore, it allows the header
information to start 512 bytes into the file, since some file transfer programs for the Macintosh
apparently prepend a Mac-specific header of this size. Note that this same 512 bytes of prefix
area could also be used to store an exec command on Unix systems, allowing one to launch
Smalltalk by invoking the image name as a command."
+ | version rawVersion headerStart headerSize headerFlags dataSize swapBytes
- | headerStart headerSize headerFlags dataSize swapBytes
minimumMemory bytesRead bytesToShift heapSize firstSegSize
hdrEdenBytes hdrMaxExtSemTabSize hdrNumStackPages allocationReserve |
<var: 'f' type: #sqImageFile>
<var: 'heapSize' type: #usqInt>
<var: 'dataSize' type: #'size_t'>
<var: 'minimumMemory' type: #usqInt>
<var: 'desiredHeapSize' type: #usqInt>
<var: 'allocationReserve' type: #usqInt>
<var: 'headerStart' type: #squeakFileOffsetType>
<var: 'imageOffset' type: #squeakFileOffsetType>
transcript := #stdout. "stdout is not available at compile time. this is the earliest available point."
metaclassNumSlots := 6. "guess Metaclass instSize"
classNameIndex := 6. "guess (Class instVarIndexFor: 'name' ifAbsent: []) - 1"
+
+ version := self checkImageVersionFrom: f startingAt: imageOffset assignRawVersion: (self addressOf: rawVersion put: [:v| rawVersion := v]).
+ version = 0 ifTrue:
+ [self bailOutOfImageLoad: rawVersion].
+ swapBytes := rawVersion ~= version.
+ multipleBytecodeSetsActive := version anyMask: MultipleBytecodeSetsBitmask.
- swapBytes := self checkImageVersionFrom: f startingAt: imageOffset.
headerStart := (self sqImageFilePosition: f) - 4. "record header start position"
headerSize := self getWord32FromFile: f swap: swapBytes.
dataSize := self getLongFromFile: f swap: swapBytes.
oldImageBaseAddress := self getLongFromFile: f swap: swapBytes.
objectMemory specialObjectsOop: (self getLongFromFile: f swap: swapBytes).
objectMemory lastHash: (self getLongFromFile: f swap: swapBytes). "N.B. not used by NewObjectMemory."
savedWindowSize := self getLongFromFile: f swap: swapBytes.
headerFlags := self getLongFromFile: f swap: swapBytes.
self setImageHeaderFlagsFrom: headerFlags.
extraVMMemory := self getWord32FromFile: f swap: swapBytes.
hdrNumStackPages := self getShortFromFile: f swap: swapBytes.
"4 stack pages is small. Should be able to run with as few as
three. 4 should be comfortable but slow. 8 is a reasonable
default. Can be changed via vmParameterAt: 43 put: n.
Can be set as a preference (Info.plist, VM.ini, command line etc).
If desiredNumStackPages is already non-zero then it has been
set as a preference. Ignore (but preserve) the header's default."
numStackPages := desiredNumStackPages ~= 0
ifTrue: [desiredNumStackPages]
ifFalse: [hdrNumStackPages = 0
ifTrue: [self defaultNumStackPages]
ifFalse: [hdrNumStackPages]].
desiredNumStackPages := hdrNumStackPages.
"pad to word boundary. This slot can be used for anything else that will fit in 16 bits.
It is used for the cog code size in Cog. Preserve it to be polite to other VMs."
theUnknownShort := self getShortFromFile: f swap: swapBytes.
hdrEdenBytes := self getWord32FromFile: f swap: swapBytes.
objectMemory edenBytes: (desiredEdenBytes ~= 0
ifTrue: [desiredEdenBytes]
ifFalse:
[hdrEdenBytes = 0
ifTrue: [objectMemory defaultEdenBytes]
ifFalse: [hdrEdenBytes]]).
desiredEdenBytes := hdrEdenBytes.
hdrMaxExtSemTabSize := self getShortFromFile: f swap: swapBytes.
hdrMaxExtSemTabSize ~= 0 ifTrue:
[self setMaxExtSemSizeTo: hdrMaxExtSemTabSize].
"pad to word boundary. This slot can be used for anything else that will fit in 16 bits.
Preserve it to be polite to other VMs."
the2ndUnknownShort := self getShortFromFile: f swap: swapBytes.
firstSegSize := self getLongFromFile: f swap: swapBytes.
objectMemory firstSegmentSize: firstSegSize.
"compare memory requirements with availability"
allocationReserve := self interpreterAllocationReserveBytes.
minimumMemory := dataSize
+ objectMemory newSpaceBytes
+ allocationReserve.
objectMemory hasSpurMemoryManagerAPI
ifTrue:
[| freeOldSpaceInImage headroom |
freeOldSpaceInImage := self getLongFromFile: f swap: swapBytes.
headroom := objectMemory
initialHeadroom: extraVMMemory
givenFreeOldSpaceInImage: freeOldSpaceInImage.
heapSize := objectMemory roundUpHeapSize:
dataSize
+ headroom
+ objectMemory newSpaceBytes
+ (headroom > allocationReserve
ifTrue: [0]
ifFalse: [allocationReserve])]
ifFalse:
[heapSize := desiredHeapSize
+ objectMemory newSpaceBytes
+ (desiredHeapSize - dataSize > allocationReserve
ifTrue: [0]
ifFalse: [allocationReserve]).
heapSize < minimumMemory ifTrue:
[self insufficientMemorySpecifiedError]].
"allocate a contiguous block of memory for the Squeak heap"
(self
allocateMemory: heapSize
minimum: minimumMemory
imageFile: f
headerSize: headerSize) asUnsignedInteger
ifNil: [self insufficientMemoryAvailableError]
ifNotNil:
[:mem|
objectMemory
setHeapBase: mem
memoryLimit: mem + heapSize
endOfMemory: mem + dataSize].
"position file after the header"
self sqImageFile: f Seek: headerStart + headerSize.
"read in the image in bulk, then swap the bytes if necessary"
bytesRead := objectMemory readHeapFromImageFile: f dataBytes: dataSize.
bytesRead ~= dataSize ifTrue: [self unableToReadImageError].
self ensureImageFormatIsUpToDate: swapBytes.
"compute difference between old and new memory base addresses"
bytesToShift := objectMemory memoryBaseForImageRead - oldImageBaseAddress.
self initializeInterpreter: bytesToShift. "adjusts all oops to new location"
^dataSize!
Item was changed:
----- Method: StackInterpreterSimulator>>openOn:extraMemory: (in category 'initialize-release') -----
openOn: fileName extraMemory: extraBytes
"StackInterpreterSimulator new openOn: 'clone.im' extraMemory: 100000"
| f version headerSize dataSize count bytesToShift swapBytes
headerFlags heapBase firstSegSize heapSize
hdrNumStackPages hdrEdenBytes hdrMaxExtSemTabSize allocationReserve |
"open image file and read the header"
(f := self openImageFileNamed: fileName) ifNil: [^self].
"Set the image name and the first argument; there are
no arguments during simulation unless set explicitly."
systemAttributes at: 1 put: fileName.
["begin ensure block..."
imageName := f fullName.
f binary.
+ version := self getWord32FromFile: f swap: false.
- version := self getWord32FromFile: f swap: false. "current version: 16r1968 (=6504) vive la revolucion!!"
(self readableFormat: version)
ifTrue: [swapBytes := false]
ifFalse: [(version := version byteSwap32) = self imageFormatVersion
ifTrue: [swapBytes := true]
ifFalse: [self error: 'incomaptible image format']].
+ multipleBytecodeSetsActive := version anyMask: MultipleBytecodeSetsBitmask.
headerSize := self getWord32FromFile: f swap: swapBytes.
dataSize := self getLongFromFile: f swap: swapBytes. "length of heap in file"
oldImageBaseAddress := self getLongFromFile: f swap: swapBytes. "object memory base address of image"
objectMemory specialObjectsOop: (self getLongFromFile: f swap: swapBytes).
objectMemory lastHash: (self getLongFromFile: f swap: swapBytes). "Should be loaded from, and saved to the image header"
savedWindowSize := self getLongFromFile: f swap: swapBytes.
headerFlags := self getLongFromFile: f swap: swapBytes.
self setImageHeaderFlagsFrom: headerFlags.
extraVMMemory := self getWord32FromFile: f swap: swapBytes.
hdrNumStackPages := self getShortFromFile: f swap: swapBytes.
"4 stack pages is small. Should be able to run with as few as
three. 4 should be comfortable but slow. 8 is a reasonable
default. Can be changed via vmParameterAt: 43 put: n"
numStackPages := desiredNumStackPages ~= 0
ifTrue: [desiredNumStackPages]
ifFalse: [hdrNumStackPages = 0
ifTrue: [self defaultNumStackPages]
ifFalse: [hdrNumStackPages]].
desiredNumStackPages := hdrNumStackPages.
"pad to word boundary. This slot can be used for anything else that will fit in 16 bits.
It is used for the cog code size in Cog. Preserve it to be polite to other VMs."
theUnknownShort := self getShortFromFile: f swap: swapBytes.
self assert: f position = (objectMemory wordSize = 4 ifTrue: [40] ifFalse: [64]).
hdrEdenBytes := self getWord32FromFile: f swap: swapBytes.
objectMemory edenBytes: (hdrEdenBytes = 0
ifTrue: [objectMemory defaultEdenBytes]
ifFalse: [hdrEdenBytes]).
desiredEdenBytes := hdrEdenBytes.
hdrMaxExtSemTabSize := self getShortFromFile: f swap: swapBytes.
hdrMaxExtSemTabSize ~= 0 ifTrue:
[self setMaxExtSemSizeTo: hdrMaxExtSemTabSize].
"pad to word boundary. This slot can be used for anything else that will fit in 16 bits.
Preserve it to be polite to other VMs."
the2ndUnknownShort := self getShortFromFile: f swap: swapBytes.
self assert: f position = (objectMemory wordSize = 4 ifTrue: [48] ifFalse: [72]).
firstSegSize := self getLongFromFile: f swap: swapBytes.
objectMemory firstSegmentSize: firstSegSize.
"compare memory requirements with availability"
allocationReserve := self interpreterAllocationReserveBytes.
objectMemory hasSpurMemoryManagerAPI
ifTrue:
[| freeOldSpaceInImage headroom |
freeOldSpaceInImage := self getLongFromFile: f swap: swapBytes.
headroom := objectMemory
initialHeadroom: extraVMMemory
givenFreeOldSpaceInImage: freeOldSpaceInImage.
heapSize := objectMemory roundUpHeapSize:
dataSize
+ headroom
+ objectMemory newSpaceBytes
+ (headroom > allocationReserve
ifTrue: [0]
ifFalse: [allocationReserve])]
ifFalse:
[heapSize := dataSize
+ extraBytes
+ objectMemory newSpaceBytes
+ (extraBytes > allocationReserve
ifTrue: [0]
ifFalse: [allocationReserve])].
"allocate interpreter memory"
heapBase := objectMemory
setHeapBase: objectMemory startOfMemory
memoryLimit: objectMemory startOfMemory + heapSize
endOfMemory: objectMemory startOfMemory + dataSize. "bogus for Spur"
objectMemory allocateMemoryOfSize: objectMemory memoryLimit.
"read in the image in bulk, then swap the bytes if necessary"
f position: headerSize.
count := objectMemory readHeapFromImageFile: f dataBytes: dataSize.
count ~= dataSize ifTrue: [self halt]]
ensure: [f close].
self ensureImageFormatIsUpToDate: swapBytes.
bytesToShift := objectMemory memoryBaseForImageRead - oldImageBaseAddress. "adjust pointers for zero base address"
UIManager default
informUser: 'Relocating object pointers...'
during: [self initializeInterpreter: bytesToShift]!
Branch: refs/heads/Cog
Home: https://github.com/OpenSmalltalk/opensmalltalk-vm
Commit: eb34a5eda187ebc8786ac01fa7dd3276665a00f0
https://github.com/OpenSmalltalk/opensmalltalk-vm/commit/eb34a5eda187ebc878…
Author: Eliot Miranda <eliot.miranda(a)gmail.com>
Date: 2024-03-21 (Thu, 21 Mar 2024)
Changed paths:
M platforms/Cross/vm/sqImageFileAccessViaStdio.h
M platforms/unix/vm/sqImageFileAccess.h
M platforms/win32/vm/sqImageFileAccess.h
M src/spur32.cog.lowcode/cointerp.c
M src/spur32.cog.lowcode/cointerp.h
M src/spur32.cog.lowcode/gcc3x-cointerp.c
M src/spur32.cog/cointerp.c
M src/spur32.cog/cointerp.h
M src/spur32.cog/cointerpmt.c
M src/spur32.cog/cointerpmt.h
M src/spur32.cog/gcc3x-cointerp.c
M src/spur32.cog/gcc3x-cointerpmt.c
M src/spur32.sista/cointerp.c
M src/spur32.sista/cointerp.h
M src/spur32.sista/gcc3x-cointerp.c
M src/spur32.stack.lowcode/gcc3x-interp.c
M src/spur32.stack.lowcode/interp.c
M src/spur32.stack/gcc3x-interp.c
M src/spur32.stack/interp.c
M src/spur32.stack/validImage.c
M src/spur64.cog.lowcode/cointerp.c
M src/spur64.cog.lowcode/cointerp.h
M src/spur64.cog.lowcode/gcc3x-cointerp.c
M src/spur64.cog/cointerp.c
M src/spur64.cog/cointerp.h
M src/spur64.cog/cointerpmt.c
M src/spur64.cog/cointerpmt.h
M src/spur64.cog/gcc3x-cointerp.c
M src/spur64.cog/gcc3x-cointerpmt.c
M src/spur64.sista/cointerp.c
M src/spur64.sista/cointerp.h
M src/spur64.sista/gcc3x-cointerp.c
M src/spur64.stack.lowcode/gcc3x-interp.c
M src/spur64.stack.lowcode/interp.c
M src/spur64.stack/gcc3x-interp.c
M src/spur64.stack/interp.c
M src/spur64.stack/validImage.c
M src/v3.cog/cointerp.c
M src/v3.cog/cointerp.h
M src/v3.cog/gcc3x-cointerp.c
M src/v3.stack/gcc3x-interp.c
M src/v3.stack/interp.c
Log Message:
-----------
CogVM source as per VMMaker.oscog-eem.3353
VMMaker side of embedded images.
Provide a function checkImageHeaderFromBytes:AndSize: that can be used
by the platform to identify likely embedded image candidates.
Answer vmParameterAt: 50 with two boolean flags (encoded in an integer)
of whether the image loaded with swizzling or not, and whether the image
is embedded or not.
Add a default definition for sqImageFileIsEmbedded
Commit: 6c62b74a10127c1849880f5f36d86a0130fac5c2
https://github.com/OpenSmalltalk/opensmalltalk-vm/commit/6c62b74a10127c1849…
Author: Eliot Miranda <eliot.miranda(a)gmail.com>
Date: 2024-03-21 (Thu, 21 Mar 2024)
Changed paths:
M platforms/minheadless/common/sqImageFileAccessViaStdio.h
M platforms/minheadless/unix/sqImageFileAccess.h
M platforms/minheadless/windows/sqImageFileAccess.h
Log Message:
-----------
...and add the minheadless sqImageFileAccess.h definitions.
Compare: https://github.com/OpenSmalltalk/opensmalltalk-vm/compare/7c142515cb3e...6c…
To unsubscribe from these emails, change your notification settings at https://github.com/OpenSmalltalk/opensmalltalk-vm/settings/notifications
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.3353.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.3353
Author: eem
Time: 21 March 2024, 2:01:43.293285 pm
UUID: 860ea747-484d-4072-8047-3b45e4687175
Ancestors: VMMaker.oscog-dtl.3352
VMMaker side of embedded images.
Provide a function checkImageHeaderFromBytes:AndSize: that can be used by the platform to identify likely embedded image candidates.
Answer vmParameterAt: 50 with two boolean flags (encoded in an integer) of whether the image loaded with swizzling or not, and whether the image is embedded or not.
=============== Diff against VMMaker.oscog-dtl.3352 ===============
Item was changed:
----- Method: CoInterpreter>>readImageFromFile:HeapSize:StartingAt: (in category 'image save/restore') -----
readImageFromFile: f HeapSize: desiredHeapSize StartingAt: imageOffset
"Read an image from the given file stream, allocating an amount of memory to its object heap.
V3: desiredHeapSize is the total size of the heap. Fail if the image has an unknown format or
requires more than the specified amount of memory.
Spur: desiredHeapSize is ignored; this routine will attempt to provide at least extraVMMemory's
ammount of free space after the image is loaded, taking any free space in the image into account.
extraVMMemory is stored in the image header and is accessible as vmParameterAt: 23. If
extraVMMemory is 0, the value defaults to the default grow headroom. Fail if the image has an
unknown format or if sufficient memory cannot be allocated.
Details: This method detects when the image was stored on a machine with the opposite byte
ordering from this machine and swaps the bytes automatically. Furthermore, it allows the header
information to start 512 bytes into the file, since some file transfer programs for the Macintosh
apparently prepend a Mac-specific header of this size. Note that this same 512 bytes of prefix
area could also be used to store an exec command on Unix systems, allowing one to launch
Smalltalk by invoking the image name as a command."
<var: 'f' type: #sqImageFile>
<var: 'desiredHeapSize' type: #usqInt>
<var: 'imageOffset' type: #squeakFileOffsetType>
| swapBytes headerStart headerSize headerFlags dataSize bytesRead bytesToShift heapSize
+ minimumMemory allocationReserve cogCodeBase firstSegSize
- oldBaseAddr minimumMemory allocationReserve cogCodeBase firstSegSize
hdrNumStackPages hdrEdenBytes hdrCogCodeSize hdrMaxExtSemTabSize |
<var: 'heapSize' type: #usqInt>
<var: 'dataSize' type: #'size_t'>
<var: 'minimumMemory' type: #usqInt>
<var: 'allocationReserve' type: #usqInt>
<var: 'headerStart' type: #squeakFileOffsetType>
transcript := #stdout. "stdout is not available at compile time. this is the earliest available point."
metaclassNumSlots := 6. "guess Metaclass instSize"
classNameIndex := 6. "guess (Class instVarIndexFor: 'name' ifAbsent: []) - 1"
swapBytes := self checkImageVersionFrom: f startingAt: imageOffset.
headerStart := (self sqImageFilePosition: f) - 4. "record header start position"
+ headerSize := self getWord32FromFile: f swap: swapBytes.
+ dataSize := self getLongFromFile: f swap: swapBytes.
+ oldImageBaseAddress := self getLongFromFile: f swap: swapBytes.
- headerSize := self getWord32FromFile: f swap: swapBytes.
- dataSize := self getLongFromFile: f swap: swapBytes.
- oldBaseAddr := self getLongFromFile: f swap: swapBytes.
objectMemory specialObjectsOop: (self getLongFromFile: f swap: swapBytes).
+ objectMemory lastHash: (self getLongFromFile: f swap: swapBytes). "N.B. not used by NewObjectMemory."
+ savedWindowSize := self getLongFromFile: f swap: swapBytes.
+ headerFlags := self getLongFromFile: f swap: swapBytes.
- objectMemory lastHash: (self getLongFromFile: f swap: swapBytes). "N.B. not used."
- savedWindowSize := self getLongFromFile: f swap: swapBytes.
- headerFlags := self getLongFromFile: f swap: swapBytes.
self setImageHeaderFlagsFrom: headerFlags.
+ extraVMMemory := self getWord32FromFile: f swap: swapBytes. "N.B. ignored in V3."
+ hdrNumStackPages := self getShortFromFile: f swap: swapBytes.
- extraVMMemory := self getWord32FromFile: f swap: swapBytes. "N.B. ignored in V3."
- hdrNumStackPages := self getShortFromFile: f swap: swapBytes.
"4 stack pages is small. Should be able to run with as few as
three. 4 should be comfortable but slow. 8 is a reasonable
default. Can be changed via vmParameterAt: 43 put: n.
Can be set as a preference (Info.plist, VM.ini, command line etc).
If desiredNumStackPages is already non-zero then it has been
set as a preference. Ignore (but preserve) the header's default."
numStackPages := desiredNumStackPages ~= 0
ifTrue: [desiredNumStackPages]
ifFalse: [hdrNumStackPages = 0
ifTrue: [self defaultNumStackPages]
ifFalse: [hdrNumStackPages]].
desiredNumStackPages := hdrNumStackPages.
"This slot holds the size of the native method zone in 1k units. (pad to word boundary)."
hdrCogCodeSize := (self getShortFromFile: f swap: swapBytes) * 1024.
cogCodeSize := desiredCogCodeSize ~= 0
ifTrue: [desiredCogCodeSize]
ifFalse:
[hdrCogCodeSize = 0
ifTrue: [cogit defaultCogCodeSize]
ifFalse: [desiredCogCodeSize := hdrCogCodeSize]]. "set for vmParameter 47"
cogCodeSize > cogit maxCogCodeSize ifTrue:
[cogCodeSize := cogit maxCogCodeSize].
hdrEdenBytes := self getWord32FromFile: f swap: swapBytes.
objectMemory edenBytes: (desiredEdenBytes ~= 0
ifTrue: [desiredEdenBytes]
ifFalse:
[hdrEdenBytes = 0
ifTrue: [objectMemory defaultEdenBytes]
ifFalse: [hdrEdenBytes]]).
desiredEdenBytes := hdrEdenBytes.
hdrMaxExtSemTabSize := self getShortFromFile: f swap: swapBytes.
hdrMaxExtSemTabSize ~= 0 ifTrue:
[self setMaxExtSemSizeTo: hdrMaxExtSemTabSize].
the2ndUnknownShort := self getShortFromFile: f swap: swapBytes.
"Set maxLiteralCountForCompile unless it has already been set on the command line."
maxLiteralCountForCompile < 0 ifTrue:
[maxLiteralCountForCompile := the2ndUnknownShort ~= 0
ifTrue: [the2ndUnknownShort]
ifFalse: [MaxLiteralCountForCompile]].
firstSegSize := self getLongFromFile: f swap: swapBytes.
objectMemory firstSegmentSize: firstSegSize.
"compare memory requirements with availability"
allocationReserve := self interpreterAllocationReserveBytes.
minimumMemory := cogCodeSize "no need to include the stackZone; this is alloca'ed"
+ dataSize
+ objectMemory newSpaceBytes
+ allocationReserve.
"Compute how much space is needed for the initial heap allocation.
no need to include the stackZone; this is alloca'ed.
no need to include the JIT code zone size; this is allocated separately."
objectMemory hasSpurMemoryManagerAPI
ifTrue:
[| freeOldSpaceInImage headroom |
freeOldSpaceInImage := self getLongFromFile: f swap: swapBytes.
headroom := objectMemory
initialHeadroom: extraVMMemory
givenFreeOldSpaceInImage: freeOldSpaceInImage.
heapSize := objectMemory roundUpHeapSize:
dataSize
+ headroom
+ objectMemory newSpaceBytes
+ (headroom > allocationReserve
ifTrue: [0]
ifFalse: [allocationReserve])]
ifFalse:
[heapSize := desiredHeapSize
+ objectMemory newSpaceBytes
+ (desiredHeapSize - dataSize > allocationReserve
ifTrue: [0]
ifFalse: [allocationReserve]).
heapSize < minimumMemory ifTrue:
[self insufficientMemorySpecifiedError]].
"allocateJITMemory will assign the actual size allocated, which is rounded up to a page boundary."
cogCodeBase := (self allocateJITMemory: (self addressOf: cogCodeSize)) asInteger.
"allocate a contiguous block of memory for the Squeak heap and ancilliary data structures"
(self
allocateMemory: heapSize
minimum: minimumMemory
imageFile: f
headerSize: headerSize) asUnsignedInteger
ifNil: [self insufficientMemoryAvailableError]
ifNotNil:
[:mem| "cannot clash with the variable memory still in use in NewCoObjectMemory and superclasses"
objectMemory
setHeapBase: (heapBase := mem)
memoryLimit: mem + heapSize
endOfMemory: mem + dataSize].
"position file after the header"
self sqImageFile: f Seek: headerStart + headerSize.
"read in the image in bulk, then swap the bytes if necessary"
bytesRead := objectMemory readHeapFromImageFile: f dataBytes: dataSize.
bytesRead ~= dataSize ifTrue: [self unableToReadImageError].
self ensureImageFormatIsUpToDate: swapBytes.
"compute difference between old and new memory base addresses"
+ bytesToShift := objectMemory memoryBaseForImageRead - oldImageBaseAddress.
- bytesToShift := objectMemory memoryBaseForImageRead - oldBaseAddr.
self initializeInterpreter: bytesToShift. "adjusts all oops to new location"
cogit initializeCodeZoneFrom: cogCodeBase upTo: cogCodeBase + cogCodeSize.
^dataSize!
Item was changed:
----- Method: CogVMSimulator>>openOn:extraMemory: (in category 'initialize-release') -----
openOn: fileName extraMemory: extraBytes
"CogVMSimulator new openOn: 'clone.im' extraMemory: 100000"
+ | f version headerSize dataSize count bytesToShift swapBytes
- | f version headerSize dataSize count oldBaseAddr bytesToShift swapBytes
headerFlags firstSegSize heapSize
hdrNumStackPages hdrEdenBytes hdrMaxExtSemTabSize hdrCogCodeSize
stackZoneSize methodCacheSize primTraceLogSize allocationReserve |
"open image file and read the header"
(f := self openImageFileNamed: fileName) ifNil: [^self].
"Set the image name and the first argument; there are
no arguments during simulation unless set explicitly."
systemAttributes at: 1 put: fileName.
["begin ensure block..."
imageName := f fullName.
f binary.
version := self getWord32FromFile: f swap: false. "current version: 16r1968 (=6504) vive la revolucion!!"
(self readableFormat: version)
ifTrue: [swapBytes := false]
ifFalse: [(version := version byteSwap32) = self imageFormatVersion
ifTrue: [swapBytes := true]
ifFalse: [self error: 'incomaptible image format']].
headerSize := self getWord32FromFile: f swap: swapBytes.
dataSize := self getLongFromFile: f swap: swapBytes. "length of heap in file"
+ oldImageBaseAddress := self getLongFromFile: f swap: swapBytes. "object memory base address of image"
- oldBaseAddr := self getLongFromFile: f swap: swapBytes. "object memory base address of image"
objectMemory specialObjectsOop: (self getLongFromFile: f swap: swapBytes).
objectMemory lastHash: (self getLongFromFile: f swap: swapBytes). "Should be loaded from, and saved to the image header"
savedWindowSize := self getLongFromFile: f swap: swapBytes.
headerFlags := self getLongFromFile: f swap: swapBytes.
self setImageHeaderFlagsFrom: headerFlags.
extraVMMemory := self getWord32FromFile: f swap: swapBytes.
hdrNumStackPages := self getShortFromFile: f swap: swapBytes.
"4 stack pages is small. Should be able to run with as few as
three. 4 should be comfortable but slow. 8 is a reasonable
default. Can be changed via vmParameterAt: 43 put: n"
numStackPages := desiredNumStackPages ~= 0
ifTrue: [desiredNumStackPages]
ifFalse: [hdrNumStackPages = 0
ifTrue: [self defaultNumStackPages]
ifFalse: [hdrNumStackPages]].
desiredNumStackPages := hdrNumStackPages.
stackZoneSize := self computeStackZoneSize.
"This slot holds the size of the native method zone in 1k units. (pad to word boundary)."
hdrCogCodeSize := (self getShortFromFile: f swap: swapBytes) * 1024.
cogCodeSize := desiredCogCodeSize ~= 0
ifTrue: [desiredCogCodeSize]
ifFalse:
[hdrCogCodeSize = 0
ifTrue: [cogit defaultCogCodeSize]
ifFalse: [hdrCogCodeSize]].
desiredCogCodeSize := hdrCogCodeSize.
self assert: f position = (objectMemory wordSize = 4 ifTrue: [40] ifFalse: [64]).
hdrEdenBytes := self getWord32FromFile: f swap: swapBytes.
objectMemory edenBytes: (desiredEdenBytes ~= 0
ifTrue: [desiredEdenBytes]
ifFalse:
[hdrEdenBytes = 0
ifTrue: [objectMemory defaultEdenBytes]
ifFalse: [hdrEdenBytes]]).
desiredEdenBytes := hdrEdenBytes.
hdrMaxExtSemTabSize := self getShortFromFile: f swap: swapBytes.
hdrMaxExtSemTabSize ~= 0 ifTrue:
[self setMaxExtSemSizeTo: hdrMaxExtSemTabSize].
the2ndUnknownShort := self getShortFromFile: f swap: swapBytes.
"Set maxLiteralCountForCompile unless it has already been set on the command line."
maxLiteralCountForCompile < 0 ifTrue:
[maxLiteralCountForCompile := the2ndUnknownShort ~= 0
ifTrue: [the2ndUnknownShort]
ifFalse: [MaxLiteralCountForCompile]].
self assert: f position = (objectMemory wordSize = 4 ifTrue: [48] ifFalse: [72]).
firstSegSize := self getLongFromFile: f swap: swapBytes.
objectMemory firstSegmentSize: firstSegSize.
"For Open PICs to be able to probe the method cache during
simulation the methodCache must be relocated to memory."
methodCacheSize := methodCache size * objectMemory wordSize.
primTraceLogSize := primTraceLog size * objectMemory wordSize.
"To cope with modern OSs that disallow executing code in writable memory we dual-map
the code zone, one mapping with read/write permissions and the other with read/execute
permissions. In simulation all we can do is use memory, so if we're simulating dual mapping
we use double the memory and simulate the memory sharing in the Cogit's backEnd."
effectiveCogCodeSize := (InitializationOptions at: #DUAL_MAPPED_CODE_ZONE ifAbsent: [false])
ifTrue: [cogCodeSize * 2 + Cogit guardPageSize]
ifFalse: [cogCodeSize].
"allocate interpreter memory. This list is in address order, low to high.
In the actual VM the stack zone exists on the C stack."
heapBase := (Cogit guardPageSize
+ effectiveCogCodeSize
+ stackZoneSize
+ methodCacheSize
+ primTraceLogSize
+ self rumpCStackSize) roundUpTo: objectMemory allocationUnit.
"compare memory requirements with availability"
allocationReserve := self interpreterAllocationReserveBytes.
objectMemory hasSpurMemoryManagerAPI
ifTrue:
[| freeOldSpaceInImage headroom |
freeOldSpaceInImage := self getLongFromFile: f swap: swapBytes.
headroom := objectMemory
initialHeadroom: extraVMMemory
givenFreeOldSpaceInImage: freeOldSpaceInImage.
heapSize := objectMemory roundUpHeapSize:
dataSize
+ headroom
+ objectMemory newSpaceBytes
+ (headroom > allocationReserve
ifTrue: [0]
ifFalse: [allocationReserve])]
ifFalse:
[heapSize := dataSize
+ extraBytes
+ objectMemory newSpaceBytes
+ (extraBytes > allocationReserve
ifTrue: [0]
ifFalse: [allocationReserve])].
"allocate interpreter memory"
heapBase := objectMemory
setHeapBase: heapBase
memoryLimit: heapBase + heapSize
endOfMemory: heapBase + dataSize. "bogus for Spur"
self assert: cogCodeSize \\ 4 = 0.
self assert: objectMemory memoryLimit \\ 4 = 0.
self assert: self rumpCStackSize \\ 4 = 0.
objectMemory allocateMemoryOfSize: objectMemory memoryLimit.
"read in the image in bulk, then swap the bytes if necessary"
f position: headerSize.
count := objectMemory readHeapFromImageFile: f dataBytes: dataSize.
count ~= dataSize ifTrue: [self halt]]
ensure: [f close].
self moveMethodCacheToMemoryAt: objectMemory cogCodeBase + effectiveCogCodeSize + stackZoneSize.
self movePrimTraceLogToMemoryAt: objectMemory cogCodeBase + effectiveCogCodeSize + stackZoneSize + methodCacheSize.
self ensureImageFormatIsUpToDate: swapBytes.
+ bytesToShift := objectMemory memoryBaseForImageRead - oldImageBaseAddress. "adjust pointers for zero base address"
- bytesToShift := objectMemory memoryBaseForImageRead - oldBaseAddr. "adjust pointers for zero base address"
UIManager default
informUser: 'Relocating object pointers...'
during: [self initializeInterpreter: bytesToShift].
cogit
initializeCodeZoneFrom: Cogit guardPageSize
upTo: Cogit guardPageSize + cogCodeSize!
Item was removed:
- ----- Method: NewObjectMemory>>baseAddressOfImage (in category 'snapshot') -----
- baseAddressOfImage
- "Answer the base address of the image data written to a sapshot."
- ^self startOfMemory!
Item was added:
+ ----- Method: NewObjectMemory>>baseAddressOfImageSnapshot (in category 'snapshot') -----
+ baseAddressOfImageSnapshot
+ "Answer the base address of the image data written to a sapshot."
+ ^self startOfMemory!
Item was removed:
- ----- Method: SpurMemoryManager>>baseAddressOfImage (in category 'snapshot') -----
- baseAddressOfImage
- ^oldSpaceStart!
Item was added:
+ ----- Method: SpurMemoryManager>>baseAddressOfImageSnapshot (in category 'snapshot') -----
+ baseAddressOfImageSnapshot
+ ^oldSpaceStart!
Item was changed:
InterpreterPrimitives subclass: #StackInterpreter
(excessive size, no diff calculated)
Item was added:
+ ----- Method: StackInterpreter>>checkImageHeaderFromBytes:AndSize: (in category 'image save/restore') -----
+ checkImageHeaderFromBytes: bytes AndSize: totalSize
+ "Support for embedded images. Check that the first few bytes of a potential header and answer if it
+ looks like something the VM can load,
+ The method checks the first three fields of the header (magic, header size & data size) & the total size.
+ The magic number should be correct.
+ The header size should be correct.
+ The size of the data should be at least as long as the headerSize plus the data size in the header"
+
+ <var: 'bytes' type: #'char *'>
+ <public>
+ | version headerSize dataSize |
+ "Need at least headerSize bytes; no point going further if not..."
+ totalSize < (objectMemory wordSize * 16) ifTrue:
+ [^false].
+
+ version := self long32At: bytes.
+ headerSize := self longAt: bytes + 4.
+ dataSize := self longAt: bytes + 4 + objectMemory wordSize.
+
+ (self readableFormat: version) ifFalse:
+ [(self readableFormat: version byteSwap32) ifFalse:
+ [^false].
+ headerSize := objectMemory byteSwapped: headerSize.
+ dataSize := objectMemory byteSwapped: dataSize].
+
+ ^headerSize = objectMemory wordSize * 16
+ and: [totalSize >= (headerSize + dataSize)]!
Item was added:
+ ----- Method: StackInterpreter>>imageLoadFlags (in category 'image save/restore') -----
+ imageLoadFlags
+ "Answer the current image load flags (vm parameter 51).
+ Bit 0: image needed swizzling on load
+ Bit 1: image is embedded (e.g. as a resource) in this VM"
+ ^(oldImageBaseAddress ~= objectMemory baseAddressOfImageSnapshot
+ ifTrue: [1]
+ ifFalse: [0])
+ + (self sqImageFileIsEmbedded
+ ifTrue: [2]
+ ifFalse: [0])!
Item was changed:
----- Method: StackInterpreter>>readImageFromFile:HeapSize:StartingAt: (in category 'image save/restore') -----
readImageFromFile: f HeapSize: desiredHeapSize StartingAt: imageOffset
"Read an image from the given file stream, allocating an amount of memory to its object heap.
V3: desiredHeapSize is the total size of the heap. Fail if the image has an unknown format or
requires more than the specified amount of memory.
Spur: desiredHeapSize is ignored; this routine will attempt to provide at least extraVMMemory's
ammount of free space after the image is loaded, taking any free space in teh image into account.
extraVMMemory is stored in the image header and is accessible as vmParameterAt: 23. If
extraVMMemory is 0, the value defaults to the default grow headroom. Fail if the image has an
unknown format or if sufficient memory cannot be allocated.
Details: This method detects when the image was stored on a machine with the opposite byte
ordering from this machine and swaps the bytes automatically. Furthermore, it allows the header
information to start 512 bytes into the file, since some file transfer programs for the Macintosh
apparently prepend a Mac-specific header of this size. Note that this same 512 bytes of prefix
area could also be used to store an exec command on Unix systems, allowing one to launch
Smalltalk by invoking the image name as a command."
+ | headerStart headerSize headerFlags dataSize swapBytes
- | headerStart headerSize headerFlags dataSize oldBaseAddr swapBytes
minimumMemory bytesRead bytesToShift heapSize firstSegSize
hdrEdenBytes hdrMaxExtSemTabSize hdrNumStackPages allocationReserve |
<var: 'f' type: #sqImageFile>
<var: 'heapSize' type: #usqInt>
<var: 'dataSize' type: #'size_t'>
<var: 'minimumMemory' type: #usqInt>
<var: 'desiredHeapSize' type: #usqInt>
<var: 'allocationReserve' type: #usqInt>
<var: 'headerStart' type: #squeakFileOffsetType>
<var: 'imageOffset' type: #squeakFileOffsetType>
transcript := #stdout. "stdout is not available at compile time. this is the earliest available point."
metaclassNumSlots := 6. "guess Metaclass instSize"
classNameIndex := 6. "guess (Class instVarIndexFor: 'name' ifAbsent: []) - 1"
swapBytes := self checkImageVersionFrom: f startingAt: imageOffset.
headerStart := (self sqImageFilePosition: f) - 4. "record header start position"
+ headerSize := self getWord32FromFile: f swap: swapBytes.
+ dataSize := self getLongFromFile: f swap: swapBytes.
+ oldImageBaseAddress := self getLongFromFile: f swap: swapBytes.
- headerSize := self getWord32FromFile: f swap: swapBytes.
- dataSize := self getLongFromFile: f swap: swapBytes.
- oldBaseAddr := self getLongFromFile: f swap: swapBytes.
objectMemory specialObjectsOop: (self getLongFromFile: f swap: swapBytes).
+ objectMemory lastHash: (self getLongFromFile: f swap: swapBytes). "N.B. not used by NewObjectMemory."
+ savedWindowSize := self getLongFromFile: f swap: swapBytes.
+ headerFlags := self getLongFromFile: f swap: swapBytes.
- objectMemory lastHash: (self getLongFromFile: f swap: swapBytes). "N.B. not used."
- savedWindowSize := self getLongFromFile: f swap: swapBytes.
- headerFlags := self getLongFromFile: f swap: swapBytes.
self setImageHeaderFlagsFrom: headerFlags.
+ extraVMMemory := self getWord32FromFile: f swap: swapBytes.
+ hdrNumStackPages := self getShortFromFile: f swap: swapBytes.
- extraVMMemory := self getWord32FromFile: f swap: swapBytes.
- hdrNumStackPages := self getShortFromFile: f swap: swapBytes.
"4 stack pages is small. Should be able to run with as few as
three. 4 should be comfortable but slow. 8 is a reasonable
default. Can be changed via vmParameterAt: 43 put: n.
Can be set as a preference (Info.plist, VM.ini, command line etc).
If desiredNumStackPages is already non-zero then it has been
set as a preference. Ignore (but preserve) the header's default."
numStackPages := desiredNumStackPages ~= 0
ifTrue: [desiredNumStackPages]
ifFalse: [hdrNumStackPages = 0
ifTrue: [self defaultNumStackPages]
ifFalse: [hdrNumStackPages]].
desiredNumStackPages := hdrNumStackPages.
"pad to word boundary. This slot can be used for anything else that will fit in 16 bits.
It is used for the cog code size in Cog. Preserve it to be polite to other VMs."
theUnknownShort := self getShortFromFile: f swap: swapBytes.
hdrEdenBytes := self getWord32FromFile: f swap: swapBytes.
objectMemory edenBytes: (desiredEdenBytes ~= 0
ifTrue: [desiredEdenBytes]
ifFalse:
[hdrEdenBytes = 0
ifTrue: [objectMemory defaultEdenBytes]
ifFalse: [hdrEdenBytes]]).
desiredEdenBytes := hdrEdenBytes.
hdrMaxExtSemTabSize := self getShortFromFile: f swap: swapBytes.
hdrMaxExtSemTabSize ~= 0 ifTrue:
[self setMaxExtSemSizeTo: hdrMaxExtSemTabSize].
"pad to word boundary. This slot can be used for anything else that will fit in 16 bits.
Preserve it to be polite to other VMs."
the2ndUnknownShort := self getShortFromFile: f swap: swapBytes.
firstSegSize := self getLongFromFile: f swap: swapBytes.
objectMemory firstSegmentSize: firstSegSize.
"compare memory requirements with availability"
allocationReserve := self interpreterAllocationReserveBytes.
minimumMemory := dataSize
+ objectMemory newSpaceBytes
+ allocationReserve.
objectMemory hasSpurMemoryManagerAPI
ifTrue:
[| freeOldSpaceInImage headroom |
freeOldSpaceInImage := self getLongFromFile: f swap: swapBytes.
headroom := objectMemory
initialHeadroom: extraVMMemory
givenFreeOldSpaceInImage: freeOldSpaceInImage.
heapSize := objectMemory roundUpHeapSize:
dataSize
+ headroom
+ objectMemory newSpaceBytes
+ (headroom > allocationReserve
ifTrue: [0]
ifFalse: [allocationReserve])]
ifFalse:
[heapSize := desiredHeapSize
+ objectMemory newSpaceBytes
+ (desiredHeapSize - dataSize > allocationReserve
ifTrue: [0]
ifFalse: [allocationReserve]).
heapSize < minimumMemory ifTrue:
[self insufficientMemorySpecifiedError]].
"allocate a contiguous block of memory for the Squeak heap"
(self
allocateMemory: heapSize
minimum: minimumMemory
imageFile: f
headerSize: headerSize) asUnsignedInteger
ifNil: [self insufficientMemoryAvailableError]
ifNotNil:
[:mem|
objectMemory
setHeapBase: mem
memoryLimit: mem + heapSize
endOfMemory: mem + dataSize].
"position file after the header"
self sqImageFile: f Seek: headerStart + headerSize.
"read in the image in bulk, then swap the bytes if necessary"
bytesRead := objectMemory readHeapFromImageFile: f dataBytes: dataSize.
bytesRead ~= dataSize ifTrue: [self unableToReadImageError].
self ensureImageFormatIsUpToDate: swapBytes.
"compute difference between old and new memory base addresses"
+ bytesToShift := objectMemory memoryBaseForImageRead - oldImageBaseAddress.
- bytesToShift := objectMemory memoryBaseForImageRead - oldBaseAddr.
self initializeInterpreter: bytesToShift. "adjusts all oops to new location"
^dataSize!
Item was added:
+ ----- Method: StackInterpreter>>sqImageFileIsEmbedded (in category 'simulation support') -----
+ sqImageFileIsEmbedded
+ "Simulation place holder for something defined more meaningfully by the platform subsystem"
+ <doNotGenerate>
+ ^false!
Item was changed:
----- Method: StackInterpreter>>writeImageFileIO (in category 'image save/restore') -----
writeImageFileIO
"Write the image header and heap contents to imageFile for snapshot. c.f. writeImageFileIOSimulation.
The game below is to maintain 64-bit alignment for all putLong:toFile: occurrences."
<inline: #never>
| imageName headerStart headerSize f imageBytes bytesWritten sCWIfn okToWrite |
<var: 'f' type: #sqImageFile>
<var: 'headerStart' type: #squeakFileOffsetType>
<var: 'sCWIfn' type: #'void *'>
<var: 'imageName' declareC: 'extern char imageName[]'>
self cCode: [] inSmalltalk: [imageName := 'sooth compiler'. ^self writeImageFileIOSimulation].
"If the security plugin can be loaded, use it to check for write permission.
If not, assume it's ok"
sCWIfn := self ioLoadFunction: 'secCanWriteImage' From: 'SecurityPlugin'.
sCWIfn ~= 0 ifTrue:
[okToWrite := self cCode: '((sqInt (*)(void))sCWIfn)()'.
okToWrite ifFalse:[^self primitiveFail]].
"local constants"
headerStart := 0.
headerSize := objectMemory wordSize * 16. "64 or 128; header size in bytes; do not change!!"
f := self sqImageFile: imageName Open: 'wb'.
(self invalidSqImageFile: f) ifTrue: "could not open the image file for writing"
[^self primitiveFailFor: PrimErrOperationFailed].
imageBytes := objectMemory imageSizeToWrite.
headerStart := self sqImage: f File: imageName StartLocation: headerSize + imageBytes.
self cCode: '/* Note: on Unix systems one could put an exec command here, padded to 512 bytes */'.
"position file to start of header"
self sqImageFile: f Seek: headerStart.
self putWord32: self imageFormatVersionForSnapshot toFile: f.
self putWord32: headerSize toFile: f.
self putLong: imageBytes toFile: f.
+ self putLong: objectMemory baseAddressOfImageSnapshot toFile: f.
- self putLong: objectMemory baseAddressOfImage toFile: f.
self putLong: objectMemory specialObjectsOop toFile: f.
self putLong: objectMemory newObjectHash toFile: f.
self putLong: self getSnapshotScreenSize toFile: f.
self putLong: self getImageHeaderFlags toFile: f.
self putWord32: extraVMMemory toFile: f.
self putShort: desiredNumStackPages toFile: f.
self putShort: self unknownShortOrCodeSizeInKs toFile: f.
self putWord32: desiredEdenBytes toFile: f.
self putShort: (maxExtSemTabSizeSet ifTrue: [self ioGetMaxExtSemTableSize] ifFalse: [0]) toFile: f.
self putShort: the2ndUnknownShort toFile: f.
objectMemory hasSpurMemoryManagerAPI
ifTrue:
[self putLong: objectMemory firstSegmentBytes toFile: f.
self putLong: objectMemory bytesLeftInOldSpace toFile: f.
2 timesRepeat: [self putLong: 0 toFile: f] "Pad the rest of the header."]
ifFalse:
[4 timesRepeat: [self putLong: 0 toFile: f]]. "Pad the rest of the header."
objectMemory wordSize = 8 ifTrue:
[3 timesRepeat: [self putLong: 0 toFile: f]]. "Pad the rest of the header."
self assert: headerStart + headerSize = (self sqImageFilePosition: f).
"position file after the header"
self sqImageFile: f Seek: headerStart + headerSize.
self successful ifFalse: "file write or seek failure"
[self sqImageFileClose: f.
^nil].
"write the image data"
objectMemory hasSpurMemoryManagerAPI
ifTrue:
[bytesWritten := objectMemory writeImageSegmentsToFile: f]
ifFalse:
+ [bytesWritten := self sq: (self pointerForOop: objectMemory baseAddressOfImageSnapshot)
- [bytesWritten := self sq: (self pointerForOop: objectMemory baseAddressOfImage)
Image: (self sizeof: #char)
File: imageBytes
Write: f].
self success: bytesWritten = imageBytes.
self sqImageFileClose: f!
Item was changed:
----- Method: StackInterpreter>>writeImageFileIOSimulation (in category 'image save/restore') -----
writeImageFileIOSimulation
"Write the image header and heap contents to imageFile for snapshot.
c.f. writeImageFileIO. The game below is to maintain 64-bit alignment
for all putLong:toFile: occurrences."
<doNotGenerate>
| headerSize file |
headerSize := objectMemory wordSize * 16.
(file := FileStream fileNamed: self imageName) ifNil:
[self primitiveFail.
^nil].
[file binary.
self putWord32: self imageFormatVersion toFile: file.
self putWord32: headerSize toFile: file.
{
objectMemory imageSizeToWrite.
+ objectMemory baseAddressOfImageSnapshot.
- objectMemory baseAddressOfImage.
objectMemory specialObjectsOop.
objectMemory lastHash.
self ioScreenSize.
self getImageHeaderFlags
}
do: [:long | self putLong: long toFile: file].
self putWord32: (extraVMMemory ifNil: [0]) toFile: file.
{ desiredNumStackPages. self unknownShortOrCodeSizeInKs } do:
[:short| self putShort: short toFile: file].
self putWord32: desiredEdenBytes toFile: file.
{ maxExtSemTabSizeSet ifTrue: [self ioGetMaxExtSemTableSize] ifFalse: [0]. 0 } do:
[:short| self putShort: short toFile: file].
objectMemory hasSpurMemoryManagerAPI
ifTrue:
[| bytesWritten |
self putLong: objectMemory firstSegmentBytes toFile: file.
self putLong: objectMemory bytesLeftInOldSpace toFile: file.
2 timesRepeat: [self putLong: 0 toFile: file] "Pad the rest of the header.".
objectMemory wordSize = 8 ifTrue:
[3 timesRepeat: [self putLong: 0 toFile: file]].
self assert: file position = headerSize.
bytesWritten := objectMemory writeImageSegmentsToFile: file.
self assert: bytesWritten = objectMemory imageSizeToWrite]
ifFalse:
["Pad the rest of the header."
4 timesRepeat: [self putLong: 0 toFile: file].
objectMemory wordSize = 8 ifTrue:
[3 timesRepeat: [self putLong: 0 toFile: file]].
self assert: file position = headerSize.
"Write the object memory."
file
next: objectMemory imageSizeToWrite // objectMemory memory bytesPerElement
putAll: objectMemory memory
+ startingAt: objectMemory baseAddressOfImageSnapshot - objectMemory memoryOffset // objectMemory memory bytesPerElement].
- startingAt: objectMemory baseAddressOfImage - objectMemory memoryOffset // objectMemory memory bytesPerElement].
file truncate: file position.
self success: true]
ensure: [file ifNotNil: [file close]]!
Item was changed:
----- Method: StackInterpreterPrimitives>>primitiveAllVMParameters: (in category 'system control primitives') -----
(excessive size, no diff calculated)
Item was changed:
----- Method: StackInterpreterPrimitives>>primitiveGetVMParameter: (in category 'system control primitives') -----
primitiveGetVMParameter: arg
"See primitiveVMParameter method comment.
N.B. written as a returning case to avoid branch limits in the V3 bytecode set."
arg caseOf: {
[1] -> [^self positiveMachineIntegerFor: objectMemory oldSpaceSize].
[2] -> [^objectMemory integerObjectOf: objectMemory newSpaceSize].
[3] -> [^self positiveMachineIntegerFor: objectMemory totalMemorySize].
[6] -> [^objectMemory integerObjectOf: objectMemory tenuringThreshold].
[7] -> [^objectMemory integerObjectOf: objectMemory statFullGCs].
[8] -> [^objectMemory integerObjectOf: objectMemory statFullGCUsecs + 500 // 1000].
[9] -> [^objectMemory integerObjectOf: (objectMemory hasSpurMemoryManagerAPI
ifTrue: [objectMemory statScavenges]
ifFalse: [objectMemory statIncrGCs])].
[10] -> [^objectMemory integerObjectOf: (objectMemory hasSpurMemoryManagerAPI
ifTrue: [objectMemory statScavengeGCUsecs]
ifFalse: [objectMemory statIncrGCUsecs]) + 500 // 1000].
[11] -> [^objectMemory integerObjectOf: objectMemory statTenures].
[12] -> [^objectMemory integerObjectOf: eventTraceMask].
[13] -> [^self getVMTickerStartUSecs].
[14] -> [^self getVMTickerCount].
[15] -> [^self getVMTickeeCallCount].
[16] -> [^self positive64BitIntegerFor: statIdleUsecs].
[17] -> [^(SistaVM and: [self isCog])
ifTrue: [objectMemory floatObjectOf: self getCogCodeZoneThreshold]
ifFalse: [ConstZero]].
[18] -> [^objectMemory hasSpurMemoryManagerAPI
ifTrue: [objectMemory integerObjectOf: objectMemory statCompactionUsecs + 500 // 1000]
ifFalse: [ConstZero]].
[19] -> [^objectMemory hasSpurMemoryManagerAPI
ifTrue: [objectMemory integerObjectOf: objectMemory scavengeThresholdAsExtent]
ifFalse: [ConstZero]].
[20] -> [^objectMemory positive64BitIntegerFor: self ioUTCStartMicroseconds].
[21] -> [^objectMemory integerObjectOf: objectMemory rootTableCount].
[22] -> [^objectMemory integerObjectOf: objectMemory statRootTableOverflows].
[23] -> [^objectMemory integerObjectOf: extraVMMemory].
[24] -> [^objectMemory integerObjectOf: objectMemory shrinkThreshold].
[25] -> [^objectMemory integerObjectOf: objectMemory growHeadroom].
[26] -> [^objectMemory integerObjectOf: self ioHeartbeatMilliseconds].
[27] -> [^objectMemory integerObjectOf: objectMemory statMarkCount].
[28] -> [^objectMemory integerObjectOf: objectMemory statSweepCount].
[29] -> [^objectMemory integerObjectOf: objectMemory statMkFwdCount].
[30] -> [^objectMemory integerObjectOf: objectMemory statCompMoveCount].
[31] -> [^objectMemory integerObjectOf: objectMemory statGrowMemory].
[32] -> [^objectMemory integerObjectOf: objectMemory statShrinkMemory].
[33] -> [^objectMemory integerObjectOf: objectMemory statRootTableCount].
[34] -> [^objectMemory hasSpurMemoryManagerAPI ifTrue:"was statAllocationCount"
[objectMemory positive64BitIntegerFor: objectMemory currentAllocatedBytes]].
[35] -> [^objectMemory integerObjectOf: objectMemory statSurvivorCount].
[36] -> [^objectMemory integerObjectOf: (self microsecondsToMilliseconds: objectMemory statGCEndUsecs)].
[37] -> [^objectMemory integerObjectOf: objectMemory statSpecialMarkCount].
[38] -> [^objectMemory integerObjectOf: objectMemory statIGCDeltaUsecs + 500 // 1000].
[39] -> [^objectMemory integerObjectOf: statPendingFinalizationSignals].
[40] -> [^objectMemory integerObjectOf: objectMemory wordSize].
[41] -> [^objectMemory integerObjectOf: self imageFormatVersion].
[42] -> [^objectMemory integerObjectOf: numStackPages].
[43] -> [^objectMemory integerObjectOf: desiredNumStackPages].
[44] -> [^objectMemory integerObjectOf: objectMemory edenBytes].
[45] -> [^objectMemory integerObjectOf: desiredEdenBytes].
[46] -> [^self getCogCodeSize].
[47] -> [^self getDesiredCogCodeSize].
[48] -> [^self getImageHeaderFlagsParameter].
[49] -> [^objectMemory integerObjectOf: self ioGetMaxExtSemTableSize].
[50] -> [^self getMaxLiteralCountForCompile].
+ [51] -> [^objectMemory integerObjectOf: self imageLoadFlags].
[52] -> [^objectMemory integerObjectOf: objectMemory rootTableCapacity].
[53] -> [^objectMemory hasSpurMemoryManagerAPI ifTrue:
[objectMemory integerObjectOf: objectMemory numSegments]].
[54] -> [^objectMemory hasSpurMemoryManagerAPI ifTrue:
[objectMemory integerObjectOf: objectMemory freeSize]].
[55] -> [^objectMemory hasSpurMemoryManagerAPI ifTrue:
[objectMemory floatObjectOf: objectMemory getHeapGrowthToSizeGCRatio]].
[56] -> [^self positive64BitIntegerFor: statProcessSwitch].
[57] -> [^self positive64BitIntegerFor: statIOProcessEvents].
[58] -> [^self positive64BitIntegerFor: statForceInterruptCheck].
[59] -> [^self positive64BitIntegerFor: statCheckForEvents].
[60] -> [^self positive64BitIntegerFor: statStackOverflow].
[61] -> [^self positive64BitIntegerFor: statStackPageDivorce].
[62] -> [^self getCodeCompactionCount].
[63] -> [^self getCodeCompactionMSecs].
[64] -> [^self getCogMethodCount].
[65] -> [^self getCogVMFeatureFlags].
[66] -> [^objectMemory integerObjectOf: stackPages bytesPerPage].
[67] -> [^objectMemory hasSpurMemoryManagerAPI ifTrue:
[self positiveMachineIntegerFor: objectMemory maxOldSpaceSize]].
[68] -> [^objectMemory floatObjectOf: stackPages statAverageLivePagesWhenMapping].
[69] -> [^objectMemory integerObjectOf: stackPages statMaxPageCountWhenMapping].
[70] -> [^objectMemory integerObjectOf: self vmProxyMajorVersion].
[71] -> [^objectMemory integerObjectOf: self vmProxyMinorVersion].
[72] -> [^objectMemory integerObjectOf: objectMemory statMarkUsecs + 500 // 1000].
[73] -> [^objectMemory integerObjectOf: objectMemory statSweepUsecs + 500 // 1000].
[74] -> [^objectMemory hasSpurMemoryManagerAPI ifTrue:
[objectMemory integerObjectOf: objectMemory statMaxAllocSegmentTime + 500 // 1000]].
[75] -> [^objectMemory booleanObjectOf: self primitiveDoMixedArithmetic].
[76] -> [^objectMemory integerObjectOf: self minimumUnusedHeadroom] }
otherwise: [^nil]!
Item was changed:
----- Method: StackInterpreterSimulator>>openOn:extraMemory: (in category 'initialize-release') -----
openOn: fileName extraMemory: extraBytes
"StackInterpreterSimulator new openOn: 'clone.im' extraMemory: 100000"
+ | f version headerSize dataSize count bytesToShift swapBytes
- | f version headerSize dataSize count oldBaseAddr bytesToShift swapBytes
headerFlags heapBase firstSegSize heapSize
hdrNumStackPages hdrEdenBytes hdrMaxExtSemTabSize allocationReserve |
"open image file and read the header"
(f := self openImageFileNamed: fileName) ifNil: [^self].
"Set the image name and the first argument; there are
no arguments during simulation unless set explicitly."
systemAttributes at: 1 put: fileName.
["begin ensure block..."
imageName := f fullName.
f binary.
version := self getWord32FromFile: f swap: false. "current version: 16r1968 (=6504) vive la revolucion!!"
(self readableFormat: version)
ifTrue: [swapBytes := false]
ifFalse: [(version := version byteSwap32) = self imageFormatVersion
ifTrue: [swapBytes := true]
ifFalse: [self error: 'incomaptible image format']].
headerSize := self getWord32FromFile: f swap: swapBytes.
dataSize := self getLongFromFile: f swap: swapBytes. "length of heap in file"
+ oldImageBaseAddress := self getLongFromFile: f swap: swapBytes. "object memory base address of image"
- oldBaseAddr := self getLongFromFile: f swap: swapBytes. "object memory base address of image"
objectMemory specialObjectsOop: (self getLongFromFile: f swap: swapBytes).
objectMemory lastHash: (self getLongFromFile: f swap: swapBytes). "Should be loaded from, and saved to the image header"
savedWindowSize := self getLongFromFile: f swap: swapBytes.
headerFlags := self getLongFromFile: f swap: swapBytes.
self setImageHeaderFlagsFrom: headerFlags.
extraVMMemory := self getWord32FromFile: f swap: swapBytes.
hdrNumStackPages := self getShortFromFile: f swap: swapBytes.
"4 stack pages is small. Should be able to run with as few as
three. 4 should be comfortable but slow. 8 is a reasonable
default. Can be changed via vmParameterAt: 43 put: n"
numStackPages := desiredNumStackPages ~= 0
ifTrue: [desiredNumStackPages]
ifFalse: [hdrNumStackPages = 0
ifTrue: [self defaultNumStackPages]
ifFalse: [hdrNumStackPages]].
desiredNumStackPages := hdrNumStackPages.
"pad to word boundary. This slot can be used for anything else that will fit in 16 bits.
It is used for the cog code size in Cog. Preserve it to be polite to other VMs."
theUnknownShort := self getShortFromFile: f swap: swapBytes.
self assert: f position = (objectMemory wordSize = 4 ifTrue: [40] ifFalse: [64]).
hdrEdenBytes := self getWord32FromFile: f swap: swapBytes.
objectMemory edenBytes: (hdrEdenBytes = 0
ifTrue: [objectMemory defaultEdenBytes]
ifFalse: [hdrEdenBytes]).
desiredEdenBytes := hdrEdenBytes.
hdrMaxExtSemTabSize := self getShortFromFile: f swap: swapBytes.
hdrMaxExtSemTabSize ~= 0 ifTrue:
[self setMaxExtSemSizeTo: hdrMaxExtSemTabSize].
"pad to word boundary. This slot can be used for anything else that will fit in 16 bits.
Preserve it to be polite to other VMs."
the2ndUnknownShort := self getShortFromFile: f swap: swapBytes.
self assert: f position = (objectMemory wordSize = 4 ifTrue: [48] ifFalse: [72]).
firstSegSize := self getLongFromFile: f swap: swapBytes.
objectMemory firstSegmentSize: firstSegSize.
"compare memory requirements with availability"
allocationReserve := self interpreterAllocationReserveBytes.
objectMemory hasSpurMemoryManagerAPI
ifTrue:
[| freeOldSpaceInImage headroom |
freeOldSpaceInImage := self getLongFromFile: f swap: swapBytes.
headroom := objectMemory
initialHeadroom: extraVMMemory
givenFreeOldSpaceInImage: freeOldSpaceInImage.
heapSize := objectMemory roundUpHeapSize:
dataSize
+ headroom
+ objectMemory newSpaceBytes
+ (headroom > allocationReserve
ifTrue: [0]
ifFalse: [allocationReserve])]
ifFalse:
[heapSize := dataSize
+ extraBytes
+ objectMemory newSpaceBytes
+ (extraBytes > allocationReserve
ifTrue: [0]
ifFalse: [allocationReserve])].
"allocate interpreter memory"
heapBase := objectMemory
setHeapBase: objectMemory startOfMemory
memoryLimit: objectMemory startOfMemory + heapSize
endOfMemory: objectMemory startOfMemory + dataSize. "bogus for Spur"
objectMemory allocateMemoryOfSize: objectMemory memoryLimit.
"read in the image in bulk, then swap the bytes if necessary"
f position: headerSize.
count := objectMemory readHeapFromImageFile: f dataBytes: dataSize.
count ~= dataSize ifTrue: [self halt]]
ensure: [f close].
self ensureImageFormatIsUpToDate: swapBytes.
+ bytesToShift := objectMemory memoryBaseForImageRead - oldImageBaseAddress. "adjust pointers for zero base address"
- bytesToShift := objectMemory memoryBaseForImageRead - oldBaseAddr. "adjust pointers for zero base address"
UIManager default
informUser: 'Relocating object pointers...'
during: [self initializeInterpreter: bytesToShift]!
Leon Matthes uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.threaded-LM.3356.mcz
==================== Summary ====================
Name: VMMaker.threaded-LM.3356
Author: LM
Time: 21 March 2024, 10:41:55.233376 am
UUID: bc5d6545-faa3-4e8e-8b64-8bd3c8ee48fe
Ancestors: VMMaker.threaded-LM.3355
Optimize threadSwitchIfNecessary:from:
We only really need to marry the context when we pass the interpreter to a thread that schedules in its own process.
Otherwise the thread can just take over the interpreter as-is.
=============== Diff against VMMaker.threaded-LM.3355 ===============
Item was added:
+ ----- Method: CoInterpreterMT>>ensureProcessHasContext: (in category 'vm scheduling') -----
+ ensureProcessHasContext: aProcess
+
+ | activeContext |
+ (objectMemory fetchPointer: SuspendedContextIndex ofObject: aProcess) = objectMemory nilObject ifTrue:
+ [self assert: aProcess = self activeProcess.
+ "The instructionPointer is popped from the stack in 'externalSetStackPageAndPointersForSuspendedContextOfProcess:' "
+ self push: instructionPointer.
+ "We at least need to externalize the stack pointers to enable a thread switch..."
+ self externalWriteBackHeadFramePointers.
+ activeContext := self ensureFrameIsMarried: framePointer SP: stackPointer.
+ objectMemory storePointer: SuspendedContextIndex ofObject: aProcess withValue: activeContext].!
Item was changed:
----- Method: CoInterpreterMT>>restoreVMStateFor:andFlags: (in category 'vm scheduling') -----
restoreVMStateFor: vmThread andFlags: flags
"We've been preempted; we must restore state and update the threadId
in our process, and may have to put the active process to sleep."
| sched activeProc myProc |
sched := self schedulerPointer.
activeProc := objectMemory fetchPointer: ActiveProcessIndex ofObject: sched.
(flags anyMask: OwnVMForeignThreadFlag)
ifTrue:
[self assert: foreignCallbackProcessSlot == ForeignCallbackProcess.
myProc := objectMemory splObj: foreignCallbackProcessSlot.
self assert: myProc ~= objectMemory nilObject.
objectMemory splObj: foreignCallbackProcessSlot put: objectMemory nilObject]
ifFalse: [myProc := self popProcessWithTemporaryAffinity: vmThread index fromList: (objectMemory splObj: ProcessInExternalCodeTag)].
self assert: (myProc ~= objectMemory nilObject and: [activeProc ~= myProc]).
(activeProc ~= objectMemory nilObject
and: [(objectMemory fetchPointer: MyListIndex ofObject: activeProc) = objectMemory nilObject]) ifTrue:
+ ["If the activeProcess doesn't have a context yet, it needs one from which we can resume later.
+ This mostly only happens when a threadSwitchIfNecessary:from: ends up switching to a thread that's CTMUnavailable (this thread).
+ See the comment in threadSwitchIfNecessary:from:"
+ self ensureProcessHasContext: activeProc.
+ self putToSleep: activeProc yieldingIf: preemptionYields].
- [self putToSleep: activeProc yieldingIf: preemptionYields].
objectMemory
storePointerUnchecked: MyListIndex ofObject: myProc withValue: objectMemory nilObject;
storePointer: ActiveProcessIndex ofObject: sched withValue: myProc.
self setTemporaryThreadAffinityOfProcess: myProc to: 0.
self initPrimCall.
self cCode:
[self externalSetStackPageAndPointersForSuspendedContextOfProcess: myProc]
inSmalltalk:
["Bypass the no-offset stack depth check in the simulator's externalSetStackPageAndPointersForSuspendedContextOfProcess:"
super externalSetStackPageAndPointersForSuspendedContextOfProcess: myProc.
"We're in ownVM:, hence in a primitive, hence need to include the argument count"
(self isMachineCodeFrame: framePointer) ifTrue:
[self maybeCheckStackDepth: vmThread argumentCount
sp: stackPointer
pc: instructionPointer]].
"If this primitive is called from machine code maintain the invariant that the return pc
of an interpreter callee calling a machine code caller is ceReturnToInterpreterPC."
(vmThread inMachineCode
and: [instructionPointer >= objectMemory startOfMemory]) ifTrue:
[self iframeSavedIP: framePointer put: instructionPointer.
instructionPointer := cogit ceReturnToInterpreterPC].
newMethod := vmThread newMethodOrNull.
argumentCount := vmThread argumentCount.
vmThread newMethodOrNull: nil.
self cCode: '' inSmalltalk:
[| range |
range := self cStackRangeForThreadIndex: vmThread index.
self assert: ((range includes: vmThread cStackPointer) and: [range includes: vmThread cFramePointer])].
self setCFramePointer: vmThread cFramePointer setCStackPointer: vmThread cStackPointer.
self assert: newMethod notNil
!
Item was changed:
----- Method: CoInterpreterMT>>threadSwitchIfNecessary:from: (in category 'process primitive support') -----
threadSwitchIfNecessary: newProc from: sourceCode
"Invoked from transferTo:from: or primitiveProcessBindToThreadId to
switch threads if the new process is bound or affined to some other thread."
+ | newProcThreadAffinity vmThread threadSwitchNecessary |
- | newProcThreadAffinity vmThread activeContext threadSwitchNecessary |
self assert: (cogThreadManager vmOwnerIs: cogThreadManager ioGetThreadLocalThreadIndex).
deferThreadSwitch ifTrue: [^self].
cogThreadManager assertValidProcessorStackPointersForIndex: cogThreadManager getVMOwner.
"If the current process is unaffined or it is affined to the current thread we're
ok to run, but we should yield asap if a higher-priority thread wants the VM."
newProcThreadAffinity := self threadAffinityOfProcess: newProc.
threadSwitchNecessary := (activeProcessAffined := newProcThreadAffinity ~= 0)
and: [(cogThreadManager vmOwnerIsCompatibleWith: newProcThreadAffinity) not].
threadSwitchNecessary ifFalse:
[(self quickFetchInteger: PriorityIndex ofObject: newProc) < self getMaxWaitingPriority ifTrue:
[checkThreadActivation := true.
self forceInterruptCheck].
"We're done, no thread switch necessary"
^self].
"The current process is affined to a thread, but not to the current owner. So switch to that owner."
self cCode: [] inSmalltalk:
[transcript
ensureCr;
f: 'threadSwitchIfNecessary: %08x from: %s(%d) owner %d -> %d\n'
printf: { newProc. TraceSources at: sourceCode. sourceCode. cogThreadManager getVMOwner. newProcThreadAffinity }].
+ "In most cases, we can just switch the thread here, without externalizing the stack pages.
+ If the Processes context is nil, it's state is on the stack. As we're already done context switching,
+ the new thread can just use the interpreter state as-is, without restoring the state from the context.
+
+ tryToExecuteSmalltalk: already includes a check whether the SuspendedContext is nil.
+ If it is, it leaves the interpreter state alone and just assumes it's correct.
+ This is nice and fast.
+ Otherwise it calls externalSetStackPageAndPointersForSuspendedContextOfProcess: to restore the interpreter state.
+
+ There is however a special case. When we switch to a thread that is currently CTMUnavailable, that thread will need
+ to restore its process when it tries to own the VM again.
+ The check to restore the context has been moved there (in restoreVMStateFor:andFlags:), so that it only happens in
+ that one case and not every time.
+ In case there are other such special-cases later, adding a call to ensureProcessHasContext: here should fix it."
- "If the activeProcess doesn't have a context yet, it needs one from which the new thread can resume execution."
- (objectMemory fetchPointer: SuspendedContextIndex ofObject: newProc) = objectMemory nilObject ifTrue:
- [self assert: newProc = self activeProcess.
- "The instructionPointer is popped from the stack in 'externalSetStackPageAndPointersForSuspendedContextOfProcess:' "
- self push: instructionPointer.
- "We at least need to externalize the stack pointers to enable a thread switch..."
- self externalWriteBackHeadFramePointers.
- activeContext := self ensureFrameIsMarried: framePointer SP: stackPointer.
- objectMemory storePointer: SuspendedContextIndex ofObject: newProc withValue: activeContext].
newProcThreadAffinity < 0
ifTrue:
[self assert: newProcThreadAffinity negated = cogThreadManager getVMOwner.
vmThread := cogThreadManager ensureWillingThread.
self deny: vmThread index = cogThreadManager getVMOwner.
self assert: (cogThreadManager threadIndex: vmThread index isCompatibleWith: newProcThreadAffinity)]
ifFalse:
[vmThread := cogThreadManager vmThreadAt: newProcThreadAffinity.
vmThread priority: (self quickFetchInteger: PriorityIndex ofObject: newProc).
vmThread vmThreadState = CTMUnavailable ifTrue:
[vmThread setVmThreadState: CTMWantingOwnership]].
self returnToSchedulingLoopAndReleaseVMOrWakeThread: vmThread source: CSSwitchIfNeccessary!
Item was changed:
----- Method: CoInterpreterMT>>tryToExecuteSmalltalk: (in category 'vm scheduling') -----
tryToExecuteSmalltalk: vmThread
"Attempt to run the current process, if it exists, on the given vmThread."
<var: #vmThread type: #'CogVMThread *'>
| activeProc threadAffinity |
self assert: (cogThreadManager vmOwnerIs: vmThread index).
self assert: cogThreadManager ioGetThreadLocalThreadIndex = vmThread index.
disowningVMThread
ifNil: [activeProc := self activeProcess]
ifNotNil:
[self preemptDisowningThread.
activeProc := self wakeHighestPriority.
activeProc
ifNil: [activeProc := objectMemory nilObject]
ifNotNil: [objectMemory
storePointerUnchecked: MyListIndex
ofObject: activeProc
withValue: objectMemory nilObject].
objectMemory
storePointer: ActiveProcessIndex
ofObject: self schedulerPointer
withValue: activeProc].
"There is a special case here.
When the VM has relinquished, but then another thread finishes external code execution, there may no longer be a process to run.
However, the relinquishing flag may already have been reset by another thread that has owned the VM again."
activeProc = objectMemory nilObject
ifTrue: ["self warning: 'tryToExecuteSmalltalk: no active process!!'."
"relinquishing := true".
^nil].
threadAffinity := self threadAffinityOfProcess: activeProc.
(cogThreadManager vmOwnerIsCompatibleWith: threadAffinity) ifTrue:
[self assert: (objectMemory fetchPointer: MyListIndex ofObject: self activeProcess) = objectMemory nilObject.
+ "If we switch threads in threadSwitchIfNecessary:from:, the interpreter state is likely
+ already in the correct state.
+ In that case, there is no suspended context and nothing to restore. We can just continue
+ execution.
+ If there is a suspended context, assume that we need to restore the state from that."
(objectMemory fetchPointer: SuspendedContextIndex ofObject: activeProc) ~= objectMemory nilObject ifTrue:
[self externalSetStackPageAndPointersForSuspendedContextOfProcess: activeProc].
instructionPointer = cogit ceReturnToInterpreterPC ifTrue:
[self deny: (self isMachineCodeFrame: framePointer).
instructionPointer := self iframeSavedIP: framePointer].
self enterSmalltalkExecutive.
"When we return here we should have already given up
the VM and so we cannot touch any interpreter state."
self error: 'NOTREACHED'.].
cogThreadManager returnToSchedulingLoopAndWakeThreadFor: threadAffinity source: CSTryToExecuteSmalltalk.
"This is only reached if the above call has failed, then ownership has not been transferred and we still need to release the VM."!
Leon Matthes uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.threaded-LM.3356.mcz
==================== Summary ====================
Name: VMMaker.threaded-LM.3356
Author: LM
Time: 21 March 2024, 10:41:55.233376 am
UUID: bc5d6545-faa3-4e8e-8b64-8bd3c8ee48fe
Ancestors: VMMaker.threaded-LM.3355
Optimize threadSwitchIfNecessary:from:
We only really need to marry the context when we pass the interpreter to a thread that schedules in its own process.
Otherwise the thread can just take over the interpreter as-is.
=============== Diff against VMMaker.threaded-LM.3355 ===============
Item was added:
+ ----- Method: CoInterpreterMT>>ensureProcessHasContext: (in category 'vm scheduling') -----
+ ensureProcessHasContext: aProcess
+
+ | activeContext |
+ (objectMemory fetchPointer: SuspendedContextIndex ofObject: aProcess) = objectMemory nilObject ifTrue:
+ [self assert: aProcess = self activeProcess.
+ "The instructionPointer is popped from the stack in 'externalSetStackPageAndPointersForSuspendedContextOfProcess:' "
+ self push: instructionPointer.
+ "We at least need to externalize the stack pointers to enable a thread switch..."
+ self externalWriteBackHeadFramePointers.
+ activeContext := self ensureFrameIsMarried: framePointer SP: stackPointer.
+ objectMemory storePointer: SuspendedContextIndex ofObject: aProcess withValue: activeContext].!
Item was changed:
----- Method: CoInterpreterMT>>restoreVMStateFor:andFlags: (in category 'vm scheduling') -----
restoreVMStateFor: vmThread andFlags: flags
"We've been preempted; we must restore state and update the threadId
in our process, and may have to put the active process to sleep."
| sched activeProc myProc |
sched := self schedulerPointer.
activeProc := objectMemory fetchPointer: ActiveProcessIndex ofObject: sched.
(flags anyMask: OwnVMForeignThreadFlag)
ifTrue:
[self assert: foreignCallbackProcessSlot == ForeignCallbackProcess.
myProc := objectMemory splObj: foreignCallbackProcessSlot.
self assert: myProc ~= objectMemory nilObject.
objectMemory splObj: foreignCallbackProcessSlot put: objectMemory nilObject]
ifFalse: [myProc := self popProcessWithTemporaryAffinity: vmThread index fromList: (objectMemory splObj: ProcessInExternalCodeTag)].
self assert: (myProc ~= objectMemory nilObject and: [activeProc ~= myProc]).
(activeProc ~= objectMemory nilObject
and: [(objectMemory fetchPointer: MyListIndex ofObject: activeProc) = objectMemory nilObject]) ifTrue:
+ ["If the activeProcess doesn't have a context yet, it needs one from which we can resume later.
+ This mostly only happens when a threadSwitchIfNecessary:from: ends up switching to a thread that's CTMUnavailable (this thread).
+ See the comment in threadSwitchIfNecessary:from:"
+ self ensureProcessHasContext: activeProc.
+ self putToSleep: activeProc yieldingIf: preemptionYields].
- [self putToSleep: activeProc yieldingIf: preemptionYields].
objectMemory
storePointerUnchecked: MyListIndex ofObject: myProc withValue: objectMemory nilObject;
storePointer: ActiveProcessIndex ofObject: sched withValue: myProc.
self setTemporaryThreadAffinityOfProcess: myProc to: 0.
self initPrimCall.
self cCode:
[self externalSetStackPageAndPointersForSuspendedContextOfProcess: myProc]
inSmalltalk:
["Bypass the no-offset stack depth check in the simulator's externalSetStackPageAndPointersForSuspendedContextOfProcess:"
super externalSetStackPageAndPointersForSuspendedContextOfProcess: myProc.
"We're in ownVM:, hence in a primitive, hence need to include the argument count"
(self isMachineCodeFrame: framePointer) ifTrue:
[self maybeCheckStackDepth: vmThread argumentCount
sp: stackPointer
pc: instructionPointer]].
"If this primitive is called from machine code maintain the invariant that the return pc
of an interpreter callee calling a machine code caller is ceReturnToInterpreterPC."
(vmThread inMachineCode
and: [instructionPointer >= objectMemory startOfMemory]) ifTrue:
[self iframeSavedIP: framePointer put: instructionPointer.
instructionPointer := cogit ceReturnToInterpreterPC].
newMethod := vmThread newMethodOrNull.
argumentCount := vmThread argumentCount.
vmThread newMethodOrNull: nil.
self cCode: '' inSmalltalk:
[| range |
range := self cStackRangeForThreadIndex: vmThread index.
self assert: ((range includes: vmThread cStackPointer) and: [range includes: vmThread cFramePointer])].
self setCFramePointer: vmThread cFramePointer setCStackPointer: vmThread cStackPointer.
self assert: newMethod notNil
!
Item was changed:
----- Method: CoInterpreterMT>>threadSwitchIfNecessary:from: (in category 'process primitive support') -----
threadSwitchIfNecessary: newProc from: sourceCode
"Invoked from transferTo:from: or primitiveProcessBindToThreadId to
switch threads if the new process is bound or affined to some other thread."
+ | newProcThreadAffinity vmThread threadSwitchNecessary |
- | newProcThreadAffinity vmThread activeContext threadSwitchNecessary |
self assert: (cogThreadManager vmOwnerIs: cogThreadManager ioGetThreadLocalThreadIndex).
deferThreadSwitch ifTrue: [^self].
cogThreadManager assertValidProcessorStackPointersForIndex: cogThreadManager getVMOwner.
"If the current process is unaffined or it is affined to the current thread we're
ok to run, but we should yield asap if a higher-priority thread wants the VM."
newProcThreadAffinity := self threadAffinityOfProcess: newProc.
threadSwitchNecessary := (activeProcessAffined := newProcThreadAffinity ~= 0)
and: [(cogThreadManager vmOwnerIsCompatibleWith: newProcThreadAffinity) not].
threadSwitchNecessary ifFalse:
[(self quickFetchInteger: PriorityIndex ofObject: newProc) < self getMaxWaitingPriority ifTrue:
[checkThreadActivation := true.
self forceInterruptCheck].
"We're done, no thread switch necessary"
^self].
"The current process is affined to a thread, but not to the current owner. So switch to that owner."
self cCode: [] inSmalltalk:
[transcript
ensureCr;
f: 'threadSwitchIfNecessary: %08x from: %s(%d) owner %d -> %d\n'
printf: { newProc. TraceSources at: sourceCode. sourceCode. cogThreadManager getVMOwner. newProcThreadAffinity }].
+ "In most cases, we can just switch the thread here, without externalizing the stack pages.
+ If the Processes context is nil, it's state is on the stack. As we're already done context switching,
+ the new thread can just use the interpreter state as-is, without restoring the state from the context.
+
+ tryToExecuteSmalltalk: already includes a check whether the SuspendedContext is nil.
+ If it is, it leaves the interpreter state alone and just assumes it's correct.
+ This is nice and fast.
+ Otherwise it calls externalSetStackPageAndPointersForSuspendedContextOfProcess: to restore the interpreter state.
+
+ There is however a special case. When we switch to a thread that is currently CTMUnavailable, that thread will need
+ to restore its process when it tries to own the VM again.
+ The check to restore the context has been moved there (in restoreVMStateFor:andFlags:), so that it only happens in
+ that one case and not every time.
+ In case there are other such special-cases later, adding a call to ensureProcessHasContext: here should fix it."
- "If the activeProcess doesn't have a context yet, it needs one from which the new thread can resume execution."
- (objectMemory fetchPointer: SuspendedContextIndex ofObject: newProc) = objectMemory nilObject ifTrue:
- [self assert: newProc = self activeProcess.
- "The instructionPointer is popped from the stack in 'externalSetStackPageAndPointersForSuspendedContextOfProcess:' "
- self push: instructionPointer.
- "We at least need to externalize the stack pointers to enable a thread switch..."
- self externalWriteBackHeadFramePointers.
- activeContext := self ensureFrameIsMarried: framePointer SP: stackPointer.
- objectMemory storePointer: SuspendedContextIndex ofObject: newProc withValue: activeContext].
newProcThreadAffinity < 0
ifTrue:
[self assert: newProcThreadAffinity negated = cogThreadManager getVMOwner.
vmThread := cogThreadManager ensureWillingThread.
self deny: vmThread index = cogThreadManager getVMOwner.
self assert: (cogThreadManager threadIndex: vmThread index isCompatibleWith: newProcThreadAffinity)]
ifFalse:
[vmThread := cogThreadManager vmThreadAt: newProcThreadAffinity.
vmThread priority: (self quickFetchInteger: PriorityIndex ofObject: newProc).
vmThread vmThreadState = CTMUnavailable ifTrue:
[vmThread setVmThreadState: CTMWantingOwnership]].
self returnToSchedulingLoopAndReleaseVMOrWakeThread: vmThread source: CSSwitchIfNeccessary!
Item was changed:
----- Method: CoInterpreterMT>>tryToExecuteSmalltalk: (in category 'vm scheduling') -----
tryToExecuteSmalltalk: vmThread
"Attempt to run the current process, if it exists, on the given vmThread."
<var: #vmThread type: #'CogVMThread *'>
| activeProc threadAffinity |
self assert: (cogThreadManager vmOwnerIs: vmThread index).
self assert: cogThreadManager ioGetThreadLocalThreadIndex = vmThread index.
disowningVMThread
ifNil: [activeProc := self activeProcess]
ifNotNil:
[self preemptDisowningThread.
activeProc := self wakeHighestPriority.
activeProc
ifNil: [activeProc := objectMemory nilObject]
ifNotNil: [objectMemory
storePointerUnchecked: MyListIndex
ofObject: activeProc
withValue: objectMemory nilObject].
objectMemory
storePointer: ActiveProcessIndex
ofObject: self schedulerPointer
withValue: activeProc].
"There is a special case here.
When the VM has relinquished, but then another thread finishes external code execution, there may no longer be a process to run.
However, the relinquishing flag may already have been reset by another thread that has owned the VM again."
activeProc = objectMemory nilObject
ifTrue: ["self warning: 'tryToExecuteSmalltalk: no active process!!'."
"relinquishing := true".
^nil].
threadAffinity := self threadAffinityOfProcess: activeProc.
(cogThreadManager vmOwnerIsCompatibleWith: threadAffinity) ifTrue:
[self assert: (objectMemory fetchPointer: MyListIndex ofObject: self activeProcess) = objectMemory nilObject.
+ "If we switch threads in threadSwitchIfNecessary:from:, the interpreter state is likely
+ already in the correct state.
+ In that case, there is no suspended context and nothing to restore. We can just continue
+ execution.
+ If there is a suspended context, assume that we need to restore the state from that."
(objectMemory fetchPointer: SuspendedContextIndex ofObject: activeProc) ~= objectMemory nilObject ifTrue:
[self externalSetStackPageAndPointersForSuspendedContextOfProcess: activeProc].
instructionPointer = cogit ceReturnToInterpreterPC ifTrue:
[self deny: (self isMachineCodeFrame: framePointer).
instructionPointer := self iframeSavedIP: framePointer].
self enterSmalltalkExecutive.
"When we return here we should have already given up
the VM and so we cannot touch any interpreter state."
self error: 'NOTREACHED'.].
cogThreadManager returnToSchedulingLoopAndWakeThreadFor: threadAffinity source: CSTryToExecuteSmalltalk.
"This is only reached if the above call has failed, then ownership has not been transferred and we still need to release the VM."!
Leon Matthes uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.threaded-LM.3356.mcz
==================== Summary ====================
Name: VMMaker.threaded-LM.3356
Author: LM
Time: 21 March 2024, 10:41:55.233376 am
UUID: bc5d6545-faa3-4e8e-8b64-8bd3c8ee48fe
Ancestors: VMMaker.threaded-LM.3355
Optimize threadSwitchIfNecessary:from:
We only really need to marry the context when we pass the interpreter to a thread that schedules in its own process.
Otherwise the thread can just take over the interpreter as-is.
=============== Diff against VMMaker.threaded-LM.3355 ===============
Item was added:
+ ----- Method: CoInterpreterMT>>ensureProcessHasContext: (in category 'vm scheduling') -----
+ ensureProcessHasContext: aProcess
+
+ | activeContext |
+ (objectMemory fetchPointer: SuspendedContextIndex ofObject: aProcess) = objectMemory nilObject ifTrue:
+ [self assert: aProcess = self activeProcess.
+ "The instructionPointer is popped from the stack in 'externalSetStackPageAndPointersForSuspendedContextOfProcess:' "
+ self push: instructionPointer.
+ "We at least need to externalize the stack pointers to enable a thread switch..."
+ self externalWriteBackHeadFramePointers.
+ activeContext := self ensureFrameIsMarried: framePointer SP: stackPointer.
+ objectMemory storePointer: SuspendedContextIndex ofObject: aProcess withValue: activeContext].!
Item was changed:
----- Method: CoInterpreterMT>>restoreVMStateFor:andFlags: (in category 'vm scheduling') -----
restoreVMStateFor: vmThread andFlags: flags
"We've been preempted; we must restore state and update the threadId
in our process, and may have to put the active process to sleep."
| sched activeProc myProc |
sched := self schedulerPointer.
activeProc := objectMemory fetchPointer: ActiveProcessIndex ofObject: sched.
(flags anyMask: OwnVMForeignThreadFlag)
ifTrue:
[self assert: foreignCallbackProcessSlot == ForeignCallbackProcess.
myProc := objectMemory splObj: foreignCallbackProcessSlot.
self assert: myProc ~= objectMemory nilObject.
objectMemory splObj: foreignCallbackProcessSlot put: objectMemory nilObject]
ifFalse: [myProc := self popProcessWithTemporaryAffinity: vmThread index fromList: (objectMemory splObj: ProcessInExternalCodeTag)].
self assert: (myProc ~= objectMemory nilObject and: [activeProc ~= myProc]).
(activeProc ~= objectMemory nilObject
and: [(objectMemory fetchPointer: MyListIndex ofObject: activeProc) = objectMemory nilObject]) ifTrue:
+ ["If the activeProcess doesn't have a context yet, it needs one from which we can resume later.
+ This mostly only happens when a threadSwitchIfNecessary:from: ends up switching to a thread that's CTMUnavailable (this thread).
+ See the comment in threadSwitchIfNecessary:from:"
+ self ensureProcessHasContext: activeProc.
+ self putToSleep: activeProc yieldingIf: preemptionYields].
- [self putToSleep: activeProc yieldingIf: preemptionYields].
objectMemory
storePointerUnchecked: MyListIndex ofObject: myProc withValue: objectMemory nilObject;
storePointer: ActiveProcessIndex ofObject: sched withValue: myProc.
self setTemporaryThreadAffinityOfProcess: myProc to: 0.
self initPrimCall.
self cCode:
[self externalSetStackPageAndPointersForSuspendedContextOfProcess: myProc]
inSmalltalk:
["Bypass the no-offset stack depth check in the simulator's externalSetStackPageAndPointersForSuspendedContextOfProcess:"
super externalSetStackPageAndPointersForSuspendedContextOfProcess: myProc.
"We're in ownVM:, hence in a primitive, hence need to include the argument count"
(self isMachineCodeFrame: framePointer) ifTrue:
[self maybeCheckStackDepth: vmThread argumentCount
sp: stackPointer
pc: instructionPointer]].
"If this primitive is called from machine code maintain the invariant that the return pc
of an interpreter callee calling a machine code caller is ceReturnToInterpreterPC."
(vmThread inMachineCode
and: [instructionPointer >= objectMemory startOfMemory]) ifTrue:
[self iframeSavedIP: framePointer put: instructionPointer.
instructionPointer := cogit ceReturnToInterpreterPC].
newMethod := vmThread newMethodOrNull.
argumentCount := vmThread argumentCount.
vmThread newMethodOrNull: nil.
self cCode: '' inSmalltalk:
[| range |
range := self cStackRangeForThreadIndex: vmThread index.
self assert: ((range includes: vmThread cStackPointer) and: [range includes: vmThread cFramePointer])].
self setCFramePointer: vmThread cFramePointer setCStackPointer: vmThread cStackPointer.
self assert: newMethod notNil
!
Item was changed:
----- Method: CoInterpreterMT>>threadSwitchIfNecessary:from: (in category 'process primitive support') -----
threadSwitchIfNecessary: newProc from: sourceCode
"Invoked from transferTo:from: or primitiveProcessBindToThreadId to
switch threads if the new process is bound or affined to some other thread."
+ | newProcThreadAffinity vmThread threadSwitchNecessary |
- | newProcThreadAffinity vmThread activeContext threadSwitchNecessary |
self assert: (cogThreadManager vmOwnerIs: cogThreadManager ioGetThreadLocalThreadIndex).
deferThreadSwitch ifTrue: [^self].
cogThreadManager assertValidProcessorStackPointersForIndex: cogThreadManager getVMOwner.
"If the current process is unaffined or it is affined to the current thread we're
ok to run, but we should yield asap if a higher-priority thread wants the VM."
newProcThreadAffinity := self threadAffinityOfProcess: newProc.
threadSwitchNecessary := (activeProcessAffined := newProcThreadAffinity ~= 0)
and: [(cogThreadManager vmOwnerIsCompatibleWith: newProcThreadAffinity) not].
threadSwitchNecessary ifFalse:
[(self quickFetchInteger: PriorityIndex ofObject: newProc) < self getMaxWaitingPriority ifTrue:
[checkThreadActivation := true.
self forceInterruptCheck].
"We're done, no thread switch necessary"
^self].
"The current process is affined to a thread, but not to the current owner. So switch to that owner."
self cCode: [] inSmalltalk:
[transcript
ensureCr;
f: 'threadSwitchIfNecessary: %08x from: %s(%d) owner %d -> %d\n'
printf: { newProc. TraceSources at: sourceCode. sourceCode. cogThreadManager getVMOwner. newProcThreadAffinity }].
+ "In most cases, we can just switch the thread here, without externalizing the stack pages.
+ If the Processes context is nil, it's state is on the stack. As we're already done context switching,
+ the new thread can just use the interpreter state as-is, without restoring the state from the context.
+
+ tryToExecuteSmalltalk: already includes a check whether the SuspendedContext is nil.
+ If it is, it leaves the interpreter state alone and just assumes it's correct.
+ This is nice and fast.
+ Otherwise it calls externalSetStackPageAndPointersForSuspendedContextOfProcess: to restore the interpreter state.
+
+ There is however a special case. When we switch to a thread that is currently CTMUnavailable, that thread will need
+ to restore its process when it tries to own the VM again.
+ The check to restore the context has been moved there (in restoreVMStateFor:andFlags:), so that it only happens in
+ that one case and not every time.
+ In case there are other such special-cases later, adding a call to ensureProcessHasContext: here should fix it."
- "If the activeProcess doesn't have a context yet, it needs one from which the new thread can resume execution."
- (objectMemory fetchPointer: SuspendedContextIndex ofObject: newProc) = objectMemory nilObject ifTrue:
- [self assert: newProc = self activeProcess.
- "The instructionPointer is popped from the stack in 'externalSetStackPageAndPointersForSuspendedContextOfProcess:' "
- self push: instructionPointer.
- "We at least need to externalize the stack pointers to enable a thread switch..."
- self externalWriteBackHeadFramePointers.
- activeContext := self ensureFrameIsMarried: framePointer SP: stackPointer.
- objectMemory storePointer: SuspendedContextIndex ofObject: newProc withValue: activeContext].
newProcThreadAffinity < 0
ifTrue:
[self assert: newProcThreadAffinity negated = cogThreadManager getVMOwner.
vmThread := cogThreadManager ensureWillingThread.
self deny: vmThread index = cogThreadManager getVMOwner.
self assert: (cogThreadManager threadIndex: vmThread index isCompatibleWith: newProcThreadAffinity)]
ifFalse:
[vmThread := cogThreadManager vmThreadAt: newProcThreadAffinity.
vmThread priority: (self quickFetchInteger: PriorityIndex ofObject: newProc).
vmThread vmThreadState = CTMUnavailable ifTrue:
[vmThread setVmThreadState: CTMWantingOwnership]].
self returnToSchedulingLoopAndReleaseVMOrWakeThread: vmThread source: CSSwitchIfNeccessary!
Item was changed:
----- Method: CoInterpreterMT>>tryToExecuteSmalltalk: (in category 'vm scheduling') -----
tryToExecuteSmalltalk: vmThread
"Attempt to run the current process, if it exists, on the given vmThread."
<var: #vmThread type: #'CogVMThread *'>
| activeProc threadAffinity |
self assert: (cogThreadManager vmOwnerIs: vmThread index).
self assert: cogThreadManager ioGetThreadLocalThreadIndex = vmThread index.
disowningVMThread
ifNil: [activeProc := self activeProcess]
ifNotNil:
[self preemptDisowningThread.
activeProc := self wakeHighestPriority.
activeProc
ifNil: [activeProc := objectMemory nilObject]
ifNotNil: [objectMemory
storePointerUnchecked: MyListIndex
ofObject: activeProc
withValue: objectMemory nilObject].
objectMemory
storePointer: ActiveProcessIndex
ofObject: self schedulerPointer
withValue: activeProc].
"There is a special case here.
When the VM has relinquished, but then another thread finishes external code execution, there may no longer be a process to run.
However, the relinquishing flag may already have been reset by another thread that has owned the VM again."
activeProc = objectMemory nilObject
ifTrue: ["self warning: 'tryToExecuteSmalltalk: no active process!!'."
"relinquishing := true".
^nil].
threadAffinity := self threadAffinityOfProcess: activeProc.
(cogThreadManager vmOwnerIsCompatibleWith: threadAffinity) ifTrue:
[self assert: (objectMemory fetchPointer: MyListIndex ofObject: self activeProcess) = objectMemory nilObject.
+ "If we switch threads in threadSwitchIfNecessary:from:, the interpreter state is likely
+ already in the correct state.
+ In that case, there is no suspended context and nothing to restore. We can just continue
+ execution.
+ If there is a suspended context, assume that we need to restore the state from that."
(objectMemory fetchPointer: SuspendedContextIndex ofObject: activeProc) ~= objectMemory nilObject ifTrue:
[self externalSetStackPageAndPointersForSuspendedContextOfProcess: activeProc].
instructionPointer = cogit ceReturnToInterpreterPC ifTrue:
[self deny: (self isMachineCodeFrame: framePointer).
instructionPointer := self iframeSavedIP: framePointer].
self enterSmalltalkExecutive.
"When we return here we should have already given up
the VM and so we cannot touch any interpreter state."
self error: 'NOTREACHED'.].
cogThreadManager returnToSchedulingLoopAndWakeThreadFor: threadAffinity source: CSTryToExecuteSmalltalk.
"This is only reached if the above call has failed, then ownership has not been transferred and we still need to release the VM."!
Branch: refs/heads/Cog
Home: https://github.com/OpenSmalltalk/opensmalltalk-vm
Commit: 7c142515cb3eeca9ed99ce023feb45414d1efe8b
https://github.com/OpenSmalltalk/opensmalltalk-vm/commit/7c142515cb3eeca9ed…
Author: Eliot Miranda <eliot.miranda(a)gmail.com>
Date: 2024-03-19 (Tue, 19 Mar 2024)
Changed paths:
M platforms/win32/plugins/FilePlugin/sqWin32FilePrims.c
M platforms/win32/vm/sqImageFileAccess.h
M platforms/win32/vm/sqWin32Main.c
Log Message:
-----------
First cut of image-as-an-embedded-resource on Windows.
First cut because the command-line parsing issues aren't fixed yet.
But the image loading code works. ResourceHacker is used to add a
binary resource of type RCDATA and name SMALLTALKIMAGE. The relevant
file plugin sqImageFile*** functions check for a special file handle
and read from the resource if this handle is supplied.
To unsubscribe from these emails, change your notification settings at https://github.com/OpenSmalltalk/opensmalltalk-vm/settings/notifications