I am writing a library which will leverage LLVM (via inkwell) to JIT compile some functions. The functions need to be able to call back into some rust functions in my code.
I have it working, but my unit tests don't work, because it seems that the callback functions are optimized away when building the tests. These callback functions are not called by the Rust code itself - only by the dynamically generated JIT functions - so I guess the linker thinks they are unused and drops them.
If I call them from the rust code, within unit test, then they are not removed - but that is not a desirable workaround. Also note that the functions are not removed when the package is built as a library, only when building the tests.
Below is an MVCE.
// lib.rs
use inkwell::{OptimizationLevel, context::Context};
use inkwell::execution_engine::JitFunction;
#[no_mangle]
pub extern "C" fn my_callback(x:i64) {
println!("Called Back x={}", x);
}
type FuncType = unsafe extern "C" fn();
pub fn compile_fn() {
let context = Context::create();
let module = context.create_module("test");
let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None).unwrap();
let builder = context.create_builder();
let func_type = context.void_type().fn_type(&[], false);
let function = module.add_function("test", func_type, None);
let basic_block = context.append_basic_block(function, "entry");
builder.position_at_end(basic_block);
let cb_fn_type = context.void_type().fn_type(&[context.i64_type().into()], false);
let cb_fn = module.add_function("my_callback", cb_fn_type, None);
let x = context.i64_type().const_int(42, false);
builder.build_call(cb_fn, &[x.into()], "callback");
builder.build_return(None);
function.print_to_stderr();
let jit_func:JitFunction<FuncType> = unsafe { execution_engine.get_function("test").unwrap() };
unsafe { jit_func.call() };
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
// If I uncomment this line, it works, otherwise it segfaults
//super::my_callback(1);
super::compile_fn();
}
}
# Cargo.toml
[package]
name = "so_example_lib"
version = "0.1.0"
authors = ["harmic <harmic@no-reply.com>"]
edition = "2018"
[dependencies]
inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", features = ["llvm7-0"] }
// build.rs
fn main() {
println!("cargo:rustc-link-search=/usr/lib64/llvm7.0/lib");
println!("cargo:rustc-link-lib=LLVM-7");
}
You can tell the function has been removed by running nm on the resulting test binary. When you run the test, it segfaults. If you run it in gdb, you can see it is trying to call a function at 0x0000000000000000.
#0 0x0000000000000000 in ?? ()
#1 0x00007ffff7ff201f in test ()
How can I instruct Rust not to drop these functions?