use super::entity::Inst;
use super::function::Function;
use super::operand::{Mem, Operand};
use crate::regs::{Reg, VReg};
use crate::target::{InstKind, OperandRule};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum RegPos {
Op(u32),
MemBase(u32),
MemIndex(u32),
Arg(u32),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Access {
Use,
Def,
}
pub(crate) fn def_count(f: &Function, inst: Inst) -> usize {
let ops = f.operands(inst);
let Some(desc) = f.opcode(inst).and_then(|o| f.target().inst(o)) else {
return 0;
};
match desc.kind() {
InstKind::Copy => ops.len().min(1),
InstKind::ParallelCopy => ops.len() / 2,
_ => desc.defs(),
}
}
impl Function {
pub(crate) fn visit_regs(&self, inst: Inst, mut visit: impl FnMut(RegPos, Reg, Access)) {
let Some(data) = self.insts.get(inst.index()) else {
return;
};
let defs = def_count(self, inst);
let base = data.ops;
let ops = self.operands(inst);
let rules: &[OperandRule] = self
.target()
.inst(data.opcode)
.map_or(&[], |d| d.operands());
for (i, op) in ops.iter().enumerate().take(defs) {
if let Operand::Reg(r) = op {
visit(RegPos::Op(base + i as u32), *r, Access::Def);
}
}
for (i, op) in ops.iter().enumerate().skip(defs) {
let at = base + i as u32;
match op {
Operand::Reg(r) => {
let access = match rules.get(i) {
Some(OperandRule::Def(_)) => Access::Def,
_ => Access::Use,
};
visit(RegPos::Op(at), *r, access);
}
Operand::Mem(m) => {
if let Some(b) = m.base_reg() {
visit(RegPos::MemBase(at), b, Access::Use);
}
if let Some((x, _)) = m.index_reg() {
visit(RegPos::MemIndex(at), x, Access::Use);
}
}
Operand::Block(call) => {
let range = call.range();
for (k, a) in self
.args
.get(range.clone())
.unwrap_or(&[])
.iter()
.enumerate()
{
if let Operand::Reg(r) = a {
visit(RegPos::Arg((range.start + k) as u32), *r, Access::Use);
}
}
}
_ => {}
}
}
}
pub(crate) fn map_vregs<E>(
&mut self,
inst: Inst,
mut map: impl FnMut(VReg) -> Result<Reg, E>,
) -> Result<(), E> {
let Some(data) = self.insts.get(inst.index()) else {
return Ok(());
};
let (start, n) = (data.ops as usize, data.nops as usize);
let Function { operands, args, .. } = self;
let Some(ops) = operands.get_mut(start..start + n) else {
return Ok(());
};
for op in ops.iter_mut() {
match op {
Operand::Reg(r) => {
if let Some(v) = r.as_virt() {
*r = map(v)?;
}
}
Operand::Mem(m) => {
if let Some(v) = m.base_reg().and_then(Reg::as_virt) {
*m = m.with_base(map(v)?);
}
if let Some((x, s)) = m.index_reg() {
if let Some(v) = x.as_virt() {
*m = m.with_index(map(v)?, s);
}
}
}
Operand::Block(call) => {
if let Some(list) = args.get_mut(call.range()) {
for a in list {
if let Operand::Reg(r) = a {
if let Some(v) = r.as_virt() {
*r = map(v)?;
}
}
}
}
}
_ => {}
}
}
Ok(())
}
pub(crate) fn set_reg_at(&mut self, pos: RegPos, reg: Reg) {
match pos {
RegPos::Op(i) => {
if let Some(op) = self.operands.get_mut(i as usize) {
*op = Operand::Reg(reg);
}
}
RegPos::MemBase(i) => {
if let Some(Operand::Mem(m)) = self.operands.get_mut(i as usize) {
let mut out = Mem::base(reg).with_disp(m.disp());
if let Some((x, s)) = m.index_reg() {
out = out.with_index(x, s);
}
*m = out;
}
}
RegPos::MemIndex(i) => {
if let Some(Operand::Mem(m)) = self.operands.get_mut(i as usize) {
if let Some((_, s)) = m.index_reg() {
*m = m.with_index(reg, s);
}
}
}
RegPos::Arg(i) => {
if let Some(a) = self.args.get_mut(i as usize) {
*a = Operand::Reg(reg);
}
}
}
}
pub(crate) fn set_operand_at(&mut self, pos: RegPos, op: Operand) {
match pos {
RegPos::Op(i) => {
if let Some(o) = self.operands.get_mut(i as usize) {
*o = op;
}
}
RegPos::Arg(i) => {
if let Some(a) = self.args.get_mut(i as usize) {
*a = op;
}
}
RegPos::MemBase(_) | RegPos::MemIndex(_) => {}
}
}
}