Skip to content

Commit ebf8be2

Browse files
authored
Merge pull request rust-lang#36 from Michael-F-Bryan/get-function-addr
Replace Get function address
2 parents 0ab5817 + 63d840c commit ebf8be2

9 files changed

+395
-137
lines changed

Cargo.toml

-4
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@ enum-methods = "0.0.8"
2222
libc = "*"
2323
llvm-sys = "38"
2424

25-
[[example]]
26-
name = "kaleidoscope"
27-
path = "examples/kaleidoscope/main.rs"
28-
2925
[badges]
3026
travis-ci = { repository = "TheDan64/inkwell" }
3127
codecov = { repository = "TheDan64/inkwell" }

README.md

+59-25
Original file line numberDiff line numberDiff line change
@@ -48,47 +48,81 @@ Documenation is automatically deployed [here](https://thedan64.github.io/inkwell
4848
### 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:
4949

5050
```rust
51-
use inkwell::context::Context;
51+
extern crate inkwell;
52+
5253
use inkwell::OptimizationLevel;
54+
use inkwell::builder::Builder;
55+
use inkwell::context::Context;
56+
use inkwell::execution_engine::{ExecutionEngine, Symbol};
57+
use inkwell::module::Module;
5358
use inkwell::targets::{InitializationConfig, Target};
54-
use std::mem::transmute;
59+
use std::error::Error;
60+
61+
/// Convenience type alias for the `sum` function.
62+
///
63+
/// Calling `sum` is innately `unsafe` because there's no guarantee it doesn't
64+
/// do `unsafe` operations internally.
65+
type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64;
66+
67+
fn main() {
68+
Target::initialize_native(&InitializationConfig::default()).unwrap();
69+
run().unwrap();
70+
}
5571

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

58-
let context = Context::create();
59-
let module = context.create_module("sum");
60-
let builder = context.create_builder();
61-
let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?;
78+
let sum = jit_compile_sum(&context, &module, &builder, &execution_engine)
79+
.ok_or("Unable to JIT compile `sum`")?;
6280

63-
let i64_type = context.i64_type();
64-
let fn_type = i64_type.fn_type(&[&i64_type, &i64_type, &i64_type], false);
81+
let x = 1u64;
82+
let y = 2u64;
83+
let z = 3u64;
6584

66-
let function = module.add_function("sum", &fn_type, None);
67-
let basic_block = context.append_basic_block(&function, "entry");
85+
unsafe {
86+
println!("{} + {} + {} = {}", x, y, z, sum(x, y, z));
87+
assert_eq!(sum(x, y, z), x + y + z);
88+
}
6889

69-
builder.position_at_end(&basic_block);
90+
Ok(())
91+
}
7092

71-
let x = function.get_nth_param(0)?.into_int_value();
72-
let y = function.get_nth_param(1)?.into_int_value();
73-
let z = function.get_nth_param(2)?.into_int_value();
93+
fn jit_compile_sum(
94+
context: &Context,
95+
module: &Module,
96+
builder: &Builder,
97+
execution_engine: &ExecutionEngine,
98+
) -> Option<Symbol<SumFunc>> {
99+
let i64_type = context.i64_type();
100+
let fn_type = i64_type.fn_type(&[&i64_type, &i64_type, &i64_type], false);
74101

75-
let sum = builder.build_int_add(&x, &y, "sum");
76-
let sum = builder.build_int_add(&sum, &z, "sum");
102+
let function = module.add_function("sum", &fn_type, None);
103+
let basic_block = context.append_basic_block(&function, "entry");
77104

78-
builder.build_return(Some(&sum));
105+
builder.position_at_end(&basic_block);
79106

80-
let addr = execution_engine.get_function_address("sum")?;
107+
let x = function.get_nth_param(0)?.into_int_value();
108+
let y = function.get_nth_param(1)?.into_int_value();
109+
let z = function.get_nth_param(2)?.into_int_value();
81110

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

84-
let x = 1u64;
85-
let y = 2u64;
86-
let z = 3u64;
114+
builder.build_return(Some(&sum));
87115

88-
assert_eq!(sum(x, y, z), x + y + z);
116+
unsafe { execution_engine.get_function("sum").ok() }
117+
}
89118
```
90119

91-
<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.
120+
<sup>1</sup> There are two uses of `unsafe` in this example because the actual
121+
act of compiling and executing code on the fly is innately `unsafe`. For one,
122+
there is no way of verifying we are calling `get_function()` with the right function
123+
signature. It is also `unsafe` to *call* the function we get because there's no
124+
guarantee the code itself doesn't do `unsafe` things internally (the same reason
125+
you need `unsafe` when calling into C).
92126

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

