1use super::functions::*;
6use crate::lcnf::{LcnfArg, LcnfExpr, LcnfFunDecl, LcnfLetValue, LcnfType, LcnfVarId};
7use std::collections::{HashMap, HashSet, VecDeque};
8
9#[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#[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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
50pub enum AliasAnalysisLevel {
51 NoAlias,
53 BasicAA,
55 TypeBasedAA,
57 ScopedNoAliasAA,
59 GlobalsAA,
61 #[default]
63 CFLAndersen,
64 CFLSteensgaard,
66}
67impl AliasAnalysisLevel {
68 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 pub fn uses_points_to(&self) -> bool {
82 matches!(
83 self,
84 AliasAnalysisLevel::CFLAndersen | AliasAnalysisLevel::CFLSteensgaard
85 )
86 }
87}
88#[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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
101pub struct PointsToNode {
102 pub var: LcnfVarId,
104 pub label: String,
106 pub kind: NodeKind,
108}
109impl PointsToNode {
110 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 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 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 pub fn is_heap(&self) -> bool {
136 matches!(self.kind, NodeKind::Heap)
137 }
138 pub fn is_local(&self) -> bool {
140 matches!(self.kind, NodeKind::Local)
141 }
142}
143#[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#[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#[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#[derive(Debug, Clone, Default)]
246pub struct AndersenSolver {
247 pub constraints: Vec<AndersenConstraint>,
249 pub graph: PointsToGraph,
251 pub field_sensitive: bool,
253 pub iterations: u32,
255}
256impl AndersenSolver {
257 pub fn new() -> Self {
259 AndersenSolver::default()
260 }
261 pub fn field_sensitive() -> Self {
263 AndersenSolver {
264 field_sensitive: true,
265 ..AndersenSolver::default()
266 }
267 }
268 pub fn add_address_of(&mut self, lhs: LcnfVarId, addr_of: LcnfVarId) {
270 self.constraints
271 .push(AndersenConstraint::AddressOf { lhs, addr_of });
272 }
273 pub fn add_copy(&mut self, lhs: LcnfVarId, rhs: LcnfVarId) {
275 self.constraints.push(AndersenConstraint::Copy { lhs, rhs });
276 }
277 pub fn add_load(&mut self, lhs: LcnfVarId, ptr: LcnfVarId) {
279 self.constraints.push(AndersenConstraint::Load { lhs, ptr });
280 }
281 pub fn add_store(&mut self, ptr: LcnfVarId, rhs: LcnfVarId) {
283 self.constraints
284 .push(AndersenConstraint::Store { ptr, rhs });
285 }
286 pub fn register_var(&mut self, node: PointsToNode) {
288 self.graph.add_node(node);
289 }
290 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 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#[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#[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#[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#[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#[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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
493pub enum AliasResult {
494 MustAlias,
496 MayAlias,
498 NoAlias,
500}
501impl AliasResult {
502 pub fn may_alias(self) -> bool {
504 matches!(self, AliasResult::MustAlias | AliasResult::MayAlias)
505 }
506 pub fn must_alias(self) -> bool {
508 matches!(self, AliasResult::MustAlias)
509 }
510 pub fn no_alias(self) -> bool {
512 matches!(self, AliasResult::NoAlias)
513 }
514 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#[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#[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#[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#[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#[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#[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#[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#[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#[derive(Debug, Clone, Default)]
672pub struct LoadStoreForwarder {
673 pub store_cache: HashMap<(LcnfVarId, Option<i64>), LcnfVarId>,
675 pub forwards_applied: usize,
677}
678impl LoadStoreForwarder {
679 pub fn new() -> Self {
681 LoadStoreForwarder::default()
682 }
683 pub fn record_store(&mut self, base: LcnfVarId, offset: Option<i64>, val: LcnfVarId) {
685 self.store_cache.insert((base, offset), val);
686 }
687 pub fn forward_load(&self, base: LcnfVarId, offset: Option<i64>) -> Option<LcnfVarId> {
691 self.store_cache.get(&(base, offset)).copied()
692 }
693 pub fn invalidate(&mut self, base: LcnfVarId) {
695 self.store_cache.retain(|(b, _), _| *b != base);
696 }
697 pub fn clear(&mut self) {
699 self.store_cache.clear();
700 }
701}
702#[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#[allow(dead_code)]
715#[derive(Debug, Clone, PartialEq, Eq)]
716pub enum ModRefResult {
717 NoModRef,
718 Ref,
719 Mod,
720 ModRef,
721}
722#[derive(Debug, Clone)]
724pub struct MemoryAccessInfo {
725 pub base: LcnfVarId,
727 pub offset: Option<i64>,
729 pub size: Option<u64>,
731 pub is_volatile: bool,
733 pub access_type: LcnfType,
735 pub is_write: bool,
737}
738impl MemoryAccessInfo {
739 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 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 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#[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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
841pub enum AndersenConstraint {
842 AddressOf { lhs: LcnfVarId, addr_of: LcnfVarId },
844 Copy { lhs: LcnfVarId, rhs: LcnfVarId },
846 Load { lhs: LcnfVarId, ptr: LcnfVarId },
848 Store { ptr: LcnfVarId, rhs: LcnfVarId },
850}
851#[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#[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#[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#[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#[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#[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#[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#[derive(Debug, Clone, Default)]
961pub struct PointsToGraph {
962 pub nodes: HashMap<LcnfVarId, PointsToNode>,
964 pub pts: HashMap<LcnfVarId, HashSet<LcnfVarId>>,
966 pub is_steensgaard: bool,
968}
969impl PointsToGraph {
970 pub fn new() -> Self {
972 PointsToGraph::default()
973 }
974 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 pub fn add_pts(&mut self, src: LcnfVarId, tgt: LcnfVarId) {
982 self.pts.entry(src).or_default().insert(tgt);
983 }
984 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 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 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#[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#[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#[derive(Debug, Clone, Default)]
1041pub struct AliasSet {
1042 pub representative: Option<LcnfVarId>,
1044 pub members: HashSet<LcnfVarId>,
1046 pub has_heap: bool,
1048 pub has_stack: bool,
1050 pub has_global: bool,
1052}
1053impl AliasSet {
1054 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 pub fn new() -> Self {
1068 AliasSet::default()
1069 }
1070 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 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 pub fn contains(&self, var: LcnfVarId) -> bool {
1089 self.members.contains(&var)
1090 }
1091 pub fn len(&self) -> usize {
1093 self.members.len()
1094 }
1095 pub fn is_empty(&self) -> bool {
1097 self.members.is_empty()
1098 }
1099}
1100#[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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1120pub enum NodeKind {
1121 Local,
1123 Heap,
1125 Global,
1127 Parameter,
1129 Return,
1131 Unknown,
1133}
1134#[derive(Debug, Clone, Default)]
1136pub struct AliasReport {
1137 pub pairs_analyzed: usize,
1139 pub must_alias: usize,
1141 pub may_alias: usize,
1143 pub no_alias: usize,
1145 pub analysis_level: AliasAnalysisLevel,
1147 pub load_store_forwards: usize,
1149 pub solver_iterations: u32,
1151}
1152impl AliasReport {
1153 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 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#[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#[derive(Debug)]
1192pub struct AliasPass {
1193 pub level: AliasAnalysisLevel,
1195 pub solver: AndersenSolver,
1197 pub type_map: HashMap<LcnfVarId, LcnfType>,
1199 pub forwarder: LoadStoreForwarder,
1201 pub(super) report: AliasReport,
1203 pub(super) query_cache: HashMap<(LcnfVarId, LcnfVarId), AliasResult>,
1205}
1206impl AliasPass {
1207 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 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 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 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 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 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 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 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 pub fn report(&self) -> AliasReport {
1393 self.report.clone()
1394 }
1395 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#[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#[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#[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#[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#[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}