use std::collections::{BTreeMap, BTreeSet, HashMap};
use analyssa::{
ir::{
function::SsaFunction,
ops::SsaOp,
variable::{SsaVarId, VariableOrigin},
},
target::Target,
};
use crate::{
analysis::{
cfg::{BlockSemantics, LoopSemantics, SemanticAnalyzer},
ssa::{target::CilTarget, types::SsaType},
LoopInfo,
},
metadata::signatures::{CustomModifiers, SignatureLocalVariable, SignatureLocalVariables},
Error, Result,
};
pub trait SsaFunctionCilExt {
fn optimize_locals(&mut self) -> Vec<Option<u16>>;
fn generate_local_signature(
&self,
override_count: Option<u16>,
temporary_types: Option<&BTreeMap<u16, SsaType>>,
) -> Result<SignatureLocalVariables>;
fn infer_local_type(&self, local_idx: usize) -> Option<SsaType>;
}
impl SsaFunctionCilExt for SsaFunction<CilTarget> {
fn optimize_locals(&mut self) -> Vec<Option<u16>> {
let mut used_locals: BTreeSet<u16> = BTreeSet::new();
for var in self.variables() {
if let VariableOrigin::Local(idx) = var.origin() {
used_locals.insert(idx);
}
}
for block in self.blocks() {
for phi in block.phi_nodes() {
if let VariableOrigin::Local(idx) = phi.origin() {
used_locals.insert(idx);
}
}
}
for block in self.blocks() {
for instr in block.instructions() {
match instr.op() {
SsaOp::LoadLocal { local_index, .. }
| SsaOp::LoadLocalAddr { local_index, .. } => {
used_locals.insert(*local_index);
}
_ => {}
}
}
}
let max_idx = used_locals.iter().copied().max().unwrap_or(0);
let max_known = u16::try_from(self.num_locals())
.unwrap_or(u16::MAX)
.saturating_sub(1)
.max(max_idx);
let mut remap: Vec<Option<u16>> = vec![None; usize::from(max_known) + 1];
let mut new_idx: u16 = 0;
for old_idx in 0..=max_known {
if used_locals.contains(&old_idx) {
if let Some(slot) = remap.get_mut(usize::from(old_idx)) {
*slot = Some(new_idx);
}
new_idx = new_idx.saturating_add(1);
}
}
let new_count = new_idx;
let mut new_origins: Vec<(SsaVarId, VariableOrigin)> = Vec::new();
for var in self.variables() {
if let VariableOrigin::Local(old) = var.origin() {
if let Some(Some(new)) = remap.get(usize::from(old)) {
new_origins.push((var.id(), VariableOrigin::Local(*new)));
}
}
}
for (id, origin) in new_origins {
if let Some(v) = self.variable_mut(id) {
v.set_origin(origin);
}
}
for block in self.blocks_mut() {
for phi in block.phi_nodes_mut() {
if let VariableOrigin::Local(old) = phi.origin() {
if let Some(Some(new)) = remap.get(usize::from(old)) {
phi.set_origin(VariableOrigin::Local(*new));
}
}
}
}
for block in self.blocks_mut() {
for instr in block.instructions_mut() {
let op = instr.op_mut();
let new_index = match op {
SsaOp::LoadLocal { local_index, .. }
| SsaOp::LoadLocalAddr { local_index, .. } => {
remap.get(usize::from(*local_index)).copied().flatten()
}
_ => None,
};
if let Some(new) = new_index {
match op {
SsaOp::LoadLocal { local_index, .. }
| SsaOp::LoadLocalAddr { local_index, .. } => *local_index = new,
_ => {}
}
}
}
}
let original = self.original_num_locals();
self.set_num_locals(usize::from(new_count), original);
remap
}
fn generate_local_signature(
&self,
override_count: Option<u16>,
temporary_types: Option<&BTreeMap<u16, SsaType>>,
) -> Result<SignatureLocalVariables> {
let empty_temps = BTreeMap::new();
let temps = temporary_types.unwrap_or(&empty_temps);
let local_count = override_count
.map(usize::from)
.unwrap_or_else(|| self.num_locals());
if let Some(orig) = self.original_local_types() {
let mut locals: Vec<SignatureLocalVariable> = Vec::with_capacity(local_count);
for sig in orig.iter().take(local_count) {
locals.push(SignatureLocalVariable {
modifiers: sig.modifiers.clone(),
is_byref: sig.is_byref,
is_pinned: sig.is_pinned,
base: sig.base.clone(),
});
}
while locals.len() < local_count {
let idx = u16::try_from(locals.len()).unwrap_or(u16::MAX);
let ty = temps
.get(&idx)
.cloned()
.or_else(|| self.infer_local_type(usize::from(idx)));
let Some(ty) = ty else {
return Err(Error::SsaError(format!(
"missing type info for local index {idx} during signature generation"
)));
};
locals.push(SignatureLocalVariable {
modifiers: CustomModifiers::default(),
is_byref: false,
is_pinned: false,
base: ty.to_type_signature(),
});
}
return Ok(SignatureLocalVariables { locals });
}
let mut local_types: Vec<Option<SsaType>> = vec![None; local_count];
for (idx, ty) in temps.iter() {
if let Some(slot) = local_types.get_mut(usize::from(*idx)) {
*slot = Some(ty.clone());
}
}
for (idx, slot) in local_types.iter_mut().enumerate() {
if slot.is_none() {
*slot = self.infer_local_type(idx);
}
}
let mut locals: Vec<SignatureLocalVariable> = Vec::with_capacity(local_types.len());
for (idx, ty) in local_types.iter().enumerate() {
let Some(ty) = ty else {
return Err(Error::SsaError(format!(
"missing type info for local index {idx} during signature generation"
)));
};
locals.push(SignatureLocalVariable {
modifiers: CustomModifiers::default(),
is_byref: false,
is_pinned: false,
base: ty.to_type_signature(),
});
}
Ok(SignatureLocalVariables { locals })
}
fn infer_local_type(&self, local_idx: usize) -> Option<SsaType> {
let target_origin = VariableOrigin::Local(u16::try_from(local_idx).ok()?);
for var in self.variables() {
if var.origin() == target_origin {
let ty = var.var_type();
if !CilTarget::is_unknown(ty) {
return Some(ty.clone());
}
}
}
None
}
}
pub trait SsaFunctionSemanticsExt<T: Target> {
fn analyze_block_semantics(&self, block_idx: usize) -> BlockSemantics;
fn analyze_blocks_semantics(&self, blocks: &[usize]) -> HashMap<usize, BlockSemantics>;
fn analyze_loop_semantics(&self, loop_info: &LoopInfo) -> LoopSemantics;
fn recover_loop_from_cases(
&self,
case_blocks: &[usize],
dispatcher_block: Option<usize>,
) -> LoopSemantics;
fn semantic_analyzer(&self) -> SemanticAnalyzer<'_, T>;
}
impl<T: Target> SsaFunctionSemanticsExt<T> for SsaFunction<T> {
fn analyze_block_semantics(&self, block_idx: usize) -> BlockSemantics {
let mut analyzer = SemanticAnalyzer::new(self);
analyzer.analyze_block(block_idx).clone()
}
fn analyze_blocks_semantics(&self, blocks: &[usize]) -> HashMap<usize, BlockSemantics> {
let mut analyzer = SemanticAnalyzer::new(self);
let mut results = HashMap::new();
for &block in blocks {
results.insert(block, analyzer.analyze_block(block).clone());
}
results
}
fn analyze_loop_semantics(&self, loop_info: &LoopInfo) -> LoopSemantics {
let mut analyzer = SemanticAnalyzer::new(self);
analyzer.analyze_loop(loop_info)
}
fn recover_loop_from_cases(
&self,
case_blocks: &[usize],
dispatcher_block: Option<usize>,
) -> LoopSemantics {
let mut analyzer = SemanticAnalyzer::new(self);
if let Some(disp) = dispatcher_block {
analyzer.mark_dispatcher(disp);
}
analyzer.recover_loop_from_cases(case_blocks)
}
fn semantic_analyzer(&self) -> SemanticAnalyzer<'_, T> {
SemanticAnalyzer::new(self)
}
}