3 use rustc::session::config::DebugInfo;
4 use rustc::ty::layout::{LayoutOf, Size};
6 use rustc_hir::def_id::CrateNum;
7 use rustc_index::vec::IndexVec;
9 use rustc_span::symbol::kw;
10 use rustc_span::{BytePos, Span};
12 use super::OperandValue;
13 use super::{FunctionCx, LocalRef};
15 pub struct FunctionDebugContext<D> {
16 pub scopes: IndexVec<mir::SourceScope, DebugScope<D>>,
17 pub source_locations_enabled: bool,
18 pub defining_crate: CrateNum,
21 #[derive(Copy, Clone)]
22 pub enum VariableKind {
23 ArgumentVariable(usize /*index*/),
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,
36 impl<D> DebugScope<D> {
37 pub fn is_valid(&self) -> bool {
38 !self.scope_metadata.is_none()
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);
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),
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)
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.
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(
81 scope_id: mir::SourceScope,
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
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,
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 {
111 let vars = match &self.per_local_var_debug_info {
112 Some(per_local) => &per_local[local],
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());
118 let (fallback_var, kind) = if self.mir.local_kind(local) == mir::LocalKind::Arg {
119 let arg_index = local.index() - 1;
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?
129 Some(mir::VarDebugInfo {
131 source_info: self.mir.local_decls[local].source_info,
135 (var, VariableKind::ArgumentVariable(arg_index + 1))
137 (None, VariableKind::LocalVariable)
140 let local_ref = &self.locals[local];
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),
148 LocalRef::Place(place) | LocalRef::UnsizedPlace(place) => {
149 bx.set_var_name(place.llval, &name);
151 LocalRef::Operand(Some(operand)) => match operand.val {
152 OperandValue::Ref(x, ..) | OperandValue::Immediate(x) => {
153 bx.set_var_name(x, &name);
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"));
162 LocalRef::Operand(None) => {}
166 if bx.sess().opts.debuginfo != DebugInfo::Full {
170 let debug_context = match &self.debug_context {
171 Some(debug_context) => debug_context,
175 // FIXME(eddyb) add debuginfo for unsized places too.
176 let base = match local_ref {
177 LocalRef::Place(place) => place,
181 let vars = vars.iter().copied().chain(if whole_local_var.is_none() {
182 fallback_var.as_ref()
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![];
194 if var.place.projection.is_empty() { kind } else { VariableKind::LocalVariable };
196 for elem in &var.place.projection[..] {
198 mir::ProjectionElem::Deref => {
199 indirect_offsets.push(Size::ZERO);
200 layout = bx.cx().layout_of(
205 span_bug!(var.source_info.span, "cannot deref `{}`", layout.ty,)
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);
216 mir::ProjectionElem::Downcast(_, variant) => {
217 layout = layout.for_variant(bx.cx(), variant);
220 var.source_info.span,
221 "unsupported var debuginfo place `{:?}`",
227 let (scope, span) = self.debug_loc(var.source_info);
228 if let Some(scope) = scope {
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);
253 /// Partition all `VarDebuginfo` in `body`, by their base `Local`.
254 pub fn per_local_var_debug_info(
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);