cranelift_codegen/ir/
trapcode.rs

1//! Trap codes describing the reason for a trap.
2
3use core::fmt::{self, Display, Formatter};
4use core::num::NonZeroU8;
5use core::str::FromStr;
6#[cfg(feature = "enable-serde")]
7use serde_derive::{Deserialize, Serialize};
8
9/// A trap code describing the reason for a trap.
10///
11/// All trap instructions have an explicit trap code.
12#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
13#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
14pub struct TrapCode(NonZeroU8);
15
16impl TrapCode {
17    /// Number of reserved opcodes for Cranelift itself. This number of traps are
18    /// defined below starting at the high end of the byte space (e.g. 255, 254,
19    /// ...)
20    const RESERVED: u8 = 5;
21    const RESERVED_START: u8 = u8::MAX - Self::RESERVED + 1;
22
23    /// Internal helper to create new reserved trap codes.
24    const fn reserved(byte: u8) -> TrapCode {
25        if let Some(code) = byte.checked_add(Self::RESERVED_START) {
26            if let Some(nz) = NonZeroU8::new(code) {
27                return TrapCode(nz);
28            }
29        }
30        panic!("invalid reserved opcode")
31    }
32
33    /// The current stack space was exhausted.
34    pub const STACK_OVERFLOW: TrapCode = TrapCode::reserved(0);
35    /// An integer arithmetic operation caused an overflow.
36    pub const INTEGER_OVERFLOW: TrapCode = TrapCode::reserved(1);
37    /// A `heap_addr` instruction detected an out-of-bounds error.
38    ///
39    /// Note that not all out-of-bounds heap accesses are reported this way;
40    /// some are detected by a segmentation fault on the heap unmapped or
41    /// offset-guard pages.
42    pub const HEAP_OUT_OF_BOUNDS: TrapCode = TrapCode::reserved(2);
43
44    /// An integer division by zero.
45    pub const INTEGER_DIVISION_BY_ZERO: TrapCode = TrapCode::reserved(3);
46
47    /// Failed float-to-int conversion.
48    pub const BAD_CONVERSION_TO_INTEGER: TrapCode = TrapCode::reserved(4);
49
50    /// Create a user-defined trap code.
51    ///
52    /// Returns `None` if `code` is zero or too large and is reserved by
53    /// Cranelift.
54    pub const fn user(code: u8) -> Option<TrapCode> {
55        if code >= Self::RESERVED_START {
56            return None;
57        }
58        match NonZeroU8::new(code) {
59            Some(nz) => Some(TrapCode(nz)),
60            None => None,
61        }
62    }
63
64    /// Alias for [`TrapCode::user`] with a panic built-in.
65    pub const fn unwrap_user(code: u8) -> TrapCode {
66        match TrapCode::user(code) {
67            Some(code) => code,
68            None => panic!("invalid user trap code"),
69        }
70    }
71
72    /// Returns the raw byte representing this trap.
73    pub const fn as_raw(&self) -> NonZeroU8 {
74        self.0
75    }
76
77    /// Creates a trap code from its raw byte, likely returned by
78    /// [`TrapCode::as_raw`] previously.
79    pub const fn from_raw(byte: NonZeroU8) -> TrapCode {
80        TrapCode(byte)
81    }
82
83    /// Returns a slice of all traps except `TrapCode::User` traps
84    pub const fn non_user_traps() -> &'static [TrapCode] {
85        &[
86            TrapCode::STACK_OVERFLOW,
87            TrapCode::HEAP_OUT_OF_BOUNDS,
88            TrapCode::INTEGER_OVERFLOW,
89            TrapCode::INTEGER_DIVISION_BY_ZERO,
90            TrapCode::BAD_CONVERSION_TO_INTEGER,
91        ]
92    }
93}
94
95impl Display for TrapCode {
96    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
97        let identifier = match *self {
98            Self::STACK_OVERFLOW => "stk_ovf",
99            Self::HEAP_OUT_OF_BOUNDS => "heap_oob",
100            Self::INTEGER_OVERFLOW => "int_ovf",
101            Self::INTEGER_DIVISION_BY_ZERO => "int_divz",
102            Self::BAD_CONVERSION_TO_INTEGER => "bad_toint",
103            TrapCode(x) => return write!(f, "user{x}"),
104        };
105        f.write_str(identifier)
106    }
107}
108
109impl FromStr for TrapCode {
110    type Err = ();
111
112    fn from_str(s: &str) -> Result<Self, Self::Err> {
113        match s {
114            "stk_ovf" => Ok(Self::STACK_OVERFLOW),
115            "heap_oob" => Ok(Self::HEAP_OUT_OF_BOUNDS),
116            "int_ovf" => Ok(Self::INTEGER_OVERFLOW),
117            "int_divz" => Ok(Self::INTEGER_DIVISION_BY_ZERO),
118            "bad_toint" => Ok(Self::BAD_CONVERSION_TO_INTEGER),
119            _ if s.starts_with("user") => {
120                let num = s[4..].parse().map_err(|_| ())?;
121                TrapCode::user(num).ok_or(())
122            }
123            _ => Err(()),
124        }
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131    use alloc::string::ToString;
132
133    #[test]
134    fn display() {
135        for r in TrapCode::non_user_traps() {
136            let tc = *r;
137            assert_eq!(tc.to_string().parse(), Ok(tc));
138        }
139        assert_eq!("bogus".parse::<TrapCode>(), Err(()));
140
141        assert_eq!(TrapCode::unwrap_user(17).to_string(), "user17");
142        assert_eq!("user22".parse(), Ok(TrapCode::unwrap_user(22)));
143        assert_eq!("user".parse::<TrapCode>(), Err(()));
144        assert_eq!("user-1".parse::<TrapCode>(), Err(()));
145        assert_eq!("users".parse::<TrapCode>(), Err(()));
146    }
147}