use qcode::{
context::Context,
value::{
BasicBlock, BlockId, InstructionId, ValueId, ValueRef,
insn::{Mnemonic, VM_INTERRUPT},
},
};
pub trait CodeInjector {
fn inject(&mut self, ctx: &mut Context<'static>, block: BlockId);
}
pub fn instruction_at_address(
ctx: &Context<'static>,
block: BlockId,
addr: u64,
) -> Option<InstructionId> {
BasicBlock::from_id(ctx, block)
.instructions()
.find(|insn| insn.address() == Some(addr))
.map(|insn| insn.id)
}
pub fn is_interrupt(
ctx: &Context<'static>,
insn: InstructionId,
code: u64,
arg: Option<u64>,
) -> bool {
let insn = qcode::value::Instruction::from_id(ctx, insn);
let Mnemonic::PCodeOp(op) = insn.mnemonic() else {
return false;
};
if ctx.shared.pcode_ops[op.id].as_ref() != VM_INTERRUPT {
return false;
}
let literal = |index: usize| -> Option<u64> {
let value = op.args.get(index)?.qualify(insn.id.func);
match ValueRef::new(value, ctx) {
ValueRef::Literal(literal) => Some(literal.value()),
_ => None,
}
};
literal(0) == Some(code) && arg.is_none_or(|arg| literal(1) == Some(arg))
}
pub fn insert_interrupt(
ctx: &mut Context<'static>,
block: BlockId,
before: Option<InstructionId>,
address: Option<u64>,
code: u64,
args: &[ValueId],
) -> InstructionId {
let op = ctx.shared.vm_interrupt_op();
let operands: Vec<ValueId> = std::iter::once(ctx.shared.get_const(code, 8))
.chain(args.iter().copied())
.collect();
let mut builder = ctx.builder(block);
match before {
Some(before) => builder.set_insert_point_before(before),
None => builder.set_insert_point_to_start(),
}
if let Some(address) = address {
builder.set_address(address);
}
builder.push_pcode_op(op, operands, None, 0).id
}