1use etk_asm::disasm::Offset;
3
4use etk_ops::shanghai::{Op, Operation};
5
6#[derive(Debug, Eq, PartialEq)]
8pub struct BasicBlock {
9 pub offset: usize,
11
12 pub ops: Vec<Op<[u8]>>,
14}
15
16impl BasicBlock {
17 pub fn size(&self) -> usize {
19 self.ops.iter().map(Op::size).sum()
20 }
21}
22
23#[derive(Debug, Default)]
25pub struct Separator {
26 complete_blocks: Vec<BasicBlock>,
27 in_progress: Option<BasicBlock>,
28}
29
30impl Separator {
31 pub fn new() -> Self {
33 Self::default()
34 }
35
36 pub fn push_all<I>(&mut self, iter: I) -> bool
40 where
41 I: IntoIterator<Item = Offset<Op<[u8]>>>,
42 {
43 let mut available = false;
44 for item in iter.into_iter() {
45 available |= self.push(item);
46 }
47 available
48 }
49
50 pub fn push(&mut self, off: Offset<Op<[u8]>>) -> bool {
53 if off.item.is_jump_target() {
54 let completed = self.in_progress.replace(BasicBlock {
56 offset: off.offset,
57 ops: vec![off.item],
58 });
59
60 if let Some(completed_block) = completed {
61 self.complete_blocks.push(completed_block);
62 return true;
63 } else {
64 return false;
65 }
66 }
67
68 let is_jump = off.item.is_jump() | off.item.is_exit();
69
70 match self.in_progress {
72 Some(ref mut p) => p.ops.push(off.item),
73 None => {
74 self.in_progress = Some(BasicBlock {
75 offset: off.offset,
76 ops: vec![off.item],
77 });
78 }
79 }
80
81 if is_jump {
83 let in_progress = self.in_progress.take().unwrap();
84 self.complete_blocks.push(in_progress);
85 true
86 } else {
87 false
88 }
89 }
90
91 pub fn take(&mut self) -> Vec<BasicBlock> {
93 std::mem::take(&mut self.complete_blocks)
94 }
95
96 #[must_use]
99 pub fn finish(&mut self) -> Option<BasicBlock> {
100 if self.complete_blocks.is_empty() {
101 self.in_progress.take()
102 } else {
103 panic!("not all basic blocks have been taken");
104 }
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use etk_ops::shanghai::*;
111
112 use super::*;
113
114 #[test]
115 fn three_pushes() {
116 let ops = vec![
117 Offset::new(0x00, Op::from(Push1([5]))),
118 Offset::new(0x02, Op::from(Push1([6]))),
119 Offset::new(0x04, Op::from(Push1([7]))),
120 ];
121
122 let blocks = [];
123
124 let last = Some(BasicBlock {
125 offset: 0x00,
126 ops: vec![
127 Op::from(Push1([5])),
128 Op::from(Push1([6])),
129 Op::from(Push1([7])),
130 ],
131 });
132
133 let mut sep = Separator::new();
134 let completed = sep.push_all(ops.into_iter());
135 assert!(!completed);
136 assert_eq!(sep.take(), blocks);
137 assert_eq!(sep.finish(), last);
138 }
139
140 #[test]
141 fn three_jumpdests() {
142 let ops = vec![
143 Offset::new(0x00, Op::from(JumpDest)),
144 Offset::new(0x01, Op::from(JumpDest)),
145 Offset::new(0x02, Op::from(JumpDest)),
146 ];
147
148 let blocks = [
149 BasicBlock {
150 offset: 0x00,
151 ops: vec![Op::from(JumpDest)],
152 },
153 BasicBlock {
154 offset: 0x01,
155 ops: vec![Op::from(JumpDest)],
156 },
157 ];
158
159 let last = Some(BasicBlock {
160 offset: 0x02,
161 ops: vec![Op::from(JumpDest)],
162 });
163
164 let mut sep = Separator::new();
165 let completed = sep.push_all(ops.into_iter());
166 assert!(completed);
167 assert_eq!(sep.take(), blocks);
168 assert_eq!(sep.finish(), last);
169 }
170
171 #[test]
172 fn jumpdest_jump_jumpdest_jump() {
173 let ops = vec![
174 Offset::new(0x00, Op::from(JumpDest)),
175 Offset::new(0x01, Op::from(Jump)),
176 Offset::new(0x02, Op::from(JumpDest)),
177 Offset::new(0x03, Op::from(Jump)),
178 ];
179
180 let blocks = [
181 BasicBlock {
182 offset: 0x00,
183 ops: vec![Op::from(JumpDest), Op::from(Jump)],
184 },
185 BasicBlock {
186 offset: 0x02,
187 ops: vec![Op::from(JumpDest), Op::from(Jump)],
188 },
189 ];
190
191 let last = None;
192
193 let mut sep = Separator::new();
194 let completed = sep.push_all(ops.into_iter());
195 assert!(completed);
196 assert_eq!(sep.take(), blocks);
197 assert_eq!(sep.finish(), last);
198 }
199
200 #[test]
201 fn jump_jumpdest_jump_jumpdest() {
202 let ops = vec![
203 Offset::new(0x00, Op::from(Jump)),
204 Offset::new(0x01, Op::from(JumpDest)),
205 Offset::new(0x02, Op::from(Jump)),
206 Offset::new(0x03, Op::from(JumpDest)),
207 ];
208
209 let blocks = [
210 BasicBlock {
211 offset: 0x00,
212 ops: vec![Op::from(Jump)],
213 },
214 BasicBlock {
215 offset: 0x01,
216 ops: vec![Op::from(JumpDest), Op::from(Jump)],
217 },
218 ];
219
220 let last = Some(BasicBlock {
221 offset: 0x03,
222 ops: vec![Op::from(JumpDest)],
223 });
224
225 let mut sep = Separator::new();
226 let completed = sep.push_all(ops.into_iter());
227 assert!(completed);
228 assert_eq!(sep.take(), blocks);
229 assert_eq!(sep.finish(), last);
230 }
231
232 #[test]
233 fn three_jumps() {
234 let ops = vec![
235 Offset::new(0x00, Op::from(Jump)),
236 Offset::new(0x01, Op::from(Jump)),
237 Offset::new(0x02, Op::from(Jump)),
238 ];
239
240 let blocks = [
241 BasicBlock {
242 offset: 0x00,
243 ops: vec![Op::from(Jump)],
244 },
245 BasicBlock {
246 offset: 0x01,
247 ops: vec![Op::from(Jump)],
248 },
249 BasicBlock {
250 offset: 0x02,
251 ops: vec![Op::from(Jump)],
252 },
253 ];
254
255 let last = None;
256
257 let mut sep = Separator::new();
258 let completed = sep.push_all(ops.into_iter());
259 assert!(completed);
260 assert_eq!(sep.take(), blocks);
261 assert_eq!(sep.finish(), last);
262 }
263}