Merge pull request #2249 from bjorn3/module_rework

Rework the interface of cranelift-module
This commit is contained in:
Pat Hickey
2020-10-08 09:52:30 -07:00
committed by GitHub
15 changed files with 660 additions and 1105 deletions

2
Cargo.lock generated
View File

@@ -489,6 +489,7 @@ dependencies = [
"anyhow", "anyhow",
"cranelift-codegen", "cranelift-codegen",
"cranelift-module", "cranelift-module",
"log",
"object 0.21.1", "object 0.21.1",
"target-lexicon", "target-lexicon",
] ]
@@ -535,6 +536,7 @@ dependencies = [
"cranelift-native", "cranelift-native",
"errno", "errno",
"libc", "libc",
"log",
"memmap", "memmap",
"region", "region",
"target-lexicon", "target-lexicon",

View File

@@ -6,14 +6,13 @@ This crate is structured as an optional layer on top of cranelift-codegen.
It provides additional functionality, such as linking, however users that It provides additional functionality, such as linking, however users that
require greater flexibility don't need to use it. require greater flexibility don't need to use it.
A `Module` is a collection of functions and data objects that are linked A module is a collection of functions and data objects that are linked
together. `Backend` is a trait that defines an interface for backends together. The `Module` trait that defines a common interface for various kinds
that compile modules into various forms. Most users will use one of the of modules. Most users will use one of the following `Module` implementations:
following `Backend` implementations:
- `SimpleJITBackend`, provided by [cranelift-simplejit], which JITs - `SimpleJITModule`, provided by [cranelift-simplejit], which JITs
code to memory for direct execution. code to memory for direct execution.
- `ObjectBackend`, provided by [cranelift-object], which emits native - `ObjectModule`, provided by [cranelift-object], which emits native
object files. object files.
[cranelift-simplejit]: https://crates.io/crates/cranelift-simplejit [cranelift-simplejit]: https://crates.io/crates/cranelift-simplejit

View File

@@ -1,200 +0,0 @@
//! Defines the `Backend` trait.
use crate::DataContext;
use crate::DataId;
use crate::FuncId;
use crate::Linkage;
use crate::ModuleNamespace;
use crate::ModuleResult;
use core::marker;
use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::Context;
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`.
///
/// Three notable implementations of this trait are:
/// - `SimpleJITBackend`, defined in [cranelift-simplejit], which JITs
/// the contents of a `Module` to memory which can be directly executed.
/// - `ObjectBackend`, defined in [cranelift-object], which writes the
/// contents of a `Module` out as a native object file.
///
/// [cranelift-simplejit]: https://docs.rs/cranelift-simplejit/
/// [cranelift-object]: https://docs.rs/cranelift-object/
pub trait Backend
where
Self: marker::Sized,
{
/// A builder for constructing `Backend` instances.
type Builder;
/// The results of compiling a function.
type CompiledFunction;
/// The results of "compiling" a data object.
type CompiledData;
/// The completed output artifact for a function, if this is meaningful for
/// the `Backend`.
type FinalizedFunction;
/// The completed output artifact for a data object, if this is meaningful for
/// the `Backend`.
type FinalizedData;
/// This is an object returned by `Module`'s
/// [`finish`](struct.Module.html#method.finish) function,
/// if the `Backend` has a purpose for this.
type Product;
/// Create a new `Backend` instance.
fn new(_: Self::Builder) -> Self;
/// Return the `TargetIsa` to compile for.
fn isa(&self) -> &dyn TargetIsa;
/// Declare a function.
fn declare_function(&mut self, id: FuncId, name: &str, linkage: Linkage);
/// Declare a data object.
fn declare_data(
&mut self,
id: DataId,
name: &str,
linkage: Linkage,
writable: bool,
tls: bool,
align: Option<u8>,
);
/// Define a function, producing the function body from the given `Context`.
///
/// Functions must be declared before being defined.
fn define_function<TS>(
&mut self,
id: FuncId,
name: &str,
ctx: &Context,
namespace: &ModuleNamespace<Self>,
code_size: u32,
trap_sink: &mut TS,
) -> ModuleResult<Self::CompiledFunction>
where
TS: binemit::TrapSink;
/// Define a function, taking the function body from the given `bytes`.
///
/// Functions must be declared before being defined.
fn define_function_bytes(
&mut self,
id: FuncId,
name: &str,
bytes: &[u8],
namespace: &ModuleNamespace<Self>,
) -> ModuleResult<Self::CompiledFunction>;
/// Define a zero-initialized data object of the given size.
///
/// Data objects must be declared before being defined.
fn define_data(
&mut self,
id: DataId,
name: &str,
writable: bool,
tls: bool,
align: Option<u8>,
data_ctx: &DataContext,
namespace: &ModuleNamespace<Self>,
) -> ModuleResult<Self::CompiledData>;
/// Write the address of `what` into the data for `data` at `offset`. `data` must refer to a
/// defined data object.
fn write_data_funcaddr(
&mut self,
data: &mut Self::CompiledData,
offset: usize,
what: ir::FuncRef,
);
/// Write the address of `what` plus `addend` into the data for `data` at `offset`. `data` must
/// refer to a defined data object.
fn write_data_dataaddr(
&mut self,
data: &mut Self::CompiledData,
offset: usize,
what: ir::GlobalValue,
addend: binemit::Addend,
);
/// Perform all outstanding relocations on the given function. This requires all `Local`
/// and `Export` entities referenced to be defined.
///
/// This method is not relevant for `Backend` implementations that do not provide
/// `Backend::FinalizedFunction`.
fn finalize_function(
&mut self,
id: FuncId,
func: &Self::CompiledFunction,
namespace: &ModuleNamespace<Self>,
) -> Self::FinalizedFunction;
/// Return the finalized artifact from the backend, if relevant.
fn get_finalized_function(&self, func: &Self::CompiledFunction) -> Self::FinalizedFunction;
/// Perform all outstanding relocations on the given data object. This requires all
/// `Local` and `Export` entities referenced to be defined.
///
/// This method is not relevant for `Backend` implementations that do not provide
/// `Backend::FinalizedData`.
fn finalize_data(
&mut self,
id: DataId,
data: &Self::CompiledData,
namespace: &ModuleNamespace<Self>,
) -> Self::FinalizedData;
/// Return the finalized artifact from the backend, if relevant.
fn get_finalized_data(&self, data: &Self::CompiledData) -> Self::FinalizedData;
/// "Publish" all finalized functions and data objects to their ultimate destinations.
///
/// This method is not relevant for `Backend` implementations that do not provide
/// `Backend::FinalizedFunction` or `Backend::FinalizedData`.
fn publish(&mut self);
/// Consume this `Backend` and return a result. Some implementations may
/// provide additional functionality through this result.
fn finish(self, namespace: &ModuleNamespace<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<dyn Fn(ir::LibCall) -> String> {
Box::new(move |libcall| match libcall {
ir::LibCall::Probestack => "__cranelift_probestack".to_owned(),
ir::LibCall::UdivI64 => "__udivdi3".to_owned(),
ir::LibCall::SdivI64 => "__divdi3".to_owned(),
ir::LibCall::UremI64 => "__umoddi3".to_owned(),
ir::LibCall::SremI64 => "__moddi3".to_owned(),
ir::LibCall::IshlI64 => "__ashldi3".to_owned(),
ir::LibCall::UshrI64 => "__lshrdi3".to_owned(),
ir::LibCall::SshrI64 => "__ashrdi3".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(),
ir::LibCall::ElfTlsGetAddr => "__tls_get_addr".to_owned(),
})
}

View File

@@ -50,6 +50,9 @@ pub struct DataDescription {
pub data_relocs: Vec<(CodeOffset, ir::GlobalValue, Addend)>, pub data_relocs: Vec<(CodeOffset, ir::GlobalValue, Addend)>,
/// Object file section /// Object file section
pub custom_segment_section: Option<(String, String)>, pub custom_segment_section: Option<(String, String)>,
/// Alignment in bytes. `None` means that the default alignment of the respective module should
/// be used.
pub align: Option<u64>,
} }
/// This is to data objects what cranelift_codegen::Context is to functions. /// This is to data objects what cranelift_codegen::Context is to functions.
@@ -68,6 +71,7 @@ impl DataContext {
function_relocs: vec![], function_relocs: vec![],
data_relocs: vec![], data_relocs: vec![],
custom_segment_section: None, custom_segment_section: None,
align: None,
}, },
} }
} }
@@ -79,6 +83,8 @@ impl DataContext {
self.description.data_decls.clear(); self.description.data_decls.clear();
self.description.function_relocs.clear(); self.description.function_relocs.clear();
self.description.data_relocs.clear(); self.description.data_relocs.clear();
self.description.custom_segment_section = None;
self.description.align = None;
} }
/// Define a zero-initialized object with the given size. /// Define a zero-initialized object with the given size.
@@ -100,6 +106,12 @@ impl DataContext {
self.description.custom_segment_section = Some((seg.to_owned(), sec.to_owned())) self.description.custom_segment_section = Some((seg.to_owned(), sec.to_owned()))
} }
/// Set the alignment for data. The alignment must be a power of two.
pub fn set_align(&mut self, align: u64) {
assert!(align.is_power_of_two());
self.description.align = Some(align);
}
/// Declare an external function import. /// Declare an external function import.
/// ///
/// Users of the `Module` API generally should call /// Users of the `Module` API generally should call

View File

@@ -29,21 +29,52 @@ extern crate std;
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
use hashbrown::{hash_map, HashMap}; use hashbrown::{hash_map, HashMap};
use std::borrow::ToOwned;
use std::boxed::Box;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::collections::{hash_map, HashMap}; use std::collections::{hash_map, HashMap};
use std::string::String;
use cranelift_codegen::ir;
mod backend;
mod data_context; mod data_context;
mod module; mod module;
mod traps; mod traps;
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, ModuleCompiledFunction, ModuleDeclarations,
ModuleResult, ModuleError, ModuleResult,
}; };
pub use crate::traps::TrapSite; pub use crate::traps::TrapSite;
/// Version number of this crate. /// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub const VERSION: &str = env!("CARGO_PKG_VERSION");
/// 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<dyn Fn(ir::LibCall) -> String> {
Box::new(move |libcall| match libcall {
ir::LibCall::Probestack => "__cranelift_probestack".to_owned(),
ir::LibCall::UdivI64 => "__udivdi3".to_owned(),
ir::LibCall::SdivI64 => "__divdi3".to_owned(),
ir::LibCall::UremI64 => "__umoddi3".to_owned(),
ir::LibCall::SremI64 => "__moddi3".to_owned(),
ir::LibCall::IshlI64 => "__ashldi3".to_owned(),
ir::LibCall::UshrI64 => "__lshrdi3".to_owned(),
ir::LibCall::SshrI64 => "__ashrdi3".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(),
ir::LibCall::ElfTlsGetAddr => "__tls_get_addr".to_owned(),
})
}

View File

@@ -7,15 +7,11 @@
use super::HashMap; use super::HashMap;
use crate::data_context::DataContext; use crate::data_context::DataContext;
use crate::Backend; use cranelift_codegen::binemit;
use cranelift_codegen::binemit::{self, CodeInfo};
use cranelift_codegen::entity::{entity_impl, PrimaryMap}; use cranelift_codegen::entity::{entity_impl, PrimaryMap};
use cranelift_codegen::{ir, isa, CodegenError, Context}; use cranelift_codegen::{ir, isa, CodegenError, Context};
use log::info;
use std::borrow::ToOwned; use std::borrow::ToOwned;
use std::convert::TryInto;
use std::string::String; use std::string::String;
use std::vec::Vec;
use thiserror::Error; use thiserror::Error;
/// A function identifier for use in the `Module` interface. /// A function identifier for use in the `Module` interface.
@@ -132,7 +128,21 @@ pub struct FunctionDeclaration {
pub signature: ir::Signature, pub signature: ir::Signature,
} }
/// Error messages for all `Module` and `Backend` methods impl FunctionDeclaration {
fn merge(&mut self, linkage: Linkage, sig: &ir::Signature) -> Result<(), ModuleError> {
self.linkage = Linkage::merge(self.linkage, linkage);
if &self.signature != sig {
return Err(ModuleError::IncompatibleSignature(
self.name.clone(),
self.signature.clone(),
sig.clone(),
));
}
Ok(())
}
}
/// Error messages for all `Module` methods
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum ModuleError { pub enum ModuleError {
/// Indicates an identifier was used before it was declared /// Indicates an identifier was used before it was declared
@@ -165,83 +175,48 @@ pub enum ModuleError {
/// A convenient alias for a `Result` that uses `ModuleError` as the error type. /// A convenient alias for a `Result` that uses `ModuleError` as the error type.
pub type ModuleResult<T> = Result<T, ModuleError>; pub type ModuleResult<T> = Result<T, ModuleError>;
/// A function belonging to a `Module`.
pub struct ModuleFunction<B>
where
B: Backend,
{
/// The function declaration.
pub decl: FunctionDeclaration,
/// The compiled artifact, once it's available.
pub compiled: Option<B::CompiledFunction>,
}
impl<B> ModuleFunction<B>
where
B: Backend,
{
fn merge(&mut self, linkage: Linkage, sig: &ir::Signature) -> Result<(), ModuleError> {
self.decl.linkage = Linkage::merge(self.decl.linkage, linkage);
if &self.decl.signature != sig {
return Err(ModuleError::IncompatibleSignature(
self.decl.name.clone(),
self.decl.signature.clone(),
sig.clone(),
));
}
Ok(())
}
}
/// Information about a data object which can be accessed. /// Information about a data object which can be accessed.
pub struct DataDeclaration { pub struct DataDeclaration {
pub name: String, pub name: String,
pub linkage: Linkage, pub linkage: Linkage,
pub writable: bool, pub writable: bool,
pub tls: bool, pub tls: bool,
pub align: Option<u8>,
} }
/// A data object belonging to a `Module`. impl DataDeclaration {
struct ModuleData<B> fn merge(&mut self, linkage: Linkage, writable: bool, tls: bool) {
where self.linkage = Linkage::merge(self.linkage, linkage);
B: Backend, self.writable = self.writable || writable;
{
/// The data object declaration.
decl: DataDeclaration,
/// The "compiled" artifact, once it's available.
compiled: Option<B::CompiledData>,
}
impl<B> ModuleData<B>
where
B: Backend,
{
fn merge(&mut self, linkage: Linkage, writable: bool, tls: bool, align: Option<u8>) {
self.decl.linkage = Linkage::merge(self.decl.linkage, linkage);
self.decl.writable = self.decl.writable || writable;
self.decl.align = self.decl.align.max(align);
assert_eq!( assert_eq!(
self.decl.tls, tls, self.tls, tls,
"Can't change TLS data object to normal or in the opposite way", "Can't change TLS data object to normal or in the opposite way",
); );
} }
} }
/// The functions and data objects belonging to a module. /// This provides a view to the state of a module which allows `ir::ExternalName`s to be translated
struct ModuleContents<B> /// into `FunctionDeclaration`s and `DataDeclaration`s.
where #[derive(Default)]
B: Backend, pub struct ModuleDeclarations {
{ names: HashMap<String, FuncOrDataId>,
functions: PrimaryMap<FuncId, ModuleFunction<B>>, functions: PrimaryMap<FuncId, FunctionDeclaration>,
data_objects: PrimaryMap<DataId, ModuleData<B>>, data_objects: PrimaryMap<DataId, DataDeclaration>,
} }
impl<B> ModuleContents<B> impl ModuleDeclarations {
where /// Get the module identifier for a given name, if that name
B: Backend, /// has been declared.
{ pub fn get_name(&self, name: &str) -> Option<FuncOrDataId> {
fn get_function_id(&self, name: &ir::ExternalName) -> FuncId { self.names.get(name).copied()
}
/// Get an iterator of all function declarations
pub fn get_functions(&self) -> impl Iterator<Item = (FuncId, &FunctionDeclaration)> {
self.functions.iter()
}
/// Get the `FuncId` for the function named by `name`.
pub fn get_function_id(&self, name: &ir::ExternalName) -> FuncId {
if let ir::ExternalName::User { namespace, index } = *name { if let ir::ExternalName::User { namespace, index } = *name {
debug_assert_eq!(namespace, 0); debug_assert_eq!(namespace, 0);
FuncId::from_u32(index) FuncId::from_u32(index)
@@ -250,7 +225,8 @@ where
} }
} }
fn get_data_id(&self, name: &ir::ExternalName) -> DataId { /// Get the `DataId` for the data object named by `name`.
pub fn get_data_id(&self, name: &ir::ExternalName) -> DataId {
if let ir::ExternalName::User { namespace, index } = *name { if let ir::ExternalName::User { namespace, index } = *name {
debug_assert_eq!(namespace, 1); debug_assert_eq!(namespace, 1);
DataId::from_u32(index) DataId::from_u32(index)
@@ -259,85 +235,19 @@ where
} }
} }
fn get_function_info(&self, name: &ir::ExternalName) -> &ModuleFunction<B> {
&self.functions[self.get_function_id(name)]
}
/// Get the `DataDeclaration` for the function named by `name`.
fn get_data_info(&self, name: &ir::ExternalName) -> &ModuleData<B> {
&self.data_objects[self.get_data_id(name)]
}
}
/// This provides a view to the state of a module which allows `ir::ExternalName`s to be translated
/// into `FunctionDeclaration`s and `DataDeclaration`s.
pub struct ModuleNamespace<'a, B: 'a>
where
B: Backend,
{
contents: &'a ModuleContents<B>,
}
impl<'a, B> ModuleNamespace<'a, B>
where
B: Backend,
{
/// Get the `FuncId` for the function named by `name`.
pub fn get_function_id(&self, name: &ir::ExternalName) -> FuncId {
self.contents.get_function_id(name)
}
/// Get the `DataId` for the data object named by `name`.
pub fn get_data_id(&self, name: &ir::ExternalName) -> DataId {
self.contents.get_data_id(name)
}
/// Get the `FunctionDeclaration` for the function named by `name`. /// Get the `FunctionDeclaration` for the function named by `name`.
pub fn get_function_decl(&self, name: &ir::ExternalName) -> &FunctionDeclaration { pub fn get_function_decl(&self, func_id: FuncId) -> &FunctionDeclaration {
&self.contents.get_function_info(name).decl &self.functions[func_id]
}
/// Get an iterator of all data declarations
pub fn get_data_objects(&self) -> impl Iterator<Item = (DataId, &DataDeclaration)> {
self.data_objects.iter()
} }
/// Get the `DataDeclaration` for the data object named by `name`. /// Get the `DataDeclaration` for the data object named by `name`.
pub fn get_data_decl(&self, name: &ir::ExternalName) -> &DataDeclaration { pub fn get_data_decl(&self, data_id: DataId) -> &DataDeclaration {
&self.contents.get_data_info(name).decl &self.data_objects[data_id]
}
/// Get the definition for the function named by `name`, along with its name
/// and signature.
pub fn get_function_definition(
&self,
name: &ir::ExternalName,
) -> (Option<&B::CompiledFunction>, &str, &ir::Signature) {
let info = self.contents.get_function_info(name);
debug_assert!(
!info.decl.linkage.is_definable() || info.compiled.is_some(),
"Finalization requires a definition for function {}.",
name,
);
debug_assert_eq!(info.decl.linkage.is_definable(), info.compiled.is_some());
(
info.compiled.as_ref(),
&info.decl.name,
&info.decl.signature,
)
}
/// Get the definition for the data object named by `name`, along with its name
/// and writable flag
pub fn get_data_definition(
&self,
name: &ir::ExternalName,
) -> (Option<&B::CompiledData>, &str, bool) {
let info = self.contents.get_data_info(name);
debug_assert!(
!info.decl.linkage.is_definable() || info.compiled.is_some(),
"Finalization requires a definition for data object {}.",
name,
);
debug_assert_eq!(info.decl.linkage.is_definable(), info.compiled.is_some());
(info.compiled.as_ref(), &info.decl.name, info.decl.writable)
} }
/// Return whether `name` names a function, rather than a data object. /// Return whether `name` names a function, rather than a data object.
@@ -348,87 +258,6 @@ where
panic!("unexpected ExternalName kind {}", name) panic!("unexpected ExternalName kind {}", name)
} }
} }
}
/// A `Module` is a utility for collecting functions and data objects, and linking them together.
pub struct Module<B>
where
B: Backend,
{
names: HashMap<String, FuncOrDataId>,
contents: ModuleContents<B>,
functions_to_finalize: Vec<FuncId>,
data_objects_to_finalize: Vec<DataId>,
backend: B,
}
pub struct ModuleCompiledFunction {
pub size: binemit::CodeOffset,
}
impl<B> Module<B>
where
B: Backend,
{
/// Create a new `Module`.
pub fn new(backend_builder: B::Builder) -> Self {
Self {
names: HashMap::new(),
contents: ModuleContents {
functions: PrimaryMap::new(),
data_objects: PrimaryMap::new(),
},
functions_to_finalize: Vec::new(),
data_objects_to_finalize: Vec::new(),
backend: B::new(backend_builder),
}
}
/// Get the module identifier for a given name, if that name
/// has been declared.
pub fn get_name(&self, name: &str) -> Option<FuncOrDataId> {
self.names.get(name).cloned()
}
/// Return the target information needed by frontends to produce Cranelift IR
/// for the current target.
pub fn target_config(&self) -> isa::TargetFrontendConfig {
self.backend.isa().frontend_config()
}
/// Create a new `Context` initialized for use with this `Module`.
///
/// This ensures that the `Context` is initialized with the default calling
/// convention for the `TargetIsa`.
pub fn make_context(&self) -> Context {
let mut ctx = Context::new();
ctx.func.signature.call_conv = self.backend.isa().default_call_conv();
ctx
}
/// Clear the given `Context` and reset it for use with a new function.
///
/// This ensures that the `Context` is initialized with the default calling
/// convention for the `TargetIsa`.
pub fn clear_context(&self, ctx: &mut Context) {
ctx.clear();
ctx.func.signature.call_conv = self.backend.isa().default_call_conv();
}
/// Create a new empty `Signature` with the default calling convention for
/// the `TargetIsa`, to which parameter and return types can be added for
/// declaring a function to be called by this `Module`.
pub fn make_signature(&self) -> ir::Signature {
ir::Signature::new(self.backend.isa().default_call_conv())
}
/// Clear the given `Signature` and reset for use with a new function.
///
/// This ensures that the `Signature` is initialized with the default
/// calling convention for the `TargetIsa`.
pub fn clear_signature(&self, sig: &mut ir::Signature) {
sig.clear(self.backend.isa().default_call_conv());
}
/// Declare a function in this module. /// Declare a function in this module.
pub fn declare_function( pub fn declare_function(
@@ -436,43 +265,32 @@ where
name: &str, name: &str,
linkage: Linkage, linkage: Linkage,
signature: &ir::Signature, signature: &ir::Signature,
) -> ModuleResult<FuncId> { ) -> ModuleResult<(FuncId, &FunctionDeclaration)> {
// TODO: Can we avoid allocating names so often? // TODO: Can we avoid allocating names so often?
use super::hash_map::Entry::*; use super::hash_map::Entry::*;
match self.names.entry(name.to_owned()) { match self.names.entry(name.to_owned()) {
Occupied(entry) => match *entry.get() { Occupied(entry) => match *entry.get() {
FuncOrDataId::Func(id) => { FuncOrDataId::Func(id) => {
let existing = &mut self.contents.functions[id]; let existing = &mut self.functions[id];
existing.merge(linkage, signature)?; existing.merge(linkage, signature)?;
self.backend Ok((id, existing))
.declare_function(id, name, existing.decl.linkage);
Ok(id)
} }
FuncOrDataId::Data(..) => { FuncOrDataId::Data(..) => {
Err(ModuleError::IncompatibleDeclaration(name.to_owned())) Err(ModuleError::IncompatibleDeclaration(name.to_owned()))
} }
}, },
Vacant(entry) => { Vacant(entry) => {
let id = self.contents.functions.push(ModuleFunction { let id = self.functions.push(FunctionDeclaration {
decl: FunctionDeclaration { name: name.to_owned(),
name: name.to_owned(), linkage,
linkage, signature: signature.clone(),
signature: signature.clone(),
},
compiled: None,
}); });
entry.insert(FuncOrDataId::Func(id)); entry.insert(FuncOrDataId::Func(id));
self.backend.declare_function(id, name, linkage); Ok((id, &self.functions[id]))
Ok(id)
} }
} }
} }
/// An iterator over functions that have been declared in this module.
pub fn declared_functions(&self) -> core::slice::Iter<'_, ModuleFunction<B>> {
self.contents.functions.values()
}
/// Declare a data object in this module. /// Declare a data object in this module.
pub fn declare_data( pub fn declare_data(
&mut self, &mut self,
@@ -480,24 +298,15 @@ where
linkage: Linkage, linkage: Linkage,
writable: bool, writable: bool,
tls: bool, tls: bool,
align: Option<u8>, // An alignment bigger than 128 is unlikely ) -> ModuleResult<(DataId, &DataDeclaration)> {
) -> ModuleResult<DataId> {
// TODO: Can we avoid allocating names so often? // TODO: Can we avoid allocating names so often?
use super::hash_map::Entry::*; use super::hash_map::Entry::*;
match self.names.entry(name.to_owned()) { match self.names.entry(name.to_owned()) {
Occupied(entry) => match *entry.get() { Occupied(entry) => match *entry.get() {
FuncOrDataId::Data(id) => { FuncOrDataId::Data(id) => {
let existing = &mut self.contents.data_objects[id]; let existing = &mut self.data_objects[id];
existing.merge(linkage, writable, tls, align); existing.merge(linkage, writable, tls);
self.backend.declare_data( Ok((id, existing))
id,
name,
existing.decl.linkage,
existing.decl.writable,
existing.decl.tls,
existing.decl.align,
);
Ok(id)
} }
FuncOrDataId::Func(..) => { FuncOrDataId::Func(..) => {
@@ -505,30 +314,102 @@ where
} }
}, },
Vacant(entry) => { Vacant(entry) => {
let id = self.contents.data_objects.push(ModuleData { let id = self.data_objects.push(DataDeclaration {
decl: DataDeclaration { name: name.to_owned(),
name: name.to_owned(), linkage,
linkage, writable,
writable, tls,
tls,
align,
},
compiled: None,
}); });
entry.insert(FuncOrDataId::Data(id)); entry.insert(FuncOrDataId::Data(id));
self.backend Ok((id, &self.data_objects[id]))
.declare_data(id, name, linkage, writable, tls, align);
Ok(id)
} }
} }
} }
}
/// Information about the compiled function.
pub struct ModuleCompiledFunction {
/// The size of the compiled function.
pub size: binemit::CodeOffset,
}
/// A `Module` is a utility for collecting functions and data objects, and linking them together.
pub trait Module {
/// Return the `TargetIsa` to compile for.
fn isa(&self) -> &dyn isa::TargetIsa;
/// Get all declarations in this module.
fn declarations(&self) -> &ModuleDeclarations;
/// Get the module identifier for a given name, if that name
/// has been declared.
fn get_name(&self, name: &str) -> Option<FuncOrDataId> {
self.declarations().get_name(name)
}
/// Return the target information needed by frontends to produce Cranelift IR
/// for the current target.
fn target_config(&self) -> isa::TargetFrontendConfig {
self.isa().frontend_config()
}
/// Create a new `Context` initialized for use with this `Module`.
///
/// This ensures that the `Context` is initialized with the default calling
/// convention for the `TargetIsa`.
fn make_context(&self) -> Context {
let mut ctx = Context::new();
ctx.func.signature.call_conv = self.isa().default_call_conv();
ctx
}
/// Clear the given `Context` and reset it for use with a new function.
///
/// This ensures that the `Context` is initialized with the default calling
/// convention for the `TargetIsa`.
fn clear_context(&self, ctx: &mut Context) {
ctx.clear();
ctx.func.signature.call_conv = self.isa().default_call_conv();
}
/// Create a new empty `Signature` with the default calling convention for
/// the `TargetIsa`, to which parameter and return types can be added for
/// declaring a function to be called by this `Module`.
fn make_signature(&self) -> ir::Signature {
ir::Signature::new(self.isa().default_call_conv())
}
/// Clear the given `Signature` and reset for use with a new function.
///
/// This ensures that the `Signature` is initialized with the default
/// calling convention for the `TargetIsa`.
fn clear_signature(&self, sig: &mut ir::Signature) {
sig.clear(self.isa().default_call_conv());
}
/// Declare a function in this module.
fn declare_function(
&mut self,
name: &str,
linkage: Linkage,
signature: &ir::Signature,
) -> ModuleResult<FuncId>;
/// Declare a data object in this module.
fn declare_data(
&mut self,
name: &str,
linkage: Linkage,
writable: bool,
tls: bool,
) -> ModuleResult<DataId>;
/// Use this when you're building the IR of a function to reference a function. /// Use this when you're building the IR of a function to reference a function.
/// ///
/// TODO: Coalesce redundant decls and signatures. /// TODO: Coalesce redundant decls and signatures.
/// TODO: Look into ways to reduce the risk of using a FuncRef in the wrong function. /// TODO: Look into ways to reduce the risk of using a FuncRef in the wrong function.
pub fn declare_func_in_func(&self, func: FuncId, in_func: &mut ir::Function) -> ir::FuncRef { fn declare_func_in_func(&self, func: FuncId, in_func: &mut ir::Function) -> ir::FuncRef {
let decl = &self.contents.functions[func].decl; let decl = &self.declarations().functions[func];
let signature = in_func.import_signature(decl.signature.clone()); let signature = in_func.import_signature(decl.signature.clone());
let colocated = decl.linkage.is_final(); let colocated = decl.linkage.is_final();
in_func.import_function(ir::ExtFuncData { in_func.import_function(ir::ExtFuncData {
@@ -541,8 +422,8 @@ where
/// Use this when you're building the IR of a function to reference a data object. /// Use this when you're building the IR of a function to reference a data object.
/// ///
/// TODO: Same as above. /// TODO: Same as above.
pub fn declare_data_in_func(&self, data: DataId, func: &mut ir::Function) -> ir::GlobalValue { fn declare_data_in_func(&self, data: DataId, func: &mut ir::Function) -> ir::GlobalValue {
let decl = &self.contents.data_objects[data].decl; let decl = &self.declarations().data_objects[data];
let colocated = decl.linkage.is_final(); let colocated = decl.linkage.is_final();
func.create_global_value(ir::GlobalValueData::Symbol { func.create_global_value(ir::GlobalValueData::Symbol {
name: ir::ExternalName::user(1, data.as_u32()), name: ir::ExternalName::user(1, data.as_u32()),
@@ -553,12 +434,12 @@ where
} }
/// TODO: Same as above. /// TODO: Same as above.
pub fn declare_func_in_data(&self, func: FuncId, ctx: &mut DataContext) -> ir::FuncRef { fn declare_func_in_data(&self, func: FuncId, ctx: &mut DataContext) -> ir::FuncRef {
ctx.import_function(ir::ExternalName::user(0, func.as_u32())) ctx.import_function(ir::ExternalName::user(0, func.as_u32()))
} }
/// TODO: Same as above. /// TODO: Same as above.
pub fn declare_data_in_data(&self, data: DataId, ctx: &mut DataContext) -> ir::GlobalValue { fn declare_data_in_data(&self, data: DataId, ctx: &mut DataContext) -> ir::GlobalValue {
ctx.import_global_value(ir::ExternalName::user(1, data.as_u32())) ctx.import_global_value(ir::ExternalName::user(1, data.as_u32()))
} }
@@ -567,44 +448,14 @@ where
/// Returns the size of the function's code and constant data. /// Returns the size of the function's code and constant data.
/// ///
/// Note: After calling this function the given `Context` will contain the compiled function. /// Note: After calling this function the given `Context` will contain the compiled function.
pub fn define_function<TS>( fn define_function<TS>(
&mut self, &mut self,
func: FuncId, func: FuncId,
ctx: &mut Context, ctx: &mut Context,
trap_sink: &mut TS, trap_sink: &mut TS,
) -> ModuleResult<ModuleCompiledFunction> ) -> ModuleResult<ModuleCompiledFunction>
where where
TS: binemit::TrapSink, TS: binemit::TrapSink;
{
info!(
"defining function {}: {}",
func,
ctx.func.display(self.backend.isa())
);
let CodeInfo { total_size, .. } = ctx.compile(self.backend.isa())?;
let info = &self.contents.functions[func];
if info.compiled.is_some() {
return Err(ModuleError::DuplicateDefinition(info.decl.name.clone()));
}
if !info.decl.linkage.is_definable() {
return Err(ModuleError::InvalidImportDefinition(info.decl.name.clone()));
}
let compiled = self.backend.define_function(
func,
&info.decl.name,
ctx,
&ModuleNamespace::<B> {
contents: &self.contents,
},
total_size,
trap_sink,
)?;
self.contents.functions[func].compiled = Some(compiled);
self.functions_to_finalize.push(func);
Ok(ModuleCompiledFunction { size: total_size })
}
/// Define a function, taking the function body from the given `bytes`. /// Define a function, taking the function body from the given `bytes`.
/// ///
@@ -613,187 +464,12 @@ where
/// `define_function`. /// `define_function`.
/// ///
/// Returns the size of the function's code. /// Returns the size of the function's code.
pub fn define_function_bytes( fn define_function_bytes(
&mut self, &mut self,
func: FuncId, func: FuncId,
bytes: &[u8], bytes: &[u8],
) -> ModuleResult<ModuleCompiledFunction> { ) -> ModuleResult<ModuleCompiledFunction>;
info!("defining function {} with bytes", func);
let info = &self.contents.functions[func];
if info.compiled.is_some() {
return Err(ModuleError::DuplicateDefinition(info.decl.name.clone()));
}
if !info.decl.linkage.is_definable() {
return Err(ModuleError::InvalidImportDefinition(info.decl.name.clone()));
}
let total_size: u32 = match bytes.len().try_into() {
Ok(total_size) => total_size,
_ => Err(ModuleError::FunctionTooLarge(info.decl.name.clone()))?,
};
let compiled = self.backend.define_function_bytes(
func,
&info.decl.name,
bytes,
&ModuleNamespace::<B> {
contents: &self.contents,
},
)?;
self.contents.functions[func].compiled = Some(compiled);
self.functions_to_finalize.push(func);
Ok(ModuleCompiledFunction { size: total_size })
}
/// Define a data object, producing the data contents from the given `DataContext`. /// Define a data object, producing the data contents from the given `DataContext`.
pub fn define_data(&mut self, data: DataId, data_ctx: &DataContext) -> ModuleResult<()> { fn define_data(&mut self, data: DataId, data_ctx: &DataContext) -> ModuleResult<()>;
let compiled = {
let info = &self.contents.data_objects[data];
if info.compiled.is_some() {
return Err(ModuleError::DuplicateDefinition(info.decl.name.clone()));
}
if !info.decl.linkage.is_definable() {
return Err(ModuleError::InvalidImportDefinition(info.decl.name.clone()));
}
Some(self.backend.define_data(
data,
&info.decl.name,
info.decl.writable,
info.decl.tls,
info.decl.align,
data_ctx,
&ModuleNamespace::<B> {
contents: &self.contents,
},
)?)
};
self.contents.data_objects[data].compiled = compiled;
self.data_objects_to_finalize.push(data);
Ok(())
}
/// Write the address of `what` into the data for `data` at `offset`. `data` must refer to a
/// defined data object.
pub fn write_data_funcaddr(&mut self, data: DataId, offset: usize, what: ir::FuncRef) {
let info = &mut self.contents.data_objects[data];
debug_assert!(
info.decl.linkage.is_definable(),
"imported data cannot contain references"
);
self.backend.write_data_funcaddr(
&mut info
.compiled
.as_mut()
.expect("`data` must refer to a defined data object"),
offset,
what,
);
}
/// Write the address of `what` plus `addend` into the data for `data` at `offset`. `data` must
/// refer to a defined data object.
pub fn write_data_dataaddr(
&mut self,
data: DataId,
offset: usize,
what: ir::GlobalValue,
addend: binemit::Addend,
) {
let info = &mut self.contents.data_objects[data];
debug_assert!(
info.decl.linkage.is_definable(),
"imported data cannot contain references"
);
self.backend.write_data_dataaddr(
&mut info
.compiled
.as_mut()
.expect("`data` must refer to a defined data object"),
offset,
what,
addend,
);
}
/// Finalize all functions and data objects that are defined but not yet finalized.
/// All symbols referenced in their bodies that are declared as needing a definition
/// must be defined by this point.
///
/// Use `get_finalized_function` and `get_finalized_data` to obtain the final
/// artifacts.
///
/// This method is not relevant for `Backend` implementations that do not provide
/// `Backend::FinalizedFunction` or `Backend::FinalizedData`.
pub fn finalize_definitions(&mut self) {
for func in self.functions_to_finalize.drain(..) {
let info = &self.contents.functions[func];
debug_assert!(info.decl.linkage.is_definable());
self.backend.finalize_function(
func,
info.compiled
.as_ref()
.expect("function must be compiled before it can be finalized"),
&ModuleNamespace::<B> {
contents: &self.contents,
},
);
}
for data in self.data_objects_to_finalize.drain(..) {
let info = &self.contents.data_objects[data];
debug_assert!(info.decl.linkage.is_definable());
self.backend.finalize_data(
data,
info.compiled
.as_ref()
.expect("data object must be compiled before it can be finalized"),
&ModuleNamespace::<B> {
contents: &self.contents,
},
);
}
self.backend.publish();
}
/// Return the finalized artifact from the backend, if it provides one.
pub fn get_finalized_function(&mut self, func: FuncId) -> B::FinalizedFunction {
let info = &self.contents.functions[func];
debug_assert!(
!self.functions_to_finalize.iter().any(|x| *x == func),
"function not yet finalized"
);
self.backend.get_finalized_function(
info.compiled
.as_ref()
.expect("function must be compiled before it can be finalized"),
)
}
/// Return the finalized artifact from the backend, if it provides one.
pub fn get_finalized_data(&mut self, data: DataId) -> B::FinalizedData {
let info = &self.contents.data_objects[data];
debug_assert!(
!self.data_objects_to_finalize.iter().any(|x| *x == data),
"data object not yet finalized"
);
self.backend.get_finalized_data(
info.compiled
.as_ref()
.expect("data object must be compiled before it can be finalized"),
)
}
/// Return the target isa
pub fn isa(&self) -> &dyn isa::TargetIsa {
self.backend.isa()
}
/// Consume the module and return the resulting `Product`. Some `Backend`
/// implementations may provide additional functionality available after
/// a `Module` is complete.
pub fn finish(self) -> B::Product {
self.backend.finish(&ModuleNamespace::<B> {
contents: &self.contents,
})
}
} }

View File

@@ -15,6 +15,7 @@ cranelift-codegen = { path = "../codegen", version = "0.67.0", default-features
object = { version = "0.21.1", default-features = false, features = ["write"] } object = { version = "0.21.1", default-features = false, features = ["write"] }
target-lexicon = "0.11" target-lexicon = "0.11"
anyhow = "1.0" anyhow = "1.0"
log = { version = "0.4.6", default-features = false }
[badges] [badges]
maintenance = { status = "experimental" } maintenance = { status = "experimental" }

View File

@@ -1,16 +1,17 @@
//! Defines `ObjectBackend`. //! Defines `ObjectModule`.
use anyhow::anyhow; use anyhow::anyhow;
use cranelift_codegen::binemit::{ use cranelift_codegen::binemit::{
Addend, CodeOffset, NullStackMapSink, Reloc, RelocSink, TrapSink, Addend, CodeInfo, CodeOffset, NullStackMapSink, Reloc, RelocSink, TrapSink,
}; };
use cranelift_codegen::entity::SecondaryMap; use cranelift_codegen::entity::SecondaryMap;
use cranelift_codegen::isa::TargetIsa; use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::{self, binemit, ir}; use cranelift_codegen::{self, ir};
use cranelift_module::{ use cranelift_module::{
Backend, DataContext, DataDescription, DataId, FuncId, Init, Linkage, ModuleError, DataContext, DataDescription, DataId, FuncId, Init, Linkage, Module, ModuleCompiledFunction,
ModuleNamespace, ModuleResult, ModuleDeclarations, ModuleError, ModuleResult,
}; };
use log::info;
use object::write::{ use object::write::{
Object, Relocation, SectionId, StandardSection, Symbol, SymbolId, SymbolSection, Object, Relocation, SectionId, StandardSection, Symbol, SymbolId, SymbolSection,
}; };
@@ -18,10 +19,11 @@ use object::{
RelocationEncoding, RelocationKind, SectionKind, SymbolFlags, SymbolKind, SymbolScope, RelocationEncoding, RelocationKind, SectionKind, SymbolFlags, SymbolKind, SymbolScope,
}; };
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryInto;
use std::mem; use std::mem;
use target_lexicon::PointerWidth; use target_lexicon::PointerWidth;
/// A builder for `ObjectBackend`. /// A builder for `ObjectModule`.
pub struct ObjectBuilder { pub struct ObjectBuilder {
isa: Box<dyn TargetIsa>, isa: Box<dyn TargetIsa>,
binary_format: object::BinaryFormat, binary_format: object::BinaryFormat,
@@ -35,7 +37,7 @@ pub struct ObjectBuilder {
impl ObjectBuilder { impl ObjectBuilder {
/// Create a new `ObjectBuilder` using the given Cranelift target, that /// Create a new `ObjectBuilder` using the given Cranelift target, that
/// can be passed to [`Module::new`](cranelift_module::Module::new). /// can be passed to [`ObjectModule::new`].
/// ///
/// 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
@@ -106,14 +108,15 @@ impl ObjectBuilder {
} }
} }
/// A `ObjectBackend` implements `Backend` and emits ".o" files using the `object` library. /// An `ObjectModule` implements `Module` and emits ".o" files using the `object` library.
/// ///
/// See the `ObjectBuilder` for a convenient way to construct `ObjectBackend` instances. /// See the `ObjectBuilder` for a convenient way to construct `ObjectModule` instances.
pub struct ObjectBackend { pub struct ObjectModule {
isa: Box<dyn TargetIsa>, isa: Box<dyn TargetIsa>,
object: Object, object: Object,
functions: SecondaryMap<FuncId, Option<SymbolId>>, declarations: ModuleDeclarations,
data_objects: SecondaryMap<DataId, Option<SymbolId>>, functions: SecondaryMap<FuncId, Option<(SymbolId, bool)>>,
data_objects: SecondaryMap<DataId, Option<(SymbolId, bool)>>,
relocs: Vec<SymbolRelocs>, relocs: Vec<SymbolRelocs>,
libcalls: HashMap<ir::LibCall, SymbolId>, libcalls: HashMap<ir::LibCall, SymbolId>,
libcall_names: Box<dyn Fn(ir::LibCall) -> String>, libcall_names: Box<dyn Fn(ir::LibCall) -> String>,
@@ -121,26 +124,15 @@ pub struct ObjectBackend {
per_function_section: bool, per_function_section: bool,
} }
impl Backend for ObjectBackend { impl ObjectModule {
type Builder = ObjectBuilder; /// Create a new `ObjectModule` using the given Cranelift target.
pub fn new(builder: ObjectBuilder) -> Self {
type CompiledFunction = ObjectCompiledFunction;
type CompiledData = ObjectCompiledData;
// There's no need to return individual artifacts; we're writing them into
// the output file instead.
type FinalizedFunction = ();
type FinalizedData = ();
type Product = ObjectProduct;
/// Create a new `ObjectBackend` using the given Cranelift target.
fn new(builder: ObjectBuilder) -> Self {
let mut object = Object::new(builder.binary_format, builder.architecture, builder.endian); let mut object = Object::new(builder.binary_format, builder.architecture, builder.endian);
object.add_file_symbol(builder.name); object.add_file_symbol(builder.name);
Self { Self {
isa: builder.isa, isa: builder.isa,
object, object,
declarations: ModuleDeclarations::default(),
functions: SecondaryMap::new(), functions: SecondaryMap::new(),
data_objects: SecondaryMap::new(), data_objects: SecondaryMap::new(),
relocs: Vec::new(), relocs: Vec::new(),
@@ -150,15 +142,30 @@ impl Backend for ObjectBackend {
per_function_section: builder.per_function_section, per_function_section: builder.per_function_section,
} }
} }
}
impl Module for ObjectModule {
fn isa(&self) -> &dyn TargetIsa { fn isa(&self) -> &dyn TargetIsa {
&*self.isa &*self.isa
} }
fn declare_function(&mut self, id: FuncId, name: &str, linkage: Linkage) { fn declarations(&self) -> &ModuleDeclarations {
let (scope, weak) = translate_linkage(linkage); &self.declarations
}
if let Some(function) = self.functions[id] { fn declare_function(
&mut self,
name: &str,
linkage: Linkage,
signature: &ir::Signature,
) -> ModuleResult<FuncId> {
let (id, decl) = self
.declarations
.declare_function(name, linkage, signature)?;
let (scope, weak) = translate_linkage(decl.linkage);
if let Some((function, _defined)) = self.functions[id] {
let symbol = self.object.symbol_mut(function); let symbol = self.object.symbol_mut(function);
symbol.scope = scope; symbol.scope = scope;
symbol.weak = weak; symbol.weak = weak;
@@ -173,27 +180,31 @@ impl Backend for ObjectBackend {
section: SymbolSection::Undefined, section: SymbolSection::Undefined,
flags: SymbolFlags::None, flags: SymbolFlags::None,
}); });
self.functions[id] = Some(symbol_id); self.functions[id] = Some((symbol_id, false));
} }
Ok(id)
} }
fn declare_data( fn declare_data(
&mut self, &mut self,
id: DataId,
name: &str, name: &str,
linkage: Linkage, linkage: Linkage,
_writable: bool, writable: bool,
tls: bool, tls: bool,
_align: Option<u8>, ) -> ModuleResult<DataId> {
) { let (id, decl) = self
let kind = if tls { .declarations
.declare_data(name, linkage, writable, tls)?;
let kind = if decl.tls {
SymbolKind::Tls SymbolKind::Tls
} else { } else {
SymbolKind::Data SymbolKind::Data
}; };
let (scope, weak) = translate_linkage(linkage); let (scope, weak) = translate_linkage(decl.linkage);
if let Some(data) = self.data_objects[id] { if let Some((data, _defined)) = self.data_objects[id] {
let symbol = self.object.symbol_mut(data); let symbol = self.object.symbol_mut(data);
symbol.kind = kind; symbol.kind = kind;
symbol.scope = scope; symbol.scope = scope;
@@ -209,22 +220,42 @@ impl Backend for ObjectBackend {
section: SymbolSection::Undefined, section: SymbolSection::Undefined,
flags: SymbolFlags::None, flags: SymbolFlags::None,
}); });
self.data_objects[id] = Some(symbol_id); self.data_objects[id] = Some((symbol_id, false));
} }
Ok(id)
} }
fn define_function<TS>( fn define_function<TS>(
&mut self, &mut self,
func_id: FuncId, func_id: FuncId,
_name: &str, ctx: &mut cranelift_codegen::Context,
ctx: &cranelift_codegen::Context,
_namespace: &ModuleNamespace<Self>,
code_size: u32,
trap_sink: &mut TS, trap_sink: &mut TS,
) -> ModuleResult<ObjectCompiledFunction> ) -> ModuleResult<ModuleCompiledFunction>
where where
TS: TrapSink, TS: TrapSink,
{ {
info!(
"defining function {}: {}",
func_id,
ctx.func.display(self.isa())
);
let CodeInfo {
total_size: code_size,
..
} = ctx.compile(self.isa())?;
let decl = self.declarations.get_function_decl(func_id);
if !decl.linkage.is_definable() {
return Err(ModuleError::InvalidImportDefinition(decl.name.clone()));
}
let &mut (symbol, ref mut defined) = self.functions[func_id].as_mut().unwrap();
if *defined {
return Err(ModuleError::DuplicateDefinition(decl.name.clone()));
}
*defined = true;
let mut code: Vec<u8> = vec![0; code_size as usize]; let mut code: Vec<u8> = vec![0; code_size as usize];
let mut reloc_sink = ObjectRelocSink::new(self.object.format()); let mut reloc_sink = ObjectRelocSink::new(self.object.format());
let mut stack_map_sink = NullStackMapSink {}; let mut stack_map_sink = NullStackMapSink {};
@@ -239,8 +270,6 @@ impl Backend for ObjectBackend {
) )
}; };
let symbol = self.functions[func_id].unwrap();
let (section, offset) = if self.per_function_section { let (section, offset) = if self.per_function_section {
let symbol_name = self.object.symbol(symbol).name.clone(); let symbol_name = self.object.symbol(symbol).name.clone();
let (section, offset) = self.object.add_subsection( let (section, offset) = self.object.add_subsection(
@@ -267,17 +296,32 @@ impl Backend for ObjectBackend {
relocs: reloc_sink.relocs, relocs: reloc_sink.relocs,
}); });
} }
Ok(ObjectCompiledFunction)
Ok(ModuleCompiledFunction { size: code_size })
} }
fn define_function_bytes( fn define_function_bytes(
&mut self, &mut self,
func_id: FuncId, func_id: FuncId,
_name: &str,
bytes: &[u8], bytes: &[u8],
_namespace: &ModuleNamespace<Self>, ) -> ModuleResult<ModuleCompiledFunction> {
) -> ModuleResult<ObjectCompiledFunction> { info!("defining function {} with bytes", func_id);
let symbol = self.functions[func_id].unwrap();
let decl = self.declarations.get_function_decl(func_id);
if !decl.linkage.is_definable() {
return Err(ModuleError::InvalidImportDefinition(decl.name.clone()));
}
let total_size: u32 = match bytes.len().try_into() {
Ok(total_size) => total_size,
_ => Err(ModuleError::FunctionTooLarge(decl.name.clone()))?,
};
let &mut (symbol, ref mut defined) = self.functions[func_id].as_mut().unwrap();
if *defined {
return Err(ModuleError::DuplicateDefinition(decl.name.clone()));
}
*defined = true;
if self.per_function_section { if self.per_function_section {
let symbol_name = self.object.symbol(symbol).name.clone(); let symbol_name = self.object.symbol(symbol).name.clone();
@@ -296,19 +340,21 @@ impl Backend for ObjectBackend {
.add_symbol_data(symbol, section, bytes, self.function_alignment); .add_symbol_data(symbol, section, bytes, self.function_alignment);
} }
Ok(ObjectCompiledFunction) Ok(ModuleCompiledFunction { size: total_size })
} }
fn define_data( fn define_data(&mut self, data_id: DataId, data_ctx: &DataContext) -> ModuleResult<()> {
&mut self, let decl = self.declarations.get_data_decl(data_id);
data_id: DataId, if !decl.linkage.is_definable() {
_name: &str, return Err(ModuleError::InvalidImportDefinition(decl.name.clone()));
writable: bool, }
tls: bool,
align: Option<u8>, let &mut (symbol, ref mut defined) = self.data_objects[data_id].as_mut().unwrap();
data_ctx: &DataContext, if *defined {
_namespace: &ModuleNamespace<Self>, return Err(ModuleError::DuplicateDefinition(decl.name.clone()));
) -> ModuleResult<ObjectCompiledData> { }
*defined = true;
let &DataDescription { let &DataDescription {
ref init, ref init,
ref function_decls, ref function_decls,
@@ -316,6 +362,7 @@ impl Backend for ObjectBackend {
ref function_relocs, ref function_relocs,
ref data_relocs, ref data_relocs,
ref custom_segment_section, ref custom_segment_section,
align,
} = data_ctx.description(); } = data_ctx.description();
let reloc_size = match self.isa.triple().pointer_width().unwrap() { let reloc_size = match self.isa.triple().pointer_width().unwrap() {
@@ -345,17 +392,16 @@ impl Backend for ObjectBackend {
}); });
} }
let symbol = self.data_objects[data_id].unwrap();
let section = if custom_segment_section.is_none() { let section = if custom_segment_section.is_none() {
let section_kind = if let Init::Zeros { .. } = *init { let section_kind = if let Init::Zeros { .. } = *init {
if tls { if decl.tls {
StandardSection::UninitializedTls StandardSection::UninitializedTls
} else { } else {
StandardSection::UninitializedData StandardSection::UninitializedData
} }
} else if tls { } else if decl.tls {
StandardSection::Tls StandardSection::Tls
} else if writable { } else if decl.writable {
StandardSection::Data StandardSection::Data
} else if relocs.is_empty() { } else if relocs.is_empty() {
StandardSection::ReadOnlyData StandardSection::ReadOnlyData
@@ -364,7 +410,7 @@ impl Backend for ObjectBackend {
}; };
self.object.section_id(section_kind) self.object.section_id(section_kind)
} else { } else {
if tls { if decl.tls {
return Err(cranelift_module::ModuleError::Backend(anyhow::anyhow!( return Err(cranelift_module::ModuleError::Backend(anyhow::anyhow!(
"Custom section not supported for TLS" "Custom section not supported for TLS"
))); )));
@@ -373,7 +419,7 @@ impl Backend for ObjectBackend {
self.object.add_section( self.object.add_section(
seg.clone().into_bytes(), seg.clone().into_bytes(),
sec.clone().into_bytes(), sec.clone().into_bytes(),
if writable { if decl.writable {
SectionKind::Data SectionKind::Data
} else if relocs.is_empty() { } else if relocs.is_empty() {
SectionKind::ReadOnlyData SectionKind::ReadOnlyData
@@ -383,7 +429,7 @@ impl Backend for ObjectBackend {
) )
}; };
let align = u64::from(align.unwrap_or(1)); let align = align.unwrap_or(1);
let offset = match *init { let offset = match *init {
Init::Uninitialized => { Init::Uninitialized => {
panic!("data is not initialized yet"); panic!("data is not initialized yet");
@@ -402,61 +448,14 @@ impl Backend for ObjectBackend {
relocs, relocs,
}); });
} }
Ok(ObjectCompiledData) Ok(())
} }
}
fn write_data_funcaddr( impl ObjectModule {
&mut self, /// Finalize all relocations and output an object.
_data: &mut ObjectCompiledData, pub fn finish(mut self) -> ObjectProduct {
_offset: usize, let symbol_relocs = mem::take(&mut self.relocs);
_what: ir::FuncRef,
) {
unimplemented!()
}
fn write_data_dataaddr(
&mut self,
_data: &mut ObjectCompiledData,
_offset: usize,
_what: ir::GlobalValue,
_usize: binemit::Addend,
) {
unimplemented!()
}
fn finalize_function(
&mut self,
_id: FuncId,
_func: &ObjectCompiledFunction,
_namespace: &ModuleNamespace<Self>,
) {
// Nothing to do.
}
fn get_finalized_function(&self, _func: &ObjectCompiledFunction) {
// Nothing to do.
}
fn finalize_data(
&mut self,
_id: DataId,
_data: &ObjectCompiledData,
_namespace: &ModuleNamespace<Self>,
) {
// Nothing to do.
}
fn get_finalized_data(&self, _data: &ObjectCompiledData) {
// Nothing to do.
}
fn publish(&mut self) {
// Nothing to do.
}
fn finish(mut self, namespace: &ModuleNamespace<Self>) -> ObjectProduct {
let mut symbol_relocs = Vec::new();
mem::swap(&mut symbol_relocs, &mut self.relocs);
for symbol in symbol_relocs { for symbol in symbol_relocs {
for &RelocRecord { for &RelocRecord {
offset, offset,
@@ -467,7 +466,7 @@ impl Backend for ObjectBackend {
addend, addend,
} in &symbol.relocs } in &symbol.relocs
{ {
let target_symbol = self.get_symbol(namespace, name); let target_symbol = self.get_symbol(name);
self.object self.object
.add_relocation( .add_relocation(
symbol.section, symbol.section,
@@ -499,24 +498,18 @@ impl Backend for ObjectBackend {
data_objects: self.data_objects, data_objects: self.data_objects,
} }
} }
}
impl ObjectBackend { /// This should only be called during finish because it creates
// This should only be called during finish because it creates /// symbols for missing libcalls.
// symbols for missing libcalls. fn get_symbol(&mut self, name: &ir::ExternalName) -> SymbolId {
fn get_symbol(
&mut self,
namespace: &ModuleNamespace<Self>,
name: &ir::ExternalName,
) -> SymbolId {
match *name { match *name {
ir::ExternalName::User { .. } => { ir::ExternalName::User { .. } => {
if namespace.is_function(name) { if self.declarations.is_function(name) {
let id = namespace.get_function_id(name); let id = self.declarations.get_function_id(name);
self.functions[id].unwrap() self.functions[id].unwrap().0
} else { } else {
let id = namespace.get_data_id(name); let id = self.declarations.get_data_id(name);
self.data_objects[id].unwrap() self.data_objects[id].unwrap().0
} }
} }
ir::ExternalName::LibCall(ref libcall) => { ir::ExternalName::LibCall(ref libcall) => {
@@ -557,9 +550,6 @@ fn translate_linkage(linkage: Linkage) -> (SymbolScope, bool) {
(scope, weak) (scope, weak)
} }
pub struct ObjectCompiledFunction;
pub struct ObjectCompiledData;
/// This is the output of `Module`'s /// This is the output of `Module`'s
/// [`finish`](../cranelift_module/struct.Module.html#method.finish) function. /// [`finish`](../cranelift_module/struct.Module.html#method.finish) function.
/// It contains the generated `Object` and other information produced during /// It contains the generated `Object` and other information produced during
@@ -568,22 +558,22 @@ pub struct ObjectProduct {
/// Object artifact with all functions and data from the module defined. /// Object artifact with all functions and data from the module defined.
pub object: Object, pub object: Object,
/// Symbol IDs for functions (both declared and defined). /// Symbol IDs for functions (both declared and defined).
pub functions: SecondaryMap<FuncId, Option<SymbolId>>, pub functions: SecondaryMap<FuncId, Option<(SymbolId, bool)>>,
/// Symbol IDs for data objects (both declared and defined). /// Symbol IDs for data objects (both declared and defined).
pub data_objects: SecondaryMap<DataId, Option<SymbolId>>, pub data_objects: SecondaryMap<DataId, Option<(SymbolId, bool)>>,
} }
impl ObjectProduct { impl ObjectProduct {
/// Return the `SymbolId` for the given function. /// Return the `SymbolId` for the given function.
#[inline] #[inline]
pub fn function_symbol(&self, id: FuncId) -> SymbolId { pub fn function_symbol(&self, id: FuncId) -> SymbolId {
self.functions[id].unwrap() self.functions[id].unwrap().0
} }
/// Return the `SymbolId` for the given data object. /// Return the `SymbolId` for the given data object.
#[inline] #[inline]
pub fn data_symbol(&self, id: DataId) -> SymbolId { pub fn data_symbol(&self, id: DataId) -> SymbolId {
self.data_objects[id].unwrap() self.data_objects[id].unwrap().0
} }
/// Write the object bytes in memory. /// Write the object bytes in memory.

View File

@@ -27,7 +27,7 @@
mod backend; mod backend;
pub use crate::backend::{ObjectBackend, ObjectBuilder, ObjectProduct}; pub use crate::backend::{ObjectBuilder, ObjectModule, ObjectProduct};
/// Version number of this crate. /// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub const VERSION: &str = env!("CARGO_PKG_VERSION");

View File

@@ -13,11 +13,13 @@ edition = "2018"
cranelift-module = { path = "../module", version = "0.67.0" } cranelift-module = { path = "../module", version = "0.67.0" }
cranelift-native = { path = "../native", version = "0.67.0" } cranelift-native = { path = "../native", version = "0.67.0" }
cranelift-codegen = { path = "../codegen", version = "0.67.0", default-features = false, features = ["std"] } cranelift-codegen = { path = "../codegen", version = "0.67.0", default-features = false, features = ["std"] }
cranelift-entity = { path = "../entity", version = "0.67.0" }
region = "2.2.0" region = "2.2.0"
libc = { version = "0.2.42" } libc = { version = "0.2.42" }
errno = "0.2.4" errno = "0.2.4"
target-lexicon = "0.11" target-lexicon = "0.11"
memmap = { version = "0.7.0", optional = true } memmap = { version = "0.7.0", optional = true }
log = { version = "0.4.6", default-features = false }
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3", features = ["winbase", "memoryapi"] } winapi = { version = "0.3", features = ["winbase", "memoryapi"] }

View File

@@ -1,12 +1,12 @@
use cranelift::prelude::*; use cranelift::prelude::*;
use cranelift_codegen::binemit::NullTrapSink; use cranelift_codegen::binemit::NullTrapSink;
use cranelift_module::{default_libcall_names, Linkage, Module}; use cranelift_module::{default_libcall_names, Linkage, Module};
use cranelift_simplejit::{SimpleJITBackend, SimpleJITBuilder}; use cranelift_simplejit::{SimpleJITBuilder, SimpleJITModule};
use std::mem; use std::mem;
fn main() { fn main() {
let mut module: Module<SimpleJITBackend> = let mut module: SimpleJITModule =
Module::new(SimpleJITBuilder::new(default_libcall_names())); SimpleJITModule::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();
@@ -70,10 +70,10 @@ fn main() {
module.clear_context(&mut ctx); module.clear_context(&mut ctx);
// Perform linking. // Perform linking.
module.finalize_definitions(); let product = module.finish();
// Get a raw pointer to the generated code. // Get a raw pointer to the generated code.
let code_b = module.get_finalized_function(func_b); let code_b = product.lookup_func(func_b);
// Cast it to a rust function pointer type. // Cast it to a rust function pointer type.
let ptr_b = unsafe { mem::transmute::<_, fn() -> u32>(code_b) }; let ptr_b = unsafe { mem::transmute::<_, fn() -> u32>(code_b) };

View File

@@ -1,20 +1,23 @@
//! Defines `SimpleJITBackend`. //! Defines `SimpleJITModule`.
use crate::memory::Memory; use crate::memory::Memory;
use cranelift_codegen::binemit::{ use cranelift_codegen::binemit::{
Addend, CodeOffset, Reloc, RelocSink, StackMap, StackMapSink, TrapSink, Addend, CodeInfo, CodeOffset, Reloc, RelocSink, StackMap, StackMapSink, TrapSink,
}; };
use cranelift_codegen::isa::TargetIsa; use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::settings::Configurable; use cranelift_codegen::settings::Configurable;
use cranelift_codegen::{self, ir, settings}; use cranelift_codegen::{self, ir, settings};
use cranelift_entity::SecondaryMap;
use cranelift_module::{ use cranelift_module::{
Backend, DataContext, DataDescription, DataId, FuncId, Init, Linkage, ModuleNamespace, DataContext, DataDescription, DataId, FuncId, FuncOrDataId, Init, Linkage, Module,
ModuleResult, ModuleCompiledFunction, ModuleDeclarations, ModuleError, ModuleResult,
}; };
use cranelift_native; use cranelift_native;
#[cfg(not(windows))] #[cfg(not(windows))]
use libc; use libc;
use log::info;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryInto;
use std::ffi::CString; use std::ffi::CString;
use std::io::Write; use std::io::Write;
use std::ptr; use std::ptr;
@@ -22,11 +25,11 @@ use target_lexicon::PointerWidth;
#[cfg(windows)] #[cfg(windows)]
use winapi; use winapi;
const EXECUTABLE_DATA_ALIGNMENT: u8 = 0x10; const EXECUTABLE_DATA_ALIGNMENT: u64 = 0x10;
const WRITABLE_DATA_ALIGNMENT: u8 = 0x8; const WRITABLE_DATA_ALIGNMENT: u64 = 0x8;
const READONLY_DATA_ALIGNMENT: u8 = 0x1; const READONLY_DATA_ALIGNMENT: u64 = 0x1;
/// A builder for `SimpleJITBackend`. /// A builder for `SimpleJITModule`.
pub struct SimpleJITBuilder { pub struct SimpleJITBuilder {
isa: Box<dyn TargetIsa>, isa: Box<dyn TargetIsa>,
symbols: HashMap<String, *const u8>, symbols: HashMap<String, *const u8>,
@@ -115,18 +118,24 @@ impl SimpleJITBuilder {
} }
} }
/// A `SimpleJITBackend` implements `Backend` and emits code and data into memory where it can be /// A `SimpleJITModule` implements `Module` and emits code and data into memory where it can be
/// directly called and accessed. /// directly called and accessed.
/// ///
/// See the `SimpleJITBuilder` for a convenient way to construct `SimpleJITBackend` instances. /// See the `SimpleJITBuilder` for a convenient way to construct `SimpleJITModule` instances.
pub struct SimpleJITBackend { pub struct SimpleJITModule {
isa: Box<dyn TargetIsa>, isa: Box<dyn TargetIsa>,
symbols: HashMap<String, *const u8>, symbols: HashMap<String, *const u8>,
libcall_names: Box<dyn Fn(ir::LibCall) -> String>, libcall_names: Box<dyn Fn(ir::LibCall) -> String>,
memory: SimpleJITMemoryHandle, memory: SimpleJITMemoryHandle,
declarations: ModuleDeclarations,
functions: SecondaryMap<FuncId, Option<SimpleJITCompiledFunction>>,
data_objects: SecondaryMap<DataId, Option<SimpleJITCompiledData>>,
functions_to_finalize: Vec<FuncId>,
data_objects_to_finalize: Vec<DataId>,
} }
/// A record of a relocation to perform. /// A record of a relocation to perform.
#[derive(Clone)]
struct RelocRecord { struct RelocRecord {
offset: CodeOffset, offset: CodeOffset,
reloc: Reloc, reloc: Reloc,
@@ -141,26 +150,74 @@ struct StackMapRecord {
stack_map: StackMap, stack_map: StackMap,
} }
#[derive(Clone)]
pub struct SimpleJITCompiledFunction { pub struct SimpleJITCompiledFunction {
code: *mut u8, code: *mut u8,
size: usize, size: usize,
relocs: Vec<RelocRecord>, relocs: Vec<RelocRecord>,
} }
#[derive(Clone)]
pub struct SimpleJITCompiledData { pub struct SimpleJITCompiledData {
storage: *mut u8, storage: *mut u8,
size: usize, size: usize,
relocs: Vec<RelocRecord>, relocs: Vec<RelocRecord>,
} }
/// A handle to allow freeing memory allocated by the `Backend`. /// A handle to allow freeing memory allocated by the `Module`.
pub struct SimpleJITMemoryHandle { struct SimpleJITMemoryHandle {
code: Memory, code: Memory,
readonly: Memory, readonly: Memory,
writable: Memory, writable: Memory,
} }
impl SimpleJITBackend { /// A `SimpleJITProduct` allows looking up the addresses of all functions and data objects
/// defined in the original module.
pub struct SimpleJITProduct {
memory: SimpleJITMemoryHandle,
declarations: ModuleDeclarations,
functions: SecondaryMap<FuncId, Option<SimpleJITCompiledFunction>>,
data_objects: SecondaryMap<DataId, Option<SimpleJITCompiledData>>,
}
impl SimpleJITProduct {
/// Free memory allocated for code and data segments of compiled functions.
///
/// # Safety
///
/// Because this function invalidates any pointers retrived from the
/// corresponding module, it should only be used when none of the functions
/// from that module are currently executing and none of the `fn` pointers
/// are called afterwards.
pub unsafe fn free_memory(&mut self) {
self.memory.code.free_memory();
self.memory.readonly.free_memory();
self.memory.writable.free_memory();
}
/// Get the `FuncOrDataId` associated with the given name.
pub fn func_or_data_for_func(&self, name: &str) -> Option<FuncOrDataId> {
self.declarations.get_name(name)
}
/// Return the address of a function.
pub fn lookup_func(&self, func_id: FuncId) -> *const u8 {
self.functions[func_id]
.as_ref()
.unwrap_or_else(|| panic!("{} is not defined", func_id))
.code
}
/// Return the address and size of a data object.
pub fn lookup_data(&self, data_id: DataId) -> (*const u8, usize) {
let data = self.data_objects[data_id]
.as_ref()
.unwrap_or_else(|| panic!("{} is not defined", data_id));
(data.storage, data.size)
}
}
impl SimpleJITModule {
fn lookup_symbol(&self, name: &str) -> *const u8 { fn lookup_symbol(&self, name: &str) -> *const u8 {
match self.symbols.get(name) { match self.symbols.get(name) {
Some(&ptr) => ptr, Some(&ptr) => ptr,
@@ -168,24 +225,22 @@ impl SimpleJITBackend {
} }
} }
fn get_definition( fn get_definition(&self, name: &ir::ExternalName) -> *const u8 {
&self,
namespace: &ModuleNamespace<Self>,
name: &ir::ExternalName,
) -> *const u8 {
match *name { match *name {
ir::ExternalName::User { .. } => { ir::ExternalName::User { .. } => {
if namespace.is_function(name) { if self.declarations.is_function(name) {
let (def, name_str, _signature) = namespace.get_function_definition(&name); let func_id = self.declarations.get_function_id(name);
match def { match &self.functions[func_id] {
Some(compiled) => compiled.code, Some(compiled) => compiled.code,
None => self.lookup_symbol(name_str), None => {
self.lookup_symbol(&self.declarations.get_function_decl(func_id).name)
}
} }
} else { } else {
let (def, name_str, _writable) = namespace.get_data_definition(&name); let data_id = self.declarations.get_data_id(name);
match def { match &self.data_objects[data_id] {
Some(compiled) => compiled.storage, Some(compiled) => compiled.storage,
None => self.lookup_symbol(name_str), None => self.lookup_symbol(&self.declarations.get_data_decl(data_id).name),
} }
} }
} }
@@ -213,34 +268,100 @@ impl SimpleJITBackend {
let _ = writeln!(map_file, "{:x} {:x} {}", ptr as usize, size, name); let _ = writeln!(map_file, "{:x} {:x} {}", ptr as usize, size, name);
} }
} }
}
impl<'simple_jit_backend> Backend for SimpleJITBackend { fn finalize_function(&mut self, id: FuncId) {
type Builder = SimpleJITBuilder; use std::ptr::write_unaligned;
/// SimpleJIT compiled function and data objects may have outstanding let func = self.functions[id]
/// relocations that need to be performed before the memory can be used. .as_ref()
/// These relocations are performed within `finalize_function` and .expect("function must be compiled before it can be finalized");
/// `finalize_data`.
type CompiledFunction = SimpleJITCompiledFunction;
type CompiledData = SimpleJITCompiledData;
/// SimpleJIT emits code and data into memory, and provides raw pointers for &RelocRecord {
/// to them. They are valid for the remainder of the program's life, unless reloc,
/// [`free_memory`] is used. offset,
/// ref name,
/// [`free_memory`]: #method.free_memory addend,
type FinalizedFunction = *const u8; } in &func.relocs
type FinalizedData = (*mut u8, usize); {
let ptr = func.code;
debug_assert!((offset as usize) < func.size);
let at = unsafe { ptr.offset(offset as isize) };
let base = self.get_definition(name);
// TODO: Handle overflow.
let what = unsafe { base.offset(addend as isize) };
match reloc {
Reloc::Abs4 => {
// TODO: Handle overflow.
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut u32, what as u32)
};
}
Reloc::Abs8 => {
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut u64, what as u64)
};
}
Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => {
// TODO: Handle overflow.
let pcrel = ((what as isize) - (at as isize)) as i32;
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut i32, pcrel)
};
}
Reloc::X86GOTPCRel4 | Reloc::X86CallPLTRel4 => panic!("unexpected PIC relocation"),
_ => unimplemented!(),
}
}
}
/// SimpleJIT emits code and data into memory as it processes them, so it fn finalize_data(&mut self, id: DataId) {
/// doesn't need to provide anything after the `Module` is complete. use std::ptr::write_unaligned;
/// The handle object that is returned can optionally be used to free
/// allocated memory if required.
type Product = SimpleJITMemoryHandle;
/// Create a new `SimpleJITBackend`. let data = self.data_objects[id]
fn new(builder: SimpleJITBuilder) -> Self { .as_ref()
.expect("data object must be compiled before it can be finalized");
for &RelocRecord {
reloc,
offset,
ref name,
addend,
} in &data.relocs
{
let ptr = data.storage;
debug_assert!((offset as usize) < data.size);
let at = unsafe { ptr.offset(offset as isize) };
let base = self.get_definition(name);
// TODO: Handle overflow.
let what = unsafe { base.offset(addend as isize) };
match reloc {
Reloc::Abs4 => {
// TODO: Handle overflow.
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut u32, what as u32)
};
}
Reloc::Abs8 => {
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut u64, what as u64)
};
}
Reloc::X86PCRel4
| Reloc::X86CallPCRel4
| Reloc::X86GOTPCRel4
| Reloc::X86CallPLTRel4 => panic!("unexpected text relocation in data"),
_ => unimplemented!(),
}
}
}
/// Create a new `SimpleJITModule`.
pub fn new(builder: SimpleJITBuilder) -> Self {
let memory = SimpleJITMemoryHandle { let memory = SimpleJITMemoryHandle {
code: Memory::new(), code: Memory::new(),
readonly: Memory::new(), readonly: Memory::new(),
@@ -252,42 +373,75 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
symbols: builder.symbols, symbols: builder.symbols,
libcall_names: builder.libcall_names, libcall_names: builder.libcall_names,
memory, memory,
declarations: ModuleDeclarations::default(),
functions: SecondaryMap::new(),
data_objects: SecondaryMap::new(),
functions_to_finalize: Vec::new(),
data_objects_to_finalize: Vec::new(),
} }
} }
}
impl<'simple_jit_backend> Module for SimpleJITModule {
fn isa(&self) -> &dyn TargetIsa { fn isa(&self) -> &dyn TargetIsa {
&*self.isa &*self.isa
} }
fn declare_function(&mut self, _id: FuncId, _name: &str, _linkage: Linkage) { fn declarations(&self) -> &ModuleDeclarations {
// Nothing to do. &self.declarations
}
fn declare_function(
&mut self,
name: &str,
linkage: Linkage,
signature: &ir::Signature,
) -> ModuleResult<FuncId> {
let (id, _decl) = self
.declarations
.declare_function(name, linkage, signature)?;
Ok(id)
} }
fn declare_data( fn declare_data(
&mut self, &mut self,
_id: DataId, name: &str,
_name: &str, linkage: Linkage,
_linkage: Linkage, writable: bool,
_writable: bool,
tls: bool, tls: bool,
_align: Option<u8>, ) -> ModuleResult<DataId> {
) {
assert!(!tls, "SimpleJIT doesn't yet support TLS"); assert!(!tls, "SimpleJIT doesn't yet support TLS");
// Nothing to do. let (id, _decl) = self
.declarations
.declare_data(name, linkage, writable, tls)?;
Ok(id)
} }
fn define_function<TS>( fn define_function<TS>(
&mut self, &mut self,
_id: FuncId, id: FuncId,
name: &str, ctx: &mut cranelift_codegen::Context,
ctx: &cranelift_codegen::Context,
_namespace: &ModuleNamespace<Self>,
code_size: u32,
trap_sink: &mut TS, trap_sink: &mut TS,
) -> ModuleResult<Self::CompiledFunction> ) -> ModuleResult<ModuleCompiledFunction>
where where
TS: TrapSink, TS: TrapSink,
{ {
info!("defining function {}: {}", id, ctx.func.display(self.isa()));
let CodeInfo {
total_size: code_size,
..
} = ctx.compile(self.isa())?;
let decl = self.declarations.get_function_decl(id);
if !decl.linkage.is_definable() {
return Err(ModuleError::InvalidImportDefinition(decl.name.clone()));
}
if !self.functions[id].is_none() {
return Err(ModuleError::DuplicateDefinition(decl.name.to_owned()));
}
self.functions_to_finalize.push(id);
let size = code_size as usize; let size = code_size as usize;
let ptr = self let ptr = self
.memory .memory
@@ -295,7 +449,7 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
.allocate(size, EXECUTABLE_DATA_ALIGNMENT) .allocate(size, EXECUTABLE_DATA_ALIGNMENT)
.expect("TODO: handle OOM etc."); .expect("TODO: handle OOM etc.");
self.record_function_for_perf(ptr, size, name); self.record_function_for_perf(ptr, size, &decl.name);
let mut reloc_sink = SimpleJITRelocSink::new(); let mut reloc_sink = SimpleJITRelocSink::new();
let mut stack_map_sink = SimpleJITStackMapSink::new(); let mut stack_map_sink = SimpleJITStackMapSink::new();
@@ -309,20 +463,35 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
) )
}; };
Ok(Self::CompiledFunction { self.functions[id] = Some(SimpleJITCompiledFunction {
code: ptr, code: ptr,
size, size,
relocs: reloc_sink.relocs, relocs: reloc_sink.relocs,
}) });
Ok(ModuleCompiledFunction { size: code_size })
} }
fn define_function_bytes( fn define_function_bytes(
&mut self, &mut self,
_id: FuncId, id: FuncId,
name: &str,
bytes: &[u8], bytes: &[u8],
_namespace: &ModuleNamespace<Self>, ) -> ModuleResult<ModuleCompiledFunction> {
) -> ModuleResult<Self::CompiledFunction> { let decl = self.declarations.get_function_decl(id);
if !decl.linkage.is_definable() {
return Err(ModuleError::InvalidImportDefinition(decl.name.clone()));
}
let total_size: u32 = match bytes.len().try_into() {
Ok(total_size) => total_size,
_ => Err(ModuleError::FunctionTooLarge(decl.name.clone()))?,
};
if !self.functions[id].is_none() {
return Err(ModuleError::DuplicateDefinition(decl.name.to_owned()));
}
self.functions_to_finalize.push(id);
let size = bytes.len(); let size = bytes.len();
let ptr = self let ptr = self
.memory .memory
@@ -330,30 +499,34 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
.allocate(size, EXECUTABLE_DATA_ALIGNMENT) .allocate(size, EXECUTABLE_DATA_ALIGNMENT)
.expect("TODO: handle OOM etc."); .expect("TODO: handle OOM etc.");
self.record_function_for_perf(ptr, size, name); self.record_function_for_perf(ptr, size, &decl.name);
unsafe { unsafe {
ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, size); ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, size);
} }
Ok(Self::CompiledFunction { self.functions[id] = Some(SimpleJITCompiledFunction {
code: ptr, code: ptr,
size, size,
relocs: vec![], relocs: vec![],
}) });
Ok(ModuleCompiledFunction { size: total_size })
} }
fn define_data( fn define_data(&mut self, id: DataId, data: &DataContext) -> ModuleResult<()> {
&mut self, let decl = self.declarations.get_data_decl(id);
_id: DataId, if !decl.linkage.is_definable() {
_name: &str, return Err(ModuleError::InvalidImportDefinition(decl.name.clone()));
writable: bool, }
tls: bool,
align: Option<u8>, if !self.data_objects[id].is_none() {
data: &DataContext, return Err(ModuleError::DuplicateDefinition(decl.name.to_owned()));
_namespace: &ModuleNamespace<Self>, }
) -> ModuleResult<Self::CompiledData> {
assert!(!tls, "SimpleJIT doesn't yet support TLS"); assert!(!decl.tls, "SimpleJIT doesn't yet support TLS");
self.data_objects_to_finalize.push(id);
let &DataDescription { let &DataDescription {
ref init, ref init,
@@ -362,10 +535,11 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
ref function_relocs, ref function_relocs,
ref data_relocs, ref data_relocs,
custom_segment_section: _, custom_segment_section: _,
align,
} = data.description(); } = data.description();
let size = init.size(); let size = init.size();
let storage = if writable { let storage = if decl.writable {
self.memory self.memory
.writable .writable
.allocate(size, align.unwrap_or(WRITABLE_DATA_ALIGNMENT)) .allocate(size, align.unwrap_or(WRITABLE_DATA_ALIGNMENT))
@@ -413,141 +587,17 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
}); });
} }
Ok(Self::CompiledData { self.data_objects[id] = Some(SimpleJITCompiledData {
storage, storage,
size, size,
relocs, relocs,
}) });
}
Ok(())
fn write_data_funcaddr(
&mut self,
_data: &mut Self::CompiledData,
_offset: usize,
_what: ir::FuncRef,
) {
unimplemented!();
}
fn write_data_dataaddr(
&mut self,
_data: &mut Self::CompiledData,
_offset: usize,
_what: ir::GlobalValue,
_usize: Addend,
) {
unimplemented!();
}
fn finalize_function(
&mut self,
_id: FuncId,
func: &Self::CompiledFunction,
namespace: &ModuleNamespace<Self>,
) -> Self::FinalizedFunction {
use std::ptr::write_unaligned;
for &RelocRecord {
reloc,
offset,
ref name,
addend,
} in &func.relocs
{
let ptr = func.code;
debug_assert!((offset as usize) < func.size);
let at = unsafe { ptr.offset(offset as isize) };
let base = self.get_definition(namespace, name);
// TODO: Handle overflow.
let what = unsafe { base.offset(addend as isize) };
match reloc {
Reloc::Abs4 => {
// TODO: Handle overflow.
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut u32, what as u32)
};
}
Reloc::Abs8 => {
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut u64, what as u64)
};
}
Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => {
// TODO: Handle overflow.
let pcrel = ((what as isize) - (at as isize)) as i32;
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut i32, pcrel)
};
}
Reloc::X86GOTPCRel4 | Reloc::X86CallPLTRel4 => panic!("unexpected PIC relocation"),
_ => unimplemented!(),
}
}
func.code
}
fn get_finalized_function(&self, func: &Self::CompiledFunction) -> Self::FinalizedFunction {
func.code
}
fn finalize_data(
&mut self,
_id: DataId,
data: &Self::CompiledData,
namespace: &ModuleNamespace<Self>,
) -> Self::FinalizedData {
use std::ptr::write_unaligned;
for &RelocRecord {
reloc,
offset,
ref name,
addend,
} in &data.relocs
{
let ptr = data.storage;
debug_assert!((offset as usize) < data.size);
let at = unsafe { ptr.offset(offset as isize) };
let base = self.get_definition(namespace, name);
// TODO: Handle overflow.
let what = unsafe { base.offset(addend as isize) };
match reloc {
Reloc::Abs4 => {
// TODO: Handle overflow.
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut u32, what as u32)
};
}
Reloc::Abs8 => {
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut u64, what as u64)
};
}
Reloc::X86PCRel4
| Reloc::X86CallPCRel4
| Reloc::X86GOTPCRel4
| Reloc::X86CallPLTRel4 => panic!("unexpected text relocation in data"),
_ => unimplemented!(),
}
}
(data.storage, data.size)
}
fn get_finalized_data(&self, data: &Self::CompiledData) -> Self::FinalizedData {
(data.storage, data.size)
}
fn publish(&mut self) {
// Now that we're done patching, prepare the memory for execution!
self.memory.readonly.set_readonly();
self.memory.code.set_readable_and_executable();
} }
}
impl SimpleJITModule {
/// SimpleJIT emits code and data into memory as it processes them. This /// SimpleJIT emits code and data into memory as it processes them. This
/// method performs no additional processing, but returns a handle which /// method performs no additional processing, but returns a handle which
/// allows freeing the allocated memory. Otherwise said memory is leaked /// allows freeing the allocated memory. Otherwise said memory is leaked
@@ -555,8 +605,28 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
/// ///
/// This method does not need to be called when access to the memory /// This method does not need to be called when access to the memory
/// handle is not required. /// handle is not required.
fn finish(self, _namespace: &ModuleNamespace<Self>) -> Self::Product { pub fn finish(mut self) -> SimpleJITProduct {
self.memory for func in std::mem::take(&mut self.functions_to_finalize) {
let decl = self.declarations.get_function_decl(func);
debug_assert!(decl.linkage.is_definable());
self.finalize_function(func);
}
for data in std::mem::take(&mut self.data_objects_to_finalize) {
let decl = self.declarations.get_data_decl(data);
debug_assert!(decl.linkage.is_definable());
self.finalize_data(data);
}
// Now that we're done patching, prepare the memory for execution!
self.memory.readonly.set_readonly();
self.memory.code.set_readable_and_executable();
SimpleJITProduct {
memory: self.memory,
declarations: self.declarations,
functions: self.functions,
data_objects: self.data_objects,
}
} }
} }
@@ -603,22 +673,6 @@ fn lookup_with_dlsym(name: &str) -> *const u8 {
} }
} }
impl SimpleJITMemoryHandle {
/// Free memory allocated for code and data segments of compiled functions.
///
/// # Safety
///
/// Because this function invalidates any pointers retrived from the
/// corresponding module, it should only be used when none of the functions
/// from that module are currently executing and none of the`fn` pointers
/// are called afterwards.
pub unsafe fn free_memory(&mut self) {
self.code.free_memory();
self.readonly.free_memory();
self.writable.free_memory();
}
}
struct SimpleJITRelocSink { struct SimpleJITRelocSink {
pub relocs: Vec<RelocRecord>, pub relocs: Vec<RelocRecord>,
} }

View File

@@ -26,7 +26,7 @@
mod backend; mod backend;
mod memory; mod memory;
pub use crate::backend::{SimpleJITBackend, SimpleJITBuilder}; pub use crate::backend::{SimpleJITBuilder, SimpleJITModule, SimpleJITProduct};
/// Version number of this crate. /// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub const VERSION: &str = env!("CARGO_PKG_VERSION");

View File

@@ -8,6 +8,7 @@ use libc;
use memmap::MmapMut; use memmap::MmapMut;
use region; use region;
use std::convert::TryFrom;
use std::mem; use std::mem;
use std::ptr; use std::ptr;
@@ -149,10 +150,11 @@ impl Memory {
} }
/// TODO: Use a proper error type. /// TODO: Use a proper error type.
pub fn allocate(&mut self, size: usize, align: u8) -> Result<*mut u8, String> { pub fn allocate(&mut self, size: usize, align: u64) -> Result<*mut u8, String> {
if self.position % align as usize != 0 { let align = usize::try_from(align).expect("alignment too big");
self.position += align as usize - self.position % align as usize; if self.position % align != 0 {
debug_assert!(self.position % align as usize == 0); self.position += align - self.position % align;
debug_assert!(self.position % align == 0);
} }
if size <= self.current.len - self.position { if size <= self.current.len - self.position {

View File

@@ -9,8 +9,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> = let mut module: SimpleJITModule =
Module::new(SimpleJITBuilder::new(default_libcall_names())); SimpleJITModule::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![],
@@ -26,7 +26,7 @@ fn error_on_incompatible_sig_in_declare_function() {
.unwrap(); // Make sure this is an error .unwrap(); // Make sure this is an error
} }
fn define_simple_function(module: &mut Module<SimpleJITBackend>) -> FuncId { fn define_simple_function(module: &mut SimpleJITModule) -> FuncId {
let sig = Signature { let sig = Signature {
params: vec![], params: vec![],
returns: vec![], returns: vec![],
@@ -55,27 +55,13 @@ fn define_simple_function(module: &mut Module<SimpleJITBackend>) -> FuncId {
func_id func_id
} }
#[test]
fn double_finalize() {
let mut module: Module<SimpleJITBackend> =
Module::new(SimpleJITBuilder::new(default_libcall_names()));
define_simple_function(&mut module);
module.finalize_definitions();
// Calling `finalize_definitions` a second time without any new definitions
// should have no effect.
module.finalize_definitions();
}
#[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> = let mut module: SimpleJITModule =
Module::new(SimpleJITBuilder::new(default_libcall_names())); SimpleJITModule::new(SimpleJITBuilder::new(default_libcall_names()));
define_simple_function(&mut module); define_simple_function(&mut module);
module.finalize_definitions();
define_simple_function(&mut module); define_simple_function(&mut module);
} }
@@ -154,8 +140,8 @@ fn switch_error() {
#[test] #[test]
fn libcall_function() { fn libcall_function() {
let mut module: Module<SimpleJITBackend> = let mut module: SimpleJITModule =
Module::new(SimpleJITBuilder::new(default_libcall_names())); SimpleJITModule::new(SimpleJITBuilder::new(default_libcall_names()));
let sig = Signature { let sig = Signature {
params: vec![], params: vec![],
@@ -200,5 +186,5 @@ fn libcall_function() {
.define_function(func_id, &mut ctx, &mut trap_sink) .define_function(func_id, &mut ctx, &mut trap_sink)
.unwrap(); .unwrap();
module.finalize_definitions(); module.finish();
} }