]>
Commit | Line | Data |
---|---|---|
fc512014 XL |
1 | //! Finds locals which are assigned once to a const and unused except for debuginfo and converts |
2 | //! their debuginfo to use the const directly, allowing the local to be removed. | |
3 | ||
4 | use rustc_middle::{ | |
5 | mir::{ | |
6 | visit::{PlaceContext, Visitor}, | |
7 | Body, Constant, Local, Location, Operand, Rvalue, StatementKind, VarDebugInfoContents, | |
8 | }, | |
9 | ty::TyCtxt, | |
10 | }; | |
11 | ||
c295e0f8 | 12 | use crate::MirPass; |
49aad941 | 13 | use rustc_index::{bit_set::BitSet, IndexVec}; |
fc512014 XL |
14 | |
15 | pub struct ConstDebugInfo; | |
16 | ||
17 | impl<'tcx> MirPass<'tcx> for ConstDebugInfo { | |
a2a8927a | 18 | fn is_enabled(&self, sess: &rustc_session::Session) -> bool { |
49aad941 | 19 | sess.mir_opt_level() > 0 |
a2a8927a | 20 | } |
fc512014 | 21 | |
923072b8 | 22 | fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { |
fc512014 XL |
23 | trace!("running ConstDebugInfo on {:?}", body.source); |
24 | ||
49aad941 | 25 | for (local, constant) in find_optimization_opportunities(body) { |
fc512014 XL |
26 | for debuginfo in &mut body.var_debug_info { |
27 | if let VarDebugInfoContents::Place(p) = debuginfo.value { | |
28 | if p.local == local && p.projection.is_empty() { | |
29 | trace!( | |
30 | "changing debug info for {:?} from place {:?} to constant {:?}", | |
31 | debuginfo.name, | |
32 | p, | |
33 | constant | |
34 | ); | |
35 | debuginfo.value = VarDebugInfoContents::Const(constant); | |
36 | } | |
37 | } | |
38 | } | |
39 | } | |
40 | } | |
41 | } | |
42 | ||
43 | struct LocalUseVisitor { | |
44 | local_mutating_uses: IndexVec<Local, u8>, | |
45 | local_assignment_locations: IndexVec<Local, Option<Location>>, | |
46 | } | |
47 | ||
49aad941 | 48 | fn find_optimization_opportunities<'tcx>(body: &Body<'tcx>) -> Vec<(Local, Constant<'tcx>)> { |
fc512014 XL |
49 | let mut visitor = LocalUseVisitor { |
50 | local_mutating_uses: IndexVec::from_elem(0, &body.local_decls), | |
51 | local_assignment_locations: IndexVec::from_elem(None, &body.local_decls), | |
52 | }; | |
53 | ||
54 | visitor.visit_body(body); | |
55 | ||
56 | let mut locals_to_debuginfo = BitSet::new_empty(body.local_decls.len()); | |
57 | for debuginfo in &body.var_debug_info { | |
5e7ed085 FG |
58 | if let VarDebugInfoContents::Place(p) = debuginfo.value && let Some(l) = p.as_local() { |
59 | locals_to_debuginfo.insert(l); | |
fc512014 XL |
60 | } |
61 | } | |
62 | ||
5e7ed085 | 63 | let mut eligible_locals = Vec::new(); |
fc512014 XL |
64 | for (local, mutating_uses) in visitor.local_mutating_uses.drain_enumerated(..) { |
65 | if mutating_uses != 1 || !locals_to_debuginfo.contains(local) { | |
66 | continue; | |
67 | } | |
68 | ||
69 | if let Some(location) = visitor.local_assignment_locations[local] { | |
70 | let bb = &body[location.block]; | |
71 | ||
72 | // The value is assigned as the result of a call, not a constant | |
73 | if bb.statements.len() == location.statement_index { | |
74 | continue; | |
75 | } | |
76 | ||
77 | if let StatementKind::Assign(box (p, Rvalue::Use(Operand::Constant(box c)))) = | |
78 | &bb.statements[location.statement_index].kind | |
79 | { | |
80 | if let Some(local) = p.as_local() { | |
5e7ed085 | 81 | eligible_locals.push((local, *c)); |
fc512014 XL |
82 | } |
83 | } | |
84 | } | |
85 | } | |
86 | ||
5e7ed085 | 87 | eligible_locals |
fc512014 XL |
88 | } |
89 | ||
a2a8927a | 90 | impl Visitor<'_> for LocalUseVisitor { |
064997fb | 91 | fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) { |
fc512014 | 92 | if context.is_mutating_use() { |
064997fb | 93 | self.local_mutating_uses[local] = self.local_mutating_uses[local].saturating_add(1); |
fc512014 XL |
94 | |
95 | if context.is_place_assignment() { | |
064997fb | 96 | self.local_assignment_locations[local] = Some(location); |
fc512014 XL |
97 | } |
98 | } | |
99 | } | |
100 | } |