//! External names. //! //! These are identifiers for declaring entities defined outside the current //! function. The name of an external declaration doesn't have any meaning to //! Cranelift, which compiles functions independently. use crate::ir::{KnownSymbol, LibCall}; use alloc::boxed::Box; use core::fmt::{self, Write}; use core::str::FromStr; use cranelift_entity::EntityRef as _; #[cfg(feature = "enable-serde")] use serde_derive::{Deserialize, Serialize}; use super::entities::UserExternalNameRef; use super::function::FunctionParameters; /// An explicit name for a user-defined function, be it defined in code or in CLIF text. /// /// This is used both for naming a function (for debugging purposes) and for declaring external /// functions. In the latter case, this becomes an `ExternalName`, which gets embedded in /// relocations later, etc. #[derive(Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub enum UserFuncName { /// A user-defined name, with semantics left to the user. User(UserExternalName), /// A name for a test case, mostly intended for Cranelift testing. Testcase(TestcaseName), } impl UserFuncName { /// Creates a new external name from a sequence of bytes. Caller is expected /// to guarantee bytes are only ascii alphanumeric or `_`. pub fn testcase>(v: T) -> Self { Self::Testcase(TestcaseName::new(v)) } /// Create a new external name from a user-defined external function reference. pub fn user(namespace: u32, index: u32) -> Self { Self::User(UserExternalName::new(namespace, index)) } /// Get a `UserExternalName` if this is a user-defined name. pub fn get_user(&self) -> Option<&UserExternalName> { match self { UserFuncName::User(user) => Some(user), UserFuncName::Testcase(_) => None, } } } impl Default for UserFuncName { fn default() -> Self { UserFuncName::User(UserExternalName::default()) } } impl fmt::Display for UserFuncName { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { UserFuncName::User(user) => user.fmt(f), UserFuncName::Testcase(testcase) => testcase.fmt(f), } } } /// An external name in a user-defined symbol table. /// /// Cranelift does not interpret these numbers in any way, so they can represent arbitrary values. #[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub struct UserExternalName { /// Arbitrary. pub namespace: u32, /// Arbitrary. pub index: u32, } impl UserExternalName { /// Creates a new [UserExternalName]. pub fn new(namespace: u32, index: u32) -> Self { Self { namespace, index } } } impl fmt::Display for UserExternalName { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "u{}:{}", self.namespace, self.index) } } /// A name for a test case. #[derive(Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub struct TestcaseName(Box<[u8]>); impl fmt::Display for TestcaseName { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_char('%')?; f.write_str(std::str::from_utf8(&self.0).unwrap()) } } impl fmt::Debug for TestcaseName { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{self}") } } impl TestcaseName { pub(crate) fn new>(v: T) -> Self { Self(v.as_ref().into()) } } /// The name of an external is either a reference to a user-defined symbol /// table, or a short sequence of ascii bytes so that test cases do not have /// to keep track of a symbol table. /// /// External names are primarily used as keys by code using Cranelift to map /// from a `cranelift_codegen::ir::FuncRef` or similar to additional associated /// data. /// /// External names can also serve as a primitive testing and debugging tool. /// In particular, many `.clif` test files use function names to identify /// functions. #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub enum ExternalName { /// A reference to a name in a user-defined symbol table. User(UserExternalNameRef), /// A test case function name of up to a hardcoded amount of ascii /// characters. This is not intended to be used outside test cases. TestCase(TestcaseName), /// A well-known runtime library function. LibCall(LibCall), /// A well-known symbol. KnownSymbol(KnownSymbol), } impl Default for ExternalName { fn default() -> Self { Self::User(UserExternalNameRef::new(0)) } } impl ExternalName { /// Creates a new external name from a sequence of bytes. Caller is expected /// to guarantee bytes are only ascii alphanumeric or `_`. /// /// # Examples /// /// ```rust /// # use cranelift_codegen::ir::ExternalName; /// // Create `ExternalName` from a string. /// let name = ExternalName::testcase("hello"); /// assert_eq!(name.display(None).to_string(), "%hello"); /// ``` pub fn testcase>(v: T) -> Self { Self::TestCase(TestcaseName::new(v)) } /// Create a new external name from a user-defined external function reference. /// /// # Examples /// ```rust /// # use cranelift_codegen::ir::{ExternalName, UserExternalNameRef}; /// let user_func_ref: UserExternalNameRef = Default::default(); // usually obtained with `Function::declare_imported_user_function()` /// let name = ExternalName::user(user_func_ref); /// assert_eq!(name.display(None).to_string(), "userextname0"); /// ``` pub fn user(func_ref: UserExternalNameRef) -> Self { Self::User(func_ref) } /// Returns a display for the current `ExternalName`, with extra context to prettify the /// output. pub fn display<'a>( &'a self, params: Option<&'a FunctionParameters>, ) -> DisplayableExternalName<'a> { DisplayableExternalName { name: self, params } } } /// An `ExternalName` that has enough context to be displayed. pub struct DisplayableExternalName<'a> { name: &'a ExternalName, params: Option<&'a FunctionParameters>, } impl<'a> fmt::Display for DisplayableExternalName<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.name { ExternalName::User(func_ref) => { if let Some(params) = self.params { let name = ¶ms.user_named_funcs()[*func_ref]; write!(f, "u{}:{}", name.namespace, name.index) } else { // Best effort. write!(f, "{}", *func_ref) } } ExternalName::TestCase(testcase) => testcase.fmt(f), ExternalName::LibCall(lc) => write!(f, "%{lc}"), ExternalName::KnownSymbol(ks) => write!(f, "%{ks}"), } } } impl FromStr for ExternalName { type Err = (); fn from_str(s: &str) -> Result { // Try to parse as a known symbol if let Ok(ks) = s.parse() { return Ok(Self::KnownSymbol(ks)); } // Try to parse as a libcall name if let Ok(lc) = s.parse() { return Ok(Self::LibCall(lc)); } // Otherwise its a test case name Ok(Self::testcase(s.as_bytes())) } } #[cfg(test)] mod tests { use super::ExternalName; use crate::ir::{ entities::UserExternalNameRef, function::FunctionParameters, LibCall, UserExternalName, }; use alloc::string::ToString; use core::u32; use cranelift_entity::EntityRef as _; #[cfg(target_pointer_width = "64")] #[test] fn externalname_size() { assert_eq!(core::mem::size_of::(), 24); } #[test] fn display_testcase() { assert_eq!(ExternalName::testcase("").display(None).to_string(), "%"); assert_eq!(ExternalName::testcase("x").display(None).to_string(), "%x"); assert_eq!( ExternalName::testcase("x_1").display(None).to_string(), "%x_1" ); assert_eq!( ExternalName::testcase("longname12345678") .display(None) .to_string(), "%longname12345678" ); assert_eq!( ExternalName::testcase("longname123456789") .display(None) .to_string(), "%longname123456789" ); } #[test] fn display_user() { assert_eq!( ExternalName::user(UserExternalNameRef::new(0)) .display(None) .to_string(), "userextname0" ); assert_eq!( ExternalName::user(UserExternalNameRef::new(1)) .display(None) .to_string(), "userextname1" ); assert_eq!( ExternalName::user(UserExternalNameRef::new((u32::MAX - 1) as _)) .display(None) .to_string(), "userextname4294967294" ); let mut func_params = FunctionParameters::new(); // ref 0 func_params.ensure_user_func_name(UserExternalName { namespace: 13, index: 37, }); // ref 1 func_params.ensure_user_func_name(UserExternalName { namespace: 2, index: 4, }); assert_eq!( ExternalName::user(UserExternalNameRef::new(0)) .display(Some(&func_params)) .to_string(), "u13:37" ); assert_eq!( ExternalName::user(UserExternalNameRef::new(1)) .display(Some(&func_params)) .to_string(), "u2:4" ); } #[test] fn parsing() { assert_eq!( "FloorF32".parse(), Ok(ExternalName::LibCall(LibCall::FloorF32)) ); assert_eq!( ExternalName::LibCall(LibCall::FloorF32) .display(None) .to_string(), "%FloorF32" ); } }