1mod backward;
2mod forward;
3mod lattice;
4
5use midenc_hir::{
6 cfg::Graph, dominance::DominanceInfo, loops::LoopForest, pass::AnalysisManager, Backward,
7 Block, BlockRef, CallOpInterface, EntityWithId, Forward, Operation, ProgramPoint,
8 RegionBranchOpInterface, RegionKindInterface, RegionRef, Report, Spanned, SymbolTable,
9};
10
11pub use self::{
12 backward::DenseBackwardDataFlowAnalysis, forward::DenseForwardDataFlowAnalysis,
13 lattice::DenseLattice,
14};
15use super::{AnalysisStrategy, DataFlowAnalysis, DataFlowSolver, Dense};
16use crate::analyses::{dce::CfgEdge, LoopAction, LoopState};
17
18pub struct DenseDataFlowAnalysis<T, D> {
86 analysis: T,
87 _direction: core::marker::PhantomData<D>,
88}
89
90impl<A: DenseForwardDataFlowAnalysis> AnalysisStrategy<A> for DenseDataFlowAnalysis<A, Forward> {
91 type Direction = Forward;
92 type Kind = Dense;
93
94 fn build(analysis: A, _solver: &mut DataFlowSolver) -> Self {
95 Self {
96 analysis,
97 _direction: core::marker::PhantomData,
98 }
99 }
100}
101
102impl<A: DenseBackwardDataFlowAnalysis> AnalysisStrategy<A> for DenseDataFlowAnalysis<A, Backward> {
103 type Direction = Backward;
104 type Kind = Dense;
105
106 fn build(analysis: A, _solver: &mut DataFlowSolver) -> Self {
107 Self {
108 analysis,
109 _direction: core::marker::PhantomData,
110 }
111 }
112}
113
114impl<A: DenseForwardDataFlowAnalysis> DataFlowAnalysis for DenseDataFlowAnalysis<A, Forward> {
115 #[inline]
116 fn debug_name(&self) -> &'static str {
117 self.analysis.debug_name()
118 }
119
120 fn analysis_id(&self) -> core::any::TypeId {
121 core::any::TypeId::of::<Self>()
122 }
123
124 fn initialize(
127 &self,
128 top: &Operation,
129 solver: &mut DataFlowSolver,
130 analysis_manager: AnalysisManager,
131 ) -> Result<(), Report> {
132 log::debug!(
133 target: self.analysis.debug_name(),
134 "initializing analysis for {top}",
135 );
136
137 forward::process_operation(self, top, solver)?;
138
139 if !top.has_regions() {
146 return Ok(());
147 }
148
149 let is_ssa_cfg = top
150 .as_trait::<dyn RegionKindInterface>()
151 .is_none_or(|rki| rki.has_ssa_dominance());
152 log::trace!(target: self.analysis.debug_name(), "visiting regions of op (is cfg={is_ssa_cfg})");
153 if is_ssa_cfg {
154 let dominfo = analysis_manager.get_analysis::<DominanceInfo>()?;
155 for (region_index, region) in top.regions().iter().enumerate() {
156 if region.has_one_block() {
158 let block = region.entry();
159 log::trace!(target: self.analysis.debug_name(), "initializing single-block region {region_index} from entry: {block}");
160 forward::visit_block(self, &block, solver);
161 log::trace!(target: self.analysis.debug_name(), "initializing {block} in pre-order");
162 for op in block.body() {
163 let child_analysis_manager = analysis_manager.nest(op.as_operation_ref());
164 self.initialize(&op, solver, child_analysis_manager)?;
165 }
166 } else {
167 let entry_block = region.entry_block_ref().unwrap();
168 log::trace!(target: self.analysis.debug_name(), "initializing multi-block region {region_index} with entry: {entry_block}");
169 log::trace!(target: self.analysis.debug_name(), "computing region dominance tree");
170 let domtree = dominfo.dominance(region.as_region_ref());
171 log::trace!(target: self.analysis.debug_name(), "computing region loop forest forward");
172 let loop_forest = LoopForest::new(&domtree);
173
174 log::trace!(
176 target: self.analysis.debug_name(),
177 "visiting region control flow graph in reverse post-order",
178 );
179 for node in domtree.reverse_postorder() {
180 let Some(block) = node.block() else {
181 continue;
182 };
183 log::trace!(target: self.analysis.debug_name(), "analyzing {block}");
184
185 if let Some(loop_info) = loop_forest.loop_for(block) {
188 if loop_info.is_loop_exiting(block) {
190 log::trace!(target: self.analysis.debug_name(), "{block} is a loop exit");
191 for succ in BlockRef::children(block) {
192 if !loop_info.contains_block(succ) {
193 log::trace!(target: self.analysis.debug_name(), "{block} can exit loop to {succ}");
194 let mut guard = solver.get_or_create_mut::<LoopState, _>(
195 CfgEdge::new(block, succ, block.span()),
196 );
197 guard.join(LoopAction::Exit);
198 }
199 }
200 }
201 }
202
203 let block = block.borrow();
204 forward::visit_block(self, &block, solver);
205 log::trace!(target: self.analysis.debug_name(), "initializing {block} in pre-order");
206 for op in block.body() {
207 let child_analysis_manager =
208 analysis_manager.nest(op.as_operation_ref());
209 self.initialize(&op, solver, child_analysis_manager)?;
210 }
211 }
212 }
213 }
214 } else {
215 for (region_index, region) in top.regions().iter().enumerate() {
216 for block in region.body() {
217 log::trace!(target: self.analysis.debug_name(), "initializing {block} of region {region_index}");
218 forward::visit_block(self, &block, solver);
219 log::trace!(target: self.analysis.debug_name(), "initializing {block} in pre-order");
220 for op in block.body() {
221 let child_analysis_manager = analysis_manager.nest(op.as_operation_ref());
222 self.initialize(&op, solver, child_analysis_manager)?;
223 }
224 }
225 }
226 }
227
228 Ok(())
229 }
230
231 fn visit(&self, point: &ProgramPoint, solver: &mut DataFlowSolver) -> Result<(), Report> {
239 if point.is_at_block_start() {
240 let block = point.block().expect("expected block");
241 forward::visit_block(self, &block.borrow(), solver);
242 } else {
243 let op = point.operation().expect("expected operation");
244 forward::process_operation(self, &op.borrow(), solver)?;
245 }
246
247 Ok(())
248 }
249}
250
251impl<A: DenseBackwardDataFlowAnalysis> DataFlowAnalysis for DenseDataFlowAnalysis<A, Backward> {
252 #[inline]
253 fn debug_name(&self) -> &'static str {
254 self.analysis.debug_name()
255 }
256
257 fn analysis_id(&self) -> core::any::TypeId {
258 core::any::TypeId::of::<Self>()
259 }
260
261 fn initialize(
264 &self,
265 top: &Operation,
266 solver: &mut DataFlowSolver,
267 analysis_manager: AnalysisManager,
268 ) -> Result<(), Report> {
269 log::trace!(
270 target: self.analysis.debug_name(),
271 "initializing analysis for {top}",
272 );
273
274 backward::process_operation(self, top, solver)?;
275
276 if !top.has_regions() {
283 return Ok(());
284 }
285
286 let is_ssa_cfg = top
287 .as_trait::<dyn RegionKindInterface>()
288 .is_none_or(|rki| rki.has_ssa_dominance());
289 log::trace!(target: self.analysis.debug_name(), "visiting regions of op (is cfg={is_ssa_cfg})");
290 if is_ssa_cfg {
291 let dominfo = analysis_manager.get_analysis::<DominanceInfo>()?;
292 for (region_index, region) in top.regions().iter().enumerate() {
293 if region.has_one_block() {
295 let block = region.entry();
296 log::trace!(target: self.analysis.debug_name(), "initializing single-block region {region_index} from entry: {block}");
297 backward::visit_block(self, &block, solver);
298 log::trace!(target: self.analysis.debug_name(), "initializing {block} in post-order");
299 for op in block.body().iter().rev() {
300 let child_analysis_manager = analysis_manager.nest(op.as_operation_ref());
301 self.initialize(&op, solver, child_analysis_manager)?;
302 }
303 } else {
304 let entry_block = region.entry_block_ref().unwrap();
305 log::trace!(target: self.analysis.debug_name(), "initializing multi-block region {region_index} with entry: {entry_block}");
306 log::trace!(target: self.analysis.debug_name(), "computing region dominance tree");
307 let domtree = dominfo.dominance(region.as_region_ref());
308 log::trace!(target: self.analysis.debug_name(), "computing region loop forest backward");
309 let loop_forest = LoopForest::new(&domtree);
310
311 log::trace!(
313 target: self.analysis.debug_name(),
314 "visiting region control flow graph in post-order",
315 );
316 for node in domtree.postorder() {
317 let Some(block) = node.block() else {
318 continue;
319 };
320 log::trace!(target: self.analysis.debug_name(), "analyzing {block}");
321
322 if let Some(loop_info) = loop_forest.loop_for(block) {
325 if loop_info.is_loop_exiting(block) {
327 log::trace!(target: self.analysis.debug_name(), "{block} is a loop exit");
328 for succ in BlockRef::children(block) {
329 if !loop_info.contains_block(succ) {
330 log::trace!(target: self.analysis.debug_name(), "{block} can exit loop to {succ}");
331 let mut guard = solver.get_or_create_mut::<LoopState, _>(
332 CfgEdge::new(block, succ, block.span()),
333 );
334 guard.join(LoopAction::Exit);
335 }
336 }
337 }
338 }
339
340 let block = block.borrow();
341 backward::visit_block(self, &block, solver);
342 log::trace!(target: self.analysis.debug_name(), "initializing {block} in post-order");
343 for op in block.body().iter().rev() {
344 let child_analysis_manager =
345 analysis_manager.nest(op.as_operation_ref());
346 self.initialize(&op, solver, child_analysis_manager)?;
347 }
348 }
349 }
350 }
351 } else {
352 for (region_index, region) in top.regions().iter().enumerate() {
353 for block in region.body().iter().rev() {
354 log::trace!(target: self.analysis.debug_name(), "initializing {block} of region {region_index}");
355 backward::visit_block(self, &block, solver);
356 log::trace!(target: self.analysis.debug_name(), "initializing {block} in post-order");
357 for op in block.body().iter().rev() {
358 let child_analysis_manager = analysis_manager.nest(op.as_operation_ref());
359 self.initialize(&op, solver, child_analysis_manager)?;
360 }
361 }
362 }
363 }
364
365 Ok(())
366 }
367
368 fn visit(&self, point: &ProgramPoint, solver: &mut DataFlowSolver) -> Result<(), Report> {
375 if point.is_at_block_end() {
376 let block = point.block().expect("expected block");
377 backward::visit_block(self, &block.borrow(), solver);
378 } else {
379 let op = point.next_operation().expect("expected operation");
380 backward::process_operation(self, &op.borrow(), solver)?;
381 }
382
383 Ok(())
384 }
385}
386
387impl<A: DenseForwardDataFlowAnalysis> DenseForwardDataFlowAnalysis
388 for DenseDataFlowAnalysis<A, Forward>
389{
390 type Lattice = <A as DenseForwardDataFlowAnalysis>::Lattice;
391
392 fn debug_name(&self) -> &'static str {
393 <A as DenseForwardDataFlowAnalysis>::debug_name(&self.analysis)
394 }
395
396 fn visit_operation(
397 &self,
398 op: &Operation,
399 before: &Self::Lattice,
400 after: &mut super::AnalysisStateGuardMut<'_, Self::Lattice>,
401 solver: &mut DataFlowSolver,
402 ) -> Result<(), Report> {
403 <A as DenseForwardDataFlowAnalysis>::visit_operation(
404 &self.analysis,
405 op,
406 before,
407 after,
408 solver,
409 )
410 }
411
412 fn set_to_entry_state(
413 &self,
414 lattice: &mut super::AnalysisStateGuardMut<'_, Self::Lattice>,
415 solver: &mut DataFlowSolver,
416 ) {
417 <A as DenseForwardDataFlowAnalysis>::set_to_entry_state(&self.analysis, lattice, solver);
418 }
419
420 fn visit_branch_control_flow_transfer(
421 &self,
422 from: BlockRef,
423 to: &Block,
424 before: &Self::Lattice,
425 after: &mut super::AnalysisStateGuardMut<'_, Self::Lattice>,
426 solver: &mut DataFlowSolver,
427 ) {
428 <A as DenseForwardDataFlowAnalysis>::visit_branch_control_flow_transfer(
429 &self.analysis,
430 from,
431 to,
432 before,
433 after,
434 solver,
435 );
436 }
437
438 fn visit_region_branch_control_flow_transfer(
439 &self,
440 branch: &dyn RegionBranchOpInterface,
441 region_from: Option<RegionRef>,
442 region_to: Option<RegionRef>,
443 before: &Self::Lattice,
444 after: &mut super::AnalysisStateGuardMut<'_, Self::Lattice>,
445 solver: &mut DataFlowSolver,
446 ) {
447 <A as DenseForwardDataFlowAnalysis>::visit_region_branch_control_flow_transfer(
448 &self.analysis,
449 branch,
450 region_from,
451 region_to,
452 before,
453 after,
454 solver,
455 );
456 }
457
458 fn visit_call_control_flow_transfer(
459 &self,
460 call: &dyn CallOpInterface,
461 action: super::CallControlFlowAction,
462 before: &Self::Lattice,
463 after: &mut super::AnalysisStateGuardMut<'_, Self::Lattice>,
464 solver: &mut DataFlowSolver,
465 ) {
466 <A as DenseForwardDataFlowAnalysis>::visit_call_control_flow_transfer(
467 &self.analysis,
468 call,
469 action,
470 before,
471 after,
472 solver,
473 );
474 }
475}
476
477impl<A: DenseBackwardDataFlowAnalysis> DenseBackwardDataFlowAnalysis
478 for DenseDataFlowAnalysis<A, Backward>
479{
480 type Lattice = <A as DenseBackwardDataFlowAnalysis>::Lattice;
481
482 fn debug_name(&self) -> &'static str {
483 <A as DenseBackwardDataFlowAnalysis>::debug_name(&self.analysis)
484 }
485
486 fn symbol_table(&self) -> Option<&dyn SymbolTable> {
487 <A as DenseBackwardDataFlowAnalysis>::symbol_table(&self.analysis)
488 }
489
490 fn set_to_exit_state(
491 &self,
492 lattice: &mut super::AnalysisStateGuardMut<'_, Self::Lattice>,
493 solver: &mut DataFlowSolver,
494 ) {
495 <A as DenseBackwardDataFlowAnalysis>::set_to_exit_state(&self.analysis, lattice, solver)
496 }
497
498 fn visit_operation(
499 &self,
500 op: &Operation,
501 after: &Self::Lattice,
502 before: &mut super::AnalysisStateGuardMut<'_, Self::Lattice>,
503 solver: &mut DataFlowSolver,
504 ) -> Result<(), Report> {
505 <A as DenseBackwardDataFlowAnalysis>::visit_operation(
506 &self.analysis,
507 op,
508 after,
509 before,
510 solver,
511 )
512 }
513
514 fn visit_branch_control_flow_transfer(
515 &self,
516 from: &Block,
517 to: BlockRef,
518 after: &Self::Lattice,
519 before: &mut super::AnalysisStateGuardMut<'_, Self::Lattice>,
520 solver: &mut DataFlowSolver,
521 ) {
522 <A as DenseBackwardDataFlowAnalysis>::visit_branch_control_flow_transfer(
523 &self.analysis,
524 from,
525 to,
526 after,
527 before,
528 solver,
529 )
530 }
531
532 fn visit_region_branch_control_flow_transfer(
533 &self,
534 branch: &dyn RegionBranchOpInterface,
535 region_from: Option<RegionRef>,
536 region_to: Option<RegionRef>,
537 after: &Self::Lattice,
538 before: &mut super::AnalysisStateGuardMut<'_, Self::Lattice>,
539 solver: &mut DataFlowSolver,
540 ) {
541 <A as DenseBackwardDataFlowAnalysis>::visit_region_branch_control_flow_transfer(
542 &self.analysis,
543 branch,
544 region_from,
545 region_to,
546 after,
547 before,
548 solver,
549 )
550 }
551
552 fn visit_call_control_flow_transfer(
553 &self,
554 call: &dyn CallOpInterface,
555 action: super::CallControlFlowAction,
556 after: &Self::Lattice,
557 before: &mut super::AnalysisStateGuardMut<'_, Self::Lattice>,
558 solver: &mut DataFlowSolver,
559 ) {
560 <A as DenseBackwardDataFlowAnalysis>::visit_call_control_flow_transfer(
561 &self.analysis,
562 call,
563 action,
564 after,
565 before,
566 solver,
567 )
568 }
569}