2 use rustc_index::vec::IndexVec;
3 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
6 use rustc_session::config::DebugInfo;
7 use rustc_span::symbol::{kw, Symbol};
8 use rustc_span::{BytePos, Span};
9 use rustc_target::abi::{LayoutOf, Size};
11 use super::operand::{OperandRef, OperandValue};
12 use super::place::PlaceRef;
13 use super::{FunctionCx, LocalRef};
15 pub struct FunctionDebugContext<S, L> {
16 pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>,
19 #[derive(Copy, Clone)]
20 pub enum VariableKind {
21 ArgumentVariable(usize /*index*/),
25 /// Like `mir::VarDebugInfo`, but within a `mir::Local`.
26 #[derive(Copy, Clone)]
27 pub struct PerLocalVarDebugInfo<'tcx, D> {
29 pub source_info: mir::SourceInfo,
31 /// `DIVariable` returned by `create_dbg_var`.
32 pub dbg_var: Option<D>,
34 /// `.place.projection` from `mir::VarDebugInfo`.
35 pub projection: &'tcx ty::List<mir::PlaceElem<'tcx>>,
38 #[derive(Clone, Copy, Debug)]
39 pub struct DebugScope<S, L> {
40 // FIXME(eddyb) this should never be `None`, after initialization.
41 pub dbg_scope: Option<S>,
43 /// Call site location, if this scope was inlined from another function.
44 pub inlined_at: Option<L>,
46 // Start and end offsets of the file to which this DIScope belongs.
47 // These are used to quickly determine whether some span refers to the same file.
48 pub file_start_pos: BytePos,
49 pub file_end_pos: BytePos,
52 impl<'tcx, S: Copy, L: Copy> DebugScope<S, L> {
53 /// DILocations inherit source file name from the parent DIScope. Due to macro expansions
54 /// it may so happen that the current span belongs to a different file than the DIScope
55 /// corresponding to span's containing source scope. If so, we need to create a DIScope
56 /// "extension" into that file.
57 pub fn adjust_dbg_scope_for_span<Cx: CodegenMethods<'tcx, DIScope = S, DILocation = L>>(
62 // FIXME(eddyb) this should never be `None`.
65 .unwrap_or_else(|| bug!("`dbg_scope` is only `None` during initialization"));
68 if pos < self.file_start_pos || pos >= self.file_end_pos {
69 let sm = cx.sess().source_map();
70 cx.extend_scope_to_file(dbg_scope, &sm.lookup_char_pos(pos).file)
77 impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
78 pub fn set_debug_loc(&self, bx: &mut Bx, source_info: mir::SourceInfo) {
79 bx.set_span(source_info.span);
80 if let Some(dbg_loc) = self.dbg_loc(source_info) {
81 bx.set_dbg_loc(dbg_loc);
85 fn dbg_loc(&self, source_info: mir::SourceInfo) -> Option<Bx::DILocation> {
86 let (dbg_scope, inlined_at, span) = self.adjusted_span_and_dbg_scope(source_info)?;
87 Some(self.cx.dbg_loc(dbg_scope, inlined_at, span))
90 fn adjusted_span_and_dbg_scope(
92 source_info: mir::SourceInfo,
93 ) -> Option<(Bx::DIScope, Option<Bx::DILocation>, Span)> {
94 let span = self.adjust_span_for_debugging(source_info.span);
95 let scope = &self.debug_context.as_ref()?.scopes[source_info.scope];
96 Some((scope.adjust_dbg_scope_for_span(self.cx, span), scope.inlined_at, span))
99 /// In order to have a good line stepping behavior in debugger, we overwrite debug
100 /// locations of macro expansions with that of the outermost expansion site
101 /// (unless the crate is being compiled with `-Z debug-macros`).
102 fn adjust_span_for_debugging(&self, mut span: Span) -> Span {
103 // Bail out if debug info emission is not enabled.
104 if self.debug_context.is_none() {
108 if span.from_expansion() && !self.cx.sess().opts.debugging_opts.debug_macros {
109 // Walk up the macro expansion chain until we reach a non-expanded span.
110 // We also stop at the function body level because no line stepping can occur
111 // at the level above that.
112 // Use span of the outermost expansion site, while keeping the original lexical scope.
113 span = rustc_span::hygiene::walk_chain(span, self.mir.span.ctxt());
119 fn spill_operand_to_stack(
120 operand: &OperandRef<'tcx, Bx::Value>,
121 name: Option<String>,
123 ) -> PlaceRef<'tcx, Bx::Value> {
124 // "Spill" the value onto the stack, for debuginfo,
125 // without forcing non-debuginfo uses of the local
126 // to also load from the stack every single time.
127 // FIXME(#68817) use `llvm.dbg.value` instead,
128 // at least for the cases which LLVM handles correctly.
129 let spill_slot = PlaceRef::alloca(bx, operand.layout);
130 if let Some(name) = name {
131 bx.set_var_name(spill_slot.llval, &(name + ".dbg.spill"));
133 operand.val.store(bx, spill_slot);
137 /// Apply debuginfo and/or name, after creating the `alloca` for a local,
138 /// or initializing the local with an operand (whichever applies).
139 pub fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) {
140 let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full;
142 // FIXME(eddyb) maybe name the return place as `_0` or `return`?
143 if local == mir::RETURN_PLACE && !self.mir.local_decls[mir::RETURN_PLACE].is_user_variable()
148 let vars = match &self.per_local_var_debug_info {
149 Some(per_local) => &per_local[local],
152 let whole_local_var = vars.iter().find(|var| var.projection.is_empty()).copied();
153 let has_proj = || vars.iter().any(|var| !var.projection.is_empty());
155 let fallback_var = if self.mir.local_kind(local) == mir::LocalKind::Arg {
156 let arg_index = local.index() - 1;
158 // Add debuginfo even to unnamed arguments.
159 // FIXME(eddyb) is this really needed?
160 if arg_index == 0 && has_proj() {
161 // Hide closure environments from debuginfo.
162 // FIXME(eddyb) shouldn't `ArgumentVariable` indices
163 // be offset to account for the hidden environment?
165 } else if whole_local_var.is_some() {
166 // No need to make up anything, there is a `mir::VarDebugInfo`
167 // covering the whole local.
168 // FIXME(eddyb) take `whole_local_var.source_info.scope` into
169 // account, just in case it doesn't use `ArgumentVariable`
170 // (after #67586 gets fixed).
173 let name = kw::Invalid;
174 let decl = &self.mir.local_decls[local];
175 let dbg_var = if full_debug_info {
176 self.adjusted_span_and_dbg_scope(decl.source_info).map(
177 |(dbg_scope, _, span)| {
178 // FIXME(eddyb) is this `+ 1` needed at all?
179 let kind = VariableKind::ArgumentVariable(arg_index + 1);
181 let arg_ty = self.monomorphize(decl.ty);
183 self.cx.create_dbg_var(name, arg_ty, dbg_scope, kind, span)
190 Some(PerLocalVarDebugInfo {
192 source_info: decl.source_info,
194 projection: ty::List::empty(),
201 let local_ref = &self.locals[local];
203 let name = if bx.sess().fewer_names() {
206 Some(match whole_local_var.or(fallback_var) {
207 Some(var) if var.name != kw::Invalid => var.name.to_string(),
208 _ => format!("{:?}", local),
212 if let Some(name) = &name {
214 LocalRef::Place(place) | LocalRef::UnsizedPlace(place) => {
215 bx.set_var_name(place.llval, name);
217 LocalRef::Operand(Some(operand)) => match operand.val {
218 OperandValue::Ref(x, ..) | OperandValue::Immediate(x) => {
219 bx.set_var_name(x, name);
221 OperandValue::Pair(a, b) => {
222 // FIXME(eddyb) these are scalar components,
223 // maybe extract the high-level fields?
224 bx.set_var_name(a, &(name.clone() + ".0"));
225 bx.set_var_name(b, &(name.clone() + ".1"));
228 LocalRef::Operand(None) => {}
232 if !full_debug_info || vars.is_empty() && fallback_var.is_none() {
236 let base = match local_ref {
237 LocalRef::Operand(None) => return,
239 LocalRef::Operand(Some(operand)) => {
240 // Don't spill operands onto the stack in naked functions.
241 // See: https://github.com/rust-lang/rust/issues/42779
242 let attrs = bx.tcx().codegen_fn_attrs(self.instance.def_id());
243 if attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
247 Self::spill_operand_to_stack(operand, name, bx)
250 LocalRef::Place(place) => *place,
252 // FIXME(eddyb) add debuginfo for unsized places too.
253 LocalRef::UnsizedPlace(_) => return,
256 let vars = vars.iter().copied().chain(fallback_var);
259 let dbg_var = match var.dbg_var {
260 Some(dbg_var) => dbg_var,
263 let dbg_loc = match self.dbg_loc(var.source_info) {
264 Some(dbg_loc) => dbg_loc,
268 let mut layout = base.layout;
269 let mut direct_offset = Size::ZERO;
270 // FIXME(eddyb) use smallvec here.
271 let mut indirect_offsets = vec![];
273 for elem in &var.projection[..] {
275 mir::ProjectionElem::Deref => {
276 indirect_offsets.push(Size::ZERO);
277 layout = bx.cx().layout_of(
282 span_bug!(var.source_info.span, "cannot deref `{}`", layout.ty)
287 mir::ProjectionElem::Field(field, _) => {
288 let i = field.index();
289 let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
290 *offset += layout.fields.offset(i);
291 layout = layout.field(bx.cx(), i);
293 mir::ProjectionElem::Downcast(_, variant) => {
294 layout = layout.for_variant(bx.cx(), variant);
297 var.source_info.span,
298 "unsupported var debuginfo place `{:?}`",
299 mir::Place { local, projection: var.projection },
304 bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, direct_offset, &indirect_offsets);
308 pub fn debug_introduce_locals(&self, bx: &mut Bx) {
309 if bx.sess().opts.debuginfo == DebugInfo::Full || !bx.sess().fewer_names() {
310 for local in self.locals.indices() {
311 self.debug_introduce_local(bx, local);
316 /// Partition all `VarDebugInfo` in `self.mir`, by their base `Local`.
317 pub fn compute_per_local_var_debug_info(
320 ) -> Option<IndexVec<mir::Local, Vec<PerLocalVarDebugInfo<'tcx, Bx::DIVariable>>>> {
321 let full_debug_info = self.cx.sess().opts.debuginfo == DebugInfo::Full;
323 if !full_debug_info && self.cx.sess().fewer_names() {
327 let mut per_local = IndexVec::from_elem(vec![], &self.mir.local_decls);
328 for var in &self.mir.var_debug_info {
329 let dbg_scope_and_span = if full_debug_info {
330 self.adjusted_span_and_dbg_scope(var.source_info)
335 let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| {
336 let (var_ty, var_kind) = match var.value {
337 mir::VarDebugInfoContents::Place(place) => {
338 let var_ty = self.monomorphized_place_ty(place.as_ref());
339 let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg
340 && place.projection.is_empty()
341 && var.source_info.scope == mir::OUTERMOST_SOURCE_SCOPE
343 let arg_index = place.local.index() - 1;
345 // FIXME(eddyb) shouldn't `ArgumentVariable` indices be
346 // offset in closures to account for the hidden environment?
347 // Also, is this `+ 1` needed at all?
348 VariableKind::ArgumentVariable(arg_index + 1)
350 VariableKind::LocalVariable
354 mir::VarDebugInfoContents::Const(c) => {
355 let ty = self.monomorphize(c.literal.ty);
356 (ty, VariableKind::LocalVariable)
360 self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
364 mir::VarDebugInfoContents::Place(place) => {
365 per_local[place.local].push(PerLocalVarDebugInfo {
367 source_info: var.source_info,
369 projection: place.projection,
372 mir::VarDebugInfoContents::Const(c) => {
373 if let Some(dbg_var) = dbg_var {
374 let dbg_loc = match self.dbg_loc(var.source_info) {
375 Some(dbg_loc) => dbg_loc,
379 if let Ok(operand) = self.eval_mir_constant_to_operand(bx, &c) {
380 let base = Self::spill_operand_to_stack(
382 Some(var.name.to_string()),
386 bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, Size::ZERO, &[]);