]>
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 | ||
064997fb FG |
13 | /// Determine whether this type may contain a reference (or box), and thus needs retagging. |
14 | /// We will only recurse `depth` times into Tuples/ADTs to bound the cost of this. | |
15 | fn may_contain_reference<'tcx>(ty: Ty<'tcx>, depth: u32, tcx: TyCtxt<'tcx>) -> bool { | |
1b1a35ee | 16 | match ty.kind() { |
a1dfa0c6 | 17 | // Primitive types that are not references |
dfeec247 XL |
18 | ty::Bool |
19 | | ty::Char | |
20 | | ty::Float(_) | |
21 | | ty::Int(_) | |
22 | | ty::Uint(_) | |
23 | | ty::RawPtr(..) | |
24 | | ty::FnPtr(..) | |
25 | | ty::Str | |
26 | | ty::FnDef(..) | |
27 | | ty::Never => false, | |
a1dfa0c6 XL |
28 | // References |
29 | ty::Ref(..) => true, | |
30 | ty::Adt(..) if ty.is_box() => true, | |
064997fb FG |
31 | // Compound types: recurse |
32 | ty::Array(ty, _) | ty::Slice(ty) => { | |
33 | // This does not branch so we keep the depth the same. | |
34 | may_contain_reference(*ty, depth, tcx) | |
35 | } | |
36 | ty::Tuple(tys) => { | |
37 | depth == 0 || tys.iter().any(|ty| may_contain_reference(ty, depth - 1, tcx)) | |
38 | } | |
39 | ty::Adt(adt, subst) => { | |
40 | depth == 0 | |
41 | || adt.variants().iter().any(|v| { | |
42 | v.fields.iter().any(|f| may_contain_reference(f.ty(tcx, subst), depth - 1, tcx)) | |
43 | }) | |
44 | } | |
a1dfa0c6 XL |
45 | // Conservative fallback |
46 | _ => true, | |
47 | } | |
48 | } | |
49 | ||
e1599b0c | 50 | impl<'tcx> MirPass<'tcx> for AddRetag { |
a2a8927a | 51 | fn is_enabled(&self, sess: &rustc_session::Session) -> bool { |
064997fb | 52 | sess.opts.unstable_opts.mir_emit_retag |
a2a8927a | 53 | } |
ba9703b0 | 54 | |
a2a8927a | 55 | fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { |
ba9703b0 | 56 | // We need an `AllCallEdges` pass before we can do any work. |
29967ef6 | 57 | super::add_call_guards::AllCallEdges.run_pass(tcx, body); |
ba9703b0 | 58 | |
064997fb FG |
59 | let basic_blocks = body.basic_blocks.as_mut(); |
60 | let local_decls = &body.local_decls; | |
a1dfa0c6 | 61 | let needs_retag = |place: &Place<'tcx>| { |
487cf647 | 62 | !place.has_deref() // we're not eally interested in stores to "outside" locations, they are hard to keep track of anyway |
064997fb FG |
63 | && may_contain_reference(place.ty(&*local_decls, tcx).ty, /*depth*/ 3, tcx) |
64 | && !local_decls[place.local].is_deref_temp() | |
a1dfa0c6 XL |
65 | }; |
66 | ||
67 | // PART 1 | |
68 | // Retag arguments at the beginning of the start block. | |
69 | { | |
a1dfa0c6 | 70 | // Gather all arguments, skip return value. |
f2b60f7d FG |
71 | let places = local_decls.iter_enumerated().skip(1).take(body.arg_count).filter_map( |
72 | |(local, decl)| { | |
73 | let place = Place::from(local); | |
74 | needs_retag(&place).then_some((place, decl.source_info)) | |
75 | }, | |
76 | ); | |
77 | ||
a1dfa0c6 | 78 | // Emit their retags. |
dfeec247 XL |
79 | basic_blocks[START_BLOCK].statements.splice( |
80 | 0..0, | |
f2b60f7d | 81 | places.map(|(place, source_info)| Statement { |
a1dfa0c6 | 82 | source_info, |
94222f64 | 83 | kind: StatementKind::Retag(RetagKind::FnEntry, Box::new(place)), |
dfeec247 | 84 | }), |
a1dfa0c6 XL |
85 | ); |
86 | } | |
87 | ||
88 | // PART 2 | |
487cf647 | 89 | // Retag return values of functions. |
a1dfa0c6 | 90 | // We collect the return destinations because we cannot mutate while iterating. |
3dfed10e XL |
91 | let returns = basic_blocks |
92 | .iter_mut() | |
93 | .filter_map(|block_data| { | |
94 | match block_data.terminator().kind { | |
923072b8 FG |
95 | TerminatorKind::Call { target: Some(target), destination, .. } |
96 | if needs_retag(&destination) => | |
3dfed10e XL |
97 | { |
98 | // Remember the return destination for later | |
923072b8 | 99 | Some((block_data.terminator().source_info, destination, target)) |
a1dfa0c6 | 100 | } |
3dfed10e | 101 | |
a1dfa0c6 | 102 | // `Drop` is also a call, but it doesn't return anything so we are good. |
3dfed10e | 103 | TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } => None, |
a1dfa0c6 | 104 | // Not a block ending in a Call -> ignore. |
3dfed10e | 105 | _ => None, |
a1dfa0c6 | 106 | } |
3dfed10e XL |
107 | }) |
108 | .collect::<Vec<_>>(); | |
a1dfa0c6 XL |
109 | // Now we go over the returns we collected to retag the return values. |
110 | for (source_info, dest_place, dest_block) in returns { | |
dfeec247 XL |
111 | basic_blocks[dest_block].statements.insert( |
112 | 0, | |
113 | Statement { | |
114 | source_info, | |
94222f64 | 115 | kind: StatementKind::Retag(RetagKind::Default, Box::new(dest_place)), |
dfeec247 XL |
116 | }, |
117 | ); | |
a1dfa0c6 XL |
118 | } |
119 | ||
120 | // PART 3 | |
487cf647 | 121 | // Add retag after assignments where data "enters" this function: the RHS is behind a deref and the LHS is not. |
a1dfa0c6 | 122 | for block_data in basic_blocks { |
9c376795 | 123 | // We want to insert statements as we iterate. To this end, we |
a1dfa0c6 XL |
124 | // iterate backwards using indices. |
125 | for i in (0..block_data.statements.len()).rev() { | |
0731742a | 126 | let (retag_kind, place) = match block_data.statements[i].kind { |
29967ef6 | 127 | // Retag after assignments of reference type. |
dfeec247 | 128 | StatementKind::Assign(box (ref place, ref rvalue)) if needs_retag(place) => { |
487cf647 FG |
129 | let add_retag = match rvalue { |
130 | // Ptr-creating operations already do their own internal retagging, no | |
131 | // need to also add a retag statement. | |
132 | Rvalue::Ref(..) | Rvalue::AddressOf(..) => false, | |
133 | _ => true, | |
0731742a | 134 | }; |
487cf647 FG |
135 | if add_retag { |
136 | (RetagKind::Default, *place) | |
137 | } else { | |
138 | continue; | |
139 | } | |
a1dfa0c6 XL |
140 | } |
141 | // Do nothing for the rest | |
0731742a | 142 | _ => continue, |
a1dfa0c6 | 143 | }; |
0731742a XL |
144 | // Insert a retag after the statement. |
145 | let source_info = block_data.statements[i].source_info; | |
dfeec247 XL |
146 | block_data.statements.insert( |
147 | i + 1, | |
94222f64 XL |
148 | Statement { |
149 | source_info, | |
150 | kind: StatementKind::Retag(retag_kind, Box::new(place)), | |
151 | }, | |
dfeec247 | 152 | ); |
a1dfa0c6 XL |
153 | } |
154 | } | |
155 | } | |
156 | } |