]>
Commit | Line | Data |
---|---|---|
a1dfa0c6 XL |
1 | //! This pass adds validation calls (AcquireValid, ReleaseValid) where appropriate. |
2 | //! It has to be run really early, before transformations like inlining, because | |
3 | //! introducing these calls *adds* UB -- so, conceptually, this pass is actually part | |
4 | //! of MIR building, and only after this pass we think of the program has having the | |
5 | //! normal MIR semantics. | |
6 | ||
c295e0f8 | 7 | use crate::MirPass; |
ba9703b0 XL |
8 | use rustc_middle::mir::*; |
9 | use rustc_middle::ty::{self, Ty, TyCtxt}; | |
a1dfa0c6 XL |
10 | |
11 | pub struct AddRetag; | |
12 | ||
13 | /// Determines whether this place is "stable": Whether, if we evaluate it again | |
14 | /// after the assignment, we can be sure to obtain the same place value. | |
15 | /// (Concurrent accesses by other threads are no problem as these are anyway non-atomic | |
16 | /// copies. Data races are UB.) | |
74b04a01 | 17 | fn is_stable(place: PlaceRef<'_>) -> bool { |
064997fb FG |
18 | // Which place this evaluates to can change with any memory write, |
19 | // so cannot assume deref to be stable. | |
20 | !place.has_deref() | |
a1dfa0c6 XL |
21 | } |
22 | ||
064997fb FG |
23 | /// Determine whether this type may contain a reference (or box), and thus needs retagging. |
24 | /// We will only recurse `depth` times into Tuples/ADTs to bound the cost of this. | |
25 | fn may_contain_reference<'tcx>(ty: Ty<'tcx>, depth: u32, tcx: TyCtxt<'tcx>) -> bool { | |
1b1a35ee | 26 | match ty.kind() { |
a1dfa0c6 | 27 | // Primitive types that are not references |
dfeec247 XL |
28 | ty::Bool |
29 | | ty::Char | |
30 | | ty::Float(_) | |
31 | | ty::Int(_) | |
32 | | ty::Uint(_) | |
33 | | ty::RawPtr(..) | |
34 | | ty::FnPtr(..) | |
35 | | ty::Str | |
36 | | ty::FnDef(..) | |
37 | | ty::Never => false, | |
a1dfa0c6 XL |
38 | // References |
39 | ty::Ref(..) => true, | |
40 | ty::Adt(..) if ty.is_box() => true, | |
064997fb FG |
41 | // Compound types: recurse |
42 | ty::Array(ty, _) | ty::Slice(ty) => { | |
43 | // This does not branch so we keep the depth the same. | |
44 | may_contain_reference(*ty, depth, tcx) | |
45 | } | |
46 | ty::Tuple(tys) => { | |
47 | depth == 0 || tys.iter().any(|ty| may_contain_reference(ty, depth - 1, tcx)) | |
48 | } | |
49 | ty::Adt(adt, subst) => { | |
50 | depth == 0 | |
51 | || adt.variants().iter().any(|v| { | |
52 | v.fields.iter().any(|f| may_contain_reference(f.ty(tcx, subst), depth - 1, tcx)) | |
53 | }) | |
54 | } | |
a1dfa0c6 XL |
55 | // Conservative fallback |
56 | _ => true, | |
57 | } | |
58 | } | |
59 | ||
e1599b0c | 60 | impl<'tcx> MirPass<'tcx> for AddRetag { |
a2a8927a | 61 | fn is_enabled(&self, sess: &rustc_session::Session) -> bool { |
064997fb | 62 | sess.opts.unstable_opts.mir_emit_retag |
a2a8927a | 63 | } |
ba9703b0 | 64 | |
a2a8927a | 65 | fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { |
ba9703b0 | 66 | // We need an `AllCallEdges` pass before we can do any work. |
29967ef6 | 67 | super::add_call_guards::AllCallEdges.run_pass(tcx, body); |
ba9703b0 | 68 | |
064997fb FG |
69 | let basic_blocks = body.basic_blocks.as_mut(); |
70 | let local_decls = &body.local_decls; | |
a1dfa0c6 XL |
71 | let needs_retag = |place: &Place<'tcx>| { |
72 | // FIXME: Instead of giving up for unstable places, we should introduce | |
73 | // a temporary and retag on that. | |
04454e1e | 74 | is_stable(place.as_ref()) |
064997fb FG |
75 | && may_contain_reference(place.ty(&*local_decls, tcx).ty, /*depth*/ 3, tcx) |
76 | && !local_decls[place.local].is_deref_temp() | |
a1dfa0c6 | 77 | }; |
29967ef6 XL |
78 | let place_base_raw = |place: &Place<'tcx>| { |
79 | // If this is a `Deref`, get the type of what we are deref'ing. | |
064997fb FG |
80 | if place.has_deref() { |
81 | let ty = &local_decls[place.local].ty; | |
29967ef6 XL |
82 | ty.is_unsafe_ptr() |
83 | } else { | |
84 | // Not a deref, and thus not raw. | |
85 | false | |
86 | } | |
87 | }; | |
a1dfa0c6 XL |
88 | |
89 | // PART 1 | |
90 | // Retag arguments at the beginning of the start block. | |
91 | { | |
a1dfa0c6 | 92 | // Gather all arguments, skip return value. |
f2b60f7d FG |
93 | let places = local_decls.iter_enumerated().skip(1).take(body.arg_count).filter_map( |
94 | |(local, decl)| { | |
95 | let place = Place::from(local); | |
96 | needs_retag(&place).then_some((place, decl.source_info)) | |
97 | }, | |
98 | ); | |
99 | ||
a1dfa0c6 | 100 | // Emit their retags. |
dfeec247 XL |
101 | basic_blocks[START_BLOCK].statements.splice( |
102 | 0..0, | |
f2b60f7d | 103 | places.map(|(place, source_info)| Statement { |
a1dfa0c6 | 104 | source_info, |
94222f64 | 105 | kind: StatementKind::Retag(RetagKind::FnEntry, Box::new(place)), |
dfeec247 | 106 | }), |
a1dfa0c6 XL |
107 | ); |
108 | } | |
109 | ||
110 | // PART 2 | |
111 | // Retag return values of functions. Also escape-to-raw the argument of `drop`. | |
112 | // We collect the return destinations because we cannot mutate while iterating. | |
3dfed10e XL |
113 | let returns = basic_blocks |
114 | .iter_mut() | |
115 | .filter_map(|block_data| { | |
116 | match block_data.terminator().kind { | |
923072b8 FG |
117 | TerminatorKind::Call { target: Some(target), destination, .. } |
118 | if needs_retag(&destination) => | |
3dfed10e XL |
119 | { |
120 | // Remember the return destination for later | |
923072b8 | 121 | Some((block_data.terminator().source_info, destination, target)) |
a1dfa0c6 | 122 | } |
3dfed10e | 123 | |
a1dfa0c6 | 124 | // `Drop` is also a call, but it doesn't return anything so we are good. |
3dfed10e | 125 | TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } => None, |
a1dfa0c6 | 126 | // Not a block ending in a Call -> ignore. |
3dfed10e | 127 | _ => None, |
a1dfa0c6 | 128 | } |
3dfed10e XL |
129 | }) |
130 | .collect::<Vec<_>>(); | |
a1dfa0c6 XL |
131 | // Now we go over the returns we collected to retag the return values. |
132 | for (source_info, dest_place, dest_block) in returns { | |
dfeec247 XL |
133 | basic_blocks[dest_block].statements.insert( |
134 | 0, | |
135 | Statement { | |
136 | source_info, | |
94222f64 | 137 | kind: StatementKind::Retag(RetagKind::Default, Box::new(dest_place)), |
dfeec247 XL |
138 | }, |
139 | ); | |
a1dfa0c6 XL |
140 | } |
141 | ||
142 | // PART 3 | |
143 | // Add retag after assignment. | |
144 | for block_data in basic_blocks { | |
145 | // We want to insert statements as we iterate. To this end, we | |
146 | // iterate backwards using indices. | |
147 | for i in (0..block_data.statements.len()).rev() { | |
0731742a | 148 | let (retag_kind, place) = match block_data.statements[i].kind { |
29967ef6 XL |
149 | // Retag-as-raw after escaping to a raw pointer, if the referent |
150 | // is not already a raw pointer. | |
151 | StatementKind::Assign(box (lplace, Rvalue::AddressOf(_, ref rplace))) | |
152 | if !place_base_raw(rplace) => | |
153 | { | |
154 | (RetagKind::Raw, lplace) | |
a1dfa0c6 | 155 | } |
29967ef6 | 156 | // Retag after assignments of reference type. |
dfeec247 | 157 | StatementKind::Assign(box (ref place, ref rvalue)) if needs_retag(place) => { |
0731742a XL |
158 | let kind = match rvalue { |
159 | Rvalue::Ref(_, borrow_kind, _) | |
dfeec247 XL |
160 | if borrow_kind.allows_two_phase_borrow() => |
161 | { | |
162 | RetagKind::TwoPhase | |
163 | } | |
164 | _ => RetagKind::Default, | |
0731742a | 165 | }; |
dfeec247 | 166 | (kind, *place) |
a1dfa0c6 XL |
167 | } |
168 | // Do nothing for the rest | |
0731742a | 169 | _ => continue, |
a1dfa0c6 | 170 | }; |
0731742a XL |
171 | // Insert a retag after the statement. |
172 | let source_info = block_data.statements[i].source_info; | |
dfeec247 XL |
173 | block_data.statements.insert( |
174 | i + 1, | |
94222f64 XL |
175 | Statement { |
176 | source_info, | |
177 | kind: StatementKind::Retag(retag_kind, Box::new(place)), | |
178 | }, | |
dfeec247 | 179 | ); |
a1dfa0c6 XL |
180 | } |
181 | } | |
182 | } | |
183 | } |