]> git.lizzy.rs Git - rust.git/blob - src/librustc_codegen_ssa/mir/debuginfo.rs
Remove PlaceBase enum and make Place base field be local: Local
[rust.git] / src / librustc_codegen_ssa / mir / debuginfo.rs
1 use crate::traits::*;
2 use rustc::mir;
3 use rustc::session::config::DebugInfo;
4 use rustc::ty::layout::{LayoutOf, Size};
5 use rustc::ty::TyCtxt;
6 use rustc_hir::def_id::CrateNum;
7 use rustc_index::vec::IndexVec;
8
9 use rustc_span::symbol::kw;
10 use rustc_span::{BytePos, Span};
11
12 use super::OperandValue;
13 use super::{FunctionCx, LocalRef};
14
15 pub struct FunctionDebugContext<D> {
16     pub scopes: IndexVec<mir::SourceScope, DebugScope<D>>,
17     pub source_locations_enabled: bool,
18     pub defining_crate: CrateNum,
19 }
20
21 #[derive(Copy, Clone)]
22 pub enum VariableKind {
23     ArgumentVariable(usize /*index*/),
24     LocalVariable,
25 }
26
27 #[derive(Clone, Copy, Debug)]
28 pub struct DebugScope<D> {
29     pub scope_metadata: Option<D>,
30     // Start and end offsets of the file to which this DIScope belongs.
31     // These are used to quickly determine whether some span refers to the same file.
32     pub file_start_pos: BytePos,
33     pub file_end_pos: BytePos,
34 }
35
36 impl<D> DebugScope<D> {
37     pub fn is_valid(&self) -> bool {
38         !self.scope_metadata.is_none()
39     }
40 }
41
42 impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
43     pub fn set_debug_loc(&mut self, bx: &mut Bx, source_info: mir::SourceInfo) {
44         let (scope, span) = self.debug_loc(source_info);
45         if let Some(debug_context) = &mut self.debug_context {
46             // FIXME(eddyb) get rid of this unwrap somehow.
47             bx.set_source_location(debug_context, scope.unwrap(), span);
48         }
49     }
50
51     pub fn debug_loc(&self, source_info: mir::SourceInfo) -> (Option<Bx::DIScope>, Span) {
52         // Bail out if debug info emission is not enabled.
53         match self.debug_context {
54             None => return (None, source_info.span),
55             Some(_) => {}
56         }
57
58         // In order to have a good line stepping behavior in debugger, we overwrite debug
59         // locations of macro expansions with that of the outermost expansion site
60         // (unless the crate is being compiled with `-Z debug-macros`).
61         if !source_info.span.from_expansion() || self.cx.sess().opts.debugging_opts.debug_macros {
62             let scope = self.scope_metadata_for_loc(source_info.scope, source_info.span.lo());
63             (scope, source_info.span)
64         } else {
65             // Walk up the macro expansion chain until we reach a non-expanded span.
66             // We also stop at the function body level because no line stepping can occur
67             // at the level above that.
68             let span = rustc_span::hygiene::walk_chain(source_info.span, self.mir.span.ctxt());
69             let scope = self.scope_metadata_for_loc(source_info.scope, span.lo());
70             // Use span of the outermost expansion site, while keeping the original lexical scope.
71             (scope, span)
72         }
73     }
74
75     // DILocations inherit source file name from the parent DIScope.  Due to macro expansions
76     // it may so happen that the current span belongs to a different file than the DIScope
77     // corresponding to span's containing source scope.  If so, we need to create a DIScope
78     // "extension" into that file.
79     fn scope_metadata_for_loc(
80         &self,
81         scope_id: mir::SourceScope,
82         pos: BytePos,
83     ) -> Option<Bx::DIScope> {
84         let debug_context = self.debug_context.as_ref()?;
85         let scope_metadata = debug_context.scopes[scope_id].scope_metadata;
86         if pos < debug_context.scopes[scope_id].file_start_pos
87             || pos >= debug_context.scopes[scope_id].file_end_pos
88         {
89             let sm = self.cx.sess().source_map();
90             let defining_crate = debug_context.defining_crate;
91             Some(self.cx.extend_scope_to_file(
92                 scope_metadata.unwrap(),
93                 &sm.lookup_char_pos(pos).file,
94                 defining_crate,
95             ))
96         } else {
97             scope_metadata
98         }
99     }
100
101     /// Apply debuginfo and/or name, after creating the `alloca` for a local,
102     /// or initializing the local with an operand (whichever applies).
103     // FIXME(eddyb) use `llvm.dbg.value` (which would work for operands),
104     // not just `llvm.dbg.declare` (which requires `alloca`).
105     pub fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) {
106         // FIXME(eddyb) maybe name the return place as `_0` or `return`?
107         if local == mir::RETURN_PLACE {
108             return;
109         }
110
111         let vars = match &self.per_local_var_debug_info {
112             Some(per_local) => &per_local[local],
113             None => return,
114         };
115         let whole_local_var = vars.iter().copied().find(|var| var.place.projection.is_empty());
116         let has_proj = || vars.iter().any(|var| !var.place.projection.is_empty());
117
118         let (fallback_var, kind) = if self.mir.local_kind(local) == mir::LocalKind::Arg {
119             let arg_index = local.index() - 1;
120
121             // Add debuginfo even to unnamed arguments.
122             // FIXME(eddyb) is this really needed?
123             let var = if arg_index == 0 && has_proj() {
124                 // Hide closure environments from debuginfo.
125                 // FIXME(eddyb) shouldn't `ArgumentVariable` indices
126                 // be offset to account for the hidden environment?
127                 None
128             } else {
129                 Some(mir::VarDebugInfo {
130                     name: kw::Invalid,
131                     source_info: self.mir.local_decls[local].source_info,
132                     place: local.into(),
133                 })
134             };
135             (var, VariableKind::ArgumentVariable(arg_index + 1))
136         } else {
137             (None, VariableKind::LocalVariable)
138         };
139
140         let local_ref = &self.locals[local];
141
142         if !bx.sess().fewer_names() {
143             let name = match whole_local_var.or(fallback_var.as_ref()) {
144                 Some(var) if var.name != kw::Invalid => var.name.to_string(),
145                 _ => format!("{:?}", local),
146             };
147             match local_ref {
148                 LocalRef::Place(place) | LocalRef::UnsizedPlace(place) => {
149                     bx.set_var_name(place.llval, &name);
150                 }
151                 LocalRef::Operand(Some(operand)) => match operand.val {
152                     OperandValue::Ref(x, ..) | OperandValue::Immediate(x) => {
153                         bx.set_var_name(x, &name);
154                     }
155                     OperandValue::Pair(a, b) => {
156                         // FIXME(eddyb) these are scalar components,
157                         // maybe extract the high-level fields?
158                         bx.set_var_name(a, &(name.clone() + ".0"));
159                         bx.set_var_name(b, &(name + ".1"));
160                     }
161                 },
162                 LocalRef::Operand(None) => {}
163             }
164         }
165
166         if bx.sess().opts.debuginfo != DebugInfo::Full {
167             return;
168         }
169
170         let debug_context = match &self.debug_context {
171             Some(debug_context) => debug_context,
172             None => return,
173         };
174
175         // FIXME(eddyb) add debuginfo for unsized places too.
176         let base = match local_ref {
177             LocalRef::Place(place) => place,
178             _ => return,
179         };
180
181         let vars = vars.iter().copied().chain(if whole_local_var.is_none() {
182             fallback_var.as_ref()
183         } else {
184             None
185         });
186
187         for var in vars {
188             let mut layout = base.layout;
189             let mut direct_offset = Size::ZERO;
190             // FIXME(eddyb) use smallvec here.
191             let mut indirect_offsets = vec![];
192
193             let kind =
194                 if var.place.projection.is_empty() { kind } else { VariableKind::LocalVariable };
195
196             for elem in &var.place.projection[..] {
197                 match *elem {
198                     mir::ProjectionElem::Deref => {
199                         indirect_offsets.push(Size::ZERO);
200                         layout = bx.cx().layout_of(
201                             layout
202                                 .ty
203                                 .builtin_deref(true)
204                                 .unwrap_or_else(|| {
205                                     span_bug!(var.source_info.span, "cannot deref `{}`", layout.ty,)
206                                 })
207                                 .ty,
208                         );
209                     }
210                     mir::ProjectionElem::Field(field, _) => {
211                         let i = field.index();
212                         let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
213                         *offset += layout.fields.offset(i);
214                         layout = layout.field(bx.cx(), i);
215                     }
216                     mir::ProjectionElem::Downcast(_, variant) => {
217                         layout = layout.for_variant(bx.cx(), variant);
218                     }
219                     _ => span_bug!(
220                         var.source_info.span,
221                         "unsupported var debuginfo place `{:?}`",
222                         var.place,
223                     ),
224                 }
225             }
226
227             let (scope, span) = self.debug_loc(var.source_info);
228             if let Some(scope) = scope {
229                 bx.declare_local(
230                     debug_context,
231                     var.name,
232                     layout.ty,
233                     scope,
234                     base.llval,
235                     direct_offset,
236                     &indirect_offsets,
237                     kind,
238                     span,
239                 );
240             }
241         }
242     }
243
244     pub fn debug_introduce_locals(&self, bx: &mut Bx) {
245         if bx.sess().opts.debuginfo == DebugInfo::Full || !bx.sess().fewer_names() {
246             for local in self.locals.indices() {
247                 self.debug_introduce_local(bx, local);
248             }
249         }
250     }
251 }
252
253 /// Partition all `VarDebuginfo` in `body`, by their base `Local`.
254 pub fn per_local_var_debug_info(
255     tcx: TyCtxt<'tcx>,
256     body: &'a mir::Body<'tcx>,
257 ) -> Option<IndexVec<mir::Local, Vec<&'a mir::VarDebugInfo<'tcx>>>> {
258     if tcx.sess.opts.debuginfo == DebugInfo::Full || !tcx.sess.fewer_names() {
259         let mut per_local = IndexVec::from_elem(vec![], &body.local_decls);
260         for var in &body.var_debug_info {
261             per_local[var.place.local].push(var);
262         }
263         Some(per_local)
264     } else {
265         None
266     }
267 }