use std::collections::{BTreeMap, BTreeSet};
use analyssa::{passes::deadcode, World};
use crate::{
analysis::{CilTarget, MethodRef, SsaFunction},
compiler::{pass::SsaPass, CompilerContext},
metadata::token::Token,
};
pub struct DeadMethodEliminationPass;
impl Default for DeadMethodEliminationPass {
fn default() -> Self {
Self::new()
}
}
impl DeadMethodEliminationPass {
#[must_use]
pub fn new() -> Self {
Self
}
}
impl SsaPass<CilTarget, CompilerContext> for DeadMethodEliminationPass {
fn name(&self) -> &'static str {
"dead-method-elimination"
}
fn is_global(&self) -> bool {
true
}
fn requires_full_scan(&self) -> bool {
true
}
fn description(&self) -> &'static str {
"Identifies methods that are never called"
}
fn run_on_method(
&self,
_ssa: &mut SsaFunction,
_method: &MethodRef,
_host: &CompilerContext,
) -> analyssa::Result<bool> {
Ok(false)
}
fn run_global(&self, host: &CompilerContext) -> analyssa::Result<bool> {
let world = CtxWorld::new(host);
Ok(deadcode::run_global::<CilTarget, _, _>(
&world,
&host.events,
))
}
}
struct CtxWorld<'a> {
ctx: &'a CompilerContext,
ssa_callees: BTreeMap<Token, BTreeSet<Token>>,
methods: Vec<Token>,
entries: Vec<Token>,
}
impl<'a> CtxWorld<'a> {
fn new(ctx: &'a CompilerContext) -> Self {
let ssa_callees = ctx.build_ssa_call_graph();
let methods: Vec<Token> = ctx.all_methods().collect();
let entries: Vec<Token> = ctx.entry_points.iter().map(|e| *e).collect();
Self {
ctx,
ssa_callees,
methods,
entries,
}
}
}
impl World<CilTarget> for CtxWorld<'_> {
fn all_methods(&self) -> Vec<MethodRef> {
self.methods.iter().copied().map(MethodRef::from).collect()
}
fn entry_points(&self) -> Vec<MethodRef> {
self.entries.iter().copied().map(MethodRef::from).collect()
}
fn callees(&self, method: &MethodRef) -> Vec<MethodRef> {
let token = method.token();
if let Some(ssa_calls) = self.ssa_callees.get(&token) {
return ssa_calls.iter().copied().map(MethodRef::from).collect();
}
self.ctx
.call_graph
.callees(token)
.into_iter()
.map(MethodRef::from)
.collect()
}
fn is_dead(&self, method: &MethodRef) -> bool {
self.ctx.is_dead(method.token())
}
fn mark_dead(&self, method: &MethodRef) {
self.ctx.mark_dead(method.token());
}
}