TheDan64 / inkwell

It's a New Kind of Wrapper for Exposing LLVM (Safely)
https://thedan64.github.io/inkwell/
Apache License 2.0
2.38k stars 229 forks source link

execution_engine.run_function keeps giving the same value for different anonymous functions. #397

Closed jzimmerman135 closed 1 year ago

jzimmerman135 commented 1 year ago

The problem I seem to get the same value returned when executing different anonymous functions.

I am looping through top level expressions and generating small functions to run them. Each time a top level expression is parsed, I append a new basic block to the module and get its FunctionValue. I then call it using the execution engine, via execution_engine.run_function(myFunctionValue, false), directly using the FunctionValue generated earlier. But this always produces the same number

To Reproduce

use inkwell::{
    self,
    builder::Builder,
    context::Context,
    module::Module,
    values::{FunctionValue, IntValue},
    OptimizationLevel,
};

fn add_function<'ctx, F>(
    codegen: F,
    context: &'ctx Context,
    module: &Module<'ctx>,
    builder: &Builder<'ctx>,
) -> FunctionValue<'ctx>
where
    F: FnOnce(&'ctx Context, &Module<'ctx>, &Builder<'ctx>) -> IntValue<'ctx>,
{
    let fn_type = context.i32_type().fn_type(&[], false);
    let function = module.add_function("", fn_type, None);
    let basic_block = context.append_basic_block(function, "top_level_entry");
    builder.position_at_end(basic_block);
    let return_val = codegen(context, module, builder);

    builder.build_return(Some(&return_val));

    assert!(function.verify(false));
    function
}

fn main() {
    let context = Context::create();
    let module = context.create_module("bug-example");
    let builder = context.create_builder();

    let f1 = add_function(
        |context, _, _| context.i32_type().const_int(20, false),
        &context,
        &module,
        &builder,
    );

    let f2 = add_function(
        |context, _, _| {
            let lhs = context.i32_type().const_int(50, false);
            let rhs = context.i32_type().const_int(13, false);
            builder.build_int_add(lhs, rhs, "add")
        },
        &context,
        &module,
        &builder,
    );

    let execution_engine = module
        .create_jit_execution_engine(OptimizationLevel::None)
        .unwrap();
    let res1 = unsafe { execution_engine.run_function(f1, &[]) };
    let res2 = unsafe { execution_engine.run_function(f2, &[]) };
    println!("f1 res: {}, from {:?}", res1.as_int(true), f1);
    println!("f2 res: {}, from {:?}", res2.as_int(true), f2);
}

This produces the output:

f1 res: 20, from FunctionValue { name: "", address: 0x13063a958, is_const: true, is_null: false, llvm_value: "define i32 @0() {\ntop_level_entry:\n  ret i32 20\n}\n", llvm_type: "i32 ()" }
f2 res: 20, from FunctionValue { name: "", address: 0x130642208, is_const: true, is_null: false, llvm_value: "define i32 @1() {\ntop_level_entry:\n  ret i32 63\n}\n", llvm_type: "i32 ()" }

Expected Behavior I would expect that it calls the correct function each time. So f2 res: 63, instead of 20. Since it seems to be calling the right function address.

LLVM Version:

Desktop:

Additional Context I'm new to LLVM and Inkwell so very likely just a user error, but I would appreciate any help! Thanks.

jzimmerman135 commented 1 year ago

I figured out the issue. Please correct me if I am wrong, but just for the sake of others. If you use a Jit engine you cannot add to the module after you run a function, you need to create an interpreter engine to do that.