1use std::cell::Cell;
2use std::collections::HashSet;
3
4use rustc_hir::def_id::DefId;
5use rustc_index::IndexVec;
6use rustc_middle::mir::Local;
7use rustc_span::{DUMMY_SP, Span};
8
9pub type EdgeIdx = usize;
10pub type GraphNodes = IndexVec<Local, DataflowNode>;
11pub type GraphEdges = IndexVec<EdgeIdx, DataflowEdge>;
12
13#[derive(Clone, Debug)]
14pub enum NodeOp {
15 Nop,
16 Err,
17 Const(String, String),
18 Use,
19 Repeat,
20 Ref,
21 ThreadLocalRef,
22 AddressOf,
23 Len,
24 Cast,
25 BinaryOp,
26 CheckedBinaryOp,
27 #[cfg(not(rapx_ge_99))]
28 NullaryOp,
29 UnaryOp,
30 Discriminant,
31 Aggregate(AggKind),
32 #[cfg(not(rapx_ge_99))]
33 ShallowInitBox,
34 CopyForDeref,
35 RawPtr,
36 Call(DefId),
37 CallOperand,
38}
39
40#[derive(Clone, Debug)]
41pub enum EdgeOp {
42 Nop,
43 Move,
44 Copy,
45 Const,
46 Immut,
47 Mut,
48 Deref,
49 Field(usize),
50 Downcast(String),
51 Index,
52 ConstIndex,
53 SubSlice,
54}
55
56#[derive(Clone, Copy, Debug)]
57pub enum AggKind {
58 Array,
59 Tuple,
60 Adt(DefId),
61 Closure(DefId),
62 Coroutine(DefId),
63 RawPtr,
64}
65
66#[derive(Clone, Debug)]
67pub struct DataflowEdge {
68 pub src: Local,
69 pub dst: Local,
70 pub op: EdgeOp,
71 pub seq: usize,
72 pub block: usize,
73 pub statement_index: usize,
74}
75
76#[derive(Clone, Debug)]
77pub struct DataflowNode {
78 pub ops: Vec<NodeOp>,
79 pub span: Span,
80 pub seq: usize,
81 pub out_edges: Vec<EdgeIdx>,
82 pub in_edges: Vec<EdgeIdx>,
83}
84
85impl DataflowNode {
86 pub fn new() -> Self {
87 Self {
88 ops: vec![NodeOp::Nop],
89 span: DUMMY_SP,
90 seq: 0,
91 out_edges: vec![],
92 in_edges: vec![],
93 }
94 }
95}
96
97#[derive(Clone)]
98pub struct DataflowGraph {
99 pub def_id: DefId,
100 pub span: Span,
101 pub argc: usize,
102 pub nodes: GraphNodes,
103 pub edges: GraphEdges,
104 pub n_locals: usize,
105 pub closures: HashSet<DefId>,
106}
107
108impl DataflowGraph {
109 pub fn new(def_id: DefId, span: Span, argc: usize, n_locals: usize) -> Self {
110 Self {
111 def_id,
112 span,
113 argc,
114 nodes: GraphNodes::from_elem_n(DataflowNode::new(), n_locals),
115 edges: GraphEdges::new(),
116 n_locals,
117 closures: HashSet::new(),
118 }
119 }
120
121 pub fn node(&self, local: Local) -> &DataflowNode {
122 &self.nodes[local]
123 }
124
125 pub fn node_mut(&mut self, local: Local) -> &mut DataflowNode {
126 &mut self.nodes[local]
127 }
128
129 pub fn edge(&self, idx: EdgeIdx) -> &DataflowEdge {
130 &self.edges[idx]
131 }
132
133 pub fn is_marker(&self, idx: Local) -> bool {
134 idx >= Local::from_usize(self.n_locals)
135 }
136
137 pub fn add_node_edge(
138 &mut self,
139 src: Local,
140 dst: Local,
141 op: EdgeOp,
142 block: usize,
143 statement_index: usize,
144 ) -> EdgeIdx {
145 let seq = self.nodes[dst].seq;
146 let edge_idx = self.edges.push(DataflowEdge {
147 src,
148 dst,
149 op,
150 seq,
151 block,
152 statement_index,
153 });
154 self.nodes[dst].in_edges.push(edge_idx);
155 self.nodes[src].out_edges.push(edge_idx);
156 edge_idx
157 }
158
159 pub fn add_const_edge(
160 &mut self,
161 src_desc: String,
162 src_ty: String,
163 dst: Local,
164 op: EdgeOp,
165 block: usize,
166 statement_index: usize,
167 ) -> EdgeIdx {
168 let seq = self.nodes[dst].seq;
169 let mut const_node = DataflowNode::new();
170 const_node.ops[0] = NodeOp::Const(src_desc, src_ty);
171 let src = self.nodes.push(const_node);
172 let edge_idx = self.edges.push(DataflowEdge {
173 src,
174 dst,
175 op,
176 seq,
177 block,
178 statement_index,
179 });
180 self.nodes[dst].in_edges.push(edge_idx);
181 edge_idx
182 }
183
184 pub fn get_upside_idx(&self, node_idx: Local, order: usize) -> Option<Local> {
185 if let Some(edge_idx) = self.nodes[node_idx].in_edges.get(order) {
186 Some(self.edges[*edge_idx].src)
187 } else {
188 None
189 }
190 }
191
192 pub fn get_downside_idx(&self, node_idx: Local, order: usize) -> Option<Local> {
193 if let Some(edge_idx) = self.nodes[node_idx].out_edges.get(order) {
194 Some(self.edges[*edge_idx].dst)
195 } else {
196 None
197 }
198 }
199
200 pub fn is_connected(&self, idx_1: Local, idx_2: Local) -> bool {
201 let target = idx_2;
202 let find = Cell::new(false);
203 let mut node_operator = |_: &DataflowGraph, idx: Local| -> DFSStatus {
204 find.set(idx == target);
205 if find.get() {
206 DFSStatus::Stop
207 } else {
208 DFSStatus::Continue
209 }
210 };
211 let mut seen = HashSet::new();
212 self.dfs(
213 idx_1,
214 Direction::Downside,
215 &mut node_operator,
216 &mut Self::always_true_edge_validator,
217 false,
218 &mut seen,
219 );
220 seen.clear();
221 if !find.get() {
222 self.dfs(
223 idx_1,
224 Direction::Upside,
225 &mut node_operator,
226 &mut Self::always_true_edge_validator,
227 false,
228 &mut seen,
229 );
230 }
231 find.get()
232 }
233
234 pub fn param_return_deps(&self) -> IndexVec<Local, bool> {
235 let _0 = Local::from_usize(0);
236 let deps = (0..self.argc + 1)
237 .map(|i| {
238 let _i = Local::from_usize(i);
239 self.is_connected(_i, _0)
240 })
241 .collect();
242 deps
243 }
244
245 pub fn dfs<F, G>(
246 &self,
247 now: Local,
248 direction: Direction,
249 node_operator: &mut F,
250 edge_validator: &mut G,
251 traverse_all: bool,
252 seen: &mut HashSet<Local>,
253 ) -> (DFSStatus, bool)
254 where
255 F: FnMut(&DataflowGraph, Local) -> DFSStatus,
256 G: FnMut(&DataflowGraph, EdgeIdx) -> DFSStatus,
257 {
258 if seen.contains(&now) {
259 return (DFSStatus::Stop, false);
260 }
261 seen.insert(now);
262 macro_rules! traverse {
263 ($edges: ident, $field: ident) => {
264 for edge_idx in self.nodes[now].$edges.iter() {
265 let edge = &self.edges[*edge_idx];
266 if matches!(edge_validator(self, *edge_idx), DFSStatus::Continue) {
267 let (dfs_status, result) = self.dfs(
268 edge.$field,
269 direction,
270 node_operator,
271 edge_validator,
272 traverse_all,
273 seen,
274 );
275 if matches!(dfs_status, DFSStatus::Stop) && result && !traverse_all {
276 return (DFSStatus::Stop, true);
277 }
278 }
279 }
280 };
281 }
282 if matches!(node_operator(self, now), DFSStatus::Continue) {
283 match direction {
284 Direction::Upside => {
285 traverse!(in_edges, src);
286 }
287 Direction::Downside => {
288 traverse!(out_edges, dst);
289 }
290 Direction::Both => {
291 traverse!(in_edges, src);
292 traverse!(out_edges, dst);
293 }
294 };
295 (DFSStatus::Continue, false)
296 } else {
297 (DFSStatus::Stop, true)
298 }
299 }
300
301 pub fn find_first_node<P, E>(
302 &self,
303 start: Local,
304 direction: Direction,
305 node_predicate: &mut P,
306 edge_validator: &mut E,
307 ) -> Option<Local>
308 where
309 P: FnMut(&DataflowGraph, Local) -> bool,
310 E: FnMut(&DataflowGraph, EdgeIdx) -> DFSStatus,
311 {
312 let mut result = None;
313 let mut node_op = |graph: &DataflowGraph, idx: Local| -> DFSStatus {
314 if node_predicate(graph, idx) {
315 result = Some(idx);
316 DFSStatus::Stop
317 } else {
318 DFSStatus::Continue
319 }
320 };
321 let mut seen = HashSet::new();
322 self.dfs(start, direction, &mut node_op, edge_validator, false, &mut seen);
323 result
324 }
325
326 pub fn find_all_nodes<P, E>(
327 &self,
328 start: Local,
329 direction: Direction,
330 node_predicate: &mut P,
331 edge_validator: &mut E,
332 ) -> Vec<Local>
333 where
334 P: FnMut(&DataflowGraph, Local) -> bool,
335 E: FnMut(&DataflowGraph, EdgeIdx) -> DFSStatus,
336 {
337 let mut results = Vec::new();
338 let mut node_op = |graph: &DataflowGraph, idx: Local| -> DFSStatus {
339 if node_predicate(graph, idx) {
340 results.push(idx);
341 }
342 DFSStatus::Continue
343 };
344 let mut seen = HashSet::new();
345 self.dfs(start, direction, &mut node_op, edge_validator, true, &mut seen);
346 results
347 }
348
349 pub fn equivalent_edge_validator(graph: &DataflowGraph, idx: EdgeIdx) -> DFSStatus {
350 match graph.edges[idx].op {
351 EdgeOp::Copy | EdgeOp::Move | EdgeOp::Mut | EdgeOp::Immut | EdgeOp::Deref => {
352 DFSStatus::Continue
353 }
354 EdgeOp::Nop
355 | EdgeOp::Const
356 | EdgeOp::Downcast(_)
357 | EdgeOp::Field(_)
358 | EdgeOp::Index
359 | EdgeOp::ConstIndex
360 | EdgeOp::SubSlice => DFSStatus::Stop,
361 }
362 }
363
364 pub fn always_true_edge_validator(_: &DataflowGraph, _: EdgeIdx) -> DFSStatus {
365 DFSStatus::Continue
366 }
367
368 pub fn collect_equivalent_locals(&self, local: Local, strict: bool) -> HashSet<Local> {
369 let mut set = HashSet::new();
370 let root = Cell::new(local);
371 let reduce_func = if strict {
372 DFSStatus::and
373 } else {
374 DFSStatus::or
375 };
376 let mut find_root_operator = |graph: &DataflowGraph, idx: Local| -> DFSStatus {
377 let node = &graph.nodes[idx];
378 node.ops
379 .iter()
380 .map(|op| match op {
381 NodeOp::Nop | NodeOp::Use | NodeOp::Ref => {
382 root.set(idx);
383 DFSStatus::Continue
384 }
385 NodeOp::Call(_) => {
386 root.set(idx);
387 DFSStatus::Stop
388 }
389 _ => DFSStatus::Stop,
390 })
391 .reduce(reduce_func)
392 .unwrap()
393 };
394 let mut find_equivalent_operator = |graph: &DataflowGraph, idx: Local| -> DFSStatus {
395 let node = &graph.nodes[idx];
396 if set.contains(&idx) {
397 return DFSStatus::Stop;
398 }
399 node.ops
400 .iter()
401 .map(|op| match op {
402 NodeOp::Nop | NodeOp::Use | NodeOp::Ref => {
403 set.insert(idx);
404 DFSStatus::Continue
405 }
406 NodeOp::Call(_) => {
407 if idx == root.get() {
408 set.insert(idx);
409 DFSStatus::Continue
410 } else {
411 DFSStatus::Stop
412 }
413 }
414 _ => DFSStatus::Stop,
415 })
416 .reduce(reduce_func)
417 .unwrap()
418 };
419 let mut seen = HashSet::new();
420 self.dfs(
421 local,
422 Direction::Upside,
423 &mut find_root_operator,
424 &mut Self::equivalent_edge_validator,
425 true,
426 &mut seen,
427 );
428 seen.clear();
429 self.dfs(
430 root.get(),
431 Direction::Downside,
432 &mut find_equivalent_operator,
433 &mut Self::equivalent_edge_validator,
434 true,
435 &mut seen,
436 );
437 set
438 }
439
440 fn collect_by_direction(
441 &self,
442 local: Local,
443 self_included: bool,
444 direction: Direction,
445 ) -> HashSet<Local> {
446 let mut ret = HashSet::new();
447 let mut node_operator = |_: &DataflowGraph, idx: Local| -> DFSStatus {
448 ret.insert(idx);
449 DFSStatus::Continue
450 };
451 let mut seen = HashSet::new();
452 self.dfs(
453 local,
454 direction,
455 &mut node_operator,
456 &mut DataflowGraph::always_true_edge_validator,
457 true,
458 &mut seen,
459 );
460 if !self_included {
461 ret.remove(&local);
462 }
463 ret
464 }
465
466 pub fn collect_ancestor_locals(&self, local: Local, self_included: bool) -> HashSet<Local> {
467 self.collect_by_direction(local, self_included, Direction::Upside)
468 }
469
470 pub fn collect_descending_locals(&self, local: Local, self_included: bool) -> HashSet<Local> {
471 self.collect_by_direction(local, self_included, Direction::Downside)
472 }
473
474 pub fn get_field_sequence(&self, local: Local) -> Option<(Local, Vec<usize>)> {
475 let mut fields = vec![];
476 let var = Cell::new(local);
477 let mut node_operator = |graph: &DataflowGraph, idx: Local| -> DFSStatus {
478 if graph.is_marker(idx) {
479 DFSStatus::Continue
480 } else {
481 var.set(idx);
482 DFSStatus::Stop
483 }
484 };
485 let mut edge_validator = |graph: &DataflowGraph, idx: EdgeIdx| -> DFSStatus {
486 if let EdgeOp::Field(field) = graph.edges[idx].op {
487 fields.insert(0, field);
488 DFSStatus::Continue
489 } else {
490 DFSStatus::Stop
491 }
492 };
493 let mut seen = HashSet::new();
494 self.dfs(
495 local,
496 Direction::Upside,
497 &mut node_operator,
498 &mut edge_validator,
499 false,
500 &mut seen,
501 );
502 if fields.is_empty() {
503 None
504 } else {
505 Some((var.get(), fields))
506 }
507 }
508
509 pub fn trace_origin(&self, local: Local) -> Local {
512 let mut current = local;
513 let mut seen = HashSet::new();
514 for _ in 0..16 {
515 if !seen.insert(current) {
516 break;
517 }
518 let next = self.nodes[current]
519 .in_edges
520 .iter()
521 .find_map(|&ei| {
522 let e = &self.edges[ei];
523 if matches!(e.op, EdgeOp::Copy | EdgeOp::Move)
524 && !self.is_marker(e.src)
525 {
526 Some(e.src)
527 } else {
528 None
529 }
530 });
531 match next {
532 Some(src) if src != current => current = src,
533 _ => break,
534 }
535 }
536 current
537 }
538
539 pub fn is_from_tuple_field(&self, local: Local) -> bool {
544 let mut current = local;
545 let mut seen = HashSet::new();
546 for _ in 0..8 {
547 if !seen.insert(current) {
548 return false;
549 }
550 let mut next_local = None;
551 for &ei in &self.nodes[current].in_edges {
552 let e = &self.edges[ei];
553 if !matches!(e.op, EdgeOp::Copy | EdgeOp::Move) {
554 continue;
555 }
556 if self.is_marker(e.src) {
557 if self.marker_chain_has_field(e.src) {
558 return true;
559 }
560 if let Some(real) = self.marker_to_real(e.src) {
561 next_local = Some(real);
562 break;
563 }
564 } else {
565 next_local = Some(e.src);
566 break;
567 }
568 }
569 match next_local {
570 Some(src) if src != current => current = src,
571 _ => return false,
572 }
573 }
574 false
575 }
576
577 fn marker_to_real(&self, marker: Local) -> Option<Local> {
579 let mut current = marker;
580 for _ in 0..8 {
581 if !self.is_marker(current) {
582 return Some(current);
583 }
584 current = self.nodes[current]
585 .in_edges
586 .first()
587 .map(|&ei| self.edges[ei].src)?;
588 }
589 None
590 }
591
592 fn marker_chain_has_field(&self, marker: Local) -> bool {
594 let mut current = marker;
595 for _ in 0..8 {
596 if !self.is_marker(current) {
597 return false;
598 }
599 let ei = match self.nodes[current].in_edges.first() {
600 Some(&ei) => ei,
601 None => return false,
602 };
603 if matches!(self.edges[ei].op, EdgeOp::Field(_)) {
604 return true;
605 }
606 current = self.edges[ei].src;
607 }
608 false
609 }
610}
611
612#[derive(Clone, Copy)]
613pub enum Direction {
614 Upside,
615 Downside,
616 Both,
617}
618
619pub enum DFSStatus {
620 Continue,
621 Stop,
622}
623
624impl DFSStatus {
625 pub fn and(s1: DFSStatus, s2: DFSStatus) -> DFSStatus {
626 if matches!(s1, DFSStatus::Stop) || matches!(s2, DFSStatus::Stop) {
627 DFSStatus::Stop
628 } else {
629 DFSStatus::Continue
630 }
631 }
632
633 pub fn or(s1: DFSStatus, s2: DFSStatus) -> DFSStatus {
634 if matches!(s1, DFSStatus::Continue) || matches!(s2, DFSStatus::Continue) {
635 DFSStatus::Continue
636 } else {
637 DFSStatus::Stop
638 }
639 }
640}