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