]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir_transform/src/add_retag.rs
New upstream version 1.70.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
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.
15fn 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 50impl<'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.
353b0b11 103 TerminatorKind::Drop { .. } => 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}