cranelift_codegen/ir/
jumptable.rs

1//! Jump table representation.
2//!
3//! Jump tables are declared in the preamble and assigned an `ir::entities::JumpTable` reference.
4//! The actual table of destinations is stored in a `JumpTableData` struct defined in this module.
5
6use crate::ir::instructions::ValueListPool;
7use crate::ir::BlockCall;
8use alloc::vec::Vec;
9use core::fmt::{self, Display, Formatter};
10use core::slice::{Iter, IterMut};
11
12#[cfg(feature = "enable-serde")]
13use serde_derive::{Deserialize, Serialize};
14
15/// Contents of a jump table.
16///
17/// All jump tables use 0-based indexing and are densely populated.
18///
19/// The default block for the jump table is stored as the first element of the underlying vector.
20/// It can be accessed through the `default_block` and `default_block_mut` functions. All blocks
21/// may be iterated using the `all_branches` and `all_branches_mut` functions, which will both
22/// iterate over the default block first.
23#[derive(Debug, Clone, PartialEq, Hash)]
24#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
25pub struct JumpTableData {
26    // Table entries.
27    table: Vec<BlockCall>,
28}
29
30impl JumpTableData {
31    /// Create a new jump table with the provided blocks.
32    pub fn new(def: BlockCall, table: &[BlockCall]) -> Self {
33        Self {
34            table: std::iter::once(def).chain(table.iter().copied()).collect(),
35        }
36    }
37
38    /// Fetch the default block for this jump table.
39    pub fn default_block(&self) -> BlockCall {
40        *self.table.first().unwrap()
41    }
42
43    /// Mutable access to the default block of this jump table.
44    pub fn default_block_mut(&mut self) -> &mut BlockCall {
45        self.table.first_mut().unwrap()
46    }
47
48    /// The jump table and default block as a single slice. The default block will always be first.
49    pub fn all_branches(&self) -> &[BlockCall] {
50        self.table.as_slice()
51    }
52
53    /// The jump table and default block as a single mutable slice. The default block will always
54    /// be first.
55    pub fn all_branches_mut(&mut self) -> &mut [BlockCall] {
56        self.table.as_mut_slice()
57    }
58
59    /// Access the jump table as a slice. This excludes the default block.
60    pub fn as_slice(&self) -> &[BlockCall] {
61        &self.table.as_slice()[1..]
62    }
63
64    /// Access the jump table as a mutable slice. This excludes the default block.
65    pub fn as_mut_slice(&mut self) -> &mut [BlockCall] {
66        &mut self.table.as_mut_slice()[1..]
67    }
68
69    /// Returns an iterator to the jump table, excluding the default block.
70    #[deprecated(since = "7.0.0", note = "please use `.as_slice()` instead")]
71    pub fn iter(&self) -> Iter<BlockCall> {
72        self.as_slice().iter()
73    }
74
75    /// Returns an iterator that allows modifying each value, excluding the default block.
76    #[deprecated(since = "7.0.0", note = "please use `.as_mut_slice()` instead")]
77    pub fn iter_mut(&mut self) -> IterMut<BlockCall> {
78        self.as_mut_slice().iter_mut()
79    }
80
81    /// Clears all entries in this jump table, except for the default block.
82    pub fn clear(&mut self) {
83        self.table.drain(1..);
84    }
85
86    /// Return a value that can display the contents of this jump table.
87    pub fn display<'a>(&'a self, pool: &'a ValueListPool) -> DisplayJumpTable<'a> {
88        DisplayJumpTable { jt: self, pool }
89    }
90}
91
92/// A wrapper for the context required to display a [JumpTableData].
93pub struct DisplayJumpTable<'a> {
94    jt: &'a JumpTableData,
95    pool: &'a ValueListPool,
96}
97
98impl<'a> Display for DisplayJumpTable<'a> {
99    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
100        write!(fmt, "{}, [", self.jt.default_block().display(self.pool))?;
101        if let Some((first, rest)) = self.jt.as_slice().split_first() {
102            write!(fmt, "{}", first.display(self.pool))?;
103            for block in rest {
104                write!(fmt, ", {}", block.display(self.pool))?;
105            }
106        }
107        write!(fmt, "]")
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::JumpTableData;
114    use crate::entity::EntityRef;
115    use crate::ir::instructions::ValueListPool;
116    use crate::ir::{Block, BlockCall, Value};
117    use std::string::ToString;
118
119    #[test]
120    fn empty() {
121        let mut pool = ValueListPool::default();
122        let def = BlockCall::new(Block::new(0), &[], &mut pool);
123
124        let jt = JumpTableData::new(def, &[]);
125
126        assert_eq!(jt.all_branches().get(0), Some(&def));
127
128        assert_eq!(jt.as_slice().get(0), None);
129        assert_eq!(jt.as_slice().get(10), None);
130
131        assert_eq!(jt.display(&pool).to_string(), "block0, []");
132
133        assert_eq!(jt.all_branches(), [def]);
134        assert_eq!(jt.as_slice(), []);
135    }
136
137    #[test]
138    fn insert() {
139        let mut pool = ValueListPool::default();
140
141        let v0 = Value::new(0);
142        let v1 = Value::new(1);
143
144        let e0 = Block::new(0);
145        let e1 = Block::new(1);
146        let e2 = Block::new(2);
147
148        let def = BlockCall::new(e0, &[], &mut pool);
149        let b1 = BlockCall::new(e1, &[v0], &mut pool);
150        let b2 = BlockCall::new(e2, &[], &mut pool);
151        let b3 = BlockCall::new(e1, &[v1], &mut pool);
152
153        let jt = JumpTableData::new(def, &[b1, b2, b3]);
154
155        assert_eq!(jt.default_block(), def);
156        assert_eq!(
157            jt.display(&pool).to_string(),
158            "block0, [block1(v0), block2, block1(v1)]"
159        );
160
161        assert_eq!(jt.all_branches(), [def, b1, b2, b3]);
162        assert_eq!(jt.as_slice(), [b1, b2, b3]);
163
164        assert_eq!(jt.as_slice()[0].args_slice(&pool), [v0]);
165        assert_eq!(jt.as_slice()[1].args_slice(&pool), []);
166        assert_eq!(jt.as_slice()[2].args_slice(&pool), [v1]);
167    }
168}