use crate::codegen::cfg::{CfgPredicate, parse_cfg_predicate};
use crate::core::ir::FunctionDef;
use ahash::AHashMap;
pub(in crate::backends::rustler::gen_bindings) fn regate_ungated_same_name_functions(
functions: &[FunctionDef],
) -> Vec<FunctionDef> {
let groups = collect_function_groups(functions);
let mut gated_cfg_by_name: AHashMap<&str, Option<String>> = AHashMap::new();
for (name, indices) in &groups {
if !is_mixed_gated_group(indices, functions) {
continue;
}
let gated = merge_gated_cfgs(indices.iter().map(|&i| functions[i].cfg.as_deref()));
gated_cfg_by_name.insert(name.as_str(), gated);
}
if gated_cfg_by_name.is_empty() {
return functions.to_vec();
}
functions
.iter()
.cloned()
.map(|mut function| {
if function.cfg.is_none()
&& let Some(Some(gated)) = gated_cfg_by_name.get(function.name.as_str())
{
function.cfg = Some(format!("not({gated})"));
}
function
})
.collect()
}
fn collect_function_groups(functions: &[FunctionDef]) -> AHashMap<String, Vec<usize>> {
let mut name_to_indices: AHashMap<String, Vec<usize>> = AHashMap::new();
for (idx, func) in functions.iter().enumerate() {
name_to_indices.entry(func.name.clone()).or_default().push(idx);
}
name_to_indices
}
fn is_mixed_gated_group(indices: &[usize], functions: &[FunctionDef]) -> bool {
if indices.len() <= 1 {
return false;
}
let has_ungated = indices.iter().any(|&i| functions[i].cfg.is_none());
let has_gated = indices.iter().any(|&i| functions[i].cfg.is_some());
has_ungated && has_gated
}
fn merge_gated_cfgs<'a>(cfgs: impl Iterator<Item = Option<&'a str>>) -> Option<String> {
let mut distinct: Vec<(&'a str, CfgPredicate)> = Vec::new();
for cfg in cfgs.flatten() {
if distinct.iter().any(|(raw, _)| *raw == cfg) {
continue;
}
let parsed = parse_cfg_predicate(cfg);
if distinct.iter().any(|(_, existing)| predicate_covers(existing, &parsed)) {
continue;
}
distinct.retain(|(_, existing)| !predicate_covers(&parsed, existing));
distinct.push((cfg, parsed));
}
match distinct.len() {
0 => None,
1 => Some(distinct[0].0.to_string()),
_ => Some(format!(
"any({})",
distinct.iter().map(|(raw, _)| *raw).collect::<Vec<_>>().join(", ")
)),
}
}
fn cfg_disjuncts(predicate: &CfgPredicate) -> Vec<&CfgPredicate> {
match predicate {
CfgPredicate::Any(arms) => arms.iter().collect(),
other => vec![other],
}
}
fn predicate_covers(covering: &CfgPredicate, covered: &CfgPredicate) -> bool {
let covering_arms = cfg_disjuncts(covering);
cfg_disjuncts(covered).iter().all(|arm| covering_arms.contains(arm))
}
#[cfg(test)]
mod tests;