Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Primitive format #802

Merged
merged 5 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions smalltalksrc/VMMaker/CogObjectRepresentation.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,25 @@ CogObjectRepresentation >> genPrimitiveFloatSubtract [
^self genDoubleArithmetic: SubRdRd preOpCheck: nil
]

{ #category : #'primitive generators' }
CogObjectRepresentation >> genPrimitiveFormat [

| jumpIsImm primitiveFailure |
"Only works for the receiver for now"
cogit methodNumArgs > 0 ifTrue: [ ^ UnimplementedPrimitive ].

jumpIsImm := self genJumpImmediate: ReceiverResultReg.

"Now the primitive cannot fail we can directly work on the ReceiverResultReg"
self genGetFormatOf: ReceiverResultReg into: ReceiverResultReg.
self genConvertIntegerToSmallIntegerInReg: ReceiverResultReg.
cogit genPrimReturn.

primitiveFailure := cogit Label.
jumpIsImm jmpTarget: primitiveFailure.
^CompletePrimitive
]

{ #category : #'primitive generators' }
CogObjectRepresentation >> genPrimitiveFullClosureValue [
"Defer to the cogit for this one, to match the split for genPrimitiveClosureValue."
Expand Down
41 changes: 36 additions & 5 deletions smalltalksrc/VMMaker/CogObjectRepresentationForSpur.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -848,9 +848,16 @@ CogObjectRepresentationForSpur >> genGetClassObjectOf: instReg into: destReg scr
<var: #loop type: #'AbstractInstruction *'>
(instReg = destReg or: [instReg = scratchReg or: [destReg = scratchReg]]) ifTrue:
[^BadRegisterSet].

"In instReg comes the receiver (or arg1) that we want to extract the class from. We move it to scratchReg, so we can operate on it (potentially modify it)"
loop := cogit MoveR: instReg R: scratchReg.
"In instReg what we receive is actually a Oop, and as such this can be a Reference (an address) or a Value (an immediate)."
"Here we check if we have an inmmediate:"
cogit AndCq: objectMemory tagMask R: scratchReg.
jumpIsImm := cogit JumpNonZero: 0.

"If we are here, then in instReg (and scratchReg) we have a reference."

self flag: #endianness.
"Get least significant half of header word in destReg"
cogit MoveMw: 0 r: instReg R: scratchReg.
Expand All @@ -864,8 +871,11 @@ CogObjectRepresentationForSpur >> genGetClassObjectOf: instReg into: destReg scr
cogit MoveMw: objectMemory baseHeaderSize r: instReg R: instReg.
cogit Jump: loop.
jumpNotForwarded jmpTarget: cogit Label].

"At this point, regardless of which path we took, in scratchReg we have the ClassIndex."
jumpIsImm jmpTarget:
(cogit MoveR: scratchReg R: destReg).

scratchReg = TempReg
ifTrue:
[cogit PushR: instReg.
Expand All @@ -879,28 +889,49 @@ CogObjectRepresentationForSpur >> genGetClassObjectOf: instReg into: destReg scr
]

{ #category : #'compile abstract instructions' }
CogObjectRepresentationForSpur >> genGetClassObjectOfClassIndex: instReg into: destReg scratchReg: scratchReg [
CogObjectRepresentationForSpur >> genGetClassObjectOfClassIndex: classIndexRegister into: destReg scratchReg: scratchReg [
"Fetch the class object whose index is in instReg into destReg.
It is non-obvious, but the Cogit assumes loading a class does not involve
a runtime call, so do not call classAtIndex:"
self assert: instReg ~= destReg.
self assert: instReg ~= scratchReg.
self assert: classIndexRegister ~= destReg.
self assert: classIndexRegister ~= scratchReg.
self assert: destReg ~= scratchReg.

""
cogit
MoveR: instReg R: scratchReg;
MoveR: classIndexRegister R: scratchReg;
"The ClassIndex is 22 bits: <12><10> . 12 bits for storing the page index. 10 bits for storing the index of the class in the page.
So, to access the upper 12 bits, we shift the index 10 bits to the right: "
LogicalShiftRightCq: objectMemory classTableMajorIndexShift R: scratchReg;
"Now in scratchReg we have the page (the second level table).
Each entry in these pages is 1 word (in 64bits this would be 64, so 8 bytes). We have the ClassIndex, but this is a number corresponding to one of the entries, we need to
convert it to a valid offset within the table, to do that we multiply by 8 (LeftShift by 3) . Now in scratchReg we have the ClassIndex but seen as bytes.
For example if our ClassIndex was 3, now in scratchReg instead of 3 we'd have 24."
LogicalShiftLeftCq: objectMemory shiftForWord R: scratchReg.

self assert: (self shouldAnnotateObjectReference: objectMemory classTableRootObj) not.

"classTableRootObj is the level one table. There is only one of this, and in particular this table is a object too! As such it has a header. So to access all the entries we need to skip the header."
(cogit backEnd isWithinMwOffsetRange: objectMemory classTableRootObj + objectMemory baseHeaderSize)
"Here we basically do a load from memory to a register. The method MoveMw:r:R: corresponds to an instruction which reads from memory, at base: the arg1, offset: the arg2 and puts the result on arg3.
In this case we add the base given by the start of the table (skipping the header) to the offset given by the scratchReg which has the ClassIndex (in bytes) to access to the entry in the table. This entry would have
in turn a reference to the second level table. This new index will be loaded on destReg."
ifTrue: [cogit MoveMw: objectMemory classTableRootObj + objectMemory baseHeaderSize r: scratchReg R: destReg]
ifFalse: [cogit
AddCq: objectMemory classTableRootObj R: scratchReg;
MoveMw: objectMemory baseHeaderSize r: scratchReg R: destReg].

"At this point we have in destReg the index in the second level table, so to access to the ClassIndex given as argument we need to access in this new table by using the 10 other bits of the ClassIndex."
cogit
MoveR: instReg R: scratchReg;
MoveR: classIndexRegister R: scratchReg;
AndCq: objectMemory classTableMinorIndexMask R: scratchReg;
AddCq: objectMemory baseHeaderSize >> objectMemory shiftForWord R: scratchReg;
"Here in scratchReg we have the base (skipping the header) of the second table. We acccess the entry referencing the Class we are interested in by using the index stored in destReg."
MoveXwr: scratchReg R: destReg R: destReg.
"Xwr - memory word whose address is r * word size away from an address in a register [From CogRTLOpcodes >> initilize ]
So here it uses destReg as base, scratchReg as offset and store the result in destReg.
"

^0
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,8 @@ CogOutOfLineLiteralsARMv8Compiler >> concretizeMoveMbrR [
ifTrue: [ :immediate9bitTwoComplementValue |
self
machineCodeAt: 0
put: (self movSize: 1 destinationRegister: destReg imm: 0 shift: 0).
self
machineCodeAt: 4
put: (self ldurbSize: 1 baseRegister: srcReg signedOffset: immediate9bitTwoComplementValue destinationRegister: destReg).
^ machineCodeSize := 8 ]
^ machineCodeSize := 4 ]
ifFalse: [
self loadRelativeLiteralIn: ConcreteIPReg.
self
Expand Down
2 changes: 2 additions & 0 deletions smalltalksrc/VMMaker/Cogit.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -1143,6 +1143,8 @@ Cogit class >> initializePrimitiveTable [

"(221 genPrimitiveClosureValue 0)" "valueNoContextSwitch"
"(222 genPrimitiveClosureValue 1)" "valueNoContextSwitch:"

(231 genPrimitiveFormat 0) "AVERRRRRRRRRRRRRRRR"
tesonep marked this conversation as resolved.
Show resolved Hide resolved

"SmallFloat primitives (540-559)"
(541 genPrimitiveSmallFloatAdd 1)
Expand Down
13 changes: 13 additions & 0 deletions smalltalksrc/VMMaker/InterpreterPrimitives.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -1753,6 +1753,19 @@ InterpreterPrimitives >> primitiveFlushExternalPrimitives [
self flushExternalPrimitives
]

{ #category : #'object access primitives' }
InterpreterPrimitives >> primitiveFormat [

| receiver header format |
receiver := self stackTop.
(objectMemory isImmediate: receiver) ifTrue: [ ^ self primitiveFail ].
guillep marked this conversation as resolved.
Show resolved Hide resolved
header := objectMemory baseHeader: receiver.
format := objectMemory formatOfHeader: header.
self
pop: argumentCount + 1
thenPush: (objectMemory integerObjectOf: format)
]

{ #category : #'arithmetic float primitives' }
InterpreterPrimitives >> primitiveFractionalPart [
"Fractional part of this float.
Expand Down
40 changes: 40 additions & 0 deletions smalltalksrc/VMMaker/ManifestVMMaker.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
Class {
#name : #ManifestVMMaker,
#superclass : #PackageManifest,
#category : #VMMaker
}

{ #category : #'code-critics' }
ManifestVMMaker class >> ruleCodeCruftLeftInMethodsRuleV1FalsePositive [

<ignoreForCoverage>
^ #(#(#(#RGMethodDefinition #(#CogObjectRepresentationForSpur #genGetClassObjectOf:into:scratchReg:instRegIsReceiver: #false)) #'2024-03-20T14:24:39.430485+01:00') )
]

{ #category : #'code-critics' }
ManifestVMMaker class >> ruleExcessiveArgumentsRuleV1FalsePositive [

<ignoreForCoverage>
^ #(#(#(#RGClassDefinition #(#DruidJIT)) #'2023-04-26T00:25:54.122832+02:00') )
]

{ #category : #'code-critics' }
ManifestVMMaker class >> ruleLongMethodsRuleV1FalsePositive [

<ignoreForCoverage>
^ #(#(#(#RGClassDefinition #(#DruidJIT)) #'2023-04-26T00:25:44.408297+02:00') #(#(#RGMethodDefinition #(#CogObjectRepresentationForSpur #genGetClassObjectOf:into:scratchReg:instRegIsReceiver: #false)) #'2024-03-20T14:24:36.77799+01:00') )
]

{ #category : #'code-critics' }
ManifestVMMaker class >> ruleTempsReadBeforeWrittenRuleV1FalsePositive [

<ignoreForCoverage>
^ #(#(#(#RGClassDefinition #(#DruidJIT)) #'2023-04-26T00:25:56.65609+02:00') )
]

{ #category : #'code-critics' }
ManifestVMMaker class >> ruleUncommonMessageSendRuleV1FalsePositive [

<ignoreForCoverage>
^ #(#(#(#RGClassDefinition #(#DruidJIT)) #'2023-04-26T00:25:40.525381+02:00') #(#(#RGClassDefinition #(#Cogit)) #'2023-11-14T14:51:46.485495+01:00') #(#(#RGMethodDefinition #(#CogObjectRepresentationForSpur #genGetClassObjectOfClassIndex:into:scratchReg: #false)) #'2024-03-20T12:06:14.044383+01:00') #(#(#RGMethodDefinition #(#CogObjectRepresentationForSpur #genGetClassObjectOf:into:scratchReg:instRegIsReceiver: #false)) #'2024-03-20T12:09:37.299869+01:00') #(#(#RGMethodDefinition #(#CogObjectRepresentation #genPrimitiveFormat #false)) #'2024-03-21T10:01:25.937395+01:00') )
]
5 changes: 4 additions & 1 deletion smalltalksrc/VMMaker/StackInterpreter.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -1247,7 +1247,10 @@ StackInterpreter class >> initializePrimitiveTable [
(223 229 primitiveFail) "reserved for Cog primitives (see 228 & 229 in CoInterpreterMT"

(230 primitiveRelinquishProcessor)
(231 239 primitiveFail)
(231 primitiveFormat)

(232 239 primitiveFail)

(240 primitiveUTCMicrosecondClock)
(241 primitiveLocalMicrosecondClock)
(242 primitiveSignalAtUTCMicroseconds)
Expand Down
47 changes: 47 additions & 0 deletions smalltalksrc/VMMakerTests/VMJittedGeneralPrimitiveTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -1342,6 +1342,53 @@ VMJittedGeneralPrimitiveTest >> testPrimitiveEqualReturnsABooleanWhenNegativeNum
self assert: self machineSimulator receiverRegisterValue equals: (memory falseObject).
]

{ #category : #'tests - primitiveFormat' }
VMJittedGeneralPrimitiveTest >> testPrimitiveFormatArray [

| primitiveAddress |

primitiveAddress := self compile: [
cogit objectRepresentation genPrimitiveFormat ].

self prepareStackForSendReceiver: (memory newArrayWith: {42 . 123 . 31}).

"Assert it reaches the caller address and we have the format in the receiver register"
self runFrom: primitiveAddress until: callerAddress.
self assert: self machineSimulator receiverRegisterValue equals: (memory integerObjectOf: memory arrayFormat).
]

{ #category : #'tests - primitiveFormat' }
VMJittedGeneralPrimitiveTest >> testPrimitiveFormatFailsWhenReceiverIsImmediate [

| endInstruction primitiveAddress |

primitiveAddress := self compile: [
cogit objectRepresentation genPrimitiveFormat.
"If the primitive fails it continues, so we need to have an instruction to detect the end"
endInstruction := cogit Stop ].

self prepareStackForSendReceiver: (memory integerObjectOf: 42).

"Assert it reaches the Stop and that it does not destroy the value inside the ReceiverResultReg "
self runFrom: primitiveAddress until: endInstruction address.
self assert: self machineSimulator receiverRegisterValue equals: (memory integerObjectOf: 42).
]
guillep marked this conversation as resolved.
Show resolved Hide resolved

{ #category : #'tests - primitiveFormat' }
VMJittedGeneralPrimitiveTest >> testPrimitiveFormatTrueObject [

| primitiveAddress |

primitiveAddress := self compile: [
cogit objectRepresentation genPrimitiveFormat ].

self prepareStackForSendReceiver: (memory trueObject).

"Assert it reaches the caller address and we have the format in the receiver register"
self runFrom: primitiveAddress until: callerAddress.
self assert: self machineSimulator receiverRegisterValue equals: (memory integerObjectOf: 0). "According to SpurMemomyManager >> formatOfHeader:, the format of the True object is 0."
]

{ #category : #'tests - primitiveGreaterOrEqual' }
VMJittedGeneralPrimitiveTest >> testPrimitiveGreaterOrEqualDoesNotCompileIfReceiverTagIsNotSmallInteger [

Expand Down
121 changes: 121 additions & 0 deletions smalltalksrc/VMMakerTests/VMPrimitiveTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -1836,6 +1836,127 @@ VMPrimitiveTest >> testPrimitiveFloat64ArrayAdd [
self assert: (memory fetchFloat64: 1 ofObject: result) equals: 22.0.
]

{ #category : #'tests - primitiveAtPut' }
VMPrimitiveTest >> testPrimitiveFormatFailsWhenReceiverIsImmediate [

| object |
object := self
newObjectWithSlots: 0
format: memory ephemeronFormat
classIndex: memory arrayClassIndexPun.

interpreter push: object.
interpreter primitiveFormat.

self deny: interpreter failed.
self
assert: interpreter stackTop
equals: (memory integerObjectOf: memory ephemeronFormat)
]

{ #category : #'tests - primitiveAtPut' }
VMPrimitiveTest >> testPrimitiveFormatShouldFail [

| object |
object := 42.

"primitiveFloat doesn't support immediates as receiver, so this should fail:"
interpreter push: object.
interpreter primitiveFormat.

self assert: interpreter failed
]

{ #category : #'tests - primitiveAtPut' }
VMPrimitiveTest >> testPrimitiveFormatWithArray [

| object |
object := self
newObjectWithSlots: 0
format: memory arrayFormat
classIndex: memory arrayClassIndexPun.

interpreter push: object.
interpreter primitiveFormat.

self deny: interpreter failed.
self
assert: interpreter stackTop
equals: (memory integerObjectOf: memory arrayFormat)
]

{ #category : #'tests - primitiveAtPut' }
VMPrimitiveTest >> testPrimitiveFormatWithByteFormat [

| object |
object := self
newObjectWithSlots: 0
format: memory firstByteFormat
classIndex: memory arrayClassIndexPun.

interpreter push: object.
interpreter primitiveFormat.

self deny: interpreter failed.
self
assert: interpreter stackTop
equals: (memory integerObjectOf: memory firstByteFormat)
]

{ #category : #'tests - primitiveAtPut' }
VMPrimitiveTest >> testPrimitiveFormatWithCompiledMethod [

| object |
object := self
newObjectWithSlots: 0
format: memory firstCompiledMethodFormat
classIndex: memory arrayClassIndexPun.

interpreter push: object.
interpreter primitiveFormat.

self deny: interpreter failed.
self
assert: interpreter stackTop
equals: (memory integerObjectOf: memory firstCompiledMethodFormat)
]

{ #category : #'tests - primitiveAtPut' }
VMPrimitiveTest >> testPrimitiveFormatWithEphemeron [

| object |
object := self
newObjectWithSlots: 0
format: memory ephemeronFormat
classIndex: memory arrayClassIndexPun.

interpreter push: object.
interpreter primitiveFormat.

self deny: interpreter failed.
self
assert: interpreter stackTop
equals: (memory integerObjectOf: memory ephemeronFormat)
]

{ #category : #'tests - primitiveAtPut' }
VMPrimitiveTest >> testPrimitiveFormatWithWeakArray [

| object |
object := self
newObjectWithSlots: 0
format: memory weakArrayFormat
classIndex: memory arrayClassIndexPun.

interpreter push: object.
interpreter primitiveFormat.

self deny: interpreter failed.
self
assert: interpreter stackTop
equals: (memory integerObjectOf: memory weakArrayFormat)
]

{ #category : #'tests - primitiveImmutability' }
VMPrimitiveTest >> testPrimitiveGetImmutabilityOfImmediateReturnsTrue [

Expand Down