use crate::BigUint;
use crate::HashMap;
use crate::HashSet;
use crate::conv::Context;
use crate::ir::{AssignDestination, Declaration, FfTable, Statement, VarId, VarKind};
use crate::symbol::Affiliation;
use crate::value::{Value, ValueBigUint};
pub type UnsafeSelfReads = HashSet<(usize, VarId, usize)>;
type Mask = Option<BigUint>;
type Written = HashMap<(VarId, usize), Mask>;
pub fn unsafe_self_reads(decls: &[Declaration], context: &mut Context) -> UnsafeSelfReads {
let mut result = UnsafeSelfReads::default();
for (decl, x) in decls.iter().enumerate() {
if let Declaration::Ff(ff) = x {
let mut written = Written::default();
walk_seq(&ff.statements, decl, context, &mut written, &mut result);
}
}
result
}
fn overlaps(read: &Mask, written: &Mask) -> bool {
match (read, written) {
(Some(r), Some(w)) => (r & w) != BigUint::ZERO,
_ => true,
}
}
fn merge(into: &mut Mask, from: Mask) {
match (into.as_mut(), from) {
(Some(a), Some(b)) => *a |= b,
_ => *into = None,
}
}
fn walk_seq(
stmts: &[Statement],
decl: usize,
context: &mut Context,
written: &mut Written,
out: &mut UnsafeSelfReads,
) {
for s in stmts {
walk_one(s, decl, context, written, out);
}
}
fn walk_one(
stmt: &Statement,
decl: usize,
context: &mut Context,
written: &mut Written,
out: &mut UnsafeSelfReads,
) {
match stmt {
Statement::Assign(x) => {
let mut dsts: Vec<((VarId, usize), Mask)> = Vec::new();
for dst in &x.dst {
add_dst(dst, context, &mut dsts);
}
let reads = element_reads(&x.expr, decl, context);
for (key, _) in &dsts {
if let Some(read) = reads.get(key)
&& let Some(prior) = written.get(key)
&& overlaps(read, prior)
{
out.insert((decl, key.0, key.1));
}
}
for (key, mask) in dsts {
merge(
written
.entry(key)
.or_insert_with(|| Some(BigUint::default())),
mask,
);
}
}
Statement::If(x) => walk_branches(&x.true_side, &x.false_side, decl, context, written, out),
Statement::IfReset(x) => {
walk_branches(&x.true_side, &x.false_side, decl, context, written, out)
}
Statement::Case(x) => walk_seq(&x.lower_to_nested_if(), decl, context, written, out),
Statement::For(x) => {
if let Some(iter) = x.range.eval_iter(context) {
for i in iter {
if let Some(var) = context.variable_mut(&x.var_id)
&& let Some(total_width) = x.var_type.total_width()
{
let val = Value::new(i as u64, total_width, x.var_type.signed);
var.set_value(&[], val, None);
}
walk_seq(&x.body, decl, context, written, out);
}
} else {
let mut body: Vec<((VarId, usize), Mask)> = Vec::new();
collect_writes(&x.body, context, &mut body);
for (key, mask) in body {
merge(
written
.entry(key)
.or_insert_with(|| Some(BigUint::default())),
mask,
);
}
walk_seq(&x.body, decl, context, written, out);
}
}
Statement::FunctionCall(x) => {
for outputs in x.outputs.values() {
let mut dsts: Vec<((VarId, usize), Mask)> = Vec::new();
for dst in outputs {
add_dst(dst, context, &mut dsts);
}
for (key, mask) in dsts {
if let Some(prior) = written.get(&key)
&& overlaps(&mask, prior)
{
out.insert((decl, key.0, key.1));
}
merge(
written
.entry(key)
.or_insert_with(|| Some(BigUint::default())),
mask,
);
}
}
}
Statement::SystemFunctionCall(_)
| Statement::TbMethodCall(_)
| Statement::Break
| Statement::Unsupported(_)
| Statement::Null => {}
}
}
fn walk_branches(
true_side: &[Statement],
false_side: &[Statement],
decl: usize,
context: &mut Context,
written: &mut Written,
out: &mut UnsafeSelfReads,
) {
let mut wt = written.clone();
let mut wf = std::mem::take(written);
walk_seq(true_side, decl, context, &mut wt, out);
walk_seq(false_side, decl, context, &mut wf, out);
for (key, mask) in wf {
merge(
wt.entry(key).or_insert_with(|| Some(BigUint::default())),
mask,
);
}
*written = wt;
}
fn collect_writes(
stmts: &[Statement],
context: &mut Context,
out: &mut Vec<((VarId, usize), Mask)>,
) {
for s in stmts {
match s {
Statement::Assign(x) => {
for dst in &x.dst {
add_dst(dst, context, out);
}
}
Statement::If(x) => {
collect_writes(&x.true_side, context, out);
collect_writes(&x.false_side, context, out);
}
Statement::IfReset(x) => {
collect_writes(&x.true_side, context, out);
collect_writes(&x.false_side, context, out);
}
Statement::Case(x) => collect_writes(&x.lower_to_nested_if(), context, out),
Statement::For(x) => collect_writes(&x.body, context, out),
Statement::FunctionCall(x) => {
for outputs in x.outputs.values() {
for dst in outputs {
add_dst(dst, context, out);
}
}
}
Statement::SystemFunctionCall(_)
| Statement::TbMethodCall(_)
| Statement::Break
| Statement::Unsupported(_)
| Statement::Null => {}
}
}
}
fn element_reads(
expr: &crate::ir::Expression,
decl: usize,
context: &mut Context,
) -> HashMap<(VarId, usize), Mask> {
let mut table = FfTable::default();
expr.gather_ff(context, &mut table, decl, None, true);
let mut out: HashMap<(VarId, usize), Mask> = HashMap::default();
for (key, entry) in table.table {
let mut mask: Mask = Some(BigUint::default());
for (_, _, src_read_mask, _) in entry.refered {
if src_read_mask == BigUint::ZERO {
mask = None;
break;
}
merge(&mut mask, Some(src_read_mask));
}
out.insert(key, mask);
}
out
}
fn add_dst(dst: &AssignDestination, context: &mut Context, out: &mut Vec<((VarId, usize), Mask)>) {
let Some(variable) = context.get_variable_info(dst.id) else {
return;
};
if variable.kind == VarKind::Let || variable.affiliation == Affiliation::AlwaysFf {
return;
}
let r#type = variable.r#type.clone();
let mask: Mask = dst
.select
.eval_value(context, &r#type, false)
.map(|(beg, end)| ValueBigUint::gen_mask_range(beg, end));
if let Some(index) = dst.index.eval_value(context) {
if let Some(variable) = context.get_variable_info(dst.id)
&& let Some(index) = variable.r#type.array.calc_index(&index)
{
out.push(((dst.id, index), mask));
}
} else if let Some(total_array) = context
.get_variable_info(dst.id)
.and_then(|v| v.r#type.total_array())
{
for i in 0..total_array {
out.push(((dst.id, i), mask.clone()));
}
}
}