Here I’ll explain how to resolve that issue and in the process explain how you can call a Rust function that you’ve written from Cranelift code.
By the end of this you’ll be able to write a simple little print function in your compiled programming language.
To start, let’s assume you have a Rust function you’d like to call. Mine is pretty simple and just prints a value from my Lust programming language to standard out:
pub fn print_lustc_word(word: Word) -> Word {
let expr = Expr::from_immediate(word);
println!("{}", expr);
Expr::Nil.immediate_rep()
}I’ve been using this to debug my programs by printing the last value that they evaluate. This morning the thought came to me that if I could call lustc_print_word from Lust code I’d have a print function. Very cool.
To start, navigate in your code to where you construct your Cranelift JIT and add the following lines:
// Register the print function.
let print_addr = print_lustc_word as *const u8;
builder.symbol("print_lustc_word", print_addr);Now my JIT building looks something like this:
let mut builder = JITBuilder::new(cranelift_module::default_libcall_names());
// Register the print function.
let print_addr = print_lustc_word as *const u8;
builder.symbol("print_lustc_word", print_addr);Once you’ve done that it’s smooth sailing. Here’s how you can call that function from Cranelift code:
let mut sig = ctx.module.make_signature();
sig.params.push(AbiParam::new(ctx.word));
sig.returns.push(AbiParam::new(ctx.word));
let callee = ctx
.module
.declare_function("print_lustc_word", cranelift_module::Linkage::Import, &sig)
.map_err(|e| e.to_string())?;
let local_callee = ctx
.module
.declare_func_in_func(callee, &mut ctx.builder.func);
let call = ctx.builder.ins().call(local_callee, &args);
let res = ctx.builder.inst_results(call)[0]Cool. You’re done. It is actually that easy. Here’s the complete code if you’re interested.
If you liked this consider giving Lust a GitHub star to make me feel better about spending my free time working on it.