1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//! 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::LibCall;
use core::cmp;
use core::fmt::{self, Write};
use core::str::FromStr;

const TESTCASE_NAME_LENGTH: usize = 16;

/// 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)]
pub enum ExternalName {
    /// A name in a user-defined symbol table. Cranelift does not interpret
    /// these numbers in any way.
    User {
        /// Arbitrary.
        namespace: u32,
        /// Arbitrary.
        index: u32,
    },
    /// 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 {
        /// How many of the bytes in `ascii` are valid?
        length: u8,
        /// Ascii bytes of the name.
        ascii: [u8; TESTCASE_NAME_LENGTH],
    },
    /// A well-known runtime library function.
    LibCall(LibCall),
}

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.to_string(), "%hello");
    /// ```
    pub fn testcase<T: AsRef<[u8]>>(v: T) -> Self {
        let vec = v.as_ref();
        let len = cmp::min(vec.len(), TESTCASE_NAME_LENGTH);
        let mut bytes = [0u8; TESTCASE_NAME_LENGTH];
        bytes[0..len].copy_from_slice(&vec[0..len]);

        Self::TestCase {
            length: len as u8,
            ascii: bytes,
        }
    }

    /// Create a new external name from user-provided integer indices.
    ///
    /// # Examples
    /// ```rust
    /// # use cranelift_codegen::ir::ExternalName;
    /// // Create `ExternalName` from integer indices
    /// let name = ExternalName::user(123, 456);
    /// assert_eq!(name.to_string(), "u123:456");
    /// ```
    pub fn user(namespace: u32, index: u32) -> Self {
        Self::User { namespace, index }
    }
}

impl Default for ExternalName {
    fn default() -> Self {
        Self::user(0, 0)
    }
}

impl fmt::Display for ExternalName {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Self::User { namespace, index } => write!(f, "u{}:{}", namespace, index),
            Self::TestCase { length, ascii } => {
                f.write_char('%')?;
                for byte in ascii.iter().take(length as usize) {
                    f.write_char(*byte as char)?;
                }
                Ok(())
            }
            Self::LibCall(lc) => write!(f, "%{}", lc),
        }
    }
}

impl FromStr for ExternalName {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // Try to parse as a libcall name, otherwise it's a test case.
        match s.parse() {
            Ok(lc) => Ok(Self::LibCall(lc)),
            Err(_) => Ok(Self::testcase(s.as_bytes())),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::ExternalName;
    use crate::ir::LibCall;
    use alloc::string::ToString;
    use core::u32;

    #[test]
    fn display_testcase() {
        assert_eq!(ExternalName::testcase("").to_string(), "%");
        assert_eq!(ExternalName::testcase("x").to_string(), "%x");
        assert_eq!(ExternalName::testcase("x_1").to_string(), "%x_1");
        assert_eq!(
            ExternalName::testcase("longname12345678").to_string(),
            "%longname12345678"
        );
        // Constructor will silently drop bytes beyond the 16th
        assert_eq!(
            ExternalName::testcase("longname123456789").to_string(),
            "%longname12345678"
        );
    }

    #[test]
    fn display_user() {
        assert_eq!(ExternalName::user(0, 0).to_string(), "u0:0");
        assert_eq!(ExternalName::user(1, 1).to_string(), "u1:1");
        assert_eq!(
            ExternalName::user(u32::MAX, u32::MAX).to_string(),
            "u4294967295:4294967295"
        );
    }

    #[test]
    fn parsing() {
        assert_eq!(
            "FloorF32".parse(),
            Ok(ExternalName::LibCall(LibCall::FloorF32))
        );
        assert_eq!(
            ExternalName::LibCall(LibCall::FloorF32).to_string(),
            "%FloorF32"
        );
    }
}