Esteban Lorenzano uploaded a new version of Cog to project VM Maker:
http://source.squeak.org/VMMaker/Cog.pharo-EstebanLorenzano.175.mcz
==================== Summary ====================
Name: Cog.pharo-EstebanLorenzano.175
Author: EstebanLorenzano
Time: 29 July 2014, 12:12:39.440319 pm
UUID: fe465b03-0277-4bc6-9b98-3af92cf63eb5
Ancestors: Cog-EstebanLorenzano.174
new merge with Eliot's
=============== Diff against Cog-eem.173 ===============
Item was changed:
+ SystemOrganization addCategory: #Cog!
+ SystemOrganization addCategory: 'Cog-Benchmarks-DeltaBlue'!
+ SystemOrganization addCategory: 'Cog-Benchmarks-Richards'!
+ SystemOrganization addCategory: 'Cog-Benchmarks-SMark'!
+ SystemOrganization addCategory: 'Cog-Benchmarks-Shootout'!
+ SystemOrganization addCategory: 'Cog-Bootstrapping'!
+ SystemOrganization addCategory: 'Cog-Morphing Bytecode Set'!
+ SystemOrganization addCategory: 'Cog-ProcessorPlugins'!
+ SystemOrganization addCategory: 'Cog-Processors'!
+ SystemOrganization addCategory: 'Cog-Processors-Tests'!
+ SystemOrganization addCategory: 'Cog-Scripting'!
+ SystemOrganization addCategory: 'Cog-Scripts'!
+ SystemOrganization addCategory: 'Cog-Tests'!
- SystemOrganization addCategory: #'Cog-Benchmarks-DeltaBlue'!
- SystemOrganization addCategory: #'Cog-Benchmarks-Richards'!
- SystemOrganization addCategory: #'Cog-Benchmarks-SMark'!
- SystemOrganization addCategory: #'Cog-Benchmarks-Shootout'!
- SystemOrganization addCategory: #'Cog-Bootstrapping'!
- SystemOrganization addCategory: #'Cog-Morphing Bytecode Set'!
- SystemOrganization addCategory: #'Cog-ProcessorPlugins'!
- SystemOrganization addCategory: #'Cog-Processors'!
- SystemOrganization addCategory: #'Cog-Processors-Tests'!
- SystemOrganization addCategory: #'Cog-Scripting'!
- SystemOrganization addCategory: #'Cog-Scripts'!
- SystemOrganization addCategory: #'Cog-Tests'!
Item was changed:
+ ----- Method: SpurBootstrap class>>CharacterPROTOTYPEclone (in category 'method prototypes squeak') -----
- ----- Method: SpurBootstrap class>>CharacterPROTOTYPEclone (in category 'method prototypes') -----
CharacterPROTOTYPEclone
"Answer the receiver, because Characters are unique."
^self!
Item was added:
+ ----- Method: SpurBootstrap class>>CharacterPROTOTYPEsetValue: (in category 'method prototypes') -----
+ CharacterPROTOTYPEsetValue: newValue
+ self error: 'Characters are immutable'!
Item was changed:
+ ----- Method: SpurBootstrap class>>CharacterPROTOTYPEshallowCopy (in category 'method prototypes pharo') -----
- ----- Method: SpurBootstrap class>>CharacterPROTOTYPEshallowCopy (in category 'method prototypes') -----
CharacterPROTOTYPEshallowCopy
"Answer the receiver, because Characters are unique."
^self!
Item was changed:
+ ----- Method: SpurBootstrap class>>ClassBuilderPROTOTYPEcomputeFormat:instSize:forSuper:ccIndex: (in category 'method prototypes') -----
- ----- Method: SpurBootstrap class>>ClassBuilderPROTOTYPEcomputeFormat:instSize:forSuper:ccIndex: (in category 'method prototypes squeak') -----
ClassBuilderPROTOTYPEcomputeFormat: type instSize: newInstSize forSuper: newSuper ccIndex: ccIndex
"Compute the new format for making oldClass a subclass of newSuper.
Answer the format or nil if there is any problem."
| instSize isVar isWords isPointers isWeak |
type == #compiledMethod ifTrue:
[newInstSize > 0 ifTrue:
[self error: 'A compiled method class cannot have named instance variables'.
^nil].
^CompiledMethod format].
instSize := newInstSize + (newSuper ifNil:[0] ifNotNil:[newSuper instSize]).
instSize > 65535 ifTrue:
[self error: 'Class has too many instance variables (', instSize printString,')'.
^nil].
type == #normal ifTrue:[isVar := isWeak := false. isWords := isPointers := true].
type == #bytes ifTrue:[isVar := true. isWords := isPointers := isWeak := false].
type == #words ifTrue:[isVar := isWords := true. isPointers := isWeak := false].
type == #variable ifTrue:[isVar := isPointers := isWords := true. isWeak := false].
type == #weak ifTrue:[isVar := isWeak := isWords := isPointers := true].
type == #ephemeron ifTrue:[isVar := false. isWeak := isWords := isPointers := true].
type == #immediate ifTrue:[isVar := isWeak := isPointers := false. isWords := true].
(isPointers not and: [instSize > 0]) ifTrue:
[self error: 'A non-pointer class cannot have named instance variables'.
^nil].
^self format: instSize variable: isVar words: isWords pointers: isPointers weak: isWeak!
Item was changed:
+ ----- Method: SpurBootstrap class>>ClassBuilderPROTOTYPEformat:variable:words:pointers:weak: (in category 'method prototypes') -----
- ----- Method: SpurBootstrap class>>ClassBuilderPROTOTYPEformat:variable:words:pointers:weak: (in category 'method prototypes squeak') -----
ClassBuilderPROTOTYPEformat: nInstVars variable: isVar words: isWords pointers: isPointers weak: isWeak
"Compute the format for the given instance specfication.
Above Cog Spur the class format is
<5 bits inst spec><16 bits inst size>
where the 5-bit inst spec is
0 = 0 sized objects (UndefinedObject True False et al)
1 = non-indexable objects with inst vars (Point et al)
2 = indexable objects with no inst vars (Array et al)
3 = indexable objects with inst vars (MethodContext AdditionalMethodState et al)
4 = weak indexable objects with inst vars (WeakArray et al)
5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron)
6 = unused
7 = immediates (SmallInteger, Character)
8 = unused
9 = reserved for 64-bit indexable
10-11 = 32-bit indexable (Bitmap)
12-15 = 16-bit indexable
16-23 = 8-bit indexable
24-31 = compiled methods (CompiledMethod)"
| instSpec |
instSpec := isWeak
ifTrue:
[isVar
ifTrue: [4]
ifFalse: [5]]
ifFalse:
[isPointers
ifTrue:
[isVar
ifTrue: [nInstVars > 0 ifTrue: [3] ifFalse: [2]]
ifFalse: [nInstVars > 0 ifTrue: [1] ifFalse: [0]]]
ifFalse:
[isVar
ifTrue: [isWords ifTrue: [12] ifFalse: [16]]
ifFalse: [7]]].
^(instSpec bitShift: 16) + nInstVars!
Item was changed:
+ ----- Method: SpurBootstrap class>>ClassBuilderPROTOTYPEsuperclass:immediateSubclass:instanceVariableNames:classVariableNames:poolDictionaries:category: (in category 'method prototypes') -----
- ----- Method: SpurBootstrap class>>ClassBuilderPROTOTYPEsuperclass:immediateSubclass:instanceVariableNames:classVariableNames:poolDictionaries:category: (in category 'method prototypes squeak') -----
ClassBuilderPROTOTYPEsuperclass: aClass
immediateSubclass: t instanceVariableNames: f
classVariableNames: d poolDictionaries: s category: cat
"This is the standard initialization message for creating a
new immediate class as a subclass of an existing class."
| env |
aClass instSize > 0
ifTrue: [^self error: 'cannot make an immediate subclass of a class with named fields'].
aClass isVariable
ifTrue: [^self error: 'cannot make an immediate subclass of a class with indexed instance variables'].
aClass isPointers
ifFalse: [^self error: 'cannot make an immediate subclass of a class without pointer fields'].
"Cope with pre-environment and environment versions. Simplify asap."
env := (Smalltalk classNamed: #EnvironmentRequest)
ifNil: [aClass environment]
ifNotNil: [:erc| erc signal ifNil: [aClass environment]].
^self
name: t
inEnvironment: env
subclassOf: aClass
type: #immediate
instanceVariableNames: f
classVariableNames: d
poolDictionaries: s
category: cat!
Item was changed:
+ ----- Method: SpurBootstrap class>>ClassBuilderPROTOTYPEupdate:to: (in category 'method prototypes') -----
- ----- Method: SpurBootstrap class>>ClassBuilderPROTOTYPEupdate:to: (in category 'method prototypes squeak') -----
ClassBuilderPROTOTYPEupdate: oldClass to: newClass
"Convert oldClass, all its instances and possibly its meta class into newClass,
instances of newClass and possibly its meta class. The process is surprisingly
simple in its implementation and surprisingly complex in its nuances and potentially
bad side effects.
We can rely on two assumptions (which are critical):
#1: The method #updateInstancesFrom: will not create any lasting pointers to
'old' instances ('old' is quote on quote since #updateInstancesFrom: will do
a become of the old vs. the new instances and therefore it will not create
pointers to *new* instances before the #become: which are *old* afterwards)
#2: The non-preemptive execution of the critical piece of code guarantees that
nobody can get a hold by 'other means' (such as process interruption and
reflection) on the old instances.
Given the above two, we know that after #updateInstancesFrom: there are no pointers
to any old instances. After the forwarding become there will be no pointers to the old
class or meta class either.
Andreas Raab, 2/27/2003 23:42"
| meta |
meta := oldClass isMeta.
"Note: Everything from here on will run without the ability to get interrupted
to prevent any other process to create new instances of the old class."
["Note: The following removal may look somewhat obscure and needs an explanation.
When we mutate the class hierarchy we create new classes for any existing subclass.
So it may look as if we don't have to remove the old class from its superclass. However,
at the top of the hierarchy (the first class we reshape) that superclass itself is not newly
created so therefore it will hold both the oldClass and newClass in its (obsolete or not)
subclasses. Since the #become: below will transparently replace the pointers to oldClass
with newClass the superclass would have newClass in its subclasses TWICE. With rather
unclear effects if we consider that we may convert the meta-class hierarchy itself (which
is derived from the non-meta class hierarchy).
Due to this problem ALL classes are removed from their superclass just prior to converting
them. Here, breaking the superclass/subclass invariant really doesn't matter since we will
effectively remove the oldClass (becomeForward:) just a few lines below."
oldClass superclass removeSubclass: oldClass.
oldClass superclass removeObsoleteSubclass: oldClass.
"make sure that the VM cache is clean"
oldClass methodDict do: [:cm | cm flushCache].
"Convert the instances of oldClass into instances of newClass"
newClass updateInstancesFrom: oldClass.
meta
ifTrue:
[oldClass becomeForward: newClass.
oldClass updateMethodBindingsTo: oldClass binding]
ifFalse:
[{oldClass. oldClass class} elementsForwardIdentityTo: {newClass. newClass class}.
oldClass updateMethodBindingsTo: oldClass binding.
oldClass class updateMethodBindingsTo: oldClass class binding].
"eem 5/31/2014 07:22 At this point there used to be a garbage collect whose purpose was
to ensure no old instances existed after the becomeForward:. Without the GC it was possible
to resurrect old instances using e.g. allInstancesDo:. This was because the becomeForward:
updated references from the old objects to new objects but didn't destroy the old objects.
But as of late 2013/early 2014 becomeForward: has been modified to free all the old objects."]
valueUnpreemptively!
Item was changed:
+ ----- Method: SpurBootstrap class>>IntegerclassPROTOTYPEinitialize (in category 'method prototypes squeak') -----
- ----- Method: SpurBootstrap class>>IntegerclassPROTOTYPEinitialize (in category 'method prototypes') -----
IntegerclassPROTOTYPEinitialize
"Integer initialize"
self initializeLowBitPerByteTable!
Item was changed:
----- Method: SpurBootstrap class>>SmalltalkImagePROTOTYPErecreateSpecialObjectsArray (in category 'method prototypes') -----
SmalltalkImagePROTOTYPErecreateSpecialObjectsArray
"Smalltalk recreateSpecialObjectsArray"
"To external package developers:
**** DO NOT OVERRIDE THIS METHOD. *****
If you are writing a plugin and need additional special object(s) for your own use,
use addGCRoot() function and use own, separate special objects registry "
"The Special Objects Array is an array of objects used by the Squeak virtual machine.
Its contents are critical and accesses to it by the VM are unchecked, so don't even
think of playing here unless you know what you are doing."
| newArray |
newArray := Array new: 60.
"Nil false and true get used throughout the interpreter"
newArray at: 1 put: nil.
newArray at: 2 put: false.
newArray at: 3 put: true.
"This association holds the active process (a ProcessScheduler)"
newArray at: 4 put: (self specialObjectsArray at: 4) "(self bindingOf: #Processor) but it answers an Alias".
"Numerous classes below used for type checking and instantiation"
newArray at: 5 put: Bitmap.
newArray at: 6 put: SmallInteger.
newArray at: 7 put: ByteString.
newArray at: 8 put: Array.
newArray at: 9 put: Smalltalk.
newArray at: 10 put: Float.
+ newArray at: 11 put: (self globals at: #Context).
+ newArray at: 12 put: nil. "was BlockContext."
- newArray at: 11 put: MethodContext.
- newArray at: 12 put: BlockContext.
newArray at: 13 put: Point.
newArray at: 14 put: LargePositiveInteger.
newArray at: 15 put: Display.
newArray at: 16 put: Message.
newArray at: 17 put: CompiledMethod.
+ newArray at: 18 put: (self specialObjectsArray
+ ifNotNil: [ self specialObjectsArray at: 18 ]
+ ifNil: [ Semaphore new ]). "(low space Semaphore)"
- newArray at: 18 put: (self specialObjectsArray at: 18). "(low space Semaphore)"
newArray at: 19 put: Semaphore.
newArray at: 20 put: Character.
newArray at: 21 put: #doesNotUnderstand:.
newArray at: 22 put: #cannotReturn:.
newArray at: 23 put: nil. "This is the process signalling low space."
"An array of the 32 selectors that are compiled as special bytecodes,
paired alternately with the number of arguments each takes."
newArray at: 24 put: #( #+ 1 #- 1 #< 1 #> 1 #<= 1 #>= 1 #= 1 #~= 1
#* 1 #/ 1 #\\ 1 #@ 1 #bitShift: 1 #// 1 #bitAnd: 1 #bitOr: 1
#at: 1 #at:put: 2 #size 0 #next 0 #nextPut: 1 #atEnd 0 #== 1 #class 0
#blockCopy: 1 #value 0 #value: 1 #do: 1 #new 0 #new: 1 #x 0 #y 0 ).
"An array of the 255 Characters in ascii order.
Cog inlines table into machine code at: prim so do not regenerate it.
This is nil in Spur, which has immediate Characters."
newArray at: 25 put: (self specialObjectsArray at: 25).
newArray at: 26 put: #mustBeBoolean.
newArray at: 27 put: ByteArray.
newArray at: 28 put: Process.
"An array of up to 31 classes whose instances will have compact headers; an empty array in Spur"
newArray at: 29 put: self compactClassesArray.
+ newArray at: 30 put: (self specialObjectsArray
+ ifNotNil: [ self specialObjectsArray at: 30 ]
+ ifNil: [ Semaphore new ]). "(delay Semaphore)"
+ newArray at: 31 put: (self specialObjectsArray
+ ifNotNil: [ self specialObjectsArray at: 31 ]
+ ifNil: [ Semaphore new ]). "(user interrupt Semaphore)"
- newArray at: 30 put: (self specialObjectsArray at: 30). "(delay Semaphore)"
- newArray at: 31 put: (self specialObjectsArray at: 31). "(user interrupt Semaphore)"
"Entries 32 - 34 unreferenced. Previously these contained prototype instances to be copied for fast initialization"
newArray at: 32 put: nil. "was the prototype Float"
newArray at: 33 put: nil. "was the prototype 4-byte LargePositiveInteger"
newArray at: 34 put: nil. "was the prototype Point"
newArray at: 35 put: #cannotInterpret:.
newArray at: 36 put: nil. "was the prototype MethodContext"
newArray at: 37 put: BlockClosure.
newArray at: 38 put: nil. "was the prototype BlockContext"
"array of objects referred to by external code"
newArray at: 39 put: (self specialObjectsArray at: 39). "external semaphores"
newArray at: 40 put: nil. "Reserved for Mutex in Cog VMs"
newArray at: 41 put: ((self specialObjectsArray at: 39) ifNil: [LinkedList new]). "Reserved for a LinkedList instance for overlapped calls in CogMT"
+ newArray at: 42 put: (self specialObjectsArray
+ ifNotNil: [ (self specialObjectsArray at: 42) ifNil: [ Semaphore new ] ]
+ ifNil: [ Semaphore new ]).
- newArray at: 42 put: ((self specialObjectsArray at: 42) ifNil: [Semaphore new]). "finalization Semaphore"
newArray at: 43 put: LargeNegativeInteger.
"External objects for callout.
Note: Written so that one can actually completely remove the FFI."
newArray at: 44 put: (self at: #ExternalAddress ifAbsent: []).
newArray at: 45 put: (self at: #ExternalStructure ifAbsent: []).
newArray at: 46 put: (self at: #ExternalData ifAbsent: []).
newArray at: 47 put: (self at: #ExternalFunction ifAbsent: []).
newArray at: 48 put: (self at: #ExternalLibrary ifAbsent: []).
newArray at: 49 put: #aboutToReturn:through:.
newArray at: 50 put: #run:with:in:.
"51 reserved for immutability message"
newArray at: 51 put: #attemptToAssign:withIndex:.
newArray at: 52 put: #(nil "nil => generic error" #'bad receiver'
#'bad argument' #'bad index'
#'bad number of arguments'
#'inappropriate operation' #'unsupported operation'
#'no modification' #'insufficient object memory'
#'insufficient C memory' #'not found' #'bad method'
#'internal error in named primitive machinery'
#'object may move' #'resource limit exceeded'
#'object is pinned' #'primitive write beyond end of object').
"53 to 55 are for Alien"
newArray at: 53 put: (self at: #Alien ifAbsent: []).
newArray at: 54 put: #invokeCallbackContext::. "use invokeCallback:stack:registers:jmpbuf: for old Alien callbacks."
newArray at: 55 put: (self at: #UnsafeAlien ifAbsent: []).
"Used to be WeakFinalizationList for WeakFinalizationList hasNewFinalization, obsoleted by ephemeron support."
newArray at: 56 put: nil.
"reserved for foreign callback process"
newArray at: 57 put: (self specialObjectsArray at: 57 ifAbsent: []).
newArray at: 58 put: #unusedBytecode.
"59 reserved for Sista counter tripped message"
newArray at: 59 put: #conditionalBranchCounterTrippedOn:.
"60 reserved for Sista class trap message"
newArray at: 60 put: #classTrapFor:.
"Now replace the interpreter's reference in one atomic operation"
self specialObjectsArray becomeForward: newArray!
Item was changed:
----- Method: SpurBootstrap>>addNewMethods (in category 'bootstrap methods') -----
addNewMethods
"Get the simulator to add any and all missing methods immediately."
| cmaiaSym basSym |
cmaiaSym := self findSymbol: #compiledMethodAt:ifAbsent:.
basSym := self findSymbol: #basicAddSelector:withMethod:.
basSym ifNil:
[basSym := self findSymbol: #addSelectorSilently:withMethod:].
self allPrototypeClassNamesDo:
[:sym :symIsMeta| | class |
class := self findClassNamed: (literalMap at: sym).
symIsMeta ifTrue: [class := oldHeap fetchClassOfNonImm: class].
self prototypeClassNameMetaSelectorMethodDo:
[:className :isMeta :selector :method| | methodOrNil |
(className = sym
and: [symIsMeta = isMeta]) ifTrue:
["probe method dictionary of the class for each method, installing a dummy if not found."
"Transcript cr; nextPutAll: 'checking for '; nextPutAll: selector; flush."
methodOrNil := self interpreter: oldInterpreter
object: class
perform: cmaiaSym
withArguments: {literalMap at: selector. oldHeap nilObject}.
methodOrNil = oldHeap nilObject
ifTrue: "no method. install the real thing now"
[Transcript
cr;
nextPutAll: 'installing ';
nextPutAll: className;
nextPutAll: (isMeta ifTrue: [' class>>'] ifFalse: ['>>']);
+ nextPutAll: selector printString;
- store: selector;
flush.
self interpreter: oldInterpreter
object: class
perform: basSym
withArguments: { literalMap at: selector.
self installableMethodFor: method
selector: selector
className: className
isMeta: isMeta}.
installedPrototypes add: method selector]
ifFalse: "existing method; collect the methodClassAssociation; its needed later"
[methodClasses add: (oldInterpreter methodClassAssociationOf: methodOrNil)]]]]!
Item was changed:
----- Method: SpurBootstrap>>bootstrapImageUsingFileReference: (in category 'public access') -----
bootstrapImageUsingFileReference: imageName
| dirName baseName dir |
dirName := imageName asFileReference parent fullName.
baseName := (imageName endsWith: '.image')
ifTrue: [ imageName asFileReference base ]
ifFalse: [ (imageName, '.image') asFileReference base ].
dir := dirName asFileReference.
self on: (dir / (baseName, '.image')) fullName.
[self transform]
on: Halt
do: [:ex|
"suppress halts from the usual suspects (development time halts)"
(#(fullGC compactImage) includes: ex signalerContext sender selector)
ifTrue: [ex resume]
ifFalse: [ex pass]].
self writeSnapshot: (dir / (baseName, '-spur.image')) fullName
ofTransformedImage: newHeap
headerFlags: oldInterpreter getImageHeaderFlags
screenSize: oldInterpreter savedWindowSize.
(dir / (baseName, '.changes')) copyTo: (dir / (baseName, '-spur.changes'))!
Item was changed:
----- Method: SpurBootstrap>>findRequiredGlobals (in category 'bootstrap image') -----
findRequiredGlobals
"Look for the necessary gobal bindings in the prototype methods in the old image.
This has to be done early by sending bindingOf: to Smalltalk. Also find out
Metaclass, needed for identofying classes."
| globals bindingOf |
globals := Set new.
self prototypeClassNameMetaSelectorMethodDo:
[:c :m :s :method|
globals addAll: (method literals select: [:l|
l isVariableBinding
and: [l key isSymbol
and: [(Smalltalk bindingOf: l key) == l]]])].
bindingOf := self findSymbol: #bindingOf:.
self withExecutableInterpreter: oldInterpreter
+ do: [globals asArray withIndexDo:
+ [:global :index|
- do: [globals do:
- [:global|
literalMap
at: global
put: (self interpreter: oldInterpreter
object: (oldHeap splObj: 8) "Smalltalk"
perform: bindingOf
withArguments: {self findSymbol: global key})]].
classMetaclass := oldHeap fetchClassOfNonImm: (oldHeap fetchClassOfNonImm: oldHeap classArray)!
Item was changed:
----- Method: SpurBootstrap>>rehashImage (in category 'bootstrap image') -----
rehashImage
"Rehash all collections in newHeap.
Find out which classes implement rehash, entering a 1 against their classIndex in rehashFlags.
Enumerate all objects, rehashing those whose class has a bit set in rehashFlags."
| n sim rehashFlags |
sim := StackInterpreterSimulator onObjectMemory: newHeap.
+ sim
+ setImageHeaderFlagsFrom: oldInterpreter getImageHeaderFlags;
+ imageName: 'spur image'.
- sim imageName: 'spur image'.
newHeap coInterpreter: sim.
sim bootstrapping: true.
sim initializeInterpreter: 0.
sim instVarNamed: 'methodDictLinearSearchLimit' put: SmallInteger maxVal.
newHeap
setHashBitsOf: newHeap nilObject to: 1;
setHashBitsOf: newHeap falseObject to: 2;
setHashBitsOf: newHeap trueObject to: 3.
rehashFlags := ByteArray new: newHeap numClassTablePages * newHeap classTablePageSize.
n := 0.
newHeap classTableObjectsDo:
[:class| | classIndex |
sim messageSelector: (map at: rehashSym).
"Lookup rehash but don't be fooled by ProtoObject>>rehash, which is just ^self."
((sim lookupMethodNoMNUEtcInClass: class) = 0
and: [(sim isQuickPrimitiveIndex: (sim primitiveIndexOf: (sim instVarNamed: 'newMethod'))) not]) ifTrue:
[n := n + 1.
classIndex := newHeap rawHashBitsOf: class.
rehashFlags
at: classIndex >> 3 + 1
put: ((rehashFlags at: classIndex >> 3 + 1)
bitOr: (1 << (classIndex bitAnd: 7)))]].
Transcript cr; print: n; nextPutAll: ' classes understand rehash. rehashing instances...'; flush.
n := 0.
self withExecutableInterpreter: sim
do: [sim setBreakSelector: 'error:'.
"don't rehash twice (actually without limit), so don't rehash any new objects created."
newHeap allExistingOldSpaceObjectsDo:
[:o| | classIndex |
classIndex := newHeap classIndexOf: o.
((rehashFlags at: classIndex >> 3 + 1) anyMask: 1 << (classIndex bitAnd: 7)) ifTrue:
[(n := n + 1) \\ 8 = 0 ifTrue:
[Transcript nextPut: $.; flush].
"2845 = n ifTrue: [self halt]."
"Rehash an object if its size is > 0.
Symbol implements rehash, but let's not waste time rehashing it; in Squeak
up to 2013 symbols are kept in a set which will get reashed anyway..
Don't rehash empty collections; they may be large for a reason and rehashing will shrink them."
((sim addressCouldBeClassObj: o)
or: [(self interpreter: sim
object: o
perform: (map at: sizeSym)
withArguments: #()) = (newHeap integerObjectOf: 0)]) ifFalse:
[self interpreter: sim
object: o
perform: (map at: rehashSym)
withArguments: #()]]]]!
Item was changed:
----- Method: SpurBootstrap>>replaceMethods (in category 'bootstrap methods') -----
replaceMethods
"Replace all the modified method prototypes."
self allPrototypeClassNamesDo:
[:sym :symIsMeta| | class |
class := self findClassNamed: (literalMap at: sym).
symIsMeta ifTrue: [class := oldHeap fetchClassOfNonImm: class].
self prototypeClassNameMetaSelectorMethodDo:
[:className :isMeta :selector :method| | replacement methodDict index |
(className = sym
and: [symIsMeta = isMeta]) ifTrue:
[(installedPrototypes includes: method selector) ifFalse:
["probe method dictionary of the class for each method, installing a dummy if not found."
Transcript
cr;
nextPutAll: 'replacing ';
nextPutAll: className;
nextPutAll: (isMeta ifTrue: [' class>>'] ifFalse: ['>>']);
+ nextPutAll: selector printString;
- store: selector;
flush.
replacement := self installableMethodFor: method
selector: selector
className: className
isMeta: isMeta.
methodDict := oldHeap fetchPointer: MethodDictionaryIndex ofObject: class.
index := self indexOfSelector: (literalMap at: selector) in: methodDict.
oldHeap
storePointer: index - SelectorStart
ofObject: (oldHeap fetchPointer: MethodArrayIndex ofObject: methodDict)
withValue: replacement.
installedPrototypes add: method selector]]]]!
Item was changed:
----- Method: SpurBootstrap>>withExecutableInterpreter:do: (in category 'bootstrap methods') -----
withExecutableInterpreter: sim do: aBlock
"With the oldInterpreter ready to execute code, evaluate aBlock,
then return the interpreter (and the heap) to the ``just snapshotted'' state."
| savedpc initialContext finalContext |
sim
initStackPages;
loadInitialContext;
internalizeIPandSP.
initialContext := sim frameContext: sim localFP.
savedpc := sim localIP.
"sim printHeadFrame."
aBlock value.
"sim printHeadFrame."
sim
internalPush: sim localIP;
externalizeIPandSP.
"now undo the execution state"
finalContext := sim voidVMStateForSnapshotFlushingExternalPrimitivesIf: false.
self assert: initialContext = finalContext.
self assert: sim localIP = savedpc.
sim objectMemory
storePointer: SuspendedContextIndex
ofObject: sim activeProcess
withValue: finalContext!
Eliot Miranda uploaded a new version of BytecodeSets to project VM Maker:
http://source.squeak.org/VMMaker/BytecodeSets-eem.6.mcz
==================== Summary ====================
Name: BytecodeSets-eem.6
Author: eem
Time: 28 July 2014, 8:52:49.245 pm
UUID: 720d4320-af8e-4389-b22c-c4249d29f4ea
Ancestors: BytecodeSets-eem.5
Use extB for extPushPseudoVariable to match NewsqueakV4
=============== Diff against BytecodeSets-eem.5 ===============
Item was changed:
BytecodeEncoder subclass: #EncoderForSistaV1
(excessive size, no diff calculated)
Item was changed:
----- Method: EncoderForSistaV1>>genPushThisContext (in category 'bytecode generation') -----
genPushThisContext
+ "82 01010010 Push thisContext, (then e.g. Extend B 1 = push thisProcess)"
- "82 01010010 Push thisContext, (then e.g. Extend 1 = push thisProcess)"
stream nextPut: 82!
Eliot Miranda uploaded a new version of BytecodeSets to project VM Maker:
http://source.squeak.org/VMMaker/BytecodeSets-eem.5.mcz
==================== Summary ====================
Name: BytecodeSets-eem.5
Author: eem
Time: 28 July 2014, 6:51:38.277 pm
UUID: 80859714-b8e4-4799-8eec-5df0c9eef0cb
Ancestors: BytecodeSets-eem.4
Change the inline rem: prim to an inline quo: prim,. There is no
proimitive support for rem: but quo: is expected. We can
always add rem: later.
=============== Diff against BytecodeSets-eem.4 ===============
Item was changed:
BytecodeEncoder subclass: #EncoderForSistaV1
(excessive size, no diff calculated)
Eliot Miranda uploaded a new version of BytecodeSets to project VM Maker:
http://source.squeak.org/VMMaker/BytecodeSets-eem.4.mcz
==================== Summary ====================
Name: BytecodeSets-eem.4
Author: eem
Time: 27 July 2014, 6:54:11.684 pm
UUID: 7db73403-61ca-43c9-8a7b-ff758a61cec2
Ancestors: BytecodeSets-eem.3
Fix a few slips in the SistaV1 definition in EncoderForSistaV1's
class comment.
=============== Diff against BytecodeSets-eem.3 ===============
Item was changed:
BytecodeEncoder subclass: #EncoderForSistaV1
(excessive size, no diff calculated)
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.835.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.835
Author: eem
Time: 26 July 2014, 7:59:57.815 am
UUID: 4bf1a46a-22b3-4062-aafa-acba4fd5177a
Ancestors: VMMaker.oscog-eem.834
Sista:
Implement extTrapIfNotInstanceOfBehaviorsBytecode.
Needs fixes to inlining below.
Fix slip in CoInterpreter>>ceCounterTripped: that would
break Spur (classForClassTag: instead of classTagForClass:).
Slang:
Fix inlining of shared case code for cases where control
does flow through to the end of the bytecode (i.e.
extTrapIfNotInstanceOfBehaviorsBytecode).
Simplify the shared code pragma allowing simply
<sharedCodeInCase: #destinationSelector>
=============== Diff against VMMaker.oscog-eem.834 ===============
Item was changed:
----- Method: CoInterpreter>>ceCounterTripped: (in category 'cog jit support') -----
ceCounterTripped: condition
"Two things are going on here. The main one is catching a counter trip and attempting
to send the SelectorCounterTripped selector. In this case we would like to back-up
the pc to the return address of the send that yields the boolean to be tested, so that
after potential optimization, computation proceeds by retrying the jump. But we cannot,
since there may be no send, just a pop (as in and: [] and or: [] chains). In this case we also
want to prevent further callbacks until optimization is complete. So we nil-out the
SelectorCounterTripped entry in the specialSelectorArray.
The minor case is that there is an unlikely possibility that the cointer tripped but condition
is not a boolean, in which case a mustBeBoolean response should occur."
<api>
<option: #SistaStackToRegisterMappingCogit>
"Send e.g. thisContext conditionalBranchCounterTrippedOn: boolean."
| context counterTrippedSelector classTag |
(condition = objectMemory falseObject
or: [condition = objectMemory trueObject]) ifFalse:
[^self ceSendMustBeBoolean: condition].
counterTrippedSelector := objectMemory maybeSplObj: SelectorCounterTripped.
(counterTrippedSelector isNil
or: [counterTrippedSelector = objectMemory nilObject]) ifTrue:
[cogit resetCountersIn: (self mframeHomeMethod: framePointer).
^condition].
classTag := objectMemory
classTagForSpecialObjectsIndex: ClassMethodContext
compactClassIndex: ClassMethodContextCompactIndex.
(self lookupInMethodCacheSel: counterTrippedSelector classTag: classTag) ifFalse:
[messageSelector := counterTrippedSelector.
+ (self lookupMethodNoMNUEtcInClass: (objectMemory classForClassTag: classTag)) ~= 0 ifTrue:
- (self lookupMethodNoMNUEtcInClass: (objectMemory classTagForClass: classTag)) ~= 0 ifTrue:
[cogit resetCountersIn: (self mframeHomeMethod: framePointer).
^condition]].
(primitiveFunctionPointer ~= 0
or: [(self argumentCountOf: newMethod) ~= 1]) ifTrue:
[cogit resetCountersIn: (self mframeHomeMethod: framePointer).
^condition].
objectMemory splObj: SelectorCounterTripped put: objectMemory nilObject.
instructionPointer := self popStack.
context := self ensureFrameIsMarried: framePointer SP: stackPointer.
self push: context.
self push: condition.
self ifAppropriateCompileToNativeCode: newMethod selector: counterTrippedSelector.
self activateNewMethod.
"not reached"
^true!
Item was added:
+ ----- Method: NewObjectMemory>>rawClassTagForClass: (in category 'interpreter access') -----
+ rawClassTagForClass: classObj
+ "Compatibility with SpurObjectMemory. In ObjectMemory there is no distinction between a
+ classTag in the first-level method cache and a class itself."
+ ^classObj!
Item was added:
+ ----- Method: ObjectMemory>>is:instanceOf: (in category 'header access') -----
+ is: oop instanceOf: classOop
+ "Answer if oop is an instance of the given class. If the class has a (non-zero)
+ compactClassIndex use that to speed up the check."
+
+ <inline: true>
+ (self isIntegerObject: oop) ifTrue:
+ [^classOop = (self splObj: ClassSmallInteger)].
+
+ ^self isClassOfNonImm: oop equalTo: classOop!
Item was added:
+ ----- Method: Spur32BitMemoryManager>>fetchClassTagOf: (in category 'interpreter access') -----
+ fetchClassTagOf: oop
+ | tagBits |
+ (tagBits := oop bitAnd: self tagMask) ~= 0 ifTrue:
+ [^(tagBits bitAnd: 1) ~= 0 ifTrue: [1] ifFalse: [tagBits]].
+ ^self classIndexOf: oop!
Item was added:
+ ----- Method: Spur64BitMemoryManager>>fetchClassTagOf: (in category 'interpreter access') -----
+ fetchClassTagOf: oop
+ | tagBits |
+ ^(tagBits := oop bitAnd: self tagMask) ~= 0
+ ifTrue: [tagBits]
+ ifFalse: [self classIndexOf: oop]!
Item was changed:
----- Method: SpurMemoryManager>>fetchClassTagOf: (in category 'interpreter access') -----
fetchClassTagOf: oop
+ self subclassResponsibility!
- | tagBits |
- (tagBits := oop bitAnd: self tagMask) ~= 0 ifTrue:
- [^(tagBits bitAnd: 1) ~= 0 ifTrue: [1] ifFalse: [tagBits]].
- ^self classIndexOf: oop!
Item was added:
+ ----- Method: SpurMemoryManager>>is:instanceOf: (in category 'object access') -----
+ is: oop instanceOf: classOop
+ "Answer if oop is an instance of the given class."
+
+ <inline: true>
+ | tag |
+ tag := self fetchClassTagOf: oop.
+ ^tag = (self rawHashBitsOf: classOop)!
Item was added:
+ ----- Method: SpurMemoryManager>>rawClassTagForClass: (in category 'interpreter access') -----
+ rawClassTagForClass: classObj
+ "Answer the classObj's identityHash to use as a tag in a class comparison."
+ ^self rawHashBitsOf: classObj!
Item was changed:
----- Method: StackInterpreter class>>initializeBytecodeTableForSistaV1 (in category 'initialization') -----
initializeBytecodeTableForSistaV1
"See e.g. the cass comment for EncoderForSistaV1"
"StackInterpreter initializeBytecodeTableForSistaV1"
"Note: This table will be used to generate a C switch statement."
BytecodeTable := Array new: 256.
self table: BytecodeTable from:
#( "1 byte bytecodes"
( 0 15 pushReceiverVariableBytecode)
( 16 31 pushLiteralVariable16CasesBytecode)
( 32 63 pushLiteralConstantBytecode)
( 64 75 pushTemporaryVariableBytecode)
( 76 pushReceiverBytecode)
( 77 pushConstantTrueBytecode)
( 78 pushConstantFalseBytecode)
( 79 pushConstantNilBytecode)
( 80 pushConstantZeroBytecode)
( 81 pushConstantOneBytecode)
( 82 extPushPseudoVariable)
( 83 duplicateTopBytecode)
( 84 87 unknownBytecode)
( 88 returnReceiver)
( 89 returnTrue)
( 90 returnFalse)
( 91 returnNil)
( 92 returnTopFromMethod)
( 93 returnNilFromBlock)
( 94 returnTopFromBlock)
( 95 extNopBytecode)
( 96 bytecodePrimAdd)
( 97 bytecodePrimSubtract)
( 98 bytecodePrimLessThanSistaV1) "for booleanCheatSistaV1:"
( 99 bytecodePrimGreaterThanSistaV1) "for booleanCheatSistaV1:"
(100 bytecodePrimLessOrEqualSistaV1) "for booleanCheatSistaV1:"
(101 bytecodePrimGreaterOrEqualSistaV1) "for booleanCheatSistaV1:"
(102 bytecodePrimEqualSistaV1) "for booleanCheatSistaV1:"
(103 bytecodePrimNotEqualSistaV1) "for booleanCheatSistaV1:"
(104 bytecodePrimMultiply)
(105 bytecodePrimDivide)
(106 bytecodePrimMod)
(107 bytecodePrimMakePoint)
(108 bytecodePrimBitShift)
(109 bytecodePrimDiv)
(110 bytecodePrimBitAnd)
(111 bytecodePrimBitOr)
(112 bytecodePrimAt)
(113 bytecodePrimAtPut)
(114 bytecodePrimSize)
(115 bytecodePrimNext) "i.e. a 0 arg special selector"
(116 bytecodePrimNextPut) "i.e. a 1 arg special selector"
(117 bytecodePrimAtEnd)
(118 bytecodePrimIdenticalSistaV1) "for booleanCheatSistaV1:"
(119 bytecodePrimClass)
(120 bytecodePrimSpecialSelector24) "was blockCopy:"
(121 bytecodePrimValue)
(122 bytecodePrimValueWithArg)
(123 bytecodePrimDo) "i.e. a 1 arg special selector"
(124 bytecodePrimNew) "i.e. a 0 arg special selector"
(125 bytecodePrimNewWithArg) "i.e. a 1 arg special selector"
(126 bytecodePrimPointX) "i.e. a 0 arg special selector"
(127 bytecodePrimPointY) "i.e. a 0 arg special selector"
(128 143 sendLiteralSelector0ArgsBytecode)
(144 159 sendLiteralSelector1ArgBytecode)
(160 175 sendLiteralSelector2ArgsBytecode)
(176 183 shortUnconditionalJump)
(184 191 shortConditionalJumpTrue)
(192 199 shortConditionalJumpFalse)
(200 207 storeAndPopReceiverVariableBytecode)
(208 215 storeAndPopTemporaryVariableBytecode)
(216 popStackBytecode)
(217 223 unknownBytecode)
"2 byte bytecodes"
(224 extABytecode)
(225 extBBytecode)
(226 extPushReceiverVariableBytecode)
(227 extPushLiteralVariableBytecode)
(228 extPushLiteralBytecode)
(229 longPushTemporaryVariableBytecode)
(230 pushClosureTempsBytecode)
(231 pushNewArrayBytecode)
(232 extPushIntegerBytecode)
(233 extPushCharacterBytecode)
(234 extSendBytecode)
(235 extSendSuperBytecode)
+ (236 extTrapIfNotInstanceOfBehaviorsBytecode)
- (236 extTrapOnBehaviorsBytecode)
(237 extUnconditionalJump)
(238 extJumpIfTrue)
(239 extJumpIfFalse)
(240 extStoreAndPopReceiverVariableBytecode)
(241 extStoreAndPopLiteralVariableBytecode)
(242 longStoreAndPopTemporaryVariableBytecode)
(243 extStoreReceiverVariableBytecode)
(244 extStoreLiteralVariableBytecode)
(245 longStoreTemporaryVariableBytecode)
(246 247 unknownBytecode)
"3 byte bytecodes"
(248 callPrimitiveBytecode)
(249 unknownBytecode) "reserved for Push Float"
(250 extPushClosureBytecode)
(251 pushRemoteTempLongBytecode)
(252 storeRemoteTempLongBytecode)
(253 storeAndPopRemoteTempLongBytecode)
(254 255 unknownBytecode)
)!
Item was changed:
----- Method: StackInterpreter class>>preambleCCode (in category 'translation') -----
preambleCCode
^
'/* Disable Intel compiler inlining of warning which is used for breakpoints */
#pragma auto_inline off
sqInt warnpid;
void
warning(char *s) { /* Print an error message but don''t exit. */
if (warnpid)
+ printf("\n%s pid %ld\n", s, (long)warnpid);
- printf("\n%s pid %ld\n", s, warnpid);
else
printf("\n%s\n", s);
}
void
warningat(char *s, int l) { /* ditto with line number. */
/* use alloca to call warning so one does not have to remember to set two breakpoints... */
char *sl = alloca(strlen(s) + 16);
sprintf(sl, "%s %d", s, l);
warning(sl);
}
#pragma auto_inline on
void
invalidCompactClassError(char *s) { /* Print a (compact) class index error message and exit. */
#if SPURVM
printf("\nClass %s does not have the required class index\n", s);
#else
printf("\nClass %s does not have the required compact class index\n", s);
#endif
exit(-1);
}
/*
* Define sigsetjmp and siglongjmp to be the most minimal setjmp/longjmp available on the platform.
*/
#if WIN32
# define sigsetjmp(jb,ssmf) setjmp(jb)
# define siglongjmp(jb,v) longjmp(jb,v)
#else
# define sigsetjmp(jb,ssmf) _setjmp(jb)
# define siglongjmp(jb,v) _longjmp(jb,v)
#endif
'!
Item was changed:
----- Method: StackInterpreter>>booleanCheatFalse (in category 'utilities') -----
booleanCheatFalse
"cheat the interpreter out of the pleasure of handling the next bytecode IFF it is a jump-on-boolean. Which it is, often enough when the current bytecode is something like bytecodePrimEqual"
+ <sharedCodeInCase: #bytecodePrimGreaterThan>
- <sharedCodeNamed: 'booleanCheatFalse' inCase: #bytecodePrimGreaterThan>
| bytecode offset |
bytecode := self fetchByte. "assume next bytecode is jumpIfFalse (99%)"
self internalPop: 2.
(bytecode < 160 and: [bytecode > 151]) ifTrue: "short jumpIfFalse"
[^self jump: bytecode - 151].
bytecode = 172 ifTrue: "long jumpIfFalse"
[offset := self fetchByte.
^self jump: offset].
"not followed by a jumpIfFalse; (un)do instruction fetch and push boolean result"
self cppIf: MULTIPLEBYTECODESETS
ifTrue: [currentBytecode := bytecode + bytecodeSetSelector]
ifFalse: [currentBytecode := bytecode].
self internalPush: objectMemory falseObject!
Item was changed:
----- Method: StackInterpreter>>booleanCheatFalseSistaV1 (in category 'utilities') -----
booleanCheatFalseSistaV1
"cheat the interpreter out of the pleasure of handling the next bytecode IFF it is a jump-on-boolean. Which it is, often enough when the current bytecode is something like bytecodePrimEqual"
+ <sharedCodeInCase: #bytecodePrimGreaterThanSistaV1>
- <sharedCodeNamed: 'booleanCheatFalseSistaV1' inCase: #bytecodePrimGreaterThanSistaV1>
| bytecode offset |
bytecode := self fetchByte. "assume next bytecode is jumpIfFalse (99%)"
self internalPop: 2.
(bytecode < 199 and: [bytecode > 191]) ifTrue: "short jumpIfFalse"
[^self jump: bytecode - 191].
bytecode = 239 ifTrue: "long jumpIfFalse"
[offset := self fetchByte.
^self jump: offset].
"not followed by a jumpIfFalse; (un)do instruction fetch and push boolean result"
self cppIf: MULTIPLEBYTECODESETS
ifTrue: [currentBytecode := bytecode + bytecodeSetSelector]
ifFalse: [currentBytecode := bytecode].
self internalPush: objectMemory falseObject!
Item was changed:
----- Method: StackInterpreter>>booleanCheatFalseV4 (in category 'utilities') -----
booleanCheatFalseV4
"cheat the interpreter out of the pleasure of handling the next bytecode IFF it is a jump-on-boolean. Which it is, often enough when the current bytecode is something like bytecodePrimEqual"
+ <sharedCodeInCase: #bytecodePrimGreaterThanV4>
- <sharedCodeNamed: 'booleanCheatFalseV4' inCase: #bytecodePrimGreaterThanV4>
| bytecode offset |
bytecode := self fetchByte. "assume next bytecode is jumpIfFalse (99%)"
self internalPop: 2.
(bytecode < 216 and: [bytecode > 207]) ifTrue: "short jumpIfFalse"
[^self jump: bytecode - 207].
bytecode = 244 ifTrue: "long jumpIfFalse"
[offset := self fetchByte.
^self jump: offset].
"not followed by a jumpIfFalse; (un)do instruction fetch and push boolean result"
self cppIf: MULTIPLEBYTECODESETS
ifTrue: [currentBytecode := bytecode + bytecodeSetSelector]
ifFalse: [currentBytecode := bytecode].
self internalPush: objectMemory falseObject!
Item was changed:
----- Method: StackInterpreter>>booleanCheatTrue (in category 'utilities') -----
booleanCheatTrue
"cheat the interpreter out of the pleasure of handling the next bytecode IFF it is a jump-on-boolean. Which it is, often enough when the current bytecode is something like bytecodePrimEqual"
+ <sharedCodeInCase: #bytecodePrimLessThan>
- <sharedCodeNamed: 'booleanCheatTrue' inCase: #bytecodePrimLessThan>
| bytecode offset |
bytecode := self fetchByte. "assume next bytecode is jumpIfFalse (99%)"
self internalPop: 2.
(bytecode < 173 and: [bytecode > 151]) ifTrue:
[bytecode < 160 ifTrue: "short jumpIfFalse 152 - 159"
[^self fetchNextBytecode].
bytecode = 172 ifTrue: "long jumpIfFalse"
[self fetchByte.
^self fetchNextBytecode].
bytecode > 167 ifTrue: "long jumpIfTrue 168 - 171"
[offset := bytecode - 168 << 8 + self fetchByte.
^self jump: offset]].
"not followed by a jumpIfFalse; (un)do instruction fetch and push boolean result"
self cppIf: MULTIPLEBYTECODESETS
ifTrue: [currentBytecode := bytecode + bytecodeSetSelector]
ifFalse: [currentBytecode := bytecode].
self internalPush: objectMemory trueObject!
Item was changed:
----- Method: StackInterpreter>>booleanCheatTrueSistaV1 (in category 'utilities') -----
booleanCheatTrueSistaV1
"cheat the interpreter out of the pleasure of handling the next bytecode IFF it is a jump-on-boolean. Which it is, often enough when the current bytecode is something like bytecodePrimEqual"
+ <sharedCodeInCase: #bytecodePrimLessThanSistaV1>
- <sharedCodeNamed: 'booleanCheatTrueSistaV1' inCase: #bytecodePrimLessThanSistaV1>
| bytecode offset |
bytecode := self fetchByte. "assume next bytecode is jumpIfFalse (99%)"
self internalPop: 2.
bytecode >= 192 ifTrue:
[bytecode <= 199 ifTrue: "short jumpIfFalse 192 - 199"
[^self fetchNextBytecode].
bytecode = 239 ifTrue: "long jumpIfFalse"
[self fetchByte.
^self fetchNextBytecode].
bytecode = 238 ifTrue: "long jumpIfTrue 238"
[offset := self fetchByte.
^self jump: offset]].
"not followed by a jumpIfFalse; (un)do instruction fetch and push boolean result"
self cppIf: MULTIPLEBYTECODESETS
ifTrue: [currentBytecode := bytecode + bytecodeSetSelector]
ifFalse: [currentBytecode := bytecode].
self internalPush: objectMemory trueObject!
Item was changed:
----- Method: StackInterpreter>>booleanCheatTrueV4 (in category 'utilities') -----
booleanCheatTrueV4
"cheat the interpreter out of the pleasure of handling the next bytecode IFF it is a jump-on-boolean. Which it is, often enough when the current bytecode is something like bytecodePrimEqual"
+ <sharedCodeInCase: #bytecodePrimLessThanV4>
- <sharedCodeNamed: 'booleanCheatTrueV4' inCase: #bytecodePrimLessThanV4>
| bytecode offset |
bytecode := self fetchByte. "assume next bytecode is jumpIfFalse (99%)"
self internalPop: 2.
bytecode >= 208 ifTrue:
[bytecode <= 215 ifTrue: "short jumpIfFalse 208 - 215"
[^self fetchNextBytecode].
bytecode = 244 ifTrue: "long jumpIfFalse"
[self fetchByte.
^self fetchNextBytecode].
bytecode = 243 ifTrue: "long jumpIfTrue 243"
[offset := self fetchByte.
^self jump: offset]].
"not followed by a jumpIfFalse; (un)do instruction fetch and push boolean result"
self cppIf: MULTIPLEBYTECODESETS
ifTrue: [currentBytecode := bytecode + bytecodeSetSelector]
ifFalse: [currentBytecode := bytecode].
self internalPush: objectMemory trueObject!
Item was changed:
----- Method: StackInterpreter>>commonCallerReturn (in category 'return bytecodes') -----
commonCallerReturn
"Return to the previous context/frame (sender for method activations, caller for block activations)."
+ <sharedCodeInCase: #returnTopFromBlock>
- <sharedCodeNamed: 'commonCallerReturn' inCase: #returnTopFromBlock>
| callersFPOrNull |
<var: #callersFPOrNull type: #'char *'>
callersFPOrNull := self frameCallerFP: localFP.
callersFPOrNull == 0 "baseFrame" ifTrue:
[self assert: localFP = stackPage baseFP.
^self baseFrameReturn].
localIP := self frameCallerSavedIP: localFP.
localSP := localFP + (self frameStackedReceiverOffset: localFP).
localFP := callersFPOrNull.
self setMethod: (self frameMethod: localFP).
self fetchNextBytecode.
self internalStackTopPut: localReturnValue!
Item was changed:
----- Method: StackInterpreter>>commonReturn (in category 'return bytecodes') -----
commonReturn
"Do an ^-return (return form method), perhaps checking for unwinds if this is a block activation.
Note: Assumed to be inlined into the dispatch loop."
+ <sharedCodeInCase: #returnReceiver>
- <sharedCodeNamed: 'commonReturn' inCase: #returnReceiver>
| closure home unwindContextOrNilOrZero frameToReturnTo contextToReturnTo theFP callerFP newPage |
<var: #frameToReturnTo type: #'char *'>
<var: #theFP type: #'char *'>
<var: #callerFP type: #'char *'>
<var: #newPage type: #'StackPage *'>
<var: #thePage type: #'StackPage *'>
"If this is a method simply return to the sender/caller."
(self frameIsBlockActivation: localFP) ifFalse:
[^self commonCallerReturn].
"Since this is a block activation the closure is on the stack above any args and the frame."
closure := self pushedReceiverOrClosureOfFrame: localFP.
home := nil.
"Walk the closure's lexical chain to find the context or frame to return from (home)."
[closure ~= objectMemory nilObject] whileTrue:
[home := objectMemory fetchPointer: ClosureOuterContextIndex ofObject: closure.
closure := objectMemory fetchPointer: ClosureIndex ofObject: home].
"home is to be returned from provided there is no unwind-protect activation between
this frame and home's sender. Search for an unwind. findUnwindThroughContext:
will answer either the context for an unwind-protect activation or nilObj if the sender
cannot be found or 0 if no unwind is found but the sender is. We must update the
current page's headFrame pointers to enable the search to identify widowed contexts
correctly."
self writeBackHeadFramePointers.
unwindContextOrNilOrZero := self internalFindUnwindThroughContext: home.
unwindContextOrNilOrZero = objectMemory nilObject ifTrue:
["error: can't find home on chain; cannot return"
^self internalCannotReturn: localReturnValue].
unwindContextOrNilOrZero ~= 0 ifTrue:
[^self internalAboutToReturn: localReturnValue through: unwindContextOrNilOrZero].
"Now we know home is on the sender chain.
We could be returning to either a context or a frame. Find out which."
contextToReturnTo := nil.
(self isMarriedOrWidowedContext: home)
ifTrue:
[self assert: (self checkIsStillMarriedContext: home currentFP: localFP).
theFP := self frameOfMarriedContext: home.
(self isBaseFrame: theFP)
ifTrue:
[contextToReturnTo := self frameCallerContext: theFP]
ifFalse:
[frameToReturnTo := self frameCallerFP: theFP]]
ifFalse:
[contextToReturnTo := objectMemory fetchPointer: SenderIndex ofObject: home.
((objectMemory isContext: contextToReturnTo)
and: [self isMarriedOrWidowedContext: contextToReturnTo]) ifTrue:
[self assert: (self checkIsStillMarriedContext: contextToReturnTo currentFP: localFP).
frameToReturnTo := self frameOfMarriedContext: contextToReturnTo.
contextToReturnTo := nil]].
"If returning to a context we must make a frame for it unless it is dead."
contextToReturnTo ~= nil ifTrue:
[frameToReturnTo := self establishFrameForContextToReturnTo: contextToReturnTo.
frameToReturnTo == 0 ifTrue:
["error: home's sender is dead; cannot return"
^self internalCannotReturn: localReturnValue]].
"Now we have a frame to return to. If it is on a different page we must free intervening pages and
nil out intervening contexts. We must free intervening stack pages because if we leave the pages
to be divorced then their contexts will be divorced with intact senders and instruction pointers. This
code is similar to primitiveTerminateTo. We must move any frames on itervening pages above the
frame linked to because these may be in use, e.g. via co-routining (see baseFrameReturn)."
self assert: stackPages pageListIsWellFormed.
newPage := stackPages stackPageFor: frameToReturnTo.
newPage ~~ stackPage ifTrue:
[| currentCtx thePage nextCntx |
currentCtx := self frameCallerContext: stackPage baseFP.
self assert: (objectMemory isContext: currentCtx).
stackPages freeStackPage: stackPage.
[self assert: (objectMemory isContext: currentCtx).
(self isMarriedOrWidowedContext: currentCtx)
and: [(stackPages stackPageFor: (theFP := self frameOfMarriedContext: currentCtx)) = newPage]] whileFalse:
[(self isMarriedOrWidowedContext: currentCtx)
ifTrue:
[thePage := stackPages stackPageFor: theFP.
theFP ~= thePage headFP ifTrue:
["Since we've just deallocated a page we know that newStackPage won't deallocate an existing one."
self moveFramesIn: thePage through: (self findFrameAbove: theFP inPage: thePage) toPage: self newStackPage].
currentCtx := self frameCallerContext: thePage baseFP.
stackPages freeStackPage: thePage]
ifFalse:
[nextCntx := objectMemory fetchPointer: SenderIndex ofObject: currentCtx.
self markContextAsDead: currentCtx.
currentCtx := nextCntx]].
self setStackPageAndLimit: newPage.
localSP := stackPage headSP.
localFP := stackPage headFP].
"Two cases. Returning to the top frame on a new page or an interior frame on the current page.
The top frame has its instruction pointer on top of stack. An interior frame has its instruction pointer
in the caller frame. We need to peel back any frames on the page until we get to the correct frame."
localFP = frameToReturnTo
ifTrue: "pop the saved IP, push the return value and continue."
[localIP := self pointerForOop: self internalStackTop]
ifFalse:
[[callerFP := localFP.
localFP := self frameCallerFP: localFP.
localFP ~~ frameToReturnTo] whileTrue.
localIP := self frameCallerSavedIP: callerFP.
localSP := (self frameCallerSP: callerFP) - BytesPerWord].
self setMethod: (self frameMethod: localFP).
self fetchNextBytecode.
^self internalStackTopPut: localReturnValue!
Item was changed:
----- Method: StackInterpreter>>commonSend (in category 'send bytecodes') -----
commonSend
"Send a message, starting lookup with the receiver's class."
"Assume: messageSelector and argumentCount have been set, and that
the receiver and arguments have been pushed onto the stack,"
"Note: This method is inlined into the interpreter dispatch loop."
+ <sharedCodeInCase: #singleExtendedSendBytecode>
- <sharedCodeNamed: 'commonSend' inCase: #singleExtendedSendBytecode>
self sendBreakpoint: messageSelector receiver: (self internalStackValue: argumentCount).
self printSends ifTrue:
[self printActivationNameForSelector: messageSelector startClass: (objectMemory classForClassTag: lkupClassTag); cr].
self internalFindNewMethod.
self internalExecuteNewMethod.
self fetchNextBytecode!
Item was changed:
----- Method: StackInterpreter>>commonSendAbsentImplicit (in category 'send bytecodes') -----
commonSendAbsentImplicit
"Send a message to the implicit receiver for that message."
"Assume: messageSelector and argumentCount have been set, and that
the arguments but not the receiver have been pushed onto the stack,"
"Note: This method is inlined into the interpreter dispatch loop."
"160-175 1010 i i i i Send To Absent Implicit Receiver Literal Selector #iiii With 0 Arguments"
"240 11110000 i i i i i j j j Send To Absent Implicit Receiver Literal Selector #iiiii (+ Extend A * 32) with jjj (+ Extend B * 8) Arguments"
+ <sharedCodeInCase: #extSendAbsentImplicitBytecode>
- <sharedCodeNamed: 'commonSendAbsentImplicit' inCase: #extSendAbsentImplicitBytecode>
| implicitReceiver |
implicitReceiver := self
implicitReceiverFor: self receiver
mixin: (self methodClassOf: method)
implementing: messageSelector.
self shuffleArgumentsAndStoreAbsentReceiver: implicitReceiver.
lkupClassTag := objectMemory fetchClassTagOf: implicitReceiver.
self assert: lkupClassTag ~= objectMemory nilObject.
self commonSend!
Item was added:
+ ----- Method: StackInterpreter>>extTrapIfNotInstanceOfBehaviorsBytecode (in category 'sista bytecodes') -----
+ extTrapIfNotInstanceOfBehaviorsBytecode
+ "SistaV1: * 236 11101100 iiiiiiii Trap If Not Instance Of Behavior/Array Of Behavior #iiiiiiii (+ Extend A * 256, where Extend A >= 0)"
+ | tos tosClassTag literal |
+ tos := self stackTop.
+ tosClassTag := objectMemory fetchClassTagOf: tos.
+ literal := self literal: extA << 8 + self fetchByte.
+ extA := 0.
+ (objectMemory isArrayNonImm: literal)
+ ifTrue:
+ [| i |
+ i := (objectMemory numSlotsOf: literal) asInteger.
+ [(i := i -1) < 0
+ or: [tosClassTag = (objectMemory rawClassTagForClass: (objectMemory fetchPointer: i ofObject: literal))]] whileTrue.
+ i < 0 ifTrue:
+ [^self respondToClassTrap]]
+ ifFalse:
+ [tosClassTag ~= (objectMemory rawClassTagForClass: literal) ifTrue:
+ [^self respondToClassTrap]].
+ self fetchNextBytecode!
Item was changed:
----- Method: StackInterpreter>>normalSend (in category 'send bytecodes') -----
normalSend
"Send a message, starting lookup with the receiver's class."
"Assume: messageSelector and argumentCount have been set, and that
the receiver and arguments have been pushed onto the stack,"
"Note: This method is inlined into the interpreter dispatch loop."
+ <sharedCodeInCase: #singleExtendedSendBytecode>
- <sharedCodeNamed: 'normalSend' inCase: #singleExtendedSendBytecode>
| rcvr |
rcvr := self internalStackValue: argumentCount.
lkupClassTag := objectMemory fetchClassTagOf: rcvr.
self assert: lkupClassTag ~= objectMemory nilObject.
self commonSend!
Item was added:
+ ----- Method: StackInterpreter>>respondToClassTrap (in category 'sista bytecodes') -----
+ respondToClassTrap
+ | ourContext tos |
+ <sharedCodeInCase: #extTrapIfNotInstanceOfBehaviorsBytecode>
+ messageSelector := objectMemory splObj: SelectorClassTrap.
+ tos := self internalStackTop.
+ ourContext := self ensureFrameIsMarried: localFP SP: localSP.
+ messageSelector = objectMemory nilObject ifTrue:
+ [self error: 'class trap'].
+ self internalPush: ourContext.
+ self internalPush: tos.
+ argumentCount := 1.
+ self normalSend!
Item was changed:
----- Method: StackInterpreter>>respondToUnknownBytecode (in category 'miscellaneous bytecodes') -----
respondToUnknownBytecode
"If an error selector is available then send it to the activeContext, otherwise abort."
+ <sharedCodeInCase: #unknownBytecode>
- <sharedCodeNamed: #respondToUnknownBytecode inCase: #unknownBytecode>
| ourContext |
messageSelector := objectMemory maybeSplObj: SelectorUnknownBytecode.
(messageSelector isNil
or: [messageSelector = objectMemory nilObject]) ifTrue:
[self error: 'Unknown bytecode'].
ourContext := self ensureFrameIsMarried: localFP SP: localSP.
"undo fetch of bytecode so that context's pc is pointing to the unknown bytecode."
localIP := localIP - 1.
self internalPush: ourContext.
argumentCount := 0.
self normalSend!
Item was changed:
----- Method: StackInterpreter>>superclassSend (in category 'send bytecodes') -----
superclassSend
"Send a message to self, starting lookup with the superclass of the class
containing the currently executing method."
"Assume: messageSelector and argumentCount have been set, and that
the receiver and arguments have been pushed onto the stack,"
"Note: This method is inlined into the interpreter dispatch loop."
+ <sharedCodeInCase: #singleExtendedSuperBytecode>
- <sharedCodeNamed: 'commonSupersend' inCase: #singleExtendedSuperBytecode>
| superclass |
superclass := self superclassOf: (self methodClassOf: method).
objectMemory ensureBehaviorHash: superclass.
lkupClassTag := objectMemory classTagForClass: superclass.
self assert: lkupClassTag ~= objectMemory nilObject.
self commonSend!
Item was changed:
----- Method: TCaseStmtNode>>processSharedCodeBlocks:forCase:in:method:expandedCases: (in category 'transformations') -----
processSharedCodeBlocks: caseTree forCase: caseIndex in: codeGen method: aTMethod expandedCases: seen
"Process any shared code blocks in the case parse tree for the given case, either inlining them or making them a 'goto sharedLabel'."
| caseMethod map meth sharedNode exitLabel |
exitLabel := nil.
"caseTree is expected to be a TStmtListNode whose first element is a comment
and whose second element is a TInlineNode for a method."
caseMethod := caseTree statements second method.
[sharedNode := nil.
map := IdentityDictionary new.
caseTree nodesDo:
[:node|
+ (sharedNode isNil
+ and: [node isSend
- (node isSend
and:[(meth := codeGen methodNamed: node selector) notNil
+ and:[meth sharedCase notNil]]]) ifTrue:
- and:[meth sharedCase notNil]]) ifTrue:
[(meth sharedCase = (meth sharedCase isSymbol
ifTrue: [caseMethod selector]
ifFalse: [caseIndex])
and: [(seen includes: meth sharedLabel) not])
ifTrue:
+ ["If the bytecode (the caseMethod) ends with a message that has a lastCase (and lastLabel) then
+ that will be converted into a goto and control will continue to that code, If the bytecode does
+ /not/ end with a message that has a lastCase (and lastLabel) then control should not continue to
+ that shared case. expandViaFallThrough captures this, true for the former, false for the latter."
+ | expandViaFallThrough |
+ expandViaFallThrough := false.
+ caseMethod statements last isSend ifTrue:
+ [(codeGen methodNamed: caseMethod statements last selector) ifNotNil:
+ [:m| expandViaFallThrough := m sharedCase notNil]].
- [sharedNode := meth.
seen add: meth sharedLabel.
+ map
+ at: node
+ put: (expandViaFallThrough
+ ifTrue: [sharedNode := meth.
+ TLabeledCommentNode new setComment: 'goto ', meth sharedLabel]
+ ifFalse: ["Still need recursive expansjon to continue but don't want
+ to duplicate the node, so substitue an empty method."
+ sharedNode := TLabeledCommentNode new setComment: 'null '.
+ meth copy
+ renameLabelsForInliningInto: aTMethod;
+ addLabelsTo: aTMethod;
+ asInlineNode])]
- map at: node put: (TLabeledCommentNode new setComment: 'goto ', meth sharedLabel)]
ifFalse:
[map at: node put: (TGoToNode new setLabel: meth sharedLabel)]]].
caseTree replaceNodesIn: map.
"recursively expand"
+ sharedNode notNil]
+ whileTrue:
+ [sharedNode isTMethod ifTrue:
+ [meth := sharedNode copy.
+ meth hasReturn ifTrue:
+ [exitLabel ifNil:
+ [exitLabel := aTMethod unusedLabelForInliningInto: aTMethod.
+ aTMethod labels add: exitLabel].
+ meth exitVar: nil label: exitLabel].
+ meth
+ renameLabelsForInliningInto: aTMethod;
+ addLabelsTo: aTMethod.
+ caseTree setStatements: (caseTree statements copyWith: meth asInlineNode)]].
- sharedNode == nil]
- whileFalse:
- [meth := sharedNode copy.
- meth hasReturn ifTrue:
- [exitLabel ifNil:
- [exitLabel := aTMethod unusedLabelForInliningInto: aTMethod.
- aTMethod labels add: exitLabel].
- meth exitVar: nil label: exitLabel].
- meth renameLabelsForInliningInto: aTMethod.
- aTMethod labels addAll: meth labels.
- caseTree setStatements: (caseTree statements copyWith: meth asInlineNode)].
exitLabel ifNotNil:
[caseTree setStatements: (caseTree statements copyWith:
(TLabeledCommentNode new setLabel: exitLabel comment: 'end case'))]!
Item was added:
+ ----- Method: TMethod>>addLabelsTo: (in category 'accessing') -----
+ addLabelsTo: aTMethod
+ aTMethod labels addAll: labels!
Item was changed:
----- Method: TMethod>>extractSharedCase (in category 'transformations') -----
extractSharedCase
+ "Scan the pragmas for an shared case directive of the form:
+ <sharedCodeNamed: 'sharedLabel' inCase: 'sharedCase'.>
+ <sharedCodeInCase: 'sharedCase'.>
+ or the older top-level statements for the form
+ self sharedCodeNamed: 'sharedLabel' inCase: 'sharedCase'.
+ self sharedCodeInCase: 'sharedCase'.
+ in which case remove the directive from the method body."
- "Scan the top-level statements for an shared case directive of the form:
- self sharedCodeNamed: <sharedLabel> inCase: <sharedCase>.
-
- and remove the directive from the method body."
-
self extractDirective: #sharedCodeNamed:inCase:
valueBlock: [:sendNode|
args isEmpty ifFalse:
[self error: 'Cannot share code sections in methods with arguments'].
sharedLabel := sendNode args first value.
sharedCase := sendNode args last value]
+ default: nil.
+ self extractDirective: #sharedCodeInCase:
+ valueBlock: [:sendNode|
+ args isEmpty ifFalse:
+ [self error: 'Cannot share code sections in methods with arguments'].
+ sharedLabel := selector.
+ sharedCase := sendNode args last value]
default: nil!
Item was added:
+ ----- Method: TMethod>>isTMethod (in category 'testing') -----
+ isTMethod
+ ^true!
Item was added:
+ ----- Method: TParseNode>>isTMethod (in category 'testing') -----
+ isTMethod
+ ^false!