1use std::collections::BTreeSet;
11
12use cranelift_entity::{entity_impl, packed_option::PackedOption, PrimaryMap, SecondaryMap};
13use fxhash::{FxHashMap, FxHashSet};
14
15use crate::{
16 cfg::ControlFlowGraph,
17 domtree::{DomTree, DominatorTreeTraversable},
18};
19
20use sonatina_ir::{
21 func_cursor::{CursorLocation, FuncCursor, InsnInserter},
22 insn::{BinaryOp, CastOp, InsnData, UnaryOp},
23 Block, DataFlowGraph, Function, Immediate, Insn, Type, Value,
24};
25
26use super::{constant_folding, simplify_impl};
27
28const INITIAL_CLASS: Class = Class(0);
32
33const IMMEDIATE_RANK: u32 = 0;
35
36#[derive(Debug)]
38pub struct GvnSolver {
39 classes: PrimaryMap<Class, ClassData>,
41
42 values: SecondaryMap<Value, GvnValueData>,
44
45 insn_table: FxHashMap<GvnInsn, Class>,
47
48 edges: PrimaryMap<Edge, EdgeData>,
50
51 blocks: SecondaryMap<Block, GvnBlockData>,
53
54 value_phi_table: FxHashMap<ValuePhi, Class>,
55
56 always_avail: Vec<Value>,
58}
59
60impl GvnSolver {
61 pub fn new() -> Self {
62 Self {
63 classes: PrimaryMap::default(),
64 values: SecondaryMap::default(),
65 insn_table: FxHashMap::default(),
66 edges: PrimaryMap::default(),
67 blocks: SecondaryMap::default(),
68 value_phi_table: FxHashMap::default(),
69 always_avail: Vec::default(),
70 }
71 }
72 pub fn run(&mut self, func: &mut Function, cfg: &mut ControlFlowGraph, domtree: &mut DomTree) {
75 self.clear();
76
77 if func.layout.entry_block().is_none() {
79 return;
80 }
81
82 self.classes.push(ClassData {
84 values: BTreeSet::new(),
85 gvn_insn: GvnInsn::Value(Value(u32::MAX)),
86 value_phi: None,
87 });
88 debug_assert!(self.classes.len() == 1);
89
90 for &value in func.dfg.immediates.values() {
92 self.assign_class_to_imm_value(value);
93 }
94
95 let mut rank = IMMEDIATE_RANK + 1;
97 for &arg in &func.arg_values {
98 self.values[arg].rank = rank;
99 rank += 1;
100 let gvn_insn = GvnInsn::Value(arg);
101 self.always_avail.push(arg);
102 let class = self.make_class(gvn_insn, None);
103 self.assign_class(arg, class);
104 }
105
106 for &block in domtree.rpo() {
108 self.blocks[block].rank = rank;
110 rank += 1;
111
112 for insn in func.layout.iter_insn(block) {
113 if let Some(insn_result) = func.dfg.insn_result(insn) {
114 self.values[insn_result].rank = rank;
115 rank += 1;
116 }
117
118 match func.dfg.insn_data(insn) {
120 InsnData::Jump { dests, .. } => {
121 let dest = dests[0];
122 self.add_edge_info(block, dest, None, None, None);
123 }
124
125 InsnData::Branch { args, dests } => {
126 let cond = args[0];
127 debug_assert_eq!(func.dfg.value_ty(cond), Type::I1);
128
129 let then_block = dests[0];
130 let else_block = dests[1];
131
132 let then_predicate = func.dfg.value_insn(cond).map(|insn| {
135 let insn_data = func.dfg.insn_data(insn).clone();
136 self.perform_predicate_simplification(&mut func.dfg, insn_data)
137 });
138 let else_predicate = self.perform_predicate_simplification(
139 &mut func.dfg,
140 InsnData::unary(UnaryOp::Not, cond),
141 );
142
143 let then_imm = self.make_imm(&mut func.dfg, true);
145 let else_imm = self.make_imm(&mut func.dfg, false);
146
147 self.add_edge_info(
148 block,
149 then_block,
150 Some(cond),
151 then_predicate,
152 Some(then_imm),
153 );
154 self.add_edge_info(
155 block,
156 else_block,
157 Some(cond),
158 Some(else_predicate),
159 Some(else_imm),
160 );
161 }
162
163 InsnData::BrTable {
164 args,
165 default,
166 table,
167 } => {
168 if let Some(default) = default {
170 self.add_edge_info(block, *default, None, None, None);
171 }
172 let cond = args[0];
173
174 for (value, dest) in args[1..].iter().zip(table.iter()) {
175 self.add_edge_info(block, *dest, Some(cond), None, Some(*value));
176 }
177 }
178
179 _ => {}
180 }
181 }
182 }
183
184 let entry = func.layout.entry_block().unwrap();
186 self.blocks[entry].reachable = true;
187
188 let mut changed = true;
192 while changed {
193 changed = false;
194 for &block in domtree.rpo() {
195 if !self.blocks[block].reachable {
197 continue;
198 }
199
200 let mut next_insn = func.layout.first_insn_of(block);
201 while let Some(insn) = next_insn {
202 if let Some(insn_result) = func.dfg.insn_result(insn) {
204 changed |= self.reassign_congruence(func, domtree, insn, insn_result);
205 }
206 next_insn = func.layout.next_insn_of(insn);
207 }
208
209 if let Some(last_insn) = func.layout.last_insn_of(block) {
211 changed |= self.analyze_last_insn(func, domtree, block, last_insn);
212 }
213 }
214 }
215
216 let mut remover = RedundantCodeRemover::new(self);
218 remover.remove_redundant_code(func, cfg, domtree);
219 }
220
221 pub fn clear(&mut self) {
223 self.classes.clear();
224 self.values.clear();
225 self.insn_table.clear();
226 self.edges.clear();
227 self.blocks.clear();
228 self.value_phi_table.clear();
229 self.always_avail.clear();
230 }
231
232 fn analyze_last_insn(
237 &mut self,
238 func: &Function,
239 domtree: &DomTree,
240 block: Block,
241 insn: Insn,
242 ) -> bool {
243 match func.dfg.insn_data(insn) {
244 InsnData::Jump { .. } => {
245 let out_edges = &self.blocks[block].out_edges;
246 debug_assert_eq!(out_edges.len(), 1);
247 let out_edge = out_edges[0];
248 self.mark_edge_reachable(out_edge)
249 }
250
251 InsnData::Branch {
252 args,
253 dests: [then_dest, else_dest],
254 } => {
255 let cond = args[0];
256 let out_edges = &self.blocks[block].out_edges;
257 debug_assert_eq!(out_edges.len(), 2);
258
259 let then_edge = *out_edges
260 .iter()
261 .find(|edge| self.edge_data(**edge).to == *then_dest)
262 .unwrap();
263 let else_edge = *out_edges
264 .iter()
265 .find(|edge| self.edge_data(**edge).to == *else_dest)
266 .unwrap();
267
268 if self.infer_edge_reachability(func, cond, then_edge, domtree) {
270 self.mark_edge_reachable(then_edge)
271 } else if self.infer_edge_reachability(func, cond, else_edge, domtree) {
272 self.mark_edge_reachable(else_edge)
273 } else {
274 let changed = self.mark_edge_reachable(then_edge);
276 changed || self.mark_edge_reachable(else_edge)
277 }
278 }
279
280 InsnData::BrTable {
281 args,
282 default,
283 table,
284 } => {
285 let cond = args[0];
286
287 for dest in table {
290 let edge = self.find_edge(&self.blocks[block].out_edges, block, *dest);
291 if self.infer_edge_reachability(func, cond, edge, domtree) {
292 return self.mark_edge_reachable(edge);
293 }
294 }
295
296 let mut changed = false;
299 if let Some(default) = default {
300 let edge_to_default =
301 self.find_edge(&self.blocks[block].out_edges, block, *default);
302 changed |= self.mark_edge_reachable(edge_to_default);
303 }
304 for dest in table {
305 let edge = self.find_edge(&self.blocks[block].out_edges, block, *dest);
306 changed |= self.mark_edge_reachable(edge);
307 }
308
309 changed
310 }
311
312 _ => false,
313 }
314 }
315
316 fn reassign_congruence(
319 &mut self,
320 func: &mut Function,
321 domtree: &DomTree,
322 insn: Insn,
323 insn_result: Value,
324 ) -> bool {
325 let block = func.layout.insn_block(insn);
327 let gvn_insn = self.perform_symbolic_evaluation(
328 func,
329 domtree,
330 func.dfg.insn_data(insn).clone(),
331 block,
332 );
333
334 if func.dfg.has_side_effect(insn) {
337 if self.value_class(insn_result) == INITIAL_CLASS {
338 let class = self.make_class(gvn_insn, None);
339 self.assign_class(insn_result, class);
340 return true;
341 } else {
342 return false;
343 }
344 }
345
346 let mut changed = false;
347 let new_class = if let GvnInsn::Value(value) = &gvn_insn {
348 self.value_class(*value)
350 } else if let Some(class) = self.insn_table.get(&gvn_insn).copied() {
351 changed |= self.recompute_value_phi(func, &gvn_insn, insn_result);
356 class
357 } else if let Some(value_phi) =
358 ValuePhiFinder::new(self, insn_result).compute_value_phi(func, &gvn_insn)
359 {
360 if let Some(class) = self.value_phi_table.get(&value_phi) {
361 *class
362 } else {
363 self.make_class(gvn_insn.clone(), Some(value_phi))
364 }
365 } else {
366 self.make_class(gvn_insn.clone(), None)
367 };
368
369 let old_class = self.value_class(insn_result);
370 if old_class == new_class {
371 changed
372 } else {
373 self.assign_class(insn_result, new_class);
374 true
375 }
376 }
377
378 fn recompute_value_phi(
383 &mut self,
384 func: &mut Function,
385 insn_data: &GvnInsn,
386 insn_result: Value,
387 ) -> bool {
388 let value_phi = ValuePhiFinder::new(self, insn_result).compute_value_phi(func, insn_data);
389 let class = self.value_class(insn_result);
390
391 let old_value_phi = &mut self.classes[class].value_phi;
392
393 if &value_phi == old_value_phi {
394 false
395 } else {
396 *old_value_phi = value_phi;
397 true
398 }
399 }
400
401 fn mark_edge_reachable(&mut self, edge: Edge) -> bool {
405 let edge_data = &mut self.edges[edge];
406 let dest = edge_data.to;
407
408 if !edge_data.reachable {
409 edge_data.reachable = true;
410 self.blocks[dest].reachable = true;
411 true
412 } else {
413 false
414 }
415 }
416
417 fn infer_edge_reachability(
423 &self,
424 func: &Function,
425 cond: Value,
426 edge: Edge,
427 domtree: &DomTree,
428 ) -> bool {
429 let edge_data = &self.edges[edge];
430 let resolved_cond = match edge_data.resolved_cond.expand() {
432 Some(cond) => cond,
433 None => return true,
434 };
435
436 let inferred_value = self.infer_value_at_block(func, domtree, cond, edge_data.from);
437 self.is_congruent_value(inferred_value, resolved_cond)
438 }
439
440 fn infer_value_at_block(
462 &self,
463 func: &Function,
464 domtree: &DomTree,
465 value: Value,
466 target_block: Block,
467 ) -> Value {
468 let mut rep_value = self.leader(value);
469
470 let mut current_block = Some(target_block);
471 while current_block.is_some() && (!func.dfg.is_imm(rep_value)) {
474 let block = current_block.unwrap();
475 if let Some(dominant_edge) = self.dominant_reachable_in_edges(block) {
476 if self.is_back_edge(dominant_edge) {
478 break;
479 }
480
481 if let Some(value) = self.infer_value_impl(dominant_edge, rep_value) {
483 rep_value = value;
484 current_block = Some(target_block);
485 } else {
486 current_block = Some(self.edge_data(dominant_edge).from);
489 }
490 } else {
491 current_block = domtree.idom_of(block);
494 }
495 }
496
497 rep_value
498 }
499
500 fn infer_value_at_edge(
504 &self,
505 func: &Function,
506 domtree: &DomTree,
507 value: Value,
508 edge: Edge,
509 ) -> Value {
510 let mut rep_value = self.leader(value);
511
512 if let Some(inferred_value) = self.infer_value_impl(edge, rep_value) {
513 rep_value = inferred_value;
514 }
515
516 self.infer_value_at_block(func, domtree, rep_value, self.edge_data(edge).from)
517 }
518
519 fn infer_value_impl(&self, edge: Edge, value: Value) -> Option<Value> {
526 let edge_data = self.edge_data(edge);
527 let edge_cond = edge_data.cond.expand()?;
528 if self.is_congruent_value(edge_cond, value) {
530 return Some(edge_data.resolved_cond.unwrap());
531 }
532
533 let predicate = edge_data.predicate.as_ref()?;
534
535 match predicate {
536 InsnData::Binary {
537 code: BinaryOp::Eq,
538 args: [lhs, rhs],
539 } => {
540 if self.is_congruent_value(*lhs, value)
541 && self.value_rank(*rhs) < self.value_rank(value)
542 {
543 Some(*rhs)
544 } else if self.is_congruent_value(*rhs, value)
545 && self.value_rank(*lhs) < self.value_rank(value)
546 {
547 Some(*lhs)
548 } else {
549 None
550 }
551 }
552 _ => None,
553 }
554 }
555
556 fn reachable_in_edges(&self, block: Block) -> impl Iterator<Item = &Edge> {
558 self.blocks[block]
559 .in_edges
560 .iter()
561 .filter(|edge| self.edge_data(**edge).reachable)
562 }
563
564 fn unreachable_out_edges(&self, block: Block) -> impl Iterator<Item = &Edge> {
566 self.blocks[block]
567 .out_edges
568 .iter()
569 .filter(|edge| !self.edge_data(**edge).reachable)
570 }
571
572 fn dominant_reachable_in_edges(&self, block: Block) -> Option<Edge> {
574 let mut edges = self.reachable_in_edges(block);
575
576 let dominant = edges.next()?;
577 if edges.next().is_none() {
578 Some(*dominant)
579 } else {
580 None
581 }
582 }
583
584 fn is_congruent_value(&self, lhs: Value, rhs: Value) -> bool {
587 let lhs_class = self.values[lhs].class;
588 let rhs_class = self.values[rhs].class;
589 (lhs_class == rhs_class) && (lhs_class != INITIAL_CLASS)
590 }
591
592 fn perform_symbolic_evaluation(
594 &mut self,
595 func: &mut Function,
596 domtree: &DomTree,
597 insn_data: InsnData,
598 block: Block,
599 ) -> GvnInsn {
600 let insn_data = match insn_data {
605 InsnData::Unary { code, args: [arg] } => {
606 let arg = self.infer_value_at_block(func, domtree, arg, block);
607 InsnData::unary(code, arg)
608 }
609
610 InsnData::Binary {
611 code,
612 args: [lhs, rhs],
613 } => {
614 let mut lhs = self.infer_value_at_block(func, domtree, lhs, block);
615 let mut rhs = self.infer_value_at_block(func, domtree, rhs, block);
616 if code.is_commutative() && self.value_rank(rhs) < self.value_rank(lhs) {
618 std::mem::swap(&mut lhs, &mut rhs);
619 }
620
621 InsnData::binary(code, lhs, rhs)
622 }
623
624 InsnData::Cast {
625 code,
626 args: [arg],
627 ty,
628 } => {
629 let arg = self.infer_value_at_block(func, domtree, arg, block);
630 InsnData::cast(code, arg, ty)
631 }
632
633 InsnData::Store { .. }
634 | InsnData::Load { .. }
635 | InsnData::Call { .. }
636 | InsnData::Jump { .. }
637 | InsnData::Branch { .. }
638 | InsnData::BrTable { .. }
639 | InsnData::Alloca { .. }
640 | InsnData::Gep { .. }
641 | InsnData::Return { .. } => insn_data.clone(),
642
643 InsnData::Phi { values, blocks, ty } => {
644 let edges = &self.blocks[block].in_edges;
645
646 let mut phi_args = Vec::with_capacity(values.len());
647 for (&value, &from) in (values).iter().zip(blocks.iter()) {
648 let edge = self.find_edge(edges, from, block);
649 if !self.edge_data(edge).reachable {
651 continue;
652 }
653
654 let inferred_value = self.infer_value_at_edge(func, domtree, value, edge);
655 phi_args.push((inferred_value, from));
656 }
657
658 phi_args.sort_unstable_by(|(_, block1), (_, block2)| {
660 self.block_rank(*block1).cmp(&self.block_rank(*block2))
661 });
662
663 InsnData::Phi {
664 values: phi_args.iter().map(|(value, _)| *value).collect(),
665 blocks: phi_args.iter().map(|(_, block)| *block).collect(),
666 ty,
667 }
668 }
669 };
670
671 if let Some(imm) = self.perform_constant_folding(func, &insn_data) {
672 GvnInsn::Value(imm)
673 } else if let Some(result) = self.perform_simplification(&mut func.dfg, &insn_data) {
674 result
675 } else {
676 GvnInsn::Insn(insn_data)
677 }
678 }
679
680 fn perform_constant_folding(
682 &mut self,
683 func: &mut Function,
684 insn_data: &InsnData,
685 ) -> Option<Value> {
686 constant_folding::fold_constant(&func.dfg, insn_data)
687 .map(|imm| self.make_imm(&mut func.dfg, imm))
688 }
689
690 fn perform_simplification(
692 &mut self,
693 dfg: &mut DataFlowGraph,
694 insn_data: &InsnData,
695 ) -> Option<GvnInsn> {
696 simplify_impl::simplify_insn_data(dfg, insn_data.clone()).map(|res| match res {
697 simplify_impl::SimplifyResult::Value(value) => {
698 if let Some(imm) = dfg.value_imm(value) {
701 GvnInsn::Value(self.make_imm(dfg, imm))
702 } else {
703 GvnInsn::Value(value)
704 }
705 }
706 simplify_impl::SimplifyResult::Insn(insn) => GvnInsn::Insn(insn),
707 })
708 }
709
710 fn perform_predicate_simplification(
712 &mut self,
713 dfg: &mut DataFlowGraph,
714 insn_data: InsnData,
715 ) -> InsnData {
716 if let Some(GvnInsn::Insn(insn)) = self.perform_simplification(dfg, &insn_data) {
717 insn
718 } else {
719 insn_data.clone()
720 }
721 }
722
723 fn add_edge_info(
725 &mut self,
726 from: Block,
727 to: Block,
728 cond: Option<Value>,
729 predicate: Option<InsnData>,
730 resolved_cond: Option<Value>,
731 ) {
732 let edge_data = EdgeData {
733 from,
734 to,
735 cond: cond.into(),
736 predicate,
737 resolved_cond: resolved_cond.into(),
738 reachable: false,
739 };
740
741 let edge = self.edges.push(edge_data);
743 self.blocks[to].in_edges.push(edge);
744 self.blocks[from].out_edges.push(edge);
745 }
746
747 fn assign_class(&mut self, value: Value, class: Class) {
749 let old_class = self.value_class(value);
750 if old_class == class {
751 return;
752 }
753
754 let value_rank = self.value_rank(value);
755
756 let class_data = &mut self.classes[class];
758 class_data.values.insert((value_rank, value));
759 self.values[value].class = class;
760
761 let old_class_data = &mut self.classes[old_class];
763 old_class_data.values.remove(&(value_rank, value));
764
765 if let Some(insn_class) = self.insn_table.get_mut(&old_class_data.gvn_insn) {
767 if *insn_class == old_class && old_class_data.values.is_empty() {
768 self.insn_table.remove(&old_class_data.gvn_insn);
769 if let Some(value_phi) = &old_class_data.value_phi {
770 self.value_phi_table.remove(value_phi);
771 }
772 }
773 }
774 }
775
776 fn make_imm(&mut self, dfg: &mut DataFlowGraph, imm: impl Into<Immediate>) -> Value {
779 let value = dfg.make_imm_value(imm);
781
782 self.assign_class_to_imm_value(value);
784
785 value
786 }
787
788 fn assign_class_to_imm_value(&mut self, value: Value) {
790 let class = self.values[value].class;
791 let gvn_insn = GvnInsn::Value(value);
792
793 if class != INITIAL_CLASS {
795 debug_assert_eq!(self.values[value].rank, IMMEDIATE_RANK);
796 return;
797 }
798
799 self.values[value].rank = IMMEDIATE_RANK;
801
802 self.always_avail.push(value);
804
805 let class = self.make_class(gvn_insn, None);
807 self.assign_class(value, class);
808 }
809
810 fn is_back_edge(&self, edge: Edge) -> bool {
812 let from = self.edge_data(edge).from;
813 let to = self.edge_data(edge).to;
814
815 self.blocks[to].rank <= self.blocks[from].rank
816 }
817
818 fn leader(&self, value: Value) -> Value {
820 let class = self.values[value].class;
821
822 self.class_data(class).values.iter().next().unwrap().1
823 }
824
825 fn class_leader(&self, class: Class) -> Value {
828 self.class_data(class).values.iter().next().unwrap().1
829 }
830
831 fn edge_data(&self, edge: Edge) -> &EdgeData {
833 &self.edges[edge]
834 }
835
836 fn class_data(&self, class: Class) -> &ClassData {
838 &self.classes[class]
839 }
840
841 fn value_class(&self, value: Value) -> Class {
843 self.values[value].class
844 }
845
846 fn value_rank(&self, value: Value) -> u32 {
848 self.values[value].rank
849 }
850
851 fn block_rank(&self, block: Block) -> u32 {
853 self.blocks[block].rank
854 }
855
856 fn make_class(&mut self, gvn_insn: GvnInsn, value_phi: Option<ValuePhi>) -> Class {
859 let class_data = ClassData {
860 values: BTreeSet::new(),
861 gvn_insn: gvn_insn.clone(),
862 value_phi: value_phi.clone(),
863 };
864 let class = self.classes.push(class_data);
865
866 self.insn_table.insert(gvn_insn, class);
868 if let Some(value_phi) = value_phi {
869 self.value_phi_table.insert(value_phi, class);
870 }
871
872 class
873 }
874
875 fn find_edge(&self, edges: &[Edge], from: Block, to: Block) -> Edge {
878 edges
879 .iter()
880 .find(|edge| {
881 let data = self.edge_data(**edge);
882 data.from == from && data.to == to
883 })
884 .copied()
885 .unwrap()
886 }
887}
888
889impl Default for GvnSolver {
890 fn default() -> Self {
891 Self::new()
892 }
893}
894
895#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash, PartialOrd, Ord)]
896struct Class(u32);
897entity_impl!(Class);
898
899#[derive(Debug, Clone, PartialEq)]
901struct ClassData {
902 values: BTreeSet<(u32, Value)>,
905
906 gvn_insn: GvnInsn,
908
909 value_phi: Option<ValuePhi>,
911}
912
913#[derive(Debug, Clone, PartialEq)]
915struct GvnValueData {
916 class: Class,
918
919 rank: u32,
923}
924
925impl Default for GvnValueData {
926 fn default() -> Self {
927 Self {
928 class: INITIAL_CLASS,
929 rank: 0,
930 }
931 }
932}
933
934#[derive(Debug, Clone, PartialEq, Eq, Hash)]
936enum GvnInsn {
937 Value(Value),
939 Insn(InsnData),
941}
942
943impl From<Value> for GvnInsn {
944 fn from(value: Value) -> Self {
945 Self::Value(value)
946 }
947}
948
949impl From<InsnData> for GvnInsn {
950 fn from(insn: InsnData) -> Self {
951 Self::Insn(insn)
952 }
953}
954
955#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash, PartialOrd, Ord)]
956struct Edge(u32);
957entity_impl!(Edge);
958
959#[derive(Debug, Clone)]
960struct EdgeData {
961 from: Block,
963
964 to: Block,
966
967 cond: PackedOption<Value>,
969
970 predicate: Option<InsnData>,
972
973 resolved_cond: PackedOption<Value>,
975
976 reachable: bool,
978}
979
980#[derive(Debug, Default, Clone)]
981struct GvnBlockData {
982 in_edges: Vec<Edge>,
984
985 out_edges: Vec<Edge>,
987
988 reachable: bool,
990
991 rank: u32,
994}
995
996struct ValuePhiFinder<'a> {
999 solver: &'a mut GvnSolver,
1000 visited: FxHashSet<Value>,
1002}
1003
1004impl<'a> ValuePhiFinder<'a> {
1005 fn new(solver: &'a mut GvnSolver, insn_result: Value) -> Self {
1006 let mut visited = FxHashSet::default();
1007 visited.insert(insn_result);
1008 Self { solver, visited }
1009 }
1010
1011 fn compute_value_phi(&mut self, func: &mut Function, gvn_insn: &GvnInsn) -> Option<ValuePhi> {
1013 let insn_data = if let GvnInsn::Insn(insn_data) = gvn_insn {
1015 insn_data
1016 } else {
1017 return None;
1018 };
1019
1020 match insn_data {
1021 InsnData::Unary { code, args: [arg] } => {
1022 let arg = self.get_phi_of(func, *arg)?;
1023 self.compute_value_phi_for_unary(func, *code, arg)
1024 }
1025
1026 InsnData::Binary {
1027 code,
1028 args: [lhs, rhs],
1029 } => {
1030 let (lhs, rhs) = if lhs == rhs {
1031 let lhs_phi = self.get_phi_of(func, *lhs)?;
1032 let rhs_phi = lhs_phi.clone();
1033 (lhs_phi, rhs_phi)
1034 } else {
1035 match (self.get_phi_of(func, *lhs), self.get_phi_of(func, *rhs)) {
1036 (Some(lhs_phi), Some(rhs_phi)) => (lhs_phi, rhs_phi),
1037 (Some(lhs_phi), None) => (lhs_phi, ValuePhi::Value(*rhs)),
1038 (None, Some(rhs_phi)) => (ValuePhi::Value(*lhs), rhs_phi),
1039 (None, None) => return None,
1040 }
1041 };
1042
1043 self.compute_value_phi_for_binary(func, *code, lhs, rhs)
1044 }
1045
1046 InsnData::Cast {
1047 code,
1048 args: [arg],
1049 ty,
1050 } => {
1051 let arg = self.get_phi_of(func, *arg)?;
1052 self.compute_value_phi_for_cast(func, *code, arg, *ty)
1053 }
1054
1055 _ => None,
1056 }
1057 }
1058
1059 fn compute_value_phi_for_unary(
1060 &mut self,
1061 func: &mut Function,
1062 code: UnaryOp,
1063 value_phi: ValuePhi,
1064 ) -> Option<ValuePhi> {
1065 let value_phi_insn = if let ValuePhi::PhiInsn(insn) = value_phi {
1066 insn
1067 } else {
1068 return None;
1069 };
1070
1071 let mut result =
1072 ValuePhiInsn::with_capacity(value_phi_insn.block, value_phi_insn.args.len());
1073
1074 for (arg, block) in value_phi_insn.args.into_iter() {
1075 match arg {
1076 ValuePhi::Value(value) => {
1077 let query_insn = InsnData::unary(code, value);
1078 let phi_arg = self.lookup_value_phi_arg(func, query_insn)?;
1079 result.args.push((phi_arg, block));
1080 }
1081 ValuePhi::PhiInsn(_) => {
1082 let phi_arg = self.compute_value_phi_for_unary(func, code, arg)?;
1083 result.args.push((phi_arg, block));
1084 }
1085 }
1086 }
1087
1088 Some(result.canonicalize())
1089 }
1090
1091 fn compute_value_phi_for_binary(
1092 &mut self,
1093 func: &mut Function,
1094 code: BinaryOp,
1095 lhs: ValuePhi,
1096 rhs: ValuePhi,
1097 ) -> Option<ValuePhi> {
1098 let (args, phi_block): (Vec<_>, _) = match (lhs, rhs) {
1115 (ValuePhi::PhiInsn(lhs_phi), ValuePhi::PhiInsn(rhs_phi)) => {
1116 if lhs_phi.block != rhs_phi.block || lhs_phi.args.len() != rhs_phi.args.len() {
1118 return None;
1119 }
1120
1121 let mut args = Vec::with_capacity(lhs_phi.args.len());
1122 for ((lhs_arg, lhs_block), (rhs_arg, rhs_block)) in
1123 lhs_phi.args.into_iter().zip(rhs_phi.args.into_iter())
1124 {
1125 if lhs_block != rhs_block {
1127 return None;
1128 }
1129 args.push(((lhs_arg, rhs_arg), lhs_block));
1130 }
1131 (args, lhs_phi.block)
1132 }
1133
1134 (ValuePhi::PhiInsn(lhs_phi), ValuePhi::Value(rhs_value)) => (
1135 lhs_phi
1136 .args
1137 .into_iter()
1138 .map(|(phi_arg, block)| ((phi_arg, ValuePhi::Value(rhs_value)), block))
1139 .collect(),
1140 lhs_phi.block,
1141 ),
1142
1143 (ValuePhi::Value(lhs_value), ValuePhi::PhiInsn(rhs_phi)) => (
1144 rhs_phi
1145 .args
1146 .into_iter()
1147 .map(|(phi_arg, block)| ((ValuePhi::Value(lhs_value), phi_arg), block))
1148 .collect(),
1149 rhs_phi.block,
1150 ),
1151
1152 (ValuePhi::Value(_), ValuePhi::Value(_)) => return None,
1153 };
1154
1155 let mut result = ValuePhiInsn::with_capacity(phi_block, args.len());
1156 for ((lhs_arg, rhs_arg), block) in args {
1157 match (lhs_arg, rhs_arg) {
1158 (ValuePhi::Value(mut lhs_value), ValuePhi::Value(mut rhs_value)) => {
1159 if code.is_commutative()
1160 && self.solver.value_rank(rhs_value) < self.solver.value_rank(lhs_value)
1161 {
1162 std::mem::swap(&mut lhs_value, &mut rhs_value);
1164 }
1165 let query_insn = InsnData::binary(code, lhs_value, rhs_value);
1166 let phi_arg = self.lookup_value_phi_arg(func, query_insn)?;
1167 result.args.push((phi_arg, block));
1168 }
1169 (lhs_arg, rhs_arg) => {
1170 let phi_arg =
1171 self.compute_value_phi_for_binary(func, code, lhs_arg, rhs_arg)?;
1172 result.args.push((phi_arg, block));
1173 }
1174 }
1175 }
1176
1177 Some(result.canonicalize())
1178 }
1179
1180 fn compute_value_phi_for_cast(
1181 &mut self,
1182 func: &mut Function,
1183 code: CastOp,
1184 value_phi: ValuePhi,
1185 ty: Type,
1186 ) -> Option<ValuePhi> {
1187 let value_phi_insn = if let ValuePhi::PhiInsn(insn) = value_phi {
1188 insn
1189 } else {
1190 return None;
1191 };
1192
1193 let mut result =
1194 ValuePhiInsn::with_capacity(value_phi_insn.block, value_phi_insn.args.len());
1195
1196 for (arg, block) in value_phi_insn.args.into_iter() {
1197 match arg {
1198 ValuePhi::Value(value) => {
1199 let query_insn = InsnData::cast(code, value, ty);
1200 let phi_arg = self.lookup_value_phi_arg(func, query_insn)?;
1201 result.args.push((phi_arg, block));
1202 }
1203 ValuePhi::PhiInsn(_) => {
1204 let phi_arg = self.compute_value_phi_for_cast(func, code, arg, ty)?;
1205 result.args.push((phi_arg, block));
1206 }
1207 }
1208 }
1209
1210 Some(result.canonicalize())
1211 }
1212
1213 fn lookup_value_phi_arg(&mut self, func: &mut Function, query: InsnData) -> Option<ValuePhi> {
1215 let query = if let Some(imm) = self.solver.perform_constant_folding(func, &query) {
1217 return Some(ValuePhi::Value(imm));
1219 } else if let Some(simplified) = self.solver.perform_simplification(&mut func.dfg, &query) {
1220 if let GvnInsn::Value(value) = simplified {
1222 return Some(ValuePhi::Value(value));
1223 } else {
1224 simplified
1225 }
1226 } else {
1227 GvnInsn::Insn(query)
1228 };
1229
1230 if let Some(class) = self.solver.insn_table.get(&query) {
1232 let leader = self.solver.class_leader(*class);
1233 return Some(ValuePhi::Value(leader));
1234 }
1235
1236 self.compute_value_phi(func, &query)
1238 }
1239
1240 fn get_phi_of(&mut self, func: &Function, value: Value) -> Option<ValuePhi> {
1248 if !self.visited.insert(value) {
1249 return None;
1250 }
1251
1252 let class = self.solver.value_class(value);
1253 let phi_block = func.layout.insn_block(func.dfg.value_insn(value)?);
1254
1255 if let GvnInsn::Insn(InsnData::Phi { values, blocks, .. }) =
1257 &self.solver.classes[class].gvn_insn
1258 {
1259 let mut result = ValuePhiInsn::with_capacity(phi_block, values.len());
1260 let in_edges = &self.solver.blocks[phi_block].in_edges;
1261
1262 for (value, from) in values.iter().copied().zip(blocks.iter().copied()) {
1263 let edge = self.solver.find_edge(in_edges, from, phi_block);
1264 if self.solver.edges[edge].reachable {
1266 result.args.push((ValuePhi::Value(value), from))
1267 }
1268 }
1269
1270 return Some(result.canonicalize());
1271 };
1272
1273 let class = self.solver.value_class(value);
1275 match &self.solver.classes[class].value_phi {
1276 value_phi @ Some(ValuePhi::PhiInsn(..)) => value_phi.clone(),
1277 _ => None,
1278 }
1279 }
1280}
1281
1282#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1285enum ValuePhi {
1286 Value(Value),
1289 PhiInsn(ValuePhiInsn),
1291}
1292
1293#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1294struct ValuePhiInsn {
1295 block: Block,
1297 args: Vec<(ValuePhi, Block)>,
1299}
1300
1301impl ValuePhiInsn {
1302 fn with_capacity(block: Block, cap: usize) -> Self {
1303 Self {
1304 block,
1305 args: Vec::with_capacity(cap),
1306 }
1307 }
1308
1309 fn canonicalize(mut self) -> ValuePhi {
1311 let first_arg = &self.args[0].0;
1312
1313 if self
1315 .args
1316 .iter()
1317 .all(|(value_phi, _)| value_phi == first_arg)
1318 {
1319 first_arg.clone()
1320 } else {
1321 self.args.sort_by_key(|(_, block)| *block);
1323 ValuePhi::PhiInsn(self)
1324 }
1325 }
1326}
1327
1328struct RedundantCodeRemover<'a> {
1331 solver: &'a GvnSolver,
1332
1333 avail_set: SecondaryMap<Block, FxHashMap<Class, Value>>,
1337
1338 resolved_value_phis: FxHashMap<ValuePhi, Value>,
1340}
1341
1342impl<'a> RedundantCodeRemover<'a> {
1343 fn new(solver: &'a GvnSolver) -> Self {
1344 Self {
1345 solver,
1346 avail_set: SecondaryMap::default(),
1347 resolved_value_phis: FxHashMap::default(),
1348 }
1349 }
1350
1351 fn remove_redundant_code(
1360 &mut self,
1361 func: &mut Function,
1362 cfg: &mut ControlFlowGraph,
1363 domtree: &mut DomTree,
1364 ) {
1365 self.remove_unreachable_edges(func);
1368
1369 cfg.compute(func);
1371 domtree.compute(cfg);
1372
1373 let mut domtree_traversable = DominatorTreeTraversable::default();
1375 domtree_traversable.compute(domtree);
1376
1377 let entry = func.layout.entry_block().unwrap();
1378 let mut blocks = vec![entry];
1379 while let Some(block) = blocks.pop() {
1380 blocks.extend_from_slice(domtree_traversable.children_of(block));
1381 self.remove_code_in_block(func, domtree, block);
1382 }
1383
1384 let mut next_block = Some(entry);
1386 while let Some(block) = next_block {
1387 self.resolve_value_phi_in_block(func, block);
1388 next_block = func.layout.next_block_of(block);
1389 }
1390 }
1391
1392 fn remove_code_in_block(&mut self, func: &mut Function, domtree: &DomTree, block: Block) {
1394 let mut avails = if block == func.layout.entry_block().unwrap() {
1396 let mut avails = FxHashMap::default();
1398 for &avail in &self.solver.always_avail {
1399 let class = self.solver.value_class(avail);
1400 avails.insert(class, avail);
1401 }
1402 avails
1403 } else {
1404 let idom = domtree.idom_of(block).unwrap();
1406 self.avail_set[idom].clone()
1407 };
1408
1409 let mut inserter = InsnInserter::new(func, CursorLocation::BlockTop(block));
1410 loop {
1411 match inserter.loc() {
1412 CursorLocation::BlockTop(_) => {
1413 inserter.proceed();
1414 }
1415
1416 CursorLocation::BlockBottom(..) | CursorLocation::NoWhere => {
1417 break;
1418 }
1419
1420 CursorLocation::At(insn) => {
1421 let block = inserter.block().unwrap();
1422 if let Some(insn_result) = inserter.func().dfg.insn_result(insn) {
1423 let class = self.solver.value_class(insn_result);
1424
1425 if let Some(value) = avails.get(&class) {
1427 inserter.func_mut().dfg.change_to_alias(insn_result, *value);
1428 inserter.remove_insn();
1429 continue;
1430 }
1431
1432 self.rewrite_phi(inserter.func_mut(), insn, block);
1434 avails.insert(class, insn_result);
1435 }
1436
1437 inserter.proceed();
1438 }
1439 }
1440 }
1441
1442 self.avail_set[block] = avails;
1444 }
1445
1446 fn resolve_value_phi_in_block(&mut self, func: &mut Function, block: Block) {
1448 let mut inserter = InsnInserter::new(func, CursorLocation::BlockTop(block));
1449 loop {
1450 match inserter.loc() {
1451 CursorLocation::BlockTop(_) => {
1452 inserter.proceed();
1453 }
1454
1455 CursorLocation::BlockBottom(..) | CursorLocation::NoWhere => {
1456 break;
1457 }
1458
1459 CursorLocation::At(insn) => {
1460 let block = inserter.block().unwrap();
1461 if let Some(insn_result) = inserter.func().dfg.insn_result(insn) {
1462 let class = self.solver.value_class(insn_result);
1465 if let Some(value_phi) = &self.solver.classes[class].value_phi {
1466 let ty = inserter.func().dfg.value_ty(insn_result);
1467 if self.is_value_phi_resolvable(value_phi, block) {
1468 let value =
1469 self.resolve_value_phi(&mut inserter, value_phi, ty, block);
1470 inserter.func_mut().dfg.change_to_alias(insn_result, value);
1471 inserter.remove_insn();
1472 continue;
1473 }
1474 }
1475 }
1476
1477 inserter.proceed();
1478 }
1479 }
1480 }
1481 }
1482
1483 fn is_value_phi_resolvable(&self, value_phi: &ValuePhi, block: Block) -> bool {
1486 if self.resolved_value_phis.contains_key(value_phi) {
1487 return true;
1488 }
1489
1490 match value_phi {
1491 ValuePhi::Value(value) => {
1492 let class = self.solver.value_class(*value);
1493 self.avail_set[block].contains_key(&class)
1494 }
1495 ValuePhi::PhiInsn(phi_insn) => {
1496 for (value_phi, phi_block) in &phi_insn.args {
1497 if !self.is_value_phi_resolvable(value_phi, *phi_block) {
1498 return false;
1499 }
1500 }
1501
1502 true
1503 }
1504 }
1505 }
1506
1507 fn resolve_value_phi(
1510 &mut self,
1511 inserter: &mut InsnInserter,
1512 value_phi: &ValuePhi,
1513 ty: Type,
1514 block: Block,
1515 ) -> Value {
1516 debug_assert!(self.is_value_phi_resolvable(value_phi, block));
1517
1518 if let Some(value) = self.resolved_value_phis.get(value_phi) {
1519 return *value;
1520 }
1521
1522 match value_phi {
1523 ValuePhi::Value(value) => {
1524 let class = self.solver.value_class(*value);
1525 self.avail_set[block].get(&class).copied().unwrap()
1526 }
1527
1528 ValuePhi::PhiInsn(phi_insn) => {
1529 let current_inserter_loc = inserter.loc();
1531
1532 let mut phi = InsnData::phi(ty);
1534 for (value_phi, phi_block) in &phi_insn.args {
1535 let resolved = self.resolve_value_phi(inserter, value_phi, ty, *phi_block);
1536 phi.append_phi_arg(resolved, *phi_block);
1537 }
1538
1539 inserter.set_loc(CursorLocation::BlockTop(phi_insn.block));
1541 let insn = inserter.insert_insn_data(phi);
1542 let result = inserter.make_result(insn).unwrap();
1543 inserter.attach_result(insn, result);
1544
1545 inserter.set_loc(current_inserter_loc);
1547
1548 self.resolved_value_phis.insert(value_phi.clone(), result);
1550
1551 result
1553 }
1554 }
1555 }
1556
1557 fn remove_unreachable_edges(&self, func: &mut Function) {
1559 let entry_block = func.layout.entry_block().unwrap();
1560 let mut inserter = InsnInserter::new(func, CursorLocation::BlockTop(entry_block));
1561
1562 loop {
1563 match inserter.loc() {
1564 CursorLocation::BlockTop(block) => {
1565 if !self.solver.blocks[block].reachable {
1566 inserter.remove_block();
1567 } else {
1568 inserter.proceed();
1569 }
1570 }
1571
1572 CursorLocation::BlockBottom(..) => inserter.proceed(),
1573
1574 CursorLocation::At(insn) => {
1575 if inserter.func().dfg.is_branch(insn) {
1576 let block = inserter.block().unwrap();
1577 for &out_edge in self.solver.unreachable_out_edges(block) {
1578 let edge_data = self.solver.edge_data(out_edge);
1579 inserter
1580 .func_mut()
1581 .dfg
1582 .remove_branch_dest(insn, edge_data.to);
1583 }
1584 }
1585 inserter.proceed();
1586 }
1587
1588 CursorLocation::NoWhere => break,
1589 }
1590 }
1591 }
1592
1593 fn rewrite_phi(&self, func: &mut Function, insn: Insn, block: Block) {
1595 if !func.dfg.is_phi(insn) {
1596 return;
1597 }
1598
1599 let edges = &self.solver.blocks[block].in_edges;
1600 for &edge in edges {
1601 let edge_data = self.solver.edge_data(edge);
1602 if !edge_data.reachable {
1604 func.dfg.remove_phi_arg(insn, edge_data.from);
1605 }
1606 }
1607 }
1608}