]>
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 | ||
29967ef6 | 7 | use crate::transform::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 { |
e1599b0c XL |
18 | place.projection.iter().all(|elem| { |
19 | match elem { | |
416331ca XL |
20 | // Which place this evaluates to can change with any memory write, |
21 | // so cannot assume this to be stable. | |
e1599b0c | 22 | ProjectionElem::Deref => false, |
74b04a01 | 23 | // Array indices are interesting, but MIR building generates a *fresh* |
416331ca XL |
24 | // temporary for every array access, so the index cannot be changed as |
25 | // a side-effect. | |
26 | ProjectionElem::Index { .. } | | |
27 | // The rest is completely boring, they just offset by a constant. | |
28 | ProjectionElem::Field { .. } | | |
29 | ProjectionElem::ConstantIndex { .. } | | |
30 | ProjectionElem::Subslice { .. } | | |
e1599b0c | 31 | ProjectionElem::Downcast { .. } => true, |
a1dfa0c6 | 32 | } |
e1599b0c | 33 | }) |
a1dfa0c6 XL |
34 | } |
35 | ||
e1599b0c | 36 | /// Determine whether this type may be a reference (or box), and thus needs retagging. |
ba9703b0 | 37 | fn may_be_reference(ty: Ty<'tcx>) -> bool { |
1b1a35ee | 38 | match ty.kind() { |
a1dfa0c6 | 39 | // Primitive types that are not references |
dfeec247 XL |
40 | ty::Bool |
41 | | ty::Char | |
42 | | ty::Float(_) | |
43 | | ty::Int(_) | |
44 | | ty::Uint(_) | |
45 | | ty::RawPtr(..) | |
46 | | ty::FnPtr(..) | |
47 | | ty::Str | |
48 | | ty::FnDef(..) | |
49 | | ty::Never => false, | |
a1dfa0c6 XL |
50 | // References |
51 | ty::Ref(..) => true, | |
52 | ty::Adt(..) if ty.is_box() => true, | |
e1599b0c | 53 | // Compound types are not references |
dfeec247 | 54 | ty::Array(..) | ty::Slice(..) | ty::Tuple(..) | ty::Adt(..) => false, |
a1dfa0c6 XL |
55 | // Conservative fallback |
56 | _ => true, | |
57 | } | |
58 | } | |
59 | ||
e1599b0c | 60 | impl<'tcx> MirPass<'tcx> for AddRetag { |
29967ef6 | 61 | fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { |
a1dfa0c6 XL |
62 | if !tcx.sess.opts.debugging_opts.mir_emit_retag { |
63 | return; | |
64 | } | |
ba9703b0 XL |
65 | |
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 | |
dc9dc135 XL |
69 | let (span, arg_count) = (body.span, body.arg_count); |
70 | let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); | |
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. | |
dfeec247 | 74 | is_stable(place.as_ref()) && may_be_reference(place.ty(&*local_decls, tcx).ty) |
a1dfa0c6 | 75 | }; |
29967ef6 XL |
76 | let place_base_raw = |place: &Place<'tcx>| { |
77 | // If this is a `Deref`, get the type of what we are deref'ing. | |
78 | let deref_base = | |
79 | place.projection.iter().rposition(|p| matches!(p, ProjectionElem::Deref)); | |
80 | if let Some(deref_base) = deref_base { | |
81 | let base_proj = &place.projection[..deref_base]; | |
82 | let ty = Place::ty_from(place.local, base_proj, &*local_decls, tcx).ty; | |
83 | ty.is_unsafe_ptr() | |
84 | } else { | |
85 | // Not a deref, and thus not raw. | |
86 | false | |
87 | } | |
88 | }; | |
a1dfa0c6 XL |
89 | |
90 | // PART 1 | |
91 | // Retag arguments at the beginning of the start block. | |
92 | { | |
f9f354fc XL |
93 | // FIXME: Consider using just the span covering the function |
94 | // argument declaration. | |
95 | let source_info = SourceInfo::outermost(span); | |
a1dfa0c6 | 96 | // Gather all arguments, skip return value. |
dfeec247 XL |
97 | let places = local_decls |
98 | .iter_enumerated() | |
99 | .skip(1) | |
100 | .take(arg_count) | |
101 | .map(|(local, _)| Place::from(local)) | |
3dfed10e | 102 | .filter(needs_retag); |
a1dfa0c6 | 103 | // Emit their retags. |
dfeec247 XL |
104 | basic_blocks[START_BLOCK].statements.splice( |
105 | 0..0, | |
3dfed10e | 106 | places.map(|place| Statement { |
a1dfa0c6 | 107 | source_info, |
94222f64 | 108 | kind: StatementKind::Retag(RetagKind::FnEntry, Box::new(place)), |
dfeec247 | 109 | }), |
a1dfa0c6 XL |
110 | ); |
111 | } | |
112 | ||
113 | // PART 2 | |
114 | // Retag return values of functions. Also escape-to-raw the argument of `drop`. | |
115 | // We collect the return destinations because we cannot mutate while iterating. | |
3dfed10e XL |
116 | let returns = basic_blocks |
117 | .iter_mut() | |
118 | .filter_map(|block_data| { | |
119 | match block_data.terminator().kind { | |
120 | TerminatorKind::Call { destination: Some(ref destination), .. } | |
121 | if needs_retag(&destination.0) => | |
122 | { | |
123 | // Remember the return destination for later | |
124 | Some((block_data.terminator().source_info, destination.0, destination.1)) | |
a1dfa0c6 | 125 | } |
3dfed10e | 126 | |
a1dfa0c6 | 127 | // `Drop` is also a call, but it doesn't return anything so we are good. |
3dfed10e | 128 | TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } => None, |
a1dfa0c6 | 129 | // Not a block ending in a Call -> ignore. |
3dfed10e | 130 | _ => None, |
a1dfa0c6 | 131 | } |
3dfed10e XL |
132 | }) |
133 | .collect::<Vec<_>>(); | |
a1dfa0c6 XL |
134 | // Now we go over the returns we collected to retag the return values. |
135 | for (source_info, dest_place, dest_block) in returns { | |
dfeec247 XL |
136 | basic_blocks[dest_block].statements.insert( |
137 | 0, | |
138 | Statement { | |
139 | source_info, | |
94222f64 | 140 | kind: StatementKind::Retag(RetagKind::Default, Box::new(dest_place)), |
dfeec247 XL |
141 | }, |
142 | ); | |
a1dfa0c6 XL |
143 | } |
144 | ||
145 | // PART 3 | |
146 | // Add retag after assignment. | |
147 | for block_data in basic_blocks { | |
148 | // We want to insert statements as we iterate. To this end, we | |
149 | // iterate backwards using indices. | |
150 | for i in (0..block_data.statements.len()).rev() { | |
0731742a | 151 | let (retag_kind, place) = match block_data.statements[i].kind { |
29967ef6 XL |
152 | // Retag-as-raw after escaping to a raw pointer, if the referent |
153 | // is not already a raw pointer. | |
154 | StatementKind::Assign(box (lplace, Rvalue::AddressOf(_, ref rplace))) | |
155 | if !place_base_raw(rplace) => | |
156 | { | |
157 | (RetagKind::Raw, lplace) | |
a1dfa0c6 | 158 | } |
29967ef6 | 159 | // Retag after assignments of reference type. |
dfeec247 | 160 | StatementKind::Assign(box (ref place, ref rvalue)) if needs_retag(place) => { |
0731742a XL |
161 | let kind = match rvalue { |
162 | Rvalue::Ref(_, borrow_kind, _) | |
dfeec247 XL |
163 | if borrow_kind.allows_two_phase_borrow() => |
164 | { | |
165 | RetagKind::TwoPhase | |
166 | } | |
167 | _ => RetagKind::Default, | |
0731742a | 168 | }; |
dfeec247 | 169 | (kind, *place) |
a1dfa0c6 XL |
170 | } |
171 | // Do nothing for the rest | |
0731742a | 172 | _ => continue, |
a1dfa0c6 | 173 | }; |
0731742a XL |
174 | // Insert a retag after the statement. |
175 | let source_info = block_data.statements[i].source_info; | |
dfeec247 XL |
176 | block_data.statements.insert( |
177 | i + 1, | |
94222f64 XL |
178 | Statement { |
179 | source_info, | |
180 | kind: StatementKind::Retag(retag_kind, Box::new(place)), | |
181 | }, | |
dfeec247 | 182 | ); |
a1dfa0c6 XL |
183 | } |
184 | } | |
185 | } | |
186 | } |