add a post about new register usage
This commit is contained in:
parent
31699b71ce
commit
898a66d3be
88
_posts/2014-09-12-register-allocation-reviewed.md
Normal file
88
_posts/2014-09-12-register-allocation-reviewed.md
Normal file
@ -0,0 +1,88 @@
|
||||
---
|
||||
layout: news
|
||||
author: Torsten
|
||||
---
|
||||
|
||||
The time of introspection is coming to an end and i am finally producing executables again. (hurrah)
|
||||
|
||||
### Block and exception
|
||||
|
||||
Even neither ruby blocks or exceptions are implemented i have figured out how to do it, which is sort of good news.
|
||||
I'll see off course when the day comes, but a plan is made and it is this:
|
||||
|
||||
No information lives on the machine stack.
|
||||
|
||||
Maybe it's easier to understand this way: All objects live in memory primarily. Whatever get's moved onto the machine
|
||||
stack is just a copy and, for purposes of the gc, does not need to be considered.
|
||||
|
||||
### 4 Objects, 4 registers
|
||||
|
||||
As far as i have determined the vm needs internal access to exactly four objects. These are:
|
||||
|
||||
- Message: the currently received one, ie the one that in a method led to the method being called
|
||||
- Self: this is an instance variable of the message
|
||||
- Frame: local and temporary variables of the method. Also part of the message.
|
||||
- NewMessage: where the next call is prepared
|
||||
|
||||
And, as stated above, all these objects live in memory.
|
||||
|
||||
### Single Set Instruction
|
||||
|
||||
Self and frame are duplicated information, because then it is easier to transfer. After inital trying, i settle on a
|
||||
single Instruction to move data around in the vm, Set. It can move instance variables from any of the objects to any
|
||||
other of the 4 objects.
|
||||
|
||||
The implementation of Set ensures that any move to the self slot in Message gets duplicated into the Self register. Same
|
||||
for the frame, but both are once per method occurances, and both are read only afterwards, so don't need updating later.
|
||||
|
||||
Set, like other instructions may use any other variables at any time. Those registers (r4 and up) are scratch.
|
||||
|
||||
### Simple call
|
||||
|
||||
This makes calling relatively simple and thus easy to understand. To make a call we must be in a method, ie Message,
|
||||
Self and Frame have been set up.
|
||||
|
||||
The method then produces values for the call. This involves operations and the result of that is stored in a variable
|
||||
(tmp/local/arg). When all values have been calculated a NewMessage is created and all data moved there (see Set)
|
||||
|
||||
A Call is then quite simple: because of the duplication of Self and Frame, we only need to push the Message to the
|
||||
machine stack. Then we move the NewMessage to Message, unroll (copy) the Self into it's register and assign a new
|
||||
Frame.
|
||||
|
||||
Returning is also not overly complicated: Remembering that the return value is an instance variable in the
|
||||
Message object. So when the method is done, the value is there, not for example in a dedicated register.
|
||||
So we need to undo the above: move the current Message to NewMessage, pop the previously pushed message from the
|
||||
machine stack and unroll the Self and Frame copies.
|
||||
|
||||
The caller then continues and can pick up the return from it's NewMessage if it is used for further calculation.
|
||||
It's like it did everything to built the (New)Message and immediately the return value was filled in.
|
||||
|
||||
As I said, often we need to calculate the values for the call, so we need to make calls. This happens in exacly the same
|
||||
way, and the result is shuffled to a Frame slot (local or temporary variable).
|
||||
|
||||
### Message creation
|
||||
|
||||
Well, i hear, that sounds good and almost too easy. But .... (always one isn't there) what about the Message and Frame
|
||||
objects, where do you get those from ?
|
||||
|
||||
And this is true: in c the Message does not exist, it's just data in registers and the Frame is created on the stack if
|
||||
needed.
|
||||
|
||||
And unfortunately we can't really make a call to get/create these objects as that would create an endless loop. Hmm
|
||||
|
||||
We need a very fast way to create and reuse these objects: a bit like a stack. So let's just use a Stack :-)
|
||||
|
||||
Off course not the machine stack, but a Stack object. An array to which we append and take from.
|
||||
It must be global off course, or rather accessible from compiling code. And fast may be that we use assembler, or
|
||||
if things work out well, we can use the same code as what makes builtin arrays tick.
|
||||
|
||||
Still, this is a different problem and the full solution will need a bit time. But clearly it is solvable and does
|
||||
not impact above register usage convention.
|
||||
|
||||
### The fineprint
|
||||
|
||||
Just for the sake of completeness: The assumtion i made a the beginning of the Simple Call section, can off course not
|
||||
possibly be always true.
|
||||
|
||||
To boot the vm, we must create the first message by "magic" and place it and the Self (Kernel module reference).
|
||||
As it can be an empty Message for now, this is not difficult, just one of those little gotachs.
|
@ -80,7 +80,7 @@ sub-title: Salama hopes make the the mysterious more accessible, shed light in t
|
||||
But to mention a few things that have crossed my mind (and that i will most certainly not implement)
|
||||
<ul>
|
||||
<li> Efficient vector extensions that use cpu/gpu instructions not supported in the core</li>
|
||||
<li> Efficient grphics extensions</li>
|
||||
<li> Efficient graphics extensions</li>
|
||||
<li> New language features, ie real dsl's that extand the parser on the fly </li>
|
||||
<li> Off course there is always new cpu's and os's</li>
|
||||
<li> Better implementation of core datastructures. Did i hear digital trees being mentioned?</li>
|
||||
|
Loading…
Reference in New Issue
Block a user