cranelift-simplejit: add a translation mechanism for LibCalls (#747)

This commit is contained in:
antoyo
2019-04-29 10:58:39 -04:00
committed by Benjamin Bouvier
parent d8d573208b
commit 79d6978e29
6 changed files with 135 additions and 57 deletions

View File

@@ -1,10 +1,11 @@
use cranelift::prelude::*;
use cranelift_module::{Linkage, Module};
use cranelift_module::{default_libcall_names, Linkage, Module};
use cranelift_simplejit::{SimpleJITBackend, SimpleJITBuilder};
use std::mem;
fn main() {
let mut module: Module<SimpleJITBackend> = Module::new(SimpleJITBuilder::new());
let mut module: Module<SimpleJITBackend> =
Module::new(SimpleJITBuilder::new(default_libcall_names()));
let mut ctx = module.make_context();
let mut func_ctx = FunctionBuilderContext::new();

View File

@@ -21,17 +21,23 @@ use winapi;
pub struct SimpleJITBuilder {
isa: Box<TargetIsa>,
symbols: HashMap<String, *const u8>,
libcall_names: Box<Fn(ir::LibCall) -> String>,
}
impl SimpleJITBuilder {
/// Create a new `SimpleJITBuilder`.
pub fn new() -> Self {
///
/// The `libcall_names` function provides a way to translate `cranelift_codegen`'s `ir::LibCall`
/// enum to symbols. LibCalls are inserted in the IR as part of the legalization for certain
/// floating point instructions, and for stack probes. If you don't know what to use for this
/// argument, use `cranelift_module::default_libcall_names()`.
pub fn new(libcall_names: Box<Fn(ir::LibCall) -> String>) -> Self {
let flag_builder = settings::builder();
let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| {
panic!("host machine is not supported: {}", msg);
});
let isa = isa_builder.finish(settings::Flags::new(flag_builder));
Self::with_isa(isa)
Self::with_isa(isa, libcall_names)
}
/// Create a new `SimpleJITBuilder` with an arbitrary target. This is mainly
@@ -41,10 +47,19 @@ impl SimpleJITBuilder {
///
/// To create a `SimpleJITBuilder` for native use, use the `new` constructor
/// instead.
pub fn with_isa(isa: Box<TargetIsa>) -> Self {
///
/// The `libcall_names` function provides a way to translate `cranelift_codegen`'s `ir::LibCall`
/// enum to symbols. LibCalls are inserted in the IR as part of the legalization for certain
/// floating point instructions, and for stack probes. If you don't know what to use for this
/// argument, use `cranelift_module::default_libcall_names()`.
pub fn with_isa(isa: Box<TargetIsa>, libcall_names: Box<Fn(ir::LibCall) -> String>) -> Self {
debug_assert!(!isa.flags().is_pic(), "SimpleJIT requires non-PIC code");
let symbols = HashMap::new();
Self { isa, symbols }
Self {
isa,
symbols,
libcall_names,
}
}
/// Define a symbol in the internal symbol table.
@@ -91,6 +106,7 @@ impl SimpleJITBuilder {
pub struct SimpleJITBackend {
isa: Box<TargetIsa>,
symbols: HashMap<String, *const u8>,
libcall_names: Box<Fn(ir::LibCall) -> String>,
code_memory: Memory,
readonly_memory: Memory,
writable_memory: Memory,
@@ -123,6 +139,35 @@ impl SimpleJITBackend {
None => lookup_with_dlsym(name),
}
}
fn get_definition(
&self,
namespace: &ModuleNamespace<Self>,
name: &ir::ExternalName,
) -> *const u8 {
match *name {
ir::ExternalName::User { .. } => {
if namespace.is_function(name) {
let (def, name_str, _signature) = namespace.get_function_definition(&name);
match def {
Some(compiled) => compiled.code,
None => self.lookup_symbol(name_str),
}
} else {
let (def, name_str, _writable) = namespace.get_data_definition(&name);
match def {
Some(compiled) => compiled.storage,
None => self.lookup_symbol(name_str),
}
}
}
ir::ExternalName::LibCall(ref libcall) => {
let sym = (self.libcall_names)(*libcall);
self.lookup_symbol(&sym)
}
_ => panic!("invalid ExternalName {}", name),
}
}
}
impl<'simple_jit_backend> Backend for SimpleJITBackend {
@@ -149,6 +194,7 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
Self {
isa: builder.isa,
symbols: builder.symbols,
libcall_names: builder.libcall_names,
code_memory: Memory::new(),
readonly_memory: Memory::new(),
writable_memory: Memory::new(),
@@ -308,19 +354,7 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
let ptr = func.code;
debug_assert!((offset as usize) < func.size);
let at = unsafe { ptr.offset(offset as isize) };
let base = if namespace.is_function(name) {
let (def, name_str, _signature) = namespace.get_function_definition(&name);
match def {
Some(compiled) => compiled.code,
None => self.lookup_symbol(name_str),
}
} else {
let (def, name_str, _writable) = namespace.get_data_definition(&name);
match def {
Some(compiled) => compiled.storage,
None => self.lookup_symbol(name_str),
}
};
let base = self.get_definition(namespace, name);
// TODO: Handle overflow.
let what = unsafe { base.offset(addend as isize) };
match reloc {
@@ -373,19 +407,7 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
let ptr = data.storage;
debug_assert!((offset as usize) < data.size);
let at = unsafe { ptr.offset(offset as isize) };
let base = if namespace.is_function(name) {
let (def, name_str, _signature) = namespace.get_function_definition(&name);
match def {
Some(compiled) => compiled.code,
None => self.lookup_symbol(name_str),
}
} else {
let (def, name_str, _writable) = namespace.get_data_definition(&name);
match def {
Some(compiled) => compiled.storage,
None => self.lookup_symbol(name_str),
}
};
let base = self.get_definition(namespace, name);
// TODO: Handle overflow.
let what = unsafe { base.offset(addend as isize) };
match reloc {

View File

@@ -1,6 +1,6 @@
use cranelift_codegen::ir::*;
use cranelift_codegen::isa::CallConv;
use cranelift_codegen::Context;
use cranelift_codegen::{ir::types::I16, Context};
use cranelift_entity::EntityRef;
use cranelift_frontend::*;
use cranelift_module::*;
@@ -8,7 +8,8 @@ use cranelift_simplejit::*;
#[test]
fn error_on_incompatible_sig_in_declare_function() {
let mut module: Module<SimpleJITBackend> = Module::new(SimpleJITBuilder::new());
let mut module: Module<SimpleJITBackend> =
Module::new(SimpleJITBuilder::new(default_libcall_names()));
let mut sig = Signature {
params: vec![AbiParam::new(types::I64)],
returns: vec![],
@@ -52,7 +53,8 @@ fn define_simple_function(module: &mut Module<SimpleJITBackend>) -> FuncId {
#[test]
fn double_finalize() {
let mut module: Module<SimpleJITBackend> = Module::new(SimpleJITBuilder::new());
let mut module: Module<SimpleJITBackend> =
Module::new(SimpleJITBuilder::new(default_libcall_names()));
define_simple_function(&mut module);
module.finalize_definitions();
@@ -65,7 +67,8 @@ fn double_finalize() {
#[test]
#[should_panic(expected = "Result::unwrap()` on an `Err` value: DuplicateDefinition(\"abc\")")]
fn panic_on_define_after_finalize() {
let mut module: Module<SimpleJITBackend> = Module::new(SimpleJITBuilder::new());
let mut module: Module<SimpleJITBackend> =
Module::new(SimpleJITBuilder::new(default_libcall_names()));
define_simple_function(&mut module);
module.finalize_definitions();
@@ -144,3 +147,51 @@ fn switch_error() {
}
}
}
#[test]
fn libcall_function() {
let mut module: Module<SimpleJITBackend> =
Module::new(SimpleJITBuilder::new(default_libcall_names()));
let sig = Signature {
params: vec![],
returns: vec![],
call_conv: CallConv::SystemV,
};
let func_id = module
.declare_function("function", Linkage::Local, &sig)
.unwrap();
let mut ctx = Context::new();
ctx.func = Function::with_name_signature(ExternalName::user(0, func_id.as_u32()), sig);
let mut func_ctx = FunctionBuilderContext::new();
{
let mut bcx: FunctionBuilder = FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
let ebb = bcx.create_ebb();
bcx.switch_to_block(ebb);
let int = module.target_config().pointer_type();
let zero = bcx.ins().iconst(I16, 0);
let size = bcx.ins().iconst(int, 10);
let mut signature = module.make_signature();
signature.params.push(AbiParam::new(int));
signature.returns.push(AbiParam::new(int));
let callee = module
.declare_function("malloc", Linkage::Import, &signature)
.expect("declare malloc function");
let local_callee = module.declare_func_in_func(callee, &mut bcx.func);
let argument_exprs = vec![size];
let call = bcx.ins().call(local_callee, &argument_exprs);
let buffer = bcx.inst_results(call)[0];
bcx.call_memset(module.target_config(), buffer, zero, size);
bcx.ins().return_(&[]);
}
module.define_function(func_id, &mut ctx).unwrap();
module.finalize_definitions();
}