use crate::recompiler::decoder::{DecodedInstruction, Operand};
use crate::recompiler::analysis::control_flow::ControlFlowGraph;
use bitvec::prelude::*;
use smallvec::SmallVec;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct DefUseChain {
pub register: u8,
pub definitions: SmallVec<[Definition; 4]>,
pub uses: SmallVec<[Use; 8]>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(C)] pub struct Definition {
pub instruction_index: usize,
pub address: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(C)] pub struct Use {
pub instruction_index: usize,
pub address: u32,
}
#[derive(Debug, Clone)]
pub struct LiveVariableAnalysis {
pub live_at_entry: HashMap<u32, BitVec<u32>>,
pub live_at_exit: HashMap<u32, BitVec<u32>>,
}
pub struct DataFlowAnalyzer;
impl DataFlowAnalyzer {
#[inline] pub fn build_def_use_chains(
instructions: &[DecodedInstruction],
) -> HashMap<u8, DefUseChain> {
let mut chains: HashMap<u8, DefUseChain> = HashMap::new();
let mut definitions: HashMap<u8, SmallVec<[Definition; 4]>> = HashMap::new();
let mut uses: HashMap<u8, SmallVec<[Use; 8]>> = HashMap::new();
let mut instruction_address: u32 = 0u32;
for (idx, inst) in instructions.iter().enumerate() {
if let Some(def_reg) = Self::get_definition_register(inst) {
definitions.entry(def_reg).or_insert_with(SmallVec::new).push(Definition {
instruction_index: idx,
address: instruction_address,
});
}
for use_reg in Self::get_use_registers(inst) {
uses.entry(use_reg).or_insert_with(SmallVec::new).push(Use {
instruction_index: idx,
address: instruction_address,
});
}
instruction_address = instruction_address.wrapping_add(4); }
for reg in 0u8..32u8 {
let defs: SmallVec<[Definition; 4]> = definitions.remove(®).unwrap_or_default();
let uses_list: SmallVec<[Use; 8]> = uses.remove(®).unwrap_or_default();
if !defs.is_empty() || !uses_list.is_empty() {
chains.insert(reg, DefUseChain {
register: reg,
definitions: defs,
uses: uses_list,
});
}
}
chains
}
#[inline] fn get_definition_register(inst: &DecodedInstruction) -> Option<u8> {
if !inst.instruction.operands.is_empty() {
if let Operand::Register(reg) = &inst.instruction.operands[0] {
match inst.instruction.instruction_type {
crate::recompiler::decoder::InstructionType::Arithmetic
| crate::recompiler::decoder::InstructionType::Load
| crate::recompiler::decoder::InstructionType::Move => {
return Some(*reg);
}
_ => {}
}
}
}
None
}
#[inline] fn get_use_registers(inst: &DecodedInstruction) -> SmallVec<[u8; 4]> {
let mut uses: SmallVec<[u8; 4]> = SmallVec::new();
let def_reg: Option<u8> = Self::get_definition_register(inst);
for operand in inst.instruction.operands.iter() {
match operand {
Operand::Register(reg) => {
if def_reg != Some(*reg) {
uses.push(*reg);
}
}
Operand::FpRegister(_) => {
}
_ => {}
}
}
uses
}
#[inline] pub fn live_variable_analysis(cfg: &ControlFlowGraph) -> LiveVariableAnalysis {
let mut live_at_entry: HashMap<u32, BitVec<u32>> = HashMap::new();
let mut live_at_exit: HashMap<u32, BitVec<u32>> = HashMap::new();
let mut changed: bool = true;
for block in cfg.nodes.iter() {
live_at_entry.insert(block.id, bitvec![u32, Lsb0; 0; 32]);
live_at_exit.insert(block.id, bitvec![u32, Lsb0; 0; 32]);
}
while changed {
changed = false;
for block in cfg.nodes.iter() {
let mut exit_live: BitVec<u32> = bitvec![u32, Lsb0; 0; 32];
for &succ in block.successors.iter() {
if let Some(entry_live) = live_at_entry.get(&succ) {
exit_live |= entry_live; }
}
let mut entry_live: BitVec<u32> = exit_live.clone();
for inst in block.instructions.iter() {
if let Some(killed) = Self::get_definition_register(inst) {
entry_live.set(killed as usize, false);
}
}
for inst in block.instructions.iter() {
for used in Self::get_use_registers(inst).iter() {
entry_live.set(*used as usize, true);
}
}
let old_entry: BitVec<u32> = live_at_entry.get(&block.id).cloned().unwrap_or_else(|| bitvec![u32, Lsb0; 0; 32]);
if old_entry != entry_live {
changed = true;
live_at_entry.insert(block.id, entry_live);
live_at_exit.insert(block.id, exit_live);
}
}
}
LiveVariableAnalysis {
live_at_entry,
live_at_exit,
}
}
#[inline] pub fn eliminate_dead_code(
instructions: &[DecodedInstruction],
_live_analysis: &LiveVariableAnalysis,
) -> Vec<DecodedInstruction> {
let mut optimized: Vec<DecodedInstruction> = Vec::new();
for inst in instructions.iter() {
if let Some(def_reg) = Self::get_definition_register(inst) {
let mut is_used: bool = false;
for other_inst in instructions.iter() {
if Self::get_use_registers(other_inst).contains(&def_reg) {
is_used = true;
break;
}
}
if is_used {
optimized.push(inst.clone());
}
} else {
optimized.push(inst.clone());
}
}
optimized
}
}