[Vm-dev] VM Maker: VMMaker.oscog-eem.2150.mcz
commits at source.squeak.org
commits at source.squeak.org
Tue Mar 14 00:39:37 UTC 2017
Eliot Miranda uploaded a new version of VMMaker to project VM Maker:
http://source.squeak.org/VMMaker/VMMaker.oscog-eem.2150.mcz
==================== Summary ====================
Name: VMMaker.oscog-eem.2150
Author: eem
Time: 13 March 2017, 5:38:40.739778 pm
UUID: 022f453c-dd21-46ff-877c-e997ce7cdc88
Ancestors: VMMaker.oscog-eem.2149
Stack/CoInterpreter:
Refactor the assignments to the stack pointers from a page's head pointers into setStackPointersFromPage:.
Remove calls to addNewmethodToCache: from clients of lookupOrdinaryNoMNUEtcInClass: which does this anyway.
Cogit:
Use unsigned int as the type of 32-bit instructions (ARM & MIPS). Reduce code on the class side in defining the CogAbstractInstruction typedefs.
Eliminate some "variable name shadows method" warnings.
Document that the limit on the Cogit's alloca more than ensures that the number of abstract instructions and bytecode fixups fits within 16 bits.
Make sure a simulation-only merthod is not generated.
FDix traceMerge: for in-image compilation.
=============== Diff against VMMaker.oscog-eem.2149 ===============
Item was changed:
----- Method: CoInterpreter>>ceBaseFrameReturn: (in category 'trampolines') -----
ceBaseFrameReturn: returnValue
"Return across a page boundary. The context to return to (which may be married)
is stored in the first word of the stack. We get here when a return instruction jumps
to the ceBaseFrameReturn: address that is the return pc for base frames. A consequence
of this is that the current frame is no longer valid since an interrupt may have overwritten
its state as soon as the stack pointer has been cut-back beyond the return pc. So to have
a context to send the cannotReturn: message to we also store the base frame's context
in the second word of the stack page."
<api>
| contextToReturnTo contextToReturnFrom isAContext thePage newPage frameAbove |
<var: #thePage type: #'StackPage *'>
<var: #newPage type: #'StackPage *'>
<var: #frameAbove type: #'char *'>
self assert: (stackPages stackPageFor: stackPointer) = stackPage.
self assert: stackPages mostRecentlyUsedPage = stackPage.
cogit assertCStackWellAligned.
self assert: framePointer = 0.
self assert: stackPointer <= (stackPage baseAddress - objectMemory wordSize).
self assert: stackPage baseFP + (2 * objectMemory wordSize) < stackPage baseAddress.
"We would like to use the following assert but we can't since the stack pointer will be above the
base frame pointer in the base frame return and hence the 0 a base frame pointer points at could
be overwritten which will cause the isBaseFrame assert in frameCallerContext: to fail."
"self assert: (self frameCallerContext: stackPage baseFP) = (stackPages longAt: stackPage baseAddress)."
self assert: ((objectMemory addressCouldBeObj: (stackPages longAt: stackPage baseAddress - objectMemory wordSize))
and: [objectMemory isContext: (stackPages longAt: stackPage baseAddress - objectMemory wordSize)]).
contextToReturnTo := stackPages longAt: stackPage baseAddress.
self assert: (objectMemory addressCouldBeObj: contextToReturnTo).
"The stack page is effectively free now, so free it. We must free it to be
correct in determining if contextToReturnTo is still married, and in case
makeBaseFrameFor: cogs a method, which may cause a code compaction,
in which case the frame must be free to avoid the relocation machinery
tracing the dead frame. Since freeing now temporarily violates the page-list
ordering invariant, use the assert-free version."
stackPages freeStackPageNoAssert: stackPage.
isAContext := objectMemory isContext: contextToReturnTo.
(isAContext
and: [self isStillMarriedContext: contextToReturnTo])
ifTrue:
[framePointer := self frameOfMarriedContext: contextToReturnTo.
thePage := stackPages stackPageFor: framePointer.
framePointer = thePage headFP
ifTrue:
[stackPointer := thePage headSP]
ifFalse:
["Returning to some interior frame, presumably because of a sender assignment.
Move the frames above to another page (they may be in use, e.g. via coroutining).
Make the interior frame the top frame."
frameAbove := self findFrameAbove: framePointer inPage: thePage.
"Since we've just deallocated a page we know that newStackPage won't deallocate an existing one."
newPage := stackPages newStackPage.
self assert: newPage = stackPage.
self moveFramesIn: thePage through: frameAbove toPage: newPage.
stackPages markStackPageMostRecentlyUsed: newPage.
+ self setStackPointersFromPage: thePage]]
- framePointer := thePage headFP.
- stackPointer := thePage headSP]]
ifFalse:
[(isAContext
and: [objectMemory isIntegerObject: (objectMemory fetchPointer: InstructionPointerIndex ofObject: contextToReturnTo)]) ifFalse:
[contextToReturnFrom := stackPages longAt: stackPage baseAddress - objectMemory wordSize.
self tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFrom: contextToReturnFrom
to: contextToReturnTo
returnValue: returnValue.
^self externalCannotReturn: returnValue from: contextToReturnFrom].
"void the instructionPointer to stop it being incorrectly updated in a code
compaction in makeBaseFrameFor:."
instructionPointer := 0.
thePage := self makeBaseFrameFor: contextToReturnTo.
+ self setStackPointersFromPage: thePage].
- framePointer := thePage headFP.
- stackPointer := thePage headSP].
self setStackPageAndLimit: thePage.
self assert: (stackPages stackPageFor: framePointer) = stackPage.
(self isMachineCodeFrame: framePointer) ifTrue:
[self push: returnValue.
cogit ceEnterCogCodePopReceiverReg.
"NOTREACHED"].
instructionPointer := self stackTop.
instructionPointer = cogit ceReturnToInterpreterPC ifTrue:
[instructionPointer := self iframeSavedIP: framePointer].
self setMethod: (self iframeMethod: framePointer).
self stackTopPut: returnValue. "a.k.a. pop saved ip then push result"
self assert: (self checkIsStillMarriedContext: contextToReturnTo currentFP: framePointer).
self siglong: reenterInterpreter jmp: ReturnToInterpreter.
"NOTREACHED"
^nil!
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: #SistaCogit>
"Send e.g. thisContext conditionalBranchCounterTrippedOn: boolean."
| context counterTrippedSelector classTag classObj |
(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.
classObj := objectMemory classForClassTag: classTag.
(self lookupOrdinaryNoMNUEtcInClass: classObj) ~= 0 ifTrue:
[cogit resetCountersIn: (self mframeHomeMethod: framePointer).
+ ^condition]].
- ^condition].
- self addNewMethodToCache: classObj].
(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 changed:
----- Method: CoInterpreter>>ceNonLocalReturn: (in category 'trampolines') -----
ceNonLocalReturn: returnValue
<api>
| closure home unwindContextOrNilOrZero ourContext 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 *'>
"self shortPrintFrameAndCallers: framePointer.
self printOop: returnValue.
self halt."
self assert: (self isMachineCodeFrame: framePointer).
self assert: (self frameIsBlockActivation: framePointer).
"Since this is a block activation the closure is on the stack above any args and the frame."
closure := self pushedReceiverOrClosureOfFrame: framePointer.
home := nil.
"Walk the closure's lexical chain to find the context or frame to return from (home)."
[closure ~= objectMemory nilObject] whileTrue:
[home := objectMemory followField: ClosureOuterContextIndex ofObject: closure.
closure := objectMemory followField: 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 externalWriteBackHeadFramePointers.
unwindContextOrNilOrZero := self findUnwindThroughContext: home.
unwindContextOrNilOrZero = objectMemory nilObject ifTrue:
["error: can't find home on chain; cannot return"
ourContext := self ensureFrameIsMarried: framePointer SP: stackPointer.
^self externalCannotReturn: returnValue from: ourContext].
unwindContextOrNilOrZero ~= 0 ifTrue:
[^self externalAboutToReturn: returnValue 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: framePointer).
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: framePointer).
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"
ourContext := self ensureFrameIsMarried: framePointer SP: stackPointer.
^self externalCannotReturn: returnValue from: ourContext]].
"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."
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.
currentCtx := self frameCallerContext: thePage baseFP.
stackPages freeStackPage: thePage]
ifFalse:
[nextCntx := objectMemory fetchPointer: SenderIndex ofObject: currentCtx.
self markContextAsDead: currentCtx.
currentCtx := nextCntx]].
self setStackPageAndLimit: newPage.
+ self setStackPointersFromPage: newPage].
- stackPointer := stackPage headSP.
- framePointer := stackPage headFP].
"Two cases. Returning to the top frame or an interior frame. 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."
framePointer = frameToReturnTo
ifTrue:
[instructionPointer := self popStack]
ifFalse:
[[callerFP := framePointer.
framePointer := self frameCallerFP: framePointer.
framePointer ~~ frameToReturnTo] whileTrue.
instructionPointer := (self frameCallerSavedIP: callerFP) asUnsignedInteger.
stackPointer := (self frameCallerSP: callerFP)].
^self return: returnValue toExecutive: false!
Item was changed:
----- Method: CoInterpreter>>ceSend:above:to:numArgs: (in category 'trampolines') -----
ceSend: selector above: startAssociationArg to: rcvr numArgs: numArgs
"Entry-point for an unlinked directed super send in a CogMethod. Smalltalk stack looks like
receiver
args
head sp -> sender return pc
startAssociation is an association whose value is the class above which to start the lookup.
If an MNU then defer to handleMNUInMachineCodeTo:... which will dispatch the MNU and
may choose to allocate a closed PIC with a fast MNU dispatch for this send. Otherwise
attempt to link the send site as efficiently as possible. All link attempts may fail; e.g.
because we're out of code memory.
Continue execution via either executeMethod or interpretMethodFromMachineCode:
depending on whether the target method is cogged or not."
<api>
<option: #BytecodeSetHasDirectedSuperSend>
| startAssociation classTag classObj errSelIdx cogMethod |
<inline: false>
<var: #cogMethod type: #'CogMethod *'>
<var: #newCogMethod type: #'CogMethod *'>
"self printExternalHeadFrame"
"self printStringOf: selector"
cogit assertCStackWellAligned.
self assert: (objectMemory addressCouldBeOop: rcvr).
self sendBreakpoint: selector receiver: rcvr.
startAssociation := objectMemory followMaybeForwarded: startAssociationArg.
classTag := objectMemory classTagForClass: (self superclassOf: (objectMemory fetchPointer: ValueIndex ofObject: startAssociation)).
argumentCount := numArgs.
(self lookupInMethodCacheSel: selector classTag: classTag)
ifTrue:"check for coggability because method is in the cache"
[self
ifAppropriateCompileToNativeCode: newMethod
selector: selector]
ifFalse:
[self deny: (objectMemory isForwardedClassTag: classTag).
(objectMemory isOopForwarded: selector) ifTrue:
[^self
ceSend: (self handleForwardedSelectorFaultFor: selector)
above: startAssociation
to: rcvr
numArgs: numArgs].
messageSelector := selector.
classObj := objectMemory classForClassTag: classTag.
(errSelIdx := self lookupOrdinaryNoMNUEtcInClass: classObj) ~= 0 ifTrue:
[(errSelIdx = SelectorDoesNotUnderstand
and: [(cogMethod := cogit cogMNUPICSelector: messageSelector
receiver: rcvr
methodOperand: (self mnuMethodOrNilFor: rcvr)
numArgs: argumentCount) asUnsignedInteger
> cogit minCogMethodAddress]) ifTrue:
[cogit
linkSendAt: (stackPages longAt: stackPointer)
in: (self mframeHomeMethod: framePointer)
to: cogMethod
offset: cogit noCheckEntryOffset
receiver: rcvr].
self handleMNU: errSelIdx
InMachineCodeTo: rcvr
classForMessage: classObj.
+ self assert: false "NOTREACHED"]].
- self assert: false "NOTREACHED"].
- self addNewMethodToCache: classObj].
"Method found and has a cog method. Attempt to link to it. The receiver's class may be young.
If the Cogit can't store young classes in inline caches we can link to an open PIC instead."
(self maybeMethodHasCogMethod: newMethod) ifTrue:
[cogMethod := self cogMethodOf: newMethod.
cogMethod selector = objectMemory nilObject
ifTrue: [cogit setSelectorOf: cogMethod to: selector]
ifFalse:
["Deal with anonymous accessors, e.g. in Newspeak. The cogMethod may not have the
correct selector. If not, try and compile a new method with the correct selector."
cogMethod selector ~= selector ifTrue:
[(cogit cog: newMethod selector: selector) ifNotNil:
[:newCogMethod| cogMethod := newCogMethod]]].
cogMethod selector = selector
ifTrue:
[cogit
linkSendAt: (stackPages longAt: stackPointer)
in: (self mframeHomeMethod: framePointer)
to: cogMethod
offset: cogit noCheckEntryOffset
receiver: rcvr]
ifFalse: "If patchToOpenPICFor:.. returns we're out of code memory"
[cogit
patchToOpenPICFor: selector
numArgs: numArgs
receiver: rcvr].
instructionPointer := self popStack.
self executeNewMethod.
self assert: false "NOTREACHED"].
instructionPointer := self popStack.
^self interpretMethodFromMachineCode
"NOTREACHED"!
Item was changed:
----- Method: CoInterpreter>>ceSend:super:to:numArgs: (in category 'trampolines') -----
ceSend: selector super: superNormalBar to: rcvr numArgs: numArgs
"Entry-point for an unlinked send in a CogMethod. Smalltalk stack looks like
receiver
args
head sp -> sender return pc
If an MNU then defer to handleMNUInMachineCodeTo:... which will dispatch the MNU and
may choose to allocate a closed PIC with a fast MNU dispatch for this send. Otherwise
attempt to link the send site as efficiently as possible. All link attempts may fail; e.g.
because we're out of code memory.
Continue execution via either executeMethod or interpretMethodFromMachineCode:
depending on whether the target method is cogged or not."
<api>
| classTag classObj errSelIdx cogMethod |
<inline: false>
<var: #cogMethod type: #'CogMethod *'>
<var: #newCogMethod type: #'CogMethod *'>
"self printExternalHeadFrame"
"self printStringOf: selector"
cogit assertCStackWellAligned.
self assert: (objectMemory addressCouldBeOop: rcvr).
self sendBreakpoint: selector receiver: rcvr.
superNormalBar = 0
ifTrue: [classTag := objectMemory fetchClassTagOf: rcvr]
ifFalse: [classTag := objectMemory classTagForClass: (self superclassOf: (self methodClassOf: (self frameMethodObject: framePointer)))].
argumentCount := numArgs.
(self lookupInMethodCacheSel: selector classTag: classTag)
ifTrue:"check for coggability because method is in the cache"
[self
ifAppropriateCompileToNativeCode: newMethod
selector: selector]
ifFalse:
[(objectMemory isOopForwarded: selector) ifTrue:
[^self
ceSend: (self handleForwardedSelectorFaultFor: selector)
super: superNormalBar
to: rcvr
numArgs: numArgs].
(objectMemory isForwardedClassTag: classTag) ifTrue:
[self assert: superNormalBar = 0.
^self
ceSend: selector
super: superNormalBar
to: (self handleForwardedSendFaultForReceiver: rcvr stackDelta: 1 "skip return pc")
numArgs: numArgs].
messageSelector := selector.
classObj := objectMemory classForClassTag: classTag.
(errSelIdx := self lookupOrdinaryNoMNUEtcInClass: classObj) ~= 0 ifTrue:
[(errSelIdx = SelectorDoesNotUnderstand
and: [(cogMethod := cogit cogMNUPICSelector: messageSelector
receiver: rcvr
methodOperand: (self mnuMethodOrNilFor: rcvr)
numArgs: argumentCount) asUnsignedInteger
> cogit minCogMethodAddress]) ifTrue:
[cogit
linkSendAt: (stackPages longAt: stackPointer)
in: (self mframeHomeMethod: framePointer)
to: cogMethod
offset: (superNormalBar = 0
ifTrue: [cogit entryOffset]
ifFalse: [cogit noCheckEntryOffset])
receiver: rcvr].
self handleMNU: errSelIdx InMachineCodeTo: rcvr classForMessage: classObj.
+ self assert: false "NOTREACHED"]].
- self assert: false "NOTREACHED"].
- self addNewMethodToCache: classObj].
"Method found and has a cog method. Attempt to link to it. The receiver's class may be young.
If the Cogit can't store young classes in inline caches we can link to an open PIC instead."
(self maybeMethodHasCogMethod: newMethod) ifTrue:
[cogMethod := self cogMethodOf: newMethod.
cogMethod selector = objectMemory nilObject
ifTrue: [cogit setSelectorOf: cogMethod to: selector]
ifFalse:
["Deal with anonymous accessors, e.g. in Newspeak. The cogMethod may not have the
correct selector. If not, try and compile a new method with the correct selector."
cogMethod selector ~= selector ifTrue:
[(cogit cog: newMethod selector: selector) ifNotNil:
[:newCogMethod| cogMethod := newCogMethod]]].
cogMethod selector = selector
ifTrue:
[cogit
linkSendAt: (stackPages longAt: stackPointer)
in: (self mframeHomeMethod: framePointer)
to: cogMethod
offset: (superNormalBar = 0
ifTrue: [cogit entryOffset]
ifFalse: [cogit noCheckEntryOffset])
receiver: rcvr]
ifFalse: "If patchToOpenPICFor:.. returns we're out of code memory"
[cogit
patchToOpenPICFor: selector
numArgs: numArgs
receiver: rcvr].
instructionPointer := self popStack.
self executeNewMethod.
self assert: false "NOTREACHED"].
instructionPointer := self popStack.
^self interpretMethodFromMachineCode
"NOTREACHED"!
Item was changed:
----- Method: CoInterpreter>>ceSendAbort:to:numArgs: (in category 'trampolines') -----
ceSendAbort: selector to: rcvr numArgs: numArgs
"Entry-point for an abort send in a CogMethod (aboutToReturn:through:, cannotReturn: et al).
Try and dispatch the send, but the send may turn into an MNU in which case defer to
handleMNUInMachineCodeTo:... which will dispatch the MNU.
Continue execution via either executeMethod or interpretMethodFromMachineCode:
depending on whether the target method is cogged or not."
<api>
| classTag classObj errSelIdx |
<inline: false>
"self printExternalHeadFrame"
"self printStringOf: selector"
cogit assertCStackWellAligned.
self assert: (objectMemory addressCouldBeOop: rcvr).
self sendBreakpoint: selector receiver: rcvr.
argumentCount := numArgs.
classTag := objectMemory fetchClassTagOf: rcvr.
(self lookupInMethodCacheSel: selector classTag: classTag)
ifTrue:"check for coggability because method is in the cache"
[self
ifAppropriateCompileToNativeCode: newMethod
selector: selector]
ifFalse:
[messageSelector := selector.
classObj := objectMemory classForClassTag: classTag.
(errSelIdx := self lookupOrdinaryNoMNUEtcInClass: classObj) ~= 0 ifTrue:
[self handleMNU: errSelIdx InMachineCodeTo: rcvr classForMessage: classObj.
"NOTREACHED"
+ self assert: false]].
- self assert: false].
- self addNewMethodToCache: classObj].
instructionPointer := self popStack.
(self maybeMethodHasCogMethod: newMethod) ifTrue:
[self executeNewMethod.
self assert: false
"NOTREACHED"].
^self interpretMethodFromMachineCode
"NOTREACHED"!
Item was changed:
----- Method: CoInterpreter>>ceSendFromInLineCacheMiss: (in category 'trampolines') -----
ceSendFromInLineCacheMiss: cogMethodOrPIC
"Send from an Open PIC when the first-level method lookup probe has failed,
or to continue when PIC creation has failed (e.g. because we're out of code space),
or when a send has failed due to a forwarded receiver."
<api>
<var: #cogMethodOrPIC type: #'CogMethod *'>
| numArgs rcvr classTag classObj errSelIdx |
"self printFrame: stackPage headFP WithSP: stackPage headSP"
"self printStringOf: selector"
numArgs := cogMethodOrPIC cmNumArgs.
rcvr := self stackValue: numArgs + 1. "skip return pc"
self assert: (objectMemory addressCouldBeOop: rcvr).
classTag := objectMemory fetchClassTagOf: rcvr.
argumentCount := numArgs.
false ifTrue: "would like to assert this but must also allow for an interpretable method in the cache."
[self deny: (cogMethodOrPIC cmType = CMOpenPIC
and: [self newMethodInLookupCacheAt: cogMethodOrPIC selector and: classTag])].
(self lookupInMethodCacheSel: cogMethodOrPIC selector classTag: classTag)
ifTrue:"check for coggability because method is in the cache"
[self
ifAppropriateCompileToNativeCode: newMethod
selector: cogMethodOrPIC selector]
ifFalse:
[(objectMemory isOopForwarded: cogMethodOrPIC selector) ifTrue:
[self handleForwardedSelectorFaultFor: cogMethodOrPIC selector.
^self ceSendFromInLineCacheMiss: cogMethodOrPIC].
(objectMemory isForwardedClassTag: classTag) ifTrue:
[self handleForwardedSendFaultForReceiver: rcvr stackDelta: 1 "skip return pc".
^self ceSendFromInLineCacheMiss: cogMethodOrPIC].
messageSelector := cogMethodOrPIC selector.
classObj := objectMemory classForClassTag: classTag.
(errSelIdx := self lookupOrdinaryNoMNUEtcInClass: classObj) ~= 0 ifTrue:
[self handleMNU: errSelIdx InMachineCodeTo: rcvr classForMessage: classObj.
"NOTREACHED"
+ self assert: false]].
- self assert: false].
- self addNewMethodToCache: classObj].
instructionPointer := self popStack.
(self maybeMethodHasCogMethod: newMethod) ifTrue:
[self executeNewMethod.
self assert: false
"NOTREACHED"].
^self interpretMethodFromMachineCode
"NOTREACHED"!
Item was changed:
----- Method: CoInterpreter>>tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFrom:to:returnValue: (in category 'return bytecodes') -----
tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFrom: contextToReturnFrom to: contextToReturnTo returnValue: returnValue
"Handle the cannot return response for a base frame return to an invalid context.
Build a new base frame for the context in the cannot resume state ready for the
send of cannotReturn:.
Since we have returned from the base frame of the page the context is effectively widowed.
But its sender needs to be contextToReturnTo, and its pc needs to be the HasBeenReturnedFromMCPC
marker. So bereave it (as a side-effect of isWidowedContext:), assign contextToReturnTo to
sender, and rebuild its frame, which will have the ceCannotResumePC as its pc. Finally push
returnValue and set instructionPointer to ceCannotResumePC in preparation for the send."
| newPage |
<inline: false>
<var: #newPage type: #'StackPage *'>
self assert: (stackPage ~= 0 and: [stackPage isFree]).
self isWidowedContext: contextToReturnFrom.
self assert: (self isMarriedOrWidowedContext: contextToReturnFrom) not.
objectMemory
storePointer: SenderIndex ofObject: contextToReturnFrom withValue: contextToReturnTo;
storePointer: InstructionPointerIndex ofObject: contextToReturnFrom withValue: HasBeenReturnedFromMCPCOop.
"void the instructionPointer to stop it being incorrectly updated in a code
compaction in makeBaseFrameFor:."
instructionPointer := 0.
newPage := self makeBaseFrameFor: contextToReturnFrom.
self assert: stackPage = newPage.
self setStackPageAndLimit: newPage.
+ self setStackPointersFromPage: newPage.
- framePointer := stackPage headFP.
- stackPointer := stackPage headSP.
self assert: self stackTop = cogit ceCannotResumePC.
"overwrite the ceSendCannotResumePC on the stack. If ever re-executed
the returnValue will be taken from top-of-stack by ceCannotResume."
self stackTopPut: returnValue.
"Assign it to instructionPointer as externalCannotReturn:from: pushes it."
instructionPointer := cogit ceCannotResumePC!
Item was changed:
----- Method: CoInterpreterPrimitives>>primitiveVoidVMStateForMethod (in category 'system control primitives') -----
primitiveVoidVMStateForMethod
"The receiver is a compiledMethod. Clear all VM state associated with the method,
including any machine code, or machine code pcs in context objects."
| activeContext methodObj hasCogMethod theFrame thePage |
<var: #theFrame type: #'char *'>
<var: #thePage type: #'StackPage *'>
super primitiveFlushCacheByMethod.
"One might think (as this author did) that the heap scan is unnecessary if the method does not
have a cog method. But it could be the case that the code zone has recently been reclaimed
and so not having a cog method is no indication that it didn't have a cog method some time in
the recent past, and that there are indeed still contexts with machine code pcs out there. The
only steps that can be avoided is divorcing frames in the stack zone, and scanning to unlink and
free if there isn't a cog method."
methodObj := self stackTop.
activeContext := self ensureFrameIsMarried: framePointer SP: stackPointer.
self ensurePushedInstructionPointer.
self externalWriteBackHeadFramePointers.
(hasCogMethod := self methodHasCogMethod: methodObj) ifTrue:
[self divorceMachineCodeFramesWithMethod: methodObj].
self ensureAllContextsWithMethodHaveBytecodePCs: methodObj.
hasCogMethod ifTrue:
[cogit unlinkSendsTo: methodObj andFreeIf: true].
(self isStillMarriedContext: activeContext)
ifTrue:
[theFrame := self frameOfMarriedContext: activeContext.
thePage := stackPages stackPageFor: theFrame.
self assert: thePage headFP = theFrame.
self setStackPageAndLimit: thePage.
+ self setStackPointersFromPage: thePage.
- stackPointer := thePage headSP.
- framePointer := thePage headFP.
instructionPointer := self popStack.
self assert: methodObj = self stackTop]
ifFalse:
[self zeroStackPage. "to avoid assert in marryContextInNewStackPageAndInitializeInterpreterRegisters:"
self marryContextInNewStackPageAndInitializeInterpreterRegisters: activeContext.
self popStack. "pop bogus machine-code instructionPointer"
self assert: methodObj = self stackTop.
self siglong: reenterInterpreter jmp: ReturnToInterpreter]!
Item was changed:
----- Method: CogARMCompiler class>>machineCodeDeclaration (in category 'translation') -----
machineCodeDeclaration
+ "Answer the declaration for the machineCode array.
+ ARM instructions are 32-bits in length."
+ ^{#'unsigned int'. '[', self basicNew machineCodeWords printString, ']'}!
- "Answer the declaration for the machineCode array."
- ^{#'usqIntptr_t'. '[', self basicNew machineCodeWords printString, ']'}!
Item was changed:
----- Method: CogARMCompiler>>rewriteCPICJumpAt:target: (in category 'inline cacheing') -----
+ rewriteCPICJumpAt: addressFollowingJump target: jumpTargetAddr
- rewriteCPICJumpAt: addressFollowingJump target: jumpTargetAddress
"Rewrite a jump instruction to call a different target. This variant is used to reset the
jumps in the prototype CPIC to suit each use,.
Answer the extent of the code change which is used to compute the range of the icache to flush."
<var: #addressFollowingJump type: #usqInt>
+ <var: #jumpTargetAddr type: #usqInt>
- <var: #jumpTargetAddress type: #usqInt>
<inline: true>
+ ^self rewriteTransferAt: addressFollowingJump target: jumpTargetAddr!
- ^self rewriteTransferAt: addressFollowingJump target: jumpTargetAddress!
Item was added:
+ ----- Method: CogAbstractInstruction class>>filteredInstVarNames (in category 'translation') -----
+ filteredInstVarNames
+ "Eliminate bcpc, which is development-time only."
+ ^super filteredInstVarNames copyWithout: 'bcpc'!
Item was changed:
----- Method: CogAbstractInstruction class>>instVarNamesAndTypesForTranslationDo: (in category 'translation') -----
instVarNamesAndTypesForTranslationDo: aBinaryBlock
+ "Enumerate aBinaryBlock with the names and C type strings for the inst vars to include in an AbstractInstruction struct."
+ "(CogAbstractInstruction withAllSubclasses reject: [:c| c name includesSubString: 'ForTests']) do:
- "enumerate aBinaryBlock with the names and C type strings for the inst vars to include in an AbstractInstruction struct."
- "{CogAbstractInstruction. CogIA32Compiler. CogARMCompiler} do:
[:c| Transcript print: c; cr. c printTypedefOn: Transcript]"
(self filteredInstVarNames copyWithout: 'machineCode'), #('machineCode') do:
[:ivn|
+ aBinaryBlock
+ value: ivn
+ value: (ivn caseOf: {
+ ['address'] -> [#usqInt]. "usqInt is always large enough to contain a pointer; we do not need to use usqIntptr_t"
+ ['machineCode'] -> [self machineCodeDeclaration].
+ ['operands'] -> [{#usqInt. '[', NumOperands, ']'}].
+ ['dependent'] -> ['struct _AbstractInstruction *']}
+ otherwise:
+ [#'unsigned char'])]!
- ivn ~= 'bcpc' ifTrue:
- [aBinaryBlock
- value: ivn
- value: (ivn caseOf: {
- ['address'] -> [#'usqIntptr_t'].
- ['machineCode'] -> [self machineCodeDeclaration].
- ['operands'] -> [{#'usqIntptr_t'. '[', NumOperands, ']'}].
- ['dependent'] -> ['struct _AbstractInstruction *']}
- otherwise:
- [#'unsigned char'])]]!
Item was changed:
----- Method: CogAbstractInstruction class>>machineCodeDeclaration (in category 'translation') -----
machineCodeDeclaration
+ "Answer a dummy declaration. Subclasses will override to provide the real one."
- "Answer a dummy declaration. Subclasses will override to provgide the real one."
^#(#'unsigned char' '[4]')!
Item was changed:
----- Method: CogBytecodeFixup class>>instVarNamesAndTypesForTranslationDo: (in category 'translation') -----
instVarNamesAndTypesForTranslationDo: aBinaryBlock
+ "Enumerate aBinaryBlock with the names and C type strings for the inst vars to include in a BytecodeFixup struct."
+ "self withAllSubclasses collect: [:ea| ea typedef]"
- "enumerate aBinaryBlock with the names and C type strings for the inst vars to include in a BytecodeFixup struct."
self filteredInstVarNames do:
[:ivn|
aBinaryBlock
value: ivn
+ value: (ivn first ~= $# ifTrue:
+ [ivn caseOf: {
+ ['targetInstruction'] -> [#'AbstractInstruction *'].
+ ['mergeSimStack'] -> [#'SimStackEntry *'].
+ ['instructionIndex'] -> [#'unsigned short'].
+ ['simStackPtr'] -> [#char].
+ ['simNativeStackPtr'] -> [#'unsigned short'].
+ ['simNativeStackSize'] -> [#'unsigned short'].
+ ['isReceiverResultRegSelf'] -> [#char] }])]!
- value: (ivn = 'targetInstruction'
- ifTrue: [#'AbstractInstruction *']
- ifFalse:
- [#sqInt])]!
Item was changed:
----- Method: CogIA32Compiler>>genPushC64: (in category 'abstract instructions') -----
genPushC64: constant64Bits
<inline: true>
<var: #constant64Bits type: #sqLong>
<returnTypeC: #'AbstractInstruction *'>
| inst highPart |
+ self assert: BytesPerWord = 4.
+ highPart := self cCode: '(constant64Bits >> 32LL)' inSmalltalk: [ constant64Bits bitShift: -32 ].
+ inst :=cogit PushCw: highPart.
+ cogit PushCw: (constant64Bits bitAnd: 16rFFFFFFFF).
+ ^inst!
- BytesPerWord == 4 ifTrue: [
- highPart := self cCode: '(constant64Bits >> 32ll)' inSmalltalk: [ constant64Bits bitShift: -32 ].
- inst :=cogit PushCw: highPart.
- cogit PushCw: (constant64Bits bitAnd: 16rFFFFFFFF).
- ^ inst
- ] ifFalse: [
- ^ cogit PushCw: constant64Bits.
- ]!
Item was changed:
----- Method: CogIA32Compiler>>rewriteCPICJumpAt:target: (in category 'inline cacheing') -----
+ rewriteCPICJumpAt: addressFollowingJump target: jumpTargetAddr
- rewriteCPICJumpAt: addressFollowingJump target: jumpTargetAddress
"Rewrite the short jump instruction to jump to a new cpic case target. "
<var: #addressFollowingJump type: #usqInt>
+ <var: #jumpTargetAddr type: #usqInt>
- <var: #jumpTargetAddress type: #usqInt>
<var: #callDistance type: #sqInt> "prevent type inference for avoiding warning on abs"
| callDistance |
+ callDistance := jumpTargetAddr - addressFollowingJump.
- callDistance := jumpTargetAddress - addressFollowingJump.
self assert: callDistance abs < 128.
objectMemory
byteAt: addressFollowingJump - 1
put: (callDistance bitAnd: 16rFF).
"self cCode: ''
+ inSmalltalk: [cogit disassembleFrom: addressFollowingJump - 10 to: addressFollowingJump - 1]."!
- inSmalltalk: [cogit disassembleFrom: addressFollowingJump - 10 to: addressFollowingJump - 1]."
- ^2!
Item was changed:
----- Method: CogMIPSELCompiler class>>machineCodeDeclaration (in category 'translation') -----
machineCodeDeclaration
+ "Answer the declaration for the machineCode array.
+ MPIS instructions are 32-bits in length."
+ ^{#'unsigned int'. '[', self basicNew machineCodeWords printString, ']'}!
- "Answer the declaration for the machineCode array."
- ^{#'usqIntptr_t'. '[', self basicNew machineCodeWords printString, ']'}!
Item was changed:
----- Method: CogMIPSELCompiler>>rewriteCPICJumpAt:target: (in category 'inline cacheing') -----
+ rewriteCPICJumpAt: addressFollowingJump target: jumpTargetAddr
- rewriteCPICJumpAt: addressFollowingJump target: jumpTargetAddress
"Rewrite a jump instruction to call a different target. This variant is used to reset the
jumps in the prototype CPIC to suit each use,.
Answer the extent of the code change which is used to compute the range of the icache to flush."
<var: #addressFollowingJump type: #usqInt>
+ <var: #jumpTargetAddr type: #usqInt>
- <var: #jumpTargetAddress type: #usqInt>
"self CmpR: ClassReg R: TempReg.
^self JumpNonZero: 0"
"bne s5, s3, +156 ; =BE7C
nop (delay slot)
.... <-- addressFollowingJump"
self assert: (self opcodeAtAddress: addressFollowingJump - 8) = BNE.
self assert: (objectMemory longAt: addressFollowingJump - 4) = self nop.
"cogit disassembleFrom: addressFollowingJump - 8 to: addressFollowingJump."
+ self rewriteITypeBranchAtAddress: addressFollowingJump - 8 target: jumpTargetAddr.
- self rewriteITypeBranchAtAddress: addressFollowingJump - 8 target: jumpTargetAddress.
self assert: (self opcodeAtAddress: addressFollowingJump - 8) = BNE.
self assert: (objectMemory longAt: addressFollowingJump - 4) = self nop.
"cogit disassembleFrom: addressFollowingJump - 8 to: addressFollowingJump."!
Item was removed:
- ----- Method: CogMethodZone>>alignment (in category 'accessing') -----
- alignment
- ^8!
Item was added:
+ ----- Method: CogMethodZone>>zoneAlignment (in category 'accessing') -----
+ zoneAlignment
+ ^8!
Item was changed:
----- Method: CogObjectRepresentationFor64BitSpur>>maybeGenerateSelectorIndexDereferenceRoutine (in category 'initialization') -----
maybeGenerateSelectorIndexDereferenceRoutine
"Generate the routine that converts selector indices into selector objects.
It is called from the send trampolines.
If the selector index is negative, convert it into a positive index into the
special selectors array and index that. Otherwise, index the current method.
The routine uses Extra0Reg & Extra1Reg, which are available, since they
are not live at point of send."
| jumpNegative jumpNotBlock jumpFullBlock |
<var: 'jumpNegative' type: #'AbstractInstruction *'>
<var: 'jumpNotBlock' type: #'AbstractInstruction *'>
<var: 'jumpFullBlock' type: #'AbstractInstruction *'>
cogit zeroOpcodeIndex.
cogit CmpCq: 0 R: ClassReg.
jumpNegative := cogit JumpLess: 0.
cogit
MoveMw: FoxMethod r: FPReg R: Extra0Reg;
AddCq: 2 R: ClassReg; "Change selector index to 1-relative, skipping the method header"
TstCq: MFMethodFlagIsBlockFlag R: Extra0Reg.
jumpNotBlock := cogit JumpZero: 0.
"If in a block, need to find the home method... If using full blocks, need to test the cpicHasMNUCaseOrCMIsFullBlock bit"
+ cogit AndCq: methodZone zoneAlignment negated R: Extra0Reg.
- cogit AndCq: methodZone alignment negated R: Extra0Reg.
SistaV1BytecodeSet ifTrue:
[self bitAndByteOffsetOfIsFullBlockBitInto:
[:bitmask :byteOffset|
jumpFullBlock := cogit
MoveMb: byteOffset r: Extra0Reg R: Extra1Reg;
TstCq: bitmask R: Extra1Reg;
JumpNonZero: 0]].
cogit
MoveM16: 0 r: Extra0Reg R: Extra1Reg;
SubR: Extra1Reg R: Extra0Reg.
jumpNotBlock jmpTarget: cogit Label.
SistaV1BytecodeSet ifTrue:
[jumpFullBlock jmpTarget: jumpNotBlock getJmpTarget].
cogit "Now fetch the method object and index with the literal index to retrieve the selector"
+ AndCq: methodZone zoneAlignment negated R: Extra0Reg;
- AndCq: methodZone alignment negated R: Extra0Reg;
MoveMw: (cogit offset: CogMethod of: #methodObject) r: Extra0Reg R: Extra1Reg;
MoveXwr: ClassReg R: Extra1Reg R: ClassReg;
RetN: 0.
jumpNegative jmpTarget: cogit Label.
cogit
NegateR: ClassReg;
LogicalShiftLeftCq: 1 R: ClassReg;
MoveAw: objectMemory specialObjectsArrayAddress R: Extra0Reg;
SubCq: 1 R: ClassReg;
MoveMw: SpecialSelectors + 1 * objectMemory wordSize r: Extra0Reg R: Extra1Reg; "Index, including header size"
MoveXwr: ClassReg R: Extra1Reg R: ClassReg;
RetN: 0.
ceDereferenceSelectorIndex := cogit methodZoneBase.
cogit
outputInstructionsForGeneratedRuntimeAt: ceDereferenceSelectorIndex;
recordGeneratedRunTime: 'ceDereferenceSelectorIndex' address: ceDereferenceSelectorIndex;
recordRunTimeObjectReferences!
Item was added:
+ ----- Method: CogRASSBytecodeFixup class>>filteredInstVarNames (in category 'translation') -----
+ filteredInstVarNames
+ "Override to group char and short vars together for compactness.
+ self typedef"
+ | vars |
+ vars := super filteredInstVarNames asOrderedCollection.
+ vars
+ remove: 'mergeSimStack';
+ add: 'mergeSimStack' afterIndex: (vars indexOf: 'targetInstruction');
+ remove: 'isReceiverResultRegSelf';
+ add: 'isReceiverResultRegSelf' afterIndex: (vars indexOf: 'simStackPtr').
+ ^vars!
Item was removed:
- ----- Method: CogRASSBytecodeFixup class>>instVarNamesAndTypesForTranslationDo: (in category 'translation') -----
- instVarNamesAndTypesForTranslationDo: aBinaryBlock
- "enumerate aBinaryBlock with the names and C type strings for the inst vars to include in a CogRASSBytecodeFixup struct."
-
- (self allInstVarNames copyWithout: 'cogit') do:
- [:ivn|
- aBinaryBlock
- value: ivn
- value: (ivn caseOf: {
- ['targetInstruction'] -> [#'AbstractInstruction *'].
- ['mergeSimStack'] -> [#'CogSimStackEntry *'] }
- otherwise: [#sqInt])]!
Item was changed:
----- Method: CogSSBytecodeFixup class>>filteredInstVarNames (in category 'translation') -----
filteredInstVarNames
+ "Override to add ifdef LowcodeVM around the native stack info, and to put
+ char vars before short vars.
- "Override to add ifdef LowcodeVM around the native stack info.
self typedef"
+ | vars |
+ vars := super filteredInstVarNames asOrderedCollection.
+ vars
+ remove: 'instructionIndex';
+ add: 'instructionIndex' afterIndex: (vars indexOf: 'simStackPtr');
+ add: '#if LowcodeVM' beforeIndex: (vars indexOf: 'simNativeStackPtr');
+ add: '#endif' afterIndex: (vars indexOf: 'simNativeStackSize').
+ ^vars!
- ^super filteredInstVarNames
- copyReplaceAll: #('simNativeStackPtr' 'simNativeStackSize')
- with: #('#if LowcodeVM' 'simNativeStackPtr' 'simNativeStackSize' '#endif')!
Item was added:
+ ----- Method: CogX64Compiler>>genPushC64: (in category 'abstract instructions') -----
+ genPushC64: constant64Bits
+ <inline: true>
+ ^cogit PushCw: constant64Bits!
Item was changed:
----- Method: CogX64Compiler>>rewriteCPICJumpAt:target: (in category 'inline cacheing') -----
+ rewriteCPICJumpAt: addressFollowingJump target: jumpTargetAddr
- rewriteCPICJumpAt: addressFollowingJump target: jumpTargetAddress
"Rewrite the short jump instruction to jump to a new cpic case target. "
<var: #addressFollowingJump type: #usqInt>
+ <var: #jumpTargetAddr type: #usqInt>
- <var: #jumpTargetAddress type: #usqInt>
<var: #callDistance type: #sqInt> "prevent type inference for avoiding warning on abs"
| callDistance |
+ callDistance := jumpTargetAddr - addressFollowingJump.
- callDistance := jumpTargetAddress - addressFollowingJump.
self assert: callDistance abs < 128.
objectMemory
byteAt: addressFollowingJump - 1
put: (callDistance bitAnd: 16rFF).
"self cCode: ''
+ inSmalltalk: [cogit disassembleFrom: addressFollowingJump - 10 to: addressFollowingJump - 1]."!
- inSmalltalk: [cogit disassembleFrom: addressFollowingJump - 10 to: addressFollowingJump - 1]."
- ^2!
Item was changed:
----- Method: CogX64Compiler>>rewriteJumpLongAt:target: (in category 'inline cacheing') -----
+ rewriteJumpLongAt: addressFollowingJump target: jumpTargetAddr
- rewriteJumpLongAt: addressFollowingJump target: jumpTargetAddress
"Rewrite a long jump instruction to jump to a different target. This variant
is used to rewrite cached primitive calls. Answer the extent of the
code change which is used to compute the range of the icache to flush."
<inline: true>
+ ^self rewriteCallAt: addressFollowingJump target: jumpTargetAddr!
- ^self rewriteCallAt: addressFollowingJump target: jumpTargetAddress!
Item was changed:
----- Method: Cogit>>allocateOpcodes:bytecodes:ifFail: (in category 'initialization') -----
allocateOpcodes: numberOfAbstractOpcodes bytecodes: numberOfBytecodes ifFail: failBlock
"Allocate the various arrays needed to compile abstract instructions, failing if the size
needed is considered too high. Notionally we only need as many fixups as there are
bytecodes. But we reuse fixups to record pc-dependent instructions in
generateInstructionsAt: and so need at least as many as there are abstract opcodes.
This *must* be inlined since the arrays are alloca'ed (stack allocated)
so that they are freed when compilation is done.
N.B. We do one single alloca to save embarrassing C optimizers that
generate incorrect code as both gcc and the intel compiler do on x86."
<inline: true>
| opcodeBytes fixupBytes allocBytes |
numAbstractOpcodes := numberOfAbstractOpcodes.
opcodeBytes := (self sizeof: CogAbstractInstruction) * numAbstractOpcodes.
fixupBytes := (self sizeof: CogBytecodeFixup) * numAbstractOpcodes.
allocBytes := opcodeBytes + fixupBytes.
+ "Document the fact that the MaxStackAllocSize ensures that the number of abstract
+ opcodes fits in a 16 bit integer (e.g. CogBytecodeFixup's instructionIndex)."
+ self assert: (self sizeof: CogAbstractInstruction) + (self sizeof: CogBytecodeFixup) * 32768 > MaxStackAllocSize.
+ allocBytes > MaxStackAllocSize ifTrue:
+ [^failBlock value].
- allocBytes > MaxStackAllocSize ifTrue: [^failBlock value].
self
cCode:
[abstractOpcodes := self alloca: allocBytes.
self b: abstractOpcodes zero: allocBytes.
fixups := (abstractOpcodes asUnsignedInteger + opcodeBytes) asVoidPointer]
inSmalltalk:
[abstractOpcodes := CArrayAccessor on:
((1 to: numAbstractOpcodes) collect: [:ign| CogCompilerClass for: self]).
fixups := CArrayAccessor on:
((1 to: numAbstractOpcodes) collect: [:ign| self bytecodeFixupClass for: self])].
self zeroOpcodeIndexForNewOpcodes.
labelCounter := 0!
Item was changed:
----- Method: Cogit>>cPICPrototypeCaseOffset (in category 'in-line cacheing') -----
cPICPrototypeCaseOffset
"Whimsey; we want 16rCA5E10 + cPICPrototypeCaseOffset to be somewhere in the middle of the zone."
+ <inline: true>
+ ^methodZoneBase + methodZone youngReferrers / 2 - 16rCA5E10!
- ^methodZoneBase + methodZone youngReferrers / 2 - 16rCA5E10!
Item was changed:
----- Method: Cogit>>findMapLocationForMcpc:inMethod: (in category 'method map') -----
findMapLocationForMcpc: targetMcpc inMethod: cogMethod
+ <var: #targetMcpc type: #usqInt>
<var: #cogMethod type: #'CogMethod *'>
| mcpc map mapByte annotation |
mcpc := self firstMappedPCFor: cogMethod.
map := self mapStartFor: cogMethod.
mcpc = targetMcpc ifTrue: [^map].
[(mapByte := objectMemory byteAt: map) ~= MapEnd] whileTrue:
[annotation := mapByte >> AnnotationShift.
annotation ~= IsAnnotationExtension ifTrue:
[mcpc := mcpc + (backEnd codeGranularity
* (annotation = IsDisplacementX2N
ifTrue: [mapByte - DisplacementX2N << AnnotationShift]
ifFalse: [mapByte bitAnd: DisplacementMask]))].
mcpc >= targetMcpc ifTrue:
[self assert: mcpc = targetMcpc.
annotation = IsDisplacementX2N ifTrue:
[map := map - 1.
mapByte := objectMemory byteAt: map.
annotation := mapByte >> AnnotationShift.
self assert: annotation > IsAnnotationExtension].
^map].
map := map - 1].
^0!
Item was changed:
----- Method: Cogit>>generateMapAt:start: (in category 'method map') -----
generateMapAt: addressOrNull start: startAddress
"Generate the method map at addressrNull (or compute it if addressOrNull is null).
Answer the length of the map in byes. Each entry in the map is in two parts. In the
least signficant bits are a displacement of how far from the start or previous entry,
unless it is an IsAnnotationExtension byte, in which case those bits are the extension.
In the most signficant bits are the type of annotation at the point reached. A null
byte ends the map."
+ <var: 'addressOrNull' type: #usqInt>
+ <var: 'startAddress' type: #usqInt>
| length location |
<var: #instruction type: #'AbstractInstruction *'>
length := 0.
location := startAddress.
0 to: opcodeIndex - 1 do:
[:i| | instruction mcpc delta maxDelta mapEntry |
instruction := self abstractInstructionAt: i.
instruction annotation ifNotNil:
[:annotation|
literalsManager assertValidAnnotation: annotation for: instruction.
mcpc := instruction mapEntryAddress.
[(delta := mcpc - location / backEnd codeGranularity) > DisplacementMask] whileTrue:
[maxDelta := (delta min: MaxX2NDisplacement) bitClear: DisplacementMask.
self assert: maxDelta >> AnnotationShift <= DisplacementMask.
addressOrNull ifNotNil:
[self addToMap: IsDisplacementX2N
instruction: instruction
byte: maxDelta >> AnnotationShift + DisplacementX2N
at: addressOrNull - length
for: mcpc].
location := location + (maxDelta * backEnd codeGranularity).
length := length + 1].
addressOrNull ifNotNil:
[mapEntry := delta + ((annotation min: IsSendCall) << AnnotationShift).
self addToMap: annotation instruction: instruction byte: mapEntry at: addressOrNull - length for: mcpc].
location := location + (delta * backEnd codeGranularity).
length := length + 1.
annotation > IsSendCall ifTrue: "Add the necessary IsAnnotationExtension"
[addressOrNull ifNotNil:
[mapEntry := IsAnnotationExtension << AnnotationShift + (annotation - IsSendCall).
self addToMap: annotation instruction: instruction byte: mapEntry at: addressOrNull - length for: mcpc].
length := length + 1]]].
addressOrNull ifNotNil:
[self addToMap: MapEnd instruction: nil byte: MapEnd at: addressOrNull - length for: 0].
^length + 1!
Item was changed:
----- Method: Cogit>>lookupFrameOffset: (in category 'disassembly') -----
lookupFrameOffset: anInteger
+ <doNotGenerate>
(self class initializationOptions at: #tempNames ifAbsent: nil) ifNotNil:
[:dict|
(self class initializationOptions at: #startpc ifAbsent: nil) ifNotNil:
[:startpc|
(dict at: startpc + 1 ifAbsent: nil) ifNotNil:
[:tempNames| | numArgs |
anInteger = FoxMFReceiver ifTrue:
[^'self'].
numArgs := self class initializationOptions at: #numArgs.
1 to: tempNames size do:
[:i|
anInteger = (self frameOffsetOfTemporary: i - 1 numArgs: numArgs) ifTrue:
[^tempNames at: i]]]]].
^nil!
Item was changed:
----- Method: StackInterpreter>>externalDivorceFrame:andContext: (in category 'frame access') -----
externalDivorceFrame: theFP andContext: ctxt
"Divorce a single frame and its context. If it is not the top frame of a stack this means splitting its stack."
| thePage onCurrent theSP callerCtx newPage frameAbove callerFP callerSP callerIP theIP |
<inline: false>
<var: #theFP type: #'char *'>
<var: #thePage type: #'StackPage *'>
<var: #theSP type: #'char *'>
<var: #newPage type: #'StackPage *'>
<var: #frameAbove type: #'char *'>
<var: #callerFP type: #'char *'>
<var: #callerSP type: #'char *'>
"stackPage needs to have current head pointers to avoid confusion."
self assert: (stackPage = 0 or: [stackPage = stackPages mostRecentlyUsedPage]).
thePage := stackPages stackPageFor: theFP.
(onCurrent := thePage = stackPage) ifFalse:
[stackPages markStackPageNextMostRecentlyUsed: thePage].
theSP := self findSPOf: theFP on: thePage.
self updateStateOfSpouseContextForFrame: theFP WithSP: theSP.
callerCtx := self ensureCallerContext: theFP.
(frameAbove := self findFrameAbove: theFP inPage: thePage) == 0
ifTrue: "If we're divorcing the top frame we can simply peel it off."
[theIP := stackPages longAt: thePage headSP]
ifFalse: "othewise move all frames above to a new stack and then peel the frame off."
[newPage := stackPages newStackPage.
theIP := self oopForPointer: (self frameCallerSavedIP: frameAbove).
frameAbove := self moveFramesIn: thePage through: frameAbove toPage: newPage.
onCurrent
ifTrue:
[self setStackPageAndLimit: newPage.
+ self setStackPointersFromPage: newPage]
- framePointer := stackPage headFP.
- stackPointer := stackPage headSP]
ifFalse:
[stackPages markStackPageMostRecentlyUsed: newPage].
self assert: (self frameCallerContext: frameAbove) = ctxt].
objectMemory storePointerUnchecked: InstructionPointerIndex
ofObject: ctxt
withValue: (self contextInstructionPointer: theIP frame: theFP).
objectMemory storePointer: SenderIndex
ofObject: ctxt
withValue: callerCtx.
callerFP := self frameCallerFP: theFP.
callerFP == 0 "theFP is a base frame; it is now alone; free the entire page"
ifTrue: [stackPages freeStackPage: thePage]
ifFalse:
[callerIP := self oopForPointer: (self frameCallerSavedIP: theFP).
callerSP := (self frameCallerSP: theFP) - objectMemory wordSize.
stackPages longAt: callerSP put: callerIP.
self setHeadFP: callerFP andSP: callerSP inPage: thePage]
!
Item was changed:
----- Method: StackInterpreter>>externalEnsureIsBaseFrame: (in category 'frame access') -----
externalEnsureIsBaseFrame: aFramePtr
"Ensure aFramePtr is a base frame. Then we can assign its sender.
Answer the possibly moved location of the frame."
| theFP thePage onCurrent |
<var: #aFramePtr type: #'char *'>
<var: #theFP type: #'char *'>
<var: #thePage type: #'StackPage *'>
<returnTypeC: 'char *'>
(self isBaseFrame: aFramePtr) ifTrue:
[^aFramePtr].
theFP := aFramePtr.
thePage := stackPages stackPageFor: theFP.
onCurrent := thePage = stackPage.
"Storing the frame's sender with its caller's context
has the side effect of making theFP a base frame."
theFP := self
storeSenderOfFrame: theFP
withValue: (self ensureCallerContext: theFP).
onCurrent
ifTrue:
[self assert: stackPage ~~ thePage. "stackPage has moved to a new page"
+ self setStackPointersFromPage: stackPage]
- framePointer := stackPage headFP.
- stackPointer := stackPage headSP]
ifFalse:
[stackPages markStackPageMostRecentlyUsed: stackPage].
self assert: stackPages pageListIsWellFormed.
self assert: stackPage = stackPages mostRecentlyUsedPage.
^theFP!
Item was changed:
----- Method: StackInterpreter>>externalInstVar:ofContext:put: (in category 'frame access') -----
externalInstVar: index ofContext: maybeMarriedContext put: anOop
| theFP thePage onCurrentPage |
<var: #theFP type: #'char *'>
<var: #thePage type: #'StackPage *'>
self assert: (objectMemory isContext: maybeMarriedContext).
self externalWriteBackHeadFramePointers.
"Assign the field of a married context."
self deny: (objectMemory isObjImmutable: maybeMarriedContext).
(self isStillMarriedContext: maybeMarriedContext) ifFalse:
[objectMemory storePointer: index ofObject: maybeMarriedContext withValue: anOop.
index = StackPointerIndex ifTrue:
[self ensureContextIsExecutionSafeAfterAssignToStackPointer: maybeMarriedContext].
^nil].
theFP := self frameOfMarriedContext: maybeMarriedContext.
thePage := stackPages stackPageFor: theFP.
self assert: stackPage = stackPages mostRecentlyUsedPage.
onCurrentPage := thePage = stackPage.
index == SenderIndex
ifTrue:
[self storeSenderOfFrame: theFP withValue: anOop]
ifFalse:
[self externalDivorceFrame: theFP andContext: maybeMarriedContext.
objectMemory storePointer: index ofObject: maybeMarriedContext withValue: anOop.
index = StackPointerIndex ifTrue:
[self ensureContextIsExecutionSafeAfterAssignToStackPointer: maybeMarriedContext]].
onCurrentPage
ifTrue:
+ [self setStackPointersFromPage: stackPage]
- [framePointer := stackPage headFP.
- stackPointer := stackPage headSP]
ifFalse:
[stackPages markStackPageMostRecentlyUsed: stackPage].
stackPages assert: stackPage = stackPages mostRecentlyUsedPage.
stackPages assert: stackPages pageListIsWellFormed.
stackPages assert: self validStackPageBaseFrames!
Item was changed:
----- Method: StackInterpreter>>externalSetStackPageAndPointersForSuspendedContextOfProcess: (in category 'frame access') -----
externalSetStackPageAndPointersForSuspendedContextOfProcess: aProcess
"Set stackPage, instructionPointer, framePointer and stackPointer for the suspendedContext of
aProcess, marrying the context if necessary, and niling the suspendedContext slot. This is used
on process switch to ensure a context has a stack frame and so can continue execution."
| newContext theFrame thePage newPage |
<inline: true>
<var: #theFrame type: #'char *'>
<var: #thePage type: #'StackPage *'>
<var: #newPage type: #'StackPage *'>
newContext := objectMemory fetchPointer: SuspendedContextIndex ofObject: aProcess.
self assert: (objectMemory isContext: newContext).
(self isMarriedOrWidowedContext: newContext) ifTrue:
[self assert: (self checkIsStillMarriedContext: newContext currentFP: framePointer)].
objectMemory
storePointerUnchecked: SuspendedContextIndex
ofObject: aProcess
withValue: objectMemory nilObject.
(self isStillMarriedContext: newContext)
ifTrue:
[theFrame := self frameOfMarriedContext: newContext.
thePage := stackPages stackPageFor: theFrame.
theFrame ~= thePage headFP ifTrue:
["explicit assignment of suspendedContext can cause switch to interior frame."
newPage := stackPages newStackPage.
self moveFramesIn: thePage
through: (self findFrameAbove: theFrame inPage: thePage)
toPage: newPage.
stackPages markStackPageLeastMostRecentlyUsed: newPage].
self assert: thePage headFP = theFrame]
ifFalse:
[thePage := self makeBaseFrameFor: newContext.
theFrame := thePage baseFP].
self setStackPageAndLimit: thePage.
+ self setStackPointersFromPage: thePage.
- stackPointer := thePage headSP.
- framePointer := thePage headFP.
(self isMachineCodeFrame: framePointer) ifFalse:
[self setMethod: (self iframeMethod: framePointer)].
instructionPointer := self popStack.
self assertValidExecutionPointe: instructionPointer r: framePointer s: stackPointer!
Item was changed:
----- Method: StackInterpreter>>handleStackOverflow (in category 'message sending') -----
handleStackOverflow
"Check for stack overflow, moving frames to another stack if so.
This should *only* be sent from checkForStackOverflow."
<inline: #never>
| newPage theFP callerFP overflowLimitAddress overflowCount |
<var: #newPage type: #'StackPage *'>
<var: #theFP type: #'char *'>
<var: #callerFP type: #'char *'>
<var: #overflowLimitAddress type: #'char *'>
self assert: stackPointer < stackPage realStackLimit.
self maybeTraceStackOverflow.
statStackOverflow := statStackOverflow + 1.
"The stack has overflowed this page. If the system is executing some recursive algorithm,
e.g. fibonacci, then the system could thrash overflowing the stack if the call soon returns
back to the current page. To avoid thrashing, since overflow is quite slow, we can move
more than one frame. The idea is to record which page has overflowed, and the first
time it overflows move one frame, the second time two frames, and so on. We move no
more frames than would leave the page half occupied."
theFP := framePointer.
stackPage = overflowedPage
ifTrue:
[overflowLimitAddress := stackPage baseAddress - stackPages overflowLimit.
overflowCount := extraFramesToMoveOnOverflow := extraFramesToMoveOnOverflow + 1.
[(overflowCount := overflowCount - 1) >= 0
and: [(callerFP := self frameCallerFP: theFP) < overflowLimitAddress
and: [(self isBaseFrame: callerFP) not]]] whileTrue:
[theFP := callerFP]]
ifFalse:
[overflowedPage := stackPage.
extraFramesToMoveOnOverflow := 0].
self ensureCallerContext: theFP.
newPage := stackPages newStackPage.
self moveFramesIn: stackPage through: theFP toPage: newPage.
self setStackPageAndLimit: newPage.
+ self setStackPointersFromPage: newPage.
- framePointer := stackPage headFP.
- stackPointer := stackPage headSP.
self isCog
ifFalse: "To overflow the stack this must be a new frame, but in Cog base frames are married."
[self assert: (self frameHasContext: framePointer) not.
self assert: (self validInstructionPointer: instructionPointer + 1
inMethod: method
framePointer: framePointer)]
ifTrue:
[self assert: (self validInstructionPointer: instructionPointer + 1
inFrame: framePointer).
self assert: ((self frameHasContext: framePointer) not
or: [objectMemory isContext: (self frameContext: framePointer)])]!
Item was changed:
+ ----- Method: StackInterpreter>>internalPopStackNativeSize: (in category 'internal interpreter access') -----
- ----- Method: StackInterpreter>>internalPopStackNativeSize: (in category 'as yet unclassified') -----
internalPopStackNativeSize: popSize
<option: #LowcodeVM>
"In the StackInterpreter stacks grow down."
nativeSP := self nativeStackPointerIn: localFP.
nativeSP := self nativeStackPointerIn: localFP put: nativeSP + popSize.!
Item was changed:
----- Method: StackInterpreter>>marryContextInNewStackPageAndInitializeInterpreterRegisters: (in category 'frame access') -----
marryContextInNewStackPageAndInitializeInterpreterRegisters: aContext
"Establish aContext at the base of a new stackPage, make the stackPage the
active one and set-up the interreter registers. This is used to boot the system
and bring it back after a snapshot."
<inline: false>
| newPage |
<var: #newPage type: #'StackPage *'>
self assert: stackPage = 0.
newPage := self makeBaseFrameFor: aContext.
self setStackPageAndLimit: newPage.
+ self setStackPointersFromPage: newPage.
- framePointer := stackPage headFP.
- stackPointer := stackPage headSP.
self setMethod: (self iframeMethod: stackPage headFP).
instructionPointer := self popStack!
Item was changed:
----- Method: StackInterpreter>>reestablishContextPriorToCallback: (in category 'callback support') -----
reestablishContextPriorToCallback: callbackContext
"callbackContext is an activation of invokeCallback:[stack:registers:jmpbuf:].
Its sender is the VM's state prior to the callback. Reestablish that state,
and mark calloutContext as dead."
| calloutContext theFP thePage |
<export: true>
<var: #theFP type: #'char *'>
<var: #thePage type: #'StackPage *'>
self flag: #obsolete.
(self isLiveContext: callbackContext) ifFalse:
[^false].
calloutContext := self externalInstVar: SenderIndex ofContext: callbackContext.
(self isLiveContext: calloutContext) ifFalse:
[^false].
"We're about to leave this stack page; must save the current frame's instructionPointer."
self push: instructionPointer.
self externalWriteBackHeadFramePointers.
"Mark callbackContext as dead; the common case is that it is the current frame.
We go the extra mile for the debugger."
(self isSingleContext: callbackContext)
ifTrue: [self markContextAsDead: callbackContext]
ifFalse:
[theFP := self frameOfMarriedContext: callbackContext.
framePointer = theFP "common case"
ifTrue:
[(self isBaseFrame: theFP)
ifTrue: [stackPages freeStackPage: stackPage]
ifFalse: "calloutContext is immediately below on the same page. Make it current."
[instructionPointer := (self frameCallerSavedIP: framePointer) asUnsignedInteger.
stackPointer := framePointer + (self frameStackedReceiverOffset: framePointer) + objectMemory wordSize.
framePointer := self frameCallerFP: framePointer.
^true]]
ifFalse:
[self externalDivorceFrame: theFP andContext: callbackContext.
self markContextAsDead: callbackContext]].
"Make the calloutContext the active frame. The case where calloutContext
is immediately below callbackContext on the same page is handled above."
(self isStillMarriedContext: calloutContext)
ifTrue:
[theFP := self frameOfMarriedContext: calloutContext.
thePage := stackPages stackPageFor: theFP.
"findSPOf:on: points to the word beneath the instructionPointer, but
there is no instructionPointer on the top frame of the current page."
self assert: thePage ~= stackPage.
stackPointer := (self findSPOf: theFP on: thePage) - objectMemory wordSize.
+ framePointer := theFP.
+ self assert: stackPointer < framePointer]
- framePointer := theFP]
ifFalse:
[thePage := self makeBaseFrameFor: calloutContext.
+ self setStackPointersFromPage: thePage].
- framePointer := thePage headFP.
- stackPointer := thePage headSP].
instructionPointer := self popStack.
self setStackPageAndLimit: thePage.
^true!
Item was added:
+ ----- Method: StackInterpreter>>setStackPointersFromPage: (in category 'stack pages') -----
+ setStackPointersFromPage: thePage
+ <var: #thePage type: #'StackPage *'>
+ <inline: true>
+ stackPointer := thePage headSP.
+ framePointer := thePage headFP!
Item was changed:
----- Method: StackInterpreterPrimitives>>primitiveStoreStackp (in category 'object access primitives') -----
primitiveStoreStackp
"Atomic store into context stackPointer.
Also ensures that any newly accessible cells are initialized to nil "
| ctxt newStackp theFP thePage onCurrentPage stackp |
<var: #theFP type: #'char *'>
<var: #thePage type: #'StackPage *'>
ctxt := self stackValue: 1.
newStackp := self stackIntegerValue: 0.
(self successful
and: [newStackp between: 0 and: (objectMemory numSlotsOf: ctxt) - CtxtTempFrameStart]) ifFalse:
[^self primitiveFail].
self externalWriteBackHeadFramePointers.
(self isStillMarriedContext: ctxt) ifTrue:
[theFP := self frameOfMarriedContext: ctxt.
thePage := stackPages stackPageFor: theFP.
((onCurrentPage := thePage = stackPage)
and: [theFP = framePointer]) ifTrue:
[^self primitiveFail]. "Probably easy to do this right here right now (just move stackPointer). But fail for now."
self externalDivorceFrame: theFP andContext: ctxt.
onCurrentPage
ifTrue:
+ [self setStackPointersFromPage: stackPage]
- [framePointer := stackPage headFP.
- stackPointer := stackPage headSP]
ifFalse:
[self assert: stackPage = (stackPages stackPageFor: framePointer).
stackPages markStackPageMostRecentlyUsed: stackPage]].
stackp := self fetchStackPointerOf: ctxt.
"Nil any newly accessible cells"
stackp + 1 to: newStackp do:
[:i | objectMemory storePointerUnchecked: i + CtxtTempFrameStart - 1 ofObject: ctxt withValue: objectMemory nilObject].
self storeStackPointerValue: newStackp inContext: ctxt.
self ensureContextIsExecutionSafeAfterAssignToStackPointer: ctxt.
self pop: 1!
Item was changed:
----- Method: StackToRegisterMappingCogit>>traceMerge: (in category 'simulation only') -----
traceMerge: fixup
<cmacro: '(ign) 0'>
| index original |
(compilationTrace anyMask: 16) ifTrue:
[index := (fixups object identityIndexOf: fixup) - 1.
+ (fixup isBackwardBranchFixup and: [compilationPass notNil and: [compilationPass > 1 and: [(original := fixup simStackPtr) < 0]]]) ifTrue:
- (fixup isBackwardBranchFixup and: [compilationPass > 1 and: [(original := fixup simStackPtr) < 0]]) ifTrue:
[fixup simStackPtr: simStackPtr].
[coInterpreter transcript
ensureCr;
print: index; nextPut: $/; print: index + initialPC;
nextPut: $:; space.
fixup printStateOn: coInterpreter transcript.
coInterpreter transcript cr; flush]
ensure: [original ifNotNil: [fixup simStackPtr: original]]]!
More information about the Vm-dev
mailing list