Skip to content

Instantly share code, notes, and snippets.

@bdw

bdw/.gitignore Secret

Forked from anonymous/fib.p6
Last active August 29, 2015 14:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bdw/3f3928bed9428fa386cb to your computer and use it in GitHub Desktop.
Save bdw/3f3928bed9428fa386cb to your computer and use it in GitHub Desktop.
spesh-log.txt
jit-log.txt
*.bin
*#*

MoarVM Internals for the Brave (and Curious)

Hi there! I'm the guy who developed the MoarVM x64 JIT compiler during this year's GSoC. Jonathan has already given a detailed (and clear) overview of MoarVMs optimization subsystems, of which the JIT compiler is but a small step. Today, I'm going to try and show you how you can see this in action. But first, a sample program:

use v6;

sub fibonacci (int $n --> int) {
    my int $x = 1;
    my int $y = 1;
    while $n > 0 {
        $x = $x + $y;
        $y = $x - $y;
        $n = $n - 1;
    }
    return $y;
}

for ^40 -> int $x {
    say fibonacci $x;
}

Careful readers will spot the native integer definitions sprinkled throughout the code. You can write perl6 code as if it is C, too. (After all, that's just one more way to do it). I'm not entirely sure about other backends, but on MoarVM these really work - no object is ever allocated for them.

Rakudo perl 6 can do more than just run this code, it can also show you how it understands it. I've saved the above program as 'fib.p6', and you can get a (rather verbose) textual representation of the syntax tree by using the special target command line option:

perl6-m --target=mast fib.p6

However, this is just a static analysis. What we're really interested in is what happens during runtime. MoarVM will show you all the gory details if you specify a filename with the MVM_SPESH_LOG environment variable, like so.

export MVM_SPESH_LOG=spesh-log.txt; perl6-m fib.p6

From the outside it looks as if nothing special has happened. As they say, looks are deceiving, because MoarVM has faithfully produced a text file that details the way your code has been changed. It is larger even than the first analysis, but it also contains more information. Let's look at the our function:

Inserting logging for specialization of 'fibonacci' (cuid: cuid_2_1418763286.32937)

Before:
Spesh of 'fibonacci' (cuid: cuid_2_1418763286.32937, file: fib.p6:3)

BB 0 (0x4f77118):
  Instructions:
    no_op 
  Successors: 1, 6, 5, 7, 9
  Predeccessors: 
  Dominance children: 1, 5, 6, 7, 9, 10

BB 1 (0x4f77188):
  Instructions:
    checkarity liti16(1), liti16(1)
    param_rp_i r2(1), liti16(0)
    bindlex lex(idx=5,outers=0,$n), r2(1)
    paramnamesused 
    const_i64_16 r0(1), liti16(0)
    bindlex lex(idx=1,outers=0,$x), r0(1)

....

This little snippet is not without jargon, so I'll try to explain. (I did warn you at the top). The 'cuid' is the compilation unit identifier, and it serves to identify the source of any function. Sometimes compilation units correspond to files, and sometimes they don't (like in a REPL loop). The indented blocks denote Basic Blocks. A Basic Block is a sequence of instructions that is uninterrupted by a (conditional) jump. They are important because within a basic block it is always obvious where all the values are coming from.

Further along in our spesh log, we can see how spesh has transformed the first block:

...
  BB 1 (0x4f77188):
    Instructions:
      sp_getarg_i r2(1), liti16(0)
      bindlex lex(idx=5,outers=0,$n), r2(1)
      const_i64_16 r0(1), liti16(0)
 ...

This is the same start of the function as before. What has happened is that the polymorphic instruction param_rp_i to the much simpler instruction sp_getarg_i. As Jonathan explained, we can get away with this because we know exactly how this function is called, which is just at line 15 of our little script.

While the spesh log is certainly interesting, it is no good for light reading. Which is why I wanted to show off a really cool tool that Timo Paulssen (timotimo) made a while ago - a way to transform a piece of the spesh log (the 'spesh graph' that represents the code of a function) into a visual form (with a little help of graphfiz). Unfortunately, I couldn't really get it to work. This demonstrates something important about all the tools that I'm showing today - they've all been designed not for language users but VM developers, and they may all be broken this time next year.

Let's wrap it up. If you're interested in what kind of assembly code the JIT will produce for you, there is also a way to get to that. Run your script again as follows:

export MVM_JIT_BYTECODE_DIR=. # or /tmp, or some other directory
perl6-m fib.p6

If you've followed these instructions directly, what you'll now see is your working directory littered with binary files representing different frames (functions). Every such file will contain the raw binary data generated by the JIT compiler. Inspecting these files is easy enough (as long as you do know x64 assembly):

objdump -D -b binary -m i386:x86-64 -M intel `ls *.fibonacci.bin`

If you're on a mac, that's gobjdump rather than objdump. And if you prefer AT&T syntax over intel syntax, just drop the '-M intel' part. Looking at the output of this bytecode, you might also see the rather wastefull way your code is compiled. After all, I've written this function specifically for simplicity. Yet I count no fewer than 215 'mov' instructions, 18 conditional move instructions, and 16 function calls. As much as MoarVM and perl6 have achieved this year, there is still a lot left to do. And with that, I wish you a hacky christmas :-)

use v6;
sub fibonacci (int $n --> int) {
my int $x = 1;
my int $y = 1;
while $n > 0 {
$x = $x + $y;
$y = $x - $y;
$n = $n - 1;
}
return $y;
}
for ^40 -> int $x {
say fibonacci $x;
}
@raiph
Copy link

raiph commented Dec 18, 2014

s/cuuid/cuid/

s/What has happened is that rather than the polymorphic instruction param_rp_i to the much simpler instruction sp_getarg_i./What has happened is the polymorphic instruction param_rp_i has been converted to the much simpler instruction sp_getarg_i./

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment