Skip to main content

ir_lang/
entity.rs

1//! Stable handles into a module and its functions.
2//!
3//! Every entity is named by a small `Copy` handle wrapping a dense `u32` index, so a
4//! pass can keep a side table as a plain `Vec` indexed by `handle.index()`. Handles are
5//! scoped to the module (functions, globals, signatures, aggregates) or the function
6//! (values, blocks, instructions, stack slots) that minted them. They are never reused:
7//! removing an instruction or a block leaves a tombstone, so a handle never silently
8//! starts naming a different entity. [`Builder::compact`](crate::Builder::compact)
9//! is the one operation that renumbers, and it returns an explicit [`Remap`](crate::Remap).
10
11use core::fmt;
12
13macro_rules! handle {
14    ($(#[$doc:meta])* $name:ident, $prefix:literal, $example:literal) => {
15        $(#[$doc])*
16        ///
17        /// # Examples
18        ///
19        /// ```
20        #[doc = concat!("use ir_lang::", stringify!($name), ";")]
21        ///
22        #[doc = concat!("let h = ", stringify!($name), "::from_u32(3);")]
23        /// assert_eq!(h.index(), 3);
24        #[doc = concat!("assert_eq!(h.to_string(), \"", $example, "\");")]
25        /// ```
26        #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
27        pub struct $name(u32);
28
29        impl $name {
30            /// Makes a handle from a raw index.
31            ///
32            /// Handles normally come from the module or function that owns the
33            /// entity. This constructor exists for side tables and tests; a handle
34            /// made up this way is checked like any other (an out-of-range handle is
35            /// a validation error, never a panic).
36            ///
37            /// # Examples
38            ///
39            /// ```
40            #[doc = concat!("use ir_lang::", stringify!($name), ";")]
41            ///
42            #[doc = concat!("assert_eq!(", stringify!($name), "::from_u32(0).index(), 0);")]
43            /// ```
44            #[inline]
45            #[must_use]
46            pub const fn from_u32(index: u32) -> Self {
47                Self(index)
48            }
49
50            /// Returns the dense zero-based index of this handle.
51            ///
52            /// # Examples
53            ///
54            /// ```
55            #[doc = concat!("use ir_lang::", stringify!($name), ";")]
56            ///
57            #[doc = concat!("assert_eq!(", stringify!($name), "::from_u32(7).index(), 7);")]
58            /// ```
59            #[inline]
60            #[must_use]
61            pub const fn index(self) -> usize {
62                self.0 as usize
63            }
64
65            /// Returns the raw `u32` index.
66            ///
67            /// # Examples
68            ///
69            /// ```
70            #[doc = concat!("use ir_lang::", stringify!($name), ";")]
71            ///
72            #[doc = concat!("assert_eq!(", stringify!($name), "::from_u32(9).as_u32(), 9);")]
73            /// ```
74            #[inline]
75            #[must_use]
76            pub const fn as_u32(self) -> u32 {
77                self.0
78            }
79        }
80
81        impl fmt::Display for $name {
82            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83                write!(f, concat!($prefix, "{}"), self.0)
84            }
85        }
86    };
87}
88
89handle!(
90    /// An SSA value in a function: a block parameter or an instruction result.
91    ///
92    /// Every value has one type and exactly one definition. It prints as `v<n>`.
93    Value,
94    "v",
95    "v3"
96);
97
98handle!(
99    /// A basic block in a function. It prints as `b<n>`.
100    Block,
101    "b",
102    "b3"
103);
104
105handle!(
106    /// An instruction in a function. Instruction handles are not printed in the
107    /// textual form (an instruction is named by its results); in messages it prints
108    /// as `inst<n>`.
109    Inst,
110    "inst",
111    "inst3"
112);
113
114handle!(
115    /// A stack slot of a function: frame memory addressed with
116    /// [`InstData::StackAddr`](crate::InstData::StackAddr). It prints as `ss<n>`.
117    StackSlot,
118    "ss",
119    "ss3"
120);
121
122handle!(
123    /// A function declaration in a module (defined or imported). In messages it
124    /// prints as `f<n>`; the textual form names functions by symbol (`@name`).
125    FuncId,
126    "f",
127    "f3"
128);
129
130handle!(
131    /// A global (data) declaration in a module. In messages it prints as `g<n>`;
132    /// the textual form names globals by symbol (`@name`).
133    GlobalId,
134    "g",
135    "g3"
136);
137
138handle!(
139    /// An interned function signature in a module. It prints as `s<n>`.
140    SigId,
141    "s",
142    "s3"
143);
144
145handle!(
146    /// An aggregate (struct or array) memory type in a module. It prints as `t<n>`.
147    AggId,
148    "t",
149    "t3"
150);
151
152handle!(
153    /// A mutable local variable of the builder's SSA construction
154    /// ([`Builder::declare_var`](crate::Builder::declare_var)): a name for "the
155    /// current value of `x`" that the builder turns into SSA values and block
156    /// parameters. Scoped to one builder. In messages it prints as `var<n>`.
157    Variable,
158    "var",
159    "var3"
160);
161
162/// The results of one instruction: a run of consecutive values.
163///
164/// An instruction defines zero or more values, always with consecutive handles, so
165/// the run is stored as a start and a length rather than a list.
166///
167/// # Examples
168///
169/// ```
170/// use ir_lang::{Module, Signature, Type, Linkage, MemOrder};
171///
172/// let mut m = Module::new("demo");
173/// let f = m.declare_function("f", &Signature::new(&[Type::Ptr], &[]), Linkage::Export)?;
174/// let mut b = m.build(f)?;
175/// let p = b.param(0)?;
176/// let old = b.iconst(Type::I64, 1)?;
177/// let new = b.iconst(Type::I64, 2)?;
178/// let inst = b.cmpxchg(p, old, new, MemOrder::SeqCst, MemOrder::SeqCst)?;
179/// let results = b.results(inst);
180/// assert_eq!(results.len(), 2);
181/// assert_eq!(results.get(1), Some(results.first().unwrap().next()));
182/// # Ok::<(), Box<dyn std::error::Error>>(())
183/// ```
184#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
185pub struct ValueRange {
186    pub(crate) start: u32,
187    pub(crate) len: u32,
188}
189
190impl ValueRange {
191    /// The number of values in the run.
192    ///
193    /// # Examples
194    ///
195    /// ```
196    /// use ir_lang::ValueRange;
197    ///
198    /// assert_eq!(ValueRange::default().len(), 0);
199    /// ```
200    #[inline]
201    #[must_use]
202    pub const fn len(self) -> usize {
203        self.len as usize
204    }
205
206    /// Whether the run is empty (the instruction defines nothing).
207    ///
208    /// # Examples
209    ///
210    /// ```
211    /// use ir_lang::ValueRange;
212    ///
213    /// assert!(ValueRange::default().is_empty());
214    /// ```
215    #[inline]
216    #[must_use]
217    pub const fn is_empty(self) -> bool {
218        self.len == 0
219    }
220
221    /// The `i`-th value of the run, if there is one.
222    ///
223    /// # Examples
224    ///
225    /// ```
226    /// use ir_lang::ValueRange;
227    ///
228    /// assert_eq!(ValueRange::default().get(0), None);
229    /// ```
230    #[inline]
231    #[must_use]
232    pub const fn get(self, i: usize) -> Option<Value> {
233        if i < self.len as usize {
234            match self.start.checked_add(i as u32) {
235                Some(v) => Some(Value(v)),
236                None => None,
237            }
238        } else {
239            None
240        }
241    }
242
243    /// The first value of the run, if there is one.
244    ///
245    /// # Examples
246    ///
247    /// ```
248    /// use ir_lang::ValueRange;
249    ///
250    /// assert_eq!(ValueRange::default().first(), None);
251    /// ```
252    #[inline]
253    #[must_use]
254    pub const fn first(self) -> Option<Value> {
255        self.get(0)
256    }
257
258    /// Whether `value` is one of the run's values.
259    ///
260    /// # Examples
261    ///
262    /// ```
263    /// use ir_lang::{Value, ValueRange};
264    ///
265    /// assert!(!ValueRange::default().contains(Value::from_u32(0)));
266    /// ```
267    #[inline]
268    #[must_use]
269    pub const fn contains(self, value: Value) -> bool {
270        value.0 >= self.start && value.0 - self.start < self.len
271    }
272
273    /// Iterates over the run's values in order.
274    ///
275    /// # Examples
276    ///
277    /// ```
278    /// use ir_lang::ValueRange;
279    ///
280    /// assert_eq!(ValueRange::default().iter().count(), 0);
281    /// ```
282    #[inline]
283    pub fn iter(self) -> impl ExactSizeIterator<Item = Value> + DoubleEndedIterator {
284        (self.start..self.start.saturating_add(self.len)).map(Value)
285    }
286}
287
288impl IntoIterator for ValueRange {
289    type Item = Value;
290    type IntoIter = core::iter::Map<core::ops::Range<u32>, fn(u32) -> Value>;
291
292    fn into_iter(self) -> Self::IntoIter {
293        (self.start..self.start.saturating_add(self.len)).map(Value as fn(u32) -> Value)
294    }
295}
296
297impl Value {
298    /// The value with the next index. Results of one instruction are consecutive,
299    /// so `first.next()` is the second result.
300    ///
301    /// # Examples
302    ///
303    /// ```
304    /// use ir_lang::Value;
305    ///
306    /// assert_eq!(Value::from_u32(4).next(), Value::from_u32(5));
307    /// ```
308    #[inline]
309    #[must_use]
310    pub const fn next(self) -> Value {
311        Value(self.0.saturating_add(1))
312    }
313}
314
315/// The sentinel used for "no entity" in the internal linked lists.
316pub(crate) const NONE: u32 = u32::MAX;
317
318#[cfg(test)]
319mod tests {
320    use super::*;
321    use alloc::string::ToString;
322
323    #[test]
324    fn test_handles_display_with_their_prefixes() {
325        assert_eq!(Value::from_u32(1).to_string(), "v1");
326        assert_eq!(Block::from_u32(2).to_string(), "b2");
327        assert_eq!(StackSlot::from_u32(0).to_string(), "ss0");
328        assert_eq!(AggId::from_u32(5).to_string(), "t5");
329        assert_eq!(SigId::from_u32(5).to_string(), "s5");
330    }
331
332    #[test]
333    fn test_value_range_contains_and_iterates() {
334        let r = ValueRange { start: 4, len: 2 };
335        assert!(r.contains(Value::from_u32(4)));
336        assert!(r.contains(Value::from_u32(5)));
337        assert!(!r.contains(Value::from_u32(6)));
338        assert!(!r.contains(Value::from_u32(3)));
339        assert_eq!(r.iter().collect::<alloc::vec::Vec<_>>().len(), 2);
340        assert_eq!(r.into_iter().next_back(), Some(Value::from_u32(5)));
341    }
342}