use std::collections::{HashSet, HashSet as StdHashSet};
use crate::decompiler::cfg::ssa::DominanceInfo;
use crate::decompiler::cfg::{BlockId, Cfg, Terminator};
use crate::decompiler::ir::{Block as IrBlock, ControlFlow, Stmt};
use super::StructCtx;
pub(super) fn compute_loop_headers(cfg: &Cfg, dominance: &DominanceInfo) -> HashSet<BlockId> {
let mut headers = HashSet::new();
for block in cfg.blocks() {
for pred in cfg.predecessors(block.id) {
if *pred == block.id || dominance.strictly_dominates(block.id, *pred) {
headers.insert(block.id);
break;
}
}
}
headers
}
impl<'a> StructCtx<'a> {
pub(super) fn emit_while_loop(
&self,
out: &mut IrBlock,
bid: BlockId,
then_target: BlockId,
else_target: BlockId,
visited: &mut StdHashSet<BlockId>,
) -> Option<BlockId> {
let cond = Self::comparison_condition_for_block(self.ssa, bid)
.unwrap_or_else(|| self.condition_for_block(bid));
let mut body = self.structure_region(then_target, Some(bid), visited, true);
self.build_loop(out, cond, &mut body);
Some(else_target)
}
pub(super) fn try_emit_do_while(
&self,
out: &mut IrBlock,
bid: BlockId,
visited: &mut StdHashSet<BlockId>,
) -> Option<Option<BlockId>> {
let (latch, exit) = self.find_dowhile_latch(bid)?;
let body = self.structure_region(bid, Some(latch), visited, false);
let cond = self.condition_for_block(latch);
out.push(Stmt::ControlFlow(Box::new(ControlFlow::do_while(
body, cond,
))));
visited.insert(latch);
Some(Some(exit))
}
fn build_loop(&self, out: &mut IrBlock, cond: crate::decompiler::ir::Expr, body: &mut IrBlock) {
out.push(Stmt::ControlFlow(Box::new(ControlFlow::while_loop(
cond,
std::mem::take(body),
))));
}
fn find_dowhile_latch(&self, header: BlockId) -> Option<(BlockId, BlockId)> {
for pred in self.cfg.predecessors(header) {
if !self.ssa.dominance.strictly_dominates(header, *pred) {
continue;
}
if let Terminator::Branch {
then_target,
else_target,
} = self.terminator(*pred)
{
if then_target == header {
return Some((*pred, else_target));
}
if else_target == header {
return Some((*pred, then_target));
}
}
}
None
}
}