blob: c411954f063bdde70a5567723b6a4812b8c85ce9 [file]
use std::collections::hash_map::Entry;
use std::marker::PhantomData;
use std::ops::Range;
use rustc_abi::{BackendRepr, FieldIdx, FieldsShape, Size, VariantIdx};
use rustc_data_structures::fx::FxHashMap;
use rustc_index::IndexVec;
use rustc_index::bit_set::DenseBitSet;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::{Instance, Ty};
use rustc_middle::{bug, mir, ty};
use rustc_session::config::DebugInfo;
use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, hygiene, sym};
use super::operand::{OperandRef, OperandValue};
use super::place::{PlaceRef, PlaceValue};
use super::{FunctionCx, LocalRef, PerLocalVarDebugInfoIndexVec};
use crate::traits::*;
pub struct FunctionDebugContext<'tcx, S, L> {
/// Maps from source code to the corresponding debug info scope.
pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>,
/// Maps from an inlined function to its debug info declaration.
pub inlined_function_scopes: FxHashMap<Instance<'tcx>, S>,
}
#[derive(Copy, Clone)]
pub enum VariableKind {
ArgumentVariable(usize /*index*/),
LocalVariable,
}
/// Like `mir::VarDebugInfo`, but within a `mir::Local`.
#[derive(Clone)]
pub struct PerLocalVarDebugInfo<'tcx, D> {
pub name: Symbol,
pub source_info: mir::SourceInfo,
/// `DIVariable` returned by `create_dbg_var`.
pub dbg_var: Option<D>,
/// Byte range in the `dbg_var` covered by this fragment,
/// if this is a fragment of a composite `VarDebugInfo`.
pub fragment: Option<Range<Size>>,
/// `.place.projection` from `mir::VarDebugInfo`.
pub projection: &'tcx ty::List<mir::PlaceElem<'tcx>>,
}
/// Information needed to emit a constant.
pub struct ConstDebugInfo<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
pub name: String,
pub source_info: mir::SourceInfo,
pub operand: OperandRef<'tcx, Bx::Value>,
pub dbg_var: Bx::DIVariable,
pub dbg_loc: Bx::DILocation,
pub fragment: Option<Range<Size>>,
pub _phantom: PhantomData<&'a ()>,
}
#[derive(Clone, Copy, Debug)]
pub struct DebugScope<S, L> {
pub dbg_scope: S,
/// Call site location, if this scope was inlined from another function.
pub inlined_at: Option<L>,
// Start and end offsets of the file to which this DIScope belongs.
// These are used to quickly determine whether some span refers to the same file.
pub file_start_pos: BytePos,
pub file_end_pos: BytePos,
}
impl<'tcx, S: Copy, L: Copy> DebugScope<S, L> {
/// DILocations inherit source file name from the parent DIScope. Due to macro expansions
/// it may so happen that the current span belongs to a different file than the DIScope
/// corresponding to span's containing source scope. If so, we need to create a DIScope
/// "extension" into that file.
pub fn adjust_dbg_scope_for_span<Cx: CodegenMethods<'tcx, DIScope = S, DILocation = L>>(
&self,
cx: &Cx,
span: Span,
) -> S {
let pos = span.lo();
if pos < self.file_start_pos || pos >= self.file_end_pos {
let sm = cx.sess().source_map();
cx.extend_scope_to_file(self.dbg_scope, &sm.lookup_char_pos(pos).file)
} else {
self.dbg_scope
}
}
}
trait DebugInfoOffsetLocation<'tcx, Bx> {
fn deref(&self, bx: &mut Bx) -> Self;
fn layout(&self) -> TyAndLayout<'tcx>;
fn project_field(&self, bx: &mut Bx, field: FieldIdx) -> Self;
fn project_constant_index(&self, bx: &mut Bx, offset: u64) -> Self;
fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self;
}
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> DebugInfoOffsetLocation<'tcx, Bx>
for PlaceRef<'tcx, Bx::Value>
{
fn deref(&self, bx: &mut Bx) -> Self {
bx.load_operand(*self).deref(bx.cx())
}
fn layout(&self) -> TyAndLayout<'tcx> {
self.layout
}
fn project_field(&self, bx: &mut Bx, field: FieldIdx) -> Self {
PlaceRef::project_field(*self, bx, field.index())
}
fn project_constant_index(&self, bx: &mut Bx, offset: u64) -> Self {
let lloffset = bx.cx().const_usize(offset);
self.project_index(bx, lloffset)
}
fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self {
self.project_downcast(bx, variant)
}
}
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> DebugInfoOffsetLocation<'tcx, Bx>
for TyAndLayout<'tcx>
{
fn deref(&self, bx: &mut Bx) -> Self {
bx.cx().layout_of(
self.ty.builtin_deref(true).unwrap_or_else(|| bug!("cannot deref `{}`", self.ty)),
)
}
fn layout(&self) -> TyAndLayout<'tcx> {
*self
}
fn project_field(&self, bx: &mut Bx, field: FieldIdx) -> Self {
self.field(bx.cx(), field.index())
}
fn project_constant_index(&self, bx: &mut Bx, index: u64) -> Self {
self.field(bx.cx(), index as usize)
}
fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self {
self.for_variant(bx.cx(), variant)
}
}
struct DebugInfoOffset<T> {
/// Offset from the `base` used to calculate the debuginfo offset.
direct_offset: Size,
/// Each offset in this vector indicates one level of indirection from the base or previous
/// indirect offset plus a dereference.
indirect_offsets: Vec<Size>,
/// The final location debuginfo should point to.
result: T,
}
fn calculate_debuginfo_offset<
'a,
'tcx,
Bx: BuilderMethods<'a, 'tcx>,
L: DebugInfoOffsetLocation<'tcx, Bx>,
>(
bx: &mut Bx,
projection: &[mir::PlaceElem<'tcx>],
base: L,
) -> DebugInfoOffset<L> {
let mut direct_offset = Size::ZERO;
// FIXME(eddyb) use smallvec here.
let mut indirect_offsets = vec![];
let mut place = base;
for elem in projection {
match *elem {
mir::ProjectionElem::Deref => {
indirect_offsets.push(Size::ZERO);
place = place.deref(bx);
}
mir::ProjectionElem::Field(field, _) => {
let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
*offset += place.layout().fields.offset(field.index());
place = place.project_field(bx, field);
}
mir::ProjectionElem::Downcast(_, variant) => {
place = place.downcast(bx, variant);
}
mir::ProjectionElem::ConstantIndex {
offset: index,
min_length: _,
from_end: false,
} => {
let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
let FieldsShape::Array { stride, count: _ } = place.layout().fields else {
bug!("ConstantIndex on non-array type {:?}", place.layout())
};
*offset += stride * index;
place = place.project_constant_index(bx, index);
}
_ => {
// Sanity check for `can_use_in_debuginfo`.
assert!(!elem.can_use_in_debuginfo());
bug!("unsupported var debuginfo projection `{:?}`", projection)
}
}
}
DebugInfoOffset { direct_offset, indirect_offsets, result: place }
}
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
pub fn set_debug_loc(&self, bx: &mut Bx, source_info: mir::SourceInfo) {
bx.set_span(source_info.span);
if let Some(dbg_loc) = self.dbg_loc(source_info) {
bx.set_dbg_loc(dbg_loc);
}
}
fn dbg_loc(&self, source_info: mir::SourceInfo) -> Option<Bx::DILocation> {
let (dbg_scope, inlined_at, span) = self.adjusted_span_and_dbg_scope(source_info)?;
Some(self.cx.dbg_loc(dbg_scope, inlined_at, span))
}
fn adjusted_span_and_dbg_scope(
&self,
source_info: mir::SourceInfo,
) -> Option<(Bx::DIScope, Option<Bx::DILocation>, Span)> {
let scope = &self.debug_context.as_ref()?.scopes[source_info.scope];
let span = hygiene::walk_chain_collapsed(source_info.span, self.mir.span);
Some((scope.adjust_dbg_scope_for_span(self.cx, span), scope.inlined_at, span))
}
fn spill_operand_to_stack(
operand: OperandRef<'tcx, Bx::Value>,
name: Option<String>,
bx: &mut Bx,
) -> PlaceRef<'tcx, Bx::Value> {
// "Spill" the value onto the stack, for debuginfo,
// without forcing non-debuginfo uses of the local
// to also load from the stack every single time.
// FIXME(#68817) use `llvm.dbg.value` instead,
// at least for the cases which LLVM handles correctly.
let spill_slot = PlaceRef::alloca(bx, operand.layout);
if let Some(name) = name {
bx.set_var_name(spill_slot.val.llval, &(name + ".dbg.spill"));
}
operand.val.store(bx, spill_slot);
spill_slot
}
// Indicates that local is set to a new value. The `layout` and `projection` are used to
// calculate the offset.
pub(crate) fn debug_new_val_to_local(
&self,
bx: &mut Bx,
local: mir::Local,
base: PlaceRef<'tcx, Bx::Value>,
projection: &[mir::PlaceElem<'tcx>],
) {
let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full;
if !full_debug_info {
return;
}
let vars = match &self.per_local_var_debug_info {
Some(per_local) => &per_local[local],
None => return,
};
let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } =
calculate_debuginfo_offset(bx, projection, base.layout);
for var in vars.iter() {
let Some(dbg_var) = var.dbg_var else {
continue;
};
let Some(dbg_loc) = self.dbg_loc(var.source_info) else {
continue;
};
bx.dbg_var_value(
dbg_var,
dbg_loc,
base.val.llval,
direct_offset,
&indirect_offsets,
&var.fragment,
);
}
}
pub(crate) fn debug_poison_to_local(&self, bx: &mut Bx, local: mir::Local) {
let ty = self.monomorphize(self.mir.local_decls[local].ty);
let layout = bx.cx().layout_of(ty);
let to_backend_ty = bx.cx().immediate_backend_type(layout);
let place_ref = PlaceRef::new_sized(bx.cx().const_poison(to_backend_ty), layout);
self.debug_new_val_to_local(bx, local, place_ref, &[]);
}
/// Apply debuginfo and/or name, after creating the `alloca` for a local,
/// or initializing the local with an operand (whichever applies).
pub(crate) fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) {
let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full;
let vars = match &self.per_local_var_debug_info {
Some(per_local) => &per_local[local],
None => return,
};
let whole_local_var = vars.iter().find(|var| var.projection.is_empty()).cloned();
let has_proj = || vars.iter().any(|var| !var.projection.is_empty());
let fallback_var = if self.mir.local_kind(local) == mir::LocalKind::Arg {
let arg_index = local.index() - 1;
// Add debuginfo even to unnamed arguments.
// FIXME(eddyb) is this really needed?
if arg_index == 0 && has_proj() {
// Hide closure environments from debuginfo.
// FIXME(eddyb) shouldn't `ArgumentVariable` indices
// be offset to account for the hidden environment?
None
} else if whole_local_var.is_some() {
// No need to make up anything, there is a `mir::VarDebugInfo`
// covering the whole local.
// FIXME(eddyb) take `whole_local_var.source_info.scope` into
// account, just in case it doesn't use `ArgumentVariable`
// (after #67586 gets fixed).
None
} else {
let name = sym::empty;
let decl = &self.mir.local_decls[local];
let dbg_var = if full_debug_info {
self.adjusted_span_and_dbg_scope(decl.source_info).map(
|(dbg_scope, _, span)| {
// FIXME(eddyb) is this `+ 1` needed at all?
let kind = VariableKind::ArgumentVariable(arg_index + 1);
let arg_ty = self.monomorphize(decl.ty);
self.cx.create_dbg_var(name, arg_ty, dbg_scope, kind, span)
},
)
} else {
None
};
Some(PerLocalVarDebugInfo {
name,
source_info: decl.source_info,
dbg_var,
fragment: None,
projection: ty::List::empty(),
})
}
} else {
None
};
let local_ref = &self.locals[local];
let name = if bx.sess().fewer_names() {
None
} else {
Some(match whole_local_var.or_else(|| fallback_var.clone()) {
Some(var) if var.name != sym::empty => var.name.to_string(),
_ => format!("{local:?}"),
})
};
if let Some(name) = &name {
match local_ref {
LocalRef::Place(place) | LocalRef::UnsizedPlace(place) => {
bx.set_var_name(place.val.llval, name);
}
LocalRef::Operand(operand) => match operand.val {
OperandValue::Ref(PlaceValue { llval: x, .. }) | OperandValue::Immediate(x) => {
bx.set_var_name(x, name);
}
OperandValue::Pair(a, b) => {
// FIXME(eddyb) these are scalar components,
// maybe extract the high-level fields?
bx.set_var_name(a, &(name.clone() + ".0"));
bx.set_var_name(b, &(name.clone() + ".1"));
}
OperandValue::ZeroSized => {
// These never have a value to talk about
}
},
LocalRef::PendingOperand => {}
}
}
if !full_debug_info || vars.is_empty() && fallback_var.is_none() {
return;
}
let base = match local_ref {
LocalRef::PendingOperand => return,
LocalRef::Operand(operand) => {
// Don't spill operands onto the stack in naked functions.
// See: https://github.com/rust-lang/rust/issues/42779
let attrs = bx.tcx().codegen_instance_attrs(self.instance.def);
if attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
return;
}
// Don't spill `<vscale x N x i1>` for `N != 16`:
//
// SVE predicates are only one bit for each byte in an SVE vector (which makes
// sense, the predicate only needs to keep track of whether a lane is
// enabled/disabled). i.e. a `<vscale x 16 x i8>` vector has a `<vscale x 16 x i1>`
// predicate type. `<vscale x 16 x i1>` corresponds to two bytes of storage,
// multiplied by the `vscale`, with one bit for each of the sixteen lanes.
//
// For a vector with fewer elements, such as `svint32_t`/`<vscale x 4 x i32>`,
// while only a `<vscale x 4 x i1>` predicate type would be strictly necessary,
// relevant intrinsics still take a `svbool_t`/`<vscale x 16 x i1>` - this is
// because a `<vscale x 4 x i1>` is only half of a byte (for `vscale=1`), and with
// memory being byte-addressable, it's unclear how to store that.
//
// Due to this, LLVM ultimately decided not to support stores of `<vscale x N x i1>`
// for `N != 16`. As for `vscale=1` and `N` fewer than sixteen, partial bytes would
// need to be stored (except for `N=8`, but that also isn't supported). `N` can
// never be greater than sixteen as that ends up larger than the 128-bit increment
// size.
//
// Internally, with an intrinsic operating on a `svint32_t`/`<vscale x 4 x i32>`
// (for example), the intrinsic takes the `svbool_t`/`<vscale x 16 x i1>` predicate
// and casts it to a `svbool4_t`/`<vscale x 4 x i1>`. Therefore, it's important that
// the `<vscale x 4 x i1>` never spills because that'll cause errors during
// instruction selection. Spilling to the stack to create debuginfo for these
// intermediate values must be avoided and doing so won't affect the
// debugging experience anyway.
if operand.layout.ty.is_scalable_vector()
&& bx.sess().target.arch == rustc_target::spec::Arch::AArch64
{
let (count, element_ty, _) =
operand.layout.ty.scalable_vector_parts(bx.tcx()).unwrap();
// i.e. `<vscale x N x i1>` when `N != 16`
if element_ty.is_bool() && count != 16 {
return;
}
}
Self::spill_operand_to_stack(*operand, name, bx)
}
LocalRef::Place(place) => *place,
// FIXME(eddyb) add debuginfo for unsized places too.
LocalRef::UnsizedPlace(_) => return,
};
let vars = vars.iter().cloned().chain(fallback_var);
for var in vars {
self.debug_introduce_local_as_var(bx, local, base, var);
}
}
fn debug_introduce_local_as_var(
&self,
bx: &mut Bx,
local: mir::Local,
base: PlaceRef<'tcx, Bx::Value>,
var: PerLocalVarDebugInfo<'tcx, Bx::DIVariable>,
) {
let Some(dbg_var) = var.dbg_var else { return };
let Some(dbg_loc) = self.dbg_loc(var.source_info) else { return };
let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } =
calculate_debuginfo_offset(bx, var.projection, base.layout);
// When targeting MSVC, create extra allocas for arguments instead of pointing multiple
// dbg_var_addr() calls into the same alloca with offsets. MSVC uses CodeView records
// not DWARF and LLVM doesn't support translating the resulting
// [DW_OP_deref, DW_OP_plus_uconst, offset, DW_OP_deref] debug info to CodeView.
// Creating extra allocas on the stack makes the resulting debug info simple enough
// that LLVM can generate correct CodeView records and thus the values appear in the
// debugger. (#83709)
let should_create_individual_allocas = bx.cx().sess().target.is_like_msvc
&& self.mir.local_kind(local) == mir::LocalKind::Arg
// LLVM can handle simple things but anything more complex than just a direct
// offset or one indirect offset of 0 is too complex for it to generate CV records
// correctly.
&& (direct_offset != Size::ZERO || !matches!(&indirect_offsets[..], [Size::ZERO] | []));
if should_create_individual_allocas {
let DebugInfoOffset { direct_offset: _, indirect_offsets: _, result: place } =
calculate_debuginfo_offset(bx, var.projection, base);
// Create a variable which will be a pointer to the actual value
let ptr_ty = Ty::new_mut_ptr(bx.tcx(), place.layout.ty);
let ptr_layout = bx.layout_of(ptr_ty);
let alloca = PlaceRef::alloca(bx, ptr_layout);
bx.set_var_name(alloca.val.llval, &(var.name.to_string() + ".dbg.spill"));
// Write the pointer to the variable
bx.store_to_place(place.val.llval, alloca.val);
// Point the debug info to `*alloca` for the current variable
bx.dbg_var_addr(
dbg_var,
dbg_loc,
alloca.val.llval,
Size::ZERO,
&[Size::ZERO],
&var.fragment,
);
} else {
bx.dbg_var_addr(
dbg_var,
dbg_loc,
base.val.llval,
direct_offset,
&indirect_offsets,
&var.fragment,
);
}
}
pub(crate) fn debug_introduce_locals(
&self,
bx: &mut Bx,
consts: Vec<ConstDebugInfo<'a, 'tcx, Bx>>,
) {
if bx.sess().opts.debuginfo == DebugInfo::Full || !bx.sess().fewer_names() {
for local in self.locals.indices() {
self.debug_introduce_local(bx, local);
}
for ConstDebugInfo { name, source_info, operand, dbg_var, dbg_loc, fragment, .. } in
consts.into_iter()
{
self.set_debug_loc(bx, source_info);
let base = FunctionCx::spill_operand_to_stack(operand, Some(name), bx);
bx.clear_dbg_loc();
bx.dbg_var_addr(dbg_var, dbg_loc, base.val.llval, Size::ZERO, &[], &fragment);
}
}
}
/// Partition all `VarDebugInfo` in `self.mir`, by their base `Local`.
pub(crate) fn compute_per_local_var_debug_info(
&self,
bx: &mut Bx,
) -> Option<(
PerLocalVarDebugInfoIndexVec<'tcx, Bx::DIVariable>,
Vec<ConstDebugInfo<'a, 'tcx, Bx>>,
)> {
let full_debug_info = self.cx.sess().opts.debuginfo == DebugInfo::Full;
let target_is_msvc = self.cx.sess().target.is_like_msvc;
if !full_debug_info && self.cx.sess().fewer_names() {
return None;
}
let mut per_local = IndexVec::from_elem(vec![], &self.mir.local_decls);
let mut constants = vec![];
let mut params_seen: FxHashMap<_, Bx::DIVariable> = Default::default();
for var in &self.mir.var_debug_info {
let dbg_scope_and_span = if full_debug_info {
self.adjusted_span_and_dbg_scope(var.source_info)
} else {
None
};
let var_ty = if let Some(ref fragment) = var.composite {
self.monomorphize(fragment.ty)
} else {
match var.value {
mir::VarDebugInfoContents::Place(place) => {
self.monomorphized_place_ty(place.as_ref())
}
mir::VarDebugInfoContents::Const(c) => self.monomorphize(c.ty()),
}
};
let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| {
let var_kind = if let Some(arg_index) = var.argument_index
&& var.composite.is_none()
&& let mir::VarDebugInfoContents::Place(place) = var.value
&& place.projection.is_empty()
{
let arg_index = arg_index as usize;
if target_is_msvc {
// ScalarPair parameters are spilled to the stack so they need to
// be marked as a `LocalVariable` for MSVC debuggers to visualize
// their data correctly. (See #81894 & #88625)
let var_ty_layout = self.cx.layout_of(var_ty);
if let BackendRepr::ScalarPair(_, _) = var_ty_layout.backend_repr {
VariableKind::LocalVariable
} else {
VariableKind::ArgumentVariable(arg_index)
}
} else {
// FIXME(eddyb) shouldn't `ArgumentVariable` indices be
// offset in closures to account for the hidden environment?
VariableKind::ArgumentVariable(arg_index)
}
} else {
VariableKind::LocalVariable
};
if let VariableKind::ArgumentVariable(arg_index) = var_kind {
match params_seen.entry((dbg_scope, arg_index)) {
Entry::Occupied(o) => o.get().clone(),
Entry::Vacant(v) => v
.insert(
self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span),
)
.clone(),
}
} else {
self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
}
});
let fragment = if let Some(ref fragment) = var.composite {
let var_layout = self.cx.layout_of(var_ty);
let DebugInfoOffset { direct_offset, indirect_offsets, result: fragment_layout } =
calculate_debuginfo_offset(bx, &fragment.projection, var_layout);
assert!(indirect_offsets.is_empty());
if fragment_layout.size == Size::ZERO {
// Fragment is a ZST, so does not represent anything. Avoid generating anything
// as this may conflict with a fragment that covers the entire variable.
continue;
} else if fragment_layout.size == var_layout.size {
// Fragment covers entire variable, so as far as
// DWARF is concerned, it's not really a fragment.
None
} else {
Some(direct_offset..direct_offset + fragment_layout.size)
}
} else {
None
};
match var.value {
mir::VarDebugInfoContents::Place(place) => {
per_local[place.local].push(PerLocalVarDebugInfo {
name: var.name,
source_info: var.source_info,
dbg_var,
fragment,
projection: place.projection,
});
}
mir::VarDebugInfoContents::Const(c) => {
if let Some(dbg_var) = dbg_var {
let Some(dbg_loc) = self.dbg_loc(var.source_info) else { continue };
let operand = self.eval_mir_constant_to_operand(bx, &c);
constants.push(ConstDebugInfo {
name: var.name.to_string(),
source_info: var.source_info,
operand,
dbg_var,
dbg_loc,
fragment,
_phantom: PhantomData,
});
}
}
}
}
Some((per_local, constants))
}
/// Creates the function-specific debug context.
///
/// Returns the FunctionDebugContext for the function which holds state needed
/// for debug info creation, if it is enabled.
pub(super) fn fill_function_debug_context(&mut self) {
if self.cx.sess().opts.debuginfo == DebugInfo::None {
return;
}
// Initialize fn debug context (including scopes).
self.debug_context = Some(FunctionDebugContext {
scopes: IndexVec::with_capacity(self.mir.source_scopes.len()),
inlined_function_scopes: Default::default(),
});
// Find all scopes with variables defined in them.
let variables = if self.cx.sess().opts.debuginfo == DebugInfo::Full {
let mut vars = DenseBitSet::new_empty(self.mir.source_scopes.len());
// FIXME(eddyb) take into account that arguments always have debuginfo,
// irrespective of their name (assuming full debuginfo is enabled).
// NOTE(eddyb) actually, on second thought, those are always in the
// function scope, which always exists.
for var_debug_info in &self.mir.var_debug_info {
vars.insert(var_debug_info.source_info.scope);
}
Some(vars)
} else {
// Nothing to emit, of course.
None
};
// Instantiate all scopes.
let mut discriminators = FxHashMap::default();
for scope in self.mir.source_scopes.indices() {
let scope_data = self.make_mir_scope(&variables, &mut discriminators, scope);
let _s = self.debug_context.as_mut().unwrap().scopes.push(scope_data);
debug_assert_eq!(_s, scope);
}
}
fn make_mir_scope(
&mut self,
variables: &Option<DenseBitSet<mir::SourceScope>>,
discriminators: &mut FxHashMap<BytePos, u32>,
scope: mir::SourceScope,
) -> DebugScope<Bx::DIScope, Bx::DILocation> {
let scope_data = &self.mir.source_scopes[scope];
let parent_scope = if let Some(parent) = scope_data.parent_scope {
debug_assert!(parent.as_u32() < scope.as_u32());
self.debug_context.as_ref().unwrap().scopes[parent]
} else {
// The root is the function itself.
let file = self.cx.sess().source_map().lookup_source_file(self.mir.span.lo());
let dbg_scope = self.cx.dbg_scope_fn(self.instance, self.fn_abi, Some(self.llfn));
return DebugScope {
dbg_scope,
inlined_at: None,
file_start_pos: file.start_pos,
file_end_pos: file.end_position(),
};
};
if let Some(vars) = variables
&& !vars.contains(scope)
&& scope_data.inlined.is_none()
{
// Do not create a DIScope if there are no variables defined in this
// MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat.
return parent_scope;
}
let dbg_scope = match scope_data.inlined {
Some((callee, _)) => {
let callee = self.monomorphize(callee);
*self
.debug_context
.as_mut()
.unwrap()
.inlined_function_scopes
.entry(callee)
.or_insert_with(|| {
let callee_fn_abi = self.cx.fn_abi_of_instance(callee, ty::List::empty());
self.cx.dbg_scope_fn(callee, callee_fn_abi, None)
})
}
None => self.cx.dbg_create_lexical_block(scope_data.span.lo(), parent_scope.dbg_scope),
};
let inlined_at = scope_data.inlined.map(|(_, callsite_span)| {
let callsite_span = hygiene::walk_chain_collapsed(callsite_span, self.mir.span);
let callsite_scope = parent_scope.adjust_dbg_scope_for_span(self.cx, callsite_span);
let loc = self.cx.dbg_loc(callsite_scope, parent_scope.inlined_at, callsite_span);
// NB: In order to produce proper debug info for variables (particularly
// arguments) in multiply-inlined functions, LLVM expects to see a single
// DILocalVariable with multiple different DILocations in the IR. While
// the source information for each DILocation would be identical, their
// inlinedAt attributes will be unique to the particular callsite.
//
// We generate DILocations here based on the callsite's location in the
// source code. A single location in the source code usually can't
// produce multiple distinct calls so this mostly works, until
// macros get involved. A macro can generate multiple calls
// at the same span, which breaks the assumption that we're going to
// produce a unique DILocation for every scope we process here. We
// have to explicitly add discriminators if we see inlines into the
// same source code location.
//
// Note further that we can't key this hashtable on the span itself,
// because these spans could have distinct SyntaxContexts. We have
// to key on exactly what we're giving to LLVM.
match discriminators.entry(callsite_span.lo()) {
Entry::Occupied(mut o) => {
*o.get_mut() += 1;
// NB: We have to emit *something* here or we'll fail LLVM IR verification
// in at least some circumstances (see issue #135322) so if the required
// discriminant cannot be encoded fall back to the dummy location.
self.cx.dbg_location_clone_with_discriminator(loc, *o.get()).unwrap_or_else(
|| self.cx.dbg_loc(callsite_scope, parent_scope.inlined_at, DUMMY_SP),
)
}
Entry::Vacant(v) => {
v.insert(0);
loc
}
}
});
let file = self.cx.sess().source_map().lookup_source_file(scope_data.span.lo());
DebugScope {
dbg_scope,
inlined_at: inlined_at.or(parent_scope.inlined_at),
file_start_pos: file.start_pos,
file_end_pos: file.end_position(),
}
}
}