]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/add_retag.rs
Changes the type `mir::Mir` into `mir::Body`
[rust.git] / src / librustc_mir / transform / add_retag.rs
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
7 use rustc::ty::{self, Ty, TyCtxt};
8 use rustc::mir::*;
9 use crate::transform::{MirPass, MirSource};
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.)
17 fn is_stable<'tcx>(
18     place: &Place<'tcx>,
19 ) -> bool {
20     use rustc::mir::Place::*;
21
22     match *place {
23         // Locals and statics have stable addresses, for sure
24         Base(PlaceBase::Local { .. }) |
25         Base(PlaceBase::Static { .. }) =>
26             true,
27         // Recurse for projections
28         Projection(ref proj) => {
29             match proj.elem {
30                 // Which place this evaluates to can change with any memory write,
31                 // so cannot assume this to be stable.
32                 ProjectionElem::Deref =>
33                     false,
34                 // Array indices are intersting, but MIR building generates a *fresh*
35                 // temporary for every array access, so the index cannot be changed as
36                 // a side-effect.
37                 ProjectionElem::Index { .. } |
38                 // The rest is completely boring, they just offset by a constant.
39                 ProjectionElem::Field { .. } |
40                 ProjectionElem::ConstantIndex { .. } |
41                 ProjectionElem::Subslice { .. } |
42                 ProjectionElem::Downcast { .. } =>
43                     is_stable(&proj.base),
44             }
45         }
46     }
47 }
48
49 /// Determine whether this type may have a reference in it, recursing below compound types but
50 /// not below references.
51 fn may_have_reference<'a, 'gcx, 'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
52     match ty.sty {
53         // Primitive types that are not references
54         ty::Bool | ty::Char |
55         ty::Float(_) | ty::Int(_) | ty::Uint(_) |
56         ty::RawPtr(..) | ty::FnPtr(..) |
57         ty::Str | ty::FnDef(..) | ty::Never =>
58             false,
59         // References
60         ty::Ref(..) => true,
61         ty::Adt(..) if ty.is_box() => true,
62         // Compound types
63         ty::Array(ty, ..) | ty::Slice(ty) =>
64             may_have_reference(ty, tcx),
65         ty::Tuple(tys) =>
66             tys.iter().any(|ty| may_have_reference(ty.expect_ty(), tcx)),
67         ty::Adt(adt, substs) =>
68             adt.variants.iter().any(|v| v.fields.iter().any(|f|
69                 may_have_reference(f.ty(tcx, substs), tcx)
70             )),
71         // Conservative fallback
72         _ => true,
73     }
74 }
75
76 impl MirPass for AddRetag {
77     fn run_pass<'a, 'tcx>(&self,
78                           tcx: TyCtxt<'a, 'tcx, 'tcx>,
79                           _src: MirSource<'tcx>,
80                           mir: &mut Body<'tcx>)
81     {
82         if !tcx.sess.opts.debugging_opts.mir_emit_retag {
83             return;
84         }
85         let (span, arg_count) = (mir.span, mir.arg_count);
86         let (basic_blocks, local_decls) = mir.basic_blocks_and_local_decls_mut();
87         let needs_retag = |place: &Place<'tcx>| {
88             // FIXME: Instead of giving up for unstable places, we should introduce
89             // a temporary and retag on that.
90             is_stable(place) && may_have_reference(place.ty(&*local_decls, tcx).ty, tcx)
91         };
92
93         // PART 1
94         // Retag arguments at the beginning of the start block.
95         {
96             let source_info = SourceInfo {
97                 scope: OUTERMOST_SOURCE_SCOPE,
98                 span: span, // FIXME: Consider using just the span covering the function
99                             // argument declaration.
100             };
101             // Gather all arguments, skip return value.
102             let places = local_decls.iter_enumerated().skip(1).take(arg_count)
103                     .map(|(local, _)| Place::Base(PlaceBase::Local(local)))
104                     .filter(needs_retag)
105                     .collect::<Vec<_>>();
106             // Emit their retags.
107             basic_blocks[START_BLOCK].statements.splice(0..0,
108                 places.into_iter().map(|place| Statement {
109                     source_info,
110                     kind: StatementKind::Retag(RetagKind::FnEntry, place),
111                 })
112             );
113         }
114
115         // PART 2
116         // Retag return values of functions.  Also escape-to-raw the argument of `drop`.
117         // We collect the return destinations because we cannot mutate while iterating.
118         let mut returns: Vec<(SourceInfo, Place<'tcx>, BasicBlock)> = Vec::new();
119         for block_data in basic_blocks.iter_mut() {
120             match block_data.terminator().kind {
121                 TerminatorKind::Call { ref destination, .. } => {
122                     // Remember the return destination for later
123                     if let Some(ref destination) = destination {
124                         if needs_retag(&destination.0) {
125                             returns.push((
126                                 block_data.terminator().source_info,
127                                 destination.0.clone(),
128                                 destination.1,
129                             ));
130                         }
131                     }
132                 }
133                 TerminatorKind::Drop { .. } |
134                 TerminatorKind::DropAndReplace { .. } => {
135                     // `Drop` is also a call, but it doesn't return anything so we are good.
136                 }
137                 _ => {
138                     // Not a block ending in a Call -> ignore.
139                 }
140             }
141         }
142         // Now we go over the returns we collected to retag the return values.
143         for (source_info, dest_place, dest_block) in returns {
144             basic_blocks[dest_block].statements.insert(0, Statement {
145                 source_info,
146                 kind: StatementKind::Retag(RetagKind::Default, dest_place),
147             });
148         }
149
150         // PART 3
151         // Add retag after assignment.
152         for block_data in basic_blocks {
153             // We want to insert statements as we iterate.  To this end, we
154             // iterate backwards using indices.
155             for i in (0..block_data.statements.len()).rev() {
156                 let (retag_kind, place) = match block_data.statements[i].kind {
157                     // If we are casting *from* a reference, we may have to retag-as-raw.
158                     StatementKind::Assign(ref place, box Rvalue::Cast(
159                         CastKind::Misc,
160                         ref src,
161                         dest_ty,
162                     )) => {
163                         let src_ty = src.ty(&*local_decls, tcx);
164                         if src_ty.is_region_ptr() {
165                             // The only `Misc` casts on references are those creating raw pointers.
166                             assert!(dest_ty.is_unsafe_ptr());
167                             (RetagKind::Raw, place.clone())
168                         } else {
169                             // Some other cast, no retag
170                             continue
171                         }
172                     }
173                     // Assignments of reference or ptr type are the ones where we may have
174                     // to update tags.  This includes `x = &[mut] ...` and hence
175                     // we also retag after taking a reference!
176                     StatementKind::Assign(ref place, box ref rvalue) if needs_retag(place) => {
177                         let kind = match rvalue {
178                             Rvalue::Ref(_, borrow_kind, _)
179                                 if borrow_kind.allows_two_phase_borrow()
180                             =>
181                                 RetagKind::TwoPhase,
182                             _ =>
183                                 RetagKind::Default,
184                         };
185                         (kind, place.clone())
186                     }
187                     // Do nothing for the rest
188                     _ => continue,
189                 };
190                 // Insert a retag after the statement.
191                 let source_info = block_data.statements[i].source_info;
192                 block_data.statements.insert(i+1, Statement {
193                     source_info,
194                     kind: StatementKind::Retag(retag_kind, place),
195                 });
196             }
197         }
198     }
199 }