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 
7 use crate::ir::LibCall;
8 use core::cmp;
9 use core::fmt::{self, Write};
10 use core::str::FromStr;
11 
12 #[cfg(feature = "enable-serde")]
13 use serde::{Deserialize, Serialize};
14 
15 const TESTCASE_NAME_LENGTH: usize = 16;
16 
17 /// The name of an external is either a reference to a user-defined symbol
18 /// table, or a short sequence of ascii bytes so that test cases do not have
19 /// to keep track of a symbol table.
20 ///
21 /// External names are primarily used as keys by code using Cranelift to map
22 /// from a `cranelift_codegen::ir::FuncRef` or similar to additional associated
23 /// data.
24 ///
25 /// External names can also serve as a primitive testing and debugging tool.
26 /// In particular, many `.clif` test files use function names to identify
27 /// functions.
28 #[derive(Debug, Clone, PartialEq, Eq)]
29 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
30 pub enum ExternalName {
31     /// A name in a user-defined symbol table. Cranelift does not interpret
32     /// these numbers in any way.
33     User {
34         /// Arbitrary.
35         namespace: u32,
36         /// Arbitrary.
37         index: u32,
38     },
39     /// A test case function name of up to a hardcoded amount of ascii
40     /// characters. This is not intended to be used outside test cases.
41     TestCase {
42         /// How many of the bytes in `ascii` are valid?
43         length: u8,
44         /// Ascii bytes of the name.
45         ascii: [u8; TESTCASE_NAME_LENGTH],
46     },
47     /// A well-known runtime library function.
48     LibCall(LibCall),
49 }
50 
51 impl ExternalName {
52     /// Creates a new external name from a sequence of bytes. Caller is expected
53     /// to guarantee bytes are only ascii alphanumeric or `_`.
54     ///
55     /// # Examples
56     ///
57     /// ```rust
58     /// # use cranelift_codegen::ir::ExternalName;
59     /// // Create `ExternalName` from a string.
60     /// let name = ExternalName::testcase("hello");
61     /// assert_eq!(name.to_string(), "%hello");
62     /// ```
63     pub fn testcase<T: AsRef<[u8]>>(v: T) -> Self {
64         let vec = v.as_ref();
65         let len = cmp::min(vec.len(), TESTCASE_NAME_LENGTH);
66         let mut bytes = [0u8; TESTCASE_NAME_LENGTH];
67         bytes[0..len].copy_from_slice(&vec[0..len]);
68 
69         Self::TestCase {
70             length: len as u8,
71             ascii: bytes,
72         }
73     }
74 
75     /// Create a new external name from user-provided integer indices.
76     ///
77     /// # Examples
78     /// ```rust
79     /// # use cranelift_codegen::ir::ExternalName;
80     /// // Create `ExternalName` from integer indices
81     /// let name = ExternalName::user(123, 456);
82     /// assert_eq!(name.to_string(), "u123:456");
83     /// ```
84     pub fn user(namespace: u32, index: u32) -> Self {
85         Self::User { namespace, index }
86     }
87 }
88 
89 impl Default for ExternalName {
90     fn default() -> Self {
91         Self::user(0, 0)
92     }
93 }
94 
95 impl fmt::Display for ExternalName {
96     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
97         match *self {
98             Self::User { namespace, index } => write!(f, "u{}:{}", namespace, index),
99             Self::TestCase { length, ascii } => {
100                 f.write_char('%')?;
101                 for byte in ascii.iter().take(length as usize) {
102                     f.write_char(*byte as char)?;
103                 }
104                 Ok(())
105             }
106             Self::LibCall(lc) => write!(f, "%{}", lc),
107         }
108     }
109 }
110 
111 impl FromStr for ExternalName {
112     type Err = ();
113 
114     fn from_str(s: &str) -> Result<Self, Self::Err> {
115         // Try to parse as a libcall name, otherwise it's a test case.
116         match s.parse() {
117             Ok(lc) => Ok(Self::LibCall(lc)),
118             Err(_) => Ok(Self::testcase(s.as_bytes())),
119         }
120     }
121 }
122 
123 #[cfg(test)]
124 mod tests {
125     use super::ExternalName;
126     use crate::ir::LibCall;
127     use alloc::string::ToString;
128     use core::u32;
129 
130     #[test]
131     fn display_testcase() {
132         assert_eq!(ExternalName::testcase("").to_string(), "%");
133         assert_eq!(ExternalName::testcase("x").to_string(), "%x");
134         assert_eq!(ExternalName::testcase("x_1").to_string(), "%x_1");
135         assert_eq!(
136             ExternalName::testcase("longname12345678").to_string(),
137             "%longname12345678"
138         );
139         // Constructor will silently drop bytes beyond the 16th
140         assert_eq!(
141             ExternalName::testcase("longname123456789").to_string(),
142             "%longname12345678"
143         );
144     }
145 
146     #[test]
147     fn display_user() {
148         assert_eq!(ExternalName::user(0, 0).to_string(), "u0:0");
149         assert_eq!(ExternalName::user(1, 1).to_string(), "u1:1");
150         assert_eq!(
151             ExternalName::user(u32::MAX, u32::MAX).to_string(),
152             "u4294967295:4294967295"
153         );
154     }
155 
156     #[test]
157     fn parsing() {
158         assert_eq!(
159             "FloorF32".parse(),
160             Ok(ExternalName::LibCall(LibCall::FloorF32))
161         );
162         assert_eq!(
163             ExternalName::LibCall(LibCall::FloorF32).to_string(),
164             "%FloorF32"
165         );
166     }
167 }
168