use rustc_hash::FxHashSet as HashSet;
use qcode::{
context::Context,
value::{BlockId, FunctionBody, InstructionId, QCodeView, ValueId},
};
use crate::{PassCtx, with_body_mut};
fn users_of_slice<'a, 'str: 'a>(
host: impl QCodeView<'a, 'str>,
v: ValueId,
) -> &'a [qcode::value::insn::LocalInsnId] {
match v.owning_function() {
Some(f) => host.function_ref(f).local_users_of(v),
None => &[],
}
}
pub fn has_users<'a, 'str: 'a>(host: impl QCodeView<'a, 'str>, v: ValueId) -> bool {
match v.owning_function() {
Some(f) => host.function_ref(f).has_users(v),
None => false,
}
}
pub fn host_users<'a, 'str: 'a>(host: impl QCodeView<'a, 'str>, v: ValueId) -> Vec<InstructionId> {
match v.owning_function() {
Some(f) => host.function_ref(f).users_of(v),
None => Vec::new(),
}
}
pub fn dead_insns<'a, 'str: 'a>(
host: impl QCodeView<'a, 'str>,
block_id: BlockId,
) -> HashSet<InstructionId> {
let mut dead: HashSet<InstructionId> = HashSet::default();
let insn_ids: Vec<InstructionId> = host.block_ref(block_id).instruction_ids().to_vec();
let func = block_id.func;
for id in insn_ids.into_iter().rev() {
if host.instruction(id).mnemonic().has_side_effects() {
continue;
}
let live_user = users_of_slice(host, ValueId::Instruction(id))
.iter()
.any(|&user| !dead.contains(&InstructionId::new(func, user)));
if !live_user {
dead.insert(id);
}
}
dead
}
pub fn remove_dead_insns(ctx: &mut Context, block_id: BlockId) -> bool {
with_body_mut(ctx, block_id.func, |body, cx| {
remove_dead_insns_body(body, cx, block_id)
})
}
pub fn remove_dead_insns_body<'a, 'str>(
body: &'a mut FunctionBody<'str>,
cx: PassCtx<'a, 'str>,
block_id: BlockId,
) -> bool {
let mut changed = false;
loop {
let dead = dead_insns(cx.body_view(body), block_id);
if dead.is_empty() {
break;
}
changed = true;
let dead: HashSet<_> = dead
.into_iter()
.map(|id| id.localize(block_id.func))
.collect();
body.remove_block_instructions(block_id, &dead);
}
let params_changed = remove_unused_no_pred_block_params(body, cx, block_id);
changed || params_changed
}
pub fn remove_unused_no_pred_block_params<'a, 'str>(
body: &'a mut FunctionBody<'str>,
cx: PassCtx<'a, 'str>,
block_id: BlockId,
) -> bool {
if cx
.body_view(body)
.block_ref(block_id)
.predecessors()
.next()
.is_some()
{
return false;
}
let is_reg_materialized_entry = cx
.body_view(body)
.block_ref(block_id)
.function()
.is_some_and(|f| f.is_reg_materialized() && f.root().map(|b| b.id) == Some(block_id));
if is_reg_materialized_entry {
return false;
}
let params: Vec<_> = cx.body_view(body).block(block_id).param_ids().to_vec();
let mut kept = Vec::with_capacity(params.len());
let mut changed = false;
for local in params {
let param = qcode::value::BlockParamId::new(block_id.func, local);
if !has_users(cx.body_view(body), ValueId::BlockParam(param)) {
body.remove_block_param(param);
changed = true;
} else {
body.block_param_mut(param).index = kept.len();
kept.push(local);
}
}
if changed {
body.block_mut(block_id).params = kept;
}
changed
}