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