Skip to content

Introduce debuginfo to statements in MIR #142771

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -1162,12 +1162,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
let opt_assignment_rhs_span =
self.find_assignments(local).first().map(|&location| {
if let Some(mir::Statement {
source_info: _,
kind:
mir::StatementKind::Assign(box (
_,
mir::Rvalue::Use(mir::Operand::Copy(place)),
)),
..
}) = self.body[location.block].statements.get(location.statement_index)
{
self.body.local_decls[place.local].source_info.span
Expand Down
7 changes: 5 additions & 2 deletions compiler/rustc_codegen_gcc/src/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,17 @@ impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> {
&mut self,
_dbg_var: Self::DIVariable,
_dbg_loc: Self::DILocation,
_variable_alloca: Self::Value,
is_declared: bool,
val: Self::Value,
_direct_offset: Size,
_indirect_offsets: &[Size],
_fragment: Option<Range<Size>>,
) {
// FIXME(tempdragon): Not sure if this is correct, probably wrong but still keep it here.
#[cfg(feature = "master")]
_variable_alloca.set_location(_dbg_loc);
if is_declared {
val.set_location(_dbg_loc);
}
}

fn insert_reference_to_gdb_debug_scripts_section_global(&mut self) {
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ declare_constant!(DW_OP_plus_uconst: u64);
/// Double-checked by a static assertion in `RustWrapper.cpp`.
#[allow(non_upper_case_globals)]
pub(crate) const DW_OP_LLVM_fragment: u64 = 0x1000;
// It describes the actual value of a source variable which might not exist in registers or in memory.
#[allow(non_upper_case_globals)]
pub(crate) const DW_OP_stack_value: u64 = 0x9f;
47 changes: 34 additions & 13 deletions compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,46 +160,67 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
&mut self,
dbg_var: &'ll DIVariable,
dbg_loc: &'ll DILocation,
variable_alloca: Self::Value,
is_declared: bool,
val: Self::Value,
direct_offset: Size,
indirect_offsets: &[Size],
fragment: Option<Range<Size>>,
) {
use dwarf_const::{DW_OP_LLVM_fragment, DW_OP_deref, DW_OP_plus_uconst};
use dwarf_const::{DW_OP_LLVM_fragment, DW_OP_deref, DW_OP_plus_uconst, DW_OP_stack_value};

// Convert the direct and indirect offsets and fragment byte range to address ops.
let mut addr_ops = SmallVec::<[u64; 8]>::new();
let mut need_stack_value = false;

if direct_offset.bytes() > 0 {
need_stack_value = true;
addr_ops.push(DW_OP_plus_uconst);
addr_ops.push(direct_offset.bytes() as u64);
}
for &offset in indirect_offsets {
need_stack_value = true;
addr_ops.push(DW_OP_deref);
if offset.bytes() > 0 {
addr_ops.push(DW_OP_plus_uconst);
addr_ops.push(offset.bytes() as u64);
}
}
if let Some(fragment) = fragment {
need_stack_value = true;
// `DW_OP_LLVM_fragment` takes as arguments the fragment's
// offset and size, both of them in bits.
addr_ops.push(DW_OP_LLVM_fragment);
addr_ops.push(fragment.start.bits() as u64);
addr_ops.push((fragment.end - fragment.start).bits() as u64);
}

unsafe {
// FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`.
llvm::LLVMRustDIBuilderInsertDeclareAtEnd(
DIB(self.cx()),
variable_alloca,
dbg_var,
addr_ops.as_ptr(),
addr_ops.len() as c_uint,
dbg_loc,
self.llbb(),
);
if is_declared {
unsafe {
llvm::LLVMRustDIBuilderInsertDeclareAtEnd(
DIB(self.cx()),
val,
dbg_var,
addr_ops.as_ptr(),
addr_ops.len() as c_uint,
dbg_loc,
self.llbb(),
);
}
} else {
if need_stack_value {
addr_ops.push(DW_OP_stack_value);
}
unsafe {
llvm::LLVMRustDIBuilderInsertDbgValueAtEnd(
DIB(self.cx()),
val,
dbg_var,
addr_ops.as_ptr(),
addr_ops.len() as c_uint,
dbg_loc,
self.llbb(),
);
}
}
}

Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2314,6 +2314,16 @@ unsafe extern "C" {
InsertAtEnd: &'a BasicBlock,
);

pub(crate) fn LLVMRustDIBuilderInsertDbgValueAtEnd<'a>(
Builder: &DIBuilder<'a>,
Val: &'a Value,
VarInfo: &'a DIVariable,
AddrOps: *const u64,
AddrOpsCount: c_uint,
DL: &'a DILocation,
InsertAtEnd: &'a BasicBlock,
);

pub(crate) fn LLVMRustDIBuilderCreateEnumerator<'a>(
Builder: &DIBuilder<'a>,
Name: *const c_char,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_ssa/src/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1317,6 +1317,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
for statement in &data.statements {
self.codegen_statement(bx, statement);
}
self.codegen_stmt_debuginfos(bx, &data.after_last_stmt_debuginfos);

let merging_succ = self.codegen_terminator(bx, bb, data.terminator());
if let MergingSucc::False = merging_succ {
Expand Down
50 changes: 49 additions & 1 deletion compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,52 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
spill_slot
}

pub(crate) fn debug_new_value_to_local(
&self,
bx: &mut Bx,
local: mir::Local,
base: PlaceValue<Bx::Value>,
layout: TyAndLayout<'tcx>,
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,
};

for var in vars.iter().cloned() {
self.debug_new_value_to_local_as_var(bx, base, layout, projection, var);
}
}

fn debug_new_value_to_local_as_var(
&self,
bx: &mut Bx,
base: PlaceValue<Bx::Value>,
layout: TyAndLayout<'tcx>,
projection: &[mir::PlaceElem<'tcx>],
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, projection, layout);
bx.dbg_var_addr(
dbg_var,
dbg_loc,
false,
base.llval,
direct_offset,
&indirect_offsets,
var.fragment,
);
}

/// 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) {
Expand Down Expand Up @@ -421,6 +467,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx.dbg_var_addr(
dbg_var,
dbg_loc,
true,
alloca.val.llval,
Size::ZERO,
&[Size::ZERO],
Expand All @@ -430,6 +477,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx.dbg_var_addr(
dbg_var,
dbg_loc,
true,
base.val.llval,
direct_offset,
&indirect_offsets,
Expand All @@ -455,7 +503,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
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);
bx.dbg_var_addr(dbg_var, dbg_loc, true, base.val.llval, Size::ZERO, &[], fragment);
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_codegen_ssa/src/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,15 +354,15 @@ fn optimize_use_clone<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(

let destination_block = target.unwrap();

bb.statements.push(mir::Statement {
source_info: bb.terminator().source_info,
kind: mir::StatementKind::Assign(Box::new((
bb.statements.push(mir::Statement::new(
bb.terminator().source_info,
mir::StatementKind::Assign(Box::new((
*destination,
mir::Rvalue::Use(mir::Operand::Copy(
arg_place.project_deeper(&[mir::ProjectionElem::Deref], tcx),
)),
))),
});
));

bb.terminator_mut().kind = mir::TerminatorKind::Goto { target: destination_block };
}
Expand Down
72 changes: 70 additions & 2 deletions compiler/rustc_codegen_ssa/src/mir/statement.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
use rustc_middle::mir::{self, NonDivergingIntrinsic};
use rustc_middle::span_bug;
use rustc_middle::mir::{self, NonDivergingIntrinsic, StmtDebugInfo};
use rustc_middle::{bug, span_bug};
use tracing::instrument;

use super::{FunctionCx, LocalRef};
use crate::common::TypeKind;
use crate::mir::operand::OperandValue;
use crate::mir::place::PlaceRef;
use crate::traits::*;

impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
#[instrument(level = "debug", skip(self, bx))]
pub(crate) fn codegen_statement(&mut self, bx: &mut Bx, statement: &mir::Statement<'tcx>) {
self.codegen_stmt_debuginfos(bx, &statement.debuginfos);
self.set_debug_loc(bx, statement.source_info);
match statement.kind {
mir::StatementKind::Assign(box (ref place, ref rvalue)) => {
Expand Down Expand Up @@ -96,4 +100,68 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
| mir::StatementKind::Nop => {}
}
}

pub(crate) fn codegen_stmt_debuginfo(&mut self, bx: &mut Bx, debuginfo: &StmtDebugInfo<'tcx>) {
match debuginfo {
StmtDebugInfo::AssignRef(dest, place) => {
let place_ref = match self.locals[place.local] {
LocalRef::Place(place_ref) | LocalRef::UnsizedPlace(place_ref) => {
Some(place_ref)
}
LocalRef::Operand(operand_ref) => match operand_ref.val {
OperandValue::Ref(_place_value) => {
todo!("supports OperandValue::Ref")
}
OperandValue::Immediate(v) => {
// FIXME: add ref to layout?
Some(PlaceRef::new_sized(v, operand_ref.layout))
}
OperandValue::Pair(_, _) => None,
OperandValue::ZeroSized => None,
},
LocalRef::PendingOperand => None,
}
.filter(|place_ref| {
// Drop unsupported projections.
// FIXME: Add a test case.
place.projection.iter().all(|p| p.can_use_in_debuginfo()) &&
// Only pointers can calculate addresses.
bx.type_kind(bx.val_ty(place_ref.val.llval)) == TypeKind::Pointer
});
let (val, layout, projection) =
match (place_ref, place.is_indirect_first_projection()) {
(Some(place_ref), false) => {
(place_ref.val, place_ref.layout, place.projection.as_slice())
}
(Some(place_ref), true) => {
let projected_ty =
place_ref.layout.ty.builtin_deref(true).unwrap_or_else(|| {
bug!("deref of non-pointer {:?}", place_ref)
});
let layout = bx.cx().layout_of(projected_ty);
(place_ref.val, layout, &place.projection[1..])
}
_ => {
// If the address cannot be computed, use poison to indicate that the value has been optimized out.
let ty = self.monomorphize(self.mir.local_decls[*dest].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);
(place_ref.val, layout, [].as_slice())
}
};
self.debug_new_value_to_local(bx, *dest, val, layout, projection);
}
}
}
pub(crate) fn codegen_stmt_debuginfos(
&mut self,
bx: &mut Bx,
debuginfos: &[StmtDebugInfo<'tcx>],
) {
for debuginfo in debuginfos {
self.codegen_stmt_debuginfo(bx, debuginfo);
}
}
}
3 changes: 2 additions & 1 deletion compiler/rustc_codegen_ssa/src/traits/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ pub trait DebugInfoBuilderMethods: BackendTypes {
&mut self,
dbg_var: Self::DIVariable,
dbg_loc: Self::DILocation,
variable_alloca: Self::Value,
is_declared: bool,
val: Self::Value,
direct_offset: Size,
// NB: each offset implies a deref (i.e. they're steps in a pointer chain).
indirect_offsets: &[Size],
Expand Down
13 changes: 13 additions & 0 deletions compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ using namespace llvm::object;
// This opcode is an LLVM detail that could hypothetically change (?), so
// verify that the hard-coded value in `dwarf_const.rs` still agrees with LLVM.
static_assert(dwarf::DW_OP_LLVM_fragment == 0x1000);
static_assert(dwarf::DW_OP_stack_value == 0x9f);

// LLVMAtomicOrdering is already an enum - don't create another
// one.
Expand Down Expand Up @@ -1241,6 +1242,18 @@ LLVMRustDIBuilderInsertDeclareAtEnd(LLVMDIBuilderRef Builder, LLVMValueRef V,
DebugLoc(cast<MDNode>(unwrap(DL))), unwrap(InsertAtEnd));
}

extern "C" void
LLVMRustDIBuilderInsertDbgValueAtEnd(LLVMDIBuilderRef Builder, LLVMValueRef V,
LLVMMetadataRef VarInfo, uint64_t *AddrOps,
unsigned AddrOpsCount, LLVMMetadataRef DL,
LLVMBasicBlockRef InsertAtEnd) {
unwrap(Builder)->insertDbgValueIntrinsic(
unwrap(V), unwrap<DILocalVariable>(VarInfo),
unwrap(Builder)->createExpression(
llvm::ArrayRef<uint64_t>(AddrOps, AddrOpsCount)),
DebugLoc(cast<MDNode>(unwrap(DL))), unwrap(InsertAtEnd));
}

extern "C" LLVMMetadataRef
LLVMRustDIBuilderCreateEnumerator(LLVMDIBuilderRef Builder, const char *Name,
size_t NameLen, const uint64_t Value[2],
Expand Down
Loading
Loading