Skip to content
Merged
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
13 changes: 12 additions & 1 deletion compiler/rustc_codegen_gcc/src/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,24 @@ 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);
}

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) {
// TODO(antoyo): insert reference to gdb debug scripts section global.
}
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;
53 changes: 51 additions & 2 deletions compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,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 Down Expand Up @@ -187,7 +187,6 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
llvm::LLVMDIBuilderCreateExpression(di_builder, addr_ops.as_ptr(), addr_ops.len())
};
unsafe {
// FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`.
llvm::LLVMDIBuilderInsertDeclareRecordAtEnd(
di_builder,
variable_alloca,
Expand All @@ -199,6 +198,56 @@ 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);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to move all debuginfo to DbgValue?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, but it's very complicated. It might be better to let LLVM do it.

LLVM might also preserve the declare: https://rust.godbolt.org/z/GKWYeT7rK, but this seems like it could be dropped.

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);
}

let di_builder = DIB(self.cx());
let addr_expr = unsafe {
llvm::LLVMDIBuilderCreateExpression(di_builder, addr_ops.as_ptr(), addr_ops.len())
};
unsafe {
llvm::LLVMDIBuilderInsertDbgValueRecordAtEnd(
di_builder,
value,
dbg_var,
addr_expr,
dbg_loc,
self.llbb(),
);
}
}

fn set_dbg_loc(&mut self, dbg_loc: &'ll DILocation) {
unsafe {
llvm::LLVMSetCurrentDebugLocation2(self.llbuilder, dbg_loc);
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1991,6 +1991,15 @@ unsafe extern "C" {
Block: &'ll BasicBlock,
) -> &'ll DbgRecord;

pub(crate) fn LLVMDIBuilderInsertDbgValueRecordAtEnd<'ll>(
Builder: &DIBuilder<'ll>,
Val: &'ll Value,
VarInfo: &'ll Metadata,
Expr: &'ll Metadata,
DebugLoc: &'ll Metadata,
Block: &'ll BasicBlock,
) -> &'ll DbgRecord;

pub(crate) fn LLVMDIBuilderCreateAutoVariable<'ll>(
Builder: &DIBuilder<'ll>,
Scope: &'ll Metadata,
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_codegen_ssa/src/mir/analyze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,10 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> Visitor<'tcx> for LocalAnalyzer
PlaceContext::MutatingUse(MutatingUseContext::Yield) => bug!(),
}
}

fn visit_statement_debuginfo(&mut self, _: &mir::StmtDebugInfo<'tcx>, _: Location) {
// Debuginfo does not generate actual code.
}
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
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 @@ -1320,6 +1320,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
53 changes: 50 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,53 @@ 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: 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) {
Expand Down Expand Up @@ -424,7 +471,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 +480,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 +502,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
17 changes: 12 additions & 5 deletions compiler/rustc_codegen_ssa/src/mir/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,23 @@ pub enum OperandValue<V> {
}

impl<V: CodegenObject> OperandValue<V> {
/// Return the data pointer and optional metadata as backend values
/// if this value can be treat as a pointer.
pub(crate) fn try_pointer_parts(self) -> Option<(V, Option<V>)> {
match self {
OperandValue::Immediate(llptr) => Some((llptr, None)),
OperandValue::Pair(llptr, llextra) => Some((llptr, Some(llextra))),
OperandValue::Ref(_) | OperandValue::ZeroSized => None,
}
}

/// Treat this value as a pointer and return the data pointer and
/// optional metadata as backend values.
///
/// If you're making a place, use [`Self::deref`] instead.
pub(crate) fn pointer_parts(self) -> (V, Option<V>) {
match self {
OperandValue::Immediate(llptr) => (llptr, None),
OperandValue::Pair(llptr, llextra) => (llptr, Some(llextra)),
_ => bug!("OperandValue cannot be a pointer: {self:?}"),
}
self.try_pointer_parts()
.unwrap_or_else(|| bug!("OperandValue cannot be a pointer: {self:?}"))
}

/// Treat this value as a pointer and return the place to which it points.
Expand Down
48 changes: 47 additions & 1 deletion compiler/rustc_codegen_ssa/src/mir/statement.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rustc_middle::mir::{self, NonDivergingIntrinsic};
use rustc_middle::mir::{self, NonDivergingIntrinsic, StmtDebugInfo};
use rustc_middle::span_bug;
use tracing::instrument;

Expand All @@ -8,6 +8,7 @@ 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 +102,49 @@ 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] {
// For an rvalue like `&(_1.1)`, when `BackendRepr` is `BackendRepr::Memory`, we allocate a block of memory to this place.
// The place is an indirect pointer, we can refer to it directly.
LocalRef::Place(place_ref) => Some((place_ref, place.projection.as_slice())),
// For an rvalue like `&((*_1).1)`, we are calculating the address of `_1.1`.
// The deref projection is no-op here.
LocalRef::Operand(operand_ref) if place.is_indirect_first_projection() => {
Some((operand_ref.deref(bx.cx()), &place.projection[1..]))
}
// For an rvalue like `&1`, when `BackendRepr` is `BackendRepr::Scalar`,
// we cannot get the address.
// N.B. `non_ssa_locals` returns that this is an SSA local.
LocalRef::Operand(_) => None,
LocalRef::UnsizedPlace(_) | LocalRef::PendingOperand => None,
}
.filter(|(_, projection)| {
// Drop unsupported projections.
projection.iter().all(|p| p.can_use_in_debuginfo())
});
if let Some((base, projection)) = local_ref {
self.debug_new_val_to_local(bx, *dest, base, projection);
} else {
// If the address cannot be calculated, 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
1 change: 1 addition & 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
Loading
Loading