cranelift-simplejit: add a translation mechanism for LibCalls (#747)
This commit is contained in:
@@ -46,7 +46,7 @@ impl FaerieBuilder {
|
|||||||
/// The `libcall_names` function provides a way to translate `cranelift_codegen`'s `ir::LibCall`
|
/// 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
|
/// 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
|
/// floating point instructions, and for stack probes. If you don't know what to use for this
|
||||||
/// argument, use `FaerieBuilder::default_libcall_names()`.
|
/// argument, use `cranelift_module::default_libcall_names()`.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
isa: Box<TargetIsa>,
|
isa: Box<TargetIsa>,
|
||||||
name: String,
|
name: String,
|
||||||
@@ -65,25 +65,6 @@ impl FaerieBuilder {
|
|||||||
libcall_names,
|
libcall_names,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Default names for `ir::LibCall`s. A function by this name is imported into the object as
|
|
||||||
/// part of the translation of a `ir::ExternalName::LibCall` variant.
|
|
||||||
pub fn default_libcall_names() -> Box<Fn(ir::LibCall) -> String> {
|
|
||||||
Box::new(move |libcall| match libcall {
|
|
||||||
ir::LibCall::Probestack => "__cranelift_probestack".to_owned(),
|
|
||||||
ir::LibCall::CeilF32 => "ceilf".to_owned(),
|
|
||||||
ir::LibCall::CeilF64 => "ceil".to_owned(),
|
|
||||||
ir::LibCall::FloorF32 => "floorf".to_owned(),
|
|
||||||
ir::LibCall::FloorF64 => "floor".to_owned(),
|
|
||||||
ir::LibCall::TruncF32 => "truncf".to_owned(),
|
|
||||||
ir::LibCall::TruncF64 => "trunc".to_owned(),
|
|
||||||
ir::LibCall::NearestF32 => "nearbyintf".to_owned(),
|
|
||||||
ir::LibCall::NearestF64 => "nearbyint".to_owned(),
|
|
||||||
ir::LibCall::Memcpy => "memcpy".to_owned(),
|
|
||||||
ir::LibCall::Memset => "memset".to_owned(),
|
|
||||||
ir::LibCall::Memmove => "memmove".to_owned(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A `FaerieBackend` implements `Backend` and emits ".o" files using the `faerie` library.
|
/// A `FaerieBackend` implements `Backend` and emits ".o" files using the `faerie` library.
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ use cranelift_codegen::isa::TargetIsa;
|
|||||||
use cranelift_codegen::Context;
|
use cranelift_codegen::Context;
|
||||||
use cranelift_codegen::{binemit, ir};
|
use cranelift_codegen::{binemit, ir};
|
||||||
|
|
||||||
|
use std::borrow::ToOwned;
|
||||||
|
use std::boxed::Box;
|
||||||
|
use std::string::String;
|
||||||
|
|
||||||
/// A `Backend` implements the functionality needed to support a `Module`.
|
/// A `Backend` implements the functionality needed to support a `Module`.
|
||||||
///
|
///
|
||||||
/// Two notable implementations of this trait are:
|
/// Two notable implementations of this trait are:
|
||||||
@@ -127,3 +131,22 @@ where
|
|||||||
/// provide additional functionality through this result.
|
/// provide additional functionality through this result.
|
||||||
fn finish(self) -> Self::Product;
|
fn finish(self) -> Self::Product;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Default names for `ir::LibCall`s. A function by this name is imported into the object as
|
||||||
|
/// part of the translation of a `ir::ExternalName::LibCall` variant.
|
||||||
|
pub fn default_libcall_names() -> Box<Fn(ir::LibCall) -> String> {
|
||||||
|
Box::new(move |libcall| match libcall {
|
||||||
|
ir::LibCall::Probestack => "__cranelift_probestack".to_owned(),
|
||||||
|
ir::LibCall::CeilF32 => "ceilf".to_owned(),
|
||||||
|
ir::LibCall::CeilF64 => "ceil".to_owned(),
|
||||||
|
ir::LibCall::FloorF32 => "floorf".to_owned(),
|
||||||
|
ir::LibCall::FloorF64 => "floor".to_owned(),
|
||||||
|
ir::LibCall::TruncF32 => "truncf".to_owned(),
|
||||||
|
ir::LibCall::TruncF64 => "trunc".to_owned(),
|
||||||
|
ir::LibCall::NearestF32 => "nearbyintf".to_owned(),
|
||||||
|
ir::LibCall::NearestF64 => "nearbyint".to_owned(),
|
||||||
|
ir::LibCall::Memcpy => "memcpy".to_owned(),
|
||||||
|
ir::LibCall::Memset => "memset".to_owned(),
|
||||||
|
ir::LibCall::Memmove => "memmove".to_owned(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ mod backend;
|
|||||||
mod data_context;
|
mod data_context;
|
||||||
mod module;
|
mod module;
|
||||||
|
|
||||||
pub use crate::backend::Backend;
|
pub use crate::backend::{default_libcall_names, Backend};
|
||||||
pub use crate::data_context::{DataContext, DataDescription, Init};
|
pub use crate::data_context::{DataContext, DataDescription, Init};
|
||||||
pub use crate::module::{
|
pub use crate::module::{
|
||||||
DataId, FuncId, FuncOrDataId, Linkage, Module, ModuleError, ModuleFunction, ModuleNamespace,
|
DataId, FuncId, FuncOrDataId, Linkage, Module, ModuleError, ModuleFunction, ModuleNamespace,
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
use cranelift::prelude::*;
|
use cranelift::prelude::*;
|
||||||
use cranelift_module::{Linkage, Module};
|
use cranelift_module::{default_libcall_names, Linkage, Module};
|
||||||
use cranelift_simplejit::{SimpleJITBackend, SimpleJITBuilder};
|
use cranelift_simplejit::{SimpleJITBackend, SimpleJITBuilder};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
fn main() {
|
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 ctx = module.make_context();
|
||||||
let mut func_ctx = FunctionBuilderContext::new();
|
let mut func_ctx = FunctionBuilderContext::new();
|
||||||
|
|
||||||
|
|||||||
@@ -21,17 +21,23 @@ use winapi;
|
|||||||
pub struct SimpleJITBuilder {
|
pub struct SimpleJITBuilder {
|
||||||
isa: Box<TargetIsa>,
|
isa: Box<TargetIsa>,
|
||||||
symbols: HashMap<String, *const u8>,
|
symbols: HashMap<String, *const u8>,
|
||||||
|
libcall_names: Box<Fn(ir::LibCall) -> String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SimpleJITBuilder {
|
impl SimpleJITBuilder {
|
||||||
/// Create a new `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 flag_builder = settings::builder();
|
||||||
let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| {
|
let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| {
|
||||||
panic!("host machine is not supported: {}", msg);
|
panic!("host machine is not supported: {}", msg);
|
||||||
});
|
});
|
||||||
let isa = isa_builder.finish(settings::Flags::new(flag_builder));
|
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
|
/// 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
|
/// To create a `SimpleJITBuilder` for native use, use the `new` constructor
|
||||||
/// instead.
|
/// 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");
|
debug_assert!(!isa.flags().is_pic(), "SimpleJIT requires non-PIC code");
|
||||||
let symbols = HashMap::new();
|
let symbols = HashMap::new();
|
||||||
Self { isa, symbols }
|
Self {
|
||||||
|
isa,
|
||||||
|
symbols,
|
||||||
|
libcall_names,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Define a symbol in the internal symbol table.
|
/// Define a symbol in the internal symbol table.
|
||||||
@@ -91,6 +106,7 @@ impl SimpleJITBuilder {
|
|||||||
pub struct SimpleJITBackend {
|
pub struct SimpleJITBackend {
|
||||||
isa: Box<TargetIsa>,
|
isa: Box<TargetIsa>,
|
||||||
symbols: HashMap<String, *const u8>,
|
symbols: HashMap<String, *const u8>,
|
||||||
|
libcall_names: Box<Fn(ir::LibCall) -> String>,
|
||||||
code_memory: Memory,
|
code_memory: Memory,
|
||||||
readonly_memory: Memory,
|
readonly_memory: Memory,
|
||||||
writable_memory: Memory,
|
writable_memory: Memory,
|
||||||
@@ -123,6 +139,35 @@ impl SimpleJITBackend {
|
|||||||
None => lookup_with_dlsym(name),
|
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 {
|
impl<'simple_jit_backend> Backend for SimpleJITBackend {
|
||||||
@@ -149,6 +194,7 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
|
|||||||
Self {
|
Self {
|
||||||
isa: builder.isa,
|
isa: builder.isa,
|
||||||
symbols: builder.symbols,
|
symbols: builder.symbols,
|
||||||
|
libcall_names: builder.libcall_names,
|
||||||
code_memory: Memory::new(),
|
code_memory: Memory::new(),
|
||||||
readonly_memory: Memory::new(),
|
readonly_memory: Memory::new(),
|
||||||
writable_memory: Memory::new(),
|
writable_memory: Memory::new(),
|
||||||
@@ -308,19 +354,7 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
|
|||||||
let ptr = func.code;
|
let ptr = func.code;
|
||||||
debug_assert!((offset as usize) < func.size);
|
debug_assert!((offset as usize) < func.size);
|
||||||
let at = unsafe { ptr.offset(offset as isize) };
|
let at = unsafe { ptr.offset(offset as isize) };
|
||||||
let base = if namespace.is_function(name) {
|
let base = self.get_definition(namespace, 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),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// TODO: Handle overflow.
|
// TODO: Handle overflow.
|
||||||
let what = unsafe { base.offset(addend as isize) };
|
let what = unsafe { base.offset(addend as isize) };
|
||||||
match reloc {
|
match reloc {
|
||||||
@@ -373,19 +407,7 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
|
|||||||
let ptr = data.storage;
|
let ptr = data.storage;
|
||||||
debug_assert!((offset as usize) < data.size);
|
debug_assert!((offset as usize) < data.size);
|
||||||
let at = unsafe { ptr.offset(offset as isize) };
|
let at = unsafe { ptr.offset(offset as isize) };
|
||||||
let base = if namespace.is_function(name) {
|
let base = self.get_definition(namespace, 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),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// TODO: Handle overflow.
|
// TODO: Handle overflow.
|
||||||
let what = unsafe { base.offset(addend as isize) };
|
let what = unsafe { base.offset(addend as isize) };
|
||||||
match reloc {
|
match reloc {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use cranelift_codegen::ir::*;
|
use cranelift_codegen::ir::*;
|
||||||
use cranelift_codegen::isa::CallConv;
|
use cranelift_codegen::isa::CallConv;
|
||||||
use cranelift_codegen::Context;
|
use cranelift_codegen::{ir::types::I16, Context};
|
||||||
use cranelift_entity::EntityRef;
|
use cranelift_entity::EntityRef;
|
||||||
use cranelift_frontend::*;
|
use cranelift_frontend::*;
|
||||||
use cranelift_module::*;
|
use cranelift_module::*;
|
||||||
@@ -8,7 +8,8 @@ use cranelift_simplejit::*;
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn error_on_incompatible_sig_in_declare_function() {
|
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 {
|
let mut sig = Signature {
|
||||||
params: vec![AbiParam::new(types::I64)],
|
params: vec![AbiParam::new(types::I64)],
|
||||||
returns: vec![],
|
returns: vec![],
|
||||||
@@ -52,7 +53,8 @@ fn define_simple_function(module: &mut Module<SimpleJITBackend>) -> FuncId {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn double_finalize() {
|
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);
|
define_simple_function(&mut module);
|
||||||
module.finalize_definitions();
|
module.finalize_definitions();
|
||||||
@@ -65,7 +67,8 @@ fn double_finalize() {
|
|||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "Result::unwrap()` on an `Err` value: DuplicateDefinition(\"abc\")")]
|
#[should_panic(expected = "Result::unwrap()` on an `Err` value: DuplicateDefinition(\"abc\")")]
|
||||||
fn panic_on_define_after_finalize() {
|
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);
|
define_simple_function(&mut module);
|
||||||
module.finalize_definitions();
|
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();
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user