sonatina_codegen/optim/
licm.rs1use fxhash::{FxHashMap, FxHashSet};
3
4use crate::{
5 cfg::ControlFlowGraph,
6 loop_analysis::{Loop, LoopTree},
7};
8
9use sonatina_ir::{
10 func_cursor::{CursorLocation, FuncCursor, InsnInserter},
11 Block, Function, Insn, InsnData, Value,
12};
13
14#[derive(Debug)]
15pub struct LicmSolver {
16 invariants: Vec<Insn>,
17}
18
19impl LicmSolver {
20 pub fn new() -> Self {
21 Self {
22 invariants: Vec::default(),
23 }
24 }
25
26 pub fn clear(&mut self) {
27 self.invariants.clear();
28 }
29
30 pub fn run(&mut self, func: &mut Function, cfg: &mut ControlFlowGraph, lpt: &mut LoopTree) {
33 for lp in lpt.loops() {
34 self.collect_invaliants(func, cfg, lpt, lp);
35
36 if !self.invariants.is_empty() {
37 let preheader = self.create_preheader(func, cfg, lpt, lp);
38 self.hoist_invariants(func, preheader);
39 self.invariants.clear();
40 }
41 }
42 }
43
44 fn collect_invaliants(
47 &mut self,
48 func: &Function,
49 cfg: &ControlFlowGraph,
50 lpt: &LoopTree,
51 lp: Loop,
52 ) {
53 let mut block_in_loop_rpo: Vec<_> = lpt.iter_blocks_post_order(cfg, lp).collect();
54 block_in_loop_rpo.reverse();
55
56 let mut loop_var = FxHashSet::default();
57 for block in block_in_loop_rpo {
58 for insn in func.layout.iter_insn(block) {
59 if self.is_invariant(func, &loop_var, insn) {
60 self.invariants.push(insn);
61 } else if let Some(result) = func.dfg.insn_result(insn) {
62 loop_var.insert(result);
63 }
64 }
65 }
66 }
67
68 fn is_invariant(&self, func: &Function, loop_var: &FxHashSet<Value>, insn: Insn) -> bool {
70 if !self.is_safe_to_hoist(func, insn) {
71 return false;
72 }
73
74 for arg in func.dfg.insn_args(insn) {
75 if loop_var.contains(arg) {
76 return false;
77 }
78 }
79
80 true
81 }
82
83 fn is_safe_to_hoist(&self, func: &Function, insn: Insn) -> bool {
85 !(func.dfg.has_side_effect(insn)
86 || func.dfg.is_branch(insn)
87 || func.dfg.may_trap(insn)
88 || func.dfg.is_phi(insn))
89 }
90
91 fn create_preheader(
97 &self,
98 func: &mut Function,
99 cfg: &mut ControlFlowGraph,
100 lpt: &mut LoopTree,
101 lp: Loop,
102 ) -> Block {
103 let lp_header = lpt.loop_header(lp);
104 let original_preheaders: Vec<Block> = cfg
105 .preds_of(lp_header)
106 .copied()
107 .filter(|block| !lpt.is_in_loop(*block, lp))
108 .collect();
109
110 if original_preheaders.len() == 1 && cfg.succs_of(original_preheaders[0]).count() == 1 {
113 return original_preheaders[0];
114 }
115
116 let new_preheader = func.dfg.make_block();
118 let mut inserter = InsnInserter::new(func, CursorLocation::BlockTop(lp_header));
119 inserter.insert_block_before(new_preheader);
120
121 inserter.set_loc(CursorLocation::BlockTop(new_preheader));
123 inserter.insert_insn_data(InsnData::jump(lp_header));
124 cfg.add_edge(new_preheader, lp_header);
125
126 for block in original_preheaders.iter().copied() {
128 let last_insn = func.layout.last_insn_of(block).unwrap();
129 func.dfg
130 .rewrite_branch_dest(last_insn, lp_header, new_preheader);
131 cfg.remove_edge(block, lp_header);
132 cfg.add_edge(block, new_preheader);
133 }
134
135 self.modify_phi_insn(func, lp_header, &original_preheaders, new_preheader);
136
137 if let Some(parent_lp) = lpt.parent_loop(lp) {
139 lpt.map_block(new_preheader, parent_lp);
140 }
141
142 new_preheader
143 }
144
145 fn hoist_invariants(&self, func: &mut Function, preheader: Block) {
147 let last_insn = func.layout.last_insn_of(preheader).unwrap();
148 for invariant in self.invariants.iter().copied() {
149 func.layout.remove_insn(invariant);
150 func.layout.insert_insn_before(invariant, last_insn);
151 }
152 }
153
154 fn modify_phi_insn(
156 &self,
157 func: &mut Function,
158 lp_header: Block,
159 original_preheaders: &[Block],
160 new_preheader: Block,
161 ) {
162 let mut inserted_phis = FxHashMap::default();
164
165 let mut next_insn = func.layout.first_insn_of(lp_header);
166 while let Some(insn) = next_insn {
167 if !func.dfg.is_phi(insn) {
168 break;
169 }
170
171 let mut phi_insn_data = InsnData::phi(func.dfg.insn_result_ty(insn).unwrap());
174 for &block in original_preheaders {
175 let value = func.dfg.remove_phi_arg(insn, block);
177 phi_insn_data.append_phi_arg(value, block);
179 }
180
181 let phi_result = match inserted_phis.get(&phi_insn_data) {
182 Some(&value) => value,
184
185 None => {
187 let mut inserter =
189 InsnInserter::new(func, CursorLocation::BlockTop(new_preheader));
190 let new_phi_insn = inserter.insert_insn_data(phi_insn_data.clone());
191 let result = inserter.make_result(new_phi_insn).unwrap();
192 inserter.attach_result(new_phi_insn, result);
193
194 inserted_phis.insert(phi_insn_data, result);
196
197 result
198 }
199 };
200
201 func.dfg.append_phi_arg(insn, phi_result, new_preheader);
203
204 next_insn = func.layout.next_insn_of(insn);
205 }
206 }
207}
208
209impl Default for LicmSolver {
210 fn default() -> Self {
211 Self::new()
212 }
213}