use alloc::vec;
use alloc::vec::Vec;
use ir_lang::{
Block, BuildError, Builder, Call, CallTarget, Function, InstData, Position, Terminator, Type,
ValidationErrorKind, Value,
};
pub(crate) fn inconsistent() -> BuildError {
BuildError::Invalid(ValidationErrorKind::ResultMismatch)
}
pub(crate) fn block_start(func: &Function, block: Block) -> Position {
match func.first_inst(block) {
Some(i) => Position::Before(i),
None => Position::End(block),
}
}
pub(crate) fn constant(
b: &mut Builder<'_>,
pos: Position,
ty: Type,
bits: u64,
) -> Result<Value, BuildError> {
b.set_position(pos)?;
let inst = b.insert(InstData::Const { ty, bits })?;
b.results(inst).first().ok_or_else(inconsistent)
}
fn call_operands(call: &Call, f: &mut impl FnMut(Value)) {
if let CallTarget::Indirect { callee, .. } = call.target {
f(callee);
}
call.args.iter().copied().for_each(&mut *f);
call.gc.iter().copied().for_each(f);
}
pub(crate) fn own_operands(term: &Terminator, mut f: impl FnMut(Value)) {
match term {
Terminator::Branch { cond, .. } => f(*cond),
Terminator::Switch { value, .. } => f(*value),
Terminator::Return { values } => values.iter().copied().for_each(f),
Terminator::TailCall(call) | Terminator::Invoke { call, .. } => {
call_operands(call, &mut f);
}
Terminator::Check { op, .. } => op.for_each_operand(f),
Terminator::Resume { payload } => f(*payload),
_ => {}
}
}
pub(crate) fn use_counts(func: &Function) -> Vec<u32> {
let mut uses = vec![0u32; func.value_count()];
let mut count = |v: Value| {
if let Some(u) = uses.get_mut(func.resolve(v).index()) {
*u = u.saturating_add(1);
}
};
for block in func.blocks() {
for inst in func.insts(block) {
if let Some(data) = func.inst(inst) {
data.for_each_operand(&mut count);
}
}
if let Some(term) = func.terminator(block) {
term.for_each_operand(&mut count);
}
}
uses
}
pub(crate) fn rewrite_terminator(
b: &mut Builder<'_>,
block: Block,
f: impl FnOnce(&mut Terminator),
) -> Result<(), BuildError> {
let Some(mut term) = b.remove_terminator(block)? else {
return Ok(());
};
f(&mut term);
b.switch_to(block)?;
b.set_terminator(term)
}
pub(crate) fn replace_terminator(
b: &mut Builder<'_>,
block: Block,
term: Terminator,
) -> Result<(), BuildError> {
let _ = b.remove_terminator(block)?;
b.switch_to(block)?;
b.set_terminator(term)
}
pub(crate) fn remove_params(
b: &mut Builder<'_>,
plans: &[(Block, Vec<bool>)],
) -> Result<(), BuildError> {
let entry = b.func().entry();
let mut gone: Vec<Value> = Vec::new();
for (block, keep) in plans {
if *block == entry || !b.func().is_live_block(*block) {
continue;
}
let params = b.func().block_params(*block);
gone.extend(
params
.iter()
.zip(keep)
.filter(|(_, k)| !**k)
.map(|(p, _)| *p),
);
}
if gone.is_empty() {
return Ok(());
}
b.remove_block_params(&gone)
}