diff options
Diffstat (limited to 'compiler/rustc_codegen_cranelift/src/debuginfo')
3 files changed, 138 insertions, 359 deletions
diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs index 589910ede..9583cd2ec 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs @@ -9,7 +9,7 @@ use gimli::{RunTimeEndian, SectionId}; use super::object::WriteDebugInfo; use super::DebugContext; -impl DebugContext<'_> { +impl DebugContext { pub(crate) fn emit(&mut self, product: &mut ObjectProduct) { let unit_range_list_id = self.dwarf.unit.ranges.add(self.unit_range_list.clone()); let root = self.dwarf.unit.root(); diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs index bbcb95913..463de6a91 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs @@ -3,8 +3,10 @@ use std::ffi::OsStr; use std::path::{Component, Path}; +use crate::debuginfo::FunctionDebugContext; use crate::prelude::*; +use rustc_data_structures::sync::Lrc; use rustc_span::{ FileName, Pos, SourceFile, SourceFileAndLine, SourceFileHash, SourceFileHashAlgorithm, }; @@ -14,7 +16,6 @@ use cranelift_codegen::MachSrcLoc; use gimli::write::{ Address, AttributeValue, FileId, FileInfo, LineProgram, LineString, LineStringTable, - UnitEntryId, }; // OPTIMIZATION: It is cheaper to do this in one pass than using `.parent()` and `.file_name()`. @@ -47,9 +48,9 @@ fn osstr_as_utf8_bytes(path: &OsStr) -> &[u8] { } } -pub(crate) const MD5_LEN: usize = 16; +const MD5_LEN: usize = 16; -pub(crate) fn make_file_info(hash: SourceFileHash) -> Option<FileInfo> { +fn make_file_info(hash: SourceFileHash) -> Option<FileInfo> { if hash.kind == SourceFileHashAlgorithm::Md5 { let mut buf = [0u8; MD5_LEN]; buf.copy_from_slice(hash.hash_bytes()); @@ -59,160 +60,132 @@ pub(crate) fn make_file_info(hash: SourceFileHash) -> Option<FileInfo> { } } -fn line_program_add_file( - line_program: &mut LineProgram, - line_strings: &mut LineStringTable, - file: &SourceFile, -) -> FileId { - match &file.name { - FileName::Real(path) => { - let (dir_path, file_name) = split_path_dir_and_file(path.remapped_path_if_available()); - let dir_name = osstr_as_utf8_bytes(dir_path.as_os_str()); - let file_name = osstr_as_utf8_bytes(file_name); - - let dir_id = if !dir_name.is_empty() { - let dir_name = LineString::new(dir_name, line_program.encoding(), line_strings); - line_program.add_directory(dir_name) - } else { - line_program.default_directory() - }; - let file_name = LineString::new(file_name, line_program.encoding(), line_strings); +impl DebugContext { + pub(crate) fn get_span_loc( + tcx: TyCtxt<'_>, + function_span: Span, + span: Span, + ) -> (Lrc<SourceFile>, u64, u64) { + // Based on https://github.com/rust-lang/rust/blob/e369d87b015a84653343032833d65d0545fd3f26/src/librustc_codegen_ssa/mir/mod.rs#L116-L131 + // In order to have a good line stepping behavior in debugger, we overwrite debug + // locations of macro expansions with that of the outermost expansion site (when the macro is + // annotated with `#[collapse_debuginfo]` or when `-Zdebug-macros` is provided). + let span = if tcx.should_collapse_debuginfo(span) { + span + } else { + // Walk up the macro expansion chain until we reach a non-expanded span. + // We also stop at the function body level because no line stepping can occur + // at the level above that. + rustc_span::hygiene::walk_chain(span, function_span.ctxt()) + }; - let info = make_file_info(file.src_hash); + match tcx.sess.source_map().lookup_line(span.lo()) { + Ok(SourceFileAndLine { sf: file, line }) => { + let line_pos = file.line_begin_pos(span.lo()); - line_program.file_has_md5 &= info.is_some(); - line_program.add_file(file_name, dir_id, info) + ( + file, + u64::try_from(line).unwrap() + 1, + u64::from((span.lo() - line_pos).to_u32()) + 1, + ) + } + Err(file) => (file, 0, 0), } - // FIXME give more appropriate file names - filename => { - let dir_id = line_program.default_directory(); - let dummy_file_name = LineString::new( - filename.prefer_remapped().to_string().into_bytes(), - line_program.encoding(), - line_strings, - ); - line_program.add_file(dummy_file_name, dir_id, None) + } + + pub(crate) fn add_source_file(&mut self, source_file: &SourceFile) -> FileId { + let line_program: &mut LineProgram = &mut self.dwarf.unit.line_program; + let line_strings: &mut LineStringTable = &mut self.dwarf.line_strings; + + match &source_file.name { + FileName::Real(path) => { + let (dir_path, file_name) = + split_path_dir_and_file(path.remapped_path_if_available()); + let dir_name = osstr_as_utf8_bytes(dir_path.as_os_str()); + let file_name = osstr_as_utf8_bytes(file_name); + + let dir_id = if !dir_name.is_empty() { + let dir_name = LineString::new(dir_name, line_program.encoding(), line_strings); + line_program.add_directory(dir_name) + } else { + line_program.default_directory() + }; + let file_name = LineString::new(file_name, line_program.encoding(), line_strings); + + let info = make_file_info(source_file.src_hash); + + line_program.file_has_md5 &= info.is_some(); + line_program.add_file(file_name, dir_id, info) + } + // FIXME give more appropriate file names + filename => { + let dir_id = line_program.default_directory(); + let dummy_file_name = LineString::new( + filename.prefer_remapped().to_string().into_bytes(), + line_program.encoding(), + line_strings, + ); + line_program.add_file(dummy_file_name, dir_id, None) + } } } } -impl<'tcx> DebugContext<'tcx> { - pub(super) fn emit_location(&mut self, entry_id: UnitEntryId, span: Span) { - let loc = self.tcx.sess.source_map().lookup_char_pos(span.lo()); - - let file_id = line_program_add_file( - &mut self.dwarf.unit.line_program, - &mut self.dwarf.line_strings, - &loc.file, - ); - - let entry = self.dwarf.unit.get_mut(entry_id); - - entry.set(gimli::DW_AT_decl_file, AttributeValue::FileIndex(Some(file_id))); - entry.set(gimli::DW_AT_decl_line, AttributeValue::Udata(loc.line as u64)); - entry.set(gimli::DW_AT_decl_column, AttributeValue::Udata(loc.col.to_usize() as u64)); +impl FunctionDebugContext { + pub(crate) fn add_dbg_loc(&mut self, file_id: FileId, line: u64, column: u64) -> SourceLoc { + let (index, _) = self.source_loc_set.insert_full((file_id, line, column)); + SourceLoc::new(u32::try_from(index).unwrap()) } pub(super) fn create_debug_lines( &mut self, + debug_context: &mut DebugContext, symbol: usize, - entry_id: UnitEntryId, context: &Context, - function_span: Span, - source_info_set: &indexmap::IndexSet<SourceInfo>, ) -> CodeOffset { - let tcx = self.tcx; - let line_program = &mut self.dwarf.unit.line_program; - - let line_strings = &mut self.dwarf.line_strings; - let mut last_span = None; - let mut last_file = None; - let mut create_row_for_span = |line_program: &mut LineProgram, span: Span| { - if let Some(last_span) = last_span { - if span == last_span { - line_program.generate_row(); - return; - } - } - last_span = Some(span); - - // Based on https://github.com/rust-lang/rust/blob/e369d87b015a84653343032833d65d0545fd3f26/src/librustc_codegen_ssa/mir/mod.rs#L116-L131 - // In order to have a good line stepping behavior in debugger, we overwrite debug - // locations of macro expansions with that of the outermost expansion site - // (unless the crate is being compiled with `-Z debug-macros`). - let span = if !span.from_expansion() || tcx.sess.opts.unstable_opts.debug_macros { - span - } else { - // Walk up the macro expansion chain until we reach a non-expanded span. - // We also stop at the function body level because no line stepping can occur - // at the level above that. - rustc_span::hygiene::walk_chain(span, function_span.ctxt()) + let create_row_for_span = + |debug_context: &mut DebugContext, source_loc: (FileId, u64, u64)| { + let (file_id, line, col) = source_loc; + + debug_context.dwarf.unit.line_program.row().file = file_id; + debug_context.dwarf.unit.line_program.row().line = line; + debug_context.dwarf.unit.line_program.row().column = col; + debug_context.dwarf.unit.line_program.generate_row(); }; - let (file, line, col) = match tcx.sess.source_map().lookup_line(span.lo()) { - Ok(SourceFileAndLine { sf: file, line }) => { - let line_pos = file.line_begin_pos(span.lo()); - - ( - file, - u64::try_from(line).unwrap() + 1, - u64::from((span.lo() - line_pos).to_u32()) + 1, - ) - } - Err(file) => (file, 0, 0), - }; - - // line_program_add_file is very slow. - // Optimize for the common case of the current file not being changed. - let current_file_changed = if let Some(last_file) = &last_file { - // If the allocations are not equal, then the files may still be equal, but that - // is not a problem, as this is just an optimization. - !rustc_data_structures::sync::Lrc::ptr_eq(last_file, &file) - } else { - true - }; - if current_file_changed { - let file_id = line_program_add_file(line_program, line_strings, &file); - line_program.row().file = file_id; - last_file = Some(file); - } - - line_program.row().line = line; - line_program.row().column = col; - line_program.generate_row(); - }; - - line_program.begin_sequence(Some(Address::Symbol { symbol, addend: 0 })); + debug_context + .dwarf + .unit + .line_program + .begin_sequence(Some(Address::Symbol { symbol, addend: 0 })); let mut func_end = 0; - let mcr = context.mach_compile_result.as_ref().unwrap(); + let mcr = context.compiled_code().unwrap(); for &MachSrcLoc { start, end, loc } in mcr.buffer.get_srclocs_sorted() { - line_program.row().address_offset = u64::from(start); + debug_context.dwarf.unit.line_program.row().address_offset = u64::from(start); if !loc.is_default() { - let source_info = *source_info_set.get_index(loc.bits() as usize).unwrap(); - create_row_for_span(line_program, source_info.span); + let source_loc = *self.source_loc_set.get_index(loc.bits() as usize).unwrap(); + create_row_for_span(debug_context, source_loc); } else { - create_row_for_span(line_program, function_span); + create_row_for_span(debug_context, self.function_source_loc); } func_end = end; } - line_program.end_sequence(u64::from(func_end)); + debug_context.dwarf.unit.line_program.end_sequence(u64::from(func_end)); let func_end = mcr.buffer.total_size(); assert_ne!(func_end, 0); - let entry = self.dwarf.unit.get_mut(entry_id); + let entry = debug_context.dwarf.unit.get_mut(self.entry_id); entry.set( gimli::DW_AT_low_pc, AttributeValue::Address(Address::Symbol { symbol, addend: 0 }), ); entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(u64::from(func_end))); - self.emit_location(entry_id, function_span); - func_end } } diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs index 693092ba5..c55db2017 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs @@ -7,35 +7,34 @@ mod unwind; use crate::prelude::*; -use rustc_index::vec::IndexVec; - -use cranelift_codegen::entity::EntityRef; -use cranelift_codegen::ir::{Endianness, LabelValueLoc, ValueLabel}; +use cranelift_codegen::ir::Endianness; use cranelift_codegen::isa::TargetIsa; -use cranelift_codegen::ValueLocRange; use gimli::write::{ - Address, AttributeValue, DwarfUnit, Expression, LineProgram, LineString, Location, - LocationList, Range, RangeList, UnitEntryId, + Address, AttributeValue, DwarfUnit, FileId, LineProgram, LineString, Range, RangeList, + UnitEntryId, }; -use gimli::{Encoding, Format, LineEncoding, RunTimeEndian, X86_64}; +use gimli::{Encoding, Format, LineEncoding, RunTimeEndian}; +use indexmap::IndexSet; pub(crate) use emit::{DebugReloc, DebugRelocName}; pub(crate) use unwind::UnwindContext; -pub(crate) struct DebugContext<'tcx> { - tcx: TyCtxt<'tcx>, - +pub(crate) struct DebugContext { endian: RunTimeEndian, dwarf: DwarfUnit, unit_range_list: RangeList, +} - types: FxHashMap<Ty<'tcx>, UnitEntryId>, +pub(crate) struct FunctionDebugContext { + entry_id: UnitEntryId, + function_source_loc: (FileId, u64, u64), + source_loc_set: indexmap::IndexSet<(FileId, u64, u64)>, } -impl<'tcx> DebugContext<'tcx> { - pub(crate) fn new(tcx: TyCtxt<'tcx>, isa: &dyn TargetIsa) -> Self { +impl DebugContext { + pub(crate) fn new(tcx: TyCtxt<'_>, isa: &dyn TargetIsa) -> Self { let encoding = Encoding { format: Format::Dwarf32, // FIXME this should be configurable @@ -101,127 +100,18 @@ impl<'tcx> DebugContext<'tcx> { root.set(gimli::DW_AT_low_pc, AttributeValue::Address(Address::Constant(0))); } - DebugContext { - tcx, - - endian, - - dwarf, - unit_range_list: RangeList(Vec::new()), - - types: FxHashMap::default(), - } - } - - fn dwarf_ty(&mut self, ty: Ty<'tcx>) -> UnitEntryId { - if let Some(type_id) = self.types.get(&ty) { - return *type_id; - } - - let new_entry = |dwarf: &mut DwarfUnit, tag| dwarf.unit.add(dwarf.unit.root(), tag); - - let primitive = |dwarf: &mut DwarfUnit, ate| { - let type_id = new_entry(dwarf, gimli::DW_TAG_base_type); - let type_entry = dwarf.unit.get_mut(type_id); - type_entry.set(gimli::DW_AT_encoding, AttributeValue::Encoding(ate)); - type_id - }; - - let name = format!("{}", ty); - let layout = self.tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap(); - - let type_id = match ty.kind() { - ty::Bool => primitive(&mut self.dwarf, gimli::DW_ATE_boolean), - ty::Char => primitive(&mut self.dwarf, gimli::DW_ATE_UTF), - ty::Uint(_) => primitive(&mut self.dwarf, gimli::DW_ATE_unsigned), - ty::Int(_) => primitive(&mut self.dwarf, gimli::DW_ATE_signed), - ty::Float(_) => primitive(&mut self.dwarf, gimli::DW_ATE_float), - ty::Ref(_, pointee_ty, _mutbl) - | ty::RawPtr(ty::TypeAndMut { ty: pointee_ty, mutbl: _mutbl }) => { - let type_id = new_entry(&mut self.dwarf, gimli::DW_TAG_pointer_type); - - // Ensure that type is inserted before recursing to avoid duplicates - self.types.insert(ty, type_id); - - let pointee = self.dwarf_ty(*pointee_ty); - - let type_entry = self.dwarf.unit.get_mut(type_id); - - //type_entry.set(gimli::DW_AT_mutable, AttributeValue::Flag(mutbl == rustc_hir::Mutability::Mut)); - type_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(pointee)); - - type_id - } - ty::Adt(adt_def, _substs) if adt_def.is_struct() && !layout.is_unsized() => { - let type_id = new_entry(&mut self.dwarf, gimli::DW_TAG_structure_type); - - // Ensure that type is inserted before recursing to avoid duplicates - self.types.insert(ty, type_id); - - let variant = adt_def.non_enum_variant(); - - for (field_idx, field_def) in variant.fields.iter().enumerate() { - let field_offset = layout.fields.offset(field_idx); - let field_layout = layout.field( - &layout::LayoutCx { tcx: self.tcx, param_env: ParamEnv::reveal_all() }, - field_idx, - ); - - let field_type = self.dwarf_ty(field_layout.ty); - - let field_id = self.dwarf.unit.add(type_id, gimli::DW_TAG_member); - let field_entry = self.dwarf.unit.get_mut(field_id); - - field_entry.set( - gimli::DW_AT_name, - AttributeValue::String(field_def.name.as_str().to_string().into_bytes()), - ); - field_entry.set( - gimli::DW_AT_data_member_location, - AttributeValue::Udata(field_offset.bytes()), - ); - field_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(field_type)); - } - - type_id - } - _ => new_entry(&mut self.dwarf, gimli::DW_TAG_structure_type), - }; - - let type_entry = self.dwarf.unit.get_mut(type_id); - - type_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes())); - type_entry.set(gimli::DW_AT_byte_size, AttributeValue::Udata(layout.size.bytes())); - - self.types.insert(ty, type_id); - - type_id - } - - fn define_local(&mut self, scope: UnitEntryId, name: String, ty: Ty<'tcx>) -> UnitEntryId { - let dw_ty = self.dwarf_ty(ty); - - let var_id = self.dwarf.unit.add(scope, gimli::DW_TAG_variable); - let var_entry = self.dwarf.unit.get_mut(var_id); - - var_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes())); - var_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(dw_ty)); - - var_id + DebugContext { endian, dwarf, unit_range_list: RangeList(Vec::new()) } } pub(crate) fn define_function( &mut self, - instance: Instance<'tcx>, - func_id: FuncId, + tcx: TyCtxt<'_>, name: &str, - isa: &dyn TargetIsa, - context: &Context, - source_info_set: &indexmap::IndexSet<SourceInfo>, - local_map: IndexVec<mir::Local, CPlace<'tcx>>, - ) { - let symbol = func_id.as_u32() as usize; - let mir = self.tcx.instance_mir(instance.def); + function_span: Span, + ) -> FunctionDebugContext { + let (file, line, column) = DebugContext::get_span_loc(tcx, function_span, function_span); + + let file_id = self.add_source_file(&file); // FIXME: add to appropriate scope instead of root let scope = self.dwarf.unit.root(); @@ -233,14 +123,35 @@ impl<'tcx> DebugContext<'tcx> { entry.set(gimli::DW_AT_name, AttributeValue::StringRef(name_id)); entry.set(gimli::DW_AT_linkage_name, AttributeValue::StringRef(name_id)); - let end = self.create_debug_lines(symbol, entry_id, context, mir.span, source_info_set); + entry.set(gimli::DW_AT_decl_file, AttributeValue::FileIndex(Some(file_id))); + entry.set(gimli::DW_AT_decl_line, AttributeValue::Udata(line)); + entry.set(gimli::DW_AT_decl_column, AttributeValue::Udata(column)); - self.unit_range_list.0.push(Range::StartLength { + FunctionDebugContext { + entry_id, + function_source_loc: (file_id, line, column), + source_loc_set: IndexSet::new(), + } + } +} + +impl FunctionDebugContext { + pub(crate) fn finalize( + mut self, + debug_context: &mut DebugContext, + func_id: FuncId, + context: &Context, + ) { + let symbol = func_id.as_u32() as usize; + + let end = self.create_debug_lines(debug_context, symbol, context); + + debug_context.unit_range_list.0.push(Range::StartLength { begin: Address::Symbol { symbol, addend: 0 }, length: u64::from(end), }); - let func_entry = self.dwarf.unit.get_mut(entry_id); + let func_entry = debug_context.dwarf.unit.get_mut(self.entry_id); // Gdb requires both DW_AT_low_pc and DW_AT_high_pc. Otherwise the DW_TAG_subprogram is skipped. func_entry.set( gimli::DW_AT_low_pc, @@ -248,110 +159,5 @@ impl<'tcx> DebugContext<'tcx> { ); // Using Udata for DW_AT_high_pc requires at least DWARF4 func_entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(u64::from(end))); - - // FIXME make it more reliable and implement scopes before re-enabling this. - if false { - let value_labels_ranges = std::collections::HashMap::new(); // FIXME - - for (local, _local_decl) in mir.local_decls.iter_enumerated() { - let ty = self.tcx.subst_and_normalize_erasing_regions( - instance.substs, - ty::ParamEnv::reveal_all(), - mir.local_decls[local].ty, - ); - let var_id = self.define_local(entry_id, format!("{:?}", local), ty); - - let location = place_location( - self, - isa, - symbol, - &local_map, - &value_labels_ranges, - Place { local, projection: ty::List::empty() }, - ); - - let var_entry = self.dwarf.unit.get_mut(var_id); - var_entry.set(gimli::DW_AT_location, location); - } - } - - // FIXME create locals for all entries in mir.var_debug_info - } -} - -fn place_location<'tcx>( - debug_context: &mut DebugContext<'tcx>, - isa: &dyn TargetIsa, - symbol: usize, - local_map: &IndexVec<mir::Local, CPlace<'tcx>>, - #[allow(rustc::default_hash_types)] value_labels_ranges: &std::collections::HashMap< - ValueLabel, - Vec<ValueLocRange>, - >, - place: Place<'tcx>, -) -> AttributeValue { - assert!(place.projection.is_empty()); // FIXME implement them - - match local_map[place.local].inner() { - CPlaceInner::Var(_local, var) => { - let value_label = cranelift_codegen::ir::ValueLabel::new(var.index()); - if let Some(value_loc_ranges) = value_labels_ranges.get(&value_label) { - let loc_list = LocationList( - value_loc_ranges - .iter() - .map(|value_loc_range| Location::StartEnd { - begin: Address::Symbol { - symbol, - addend: i64::from(value_loc_range.start), - }, - end: Address::Symbol { symbol, addend: i64::from(value_loc_range.end) }, - data: translate_loc(isa, value_loc_range.loc).unwrap(), - }) - .collect(), - ); - let loc_list_id = debug_context.dwarf.unit.locations.add(loc_list); - - AttributeValue::LocationListRef(loc_list_id) - } else { - // FIXME set value labels for unused locals - - AttributeValue::Exprloc(Expression::new()) - } - } - CPlaceInner::VarPair(_, _, _) => { - // FIXME implement this - - AttributeValue::Exprloc(Expression::new()) - } - CPlaceInner::VarLane(_, _, _) => { - // FIXME implement this - - AttributeValue::Exprloc(Expression::new()) - } - CPlaceInner::Addr(_, _) => { - // FIXME implement this (used by arguments and returns) - - AttributeValue::Exprloc(Expression::new()) - - // For PointerBase::Stack: - //AttributeValue::Exprloc(translate_loc(ValueLoc::Stack(*stack_slot)).unwrap()) - } - } -} - -// Adapted from https://github.com/CraneStation/wasmtime/blob/5a1845b4caf7a5dba8eda1fef05213a532ed4259/crates/debug/src/transform/expression.rs#L59-L137 -fn translate_loc(isa: &dyn TargetIsa, loc: LabelValueLoc) -> Option<Expression> { - match loc { - LabelValueLoc::Reg(reg) => { - let machine_reg = isa.map_regalloc_reg_to_dwarf(reg).unwrap(); - let mut expr = Expression::new(); - expr.op_reg(gimli::Register(machine_reg)); - Some(expr) - } - LabelValueLoc::SPOffset(offset) => { - let mut expr = Expression::new(); - expr.op_breg(X86_64::RSP, offset); - Some(expr) - } } } |