Skip to content

Commit

Permalink
Moved #moveFramesIn:through:toPage: method to the super class and add…
Browse files Browse the repository at this point in the history
…ed hook methods to specialize in the subclasses (#defaultCallerSavedIP, #getCallerIPFromFP:callerFP:).

Moved #genPushRegisterArgsForAbortMissNumArgs: to the new super class CogX86Compiler.
Moved #changeClassOf:to: method to the super class and rewrote it in terms of the word size.
Added a new test class VMPrimitiveChangeClassParametrizedTest
  • Loading branch information
jordanmontt committed Oct 1, 2024
1 parent bf4e091 commit daf4e3a
Show file tree
Hide file tree
Showing 9 changed files with 261 additions and 321 deletions.
102 changes: 18 additions & 84 deletions smalltalksrc/VMMaker/CoInterpreter.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
Expand Down Expand Up @@ -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 [
<api>
Expand Down Expand Up @@ -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 |
<inline: false>
<var: #oldPage type: #'StackPage *'>
<var: #theFP type: #'char *'>
<var: #newPage type: #'StackPage *'>
<var: #newSP type: #'char *'>
<var: #newFP type: #'char *'>
<var: #callerFP type: #'char *'>
<var: #fpInNewPage type: #'char *'>
<var: #offsetCallerFP type: #'char *'>
<var: #source type: #'char *'>
<returnTypeC: #'char *'>
"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 [

Expand Down
50 changes: 1 addition & 49 deletions smalltalksrc/VMMaker/CogIA32Compiler.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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
Expand Down
50 changes: 1 addition & 49 deletions smalltalksrc/VMMaker/CogX64Compiler.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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
Expand Down
55 changes: 55 additions & 0 deletions smalltalksrc/VMMaker/CogX86Compiler.class.st
Original file line number Diff line number Diff line change
@@ -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]]
]
68 changes: 0 additions & 68 deletions smalltalksrc/VMMaker/Spur32BitMemoryManager.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -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."
<inline: false>
| 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 |
Expand Down
Loading

0 comments on commit daf4e3a

Please sign in to comment.