1use serde::{Deserialize, Serialize};
2use std::collections::{BTreeMap, BTreeSet};
3use vyre_lower::verify::{classify_operand, OperandClass};
4use vyre_lower::{KernelBody, KernelDescriptor};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct DanglingRef {
8 pub ref_id: u32,
9 pub produced_in_body_path: Vec<usize>,
10 pub producing_op_index: usize,
11 pub producing_op_kind: String,
12 pub referenced_in_body_path: Vec<usize>,
13 pub referencing_op_index: usize,
14 pub referencing_op_kind: String,
15 pub operand_position: usize,
16}
17
18pub fn find_dangling_refs(desc: &KernelDescriptor) -> Vec<DanglingRef> {
19 let mut id_origins = BTreeMap::new();
20
21 fn build_id_origins(
22 body: &KernelBody,
23 path: &mut Vec<usize>,
24 map: &mut BTreeMap<u32, (Vec<usize>, usize, String)>,
25 ) {
26 for (i, op) in body.ops.iter().enumerate() {
27 for r in op.result_ids() {
28 map.insert(r, (path.clone(), i, format!("{:?}", op.kind)));
29 }
30 }
31 for (idx, child) in body.child_bodies.iter().enumerate() {
32 path.push(idx);
33 build_id_origins(child, path, map);
34 path.pop();
35 }
36 }
37 build_id_origins(&desc.body, &mut vec![], &mut id_origins);
38
39 let mut refs = Vec::new();
40 check_body(
41 &desc.body,
42 &mut vec![],
43 &BTreeSet::new(),
44 &mut refs,
45 &id_origins,
46 );
47 refs
48}
49
50fn check_body(
51 body: &KernelBody,
52 path: &mut Vec<usize>,
53 inherited_results: &BTreeSet<u32>,
54 refs: &mut Vec<DanglingRef>,
55 id_origins: &BTreeMap<u32, (Vec<usize>, usize, String)>,
56) {
57 let mut produced: BTreeSet<u32> = BTreeSet::new();
58 for op in &body.ops {
59 for r in op.result_ids() {
60 produced.insert(r);
61 }
62 }
63
64 let mut produced_so_far: BTreeSet<u32> = BTreeSet::new();
65 let child_results: Vec<BTreeSet<u32>> =
66 body.child_bodies.iter().map(collect_body_results).collect();
67 let mut completed_child_results: BTreeSet<u32> = BTreeSet::new();
68 let mut child_scopes = vec![BTreeSet::new(); body.child_bodies.len()];
69
70 for (i, op) in body.ops.iter().enumerate() {
71 for (pos, &val) in op.operands.iter().enumerate() {
72 let cls = classify_operand(&op.kind, pos);
73 match cls {
74 OperandClass::ResultRef => {
75 if !produced_so_far.contains(&val)
76 && !produced.contains(&val)
77 && !inherited_results.contains(&val)
78 && !completed_child_results.contains(&val)
79 {
80 let (prod_path, prod_idx, prod_kind) =
81 if let Some(orig) = id_origins.get(&val) {
82 (orig.0.clone(), orig.1, orig.2.clone())
83 } else {
84 (vec![], 0, "Unknown".to_string())
85 };
86
87 refs.push(DanglingRef {
88 ref_id: val,
89 produced_in_body_path: prod_path,
90 producing_op_index: prod_idx,
91 producing_op_kind: prod_kind,
92 referenced_in_body_path: path.clone(),
93 referencing_op_index: i,
94 referencing_op_kind: format!("{:?}", op.kind),
95 operand_position: pos,
96 });
97 }
98 }
99 OperandClass::ChildBodyIdx => {
100 if (val as usize) < body.child_bodies.len() {
101 let child_scope = &mut child_scopes[val as usize];
102 child_scope.extend(inherited_results.iter().copied());
103 child_scope.extend(produced_so_far.iter().copied());
104 child_scope.extend(completed_child_results.iter().copied());
105 }
106 }
107 _ => {}
108 }
109 }
110
111 for r in op.result_ids() {
112 produced_so_far.insert(r);
113 }
114
115 for (pos, &val) in op.operands.iter().enumerate() {
116 if classify_operand(&op.kind, pos) == OperandClass::ChildBodyIdx {
117 if let Some(results) = child_results.get(val as usize) {
118 completed_child_results.extend(results.iter().copied());
119 }
120 }
121 }
122 }
123
124 for (idx, child) in body.child_bodies.iter().enumerate() {
125 path.push(idx);
126 check_body(child, path, &child_scopes[idx], refs, id_origins);
127 path.pop();
128 }
129}
130
131fn collect_body_results(body: &KernelBody) -> BTreeSet<u32> {
132 let mut results = BTreeSet::new();
133 for op in &body.ops {
134 for result in op.result_ids() {
135 results.insert(result);
136 }
137 }
138 for child in &body.child_bodies {
139 results.extend(collect_body_results(child));
140 }
141 results
142}