[Pharo4] Slots: Code Generation and an Example
Hi, So now Opal delegates code generation for read and write to the Slot meta objects. The methods called are #emitValue: and #emitStore:, the parameter is an IRBuilder. With this the implementor of a Slot is free to generate whatever bytecode needed, it will be inlined at the read or assignment site in the method. For the ivar Slot this is e.g. just emitStore: methodBuilder methodBuilder storeInstVar: index emitValue: methodBuilder methodBuilder pushInstVar: index. Now the implementors of Slots should not need to be bothered about bytecode, therefore we have in the superclass of all Slots defaults emit methods that implement a *reflective* read and write using the MOP of the Slot itself (slower than code generation, but often enough): emitValue: aMethodBuilder aMethodBuilder pushLiteral: self; pushReceiver; send: #read: emitStore: aMethodBuilder aMethodBuilder pushLiteral: self; pushReceiver; send: #writeToSlot:of: What this means is that for someone who wants to implement a slot, you do not have to deal with bytecode at all. Here is a simple Slot: Slot subclass: #TestSlot instanceVariableNames: 'value' classVariableNames: '' category: 'Slot-Scopeâ read: anObject ^ value write: aValue to: anObject value := aValue So this is a strange kind of Slot that stores the value in itself, which means it is shared between all objects as there is only one Slot meta object per class per slot. (not something you want to do in real world, but a nice demo) Now we can create a class (not yet easily as we have not yet enabled a class template, so lets use the ClassBuilder directly): PharoClassInstaller make: [ :builder | builder name: #A; slots: { TestSlot named: #iv }; category: #Playground ]. In class A we can implement accessors: iv ^iv iv: anObject iv := anObject They look like normal ivar accesses, but in the background, the compiler delegated to the Slot the code generation, calling emit*, which leads to the bytecode e.g. for the read: 21 <20> pushConstant: iv => TestSlot 22 <70> self 23 <E1> send: read: 24 <7C> returnTop you could override the emit* methods to generate faster code, but to get something running it is not needed. To test, e.g. you can set the slot from one object and read it from another: A new iv: 6 A new iv Then inspect the slot: (A slotNamed: #iv) inspect (this should all Work in 4.0 048, the example is there as TestSlot for now) ...next steps: 1) Class Definition template for classes with Slots 2) How do we save these classes in Monticello? 3) Virtual Slots (Boolean and PropertySlots) Marcus
This is so cool. I remember when we played one afternoon in 2008 to see if this interplay would work. Stef On 27/6/14 10:19, Marcus Denker wrote:
Hi,
So now Opal delegates code generation for read and write to the Slot meta objects. The methods called are #emitValue: and #emitStore:, the parameter is an IRBuilder.
With this the implementor of a Slot is free to generate whatever bytecode needed, it will be inlined at the read or assignment site in the method.
For the ivar Slot this is e.g. just
emitStore: methodBuilder
methodBuilder storeInstVar: index
emitValue: methodBuilder
methodBuilder pushInstVar: index.
Now the implementors of Slots should not need to be bothered about bytecode, therefore we have in the superclass of all Slots defaults emit methods that implement a*reflective* read and write using the MOP of the Slot itself (slower than code generation, but often enough):
emitValue: aMethodBuilder aMethodBuilder pushLiteral: self; pushReceiver; send: #read:
emitStore: aMethodBuilder aMethodBuilder pushLiteral: self; pushReceiver; send: #writeToSlot:of:
What this means is that for someone who wants to implement a slot, you do not have to deal with bytecode at all.
Here is a simple Slot:
Slot subclass: #TestSlot instanceVariableNames: 'value' classVariableNames: '' category: 'Slot-Scopeâ
read: anObject ^ value
write: aValue to: anObject value := aValue
So this is a strange kind of Slot that stores the value in itself, which means it is shared between all objects as there is only one Slot meta object per class per slot. (not something you want to do in real world, but a nice demo)
Now we can create a class (not yet easily as we have not yet enabled a class template, so lets use the ClassBuilder directly):
PharoClassInstaller make: [ :builder | builder name: #A; slots: { TestSlot named: #iv }; category: #Playground ].
In class A we can implement accessors:
iv ^iv
iv: anObject iv := anObject
They look like normal ivar accesses, but in the background, the compiler delegated to the Slot the code generation, calling emit*, which leads to the bytecode e.g. for the read:
21 <20> pushConstant: iv => TestSlot 22 <70> self 23 <E1> send: read: 24 <7C> returnTop
you could override the emit* methods to generate faster code, but to get something running it is not needed.
To test, e.g. you can set the slot from one object and read it from another:
A new iv: 6 A new iv
Then inspect the slot:
(A slotNamed: #iv) inspect
(this should all Work in 4.0 048, the example is there as TestSlot for now)
...next steps:
1) Class Definition template for classes with Slots 2) How do we save these classes in Monticello? 3) Virtual Slots (Boolean and PropertySlots)
Marcus
This is really brilliant! Doru On Sat, Jun 28, 2014 at 9:34 AM, stepharo <stepharo@free.fr> wrote:
This is so cool. I remember when we played one afternoon in 2008 to see if this interplay would work.
Stef
On 27/6/14 10:19, Marcus Denker wrote:
Hi,
So now Opal delegates code generation for read and write to the Slot meta objects. The methods called are #emitValue: and #emitStore:, the parameter is an IRBuilder.
With this the implementor of a Slot is free to generate whatever bytecode needed, it will be inlined at the read or assignment site in the method.
For the ivar Slot this is e.g. just
emitStore: methodBuilder
methodBuilder storeInstVar: index
emitValue: methodBuilder
methodBuilder pushInstVar: index.
Now the implementors of Slots should not need to be bothered about bytecode, therefore we have in the superclass of all Slots defaults emit methods that implement a **reflective** read and write using the MOP of the Slot itself (slower than code generation, but often enough):
emitValue: aMethodBuilder aMethodBuilder pushLiteral: self; pushReceiver; send: #read:
emitStore: aMethodBuilder aMethodBuilder pushLiteral: self; pushReceiver; send: #writeToSlot:of:
What this means is that for someone who wants to implement a slot, you do not have to deal with bytecode at all.
Here is a simple Slot:
Slot subclass: #TestSlot instanceVariableNames: 'value' classVariableNames: '' category: 'Slot-Scopeâ
read: anObject ^ value
write: aValue to: anObject value := aValue
So this is a strange kind of Slot that stores the value in itself, which means it is shared between all objects as there is only one Slot meta object per class per slot. (not something you want to do in real world, but a nice demo)
Now we can create a class (not yet easily as we have not yet enabled a class template, so lets use the ClassBuilder directly):
PharoClassInstaller make: [ :builder | builder name: #A; slots: { TestSlot named: #iv }; category: #Playground ].
In class A we can implement accessors:
iv ^iv
iv: anObject iv := anObject
They look like normal ivar accesses, but in the background, the compiler delegated to the Slot the code generation, calling emit*, which leads to the bytecode e.g. for the read:
21 <20> pushConstant: iv => TestSlot 22 <70> self 23 <E1> send: read: 24 <7C> returnTop
you could override the emit* methods to generate faster code, but to get something running it is not needed.
To test, e.g. you can set the slot from one object and read it from another:
A new iv: 6 A new iv
Then inspect the slot:
(A slotNamed: #iv) inspect
(this should all Work in 4.0 048, the example is there as TestSlot for now)
...next steps:
1) Class Definition template for classes with Slots 2) How do we save these classes in Monticello? 3) Virtual Slots (Boolean and PropertySlots)
Marcus
-- www.tudorgirba.com "Every thing has its own flow"
Marcus Denker wrote:
What this means is that for someone who wants to implement a slot, you do not have to deal with bytecode at all.
This is very cool. Thanks Marcus. I look forward to playing with it.
Here is a simple Slot:
Slot subclass: #TestSlot instanceVariableNames: 'value' classVariableNames: '' category: 'Slot-Scopeâ
read: anObject ^ value
write: aValue to: anObject value := aValue
So this is a strange kind of Slot that stores the value in itself, which means it is shared between all objects as there is only one Slot meta object per class per slot. (not something you want to do in real world, but a nice demo)
Would a real world example for this be an atomic-counter? Such that the first time an instance reads its instance-slot, the slot-class-variable is incremented and stores the result in the instance-slot in an atomic way. (its a bit hard to work out the terminology to use in relation to that example) Just a random thought - should we now describe ':=' assignment operator as the 'slotting' operator, and the result is that the value has been 'slotted' ? /grin -ben
participants (4)
-
Ben Coman -
Marcus Denker -
stepharo -
Tudor Girba