Monday, 28 March 2011

Crazy JIT Prototype. Part 3.

Hi there


Since last post about “Crazy JIT Prototype” and progress of opsc_llvm branch I moved little bit further. Two major achievements:
  • Skeleton for generating JITted Subs is done.
  • JITting of simple ops with function calls and constants
  • Emulator of Parrot's runcore from within Parrot it self.

Emulation of runcore is quite significant in terms of prototyping progress. Now I can not only generate functions using LLVM bindings, but also invoke it. This was grown up from another “crazy” idea for accessing parrot's guts.

Major problem with “emulated runcores” was that we don't expose enough guts to external world. And my JITter is very external. I was struggling by few days thinking how I can implement it. Until after plobsing mentioned on irc channel that op dlopen (for lookup symbols in loaded DLLs) without actual DLL will lookup symbols inside currently running application. Bingo! “I can use dlopen for lookup inside Parrot. And use Parrot Embed API for generating required stuff”. Few hours later I hacked first version of runcore emulator using this technique. It looks like this:

# Create interp and seed it with bytecode
my $this_interp := pir::getinterp();
my $interp := func("make_interpreter", "ppi")($this_interp, 0);
func("Parrot_api_load_bytecode_file", "iPSP")($this_interp, $pbc, undef);

# "Invoke" target Sub inside target interp.
my $sub := Q:PIR{ %r = find_sub_not_null "main" };
my $pc;
$pc := func("Parrot_PMC_invoke", "ppPP")($interp, $sub, $pc);
What happens in this code is:
  1. Create new parrot_interp as child of current one.
  2. Load PBC into it.
  3. Call Sub.invoke() which do a lot of things to prepare CallContext, Continuation, etc inside new interpreter.
After this I just generate JITted version of Sub using LLVM and call it:
# Some engine
my $engine := pir::new__psp("LLVM_Engine", $module);
my $call := $engine.create_call(%jit_context, "ppp");

# Go!
say("================= INVOKE ===================");
$pc := $call($pc, $interp);
say("=================  DONE  ===================");
All this glued together: First — PIR:
.sub 'main' :main
    say "Answer"
    say 42
    say "Correct"
    say 3.1415926
.end
Second — old good t/jit/test.jit (which is much shorter now due proper encapsulation inside Ops::JIT
#! parrot-nqp

# We want Test::More features for testing. Not NQP's builtin.
pir::load_bytecode("opsc.pbc");

# Some preparation
my $debug := 0;
my $pir := 't/compilers/opsc/data/03.pir';
my $pbc := subst($pir, / 'pir' $/, 'pbc');

# Generate PBC file
my @args := list("./parrot", "-o", $pbc, $pir);
my $res := pir::spawnw__ip(@args);

# OpLib
my $oplib := pir::new__psp("OpLib", "core_ops");

# Parse "jitted.ops"
my $ops_file := Ops::File.new("t/jit/jitted.ops",
    :oplib($oplib),
    :core(0),
    :quiet(!$debug),
);

my $jitter := Ops::JIT.new($pbc, $ops_file, $oplib, debug => $debug);

my $start := 0;
my %jit_context := $jitter.jit($start);
my $module := %jit_context<_module>;

#%jit_context.dump();
#$module.dump();

$module.verify();

# Create interp and seed it with bytecode
my $this_interp := pir::getinterp();
my $interp := func("make_interpreter", "ppi")($this_interp, 0);
func("Parrot_api_load_bytecode_file", "iPSP")($this_interp, $pbc, undef);

# "Invoke" target Sub inside target interp.
my $sub := Q:PIR{ %r = find_sub_not_null "main" };
my $pc;
$pc := func("Parrot_PMC_invoke", "ppPP")($interp, $sub, $pc);


# Some engine
my $engine := pir::new__psp("LLVM_Engine", $module);
my $call := $engine.create_call(%jit_context, "ppp");

# Go!
say("================= INVOKE ===================");
$pc := $call($pc, $interp);
say("=================  DONE  ===================");


sub func($name, $sig) {
    pir::dlfunc__ppss(undef, $name, $sig);
}

#%jit_context<_module>.dump();
# vim: ft=perl6
And (drum roll) test run:
~/src/parrot (jit_prototype)$ ./parrot-nqp t/jit/test.t 
Segmentation fault

Hooray!

Wait a second. Wrong window.

~/src/parrot (jit_prototype)$ ./parrot-nqp t/jit/test.t
================= INVOKE ===================
Answer
42
Correct
3.1415926
=================  DONE  ===================
Much better now :) See you next time. Probably with some more good news. E.g. handling of control structures in Ops::JIT. E.g. if, for, etc.