Addition

Setup

The code is available in calculator/examples/llvm/src/main.rs. Because my llvm-config --version shows 14.0.6 so I'm using features = ["llvm14-0"] in inkwell

inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", features = ["llvm14-0"] }

Go to calculator/examples/llvm sub-crate and cargo run.

Add Function

We want to define an add function like

add(x: i32, y: i32) -> i32 { x + y }

but using the LLVM language and JIT it. For that, we need to define every bit of what makes up a function through LLVM basic constructs such as context, module, function signature setups, argument types, basic block, etc.

Here is how to stitch our add function in LLVM

  1. We start by creating a context, adding the addition module and setting up the data type we want to use i32_type of type IntType
    let context = Context::create();
    let module = context.create_module("addition");
    let i32_type = context.i32_type();
  1. We define the signature of add(i32, i32) -> i32, add the function to our module, create a basic block entry point and a builder to add later parts

    let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false);
    let fn_val = module.add_function("add", fn_type, None);
    let entry_basic_block = context.append_basic_block(fn_val, "entry");

    let builder = context.create_builder();
    builder.position_at_end(entry_basic_block);
  1. We create the arguments x and y and add them to the builder to make up the return instruction
    let x = fn_val.get_nth_param(0).unwrap().into_int_value();
    let y = fn_val.get_nth_param(1).unwrap().into_int_value();

    let ret = builder.build_int_add(x, y, "add").unwrap();
    let return_instruction = builder.build_return(Some(&ret)).unwrap();
  1. Finally, we create a JIT execution engine (with no optimization for now) and let LLVM handle rest of the work for us
    let execution_engine = module
        .create_jit_execution_engine(OptimizationLevel::None)
        .unwrap();
    unsafe {
        type Addition = unsafe extern "C" fn(i32, i32) -> i32;
        let add: JitFunction<Addition> = execution_engine.get_function("add").unwrap();
        let x = 1;
        let y = 2;
        assert_eq!(add.call(x, y), x + y);
    }

Yes! all of this just to add two integers.