use std::collections::HashMap;
use machine_check_common::ir_common::IrReference;
use crate::{
abstr::{WAbstrItemImplTrait, YAbstr, ZAbstr, ZAbstrIfPolarity},
wir::{
WBlock, WElementaryType, WExpr, WExprCall, WExprReference, WFnArg, WGeneralType, WIdent,
WIfCondition, WItemFn, WItemImpl, WItemImplTrait, WPath, WPathSegment, WSignature,
WSsaLocal, WStmt, WStmtAssign, WStmtIf, WType, YConverted, ZConverted,
},
};
mod used_open;
pub fn preprocess_item_impl(item_impl: &WItemImpl<YConverted>) -> Option<WPath> {
let Some(WItemImplTrait::Machine(_)) = item_impl.trait_ else {
return None;
};
let mut ty = item_impl.self_ty.clone();
let span = ty.span();
ty.segments.insert(
0,
WPathSegment {
ident: WIdent::new(String::from("super"), span),
},
);
Some(ty)
}
pub fn process_item_impl(
item_impl: WItemImpl<YConverted>,
machine_types: &[WPath],
) -> Vec<WItemImpl<YAbstr>> {
let mut impl_item_fns = Vec::new();
for impl_item_fn in item_impl.impl_item_fns {
impl_item_fns.push(fold_impl_item_fn(impl_item_fn));
}
let self_ty = item_impl.self_ty;
let trait_ = item_impl.trait_;
let impl_item_types = item_impl.impl_item_types;
let mut results = Vec::new();
for machine_type in machine_types {
let current_trait = trait_.as_ref().map(|trait_| WAbstrItemImplTrait {
machine_type: machine_type.clone(),
trait_: trait_.clone(),
});
results.push(WItemImpl {
self_ty: self_ty.clone(),
trait_: current_trait,
impl_item_fns: impl_item_fns.clone(),
impl_item_types: impl_item_types.clone(),
});
}
results
}
pub fn fold_impl_item_fn(mut impl_item_fn: WItemFn<YConverted>) -> WItemFn<YAbstr> {
let signature = WSignature {
ident: impl_item_fn.signature.ident,
inputs: impl_item_fn.signature.inputs,
output: impl_item_fn.signature.output,
};
let mut locals_and_args_map = HashMap::new();
for input in &signature.inputs {
let ty: &WType<WElementaryType> = &input.ty;
locals_and_args_map.insert(
input.ident.clone(),
WSsaLocal {
ident: input.ident.clone(),
original: input.ident.clone(),
ty: WGeneralType::Normal(ty.clone()),
},
);
}
for local in &impl_item_fn.locals {
locals_and_args_map.insert(local.ident.clone(), local.clone());
}
let mut converter = AbstractConverter {
locals: &mut impl_item_fn.locals,
inputs: &signature.inputs,
next_cloned_id: 0,
};
let block = converter.fold_block(impl_item_fn.block);
WItemFn {
visibility: impl_item_fn.visibility,
signature,
locals: impl_item_fn.locals,
block,
result: impl_item_fn.result,
}
}
#[derive(Debug)]
struct AbstractConverter<'a> {
locals: &'a mut Vec<WSsaLocal<WGeneralType<WElementaryType>>>,
inputs: &'a Vec<WFnArg<WType<WElementaryType>>>,
next_cloned_id: u64,
}
impl AbstractConverter<'_> {
fn fold_block(&mut self, block: WBlock<ZConverted>) -> WBlock<ZAbstr> {
WBlock {
stmts: block
.stmts
.into_iter()
.flat_map(|stmt| self.fold_stmt(stmt))
.collect(),
}
}
fn fold_stmt(&mut self, stmt: WStmt<ZConverted>) -> Vec<WStmt<ZAbstr>> {
match stmt {
WStmt::Assign(stmt_assign) => {
vec![WStmt::Assign(WStmtAssign {
left: stmt_assign.left,
right: stmt_assign.right,
})]
}
WStmt::If(stmt_if) => self.fold_if(stmt_if),
}
}
fn fold_if(&mut self, stmt_if: WStmtIf<ZConverted>) -> Vec<WStmt<ZAbstr>> {
let can_be_true_stmt_if =
self.create_branch_if(&stmt_if.condition.ident, true, stmt_if.then_block);
let can_be_false_stmt_if =
self.create_branch_if(&stmt_if.condition.ident, false, stmt_if.else_block);
vec![
WStmt::If(can_be_true_stmt_if),
WStmt::If(can_be_false_stmt_if),
]
}
fn create_branch_if(
&mut self,
condition: &WIdent,
polarity: bool,
mut taken_block: WBlock<ZConverted>,
) -> WStmtIf<ZAbstr> {
let used_open_idents = used_open::used_open_idents(&taken_block);
let mut added_start_stmts = Vec::new();
for used_open_ident in used_open_idents {
self.process_used_open_ident(&mut taken_block, &mut added_start_stmts, used_open_ident);
}
added_start_stmts.append(&mut taken_block.stmts);
taken_block.stmts = added_start_stmts;
let (taken_block, not_taken_block) = self.process_taken_branch_block(taken_block);
WStmtIf {
condition: WIfCondition {
polarity: ZAbstrIfPolarity(polarity),
ident: condition.clone(),
},
then_block: taken_block,
else_block: not_taken_block,
}
}
fn process_taken_branch_block(
&mut self,
taken_block: WBlock<ZConverted>,
) -> (WBlock<ZAbstr>, WBlock<ZAbstr>) {
let mut taken_stmts = Vec::new();
let mut not_taken_stmts = Vec::new();
for stmt in taken_block.stmts {
let stmt_assign = match stmt {
WStmt::Assign(stmt_assign) => stmt_assign,
WStmt::If(stmt_if) => {
taken_stmts.extend(self.fold_if(stmt_if));
continue;
}
};
let taken = match stmt_assign.right {
WExpr::Call(WExprCall::PhiTaken(ident)) => ident,
WExpr::Call(WExprCall::PhiNotTaken) => {
continue;
}
_ => {
taken_stmts.extend(self.fold_stmt(WStmt::Assign(stmt_assign)));
continue;
}
};
taken_stmts.push(WStmt::Assign(WStmtAssign {
left: stmt_assign.left.clone(),
right: WExpr::Call(WExprCall::PhiTaken(taken)),
}));
not_taken_stmts.push(WStmt::Assign(WStmtAssign {
left: stmt_assign.left,
right: WExpr::Call(WExprCall::PhiNotTaken),
}));
}
(
WBlock { stmts: taken_stmts },
WBlock {
stmts: not_taken_stmts,
},
)
}
fn process_used_open_ident(
&mut self,
taken_block: &mut WBlock<ZConverted>,
added_start_stmts: &mut Vec<WStmt<ZConverted>>,
used_open_ident: WIdent,
) {
let Some(local) = self.get_from_locals_and_idents(&used_open_ident) else {
panic!("Not found open used {:?} in locals", used_open_ident);
};
let clone_type = if let WGeneralType::Normal(WType {
reference: IrReference::None,
inner,
}) = &local.ty
{
match inner {
WElementaryType::Bitvector(_) | WElementaryType::Boolean => None,
WElementaryType::Array(_) | WElementaryType::Path(_) => Some(inner.clone()),
}
} else {
None
};
let Some(clone_type) = clone_type else {
return;
};
let clone_ref_ident =
used_open_ident.mck_prefixed(&format!("clone_ref_{}", self.next_cloned_id));
let cloned_ident = used_open_ident.mck_prefixed(&format!("cloned_{}", self.next_cloned_id));
self.next_cloned_id += 1;
for stmt in &mut taken_block.stmts {
replace_stmt_ident(stmt, &used_open_ident, &cloned_ident);
}
let clone_ref_stmt = WStmt::Assign(WStmtAssign {
left: clone_ref_ident.clone(),
right: WExpr::Reference(WExprReference::Ident(used_open_ident.clone())),
});
let clone_stmt = WStmt::Assign(WStmtAssign {
left: cloned_ident.clone(),
right: WExpr::Call(WExprCall::StdClone(clone_ref_ident.clone())),
});
added_start_stmts.push(clone_ref_stmt.clone());
added_start_stmts.push(clone_stmt.clone());
let original_ident = local.original.clone();
let original_ty = local.ty.clone();
self.locals.push(WSsaLocal {
ident: clone_ref_ident,
original: original_ident.clone(),
ty: WGeneralType::Normal(WType {
reference: IrReference::Immutable,
inner: clone_type,
}),
});
self.locals.push(WSsaLocal {
ident: cloned_ident,
original: original_ident,
ty: original_ty,
});
}
fn get_from_locals_and_idents(
&self,
ident: &WIdent,
) -> Option<WSsaLocal<WGeneralType<WElementaryType>>> {
for local in self.locals.iter() {
if &local.ident == ident {
return Some(local.clone());
}
}
for input in self.inputs {
if &input.ident == ident {
return Some(WSsaLocal {
ident: input.ident.clone(),
original: input.ident.clone(),
ty: WGeneralType::Normal(input.ty.clone()),
});
}
}
None
}
}
fn replace_stmt_ident(stmt: &mut WStmt<ZConverted>, original: &WIdent, replacement: &WIdent) {
match stmt {
WStmt::Assign(stmt_assign) => match &mut stmt_assign.right {
WExpr::Move(ident) | WExpr::Reference(WExprReference::Ident(ident)) => {
if ident == original {
*ident = replacement.clone();
}
}
WExpr::Call(call) => call.replace_ident(original, replacement),
WExpr::Field(expr_field) | WExpr::Reference(WExprReference::Field(expr_field)) => {
if &expr_field.base == original {
expr_field.base = replacement.clone();
}
}
WExpr::Struct(expr_struct) => {
for (_field_name, field_value) in &mut expr_struct.fields {
if field_value == original {
*field_value = replacement.clone();
}
}
}
WExpr::Lit(_, _) => {}
},
WStmt::If(stmt_if) => {
if &stmt_if.condition.ident == original {
stmt_if.condition.ident = replacement.clone();
}
for stmt in stmt_if
.then_block
.stmts
.iter_mut()
.chain(stmt_if.else_block.stmts.iter_mut())
{
replace_stmt_ident(stmt, original, replacement);
}
}
}
}