use std::collections::HashSet;
use rucc_sema::{Decl, DeclId, DeclKind, ExprId, ExprKind, InitList, Linkage, Stmt, StmtId, Tast};
#[must_use]
pub(crate) fn reachable(tast: &Tast) -> HashSet<DeclId> {
let mut walk = Reach { tast, seen: HashSet::new(), work: Vec::new() };
for index in 0..tast.top_level().len() {
let decl = tast.top_level()[index];
if is_root(&tast[decl]) {
walk.mark(decl);
}
}
while let Some(decl) = walk.work.pop() {
walk.decl(decl);
}
walk.seen
}
fn is_root(node: &Decl) -> bool {
if node.retained {
return true;
}
match node.kind {
DeclKind::Object => true,
DeclKind::Function => node.linkage == Linkage::External,
}
}
struct Reach<'a> {
tast: &'a Tast,
seen: HashSet<DeclId>,
work: Vec<DeclId>,
}
impl Reach<'_> {
fn mark(&mut self, decl: DeclId) {
if self.seen.insert(decl) {
self.work.push(decl);
}
}
fn decl(&mut self, decl: DeclId) {
let node = &self.tast[decl];
let (init, body) = (node.init, node.body);
if let Some(init) = init {
self.init(init);
}
if let Some(body) = body {
self.stmt(body);
}
}
fn init(&mut self, init: InitList) {
for index in 0..self.tast[init].len() {
let entry = self.tast[init][index];
self.expr(entry.value);
}
}
fn stmt(&mut self, id: StmtId) {
match self.tast[id] {
Stmt::Error
| Stmt::Empty
| Stmt::Goto(_)
| Stmt::Break
| Stmt::Continue
| Stmt::Return(None) => {}
Stmt::Expr(value) | Stmt::IndirectGoto(value) | Stmt::Return(Some(value)) => {
self.expr(value);
}
Stmt::While { cond, body } | Stmt::DoWhile { body, cond } => {
self.expr(cond);
self.stmt(body);
}
Stmt::Block(body) => {
for index in 0..self.tast[body].len() {
let stmt = self.tast[body][index];
self.stmt(stmt);
}
}
Stmt::Decls(decls) => {
for index in 0..self.tast[decls].len() {
let decl = self.tast[decls][index];
self.mark(decl);
}
}
Stmt::If { cond, then, otherwise } => {
self.expr(cond);
self.stmt(then);
if let Some(otherwise) = otherwise {
self.stmt(otherwise);
}
}
Stmt::For { init, cond, step, body } => {
if let Some(init) = init {
self.stmt(init);
}
if let Some(cond) = cond {
self.expr(cond);
}
if let Some(step) = step {
self.expr(step);
}
self.stmt(body);
}
Stmt::Switch { cond, body, .. } => {
self.expr(cond);
self.stmt(body);
}
Stmt::Case { body, .. } | Stmt::Default { body } | Stmt::Label { body, .. } => {
self.stmt(body);
}
Stmt::Asm(asm) => {
let node = self.tast[asm];
for list in [node.outputs, node.inputs] {
for index in 0..self.tast[list].len() {
let operand = self.tast[list][index];
self.expr(operand.value);
}
}
}
}
}
fn expr(&mut self, id: ExprId) {
match self.tast[id].kind {
ExprKind::Error
| ExprKind::Const(_)
| ExprKind::Str(_)
| ExprKind::LabelAddr(_)
| ExprKind::Unreachable => {}
ExprKind::Decl(decl) | ExprKind::CompoundLiteral(decl) => self.mark(decl),
ExprKind::StmtExpr(body) => self.stmt(body),
ExprKind::Member { base, .. }
| ExprKind::Cast(base)
| ExprKind::VaArg { list: base }
| ExprKind::VaStart { list: base }
| ExprKind::VaEnd { list: base }
| ExprKind::Convert { operand: base, .. }
| ExprKind::Unary { operand: base, .. } => self.expr(base),
ExprKind::Subscript { base: lhs, index: rhs }
| ExprKind::Binary { lhs, rhs, .. }
| ExprKind::Assign { lhs, rhs, .. }
| ExprKind::VaCopy { dst: lhs, src: rhs }
| ExprKind::Comma { lhs, rhs } => {
self.expr(lhs);
self.expr(rhs);
}
ExprKind::Call { callee, args } => {
self.expr(callee);
for index in 0..self.tast[args].len() {
let arg = self.tast[args][index];
self.expr(arg);
}
}
ExprKind::Cond { cond, then, otherwise } => {
self.expr(cond);
self.expr(then);
self.expr(otherwise);
}
ExprKind::Classify { lhs, rhs, .. } | ExprKind::Sign { lhs, rhs, .. } => {
self.expr(lhs);
if let Some(rhs) = rhs {
self.expr(rhs);
}
}
}
}
}