diff --git a/smalltalksrc/VMMaker/CogObjectRepresentation.class.st b/smalltalksrc/VMMaker/CogObjectRepresentation.class.st index 21186e93cf..372281038a 100644 --- a/smalltalksrc/VMMaker/CogObjectRepresentation.class.st +++ b/smalltalksrc/VMMaker/CogObjectRepresentation.class.st @@ -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." diff --git a/smalltalksrc/VMMaker/CogObjectRepresentationForSpur.class.st b/smalltalksrc/VMMaker/CogObjectRepresentationForSpur.class.st index 0badf3544d..7e1ece0483 100644 --- a/smalltalksrc/VMMaker/CogObjectRepresentationForSpur.class.st +++ b/smalltalksrc/VMMaker/CogObjectRepresentationForSpur.class.st @@ -848,9 +848,16 @@ CogObjectRepresentationForSpur >> genGetClassObjectOf: instReg into: destReg scr (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. @@ -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. @@ -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 ] diff --git a/smalltalksrc/VMMaker/CogOutOfLineLiteralsARMv8Compiler.class.st b/smalltalksrc/VMMaker/CogOutOfLineLiteralsARMv8Compiler.class.st index ee77dafd33..8c734fac6d 100644 --- a/smalltalksrc/VMMaker/CogOutOfLineLiteralsARMv8Compiler.class.st +++ b/smalltalksrc/VMMaker/CogOutOfLineLiteralsARMv8Compiler.class.st @@ -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 diff --git a/smalltalksrc/VMMaker/Cogit.class.st b/smalltalksrc/VMMaker/Cogit.class.st index 8d8d479640..b1796502a7 100644 --- a/smalltalksrc/VMMaker/Cogit.class.st +++ b/smalltalksrc/VMMaker/Cogit.class.st @@ -1143,6 +1143,8 @@ Cogit class >> initializePrimitiveTable [ "(221 genPrimitiveClosureValue 0)" "valueNoContextSwitch" "(222 genPrimitiveClosureValue 1)" "valueNoContextSwitch:" + + (231 genPrimitiveFormat 0) "AVERRRRRRRRRRRRRRRR" "SmallFloat primitives (540-559)" (541 genPrimitiveSmallFloatAdd 1) diff --git a/smalltalksrc/VMMaker/InterpreterPrimitives.class.st b/smalltalksrc/VMMaker/InterpreterPrimitives.class.st index b0554906c8..61ee5c2644 100644 --- a/smalltalksrc/VMMaker/InterpreterPrimitives.class.st +++ b/smalltalksrc/VMMaker/InterpreterPrimitives.class.st @@ -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 ]. + 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. diff --git a/smalltalksrc/VMMaker/ManifestVMMaker.class.st b/smalltalksrc/VMMaker/ManifestVMMaker.class.st new file mode 100644 index 0000000000..ffd829f1d1 --- /dev/null +++ b/smalltalksrc/VMMaker/ManifestVMMaker.class.st @@ -0,0 +1,40 @@ +Class { + #name : #ManifestVMMaker, + #superclass : #PackageManifest, + #category : #VMMaker +} + +{ #category : #'code-critics' } +ManifestVMMaker class >> ruleCodeCruftLeftInMethodsRuleV1FalsePositive [ + + + ^ #(#(#(#RGMethodDefinition #(#CogObjectRepresentationForSpur #genGetClassObjectOf:into:scratchReg:instRegIsReceiver: #false)) #'2024-03-20T14:24:39.430485+01:00') ) +] + +{ #category : #'code-critics' } +ManifestVMMaker class >> ruleExcessiveArgumentsRuleV1FalsePositive [ + + + ^ #(#(#(#RGClassDefinition #(#DruidJIT)) #'2023-04-26T00:25:54.122832+02:00') ) +] + +{ #category : #'code-critics' } +ManifestVMMaker class >> ruleLongMethodsRuleV1FalsePositive [ + + + ^ #(#(#(#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 [ + + + ^ #(#(#(#RGClassDefinition #(#DruidJIT)) #'2023-04-26T00:25:56.65609+02:00') ) +] + +{ #category : #'code-critics' } +ManifestVMMaker class >> ruleUncommonMessageSendRuleV1FalsePositive [ + + + ^ #(#(#(#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') ) +] diff --git a/smalltalksrc/VMMaker/StackInterpreter.class.st b/smalltalksrc/VMMaker/StackInterpreter.class.st index 978967c393..0103f99d3c 100644 --- a/smalltalksrc/VMMaker/StackInterpreter.class.st +++ b/smalltalksrc/VMMaker/StackInterpreter.class.st @@ -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) diff --git a/smalltalksrc/VMMakerTests/VMJittedGeneralPrimitiveTest.class.st b/smalltalksrc/VMMakerTests/VMJittedGeneralPrimitiveTest.class.st index 4c246543ac..944daa4de2 100644 --- a/smalltalksrc/VMMakerTests/VMJittedGeneralPrimitiveTest.class.st +++ b/smalltalksrc/VMMakerTests/VMJittedGeneralPrimitiveTest.class.st @@ -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). +] + +{ #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 [ diff --git a/smalltalksrc/VMMakerTests/VMPrimitiveTest.class.st b/smalltalksrc/VMMakerTests/VMPrimitiveTest.class.st index 5babef8362..204fdd5d58 100644 --- a/smalltalksrc/VMMakerTests/VMPrimitiveTest.class.st +++ b/smalltalksrc/VMMakerTests/VMPrimitiveTest.class.st @@ -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 [