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