]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir_transform/src/add_retag.rs
New upstream version 1.65.0+dfsg1
[rustc.git] / compiler / rustc_mir_transform / src / 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
c295e0f8 7use crate::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 {
064997fb
FG
18 // Which place this evaluates to can change with any memory write,
19 // so cannot assume deref to be stable.
20 !place.has_deref()
a1dfa0c6
XL
21}
22
064997fb
FG
23/// Determine whether this type may contain a reference (or box), and thus needs retagging.
24/// We will only recurse `depth` times into Tuples/ADTs to bound the cost of this.
25fn may_contain_reference<'tcx>(ty: Ty<'tcx>, depth: u32, tcx: TyCtxt<'tcx>) -> bool {
1b1a35ee 26 match ty.kind() {
a1dfa0c6 27 // Primitive types that are not references
dfeec247
XL
28 ty::Bool
29 | ty::Char
30 | ty::Float(_)
31 | ty::Int(_)
32 | ty::Uint(_)
33 | ty::RawPtr(..)
34 | ty::FnPtr(..)
35 | ty::Str
36 | ty::FnDef(..)
37 | ty::Never => false,
a1dfa0c6
XL
38 // References
39 ty::Ref(..) => true,
40 ty::Adt(..) if ty.is_box() => true,
064997fb
FG
41 // Compound types: recurse
42 ty::Array(ty, _) | ty::Slice(ty) => {
43 // This does not branch so we keep the depth the same.
44 may_contain_reference(*ty, depth, tcx)
45 }
46 ty::Tuple(tys) => {
47 depth == 0 || tys.iter().any(|ty| may_contain_reference(ty, depth - 1, tcx))
48 }
49 ty::Adt(adt, subst) => {
50 depth == 0
51 || adt.variants().iter().any(|v| {
52 v.fields.iter().any(|f| may_contain_reference(f.ty(tcx, subst), depth - 1, tcx))
53 })
54 }
a1dfa0c6
XL
55 // Conservative fallback
56 _ => true,
57 }
58}
59
e1599b0c 60impl<'tcx> MirPass<'tcx> for AddRetag {
a2a8927a 61 fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
064997fb 62 sess.opts.unstable_opts.mir_emit_retag
a2a8927a 63 }
ba9703b0 64
a2a8927a 65 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
ba9703b0 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
064997fb
FG
69 let basic_blocks = body.basic_blocks.as_mut();
70 let local_decls = &body.local_decls;
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.
04454e1e 74 is_stable(place.as_ref())
064997fb
FG
75 && may_contain_reference(place.ty(&*local_decls, tcx).ty, /*depth*/ 3, tcx)
76 && !local_decls[place.local].is_deref_temp()
a1dfa0c6 77 };
29967ef6
XL
78 let place_base_raw = |place: &Place<'tcx>| {
79 // If this is a `Deref`, get the type of what we are deref'ing.
064997fb
FG
80 if place.has_deref() {
81 let ty = &local_decls[place.local].ty;
29967ef6
XL
82 ty.is_unsafe_ptr()
83 } else {
84 // Not a deref, and thus not raw.
85 false
86 }
87 };
a1dfa0c6
XL
88
89 // PART 1
90 // Retag arguments at the beginning of the start block.
91 {
a1dfa0c6 92 // Gather all arguments, skip return value.
f2b60f7d
FG
93 let places = local_decls.iter_enumerated().skip(1).take(body.arg_count).filter_map(
94 |(local, decl)| {
95 let place = Place::from(local);
96 needs_retag(&place).then_some((place, decl.source_info))
97 },
98 );
99
a1dfa0c6 100 // Emit their retags.
dfeec247
XL
101 basic_blocks[START_BLOCK].statements.splice(
102 0..0,
f2b60f7d 103 places.map(|(place, source_info)| Statement {
a1dfa0c6 104 source_info,
94222f64 105 kind: StatementKind::Retag(RetagKind::FnEntry, Box::new(place)),
dfeec247 106 }),
a1dfa0c6
XL
107 );
108 }
109
110 // PART 2
111 // Retag return values of functions. Also escape-to-raw the argument of `drop`.
112 // We collect the return destinations because we cannot mutate while iterating.
3dfed10e
XL
113 let returns = basic_blocks
114 .iter_mut()
115 .filter_map(|block_data| {
116 match block_data.terminator().kind {
923072b8
FG
117 TerminatorKind::Call { target: Some(target), destination, .. }
118 if needs_retag(&destination) =>
3dfed10e
XL
119 {
120 // Remember the return destination for later
923072b8 121 Some((block_data.terminator().source_info, destination, target))
a1dfa0c6 122 }
3dfed10e 123
a1dfa0c6 124 // `Drop` is also a call, but it doesn't return anything so we are good.
3dfed10e 125 TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } => None,
a1dfa0c6 126 // Not a block ending in a Call -> ignore.
3dfed10e 127 _ => None,
a1dfa0c6 128 }
3dfed10e
XL
129 })
130 .collect::<Vec<_>>();
a1dfa0c6
XL
131 // Now we go over the returns we collected to retag the return values.
132 for (source_info, dest_place, dest_block) in returns {
dfeec247
XL
133 basic_blocks[dest_block].statements.insert(
134 0,
135 Statement {
136 source_info,
94222f64 137 kind: StatementKind::Retag(RetagKind::Default, Box::new(dest_place)),
dfeec247
XL
138 },
139 );
a1dfa0c6
XL
140 }
141
142 // PART 3
143 // Add retag after assignment.
144 for block_data in basic_blocks {
145 // We want to insert statements as we iterate. To this end, we
146 // iterate backwards using indices.
147 for i in (0..block_data.statements.len()).rev() {
0731742a 148 let (retag_kind, place) = match block_data.statements[i].kind {
29967ef6
XL
149 // Retag-as-raw after escaping to a raw pointer, if the referent
150 // is not already a raw pointer.
151 StatementKind::Assign(box (lplace, Rvalue::AddressOf(_, ref rplace)))
152 if !place_base_raw(rplace) =>
153 {
154 (RetagKind::Raw, lplace)
a1dfa0c6 155 }
29967ef6 156 // Retag after assignments of reference type.
dfeec247 157 StatementKind::Assign(box (ref place, ref rvalue)) if needs_retag(place) => {
0731742a
XL
158 let kind = match rvalue {
159 Rvalue::Ref(_, borrow_kind, _)
dfeec247
XL
160 if borrow_kind.allows_two_phase_borrow() =>
161 {
162 RetagKind::TwoPhase
163 }
164 _ => RetagKind::Default,
0731742a 165 };
dfeec247 166 (kind, *place)
a1dfa0c6
XL
167 }
168 // Do nothing for the rest
0731742a 169 _ => continue,
a1dfa0c6 170 };
0731742a
XL
171 // Insert a retag after the statement.
172 let source_info = block_data.statements[i].source_info;
dfeec247
XL
173 block_data.statements.insert(
174 i + 1,
94222f64
XL
175 Statement {
176 source_info,
177 kind: StatementKind::Retag(retag_kind, Box::new(place)),
178 },
dfeec247 179 );
a1dfa0c6
XL
180 }
181 }
182 }
183}