1use std::collections::{HashMap, HashSet};
19use synth_cfg::{BlockId, Cfg};
20
21pub trait OptimizationPass {
23 fn name(&self) -> &'static str;
25
26 fn run(&mut self, cfg: &mut Cfg, instructions: &mut Vec<Instruction>) -> OptResult;
28}
29
30#[derive(Debug, Clone)]
32pub struct OptResult {
33 pub changed: bool,
35
36 pub removed_count: usize,
38
39 pub added_count: usize,
41
42 pub modified_count: usize,
44}
45
46impl OptResult {
47 pub fn no_change() -> Self {
48 Self {
49 changed: false,
50 removed_count: 0,
51 added_count: 0,
52 modified_count: 0,
53 }
54 }
55
56 pub fn merge(&mut self, other: OptResult) {
57 self.changed |= other.changed;
58 self.removed_count += other.removed_count;
59 self.added_count += other.added_count;
60 self.modified_count += other.modified_count;
61 }
62}
63
64#[derive(Debug, Clone, PartialEq, Eq)]
66pub struct Instruction {
67 pub id: usize,
68 pub opcode: Opcode,
69 pub block_id: BlockId,
70 pub is_dead: bool,
71}
72
73#[derive(Debug, Clone, PartialEq, Eq)]
74pub enum Opcode {
75 Nop,
76 Add {
78 dest: Reg,
79 src1: Reg,
80 src2: Reg,
81 },
82 Sub {
83 dest: Reg,
84 src1: Reg,
85 src2: Reg,
86 },
87 Mul {
88 dest: Reg,
89 src1: Reg,
90 src2: Reg,
91 },
92 DivS {
93 dest: Reg,
94 src1: Reg,
95 src2: Reg,
96 }, DivU {
98 dest: Reg,
99 src1: Reg,
100 src2: Reg,
101 }, RemS {
103 dest: Reg,
104 src1: Reg,
105 src2: Reg,
106 }, RemU {
108 dest: Reg,
109 src1: Reg,
110 src2: Reg,
111 }, And {
114 dest: Reg,
115 src1: Reg,
116 src2: Reg,
117 },
118 Or {
119 dest: Reg,
120 src1: Reg,
121 src2: Reg,
122 },
123 Xor {
124 dest: Reg,
125 src1: Reg,
126 src2: Reg,
127 },
128 Shl {
129 dest: Reg,
130 src1: Reg,
131 src2: Reg,
132 }, ShrS {
134 dest: Reg,
135 src1: Reg,
136 src2: Reg,
137 }, ShrU {
139 dest: Reg,
140 src1: Reg,
141 src2: Reg,
142 }, Rotl {
144 dest: Reg,
145 src1: Reg,
146 src2: Reg,
147 }, Rotr {
149 dest: Reg,
150 src1: Reg,
151 src2: Reg,
152 }, Clz {
155 dest: Reg,
156 src: Reg,
157 }, Ctz {
159 dest: Reg,
160 src: Reg,
161 }, Popcnt {
163 dest: Reg,
164 src: Reg,
165 }, Extend8S {
168 dest: Reg,
169 src: Reg,
170 }, Extend16S {
172 dest: Reg,
173 src: Reg,
174 }, Eqz {
177 dest: Reg,
178 src: Reg,
179 }, Eq {
181 dest: Reg,
182 src1: Reg,
183 src2: Reg,
184 },
185 Ne {
186 dest: Reg,
187 src1: Reg,
188 src2: Reg,
189 },
190 LtS {
191 dest: Reg,
192 src1: Reg,
193 src2: Reg,
194 }, LtU {
196 dest: Reg,
197 src1: Reg,
198 src2: Reg,
199 }, LeS {
201 dest: Reg,
202 src1: Reg,
203 src2: Reg,
204 }, LeU {
206 dest: Reg,
207 src1: Reg,
208 src2: Reg,
209 }, GtS {
211 dest: Reg,
212 src1: Reg,
213 src2: Reg,
214 }, GtU {
216 dest: Reg,
217 src1: Reg,
218 src2: Reg,
219 }, GeS {
221 dest: Reg,
222 src1: Reg,
223 src2: Reg,
224 }, GeU {
226 dest: Reg,
227 src1: Reg,
228 src2: Reg,
229 }, Load {
232 dest: Reg,
233 addr: u32,
234 },
235 Store {
236 src: Reg,
237 addr: u32,
238 },
239 I64Load {
241 dest_lo: Reg,
242 dest_hi: Reg,
243 addr: u32,
244 },
245 MemLoad {
248 dest: Reg,
249 addr: Reg,
250 offset: u32,
251 },
252 MemStore {
254 src: Reg,
255 addr: Reg,
256 offset: u32,
257 },
258
259 MemLoadSubword {
264 dest: Reg,
265 addr: Reg,
266 offset: u32,
267 width: u32, signed: bool,
269 },
270 MemStoreSubword {
274 src: Reg,
275 addr: Reg,
276 offset: u32,
277 width: u32, },
279
280 GlobalGet {
286 dest: Reg,
287 idx: u32,
288 },
289 GlobalSet {
292 src: Reg,
293 idx: u32,
294 },
295
296 MemorySize {
300 dest: Reg,
301 },
302 MemoryGrow {
306 dest: Reg,
307 delta: Reg,
308 },
309 Branch {
311 target: BlockId,
312 },
313 CondBranch {
314 cond: Reg,
315 target: BlockId,
316 },
317 Call {
333 dest: Reg,
334 func_idx: u32,
335 },
336 Return {
337 value: Option<Reg>,
338 },
339 Label {
341 id: BlockId,
342 },
343 Copy {
345 dest: Reg,
346 src: Reg,
347 },
348 TeeStore {
351 dest: Reg,
352 src: Reg,
353 addr: u32,
354 },
355 Select {
357 dest: Reg,
358 val_true: Reg,
359 val_false: Reg,
360 cond: Reg,
361 },
362 Const {
364 dest: Reg,
365 value: i32,
366 },
367
368 I64Add {
371 dest_lo: Reg,
372 dest_hi: Reg,
373 src1_lo: Reg,
374 src1_hi: Reg,
375 src2_lo: Reg,
376 src2_hi: Reg,
377 },
378 I64Sub {
379 dest_lo: Reg,
380 dest_hi: Reg,
381 src1_lo: Reg,
382 src1_hi: Reg,
383 src2_lo: Reg,
384 src2_hi: Reg,
385 },
386 I64And {
387 dest_lo: Reg,
388 dest_hi: Reg,
389 src1_lo: Reg,
390 src1_hi: Reg,
391 src2_lo: Reg,
392 src2_hi: Reg,
393 },
394 I64Or {
395 dest_lo: Reg,
396 dest_hi: Reg,
397 src1_lo: Reg,
398 src1_hi: Reg,
399 src2_lo: Reg,
400 src2_hi: Reg,
401 },
402 I64Xor {
403 dest_lo: Reg,
404 dest_hi: Reg,
405 src1_lo: Reg,
406 src1_hi: Reg,
407 src2_lo: Reg,
408 src2_hi: Reg,
409 },
410 I64Const {
411 dest_lo: Reg,
412 dest_hi: Reg,
413 value: i64,
414 },
415
416 I64Eq {
419 dest: Reg,
420 src1_lo: Reg,
421 src1_hi: Reg,
422 src2_lo: Reg,
423 src2_hi: Reg,
424 },
425 I64Ne {
426 dest: Reg,
427 src1_lo: Reg,
428 src1_hi: Reg,
429 src2_lo: Reg,
430 src2_hi: Reg,
431 },
432 I64LtS {
433 dest: Reg,
434 src1_lo: Reg,
435 src1_hi: Reg,
436 src2_lo: Reg,
437 src2_hi: Reg,
438 },
439 I64GtS {
440 dest: Reg,
441 src1_lo: Reg,
442 src1_hi: Reg,
443 src2_lo: Reg,
444 src2_hi: Reg,
445 },
446 I64LeS {
447 dest: Reg,
448 src1_lo: Reg,
449 src1_hi: Reg,
450 src2_lo: Reg,
451 src2_hi: Reg,
452 },
453 I64GeS {
454 dest: Reg,
455 src1_lo: Reg,
456 src1_hi: Reg,
457 src2_lo: Reg,
458 src2_hi: Reg,
459 },
460 I64LtU {
462 dest: Reg,
463 src1_lo: Reg,
464 src1_hi: Reg,
465 src2_lo: Reg,
466 src2_hi: Reg,
467 },
468 I64GtU {
469 dest: Reg,
470 src1_lo: Reg,
471 src1_hi: Reg,
472 src2_lo: Reg,
473 src2_hi: Reg,
474 },
475 I64LeU {
476 dest: Reg,
477 src1_lo: Reg,
478 src1_hi: Reg,
479 src2_lo: Reg,
480 src2_hi: Reg,
481 },
482 I64GeU {
483 dest: Reg,
484 src1_lo: Reg,
485 src1_hi: Reg,
486 src2_lo: Reg,
487 src2_hi: Reg,
488 },
489 I64Eqz {
490 dest: Reg,
491 src_lo: Reg,
492 src_hi: Reg,
493 },
494
495 I64Clz {
497 dest: Reg,
498 src_lo: Reg,
499 src_hi: Reg,
500 },
501 I64Ctz {
502 dest: Reg,
503 src_lo: Reg,
504 src_hi: Reg,
505 },
506 I64Popcnt {
507 dest: Reg,
508 src_lo: Reg,
509 src_hi: Reg,
510 },
511
512 I64Extend8S {
514 dest_lo: Reg,
515 dest_hi: Reg,
516 src_lo: Reg,
517 },
518 I64Extend16S {
519 dest_lo: Reg,
520 dest_hi: Reg,
521 src_lo: Reg,
522 },
523 I64Extend32S {
524 dest_lo: Reg,
525 dest_hi: Reg,
526 src_lo: Reg,
527 },
528
529 I64ExtendI32U {
539 dest_lo: Reg,
540 dest_hi: Reg,
541 src: Reg,
542 },
543
544 I64ExtendI32S {
547 dest_lo: Reg,
548 dest_hi: Reg,
549 src: Reg,
550 },
551
552 I32WrapI64 {
555 dest: Reg,
556 src_lo: Reg,
557 },
558
559 I64Mul {
561 dest_lo: Reg,
562 dest_hi: Reg,
563 src1_lo: Reg,
564 src1_hi: Reg,
565 src2_lo: Reg,
566 src2_hi: Reg,
567 },
568 I64DivS {
570 dest_lo: Reg,
571 dest_hi: Reg,
572 src1_lo: Reg,
573 src1_hi: Reg,
574 src2_lo: Reg,
575 src2_hi: Reg,
576 },
577 I64DivU {
578 dest_lo: Reg,
579 dest_hi: Reg,
580 src1_lo: Reg,
581 src1_hi: Reg,
582 src2_lo: Reg,
583 src2_hi: Reg,
584 },
585 I64RemS {
586 dest_lo: Reg,
587 dest_hi: Reg,
588 src1_lo: Reg,
589 src1_hi: Reg,
590 src2_lo: Reg,
591 src2_hi: Reg,
592 },
593 I64RemU {
594 dest_lo: Reg,
595 dest_hi: Reg,
596 src1_lo: Reg,
597 src1_hi: Reg,
598 src2_lo: Reg,
599 src2_hi: Reg,
600 },
601 I64Shl {
602 dest_lo: Reg,
603 dest_hi: Reg,
604 src1_lo: Reg,
605 src1_hi: Reg,
606 src2_lo: Reg,
607 src2_hi: Reg,
608 },
609 I64ShrS {
610 dest_lo: Reg,
611 dest_hi: Reg,
612 src1_lo: Reg,
613 src1_hi: Reg,
614 src2_lo: Reg,
615 src2_hi: Reg,
616 },
617 I64ShrU {
618 dest_lo: Reg,
619 dest_hi: Reg,
620 src1_lo: Reg,
621 src1_hi: Reg,
622 src2_lo: Reg,
623 src2_hi: Reg,
624 },
625 I64Rotl {
626 dest_lo: Reg,
627 dest_hi: Reg,
628 src1_lo: Reg,
629 src1_hi: Reg,
630 src2_lo: Reg,
631 src2_hi: Reg,
632 },
633 I64Rotr {
634 dest_lo: Reg,
635 dest_hi: Reg,
636 src1_lo: Reg,
637 src1_hi: Reg,
638 src2_lo: Reg,
639 src2_hi: Reg,
640 },
641}
642
643#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
644pub struct Reg(pub u32);
645
646pub struct DeadCodeElimination {
648 verbose: bool,
650}
651
652impl DeadCodeElimination {
653 pub fn new() -> Self {
654 Self { verbose: false }
655 }
656
657 pub fn with_verbose(mut self) -> Self {
658 self.verbose = true;
659 self
660 }
661
662 fn mark_reachable_blocks(&self, cfg: &Cfg) -> HashSet<BlockId> {
664 let mut reachable = HashSet::new();
665 let mut worklist = vec![cfg.entry];
666
667 while let Some(block_id) = worklist.pop() {
668 if reachable.contains(&block_id) {
669 continue;
670 }
671
672 reachable.insert(block_id);
673
674 if let Some(block) = cfg.block(block_id) {
675 for &succ in &block.successors {
676 if !reachable.contains(&succ) {
677 worklist.push(succ);
678 }
679 }
680 }
681 }
682
683 reachable
684 }
685
686 fn remove_unreachable(&self, cfg: &Cfg, instructions: &mut [Instruction]) -> OptResult {
688 let reachable = self.mark_reachable_blocks(cfg);
689
690 let mut removed = 0;
691 for inst in instructions.iter_mut() {
692 if !reachable.contains(&inst.block_id) && !inst.is_dead {
693 inst.is_dead = true;
694 removed += 1;
695 }
696 }
697
698 if self.verbose && removed > 0 {
699 eprintln!("DCE: Removed {} unreachable instructions", removed);
700 }
701
702 OptResult {
703 changed: removed > 0,
704 removed_count: removed,
705 added_count: 0,
706 modified_count: 0,
707 }
708 }
709}
710
711impl Default for DeadCodeElimination {
712 fn default() -> Self {
713 Self::new()
714 }
715}
716
717impl OptimizationPass for DeadCodeElimination {
718 fn name(&self) -> &'static str {
719 "dead-code-elimination"
720 }
721
722 fn run(&mut self, cfg: &mut Cfg, instructions: &mut Vec<Instruction>) -> OptResult {
723 self.remove_unreachable(cfg, instructions)
724 }
725}
726
727pub struct ConstantFolding {
729 verbose: bool,
730}
731
732impl ConstantFolding {
733 pub fn new() -> Self {
734 Self { verbose: false }
735 }
736
737 pub fn with_verbose(mut self) -> Self {
738 self.verbose = true;
739 self
740 }
741
742 fn fold_constants(&mut self, instructions: &mut [Instruction]) -> OptResult {
744 let mut const_values: HashMap<Reg, i32> = HashMap::new();
746 let mut modified = 0;
747
748 for inst in instructions.iter_mut() {
749 if inst.is_dead {
750 continue;
751 }
752
753 let opcode = inst.opcode.clone();
755
756 match opcode {
757 Opcode::Const { dest, value } => {
759 const_values.insert(dest, value);
760 }
761
762 Opcode::Add { dest, src1, src2 } => {
764 if let (Some(&val1), Some(&val2)) =
765 (const_values.get(&src1), const_values.get(&src2))
766 {
767 let result = val1.wrapping_add(val2);
768 inst.opcode = Opcode::Const {
769 dest,
770 value: result,
771 };
772 const_values.insert(dest, result);
773 modified += 1;
774
775 if self.verbose {
776 eprintln!(
777 "Folded: add {} = {} + {} -> const {} = {}",
778 dest.0, val1, val2, dest.0, result
779 );
780 }
781 }
782 }
783
784 Opcode::Sub { dest, src1, src2 } => {
786 if let (Some(&val1), Some(&val2)) =
787 (const_values.get(&src1), const_values.get(&src2))
788 {
789 let result = val1.wrapping_sub(val2);
790 inst.opcode = Opcode::Const {
791 dest,
792 value: result,
793 };
794 const_values.insert(dest, result);
795 modified += 1;
796
797 if self.verbose {
798 eprintln!(
799 "Folded: sub {} = {} - {} -> const {} = {}",
800 dest.0, val1, val2, dest.0, result
801 );
802 }
803 }
804 }
805
806 Opcode::Mul { dest, src1, src2 } => {
808 if let (Some(&val1), Some(&val2)) =
809 (const_values.get(&src1), const_values.get(&src2))
810 {
811 let result = val1.wrapping_mul(val2);
812 inst.opcode = Opcode::Const {
813 dest,
814 value: result,
815 };
816 const_values.insert(dest, result);
817 modified += 1;
818
819 if self.verbose {
820 eprintln!(
821 "Folded: mul {} = {} * {} -> const {} = {}",
822 dest.0, val1, val2, dest.0, result
823 );
824 }
825 }
826 }
827
828 _ => {}
829 }
830 }
831
832 if self.verbose && modified > 0 {
833 eprintln!("Constant folding: {} operations folded", modified);
834 }
835
836 OptResult {
837 changed: modified > 0,
838 removed_count: 0,
839 added_count: 0,
840 modified_count: modified,
841 }
842 }
843}
844
845impl Default for ConstantFolding {
846 fn default() -> Self {
847 Self::new()
848 }
849}
850
851impl OptimizationPass for ConstantFolding {
852 fn name(&self) -> &'static str {
853 "constant-folding"
854 }
855
856 fn run(&mut self, _cfg: &mut Cfg, instructions: &mut Vec<Instruction>) -> OptResult {
857 self.fold_constants(instructions)
858 }
859}
860
861#[derive(Debug, Clone, PartialEq, Eq, Hash)]
863enum ExprKey {
864 Add(Reg, Reg),
865 Sub(Reg, Reg),
866 Mul(Reg, Reg),
867 Load(u32),
868}
869
870pub struct CommonSubexpressionElimination {
872 verbose: bool,
873}
874
875impl CommonSubexpressionElimination {
876 pub fn new() -> Self {
877 Self { verbose: false }
878 }
879
880 pub fn with_verbose(mut self) -> Self {
881 self.verbose = true;
882 self
883 }
884
885 fn eliminate_cse(&mut self, instructions: &mut [Instruction]) -> OptResult {
887 let mut expr_map: HashMap<ExprKey, Reg> = HashMap::new();
889
890 let mut reg_map: HashMap<Reg, Reg> = HashMap::new();
892
893 let mut modified = 0;
894
895 for inst in instructions.iter_mut() {
896 if inst.is_dead {
897 continue;
898 }
899
900 let opcode = inst.opcode.clone();
902
903 let resolve = |r: Reg| -> Reg { reg_map.get(&r).copied().unwrap_or(r) };
905
906 let dests_to_invalidate: Vec<Reg> = match &opcode {
927 Opcode::Add { dest, .. }
928 | Opcode::Sub { dest, .. }
929 | Opcode::Mul { dest, .. }
930 | Opcode::DivS { dest, .. }
931 | Opcode::DivU { dest, .. }
932 | Opcode::RemS { dest, .. }
933 | Opcode::RemU { dest, .. }
934 | Opcode::And { dest, .. }
935 | Opcode::Or { dest, .. }
936 | Opcode::Xor { dest, .. }
937 | Opcode::Shl { dest, .. }
938 | Opcode::ShrS { dest, .. }
939 | Opcode::ShrU { dest, .. }
940 | Opcode::Rotl { dest, .. }
941 | Opcode::Rotr { dest, .. }
942 | Opcode::Clz { dest, .. }
943 | Opcode::Ctz { dest, .. }
944 | Opcode::Popcnt { dest, .. }
945 | Opcode::Extend8S { dest, .. }
946 | Opcode::Extend16S { dest, .. }
947 | Opcode::Eqz { dest, .. }
948 | Opcode::Eq { dest, .. }
949 | Opcode::Ne { dest, .. }
950 | Opcode::LtS { dest, .. }
951 | Opcode::LtU { dest, .. }
952 | Opcode::LeS { dest, .. }
953 | Opcode::LeU { dest, .. }
954 | Opcode::GtS { dest, .. }
955 | Opcode::GtU { dest, .. }
956 | Opcode::GeS { dest, .. }
957 | Opcode::GeU { dest, .. }
958 | Opcode::Load { dest, .. }
959 | Opcode::MemLoad { dest, .. }
960 | Opcode::Copy { dest, .. }
961 | Opcode::TeeStore { dest, .. }
962 | Opcode::Select { dest, .. }
963 | Opcode::Const { dest, .. }
964 | Opcode::I64Eq { dest, .. }
965 | Opcode::I64Ne { dest, .. }
966 | Opcode::I64LtS { dest, .. }
967 | Opcode::I64LtU { dest, .. }
968 | Opcode::I64LeS { dest, .. }
969 | Opcode::I64LeU { dest, .. }
970 | Opcode::I64GtS { dest, .. }
971 | Opcode::I64GtU { dest, .. }
972 | Opcode::I64GeS { dest, .. }
973 | Opcode::I64GeU { dest, .. }
974 | Opcode::I64Eqz { dest, .. }
975 | Opcode::I64Clz { dest, .. }
976 | Opcode::I64Ctz { dest, .. }
977 | Opcode::I64Popcnt { dest, .. }
978 | Opcode::I32WrapI64 { dest, .. }
979 | Opcode::MemLoadSubword { dest, .. }
980 | Opcode::GlobalGet { dest, .. }
981 | Opcode::MemorySize { dest, .. }
982 | Opcode::MemoryGrow { dest, .. }
983 | Opcode::Call { dest, .. } => vec![*dest],
984 Opcode::I64Add {
985 dest_lo, dest_hi, ..
986 }
987 | Opcode::I64Sub {
988 dest_lo, dest_hi, ..
989 }
990 | Opcode::I64And {
991 dest_lo, dest_hi, ..
992 }
993 | Opcode::I64Or {
994 dest_lo, dest_hi, ..
995 }
996 | Opcode::I64Xor {
997 dest_lo, dest_hi, ..
998 }
999 | Opcode::I64Mul {
1000 dest_lo, dest_hi, ..
1001 }
1002 | Opcode::I64DivS {
1003 dest_lo, dest_hi, ..
1004 }
1005 | Opcode::I64DivU {
1006 dest_lo, dest_hi, ..
1007 }
1008 | Opcode::I64RemS {
1009 dest_lo, dest_hi, ..
1010 }
1011 | Opcode::I64RemU {
1012 dest_lo, dest_hi, ..
1013 }
1014 | Opcode::I64Shl {
1015 dest_lo, dest_hi, ..
1016 }
1017 | Opcode::I64ShrS {
1018 dest_lo, dest_hi, ..
1019 }
1020 | Opcode::I64ShrU {
1021 dest_lo, dest_hi, ..
1022 }
1023 | Opcode::I64Rotl {
1024 dest_lo, dest_hi, ..
1025 }
1026 | Opcode::I64Rotr {
1027 dest_lo, dest_hi, ..
1028 }
1029 | Opcode::I64Const {
1030 dest_lo, dest_hi, ..
1031 }
1032 | Opcode::I64Load {
1033 dest_lo, dest_hi, ..
1034 }
1035 | Opcode::I64Extend8S {
1036 dest_lo, dest_hi, ..
1037 }
1038 | Opcode::I64Extend16S {
1039 dest_lo, dest_hi, ..
1040 }
1041 | Opcode::I64Extend32S {
1042 dest_lo, dest_hi, ..
1043 }
1044 | Opcode::I64ExtendI32U {
1045 dest_lo, dest_hi, ..
1046 }
1047 | Opcode::I64ExtendI32S {
1048 dest_lo, dest_hi, ..
1049 } => vec![*dest_lo, *dest_hi],
1050 _ => Vec::new(),
1051 };
1052
1053 match opcode {
1054 Opcode::Add { dest, src1, src2 } => {
1055 let src1 = resolve(src1);
1056 let src2 = resolve(src2);
1057 let key = ExprKey::Add(src1, src2);
1058
1059 if let Some(&existing) = expr_map.get(&key) {
1060 inst.opcode = Opcode::Const { dest, value: 0 }; inst.is_dead = true; reg_map.insert(dest, existing);
1064 modified += 1;
1065
1066 if self.verbose {
1067 eprintln!(
1068 "CSE: Eliminated add r{} = r{} + r{}, reuse r{}",
1069 dest.0, src1.0, src2.0, existing.0
1070 );
1071 }
1072 } else {
1073 expr_map.insert(key, dest);
1074 inst.opcode = Opcode::Add { dest, src1, src2 };
1076 }
1077 }
1078
1079 Opcode::Sub { dest, src1, src2 } => {
1080 let src1 = resolve(src1);
1081 let src2 = resolve(src2);
1082 let key = ExprKey::Sub(src1, src2);
1083
1084 if let Some(&existing) = expr_map.get(&key) {
1085 inst.opcode = Opcode::Const { dest, value: 0 };
1086 inst.is_dead = true;
1087 reg_map.insert(dest, existing);
1088 modified += 1;
1089
1090 if self.verbose {
1091 eprintln!(
1092 "CSE: Eliminated sub r{} = r{} - r{}, reuse r{}",
1093 dest.0, src1.0, src2.0, existing.0
1094 );
1095 }
1096 } else {
1097 expr_map.insert(key, dest);
1098 inst.opcode = Opcode::Sub { dest, src1, src2 };
1099 }
1100 }
1101
1102 Opcode::Mul { dest, src1, src2 } => {
1103 let src1 = resolve(src1);
1104 let src2 = resolve(src2);
1105 let key = ExprKey::Mul(src1, src2);
1106
1107 if let Some(&existing) = expr_map.get(&key) {
1108 inst.opcode = Opcode::Const { dest, value: 0 };
1109 inst.is_dead = true;
1110 reg_map.insert(dest, existing);
1111 modified += 1;
1112
1113 if self.verbose {
1114 eprintln!(
1115 "CSE: Eliminated mul r{} = r{} * r{}, reuse r{}",
1116 dest.0, src1.0, src2.0, existing.0
1117 );
1118 }
1119 } else {
1120 expr_map.insert(key, dest);
1121 inst.opcode = Opcode::Mul { dest, src1, src2 };
1122 }
1123 }
1124
1125 Opcode::Load { dest, addr } => {
1126 let key = ExprKey::Load(addr);
1127
1128 if let Some(&existing) = expr_map.get(&key) {
1129 inst.opcode = Opcode::Const { dest, value: 0 };
1130 inst.is_dead = true;
1131 reg_map.insert(dest, existing);
1132 modified += 1;
1133
1134 if self.verbose {
1135 eprintln!(
1136 "CSE: Eliminated load r{} = [0x{:x}], reuse r{}",
1137 dest.0, addr, existing.0
1138 );
1139 }
1140 } else {
1141 expr_map.insert(key, dest);
1142 }
1143 }
1144
1145 Opcode::Store { src, addr } => {
1149 let src = resolve(src);
1150 inst.opcode = Opcode::Store { src, addr };
1151 expr_map.remove(&ExprKey::Load(addr));
1152 }
1153
1154 Opcode::TeeStore { dest, src, addr } => {
1157 let src = resolve(src);
1158 inst.opcode = Opcode::TeeStore { dest, src, addr };
1159 expr_map.remove(&ExprKey::Load(addr));
1160 }
1161
1162 Opcode::MemLoad { dest, addr, offset } => {
1176 let addr = resolve(addr);
1177 inst.opcode = Opcode::MemLoad { dest, addr, offset };
1178 }
1179
1180 Opcode::MemStore { src, addr, offset } => {
1183 let src = resolve(src);
1184 let addr = resolve(addr);
1185 inst.opcode = Opcode::MemStore { src, addr, offset };
1186 }
1187
1188 Opcode::Extend8S { dest, src } => {
1190 let src = resolve(src);
1191 inst.opcode = Opcode::Extend8S { dest, src };
1192 }
1193 Opcode::Extend16S { dest, src } => {
1194 let src = resolve(src);
1195 inst.opcode = Opcode::Extend16S { dest, src };
1196 }
1197
1198 Opcode::Label { .. } => {
1201 expr_map.retain(|k, _| !matches!(k, ExprKey::Load(_)));
1202 }
1203
1204 Opcode::CondBranch { cond, target } => {
1206 let cond = resolve(cond);
1207 inst.opcode = Opcode::CondBranch { cond, target };
1208 expr_map.retain(|k, _| !matches!(k, ExprKey::Load(_)));
1210 }
1211
1212 Opcode::Eq { dest, src1, src2 } => {
1214 let src1 = resolve(src1);
1215 let src2 = resolve(src2);
1216 inst.opcode = Opcode::Eq { dest, src1, src2 };
1217 }
1218 Opcode::Ne { dest, src1, src2 } => {
1219 let src1 = resolve(src1);
1220 let src2 = resolve(src2);
1221 inst.opcode = Opcode::Ne { dest, src1, src2 };
1222 }
1223 Opcode::LtS { dest, src1, src2 } => {
1224 let src1 = resolve(src1);
1225 let src2 = resolve(src2);
1226 inst.opcode = Opcode::LtS { dest, src1, src2 };
1227 }
1228 Opcode::LtU { dest, src1, src2 } => {
1229 let src1 = resolve(src1);
1230 let src2 = resolve(src2);
1231 inst.opcode = Opcode::LtU { dest, src1, src2 };
1232 }
1233 Opcode::LeS { dest, src1, src2 } => {
1234 let src1 = resolve(src1);
1235 let src2 = resolve(src2);
1236 inst.opcode = Opcode::LeS { dest, src1, src2 };
1237 }
1238 Opcode::LeU { dest, src1, src2 } => {
1239 let src1 = resolve(src1);
1240 let src2 = resolve(src2);
1241 inst.opcode = Opcode::LeU { dest, src1, src2 };
1242 }
1243 Opcode::GtS { dest, src1, src2 } => {
1244 let src1 = resolve(src1);
1245 let src2 = resolve(src2);
1246 inst.opcode = Opcode::GtS { dest, src1, src2 };
1247 }
1248 Opcode::GtU { dest, src1, src2 } => {
1249 let src1 = resolve(src1);
1250 let src2 = resolve(src2);
1251 inst.opcode = Opcode::GtU { dest, src1, src2 };
1252 }
1253 Opcode::GeS { dest, src1, src2 } => {
1254 let src1 = resolve(src1);
1255 let src2 = resolve(src2);
1256 inst.opcode = Opcode::GeS { dest, src1, src2 };
1257 }
1258 Opcode::GeU { dest, src1, src2 } => {
1259 let src1 = resolve(src1);
1260 let src2 = resolve(src2);
1261 inst.opcode = Opcode::GeU { dest, src1, src2 };
1262 }
1263
1264 Opcode::Eqz { dest, src } => {
1266 let src = resolve(src);
1267 inst.opcode = Opcode::Eqz { dest, src };
1268 }
1269
1270 Opcode::Return { value: Some(v) } => {
1271 let v = resolve(v);
1272 inst.opcode = Opcode::Return { value: Some(v) };
1273 }
1274
1275 Opcode::Select {
1276 dest,
1277 val_true,
1278 val_false,
1279 cond,
1280 } => {
1281 let val_true = resolve(val_true);
1282 let val_false = resolve(val_false);
1283 let cond = resolve(cond);
1284 inst.opcode = Opcode::Select {
1285 dest,
1286 val_true,
1287 val_false,
1288 cond,
1289 };
1290 }
1291
1292 Opcode::DivS { dest, src1, src2 } => {
1294 let src1 = resolve(src1);
1295 let src2 = resolve(src2);
1296 inst.opcode = Opcode::DivS { dest, src1, src2 };
1297 }
1298 Opcode::DivU { dest, src1, src2 } => {
1299 let src1 = resolve(src1);
1300 let src2 = resolve(src2);
1301 inst.opcode = Opcode::DivU { dest, src1, src2 };
1302 }
1303 Opcode::RemS { dest, src1, src2 } => {
1304 let src1 = resolve(src1);
1305 let src2 = resolve(src2);
1306 inst.opcode = Opcode::RemS { dest, src1, src2 };
1307 }
1308 Opcode::RemU { dest, src1, src2 } => {
1309 let src1 = resolve(src1);
1310 let src2 = resolve(src2);
1311 inst.opcode = Opcode::RemU { dest, src1, src2 };
1312 }
1313
1314 Opcode::And { dest, src1, src2 } => {
1316 let src1 = resolve(src1);
1317 let src2 = resolve(src2);
1318 inst.opcode = Opcode::And { dest, src1, src2 };
1319 }
1320 Opcode::Or { dest, src1, src2 } => {
1321 let src1 = resolve(src1);
1322 let src2 = resolve(src2);
1323 inst.opcode = Opcode::Or { dest, src1, src2 };
1324 }
1325 Opcode::Xor { dest, src1, src2 } => {
1326 let src1 = resolve(src1);
1327 let src2 = resolve(src2);
1328 inst.opcode = Opcode::Xor { dest, src1, src2 };
1329 }
1330
1331 Opcode::Shl { dest, src1, src2 } => {
1333 let src1 = resolve(src1);
1334 let src2 = resolve(src2);
1335 inst.opcode = Opcode::Shl { dest, src1, src2 };
1336 }
1337 Opcode::ShrS { dest, src1, src2 } => {
1338 let src1 = resolve(src1);
1339 let src2 = resolve(src2);
1340 inst.opcode = Opcode::ShrS { dest, src1, src2 };
1341 }
1342 Opcode::ShrU { dest, src1, src2 } => {
1343 let src1 = resolve(src1);
1344 let src2 = resolve(src2);
1345 inst.opcode = Opcode::ShrU { dest, src1, src2 };
1346 }
1347 Opcode::Rotl { dest, src1, src2 } => {
1348 let src1 = resolve(src1);
1349 let src2 = resolve(src2);
1350 inst.opcode = Opcode::Rotl { dest, src1, src2 };
1351 }
1352 Opcode::Rotr { dest, src1, src2 } => {
1353 let src1 = resolve(src1);
1354 let src2 = resolve(src2);
1355 inst.opcode = Opcode::Rotr { dest, src1, src2 };
1356 }
1357
1358 Opcode::Clz { dest, src } => {
1360 let src = resolve(src);
1361 inst.opcode = Opcode::Clz { dest, src };
1362 }
1363 Opcode::Ctz { dest, src } => {
1364 let src = resolve(src);
1365 inst.opcode = Opcode::Ctz { dest, src };
1366 }
1367 Opcode::Popcnt { dest, src } => {
1368 let src = resolve(src);
1369 inst.opcode = Opcode::Popcnt { dest, src };
1370 }
1371
1372 Opcode::Copy { dest, src } => {
1374 let src = resolve(src);
1375 inst.opcode = Opcode::Copy { dest, src };
1376 }
1377
1378 Opcode::I64Load { .. } | Opcode::I64Const { .. } => {
1396 }
1398 Opcode::I64Add {
1399 dest_lo,
1400 dest_hi,
1401 src1_lo,
1402 src1_hi,
1403 src2_lo,
1404 src2_hi,
1405 } => {
1406 inst.opcode = Opcode::I64Add {
1407 dest_lo,
1408 dest_hi,
1409 src1_lo: resolve(src1_lo),
1410 src1_hi: resolve(src1_hi),
1411 src2_lo: resolve(src2_lo),
1412 src2_hi: resolve(src2_hi),
1413 };
1414 }
1415 Opcode::I64Sub {
1416 dest_lo,
1417 dest_hi,
1418 src1_lo,
1419 src1_hi,
1420 src2_lo,
1421 src2_hi,
1422 } => {
1423 inst.opcode = Opcode::I64Sub {
1424 dest_lo,
1425 dest_hi,
1426 src1_lo: resolve(src1_lo),
1427 src1_hi: resolve(src1_hi),
1428 src2_lo: resolve(src2_lo),
1429 src2_hi: resolve(src2_hi),
1430 };
1431 }
1432 Opcode::I64And {
1433 dest_lo,
1434 dest_hi,
1435 src1_lo,
1436 src1_hi,
1437 src2_lo,
1438 src2_hi,
1439 } => {
1440 inst.opcode = Opcode::I64And {
1441 dest_lo,
1442 dest_hi,
1443 src1_lo: resolve(src1_lo),
1444 src1_hi: resolve(src1_hi),
1445 src2_lo: resolve(src2_lo),
1446 src2_hi: resolve(src2_hi),
1447 };
1448 }
1449 Opcode::I64Or {
1450 dest_lo,
1451 dest_hi,
1452 src1_lo,
1453 src1_hi,
1454 src2_lo,
1455 src2_hi,
1456 } => {
1457 inst.opcode = Opcode::I64Or {
1458 dest_lo,
1459 dest_hi,
1460 src1_lo: resolve(src1_lo),
1461 src1_hi: resolve(src1_hi),
1462 src2_lo: resolve(src2_lo),
1463 src2_hi: resolve(src2_hi),
1464 };
1465 }
1466 Opcode::I64Xor {
1467 dest_lo,
1468 dest_hi,
1469 src1_lo,
1470 src1_hi,
1471 src2_lo,
1472 src2_hi,
1473 } => {
1474 inst.opcode = Opcode::I64Xor {
1475 dest_lo,
1476 dest_hi,
1477 src1_lo: resolve(src1_lo),
1478 src1_hi: resolve(src1_hi),
1479 src2_lo: resolve(src2_lo),
1480 src2_hi: resolve(src2_hi),
1481 };
1482 }
1483 Opcode::I64Mul {
1484 dest_lo,
1485 dest_hi,
1486 src1_lo,
1487 src1_hi,
1488 src2_lo,
1489 src2_hi,
1490 } => {
1491 inst.opcode = Opcode::I64Mul {
1492 dest_lo,
1493 dest_hi,
1494 src1_lo: resolve(src1_lo),
1495 src1_hi: resolve(src1_hi),
1496 src2_lo: resolve(src2_lo),
1497 src2_hi: resolve(src2_hi),
1498 };
1499 }
1500 Opcode::I64DivS {
1501 dest_lo,
1502 dest_hi,
1503 src1_lo,
1504 src1_hi,
1505 src2_lo,
1506 src2_hi,
1507 } => {
1508 inst.opcode = Opcode::I64DivS {
1509 dest_lo,
1510 dest_hi,
1511 src1_lo: resolve(src1_lo),
1512 src1_hi: resolve(src1_hi),
1513 src2_lo: resolve(src2_lo),
1514 src2_hi: resolve(src2_hi),
1515 };
1516 }
1517 Opcode::I64DivU {
1518 dest_lo,
1519 dest_hi,
1520 src1_lo,
1521 src1_hi,
1522 src2_lo,
1523 src2_hi,
1524 } => {
1525 inst.opcode = Opcode::I64DivU {
1526 dest_lo,
1527 dest_hi,
1528 src1_lo: resolve(src1_lo),
1529 src1_hi: resolve(src1_hi),
1530 src2_lo: resolve(src2_lo),
1531 src2_hi: resolve(src2_hi),
1532 };
1533 }
1534 Opcode::I64RemS {
1535 dest_lo,
1536 dest_hi,
1537 src1_lo,
1538 src1_hi,
1539 src2_lo,
1540 src2_hi,
1541 } => {
1542 inst.opcode = Opcode::I64RemS {
1543 dest_lo,
1544 dest_hi,
1545 src1_lo: resolve(src1_lo),
1546 src1_hi: resolve(src1_hi),
1547 src2_lo: resolve(src2_lo),
1548 src2_hi: resolve(src2_hi),
1549 };
1550 }
1551 Opcode::I64RemU {
1552 dest_lo,
1553 dest_hi,
1554 src1_lo,
1555 src1_hi,
1556 src2_lo,
1557 src2_hi,
1558 } => {
1559 inst.opcode = Opcode::I64RemU {
1560 dest_lo,
1561 dest_hi,
1562 src1_lo: resolve(src1_lo),
1563 src1_hi: resolve(src1_hi),
1564 src2_lo: resolve(src2_lo),
1565 src2_hi: resolve(src2_hi),
1566 };
1567 }
1568 Opcode::I64Shl {
1569 dest_lo,
1570 dest_hi,
1571 src1_lo,
1572 src1_hi,
1573 src2_lo,
1574 src2_hi,
1575 } => {
1576 inst.opcode = Opcode::I64Shl {
1577 dest_lo,
1578 dest_hi,
1579 src1_lo: resolve(src1_lo),
1580 src1_hi: resolve(src1_hi),
1581 src2_lo: resolve(src2_lo),
1582 src2_hi: resolve(src2_hi),
1583 };
1584 }
1585 Opcode::I64ShrS {
1586 dest_lo,
1587 dest_hi,
1588 src1_lo,
1589 src1_hi,
1590 src2_lo,
1591 src2_hi,
1592 } => {
1593 inst.opcode = Opcode::I64ShrS {
1594 dest_lo,
1595 dest_hi,
1596 src1_lo: resolve(src1_lo),
1597 src1_hi: resolve(src1_hi),
1598 src2_lo: resolve(src2_lo),
1599 src2_hi: resolve(src2_hi),
1600 };
1601 }
1602 Opcode::I64ShrU {
1603 dest_lo,
1604 dest_hi,
1605 src1_lo,
1606 src1_hi,
1607 src2_lo,
1608 src2_hi,
1609 } => {
1610 inst.opcode = Opcode::I64ShrU {
1611 dest_lo,
1612 dest_hi,
1613 src1_lo: resolve(src1_lo),
1614 src1_hi: resolve(src1_hi),
1615 src2_lo: resolve(src2_lo),
1616 src2_hi: resolve(src2_hi),
1617 };
1618 }
1619 Opcode::I64Rotl {
1620 dest_lo,
1621 dest_hi,
1622 src1_lo,
1623 src1_hi,
1624 src2_lo,
1625 src2_hi,
1626 } => {
1627 inst.opcode = Opcode::I64Rotl {
1628 dest_lo,
1629 dest_hi,
1630 src1_lo: resolve(src1_lo),
1631 src1_hi: resolve(src1_hi),
1632 src2_lo: resolve(src2_lo),
1633 src2_hi: resolve(src2_hi),
1634 };
1635 }
1636 Opcode::I64Rotr {
1637 dest_lo,
1638 dest_hi,
1639 src1_lo,
1640 src1_hi,
1641 src2_lo,
1642 src2_hi,
1643 } => {
1644 inst.opcode = Opcode::I64Rotr {
1645 dest_lo,
1646 dest_hi,
1647 src1_lo: resolve(src1_lo),
1648 src1_hi: resolve(src1_hi),
1649 src2_lo: resolve(src2_lo),
1650 src2_hi: resolve(src2_hi),
1651 };
1652 }
1653 Opcode::I64Eq {
1655 dest,
1656 src1_lo,
1657 src1_hi,
1658 src2_lo,
1659 src2_hi,
1660 } => {
1661 inst.opcode = Opcode::I64Eq {
1662 dest,
1663 src1_lo: resolve(src1_lo),
1664 src1_hi: resolve(src1_hi),
1665 src2_lo: resolve(src2_lo),
1666 src2_hi: resolve(src2_hi),
1667 };
1668 }
1669 Opcode::I64Ne {
1670 dest,
1671 src1_lo,
1672 src1_hi,
1673 src2_lo,
1674 src2_hi,
1675 } => {
1676 inst.opcode = Opcode::I64Ne {
1677 dest,
1678 src1_lo: resolve(src1_lo),
1679 src1_hi: resolve(src1_hi),
1680 src2_lo: resolve(src2_lo),
1681 src2_hi: resolve(src2_hi),
1682 };
1683 }
1684 Opcode::I64LtS {
1685 dest,
1686 src1_lo,
1687 src1_hi,
1688 src2_lo,
1689 src2_hi,
1690 } => {
1691 inst.opcode = Opcode::I64LtS {
1692 dest,
1693 src1_lo: resolve(src1_lo),
1694 src1_hi: resolve(src1_hi),
1695 src2_lo: resolve(src2_lo),
1696 src2_hi: resolve(src2_hi),
1697 };
1698 }
1699 Opcode::I64GtS {
1700 dest,
1701 src1_lo,
1702 src1_hi,
1703 src2_lo,
1704 src2_hi,
1705 } => {
1706 inst.opcode = Opcode::I64GtS {
1707 dest,
1708 src1_lo: resolve(src1_lo),
1709 src1_hi: resolve(src1_hi),
1710 src2_lo: resolve(src2_lo),
1711 src2_hi: resolve(src2_hi),
1712 };
1713 }
1714 Opcode::I64LeS {
1715 dest,
1716 src1_lo,
1717 src1_hi,
1718 src2_lo,
1719 src2_hi,
1720 } => {
1721 inst.opcode = Opcode::I64LeS {
1722 dest,
1723 src1_lo: resolve(src1_lo),
1724 src1_hi: resolve(src1_hi),
1725 src2_lo: resolve(src2_lo),
1726 src2_hi: resolve(src2_hi),
1727 };
1728 }
1729 Opcode::I64GeS {
1730 dest,
1731 src1_lo,
1732 src1_hi,
1733 src2_lo,
1734 src2_hi,
1735 } => {
1736 inst.opcode = Opcode::I64GeS {
1737 dest,
1738 src1_lo: resolve(src1_lo),
1739 src1_hi: resolve(src1_hi),
1740 src2_lo: resolve(src2_lo),
1741 src2_hi: resolve(src2_hi),
1742 };
1743 }
1744 Opcode::I64LtU {
1745 dest,
1746 src1_lo,
1747 src1_hi,
1748 src2_lo,
1749 src2_hi,
1750 } => {
1751 inst.opcode = Opcode::I64LtU {
1752 dest,
1753 src1_lo: resolve(src1_lo),
1754 src1_hi: resolve(src1_hi),
1755 src2_lo: resolve(src2_lo),
1756 src2_hi: resolve(src2_hi),
1757 };
1758 }
1759 Opcode::I64GtU {
1760 dest,
1761 src1_lo,
1762 src1_hi,
1763 src2_lo,
1764 src2_hi,
1765 } => {
1766 inst.opcode = Opcode::I64GtU {
1767 dest,
1768 src1_lo: resolve(src1_lo),
1769 src1_hi: resolve(src1_hi),
1770 src2_lo: resolve(src2_lo),
1771 src2_hi: resolve(src2_hi),
1772 };
1773 }
1774 Opcode::I64LeU {
1775 dest,
1776 src1_lo,
1777 src1_hi,
1778 src2_lo,
1779 src2_hi,
1780 } => {
1781 inst.opcode = Opcode::I64LeU {
1782 dest,
1783 src1_lo: resolve(src1_lo),
1784 src1_hi: resolve(src1_hi),
1785 src2_lo: resolve(src2_lo),
1786 src2_hi: resolve(src2_hi),
1787 };
1788 }
1789 Opcode::I64GeU {
1790 dest,
1791 src1_lo,
1792 src1_hi,
1793 src2_lo,
1794 src2_hi,
1795 } => {
1796 inst.opcode = Opcode::I64GeU {
1797 dest,
1798 src1_lo: resolve(src1_lo),
1799 src1_hi: resolve(src1_hi),
1800 src2_lo: resolve(src2_lo),
1801 src2_hi: resolve(src2_hi),
1802 };
1803 }
1804 Opcode::I64Eqz {
1805 dest,
1806 src_lo,
1807 src_hi,
1808 } => {
1809 inst.opcode = Opcode::I64Eqz {
1810 dest,
1811 src_lo: resolve(src_lo),
1812 src_hi: resolve(src_hi),
1813 };
1814 }
1815 Opcode::I64Clz {
1816 dest,
1817 src_lo,
1818 src_hi,
1819 } => {
1820 inst.opcode = Opcode::I64Clz {
1821 dest,
1822 src_lo: resolve(src_lo),
1823 src_hi: resolve(src_hi),
1824 };
1825 }
1826 Opcode::I64Ctz {
1827 dest,
1828 src_lo,
1829 src_hi,
1830 } => {
1831 inst.opcode = Opcode::I64Ctz {
1832 dest,
1833 src_lo: resolve(src_lo),
1834 src_hi: resolve(src_hi),
1835 };
1836 }
1837 Opcode::I64Popcnt {
1838 dest,
1839 src_lo,
1840 src_hi,
1841 } => {
1842 inst.opcode = Opcode::I64Popcnt {
1843 dest,
1844 src_lo: resolve(src_lo),
1845 src_hi: resolve(src_hi),
1846 };
1847 }
1848 Opcode::I64Extend8S {
1849 dest_lo,
1850 dest_hi,
1851 src_lo,
1852 } => {
1853 inst.opcode = Opcode::I64Extend8S {
1854 dest_lo,
1855 dest_hi,
1856 src_lo: resolve(src_lo),
1857 };
1858 }
1859 Opcode::I64Extend16S {
1860 dest_lo,
1861 dest_hi,
1862 src_lo,
1863 } => {
1864 inst.opcode = Opcode::I64Extend16S {
1865 dest_lo,
1866 dest_hi,
1867 src_lo: resolve(src_lo),
1868 };
1869 }
1870 Opcode::I64Extend32S {
1871 dest_lo,
1872 dest_hi,
1873 src_lo,
1874 } => {
1875 inst.opcode = Opcode::I64Extend32S {
1876 dest_lo,
1877 dest_hi,
1878 src_lo: resolve(src_lo),
1879 };
1880 }
1881 Opcode::I64ExtendI32U {
1882 dest_lo,
1883 dest_hi,
1884 src,
1885 } => {
1886 inst.opcode = Opcode::I64ExtendI32U {
1887 dest_lo,
1888 dest_hi,
1889 src: resolve(src),
1890 };
1891 }
1892 Opcode::I64ExtendI32S {
1893 dest_lo,
1894 dest_hi,
1895 src,
1896 } => {
1897 inst.opcode = Opcode::I64ExtendI32S {
1898 dest_lo,
1899 dest_hi,
1900 src: resolve(src),
1901 };
1902 }
1903 Opcode::I32WrapI64 { dest, src_lo } => {
1904 inst.opcode = Opcode::I32WrapI64 {
1905 dest,
1906 src_lo: resolve(src_lo),
1907 };
1908 }
1909
1910 Opcode::MemLoadSubword {
1919 dest,
1920 addr,
1921 offset,
1922 width,
1923 signed,
1924 } => {
1925 inst.opcode = Opcode::MemLoadSubword {
1926 dest,
1927 addr: resolve(addr),
1928 offset,
1929 width,
1930 signed,
1931 };
1932 }
1933 Opcode::MemStoreSubword {
1934 src,
1935 addr,
1936 offset,
1937 width,
1938 } => {
1939 inst.opcode = Opcode::MemStoreSubword {
1940 src: resolve(src),
1941 addr: resolve(addr),
1942 offset,
1943 width,
1944 };
1945 }
1946 Opcode::GlobalSet { src, idx } => {
1947 inst.opcode = Opcode::GlobalSet {
1948 src: resolve(src),
1949 idx,
1950 };
1951 }
1952 Opcode::MemoryGrow { dest, delta } => {
1953 inst.opcode = Opcode::MemoryGrow {
1954 dest,
1955 delta: resolve(delta),
1956 };
1957 }
1958
1959 _ => {}
1960 }
1961
1962 if !inst.is_dead {
1969 for dest in &dests_to_invalidate {
1970 reg_map.remove(dest);
1971 }
1972 }
1973 }
1974
1975 if self.verbose && modified > 0 {
1976 eprintln!("CSE: {} subexpressions eliminated", modified);
1977 }
1978
1979 OptResult {
1980 changed: modified > 0,
1981 removed_count: modified, added_count: 0,
1983 modified_count: 0,
1984 }
1985 }
1986}
1987
1988impl Default for CommonSubexpressionElimination {
1989 fn default() -> Self {
1990 Self::new()
1991 }
1992}
1993
1994impl OptimizationPass for CommonSubexpressionElimination {
1995 fn name(&self) -> &'static str {
1996 "common-subexpression-elimination"
1997 }
1998
1999 fn run(&mut self, _cfg: &mut Cfg, instructions: &mut Vec<Instruction>) -> OptResult {
2000 self.eliminate_cse(instructions)
2001 }
2002}
2003
2004pub struct AlgebraicSimplification {
2006 verbose: bool,
2007}
2008
2009impl AlgebraicSimplification {
2010 pub fn new() -> Self {
2011 Self { verbose: false }
2012 }
2013
2014 pub fn with_verbose(mut self) -> Self {
2015 self.verbose = true;
2016 self
2017 }
2018
2019 fn simplify(&mut self, instructions: &mut [Instruction]) -> OptResult {
2021 let mut const_values: HashMap<Reg, i32> = HashMap::new();
2023 let mut modified = 0;
2024
2025 for inst in instructions.iter_mut() {
2026 if inst.is_dead {
2027 continue;
2028 }
2029
2030 let opcode = inst.opcode.clone();
2031
2032 match opcode {
2033 Opcode::Const { dest, value } => {
2035 const_values.insert(dest, value);
2036 }
2037
2038 Opcode::Add {
2040 dest: _,
2041 src1,
2042 src2,
2043 } => {
2044 let val1 = const_values.get(&src1);
2045 let val2 = const_values.get(&src2);
2046
2047 match (val1, val2) {
2048 (Some(&0), _) => {
2049 inst.is_dead = true;
2051 modified += 1;
2052 if self.verbose {
2053 eprintln!("Simplified: 0 + r{} -> r{}", src2.0, src2.0);
2054 }
2055 }
2056 (_, Some(&0)) => {
2057 inst.is_dead = true;
2059 modified += 1;
2060 if self.verbose {
2061 eprintln!("Simplified: r{} + 0 -> r{}", src1.0, src1.0);
2062 }
2063 }
2064 _ => {}
2065 }
2066 }
2067
2068 Opcode::Sub { dest, src1, src2 } => {
2070 let val2 = const_values.get(&src2);
2071
2072 if let Some(&0) = val2 {
2073 inst.is_dead = true;
2075 modified += 1;
2076 if self.verbose {
2077 eprintln!("Simplified: r{} - 0 -> r{}", src1.0, src1.0);
2078 }
2079 } else if src1 == src2 {
2080 inst.opcode = Opcode::Const { dest, value: 0 };
2082 const_values.insert(dest, 0);
2083 modified += 1;
2084 if self.verbose {
2085 eprintln!("Simplified: r{} - r{} -> 0", src1.0, src2.0);
2086 }
2087 }
2088 }
2089
2090 Opcode::Mul { dest, src1, src2 } => {
2092 let val1 = const_values.get(&src1);
2093 let val2 = const_values.get(&src2);
2094
2095 match (val1, val2) {
2096 (Some(&0), _) | (_, Some(&0)) => {
2097 inst.opcode = Opcode::Const { dest, value: 0 };
2099 const_values.insert(dest, 0);
2100 modified += 1;
2101 if self.verbose {
2102 eprintln!("Simplified: mul with 0 -> 0");
2103 }
2104 }
2105 (Some(&1), _) => {
2106 inst.is_dead = true;
2108 modified += 1;
2109 if self.verbose {
2110 eprintln!("Simplified: 1 * r{} -> r{}", src2.0, src2.0);
2111 }
2112 }
2113 (_, Some(&1)) => {
2114 inst.is_dead = true;
2116 modified += 1;
2117 if self.verbose {
2118 eprintln!("Simplified: r{} * 1 -> r{}", src1.0, src1.0);
2119 }
2120 }
2121 _ => {}
2122 }
2123 }
2124
2125 _ => {}
2126 }
2127 }
2128
2129 if self.verbose && modified > 0 {
2130 eprintln!(
2131 "Algebraic simplification: {} operations simplified",
2132 modified
2133 );
2134 }
2135
2136 OptResult {
2137 changed: modified > 0,
2138 removed_count: 0,
2139 added_count: 0,
2140 modified_count: modified,
2141 }
2142 }
2143}
2144
2145impl Default for AlgebraicSimplification {
2146 fn default() -> Self {
2147 Self::new()
2148 }
2149}
2150
2151impl OptimizationPass for AlgebraicSimplification {
2152 fn name(&self) -> &'static str {
2153 "algebraic-simplification"
2154 }
2155
2156 fn run(&mut self, _cfg: &mut Cfg, instructions: &mut Vec<Instruction>) -> OptResult {
2157 self.simplify(instructions)
2158 }
2159}
2160
2161pub struct PeepholeOptimization {
2163 verbose: bool,
2164}
2165
2166impl PeepholeOptimization {
2167 pub fn new() -> Self {
2168 Self { verbose: false }
2169 }
2170
2171 pub fn with_verbose(mut self) -> Self {
2172 self.verbose = true;
2173 self
2174 }
2175
2176 fn optimize(&mut self, instructions: &mut [Instruction]) -> OptResult {
2178 let mut modified = 0;
2179
2180 let mut i = 0;
2182 while i + 1 < instructions.len() {
2183 if instructions[i].is_dead || instructions[i + 1].is_dead {
2184 i += 1;
2185 continue;
2186 }
2187
2188 let inst1 = instructions[i].opcode.clone();
2189 let inst2 = instructions[i + 1].opcode.clone();
2190
2191 match (&inst1, &inst2) {
2193 (Opcode::Const { dest: dest1, .. }, Opcode::Const { dest: dest2, .. })
2194 if dest1 == dest2 =>
2195 {
2196 instructions[i].is_dead = true;
2198 modified += 1;
2199
2200 if self.verbose {
2201 eprintln!("Peephole: Eliminated redundant const to r{}", dest1.0);
2202 }
2203 }
2204
2205 _ => {}
2207 }
2208
2209 i += 1;
2210 }
2211
2212 let mut i = 0;
2214 while i + 2 < instructions.len() {
2215 if instructions[i].is_dead || instructions[i + 1].is_dead || instructions[i + 2].is_dead
2216 {
2217 i += 1;
2218 continue;
2219 }
2220
2221 let _inst1 = instructions[i].opcode.clone();
2222 let _inst2 = instructions[i + 1].opcode.clone();
2223 let _inst3 = instructions[i + 2].opcode.clone();
2224
2225 i += 1;
2229 }
2230
2231 if self.verbose && modified > 0 {
2232 eprintln!("Peephole optimization: {} patterns matched", modified);
2233 }
2234
2235 OptResult {
2236 changed: modified > 0,
2237 removed_count: modified,
2238 added_count: 0,
2239 modified_count: 0,
2240 }
2241 }
2242}
2243
2244impl Default for PeepholeOptimization {
2245 fn default() -> Self {
2246 Self::new()
2247 }
2248}
2249
2250impl OptimizationPass for PeepholeOptimization {
2251 fn name(&self) -> &'static str {
2252 "peephole-optimization"
2253 }
2254
2255 fn run(&mut self, _cfg: &mut Cfg, instructions: &mut Vec<Instruction>) -> OptResult {
2256 self.optimize(instructions)
2257 }
2258}
2259
2260pub struct StrengthReduction {
2262 verbose: bool,
2263}
2264
2265impl StrengthReduction {
2266 pub fn new() -> Self {
2267 Self { verbose: false }
2268 }
2269
2270 pub fn with_verbose(mut self) -> Self {
2271 self.verbose = true;
2272 self
2273 }
2274
2275 fn is_power_of_2(n: i32) -> bool {
2277 n > 0 && (n & (n - 1)) == 0
2278 }
2279
2280 fn log2(n: i32) -> u32 {
2282 n.trailing_zeros()
2283 }
2284
2285 fn reduce(&mut self, instructions: &mut [Instruction]) -> OptResult {
2287 let mut const_values: HashMap<Reg, i32> = HashMap::new();
2288 let mut modified = 0;
2289
2290 for inst in instructions.iter_mut() {
2291 if inst.is_dead {
2292 continue;
2293 }
2294
2295 let opcode = inst.opcode.clone();
2296
2297 match opcode {
2298 Opcode::Const { dest, value } => {
2300 const_values.insert(dest, value);
2301 }
2302
2303 Opcode::Mul {
2305 dest: _,
2306 src1,
2307 src2,
2308 } => {
2309 let val1 = const_values.get(&src1);
2310 let val2 = const_values.get(&src2);
2311
2312 if let Some(&val) = val2
2313 && Self::is_power_of_2(val)
2314 {
2315 modified += 1;
2318 if self.verbose {
2319 eprintln!(
2320 "Strength reduction: r{} * {} -> r{} << {}",
2321 src1.0,
2322 val,
2323 src1.0,
2324 Self::log2(val)
2325 );
2326 }
2327 } else if let Some(&val) = val1
2328 && Self::is_power_of_2(val)
2329 {
2330 modified += 1;
2331 if self.verbose {
2332 eprintln!(
2333 "Strength reduction: {} * r{} -> r{} << {}",
2334 val,
2335 src2.0,
2336 src2.0,
2337 Self::log2(val)
2338 );
2339 }
2340 }
2341 }
2342
2343 _ => {}
2344 }
2345 }
2346
2347 if self.verbose && modified > 0 {
2348 eprintln!("Strength reduction: {} operations reduced", modified);
2349 }
2350
2351 OptResult {
2352 changed: modified > 0,
2353 removed_count: 0,
2354 added_count: 0,
2355 modified_count: modified,
2356 }
2357 }
2358}
2359
2360impl Default for StrengthReduction {
2361 fn default() -> Self {
2362 Self::new()
2363 }
2364}
2365
2366impl OptimizationPass for StrengthReduction {
2367 fn name(&self) -> &'static str {
2368 "strength-reduction"
2369 }
2370
2371 fn run(&mut self, _cfg: &mut Cfg, instructions: &mut Vec<Instruction>) -> OptResult {
2372 self.reduce(instructions)
2373 }
2374}
2375
2376pub struct LoopInvariantCodeMotion {
2378 verbose: bool,
2379}
2380
2381impl LoopInvariantCodeMotion {
2382 pub fn new() -> Self {
2383 Self { verbose: false }
2384 }
2385
2386 pub fn with_verbose(mut self) -> Self {
2387 self.verbose = true;
2388 self
2389 }
2390
2391 fn detect_invariants(&self, cfg: &Cfg, instructions: &[Instruction]) -> HashSet<usize> {
2393 let mut invariants = HashSet::new();
2394
2395 for loop_info in &cfg.loops {
2397 for inst in instructions {
2399 if inst.is_dead || !loop_info.body.contains(&inst.block_id) {
2400 continue;
2401 }
2402
2403 let is_invariant = match &inst.opcode {
2405 Opcode::Const { .. } => true,
2407
2408 Opcode::Add {
2410 src1: _, src2: _, ..
2411 }
2412 | Opcode::Sub {
2413 src1: _, src2: _, ..
2414 }
2415 | Opcode::Mul {
2416 src1: _, src2: _, ..
2417 } => {
2418 false }
2422
2423 Opcode::Load { .. } => false,
2425
2426 _ => false,
2427 };
2428
2429 if is_invariant {
2430 invariants.insert(inst.id);
2431 }
2432 }
2433 }
2434
2435 invariants
2436 }
2437
2438 fn hoist(&mut self, cfg: &mut Cfg, instructions: &mut [Instruction]) -> OptResult {
2440 let invariants = self.detect_invariants(cfg, instructions);
2441
2442 if self.verbose && !invariants.is_empty() {
2445 eprintln!(
2446 "LICM: {} loop-invariant instructions detected",
2447 invariants.len()
2448 );
2449 }
2450
2451 let modified = invariants.len();
2452
2453 OptResult {
2454 changed: modified > 0,
2455 removed_count: 0,
2456 added_count: 0,
2457 modified_count: modified,
2458 }
2459 }
2460}
2461
2462impl Default for LoopInvariantCodeMotion {
2463 fn default() -> Self {
2464 Self::new()
2465 }
2466}
2467
2468impl OptimizationPass for LoopInvariantCodeMotion {
2469 fn name(&self) -> &'static str {
2470 "loop-invariant-code-motion"
2471 }
2472
2473 fn run(&mut self, cfg: &mut Cfg, instructions: &mut Vec<Instruction>) -> OptResult {
2474 self.hoist(cfg, instructions)
2475 }
2476}
2477
2478pub struct CopyPropagation {
2480 verbose: bool,
2481}
2482
2483impl CopyPropagation {
2484 pub fn new() -> Self {
2485 Self { verbose: false }
2486 }
2487
2488 pub fn with_verbose(mut self) -> Self {
2489 self.verbose = true;
2490 self
2491 }
2492
2493 fn propagate(&mut self, instructions: &mut [Instruction]) -> OptResult {
2495 let copy_map: HashMap<Reg, Reg> = HashMap::new();
2496 let mut modified = 0;
2497
2498 for inst in instructions.iter_mut() {
2505 if inst.is_dead {
2506 continue;
2507 }
2508
2509 let mut changed = false;
2510 let opcode = inst.opcode.clone();
2511
2512 match opcode {
2513 Opcode::Add { dest, src1, src2 } => {
2514 let new_src1 = Self::resolve(©_map, src1);
2515 let new_src2 = Self::resolve(©_map, src2);
2516
2517 if new_src1 != src1 || new_src2 != src2 {
2518 inst.opcode = Opcode::Add {
2519 dest,
2520 src1: new_src1,
2521 src2: new_src2,
2522 };
2523 changed = true;
2524 modified += 1;
2525 }
2526 }
2527
2528 Opcode::Sub { dest, src1, src2 } => {
2529 let new_src1 = Self::resolve(©_map, src1);
2530 let new_src2 = Self::resolve(©_map, src2);
2531
2532 if new_src1 != src1 || new_src2 != src2 {
2533 inst.opcode = Opcode::Sub {
2534 dest,
2535 src1: new_src1,
2536 src2: new_src2,
2537 };
2538 changed = true;
2539 modified += 1;
2540 }
2541 }
2542
2543 Opcode::Mul { dest, src1, src2 } => {
2544 let new_src1 = Self::resolve(©_map, src1);
2545 let new_src2 = Self::resolve(©_map, src2);
2546
2547 if new_src1 != src1 || new_src2 != src2 {
2548 inst.opcode = Opcode::Mul {
2549 dest,
2550 src1: new_src1,
2551 src2: new_src2,
2552 };
2553 changed = true;
2554 modified += 1;
2555 }
2556 }
2557
2558 Opcode::Load { dest: _, addr: _ } => {
2559 }
2561
2562 Opcode::Store { src, addr } => {
2563 let new_src = Self::resolve(©_map, src);
2564
2565 if new_src != src {
2566 inst.opcode = Opcode::Store { src: new_src, addr };
2567 changed = true;
2568 modified += 1;
2569 }
2570 }
2571
2572 _ => {}
2573 }
2574
2575 if changed && self.verbose {
2576 eprintln!("Copy propagation: updated instruction {}", inst.id);
2577 }
2578 }
2579
2580 if self.verbose && modified > 0 {
2581 eprintln!("Copy propagation: {} instructions updated", modified);
2582 }
2583
2584 OptResult {
2585 changed: modified > 0,
2586 removed_count: 0,
2587 added_count: 0,
2588 modified_count: modified,
2589 }
2590 }
2591
2592 fn resolve(copy_map: &HashMap<Reg, Reg>, reg: Reg) -> Reg {
2594 let mut current = reg;
2595 let mut visited = HashSet::new();
2596
2597 while let Some(&next) = copy_map.get(¤t) {
2599 if !visited.insert(current) {
2600 break;
2602 }
2603 if next == current {
2604 break;
2606 }
2607 current = next;
2608 }
2609
2610 current
2611 }
2612}
2613
2614impl Default for CopyPropagation {
2615 fn default() -> Self {
2616 Self::new()
2617 }
2618}
2619
2620impl OptimizationPass for CopyPropagation {
2621 fn name(&self) -> &'static str {
2622 "copy-propagation"
2623 }
2624
2625 fn run(&mut self, _cfg: &mut Cfg, instructions: &mut Vec<Instruction>) -> OptResult {
2626 self.propagate(instructions)
2627 }
2628}
2629
2630pub struct InstructionCombining {
2632 verbose: bool,
2633}
2634
2635impl InstructionCombining {
2636 pub fn new() -> Self {
2637 Self { verbose: false }
2638 }
2639
2640 pub fn with_verbose(mut self) -> Self {
2641 self.verbose = true;
2642 self
2643 }
2644
2645 fn combine(&mut self, instructions: &mut [Instruction]) -> OptResult {
2647 let mut const_values: HashMap<Reg, i32> = HashMap::new();
2648 let mut def_map: HashMap<Reg, usize> = HashMap::new();
2649 let mut inst_opcodes: HashMap<usize, Opcode> = HashMap::new();
2650 let mut modified = 0;
2651
2652 for inst in instructions.iter() {
2654 if inst.is_dead {
2655 continue;
2656 }
2657
2658 match &inst.opcode {
2659 Opcode::Const { dest, value } => {
2660 const_values.insert(*dest, *value);
2661 def_map.insert(*dest, inst.id);
2662 }
2663 Opcode::Add { dest, .. } | Opcode::Sub { dest, .. } | Opcode::Mul { dest, .. } => {
2664 def_map.insert(*dest, inst.id);
2665 }
2666 _ => {}
2667 }
2668 inst_opcodes.insert(inst.id, inst.opcode.clone());
2669 }
2670
2671 for inst in instructions.iter() {
2673 if inst.is_dead {
2674 continue;
2675 }
2676
2677 match &inst.opcode {
2678 Opcode::Add {
2680 dest: _,
2681 src1,
2682 src2,
2683 } => {
2684 if let Some(&val2) = const_values.get(src2)
2686 && let Some(&def_id) = def_map.get(src1)
2687 && let Some(Opcode::Add {
2688 dest: _,
2689 src1: inner_src1,
2690 src2: inner_src2,
2691 }) = inst_opcodes.get(&def_id)
2692 && let Some(&val1) = const_values.get(inner_src2)
2693 {
2694 let combined = val1.wrapping_add(val2);
2696
2697 modified += 1;
2700
2701 if self.verbose {
2702 eprintln!(
2703 "Instruction combining: (r{} + {}) + {} => r{} + {}",
2704 inner_src1.0, val1, val2, inner_src1.0, combined
2705 );
2706 }
2707 }
2708 }
2709
2710 Opcode::Mul {
2714 dest: _,
2715 src1: _,
2716 src2: _,
2717 } => {
2718 }
2721
2722 _ => {}
2723 }
2724 }
2725
2726 if self.verbose && modified > 0 {
2727 eprintln!("Instruction combining: {} opportunities found", modified);
2728 }
2729
2730 OptResult {
2731 changed: modified > 0,
2732 removed_count: 0,
2733 added_count: 0,
2734 modified_count: modified,
2735 }
2736 }
2737}
2738
2739impl Default for InstructionCombining {
2740 fn default() -> Self {
2741 Self::new()
2742 }
2743}
2744
2745impl OptimizationPass for InstructionCombining {
2746 fn name(&self) -> &'static str {
2747 "instruction-combining"
2748 }
2749
2750 fn run(&mut self, _cfg: &mut Cfg, instructions: &mut Vec<Instruction>) -> OptResult {
2751 self.combine(instructions)
2752 }
2753}
2754
2755pub struct PassManager {
2757 passes: Vec<Box<dyn OptimizationPass>>,
2758 max_iterations: usize,
2759}
2760
2761impl PassManager {
2762 pub fn new() -> Self {
2763 Self {
2764 passes: Vec::new(),
2765 max_iterations: 10,
2766 }
2767 }
2768
2769 pub fn add_pass<P: OptimizationPass + 'static>(mut self, pass: P) -> Self {
2770 self.passes.push(Box::new(pass));
2771 self
2772 }
2773
2774 pub fn with_max_iterations(mut self, max: usize) -> Self {
2775 self.max_iterations = max;
2776 self
2777 }
2778
2779 pub fn run(&mut self, cfg: &mut Cfg, instructions: &mut Vec<Instruction>) -> OptResult {
2780 let mut total_result = OptResult::no_change();
2781 let mut iteration = 0;
2782
2783 loop {
2784 iteration += 1;
2785 if iteration > self.max_iterations {
2786 break;
2787 }
2788
2789 let mut iteration_result = OptResult::no_change();
2790
2791 for pass in &mut self.passes {
2792 let result = pass.run(cfg, instructions);
2793 iteration_result.merge(result);
2794 }
2795
2796 total_result.merge(iteration_result.clone());
2797
2798 if !iteration_result.changed {
2800 break;
2801 }
2802 }
2803
2804 total_result
2805 }
2806}
2807
2808impl Default for PassManager {
2809 fn default() -> Self {
2810 Self::new()
2811 }
2812}
2813
2814#[cfg(test)]
2815mod tests {
2816 use super::*;
2817 use synth_cfg::CfgBuilder;
2818 use synth_cfg::Loop;
2819
2820 #[test]
2821 fn test_dce_removes_unreachable() {
2822 let mut builder = CfgBuilder::new();
2824
2825 builder.add_instruction();
2827
2828 let reachable = builder.start_block();
2830 builder.add_instruction();
2831
2832 let unreachable = builder.start_block();
2834 builder.add_instruction();
2835
2836 builder.set_current_block(0);
2838 builder.add_branch(reachable);
2839
2840 let mut cfg = builder.build();
2841
2842 let mut instructions = vec![
2844 Instruction {
2845 id: 0,
2846 opcode: Opcode::Nop,
2847 block_id: 0,
2848 is_dead: false,
2849 },
2850 Instruction {
2851 id: 1,
2852 opcode: Opcode::Nop,
2853 block_id: reachable,
2854 is_dead: false,
2855 },
2856 Instruction {
2857 id: 2,
2858 opcode: Opcode::Nop,
2859 block_id: unreachable,
2860 is_dead: false,
2861 },
2862 ];
2863
2864 let mut dce = DeadCodeElimination::new();
2866 let result = dce.run(&mut cfg, &mut instructions);
2867
2868 assert!(result.changed);
2870 assert_eq!(result.removed_count, 1);
2871 assert!(instructions[2].is_dead);
2872 assert!(!instructions[0].is_dead);
2873 assert!(!instructions[1].is_dead);
2874 }
2875
2876 #[test]
2877 fn test_dce_keeps_reachable() {
2878 let mut builder = CfgBuilder::new();
2879 builder.add_instruction();
2880
2881 let block1 = builder.start_block();
2882 builder.add_instruction();
2883
2884 builder.set_current_block(0);
2885 builder.add_branch(block1);
2886
2887 let mut cfg = builder.build();
2888
2889 let mut instructions = vec![
2890 Instruction {
2891 id: 0,
2892 opcode: Opcode::Nop,
2893 block_id: 0,
2894 is_dead: false,
2895 },
2896 Instruction {
2897 id: 1,
2898 opcode: Opcode::Nop,
2899 block_id: block1,
2900 is_dead: false,
2901 },
2902 ];
2903
2904 let mut dce = DeadCodeElimination::new();
2905 let result = dce.run(&mut cfg, &mut instructions);
2906
2907 assert!(!result.changed);
2909 assert_eq!(result.removed_count, 0);
2910 assert!(!instructions[0].is_dead);
2911 assert!(!instructions[1].is_dead);
2912 }
2913
2914 #[test]
2915 fn test_pass_manager() {
2916 let mut builder = CfgBuilder::new();
2917 builder.add_instruction();
2918
2919 let mut cfg = builder.build();
2920 let mut instructions = vec![Instruction {
2921 id: 0,
2922 opcode: Opcode::Nop,
2923 block_id: 0,
2924 is_dead: false,
2925 }];
2926
2927 let mut manager = PassManager::new()
2928 .add_pass(DeadCodeElimination::new())
2929 .add_pass(ConstantFolding::new());
2930
2931 let result = manager.run(&mut cfg, &mut instructions);
2932
2933 assert_eq!(result.removed_count, 0); }
2936
2937 #[test]
2938 fn test_opt_result_merge() {
2939 let mut result1 = OptResult {
2940 changed: true,
2941 removed_count: 5,
2942 added_count: 2,
2943 modified_count: 3,
2944 };
2945
2946 let result2 = OptResult {
2947 changed: false,
2948 removed_count: 1,
2949 added_count: 1,
2950 modified_count: 2,
2951 };
2952
2953 result1.merge(result2);
2954
2955 assert!(result1.changed);
2956 assert_eq!(result1.removed_count, 6);
2957 assert_eq!(result1.added_count, 3);
2958 assert_eq!(result1.modified_count, 5);
2959 }
2960
2961 #[test]
2962 fn test_constant_folding_add() {
2963 let mut builder = CfgBuilder::new();
2964 builder.add_instruction();
2965 builder.add_instruction();
2966 builder.add_instruction();
2967
2968 let mut cfg = builder.build();
2969
2970 let mut instructions = vec![
2972 Instruction {
2973 id: 0,
2974 opcode: Opcode::Const {
2975 dest: Reg(0),
2976 value: 10,
2977 },
2978 block_id: 0,
2979 is_dead: false,
2980 },
2981 Instruction {
2982 id: 1,
2983 opcode: Opcode::Const {
2984 dest: Reg(1),
2985 value: 20,
2986 },
2987 block_id: 0,
2988 is_dead: false,
2989 },
2990 Instruction {
2991 id: 2,
2992 opcode: Opcode::Add {
2993 dest: Reg(2),
2994 src1: Reg(0),
2995 src2: Reg(1),
2996 },
2997 block_id: 0,
2998 is_dead: false,
2999 },
3000 ];
3001
3002 let mut folder = ConstantFolding::new();
3003 let result = folder.run(&mut cfg, &mut instructions);
3004
3005 assert!(result.changed);
3007 assert_eq!(result.modified_count, 1);
3008 assert_eq!(
3009 instructions[2].opcode,
3010 Opcode::Const {
3011 dest: Reg(2),
3012 value: 30
3013 }
3014 );
3015 }
3016
3017 #[test]
3018 fn test_constant_folding_multiple_ops() {
3019 let mut builder = CfgBuilder::new();
3020 for _ in 0..6 {
3021 builder.add_instruction();
3022 }
3023
3024 let mut cfg = builder.build();
3025
3026 let mut instructions = vec![
3028 Instruction {
3029 id: 0,
3030 opcode: Opcode::Const {
3031 dest: Reg(0),
3032 value: 5,
3033 },
3034 block_id: 0,
3035 is_dead: false,
3036 },
3037 Instruction {
3038 id: 1,
3039 opcode: Opcode::Const {
3040 dest: Reg(1),
3041 value: 3,
3042 },
3043 block_id: 0,
3044 is_dead: false,
3045 },
3046 Instruction {
3047 id: 2,
3048 opcode: Opcode::Add {
3049 dest: Reg(2),
3050 src1: Reg(0),
3051 src2: Reg(1),
3052 },
3053 block_id: 0,
3054 is_dead: false,
3055 },
3056 Instruction {
3057 id: 3,
3058 opcode: Opcode::Sub {
3059 dest: Reg(3),
3060 src1: Reg(0),
3061 src2: Reg(1),
3062 },
3063 block_id: 0,
3064 is_dead: false,
3065 },
3066 Instruction {
3067 id: 4,
3068 opcode: Opcode::Mul {
3069 dest: Reg(4),
3070 src1: Reg(0),
3071 src2: Reg(1),
3072 },
3073 block_id: 0,
3074 is_dead: false,
3075 },
3076 ];
3077
3078 let mut folder = ConstantFolding::new();
3079 let result = folder.run(&mut cfg, &mut instructions);
3080
3081 assert!(result.changed);
3083 assert_eq!(result.modified_count, 3);
3084 assert_eq!(
3085 instructions[2].opcode,
3086 Opcode::Const {
3087 dest: Reg(2),
3088 value: 8
3089 }
3090 ); assert_eq!(
3092 instructions[3].opcode,
3093 Opcode::Const {
3094 dest: Reg(3),
3095 value: 2
3096 }
3097 ); assert_eq!(
3099 instructions[4].opcode,
3100 Opcode::Const {
3101 dest: Reg(4),
3102 value: 15
3103 }
3104 ); }
3106
3107 #[test]
3108 fn test_constant_folding_chained() {
3109 let mut builder = CfgBuilder::new();
3110 for _ in 0..4 {
3111 builder.add_instruction();
3112 }
3113
3114 let mut cfg = builder.build();
3115
3116 let mut instructions = vec![
3118 Instruction {
3119 id: 0,
3120 opcode: Opcode::Const {
3121 dest: Reg(0),
3122 value: 2,
3123 },
3124 block_id: 0,
3125 is_dead: false,
3126 },
3127 Instruction {
3128 id: 1,
3129 opcode: Opcode::Const {
3130 dest: Reg(1),
3131 value: 3,
3132 },
3133 block_id: 0,
3134 is_dead: false,
3135 },
3136 Instruction {
3137 id: 2,
3138 opcode: Opcode::Add {
3139 dest: Reg(2),
3140 src1: Reg(0),
3141 src2: Reg(1),
3142 },
3143 block_id: 0,
3144 is_dead: false,
3145 },
3146 Instruction {
3147 id: 3,
3148 opcode: Opcode::Mul {
3149 dest: Reg(3),
3150 src1: Reg(2),
3151 src2: Reg(0),
3152 },
3153 block_id: 0,
3154 is_dead: false,
3155 },
3156 ];
3157
3158 let mut folder = ConstantFolding::new();
3159 let result = folder.run(&mut cfg, &mut instructions);
3160
3161 assert!(result.changed);
3163 assert_eq!(result.modified_count, 2); assert_eq!(
3165 instructions[2].opcode,
3166 Opcode::Const {
3167 dest: Reg(2),
3168 value: 5
3169 }
3170 ); assert_eq!(
3172 instructions[3].opcode,
3173 Opcode::Const {
3174 dest: Reg(3),
3175 value: 10
3176 }
3177 ); }
3179
3180 #[test]
3181 fn test_constant_folding_no_change() {
3182 let mut builder = CfgBuilder::new();
3183 builder.add_instruction();
3184
3185 let mut cfg = builder.build();
3186
3187 let mut instructions = vec![Instruction {
3189 id: 0,
3190 opcode: Opcode::Add {
3191 dest: Reg(2),
3192 src1: Reg(0),
3193 src2: Reg(1),
3194 },
3195 block_id: 0,
3196 is_dead: false,
3197 }];
3198
3199 let mut folder = ConstantFolding::new();
3200 let result = folder.run(&mut cfg, &mut instructions);
3201
3202 assert!(!result.changed);
3204 assert_eq!(result.modified_count, 0);
3205 }
3206
3207 #[test]
3208 fn test_cse_simple() {
3209 let mut builder = CfgBuilder::new();
3210 for _ in 0..3 {
3211 builder.add_instruction();
3212 }
3213
3214 let mut cfg = builder.build();
3215
3216 let mut instructions = vec![
3218 Instruction {
3219 id: 0,
3220 opcode: Opcode::Add {
3221 dest: Reg(2),
3222 src1: Reg(0),
3223 src2: Reg(1),
3224 },
3225 block_id: 0,
3226 is_dead: false,
3227 },
3228 Instruction {
3229 id: 1,
3230 opcode: Opcode::Add {
3231 dest: Reg(3),
3232 src1: Reg(0),
3233 src2: Reg(1),
3234 },
3235 block_id: 0,
3236 is_dead: false,
3237 },
3238 ];
3239
3240 let mut cse = CommonSubexpressionElimination::new();
3241 let result = cse.run(&mut cfg, &mut instructions);
3242
3243 assert!(result.changed);
3245 assert_eq!(result.removed_count, 1);
3246 assert!(instructions[1].is_dead);
3247 assert!(!instructions[0].is_dead);
3248 }
3249
3250 #[test]
3251 fn test_cse_multiple_ops() {
3252 let mut builder = CfgBuilder::new();
3253 for _ in 0..6 {
3254 builder.add_instruction();
3255 }
3256
3257 let mut cfg = builder.build();
3258
3259 let mut instructions = vec![
3261 Instruction {
3262 id: 0,
3263 opcode: Opcode::Add {
3264 dest: Reg(4),
3265 src1: Reg(0),
3266 src2: Reg(1),
3267 },
3268 block_id: 0,
3269 is_dead: false,
3270 },
3271 Instruction {
3272 id: 1,
3273 opcode: Opcode::Add {
3274 dest: Reg(5),
3275 src1: Reg(0),
3276 src2: Reg(1),
3277 },
3278 block_id: 0,
3279 is_dead: false,
3280 },
3281 Instruction {
3282 id: 2,
3283 opcode: Opcode::Sub {
3284 dest: Reg(6),
3285 src1: Reg(2),
3286 src2: Reg(3),
3287 },
3288 block_id: 0,
3289 is_dead: false,
3290 },
3291 Instruction {
3292 id: 3,
3293 opcode: Opcode::Sub {
3294 dest: Reg(7),
3295 src1: Reg(2),
3296 src2: Reg(3),
3297 },
3298 block_id: 0,
3299 is_dead: false,
3300 },
3301 ];
3302
3303 let mut cse = CommonSubexpressionElimination::new();
3304 let result = cse.run(&mut cfg, &mut instructions);
3305
3306 assert!(result.changed);
3308 assert_eq!(result.removed_count, 2);
3309 assert!(instructions[1].is_dead); assert!(instructions[3].is_dead); assert!(!instructions[0].is_dead);
3312 assert!(!instructions[2].is_dead);
3313 }
3314
3315 #[test]
3316 fn test_cse_load() {
3317 let mut builder = CfgBuilder::new();
3318 for _ in 0..2 {
3319 builder.add_instruction();
3320 }
3321
3322 let mut cfg = builder.build();
3323
3324 let mut instructions = vec![
3326 Instruction {
3327 id: 0,
3328 opcode: Opcode::Load {
3329 dest: Reg(0),
3330 addr: 0x100,
3331 },
3332 block_id: 0,
3333 is_dead: false,
3334 },
3335 Instruction {
3336 id: 1,
3337 opcode: Opcode::Load {
3338 dest: Reg(1),
3339 addr: 0x100,
3340 },
3341 block_id: 0,
3342 is_dead: false,
3343 },
3344 ];
3345
3346 let mut cse = CommonSubexpressionElimination::new();
3347 let result = cse.run(&mut cfg, &mut instructions);
3348
3349 assert!(result.changed);
3351 assert_eq!(result.removed_count, 1);
3352 assert!(instructions[1].is_dead);
3353 assert!(!instructions[0].is_dead);
3354 }
3355
3356 #[test]
3357 fn test_cse_store_invalidates_load() {
3358 let mut builder = CfgBuilder::new();
3359 for _ in 0..3 {
3360 builder.add_instruction();
3361 }
3362
3363 let mut cfg = builder.build();
3364
3365 let mut instructions = vec![
3367 Instruction {
3368 id: 0,
3369 opcode: Opcode::Load {
3370 dest: Reg(0),
3371 addr: 0x100,
3372 },
3373 block_id: 0,
3374 is_dead: false,
3375 },
3376 Instruction {
3377 id: 1,
3378 opcode: Opcode::Store {
3379 src: Reg(2),
3380 addr: 0x100,
3381 },
3382 block_id: 0,
3383 is_dead: false,
3384 },
3385 Instruction {
3386 id: 2,
3387 opcode: Opcode::Load {
3388 dest: Reg(1),
3389 addr: 0x100,
3390 },
3391 block_id: 0,
3392 is_dead: false,
3393 },
3394 ];
3395
3396 let mut cse = CommonSubexpressionElimination::new();
3397 let result = cse.run(&mut cfg, &mut instructions);
3398
3399 assert!(!result.changed);
3401 assert_eq!(result.removed_count, 0);
3402 assert!(!instructions[0].is_dead);
3403 assert!(!instructions[1].is_dead);
3404 assert!(!instructions[2].is_dead);
3405 }
3406
3407 #[test]
3408 fn test_cse_no_duplicates() {
3409 let mut builder = CfgBuilder::new();
3410 for _ in 0..2 {
3411 builder.add_instruction();
3412 }
3413
3414 let mut cfg = builder.build();
3415
3416 let mut instructions = vec![
3418 Instruction {
3419 id: 0,
3420 opcode: Opcode::Add {
3421 dest: Reg(2),
3422 src1: Reg(0),
3423 src2: Reg(1),
3424 },
3425 block_id: 0,
3426 is_dead: false,
3427 },
3428 Instruction {
3429 id: 1,
3430 opcode: Opcode::Sub {
3431 dest: Reg(3),
3432 src1: Reg(0),
3433 src2: Reg(1),
3434 },
3435 block_id: 0,
3436 is_dead: false,
3437 },
3438 ];
3439
3440 let mut cse = CommonSubexpressionElimination::new();
3441 let result = cse.run(&mut cfg, &mut instructions);
3442
3443 assert!(!result.changed);
3445 assert_eq!(result.removed_count, 0);
3446 }
3447
3448 #[test]
3449 fn test_algebraic_add_zero() {
3450 let mut builder = CfgBuilder::new();
3451 for _ in 0..3 {
3452 builder.add_instruction();
3453 }
3454
3455 let mut cfg = builder.build();
3456
3457 let mut instructions = vec![
3459 Instruction {
3460 id: 0,
3461 opcode: Opcode::Const {
3462 dest: Reg(0),
3463 value: 0,
3464 },
3465 block_id: 0,
3466 is_dead: false,
3467 },
3468 Instruction {
3469 id: 1,
3470 opcode: Opcode::Add {
3471 dest: Reg(2),
3472 src1: Reg(1),
3473 src2: Reg(0),
3474 },
3475 block_id: 0,
3476 is_dead: false,
3477 },
3478 ];
3479
3480 let mut simplify = AlgebraicSimplification::new();
3481 let result = simplify.run(&mut cfg, &mut instructions);
3482
3483 assert!(result.changed);
3485 assert_eq!(result.modified_count, 1);
3486 assert!(instructions[1].is_dead);
3487 }
3488
3489 #[test]
3490 fn test_algebraic_sub_zero() {
3491 let mut builder = CfgBuilder::new();
3492 for _ in 0..2 {
3493 builder.add_instruction();
3494 }
3495
3496 let mut cfg = builder.build();
3497
3498 let mut instructions = vec![
3500 Instruction {
3501 id: 0,
3502 opcode: Opcode::Const {
3503 dest: Reg(0),
3504 value: 0,
3505 },
3506 block_id: 0,
3507 is_dead: false,
3508 },
3509 Instruction {
3510 id: 1,
3511 opcode: Opcode::Sub {
3512 dest: Reg(2),
3513 src1: Reg(1),
3514 src2: Reg(0),
3515 },
3516 block_id: 0,
3517 is_dead: false,
3518 },
3519 ];
3520
3521 let mut simplify = AlgebraicSimplification::new();
3522 let result = simplify.run(&mut cfg, &mut instructions);
3523
3524 assert!(result.changed);
3526 assert_eq!(result.modified_count, 1);
3527 assert!(instructions[1].is_dead);
3528 }
3529
3530 #[test]
3531 fn test_algebraic_sub_self() {
3532 let mut builder = CfgBuilder::new();
3533 builder.add_instruction();
3534
3535 let mut cfg = builder.build();
3536
3537 let mut instructions = vec![Instruction {
3539 id: 0,
3540 opcode: Opcode::Sub {
3541 dest: Reg(2),
3542 src1: Reg(1),
3543 src2: Reg(1),
3544 },
3545 block_id: 0,
3546 is_dead: false,
3547 }];
3548
3549 let mut simplify = AlgebraicSimplification::new();
3550 let result = simplify.run(&mut cfg, &mut instructions);
3551
3552 assert!(result.changed);
3554 assert_eq!(result.modified_count, 1);
3555 assert_eq!(
3556 instructions[0].opcode,
3557 Opcode::Const {
3558 dest: Reg(2),
3559 value: 0
3560 }
3561 );
3562 }
3563
3564 #[test]
3565 fn test_algebraic_mul_zero() {
3566 let mut builder = CfgBuilder::new();
3567 for _ in 0..2 {
3568 builder.add_instruction();
3569 }
3570
3571 let mut cfg = builder.build();
3572
3573 let mut instructions = vec![
3575 Instruction {
3576 id: 0,
3577 opcode: Opcode::Const {
3578 dest: Reg(0),
3579 value: 0,
3580 },
3581 block_id: 0,
3582 is_dead: false,
3583 },
3584 Instruction {
3585 id: 1,
3586 opcode: Opcode::Mul {
3587 dest: Reg(2),
3588 src1: Reg(1),
3589 src2: Reg(0),
3590 },
3591 block_id: 0,
3592 is_dead: false,
3593 },
3594 ];
3595
3596 let mut simplify = AlgebraicSimplification::new();
3597 let result = simplify.run(&mut cfg, &mut instructions);
3598
3599 assert!(result.changed);
3601 assert_eq!(result.modified_count, 1);
3602 assert_eq!(
3603 instructions[1].opcode,
3604 Opcode::Const {
3605 dest: Reg(2),
3606 value: 0
3607 }
3608 );
3609 }
3610
3611 #[test]
3612 fn test_algebraic_mul_one() {
3613 let mut builder = CfgBuilder::new();
3614 for _ in 0..2 {
3615 builder.add_instruction();
3616 }
3617
3618 let mut cfg = builder.build();
3619
3620 let mut instructions = vec![
3622 Instruction {
3623 id: 0,
3624 opcode: Opcode::Const {
3625 dest: Reg(0),
3626 value: 1,
3627 },
3628 block_id: 0,
3629 is_dead: false,
3630 },
3631 Instruction {
3632 id: 1,
3633 opcode: Opcode::Mul {
3634 dest: Reg(2),
3635 src1: Reg(1),
3636 src2: Reg(0),
3637 },
3638 block_id: 0,
3639 is_dead: false,
3640 },
3641 ];
3642
3643 let mut simplify = AlgebraicSimplification::new();
3644 let result = simplify.run(&mut cfg, &mut instructions);
3645
3646 assert!(result.changed);
3648 assert_eq!(result.modified_count, 1);
3649 assert!(instructions[1].is_dead);
3650 }
3651
3652 #[test]
3653 fn test_algebraic_multiple() {
3654 let mut builder = CfgBuilder::new();
3655 for _ in 0..5 {
3656 builder.add_instruction();
3657 }
3658
3659 let mut cfg = builder.build();
3660
3661 let mut instructions = vec![
3663 Instruction {
3664 id: 0,
3665 opcode: Opcode::Const {
3666 dest: Reg(0),
3667 value: 0,
3668 },
3669 block_id: 0,
3670 is_dead: false,
3671 },
3672 Instruction {
3673 id: 1,
3674 opcode: Opcode::Const {
3675 dest: Reg(1),
3676 value: 1,
3677 },
3678 block_id: 0,
3679 is_dead: false,
3680 },
3681 Instruction {
3682 id: 2,
3683 opcode: Opcode::Add {
3684 dest: Reg(5),
3685 src1: Reg(2),
3686 src2: Reg(0),
3687 },
3688 block_id: 0,
3689 is_dead: false,
3690 },
3691 Instruction {
3692 id: 3,
3693 opcode: Opcode::Mul {
3694 dest: Reg(6),
3695 src1: Reg(3),
3696 src2: Reg(1),
3697 },
3698 block_id: 0,
3699 is_dead: false,
3700 },
3701 Instruction {
3702 id: 4,
3703 opcode: Opcode::Sub {
3704 dest: Reg(7),
3705 src1: Reg(4),
3706 src2: Reg(4),
3707 },
3708 block_id: 0,
3709 is_dead: false,
3710 },
3711 ];
3712
3713 let mut simplify = AlgebraicSimplification::new();
3714 let result = simplify.run(&mut cfg, &mut instructions);
3715
3716 assert!(result.changed);
3718 assert_eq!(result.modified_count, 3);
3719 assert!(instructions[2].is_dead); assert!(instructions[3].is_dead); assert_eq!(
3722 instructions[4].opcode,
3723 Opcode::Const {
3724 dest: Reg(7),
3725 value: 0
3726 }
3727 ); }
3729
3730 #[test]
3731 fn test_peephole_redundant_const() {
3732 let mut builder = CfgBuilder::new();
3733 for _ in 0..2 {
3734 builder.add_instruction();
3735 }
3736
3737 let mut cfg = builder.build();
3738
3739 let mut instructions = vec![
3741 Instruction {
3742 id: 0,
3743 opcode: Opcode::Const {
3744 dest: Reg(0),
3745 value: 5,
3746 },
3747 block_id: 0,
3748 is_dead: false,
3749 },
3750 Instruction {
3751 id: 1,
3752 opcode: Opcode::Const {
3753 dest: Reg(0),
3754 value: 10,
3755 },
3756 block_id: 0,
3757 is_dead: false,
3758 },
3759 ];
3760
3761 let mut peephole = PeepholeOptimization::new();
3762 let result = peephole.run(&mut cfg, &mut instructions);
3763
3764 assert!(result.changed);
3766 assert_eq!(result.removed_count, 1);
3767 assert!(instructions[0].is_dead);
3768 assert!(!instructions[1].is_dead);
3769 }
3770
3771 #[test]
3772 fn test_peephole_no_redundant_const() {
3773 let mut builder = CfgBuilder::new();
3774 for _ in 0..2 {
3775 builder.add_instruction();
3776 }
3777
3778 let mut cfg = builder.build();
3779
3780 let mut instructions = vec![
3782 Instruction {
3783 id: 0,
3784 opcode: Opcode::Const {
3785 dest: Reg(0),
3786 value: 5,
3787 },
3788 block_id: 0,
3789 is_dead: false,
3790 },
3791 Instruction {
3792 id: 1,
3793 opcode: Opcode::Const {
3794 dest: Reg(1),
3795 value: 10,
3796 },
3797 block_id: 0,
3798 is_dead: false,
3799 },
3800 ];
3801
3802 let mut peephole = PeepholeOptimization::new();
3803 let result = peephole.run(&mut cfg, &mut instructions);
3804
3805 assert!(!result.changed);
3807 assert_eq!(result.removed_count, 0);
3808 }
3809
3810 #[test]
3811 fn test_full_optimization_pipeline() {
3812 let mut builder = CfgBuilder::new();
3813 for _ in 0..10 {
3814 builder.add_instruction();
3815 }
3816
3817 let mut cfg = builder.build();
3818
3819 let mut instructions = vec![
3821 Instruction {
3823 id: 0,
3824 opcode: Opcode::Const {
3825 dest: Reg(0),
3826 value: 5,
3827 },
3828 block_id: 0,
3829 is_dead: false,
3830 },
3831 Instruction {
3832 id: 1,
3833 opcode: Opcode::Const {
3834 dest: Reg(0),
3835 value: 10,
3836 },
3837 block_id: 0,
3838 is_dead: false,
3839 },
3840 Instruction {
3842 id: 2,
3843 opcode: Opcode::Const {
3844 dest: Reg(1),
3845 value: 20,
3846 },
3847 block_id: 0,
3848 is_dead: false,
3849 },
3850 Instruction {
3851 id: 3,
3852 opcode: Opcode::Add {
3853 dest: Reg(2),
3854 src1: Reg(0),
3855 src2: Reg(1),
3856 },
3857 block_id: 0,
3858 is_dead: false,
3859 },
3860 Instruction {
3862 id: 4,
3863 opcode: Opcode::Const {
3864 dest: Reg(3),
3865 value: 0,
3866 },
3867 block_id: 0,
3868 is_dead: false,
3869 },
3870 Instruction {
3871 id: 5,
3872 opcode: Opcode::Add {
3873 dest: Reg(4),
3874 src1: Reg(2),
3875 src2: Reg(3),
3876 },
3877 block_id: 0,
3878 is_dead: false,
3879 },
3880 Instruction {
3882 id: 6,
3883 opcode: Opcode::Add {
3884 dest: Reg(5),
3885 src1: Reg(0),
3886 src2: Reg(1),
3887 },
3888 block_id: 0,
3889 is_dead: false,
3890 },
3891 ];
3892
3893 let mut manager = PassManager::new()
3895 .add_pass(PeepholeOptimization::new())
3896 .add_pass(ConstantFolding::new())
3897 .add_pass(AlgebraicSimplification::new())
3898 .add_pass(CommonSubexpressionElimination::new())
3899 .with_max_iterations(5);
3900
3901 let result = manager.run(&mut cfg, &mut instructions);
3902
3903 assert!(result.changed);
3905
3906 let total_opts = result.removed_count + result.modified_count;
3908 assert!(
3909 total_opts >= 2,
3910 "Expected at least 2 optimizations, got {}",
3911 total_opts
3912 );
3913
3914 assert!(instructions[0].is_dead, "Redundant const not eliminated");
3916
3917 if let Opcode::Const { value, .. } = instructions[3].opcode {
3919 assert_eq!(value, 30, "Constant folding failed");
3920 } else {
3921 panic!("Expected const, got {:?}", instructions[3].opcode);
3922 }
3923 }
3924
3925 #[test]
3926 fn test_strength_reduction_mul_power_of_2() {
3927 let mut builder = CfgBuilder::new();
3928 for _ in 0..2 {
3929 builder.add_instruction();
3930 }
3931
3932 let mut cfg = builder.build();
3933
3934 let mut instructions = vec![
3937 Instruction {
3938 id: 0,
3939 opcode: Opcode::Const {
3940 dest: Reg(0),
3941 value: 8,
3942 },
3943 block_id: 0,
3944 is_dead: false,
3945 },
3946 Instruction {
3947 id: 1,
3948 opcode: Opcode::Mul {
3949 dest: Reg(2),
3950 src1: Reg(1),
3951 src2: Reg(0),
3952 },
3953 block_id: 0,
3954 is_dead: false,
3955 },
3956 ];
3957
3958 let mut sr = StrengthReduction::new();
3959 let result = sr.run(&mut cfg, &mut instructions);
3960
3961 assert!(result.changed);
3963 assert_eq!(result.modified_count, 1);
3964 }
3965
3966 #[test]
3967 fn test_strength_reduction_mul_non_power_of_2() {
3968 let mut builder = CfgBuilder::new();
3969 for _ in 0..2 {
3970 builder.add_instruction();
3971 }
3972
3973 let mut cfg = builder.build();
3974
3975 let mut instructions = vec![
3978 Instruction {
3979 id: 0,
3980 opcode: Opcode::Const {
3981 dest: Reg(0),
3982 value: 7,
3983 },
3984 block_id: 0,
3985 is_dead: false,
3986 },
3987 Instruction {
3988 id: 1,
3989 opcode: Opcode::Mul {
3990 dest: Reg(2),
3991 src1: Reg(1),
3992 src2: Reg(0),
3993 },
3994 block_id: 0,
3995 is_dead: false,
3996 },
3997 ];
3998
3999 let mut sr = StrengthReduction::new();
4000 let result = sr.run(&mut cfg, &mut instructions);
4001
4002 assert!(!result.changed);
4004 assert_eq!(result.modified_count, 0);
4005 }
4006
4007 #[test]
4008 fn test_strength_reduction_multiple_powers() {
4009 let mut builder = CfgBuilder::new();
4010 for _ in 0..6 {
4011 builder.add_instruction();
4012 }
4013
4014 let mut cfg = builder.build();
4015
4016 let mut instructions = vec![
4018 Instruction {
4019 id: 0,
4020 opcode: Opcode::Const {
4021 dest: Reg(0),
4022 value: 4,
4023 },
4024 block_id: 0,
4025 is_dead: false,
4026 },
4027 Instruction {
4028 id: 1,
4029 opcode: Opcode::Mul {
4030 dest: Reg(2),
4031 src1: Reg(1),
4032 src2: Reg(0),
4033 },
4034 block_id: 0,
4035 is_dead: false,
4036 },
4037 Instruction {
4038 id: 2,
4039 opcode: Opcode::Const {
4040 dest: Reg(3),
4041 value: 16,
4042 },
4043 block_id: 0,
4044 is_dead: false,
4045 },
4046 Instruction {
4047 id: 3,
4048 opcode: Opcode::Mul {
4049 dest: Reg(5),
4050 src1: Reg(4),
4051 src2: Reg(3),
4052 },
4053 block_id: 0,
4054 is_dead: false,
4055 },
4056 Instruction {
4057 id: 4,
4058 opcode: Opcode::Const {
4059 dest: Reg(6),
4060 value: 5,
4061 },
4062 block_id: 0,
4063 is_dead: false,
4064 },
4065 Instruction {
4066 id: 5,
4067 opcode: Opcode::Mul {
4068 dest: Reg(8),
4069 src1: Reg(7),
4070 src2: Reg(6),
4071 },
4072 block_id: 0,
4073 is_dead: false,
4074 },
4075 ];
4076
4077 let mut sr = StrengthReduction::new();
4078 let result = sr.run(&mut cfg, &mut instructions);
4079
4080 assert!(result.changed);
4082 assert_eq!(result.modified_count, 2);
4083 }
4084
4085 #[test]
4086 fn test_licm_detect_invariants() {
4087 let mut builder = CfgBuilder::new();
4088 let block0 = 0; let block1 = builder.start_block();
4091 let block2 = builder.start_block();
4092
4093 builder.set_current_block(block0);
4095 builder.add_branch(block1);
4096
4097 builder.set_current_block(block1);
4098 builder.add_branch(block2);
4099
4100 builder.set_current_block(block2);
4101 builder.add_branch(block1); builder.set_current_block(block1);
4104 builder.add_branch(block0); let mut cfg = builder.build();
4107
4108 cfg.loops.push(Loop {
4110 header: block1,
4111 body: vec![block1, block2].into_iter().collect(),
4112 depth: 1,
4113 });
4114
4115 let mut instructions = vec![
4117 Instruction {
4119 id: 0,
4120 opcode: Opcode::Const {
4121 dest: Reg(0),
4122 value: 10,
4123 },
4124 block_id: block1,
4125 is_dead: false,
4126 },
4127 Instruction {
4129 id: 1,
4130 opcode: Opcode::Add {
4131 dest: Reg(2),
4132 src1: Reg(1),
4133 src2: Reg(0),
4134 },
4135 block_id: block1,
4136 is_dead: false,
4137 },
4138 ];
4139
4140 let mut licm = LoopInvariantCodeMotion::new();
4141 let result = licm.run(&mut cfg, &mut instructions);
4142
4143 assert!(result.changed);
4145 assert_eq!(result.modified_count, 1);
4146 }
4147
4148 #[test]
4149 fn test_licm_no_loops() {
4150 let mut builder = CfgBuilder::new();
4151 builder.add_instruction();
4152
4153 let mut cfg = builder.build();
4154
4155 let mut instructions = vec![Instruction {
4156 id: 0,
4157 opcode: Opcode::Const {
4158 dest: Reg(0),
4159 value: 10,
4160 },
4161 block_id: 0,
4162 is_dead: false,
4163 }];
4164
4165 let mut licm = LoopInvariantCodeMotion::new();
4166 let result = licm.run(&mut cfg, &mut instructions);
4167
4168 assert!(!result.changed);
4170 assert_eq!(result.modified_count, 0);
4171 }
4172
4173 #[test]
4174 fn test_pass_manager_with_advanced_passes() {
4175 let mut builder = CfgBuilder::new();
4176 for _ in 0..4 {
4177 builder.add_instruction();
4178 }
4179
4180 let mut cfg = builder.build();
4181
4182 let mut instructions = vec![
4184 Instruction {
4185 id: 0,
4186 opcode: Opcode::Const {
4187 dest: Reg(0),
4188 value: 8,
4189 },
4190 block_id: 0,
4191 is_dead: false,
4192 },
4193 Instruction {
4194 id: 1,
4195 opcode: Opcode::Const {
4196 dest: Reg(1),
4197 value: 5,
4198 },
4199 block_id: 0,
4200 is_dead: false,
4201 },
4202 Instruction {
4203 id: 2,
4204 opcode: Opcode::Mul {
4205 dest: Reg(2),
4206 src1: Reg(1),
4207 src2: Reg(0),
4208 },
4209 block_id: 0,
4210 is_dead: false,
4211 },
4212 Instruction {
4213 id: 3,
4214 opcode: Opcode::Add {
4215 dest: Reg(3),
4216 src1: Reg(0),
4217 src2: Reg(1),
4218 },
4219 block_id: 0,
4220 is_dead: false,
4221 },
4222 ];
4223
4224 let mut manager = PassManager::new()
4226 .add_pass(StrengthReduction::new())
4227 .add_pass(ConstantFolding::new())
4228 .with_max_iterations(3);
4229
4230 let result = manager.run(&mut cfg, &mut instructions);
4231
4232 assert!(result.changed);
4234
4235 let total_opts = result.removed_count + result.modified_count;
4237 assert!(total_opts >= 1);
4238 }
4239
4240 #[test]
4241 fn test_copy_propagation_basic() {
4242 let mut builder = CfgBuilder::new();
4243 for _ in 0..2 {
4244 builder.add_instruction();
4245 }
4246
4247 let mut cfg = builder.build();
4248
4249 let mut instructions = vec![
4252 Instruction {
4253 id: 0,
4254 opcode: Opcode::Const {
4255 dest: Reg(0),
4256 value: 10,
4257 },
4258 block_id: 0,
4259 is_dead: false,
4260 },
4261 Instruction {
4262 id: 1,
4263 opcode: Opcode::Add {
4264 dest: Reg(2),
4265 src1: Reg(0),
4266 src2: Reg(1),
4267 },
4268 block_id: 0,
4269 is_dead: false,
4270 },
4271 ];
4272
4273 let mut cp = CopyPropagation::new();
4274 let result = cp.run(&mut cfg, &mut instructions);
4275
4276 assert!(!result.changed);
4278 }
4279
4280 #[test]
4281 fn test_copy_propagation_with_store() {
4282 let mut builder = CfgBuilder::new();
4283 for _ in 0..2 {
4284 builder.add_instruction();
4285 }
4286
4287 let mut cfg = builder.build();
4288
4289 let mut instructions = vec![
4290 Instruction {
4291 id: 0,
4292 opcode: Opcode::Const {
4293 dest: Reg(0),
4294 value: 10,
4295 },
4296 block_id: 0,
4297 is_dead: false,
4298 },
4299 Instruction {
4300 id: 1,
4301 opcode: Opcode::Store {
4302 src: Reg(0),
4303 addr: 100,
4304 },
4305 block_id: 0,
4306 is_dead: false,
4307 },
4308 ];
4309
4310 let mut cp = CopyPropagation::new();
4311 let result = cp.run(&mut cfg, &mut instructions);
4312
4313 assert!(!result.changed);
4315 }
4316
4317 #[test]
4318 fn test_instruction_combining_nested_add() {
4319 let mut builder = CfgBuilder::new();
4320 for _ in 0..4 {
4321 builder.add_instruction();
4322 }
4323
4324 let mut cfg = builder.build();
4325
4326 let mut instructions = vec![
4328 Instruction {
4329 id: 0,
4330 opcode: Opcode::Const {
4331 dest: Reg(1),
4332 value: 5,
4333 },
4334 block_id: 0,
4335 is_dead: false,
4336 },
4337 Instruction {
4338 id: 1,
4339 opcode: Opcode::Add {
4340 dest: Reg(2),
4341 src1: Reg(0),
4342 src2: Reg(1),
4343 },
4344 block_id: 0,
4345 is_dead: false,
4346 },
4347 Instruction {
4348 id: 2,
4349 opcode: Opcode::Const {
4350 dest: Reg(3),
4351 value: 10,
4352 },
4353 block_id: 0,
4354 is_dead: false,
4355 },
4356 Instruction {
4357 id: 3,
4358 opcode: Opcode::Add {
4359 dest: Reg(4),
4360 src1: Reg(2),
4361 src2: Reg(3),
4362 },
4363 block_id: 0,
4364 is_dead: false,
4365 },
4366 ];
4367
4368 let mut ic = InstructionCombining::new();
4369 let result = ic.run(&mut cfg, &mut instructions);
4370
4371 assert!(result.changed);
4373 assert_eq!(result.modified_count, 1);
4374 }
4375
4376 #[test]
4377 fn test_instruction_combining_no_pattern() {
4378 let mut builder = CfgBuilder::new();
4379 for _ in 0..2 {
4380 builder.add_instruction();
4381 }
4382
4383 let mut cfg = builder.build();
4384
4385 let mut instructions = vec![
4387 Instruction {
4388 id: 0,
4389 opcode: Opcode::Const {
4390 dest: Reg(0),
4391 value: 10,
4392 },
4393 block_id: 0,
4394 is_dead: false,
4395 },
4396 Instruction {
4397 id: 1,
4398 opcode: Opcode::Const {
4399 dest: Reg(1),
4400 value: 20,
4401 },
4402 block_id: 0,
4403 is_dead: false,
4404 },
4405 ];
4406
4407 let mut ic = InstructionCombining::new();
4408 let result = ic.run(&mut cfg, &mut instructions);
4409
4410 assert!(!result.changed);
4412 }
4413
4414 #[test]
4415 fn test_all_passes_together() {
4416 let mut builder = CfgBuilder::new();
4417 for _ in 0..10 {
4418 builder.add_instruction();
4419 }
4420
4421 let mut cfg = builder.build();
4422
4423 let mut instructions = vec![
4425 Instruction {
4426 id: 0,
4427 opcode: Opcode::Const {
4428 dest: Reg(0),
4429 value: 8,
4430 },
4431 block_id: 0,
4432 is_dead: false,
4433 },
4434 Instruction {
4435 id: 1,
4436 opcode: Opcode::Const {
4437 dest: Reg(1),
4438 value: 5,
4439 },
4440 block_id: 0,
4441 is_dead: false,
4442 },
4443 Instruction {
4444 id: 2,
4445 opcode: Opcode::Mul {
4446 dest: Reg(2),
4447 src1: Reg(1),
4448 src2: Reg(0),
4449 },
4450 block_id: 0,
4451 is_dead: false,
4452 },
4453 Instruction {
4454 id: 3,
4455 opcode: Opcode::Const {
4456 dest: Reg(3),
4457 value: 10,
4458 },
4459 block_id: 0,
4460 is_dead: false,
4461 },
4462 Instruction {
4463 id: 4,
4464 opcode: Opcode::Const {
4465 dest: Reg(4),
4466 value: 20,
4467 },
4468 block_id: 0,
4469 is_dead: false,
4470 },
4471 Instruction {
4472 id: 5,
4473 opcode: Opcode::Add {
4474 dest: Reg(5),
4475 src1: Reg(3),
4476 src2: Reg(4),
4477 },
4478 block_id: 0,
4479 is_dead: false,
4480 },
4481 Instruction {
4482 id: 6,
4483 opcode: Opcode::Const {
4484 dest: Reg(6),
4485 value: 0,
4486 },
4487 block_id: 0,
4488 is_dead: false,
4489 },
4490 Instruction {
4491 id: 7,
4492 opcode: Opcode::Add {
4493 dest: Reg(7),
4494 src1: Reg(2),
4495 src2: Reg(6),
4496 },
4497 block_id: 0,
4498 is_dead: false,
4499 },
4500 ];
4501
4502 let mut manager = PassManager::new()
4504 .add_pass(PeepholeOptimization::new())
4505 .add_pass(ConstantFolding::new())
4506 .add_pass(AlgebraicSimplification::new())
4507 .add_pass(StrengthReduction::new())
4508 .add_pass(CopyPropagation::new())
4509 .add_pass(InstructionCombining::new())
4510 .add_pass(CommonSubexpressionElimination::new())
4511 .add_pass(DeadCodeElimination::new())
4512 .with_max_iterations(5);
4513
4514 let result = manager.run(&mut cfg, &mut instructions);
4515
4516 assert!(result.changed);
4518
4519 let total_opts = result.removed_count + result.modified_count + result.added_count;
4520 assert!(
4521 total_opts >= 3,
4522 "Expected at least 3 optimizations, got {}",
4523 total_opts
4524 );
4525 }
4526}