Skip to main content

cranelift_codegen/ir/
extname.rs

1//! External names.
2//!
3//! These are identifiers for declaring entities defined outside the current
4//! function. The name of an external declaration doesn't have any meaning to
5//! Cranelift, which compiles functions independently.
6
7use crate::ir::{KnownSymbol, LibCall};
8use alloc::boxed::Box;
9use core::fmt::{self, Write};
10use core::str::FromStr;
11
12use cranelift_entity::EntityRef as _;
13#[cfg(feature = "enable-serde")]
14use serde_derive::{Deserialize, Serialize};
15
16use super::entities::UserExternalNameRef;
17use super::function::FunctionParameters;
18
19/// An explicit name for a user-defined function, be it defined in code or in CLIF text.
20///
21/// This is used both for naming a function (for debugging purposes) and for declaring external
22/// functions. In the latter case, this becomes an `ExternalName`, which gets embedded in
23/// relocations later, etc.
24#[derive(Clone, Debug, PartialEq, Eq, Hash)]
25#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
26pub enum UserFuncName {
27    /// A user-defined name, with semantics left to the user.
28    User(UserExternalName),
29    /// A name for a test case, mostly intended for Cranelift testing.
30    Testcase(TestcaseName),
31}
32
33impl UserFuncName {
34    /// Creates a new external name from a sequence of bytes. Caller is expected
35    /// to guarantee bytes are only ascii alphanumeric or `_`.
36    pub fn testcase<T: AsRef<[u8]>>(v: T) -> Self {
37        Self::Testcase(TestcaseName::new(v))
38    }
39
40    /// Create a new external name from a user-defined external function reference.
41    pub fn user(namespace: u32, index: u32) -> Self {
42        Self::User(UserExternalName::new(namespace, index))
43    }
44
45    /// Get a `UserExternalName` if this is a user-defined name.
46    pub fn get_user(&self) -> Option<&UserExternalName> {
47        match self {
48            UserFuncName::User(user) => Some(user),
49            UserFuncName::Testcase(_) => None,
50        }
51    }
52}
53
54impl Default for UserFuncName {
55    fn default() -> Self {
56        UserFuncName::User(UserExternalName::default())
57    }
58}
59
60impl fmt::Display for UserFuncName {
61    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62        match self {
63            UserFuncName::User(user) => user.fmt(f),
64            UserFuncName::Testcase(testcase) => testcase.fmt(f),
65        }
66    }
67}
68
69/// An external name in a user-defined symbol table.
70///
71/// Cranelift does not interpret these numbers in any way, so they can represent arbitrary values.
72#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
73#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
74pub struct UserExternalName {
75    /// Arbitrary.
76    pub namespace: u32,
77    /// Arbitrary.
78    pub index: u32,
79}
80
81impl UserExternalName {
82    /// Creates a new [UserExternalName].
83    pub fn new(namespace: u32, index: u32) -> Self {
84        Self { namespace, index }
85    }
86}
87
88impl fmt::Display for UserExternalName {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        write!(f, "u{}:{}", self.namespace, self.index)
91    }
92}
93
94/// A name for a test case.
95#[derive(Clone, PartialEq, Eq, Hash)]
96#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
97pub struct TestcaseName(Box<[u8]>);
98
99impl fmt::Display for TestcaseName {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        f.write_char('%')?;
102        f.write_str(core::str::from_utf8(&self.0).unwrap())
103    }
104}
105
106impl fmt::Debug for TestcaseName {
107    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108        write!(f, "{self}")
109    }
110}
111
112impl TestcaseName {
113    pub(crate) fn new<T: AsRef<[u8]>>(v: T) -> Self {
114        Self(v.as_ref().into())
115    }
116
117    /// Get the raw test case name as bytes.
118    pub fn raw(&self) -> &[u8] {
119        &self.0
120    }
121}
122
123/// The name of an external is either a reference to a user-defined symbol
124/// table, or a short sequence of ascii bytes so that test cases do not have
125/// to keep track of a symbol table.
126///
127/// External names are primarily used as keys by code using Cranelift to map
128/// from a `cranelift_codegen::ir::FuncRef` or similar to additional associated
129/// data.
130///
131/// External names can also serve as a primitive testing and debugging tool.
132/// In particular, many `.clif` test files use function names to identify
133/// functions.
134#[derive(Debug, Clone, PartialEq, Eq, Hash)]
135#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
136pub enum ExternalName {
137    /// A reference to a name in a user-defined symbol table.
138    User(UserExternalNameRef),
139    /// A test case function name of up to a hardcoded amount of ascii
140    /// characters. This is not intended to be used outside test cases.
141    TestCase(TestcaseName),
142    /// A well-known runtime library function.
143    LibCall(LibCall),
144    /// A well-known symbol.
145    KnownSymbol(KnownSymbol),
146}
147
148impl Default for ExternalName {
149    fn default() -> Self {
150        Self::User(UserExternalNameRef::new(0))
151    }
152}
153
154impl ExternalName {
155    /// Creates a new external name from a sequence of bytes. Caller is expected
156    /// to guarantee bytes are only ascii alphanumeric or `_`.
157    ///
158    /// # Examples
159    ///
160    /// ```rust
161    /// # use cranelift_codegen::ir::ExternalName;
162    /// // Create `ExternalName` from a string.
163    /// let name = ExternalName::testcase("hello");
164    /// assert_eq!(name.display(None).to_string(), "%hello");
165    /// ```
166    pub fn testcase<T: AsRef<[u8]>>(v: T) -> Self {
167        Self::TestCase(TestcaseName::new(v))
168    }
169
170    /// Create a new external name from a user-defined external function reference.
171    ///
172    /// # Examples
173    /// ```rust
174    /// # use cranelift_codegen::ir::{ExternalName, UserExternalNameRef};
175    /// let user_func_ref: UserExternalNameRef = Default::default(); // usually obtained with `Function::declare_imported_user_function()`
176    /// let name = ExternalName::user(user_func_ref);
177    /// assert_eq!(name.display(None).to_string(), "userextname0");
178    /// ```
179    pub fn user(func_ref: UserExternalNameRef) -> Self {
180        Self::User(func_ref)
181    }
182
183    /// Returns a display for the current `ExternalName`, with extra context to prettify the
184    /// output.
185    pub fn display<'a>(
186        &'a self,
187        params: Option<&'a FunctionParameters>,
188    ) -> DisplayableExternalName<'a> {
189        DisplayableExternalName { name: self, params }
190    }
191}
192
193/// An `ExternalName` that has enough context to be displayed.
194pub struct DisplayableExternalName<'a> {
195    name: &'a ExternalName,
196    params: Option<&'a FunctionParameters>,
197}
198
199impl<'a> fmt::Display for DisplayableExternalName<'a> {
200    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201        match &self.name {
202            ExternalName::User(func_ref) => {
203                if let Some(params) = self.params {
204                    let name = &params.user_named_funcs()[*func_ref];
205                    write!(f, "u{}:{}", name.namespace, name.index)
206                } else {
207                    // Best effort.
208                    write!(f, "{}", *func_ref)
209                }
210            }
211            ExternalName::TestCase(testcase) => testcase.fmt(f),
212            ExternalName::LibCall(lc) => write!(f, "%{lc}"),
213            ExternalName::KnownSymbol(ks) => write!(f, "%{ks}"),
214        }
215    }
216}
217
218impl FromStr for ExternalName {
219    type Err = ();
220
221    fn from_str(s: &str) -> Result<Self, Self::Err> {
222        // Try to parse as a known symbol
223        if let Ok(ks) = s.parse() {
224            return Ok(Self::KnownSymbol(ks));
225        }
226
227        // Try to parse as a libcall name
228        if let Ok(lc) = s.parse() {
229            return Ok(Self::LibCall(lc));
230        }
231
232        // Otherwise its a test case name
233        Ok(Self::testcase(s.as_bytes()))
234    }
235}
236
237#[cfg(test)]
238mod tests {
239    use super::ExternalName;
240    use crate::ir::{
241        LibCall, UserExternalName, entities::UserExternalNameRef, function::FunctionParameters,
242    };
243    use alloc::string::ToString;
244    use cranelift_entity::EntityRef as _;
245
246    #[cfg(target_pointer_width = "64")]
247    #[test]
248    fn externalname_size() {
249        assert_eq!(core::mem::size_of::<ExternalName>(), 24);
250    }
251
252    #[test]
253    fn display_testcase() {
254        assert_eq!(ExternalName::testcase("").display(None).to_string(), "%");
255        assert_eq!(ExternalName::testcase("x").display(None).to_string(), "%x");
256        assert_eq!(
257            ExternalName::testcase("x_1").display(None).to_string(),
258            "%x_1"
259        );
260        assert_eq!(
261            ExternalName::testcase("longname12345678")
262                .display(None)
263                .to_string(),
264            "%longname12345678"
265        );
266        assert_eq!(
267            ExternalName::testcase("longname123456789")
268                .display(None)
269                .to_string(),
270            "%longname123456789"
271        );
272    }
273
274    #[test]
275    fn display_user() {
276        assert_eq!(
277            ExternalName::user(UserExternalNameRef::new(0))
278                .display(None)
279                .to_string(),
280            "userextname0"
281        );
282        assert_eq!(
283            ExternalName::user(UserExternalNameRef::new(1))
284                .display(None)
285                .to_string(),
286            "userextname1"
287        );
288        assert_eq!(
289            ExternalName::user(UserExternalNameRef::new((u32::MAX - 1) as _))
290                .display(None)
291                .to_string(),
292            "userextname4294967294"
293        );
294
295        let mut func_params = FunctionParameters::new();
296
297        // ref 0
298        func_params.ensure_user_func_name(UserExternalName {
299            namespace: 13,
300            index: 37,
301        });
302
303        // ref 1
304        func_params.ensure_user_func_name(UserExternalName {
305            namespace: 2,
306            index: 4,
307        });
308
309        assert_eq!(
310            ExternalName::user(UserExternalNameRef::new(0))
311                .display(Some(&func_params))
312                .to_string(),
313            "u13:37"
314        );
315
316        assert_eq!(
317            ExternalName::user(UserExternalNameRef::new(1))
318                .display(Some(&func_params))
319                .to_string(),
320            "u2:4"
321        );
322    }
323
324    #[test]
325    fn parsing() {
326        assert_eq!(
327            "FloorF32".parse(),
328            Ok(ExternalName::LibCall(LibCall::FloorF32))
329        );
330        assert_eq!(
331            ExternalName::LibCall(LibCall::FloorF32)
332                .display(None)
333                .to_string(),
334            "%FloorF32"
335        );
336    }
337}