use alloc::vec::Vec;
use midenc_hir::{
Builder, DialectRegistration, Operation, OperationRef, SmallVec, Spanned, ValueRef,
dialects::debuginfo::attributes::ExpressionOp,
};
use super::{DIBuilder, ops::DebugValue};
#[derive(Clone, Debug)]
pub enum SalvageAction {
Deref {
new_value: ValueRef,
},
OffsetBy {
new_value: ValueRef,
offset: u64,
},
WithExpression {
new_value: ValueRef,
ops: Vec<ExpressionOp>,
},
Constant {
value: u64,
},
Undef,
}
pub fn salvage_debug_info<B: ?Sized + Builder>(
old_value: &ValueRef,
action: &SalvageAction,
builder: &mut B,
) {
for mut debug_op in debug_value_users(old_value) {
apply_salvage_action(&mut debug_op, action, builder);
}
}
pub fn erase_debug_info(old_value: &ValueRef) {
for mut debug_op in debug_value_users(old_value) {
debug_op.borrow_mut().erase();
}
}
fn apply_salvage_action<B: ?Sized + Builder>(
debug_op: &mut OperationRef,
action: &SalvageAction,
builder: &mut B,
) {
let span = debug_op.borrow().span();
match action {
SalvageAction::Deref { new_value } => {
let (variable, mut expr) = {
let op = debug_op.borrow();
let dv = op.downcast_ref::<DebugValue>().unwrap();
(dv.variable().as_value().clone(), dv.expression().as_value().clone())
};
expr.operations.insert(0, ExpressionOp::Deref);
debug_op.borrow_mut().erase();
let _ = builder.debug_value_with_expr(*new_value, variable, Some(expr), span);
}
SalvageAction::OffsetBy { new_value, offset } => {
let (variable, mut expr) = {
let op = debug_op.borrow();
let dv = op.downcast_ref::<DebugValue>().unwrap();
(dv.variable().as_value().clone(), dv.expression().as_value().clone())
};
expr.operations.push(ExpressionOp::ConstU64(*offset));
expr.operations.push(ExpressionOp::Minus);
debug_op.borrow_mut().erase();
let _ = builder.debug_value_with_expr(*new_value, variable, Some(expr), span);
}
SalvageAction::WithExpression { new_value, ops } => {
let (variable, mut expr) = {
let op = debug_op.borrow();
let dv = op.downcast_ref::<DebugValue>().unwrap();
(dv.variable().as_value().clone(), dv.expression().as_value().clone())
};
expr.operations.extend(ops.iter().cloned());
debug_op.borrow_mut().erase();
let _ = builder.debug_value_with_expr(*new_value, variable, Some(expr), span);
}
SalvageAction::Constant { value } => {
let variable = {
let op = debug_op.borrow();
let dv = op.downcast_ref::<DebugValue>().unwrap();
dv.variable().as_value().clone()
};
debug_op.borrow_mut().erase();
let _ = builder.debug_kill(variable, span);
let _ = value;
}
SalvageAction::Undef => {
let variable = {
let op = debug_op.borrow();
let dv = op.downcast_ref::<DebugValue>().unwrap();
dv.variable().as_value().clone()
};
debug_op.borrow_mut().erase();
let _ = builder.debug_kill(variable, span);
}
}
}
pub fn is_debug_info_op(op: &Operation) -> bool {
op.dialect().name() == super::DebugInfoDialect::NAMESPACE
}
pub fn debug_value_users(value: &ValueRef) -> SmallVec<[OperationRef; 2]> {
let value = value.borrow();
let mut ops = SmallVec::new_const();
for user in value.iter_uses() {
if user.owner.borrow().is::<DebugValue>() {
ops.push(user.owner);
}
}
ops
}
pub fn collect_debug_ops(op: &OperationRef) -> Vec<OperationRef> {
let mut debug_ops = Vec::new();
collect_debug_ops_recursive(op, &mut debug_ops);
debug_ops
}
fn collect_debug_ops_recursive(op: &OperationRef, debug_ops: &mut Vec<OperationRef>) {
use midenc_hir::{Forward, RawWalk};
op.raw_prewalk_all::<Forward, _>(|op: OperationRef| {
if is_debug_info_op(&op.borrow()) {
debug_ops.push(op);
}
});
}