1 //! Performs a simple taint analysis, to find all live ranges that are reftyped.
2 
3 use crate::data_structures::{
4     InstPoint, Map, MoveInfo, MoveInfoElem, RangeFrag, RangeFragIx, RangeId, RealRange,
5     RealRangeIx, Reg, RegClass, RegToRangesMaps, TypedIxVec, VirtualRange, VirtualRangeIx,
6     VirtualReg,
7 };
8 use crate::sparse_set::{SparseSet, SparseSetU};
9 
10 use log::debug;
11 use smallvec::SmallVec;
12 
do_reftypes_analysis( rlr_env: &mut TypedIxVec<RealRangeIx, RealRange>, vlr_env: &mut TypedIxVec<VirtualRangeIx, VirtualRange>, frag_env: &TypedIxVec<RangeFragIx, RangeFrag>, reg_to_ranges_maps: &RegToRangesMaps, move_info: &MoveInfo, reftype_class: RegClass, reftyped_vregs: &Vec<VirtualReg>, )13 pub fn do_reftypes_analysis(
14     // From dataflow/liveness analysis.  Modified by setting their is_ref bit.
15     rlr_env: &mut TypedIxVec<RealRangeIx, RealRange>,
16     vlr_env: &mut TypedIxVec<VirtualRangeIx, VirtualRange>,
17     // From dataflow analysis
18     frag_env: &TypedIxVec<RangeFragIx, RangeFrag>,
19     reg_to_ranges_maps: &RegToRangesMaps,
20     move_info: &MoveInfo,
21     // As supplied by the client
22     reftype_class: RegClass,
23     reftyped_vregs: &Vec<VirtualReg>,
24 ) {
25     // Helper: find the RangeId (RealRange or VirtualRange) for a register at an InstPoint.
26     let find_range_id_for_reg = |pt: InstPoint, reg: Reg| -> RangeId {
27         if reg.is_real() {
28             for &rlrix in &reg_to_ranges_maps.rreg_to_rlrs_map[reg.get_index() as usize] {
29                 if rlr_env[rlrix].sorted_frags.contains_pt(frag_env, pt) {
30                     return RangeId::new_real(rlrix);
31                 }
32             }
33         } else {
34             for &vlrix in &reg_to_ranges_maps.vreg_to_vlrs_map[reg.get_index() as usize] {
35                 if vlr_env[vlrix].sorted_frags.contains_pt(pt) {
36                     return RangeId::new_virtual(vlrix);
37                 }
38             }
39         }
40         panic!("do_reftypes_analysis::find_range_for_reg: can't find range");
41     };
42 
43     // The game here is: starting with `reftyped_vregs`, find *all* the VirtualRanges and
44     // RealRanges to which refness can flow, via instructions which the client's `is_move`
45     // function considers to be moves.
46 
47     // This is done in three stages:
48     //
49     // (1) Create a mapping from source (virtual or real) ranges to sets of destination ranges.
50     //     We have `move_info`, which tells us which (virtual or real) regs are connected by
51     //     moves.  However, that's not directly useful -- we need to know which *ranges* are
52     //     connected by moves.  `move_info` as supplied helpfully indicates both source and
53     //     destination regs and ranges, so we can simply use that.
54     //
55     // (2) Similarly, convert `reftyped_vregs` into a set of reftyped ranges by consulting
56     //     `reg_to_ranges_maps`.
57     //
58     // (3) Compute the transitive closure of (1) starting from the ranges in (2).  This is done
59     //     by a depth first search of the graph implied by (1).
60 
61     // ====== Compute (1) above ======
62     // Each entry in `succ` maps from `src` to a `SparseSet<dsts>`, so to speak.  That is, for
63     // `d1`, `d2`, etc, in `dsts`, the function contains moves `d1 := src`, `d2 := src`, etc.
64     let mut succ = Map::<RangeId, SparseSetU<[RangeId; 4]>>::default();
65     for &MoveInfoElem { dst, src, iix, .. } in &move_info.moves {
66         // Don't waste time processing moves which can't possibly be of reftyped values.
67         debug_assert!(dst.get_class() == src.get_class());
68         if dst.get_class() != reftype_class {
69             continue;
70         }
71         let src_range = find_range_id_for_reg(InstPoint::new_use(iix), src);
72         let dst_range = find_range_id_for_reg(InstPoint::new_def(iix), dst);
73         debug!(
74             "move from {:?} (range {:?}) to {:?} (range {:?}) at inst {:?}",
75             src, src_range, dst, dst_range, iix
76         );
77         match succ.get_mut(&src_range) {
78             Some(dst_ranges) => dst_ranges.insert(dst_range),
79             None => {
80                 // Re `; 4`: we expect most copies copy a register to only a few destinations.
81                 let mut dst_ranges = SparseSetU::<[RangeId; 4]>::empty();
82                 dst_ranges.insert(dst_range);
83                 let r = succ.insert(src_range, dst_ranges);
84                 assert!(r.is_none());
85             }
86         }
87     }
88 
89     // ====== Compute (2) above ======
90     let mut reftyped_ranges = SparseSet::<RangeId>::empty();
91     for vreg in reftyped_vregs {
92         // If this fails, the client has been telling is that some virtual reg is reftyped, yet
93         // it doesn't belong to the class of regs that it claims can carry refs.  So the client
94         // is buggy.
95         debug_assert!(vreg.get_class() == reftype_class);
96         for vlrix in &reg_to_ranges_maps.vreg_to_vlrs_map[vreg.get_index()] {
97             debug!("range {:?} is reffy due to reffy vreg {:?}", vlrix, vreg);
98             reftyped_ranges.insert(RangeId::new_virtual(*vlrix));
99         }
100     }
101 
102     // ====== Compute (3) above ======
103     // Almost all chains of copies will be less than 64 long, I would guess.
104     let mut stack = SmallVec::<[RangeId; 64]>::new();
105     let mut visited = reftyped_ranges.clone();
106     for start_point_range in reftyped_ranges.iter() {
107         // Perform DFS from `start_point_range`.
108         stack.clear();
109         stack.push(*start_point_range);
110         while let Some(src_range) = stack.pop() {
111             visited.insert(src_range);
112             if let Some(dst_ranges) = succ.get(&src_range) {
113                 for dst_range in dst_ranges.iter() {
114                     if !visited.contains(*dst_range) {
115                         stack.push(*dst_range);
116                     }
117                 }
118             }
119         }
120     }
121 
122     // Finally, annotate rlr_env/vlr_env with the results of the analysis.  (That was the whole
123     // point!)
124     for range in visited.iter() {
125         if range.is_real() {
126             let rrange = &mut rlr_env[range.to_real()];
127             debug_assert!(!rrange.is_ref);
128             debug!(" -> rrange {:?} is reffy", range.to_real());
129             rrange.is_ref = true;
130         } else {
131             let vrange = &mut vlr_env[range.to_virtual()];
132             debug_assert!(!vrange.is_ref);
133             debug!(" -> rrange {:?} is reffy", range.to_virtual());
134             vrange.is_ref = true;
135         }
136     }
137 }
138