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!
vm-dev@lists.squeakfoundation.org