Using libJIT in Anger

LibJIT is a Just-In-Time compilation library, aimed at providing a flexible and efficient environment for generating and executing machine code from C, C++, or other source languages. Here’s a simple tutorial on creating a basic JIT-compiled function using LibJIT in C.

Ensure LibJIT is installed on your system. You can find installation instructions or pre-built packages specific to your operating system from the official LibJIT repository or package managers.

Start by including the necessary headers. You'll primarily need jit.h.

#include <jit/jit.h>
#include <jit/jit-dump.h>  // If you want to enable debug dumping

We need to describe the function signature — both the return type and the types of the arguments.

int main() {
    jit_context_t context;
    jit_type_t params[2];
    jit_type_t signature;
    jit_function_t function;
    jit_int arg1 = 1, arg2 = 2;
    jit_int result;

    // Create a context to hold the JIT's state
    context = jit_context_create();

    // Build the function signature: (int, int) returns int
    params[0] = jit_type_int;
    params[1] = jit_type_int;
    signature = jit_type_create_signature(jit_abi_cdecl, jit_type_int, params, 2, 1);

Create a function instance within the context and build it by adding operations (instructions).

    // Lock the context while we build and compile the function
    jit_context_build_start(context);

    // Create a new function based on the previously created signature
    function = jit_function_create(context, signature);

    // Define function parameters
    jit_value_t x = jit_value_get_param(function, 0);
    jit_value_t y = jit_value_get_param(function, 1);

    // Perform the addition of x and y
    jit_value_t sum = jit_insn_add(function, x, y);

    // Return the result
    jit_insn_return(function, sum);

    // Compile the function
    jit_function_compile(function);

    // End context lock
    jit_context_build_end(context);

Now that the function is compiled, you can execute it.

    // Execute the compiled function
    jit_function_apply(function, (void **)&args, &result);

    // Print the result
    printf("Result: %d\n", result);

    // Cleanup resources
    jit_context_destroy(context);

    return 0;
}

To compile this example, you need to link against libjit. Typically, you can do this using the following command on a Unix-like OS:

gcc -o jit_example jit_example.c $(pkg-config --cflags --libs libjit)

Then, run the output binary:

./jit_example

This should output the result of 1 + 2.

Ok, now let's create a function that performs addition on two vectors of integers. First, we need to define the vector data type and the function signature:

int main() {
    jit_context_t context;
    jit_type_t params[2];
    jit_type_t signature;
    jit_function_t function;
    jit_int result[4];

    // Create a context to hold the JIT's state
    context = jit_context_create();

    // Define vector data type (for 4 integer values)
    jit_type_t vec_type = jit_type_create_nint_type(4, 0);

    // Build the function signature: (vector, vector) returns vector
    params[0] = vec_type;
    params[1] = vec_type;
    signature = jit_type_create_signature(jit_abi_cdecl, vec_type, params, 2, 1);

Lock the context to build and compile the SIMD function:

    // Lock the context while we build and compile the function
    jit_context_build_start(context);

    // Create a new function based on the previously created signature
    function = jit_function_create(context, signature);

    // Define function parameters
    jit_value_t vec1 = jit_value_get_param(function, 0);
    jit_value_t vec2 = jit_value_get_param(function, 1);

    // Perform the SIMD addition of vec1 and vec2
    jit_value_t vec_sum = jit_insn_add(function, vec1, vec2);

    // Return the result
    jit_insn_return(function, vec_sum);

    // Compile the function
    jit_function_compile(function);

    // End context lock
    jit_context_build_end(context);

Define input vectors and execute the compiled function:

    // Define input vectors
    int args1[] = {10, 20, 30, 40};
    int args2[] = {1, 2, 3, 4};
    void *args[] = {args1, args2};

    // Execute the compiled function
    jit_function_apply(function, args, result);

    // Print the result
    printf("Result: [%d, %d, %d, %d]\n", result[0], result[1], result[2], result[3]);

    // Cleanup resources
    jit_context_destroy(context);

    return 0;
}

And then compile and run:

gcc -o jit_vector_example jit_vector_example.c $(pkg-config --cflags --libs libjit)

Run the binary:

./jit_vector_example

This will output the result of the vector addition [11, 22, 33, 44], demonstrating how SIMD operations can be effectively utilized.