]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/util/def_use.rs
Rollup merge of #69734 - tmiasko:di-enumerator, r=ecstatic-morse
[rust.git] / src / librustc_mir / util / def_use.rs
1 //! Def-use analysis.
2
3 use rustc::mir::visit::{MutVisitor, PlaceContext, Visitor};
4 use rustc::mir::{
5     Body, BodyAndCache, Local, Location, PlaceElem, ReadOnlyBodyAndCache, VarDebugInfo,
6 };
7 use rustc::ty::TyCtxt;
8 use rustc_index::vec::IndexVec;
9 use std::mem;
10
11 pub struct DefUseAnalysis {
12     info: IndexVec<Local, Info>,
13 }
14
15 #[derive(Clone)]
16 pub struct Info {
17     // FIXME(eddyb) use smallvec where possible.
18     pub defs_and_uses: Vec<Use>,
19     var_debug_info_indices: Vec<usize>,
20 }
21
22 #[derive(Clone)]
23 pub struct Use {
24     pub context: PlaceContext,
25     pub location: Location,
26 }
27
28 impl DefUseAnalysis {
29     pub fn new(body: &Body<'_>) -> DefUseAnalysis {
30         DefUseAnalysis { info: IndexVec::from_elem_n(Info::new(), body.local_decls.len()) }
31     }
32
33     pub fn analyze(&mut self, body: ReadOnlyBodyAndCache<'_, '_>) {
34         self.clear();
35
36         let mut finder = DefUseFinder {
37             info: mem::take(&mut self.info),
38             var_debug_info_index: 0,
39             in_var_debug_info: false,
40         };
41         finder.visit_body(body);
42         self.info = finder.info
43     }
44
45     fn clear(&mut self) {
46         for info in &mut self.info {
47             info.clear();
48         }
49     }
50
51     pub fn local_info(&self, local: Local) -> &Info {
52         &self.info[local]
53     }
54
55     fn mutate_defs_and_uses(
56         &self,
57         local: Local,
58         body: &mut BodyAndCache<'tcx>,
59         new_local: Local,
60         tcx: TyCtxt<'tcx>,
61     ) {
62         let mut visitor = MutateUseVisitor::new(local, new_local, tcx);
63         let info = &self.info[local];
64         for place_use in &info.defs_and_uses {
65             visitor.visit_location(body, place_use.location)
66         }
67         // Update debuginfo as well, alongside defs/uses.
68         for &i in &info.var_debug_info_indices {
69             visitor.visit_var_debug_info(&mut body.var_debug_info[i]);
70         }
71     }
72
73     // FIXME(pcwalton): this should update the def-use chains.
74     pub fn replace_all_defs_and_uses_with(
75         &self,
76         local: Local,
77         body: &mut BodyAndCache<'tcx>,
78         new_local: Local,
79         tcx: TyCtxt<'tcx>,
80     ) {
81         self.mutate_defs_and_uses(local, body, new_local, tcx)
82     }
83 }
84
85 struct DefUseFinder {
86     info: IndexVec<Local, Info>,
87     var_debug_info_index: usize,
88     in_var_debug_info: bool,
89 }
90
91 impl Visitor<'_> for DefUseFinder {
92     fn visit_local(&mut self, &local: &Local, context: PlaceContext, location: Location) {
93         let info = &mut self.info[local];
94         if self.in_var_debug_info {
95             info.var_debug_info_indices.push(self.var_debug_info_index);
96         } else {
97             info.defs_and_uses.push(Use { context, location });
98         }
99     }
100     fn visit_var_debug_info(&mut self, var_debug_info: &VarDebugInfo<'tcx>) {
101         assert!(!self.in_var_debug_info);
102         self.in_var_debug_info = true;
103         self.super_var_debug_info(var_debug_info);
104         self.in_var_debug_info = false;
105         self.var_debug_info_index += 1;
106     }
107 }
108
109 impl Info {
110     fn new() -> Info {
111         Info { defs_and_uses: vec![], var_debug_info_indices: vec![] }
112     }
113
114     fn clear(&mut self) {
115         self.defs_and_uses.clear();
116         self.var_debug_info_indices.clear();
117     }
118
119     pub fn def_count(&self) -> usize {
120         self.defs_and_uses.iter().filter(|place_use| place_use.context.is_mutating_use()).count()
121     }
122
123     pub fn def_count_not_including_drop(&self) -> usize {
124         self.defs_not_including_drop().count()
125     }
126
127     pub fn defs_not_including_drop(&self) -> impl Iterator<Item = &Use> {
128         self.defs_and_uses
129             .iter()
130             .filter(|place_use| place_use.context.is_mutating_use() && !place_use.context.is_drop())
131     }
132
133     pub fn use_count(&self) -> usize {
134         self.defs_and_uses.iter().filter(|place_use| place_use.context.is_nonmutating_use()).count()
135     }
136 }
137
138 struct MutateUseVisitor<'tcx> {
139     query: Local,
140     new_local: Local,
141     tcx: TyCtxt<'tcx>,
142 }
143
144 impl MutateUseVisitor<'tcx> {
145     fn new(query: Local, new_local: Local, tcx: TyCtxt<'tcx>) -> MutateUseVisitor<'tcx> {
146         MutateUseVisitor { query, new_local, tcx }
147     }
148 }
149
150 impl MutVisitor<'tcx> for MutateUseVisitor<'tcx> {
151     fn tcx(&self) -> TyCtxt<'tcx> {
152         self.tcx
153     }
154
155     fn visit_local(&mut self, local: &mut Local, _context: PlaceContext, _location: Location) {
156         if *local == self.query {
157             *local = self.new_local;
158         }
159     }
160
161     fn process_projection_elem(&mut self, elem: &PlaceElem<'tcx>) -> Option<PlaceElem<'tcx>> {
162         match elem {
163             PlaceElem::Index(local) if *local == self.query => {
164                 Some(PlaceElem::Index(self.new_local))
165             }
166             _ => None,
167         }
168     }
169 }