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 
6 use crate::ir::entities::Block;
7 use alloc::vec::Vec;
8 use core::fmt::{self, Display, Formatter};
9 use core::slice::{Iter, IterMut};
10 
11 #[cfg(feature = "enable-serde")]
12 use serde::{Deserialize, Serialize};
13 
14 /// Contents of a jump table.
15 ///
16 /// All jump tables use 0-based indexing and are densely populated.
17 #[derive(Clone)]
18 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
19 pub struct JumpTableData {
20     // Table entries.
21     table: Vec<Block>,
22 }
23 
24 impl JumpTableData {
25     /// Create a new empty jump table.
new() -> Self26     pub fn new() -> Self {
27         Self { table: Vec::new() }
28     }
29 
30     /// Create a new empty jump table with the specified capacity.
with_capacity(capacity: usize) -> Self31     pub fn with_capacity(capacity: usize) -> Self {
32         Self {
33             table: Vec::with_capacity(capacity),
34         }
35     }
36 
37     /// Get the number of table entries.
len(&self) -> usize38     pub fn len(&self) -> usize {
39         self.table.len()
40     }
41 
42     /// Append a table entry.
push_entry(&mut self, dest: Block)43     pub fn push_entry(&mut self, dest: Block) {
44         self.table.push(dest)
45     }
46 
47     /// Checks if any of the entries branch to `block`.
branches_to(&self, block: Block) -> bool48     pub fn branches_to(&self, block: Block) -> bool {
49         self.table.iter().any(|target_block| *target_block == block)
50     }
51 
52     /// Access the whole table as a slice.
as_slice(&self) -> &[Block]53     pub fn as_slice(&self) -> &[Block] {
54         self.table.as_slice()
55     }
56 
57     /// Access the whole table as a mutable slice.
as_mut_slice(&mut self) -> &mut [Block]58     pub fn as_mut_slice(&mut self) -> &mut [Block] {
59         self.table.as_mut_slice()
60     }
61 
62     /// Returns an iterator over the table.
iter(&self) -> Iter<Block>63     pub fn iter(&self) -> Iter<Block> {
64         self.table.iter()
65     }
66 
67     /// Returns an iterator that allows modifying each value.
iter_mut(&mut self) -> IterMut<Block>68     pub fn iter_mut(&mut self) -> IterMut<Block> {
69         self.table.iter_mut()
70     }
71 
72     /// Clears all entries in this jump table.
clear(&mut self)73     pub fn clear(&mut self) {
74         self.table.clear();
75     }
76 }
77 
78 impl Display for JumpTableData {
fmt(&self, fmt: &mut Formatter) -> fmt::Result79     fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
80         write!(fmt, "jump_table [")?;
81         match self.table.first() {
82             None => (),
83             Some(first) => write!(fmt, "{}", first)?,
84         }
85         for block in self.table.iter().skip(1) {
86             write!(fmt, ", {}", block)?;
87         }
88         write!(fmt, "]")
89     }
90 }
91 
92 #[cfg(test)]
93 mod tests {
94     use super::JumpTableData;
95     use crate::entity::EntityRef;
96     use crate::ir::Block;
97     use alloc::string::ToString;
98 
99     #[test]
empty()100     fn empty() {
101         let jt = JumpTableData::new();
102 
103         assert_eq!(jt.as_slice().get(0), None);
104         assert_eq!(jt.as_slice().get(10), None);
105 
106         assert_eq!(jt.to_string(), "jump_table []");
107 
108         let v = jt.as_slice();
109         assert_eq!(v, []);
110     }
111 
112     #[test]
insert()113     fn insert() {
114         let e1 = Block::new(1);
115         let e2 = Block::new(2);
116 
117         let mut jt = JumpTableData::new();
118 
119         jt.push_entry(e1);
120         jt.push_entry(e2);
121         jt.push_entry(e1);
122 
123         assert_eq!(jt.to_string(), "jump_table [block1, block2, block1]");
124 
125         let v = jt.as_slice();
126         assert_eq!(v, [e1, e2, e1]);
127     }
128 }
129