Skip to content
Open
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;
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);
}
}
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);
}

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
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
73 changes: 71 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, RETURN_PLACE, 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,69 @@ 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) => operand_ref
.val
.try_pointer_parts()
.map(|(pointer, _)| 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.
let local_ref_pass_mode = place.as_local().and_then(|local| {
if local == RETURN_PLACE {
None
} else {
self.fn_abi.args.get(local.as_usize() - 1).map(|arg| &arg.mode)
}
});
matches!(local_ref_pass_mode, Some(&PassMode::Indirect {..}) | None) &&
// Drop unsupported projections.
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);
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we do simpler and use codegen_place_to_pointer? Or try to mimic codegen_place to some extent?

Copy link
Member Author

Choose a reason for hiding this comment

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

No, we cannot emit any IR that is a non-debug value here. I think we will know a general code when we handle all rvalue.

} 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
Loading
Loading