mod analysis;
mod rewrite;
use crate::{expression::Expression, literal::Literal, statement::Statement};
use analysis::{
count_reads, count_straight_line_reads, is_cheap_to_rematerialize, is_compiler_temp, is_pure,
is_written,
};
use rewrite::{replace_ident_in_expr, replace_ident_in_statement};
pub(super) fn eliminate_copy_temps(mut stmts: Vec<Statement>) -> Vec<Statement> {
stmts = fold_temp_init_overwrite(stmts);
let mut i = 0;
while i < stmts.len() {
let Statement::VariableDecl { name, value, .. } = &stmts[i] else {
i += 1;
continue;
};
if !is_compiler_temp(name) {
i += 1;
continue;
}
let name = name.clone();
let value = value.clone();
let rest = &stmts[i + 1..];
if is_written(&name, rest) {
i += 1;
continue;
}
let reads = count_reads(&name, rest);
let can_subst = reads == 0
|| is_cheap_to_rematerialize(&value)
|| (reads == 1 && (is_pure(&value) || count_straight_line_reads(&name, rest) == 1));
if !can_subst {
i += 1;
continue;
}
stmts.remove(i);
if reads > 0 {
for statement in &mut stmts[i..] {
replace_ident_in_statement(statement, &name, &value);
}
}
}
fold_temp_into_single_copy(stmts)
}
fn fold_temp_init_overwrite(mut stmts: Vec<Statement>) -> Vec<Statement> {
let mut i = 0;
while i + 1 < stmts.len() {
let Statement::VariableDecl {
name, value: init, ..
} = &stmts[i]
else {
i += 1;
continue;
};
if !is_compiler_temp(name) {
i += 1;
continue;
}
let Statement::Assignment {
target: Expression::Identifier(dest),
value: overwrite,
} = &stmts[i + 1]
else {
i += 1;
continue;
};
if dest != name {
i += 1;
continue;
}
let name = name.clone();
let init = init.clone();
let mut new_init = overwrite.clone();
replace_ident_in_expr(&mut new_init, &name, &init);
if let Statement::VariableDecl { value, .. } = &mut stmts[i] {
*value = new_init;
}
stmts.remove(i + 1);
}
stmts
}
fn fold_temp_into_single_copy(mut stmts: Vec<Statement>) -> Vec<Statement> {
let mut i = 0;
while i < stmts.len() {
if try_fold_nil_then_temp_assign(&mut stmts, i) {
continue;
}
let Statement::VariableDecl { name: tmp, .. } = &stmts[i] else {
i += 1;
continue;
};
if !is_compiler_temp(tmp) {
i += 1;
continue;
}
let tmp = tmp.clone();
let Some((copy_idx, dest)) = find_final_ident_copy(&stmts, i, &tmp) else {
i += 1;
continue;
};
let window = &stmts[i..copy_idx];
if count_reads(&dest, window) > 0 || is_written(&dest, window) {
i += 1;
continue;
}
if let Statement::VariableDecl { name, .. } = &mut stmts[i] {
name.clone_from(&dest);
}
let dest_expr = Expression::Identifier(dest.clone());
for statement in &mut stmts[i + 1..copy_idx] {
replace_ident_in_statement(statement, &tmp, &dest_expr);
}
stmts.remove(copy_idx);
}
stmts
}
fn try_fold_nil_then_temp_assign(stmts: &mut Vec<Statement>, nil_idx: usize) -> bool {
let Statement::VariableDecl {
name: dest,
ty,
source_type,
value: Expression::Literal(Literal::Nil),
} = &stmts[nil_idx]
else {
return false;
};
let dest = dest.clone();
let ty = ty.clone();
let source_type = source_type.clone();
let Some(Statement::VariableDecl { name: tmp, .. }) = stmts.get(nil_idx + 1) else {
return false;
};
if !is_compiler_temp(tmp) {
return false;
}
let tmp = tmp.clone();
let tmp_decl_idx = nil_idx + 1;
let Some(assign_idx) = find_final_ident_assign(stmts, tmp_decl_idx, &tmp, &dest) else {
return false;
};
let window = &stmts[tmp_decl_idx..assign_idx];
if count_reads(&dest, window) > 0 || is_written(&dest, window) {
return false;
}
if let Statement::VariableDecl {
name,
ty: tmp_ty,
source_type: tmp_source,
..
} = &mut stmts[tmp_decl_idx]
{
name.clone_from(&dest);
*tmp_ty = ty;
*tmp_source = source_type;
}
let dest_expr = Expression::Identifier(dest);
for statement in &mut stmts[tmp_decl_idx + 1..assign_idx] {
replace_ident_in_statement(statement, &tmp, &dest_expr);
}
stmts.remove(assign_idx);
stmts.remove(nil_idx);
true
}
fn find_final_ident_copy(
stmts: &[Statement],
tmp_decl_idx: usize,
tmp: &str,
) -> Option<(usize, String)> {
for (offset, statement) in stmts[tmp_decl_idx + 1..].iter().enumerate() {
let Statement::VariableDecl {
name: dest,
value: Expression::Identifier(src),
..
} = statement
else {
continue;
};
if src != tmp {
continue;
}
let copy_idx = tmp_decl_idx + 1 + offset;
let after = &stmts[copy_idx + 1..];
if count_reads(tmp, after) == 0 && !is_written(tmp, after) {
return Some((copy_idx, dest.clone()));
}
return None;
}
None
}
fn find_final_ident_assign(
stmts: &[Statement],
tmp_decl_idx: usize,
tmp: &str,
dest: &str,
) -> Option<usize> {
for (offset, statement) in stmts[tmp_decl_idx + 1..].iter().enumerate() {
let Statement::Assignment {
target: Expression::Identifier(assign_dest),
value: Expression::Identifier(src),
} = statement
else {
continue;
};
if src != tmp || assign_dest != dest {
continue;
}
let assign_idx = tmp_decl_idx + 1 + offset;
let after = &stmts[assign_idx + 1..];
if count_reads(tmp, after) == 0 && !is_written(tmp, after) {
return Some(assign_idx);
}
return None;
}
None
}