Skip to content

Replace Get function address #36

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Apr 23, 2018
4 changes: 0 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ enum-methods = "0.0.8"
libc = "*"
llvm-sys = "38"

[[example]]
name = "kaleidoscope"
path = "examples/kaleidoscope/main.rs"

[badges]
travis-ci = { repository = "TheDan64/inkwell" }
codecov = { repository = "TheDan64/inkwell" }
Expand Down
84 changes: 59 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,47 +48,81 @@ Documenation is automatically deployed [here](https://thedan64.github.io/inkwell
### Tari's [llvm-sys example](https://bitbucket.org/tari/llvm-sys.rs/src/ea4ac92a171da2c1851806b91e531ed3a0b41091/examples/jit-function.rs) written in safe code<sup>1</sup> with Inkwell:

```rust
use inkwell::context::Context;
extern crate inkwell;

use inkwell::OptimizationLevel;
use inkwell::builder::Builder;
use inkwell::context::Context;
use inkwell::execution_engine::{ExecutionEngine, Symbol};
use inkwell::module::Module;
use inkwell::targets::{InitializationConfig, Target};
use std::mem::transmute;
use std::error::Error;

/// Convenience type alias for the `sum` function.
///
/// Calling `sum` is innately `unsafe` because there's no guarantee it doesn't
/// do `unsafe` operations internally.
type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64;

fn main() {
Target::initialize_native(&InitializationConfig::default()).unwrap();
run().unwrap();
}

Target::initialize_native(&InitializationConfig::default())?;
fn run() -> Result<(), Box<Error>> {
let context = Context::create();
let module = context.create_module("sum");
let builder = context.create_builder();
let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?;

let context = Context::create();
let module = context.create_module("sum");
let builder = context.create_builder();
let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?;
let sum = jit_compile_sum(&context, &module, &builder, &execution_engine)
.ok_or("Unable to JIT compile `sum`")?;

let i64_type = context.i64_type();
let fn_type = i64_type.fn_type(&[&i64_type, &i64_type, &i64_type], false);
let x = 1u64;
let y = 2u64;
let z = 3u64;

let function = module.add_function("sum", &fn_type, None);
let basic_block = context.append_basic_block(&function, "entry");
unsafe {
println!("{} + {} + {} = {}", x, y, z, sum(x, y, z));
assert_eq!(sum(x, y, z), x + y + z);
}

builder.position_at_end(&basic_block);
Ok(())
}

let x = function.get_nth_param(0)?.into_int_value();
let y = function.get_nth_param(1)?.into_int_value();
let z = function.get_nth_param(2)?.into_int_value();
fn jit_compile_sum(
context: &Context,
module: &Module,
builder: &Builder,
execution_engine: &ExecutionEngine,
) -> Option<Symbol<SumFunc>> {
let i64_type = context.i64_type();
let fn_type = i64_type.fn_type(&[&i64_type, &i64_type, &i64_type], false);

let sum = builder.build_int_add(&x, &y, "sum");
let sum = builder.build_int_add(&sum, &z, "sum");
let function = module.add_function("sum", &fn_type, None);
let basic_block = context.append_basic_block(&function, "entry");

builder.build_return(Some(&sum));
builder.position_at_end(&basic_block);

let addr = execution_engine.get_function_address("sum")?;
let x = function.get_nth_param(0)?.into_int_value();
let y = function.get_nth_param(1)?.into_int_value();
let z = function.get_nth_param(2)?.into_int_value();

let sum: extern "C" fn(u64, u64, u64) -> u64 = unsafe { transmute(addr) };
let sum = builder.build_int_add(&x, &y, "sum");
let sum = builder.build_int_add(&sum, &z, "sum");

let x = 1u64;
let y = 2u64;
let z = 3u64;
builder.build_return(Some(&sum));

assert_eq!(sum(x, y, z), x + y + z);
unsafe { execution_engine.get_function("sum").ok() }
}
```

<sup>1</sup> Casting the LLVM JIT function address into a rust function does require a single unsafe transmute, since Inkwell doesn't know what the function signature is. Maybe we can do something about this in the future? In theory, fn_type does contain all the needed info, so whether or not we can do this automagically depends on what rust is capable of. Converting structs, pointers, and other types could be tricky but might be seen as a form of deserialization. See [#5](https://github.com/TheDan64/inkwell/issues/5) for the tracking issue.
<sup>1</sup> There are two uses of `unsafe` in this example because the actual
act of compiling and executing code on the fly is innately `unsafe`. For one,
there is no way of verifying we are calling `get_function()` with the right function
signature. It is also `unsafe` to *call* the function we get because there's no
guarantee the code itself doesn't do `unsafe` things internally (the same reason
you need `unsafe` when calling into C).

### LLVM's [Kaleidoscope Tutorial](https://llvm.org/docs/tutorial/index.html)

Expand Down
67 changes: 67 additions & 0 deletions examples/jit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
extern crate inkwell;

use inkwell::OptimizationLevel;
use inkwell::builder::Builder;
use inkwell::context::Context;
use inkwell::execution_engine::{ExecutionEngine, Symbol};
use inkwell::module::Module;
use inkwell::targets::{InitializationConfig, Target};
use std::error::Error;

/// Convenience type alias for the `sum` function.
///
/// Calling this is innately `unsafe` because there's no guarantee it doesn't
/// do `unsafe` operations internally.
type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64;

fn jit_compile_sum(
context: &Context,
module: &Module,
builder: &Builder,
execution_engine: &ExecutionEngine,
) -> Option<Symbol<SumFunc>> {
let i64_type = context.i64_type();
let fn_type = i64_type.fn_type(&[&i64_type, &i64_type, &i64_type], false);

let function = module.add_function("sum", &fn_type, None);
let basic_block = context.append_basic_block(&function, "entry");

builder.position_at_end(&basic_block);

let x = function.get_nth_param(0)?.into_int_value();
let y = function.get_nth_param(1)?.into_int_value();
let z = function.get_nth_param(2)?.into_int_value();

let sum = builder.build_int_add(&x, &y, "sum");
let sum = builder.build_int_add(&sum, &z, "sum");

builder.build_return(Some(&sum));

unsafe { execution_engine.get_function("sum").ok() }
}

fn run() -> Result<(), Box<Error>> {
let context = Context::create();
let module = context.create_module("sum");
let builder = context.create_builder();
let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?;

let sum = jit_compile_sum(&context, &module, &builder, &execution_engine)
.ok_or("Unable to JIT compile `sum`")?;

let x = 1u64;
let y = 2u64;
let z = 3u64;

unsafe {
println!("{} + {} + {} = {}", x, y, z, sum(x, y, z));
assert_eq!(sum(x, y, z), x + y + z);
}

Ok(())
}

fn main() {
Target::initialize_native(&InitializationConfig::default()).unwrap();
run().unwrap();
}
11 changes: 6 additions & 5 deletions examples/kaleidoscope/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1317,17 +1317,18 @@ pub fn main() {
if is_anonymous {
let ee = module.create_jit_execution_engine(OptimizationLevel::None).unwrap();

let addr = match ee.get_function_address(name.as_str()) {
Ok(addr) => addr,
let maybe_fn = unsafe { ee.get_function::<unsafe extern "C" fn() -> f64>(name.as_str()) };
let compiled_fn = match maybe_fn {
Ok(f) => f,
Err(err) => {
println!("!> Error during execution: {:?}", err);
continue;
}
};

let compiled_fn: extern "C" fn() -> f64 = unsafe { std::mem::transmute(addr) };

println!("=> {}", compiled_fn());
unsafe {
println!("=> {}", compiled_fn());
}
}
}
}
Loading