Merge pull request #2249 from bjorn3/module_rework
Rework the interface of cranelift-module
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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<()>;
|
||||
}
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -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) };
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user