Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions compiler/rustc_codegen_gcc/src/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,22 @@ impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> {
_variable_alloca: Self::Value,
_direct_offset: Size,
_indirect_offsets: &[Size],
_fragment: Option<Range<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);
val.set_location(_dbg_loc);
}

fn dbg_var_value(
&mut self,
_dbg_var: Self::DIVariable,
_dbg_loc: Self::DILocation,
_value: Self::Value,
_direct_offset: Size,
_indirect_offsets: &[Size],
_fragment: &Option<Range<Size>>,
) {
}

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;
50 changes: 48 additions & 2 deletions compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
variable_alloca: Self::Value,
direct_offset: Size,
indirect_offsets: &[Size],
fragment: Option<Range<Size>>,
fragment: &Option<Range<Size>>,
) {
use dwarf_const::{DW_OP_LLVM_fragment, DW_OP_deref, DW_OP_plus_uconst};

Expand All @@ -190,7 +190,6 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
}

unsafe {
// FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`.
llvm::LLVMRustDIBuilderInsertDeclareAtEnd(
DIB(self.cx()),
variable_alloca,
Expand All @@ -203,6 +202,53 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
}
}

fn dbg_var_value(
&mut self,
dbg_var: &'ll DIVariable,
dbg_loc: &'ll DILocation,
value: 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, 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();

if direct_offset.bytes() > 0 {
addr_ops.push(DW_OP_plus_uconst);
addr_ops.push(direct_offset.bytes() as u64);
addr_ops.push(DW_OP_stack_value);
}
for &offset in indirect_offsets {
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 {
// `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 {
llvm::LLVMRustDIBuilderInsertDbgValueAtEnd(
DIB(self.cx()),
value,
dbg_var,
addr_ops.as_ptr(),
addr_ops.len() as c_uint,
dbg_loc,
self.llbb(),
);
}
}

fn set_dbg_loc(&mut self, dbg_loc: &'ll DILocation) {
unsafe {
llvm::LLVMSetCurrentDebugLocation2(self.llbuilder, dbg_loc);
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 @@ -2365,6 +2365,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
54 changes: 51 additions & 3 deletions compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,54 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
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: 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,
};

let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } =
calculate_debuginfo_offset(bx, projection, 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.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.val, layout, &[]);
}

/// 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 @@ -424,7 +472,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
alloca.val.llval,
Size::ZERO,
&[Size::ZERO],
var.fragment,
&var.fragment,
);
} else {
bx.dbg_var_addr(
Expand All @@ -433,7 +481,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
base.val.llval,
direct_offset,
&indirect_offsets,
var.fragment,
&var.fragment,
);
}
}
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, base.val.llval, Size::ZERO, &[], &fragment);
}
}
}
Expand Down
67 changes: 65 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 rustc_target::callconv::PassMode;
use tracing::instrument;

use super::{FunctionCx, LocalRef};
use crate::common::TypeKind;
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 @@ -101,4 +105,63 @@ 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 local_ref = match self.locals[place.local] {
LocalRef::Place(place_ref) | LocalRef::UnsizedPlace(place_ref) =>
Some(place_ref),

LocalRef::Operand(operand_ref) => {
let pointer = operand_ref.val.pointer_parts().0;
Some(PlaceRef::new_sized(pointer, operand_ref.layout))
}
LocalRef::PendingOperand => None,
}
.filter(|place_ref| {
// For the reference of an argument (e.x. `&_1`), it's only valid if the pass mode is indirect, and its reference is
// llval.
matches!(place.as_local().and_then(|local| self.fn_abi.args.get(local.as_usize() - 1).map(|arg| &arg.mode)), Some(&PassMode::Indirect {..}) | None) &&
// Drop unsupported projections.
// FIXME: Add a test case.
place.projection.iter().all(|p| p.can_use_in_debuginfo()) &&
// Only pointers can be calculated addresses.
bx.type_kind(bx.val_ty(place_ref.val.llval)) == TypeKind::Pointer
});
if let Some(local_ref) = local_ref {
let (base_layout, projection) = if place.is_indirect_first_projection() {
// For `_n = &((*_1).0: i32);`, we are calculating the address of `_1.0`, so
// we should drop the deref projection.
let projected_ty = local_ref
.layout
.ty
.builtin_deref(true)
.unwrap_or_else(|| bug!("deref of non-pointer {:?}", local_ref));
let layout = bx.cx().layout_of(projected_ty);
(layout, &place.projection[1..])
} else {
(local_ref.layout, place.projection.as_slice())
};
self.debug_new_val_to_local(bx, *dest, local_ref.val, base_layout, projection);
} else {
// If the address cannot be computed, use poison to indicate that the value has been optimized out.
self.debug_poison_to_local(bx, *dest);
}
}
StmtDebugInfo::InvalidAssign(local) => {
self.debug_poison_to_local(bx, *local);
}
}
}

pub(crate) fn codegen_stmt_debuginfos(
&mut self,
bx: &mut Bx,
debuginfos: &[StmtDebugInfo<'tcx>],
) {
for debuginfo in debuginfos {
self.codegen_stmt_debuginfo(bx, debuginfo);
}
}
}
14 changes: 13 additions & 1 deletion compiler/rustc_codegen_ssa/src/traits/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,19 @@ pub trait DebugInfoBuilderMethods: BackendTypes {
indirect_offsets: &[Size],
// Byte range in the `dbg_var` covered by this fragment,
// if this is a fragment of a composite `DIVariable`.
fragment: Option<Range<Size>>,
fragment: &Option<Range<Size>>,
);
fn dbg_var_value(
&mut self,
dbg_var: Self::DIVariable,
dbg_loc: Self::DILocation,
value: Self::Value,
direct_offset: Size,
// NB: each offset implies a deref (i.e. they're steps in a pointer chain).
indirect_offsets: &[Size],
// Byte range in the `dbg_var` covered by this fragment,
// if this is a fragment of a composite `DIVariable`.
fragment: &Option<Range<Size>>,
);
fn set_dbg_loc(&mut self, dbg_loc: Self::DILocation);
fn clear_dbg_loc(&mut self);
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 @@ -1254,6 +1255,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
3 changes: 3 additions & 0 deletions compiler/rustc_middle/src/mir/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,9 @@ impl Debug for StmtDebugInfo<'_> {
StmtDebugInfo::AssignRef(local, place) => {
write!(fmt, "{local:?} = &{place:?}")
}
StmtDebugInfo::InvalidAssign(local) => {
write!(fmt, "{local:?} = &?")
}
}
}
}
Expand Down
Loading
Loading