Skip to main content

oxilean_codegen/opt_alias/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::functions::*;
6use crate::lcnf::{LcnfArg, LcnfExpr, LcnfFunDecl, LcnfLetValue, LcnfType, LcnfVarId};
7use std::collections::{HashMap, HashSet, VecDeque};
8
9/// Global alias summary for a function
10#[allow(dead_code)]
11#[derive(Debug, Clone)]
12pub struct FuncAliasSummary {
13    pub func_name: String,
14    pub mem_effect: MemEffect,
15    pub modifies: Vec<u32>,
16    pub reads: Vec<u32>,
17    pub return_aliases: Vec<u32>,
18}
19/// Whole-program alias summary
20#[allow(dead_code)]
21#[derive(Debug, Default)]
22pub struct WholeProgramAlias {
23    pub func_summaries: std::collections::HashMap<String, FuncAliasSummary>,
24    pub global_points_to: std::collections::HashMap<u32, PointsToSet>,
25}
26#[allow(dead_code)]
27impl WholeProgramAlias {
28    pub fn new() -> Self {
29        Self::default()
30    }
31    pub fn add_func_summary(&mut self, summary: FuncAliasSummary) {
32        self.func_summaries
33            .insert(summary.func_name.clone(), summary);
34    }
35    pub fn get_func_summary(&self, func: &str) -> Option<&FuncAliasSummary> {
36        self.func_summaries.get(func)
37    }
38    pub fn add_global_points_to(&mut self, var: u32, pts: PointsToSet) {
39        self.global_points_to
40            .entry(var)
41            .or_default()
42            .union_with(&pts);
43    }
44    pub fn func_count(&self) -> usize {
45        self.func_summaries.len()
46    }
47}
48/// Which level / flavor of alias analysis to run.
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
50pub enum AliasAnalysisLevel {
51    /// Disable alias analysis (assume everything MayAlias).
52    NoAlias,
53    /// Basic alias analysis: trivial same-variable queries only.
54    BasicAA,
55    /// Type-based alias analysis (TBAA): use LCNF types to rule out aliases.
56    TypeBasedAA,
57    /// Scoped no-alias: use structural scoping to prove NoAlias.
58    ScopedNoAliasAA,
59    /// Global variables analysis: treat globals as separate alias class.
60    GlobalsAA,
61    /// CFL Andersen: context-free-language reachability, inclusion-based.
62    #[default]
63    CFLAndersen,
64    /// CFL Steensgaard: context-free-language reachability, unification-based.
65    CFLSteensgaard,
66}
67impl AliasAnalysisLevel {
68    /// A human-readable description of this level.
69    pub fn description(&self) -> &'static str {
70        match self {
71            AliasAnalysisLevel::NoAlias => "No alias analysis (conservative MayAlias)",
72            AliasAnalysisLevel::BasicAA => "Basic AA (identity only)",
73            AliasAnalysisLevel::TypeBasedAA => "Type-based AA (TBAA)",
74            AliasAnalysisLevel::ScopedNoAliasAA => "Scoped no-alias AA",
75            AliasAnalysisLevel::GlobalsAA => "Globals AA",
76            AliasAnalysisLevel::CFLAndersen => "CFL Andersen (inclusion-based)",
77            AliasAnalysisLevel::CFLSteensgaard => "CFL Steensgaard (unification-based)",
78        }
79    }
80    /// Returns `true` if this level uses points-to analysis.
81    pub fn uses_points_to(&self) -> bool {
82        matches!(
83            self,
84            AliasAnalysisLevel::CFLAndersen | AliasAnalysisLevel::CFLSteensgaard
85        )
86    }
87}
88/// Memory effect annotation
89#[allow(dead_code)]
90#[derive(Debug, Clone, PartialEq, Eq)]
91pub enum MemEffect {
92    ReadNone,
93    ReadOnly,
94    WriteOnly,
95    ReadWrite,
96    ArgMemOnly,
97    InaccessibleMemOnly,
98}
99/// A node in the points-to graph representing an allocation site or variable.
100#[derive(Debug, Clone, PartialEq, Eq, Hash)]
101pub struct PointsToNode {
102    /// The variable ID this node represents.
103    pub var: LcnfVarId,
104    /// A human-readable label for debugging.
105    pub label: String,
106    /// The kind of node.
107    pub kind: NodeKind,
108}
109impl PointsToNode {
110    /// Create a new local node.
111    pub fn local(var: LcnfVarId, label: impl Into<String>) -> Self {
112        PointsToNode {
113            var,
114            label: label.into(),
115            kind: NodeKind::Local,
116        }
117    }
118    /// Create a new heap node.
119    pub fn heap(var: LcnfVarId, label: impl Into<String>) -> Self {
120        PointsToNode {
121            var,
122            label: label.into(),
123            kind: NodeKind::Heap,
124        }
125    }
126    /// Create a new parameter node.
127    pub fn parameter(var: LcnfVarId, label: impl Into<String>) -> Self {
128        PointsToNode {
129            var,
130            label: label.into(),
131            kind: NodeKind::Parameter,
132        }
133    }
134    /// Returns `true` if this node is a heap allocation site.
135    pub fn is_heap(&self) -> bool {
136        matches!(self.kind, NodeKind::Heap)
137    }
138    /// Returns `true` if this node is a local/stack variable.
139    pub fn is_local(&self) -> bool {
140        matches!(self.kind, NodeKind::Local)
141    }
142}
143/// Alias analysis pass summary
144#[allow(dead_code)]
145#[derive(Debug, Clone)]
146pub struct AliasPassSummary {
147    pub pass_name: String,
148    pub functions_analyzed: usize,
149    pub queries_answered: usize,
150    pub must_alias_rate: f64,
151    pub no_alias_rate: f64,
152    pub duration_us: u64,
153}
154/// Alias query batch
155#[allow(dead_code)]
156#[derive(Debug, Default)]
157pub struct AliasQueryBatch {
158    pub queries: Vec<(u32, u32)>,
159    pub results: Vec<AliasResultExt>,
160}
161#[allow(dead_code)]
162impl AliasQueryBatch {
163    pub fn new() -> Self {
164        Self::default()
165    }
166    pub fn add(&mut self, a: u32, b: u32) {
167        self.queries.push((a, b));
168    }
169    pub fn record_result(&mut self, r: AliasResultExt) {
170        self.results.push(r);
171    }
172    pub fn must_alias_count(&self) -> usize {
173        self.results
174            .iter()
175            .filter(|r| **r == AliasResultExt::MustAlias)
176            .count()
177    }
178    pub fn no_alias_count(&self) -> usize {
179        self.results
180            .iter()
181            .filter(|r| **r == AliasResultExt::NoAlias)
182            .count()
183    }
184}
185/// TBAA tree (type hierarchy for alias analysis)
186#[allow(dead_code)]
187#[derive(Debug, Default)]
188pub struct TbaaTree {
189    pub nodes: std::collections::HashMap<String, TbaaTypeNode>,
190}
191#[allow(dead_code)]
192impl TbaaTree {
193    pub fn new() -> Self {
194        Self::default()
195    }
196    pub fn add_root(&mut self, name: &str) {
197        self.nodes.insert(
198            name.to_string(),
199            TbaaTypeNode {
200                name: name.to_string(),
201                parent: None,
202                is_const: false,
203            },
204        );
205    }
206    pub fn add_child(&mut self, name: &str, parent: &str, is_const: bool) {
207        self.nodes.insert(
208            name.to_string(),
209            TbaaTypeNode {
210                name: name.to_string(),
211                parent: Some(parent.to_string()),
212                is_const,
213            },
214        );
215    }
216    pub fn is_ancestor(&self, potential_ancestor: &str, node: &str) -> bool {
217        let mut current = node;
218        loop {
219            if current == potential_ancestor {
220                return true;
221            }
222            match self.nodes.get(current) {
223                Some(n) => {
224                    if let Some(p) = &n.parent {
225                        current = p.as_str();
226                    } else {
227                        return false;
228                    }
229                }
230                None => return false,
231            }
232        }
233    }
234    pub fn may_alias_tbaa(&self, t1: &str, t2: &str) -> bool {
235        if t1 == t2 {
236            return true;
237        }
238        self.is_ancestor(t1, t2) || self.is_ancestor(t2, t1)
239    }
240}
241/// Inclusion-based (Andersen) points-to solver.
242///
243/// Processes constraints until a fixed point is reached.
244/// Optionally field-sensitive.
245#[derive(Debug, Clone, Default)]
246pub struct AndersenSolver {
247    /// Accumulated constraints.
248    pub constraints: Vec<AndersenConstraint>,
249    /// Current points-to graph.
250    pub graph: PointsToGraph,
251    /// Whether to use field-sensitive analysis.
252    pub field_sensitive: bool,
253    /// Number of solver iterations performed.
254    pub iterations: u32,
255}
256impl AndersenSolver {
257    /// Create a new solver.
258    pub fn new() -> Self {
259        AndersenSolver::default()
260    }
261    /// Create a field-sensitive solver.
262    pub fn field_sensitive() -> Self {
263        AndersenSolver {
264            field_sensitive: true,
265            ..AndersenSolver::default()
266        }
267    }
268    /// Add an AddressOf constraint: `lhs ⊇ {&addr_of}`.
269    pub fn add_address_of(&mut self, lhs: LcnfVarId, addr_of: LcnfVarId) {
270        self.constraints
271            .push(AndersenConstraint::AddressOf { lhs, addr_of });
272    }
273    /// Add a Copy constraint: `pts(lhs) ⊇ pts(rhs)`.
274    pub fn add_copy(&mut self, lhs: LcnfVarId, rhs: LcnfVarId) {
275        self.constraints.push(AndersenConstraint::Copy { lhs, rhs });
276    }
277    /// Add a Load constraint: `lhs = *ptr`.
278    pub fn add_load(&mut self, lhs: LcnfVarId, ptr: LcnfVarId) {
279        self.constraints.push(AndersenConstraint::Load { lhs, ptr });
280    }
281    /// Add a Store constraint: `*ptr = rhs`.
282    pub fn add_store(&mut self, ptr: LcnfVarId, rhs: LcnfVarId) {
283        self.constraints
284            .push(AndersenConstraint::Store { ptr, rhs });
285    }
286    /// Register a variable in the underlying points-to graph.
287    pub fn register_var(&mut self, node: PointsToNode) {
288        self.graph.add_node(node);
289    }
290    /// Solve constraints to a fixed point.
291    ///
292    /// Uses a worklist algorithm for efficiency.
293    pub fn solve(&mut self) {
294        let mut changed = true;
295        while changed {
296            changed = false;
297            self.iterations += 1;
298            for constraint in self.constraints.clone() {
299                match constraint {
300                    AndersenConstraint::AddressOf { lhs, addr_of } => {
301                        let pts = self.graph.pts.entry(lhs).or_default();
302                        changed |= pts.insert(addr_of);
303                    }
304                    AndersenConstraint::Copy { lhs, rhs } => {
305                        let pts_rhs: HashSet<LcnfVarId> = self.graph.pts_of(rhs).clone();
306                        let pts_lhs = self.graph.pts.entry(lhs).or_default();
307                        for v in &pts_rhs {
308                            changed |= pts_lhs.insert(*v);
309                        }
310                    }
311                    AndersenConstraint::Load { lhs, ptr } => {
312                        let ptrs: Vec<LcnfVarId> =
313                            self.graph.pts_of(ptr).clone().into_iter().collect();
314                        for p in ptrs {
315                            let pts_p: HashSet<LcnfVarId> = self.graph.pts_of(p).clone();
316                            let pts_lhs = self.graph.pts.entry(lhs).or_default();
317                            for v in &pts_p {
318                                changed |= pts_lhs.insert(*v);
319                            }
320                        }
321                    }
322                    AndersenConstraint::Store { ptr, rhs } => {
323                        let ptrs: Vec<LcnfVarId> =
324                            self.graph.pts_of(ptr).clone().into_iter().collect();
325                        let pts_rhs: HashSet<LcnfVarId> = self.graph.pts_of(rhs).clone();
326                        for p in ptrs {
327                            let pts_p = self.graph.pts.entry(p).or_default();
328                            for v in &pts_rhs {
329                                changed |= pts_p.insert(*v);
330                            }
331                        }
332                    }
333                }
334            }
335        }
336    }
337    /// Query alias relationship between two variables after solving.
338    pub fn query(&self, a: LcnfVarId, b: LcnfVarId) -> AliasResult {
339        if a == b {
340            return AliasResult::MustAlias;
341        }
342        let pa = self.graph.pts_of(a);
343        let pb = self.graph.pts_of(b);
344        if pa.is_empty() || pb.is_empty() {
345            return AliasResult::NoAlias;
346        }
347        let overlap: bool = pa.iter().any(|x| pb.contains(x));
348        if overlap {
349            if pa.len() == 1 && pb.len() == 1 {
350                AliasResult::MustAlias
351            } else {
352                AliasResult::MayAlias
353            }
354        } else {
355            AliasResult::NoAlias
356        }
357    }
358}
359/// Steensgaard-style union-find for alias analysis
360#[allow(dead_code)]
361#[derive(Debug, Default)]
362pub struct SteensgaardUnionFind {
363    pub parent: Vec<u32>,
364    pub rank: Vec<u32>,
365    pub points_to: Vec<Option<u32>>,
366}
367#[allow(dead_code)]
368impl SteensgaardUnionFind {
369    pub fn new(size: usize) -> Self {
370        Self {
371            parent: (0..size as u32).collect(),
372            rank: vec![0; size],
373            points_to: vec![None; size],
374        }
375    }
376    pub fn find(&mut self, x: u32) -> u32 {
377        if self.parent[x as usize] != x {
378            let root = self.find(self.parent[x as usize]);
379            self.parent[x as usize] = root;
380        }
381        self.parent[x as usize]
382    }
383    pub fn union(&mut self, x: u32, y: u32) -> u32 {
384        let rx = self.find(x);
385        let ry = self.find(y);
386        if rx == ry {
387            return rx;
388        }
389        if self.rank[rx as usize] < self.rank[ry as usize] {
390            self.parent[rx as usize] = ry;
391            ry
392        } else if self.rank[rx as usize] > self.rank[ry as usize] {
393            self.parent[ry as usize] = rx;
394            rx
395        } else {
396            self.parent[ry as usize] = rx;
397            self.rank[rx as usize] += 1;
398            rx
399        }
400    }
401    pub fn same_class(&mut self, x: u32, y: u32) -> bool {
402        self.find(x) == self.find(y)
403    }
404}
405/// Alias analysis source buffer
406#[allow(dead_code)]
407#[derive(Debug, Default)]
408pub struct AliasExtSourceBuffer {
409    pub content: String,
410}
411#[allow(dead_code)]
412impl AliasExtSourceBuffer {
413    pub fn new() -> Self {
414        Self::default()
415    }
416    pub fn write(&mut self, s: &str) {
417        self.content.push_str(s);
418    }
419    pub fn writeln(&mut self, s: &str) {
420        self.content.push_str(s);
421        self.content.push('\n');
422    }
423    pub fn finish(self) -> String {
424        self.content
425    }
426}
427/// Alias cache (for repeated queries)
428#[allow(dead_code)]
429#[derive(Debug, Default)]
430pub struct AliasCache {
431    pub cache: std::collections::HashMap<(u32, u32), AliasResultExt>,
432    pub hits: u64,
433    pub misses: u64,
434}
435#[allow(dead_code)]
436impl AliasCache {
437    pub fn new() -> Self {
438        Self::default()
439    }
440    pub fn get(&mut self, a: u32, b: u32) -> Option<AliasResultExt> {
441        let key = if a <= b { (a, b) } else { (b, a) };
442        if let Some(r) = self.cache.get(&key) {
443            self.hits += 1;
444            Some(r.clone())
445        } else {
446            self.misses += 1;
447            None
448        }
449    }
450    pub fn insert(&mut self, a: u32, b: u32, result: AliasResultExt) {
451        let key = if a <= b { (a, b) } else { (b, a) };
452        self.cache.insert(key, result);
453    }
454    pub fn hit_rate(&self) -> f64 {
455        let total = self.hits + self.misses;
456        if total == 0 {
457            0.0
458        } else {
459            self.hits as f64 / total as f64
460        }
461    }
462    pub fn invalidate(&mut self) {
463        self.cache.clear();
464    }
465}
466/// Heap allocation summary
467#[allow(dead_code)]
468#[derive(Debug, Clone)]
469pub struct HeapAllocSummary {
470    pub alloc_id: u32,
471    pub site: String,
472    pub may_escape: bool,
473    pub is_unique: bool,
474    pub estimated_size: Option<u64>,
475}
476/// Alias analysis inline hint
477#[allow(dead_code)]
478#[derive(Debug, Clone)]
479pub struct AliasInlineHint {
480    pub call_site: u32,
481    pub callee: String,
482    pub benefit: f64,
483    pub cost: f64,
484}
485impl AliasInlineHint {
486    #[allow(dead_code)]
487    pub fn should_inline(&self, threshold: f64) -> bool {
488        self.benefit / self.cost.max(1.0) >= threshold
489    }
490}
491/// The result of querying whether two values may alias.
492#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
493pub enum AliasResult {
494    /// The two values definitely refer to the same memory location.
495    MustAlias,
496    /// The two values may or may not refer to the same location.
497    MayAlias,
498    /// The two values definitely do not refer to the same location.
499    NoAlias,
500}
501impl AliasResult {
502    /// Returns `true` if aliasing is possible (MustAlias or MayAlias).
503    pub fn may_alias(self) -> bool {
504        matches!(self, AliasResult::MustAlias | AliasResult::MayAlias)
505    }
506    /// Returns `true` if aliasing is certain.
507    pub fn must_alias(self) -> bool {
508        matches!(self, AliasResult::MustAlias)
509    }
510    /// Returns `true` if no alias is possible.
511    pub fn no_alias(self) -> bool {
512        matches!(self, AliasResult::NoAlias)
513    }
514    /// Merge two alias results conservatively.
515    pub fn merge(self, other: AliasResult) -> AliasResult {
516        match (self, other) {
517            (AliasResult::MustAlias, AliasResult::MustAlias) => AliasResult::MustAlias,
518            (AliasResult::NoAlias, AliasResult::NoAlias) => AliasResult::NoAlias,
519            _ => AliasResult::MayAlias,
520        }
521    }
522}
523/// Value numbering for alias analysis
524#[allow(dead_code)]
525#[derive(Debug, Default)]
526pub struct AliasValueNumbering {
527    pub map: std::collections::HashMap<String, u32>,
528    pub next: u32,
529}
530#[allow(dead_code)]
531impl AliasValueNumbering {
532    pub fn new() -> Self {
533        Self::default()
534    }
535    pub fn get_or_insert(&mut self, key: &str) -> u32 {
536        if let Some(&n) = self.map.get(key) {
537            n
538        } else {
539            let n = self.next;
540            self.next += 1;
541            self.map.insert(key.to_string(), n);
542            n
543        }
544    }
545    pub fn lookup(&self, key: &str) -> Option<u32> {
546        self.map.get(key).copied()
547    }
548}
549/// CFL-Reachability alias analysis
550#[allow(dead_code)]
551#[derive(Debug, Default)]
552pub struct CflReachabilityAnalysis {
553    pub graph: Vec<Vec<(u32, String)>>,
554    pub num_nodes: usize,
555}
556#[allow(dead_code)]
557impl CflReachabilityAnalysis {
558    pub fn new(n: usize) -> Self {
559        Self {
560            graph: vec![Vec::new(); n],
561            num_nodes: n,
562        }
563    }
564    pub fn add_edge(&mut self, from: u32, to: u32, label: &str) {
565        if (from as usize) < self.num_nodes {
566            self.graph[from as usize].push((to, label.to_string()));
567        }
568    }
569    pub fn reachable(&self, src: u32, dst: u32) -> bool {
570        if src == dst {
571            return true;
572        }
573        let mut visited = vec![false; self.num_nodes];
574        let mut stack = vec![src];
575        while let Some(n) = stack.pop() {
576            if n == dst {
577                return true;
578            }
579            if n as usize >= self.num_nodes {
580                continue;
581            }
582            if visited[n as usize] {
583                continue;
584            }
585            visited[n as usize] = true;
586            for (next, _) in &self.graph[n as usize] {
587                stack.push(*next);
588            }
589        }
590        false
591    }
592}
593/// Alias analysis code stats
594#[allow(dead_code)]
595#[derive(Debug, Default, Clone)]
596pub struct AliasCodeStats {
597    pub functions_analyzed: usize,
598    pub constraints_solved: usize,
599    pub must_aliases_found: usize,
600    pub no_aliases_found: usize,
601    pub escape_candidates: usize,
602    pub stack_promotions: usize,
603}
604/// Type-based alias analysis (TBAA) type descriptor
605#[allow(dead_code)]
606#[derive(Debug, Clone, PartialEq, Eq, Hash)]
607pub struct TbaaTypeNode {
608    pub name: String,
609    pub parent: Option<String>,
610    pub is_const: bool,
611}
612/// Alias analysis optimizer state
613#[allow(dead_code)]
614#[derive(Debug, Default)]
615pub struct AliasOptimizerState {
616    pub pipeline: AliasAnalysisPipeline,
617    pub escape: EscapeAnalysis,
618    pub hints: AliasHintCollector,
619    pub refmod: RefModTable,
620    pub stats: AliasCodeStats,
621}
622#[allow(dead_code)]
623impl AliasOptimizerState {
624    pub fn new() -> Self {
625        Self::default()
626    }
627    pub fn query(&mut self, a: u32, b: u32) -> AliasResultExt {
628        self.pipeline.query(a, b)
629    }
630    pub fn report(&self) -> String {
631        format!("{}", self.stats)
632    }
633}
634/// Alias query result (extended)
635#[allow(dead_code)]
636#[derive(Debug, Clone, PartialEq, Eq)]
637pub enum AliasResultExt {
638    NoAlias,
639    MayAlias,
640    PartialAlias,
641    MustAlias,
642}
643#[allow(dead_code)]
644#[derive(Debug, Clone, PartialEq, Eq, Hash)]
645pub enum MemSsaKind {
646    MemDef,
647    MemPhi,
648    MemUse,
649    LiveOnEntry,
650}
651/// Alias analysis feature flags
652#[allow(dead_code)]
653#[derive(Debug, Clone, Default)]
654pub struct AliasFeatureFlags {
655    pub enable_tbaa: bool,
656    pub enable_field_sensitivity: bool,
657    pub enable_flow_sensitivity: bool,
658    pub enable_context_sensitivity: bool,
659    pub enable_escape_analysis: bool,
660    pub enable_cfl_reachability: bool,
661}
662/// Memory SSA (a simplified model for alias analysis)
663#[allow(dead_code)]
664#[derive(Debug, Clone, PartialEq, Eq, Hash)]
665pub struct MemSsaDef {
666    pub id: u32,
667    pub version: u32,
668    pub kind: MemSsaKind,
669}
670/// Tracks in-flight loads for forwarding.
671#[derive(Debug, Clone, Default)]
672pub struct LoadStoreForwarder {
673    /// Maps (base_var, offset) → last-written value variable.
674    pub store_cache: HashMap<(LcnfVarId, Option<i64>), LcnfVarId>,
675    /// Number of forwarding substitutions performed.
676    pub forwards_applied: usize,
677}
678impl LoadStoreForwarder {
679    /// Create a new forwarder.
680    pub fn new() -> Self {
681        LoadStoreForwarder::default()
682    }
683    /// Record a store: `*base[offset] = val`.
684    pub fn record_store(&mut self, base: LcnfVarId, offset: Option<i64>, val: LcnfVarId) {
685        self.store_cache.insert((base, offset), val);
686    }
687    /// Attempt to forward a load from `base[offset]`.
688    ///
689    /// Returns the variable holding the cached value if available.
690    pub fn forward_load(&self, base: LcnfVarId, offset: Option<i64>) -> Option<LcnfVarId> {
691        self.store_cache.get(&(base, offset)).copied()
692    }
693    /// Invalidate all cache entries that may alias `base`.
694    pub fn invalidate(&mut self, base: LcnfVarId) {
695        self.store_cache.retain(|(b, _), _| *b != base);
696    }
697    /// Clear the entire cache (conservative: on unknown calls).
698    pub fn clear(&mut self) {
699        self.store_cache.clear();
700    }
701}
702/// Abstract memory location for alias analysis
703#[allow(dead_code)]
704#[derive(Debug, Clone, PartialEq, Eq, Hash)]
705pub enum MemLocation {
706    Stack(u32),
707    Heap(u32),
708    Global(String),
709    Field(Box<MemLocation>, String),
710    Index(Box<MemLocation>, u32),
711    Unknown,
712}
713/// Mod-Ref analysis result
714#[allow(dead_code)]
715#[derive(Debug, Clone, PartialEq, Eq)]
716pub enum ModRefResult {
717    NoModRef,
718    Ref,
719    Mod,
720    ModRef,
721}
722/// Describes a single memory access for alias comparison.
723#[derive(Debug, Clone)]
724pub struct MemoryAccessInfo {
725    /// The base pointer variable being accessed.
726    pub base: LcnfVarId,
727    /// Byte offset from the base pointer (None = unknown).
728    pub offset: Option<i64>,
729    /// Size of the access in bytes (None = unknown).
730    pub size: Option<u64>,
731    /// Whether this is a volatile access.
732    pub is_volatile: bool,
733    /// The LCNF type of the accessed value.
734    pub access_type: LcnfType,
735    /// Whether this is a read or a write.
736    pub is_write: bool,
737}
738impl MemoryAccessInfo {
739    /// Create a simple read access with unknown offset/size.
740    pub fn read(base: LcnfVarId, ty: LcnfType) -> Self {
741        MemoryAccessInfo {
742            base,
743            offset: None,
744            size: None,
745            is_volatile: false,
746            access_type: ty,
747            is_write: false,
748        }
749    }
750    /// Create a simple write access with unknown offset/size.
751    pub fn write(base: LcnfVarId, ty: LcnfType) -> Self {
752        MemoryAccessInfo {
753            base,
754            offset: None,
755            size: None,
756            is_volatile: false,
757            access_type: ty,
758            is_write: true,
759        }
760    }
761    /// Returns `true` if offsets are known and do not overlap.
762    pub fn definitely_disjoint(&self, other: &MemoryAccessInfo) -> bool {
763        match (self.offset, self.size, other.offset, other.size) {
764            (Some(o1), Some(s1), Some(o2), Some(s2)) => {
765                let end1 = o1 + s1 as i64;
766                let end2 = o2 + s2 as i64;
767                end1 <= o2 || end2 <= o1
768            }
769            _ => false,
770        }
771    }
772}
773/// Andersen constraint solver
774#[allow(dead_code)]
775#[derive(Debug, Default)]
776pub struct AndersenSolver2 {
777    pub constraints: Vec<AndersenConstraint2>,
778    pub points_to: std::collections::HashMap<u32, PointsToSet>,
779    pub worklist: Vec<u32>,
780}
781#[allow(dead_code)]
782impl AndersenSolver2 {
783    pub fn new() -> Self {
784        Self::default()
785    }
786    pub fn add_constraint(&mut self, c: AndersenConstraint2) {
787        self.constraints.push(c);
788    }
789    pub fn get_points_to(&self, var: u32) -> PointsToSet {
790        self.points_to.get(&var).cloned().unwrap_or_default()
791    }
792    pub fn add_to_points_to(&mut self, var: u32, loc: MemLocation) -> bool {
793        let set = self.points_to.entry(var).or_default();
794        if set.locations.contains(&loc) {
795            false
796        } else {
797            set.locations.insert(loc);
798            true
799        }
800    }
801    pub fn solve(&mut self) {
802        let addrs: Vec<_> = self
803            .constraints
804            .iter()
805            .filter_map(|c| {
806                if let AndersenConstraint2::AddressOf { dest, src } = c {
807                    Some((*dest, src.clone()))
808                } else {
809                    None
810                }
811            })
812            .collect();
813        for (dest, src) in addrs {
814            self.add_to_points_to(dest, src);
815            self.worklist.push(dest);
816        }
817        let copies: Vec<_> = self
818            .constraints
819            .iter()
820            .filter_map(|c| {
821                if let AndersenConstraint2::Copy { dest, src } = c {
822                    Some((*dest, *src))
823                } else {
824                    None
825                }
826            })
827            .collect();
828        for _ in 0..10 {
829            for (dest, src) in &copies {
830                let src_set = self.get_points_to(*src);
831                let locs: Vec<_> = src_set.locations.into_iter().collect();
832                for loc in locs {
833                    self.add_to_points_to(*dest, loc);
834                }
835            }
836        }
837    }
838}
839/// A constraint in Andersen-style points-to analysis.
840#[derive(Debug, Clone, PartialEq, Eq, Hash)]
841pub enum AndersenConstraint {
842    /// AddressOf: `x` contains the address of `y` (x ⊇ {&y}).
843    AddressOf { lhs: LcnfVarId, addr_of: LcnfVarId },
844    /// Copy: `x = y` — pts(x) ⊇ pts(y).
845    Copy { lhs: LcnfVarId, rhs: LcnfVarId },
846    /// Load: `x = *y` — for each p ∈ pts(y): pts(x) ⊇ pts(p).
847    Load { lhs: LcnfVarId, ptr: LcnfVarId },
848    /// Store: `*x = y` — for each p ∈ pts(x): pts(p) ⊇ pts(y).
849    Store { ptr: LcnfVarId, rhs: LcnfVarId },
850}
851/// Alias analysis emit stats
852#[allow(dead_code)]
853#[derive(Debug, Default, Clone)]
854pub struct AliasExtEmitStats {
855    pub bytes_written: usize,
856    pub items_emitted: usize,
857    pub errors: usize,
858    pub warnings: usize,
859}
860/// Alias analysis statistics (extended)
861#[allow(dead_code)]
862#[derive(Debug, Default, Clone)]
863pub struct AliasStatsExt {
864    pub queries_total: usize,
865    pub must_alias_count: usize,
866    pub no_alias_count: usize,
867    pub may_alias_count: usize,
868    pub partial_alias_count: usize,
869    pub constraints_generated: usize,
870    pub constraint_solving_iters: usize,
871    pub points_to_sets_computed: usize,
872}
873/// Alias analysis version info
874#[allow(dead_code)]
875#[derive(Debug, Clone)]
876pub struct AliasVersionInfo {
877    pub pass_version: u32,
878    pub supports_tbaa: bool,
879    pub supports_field_sensitivity: bool,
880    pub supports_context_sensitivity: bool,
881    pub default_level: String,
882}
883/// Ref/Mod table (per instruction)
884#[allow(dead_code)]
885#[derive(Debug, Default)]
886pub struct RefModTable {
887    pub entries: std::collections::HashMap<u32, ModRefResult>,
888}
889#[allow(dead_code)]
890impl RefModTable {
891    pub fn new() -> Self {
892        Self::default()
893    }
894    pub fn set(&mut self, inst: u32, mr: ModRefResult) {
895        self.entries.insert(inst, mr);
896    }
897    pub fn get(&self, inst: u32) -> &ModRefResult {
898        self.entries.get(&inst).unwrap_or(&ModRefResult::NoModRef)
899    }
900    pub fn reads_count(&self) -> usize {
901        self.entries
902            .values()
903            .filter(|m| matches!(m, ModRefResult::Ref | ModRefResult::ModRef))
904            .count()
905    }
906    pub fn writes_count(&self) -> usize {
907        self.entries
908            .values()
909            .filter(|m| matches!(m, ModRefResult::Mod | ModRefResult::ModRef))
910            .count()
911    }
912}
913/// Alias-aware code motion hint
914#[allow(dead_code)]
915#[derive(Debug, Clone)]
916pub struct AliasCodeMotionHint {
917    pub inst_id: u32,
918    pub target_block: u32,
919    pub alias_safe: bool,
920    pub estimated_savings: i32,
921}
922/// Andersen-style points-to constraint
923#[allow(dead_code)]
924#[derive(Debug, Clone)]
925pub enum AndersenConstraint2 {
926    AddressOf { dest: u32, src: MemLocation },
927    Copy { dest: u32, src: u32 },
928    Load { dest: u32, ptr: u32 },
929    Store { ptr: u32, src: u32 },
930    Call { ret: u32, func: u32, args: Vec<u32> },
931}
932/// Escape analysis result
933#[allow(dead_code)]
934#[derive(Debug, Default)]
935pub struct EscapeAnalysis {
936    pub allocs: Vec<HeapAllocSummary>,
937    pub escaping: std::collections::HashSet<u32>,
938}
939#[allow(dead_code)]
940impl EscapeAnalysis {
941    pub fn new() -> Self {
942        Self::default()
943    }
944    pub fn add_alloc(&mut self, summary: HeapAllocSummary) {
945        if summary.may_escape {
946            self.escaping.insert(summary.alloc_id);
947        }
948        self.allocs.push(summary);
949    }
950    pub fn does_escape(&self, alloc_id: u32) -> bool {
951        self.escaping.contains(&alloc_id)
952    }
953    pub fn non_escaping_count(&self) -> usize {
954        self.allocs.iter().filter(|a| !a.may_escape).count()
955    }
956}
957/// A points-to graph mapping each variable to the set of nodes it may point to.
958///
959/// This implements a simplified Andersen / Steensgaard style analysis.
960#[derive(Debug, Clone, Default)]
961pub struct PointsToGraph {
962    /// All known nodes in the graph.
963    pub nodes: HashMap<LcnfVarId, PointsToNode>,
964    /// `pts[x]` = set of node IDs that variable `x` may point to.
965    pub pts: HashMap<LcnfVarId, HashSet<LcnfVarId>>,
966    /// Whether this graph was built with Steensgaard (union-find) or Andersen style.
967    pub is_steensgaard: bool,
968}
969impl PointsToGraph {
970    /// Create a new empty points-to graph.
971    pub fn new() -> Self {
972        PointsToGraph::default()
973    }
974    /// Register a node in the graph.
975    pub fn add_node(&mut self, node: PointsToNode) {
976        let id = node.var;
977        self.nodes.insert(id, node);
978        self.pts.entry(id).or_default();
979    }
980    /// Add a points-to edge: `src` may point to `tgt`.
981    pub fn add_pts(&mut self, src: LcnfVarId, tgt: LcnfVarId) {
982        self.pts.entry(src).or_default().insert(tgt);
983    }
984    /// Retrieve the points-to set for `var`.
985    pub fn pts_of(&self, var: LcnfVarId) -> &HashSet<LcnfVarId> {
986        static EMPTY: std::sync::OnceLock<HashSet<LcnfVarId>> = std::sync::OnceLock::new();
987        self.pts
988            .get(&var)
989            .unwrap_or_else(|| EMPTY.get_or_init(HashSet::new))
990    }
991    /// Check whether two variables share a common points-to target.
992    pub fn intersects(&self, a: LcnfVarId, b: LcnfVarId) -> bool {
993        let pa = self.pts_of(a);
994        let pb = self.pts_of(b);
995        pa.iter().any(|x| pb.contains(x))
996    }
997    /// Propagate points-to sets: if `from` ⊆ `to`, propagate pts(from) → pts(to).
998    pub fn propagate(&mut self, from: LcnfVarId, to: LcnfVarId) {
999        let pts_from: HashSet<LcnfVarId> = self.pts_of(from).clone();
1000        self.pts.entry(to).or_default().extend(pts_from);
1001    }
1002}
1003/// Alias analysis hint collector
1004#[allow(dead_code)]
1005#[derive(Debug, Default)]
1006pub struct AliasHintCollector {
1007    pub dead_stores: Vec<DeadStoreHint>,
1008    pub forwarded_loads: Vec<LoadForwardHint>,
1009}
1010#[allow(dead_code)]
1011impl AliasHintCollector {
1012    pub fn new() -> Self {
1013        Self::default()
1014    }
1015    pub fn add_dead_store(&mut self, hint: DeadStoreHint) {
1016        self.dead_stores.push(hint);
1017    }
1018    pub fn add_load_forward(&mut self, hint: LoadForwardHint) {
1019        self.forwarded_loads.push(hint);
1020    }
1021    pub fn dead_store_count(&self) -> usize {
1022        self.dead_stores.len()
1023    }
1024    pub fn load_forward_count(&self) -> usize {
1025        self.forwarded_loads.len()
1026    }
1027}
1028/// TBAA metadata tag (for IR annotations)
1029#[allow(dead_code)]
1030#[derive(Debug, Clone)]
1031pub struct TbaaTag {
1032    pub base_type: TbaaTypeNode,
1033    pub access_type: TbaaTypeNode,
1034    pub offset: u64,
1035    pub is_const: bool,
1036}
1037/// A set of variable IDs that may alias each other.
1038///
1039/// All variables in an alias set may point to the same memory location.
1040#[derive(Debug, Clone, Default)]
1041pub struct AliasSet {
1042    /// The representative (canonical) variable of this set.
1043    pub representative: Option<LcnfVarId>,
1044    /// All variables in this alias set.
1045    pub members: HashSet<LcnfVarId>,
1046    /// Whether this set contains any heap-allocated variables.
1047    pub has_heap: bool,
1048    /// Whether this set contains any stack-allocated variables.
1049    pub has_stack: bool,
1050    /// Whether this set contains any global variables.
1051    pub has_global: bool,
1052}
1053impl AliasSet {
1054    /// Create a new singleton alias set.
1055    pub fn singleton(var: LcnfVarId) -> Self {
1056        let mut members = HashSet::new();
1057        members.insert(var);
1058        AliasSet {
1059            representative: Some(var),
1060            members,
1061            has_heap: false,
1062            has_stack: false,
1063            has_global: false,
1064        }
1065    }
1066    /// Create an empty alias set.
1067    pub fn new() -> Self {
1068        AliasSet::default()
1069    }
1070    /// Insert a variable into this alias set.
1071    pub fn insert(&mut self, var: LcnfVarId) {
1072        if self.representative.is_none() {
1073            self.representative = Some(var);
1074        }
1075        self.members.insert(var);
1076    }
1077    /// Merge another alias set into this one.
1078    pub fn merge_with(&mut self, other: &AliasSet) {
1079        self.members.extend(other.members.iter().copied());
1080        self.has_heap |= other.has_heap;
1081        self.has_stack |= other.has_stack;
1082        self.has_global |= other.has_global;
1083        if self.representative.is_none() {
1084            self.representative = other.representative;
1085        }
1086    }
1087    /// Returns `true` if `var` is in this set.
1088    pub fn contains(&self, var: LcnfVarId) -> bool {
1089        self.members.contains(&var)
1090    }
1091    /// Number of members.
1092    pub fn len(&self) -> usize {
1093        self.members.len()
1094    }
1095    /// Returns `true` if no members.
1096    pub fn is_empty(&self) -> bool {
1097        self.members.is_empty()
1098    }
1099}
1100/// Alias analysis profiler
1101#[allow(dead_code)]
1102#[derive(Debug, Default)]
1103pub struct AliasExtProfiler {
1104    pub timings: Vec<(String, u64)>,
1105}
1106#[allow(dead_code)]
1107impl AliasExtProfiler {
1108    pub fn new() -> Self {
1109        Self::default()
1110    }
1111    pub fn record(&mut self, pass: &str, us: u64) {
1112        self.timings.push((pass.to_string(), us));
1113    }
1114    pub fn total_us(&self) -> u64 {
1115        self.timings.iter().map(|(_, t)| *t).sum()
1116    }
1117}
1118/// Classification of a points-to node.
1119#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1120pub enum NodeKind {
1121    /// A local (stack-allocated) variable.
1122    Local,
1123    /// A heap-allocated object (constructor application, closure).
1124    Heap,
1125    /// A global or top-level constant.
1126    Global,
1127    /// A function parameter.
1128    Parameter,
1129    /// A return value placeholder.
1130    Return,
1131    /// Unknown / conservative.
1132    Unknown,
1133}
1134/// Summary statistics from alias analysis.
1135#[derive(Debug, Clone, Default)]
1136pub struct AliasReport {
1137    /// Total number of alias pairs analyzed.
1138    pub pairs_analyzed: usize,
1139    /// Number of pairs that are MustAlias.
1140    pub must_alias: usize,
1141    /// Number of pairs that are MayAlias.
1142    pub may_alias: usize,
1143    /// Number of pairs that are NoAlias.
1144    pub no_alias: usize,
1145    /// The analysis level used.
1146    pub analysis_level: AliasAnalysisLevel,
1147    /// Number of load-store forwards performed.
1148    pub load_store_forwards: usize,
1149    /// Number of solver iterations (Andersen only).
1150    pub solver_iterations: u32,
1151}
1152impl AliasReport {
1153    /// Percentage of pairs proven NoAlias (precision metric).
1154    pub fn no_alias_ratio(&self) -> f64 {
1155        if self.pairs_analyzed == 0 {
1156            0.0
1157        } else {
1158            self.no_alias as f64 / self.pairs_analyzed as f64
1159        }
1160    }
1161    /// Percentage of pairs proven MustAlias.
1162    pub fn must_alias_ratio(&self) -> f64 {
1163        if self.pairs_analyzed == 0 {
1164            0.0
1165        } else {
1166            self.must_alias as f64 / self.pairs_analyzed as f64
1167        }
1168    }
1169}
1170/// Alias analysis id generator
1171#[allow(dead_code)]
1172#[derive(Debug, Default)]
1173pub struct AliasExtIdGen {
1174    pub(super) counter: u32,
1175}
1176#[allow(dead_code)]
1177impl AliasExtIdGen {
1178    pub fn new() -> Self {
1179        Self::default()
1180    }
1181    pub fn next(&mut self) -> u32 {
1182        let id = self.counter;
1183        self.counter += 1;
1184        id
1185    }
1186}
1187/// The alias analysis pass.
1188///
1189/// Collects variables from LCNF declarations, builds constraints,
1190/// solves the points-to graph, and answers alias queries.
1191#[derive(Debug)]
1192pub struct AliasPass {
1193    /// The analysis level to use.
1194    pub level: AliasAnalysisLevel,
1195    /// The Andersen solver (used for CFL levels).
1196    pub solver: AndersenSolver,
1197    /// Type-based alias map: (var, type) pairs.
1198    pub type_map: HashMap<LcnfVarId, LcnfType>,
1199    /// Load-store forwarder.
1200    pub forwarder: LoadStoreForwarder,
1201    /// Accumulated report.
1202    pub(super) report: AliasReport,
1203    /// All queried pairs and their results.
1204    pub(super) query_cache: HashMap<(LcnfVarId, LcnfVarId), AliasResult>,
1205}
1206impl AliasPass {
1207    /// Create a new alias pass with default (CFLAndersen) level.
1208    pub fn new() -> Self {
1209        AliasPass {
1210            level: AliasAnalysisLevel::CFLAndersen,
1211            solver: AndersenSolver::new(),
1212            type_map: HashMap::new(),
1213            forwarder: LoadStoreForwarder::new(),
1214            report: AliasReport {
1215                analysis_level: AliasAnalysisLevel::CFLAndersen,
1216                ..Default::default()
1217            },
1218            query_cache: HashMap::new(),
1219        }
1220    }
1221    /// Create a pass with a specific analysis level.
1222    pub fn with_level(level: AliasAnalysisLevel) -> Self {
1223        AliasPass {
1224            level,
1225            solver: AndersenSolver::new(),
1226            type_map: HashMap::new(),
1227            forwarder: LoadStoreForwarder::new(),
1228            report: AliasReport {
1229                analysis_level: level,
1230                ..Default::default()
1231            },
1232            query_cache: HashMap::new(),
1233        }
1234    }
1235    /// Run alias analysis over all function declarations.
1236    pub fn run(&mut self, decls: &mut [LcnfFunDecl]) {
1237        for decl in decls.iter() {
1238            self.collect_decl(decl);
1239        }
1240        if self.level.uses_points_to() {
1241            self.solver.solve();
1242            self.report.solver_iterations = self.solver.iterations;
1243        }
1244    }
1245    /// Collect variables and constraints from a single function declaration.
1246    pub(super) fn collect_decl(&mut self, decl: &LcnfFunDecl) {
1247        for param in &decl.params {
1248            let node = PointsToNode::parameter(param.id, param.name.clone());
1249            self.solver.register_var(node);
1250            self.type_map.insert(param.id, param.ty.clone());
1251        }
1252        self.collect_expr(&decl.body);
1253    }
1254    /// Walk an LCNF expression and gather constraints.
1255    pub(super) fn collect_expr(&mut self, expr: &LcnfExpr) {
1256        match expr {
1257            LcnfExpr::Let {
1258                id,
1259                name,
1260                ty,
1261                value,
1262                body,
1263            } => {
1264                let node = PointsToNode::local(*id, name.clone());
1265                self.solver.register_var(node);
1266                self.type_map.insert(*id, ty.clone());
1267                self.collect_let_value(*id, value);
1268                self.collect_expr(body);
1269            }
1270            LcnfExpr::Case { alts, default, .. } => {
1271                for alt in alts {
1272                    for param in &alt.params {
1273                        let node = PointsToNode::local(param.id, param.name.clone());
1274                        self.solver.register_var(node);
1275                        self.type_map.insert(param.id, param.ty.clone());
1276                    }
1277                    self.collect_expr(&alt.body);
1278                }
1279                if let Some(def) = default {
1280                    self.collect_expr(def);
1281                }
1282            }
1283            LcnfExpr::Return(_) | LcnfExpr::TailCall(_, _) | LcnfExpr::Unreachable => {}
1284        }
1285    }
1286    /// Gather constraints from a let-binding value.
1287    pub(super) fn collect_let_value(&mut self, lhs: LcnfVarId, value: &LcnfLetValue) {
1288        match value {
1289            LcnfLetValue::App(func_arg, _args) => {
1290                if let LcnfArg::Var(f) = func_arg {
1291                    self.solver.add_copy(lhs, *f);
1292                }
1293            }
1294            LcnfLetValue::Ctor(_, _, _args) => {
1295                self.solver.add_address_of(lhs, lhs);
1296            }
1297            LcnfLetValue::Reuse(slot, _, _, _args) => {
1298                self.solver.add_address_of(lhs, *slot);
1299            }
1300            LcnfLetValue::FVar(src) => {
1301                self.solver.add_copy(lhs, *src);
1302            }
1303            LcnfLetValue::Proj(_, _, src) => {
1304                self.solver.add_load(lhs, *src);
1305            }
1306            LcnfLetValue::Reset(src) => {
1307                self.solver.add_copy(lhs, *src);
1308            }
1309            LcnfLetValue::Lit(_) | LcnfLetValue::Erased => {}
1310        }
1311    }
1312    /// Query whether two variables alias after analysis.
1313    pub fn query(&mut self, a: LcnfVarId, b: LcnfVarId) -> AliasResult {
1314        let key = if a.0 <= b.0 { (a, b) } else { (b, a) };
1315        if let Some(&cached) = self.query_cache.get(&key) {
1316            return cached;
1317        }
1318        let result = self.compute_alias(a, b);
1319        self.query_cache.insert(key, result);
1320        self.report.pairs_analyzed += 1;
1321        match result {
1322            AliasResult::MustAlias => self.report.must_alias += 1,
1323            AliasResult::MayAlias => self.report.may_alias += 1,
1324            AliasResult::NoAlias => self.report.no_alias += 1,
1325        }
1326        result
1327    }
1328    /// Internal alias computation (uncached).
1329    pub(super) fn compute_alias(&self, a: LcnfVarId, b: LcnfVarId) -> AliasResult {
1330        match self.level {
1331            AliasAnalysisLevel::NoAlias => AliasResult::MayAlias,
1332            AliasAnalysisLevel::BasicAA => {
1333                if a == b {
1334                    AliasResult::MustAlias
1335                } else {
1336                    AliasResult::MayAlias
1337                }
1338            }
1339            AliasAnalysisLevel::TypeBasedAA => {
1340                if a == b {
1341                    return AliasResult::MustAlias;
1342                }
1343                if let (Some(ta), Some(tb)) = (self.type_map.get(&a), self.type_map.get(&b)) {
1344                    if tbaa_no_alias(ta, tb) {
1345                        return AliasResult::NoAlias;
1346                    }
1347                }
1348                AliasResult::MayAlias
1349            }
1350            AliasAnalysisLevel::ScopedNoAliasAA => {
1351                if a == b {
1352                    return AliasResult::MustAlias;
1353                }
1354                let a_is_alloc = self.solver.graph.pts_of(a).contains(&a);
1355                let b_is_alloc = self.solver.graph.pts_of(b).contains(&b);
1356                if a_is_alloc && b_is_alloc {
1357                    AliasResult::NoAlias
1358                } else {
1359                    AliasResult::MayAlias
1360                }
1361            }
1362            AliasAnalysisLevel::GlobalsAA => {
1363                if a == b {
1364                    return AliasResult::MustAlias;
1365                }
1366                let a_global = self
1367                    .solver
1368                    .graph
1369                    .nodes
1370                    .get(&a)
1371                    .map(|n| matches!(n.kind, NodeKind::Global))
1372                    .unwrap_or(false);
1373                let b_global = self
1374                    .solver
1375                    .graph
1376                    .nodes
1377                    .get(&b)
1378                    .map(|n| matches!(n.kind, NodeKind::Global))
1379                    .unwrap_or(false);
1380                if a_global != b_global {
1381                    AliasResult::NoAlias
1382                } else {
1383                    AliasResult::MayAlias
1384                }
1385            }
1386            AliasAnalysisLevel::CFLAndersen | AliasAnalysisLevel::CFLSteensgaard => {
1387                self.solver.query(a, b)
1388            }
1389        }
1390    }
1391    /// Return a copy of the accumulated report.
1392    pub fn report(&self) -> AliasReport {
1393        self.report.clone()
1394    }
1395    /// Apply load-store forwarding to all declarations (using alias info).
1396    pub fn apply_load_store_forwarding(&mut self, decls: &mut [LcnfFunDecl]) {
1397        for decl in decls.iter_mut() {
1398            apply_forwarding_to_expr(&mut decl.body, &mut self.forwarder);
1399        }
1400        self.report.load_store_forwards = self.forwarder.forwards_applied;
1401    }
1402}
1403/// Points-to set (abstract)
1404#[allow(dead_code)]
1405#[derive(Debug, Default, Clone)]
1406pub struct PointsToSet {
1407    pub locations: std::collections::HashSet<MemLocation>,
1408    pub may_alias_unknown: bool,
1409}
1410#[allow(dead_code)]
1411impl PointsToSet {
1412    pub fn new() -> Self {
1413        Self::default()
1414    }
1415    pub fn top() -> Self {
1416        Self {
1417            locations: std::collections::HashSet::new(),
1418            may_alias_unknown: true,
1419        }
1420    }
1421    pub fn singleton(loc: MemLocation) -> Self {
1422        let mut s = Self::new();
1423        s.locations.insert(loc);
1424        s
1425    }
1426    pub fn add(&mut self, loc: MemLocation) {
1427        self.locations.insert(loc);
1428    }
1429    pub fn union_with(&mut self, other: &PointsToSet) {
1430        self.may_alias_unknown |= other.may_alias_unknown;
1431        for loc in &other.locations {
1432            self.locations.insert(loc.clone());
1433        }
1434    }
1435    pub fn may_alias(&self, other: &PointsToSet) -> bool {
1436        if self.may_alias_unknown || other.may_alias_unknown {
1437            return true;
1438        }
1439        self.locations
1440            .iter()
1441            .any(|loc| other.locations.contains(loc))
1442    }
1443    pub fn must_alias(&self, other: &PointsToSet) -> bool {
1444        if self.may_alias_unknown || other.may_alias_unknown {
1445            return false;
1446        }
1447        self.locations.len() == 1 && other.locations.len() == 1 && self.locations == other.locations
1448    }
1449    pub fn is_empty(&self) -> bool {
1450        self.locations.is_empty() && !self.may_alias_unknown
1451    }
1452    pub fn size(&self) -> usize {
1453        self.locations.len()
1454    }
1455}
1456/// Alias analysis pipeline (compose multiple analyses)
1457#[allow(dead_code)]
1458#[derive(Debug, Default)]
1459pub struct AliasAnalysisPipeline {
1460    pub passes: Vec<String>,
1461    pub cache: AliasCache,
1462    pub stats: AliasStatsExt,
1463    pub tbaa: TbaaTree,
1464}
1465#[allow(dead_code)]
1466impl AliasAnalysisPipeline {
1467    pub fn new() -> Self {
1468        Self::default()
1469    }
1470    pub fn add_pass(&mut self, name: &str) {
1471        self.passes.push(name.to_string());
1472    }
1473    pub fn query(&mut self, a: u32, b: u32) -> AliasResultExt {
1474        self.stats.queries_total += 1;
1475        if let Some(cached) = self.cache.get(a, b) {
1476            return cached;
1477        }
1478        let result = AliasResultExt::MayAlias;
1479        self.stats.may_alias_count += 1;
1480        self.cache.insert(a, b, result.clone());
1481        result
1482    }
1483    pub fn query_modref(&self, _func: u32, _loc: u32) -> ModRefResult {
1484        ModRefResult::ModRef
1485    }
1486}
1487/// Alias analysis pass configuration (extended)
1488#[allow(dead_code)]
1489#[derive(Debug, Clone)]
1490pub struct AliasConfigExt {
1491    pub level: String,
1492    pub max_iterations: usize,
1493    pub max_points_to_size: usize,
1494    pub enable_field_sensitivity: bool,
1495    pub enable_flow_sensitivity: bool,
1496    pub enable_context_sensitivity: bool,
1497    pub track_heap: bool,
1498    pub track_globals: bool,
1499}
1500/// Alias-based dead store elimination hint
1501#[allow(dead_code)]
1502#[derive(Debug, Clone)]
1503pub struct DeadStoreHint {
1504    pub store_id: u32,
1505    pub overwritten_by: u32,
1506    pub alias_result: AliasResultExt,
1507}
1508/// Load-store forwarding hint
1509#[allow(dead_code)]
1510#[derive(Debug, Clone)]
1511pub struct LoadForwardHint {
1512    pub load_id: u32,
1513    pub from_store: u32,
1514    pub is_exact: bool,
1515}