ruby-x.github.io/app/views/posts/2018/_06-22-1000-tests-and-worki...

127 lines
5.7 KiB
Plaintext

%p
It was almost going to be working binaries and over 1000 tests. But i am coming
more and more to the point where software is measured in number of tests, not
lines of code.
%h2 1000+ Tests
%p.full_width
=image_tag "1000_tests.jpg"
It was shortly after the last post that i first noticed that 1k was approaching.
A little hard to grasp that i have written all those, kind of: what do they
all do?
%p
A good step too: just about 200 tests in 2 months of work. I noticed the couple of
times i didn't have good coverage for new code immediately, i started to have
problems and had to write tests later. It seems it is the only way to understand
even my own code anymore: by making the assumptions explicit. Some of the bugs
where tests were missing are just the classics, +1 or -1 errors. And i feel a real
newbie having to debug for 5 hours to find it was "<" not "<=" .
%h2 Working binaries
%p
But off course it feels good to finally have
%b working binaries.
This is after all the first time that i compile real ruby into real binary.
The compiler does off course have many limitations, but what it does, it does
right. Even it was just Hello World for starters.
%br
=image_tag "hello.jpg"
Off course i tried the next one straight after, "2+2" and .... it worked too.
I don't know if that is just my bad habit or an occupational thing, this
being surprised when things work.
%br
But a little bit about the journey and what how this works.
%h3 Positioning
%p
Since ruby-x approach is oo from the start, we do not rely on the C way of creating
binaries. Instead, the binary is a sort of snapshot of a running system, or
in other words there is only heap.
%p
This means we create binaries that look the same as the memory during runtime,
which is made up of small fixed sized objects. Currently we only have objects
of sizes 2 to the power of 2,3, 4 and 5. Maybe larger later, but with oo
complete data hiding it is easy to extend objects transparently.
%h3 Constant loading
%p
Especially for Code (the only objects larger than 16 words currently),
this presented a challenge. Maybe even an extra challenge on top of the
purely static one, because of the way ARM load constants.
%p
Constant loading happens when a known object or address is loaded into a register.
Arms constant 32bit instruction only allow 10 bit constants to be loaded.
So if the constant is larger (eg the object further away) two instructions instead
of one are needed. But this only becomes clear when all positions of objects
have been determined.
%h3 Event approach
%p
Off course this is not new, and this is in fact the third time i have coded this,
finally getting it right. The problem gets hairy with the 16 words limit,
when the code overlaps the originally assigned length and a new object has
to be inserted.
%p
To keep one methods code continuous, all other methods code has to be moved up,
an thus a whole lot of positions change. Off course when some objects position
change, a load depending on that may go from 1 to 2 instructions and so on and
on.
And then there are the branches that load their targets (forward and backward
branches) off course, and they need to be updated etc etc.
%p
I now have position objects which fire events, and about 4 different kind of
listeners reacting in different ways when different objects change. The whole
thing works, though as with many an event system, it is difficult to say
exactly how. (only easy in the small, not the whole i mean)
%h3 Object continuation
%p
As i mentioned, it is quite straightforward to have larger data amounts, made up
of 16 word chunks, by having a linked list. This is how the BinaryCode objects, that
hold the binary code, do it.
%p
But with the binaries there is an extra twist to this. The BinaryCode object has a
header (the type and next), which are not code. So the code has to jump over this
header at every end of an object.
%p.full_width
=image_tag "binary_codes.jpg"
This is demonstrated by the object dump above. If the assembly is scary, don't
worry, just look at the top left, address 16260, where the BinaryCode object for
the main method starts. You see the first two words are separated, as i said the
type and next (see the 162a0 value is the address of the BinaryCode on the right).
%p
Mainly i wanted to demonstrate the jump, which is the last instruction on the left
side. The
%b b
stands for branch and the address 162a8 is exactly the code of the next BinaryCode,
ie just after the header.
%p
You can just make out on the bottom left, that this is in fact the code for the
"Hello World" , as it jumps to the (Word_Type.) putstring.
%h2 Next steps
%p
Hello World is off course a very small step and work will continue on making other
things work. On the Interpreter side, many more things, like loops, conditionals,
maths and dynamic dispatch already work.
%p
Luckily, part of this push was to make the Interpreter a platform similar to the
Arm. So it too has BinaryCode and works with addresses, not objects as before.
In short the differences between Interpreter and Arm have shrunk, and there is
good reason to believe that much will work quite soon.
%p
Next i will build a testing framework to test the same code on Interpreter and
Arm and see that both work. And specifically get all those working Interpreter
tests working on Arm.
%p
I think then it is time for some benchmarks. It has been a while since
=link_to "i made some," , "/misc/soml_benchmarks.html"
and they were quite promising. Especially loops of the Hello World and
Fibonacci.
%p
On the further horizon i was planning for continuations next, probably with
a small rework of the return mechanism (unified return sequence).