]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir/src/transform/add_retag.rs
New upstream version 1.56.0+dfsg1
[rustc.git] / compiler / rustc_mir / src / transform / add_retag.rs
CommitLineData
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 7use crate::transform::MirPass;
ba9703b0
XL
8use rustc_middle::mir::*;
9use rustc_middle::ty::{self, Ty, TyCtxt};
a1dfa0c6
XL
10
11pub 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 17fn 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 37fn 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 60impl<'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}