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 ®_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 ®_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 ®_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