use crate::ast::stmt::{Block, ClosureBody, Expr, MatchArm, Stmt, StringPart, TypeExpr};
use super::context::RefinementContext;
use super::types::codegen_type_expr;
use logicaffeine_base::intern::{Interner, Symbol};
use std::collections::{HashMap, HashSet};
pub(super) fn map_rust_type(k: &str, v: &str, var: Symbol, ctx: &RefinementContext) -> String {
if k == "i64" && v == "i64" {
if let Some(kind) = ctx.dense_kind(var) {
return dense_type_name(kind).to_string();
}
if ctx.is_i32_set(var) {
return "LogosI32Set".to_string();
}
if ctx.is_i32_map(var) {
return "LogosI32Map".to_string();
}
if ctx.is_i64_set(var) {
return "LogosI64Set".to_string();
}
if ctx.is_i64_map(var) {
return "LogosI64Map".to_string();
}
}
format!("LogosMap<{}, {}>", k, v)
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum DenseKind {
Map,
MapNoPresence,
Set,
}
#[derive(Clone, Copy, Debug)]
pub struct DenseMapInfo {
pub lo: i64,
pub kind: DenseKind,
}
pub(super) fn dense_type_name(kind: DenseKind) -> &'static str {
match kind {
DenseKind::Set => "LogosDenseI64Set",
DenseKind::MapNoPresence => "LogosDenseI64MapNoPresence",
DenseKind::Map => "LogosDenseI64Map",
}
}
pub fn detect_i64_maps(stmts: &[Stmt], interner: &Interner) -> I64MapClasses {
let mut let_counts: HashMap<Symbol, usize> = HashMap::new();
let mut int_map_lets: HashSet<Symbol> = HashSet::new();
collect_candidates(stmts, interner, &mut let_counts, &mut int_map_lets);
let mut candidates: HashSet<Symbol> = int_map_lets
.into_iter()
.filter(|s| let_counts.get(s) == Some(&1))
.collect();
if candidates.is_empty() {
return I64MapClasses::default();
}
let mut walk = Disq {
candidates: &candidates,
disq: HashSet::new(),
inserted: HashSet::new(),
got: HashSet::new(),
has_contains: HashSet::new(),
key_sites: HashMap::new(),
value_sites: HashMap::new(),
};
walk.block(stmts);
let Disq { disq, inserted, got, has_contains, key_sites, value_sites, .. } = walk;
candidates.retain(|s| !disq.contains(s) && inserted.contains(s));
let sets: HashSet<Symbol> = candidates.iter().copied().filter(|s| !got.contains(s)).collect();
I64MapClasses { maps: candidates, sets, has_contains, key_sites, value_sites }
}
fn expr_addr(e: &Expr) -> usize {
e as *const Expr as usize
}
#[derive(Default)]
pub struct I64MapClasses {
pub maps: HashSet<Symbol>,
pub sets: HashSet<Symbol>,
pub has_contains: HashSet<Symbol>,
pub key_sites: HashMap<Symbol, Vec<usize>>,
pub value_sites: HashMap<Symbol, Vec<usize>>,
}
pub fn detect_dense_i64_maps(
classes: &I64MapClasses,
oracle: &crate::optimize::OracleFacts,
interner: &Interner,
) -> HashMap<Symbol, DenseMapInfo> {
let mut out = HashMap::new();
if !crate::optimize::active_config().is_on(crate::optimization::Opt::DenseMap) {
return out;
}
for &m in &classes.maps {
if !oracle.has_dense_map_capacity(m) {
continue;
}
if oracle
.map_cap_lin(m)
.and_then(|l| crate::optimize::lin_to_rust(l, interner))
.is_none()
{
continue;
}
let sites = match classes.key_sites.get(&m) {
Some(s) if !s.is_empty() => s,
_ => continue,
};
if sites.iter().all(|&addr| oracle.dense_map_key_proven_addr(addr, m)) {
let kind = if classes.sets.contains(&m) {
DenseKind::Set
} else if !classes.has_contains.contains(&m)
&& oracle.dense_map_has_full_coverage(m)
&& sites.iter().all(|&addr| oracle.dense_key_covered_addr(addr, m))
{
DenseKind::MapNoPresence
} else {
DenseKind::Map
};
out.insert(m, DenseMapInfo { lo: 0, kind });
crate::optimize::mark_fired(crate::optimization::Opt::DenseMap);
}
}
out
}
pub fn detect_i32_maps(
classes: &I64MapClasses,
dense: &HashMap<Symbol, DenseMapInfo>,
oracle: &crate::optimize::OracleFacts,
) -> (HashSet<Symbol>, HashSet<Symbol>) {
let mut maps = HashSet::new();
let mut sets = HashSet::new();
if !crate::optimize::active_config().is_on(crate::optimization::Opt::NarrowMap) {
return (maps, sets);
}
let fits_i32 = |addr: usize| match oracle.expr_int_range_addr(addr) {
Some((lo, hi)) => lo >= i32::MIN as i64 && hi <= i32::MAX as i64,
None => false,
};
for &m in &classes.maps {
if dense.contains_key(&m) {
crate::optimize::mark_preempted(
crate::optimization::Opt::DenseMap,
crate::optimization::Opt::NarrowMap,
);
continue;
}
let keys = match classes.key_sites.get(&m) {
Some(s) if !s.is_empty() => s,
_ => continue,
};
if !keys.iter().all(|&a| fits_i32(a)) {
continue;
}
if classes.sets.contains(&m) {
sets.insert(m);
} else {
let vals = classes.value_sites.get(&m).map(|v| v.as_slice()).unwrap_or(&[]);
if vals.iter().all(|&a| fits_i32(a)) {
maps.insert(m);
}
}
}
if !maps.is_empty() || !sets.is_empty() {
crate::optimize::mark_fired(crate::optimization::Opt::NarrowMap);
}
(maps, sets)
}
fn is_int_int_map(ty: Option<&TypeExpr>, value: &Expr, interner: &Interner) -> bool {
let both_i64 = |k: &TypeExpr, v: &TypeExpr| {
codegen_type_expr(k, interner) == "i64" && codegen_type_expr(v, interner) == "i64"
};
if let Some(TypeExpr::Generic { base, params }) = ty {
if matches!(interner.resolve(*base), "Map" | "HashMap") && params.len() >= 2 {
return both_i64(¶ms[0], ¶ms[1]);
}
}
match value {
Expr::New { type_name, type_args, .. } => {
matches!(interner.resolve(*type_name), "Map" | "HashMap")
&& type_args.len() >= 2
&& both_i64(&type_args[0], &type_args[1])
}
Expr::WithCapacity { value: inner, .. } => {
if let Expr::New { type_name, type_args, .. } = inner {
matches!(interner.resolve(*type_name), "Map" | "HashMap")
&& type_args.len() >= 2
&& both_i64(&type_args[0], &type_args[1])
} else {
false
}
}
_ => false,
}
}
fn collect_candidates(
stmts: &[Stmt],
interner: &Interner,
let_counts: &mut HashMap<Symbol, usize>,
int_map_lets: &mut HashSet<Symbol>,
) {
for stmt in stmts {
if let Stmt::Let { var, ty, value, .. } = stmt {
*let_counts.entry(*var).or_insert(0) += 1;
if is_int_int_map(*ty, value, interner) {
int_map_lets.insert(*var);
}
}
for block in child_blocks(stmt) {
collect_candidates(block, interner, let_counts, int_map_lets);
}
}
}
fn child_blocks<'a>(stmt: &Stmt<'a>) -> Vec<Block<'a>> {
match stmt {
Stmt::If { then_block, else_block, .. } => {
let mut v = vec![*then_block];
if let Some(eb) = else_block {
v.push(*eb);
}
v
}
Stmt::While { body, .. }
| Stmt::Repeat { body, .. }
| Stmt::Zone { body, .. } => vec![*body],
Stmt::Concurrent { tasks } | Stmt::Parallel { tasks } => vec![*tasks],
Stmt::Inspect { arms, .. } => arms.iter().map(|a: &MatchArm<'a>| a.body).collect(),
_ => Vec::new(),
}
}
struct Disq<'c> {
candidates: &'c HashSet<Symbol>,
disq: HashSet<Symbol>,
inserted: HashSet<Symbol>,
got: HashSet<Symbol>,
has_contains: HashSet<Symbol>,
key_sites: HashMap<Symbol, Vec<usize>>,
value_sites: HashMap<Symbol, Vec<usize>>,
}
impl<'c> Disq<'c> {
fn mark_disq(&mut self, sym: Symbol) {
if self.candidates.contains(&sym) {
self.disq.insert(sym);
}
}
fn disq_all(&mut self) {
for &s in self.candidates {
self.disq.insert(s);
}
}
fn coll(&mut self, e: &Expr) -> Option<Symbol> {
if let Expr::Identifier(s) = e {
if self.candidates.contains(s) {
return Some(*s);
}
}
self.expr(e);
None
}
fn block(&mut self, stmts: &[Stmt]) {
for s in stmts {
self.stmt(s);
}
}
fn stmt(&mut self, s: &Stmt) {
match s {
Stmt::Let { value, .. } => self.expr(value),
Stmt::Set { target, value } => {
self.mark_disq(*target);
self.expr(value);
}
Stmt::Call { args, .. } => {
for a in args {
self.expr(a);
}
}
Stmt::If { cond, then_block, else_block } => {
self.expr(cond);
self.block(then_block);
if let Some(eb) = else_block {
self.block(eb);
}
}
Stmt::While { cond, body, decreasing } => {
self.expr(cond);
self.block(body);
if let Some(d) = decreasing {
self.expr(d);
}
}
Stmt::Repeat { iterable, body, .. } => {
self.expr(iterable);
self.block(body);
}
Stmt::Return { value } => {
if let Some(v) = value {
self.expr(v);
}
}
Stmt::Break => {}
Stmt::Assert { .. } | Stmt::Trust { .. } => {}
Stmt::RuntimeAssert { condition, .. } => self.expr(condition),
Stmt::Give { object, recipient } => {
self.expr(object);
self.expr(recipient);
}
Stmt::Show { object, recipient } => {
self.expr(object);
self.expr(recipient);
}
Stmt::SetField { object, value, .. } => {
self.expr(object);
self.expr(value);
}
Stmt::StructDef { .. } => {}
Stmt::FunctionDef { .. } => {}
Stmt::Inspect { target, arms, .. } => {
self.expr(target);
for a in arms {
self.block(a.body);
}
}
Stmt::Push { value, collection } => {
self.expr(value);
self.expr(collection);
}
Stmt::Pop { collection, into } => {
self.expr(collection);
if let Some(i) = into {
self.mark_disq(*i);
}
}
Stmt::Add { value, collection } | Stmt::Remove { value, collection } => {
self.expr(value);
self.expr(collection);
}
Stmt::SetIndex { collection, index, value } => {
if let Some(m) = self.coll(collection) {
self.inserted.insert(m);
self.key_sites.entry(m).or_default().push(expr_addr(index));
self.value_sites.entry(m).or_default().push(expr_addr(value));
}
self.expr(index);
self.expr(value);
}
_ => self.disq_all(),
}
}
fn expr(&mut self, e: &Expr) {
match e {
Expr::Identifier(s) => self.mark_disq(*s),
Expr::Literal(_) | Expr::OptionNone | Expr::Escape { .. } => {}
Expr::Index { collection, index } => {
if let Some(m) = self.coll(collection) {
self.got.insert(m);
self.key_sites.entry(m).or_default().push(expr_addr(index));
}
self.expr(index);
}
Expr::Contains { collection, value } => {
if let Some(m) = self.coll(collection) {
self.has_contains.insert(m);
self.key_sites.entry(m).or_default().push(expr_addr(value));
}
self.expr(value);
}
Expr::BinaryOp { left, right, .. }
| Expr::Union { left, right }
| Expr::Intersection { left, right }
| Expr::Range { start: left, end: right } => {
self.expr(left);
self.expr(right);
}
Expr::Not { operand } => self.expr(operand),
Expr::Call { args, .. } => {
for a in args {
self.expr(a);
}
}
Expr::CallExpr { callee, args } => {
self.expr(callee);
for a in args {
self.expr(a);
}
}
Expr::Slice { collection, start, end } => {
self.expr(collection);
self.expr(start);
self.expr(end);
}
Expr::Copy { expr } => self.expr(expr),
Expr::Give { value } => self.expr(value),
Expr::Length { collection } => self.expr(collection),
Expr::ManifestOf { zone } => self.expr(zone),
Expr::ChunkAt { index, zone } => {
self.expr(index);
self.expr(zone);
}
Expr::List(items) | Expr::Tuple(items) => {
for i in items {
self.expr(i);
}
}
Expr::FieldAccess { object, .. } => self.expr(object),
Expr::New { init_fields, .. } => {
for (_, v) in init_fields {
self.expr(v);
}
}
Expr::NewVariant { fields, .. } => {
for (_, v) in fields {
self.expr(v);
}
}
Expr::OptionSome { value } => self.expr(value),
Expr::WithCapacity { value, capacity } => {
self.expr(value);
self.expr(capacity);
}
Expr::Closure { body, .. } => match body {
ClosureBody::Expression(e) => self.expr(e),
ClosureBody::Block(b) => self.block(b),
},
Expr::InterpolatedString(parts) => {
for p in parts {
if let StringPart::Expr { value, .. } = p {
self.expr(value);
}
}
}
}
}
}
pub(super) fn is_logos_map_type(t: &str) -> bool {
t.starts_with("LogosMap")
|| t.starts_with("LogosI64Map")
|| t.starts_with("LogosI64Set")
|| t.starts_with("LogosI32Map")
|| t.starts_with("LogosI32Set")
|| t.starts_with("LogosDenseI64Map")
|| t.starts_with("LogosDenseI64Set")
}