From 1f75f0fcad63b577ed38d5cf10b62c5af47a3269 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 27 Mar 2024 14:14:30 +0000 Subject: [PATCH 1/3] Move FileId caching to DebugContext::add_source_file --- src/base.rs | 1 - src/common.rs | 29 +----------- src/debuginfo/line_info.rs | 97 ++++++++++++++++++++------------------ src/debuginfo/mod.rs | 7 +-- 4 files changed, 57 insertions(+), 77 deletions(-) diff --git a/src/base.rs b/src/base.rs index 4e60f820594..9c66fa6d9cc 100644 --- a/src/base.rs +++ b/src/base.rs @@ -95,7 +95,6 @@ pub(crate) fn codegen_fn<'tcx>( caller_location: None, // set by `codegen_fn_prelude` clif_comments, - last_source_file: None, next_ssa_var: 0, }; diff --git a/src/common.rs b/src/common.rs index a7c3d68ff8c..b038c7194cd 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,12 +1,9 @@ use cranelift_codegen::isa::TargetFrontendConfig; -use gimli::write::FileId; -use rustc_data_structures::sync::Lrc; use rustc_index::IndexVec; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, }; use rustc_span::source_map::Spanned; -use rustc_span::SourceFile; use rustc_target::abi::call::FnAbi; use rustc_target::abi::{Integer, Primitive}; use rustc_target::spec::{HasTargetSpec, Target}; @@ -305,11 +302,6 @@ pub(crate) struct FunctionCx<'m, 'clif, 'tcx: 'm> { pub(crate) clif_comments: crate::pretty_clif::CommentWriter, - /// Last accessed source file and it's debuginfo file id. - /// - /// For optimization purposes only - pub(crate) last_source_file: Option<(Lrc, FileId)>, - /// This should only be accessed by `CPlace::new_var`. pub(crate) next_ssa_var: u32, } @@ -419,25 +411,8 @@ pub(crate) fn create_stack_slot(&mut self, size: u32, align: u32) -> Pointer { pub(crate) fn set_debug_loc(&mut self, source_info: mir::SourceInfo) { if let Some(debug_context) = &mut self.cx.debug_context { - let (file, line, column) = - DebugContext::get_span_loc(self.tcx, self.mir.span, source_info.span); - - // add_source_file is very slow. - // Optimize for the common case of the current file not being changed. - let mut cached_file_id = None; - if let Some((ref last_source_file, last_file_id)) = self.last_source_file { - // If the allocations are not equal, the files may still be equal, but that - // doesn't matter, as this is just an optimization. - if rustc_data_structures::sync::Lrc::ptr_eq(last_source_file, &file) { - cached_file_id = Some(last_file_id); - } - } - - let file_id = if let Some(file_id) = cached_file_id { - file_id - } else { - debug_context.add_source_file(&file) - }; + let (file_id, line, column) = + debug_context.get_span_loc(self.tcx, self.mir.span, source_info.span); let source_loc = self.func_debug_cx.as_mut().unwrap().add_dbg_loc(file_id, line, column); diff --git a/src/debuginfo/line_info.rs b/src/debuginfo/line_info.rs index d1b21d0a0b6..916abf31869 100644 --- a/src/debuginfo/line_info.rs +++ b/src/debuginfo/line_info.rs @@ -8,7 +8,6 @@ use gimli::write::{ Address, AttributeValue, FileId, FileInfo, LineProgram, LineString, LineStringTable, }; -use rustc_data_structures::sync::Lrc; use rustc_span::{ FileName, Pos, SourceFile, SourceFileAndLine, SourceFileHash, SourceFileHashAlgorithm, }; @@ -60,10 +59,11 @@ fn make_file_info(hash: SourceFileHash) -> Option { impl DebugContext { pub(crate) fn get_span_loc( + &mut self, tcx: TyCtxt<'_>, function_span: Span, span: Span, - ) -> (Lrc, u64, u64) { + ) -> (FileId, 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 @@ -71,61 +71,66 @@ pub(crate) fn get_span_loc( let span = tcx.collapsed_debuginfo(span, function_span); match tcx.sess.source_map().lookup_line(span.lo()) { Ok(SourceFileAndLine { sf: file, line }) => { + let file_id = self.add_source_file(&file); let line_pos = file.lines()[line]; let col = file.relative_position(span.lo()) - line_pos; - (file, u64::try_from(line).unwrap() + 1, u64::from(col.to_u32()) + 1) + (file_id, u64::try_from(line).unwrap() + 1, u64::from(col.to_u32()) + 1) } - Err(file) => (file, 0, 0), + Err(file) => (self.add_source_file(&file), 0, 0), } } 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; + let cache_key = (source_file.stable_id, source_file.src_hash); + *self.created_files.entry(cache_key).or_insert_with(|| { + 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(if self.should_remap_filepaths { - path.remapped_path_if_available() - } else { - path.local_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 - .display(if self.should_remap_filepaths { - FileNameDisplayPreference::Remapped + match &source_file.name { + FileName::Real(path) => { + let (dir_path, file_name) = + split_path_dir_and_file(if self.should_remap_filepaths { + path.remapped_path_if_available() } else { - FileNameDisplayPreference::Local - }) - .to_string() - .into_bytes(), - line_program.encoding(), - line_strings, - ); - line_program.add_file(dummy_file_name, dir_id, None) + path.local_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) + } + filename => { + let dir_id = line_program.default_directory(); + let dummy_file_name = LineString::new( + filename + .display(if self.should_remap_filepaths { + FileNameDisplayPreference::Remapped + } else { + FileNameDisplayPreference::Local + }) + .to_string() + .into_bytes(), + line_program.encoding(), + line_strings, + ); + line_program.add_file(dummy_file_name, dir_id, None) + } } - } + }) } } diff --git a/src/debuginfo/mod.rs b/src/debuginfo/mod.rs index b541cf0512e..172e72c0e26 100644 --- a/src/debuginfo/mod.rs +++ b/src/debuginfo/mod.rs @@ -16,6 +16,7 @@ use rustc_codegen_ssa::debuginfo::type_names; use rustc_hir::def_id::DefIdMap; use rustc_session::Session; +use rustc_span::{SourceFileHash, StableSourceFileId}; pub(crate) use self::emit::{DebugReloc, DebugRelocName}; pub(crate) use self::unwind::UnwindContext; @@ -30,6 +31,7 @@ pub(crate) struct DebugContext { dwarf: DwarfUnit, unit_range_list: RangeList, + created_files: FxHashMap<(StableSourceFileId, SourceFileHash), FileId>, stack_pointer_register: Register, namespace_map: DefIdMap, @@ -130,6 +132,7 @@ pub(crate) fn new(tcx: TyCtxt<'_>, isa: &dyn TargetIsa, cgu_name: &str) -> Self endian, dwarf, unit_range_list: RangeList(Vec::new()), + created_files: FxHashMap::default(), stack_pointer_register, namespace_map: DefIdMap::default(), should_remap_filepaths, @@ -169,9 +172,7 @@ pub(crate) fn define_function<'tcx>( linkage_name: &str, 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); + let (file_id, line, column) = self.get_span_loc(tcx, function_span, function_span); // FIXME: add to appropriate scope instead of root let scope = self.item_namespace(tcx, tcx.parent(instance.def_id())); From f086e7abaf2fc954a2801231bfe3aa4508b8f424 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 27 Mar 2024 15:00:41 +0000 Subject: [PATCH 2/3] Allow debuginfo to reference global variables --- src/debuginfo/emit.rs | 15 +++++++++++++++ src/debuginfo/line_info.rs | 18 +++++------------- src/debuginfo/mod.rs | 19 +++++++------------ src/debuginfo/object.rs | 13 ++++++++----- src/debuginfo/unwind.rs | 10 ++++------ 5 files changed, 39 insertions(+), 36 deletions(-) diff --git a/src/debuginfo/emit.rs b/src/debuginfo/emit.rs index 81b819a5546..d376766966d 100644 --- a/src/debuginfo/emit.rs +++ b/src/debuginfo/emit.rs @@ -1,5 +1,6 @@ //! Write the debuginfo into an object file. +use cranelift_module::{DataId, FuncId}; use cranelift_object::ObjectProduct; use gimli::write::{Address, AttributeValue, EndianVec, Result, Sections, Writer}; use gimli::{RunTimeEndian, SectionId}; @@ -8,6 +9,19 @@ use super::object::WriteDebugInfo; use super::DebugContext; +pub(super) fn address_for_func(func_id: FuncId) -> Address { + let symbol = func_id.as_u32(); + assert!(symbol & 1 << 31 == 0); + Address::Symbol { symbol: symbol as usize, addend: 0 } +} + +#[allow(dead_code)] +pub(super) fn address_for_data(data_id: DataId) -> Address { + let symbol = data_id.as_u32(); + assert!(symbol & 1 << 31 == 0); + Address::Symbol { symbol: (symbol | 1 << 31) as usize, addend: 0 } +} + 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()); @@ -171,6 +185,7 @@ fn write_eh_pointer(&mut self, address: Address, eh_pe: gimli::DwEhPe, size: u8) gimli::DW_EH_PE_pcrel => { let size = match eh_pe.format() { gimli::DW_EH_PE_sdata4 => 4, + gimli::DW_EH_PE_sdata8 => 8, _ => return Err(gimli::write::Error::UnsupportedPointerEncoding(eh_pe)), }; self.relocs.push(DebugReloc { diff --git a/src/debuginfo/line_info.rs b/src/debuginfo/line_info.rs index 916abf31869..380eba437c2 100644 --- a/src/debuginfo/line_info.rs +++ b/src/debuginfo/line_info.rs @@ -5,13 +5,12 @@ use cranelift_codegen::binemit::CodeOffset; use cranelift_codegen::MachSrcLoc; -use gimli::write::{ - Address, AttributeValue, FileId, FileInfo, LineProgram, LineString, LineStringTable, -}; +use gimli::write::{AttributeValue, FileId, FileInfo, LineProgram, LineString, LineStringTable}; use rustc_span::{ FileName, Pos, SourceFile, SourceFileAndLine, SourceFileHash, SourceFileHashAlgorithm, }; +use crate::debuginfo::emit::address_for_func; use crate::debuginfo::FunctionDebugContext; use crate::prelude::*; @@ -143,7 +142,7 @@ pub(crate) fn add_dbg_loc(&mut self, file_id: FileId, line: u64, column: u64) -> pub(super) fn create_debug_lines( &mut self, debug_context: &mut DebugContext, - symbol: usize, + func_id: FuncId, context: &Context, ) -> CodeOffset { let create_row_for_span = @@ -156,11 +155,7 @@ pub(super) fn create_debug_lines( debug_context.dwarf.unit.line_program.generate_row(); }; - debug_context - .dwarf - .unit - .line_program - .begin_sequence(Some(Address::Symbol { symbol, addend: 0 })); + debug_context.dwarf.unit.line_program.begin_sequence(Some(address_for_func(func_id))); let mut func_end = 0; @@ -183,10 +178,7 @@ pub(super) fn create_debug_lines( assert_ne!(func_end, 0); 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_low_pc, AttributeValue::Address(address_for_func(func_id))); entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(u64::from(func_end))); func_end diff --git a/src/debuginfo/mod.rs b/src/debuginfo/mod.rs index 172e72c0e26..3bf75e989c5 100644 --- a/src/debuginfo/mod.rs +++ b/src/debuginfo/mod.rs @@ -20,6 +20,7 @@ pub(crate) use self::emit::{DebugReloc, DebugRelocName}; pub(crate) use self::unwind::UnwindContext; +use crate::debuginfo::emit::address_for_func; use crate::prelude::*; pub(crate) fn producer(sess: &Session) -> String { @@ -174,7 +175,6 @@ pub(crate) fn define_function<'tcx>( ) -> FunctionDebugContext { let (file_id, line, column) = self.get_span_loc(tcx, function_span, function_span); - // FIXME: add to appropriate scope instead of root let scope = self.item_namespace(tcx, tcx.parent(instance.def_id())); let mut name = String::new(); @@ -240,21 +240,16 @@ pub(crate) fn finalize( func_id: FuncId, context: &Context, ) { - let symbol = func_id.as_u32() as usize; + let end = self.create_debug_lines(debug_context, func_id, context); - 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), - }); + debug_context + .unit_range_list + .0 + .push(Range::StartLength { begin: address_for_func(func_id), length: u64::from(end) }); 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, - AttributeValue::Address(Address::Symbol { symbol, addend: 0 }), - ); + func_entry.set(gimli::DW_AT_low_pc, AttributeValue::Address(address_for_func(func_id))); // Using Udata for DW_AT_high_pc requires at least DWARF4 func_entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(u64::from(end))); } diff --git a/src/debuginfo/object.rs b/src/debuginfo/object.rs index f1840a7bf73..27eabd8a0a6 100644 --- a/src/debuginfo/object.rs +++ b/src/debuginfo/object.rs @@ -1,4 +1,4 @@ -use cranelift_module::FuncId; +use cranelift_module::{DataId, FuncId}; use cranelift_object::ObjectProduct; use gimli::SectionId; use object::write::{Relocation, StandardSegment}; @@ -57,10 +57,13 @@ fn add_debug_reloc( let (symbol, symbol_offset) = match reloc.name { DebugRelocName::Section(id) => (section_map.get(&id).unwrap().1, 0), DebugRelocName::Symbol(id) => { - let symbol_id = self.function_symbol(FuncId::from_u32(id.try_into().unwrap())); - self.object - .symbol_section_and_offset(symbol_id) - .expect("Debug reloc for undef sym???") + let id = id.try_into().unwrap(); + let symbol_id = if id & 1 << 31 == 0 { + self.function_symbol(FuncId::from_u32(id)) + } else { + self.data_symbol(DataId::from_u32(id & !(1 << 31))) + }; + self.object.symbol_section_and_offset(symbol_id).unwrap_or((symbol_id, 0)) } }; self.object diff --git a/src/debuginfo/unwind.rs b/src/debuginfo/unwind.rs index 35278e6fb29..96ab7a29205 100644 --- a/src/debuginfo/unwind.rs +++ b/src/debuginfo/unwind.rs @@ -3,9 +3,10 @@ use cranelift_codegen::ir::Endianness; use cranelift_codegen::isa::{unwind::UnwindInfo, TargetIsa}; use cranelift_object::ObjectProduct; -use gimli::write::{Address, CieId, EhFrame, FrameTable, Section}; +use gimli::write::{CieId, EhFrame, FrameTable, Section}; use gimli::RunTimeEndian; +use super::emit::address_for_func; use super::object::WriteDebugInfo; use crate::prelude::*; @@ -47,11 +48,8 @@ pub(crate) fn add_function(&mut self, func_id: FuncId, context: &Context, isa: & match unwind_info { UnwindInfo::SystemV(unwind_info) => { - self.frame_table.add_fde( - self.cie_id.unwrap(), - unwind_info - .to_fde(Address::Symbol { symbol: func_id.as_u32() as usize, addend: 0 }), - ); + self.frame_table + .add_fde(self.cie_id.unwrap(), unwind_info.to_fde(address_for_func(func_id))); } UnwindInfo::WindowsX64(_) => { // FIXME implement this From 4b80941ae36ce0e49ecbb772872108e4808b2c4a Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:53:12 +0000 Subject: [PATCH 3/3] Add debuginfo for statics Most types are still unimplemented and get a placeholder byte array instead. --- src/constant.rs | 8 +- src/debuginfo/emit.rs | 1 - src/debuginfo/mod.rs | 77 +++++++++++++++++++- src/debuginfo/types.rs | 162 +++++++++++++++++++++++++++++++++++++++++ src/driver/aot.rs | 7 +- 5 files changed, 251 insertions(+), 4 deletions(-) create mode 100644 src/debuginfo/types.rs diff --git a/src/constant.rs b/src/constant.rs index 690e8fe0e20..635ed6c8e88 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -31,10 +31,16 @@ pub(crate) fn finalize(mut self, tcx: TyCtxt<'_>, module: &mut dyn Module) { } } -pub(crate) fn codegen_static(tcx: TyCtxt<'_>, module: &mut dyn Module, def_id: DefId) { +pub(crate) fn codegen_static(tcx: TyCtxt<'_>, module: &mut dyn Module, def_id: DefId) -> DataId { let mut constants_cx = ConstantCx::new(); constants_cx.todo.push(TodoItem::Static(def_id)); constants_cx.finalize(tcx, module); + + data_id_for_static( + tcx, module, def_id, false, + // For a declaration the stated mutability doesn't matter. + false, + ) } pub(crate) fn codegen_tls_ref<'tcx>( diff --git a/src/debuginfo/emit.rs b/src/debuginfo/emit.rs index d376766966d..36af7d4450d 100644 --- a/src/debuginfo/emit.rs +++ b/src/debuginfo/emit.rs @@ -15,7 +15,6 @@ pub(super) fn address_for_func(func_id: FuncId) -> Address { Address::Symbol { symbol: symbol as usize, addend: 0 } } -#[allow(dead_code)] pub(super) fn address_for_data(data_id: DataId) -> Address { let symbol = data_id.as_u32(); assert!(symbol & 1 << 31 == 0); diff --git a/src/debuginfo/mod.rs b/src/debuginfo/mod.rs index 3bf75e989c5..2bab897dbcd 100644 --- a/src/debuginfo/mod.rs +++ b/src/debuginfo/mod.rs @@ -3,10 +3,12 @@ mod emit; mod line_info; mod object; +mod types; mod unwind; use cranelift_codegen::ir::Endianness; use cranelift_codegen::isa::TargetIsa; +use cranelift_module::DataId; use gimli::write::{ Address, AttributeValue, DwarfUnit, Expression, FileId, LineProgram, LineString, Range, RangeList, UnitEntryId, @@ -14,13 +16,15 @@ use gimli::{AArch64, Encoding, Format, LineEncoding, Register, RiscV, RunTimeEndian, X86_64}; use indexmap::IndexSet; use rustc_codegen_ssa::debuginfo::type_names; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefIdMap; use rustc_session::Session; use rustc_span::{SourceFileHash, StableSourceFileId}; pub(crate) use self::emit::{DebugReloc, DebugRelocName}; +pub(crate) use self::types::TypeDebugContext; pub(crate) use self::unwind::UnwindContext; -use crate::debuginfo::emit::address_for_func; +use crate::debuginfo::emit::{address_for_data, address_for_func}; use crate::prelude::*; pub(crate) fn producer(sess: &Session) -> String { @@ -35,6 +39,7 @@ pub(crate) struct DebugContext { created_files: FxHashMap<(StableSourceFileId, SourceFileHash), FileId>, stack_pointer_register: Register, namespace_map: DefIdMap, + array_size_type: UnitEntryId, should_remap_filepaths: bool, } @@ -129,6 +134,19 @@ pub(crate) fn new(tcx: TyCtxt<'_>, isa: &dyn TargetIsa, cgu_name: &str) -> Self root.set(gimli::DW_AT_low_pc, AttributeValue::Address(Address::Constant(0))); } + let array_size_type = dwarf.unit.add(dwarf.unit.root(), gimli::DW_TAG_base_type); + let array_size_type_entry = dwarf.unit.get_mut(array_size_type); + array_size_type_entry.set( + gimli::DW_AT_name, + AttributeValue::StringRef(dwarf.strings.add("__ARRAY_SIZE_TYPE__")), + ); + array_size_type_entry + .set(gimli::DW_AT_encoding, AttributeValue::Encoding(gimli::DW_ATE_unsigned)); + array_size_type_entry.set( + gimli::DW_AT_byte_size, + AttributeValue::Udata(isa.frontend_config().pointer_bytes().into()), + ); + DebugContext { endian, dwarf, @@ -136,6 +154,7 @@ pub(crate) fn new(tcx: TyCtxt<'_>, isa: &dyn TargetIsa, cgu_name: &str) -> Self created_files: FxHashMap::default(), stack_pointer_register, namespace_map: DefIdMap::default(), + array_size_type, should_remap_filepaths, } } @@ -231,6 +250,62 @@ pub(crate) fn define_function<'tcx>( source_loc_set: IndexSet::new(), } } + + // Adapted from https://github.com/rust-lang/rust/blob/10a7aa14fed9b528b74b0f098c4899c37c09a9c7/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs#L1288-L1346 + pub(crate) fn define_static<'tcx>( + &mut self, + tcx: TyCtxt<'tcx>, + type_dbg: &mut TypeDebugContext<'tcx>, + def_id: DefId, + data_id: DataId, + ) { + let DefKind::Static { nested, .. } = tcx.def_kind(def_id) else { bug!() }; + if nested { + return; + } + + let scope = self.item_namespace(tcx, tcx.parent(def_id)); + + let span = tcx.def_span(def_id); + let (file_id, line, _column) = self.get_span_loc(tcx, span, span); + + let static_type = Instance::mono(tcx, def_id).ty(tcx, ty::ParamEnv::reveal_all()); + let static_layout = tcx.layout_of(ty::ParamEnv::reveal_all().and(static_type)).unwrap(); + // FIXME use the actual type layout + let type_id = self.debug_type(tcx, type_dbg, static_type); + + let name = tcx.item_name(def_id); + let linkage_name = tcx.symbol_name(Instance::mono(tcx, def_id)).name; + + let entry_id = self.dwarf.unit.add(scope, gimli::DW_TAG_variable); + let entry = self.dwarf.unit.get_mut(entry_id); + let linkage_name_id = if name.as_str() != linkage_name { + Some(self.dwarf.strings.add(linkage_name)) + } else { + None + }; + let name_id = self.dwarf.strings.add(name.as_str()); + + entry.set(gimli::DW_AT_name, AttributeValue::StringRef(name_id)); + entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(type_id)); + + if tcx.is_reachable_non_generic(def_id) { + entry.set(gimli::DW_AT_external, AttributeValue::FlagPresent); + } + + 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_alignment, AttributeValue::Udata(static_layout.align.pref.bytes())); + + let mut expr = Expression::new(); + expr.op_addr(address_for_data(data_id)); + entry.set(gimli::DW_AT_location, AttributeValue::Exprloc(expr)); + + if let Some(linkage_name_id) = linkage_name_id { + entry.set(gimli::DW_AT_linkage_name, AttributeValue::StringRef(linkage_name_id)); + } + } } impl FunctionDebugContext { diff --git a/src/debuginfo/types.rs b/src/debuginfo/types.rs new file mode 100644 index 00000000000..650de945c7b --- /dev/null +++ b/src/debuginfo/types.rs @@ -0,0 +1,162 @@ +// Adapted from https://github.com/rust-lang/rust/blob/10a7aa14fed9b528b74b0f098c4899c37c09a9c7/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs + +use gimli::write::{AttributeValue, UnitEntryId}; +use rustc_codegen_ssa::debuginfo::type_names; +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::ty::{self, Ty, TyCtxt}; + +use crate::{has_ptr_meta, DebugContext}; + +#[derive(Default)] +pub(crate) struct TypeDebugContext<'tcx> { + type_map: FxHashMap, UnitEntryId>, +} + +/// Returns from the enclosing function if the type debuginfo node with the given +/// unique ID can be found in the type map. +macro_rules! return_if_type_created_in_meantime { + ($type_dbg:expr, $ty:expr) => { + if let Some(&type_id) = $type_dbg.type_map.get(&$ty) { + return type_id; + } + }; +} + +impl DebugContext { + pub(crate) fn debug_type<'tcx>( + &mut self, + tcx: TyCtxt<'tcx>, + type_dbg: &mut TypeDebugContext<'tcx>, + ty: Ty<'tcx>, + ) -> UnitEntryId { + if let Some(&type_id) = type_dbg.type_map.get(&ty) { + return type_id; + } + + let type_id = match ty.kind() { + ty::Never | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => { + self.basic_type(tcx, ty) + } + ty::Tuple(elems) if elems.is_empty() => self.basic_type(tcx, ty), + ty::Array(elem_ty, len) => self.array_type( + tcx, + type_dbg, + *elem_ty, + len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()), + ), + // ty::Slice(_) | ty::Str + // ty::Dynamic + // ty::Foreign + ty::RawPtr(pointee_type, _) | ty::Ref(_, pointee_type, _) => { + self.pointer_type(tcx, type_dbg, ty, *pointee_type) + } + // ty::Adt(def, args) if def.is_box() && args.get(1).map_or(true, |arg| cx.layout_of(arg.expect_ty()).is_1zst()) + // ty::FnDef(..) | ty::FnPtr(..) + // ty::Closure(..) + // ty::Adt(def, ..) + // ty::Tuple(_) + // ty::Param(_) + // FIXME implement remaining types and add unreachable!() to the fallback branch + _ => self.placeholder_for_type(tcx, type_dbg, ty), + }; + + type_dbg.type_map.insert(ty, type_id); + + type_id + } + + fn basic_type<'tcx>(&mut self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> UnitEntryId { + let (name, encoding) = match ty.kind() { + ty::Never => ("!", gimli::DW_ATE_unsigned), + ty::Tuple(elems) if elems.is_empty() => ("()", gimli::DW_ATE_unsigned), + ty::Bool => ("bool", gimli::DW_ATE_boolean), + ty::Char => ("char", gimli::DW_ATE_UTF), + ty::Int(int_ty) => (int_ty.name_str(), gimli::DW_ATE_signed), + ty::Uint(uint_ty) => (uint_ty.name_str(), gimli::DW_ATE_unsigned), + ty::Float(float_ty) => (float_ty.name_str(), gimli::DW_ATE_float), + _ => unreachable!(), + }; + + let type_id = self.dwarf.unit.add(self.dwarf.unit.root(), gimli::DW_TAG_base_type); + let type_entry = self.dwarf.unit.get_mut(type_id); + type_entry.set(gimli::DW_AT_name, AttributeValue::StringRef(self.dwarf.strings.add(name))); + type_entry.set(gimli::DW_AT_encoding, AttributeValue::Encoding(encoding)); + type_entry.set( + gimli::DW_AT_byte_size, + AttributeValue::Udata( + tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)).expect("FIXME").size.bytes(), + ), + ); + + type_id + } + + fn array_type<'tcx>( + &mut self, + tcx: TyCtxt<'tcx>, + type_dbg: &mut TypeDebugContext<'tcx>, + elem_ty: Ty<'tcx>, + len: u64, + ) -> UnitEntryId { + let elem_dw_ty = self.debug_type(tcx, type_dbg, elem_ty); + + return_if_type_created_in_meantime!(type_dbg, elem_ty); + + let array_type_id = self.dwarf.unit.add(self.dwarf.unit.root(), gimli::DW_TAG_array_type); + let array_type_entry = self.dwarf.unit.get_mut(array_type_id); + array_type_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(elem_dw_ty)); + + let subrange_id = self.dwarf.unit.add(array_type_id, gimli::DW_TAG_subrange_type); + let subrange_entry = self.dwarf.unit.get_mut(subrange_id); + subrange_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(self.array_size_type)); + subrange_entry.set(gimli::DW_AT_lower_bound, AttributeValue::Udata(0)); + subrange_entry.set(gimli::DW_AT_count, AttributeValue::Udata(len)); + + array_type_id + } + + fn pointer_type<'tcx>( + &mut self, + tcx: TyCtxt<'tcx>, + type_dbg: &mut TypeDebugContext<'tcx>, + ptr_type: Ty<'tcx>, + pointee_type: Ty<'tcx>, + ) -> UnitEntryId { + let pointee_dw_ty = self.debug_type(tcx, type_dbg, pointee_type); + + return_if_type_created_in_meantime!(type_dbg, ptr_type); + + let name = type_names::compute_debuginfo_type_name(tcx, ptr_type, true); + + if !has_ptr_meta(tcx, ptr_type) { + let pointer_type_id = + self.dwarf.unit.add(self.dwarf.unit.root(), gimli::DW_TAG_pointer_type); + let pointer_entry = self.dwarf.unit.get_mut(pointer_type_id); + pointer_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(pointee_dw_ty)); + pointer_entry + .set(gimli::DW_AT_name, AttributeValue::StringRef(self.dwarf.strings.add(name))); + + pointer_type_id + } else { + // FIXME implement debuginfo for fat pointers + self.placeholder_for_type(tcx, type_dbg, ptr_type) + } + } + + fn placeholder_for_type<'tcx>( + &mut self, + tcx: TyCtxt<'tcx>, + type_dbg: &mut TypeDebugContext<'tcx>, + ty: Ty<'tcx>, + ) -> UnitEntryId { + self.debug_type( + tcx, + type_dbg, + Ty::new_array( + tcx, + tcx.types.u8, + tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)).unwrap().size.bytes(), + ), + ) + } +} diff --git a/src/driver/aot.rs b/src/driver/aot.rs index 4a28b2d93fa..e5caceab345 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -23,6 +23,7 @@ use rustc_session::Session; use crate::concurrency_limiter::{ConcurrencyLimiter, ConcurrencyLimiterToken}; +use crate::debuginfo::TypeDebugContext; use crate::global_asm::GlobalAsmConfig; use crate::{prelude::*, BackendConfig}; @@ -460,6 +461,7 @@ fn module_codegen( tcx.sess.opts.debuginfo != DebugInfo::None, cgu_name, ); + let mut type_dbg = TypeDebugContext::default(); super::predefine_mono_items(tcx, &mut module, &mono_items); let mut codegened_functions = vec![]; for (mono_item, _) in mono_items { @@ -475,7 +477,10 @@ fn module_codegen( codegened_functions.push(codegened_function); } MonoItem::Static(def_id) => { - crate::constant::codegen_static(tcx, &mut module, def_id) + let data_id = crate::constant::codegen_static(tcx, &mut module, def_id); + if let Some(debug_context) = &mut cx.debug_context { + debug_context.define_static(tcx, &mut type_dbg, def_id, data_id); + } } MonoItem::GlobalAsm(item_id) => { crate::global_asm::codegen_global_asm_item(