examples/jit.rs

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
extern crate inkwell;
2+
3+
use inkwell::OptimizationLevel;
4+
use inkwell::builder::Builder;
5+
use inkwell::context::Context;
6+
use inkwell::execution_engine::{ExecutionEngine, Symbol};
7+
use inkwell::module::Module;
8+
use inkwell::targets::{InitializationConfig, Target};
9+
use std::error::Error;
10+
11+
/// Convenience type alias for the `sum` function.
12+
///
13+
/// Calling this is innately `unsafe` because there's no guarantee it doesn't
14+
/// do `unsafe` operations internally.
15+
type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64;
16+
17+
fn jit_compile_sum(
18+
context: &Context,
19+
module: &Module,
20+
builder: &Builder,
21+
execution_engine: &ExecutionEngine,
22+
) -> Option<Symbol<SumFunc>> {
23+
let i64_type = context.i64_type();
24+
let fn_type = i64_type.fn_type(&[&i64_type, &i64_type, &i64_type], false);
25+
26+
let function = module.add_function("sum", &fn_type, None);
27+
let basic_block = context.append_basic_block(&function, "entry");
28+
29+
builder.position_at_end(&basic_block);
30+
31+
let x = function.get_nth_param(0)?.into_int_value();
32+
let y = function.get_nth_param(1)?.into_int_value();
33+
let z = function.get_nth_param(2)?.into_int_value();
34+
35+
let sum = builder.build_int_add(&x, &y, "sum");
36+
let sum = builder.build_int_add(&sum, &z, "sum");
37+
38+
builder.build_return(Some(&sum));
39+
40+
unsafe { execution_engine.get_function("sum").ok() }
41+
}
42+
43+
fn run() -> Result<(), Box<Error>> {
44+
let context = Context::create();
45+
let module = context.create_module("sum");
46+
let builder = context.create_builder();
47+
let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?;
48+
49+
let sum = jit_compile_sum(&context, &module, &builder, &execution_engine)
50+
.ok_or("Unable to JIT compile `sum`")?;
51+
52+
let x = 1u64;
53+
let y = 2u64;
54+
let z = 3u64;
55+
56+
unsafe {
57+
println!("{} + {} + {} = {}", x, y, z, sum(x, y, z));
58+
assert_eq!(sum(x, y, z), x + y + z);
59+
}
60+
61+
Ok(())
62+
}
63+
64+
fn main() {
65+
Target::initialize_native(&InitializationConfig::default()).unwrap();
66+
run().unwrap();
67+
}

examples/kaleidoscope/main.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -1317,17 +1317,18 @@ pub fn main() {
13171317
if is_anonymous {
13181318
let ee = module.create_jit_execution_engine(OptimizationLevel::None).unwrap();
13191319

1320-
let addr = match ee.get_function_address(name.as_str()) {
1321-
Ok(addr) => addr,
1320+
let maybe_fn = unsafe { ee.get_function::<unsafe extern "C" fn() -> f64>(name.as_str()) };
1321+
let compiled_fn = match maybe_fn {
1322+
Ok(f) => f,
13221323
Err(err) => {
13231324
println!("!> Error during execution: {:?}", err);
13241325
continue;
13251326
}
13261327
};
13271328

1328-
let compiled_fn: extern "C" fn() -> f64 = unsafe { std::mem::transmute(addr) };
1329-
1330-
println!("=> {}", compiled_fn());
1329+
unsafe {
1330+
println!("=> {}", compiled_fn());
1331+
}
13311332
}
13321333
}
13331334
}

0 commit comments

Comments
 (0)