]> git.lizzy.rs Git - rust.git/blob - src/librustc_codegen_ssa/mir/debuginfo.rs
Rollup merge of #69528 - HeroicKatora:finalize-ref-cell, r=dtolnay
[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;
5 use rustc::ty::layout::{LayoutOf, Size};
6 use rustc_hir::def_id::CrateNum;
7 use rustc_index::vec::IndexVec;
8
9 use rustc_span::symbol::{kw, Symbol};
10 use rustc_span::{BytePos, Span};
11
12 use super::operand::OperandValue;
13 use super::place::PlaceRef;
14 use super::{FunctionCx, LocalRef};
15
16 pub struct FunctionDebugContext<D> {
17     pub scopes: IndexVec<mir::SourceScope, DebugScope<D>>,
18     pub defining_crate: CrateNum,
19 }
20
21 #[derive(Copy, Clone)]
22 pub enum VariableKind {
23     ArgumentVariable(usize /*index*/),
24     LocalVariable,
25 }
26
27 /// Like `mir::VarDebugInfo`, but within a `mir::Local`.
28 #[derive(Copy, Clone)]
29 pub struct PerLocalVarDebugInfo<'tcx, D> {
30     pub name: Symbol,
31     pub source_info: mir::SourceInfo,
32
33     /// `DIVariable` returned by `create_dbg_var`.
34     pub dbg_var: Option<D>,
35
36     /// `.place.projection` from `mir::VarDebugInfo`.
37     pub projection: &'tcx ty::List<mir::PlaceElem<'tcx>>,
38 }
39
40 #[derive(Clone, Copy, Debug)]
41 pub struct DebugScope<D> {
42     pub scope_metadata: Option<D>,
43     // Start and end offsets of the file to which this DIScope belongs.
44     // These are used to quickly determine whether some span refers to the same file.
45     pub file_start_pos: BytePos,
46     pub file_end_pos: BytePos,
47 }
48
49 impl<D> DebugScope<D> {
50     pub fn is_valid(&self) -> bool {
51         self.scope_metadata.is_some()
52     }
53 }
54
55 impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
56     pub fn set_debug_loc(&self, bx: &mut Bx, source_info: mir::SourceInfo) {
57         let (scope, span) = self.debug_loc(source_info);
58         if let Some(scope) = scope {
59             bx.set_source_location(scope, span);
60         }
61     }
62
63     pub fn debug_loc(&self, source_info: mir::SourceInfo) -> (Option<Bx::DIScope>, Span) {
64         // Bail out if debug info emission is not enabled.
65         match self.debug_context {
66             None => return (None, source_info.span),
67             Some(_) => {}
68         }
69
70         // In order to have a good line stepping behavior in debugger, we overwrite debug
71         // locations of macro expansions with that of the outermost expansion site
72         // (unless the crate is being compiled with `-Z debug-macros`).
73         if !source_info.span.from_expansion() || self.cx.sess().opts.debugging_opts.debug_macros {
74             let scope = self.scope_metadata_for_loc(source_info.scope, source_info.span.lo());
75             (scope, source_info.span)
76         } else {
77             // Walk up the macro expansion chain until we reach a non-expanded span.
78             // We also stop at the function body level because no line stepping can occur
79             // at the level above that.
80             let span = rustc_span::hygiene::walk_chain(source_info.span, self.mir.span.ctxt());
81             let scope = self.scope_metadata_for_loc(source_info.scope, span.lo());
82             // Use span of the outermost expansion site, while keeping the original lexical scope.
83             (scope, span)
84         }
85     }
86
87     // DILocations inherit source file name from the parent DIScope.  Due to macro expansions
88     // it may so happen that the current span belongs to a different file than the DIScope
89     // corresponding to span's containing source scope.  If so, we need to create a DIScope
90     // "extension" into that file.
91     fn scope_metadata_for_loc(
92         &self,
93         scope_id: mir::SourceScope,
94         pos: BytePos,
95     ) -> Option<Bx::DIScope> {
96         let debug_context = self.debug_context.as_ref()?;
97         let scope_metadata = debug_context.scopes[scope_id].scope_metadata;
98         if pos < debug_context.scopes[scope_id].file_start_pos
99             || pos >= debug_context.scopes[scope_id].file_end_pos
100         {
101             let sm = self.cx.sess().source_map();
102             let defining_crate = debug_context.defining_crate;
103             Some(self.cx.extend_scope_to_file(
104                 scope_metadata.unwrap(),
105                 &sm.lookup_char_pos(pos).file,
106                 defining_crate,
107             ))
108         } else {
109             scope_metadata
110         }
111     }
112
113     /// Apply debuginfo and/or name, after creating the `alloca` for a local,
114     /// or initializing the local with an operand (whichever applies).
115     pub fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) {
116         let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full;
117
118         // FIXME(eddyb) maybe name the return place as `_0` or `return`?
119         if local == mir::RETURN_PLACE {
120             return;
121         }
122
123         let vars = match &self.per_local_var_debug_info {
124             Some(per_local) => &per_local[local],
125             None => return,
126         };
127         let whole_local_var = vars.iter().find(|var| var.projection.is_empty()).copied();
128         let has_proj = || vars.iter().any(|var| !var.projection.is_empty());
129
130         let fallback_var = if self.mir.local_kind(local) == mir::LocalKind::Arg {
131             let arg_index = local.index() - 1;
132
133             // Add debuginfo even to unnamed arguments.
134             // FIXME(eddyb) is this really needed?
135             if arg_index == 0 && has_proj() {
136                 // Hide closure environments from debuginfo.
137                 // FIXME(eddyb) shouldn't `ArgumentVariable` indices
138                 // be offset to account for the hidden environment?
139                 None
140             } else if whole_local_var.is_some() {
141                 // No need to make up anything, there is a `mir::VarDebugInfo`
142                 // covering the whole local.
143                 // FIXME(eddyb) take `whole_local_var.source_info.scope` into
144                 // account, just in case it doesn't use `ArgumentVariable`
145                 // (after #67586 gets fixed).
146                 None
147             } else {
148                 let name = kw::Invalid;
149                 let decl = &self.mir.local_decls[local];
150                 let (scope, span) = if full_debug_info {
151                     self.debug_loc(decl.source_info)
152                 } else {
153                     (None, decl.source_info.span)
154                 };
155                 let dbg_var = scope.map(|scope| {
156                     // FIXME(eddyb) is this `+ 1` needed at all?
157                     let kind = VariableKind::ArgumentVariable(arg_index + 1);
158
159                     self.cx.create_dbg_var(
160                         self.debug_context.as_ref().unwrap(),
161                         name,
162                         self.monomorphize(&decl.ty),
163                         scope,
164                         kind,
165                         span,
166                     )
167                 });
168
169                 Some(PerLocalVarDebugInfo {
170                     name,
171                     source_info: decl.source_info,
172                     dbg_var,
173                     projection: ty::List::empty(),
174                 })
175             }
176         } else {
177             None
178         };
179
180         let local_ref = &self.locals[local];
181
182         let name = if bx.sess().fewer_names() {
183             None
184         } else {
185             Some(match whole_local_var.or(fallback_var) {
186                 Some(var) if var.name != kw::Invalid => var.name.to_string(),
187                 _ => format!("{:?}", local),
188             })
189         };
190
191         if let Some(name) = &name {
192             match local_ref {
193                 LocalRef::Place(place) | LocalRef::UnsizedPlace(place) => {
194                     bx.set_var_name(place.llval, name);
195                 }
196                 LocalRef::Operand(Some(operand)) => match operand.val {
197                     OperandValue::Ref(x, ..) | OperandValue::Immediate(x) => {
198                         bx.set_var_name(x, name);
199                     }
200                     OperandValue::Pair(a, b) => {
201                         // FIXME(eddyb) these are scalar components,
202                         // maybe extract the high-level fields?
203                         bx.set_var_name(a, &(name.clone() + ".0"));
204                         bx.set_var_name(b, &(name.clone() + ".1"));
205                     }
206                 },
207                 LocalRef::Operand(None) => {}
208             }
209         }
210
211         if !full_debug_info || vars.is_empty() && fallback_var.is_none() {
212             return;
213         }
214
215         let base = match local_ref {
216             LocalRef::Operand(None) => return,
217
218             LocalRef::Operand(Some(operand)) => {
219                 // "Spill" the value onto the stack, for debuginfo,
220                 // without forcing non-debuginfo uses of the local
221                 // to also load from the stack every single time.
222                 // FIXME(#68817) use `llvm.dbg.value` instead,
223                 // at least for the cases which LLVM handles correctly.
224                 let spill_slot = PlaceRef::alloca(bx, operand.layout);
225                 if let Some(name) = name {
226                     bx.set_var_name(spill_slot.llval, &(name + ".dbg.spill"));
227                 }
228                 operand.val.store(bx, spill_slot);
229                 spill_slot
230             }
231
232             LocalRef::Place(place) => *place,
233
234             // FIXME(eddyb) add debuginfo for unsized places too.
235             LocalRef::UnsizedPlace(_) => return,
236         };
237
238         let vars = vars.iter().copied().chain(fallback_var);
239
240         for var in vars {
241             let mut layout = base.layout;
242             let mut direct_offset = Size::ZERO;
243             // FIXME(eddyb) use smallvec here.
244             let mut indirect_offsets = vec![];
245
246             for elem in &var.projection[..] {
247                 match *elem {
248                     mir::ProjectionElem::Deref => {
249                         indirect_offsets.push(Size::ZERO);
250                         layout = bx.cx().layout_of(
251                             layout
252                                 .ty
253                                 .builtin_deref(true)
254                                 .unwrap_or_else(|| {
255                                     span_bug!(var.source_info.span, "cannot deref `{}`", layout.ty)
256                                 })
257                                 .ty,
258                         );
259                     }
260                     mir::ProjectionElem::Field(field, _) => {
261                         let i = field.index();
262                         let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
263                         *offset += layout.fields.offset(i);
264                         layout = layout.field(bx.cx(), i);
265                     }
266                     mir::ProjectionElem::Downcast(_, variant) => {
267                         layout = layout.for_variant(bx.cx(), variant);
268                     }
269                     _ => span_bug!(
270                         var.source_info.span,
271                         "unsupported var debuginfo place `{:?}`",
272                         mir::Place { local, projection: var.projection },
273                     ),
274                 }
275             }
276
277             let (scope, span) = self.debug_loc(var.source_info);
278             if let Some(scope) = scope {
279                 if let Some(dbg_var) = var.dbg_var {
280                     bx.dbg_var_addr(
281                         dbg_var,
282                         scope,
283                         base.llval,
284                         direct_offset,
285                         &indirect_offsets,
286                         span,
287                     );
288                 }
289             }
290         }
291     }
292
293     pub fn debug_introduce_locals(&self, bx: &mut Bx) {
294         if bx.sess().opts.debuginfo == DebugInfo::Full || !bx.sess().fewer_names() {
295             for local in self.locals.indices() {
296                 self.debug_introduce_local(bx, local);
297             }
298         }
299     }
300
301     /// Partition all `VarDebugInfo` in `self.mir`, by their base `Local`.
302     pub fn compute_per_local_var_debug_info(
303         &self,
304     ) -> Option<IndexVec<mir::Local, Vec<PerLocalVarDebugInfo<'tcx, Bx::DIVariable>>>> {
305         let full_debug_info = self.cx.sess().opts.debuginfo == DebugInfo::Full;
306
307         if !full_debug_info && self.cx.sess().fewer_names() {
308             return None;
309         }
310
311         let mut per_local = IndexVec::from_elem(vec![], &self.mir.local_decls);
312         for var in &self.mir.var_debug_info {
313             let (scope, span) = if full_debug_info {
314                 self.debug_loc(var.source_info)
315             } else {
316                 (None, var.source_info.span)
317             };
318             let dbg_var = scope.map(|scope| {
319                 let place = var.place;
320                 let var_ty = self.monomorphized_place_ty(place.as_ref());
321                 let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg
322                     && place.projection.is_empty()
323                     && var.source_info.scope == mir::OUTERMOST_SOURCE_SCOPE
324                 {
325                     let arg_index = place.local.index() - 1;
326
327                     // FIXME(eddyb) shouldn't `ArgumentVariable` indices be
328                     // offset in closures to account for the hidden environment?
329                     // Also, is this `+ 1` needed at all?
330                     VariableKind::ArgumentVariable(arg_index + 1)
331                 } else {
332                     VariableKind::LocalVariable
333                 };
334                 self.cx.create_dbg_var(
335                     self.debug_context.as_ref().unwrap(),
336                     var.name,
337                     var_ty,
338                     scope,
339                     var_kind,
340                     span,
341                 )
342             });
343
344             per_local[var.place.local].push(PerLocalVarDebugInfo {
345                 name: var.name,
346                 source_info: var.source_info,
347                 dbg_var,
348                 projection: var.place.projection,
349             });
350         }
351         Some(per_local)
352     }
353 }