diff --git a/Cargo.lock b/Cargo.lock index 388927fd8a..7709f83295 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/cranelift/module/README.md b/cranelift/module/README.md index a357aa637d..1727975f96 100644 --- a/cranelift/module/README.md +++ b/cranelift/module/README.md @@ -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 diff --git a/cranelift/module/src/backend.rs b/cranelift/module/src/backend.rs deleted file mode 100644 index ff1372208b..0000000000 --- a/cranelift/module/src/backend.rs +++ /dev/null @@ -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, - ); - - /// Define a function, producing the function body from the given `Context`. - /// - /// Functions must be declared before being defined. - fn define_function( - &mut self, - id: FuncId, - name: &str, - ctx: &Context, - namespace: &ModuleNamespace, - code_size: u32, - trap_sink: &mut TS, - ) -> ModuleResult - 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, - ) -> ModuleResult; - - /// 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, - data_ctx: &DataContext, - namespace: &ModuleNamespace, - ) -> ModuleResult; - - /// 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::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::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::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 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(), - }) -} diff --git a/cranelift/module/src/data_context.rs b/cranelift/module/src/data_context.rs index bca8471a77..ca490a09e9 100644 --- a/cranelift/module/src/data_context.rs +++ b/cranelift/module/src/data_context.rs @@ -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, } /// 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 diff --git a/cranelift/module/src/lib.rs b/cranelift/module/src/lib.rs index 25a2759be4..9292a9b188 100644 --- a/cranelift/module/src/lib.rs +++ b/cranelift/module/src/lib.rs @@ -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 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(), + }) +} diff --git a/cranelift/module/src/module.rs b/cranelift/module/src/module.rs index 3a6f90fc88..58ddb2aa76 100644 --- a/cranelift/module/src/module.rs +++ b/cranelift/module/src/module.rs @@ -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 = Result; -/// A function belonging to a `Module`. -pub struct ModuleFunction -where - B: Backend, -{ - /// The function declaration. - pub decl: FunctionDeclaration, - /// The compiled artifact, once it's available. - pub compiled: Option, -} - -impl ModuleFunction -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, } -/// A data object belonging to a `Module`. -struct ModuleData -where - B: Backend, -{ - /// The data object declaration. - decl: DataDeclaration, - /// The "compiled" artifact, once it's available. - compiled: Option, -} - -impl ModuleData -where - B: Backend, -{ - fn merge(&mut self, linkage: Linkage, writable: bool, tls: bool, align: Option) { - 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 -where - B: Backend, -{ - functions: PrimaryMap>, - data_objects: PrimaryMap>, +/// 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, + functions: PrimaryMap, + data_objects: PrimaryMap, } -impl ModuleContents -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 { + self.names.get(name).copied() + } + + /// Get an iterator of all function declarations + pub fn get_functions(&self) -> impl Iterator { + 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 { - &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 { - &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, -} - -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 { + 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 -where - B: Backend, -{ - names: HashMap, - contents: ModuleContents, - functions_to_finalize: Vec, - data_objects_to_finalize: Vec, - backend: B, -} - -pub struct ModuleCompiledFunction { - pub size: binemit::CodeOffset, -} - -impl Module -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 { - 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 { + ) -> 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 { - name: name.to_owned(), - linkage, - signature: signature.clone(), - }, - compiled: None, + let id = self.functions.push(FunctionDeclaration { + name: name.to_owned(), + linkage, + signature: signature.clone(), }); 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> { - 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, // An alignment bigger than 128 is unlikely - ) -> ModuleResult { + ) -> 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 { - name: name.to_owned(), - linkage, - writable, - tls, - align, - }, - compiled: None, + let id = self.data_objects.push(DataDeclaration { + name: name.to_owned(), + linkage, + writable, + tls, }); 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 { + 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; + + /// Declare a data object in this module. + fn declare_data( + &mut self, + name: &str, + linkage: Linkage, + writable: bool, + tls: bool, + ) -> ModuleResult; /// 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( + fn define_function( &mut self, func: FuncId, ctx: &mut Context, trap_sink: &mut TS, ) -> ModuleResult 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:: { - 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 { - 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:: { - contents: &self.contents, - }, - )?; - - self.contents.functions[func].compiled = Some(compiled); - self.functions_to_finalize.push(func); - Ok(ModuleCompiledFunction { size: total_size }) - } + ) -> ModuleResult; /// 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:: { - 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:: { - 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:: { - 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:: { - contents: &self.contents, - }) - } + fn define_data(&mut self, data: DataId, data_ctx: &DataContext) -> ModuleResult<()>; } diff --git a/cranelift/object/Cargo.toml b/cranelift/object/Cargo.toml index c39332ce2f..7257652a4b 100644 --- a/cranelift/object/Cargo.toml +++ b/cranelift/object/Cargo.toml @@ -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" } diff --git a/cranelift/object/src/backend.rs b/cranelift/object/src/backend.rs index 3cceda4d88..56008a1cf9 100644 --- a/cranelift/object/src/backend.rs +++ b/cranelift/object/src/backend.rs @@ -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, 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, object: Object, - functions: SecondaryMap>, - data_objects: SecondaryMap>, + declarations: ModuleDeclarations, + functions: SecondaryMap>, + data_objects: SecondaryMap>, relocs: Vec, libcalls: HashMap, libcall_names: Box 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 { + 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, - ) { - let kind = if tls { + ) -> ModuleResult { + 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( &mut self, func_id: FuncId, - _name: &str, - ctx: &cranelift_codegen::Context, - _namespace: &ModuleNamespace, - code_size: u32, + ctx: &mut cranelift_codegen::Context, trap_sink: &mut TS, - ) -> ModuleResult + ) -> ModuleResult 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 = 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, - ) -> ModuleResult { - let symbol = self.functions[func_id].unwrap(); + ) -> ModuleResult { + 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, - data_ctx: &DataContext, - _namespace: &ModuleNamespace, - ) -> ModuleResult { + 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, - ) { - // Nothing to do. - } - - fn get_finalized_function(&self, _func: &ObjectCompiledFunction) { - // Nothing to do. - } - - fn finalize_data( - &mut self, - _id: DataId, - _data: &ObjectCompiledData, - _namespace: &ModuleNamespace, - ) { - // 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) -> 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, - 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>, + pub functions: SecondaryMap>, /// Symbol IDs for data objects (both declared and defined). - pub data_objects: SecondaryMap>, + pub data_objects: SecondaryMap>, } 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. diff --git a/cranelift/object/src/lib.rs b/cranelift/object/src/lib.rs index 0eeaec1484..1b9b677e89 100644 --- a/cranelift/object/src/lib.rs +++ b/cranelift/object/src/lib.rs @@ -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"); diff --git a/cranelift/simplejit/Cargo.toml b/cranelift/simplejit/Cargo.toml index 9fe617bd14..623a56add6 100644 --- a/cranelift/simplejit/Cargo.toml +++ b/cranelift/simplejit/Cargo.toml @@ -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"] } diff --git a/cranelift/simplejit/examples/simplejit-minimal.rs b/cranelift/simplejit/examples/simplejit-minimal.rs index 1534e144de..7c5df36eee 100644 --- a/cranelift/simplejit/examples/simplejit-minimal.rs +++ b/cranelift/simplejit/examples/simplejit-minimal.rs @@ -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 = - 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) }; diff --git a/cranelift/simplejit/src/backend.rs b/cranelift/simplejit/src/backend.rs index 6e21e7f154..369561f06b 100644 --- a/cranelift/simplejit/src/backend.rs +++ b/cranelift/simplejit/src/backend.rs @@ -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, symbols: HashMap, @@ -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, symbols: HashMap, libcall_names: Box String>, memory: SimpleJITMemoryHandle, + declarations: ModuleDeclarations, + functions: SecondaryMap>, + data_objects: SecondaryMap>, + functions_to_finalize: Vec, + data_objects_to_finalize: Vec, } /// 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, } +#[derive(Clone)] pub struct SimpleJITCompiledData { storage: *mut u8, size: usize, relocs: Vec, } -/// 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>, + data_objects: SecondaryMap>, +} + +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 { + 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, - 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 { + 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, - ) { + ) -> ModuleResult { 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( &mut self, - _id: FuncId, - name: &str, - ctx: &cranelift_codegen::Context, - _namespace: &ModuleNamespace, - code_size: u32, + id: FuncId, + ctx: &mut cranelift_codegen::Context, trap_sink: &mut TS, - ) -> ModuleResult + ) -> ModuleResult 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, - ) -> ModuleResult { + ) -> ModuleResult { + 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, - data: &DataContext, - _namespace: &ModuleNamespace, - ) -> ModuleResult { - 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::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::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::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, } diff --git a/cranelift/simplejit/src/lib.rs b/cranelift/simplejit/src/lib.rs index d907964cff..682dcaecb6 100644 --- a/cranelift/simplejit/src/lib.rs +++ b/cranelift/simplejit/src/lib.rs @@ -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"); diff --git a/cranelift/simplejit/src/memory.rs b/cranelift/simplejit/src/memory.rs index 2618233c63..7d0d84311b 100644 --- a/cranelift/simplejit/src/memory.rs +++ b/cranelift/simplejit/src/memory.rs @@ -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 { diff --git a/cranelift/simplejit/tests/basic.rs b/cranelift/simplejit/tests/basic.rs index 31d19fa97a..e7b52417c1 100644 --- a/cranelift/simplejit/tests/basic.rs +++ b/cranelift/simplejit/tests/basic.rs @@ -9,8 +9,8 @@ use cranelift_simplejit::*; #[test] fn error_on_incompatible_sig_in_declare_function() { - let mut module: Module = - 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) -> 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) -> FuncId { func_id } -#[test] -fn double_finalize() { - let mut module: Module = - 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 = - 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 = - 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(); }