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",
"cranelift-codegen",
"cranelift-module",
"log",
"object 0.21.1",
"target-lexicon",
]
@@ -535,6 +536,7 @@ dependencies = [
"cranelift-native",
"errno",
"libc",
"log",
"memmap",
"region",
"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
require greater flexibility don't need to use it.
A `Module` is a collection of functions and data objects that are linked
together. `Backend` is a trait that defines an interface for backends
that compile modules into various forms. Most users will use one of the
following `Backend` implementations:
A module is a collection of functions and data objects that are linked
together. The `Module` trait that defines a common interface for various kinds
of modules. Most users will use one of the following `Module` implementations:
- `SimpleJITBackend`, provided by [cranelift-simplejit], which JITs
- `SimpleJITModule`, provided by [cranelift-simplejit], which JITs
code to memory for direct execution.
- `ObjectBackend`, provided by [cranelift-object], which emits native
- `ObjectModule`, provided by [cranelift-object], which emits native
object files.
[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)>,
/// Object file section
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.
@@ -68,6 +71,7 @@ impl DataContext {
function_relocs: vec![],
data_relocs: vec![],
custom_segment_section: None,
align: None,
},
}
}
@@ -79,6 +83,8 @@ impl DataContext {
self.description.data_decls.clear();
self.description.function_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.
@@ -100,6 +106,12 @@ impl DataContext {
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.
///
/// Users of the `Module` API generally should call

View File

@@ -29,21 +29,52 @@ extern crate std;
#[cfg(not(feature = "std"))]
use hashbrown::{hash_map, HashMap};
use std::borrow::ToOwned;
use std::boxed::Box;
#[cfg(feature = "std")]
use std::collections::{hash_map, HashMap};
use std::string::String;
use cranelift_codegen::ir;
mod backend;
mod data_context;
mod module;
mod traps;
pub use crate::backend::{default_libcall_names, Backend};
pub use crate::data_context::{DataContext, DataDescription, Init};
pub use crate::module::{
DataId, FuncId, FuncOrDataId, Linkage, Module, ModuleError, ModuleFunction, ModuleNamespace,
ModuleResult,
DataId, FuncId, FuncOrDataId, Linkage, Module, ModuleCompiledFunction, ModuleDeclarations,
ModuleError, ModuleResult,
};
pub use crate::traps::TrapSite;
/// Version number of this crate.
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 crate::data_context::DataContext;
use crate::Backend;
use cranelift_codegen::binemit::{self, CodeInfo};
use cranelift_codegen::binemit;
use cranelift_codegen::entity::{entity_impl, PrimaryMap};
use cranelift_codegen::{ir, isa, CodegenError, Context};
use log::info;
use std::borrow::ToOwned;
use std::convert::TryInto;
use std::string::String;
use std::vec::Vec;
use thiserror::Error;
/// A function identifier for use in the `Module` interface.
@@ -132,7 +128,21 @@ pub struct FunctionDeclaration {
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)]
pub enum ModuleError {
/// 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.
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.
pub struct DataDeclaration {
pub name: String,
pub linkage: Linkage,
pub writable: bool,
pub tls: bool,
pub align: Option<u8>,
}
/// A data object belonging to a `Module`.
struct ModuleData<B>
where
B: Backend,
{
/// 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);
impl DataDeclaration {
fn merge(&mut self, linkage: Linkage, writable: bool, tls: bool) {
self.linkage = Linkage::merge(self.linkage, linkage);
self.writable = self.writable || writable;
assert_eq!(
self.decl.tls, tls,
self.tls, tls,
"Can't change TLS data object to normal or in the opposite way",
);
}
}
/// The functions and data objects belonging to a module.
struct ModuleContents<B>
where
B: Backend,
{
functions: PrimaryMap<FuncId, ModuleFunction<B>>,
data_objects: PrimaryMap<DataId, ModuleData<B>>,
/// This provides a view to the state of a module which allows `ir::ExternalName`s to be translated
/// into `FunctionDeclaration`s and `DataDeclaration`s.
#[derive(Default)]
pub struct ModuleDeclarations {
names: HashMap<String, FuncOrDataId>,
functions: PrimaryMap<FuncId, FunctionDeclaration>,
data_objects: PrimaryMap<DataId, DataDeclaration>,
}
impl<B> ModuleContents<B>
where
B: Backend,
{
fn get_function_id(&self, name: &ir::ExternalName) -> FuncId {
impl ModuleDeclarations {
/// 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).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 {
debug_assert_eq!(namespace, 0);
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 {
debug_assert_eq!(namespace, 1);
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`.
pub fn get_function_decl(&self, name: &ir::ExternalName) -> &FunctionDeclaration {
&self.contents.get_function_info(name).decl
pub fn get_function_decl(&self, func_id: FuncId) -> &FunctionDeclaration {
&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`.
pub fn get_data_decl(&self, name: &ir::ExternalName) -> &DataDeclaration {
&self.contents.get_data_info(name).decl
}
/// 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)
pub fn get_data_decl(&self, data_id: DataId) -> &DataDeclaration {
&self.data_objects[data_id]
}
/// Return whether `name` names a function, rather than a data object.
@@ -348,87 +258,6 @@ where
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.
pub fn declare_function(
@@ -436,43 +265,32 @@ where
name: &str,
linkage: Linkage,
signature: &ir::Signature,
) -> ModuleResult<FuncId> {
) -> ModuleResult<(FuncId, &FunctionDeclaration)> {
// TODO: Can we avoid allocating names so often?
use super::hash_map::Entry::*;
match self.names.entry(name.to_owned()) {
Occupied(entry) => match *entry.get() {
FuncOrDataId::Func(id) => {
let existing = &mut self.contents.functions[id];
let existing = &mut self.functions[id];
existing.merge(linkage, signature)?;
self.backend
.declare_function(id, name, existing.decl.linkage);
Ok(id)
Ok((id, existing))
}
FuncOrDataId::Data(..) => {
Err(ModuleError::IncompatibleDeclaration(name.to_owned()))
}
},
Vacant(entry) => {
let id = self.contents.functions.push(ModuleFunction {
decl: FunctionDeclaration {
let id = self.functions.push(FunctionDeclaration {
name: name.to_owned(),
linkage,
signature: signature.clone(),
},
compiled: None,
});
entry.insert(FuncOrDataId::Func(id));
self.backend.declare_function(id, name, linkage);
Ok(id)
Ok((id, &self.functions[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.
pub fn declare_data(
&mut self,
@@ -480,24 +298,15 @@ where
linkage: Linkage,
writable: bool,
tls: bool,
align: Option<u8>, // An alignment bigger than 128 is unlikely
) -> ModuleResult<DataId> {
) -> ModuleResult<(DataId, &DataDeclaration)> {
// TODO: Can we avoid allocating names so often?
use super::hash_map::Entry::*;
match self.names.entry(name.to_owned()) {
Occupied(entry) => match *entry.get() {
FuncOrDataId::Data(id) => {
let existing = &mut self.contents.data_objects[id];
existing.merge(linkage, writable, tls, align);
self.backend.declare_data(
id,
name,
existing.decl.linkage,
existing.decl.writable,
existing.decl.tls,
existing.decl.align,
);
Ok(id)
let existing = &mut self.data_objects[id];
existing.merge(linkage, writable, tls);
Ok((id, existing))
}
FuncOrDataId::Func(..) => {
@@ -505,30 +314,102 @@ where
}
},
Vacant(entry) => {
let id = self.contents.data_objects.push(ModuleData {
decl: DataDeclaration {
let id = self.data_objects.push(DataDeclaration {
name: name.to_owned(),
linkage,
writable,
tls,
align,
},
compiled: None,
});
entry.insert(FuncOrDataId::Data(id));
self.backend
.declare_data(id, name, linkage, writable, tls, align);
Ok(id)
Ok((id, &self.data_objects[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.
///
/// TODO: Coalesce redundant decls and signatures.
/// 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 {
let decl = &self.contents.functions[func].decl;
fn declare_func_in_func(&self, func: FuncId, in_func: &mut ir::Function) -> ir::FuncRef {
let decl = &self.declarations().functions[func];
let signature = in_func.import_signature(decl.signature.clone());
let colocated = decl.linkage.is_final();
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.
///
/// TODO: Same as above.
pub fn declare_data_in_func(&self, data: DataId, func: &mut ir::Function) -> ir::GlobalValue {
let decl = &self.contents.data_objects[data].decl;
fn declare_data_in_func(&self, data: DataId, func: &mut ir::Function) -> ir::GlobalValue {
let decl = &self.declarations().data_objects[data];
let colocated = decl.linkage.is_final();
func.create_global_value(ir::GlobalValueData::Symbol {
name: ir::ExternalName::user(1, data.as_u32()),
@@ -553,12 +434,12 @@ where
}
/// 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()))
}
/// 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()))
}
@@ -567,44 +448,14 @@ where
/// Returns the size of the function's code and constant data.
///
/// Note: After calling this function the given `Context` will contain the compiled function.
pub fn define_function<TS>(
fn define_function<TS>(
&mut self,
func: FuncId,
ctx: &mut Context,
trap_sink: &mut TS,
) -> ModuleResult<ModuleCompiledFunction>
where
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 })
}
TS: binemit::TrapSink;
/// Define a function, taking the function body from the given `bytes`.
///
@@ -613,187 +464,12 @@ where
/// `define_function`.
///
/// Returns the size of the function's code.
pub fn define_function_bytes(
fn define_function_bytes(
&mut self,
func: FuncId,
bytes: &[u8],
) -> 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 })
}
) -> ModuleResult<ModuleCompiledFunction>;
/// Define a data object, producing the data contents from the given `DataContext`.
pub 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,
})
}
fn define_data(&mut self, data: DataId, data_ctx: &DataContext) -> ModuleResult<()>;
}

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"] }
target-lexicon = "0.11"
anyhow = "1.0"
log = { version = "0.4.6", default-features = false }
[badges]
maintenance = { status = "experimental" }

View File

@@ -1,16 +1,17 @@
//! Defines `ObjectBackend`.
//! Defines `ObjectModule`.
use anyhow::anyhow;
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::isa::TargetIsa;
use cranelift_codegen::{self, binemit, ir};
use cranelift_codegen::{self, ir};
use cranelift_module::{
Backend, DataContext, DataDescription, DataId, FuncId, Init, Linkage, ModuleError,
ModuleNamespace, ModuleResult,
DataContext, DataDescription, DataId, FuncId, Init, Linkage, Module, ModuleCompiledFunction,
ModuleDeclarations, ModuleError, ModuleResult,
};
use log::info;
use object::write::{
Object, Relocation, SectionId, StandardSection, Symbol, SymbolId, SymbolSection,
};
@@ -18,10 +19,11 @@ use object::{
RelocationEncoding, RelocationKind, SectionKind, SymbolFlags, SymbolKind, SymbolScope,
};
use std::collections::HashMap;
use std::convert::TryInto;
use std::mem;
use target_lexicon::PointerWidth;
/// A builder for `ObjectBackend`.
/// A builder for `ObjectModule`.
pub struct ObjectBuilder {
isa: Box<dyn TargetIsa>,
binary_format: object::BinaryFormat,
@@ -35,7 +37,7 @@ pub struct ObjectBuilder {
impl ObjectBuilder {
/// 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`
/// 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.
pub struct ObjectBackend {
/// See the `ObjectBuilder` for a convenient way to construct `ObjectModule` instances.
pub struct ObjectModule {
isa: Box<dyn TargetIsa>,
object: Object,
functions: SecondaryMap<FuncId, Option<SymbolId>>,
data_objects: SecondaryMap<DataId, Option<SymbolId>>,
declarations: ModuleDeclarations,
functions: SecondaryMap<FuncId, Option<(SymbolId, bool)>>,
data_objects: SecondaryMap<DataId, Option<(SymbolId, bool)>>,
relocs: Vec<SymbolRelocs>,
libcalls: HashMap<ir::LibCall, SymbolId>,
libcall_names: Box<dyn Fn(ir::LibCall) -> String>,
@@ -121,26 +124,15 @@ pub struct ObjectBackend {
per_function_section: bool,
}
impl Backend for ObjectBackend {
type Builder = ObjectBuilder;
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 {
impl ObjectModule {
/// Create a new `ObjectModule` using the given Cranelift target.
pub fn new(builder: ObjectBuilder) -> Self {
let mut object = Object::new(builder.binary_format, builder.architecture, builder.endian);
object.add_file_symbol(builder.name);
Self {
isa: builder.isa,
object,
declarations: ModuleDeclarations::default(),
functions: SecondaryMap::new(),
data_objects: SecondaryMap::new(),
relocs: Vec::new(),
@@ -150,15 +142,30 @@ impl Backend for ObjectBackend {
per_function_section: builder.per_function_section,
}
}
}
impl Module for ObjectModule {
fn isa(&self) -> &dyn TargetIsa {
&*self.isa
}
fn declare_function(&mut self, id: FuncId, name: &str, linkage: Linkage) {
let (scope, weak) = translate_linkage(linkage);
fn declarations(&self) -> &ModuleDeclarations {
&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);
symbol.scope = scope;
symbol.weak = weak;
@@ -173,27 +180,31 @@ impl Backend for ObjectBackend {
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
});
self.functions[id] = Some(symbol_id);
self.functions[id] = Some((symbol_id, false));
}
Ok(id)
}
fn declare_data(
&mut self,
id: DataId,
name: &str,
linkage: Linkage,
_writable: bool,
writable: bool,
tls: bool,
_align: Option<u8>,
) {
let kind = if tls {
) -> ModuleResult<DataId> {
let (id, decl) = self
.declarations
.declare_data(name, linkage, writable, tls)?;
let kind = if decl.tls {
SymbolKind::Tls
} else {
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);
symbol.kind = kind;
symbol.scope = scope;
@@ -209,22 +220,42 @@ impl Backend for ObjectBackend {
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
});
self.data_objects[id] = Some(symbol_id);
self.data_objects[id] = Some((symbol_id, false));
}
Ok(id)
}
fn define_function<TS>(
&mut self,
func_id: FuncId,
_name: &str,
ctx: &cranelift_codegen::Context,
_namespace: &ModuleNamespace<Self>,
code_size: u32,
ctx: &mut cranelift_codegen::Context,
trap_sink: &mut TS,
) -> ModuleResult<ObjectCompiledFunction>
) -> ModuleResult<ModuleCompiledFunction>
where
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 reloc_sink = ObjectRelocSink::new(self.object.format());
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 symbol_name = self.object.symbol(symbol).name.clone();
let (section, offset) = self.object.add_subsection(
@@ -267,17 +296,32 @@ impl Backend for ObjectBackend {
relocs: reloc_sink.relocs,
});
}
Ok(ObjectCompiledFunction)
Ok(ModuleCompiledFunction { size: code_size })
}
fn define_function_bytes(
&mut self,
func_id: FuncId,
_name: &str,
bytes: &[u8],
_namespace: &ModuleNamespace<Self>,
) -> ModuleResult<ObjectCompiledFunction> {
let symbol = self.functions[func_id].unwrap();
) -> ModuleResult<ModuleCompiledFunction> {
info!("defining function {} with bytes", func_id);
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 {
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);
}
Ok(ObjectCompiledFunction)
Ok(ModuleCompiledFunction { size: total_size })
}
fn define_data(
&mut self,
data_id: DataId,
_name: &str,
writable: bool,
tls: bool,
align: Option<u8>,
data_ctx: &DataContext,
_namespace: &ModuleNamespace<Self>,
) -> ModuleResult<ObjectCompiledData> {
fn define_data(&mut self, data_id: DataId, data_ctx: &DataContext) -> ModuleResult<()> {
let decl = self.declarations.get_data_decl(data_id);
if !decl.linkage.is_definable() {
return Err(ModuleError::InvalidImportDefinition(decl.name.clone()));
}
let &mut (symbol, ref mut defined) = self.data_objects[data_id].as_mut().unwrap();
if *defined {
return Err(ModuleError::DuplicateDefinition(decl.name.clone()));
}
*defined = true;
let &DataDescription {
ref init,
ref function_decls,
@@ -316,6 +362,7 @@ impl Backend for ObjectBackend {
ref function_relocs,
ref data_relocs,
ref custom_segment_section,
align,
} = data_ctx.description();
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_kind = if let Init::Zeros { .. } = *init {
if tls {
if decl.tls {
StandardSection::UninitializedTls
} else {
StandardSection::UninitializedData
}
} else if tls {
} else if decl.tls {
StandardSection::Tls
} else if writable {
} else if decl.writable {
StandardSection::Data
} else if relocs.is_empty() {
StandardSection::ReadOnlyData
@@ -364,7 +410,7 @@ impl Backend for ObjectBackend {
};
self.object.section_id(section_kind)
} else {
if tls {
if decl.tls {
return Err(cranelift_module::ModuleError::Backend(anyhow::anyhow!(
"Custom section not supported for TLS"
)));
@@ -373,7 +419,7 @@ impl Backend for ObjectBackend {
self.object.add_section(
seg.clone().into_bytes(),
sec.clone().into_bytes(),
if writable {
if decl.writable {
SectionKind::Data
} else if relocs.is_empty() {
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 {
Init::Uninitialized => {
panic!("data is not initialized yet");
@@ -402,61 +448,14 @@ impl Backend for ObjectBackend {
relocs,
});
}
Ok(ObjectCompiledData)
Ok(())
}
}
fn write_data_funcaddr(
&mut self,
_data: &mut ObjectCompiledData,
_offset: usize,
_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);
impl ObjectModule {
/// Finalize all relocations and output an object.
pub fn finish(mut self) -> ObjectProduct {
let symbol_relocs = mem::take(&mut self.relocs);
for symbol in symbol_relocs {
for &RelocRecord {
offset,
@@ -467,7 +466,7 @@ impl Backend for ObjectBackend {
addend,
} in &symbol.relocs
{
let target_symbol = self.get_symbol(namespace, name);
let target_symbol = self.get_symbol(name);
self.object
.add_relocation(
symbol.section,
@@ -499,24 +498,18 @@ impl Backend for ObjectBackend {
data_objects: self.data_objects,
}
}
}
impl ObjectBackend {
// This should only be called during finish because it creates
// symbols for missing libcalls.
fn get_symbol(
&mut self,
namespace: &ModuleNamespace<Self>,
name: &ir::ExternalName,
) -> SymbolId {
/// This should only be called during finish because it creates
/// symbols for missing libcalls.
fn get_symbol(&mut self, name: &ir::ExternalName) -> SymbolId {
match *name {
ir::ExternalName::User { .. } => {
if namespace.is_function(name) {
let id = namespace.get_function_id(name);
self.functions[id].unwrap()
if self.declarations.is_function(name) {
let id = self.declarations.get_function_id(name);
self.functions[id].unwrap().0
} else {
let id = namespace.get_data_id(name);
self.data_objects[id].unwrap()
let id = self.declarations.get_data_id(name);
self.data_objects[id].unwrap().0
}
}
ir::ExternalName::LibCall(ref libcall) => {
@@ -557,9 +550,6 @@ fn translate_linkage(linkage: Linkage) -> (SymbolScope, bool) {
(scope, weak)
}
pub struct ObjectCompiledFunction;
pub struct ObjectCompiledData;
/// This is the output of `Module`'s
/// [`finish`](../cranelift_module/struct.Module.html#method.finish) function.
/// 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.
pub object: Object,
/// 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).
pub data_objects: SecondaryMap<DataId, Option<SymbolId>>,
pub data_objects: SecondaryMap<DataId, Option<(SymbolId, bool)>>,
}
impl ObjectProduct {
/// Return the `SymbolId` for the given function.
#[inline]
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.
#[inline]
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.

View File

@@ -27,7 +27,7 @@
mod backend;
pub use crate::backend::{ObjectBackend, ObjectBuilder, ObjectProduct};
pub use crate::backend::{ObjectBuilder, ObjectModule, ObjectProduct};
/// Version number of this crate.
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-native = { path = "../native", version = "0.67.0" }
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"
libc = { version = "0.2.42" }
errno = "0.2.4"
target-lexicon = "0.11"
memmap = { version = "0.7.0", optional = true }
log = { version = "0.4.6", default-features = false }
[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3", features = ["winbase", "memoryapi"] }

View File

@@ -1,12 +1,12 @@
use cranelift::prelude::*;
use cranelift_codegen::binemit::NullTrapSink;
use cranelift_module::{default_libcall_names, Linkage, Module};
use cranelift_simplejit::{SimpleJITBackend, SimpleJITBuilder};
use cranelift_simplejit::{SimpleJITBuilder, SimpleJITModule};
use std::mem;
fn main() {
let mut module: Module<SimpleJITBackend> =
Module::new(SimpleJITBuilder::new(default_libcall_names()));
let mut module: SimpleJITModule =
SimpleJITModule::new(SimpleJITBuilder::new(default_libcall_names()));
let mut ctx = module.make_context();
let mut func_ctx = FunctionBuilderContext::new();
@@ -70,10 +70,10 @@ fn main() {
module.clear_context(&mut ctx);
// Perform linking.
module.finalize_definitions();
let product = module.finish();
// 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.
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 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::settings::Configurable;
use cranelift_codegen::{self, ir, settings};
use cranelift_entity::SecondaryMap;
use cranelift_module::{
Backend, DataContext, DataDescription, DataId, FuncId, Init, Linkage, ModuleNamespace,
ModuleResult,
DataContext, DataDescription, DataId, FuncId, FuncOrDataId, Init, Linkage, Module,
ModuleCompiledFunction, ModuleDeclarations, ModuleError, ModuleResult,
};
use cranelift_native;
#[cfg(not(windows))]
use libc;
use log::info;
use std::collections::HashMap;
use std::convert::TryInto;
use std::ffi::CString;
use std::io::Write;
use std::ptr;
@@ -22,11 +25,11 @@ use target_lexicon::PointerWidth;
#[cfg(windows)]
use winapi;
const EXECUTABLE_DATA_ALIGNMENT: u8 = 0x10;
const WRITABLE_DATA_ALIGNMENT: u8 = 0x8;
const READONLY_DATA_ALIGNMENT: u8 = 0x1;
const EXECUTABLE_DATA_ALIGNMENT: u64 = 0x10;
const WRITABLE_DATA_ALIGNMENT: u64 = 0x8;
const READONLY_DATA_ALIGNMENT: u64 = 0x1;
/// A builder for `SimpleJITBackend`.
/// A builder for `SimpleJITModule`.
pub struct SimpleJITBuilder {
isa: Box<dyn TargetIsa>,
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.
///
/// See the `SimpleJITBuilder` for a convenient way to construct `SimpleJITBackend` instances.
pub struct SimpleJITBackend {
/// See the `SimpleJITBuilder` for a convenient way to construct `SimpleJITModule` instances.
pub struct SimpleJITModule {
isa: Box<dyn TargetIsa>,
symbols: HashMap<String, *const u8>,
libcall_names: Box<dyn Fn(ir::LibCall) -> String>,
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.
#[derive(Clone)]
struct RelocRecord {
offset: CodeOffset,
reloc: Reloc,
@@ -141,26 +150,74 @@ struct StackMapRecord {
stack_map: StackMap,
}
#[derive(Clone)]
pub struct SimpleJITCompiledFunction {
code: *mut u8,
size: usize,
relocs: Vec<RelocRecord>,
}
#[derive(Clone)]
pub struct SimpleJITCompiledData {
storage: *mut u8,
size: usize,
relocs: Vec<RelocRecord>,
}
/// A handle to allow freeing memory allocated by the `Backend`.
pub struct SimpleJITMemoryHandle {
/// A handle to allow freeing memory allocated by the `Module`.
struct SimpleJITMemoryHandle {
code: Memory,
readonly: 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 {
match self.symbols.get(name) {
Some(&ptr) => ptr,
@@ -168,24 +225,22 @@ impl SimpleJITBackend {
}
}
fn get_definition(
&self,
namespace: &ModuleNamespace<Self>,
name: &ir::ExternalName,
) -> *const u8 {
fn get_definition(&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 {
if self.declarations.is_function(name) {
let func_id = self.declarations.get_function_id(name);
match &self.functions[func_id] {
Some(compiled) => compiled.code,
None => self.lookup_symbol(name_str),
None => {
self.lookup_symbol(&self.declarations.get_function_decl(func_id).name)
}
}
} else {
let (def, name_str, _writable) = namespace.get_data_definition(&name);
match def {
let data_id = self.declarations.get_data_id(name);
match &self.data_objects[data_id] {
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);
}
}
}
impl<'simple_jit_backend> Backend for SimpleJITBackend {
type Builder = SimpleJITBuilder;
fn finalize_function(&mut self, id: FuncId) {
use std::ptr::write_unaligned;
/// SimpleJIT compiled function and data objects may have outstanding
/// relocations that need to be performed before the memory can be used.
/// These relocations are performed within `finalize_function` and
/// `finalize_data`.
type CompiledFunction = SimpleJITCompiledFunction;
type CompiledData = SimpleJITCompiledData;
let func = self.functions[id]
.as_ref()
.expect("function must be compiled before it can be finalized");
/// SimpleJIT emits code and data into memory, and provides raw pointers
/// to them. They are valid for the remainder of the program's life, unless
/// [`free_memory`] is used.
///
/// [`free_memory`]: #method.free_memory
type FinalizedFunction = *const u8;
type FinalizedData = (*mut u8, usize);
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(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
/// doesn't need to provide anything after the `Module` is complete.
/// The handle object that is returned can optionally be used to free
/// allocated memory if required.
type Product = SimpleJITMemoryHandle;
fn finalize_data(&mut self, id: DataId) {
use std::ptr::write_unaligned;
/// Create a new `SimpleJITBackend`.
fn new(builder: SimpleJITBuilder) -> Self {
let data = self.data_objects[id]
.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 {
code: Memory::new(),
readonly: Memory::new(),
@@ -252,42 +373,75 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
symbols: builder.symbols,
libcall_names: builder.libcall_names,
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 {
&*self.isa
}
fn declare_function(&mut self, _id: FuncId, _name: &str, _linkage: Linkage) {
// Nothing to do.
fn declarations(&self) -> &ModuleDeclarations {
&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(
&mut self,
_id: DataId,
_name: &str,
_linkage: Linkage,
_writable: bool,
name: &str,
linkage: Linkage,
writable: bool,
tls: bool,
_align: Option<u8>,
) {
) -> ModuleResult<DataId> {
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>(
&mut self,
_id: FuncId,
name: &str,
ctx: &cranelift_codegen::Context,
_namespace: &ModuleNamespace<Self>,
code_size: u32,
id: FuncId,
ctx: &mut cranelift_codegen::Context,
trap_sink: &mut TS,
) -> ModuleResult<Self::CompiledFunction>
) -> ModuleResult<ModuleCompiledFunction>
where
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 ptr = self
.memory
@@ -295,7 +449,7 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
.allocate(size, EXECUTABLE_DATA_ALIGNMENT)
.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 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,
size,
relocs: reloc_sink.relocs,
})
});
Ok(ModuleCompiledFunction { size: code_size })
}
fn define_function_bytes(
&mut self,
_id: FuncId,
name: &str,
id: FuncId,
bytes: &[u8],
_namespace: &ModuleNamespace<Self>,
) -> ModuleResult<Self::CompiledFunction> {
) -> ModuleResult<ModuleCompiledFunction> {
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 ptr = self
.memory
@@ -330,30 +499,34 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
.allocate(size, EXECUTABLE_DATA_ALIGNMENT)
.expect("TODO: handle OOM etc.");
self.record_function_for_perf(ptr, size, name);
self.record_function_for_perf(ptr, size, &decl.name);
unsafe {
ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, size);
}
Ok(Self::CompiledFunction {
self.functions[id] = Some(SimpleJITCompiledFunction {
code: ptr,
size,
relocs: vec![],
})
});
Ok(ModuleCompiledFunction { size: total_size })
}
fn define_data(
&mut self,
_id: DataId,
_name: &str,
writable: bool,
tls: bool,
align: Option<u8>,
data: &DataContext,
_namespace: &ModuleNamespace<Self>,
) -> ModuleResult<Self::CompiledData> {
assert!(!tls, "SimpleJIT doesn't yet support TLS");
fn define_data(&mut self, id: DataId, data: &DataContext) -> ModuleResult<()> {
let decl = self.declarations.get_data_decl(id);
if !decl.linkage.is_definable() {
return Err(ModuleError::InvalidImportDefinition(decl.name.clone()));
}
if !self.data_objects[id].is_none() {
return Err(ModuleError::DuplicateDefinition(decl.name.to_owned()));
}
assert!(!decl.tls, "SimpleJIT doesn't yet support TLS");
self.data_objects_to_finalize.push(id);
let &DataDescription {
ref init,
@@ -362,10 +535,11 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
ref function_relocs,
ref data_relocs,
custom_segment_section: _,
align,
} = data.description();
let size = init.size();
let storage = if writable {
let storage = if decl.writable {
self.memory
.writable
.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,
size,
relocs,
})
}
});
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();
Ok(())
}
}
impl SimpleJITModule {
/// SimpleJIT emits code and data into memory as it processes them. This
/// method performs no additional processing, but returns a handle which
/// 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
/// handle is not required.
fn finish(self, _namespace: &ModuleNamespace<Self>) -> Self::Product {
self.memory
pub fn finish(mut self) -> SimpleJITProduct {
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 {
pub relocs: Vec<RelocRecord>,
}

View File

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

View File

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

View File

@@ -9,8 +9,8 @@ use cranelift_simplejit::*;
#[test]
fn error_on_incompatible_sig_in_declare_function() {
let mut module: Module<SimpleJITBackend> =
Module::new(SimpleJITBuilder::new(default_libcall_names()));
let mut module: SimpleJITModule =
SimpleJITModule::new(SimpleJITBuilder::new(default_libcall_names()));
let mut sig = Signature {
params: vec![AbiParam::new(types::I64)],
returns: vec![],
@@ -26,7 +26,7 @@ fn error_on_incompatible_sig_in_declare_function() {
.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 {
params: vec![],
returns: vec![],
@@ -55,27 +55,13 @@ fn define_simple_function(module: &mut Module<SimpleJITBackend>) -> FuncId {
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]
#[should_panic(expected = "Result::unwrap()` on an `Err` value: DuplicateDefinition(\"abc\")")]
fn panic_on_define_after_finalize() {
let mut module: Module<SimpleJITBackend> =
Module::new(SimpleJITBuilder::new(default_libcall_names()));
let mut module: SimpleJITModule =
SimpleJITModule::new(SimpleJITBuilder::new(default_libcall_names()));
define_simple_function(&mut module);
module.finalize_definitions();
define_simple_function(&mut module);
}
@@ -154,8 +140,8 @@ fn switch_error() {
#[test]
fn libcall_function() {
let mut module: Module<SimpleJITBackend> =
Module::new(SimpleJITBuilder::new(default_libcall_names()));
let mut module: SimpleJITModule =
SimpleJITModule::new(SimpleJITBuilder::new(default_libcall_names()));
let sig = Signature {
params: vec![],
@@ -200,5 +186,5 @@ fn libcall_function() {
.define_function(func_id, &mut ctx, &mut trap_sink)
.unwrap();
module.finalize_definitions();
module.finish();
}