sonatina_codegen/optim/
adce.rs1use cranelift_entity::SecondaryMap;
4use std::collections::BTreeSet;
5
6use crate::post_domtree::{PDFSet, PDTIdom, PostDomTree};
7
8use sonatina_ir::{
9 func_cursor::{CursorLocation, FuncCursor, InsnInserter},
10 insn::InsnData,
11 Block, Function, Insn,
12};
13
14pub struct AdceSolver {
15 live_insns: SecondaryMap<Insn, bool>,
16 live_blocks: SecondaryMap<Block, bool>,
17 empty_blocks: BTreeSet<Block>,
18 post_domtree: PostDomTree,
19 worklist: Vec<Insn>,
20}
21
22impl AdceSolver {
23 pub fn new() -> Self {
24 Self {
25 live_insns: SecondaryMap::default(),
26 live_blocks: SecondaryMap::default(),
27 empty_blocks: BTreeSet::default(),
28 post_domtree: PostDomTree::default(),
29 worklist: Vec::default(),
30 }
31 }
32
33 pub fn clear(&mut self) {
34 self.live_insns.clear();
35 self.live_blocks.clear();
36 self.empty_blocks.clear();
37 self.post_domtree.clear();
38 self.worklist.clear();
39 }
40
41 pub fn run(&mut self, func: &mut Function) {
42 while self.run_dce(func) {}
43 }
44
45 fn run_dce(&mut self, func: &mut Function) -> bool {
47 self.clear();
48
49 self.post_domtree.compute(func);
50 let pdf_set = self.post_domtree.compute_df();
51
52 if self.has_infinite_loop(func) {
55 return false;
56 }
57
58 for block in func.layout.iter_block() {
59 for insn in func.layout.iter_insn(block) {
60 if func.dfg.has_side_effect(insn) {
61 self.mark_insn(func, insn);
62 }
63 }
64 }
65
66 while let Some(insn) = self.worklist.pop() {
67 self.mark_by_insn(func, insn, &pdf_set);
68 }
69
70 self.eliminate_dead_code(func)
71 }
72
73 fn has_infinite_loop(&self, func: &Function) -> bool {
74 for block in func.layout.iter_block() {
75 if !self.post_domtree.is_reachable(block) {
76 return true;
77 }
78 }
79
80 false
81 }
82
83 fn mark_insn(&mut self, func: &Function, insn: Insn) {
84 let mut mark_insn = |insn, block| {
85 if !self.does_insn_live(insn) {
86 self.live_insns[insn] = true;
87 self.worklist.push(insn);
88 self.mark_block(block);
89 true
90 } else {
91 false
92 }
93 };
94
95 let insn_block = func.layout.insn_block(insn);
96 if mark_insn(insn, insn_block) {
97 let last_insn = func.layout.last_insn_of(insn_block).unwrap();
98 mark_insn(last_insn, insn_block);
99 }
100 }
101
102 fn mark_block(&mut self, block: Block) {
103 self.live_blocks[block] = true;
104 }
105
106 fn does_insn_live(&self, insn: Insn) -> bool {
107 self.live_insns[insn]
108 }
109
110 fn does_block_live(&self, block: Block) -> bool {
111 self.live_blocks[block]
112 }
113
114 fn mark_by_insn(&mut self, func: &Function, insn: Insn, pdf_set: &PDFSet) {
115 for &value in func.dfg.insn_args(insn) {
116 if let Some(value_insn) = func.dfg.value_insn(value) {
117 self.mark_insn(func, value_insn);
118 }
119 }
120
121 let insn_block = func.layout.insn_block(insn);
122 for &block in pdf_set.frontiers(insn_block) {
123 if let Some(last_insn) = func.layout.last_insn_of(block) {
124 self.mark_insn(func, last_insn)
125 }
126 }
127 }
128
129 fn eliminate_dead_code(&mut self, func: &mut Function) -> bool {
130 let entry = if let Some(entry) = func.layout.entry_block() {
131 entry
132 } else {
133 return false;
134 };
135
136 let mut inserter = InsnInserter::new(func, CursorLocation::BlockTop(entry));
137 loop {
138 match inserter.loc() {
139 CursorLocation::At(insn) => {
140 if self.does_insn_live(insn) {
141 inserter.proceed();
142 } else {
143 inserter.remove_insn()
144 }
145 }
146
147 CursorLocation::BlockTop(block) => {
148 if self.does_block_live(block) {
149 inserter.proceed()
150 } else {
151 inserter.remove_block()
152 }
153 }
154
155 CursorLocation::BlockBottom(_) => {
156 inserter.proceed();
157 }
158
159 CursorLocation::NoWhere => break,
160 }
161 }
162
163 inserter.set_to_entry();
165 let mut br_insn_modified = false;
166 while let Some(block) = inserter.block() {
167 br_insn_modified |= self.modify_branch(&mut inserter, block);
168 inserter.proceed_block();
169 }
170
171 br_insn_modified
172 }
173
174 fn living_post_dom(&self, mut block: Block) -> Option<Block> {
175 loop {
176 let idom = self.post_domtree.idom_of(block)?;
177 match idom {
178 PDTIdom::Real(block) if self.does_block_live(block) => return Some(block),
179 PDTIdom::Real(post_dom) => block = post_dom,
180 PDTIdom::DummyExit(_) | PDTIdom::DummyEntry(_) => return None,
181 }
182 }
183 }
184
185 fn modify_branch(&self, inserter: &mut InsnInserter, block: Block) -> bool {
187 let last_insn = match inserter.func().layout.last_insn_of(block) {
188 Some(insn) => insn,
189 None => return false,
190 };
191 inserter.set_loc(CursorLocation::At(last_insn));
192
193 let dests: Vec<_> = inserter
194 .func()
195 .dfg
196 .analyze_branch(last_insn)
197 .iter_dests()
198 .collect();
199
200 let mut changed = false;
201 for dest in dests {
202 if self.does_block_live(dest) {
203 continue;
204 }
205
206 match self.living_post_dom(dest) {
207 Some(postdom) => inserter
210 .func_mut()
211 .dfg
212 .rewrite_branch_dest(last_insn, dest, postdom),
213
214 None => {
216 inserter.func_mut().dfg.remove_branch_dest(last_insn, dest);
217 }
218 }
219
220 changed = true;
221 }
222
223 let branch_info = inserter.func().dfg.analyze_branch(last_insn);
225 if branch_info.dests_num() > 1 {
226 let mut branch_dests = branch_info.iter_dests();
227 let first_dest = branch_dests.next().unwrap();
228 if branch_dests.all(|dest| dest == first_dest) {
229 changed = true;
230 inserter.replace(InsnData::jump(first_dest));
231 }
232 }
233
234 changed
235 }
236}
237
238impl Default for AdceSolver {
239 fn default() -> Self {
240 Self::new()
241 }
242}