| //! Defines `Module` and related types. |
| |
| // TODO: Should `ir::Function` really have a `name`? |
| |
| // TODO: Factor out `ir::Function`'s `ext_funcs` and `global_values` into a struct |
| // shared with `DataDescription`? |
| |
| use super::HashMap; |
| use crate::data_context::DataDescription; |
| use core::fmt::Display; |
| use cranelift_codegen::binemit::{CodeOffset, Reloc}; |
| use cranelift_codegen::entity::{entity_impl, PrimaryMap}; |
| use cranelift_codegen::ir::function::{Function, VersionMarker}; |
| use cranelift_codegen::ir::ExternalName; |
| use cranelift_codegen::settings::SetError; |
| use cranelift_codegen::{ |
| ir, isa, CodegenError, CompileError, Context, FinalizedMachReloc, FinalizedRelocTarget, |
| }; |
| use cranelift_control::ControlPlane; |
| use std::borrow::{Cow, ToOwned}; |
| use std::string::String; |
| |
| /// A module relocation. |
| #[derive(Clone)] |
| pub struct ModuleReloc { |
| /// The offset at which the relocation applies, *relative to the |
| /// containing section*. |
| pub offset: CodeOffset, |
| /// The kind of relocation. |
| pub kind: Reloc, |
| /// The external symbol / name to which this relocation refers. |
| pub name: ModuleRelocTarget, |
| /// The addend to add to the symbol value. |
| pub addend: i64, |
| } |
| |
| impl ModuleReloc { |
| /// Converts a `FinalizedMachReloc` produced from a `Function` into a `ModuleReloc`. |
| pub fn from_mach_reloc( |
| mach_reloc: &FinalizedMachReloc, |
| func: &Function, |
| func_id: FuncId, |
| ) -> Self { |
| let name = match mach_reloc.target { |
| FinalizedRelocTarget::ExternalName(ExternalName::User(reff)) => { |
| let name = &func.params.user_named_funcs()[reff]; |
| ModuleRelocTarget::user(name.namespace, name.index) |
| } |
| FinalizedRelocTarget::ExternalName(ExternalName::TestCase(_)) => unimplemented!(), |
| FinalizedRelocTarget::ExternalName(ExternalName::LibCall(libcall)) => { |
| ModuleRelocTarget::LibCall(libcall) |
| } |
| FinalizedRelocTarget::ExternalName(ExternalName::KnownSymbol(ks)) => { |
| ModuleRelocTarget::KnownSymbol(ks) |
| } |
| FinalizedRelocTarget::Func(offset) => { |
| ModuleRelocTarget::FunctionOffset(func_id, offset) |
| } |
| }; |
| Self { |
| offset: mach_reloc.offset, |
| kind: mach_reloc.kind, |
| name, |
| addend: mach_reloc.addend, |
| } |
| } |
| } |
| |
| /// A function identifier for use in the `Module` interface. |
| #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] |
| #[cfg_attr( |
| feature = "enable-serde", |
| derive(serde_derive::Serialize, serde_derive::Deserialize) |
| )] |
| pub struct FuncId(u32); |
| entity_impl!(FuncId, "funcid"); |
| |
| /// Function identifiers are namespace 0 in `ir::ExternalName` |
| impl From<FuncId> for ModuleRelocTarget { |
| fn from(id: FuncId) -> Self { |
| Self::User { |
| namespace: 0, |
| index: id.0, |
| } |
| } |
| } |
| |
| impl FuncId { |
| /// Get the `FuncId` for the function named by `name`. |
| pub fn from_name(name: &ModuleRelocTarget) -> FuncId { |
| if let ModuleRelocTarget::User { namespace, index } = name { |
| debug_assert_eq!(*namespace, 0); |
| FuncId::from_u32(*index) |
| } else { |
| panic!("unexpected name in DataId::from_name") |
| } |
| } |
| } |
| |
| /// A data object identifier for use in the `Module` interface. |
| #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] |
| #[cfg_attr( |
| feature = "enable-serde", |
| derive(serde_derive::Serialize, serde_derive::Deserialize) |
| )] |
| pub struct DataId(u32); |
| entity_impl!(DataId, "dataid"); |
| |
| /// Data identifiers are namespace 1 in `ir::ExternalName` |
| impl From<DataId> for ModuleRelocTarget { |
| fn from(id: DataId) -> Self { |
| Self::User { |
| namespace: 1, |
| index: id.0, |
| } |
| } |
| } |
| |
| impl DataId { |
| /// Get the `DataId` for the data object named by `name`. |
| pub fn from_name(name: &ModuleRelocTarget) -> DataId { |
| if let ModuleRelocTarget::User { namespace, index } = name { |
| debug_assert_eq!(*namespace, 1); |
| DataId::from_u32(*index) |
| } else { |
| panic!("unexpected name in DataId::from_name") |
| } |
| } |
| } |
| |
| /// Linkage refers to where an entity is defined and who can see it. |
| #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
| #[cfg_attr( |
| feature = "enable-serde", |
| derive(serde_derive::Serialize, serde_derive::Deserialize) |
| )] |
| pub enum Linkage { |
| /// Defined outside of a module. |
| Import, |
| /// Defined inside the module, but not visible outside it. |
| Local, |
| /// Defined inside the module, visible outside it, and may be preempted. |
| Preemptible, |
| /// Defined inside the module, visible inside the current static linkage unit, but not outside. |
| /// |
| /// A static linkage unit is the combination of all object files passed to a linker to create |
| /// an executable or dynamic library. |
| Hidden, |
| /// Defined inside the module, and visible outside it. |
| Export, |
| } |
| |
| impl Linkage { |
| fn merge(a: Self, b: Self) -> Self { |
| match a { |
| Self::Export => Self::Export, |
| Self::Hidden => match b { |
| Self::Export => Self::Export, |
| Self::Preemptible => Self::Preemptible, |
| _ => Self::Hidden, |
| }, |
| Self::Preemptible => match b { |
| Self::Export => Self::Export, |
| _ => Self::Preemptible, |
| }, |
| Self::Local => match b { |
| Self::Export => Self::Export, |
| Self::Hidden => Self::Hidden, |
| Self::Preemptible => Self::Preemptible, |
| Self::Local | Self::Import => Self::Local, |
| }, |
| Self::Import => b, |
| } |
| } |
| |
| /// Test whether this linkage can have a definition. |
| pub fn is_definable(self) -> bool { |
| match self { |
| Self::Import => false, |
| Self::Local | Self::Preemptible | Self::Hidden | Self::Export => true, |
| } |
| } |
| |
| /// Test whether this linkage will have a definition that cannot be preempted. |
| pub fn is_final(self) -> bool { |
| match self { |
| Self::Import | Self::Preemptible => false, |
| Self::Local | Self::Hidden | Self::Export => true, |
| } |
| } |
| } |
| |
| /// A declared name may refer to either a function or data declaration |
| #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] |
| #[cfg_attr( |
| feature = "enable-serde", |
| derive(serde_derive::Serialize, serde_derive::Deserialize) |
| )] |
| pub enum FuncOrDataId { |
| /// When it's a FuncId |
| Func(FuncId), |
| /// When it's a DataId |
| Data(DataId), |
| } |
| |
| /// Mapping to `ModuleExtName` is trivial based on the `FuncId` and `DataId` mapping. |
| impl From<FuncOrDataId> for ModuleRelocTarget { |
| fn from(id: FuncOrDataId) -> Self { |
| match id { |
| FuncOrDataId::Func(funcid) => Self::from(funcid), |
| FuncOrDataId::Data(dataid) => Self::from(dataid), |
| } |
| } |
| } |
| |
| /// Information about a function which can be called. |
| #[derive(Debug)] |
| #[cfg_attr( |
| feature = "enable-serde", |
| derive(serde_derive::Serialize, serde_derive::Deserialize) |
| )] |
| pub struct FunctionDeclaration { |
| #[allow(missing_docs)] |
| pub name: Option<String>, |
| #[allow(missing_docs)] |
| pub linkage: Linkage, |
| #[allow(missing_docs)] |
| pub signature: ir::Signature, |
| } |
| |
| impl FunctionDeclaration { |
| /// The linkage name of the function. |
| /// |
| /// Synthesized from the given function id if it is an anonymous function. |
| pub fn linkage_name(&self, id: FuncId) -> Cow<'_, str> { |
| match &self.name { |
| Some(name) => Cow::Borrowed(name), |
| // Symbols starting with .L are completely omitted from the symbol table after linking. |
| // Using hexadecimal instead of decimal for slightly smaller symbol names and often |
| // slightly faster linking. |
| None => Cow::Owned(format!(".Lfn{:x}", id.as_u32())), |
| } |
| } |
| |
| fn merge( |
| &mut self, |
| id: FuncId, |
| linkage: Linkage, |
| sig: &ir::Signature, |
| ) -> Result<(), ModuleError> { |
| self.linkage = Linkage::merge(self.linkage, linkage); |
| if &self.signature != sig { |
| return Err(ModuleError::IncompatibleSignature( |
| self.linkage_name(id).into_owned(), |
| self.signature.clone(), |
| sig.clone(), |
| )); |
| } |
| Ok(()) |
| } |
| } |
| |
| /// Error messages for all `Module` methods |
| #[derive(Debug)] |
| pub enum ModuleError { |
| /// Indicates an identifier was used before it was declared |
| Undeclared(String), |
| |
| /// Indicates an identifier was used as data/function first, but then used as the other |
| IncompatibleDeclaration(String), |
| |
| /// Indicates a function identifier was declared with a |
| /// different signature than declared previously |
| IncompatibleSignature(String, ir::Signature, ir::Signature), |
| |
| /// Indicates an identifier was defined more than once |
| DuplicateDefinition(String), |
| |
| /// Indicates an identifier was defined, but was declared as an import |
| InvalidImportDefinition(String), |
| |
| /// Wraps a `cranelift-codegen` error |
| Compilation(CodegenError), |
| |
| /// Memory allocation failure from a backend |
| Allocation { |
| /// Tell where the allocation came from |
| message: &'static str, |
| /// Io error the allocation failed with |
| err: std::io::Error, |
| }, |
| |
| /// Wraps a generic error from a backend |
| Backend(anyhow::Error), |
| |
| /// Wraps an error from a flag definition. |
| Flag(SetError), |
| } |
| |
| impl<'a> From<CompileError<'a>> for ModuleError { |
| fn from(err: CompileError<'a>) -> Self { |
| Self::Compilation(err.inner) |
| } |
| } |
| |
| // This is manually implementing Error and Display instead of using thiserror to reduce the amount |
| // of dependencies used by Cranelift. |
| impl std::error::Error for ModuleError { |
| fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { |
| match self { |
| Self::Undeclared { .. } |
| | Self::IncompatibleDeclaration { .. } |
| | Self::IncompatibleSignature { .. } |
| | Self::DuplicateDefinition { .. } |
| | Self::InvalidImportDefinition { .. } => None, |
| Self::Compilation(source) => Some(source), |
| Self::Allocation { err: source, .. } => Some(source), |
| Self::Backend(source) => Some(&**source), |
| Self::Flag(source) => Some(source), |
| } |
| } |
| } |
| |
| impl std::fmt::Display for ModuleError { |
| fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |
| match self { |
| Self::Undeclared(name) => { |
| write!(f, "Undeclared identifier: {}", name) |
| } |
| Self::IncompatibleDeclaration(name) => { |
| write!(f, "Incompatible declaration of identifier: {}", name,) |
| } |
| Self::IncompatibleSignature(name, prev_sig, new_sig) => { |
| write!( |
| f, |
| "Function {} signature {:?} is incompatible with previous declaration {:?}", |
| name, new_sig, prev_sig, |
| ) |
| } |
| Self::DuplicateDefinition(name) => { |
| write!(f, "Duplicate definition of identifier: {}", name) |
| } |
| Self::InvalidImportDefinition(name) => { |
| write!( |
| f, |
| "Invalid to define identifier declared as an import: {}", |
| name, |
| ) |
| } |
| Self::Compilation(err) => { |
| write!(f, "Compilation error: {}", err) |
| } |
| Self::Allocation { message, err } => { |
| write!(f, "Allocation error: {}: {}", message, err) |
| } |
| Self::Backend(err) => write!(f, "Backend error: {}", err), |
| Self::Flag(err) => write!(f, "Flag error: {}", err), |
| } |
| } |
| } |
| |
| impl std::convert::From<CodegenError> for ModuleError { |
| fn from(source: CodegenError) -> Self { |
| Self::Compilation { 0: source } |
| } |
| } |
| |
| impl std::convert::From<SetError> for ModuleError { |
| fn from(source: SetError) -> Self { |
| Self::Flag { 0: source } |
| } |
| } |
| |
| /// A convenient alias for a `Result` that uses `ModuleError` as the error type. |
| pub type ModuleResult<T> = Result<T, ModuleError>; |
| |
| /// Information about a data object which can be accessed. |
| #[derive(Debug)] |
| #[cfg_attr( |
| feature = "enable-serde", |
| derive(serde_derive::Serialize, serde_derive::Deserialize) |
| )] |
| pub struct DataDeclaration { |
| #[allow(missing_docs)] |
| pub name: Option<String>, |
| #[allow(missing_docs)] |
| pub linkage: Linkage, |
| #[allow(missing_docs)] |
| pub writable: bool, |
| #[allow(missing_docs)] |
| pub tls: bool, |
| } |
| |
| impl DataDeclaration { |
| /// The linkage name of the data object. |
| /// |
| /// Synthesized from the given data id if it is an anonymous function. |
| pub fn linkage_name(&self, id: DataId) -> Cow<'_, str> { |
| match &self.name { |
| Some(name) => Cow::Borrowed(name), |
| // Symbols starting with .L are completely omitted from the symbol table after linking. |
| // Using hexadecimal instead of decimal for slightly smaller symbol names and often |
| // slightly faster linking. |
| None => Cow::Owned(format!(".Ldata{:x}", id.as_u32())), |
| } |
| } |
| |
| 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.tls, tls, |
| "Can't change TLS data object to normal or in the opposite way", |
| ); |
| } |
| } |
| |
| /// A translated `ExternalName` into something global we can handle. |
| #[derive(Clone, Debug)] |
| #[cfg_attr( |
| feature = "enable-serde", |
| derive(serde_derive::Serialize, serde_derive::Deserialize) |
| )] |
| pub enum ModuleRelocTarget { |
| /// User defined function, converted from `ExternalName::User`. |
| User { |
| /// Arbitrary. |
| namespace: u32, |
| /// Arbitrary. |
| index: u32, |
| }, |
| /// Call into a library function. |
| LibCall(ir::LibCall), |
| /// Symbols known to the linker. |
| KnownSymbol(ir::KnownSymbol), |
| /// A offset inside a function |
| FunctionOffset(FuncId, CodeOffset), |
| } |
| |
| impl ModuleRelocTarget { |
| /// Creates a user-defined external name. |
| pub fn user(namespace: u32, index: u32) -> Self { |
| Self::User { namespace, index } |
| } |
| } |
| |
| impl Display for ModuleRelocTarget { |
| fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
| match self { |
| Self::User { namespace, index } => write!(f, "u{}:{}", namespace, index), |
| Self::LibCall(lc) => write!(f, "%{}", lc), |
| Self::KnownSymbol(ks) => write!(f, "{}", ks), |
| Self::FunctionOffset(fname, offset) => write!(f, "{fname}+{offset}"), |
| } |
| } |
| } |
| |
| /// 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(Debug, Default)] |
| pub struct ModuleDeclarations { |
| /// A version marker used to ensure that serialized clif ir is never deserialized with a |
| /// different version of Cranelift. |
| // Note: This must be the first field to ensure that Serde will deserialize it before |
| // attempting to deserialize other fields that are potentially changed between versions. |
| _version_marker: VersionMarker, |
| |
| names: HashMap<String, FuncOrDataId>, |
| functions: PrimaryMap<FuncId, FunctionDeclaration>, |
| data_objects: PrimaryMap<DataId, DataDeclaration>, |
| } |
| |
| #[cfg(feature = "enable-serde")] |
| mod serialize { |
| // This is manually implementing Serialize and Deserialize to avoid serializing the names field, |
| // which can be entirely reconstructed from the functions and data_objects fields, saving space. |
| |
| use super::*; |
| |
| use serde::de::{Deserialize, Deserializer, Error, MapAccess, SeqAccess, Unexpected, Visitor}; |
| use serde::ser::{Serialize, SerializeStruct, Serializer}; |
| use std::fmt; |
| |
| fn get_names<E: Error>( |
| functions: &PrimaryMap<FuncId, FunctionDeclaration>, |
| data_objects: &PrimaryMap<DataId, DataDeclaration>, |
| ) -> Result<HashMap<String, FuncOrDataId>, E> { |
| let mut names = HashMap::new(); |
| for (func_id, decl) in functions.iter() { |
| if let Some(name) = &decl.name { |
| let old = names.insert(name.clone(), FuncOrDataId::Func(func_id)); |
| if old.is_some() { |
| return Err(E::invalid_value( |
| Unexpected::Other("duplicate name"), |
| &"FunctionDeclaration's with no duplicate names", |
| )); |
| } |
| } |
| } |
| for (data_id, decl) in data_objects.iter() { |
| if let Some(name) = &decl.name { |
| let old = names.insert(name.clone(), FuncOrDataId::Data(data_id)); |
| if old.is_some() { |
| return Err(E::invalid_value( |
| Unexpected::Other("duplicate name"), |
| &"DataDeclaration's with no duplicate names", |
| )); |
| } |
| } |
| } |
| Ok(names) |
| } |
| |
| impl Serialize for ModuleDeclarations { |
| fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> { |
| let ModuleDeclarations { |
| _version_marker, |
| functions, |
| data_objects, |
| names: _, |
| } = self; |
| |
| let mut state = s.serialize_struct("ModuleDeclarations", 4)?; |
| state.serialize_field("_version_marker", _version_marker)?; |
| state.serialize_field("functions", functions)?; |
| state.serialize_field("data_objects", data_objects)?; |
| state.end() |
| } |
| } |
| |
| enum ModuleDeclarationsField { |
| VersionMarker, |
| Functions, |
| DataObjects, |
| Ignore, |
| } |
| |
| struct ModuleDeclarationsFieldVisitor; |
| |
| impl<'de> serde::de::Visitor<'de> for ModuleDeclarationsFieldVisitor { |
| type Value = ModuleDeclarationsField; |
| |
| fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| f.write_str("field identifier") |
| } |
| |
| fn visit_u64<E: Error>(self, val: u64) -> Result<Self::Value, E> { |
| match val { |
| 0u64 => Ok(ModuleDeclarationsField::VersionMarker), |
| 1u64 => Ok(ModuleDeclarationsField::Functions), |
| 2u64 => Ok(ModuleDeclarationsField::DataObjects), |
| _ => Ok(ModuleDeclarationsField::Ignore), |
| } |
| } |
| |
| fn visit_str<E: Error>(self, val: &str) -> Result<Self::Value, E> { |
| match val { |
| "_version_marker" => Ok(ModuleDeclarationsField::VersionMarker), |
| "functions" => Ok(ModuleDeclarationsField::Functions), |
| "data_objects" => Ok(ModuleDeclarationsField::DataObjects), |
| _ => Ok(ModuleDeclarationsField::Ignore), |
| } |
| } |
| |
| fn visit_bytes<E: Error>(self, val: &[u8]) -> Result<Self::Value, E> { |
| match val { |
| b"_version_marker" => Ok(ModuleDeclarationsField::VersionMarker), |
| b"functions" => Ok(ModuleDeclarationsField::Functions), |
| b"data_objects" => Ok(ModuleDeclarationsField::DataObjects), |
| _ => Ok(ModuleDeclarationsField::Ignore), |
| } |
| } |
| } |
| |
| impl<'de> Deserialize<'de> for ModuleDeclarationsField { |
| #[inline] |
| fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> { |
| d.deserialize_identifier(ModuleDeclarationsFieldVisitor) |
| } |
| } |
| |
| struct ModuleDeclarationsVisitor; |
| |
| impl<'de> Visitor<'de> for ModuleDeclarationsVisitor { |
| type Value = ModuleDeclarations; |
| |
| fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| f.write_str("struct ModuleDeclarations") |
| } |
| |
| #[inline] |
| fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> { |
| let _version_marker = match seq.next_element()? { |
| Some(val) => val, |
| None => { |
| return Err(Error::invalid_length( |
| 0usize, |
| &"struct ModuleDeclarations with 4 elements", |
| )); |
| } |
| }; |
| let functions = match seq.next_element()? { |
| Some(val) => val, |
| None => { |
| return Err(Error::invalid_length( |
| 2usize, |
| &"struct ModuleDeclarations with 4 elements", |
| )); |
| } |
| }; |
| let data_objects = match seq.next_element()? { |
| Some(val) => val, |
| None => { |
| return Err(Error::invalid_length( |
| 3usize, |
| &"struct ModuleDeclarations with 4 elements", |
| )); |
| } |
| }; |
| let names = get_names(&functions, &data_objects)?; |
| Ok(ModuleDeclarations { |
| _version_marker, |
| names, |
| functions, |
| data_objects, |
| }) |
| } |
| |
| #[inline] |
| fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> { |
| let mut _version_marker: Option<VersionMarker> = None; |
| let mut functions: Option<PrimaryMap<FuncId, FunctionDeclaration>> = None; |
| let mut data_objects: Option<PrimaryMap<DataId, DataDeclaration>> = None; |
| while let Some(key) = map.next_key::<ModuleDeclarationsField>()? { |
| match key { |
| ModuleDeclarationsField::VersionMarker => { |
| if _version_marker.is_some() { |
| return Err(Error::duplicate_field("_version_marker")); |
| } |
| _version_marker = Some(map.next_value()?); |
| } |
| ModuleDeclarationsField::Functions => { |
| if functions.is_some() { |
| return Err(Error::duplicate_field("functions")); |
| } |
| functions = Some(map.next_value()?); |
| } |
| ModuleDeclarationsField::DataObjects => { |
| if data_objects.is_some() { |
| return Err(Error::duplicate_field("data_objects")); |
| } |
| data_objects = Some(map.next_value()?); |
| } |
| _ => { |
| map.next_value::<serde::de::IgnoredAny>()?; |
| } |
| } |
| } |
| let _version_marker = match _version_marker { |
| Some(_version_marker) => _version_marker, |
| None => return Err(Error::missing_field("_version_marker")), |
| }; |
| let functions = match functions { |
| Some(functions) => functions, |
| None => return Err(Error::missing_field("functions")), |
| }; |
| let data_objects = match data_objects { |
| Some(data_objects) => data_objects, |
| None => return Err(Error::missing_field("data_objects")), |
| }; |
| let names = get_names(&functions, &data_objects)?; |
| Ok(ModuleDeclarations { |
| _version_marker, |
| names, |
| functions, |
| data_objects, |
| }) |
| } |
| } |
| |
| impl<'de> Deserialize<'de> for ModuleDeclarations { |
| fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> { |
| d.deserialize_struct( |
| "ModuleDeclarations", |
| &["_version_marker", "functions", "data_objects"], |
| ModuleDeclarationsVisitor, |
| ) |
| } |
| } |
| } |
| |
| 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() |
| } |
| |
| /// Return whether `name` names a function, rather than a data object. |
| pub fn is_function(name: &ModuleRelocTarget) -> bool { |
| match name { |
| ModuleRelocTarget::User { namespace, .. } => *namespace == 0, |
| ModuleRelocTarget::LibCall(_) |
| | ModuleRelocTarget::KnownSymbol(_) |
| | ModuleRelocTarget::FunctionOffset(..) => { |
| panic!("unexpected module ext name") |
| } |
| } |
| } |
| |
| /// Get the `FunctionDeclaration` for the function named by `name`. |
| 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, data_id: DataId) -> &DataDeclaration { |
| &self.data_objects[data_id] |
| } |
| |
| /// Declare a function in this module. |
| pub fn declare_function( |
| &mut self, |
| name: &str, |
| linkage: Linkage, |
| signature: &ir::Signature, |
| ) -> ModuleResult<(FuncId, Linkage)> { |
| // 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.functions[id]; |
| existing.merge(id, linkage, signature)?; |
| Ok((id, existing.linkage)) |
| } |
| FuncOrDataId::Data(..) => { |
| Err(ModuleError::IncompatibleDeclaration(name.to_owned())) |
| } |
| }, |
| Vacant(entry) => { |
| let id = self.functions.push(FunctionDeclaration { |
| name: Some(name.to_owned()), |
| linkage, |
| signature: signature.clone(), |
| }); |
| entry.insert(FuncOrDataId::Func(id)); |
| Ok((id, self.functions[id].linkage)) |
| } |
| } |
| } |
| |
| /// Declare an anonymous function in this module. |
| pub fn declare_anonymous_function( |
| &mut self, |
| signature: &ir::Signature, |
| ) -> ModuleResult<FuncId> { |
| let id = self.functions.push(FunctionDeclaration { |
| name: None, |
| linkage: Linkage::Local, |
| signature: signature.clone(), |
| }); |
| Ok(id) |
| } |
| |
| /// Declare a data object in this module. |
| pub fn declare_data( |
| &mut self, |
| name: &str, |
| linkage: Linkage, |
| writable: bool, |
| tls: bool, |
| ) -> ModuleResult<(DataId, Linkage)> { |
| // 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.data_objects[id]; |
| existing.merge(linkage, writable, tls); |
| Ok((id, existing.linkage)) |
| } |
| |
| FuncOrDataId::Func(..) => { |
| Err(ModuleError::IncompatibleDeclaration(name.to_owned())) |
| } |
| }, |
| Vacant(entry) => { |
| let id = self.data_objects.push(DataDeclaration { |
| name: Some(name.to_owned()), |
| linkage, |
| writable, |
| tls, |
| }); |
| entry.insert(FuncOrDataId::Data(id)); |
| Ok((id, self.data_objects[id].linkage)) |
| } |
| } |
| } |
| |
| /// Declare an anonymous data object in this module. |
| pub fn declare_anonymous_data(&mut self, writable: bool, tls: bool) -> ModuleResult<DataId> { |
| let id = self.data_objects.push(DataDeclaration { |
| name: None, |
| linkage: Linkage::Local, |
| writable, |
| tls, |
| }); |
| Ok(id) |
| } |
| } |
| |
| /// 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 an anonymous function in this module. |
| fn declare_anonymous_function(&mut self, 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>; |
| |
| /// Declare an anonymous data object in this module. |
| fn declare_anonymous_data(&mut self, 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. |
| fn declare_func_in_func(&mut self, func_id: FuncId, func: &mut ir::Function) -> ir::FuncRef { |
| let decl = &self.declarations().functions[func_id]; |
| let signature = func.import_signature(decl.signature.clone()); |
| let user_name_ref = func.declare_imported_user_function(ir::UserExternalName { |
| namespace: 0, |
| index: func_id.as_u32(), |
| }); |
| let colocated = decl.linkage.is_final(); |
| func.import_function(ir::ExtFuncData { |
| name: ir::ExternalName::user(user_name_ref), |
| signature, |
| colocated, |
| }) |
| } |
| |
| /// Use this when you're building the IR of a function to reference a data object. |
| /// |
| /// TODO: Same as above. |
| 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(); |
| let user_name_ref = func.declare_imported_user_function(ir::UserExternalName { |
| namespace: 1, |
| index: data.as_u32(), |
| }); |
| func.create_global_value(ir::GlobalValueData::Symbol { |
| name: ir::ExternalName::user(user_name_ref), |
| offset: ir::immediates::Imm64::new(0), |
| colocated, |
| tls: decl.tls, |
| }) |
| } |
| |
| /// TODO: Same as above. |
| fn declare_func_in_data(&self, func_id: FuncId, data: &mut DataDescription) -> ir::FuncRef { |
| data.import_function(ModuleRelocTarget::user(0, func_id.as_u32())) |
| } |
| |
| /// TODO: Same as above. |
| fn declare_data_in_data(&self, data_id: DataId, data: &mut DataDescription) -> ir::GlobalValue { |
| data.import_global_value(ModuleRelocTarget::user(1, data_id.as_u32())) |
| } |
| |
| /// Define a function, producing the function body from the given `Context`. |
| /// |
| /// Returns the size of the function's code and constant data. |
| /// |
| /// Unlike [`define_function_with_control_plane`] this uses a default [`ControlPlane`] for |
| /// convenience. |
| /// |
| /// Note: After calling this function the given `Context` will contain the compiled function. |
| /// |
| /// [`define_function_with_control_plane`]: Self::define_function_with_control_plane |
| fn define_function(&mut self, func: FuncId, ctx: &mut Context) -> ModuleResult<()> { |
| self.define_function_with_control_plane(func, ctx, &mut ControlPlane::default()) |
| } |
| |
| /// Define a function, producing the function body from the given `Context`. |
| /// |
| /// Returns the size of the function's code and constant data. |
| /// |
| /// Note: After calling this function the given `Context` will contain the compiled function. |
| fn define_function_with_control_plane( |
| &mut self, |
| func: FuncId, |
| ctx: &mut Context, |
| ctrl_plane: &mut ControlPlane, |
| ) -> ModuleResult<()>; |
| |
| /// Define a function, taking the function body from the given `bytes`. |
| /// |
| /// This function is generally only useful if you need to precisely specify |
| /// the emitted instructions for some reason; otherwise, you should use |
| /// `define_function`. |
| /// |
| /// Returns the size of the function's code. |
| fn define_function_bytes( |
| &mut self, |
| func_id: FuncId, |
| func: &ir::Function, |
| alignment: u64, |
| bytes: &[u8], |
| relocs: &[FinalizedMachReloc], |
| ) -> ModuleResult<()>; |
| |
| /// Define a data object, producing the data contents from the given `DataContext`. |
| fn define_data(&mut self, data_id: DataId, data: &DataDescription) -> ModuleResult<()>; |
| } |
| |
| impl<M: Module> Module for &mut M { |
| fn isa(&self) -> &dyn isa::TargetIsa { |
| (**self).isa() |
| } |
| |
| fn declarations(&self) -> &ModuleDeclarations { |
| (**self).declarations() |
| } |
| |
| fn get_name(&self, name: &str) -> Option<FuncOrDataId> { |
| (**self).get_name(name) |
| } |
| |
| fn target_config(&self) -> isa::TargetFrontendConfig { |
| (**self).target_config() |
| } |
| |
| fn make_context(&self) -> Context { |
| (**self).make_context() |
| } |
| |
| fn clear_context(&self, ctx: &mut Context) { |
| (**self).clear_context(ctx) |
| } |
| |
| fn make_signature(&self) -> ir::Signature { |
| (**self).make_signature() |
| } |
| |
| fn clear_signature(&self, sig: &mut ir::Signature) { |
| (**self).clear_signature(sig) |
| } |
| |
| fn declare_function( |
| &mut self, |
| name: &str, |
| linkage: Linkage, |
| signature: &ir::Signature, |
| ) -> ModuleResult<FuncId> { |
| (**self).declare_function(name, linkage, signature) |
| } |
| |
| fn declare_anonymous_function(&mut self, signature: &ir::Signature) -> ModuleResult<FuncId> { |
| (**self).declare_anonymous_function(signature) |
| } |
| |
| fn declare_data( |
| &mut self, |
| name: &str, |
| linkage: Linkage, |
| writable: bool, |
| tls: bool, |
| ) -> ModuleResult<DataId> { |
| (**self).declare_data(name, linkage, writable, tls) |
| } |
| |
| fn declare_anonymous_data(&mut self, writable: bool, tls: bool) -> ModuleResult<DataId> { |
| (**self).declare_anonymous_data(writable, tls) |
| } |
| |
| fn declare_func_in_func(&mut self, func: FuncId, in_func: &mut ir::Function) -> ir::FuncRef { |
| (**self).declare_func_in_func(func, in_func) |
| } |
| |
| fn declare_data_in_func(&self, data: DataId, func: &mut ir::Function) -> ir::GlobalValue { |
| (**self).declare_data_in_func(data, func) |
| } |
| |
| fn declare_func_in_data(&self, func_id: FuncId, data: &mut DataDescription) -> ir::FuncRef { |
| (**self).declare_func_in_data(func_id, data) |
| } |
| |
| fn declare_data_in_data(&self, data_id: DataId, data: &mut DataDescription) -> ir::GlobalValue { |
| (**self).declare_data_in_data(data_id, data) |
| } |
| |
| fn define_function(&mut self, func: FuncId, ctx: &mut Context) -> ModuleResult<()> { |
| (**self).define_function(func, ctx) |
| } |
| |
| fn define_function_with_control_plane( |
| &mut self, |
| func: FuncId, |
| ctx: &mut Context, |
| ctrl_plane: &mut ControlPlane, |
| ) -> ModuleResult<()> { |
| (**self).define_function_with_control_plane(func, ctx, ctrl_plane) |
| } |
| |
| fn define_function_bytes( |
| &mut self, |
| func_id: FuncId, |
| func: &ir::Function, |
| alignment: u64, |
| bytes: &[u8], |
| relocs: &[FinalizedMachReloc], |
| ) -> ModuleResult<()> { |
| (**self).define_function_bytes(func_id, func, alignment, bytes, relocs) |
| } |
| |
| fn define_data(&mut self, data_id: DataId, data: &DataDescription) -> ModuleResult<()> { |
| (**self).define_data(data_id, data) |
| } |
| } |