Just In Time Compiler

Since 3.12

The old just-in-time compiler didn't work anymore because of LLVM being not backward-compatible. As we have no news from the author, I decided to write a new just-in-time compiler from scratch.

By the way, I'd like to know if the LLVM developers and the GTK+ developers are the same guys...

This unfortunate experience has burned me, so I decided to design it differently. Instead of relying on a undeterministic moving beta API of all possible compilers, the new Gambas JIT compiler translates the Gambas code into C.

How it works

The process is the following:

  • When a function marked with the FAST keyword is called for the first time, the JIT compiler is called.

  • If not yet done, the JIT compiler translates all the fast methods of the current project (or component) into a big C source file.

  • Then the C compiler is called to generate a shared library from that big C source file.

  • The shared lbrary is loaded dynamically.

  • The JIT compiled function is called.

This design has many advantages:
  • You can use either gcc or clang, or tcc (see below).

  • As long as the C standard does not change, the JIT compiler will not break.

But some disadvantages too:
  • The code generation is slower (and even slower with gcc). To workaround this slowness, the compilation is done in background processes.

  • Sometimes a compiler quirk bites you (for example, using __attribute__((noreturn)) makes the gcc optimizer become mad and slow as hell).

The JIT compiler is implemented in a component that is automatically loaded when needed. It has a C part that implements the Gambas to C code translator, and a Gambas part that deals with extracting Gambas source code from executable archive files and assembling all the translations into one big file.

Syntax

The syntax did not change: place the keyword FAST at the top of a Gambas class file, for a class that you want all functions JIT-compiled instead of interpreted, or at the beginning of a function declaration, so that only that function is JIT-compiled.

JIT compiling has a cost, so try to use it only on specific methods that need a boost.

Caveats

Some language syntax is not supported at the moment:
  • The BYREF keyword.

  • Propagating variable arguments to another function call with the ... syntax.

  • Using tcc is possible, but that compiler is not very reliable and may generate incorrect code.

Performance

The biggest increases in speed will be in functions that use a lot of low-level computations and control flows (except For Each loops), such as functions that do a lot of math. However, if the function mostly calls other libraries, you won't see any great increase in speed.

You won't see any significative gain in string management.

See Benchmarks for more details.

Debugging

At the moment, some environment variables control the behavior of the JIT compiler.

Environment variable Description
GB_NO_JIT Set to 1 if you want to disable the JIT compiler entirely.
GB_JIT_DEBUG Set to 1 if you want to see the debugging message emitted by the JIT compiler.
GB_JIT_CC Set to the name of the compiler to use. By default gcc is used. But you can put clang instead, or anything else provided that it uses the same options as gcc or support for it is added in the JIT compiler Gambas part.
GB_JIT_CFLAGS Set it to the flags used for compiling the C code. The default is -O3.

See also