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