pub struct Compiled { /* private fields */ }Expand description
A compiled function: native machine code living in executable memory, ready to run.
A Jit produces one of these by lowering an
ir_lang::Function to host machine code and placing it in a
page-aligned, read-execute Region. Compiled owns that region, so the code
stays mapped and runnable for exactly as long as the value is alive and is freed
when it is dropped — calling into a function whose Compiled has been dropped is a
use-after-free.
The signature accessors — name, params, and
ret — report the function as it was compiled, so a caller can pick
the right function-pointer type before calling. Running the code is inherently
unsafe: the machine has no way to check that the type you transmute the entry point
to matches what was compiled, so that final step is your responsibility through
entry. See its documentation for the contract.
Compiled is Send and Sync: the region it owns is, and the rest of its
fields are plain data. Moving it between threads moves the mapping with it.
§Examples
Compile a function and read back what it is:
use jit_lang::compile;
use ir_lang::{Builder, BinOp, Type};
// fn double(x: int) -> int { x + x }
let mut b = Builder::new("double", &[Type::Int], Type::Int);
let x = b.block_params(b.entry())[0];
let sum = b.bin(BinOp::Add, x, x);
b.ret(Some(sum));
let f = compile(&b.finish()).expect("double is well-formed");
assert_eq!(f.name(), "double");
assert_eq!(f.params(), &[Type::Int]);
assert_eq!(f.ret(), Type::Int);
assert!(f.code_len() > 0);Implementations§
Source§impl Compiled
impl Compiled
Sourcepub fn name(&self) -> &str
pub fn name(&self) -> &str
The compiled function’s name, as it was given to the IR builder.
§Examples
use jit_lang::compile;
use ir_lang::{Builder, Type};
let mut b = Builder::new("noop", &[], Type::Unit);
b.ret(None);
assert_eq!(compile(&b.finish()).unwrap().name(), "noop");Sourcepub fn params(&self) -> &[Type]
pub fn params(&self) -> &[Type]
The parameter types, in declaration order. These tell you the argument types of
the function-pointer you transmute to with entry.
§Examples
use jit_lang::compile;
use ir_lang::{Builder, Type};
let mut b = Builder::new("f", &[Type::Int, Type::Float], Type::Int);
let x = b.block_params(b.entry())[0];
b.ret(Some(x));
assert_eq!(compile(&b.finish()).unwrap().params(), &[Type::Int, Type::Float]);Sourcepub fn ret(&self) -> Type
pub fn ret(&self) -> Type
The return type. A Unit return means the function
yields no value, so the matching function-pointer type has no return.
§Examples
use jit_lang::compile;
use ir_lang::{Builder, Type};
let mut b = Builder::new("f", &[], Type::Unit);
b.ret(None);
assert_eq!(compile(&b.finish()).unwrap().ret(), Type::Unit);Sourcepub fn code_len(&self) -> usize
pub fn code_len(&self) -> usize
The length of the emitted machine code, in bytes.
This is the size of the function body, which is at most the length of the region holding it — the region is rounded up to whole pages, so it is usually larger. Useful for inspecting or logging how much code a function compiled to.
§Examples
use jit_lang::compile;
use ir_lang::{Builder, Type};
let mut b = Builder::new("f", &[Type::Int], Type::Int);
let x = b.block_params(b.entry())[0];
b.ret(Some(x));
// An identity function still compiles to at least one instruction.
assert!(compile(&b.finish()).unwrap().code_len() > 0);Sourcepub fn as_ptr(&self) -> *const u8
pub fn as_ptr(&self) -> *const u8
A raw pointer to the first byte of the compiled code — the function’s entry point.
The pointer is valid to obtain and to compare; it is into read-execute memory,
so reading or running through it is unsafe. For the common case of calling
the function, prefer entry, which produces a typed
function-pointer. The pointer is valid only while this Compiled is alive.
§Examples
use jit_lang::compile;
use ir_lang::{Builder, Type};
let mut b = Builder::new("f", &[], Type::Unit);
b.ret(None);
let f = compile(&b.finish()).unwrap();
assert!(!f.as_ptr().is_null());Sourcepub unsafe fn entry<F: Copy>(&self) -> F
pub unsafe fn entry<F: Copy>(&self) -> F
Reinterprets the entry point as a function pointer of type F so the compiled
code can be called.
This is how you run a compiled function: pick an extern "C" function-pointer
type whose signature matches what was compiled, get it from entry, and call
it. The mapping from IR types to the C ABI is int to a 64-bit integer,
float to f64, bool to a byte that is 0 or 1, and a unit return to no
return value. The compiled code uses the host C calling convention, which is
what extern "C" denotes, so the two line up.
§Safety
The caller guarantees all of the following:
Fis a function-pointer type (so it is pointer-sized, the size of the entry address). Passing a non-function or differently-sized type is undefined behavior.F’s signature matches the compiled function: oneextern "C"argument per entry inparamswith the ABI type above, and a return that matchesret. A mismatch is undefined behavior.- Every call through the returned pointer happens while
selfis alive. Onceselfis dropped the code is unmapped and the pointer dangles.
§Examples
Call a compiled fn double(x: int) -> int:
use jit_lang::compile;
use ir_lang::{Builder, BinOp, Type};
let mut b = Builder::new("double", &[Type::Int], Type::Int);
let x = b.block_params(b.entry())[0];
let sum = b.bin(BinOp::Add, x, x);
b.ret(Some(sum));
let f = compile(&b.finish()).unwrap();
// SAFETY: the signature matches `fn double(x: int) -> int`, and `f` outlives the call.
let double: extern "C" fn(i64) -> i64 = unsafe { f.entry() };
assert_eq!(double(21), 42);
assert_eq!(double(-5), -10);Call one that returns a bool:
use jit_lang::compile;
use ir_lang::{Builder, BinOp, Type};
// fn lt(a: int, b: int) -> bool { a < b }
let mut b = Builder::new("lt", &[Type::Int, Type::Int], Type::Bool);
let a = b.block_params(b.entry())[0];
let c = b.block_params(b.entry())[1];
let lt = b.bin(BinOp::Lt, a, c);
b.ret(Some(lt));
let f = compile(&b.finish()).unwrap();
// SAFETY: a `bool` return is a 0/1 byte, which `extern "C" fn(..) -> bool` reads correctly.
let lt: extern "C" fn(i64, i64) -> bool = unsafe { f.entry() };
assert!(lt(1, 2));
assert!(!lt(2, 1));