b2c2_compiler/
optimize.rs1use super::*;
5
6pub fn remove_comment(statements: &[casl2::Statement]) -> Vec<casl2::Statement> {
8 let mut ret = vec![];
9
10 for stmt in statements.iter() {
11 if let casl2::Statement::Code { label, command, .. } = stmt {
12 ret.labeled(label.clone(), command.clone());
13 }
14 }
15
16 ret
17}
18
19pub fn remove_nop(statements: &[casl2::Statement]) -> Vec<casl2::Statement> {
21 let mut ret: Vec<casl2::Statement> = vec![];
22 let mut marge_label: Option<&str> = None;
23 let mut mapped_label = HashMap::<&str, &str>::new();
24
25 for stmt in statements.iter() {
26 match stmt {
27 casl2::Statement::Code {
28 label: None,
29 command: casl2::Command::Nop,
30 comment,
31 } => {
32 if let Some(comment) = comment {
33 ret.comment(comment.clone());
34 }
35 }
36 casl2::Statement::Code {
37 label: Some(label),
38 command: casl2::Command::Nop,
39 comment,
40 } => {
41 if let Some(comment) = comment {
42 ret.comment(comment.clone());
43 }
44 if let Some(key) = marge_label {
45 mapped_label.insert(label.as_str(), key);
46 } else {
47 marge_label = Some(label.as_str());
48 }
49 }
50 casl2::Statement::Code {
51 label,
52 command,
53 comment,
54 } => {
55 if let Some(key) = marge_label.take() {
56 if let Some(label) = label {
57 mapped_label.insert(label.as_str(), key);
58 }
59 ret.push(casl2::Statement::Code {
60 label: Some(key.into()),
61 command: command.clone(),
62 comment: comment.clone(),
63 });
64 } else {
65 ret.push(stmt.clone());
66 }
67 }
68 casl2::Statement::Comment { .. } => ret.push(stmt.clone()),
69 }
70 }
71
72 assert!(marge_label.is_none());
73
74 for command in ret.iter_mut().filter_map(|stmt| {
75 if let casl2::Statement::Code { command, .. } = stmt {
76 Some(command)
77 } else {
78 None
79 }
80 }) {
81 match command {
82 casl2::Command::Start { entry_point } => {
83 let new_label = entry_point
84 .as_ref()
85 .and_then(|label| mapped_label.get(label.as_str()));
86 if let Some(&label) = new_label {
87 *entry_point = Some(label.into());
88 }
89 }
90 casl2::Command::Dc { constants } => {
91 for label in constants.iter_mut().filter_map(|cnst| {
92 if let casl2::Constant::Label(label) = cnst {
93 Some(label)
94 } else {
95 None
96 }
97 }) {
98 if let Some(&new_label) = mapped_label.get(label.as_str()) {
99 *label = new_label.into();
100 }
101 }
102 }
103 casl2::Command::In { pos, len } | casl2::Command::Out { pos, len } => {
104 if let Some(&label) = mapped_label.get(pos.as_str()) {
105 *pos = label.into();
106 }
107 if let Some(&label) = mapped_label.get(len.as_str()) {
108 *len = label.into();
109 }
110 }
111 casl2::Command::A { adr, .. } | casl2::Command::P { adr, .. } => {
112 if let casl2::Adr::Label(label) = adr {
113 if let Some(&new_label) = mapped_label.get(label.as_str()) {
114 *label = new_label.into();
115 }
116 }
117 }
118 casl2::Command::End
119 | casl2::Command::Ds { .. }
120 | casl2::Command::Rpush
121 | casl2::Command::Rpop
122 | casl2::Command::R { .. }
123 | casl2::Command::Pop { .. }
124 | casl2::Command::Ret => {}
125 casl2::Command::Nop => unreachable!("BUG"),
126 casl2::Command::DebugBasicStep { .. } => {}
127 }
128 }
129
130 ret
131}
132
133pub fn remove_unreferenced_label(statements: &[casl2::Statement]) -> Vec<casl2::Statement> {
135 let mut ret: Vec<casl2::Statement> = vec![];
136 let mut set = std::collections::HashSet::<&str>::new();
137
138 for command in statements.iter().filter_map(|stmt| {
139 if let casl2::Statement::Code { command, .. } = stmt {
140 Some(command)
141 } else {
142 None
143 }
144 }) {
145 match command {
146 casl2::Command::Start { entry_point } => {
147 if let Some(label) = entry_point {
148 set.insert(label.as_str());
149 }
150 }
151 casl2::Command::Dc { constants } => {
152 for cnst in constants.iter() {
153 if let casl2::Constant::Label(label) = cnst {
154 set.insert(label.as_str());
155 }
156 }
157 }
158 casl2::Command::In { pos, len } | casl2::Command::Out { pos, len } => {
159 set.insert(pos.as_str());
160 set.insert(len.as_str());
161 }
162 casl2::Command::A { adr, .. } | casl2::Command::P { adr, .. } => {
163 if let casl2::Adr::Label(label) = adr {
164 set.insert(label.as_str());
165 }
166 }
167 casl2::Command::End
168 | casl2::Command::Ds { .. }
169 | casl2::Command::Rpush
170 | casl2::Command::Rpop
171 | casl2::Command::R { .. }
172 | casl2::Command::Pop { .. }
173 | casl2::Command::Ret
174 | casl2::Command::Nop
175 | casl2::Command::DebugBasicStep { .. } => {}
176 }
177 }
178
179 for stmt in statements.iter() {
180 match stmt {
181 casl2::Statement::Code {
182 label: Some(label),
183 command,
184 comment,
185 } if !matches!(command, casl2::Command::Start { .. })
186 && !set.contains(label.as_str()) =>
187 {
188 ret.push(casl2::Statement::Code {
189 label: None,
190 command: command.clone(),
191 comment: comment.clone(),
192 });
193 }
194 _ => ret.push(stmt.clone()),
195 }
196 }
197
198 ret
199}
200
201trait Casl2StatementExtension {
203 fn is_jump_code(&self) -> bool;
204 fn is_push_code(&self) -> bool;
205 fn is_pop_code(&self) -> bool;
206}
207
208impl Casl2StatementExtension for casl2::Statement {
209 fn is_jump_code(&self) -> bool {
210 if let casl2::Statement::Code {
211 command: casl2::Command::P { code, .. },
212 ..
213 } = self
214 {
215 use casl2::P::*;
216 match code {
217 Jpl | Jmi | Jnz | Jze | Jov | Jump => true,
218 Push | Call | Svc => false,
219 }
220 } else {
221 false
222 }
223 }
224
225 fn is_push_code(&self) -> bool {
226 matches!(
227 self,
228 casl2::Statement::Code {
229 command: casl2::Command::P {
230 code: casl2::P::Push,
231 ..
232 },
233 ..
234 }
235 )
236 }
237
238 fn is_pop_code(&self) -> bool {
239 matches!(
240 self,
241 casl2::Statement::Code {
242 command: casl2::Command::Pop { .. },
243 ..
244 }
245 )
246 }
247}
248
249fn can_include_snippet(stmt: &casl2::Statement) -> bool {
250 stmt.is_code() && !stmt.is_jump_code()
251}
252
253pub fn collect_duplicates(mut statements: Vec<casl2::Statement>) -> Vec<casl2::Statement> {
255 let mut snippets = vec![];
256 let mut id = 0;
257
258 loop {
259 let end = {
260 let mut end = statements.len();
261 for (i, stmt) in statements.iter().enumerate() {
262 if let casl2::Statement::Code {
263 command: casl2::Command::Ret,
264 ..
265 } = stmt
266 {
267 end = i;
268 break;
269 }
270 }
271 end
272 };
273
274 let mut hset = std::collections::HashSet::new();
275
276 let mut best: Option<(usize, usize, Vec<usize>)> = None;
277
278 for i in 0..end {
279 if !can_include_snippet(&statements[i]) {
280 continue;
281 }
282 let mut stack: i32 = 0;
283 if statements[i].is_pop_code() {
284 continue;
285 } else if statements[i].is_push_code() {
286 stack += 1;
287 }
288 for r in i + 1..end {
289 if !can_include_snippet(&statements[r]) {
290 break;
291 }
292 if statements[r].is_pop_code() {
293 stack -= 1;
294 if stack < 0 {
295 break;
296 }
297 } else if statements[r].is_push_code() {
298 stack += 1;
299 }
300 if stack != 0 {
301 continue;
302 }
303 let len = r - i + 1;
304 if hset.contains(&(i, len)) {
305 continue;
306 }
307 let mut dups = vec![i];
308 for k in r + 1..end - len {
309 if statements[i..=r] != statements[k..k + len] {
310 continue;
311 }
312 if hset.insert((k, len)) {
313 dups.push(k);
314 }
315 }
316 if dups.len() < 2 {
317 continue;
318 }
319 let before_cost = len * dups.len();
320 let after_cost = len + 1 + dups.len();
321 if after_cost >= before_cost {
322 continue;
323 }
324 let score = before_cost - after_cost;
325 if let Some((best_score, best_len, _)) = best.as_ref() {
326 if *best_score > score {
327 continue;
328 }
329 if *best_score == score && *best_len <= len {
330 continue;
331 }
332 }
333 best = Some((score, len, dups));
334 }
335 }
336
337 if let Some((_, len, mut dups)) = best {
338 id += 1;
339 let name = format!("F{:03}", id);
340 let mut snippet = None;
341 while let Some(pos) = dups.pop() {
342 let rest = statements.split_off(pos + len);
343 if snippet.is_none() {
344 let code = statements.split_off(pos);
345 snippet = Some(code);
346 } else {
347 statements.truncate(pos);
348 }
349 statements.code(format!(" CALL {}", name));
350 statements.extend(rest);
351 }
352 if let Some(mut snippet) = snippet {
353 if let Some(casl2::Statement::Code { label, .. }) = snippet.first_mut() {
354 *label = Some(name.into());
355 }
356 snippet.code(casl2::Command::Ret);
357 snippets.push(snippet);
358 }
359 } else {
360 break;
361 }
362 }
363
364 let rest = {
365 let mut end = statements.len();
366 for (i, stmt) in statements.iter().enumerate() {
367 if let casl2::Statement::Code {
368 command: casl2::Command::Ret,
369 ..
370 } = stmt
371 {
372 end = i + 1;
373 break;
374 }
375 }
376 statements.split_off(end)
377 };
378
379 for snippet in snippets {
380 statements.extend(snippet);
381 }
382
383 statements.extend(rest);
384
385 statements
386}