Version 1 (modified by whiteknight, 5 years ago)

Initial draft

The current JIT system is not a suitable implementation of JIT for Parrot. It should be deprecated and replaced with a better system

Definitions

  • A "C Function" is a function written in C that performs some task
  • A "JIT Definition Function" is a function written in C that generates the machine code for a function that performs some task

A Good JIT Will…

Here are some basic requirements:

  • Generate machine code on the fly for all supported platforms
  • Generate call frames on the fly for NCI calls, on all supported platforms, for all NCI call signatures

Notice that we could end up with NCI call frame builders that do not require the JIT (will probably require lots of platform-specific assembly, but is possible).

Here are some nice extras that a "Great" JIT will provide:

  • Perform optimizations on generated machine code, to a degree specified by the user
  • Be able to output generated machine code to an executable or object file
  • Be usable in other situations where machine code will be needed, not just tied to the translation of PASM ops
  • Be platform agnostic, so far as Parrot's internals are concerned. Not require different behaviors and calls internally for different platforms.

Generating JIT Definition Functions

Every op will need at least two implementations: One for each of the "normal" runcores, and one to build the JIT version at runtime. Ideally, we would be able to produce both from a single specification, without needing to create and maintain two separate implementations of each op. There are two paths to go about doing this:

  • Write an OPS parser using PGE. Use miniparrot to parse the .ops files and output both C language and JIT definition code for each op. Compile Parrot using both of these.
  • Implement OPS in Lorito. Write a series of conversion tools to convert Lorito ops into C language and JIT definition code for each op. Compile Parrot using both of these.
  • Implement all OPS as JIT definitions. Use miniparrot to output the machine code definitions for all ops, and link those into Parrot when we build.

We absolutely do not want to have to maintain multiple separate implementations of each op. We should be able to define them once and use automated tools to generate different output forms for each. These conversions can all be done at build time.

Notice that using Lorito as our implementation target will allow us to translate Parrot ops into any target language we want (machine code, JIT definitions, bytecode formats for other VMs, etc)

JIT Engine Options

Because we can be generating JIT definitions at build time, a smart code generator can be used to generate the code for multiple JIT backends. Here are some options for backends we can support. We should be able to target all of these, depending on which the user has installed on their system.

  • LLVM
  • libJIT
  • GNU Lightning
  • Roll our own

The fourth option is possible, but it is unlikely that we will be able to produce a JIT engine as portable, performant, and robust as an existing solution. It is more likely that we can generate a variety of code generators that target all these JITs with less effort then it would take to write our own that works on all our target platform

Path Forward

Here are some general steps forward

  • Decide on a standard intermediate language for our Ops (C what we have now, Lorito, or JIT Definitions)
  • Write a parser for that intermediate language
  • Write code generation backends for the intermediate language to output C Functions and JIT Definitions for at least one JIT engine *
  • Write all the ops in the intermediate language *

Note: if our intermediate language for Ops is C, we can ignore at least part of the starred steps, but will have to do a lot more work building a suitable parser.