diff --git a/smalltalksrc/VMMaker/CoInterpreter.class.st b/smalltalksrc/VMMaker/CoInterpreter.class.st index 02ce203b2a..46285b7d93 100644 --- a/smalltalksrc/VMMaker/CoInterpreter.class.st +++ b/smalltalksrc/VMMaker/CoInterpreter.class.st @@ -2139,6 +2139,12 @@ CoInterpreter >> convertToMachineCodeFrame: cogHomeMethod bcpc: bcpc [ ^pc ] +{ #category : 'frame access' } +CoInterpreter >> defaultCallerSavedIP [ + + ^ cogit ceBaseFrameReturnPC +] + { #category : 'process primitive support' } CoInterpreter >> deferStackLimitSmashAround: functionSymbol [ "Defer smashes of the stackLimit around the call of functionSymbol (for assert checks)" @@ -2824,6 +2830,18 @@ CoInterpreter >> getBlockCompilationMSecs [ ^ cogit getBlockCompilationMSecs ] +{ #category : 'frame access' } +CoInterpreter >> getCallerIPFromFP: theFP callerFP: callerFP [ + + | callerIP | + callerIP := super getCallerIPFromFP: theFP callerFP: callerFP. + (self isMachineCodeIP: callerIP) ifTrue: [ ^ callerIP ]. + + "If we call a machine code method from the interpreter, we need to use a machine code return address. Then, store the actual caller IP in an extra frame slot and use a trampoline as the return address. When the machine code method returns, it'll return to the trampoline which will restore the interpreter method." + self iframeSavedIP: callerFP put: callerIP. + ^ cogit ceReturnToInterpreterPC +] + { #category : 'cog jit support' } CoInterpreter >> getCheckAllocFiller [ @@ -3812,90 +3830,6 @@ CoInterpreter >> mnuMethodOrNilFor: rcvr [ ^ nil ] -{ #category : 'frame access' } -CoInterpreter >> moveFramesIn: oldPage through: theFP toPage: newPage [ - "Move frames from the hot end of oldPage through to theFP to newPage. - This has the effect of making theFP a base frame which can be stored into. - Answer theFP's new location." - | newSP newFP stackedReceiverOffset delta callerFP callerIP fpInNewPage offsetCallerFP theContext | - - - - - - - - - - - - "A base frame must have a context for cannotReturn: processing." - self assert: (self isBaseFrame: theFP) not. - self assert: self validStackPageBaseFrames. - callerFP := self frameCallerFP: theFP. - self assert: (self frameHasContext: callerFP). - self assert: (objectMemory isContext: (self frameContext: callerFP)). - - "In cog the base frames are married, in non cog they are not" - self isCog ifTrue: [ - theContext := self ensureFrameIsMarried: theFP SP: (self frameReceiverLocation: theFP). - ] ifFalse: [ - theContext := objectMemory nilObject. - ]. - - stackPages - unsignedLongAt: (newSP := newPage baseAddress) put: (self frameContext: callerFP); - unsignedLongAt: (newSP := newSP - objectMemory wordSize) put: theContext. - - stackedReceiverOffset := self frameStackedReceiverOffset: theFP. - "First move the data, leaving room for the caller and base frame contexts. We will fix up frame pointers later." - theFP + stackedReceiverOffset - to: oldPage headSP - by: objectMemory wordSize negated - do: [:source| - newSP := newSP - objectMemory wordSize. - stackPages longAt: newSP put: (stackPages longAt: source)]. - - "newSP = oldSP + delta => delta = newSP - oldSP" - delta := newSP - oldPage headSP. - newFP := newPage baseAddress - stackedReceiverOffset - (2 * objectMemory wordSize). - self setHeadFP: oldPage headFP + delta andSP: newSP inPage: newPage. - newPage baseFP: newFP. - - callerIP := self oopForPointer: (self frameCallerSavedIP: theFP). - callerIP asUnsignedInteger >= objectMemory getMemoryMap startOfObjectMemory ifTrue: - [self iframeSavedIP: callerFP put: callerIP. - callerIP := cogit ceReturnToInterpreterPC]. - stackPages longAt: theFP + stackedReceiverOffset put: callerIP. - self assert: (callerFP < oldPage baseAddress - and: [callerFP > (oldPage realStackLimit - (LargeContextSlots * objectMemory bytesPerOop / 2))]). - oldPage - headFP: callerFP; - headSP: theFP + stackedReceiverOffset. - "Mark the new base frame in the new page" - stackPages - longAt: newFP + FoxCallerSavedIP put: cogit ceBaseFrameReturnPC; - longAt: newFP + FoxSavedFP put: 0. - "Now relocate frame pointers, updating married contexts to refer to their moved spouse frames." - fpInNewPage := newPage headFP. - [offsetCallerFP := self frameCallerFP: fpInNewPage. - offsetCallerFP ~= 0 ifTrue: - [offsetCallerFP := offsetCallerFP + delta]. - stackPages longAt: fpInNewPage + FoxSavedFP put: (self oopForPointer: offsetCallerFP). - (self frameHasContext: fpInNewPage) ifTrue: - [theContext := self frameContext: fpInNewPage. - objectMemory storePointerUnchecked: SenderIndex - ofObject: theContext - withValue: (self withSmallIntegerTags: fpInNewPage). - objectMemory storePointerUnchecked: InstructionPointerIndex - ofObject: theContext - withValue: (self withSmallIntegerTags: offsetCallerFP)]. - fpInNewPage := offsetCallerFP. - fpInNewPage ~= 0] whileTrue. - self assert: self validStackPageBaseFrames. - ^newFP -] - { #category : 'internal interpreter access' } CoInterpreter >> mtemporary: offset in: theFP [ diff --git a/smalltalksrc/VMMaker/CogIA32Compiler.class.st b/smalltalksrc/VMMaker/CogIA32Compiler.class.st index e4a3dcd32b..caaadf26b6 100644 --- a/smalltalksrc/VMMaker/CogIA32Compiler.class.st +++ b/smalltalksrc/VMMaker/CogIA32Compiler.class.st @@ -9,7 +9,7 @@ This class does not take any special action to flush the instruction cache on in " Class { #name : 'CogIA32Compiler', - #superclass : 'CogAbstractInstruction', + #superclass : 'CogX86Compiler', #classVars : [ 'CDQ', 'CLD', @@ -3398,54 +3398,6 @@ CogIA32Compiler >> genPushRd: reg [ ^ inst ] -{ #category : 'smalltalk calling convention' } -CogIA32Compiler >> genPushRegisterArgsForAbortMissNumArgs: numArgs [ - "Ensure that the register args are pushed before the outer and - inner retpcs at an entry miss for arity <= self numRegArgs. The - outer retpc is that of a call at a send site. The inner is the call - from a method or PIC abort/miss to the trampoline." - - "This won't be as clumsy on a RISC. But putting the receiver and - args above the return address means the CoInterpreter has a - single machine-code frame format which saves us a lot of work." - - "Iff there are register args convert - base -> outerRetpc (send site retpc) - sp -> innerRetpc (PIC abort/miss retpc) - to - base -> receiver - (arg0) - (arg1) - outerRetpc - sp -> innerRetpc (PIC abort/miss retpc)" - numArgs <= cogit numRegArgs ifTrue: - [self assert: cogit numRegArgs <= 2. - numArgs = 0 ifTrue: - [cogit MoveMw: 0 r: SPReg R: TempReg. - cogit PushR: TempReg. - cogit MoveMw: objectMemory wordSize * 2 r: SPReg R: TempReg. - cogit MoveR: TempReg Mw: objectMemory wordSize r: SPReg. - cogit MoveR: ReceiverResultReg Mw: 2 * objectMemory wordSize r: SPReg. - ^self]. - numArgs = 1 ifTrue: - [cogit MoveMw: objectMemory wordSize r: SPReg R: TempReg. - cogit PushR: TempReg. - cogit MoveMw: objectMemory wordSize r: SPReg R: TempReg. - cogit PushR: TempReg. - cogit MoveR: ReceiverResultReg Mw: 3 * objectMemory wordSize r: SPReg. - cogit MoveR: Arg0Reg Mw: 2 * objectMemory wordSize r: SPReg. - ^self]. - numArgs = 2 ifTrue: - [cogit PushR: Arg1Reg. - cogit MoveMw: objectMemory wordSize * 2 r: SPReg R: TempReg. - cogit PushR: TempReg. - cogit MoveMw: objectMemory wordSize * 2 r: SPReg R: TempReg. - cogit PushR: TempReg. - cogit MoveR: ReceiverResultReg Mw: 4 * objectMemory wordSize r: SPReg. - cogit MoveR: Arg0Reg Mw: 3 * objectMemory wordSize r: SPReg. - ^self]] -] - { #category : 'smalltalk calling convention' } CogIA32Compiler >> genPushRegisterArgsForNumArgs: numArgs scratchReg: scratchReg [ "Ensure that the register args are pushed before the retpc for arity <= self numRegArgs. This diff --git a/smalltalksrc/VMMaker/CogX64Compiler.class.st b/smalltalksrc/VMMaker/CogX64Compiler.class.st index 2e415f0682..284f91016f 100644 --- a/smalltalksrc/VMMaker/CogX64Compiler.class.st +++ b/smalltalksrc/VMMaker/CogX64Compiler.class.st @@ -12,7 +12,7 @@ AMD64 Architecture Programmer's Manual Volume 5: 64-bit Media and x87 Floating P " Class { #name : 'CogX64Compiler', - #superclass : 'CogAbstractInstruction', + #superclass : 'CogX86Compiler', #classVars : [ 'CDQ', 'CLD', @@ -4021,54 +4021,6 @@ CogX64Compiler >> genPushRd: reg [ ^ inst ] -{ #category : 'smalltalk calling convention' } -CogX64Compiler >> genPushRegisterArgsForAbortMissNumArgs: numArgs [ - "Ensure that the register args are pushed before the outer and - inner retpcs at an entry miss for arity <= self numRegArgs. The - outer retpc is that of a call at a send site. The inner is the call - from a method or PIC abort/miss to the trampoline." - - "This won't be as clumsy on a RISC. But putting the receiver and - args above the return address means the CoInterpreter has a - single machine-code frame format which saves us a lot of work." - - "Iff there are register args convert - base -> outerRetpc (send site retpc) - sp -> innerRetpc (PIC abort/miss retpc) - to - base -> receiver - (arg0) - (arg1) - outerRetpc - sp -> innerRetpc (PIC abort/miss retpc)" - numArgs <= cogit numRegArgs ifTrue: - [self assert: cogit numRegArgs <= 2. - numArgs = 0 ifTrue: - [cogit MoveMw: 0 r: SPReg R: TempReg. - cogit PushR: TempReg. - cogit MoveMw: objectMemory wordSize * 2 r: SPReg R: TempReg. - cogit MoveR: TempReg Mw: objectMemory wordSize r: SPReg. - cogit MoveR: ReceiverResultReg Mw: 2 * objectMemory wordSize r: SPReg. - ^self]. - numArgs = 1 ifTrue: - [cogit MoveMw: objectMemory wordSize r: SPReg R: TempReg. - cogit PushR: TempReg. - cogit MoveMw: objectMemory wordSize r: SPReg R: TempReg. - cogit PushR: TempReg. - cogit MoveR: ReceiverResultReg Mw: 3 * objectMemory wordSize r: SPReg. - cogit MoveR: Arg0Reg Mw: 2 * objectMemory wordSize r: SPReg. - ^self]. - numArgs = 2 ifTrue: - [cogit PushR: Arg1Reg. - cogit MoveMw: objectMemory wordSize * 2 r: SPReg R: TempReg. - cogit PushR: TempReg. - cogit MoveMw: objectMemory wordSize * 2 r: SPReg R: TempReg. - cogit PushR: TempReg. - cogit MoveR: ReceiverResultReg Mw: 4 * objectMemory wordSize r: SPReg. - cogit MoveR: Arg0Reg Mw: 3 * objectMemory wordSize r: SPReg. - ^self]] -] - { #category : 'smalltalk calling convention' } CogX64Compiler >> genPushRegisterArgsForNumArgs: numArgs scratchReg: scratchReg [ "Ensure that the register args are pushed before the retpc for arity <= self numRegArgs. This diff --git a/smalltalksrc/VMMaker/CogX86Compiler.class.st b/smalltalksrc/VMMaker/CogX86Compiler.class.st new file mode 100644 index 0000000000..06898bfddb --- /dev/null +++ b/smalltalksrc/VMMaker/CogX86Compiler.class.st @@ -0,0 +1,55 @@ +Class { + #name : 'CogX86Compiler', + #superclass : 'CogAbstractInstruction', + #category : 'VMMaker-JIT', + #package : 'VMMaker', + #tag : 'JIT' +} + +{ #category : 'smalltalk calling convention' } +CogX86Compiler >> genPushRegisterArgsForAbortMissNumArgs: numArgs [ + "Ensure that the register args are pushed before the outer and + inner retpcs at an entry miss for arity <= self numRegArgs. The + outer retpc is that of a call at a send site. The inner is the call + from a method or PIC abort/miss to the trampoline." + + "This won't be as clumsy on a RISC. But putting the receiver and + args above the return address means the CoInterpreter has a + single machine-code frame format which saves us a lot of work." + + "Iff there are register args convert + base -> outerRetpc (send site retpc) + sp -> innerRetpc (PIC abort/miss retpc) + to + base -> receiver + (arg0) + (arg1) + outerRetpc + sp -> innerRetpc (PIC abort/miss retpc)" + numArgs <= cogit numRegArgs ifTrue: + [self assert: cogit numRegArgs <= 2. + numArgs = 0 ifTrue: + [cogit MoveMw: 0 r: SPReg R: TempReg. + cogit PushR: TempReg. + cogit MoveMw: objectMemory wordSize * 2 r: SPReg R: TempReg. + cogit MoveR: TempReg Mw: objectMemory wordSize r: SPReg. + cogit MoveR: ReceiverResultReg Mw: 2 * objectMemory wordSize r: SPReg. + ^self]. + numArgs = 1 ifTrue: + [cogit MoveMw: objectMemory wordSize r: SPReg R: TempReg. + cogit PushR: TempReg. + cogit MoveMw: objectMemory wordSize r: SPReg R: TempReg. + cogit PushR: TempReg. + cogit MoveR: ReceiverResultReg Mw: 3 * objectMemory wordSize r: SPReg. + cogit MoveR: Arg0Reg Mw: 2 * objectMemory wordSize r: SPReg. + ^self]. + numArgs = 2 ifTrue: + [cogit PushR: Arg1Reg. + cogit MoveMw: objectMemory wordSize * 2 r: SPReg R: TempReg. + cogit PushR: TempReg. + cogit MoveMw: objectMemory wordSize * 2 r: SPReg R: TempReg. + cogit PushR: TempReg. + cogit MoveR: ReceiverResultReg Mw: 4 * objectMemory wordSize r: SPReg. + cogit MoveR: Arg0Reg Mw: 3 * objectMemory wordSize r: SPReg. + ^self]] +] diff --git a/smalltalksrc/VMMaker/Spur32BitMemoryManager.class.st b/smalltalksrc/VMMaker/Spur32BitMemoryManager.class.st index bf961a0a13..3282c3ee15 100644 --- a/smalltalksrc/VMMaker/Spur32BitMemoryManager.class.st +++ b/smalltalksrc/VMMaker/Spur32BitMemoryManager.class.st @@ -102,74 +102,6 @@ Spur32BitMemoryManager >> bytesPerOop [ ^4 ] -{ #category : 'interpreter access' } -Spur32BitMemoryManager >> changeClassOf: rcvr to: argClass [ - "Attempt to change the class of the receiver to the argument given that the - format of the receiver matches the format of the argument. If successful, - answer 0, otherwise answer an error code indicating the reason for failure. - Fail if the format of the receiver is incompatible with the format of the argument, - or if the argument is a fixed class and the receiver's size differs from the size - that an instance of the argument should have." - - | classFormat fixedFields instFormat newFormat classIndex instSlots instBytes | - (self isObjImmutable: rcvr) ifTrue: - [^PrimErrNoModification]. - classFormat := self formatOfClass: argClass. - fixedFields := self fixedFieldsOfClassFormat: classFormat. - classFormat := self instSpecOfClassFormat: classFormat. - instFormat := self formatOf: rcvr. - - "Fail for inability to access classIndex before making contexts snapshot-safe." - (classIndex := self ensureBehaviorHash: argClass) < 0 ifTrue: - [^classIndex negated]. - - "Now check the instance for compatibility and compute odd bits if necessary." - classFormat <= self lastPointerFormat - ifTrue: - [instFormat > self lastPointerFormat ifTrue: - [^PrimErrInappropriate]. - ((instSlots := self numSlotsOf: rcvr) < fixedFields - or: [instSlots > fixedFields and: [self isFixedSizePointerFormat: classFormat]]) ifTrue: - [^PrimErrBadReceiver]. - (instFormat = self indexablePointersFormat - and: [self isContextNonImm: rcvr]) ifTrue: - [coInterpreter makeContextSnapshotSafe: rcvr]. - newFormat := classFormat] - ifFalse: - ["Fail if the class's format is somehow invalid." - classFormat ~= (self classFormatForInstanceFormat: classFormat) ifTrue: - [^PrimErrBadArgument]. - - instBytes := self numBytesOf: rcvr. - classFormat - caseOf: { - [self sixtyFourBitIndexableFormat] - -> [(instBytes anyMask: 7) ifTrue: [^PrimErrBadReceiver]. - newFormat := classFormat]. - [self firstLongFormat] - -> [(instBytes anyMask: 3) ifTrue: [^PrimErrBadReceiver]. - newFormat := classFormat]. - [self firstShortFormat] - -> [(instBytes anyMask: 1) ifTrue: [^PrimErrBadReceiver]. - newFormat := classFormat + (2 - (instBytes >> 1) bitAnd: 1)]. - [self firstByteFormat] - -> [newFormat := classFormat + (4 - instBytes bitAnd: 3)]. - [self firstCompiledMethodFormat] - -> [classFormat ~= self firstCompiledMethodFormat ifTrue: - [^PrimErrInappropriate]. - newFormat := instFormat] } - otherwise: "bits instances cannot be adopted by pointer-like classes..." - [^PrimErrInappropriate]]. - - self set: rcvr classIndexTo: classIndex formatTo: newFormat. - - self assert: (self numBytesOf: rcvr) = (classFormat <= self lastPointerFormat - ifTrue: [instSlots * self bytesPerOop] - ifFalse: [instBytes]). - "ok" - ^0 -] - { #category : 'gc - global' } Spur32BitMemoryManager >> coalesce: obj1 and: obj2 [ | header1NumSlots header2NumSlots obj2slots newNumSlots | diff --git a/smalltalksrc/VMMaker/Spur64BitMemoryManager.class.st b/smalltalksrc/VMMaker/Spur64BitMemoryManager.class.st index c2e12a85ed..c69eaa0695 100644 --- a/smalltalksrc/VMMaker/Spur64BitMemoryManager.class.st +++ b/smalltalksrc/VMMaker/Spur64BitMemoryManager.class.st @@ -115,74 +115,6 @@ Spur64BitMemoryManager >> bytesPerOop [ ^8 ] -{ #category : 'interpreter access' } -Spur64BitMemoryManager >> changeClassOf: rcvr to: argClass [ - "Attempt to change the class of the receiver to the argument given that the - format of the receiver matches the format of the argument. If successful, - answer 0, otherwise answer an error code indicating the reason for failure. - Fail if the format of the receiver is incompatible with the format of the argument, - or if the argument is a fixed class and the receiver's size differs from the size - that an instance of the argument should have." - - | classFormat fixedFields instFormat newFormat classIndex instSlots instBytes | - (self isObjImmutable: rcvr) ifTrue: - [^PrimErrNoModification]. - classFormat := self formatOfClass: argClass. - fixedFields := self fixedFieldsOfClassFormat: classFormat. - classFormat := self instSpecOfClassFormat: classFormat. - instFormat := self formatOf: rcvr. - - "Fail for inability to access classIndex before making contexts snapshot-safe." - (classIndex := self ensureBehaviorHash: argClass) < 0 ifTrue: - [^classIndex negated]. - - "Now check the instance for compatibility and compute odd bits if necessary." - classFormat <= self lastPointerFormat - ifTrue: - [instFormat > self lastPointerFormat ifTrue: - [^PrimErrInappropriate]. - ((instSlots := self numSlotsOf: rcvr) < fixedFields - or: [instSlots > fixedFields and: [self isFixedSizePointerFormat: classFormat]]) ifTrue: - [^PrimErrBadReceiver]. - (instFormat = self indexablePointersFormat - and: [self isContextNonImm: rcvr]) ifTrue: - [coInterpreter makeContextSnapshotSafe: rcvr]. - newFormat := classFormat] - ifFalse: - ["Fail if the class's format is somehow invalid." - classFormat ~= (self classFormatForInstanceFormat: classFormat) ifTrue: - [^PrimErrBadArgument]. - - instBytes := self numBytesOf: rcvr. - classFormat - caseOf: { - [self sixtyFourBitIndexableFormat] - -> [(instBytes anyMask: 7) ifTrue: [^PrimErrBadReceiver]. - newFormat := classFormat]. - [self firstLongFormat] - -> [(instBytes anyMask: 3) ifTrue: [^PrimErrBadReceiver]. - newFormat := classFormat + (2 - (instBytes >> 2) bitAnd: 1)]. - [self firstShortFormat] - -> [(instBytes anyMask: 1) ifTrue: [^PrimErrBadReceiver]. - newFormat := classFormat + (4 - (instBytes >> 1) bitAnd: 3)]. - [self firstByteFormat] - -> [newFormat := classFormat + (8 - instBytes bitAnd: 7)]. - [self firstCompiledMethodFormat] - -> [classFormat ~= self firstCompiledMethodFormat ifTrue: - [^PrimErrInappropriate]. - newFormat := instFormat] } - otherwise: "bits instances cannot be adopted by pointer-like classes..." - [^PrimErrInappropriate]]. - - self set: rcvr classIndexTo: classIndex formatTo: newFormat. - - self assert: (self numBytesOf: rcvr) = (classFormat <= self lastPointerFormat - ifTrue: [instSlots * self bytesPerOop] - ifFalse: [instBytes]). - "ok" - ^0 -] - { #category : 'gc - global' } Spur64BitMemoryManager >> coalesce: obj1 and: obj2 [ | header1NumSlots header2NumSlots obj2slots newNumSlots | diff --git a/smalltalksrc/VMMaker/SpurMemoryManager.class.st b/smalltalksrc/VMMaker/SpurMemoryManager.class.st index 1253d88cc6..4b6a9ef6e9 100644 --- a/smalltalksrc/VMMaker/SpurMemoryManager.class.st +++ b/smalltalksrc/VMMaker/SpurMemoryManager.class.st @@ -2985,7 +2985,64 @@ SpurMemoryManager >> changeClassOf: rcvr to: argClass [ Fail if the format of the receiver is incompatible with the format of the argument, or if the argument is a fixed class and the receiver's size differs from the size that an instance of the argument should have." - self subclassResponsibility + + | classFormat fixedFields instFormat newFormat classIndex instSlots instBytes | + (self isObjImmutable: rcvr) ifTrue: + [^PrimErrNoModification]. + classFormat := self formatOfClass: argClass. + fixedFields := self fixedFieldsOfClassFormat: classFormat. + classFormat := self instSpecOfClassFormat: classFormat. + instFormat := self formatOf: rcvr. + + "Fail for inability to access classIndex before making contexts snapshot-safe." + (classIndex := self ensureBehaviorHash: argClass) < 0 ifTrue: + [^classIndex negated]. + + "Now check the instance for compatibility and compute odd bits if necessary." + classFormat <= self lastPointerFormat + ifTrue: + [instFormat > self lastPointerFormat ifTrue: + [^PrimErrInappropriate]. + ((instSlots := self numSlotsOf: rcvr) < fixedFields + or: [instSlots > fixedFields and: [self isFixedSizePointerFormat: classFormat]]) ifTrue: + [^PrimErrBadReceiver]. + (instFormat = self indexablePointersFormat + and: [self isContextNonImm: rcvr]) ifTrue: + [coInterpreter makeContextSnapshotSafe: rcvr]. + newFormat := classFormat] + ifFalse: + ["Fail if the class's format is somehow invalid." + classFormat ~= (self classFormatForInstanceFormat: classFormat) ifTrue: + [^PrimErrBadArgument]. + + instBytes := self numBytesOf: rcvr. + classFormat + caseOf: { + [self sixtyFourBitIndexableFormat] + -> [(instBytes anyMask: 7) ifTrue: [^PrimErrBadReceiver]. + newFormat := classFormat]. + [self firstLongFormat] + -> [(instBytes anyMask: 3) ifTrue: [^PrimErrBadReceiver]. + newFormat := classFormat + (self paddingForFormatInstBytes: instBytes forElementBytes: 4 shiftForElement: 2)]. + [self firstShortFormat] + -> [(instBytes anyMask: 1) ifTrue: [^PrimErrBadReceiver]. + newFormat := classFormat + (self paddingForFormatInstBytes: instBytes forElementBytes: 2 shiftForElement: 1)]. + [self firstByteFormat] + -> [newFormat := classFormat + (self paddingForFormatInstBytes: instBytes forElementBytes: 1 shiftForElement: 0)]. + [self firstCompiledMethodFormat] + -> [classFormat ~= self firstCompiledMethodFormat ifTrue: + [^PrimErrInappropriate]. + newFormat := instFormat] } + otherwise: "bits instances cannot be adopted by pointer-like classes..." + [^PrimErrInappropriate]]. + + self set: rcvr classIndexTo: classIndex formatTo: newFormat. + + self assert: (self numBytesOf: rcvr) = (classFormat <= self lastPointerFormat + ifTrue: [instSlots * self bytesPerOop] + ifFalse: [instBytes]). + "ok" + ^0 ] { #category : 'object access' } @@ -10231,6 +10288,20 @@ SpurMemoryManager >> outerContextOf: aBlockClosureOop [ ^ self fetchPointer: FullClosureOuterContextIndex ofObject: aBlockClosureOop ] +{ #category : 'interpreter access' } +SpurMemoryManager >> paddingForFormatInstBytes: instBytes forElementBytes: elementSizeInBytes shiftForElement: shiftForElement [ + + "This method is doing the same as below but using bit arithmetic. + shiftForElement SHOULD be log 2 of element size in bytes. We don't do the log2 because 1) is slow 2) it can return floats + + slotsPerWord := (wordSize / elementSize). + trailingBytes := instBytes \\ wordSize. + trailingSlots := trailingBytes / elementSize. + ^ slotsPerWord - trailingSlots. " + + ^ self wordSize // elementSizeInBytes - (instBytes >> shiftForElement) bitAnd: self wordSize // elementSizeInBytes - 1 +] + { #category : 'accessing' } SpurMemoryManager >> pastSpaceStart [ diff --git a/smalltalksrc/VMMaker/StackInterpreter.class.st b/smalltalksrc/VMMaker/StackInterpreter.class.st index c406c444b0..acade6fc72 100644 --- a/smalltalksrc/VMMaker/StackInterpreter.class.st +++ b/smalltalksrc/VMMaker/StackInterpreter.class.st @@ -4921,6 +4921,12 @@ StackInterpreter >> currentBytecode [ ^ currentBytecode ] +{ #category : 'frame access' } +StackInterpreter >> defaultCallerSavedIP [ + + ^ objectMemory nilObject +] + { #category : 'initialization' } StackInterpreter >> defaultNumStackPages [ "Return the default number of stack pages allocate at startup. @@ -6948,6 +6954,12 @@ StackInterpreter >> getBlockCompilationMSecs [ ^objectMemory nilObject ] +{ #category : 'frame access' } +StackInterpreter >> getCallerIPFromFP: theFP callerFP: callerFP [ + + ^ self oopForPointer: (self frameCallerSavedIP: theFP) +] + { #category : 'internal interpreter access' } StackInterpreter >> getCodeCompactionCount [ "This is nil in the StackVM" @@ -10289,7 +10301,7 @@ StackInterpreter >> moveFramesIn: oldPage through: theFP toPage: newPage [ self setHeadFP: oldPage headFP + delta andSP: newSP inPage: newPage. newPage baseFP: newFP. - callerIP := self oopForPointer: (self frameCallerSavedIP: theFP). + callerIP := self getCallerIPFromFP: theFP callerFP: callerFP. stackPages longAt: theFP + stackedReceiverOffset put: callerIP. self assert: (callerFP < oldPage baseAddress and: [callerFP > (oldPage realStackLimit - (LargeContextSlots * objectMemory bytesPerOop / 2))]). @@ -10298,7 +10310,7 @@ StackInterpreter >> moveFramesIn: oldPage through: theFP toPage: newPage [ headSP: theFP + stackedReceiverOffset. "Mark the new base frame in the new page" stackPages - longAt: newFP + FoxCallerSavedIP put: objectMemory nilObject; + longAt: newFP + FoxCallerSavedIP put: self defaultCallerSavedIP; longAt: newFP + FoxSavedFP put: 0. "Now relocate frame pointers, updating married contexts to refer to their moved spouse frames." fpInNewPage := newPage headFP. diff --git a/smalltalksrc/VMMakerTests/VMPrimitiveChangeClassParametrizedTest.class.st b/smalltalksrc/VMMakerTests/VMPrimitiveChangeClassParametrizedTest.class.st new file mode 100644 index 0000000000..900071ad35 --- /dev/null +++ b/smalltalksrc/VMMakerTests/VMPrimitiveChangeClassParametrizedTest.class.st @@ -0,0 +1,100 @@ +Class { + #name : 'VMPrimitiveChangeClassParametrizedTest', + #superclass : 'VMPrimitiveTest', + #instVars : [ + 'originBytes', + 'destinationBytes', + 'arraySize' + ], + #pools : [ + 'VMBasicConstants', + 'VMBytecodeConstants', + 'VMObjectIndices' + ], + #category : 'VMMakerTests-InterpreterTests', + #package : 'VMMakerTests', + #tag : 'InterpreterTests' +} + +{ #category : 'building suites' } +VMPrimitiveChangeClassParametrizedTest class >> testParameters [ + + ^ super testParameters + * (ParametrizedTestMatrix new + forSelector: #originBytes addOptions: { 1. 2. 4. 8 }; + forSelector: #destinationBytes addOptions: { 1. 2. 4. 8 }; + forSelector: #arraySize addOptions: (1 to: 8); + yourself) +] + +{ #category : 'accessing' } +VMPrimitiveChangeClassParametrizedTest >> arraySize [ + + ^ arraySize +] + +{ #category : 'accessing' } +VMPrimitiveChangeClassParametrizedTest >> arraySize: anObject [ + + arraySize := anObject +] + +{ #category : 'accessing' } +VMPrimitiveChangeClassParametrizedTest >> destinationBytes [ + + ^ destinationBytes +] + +{ #category : 'accessing' } +VMPrimitiveChangeClassParametrizedTest >> destinationBytes: anObject [ + + destinationBytes := anObject +] + +{ #category : 'tests - primitiveClass' } +VMPrimitiveChangeClassParametrizedTest >> formatForBytes: bytes [ + + bytes = 1 ifTrue: [ ^ memory firstByteFormat ]. + bytes = 2 ifTrue: [ ^ memory firstShortFormat ]. + bytes = 4 ifTrue: [ ^ memory firstLongFormat ]. + bytes = 8 ifTrue: [ ^ memory sixtyFourBitIndexableFormat ]. + self error: 'Ask Guille or Rasta' +] + +{ #category : 'accessing' } +VMPrimitiveChangeClassParametrizedTest >> originBytes [ + + ^ originBytes +] + +{ #category : 'accessing' } +VMPrimitiveChangeClassParametrizedTest >> originBytes: anObject [ + + originBytes := anObject +] + +{ #category : 'tests - primitiveClass' } +VMPrimitiveChangeClassParametrizedTest >> testPrimitiveChangeClassOfIntegerArrayToByteArray [ + + | integerArray byteArray conversionRatio | + + (arraySize * originBytes) \\ destinationBytes = 0 ifFalse: [ ^ self skip ]. + + conversionRatio := originBytes / destinationBytes. + integerArray := self newBitIndexableOfSize: arraySize + bytesPerSlot: originBytes + format: (self formatForBytes: originBytes). + byteArray := self newBitIndexableOfSize: 0 "the size does not matter" + bytesPerSlot: destinationBytes + format: (self formatForBytes: destinationBytes). + + interpreter + push: integerArray; + push: byteArray. + + interpreter primitiveChangeClass. + + self assert: interpreter successful. + self assert: (memory fetchClassOf: integerArray) equals: (memory fetchClassOf: byteArray). + self assert: (memory stSizeOf: integerArray) equals: arraySize * conversionRatio +]