diff options
Diffstat (limited to 'compiler/rustc_codegen_llvm/src/debuginfo')
11 files changed, 4415 insertions, 0 deletions
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs new file mode 100644 index 000000000..99e4ded62 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs @@ -0,0 +1,126 @@ +use super::metadata::file_metadata; +use super::utils::DIB; +use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext}; +use rustc_codegen_ssa::traits::*; + +use crate::common::CodegenCx; +use crate::llvm; +use crate::llvm::debuginfo::{DILocation, DIScope}; +use rustc_middle::mir::{Body, SourceScope}; +use rustc_middle::ty::layout::FnAbiOf; +use rustc_middle::ty::{self, Instance}; +use rustc_session::config::DebugInfo; + +use rustc_index::bit_set::BitSet; +use rustc_index::vec::Idx; + +/// Produces DIScope DIEs for each MIR Scope which has variables defined in it. +// FIXME(eddyb) almost all of this should be in `rustc_codegen_ssa::mir::debuginfo`. +pub fn compute_mir_scopes<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + instance: Instance<'tcx>, + mir: &Body<'tcx>, + debug_context: &mut FunctionDebugContext<&'ll DIScope, &'ll DILocation>, +) { + // Find all scopes with variables defined in them. + let variables = if cx.sess().opts.debuginfo == DebugInfo::Full { + let mut vars = BitSet::new_empty(mir.source_scopes.len()); + // FIXME(eddyb) take into account that arguments always have debuginfo, + // irrespective of their name (assuming full debuginfo is enabled). + // NOTE(eddyb) actually, on second thought, those are always in the + // function scope, which always exists. + for var_debug_info in &mir.var_debug_info { + vars.insert(var_debug_info.source_info.scope); + } + Some(vars) + } else { + // Nothing to emit, of course. + None + }; + let mut instantiated = BitSet::new_empty(mir.source_scopes.len()); + // Instantiate all scopes. + for idx in 0..mir.source_scopes.len() { + let scope = SourceScope::new(idx); + make_mir_scope(cx, instance, mir, &variables, debug_context, &mut instantiated, scope); + } + assert!(instantiated.count() == mir.source_scopes.len()); +} + +fn make_mir_scope<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + instance: Instance<'tcx>, + mir: &Body<'tcx>, + variables: &Option<BitSet<SourceScope>>, + debug_context: &mut FunctionDebugContext<&'ll DIScope, &'ll DILocation>, + instantiated: &mut BitSet<SourceScope>, + scope: SourceScope, +) { + if instantiated.contains(scope) { + return; + } + + let scope_data = &mir.source_scopes[scope]; + let parent_scope = if let Some(parent) = scope_data.parent_scope { + make_mir_scope(cx, instance, mir, variables, debug_context, instantiated, parent); + debug_context.scopes[parent] + } else { + // The root is the function itself. + let loc = cx.lookup_debug_loc(mir.span.lo()); + debug_context.scopes[scope] = DebugScope { + file_start_pos: loc.file.start_pos, + file_end_pos: loc.file.end_pos, + ..debug_context.scopes[scope] + }; + instantiated.insert(scope); + return; + }; + + if let Some(vars) = variables && !vars.contains(scope) && scope_data.inlined.is_none() { + // Do not create a DIScope if there are no variables defined in this + // MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat. + debug_context.scopes[scope] = parent_scope; + instantiated.insert(scope); + return; + } + + let loc = cx.lookup_debug_loc(scope_data.span.lo()); + let file_metadata = file_metadata(cx, &loc.file); + + let dbg_scope = match scope_data.inlined { + Some((callee, _)) => { + // FIXME(eddyb) this would be `self.monomorphize(&callee)` + // if this is moved to `rustc_codegen_ssa::mir::debuginfo`. + let callee = cx.tcx.subst_and_normalize_erasing_regions( + instance.substs, + ty::ParamEnv::reveal_all(), + callee, + ); + let callee_fn_abi = cx.fn_abi_of_instance(callee, ty::List::empty()); + cx.dbg_scope_fn(callee, callee_fn_abi, None) + } + None => unsafe { + llvm::LLVMRustDIBuilderCreateLexicalBlock( + DIB(cx), + parent_scope.dbg_scope, + file_metadata, + loc.line, + loc.col, + ) + }, + }; + + let inlined_at = scope_data.inlined.map(|(_, callsite_span)| { + // FIXME(eddyb) this doesn't account for the macro-related + // `Span` fixups that `rustc_codegen_ssa::mir::debuginfo` does. + let callsite_scope = parent_scope.adjust_dbg_scope_for_span(cx, callsite_span); + cx.dbg_loc(callsite_scope, parent_scope.inlined_at, callsite_span) + }); + + debug_context.scopes[scope] = DebugScope { + dbg_scope, + inlined_at: inlined_at.or(parent_scope.inlined_at), + file_start_pos: loc.file.start_pos, + file_end_pos: loc.file.end_pos, + }; + instantiated.insert(scope); +} diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/doc.md b/compiler/rustc_codegen_llvm/src/debuginfo/doc.md new file mode 100644 index 000000000..aaec4e68c --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/debuginfo/doc.md @@ -0,0 +1,131 @@ +# Debug Info Module + +This module serves the purpose of generating debug symbols. We use LLVM's +[source level debugging](https://llvm.org/docs/SourceLevelDebugging.html) +features for generating the debug information. The general principle is +this: + +Given the right metadata in the LLVM IR, the LLVM code generator is able to +create DWARF debug symbols for the given code. The +[metadata](https://llvm.org/docs/LangRef.html#metadata-type) is structured +much like DWARF *debugging information entries* (DIE), representing type +information such as datatype layout, function signatures, block layout, +variable location and scope information, etc. It is the purpose of this +module to generate correct metadata and insert it into the LLVM IR. + +As the exact format of metadata trees may change between different LLVM +versions, we now use LLVM +[DIBuilder](https://llvm.org/docs/doxygen/html/classllvm_1_1DIBuilder.html) +to create metadata where possible. This will hopefully ease the adaption of +this module to future LLVM versions. + +The public API of the module is a set of functions that will insert the +correct metadata into the LLVM IR when called with the right parameters. +The module is thus driven from an outside client with functions like +`debuginfo::create_local_var_metadata(bx: block, local: &ast::local)`. + +Internally the module will try to reuse already created metadata by +utilizing a cache. The way to get a shared metadata node when needed is +thus to just call the corresponding function in this module: +```ignore (illustrative) +let file_metadata = file_metadata(cx, file); +``` +The function will take care of probing the cache for an existing node for +that exact file path. + +All private state used by the module is stored within either the +CodegenUnitDebugContext struct (owned by the CodegenCx) or the +FunctionDebugContext (owned by the FunctionCx). + +This file consists of three conceptual sections: +1. The public interface of the module +2. Module-internal metadata creation functions +3. Minor utility functions + + +## Recursive Types + +Some kinds of types, such as structs and enums can be recursive. That means +that the type definition of some type X refers to some other type which in +turn (transitively) refers to X. This introduces cycles into the type +referral graph. A naive algorithm doing an on-demand, depth-first traversal +of this graph when describing types, can get trapped in an endless loop +when it reaches such a cycle. + +For example, the following simple type for a singly-linked list... + +``` +struct List { + value: i32, + tail: Option<Box<List>>, +} +``` + +will generate the following callstack with a naive DFS algorithm: + +```ignore (illustrative) +describe(t = List) + describe(t = i32) + describe(t = Option<Box<List>>) + describe(t = Box<List>) + describe(t = List) // at the beginning again... + ... +``` + +To break cycles like these, we use "stubs". That is, when +the algorithm encounters a possibly recursive type (any struct or enum), it +immediately creates a type description node and inserts it into the cache +*before* describing the members of the type. This type description is just +a stub (as type members are not described and added to it yet) but it +allows the algorithm to already refer to the type. After the stub is +inserted into the cache, the algorithm continues as before. If it now +encounters a recursive reference, it will hit the cache and does not try to +describe the type anew. This behavior is encapsulated in the +`type_map::build_type_with_children()` function. + + +## Source Locations and Line Information + +In addition to data type descriptions the debugging information must also +allow to map machine code locations back to source code locations in order +to be useful. This functionality is also handled in this module. The +following functions allow to control source mappings: + ++ `set_source_location()` ++ `clear_source_location()` ++ `start_emitting_source_locations()` + +`set_source_location()` allows to set the current source location. All IR +instructions created after a call to this function will be linked to the +given source location, until another location is specified with +`set_source_location()` or the source location is cleared with +`clear_source_location()`. In the later case, subsequent IR instruction +will not be linked to any source location. As you can see, this is a +stateful API (mimicking the one in LLVM), so be careful with source +locations set by previous calls. It's probably best to not rely on any +specific state being present at a given point in code. + +One topic that deserves some extra attention is *function prologues*. At +the beginning of a function's machine code there are typically a few +instructions for loading argument values into allocas and checking if +there's enough stack space for the function to execute. This *prologue* is +not visible in the source code and LLVM puts a special PROLOGUE END marker +into the line table at the first non-prologue instruction of the function. +In order to find out where the prologue ends, LLVM looks for the first +instruction in the function body that is linked to a source location. So, +when generating prologue instructions we have to make sure that we don't +emit source location information until the 'real' function body begins. For +this reason, source location emission is disabled by default for any new +function being codegened and is only activated after a call to the third +function from the list above, `start_emitting_source_locations()`. This +function should be called right before regularly starting to codegen the +top-level block of the given function. + +There is one exception to the above rule: `llvm.dbg.declare` instruction +must be linked to the source location of the variable being declared. For +function parameters these `llvm.dbg.declare` instructions typically occur +in the middle of the prologue, however, they are ignored by LLVM's prologue +detection. The `create_argument_metadata()` and related functions take care +of linking the `llvm.dbg.declare` instructions to the correct source +locations even while source location emission is still disabled, so there +is no need to do anything special with source location handling here. diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs new file mode 100644 index 000000000..80fd9726f --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -0,0 +1,120 @@ +// .debug_gdb_scripts binary section. + +use crate::llvm; + +use crate::builder::Builder; +use crate::common::CodegenCx; +use crate::value::Value; +use rustc_codegen_ssa::base::collect_debugger_visualizers_transitive; +use rustc_codegen_ssa::traits::*; +use rustc_hir::def_id::LOCAL_CRATE; +use rustc_middle::bug; +use rustc_session::config::{CrateType, DebugInfo}; + +use rustc_span::symbol::sym; +use rustc_span::DebuggerVisualizerType; + +/// Inserts a side-effect free instruction sequence that makes sure that the +/// .debug_gdb_scripts global is referenced, so it isn't removed by the linker. +pub fn insert_reference_to_gdb_debug_scripts_section_global(bx: &mut Builder<'_, '_, '_>) { + if needs_gdb_debug_scripts_section(bx) { + let gdb_debug_scripts_section = + bx.const_bitcast(get_or_insert_gdb_debug_scripts_section_global(bx), bx.type_i8p()); + // Load just the first byte as that's all that's necessary to force + // LLVM to keep around the reference to the global. + let volative_load_instruction = bx.volatile_load(bx.type_i8(), gdb_debug_scripts_section); + unsafe { + llvm::LLVMSetAlignment(volative_load_instruction, 1); + } + } +} + +/// Allocates the global variable responsible for the .debug_gdb_scripts binary +/// section. +pub fn get_or_insert_gdb_debug_scripts_section_global<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll Value { + let c_section_var_name = "__rustc_debug_gdb_scripts_section__\0"; + let section_var_name = &c_section_var_name[..c_section_var_name.len() - 1]; + + let section_var = + unsafe { llvm::LLVMGetNamedGlobal(cx.llmod, c_section_var_name.as_ptr().cast()) }; + + section_var.unwrap_or_else(|| { + let section_name = b".debug_gdb_scripts\0"; + let mut section_contents = Vec::new(); + + // Add the pretty printers for the standard library first. + section_contents.extend_from_slice(b"\x01gdb_load_rust_pretty_printers.py\0"); + + // Next, add the pretty printers that were specified via the `#[debugger_visualizer]` attribute. + let visualizers = collect_debugger_visualizers_transitive( + cx.tcx, + DebuggerVisualizerType::GdbPrettyPrinter, + ); + let crate_name = cx.tcx.crate_name(LOCAL_CRATE); + for (index, visualizer) in visualizers.iter().enumerate() { + // The initial byte `4` instructs GDB that the following pretty printer + // is defined inline as opposed to in a standalone file. + section_contents.extend_from_slice(b"\x04"); + let vis_name = format!("pretty-printer-{}-{}\n", crate_name, index); + section_contents.extend_from_slice(vis_name.as_bytes()); + section_contents.extend_from_slice(&visualizer.src); + + // The final byte `0` tells GDB that the pretty printer has been + // fully defined and can continue searching for additional + // pretty printers. + section_contents.extend_from_slice(b"\0"); + } + + unsafe { + let section_contents = section_contents.as_slice(); + let llvm_type = cx.type_array(cx.type_i8(), section_contents.len() as u64); + + let section_var = cx + .define_global(section_var_name, llvm_type) + .unwrap_or_else(|| bug!("symbol `{}` is already defined", section_var_name)); + llvm::LLVMSetSection(section_var, section_name.as_ptr().cast()); + llvm::LLVMSetInitializer(section_var, cx.const_bytes(section_contents)); + llvm::LLVMSetGlobalConstant(section_var, llvm::True); + llvm::LLVMSetUnnamedAddress(section_var, llvm::UnnamedAddr::Global); + llvm::LLVMRustSetLinkage(section_var, llvm::Linkage::LinkOnceODRLinkage); + // This should make sure that the whole section is not larger than + // the string it contains. Otherwise we get a warning from GDB. + llvm::LLVMSetAlignment(section_var, 1); + section_var + } + }) +} + +pub fn needs_gdb_debug_scripts_section(cx: &CodegenCx<'_, '_>) -> bool { + let omit_gdb_pretty_printer_section = + cx.tcx.sess.contains_name(cx.tcx.hir().krate_attrs(), sym::omit_gdb_pretty_printer_section); + + // To ensure the section `__rustc_debug_gdb_scripts_section__` will not create + // ODR violations at link time, this section will not be emitted for rlibs since + // each rlib could produce a different set of visualizers that would be embedded + // in the `.debug_gdb_scripts` section. For that reason, we make sure that the + // section is only emitted for leaf crates. + let embed_visualizers = cx.sess().crate_types().iter().any(|&crate_type| match crate_type { + CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::Staticlib => { + // These are crate types for which we will embed pretty printers since they + // are treated as leaf crates. + true + } + CrateType::ProcMacro => { + // We could embed pretty printers for proc macro crates too but it does not + // seem like a good default, since this is a rare use case and we don't + // want to slow down the common case. + false + } + CrateType::Rlib => { + // As per the above description, embedding pretty printers for rlibs could + // lead to ODR violations so we skip this crate type as well. + false + } + }); + + !omit_gdb_pretty_printer_section + && cx.sess().opts.debuginfo != DebugInfo::None + && cx.sess().target.emit_debug_gdb_scripts + && embed_visualizers +} diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs new file mode 100644 index 000000000..bd84100e0 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -0,0 +1,1618 @@ +use self::type_map::DINodeCreationResult; +use self::type_map::Stub; +use self::type_map::UniqueTypeId; + +use super::namespace::mangled_name_of_instance; +use super::type_names::{compute_debuginfo_type_name, compute_debuginfo_vtable_name}; +use super::utils::{ + create_DIArray, debug_context, get_namespace_for_item, is_node_local_to_unit, DIB, +}; +use super::CodegenUnitDebugContext; + +use crate::abi; +use crate::common::CodegenCx; +use crate::debuginfo::metadata::type_map::build_type_with_children; +use crate::debuginfo::utils::fat_pointer_kind; +use crate::debuginfo::utils::FatPtrKind; +use crate::llvm; +use crate::llvm::debuginfo::{ + DIDescriptor, DIFile, DIFlags, DILexicalBlock, DIScope, DIType, DebugEmissionKind, +}; +use crate::value::Value; + +use cstr::cstr; +use rustc_codegen_ssa::debuginfo::type_names::cpp_like_debuginfo; +use rustc_codegen_ssa::debuginfo::type_names::VTableNameKind; +use rustc_codegen_ssa::traits::*; +use rustc_fs_util::path_to_c_string; +use rustc_hir::def::CtorKind; +use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::bug; +use rustc_middle::mir::{self, GeneratorLayout}; +use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::{ + self, AdtKind, Instance, ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt, Visibility, +}; +use rustc_session::config::{self, DebugInfo, Lto}; +use rustc_span::symbol::Symbol; +use rustc_span::FileName; +use rustc_span::{self, FileNameDisplayPreference, SourceFile}; +use rustc_symbol_mangling::typeid_for_trait_ref; +use rustc_target::abi::{Align, Size}; +use smallvec::smallvec; +use tracing::debug; + +use libc::{c_char, c_longlong, c_uint}; +use std::borrow::Cow; +use std::fmt::{self, Write}; +use std::hash::{Hash, Hasher}; +use std::iter; +use std::path::{Path, PathBuf}; +use std::ptr; +use tracing::instrument; + +impl PartialEq for llvm::Metadata { + fn eq(&self, other: &Self) -> bool { + ptr::eq(self, other) + } +} + +impl Eq for llvm::Metadata {} + +impl Hash for llvm::Metadata { + fn hash<H: Hasher>(&self, hasher: &mut H) { + (self as *const Self).hash(hasher); + } +} + +impl fmt::Debug for llvm::Metadata { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (self as *const Self).fmt(f) + } +} + +// From DWARF 5. +// See http://www.dwarfstd.org/ShowIssue.php?issue=140129.1. +const DW_LANG_RUST: c_uint = 0x1c; +#[allow(non_upper_case_globals)] +const DW_ATE_boolean: c_uint = 0x02; +#[allow(non_upper_case_globals)] +const DW_ATE_float: c_uint = 0x04; +#[allow(non_upper_case_globals)] +const DW_ATE_signed: c_uint = 0x05; +#[allow(non_upper_case_globals)] +const DW_ATE_unsigned: c_uint = 0x07; +#[allow(non_upper_case_globals)] +const DW_ATE_UTF: c_uint = 0x10; + +pub(super) const UNKNOWN_LINE_NUMBER: c_uint = 0; +pub(super) const UNKNOWN_COLUMN_NUMBER: c_uint = 0; + +const NO_SCOPE_METADATA: Option<&DIScope> = None; +/// A function that returns an empty list of generic parameter debuginfo nodes. +const NO_GENERICS: for<'ll> fn(&CodegenCx<'ll, '_>) -> SmallVec<&'ll DIType> = |_| SmallVec::new(); + +// SmallVec is used quite a bit in this module, so create a shorthand. +// The actual number of elements is not so important. +pub type SmallVec<T> = smallvec::SmallVec<[T; 16]>; + +mod enums; +mod type_map; + +pub(crate) use type_map::TypeMap; + +/// 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_di_node_created_in_meantime { + ($cx: expr, $unique_type_id: expr) => { + if let Some(di_node) = debug_context($cx).type_map.di_node_for_unique_id($unique_type_id) { + return DINodeCreationResult::new(di_node, true); + } + }; +} + +/// Extract size and alignment from a TyAndLayout. +fn size_and_align_of<'tcx>(ty_and_layout: TyAndLayout<'tcx>) -> (Size, Align) { + (ty_and_layout.size, ty_and_layout.align.abi) +} + +/// Creates debuginfo for a fixed size array (e.g. `[u64; 123]`). +/// For slices (that is, "arrays" of unknown size) use [build_slice_type_di_node]. +fn build_fixed_size_array_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, + array_type: Ty<'tcx>, +) -> DINodeCreationResult<'ll> { + let ty::Array(element_type, len) = array_type.kind() else { + bug!("build_fixed_size_array_di_node() called with non-ty::Array type `{:?}`", array_type) + }; + + let element_type_di_node = type_di_node(cx, *element_type); + + return_if_di_node_created_in_meantime!(cx, unique_type_id); + + let (size, align) = cx.size_and_align_of(array_type); + + let upper_bound = len.eval_usize(cx.tcx, ty::ParamEnv::reveal_all()) as c_longlong; + + let subrange = + unsafe { Some(llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound)) }; + + let subscripts = create_DIArray(DIB(cx), &[subrange]); + let di_node = unsafe { + llvm::LLVMRustDIBuilderCreateArrayType( + DIB(cx), + size.bits(), + align.bits() as u32, + element_type_di_node, + subscripts, + ) + }; + + DINodeCreationResult::new(di_node, false) +} + +/// Creates debuginfo for built-in pointer-like things: +/// +/// - ty::Ref +/// - ty::RawPtr +/// - ty::Adt in the case it's Box +/// +/// At some point we might want to remove the special handling of Box +/// and treat it the same as other smart pointers (like Rc, Arc, ...). +fn build_pointer_or_reference_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + ptr_type: Ty<'tcx>, + pointee_type: Ty<'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + // The debuginfo generated by this function is only valid if `ptr_type` is really just + // a (fat) pointer. Make sure it is not called for e.g. `Box<T, NonZSTAllocator>`. + debug_assert_eq!( + cx.size_and_align_of(ptr_type), + cx.size_and_align_of(cx.tcx.mk_mut_ptr(pointee_type)) + ); + + let pointee_type_di_node = type_di_node(cx, pointee_type); + + return_if_di_node_created_in_meantime!(cx, unique_type_id); + + let (thin_pointer_size, thin_pointer_align) = + cx.size_and_align_of(cx.tcx.mk_imm_ptr(cx.tcx.types.unit)); + let ptr_type_debuginfo_name = compute_debuginfo_type_name(cx.tcx, ptr_type, true); + + match fat_pointer_kind(cx, pointee_type) { + None => { + // This is a thin pointer. Create a regular pointer type and give it the correct name. + debug_assert_eq!( + (thin_pointer_size, thin_pointer_align), + cx.size_and_align_of(ptr_type), + "ptr_type={}, pointee_type={}", + ptr_type, + pointee_type, + ); + + let di_node = unsafe { + llvm::LLVMRustDIBuilderCreatePointerType( + DIB(cx), + pointee_type_di_node, + thin_pointer_size.bits(), + thin_pointer_align.bits() as u32, + 0, // Ignore DWARF address space. + ptr_type_debuginfo_name.as_ptr().cast(), + ptr_type_debuginfo_name.len(), + ) + }; + + DINodeCreationResult { di_node, already_stored_in_typemap: false } + } + Some(fat_pointer_kind) => { + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + Stub::Struct, + unique_type_id, + &ptr_type_debuginfo_name, + cx.size_and_align_of(ptr_type), + NO_SCOPE_METADATA, + DIFlags::FlagZero, + ), + |cx, owner| { + // FIXME: If this fat pointer is a `Box` then we don't want to use its + // type layout and instead use the layout of the raw pointer inside + // of it. + // The proper way to handle this is to not treat Box as a pointer + // at all and instead emit regular struct debuginfo for it. We just + // need to make sure that we don't break existing debuginfo consumers + // by doing that (at least not without a warning period). + let layout_type = + if ptr_type.is_box() { cx.tcx.mk_mut_ptr(pointee_type) } else { ptr_type }; + + let layout = cx.layout_of(layout_type); + let addr_field = layout.field(cx, abi::FAT_PTR_ADDR); + let extra_field = layout.field(cx, abi::FAT_PTR_EXTRA); + + let (addr_field_name, extra_field_name) = match fat_pointer_kind { + FatPtrKind::Dyn => ("pointer", "vtable"), + FatPtrKind::Slice => ("data_ptr", "length"), + }; + + debug_assert_eq!(abi::FAT_PTR_ADDR, 0); + debug_assert_eq!(abi::FAT_PTR_EXTRA, 1); + + // The data pointer type is a regular, thin pointer, regardless of whether this + // is a slice or a trait object. + let data_ptr_type_di_node = unsafe { + llvm::LLVMRustDIBuilderCreatePointerType( + DIB(cx), + pointee_type_di_node, + addr_field.size.bits(), + addr_field.align.abi.bits() as u32, + 0, // Ignore DWARF address space. + std::ptr::null(), + 0, + ) + }; + + smallvec![ + build_field_di_node( + cx, + owner, + addr_field_name, + (addr_field.size, addr_field.align.abi), + layout.fields.offset(abi::FAT_PTR_ADDR), + DIFlags::FlagZero, + data_ptr_type_di_node, + ), + build_field_di_node( + cx, + owner, + extra_field_name, + (extra_field.size, extra_field.align.abi), + layout.fields.offset(abi::FAT_PTR_EXTRA), + DIFlags::FlagZero, + type_di_node(cx, extra_field.ty), + ), + ] + }, + NO_GENERICS, + ) + } + } +} + +fn build_subroutine_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + // It's possible to create a self-referential + // type in Rust by using 'impl trait': + // + // fn foo() -> impl Copy { foo } + // + // Unfortunately LLVM's API does not allow us to create recursive subroutine types. + // In order to work around that restriction we place a marker type in the type map, + // before creating the actual type. If the actual type is recursive, it will hit the + // marker type. So we end up with a type that looks like + // + // fn foo() -> <recursive_type> + // + // Once that is created, we replace the marker in the typemap with the actual type. + debug_context(cx) + .type_map + .unique_id_to_di_node + .borrow_mut() + .insert(unique_type_id, recursion_marker_type_di_node(cx)); + + let fn_ty = unique_type_id.expect_ty(); + let signature = cx + .tcx + .normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), fn_ty.fn_sig(cx.tcx)); + + let signature_di_nodes: SmallVec<_> = iter::once( + // return type + match signature.output().kind() { + ty::Tuple(tys) if tys.is_empty() => { + // this is a "void" function + None + } + _ => Some(type_di_node(cx, signature.output())), + }, + ) + .chain( + // regular arguments + signature.inputs().iter().map(|&argument_type| Some(type_di_node(cx, argument_type))), + ) + .collect(); + + debug_context(cx).type_map.unique_id_to_di_node.borrow_mut().remove(&unique_type_id); + + let fn_di_node = unsafe { + llvm::LLVMRustDIBuilderCreateSubroutineType( + DIB(cx), + create_DIArray(DIB(cx), &signature_di_nodes[..]), + ) + }; + + // This is actually a function pointer, so wrap it in pointer DI. + let name = compute_debuginfo_type_name(cx.tcx, fn_ty, false); + let di_node = unsafe { + llvm::LLVMRustDIBuilderCreatePointerType( + DIB(cx), + fn_di_node, + cx.tcx.data_layout.pointer_size.bits(), + cx.tcx.data_layout.pointer_align.abi.bits() as u32, + 0, // Ignore DWARF address space. + name.as_ptr().cast(), + name.len(), + ) + }; + + DINodeCreationResult::new(di_node, false) +} + +/// Create debuginfo for `dyn SomeTrait` types. Currently these are empty structs +/// we with the correct type name (e.g. "dyn SomeTrait<Foo, Item=u32> + Sync"). +fn build_dyn_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + dyn_type: Ty<'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + if let ty::Dynamic(..) = dyn_type.kind() { + let type_name = compute_debuginfo_type_name(cx.tcx, dyn_type, true); + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + Stub::Struct, + unique_type_id, + &type_name, + cx.size_and_align_of(dyn_type), + NO_SCOPE_METADATA, + DIFlags::FlagZero, + ), + |_, _| smallvec![], + NO_GENERICS, + ) + } else { + bug!( + "Only ty::Dynamic is valid for build_dyn_type_di_node(). Found {:?} instead.", + dyn_type + ) + } +} + +/// Create debuginfo for `[T]` and `str`. These are unsized. +/// +/// NOTE: We currently emit just emit the debuginfo for the element type here +/// (i.e. `T` for slices and `u8` for `str`), so that we end up with +/// `*const T` for the `data_ptr` field of the corresponding fat-pointer +/// debuginfo of `&[T]`. +/// +/// It would be preferable and more accurate if we emitted a DIArray of T +/// without an upper bound instead. That is, LLVM already supports emitting +/// debuginfo of arrays of unknown size. But GDB currently seems to end up +/// in an infinite loop when confronted with such a type. +/// +/// As a side effect of the current encoding every instance of a type like +/// `struct Foo { unsized_field: [u8] }` will look like +/// `struct Foo { unsized_field: u8 }` in debuginfo. If the length of the +/// slice is zero, then accessing `unsized_field` in the debugger would +/// result in an out-of-bounds access. +fn build_slice_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + slice_type: Ty<'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + let element_type = match slice_type.kind() { + ty::Slice(element_type) => *element_type, + ty::Str => cx.tcx.types.u8, + _ => { + bug!( + "Only ty::Slice is valid for build_slice_type_di_node(). Found {:?} instead.", + slice_type + ) + } + }; + + let element_type_di_node = type_di_node(cx, element_type); + return_if_di_node_created_in_meantime!(cx, unique_type_id); + DINodeCreationResult { di_node: element_type_di_node, already_stored_in_typemap: false } +} + +/// Get the debuginfo node for the given type. +/// +/// This function will look up the debuginfo node in the TypeMap. If it can't find it, it +/// will create the node by dispatching to the corresponding `build_*_di_node()` function. +pub fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType { + let unique_type_id = UniqueTypeId::for_ty(cx.tcx, t); + + if let Some(existing_di_node) = debug_context(cx).type_map.di_node_for_unique_id(unique_type_id) + { + return existing_di_node; + } + + debug!("type_di_node: {:?}", t); + + let DINodeCreationResult { di_node, already_stored_in_typemap } = match *t.kind() { + ty::Never | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => { + build_basic_type_di_node(cx, t) + } + ty::Tuple(elements) if elements.is_empty() => build_basic_type_di_node(cx, t), + ty::Array(..) => build_fixed_size_array_di_node(cx, unique_type_id, t), + ty::Slice(_) | ty::Str => build_slice_type_di_node(cx, t, unique_type_id), + ty::Dynamic(..) => build_dyn_type_di_node(cx, t, unique_type_id), + ty::Foreign(..) => build_foreign_type_di_node(cx, t, unique_type_id), + ty::RawPtr(ty::TypeAndMut { ty: pointee_type, .. }) | ty::Ref(_, pointee_type, _) => { + build_pointer_or_reference_di_node(cx, t, pointee_type, unique_type_id) + } + // Box<T, A> may have a non-ZST allocator A. In that case, we + // cannot treat Box<T, A> as just an owned alias of `*mut T`. + ty::Adt(def, substs) if def.is_box() && cx.layout_of(substs.type_at(1)).is_zst() => { + build_pointer_or_reference_di_node(cx, t, t.boxed_ty(), unique_type_id) + } + ty::FnDef(..) | ty::FnPtr(_) => build_subroutine_type_di_node(cx, unique_type_id), + ty::Closure(..) => build_closure_env_di_node(cx, unique_type_id), + ty::Generator(..) => enums::build_generator_di_node(cx, unique_type_id), + ty::Adt(def, ..) => match def.adt_kind() { + AdtKind::Struct => build_struct_type_di_node(cx, unique_type_id), + AdtKind::Union => build_union_type_di_node(cx, unique_type_id), + AdtKind::Enum => enums::build_enum_type_di_node(cx, unique_type_id), + }, + ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id), + // Type parameters from polymorphized functions. + ty::Param(_) => build_param_type_di_node(cx, t), + _ => bug!("debuginfo: unexpected type in type_di_node(): {:?}", t), + }; + + { + if already_stored_in_typemap { + // Make sure that we really do have a `TypeMap` entry for the unique type ID. + let di_node_for_uid = + match debug_context(cx).type_map.di_node_for_unique_id(unique_type_id) { + Some(di_node) => di_node, + None => { + bug!( + "expected type debuginfo node for unique \ + type ID '{:?}' to already be in \ + the `debuginfo::TypeMap` but it \ + was not.", + unique_type_id, + ); + } + }; + + debug_assert_eq!(di_node_for_uid as *const _, di_node as *const _); + } else { + debug_context(cx).type_map.insert(unique_type_id, di_node); + } + } + + di_node +} + +// FIXME(mw): Cache this via a regular UniqueTypeId instead of an extra field in the debug context. +fn recursion_marker_type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> &'ll DIType { + *debug_context(cx).recursion_marker_type.get_or_init(move || { + unsafe { + // The choice of type here is pretty arbitrary - + // anything reading the debuginfo for a recursive + // type is going to see *something* weird - the only + // question is what exactly it will see. + // + // FIXME: the name `<recur_type>` does not fit the naming scheme + // of other types. + // + // FIXME: it might make sense to use an actual pointer type here + // so that debuggers can show the address. + let name = "<recur_type>"; + llvm::LLVMRustDIBuilderCreateBasicType( + DIB(cx), + name.as_ptr().cast(), + name.len(), + cx.tcx.data_layout.pointer_size.bits(), + DW_ATE_unsigned, + ) + } + }) +} + +fn hex_encode(data: &[u8]) -> String { + let mut hex_string = String::with_capacity(data.len() * 2); + for byte in data.iter() { + write!(&mut hex_string, "{:02x}", byte).unwrap(); + } + hex_string +} + +pub fn file_metadata<'ll>(cx: &CodegenCx<'ll, '_>, source_file: &SourceFile) -> &'ll DIFile { + let cache_key = Some((source_file.name_hash, source_file.src_hash)); + return debug_context(cx) + .created_files + .borrow_mut() + .entry(cache_key) + .or_insert_with(|| alloc_new_file_metadata(cx, source_file)); + + #[instrument(skip(cx, source_file), level = "debug")] + fn alloc_new_file_metadata<'ll>( + cx: &CodegenCx<'ll, '_>, + source_file: &SourceFile, + ) -> &'ll DIFile { + debug!(?source_file.name); + + let (directory, file_name) = match &source_file.name { + FileName::Real(filename) => { + let working_directory = &cx.sess().opts.working_dir; + debug!(?working_directory); + + let filename = cx + .sess() + .source_map() + .path_mapping() + .to_embeddable_absolute_path(filename.clone(), working_directory); + + // Construct the absolute path of the file + let abs_path = filename.remapped_path_if_available(); + debug!(?abs_path); + + if let Ok(rel_path) = + abs_path.strip_prefix(working_directory.remapped_path_if_available()) + { + // If the compiler's working directory (which also is the DW_AT_comp_dir of + // the compilation unit) is a prefix of the path we are about to emit, then + // only emit the part relative to the working directory. + // Because of path remapping we sometimes see strange things here: `abs_path` + // might actually look like a relative path + // (e.g. `<crate-name-and-version>/src/lib.rs`), so if we emit it without + // taking the working directory into account, downstream tooling will + // interpret it as `<working-directory>/<crate-name-and-version>/src/lib.rs`, + // which makes no sense. Usually in such cases the working directory will also + // be remapped to `<crate-name-and-version>` or some other prefix of the path + // we are remapping, so we end up with + // `<crate-name-and-version>/<crate-name-and-version>/src/lib.rs`. + // By moving the working directory portion into the `directory` part of the + // DIFile, we allow LLVM to emit just the relative path for DWARF, while + // still emitting the correct absolute path for CodeView. + ( + working_directory.to_string_lossy(FileNameDisplayPreference::Remapped), + rel_path.to_string_lossy().into_owned(), + ) + } else { + ("".into(), abs_path.to_string_lossy().into_owned()) + } + } + other => ("".into(), other.prefer_remapped().to_string_lossy().into_owned()), + }; + + let hash_kind = match source_file.src_hash.kind { + rustc_span::SourceFileHashAlgorithm::Md5 => llvm::ChecksumKind::MD5, + rustc_span::SourceFileHashAlgorithm::Sha1 => llvm::ChecksumKind::SHA1, + rustc_span::SourceFileHashAlgorithm::Sha256 => llvm::ChecksumKind::SHA256, + }; + let hash_value = hex_encode(source_file.src_hash.hash_bytes()); + + unsafe { + llvm::LLVMRustDIBuilderCreateFile( + DIB(cx), + file_name.as_ptr().cast(), + file_name.len(), + directory.as_ptr().cast(), + directory.len(), + hash_kind, + hash_value.as_ptr().cast(), + hash_value.len(), + ) + } + } +} + +pub fn unknown_file_metadata<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll DIFile { + debug_context(cx).created_files.borrow_mut().entry(None).or_insert_with(|| unsafe { + let file_name = "<unknown>"; + let directory = ""; + let hash_value = ""; + + llvm::LLVMRustDIBuilderCreateFile( + DIB(cx), + file_name.as_ptr().cast(), + file_name.len(), + directory.as_ptr().cast(), + directory.len(), + llvm::ChecksumKind::None, + hash_value.as_ptr().cast(), + hash_value.len(), + ) + }) +} + +trait MsvcBasicName { + fn msvc_basic_name(self) -> &'static str; +} + +impl MsvcBasicName for ty::IntTy { + fn msvc_basic_name(self) -> &'static str { + match self { + ty::IntTy::Isize => "ptrdiff_t", + ty::IntTy::I8 => "__int8", + ty::IntTy::I16 => "__int16", + ty::IntTy::I32 => "__int32", + ty::IntTy::I64 => "__int64", + ty::IntTy::I128 => "__int128", + } + } +} + +impl MsvcBasicName for ty::UintTy { + fn msvc_basic_name(self) -> &'static str { + match self { + ty::UintTy::Usize => "size_t", + ty::UintTy::U8 => "unsigned __int8", + ty::UintTy::U16 => "unsigned __int16", + ty::UintTy::U32 => "unsigned __int32", + ty::UintTy::U64 => "unsigned __int64", + ty::UintTy::U128 => "unsigned __int128", + } + } +} + +impl MsvcBasicName for ty::FloatTy { + fn msvc_basic_name(self) -> &'static str { + match self { + ty::FloatTy::F32 => "float", + ty::FloatTy::F64 => "double", + } + } +} + +fn build_basic_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + t: Ty<'tcx>, +) -> DINodeCreationResult<'ll> { + debug!("build_basic_type_di_node: {:?}", t); + + // When targeting MSVC, emit MSVC style type names for compatibility with + // .natvis visualizers (and perhaps other existing native debuggers?) + let cpp_like_debuginfo = cpp_like_debuginfo(cx.tcx); + + let (name, encoding) = match t.kind() { + ty::Never => ("!", DW_ATE_unsigned), + ty::Tuple(elements) if elements.is_empty() => { + if cpp_like_debuginfo { + return build_tuple_type_di_node(cx, UniqueTypeId::for_ty(cx.tcx, t)); + } else { + ("()", DW_ATE_unsigned) + } + } + ty::Bool => ("bool", DW_ATE_boolean), + ty::Char => ("char", DW_ATE_UTF), + ty::Int(int_ty) if cpp_like_debuginfo => (int_ty.msvc_basic_name(), DW_ATE_signed), + ty::Uint(uint_ty) if cpp_like_debuginfo => (uint_ty.msvc_basic_name(), DW_ATE_unsigned), + ty::Float(float_ty) if cpp_like_debuginfo => (float_ty.msvc_basic_name(), DW_ATE_float), + ty::Int(int_ty) => (int_ty.name_str(), DW_ATE_signed), + ty::Uint(uint_ty) => (uint_ty.name_str(), DW_ATE_unsigned), + ty::Float(float_ty) => (float_ty.name_str(), DW_ATE_float), + _ => bug!("debuginfo::build_basic_type_di_node - `t` is invalid type"), + }; + + let ty_di_node = unsafe { + llvm::LLVMRustDIBuilderCreateBasicType( + DIB(cx), + name.as_ptr().cast(), + name.len(), + cx.size_of(t).bits(), + encoding, + ) + }; + + if !cpp_like_debuginfo { + return DINodeCreationResult::new(ty_di_node, false); + } + + let typedef_name = match t.kind() { + ty::Int(int_ty) => int_ty.name_str(), + ty::Uint(uint_ty) => uint_ty.name_str(), + ty::Float(float_ty) => float_ty.name_str(), + _ => return DINodeCreationResult::new(ty_di_node, false), + }; + + let typedef_di_node = unsafe { + llvm::LLVMRustDIBuilderCreateTypedef( + DIB(cx), + ty_di_node, + typedef_name.as_ptr().cast(), + typedef_name.len(), + unknown_file_metadata(cx), + 0, + None, + ) + }; + + DINodeCreationResult::new(typedef_di_node, false) +} + +fn build_foreign_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + t: Ty<'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + debug!("build_foreign_type_di_node: {:?}", t); + + let &ty::Foreign(def_id) = unique_type_id.expect_ty().kind() else { + bug!("build_foreign_type_di_node() called with unexpected type: {:?}", unique_type_id.expect_ty()); + }; + + build_type_with_children( + cx, + type_map::stub( + cx, + Stub::Struct, + unique_type_id, + &compute_debuginfo_type_name(cx.tcx, t, false), + cx.size_and_align_of(t), + Some(get_namespace_for_item(cx, def_id)), + DIFlags::FlagZero, + ), + |_, _| smallvec![], + NO_GENERICS, + ) +} + +fn build_param_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + t: Ty<'tcx>, +) -> DINodeCreationResult<'ll> { + debug!("build_param_type_di_node: {:?}", t); + let name = format!("{:?}", t); + DINodeCreationResult { + di_node: unsafe { + llvm::LLVMRustDIBuilderCreateBasicType( + DIB(cx), + name.as_ptr().cast(), + name.len(), + Size::ZERO.bits(), + DW_ATE_unsigned, + ) + }, + already_stored_in_typemap: false, + } +} + +pub fn build_compile_unit_di_node<'ll, 'tcx>( + tcx: TyCtxt<'tcx>, + codegen_unit_name: &str, + debug_context: &CodegenUnitDebugContext<'ll, 'tcx>, +) -> &'ll DIDescriptor { + let mut name_in_debuginfo = match tcx.sess.local_crate_source_file { + Some(ref path) => path.clone(), + None => PathBuf::from(tcx.crate_name(LOCAL_CRATE).as_str()), + }; + + // To avoid breaking split DWARF, we need to ensure that each codegen unit + // has a unique `DW_AT_name`. This is because there's a remote chance that + // different codegen units for the same module will have entirely + // identical DWARF entries for the purpose of the DWO ID, which would + // violate Appendix F ("Split Dwarf Object Files") of the DWARF 5 + // specification. LLVM uses the algorithm specified in section 7.32 "Type + // Signature Computation" to compute the DWO ID, which does not include + // any fields that would distinguish compilation units. So we must embed + // the codegen unit name into the `DW_AT_name`. (Issue #88521.) + // + // Additionally, the OSX linker has an idiosyncrasy where it will ignore + // some debuginfo if multiple object files with the same `DW_AT_name` are + // linked together. + // + // As a workaround for these two issues, we generate unique names for each + // object file. Those do not correspond to an actual source file but that + // is harmless. + name_in_debuginfo.push("@"); + name_in_debuginfo.push(codegen_unit_name); + + debug!("build_compile_unit_di_node: {:?}", name_in_debuginfo); + let rustc_producer = + format!("rustc version {}", option_env!("CFG_VERSION").expect("CFG_VERSION"),); + // FIXME(#41252) Remove "clang LLVM" if we can get GDB and LLVM to play nice. + let producer = format!("clang LLVM ({})", rustc_producer); + + let name_in_debuginfo = name_in_debuginfo.to_string_lossy(); + let work_dir = tcx.sess.opts.working_dir.to_string_lossy(FileNameDisplayPreference::Remapped); + let flags = "\0"; + let output_filenames = tcx.output_filenames(()); + let split_name = if tcx.sess.target_can_use_split_dwarf() { + output_filenames + .split_dwarf_path( + tcx.sess.split_debuginfo(), + tcx.sess.opts.unstable_opts.split_dwarf_kind, + Some(codegen_unit_name), + ) + // We get a path relative to the working directory from split_dwarf_path + .map(|f| tcx.sess.source_map().path_mapping().map_prefix(f).0) + } else { + None + } + .unwrap_or_default(); + let split_name = split_name.to_str().unwrap(); + + // FIXME(#60020): + // + // This should actually be + // + // let kind = DebugEmissionKind::from_generic(tcx.sess.opts.debuginfo); + // + // That is, we should set LLVM's emission kind to `LineTablesOnly` if + // we are compiling with "limited" debuginfo. However, some of the + // existing tools relied on slightly more debuginfo being generated than + // would be the case with `LineTablesOnly`, and we did not want to break + // these tools in a "drive-by fix", without a good idea or plan about + // what limited debuginfo should exactly look like. So for now we keep + // the emission kind as `FullDebug`. + // + // See https://github.com/rust-lang/rust/issues/60020 for details. + let kind = DebugEmissionKind::FullDebug; + assert!(tcx.sess.opts.debuginfo != DebugInfo::None); + + unsafe { + let compile_unit_file = llvm::LLVMRustDIBuilderCreateFile( + debug_context.builder, + name_in_debuginfo.as_ptr().cast(), + name_in_debuginfo.len(), + work_dir.as_ptr().cast(), + work_dir.len(), + llvm::ChecksumKind::None, + ptr::null(), + 0, + ); + + let unit_metadata = llvm::LLVMRustDIBuilderCreateCompileUnit( + debug_context.builder, + DW_LANG_RUST, + compile_unit_file, + producer.as_ptr().cast(), + producer.len(), + tcx.sess.opts.optimize != config::OptLevel::No, + flags.as_ptr().cast(), + 0, + // NB: this doesn't actually have any perceptible effect, it seems. LLVM will instead + // put the path supplied to `MCSplitDwarfFile` into the debug info of the final + // output(s). + split_name.as_ptr().cast(), + split_name.len(), + kind, + 0, + tcx.sess.opts.unstable_opts.split_dwarf_inlining, + ); + + if tcx.sess.opts.unstable_opts.profile { + let cu_desc_metadata = + llvm::LLVMRustMetadataAsValue(debug_context.llcontext, unit_metadata); + let default_gcda_path = &output_filenames.with_extension("gcda"); + let gcda_path = + tcx.sess.opts.unstable_opts.profile_emit.as_ref().unwrap_or(default_gcda_path); + + let gcov_cu_info = [ + path_to_mdstring(debug_context.llcontext, &output_filenames.with_extension("gcno")), + path_to_mdstring(debug_context.llcontext, gcda_path), + cu_desc_metadata, + ]; + let gcov_metadata = llvm::LLVMMDNodeInContext( + debug_context.llcontext, + gcov_cu_info.as_ptr(), + gcov_cu_info.len() as c_uint, + ); + + let llvm_gcov_ident = cstr!("llvm.gcov"); + llvm::LLVMAddNamedMetadataOperand( + debug_context.llmod, + llvm_gcov_ident.as_ptr(), + gcov_metadata, + ); + } + + // Insert `llvm.ident` metadata on the wasm targets since that will + // get hooked up to the "producer" sections `processed-by` information. + if tcx.sess.target.is_like_wasm { + let name_metadata = llvm::LLVMMDStringInContext( + debug_context.llcontext, + rustc_producer.as_ptr().cast(), + rustc_producer.as_bytes().len() as c_uint, + ); + llvm::LLVMAddNamedMetadataOperand( + debug_context.llmod, + cstr!("llvm.ident").as_ptr(), + llvm::LLVMMDNodeInContext(debug_context.llcontext, &name_metadata, 1), + ); + } + + return unit_metadata; + }; + + fn path_to_mdstring<'ll>(llcx: &'ll llvm::Context, path: &Path) -> &'ll Value { + let path_str = path_to_c_string(path); + unsafe { + llvm::LLVMMDStringInContext( + llcx, + path_str.as_ptr(), + path_str.as_bytes().len() as c_uint, + ) + } + } +} + +/// Creates a `DW_TAG_member` entry inside the DIE represented by the given `type_di_node`. +fn build_field_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + owner: &'ll DIScope, + name: &str, + size_and_align: (Size, Align), + offset: Size, + flags: DIFlags, + type_di_node: &'ll DIType, +) -> &'ll DIType { + unsafe { + llvm::LLVMRustDIBuilderCreateMemberType( + DIB(cx), + owner, + name.as_ptr().cast(), + name.len(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + size_and_align.0.bits(), + size_and_align.1.bits() as u32, + offset.bits(), + flags, + type_di_node, + ) + } +} + +/// Creates the debuginfo node for a Rust struct type. Maybe be a regular struct or a tuple-struct. +fn build_struct_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + let struct_type = unique_type_id.expect_ty(); + let ty::Adt(adt_def, _) = struct_type.kind() else { + bug!("build_struct_type_di_node() called with non-struct-type: {:?}", struct_type); + }; + debug_assert!(adt_def.is_struct()); + let containing_scope = get_namespace_for_item(cx, adt_def.did()); + let struct_type_and_layout = cx.layout_of(struct_type); + let variant_def = adt_def.non_enum_variant(); + + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + Stub::Struct, + unique_type_id, + &compute_debuginfo_type_name(cx.tcx, struct_type, false), + size_and_align_of(struct_type_and_layout), + Some(containing_scope), + DIFlags::FlagZero, + ), + // Fields: + |cx, owner| { + variant_def + .fields + .iter() + .enumerate() + .map(|(i, f)| { + let field_name = if variant_def.ctor_kind == CtorKind::Fn { + // This is a tuple struct + tuple_field_name(i) + } else { + // This is struct with named fields + Cow::Borrowed(f.name.as_str()) + }; + let field_layout = struct_type_and_layout.field(cx, i); + build_field_di_node( + cx, + owner, + &field_name[..], + (field_layout.size, field_layout.align.abi), + struct_type_and_layout.fields.offset(i), + DIFlags::FlagZero, + type_di_node(cx, field_layout.ty), + ) + }) + .collect() + }, + |cx| build_generic_type_param_di_nodes(cx, struct_type), + ) +} + +//=----------------------------------------------------------------------------- +// Tuples +//=----------------------------------------------------------------------------- + +/// Returns names of captured upvars for closures and generators. +/// +/// Here are some examples: +/// - `name__field1__field2` when the upvar is captured by value. +/// - `_ref__name__field` when the upvar is captured by reference. +/// +/// For generators this only contains upvars that are shared by all states. +fn closure_saved_names_of_captured_variables(tcx: TyCtxt<'_>, def_id: DefId) -> SmallVec<String> { + let body = tcx.optimized_mir(def_id); + + body.var_debug_info + .iter() + .filter_map(|var| { + let is_ref = match var.value { + mir::VarDebugInfoContents::Place(place) if place.local == mir::Local::new(1) => { + // The projection is either `[.., Field, Deref]` or `[.., Field]`. It + // implies whether the variable is captured by value or by reference. + matches!(place.projection.last().unwrap(), mir::ProjectionElem::Deref) + } + _ => return None, + }; + let prefix = if is_ref { "_ref__" } else { "" }; + Some(prefix.to_owned() + var.name.as_str()) + }) + .collect() +} + +/// Builds the DW_TAG_member debuginfo nodes for the upvars of a closure or generator. +/// For a generator, this will handle upvars shared by all states. +fn build_upvar_field_di_nodes<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + closure_or_generator_ty: Ty<'tcx>, + closure_or_generator_di_node: &'ll DIType, +) -> SmallVec<&'ll DIType> { + let (&def_id, up_var_tys) = match closure_or_generator_ty.kind() { + ty::Generator(def_id, substs, _) => { + let upvar_tys: SmallVec<_> = substs.as_generator().prefix_tys().collect(); + (def_id, upvar_tys) + } + ty::Closure(def_id, substs) => { + let upvar_tys: SmallVec<_> = substs.as_closure().upvar_tys().collect(); + (def_id, upvar_tys) + } + _ => { + bug!( + "build_upvar_field_di_nodes() called with non-closure-or-generator-type: {:?}", + closure_or_generator_ty + ) + } + }; + + debug_assert!( + up_var_tys + .iter() + .all(|&t| t == cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t)) + ); + + let capture_names = closure_saved_names_of_captured_variables(cx.tcx, def_id); + let layout = cx.layout_of(closure_or_generator_ty); + + up_var_tys + .into_iter() + .zip(capture_names.iter()) + .enumerate() + .map(|(index, (up_var_ty, capture_name))| { + build_field_di_node( + cx, + closure_or_generator_di_node, + capture_name, + cx.size_and_align_of(up_var_ty), + layout.fields.offset(index), + DIFlags::FlagZero, + type_di_node(cx, up_var_ty), + ) + }) + .collect() +} + +/// Builds the DW_TAG_structure_type debuginfo node for a Rust tuple type. +fn build_tuple_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + let tuple_type = unique_type_id.expect_ty(); + let &ty::Tuple(component_types) = tuple_type.kind() else { + bug!("build_tuple_type_di_node() called with non-tuple-type: {:?}", tuple_type) + }; + + let tuple_type_and_layout = cx.layout_of(tuple_type); + let type_name = compute_debuginfo_type_name(cx.tcx, tuple_type, false); + + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + Stub::Struct, + unique_type_id, + &type_name, + size_and_align_of(tuple_type_and_layout), + NO_SCOPE_METADATA, + DIFlags::FlagZero, + ), + // Fields: + |cx, tuple_di_node| { + component_types + .into_iter() + .enumerate() + .map(|(index, component_type)| { + build_field_di_node( + cx, + tuple_di_node, + &tuple_field_name(index), + cx.size_and_align_of(component_type), + tuple_type_and_layout.fields.offset(index), + DIFlags::FlagZero, + type_di_node(cx, component_type), + ) + }) + .collect() + }, + NO_GENERICS, + ) +} + +/// Builds the debuginfo node for a closure environment. +fn build_closure_env_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + let closure_env_type = unique_type_id.expect_ty(); + let &ty::Closure(def_id, _substs) = closure_env_type.kind() else { + bug!("build_closure_env_di_node() called with non-closure-type: {:?}", closure_env_type) + }; + let containing_scope = get_namespace_for_item(cx, def_id); + let type_name = compute_debuginfo_type_name(cx.tcx, closure_env_type, false); + + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + Stub::Struct, + unique_type_id, + &type_name, + cx.size_and_align_of(closure_env_type), + Some(containing_scope), + DIFlags::FlagZero, + ), + // Fields: + |cx, owner| build_upvar_field_di_nodes(cx, closure_env_type, owner), + NO_GENERICS, + ) +} + +/// Build the debuginfo node for a Rust `union` type. +fn build_union_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + let union_type = unique_type_id.expect_ty(); + let (union_def_id, variant_def) = match union_type.kind() { + ty::Adt(def, _) => (def.did(), def.non_enum_variant()), + _ => bug!("build_union_type_di_node on a non-ADT"), + }; + let containing_scope = get_namespace_for_item(cx, union_def_id); + let union_ty_and_layout = cx.layout_of(union_type); + let type_name = compute_debuginfo_type_name(cx.tcx, union_type, false); + + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + Stub::Union, + unique_type_id, + &type_name, + size_and_align_of(union_ty_and_layout), + Some(containing_scope), + DIFlags::FlagZero, + ), + // Fields: + |cx, owner| { + variant_def + .fields + .iter() + .enumerate() + .map(|(i, f)| { + let field_layout = union_ty_and_layout.field(cx, i); + build_field_di_node( + cx, + owner, + f.name.as_str(), + size_and_align_of(field_layout), + Size::ZERO, + DIFlags::FlagZero, + type_di_node(cx, field_layout.ty), + ) + }) + .collect() + }, + // Generics: + |cx| build_generic_type_param_di_nodes(cx, union_type), + ) +} + +// FIXME(eddyb) maybe precompute this? Right now it's computed once +// per generator monomorphization, but it doesn't depend on substs. +fn generator_layout_and_saved_local_names<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, +) -> (&'tcx GeneratorLayout<'tcx>, IndexVec<mir::GeneratorSavedLocal, Option<Symbol>>) { + let body = tcx.optimized_mir(def_id); + let generator_layout = body.generator_layout().unwrap(); + let mut generator_saved_local_names = IndexVec::from_elem(None, &generator_layout.field_tys); + + let state_arg = mir::Local::new(1); + for var in &body.var_debug_info { + let mir::VarDebugInfoContents::Place(place) = &var.value else { continue }; + if place.local != state_arg { + continue; + } + match place.projection[..] { + [ + // Deref of the `Pin<&mut Self>` state argument. + mir::ProjectionElem::Field(..), + mir::ProjectionElem::Deref, + // Field of a variant of the state. + mir::ProjectionElem::Downcast(_, variant), + mir::ProjectionElem::Field(field, _), + ] => { + let name = &mut generator_saved_local_names + [generator_layout.variant_fields[variant][field]]; + if name.is_none() { + name.replace(var.name); + } + } + _ => {} + } + } + (generator_layout, generator_saved_local_names) +} + +/// Computes the type parameters for a type, if any, for the given metadata. +fn build_generic_type_param_di_nodes<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + ty: Ty<'tcx>, +) -> SmallVec<&'ll DIType> { + if let ty::Adt(def, substs) = *ty.kind() { + if substs.types().next().is_some() { + let generics = cx.tcx.generics_of(def.did()); + let names = get_parameter_names(cx, generics); + let template_params: SmallVec<_> = iter::zip(substs, names) + .filter_map(|(kind, name)| { + if let GenericArgKind::Type(ty) = kind.unpack() { + let actual_type = + cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty); + let actual_type_di_node = type_di_node(cx, actual_type); + let name = name.as_str(); + Some(unsafe { + llvm::LLVMRustDIBuilderCreateTemplateTypeParameter( + DIB(cx), + None, + name.as_ptr().cast(), + name.len(), + actual_type_di_node, + ) + }) + } else { + None + } + }) + .collect(); + + return template_params; + } + } + + return smallvec![]; + + fn get_parameter_names(cx: &CodegenCx<'_, '_>, generics: &ty::Generics) -> Vec<Symbol> { + let mut names = generics + .parent + .map_or_else(Vec::new, |def_id| get_parameter_names(cx, cx.tcx.generics_of(def_id))); + names.extend(generics.params.iter().map(|param| param.name)); + names + } +} + +/// Creates debug information for the given global variable. +/// +/// Adds the created debuginfo nodes directly to the crate's IR. +pub fn build_global_var_di_node<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId, global: &'ll Value) { + if cx.dbg_cx.is_none() { + return; + } + + // Only create type information if full debuginfo is enabled + if cx.sess().opts.debuginfo != DebugInfo::Full { + return; + } + + let tcx = cx.tcx; + + // We may want to remove the namespace scope if we're in an extern block (see + // https://github.com/rust-lang/rust/pull/46457#issuecomment-351750952). + let var_scope = get_namespace_for_item(cx, def_id); + let span = tcx.def_span(def_id); + + let (file_metadata, line_number) = if !span.is_dummy() { + let loc = cx.lookup_debug_loc(span.lo()); + (file_metadata(cx, &loc.file), loc.line) + } else { + (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER) + }; + + let is_local_to_unit = is_node_local_to_unit(cx, def_id); + let variable_type = Instance::mono(cx.tcx, def_id).ty(cx.tcx, ty::ParamEnv::reveal_all()); + let type_di_node = type_di_node(cx, variable_type); + let var_name = tcx.item_name(def_id); + let var_name = var_name.as_str(); + let linkage_name = mangled_name_of_instance(cx, Instance::mono(tcx, def_id)).name; + // When empty, linkage_name field is omitted, + // which is what we want for no_mangle statics + let linkage_name = if var_name == linkage_name { "" } else { linkage_name }; + + let global_align = cx.align_of(variable_type); + + unsafe { + llvm::LLVMRustDIBuilderCreateStaticVariable( + DIB(cx), + Some(var_scope), + var_name.as_ptr().cast(), + var_name.len(), + linkage_name.as_ptr().cast(), + linkage_name.len(), + file_metadata, + line_number, + type_di_node, + is_local_to_unit, + global, + None, + global_align.bits() as u32, + ); + } +} + +/// Generates LLVM debuginfo for a vtable. +/// +/// The vtable type looks like a struct with a field for each function pointer and super-trait +/// pointer it contains (plus the `size` and `align` fields). +/// +/// Except for `size`, `align`, and `drop_in_place`, the field names don't try to mirror +/// the name of the method they implement. This can be implemented in the future once there +/// is a proper disambiguation scheme for dealing with methods from different traits that have +/// the same name. +fn build_vtable_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + ty: Ty<'tcx>, + poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>, +) -> &'ll DIType { + let tcx = cx.tcx; + + let vtable_entries = if let Some(poly_trait_ref) = poly_trait_ref { + let trait_ref = poly_trait_ref.with_self_ty(tcx, ty); + let trait_ref = tcx.erase_regions(trait_ref); + + tcx.vtable_entries(trait_ref) + } else { + TyCtxt::COMMON_VTABLE_ENTRIES + }; + + // All function pointers are described as opaque pointers. This could be improved in the future + // by describing them as actual function pointers. + let void_pointer_ty = tcx.mk_imm_ptr(tcx.types.unit); + let void_pointer_type_di_node = type_di_node(cx, void_pointer_ty); + let usize_di_node = type_di_node(cx, tcx.types.usize); + let (pointer_size, pointer_align) = cx.size_and_align_of(void_pointer_ty); + // If `usize` is not pointer-sized and -aligned then the size and alignment computations + // for the vtable as a whole would be wrong. Let's make sure this holds even on weird + // platforms. + assert_eq!(cx.size_and_align_of(tcx.types.usize), (pointer_size, pointer_align)); + + let vtable_type_name = + compute_debuginfo_vtable_name(cx.tcx, ty, poly_trait_ref, VTableNameKind::Type); + let unique_type_id = UniqueTypeId::for_vtable_ty(tcx, ty, poly_trait_ref); + let size = pointer_size * vtable_entries.len() as u64; + + // This gets mapped to a DW_AT_containing_type attribute which allows GDB to correlate + // the vtable to the type it is for. + let vtable_holder = type_di_node(cx, ty); + + build_type_with_children( + cx, + type_map::stub( + cx, + Stub::VTableTy { vtable_holder }, + unique_type_id, + &vtable_type_name, + (size, pointer_align), + NO_SCOPE_METADATA, + DIFlags::FlagArtificial, + ), + |cx, vtable_type_di_node| { + vtable_entries + .iter() + .enumerate() + .filter_map(|(index, vtable_entry)| { + let (field_name, field_type_di_node) = match vtable_entry { + ty::VtblEntry::MetadataDropInPlace => { + ("drop_in_place".to_string(), void_pointer_type_di_node) + } + ty::VtblEntry::Method(_) => { + // Note: This code does not try to give a proper name to each method + // because their might be multiple methods with the same name + // (coming from different traits). + (format!("__method{}", index), void_pointer_type_di_node) + } + ty::VtblEntry::TraitVPtr(_) => { + (format!("__super_trait_ptr{}", index), void_pointer_type_di_node) + } + ty::VtblEntry::MetadataAlign => ("align".to_string(), usize_di_node), + ty::VtblEntry::MetadataSize => ("size".to_string(), usize_di_node), + ty::VtblEntry::Vacant => return None, + }; + + let field_offset = pointer_size * index as u64; + + Some(build_field_di_node( + cx, + vtable_type_di_node, + &field_name, + (pointer_size, pointer_align), + field_offset, + DIFlags::FlagZero, + field_type_di_node, + )) + }) + .collect() + }, + NO_GENERICS, + ) + .di_node +} + +fn vcall_visibility_metadata<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + ty: Ty<'tcx>, + trait_ref: Option<PolyExistentialTraitRef<'tcx>>, + vtable: &'ll Value, +) { + enum VCallVisibility { + Public = 0, + LinkageUnit = 1, + TranslationUnit = 2, + } + + let Some(trait_ref) = trait_ref else { return }; + + let trait_ref_self = trait_ref.with_self_ty(cx.tcx, ty); + let trait_ref_self = cx.tcx.erase_regions(trait_ref_self); + let trait_def_id = trait_ref_self.def_id(); + let trait_vis = cx.tcx.visibility(trait_def_id); + + let cgus = cx.sess().codegen_units(); + let single_cgu = cgus == 1; + + let lto = cx.sess().lto(); + + // Since LLVM requires full LTO for the virtual function elimination optimization to apply, + // only the `Lto::Fat` cases are relevant currently. + let vcall_visibility = match (lto, trait_vis, single_cgu) { + // If there is not LTO and the visibility in public, we have to assume that the vtable can + // be seen from anywhere. With multiple CGUs, the vtable is quasi-public. + (Lto::No | Lto::ThinLocal, Visibility::Public, _) + | (Lto::No, Visibility::Restricted(_) | Visibility::Invisible, false) => { + VCallVisibility::Public + } + // With LTO and a quasi-public visibility, the usages of the functions of the vtable are + // all known by the `LinkageUnit`. + // FIXME: LLVM only supports this optimization for `Lto::Fat` currently. Once it also + // supports `Lto::Thin` the `VCallVisibility` may have to be adjusted for those. + (Lto::Fat | Lto::Thin, Visibility::Public, _) + | ( + Lto::ThinLocal | Lto::Thin | Lto::Fat, + Visibility::Restricted(_) | Visibility::Invisible, + false, + ) => VCallVisibility::LinkageUnit, + // If there is only one CGU, private vtables can only be seen by that CGU/translation unit + // and therefore we know of all usages of functions in the vtable. + (_, Visibility::Restricted(_) | Visibility::Invisible, true) => { + VCallVisibility::TranslationUnit + } + }; + + let trait_ref_typeid = typeid_for_trait_ref(cx.tcx, trait_ref); + + unsafe { + let typeid = llvm::LLVMMDStringInContext( + cx.llcx, + trait_ref_typeid.as_ptr() as *const c_char, + trait_ref_typeid.as_bytes().len() as c_uint, + ); + let v = [cx.const_usize(0), typeid]; + llvm::LLVMRustGlobalAddMetadata( + vtable, + llvm::MD_type as c_uint, + llvm::LLVMValueAsMetadata(llvm::LLVMMDNodeInContext( + cx.llcx, + v.as_ptr(), + v.len() as c_uint, + )), + ); + let vcall_visibility = llvm::LLVMValueAsMetadata(cx.const_u64(vcall_visibility as u64)); + let vcall_visibility_metadata = llvm::LLVMMDNodeInContext2(cx.llcx, &vcall_visibility, 1); + llvm::LLVMGlobalSetMetadata( + vtable, + llvm::MetadataType::MD_vcall_visibility as c_uint, + vcall_visibility_metadata, + ); + } +} + +/// Creates debug information for the given vtable, which is for the +/// given type. +/// +/// Adds the created metadata nodes directly to the crate's IR. +pub fn create_vtable_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + ty: Ty<'tcx>, + poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>, + vtable: &'ll Value, +) { + // FIXME(flip1995): The virtual function elimination optimization only works with full LTO in + // LLVM at the moment. + if cx.sess().opts.unstable_opts.virtual_function_elimination && cx.sess().lto() == Lto::Fat { + vcall_visibility_metadata(cx, ty, poly_trait_ref, vtable); + } + + if cx.dbg_cx.is_none() { + return; + } + + // Only create type information if full debuginfo is enabled + if cx.sess().opts.debuginfo != DebugInfo::Full { + return; + } + + let vtable_name = + compute_debuginfo_vtable_name(cx.tcx, ty, poly_trait_ref, VTableNameKind::GlobalVariable); + let vtable_type_di_node = build_vtable_type_di_node(cx, ty, poly_trait_ref); + let linkage_name = ""; + + unsafe { + llvm::LLVMRustDIBuilderCreateStaticVariable( + DIB(cx), + NO_SCOPE_METADATA, + vtable_name.as_ptr().cast(), + vtable_name.len(), + linkage_name.as_ptr().cast(), + linkage_name.len(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + vtable_type_di_node, + true, + vtable, + None, + 0, + ); + } +} + +/// Creates an "extension" of an existing `DIScope` into another file. +pub fn extend_scope_to_file<'ll>( + cx: &CodegenCx<'ll, '_>, + scope_metadata: &'ll DIScope, + file: &SourceFile, +) -> &'ll DILexicalBlock { + let file_metadata = file_metadata(cx, file); + unsafe { llvm::LLVMRustDIBuilderCreateLexicalBlockFile(DIB(cx), scope_metadata, file_metadata) } +} + +pub fn tuple_field_name(field_index: usize) -> Cow<'static, str> { + const TUPLE_FIELD_NAMES: [&'static str; 16] = [ + "__0", "__1", "__2", "__3", "__4", "__5", "__6", "__7", "__8", "__9", "__10", "__11", + "__12", "__13", "__14", "__15", + ]; + TUPLE_FIELD_NAMES + .get(field_index) + .map(|s| Cow::from(*s)) + .unwrap_or_else(|| Cow::from(format!("__{}", field_index))) +} diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs new file mode 100644 index 000000000..d6e2c8ccd --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs @@ -0,0 +1,514 @@ +use std::borrow::Cow; + +use libc::c_uint; +use rustc_codegen_ssa::debuginfo::{ + type_names::compute_debuginfo_type_name, wants_c_like_enum_debuginfo, +}; +use rustc_middle::{ + bug, + ty::{ + self, + layout::{LayoutOf, TyAndLayout}, + util::Discr, + AdtDef, GeneratorSubsts, + }, +}; +use rustc_target::abi::{Size, TagEncoding, VariantIdx, Variants}; +use smallvec::smallvec; + +use crate::{ + common::CodegenCx, + debuginfo::{ + metadata::{ + build_field_di_node, closure_saved_names_of_captured_variables, + enums::tag_base_type, + file_metadata, generator_layout_and_saved_local_names, size_and_align_of, + type_map::{self, UniqueTypeId}, + unknown_file_metadata, DINodeCreationResult, SmallVec, NO_GENERICS, NO_SCOPE_METADATA, + UNKNOWN_LINE_NUMBER, + }, + utils::DIB, + }, + llvm::{ + self, + debuginfo::{DIFile, DIFlags, DIType}, + }, +}; + +/// In CPP-like mode, we generate a union of structs for each variant and an +/// explicit discriminant field roughly equivalent to the following C/C++ code: +/// +/// ```c +/// union enum$<{fully-qualified-name}> { +/// struct {variant 0 name} { +/// <variant 0 fields> +/// } variant0; +/// <other variant structs> +/// {name} discriminant; +/// } +/// ``` +/// +/// As you can see, the type name is wrapped `enum$`. This way we can have a +/// single NatVis rule for handling all enums. +/// +/// At the LLVM IR level this looks like +/// +/// ```txt +/// DW_TAG_union_type (top-level type for enum) +/// DW_TAG_member (member for variant 1) +/// DW_TAG_member (member for variant 2) +/// DW_TAG_member (member for variant 3) +/// DW_TAG_structure_type (type of variant 1) +/// DW_TAG_structure_type (type of variant 2) +/// DW_TAG_structure_type (type of variant 3) +/// DW_TAG_enumeration_type (type of tag) +/// ``` +/// +/// The above encoding applies for enums with a direct tag. For niche-tag we have to do things +/// differently in order to allow a NatVis visualizer to extract all the information needed: +/// We generate a union of two fields, one for the dataful variant +/// and one that just points to the discriminant (which is some field within the dataful variant). +/// We also create a DW_TAG_enumeration_type DIE that contains tag values for the non-dataful +/// variants and make the discriminant field that type. We then use NatVis to render the enum type +/// correctly in Windbg/VS. This will generate debuginfo roughly equivalent to the following C: +/// +/// ```c +/// union enum$<{name}, {min niche}, {max niche}, {dataful variant name}> { +/// struct <dataful variant name> { +/// <fields in dataful variant> +/// } dataful_variant; +/// enum Discriminant$ { +/// <non-dataful variants> +/// } discriminant; +/// } +/// ``` +/// +/// The NatVis in `intrinsic.natvis` matches on the type name `enum$<*, *, *, *>` +/// and evaluates `this.discriminant`. If the value is between the min niche and max +/// niche, then the enum is in the dataful variant and `this.dataful_variant` is +/// rendered. Otherwise, the enum is in one of the non-dataful variants. In that +/// case, we just need to render the name of the `this.discriminant` enum. +pub(super) fn build_enum_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + let enum_type = unique_type_id.expect_ty(); + let &ty::Adt(enum_adt_def, _) = enum_type.kind() else { + bug!("build_enum_type_di_node() called with non-enum type: `{:?}`", enum_type) + }; + + let enum_type_and_layout = cx.layout_of(enum_type); + let enum_type_name = compute_debuginfo_type_name(cx.tcx, enum_type, false); + + debug_assert!(!wants_c_like_enum_debuginfo(enum_type_and_layout)); + + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + type_map::Stub::Union, + unique_type_id, + &enum_type_name, + cx.size_and_align_of(enum_type), + NO_SCOPE_METADATA, + DIFlags::FlagZero, + ), + |cx, enum_type_di_node| { + match enum_type_and_layout.variants { + Variants::Single { index: variant_index } => { + if enum_adt_def.variants().is_empty() { + // Uninhabited enums have Variants::Single. We don't generate + // any members for them. + return smallvec![]; + } + + build_single_variant_union_fields( + cx, + enum_adt_def, + enum_type_and_layout, + enum_type_di_node, + variant_index, + ) + } + Variants::Multiple { + tag_encoding: TagEncoding::Direct, + ref variants, + tag_field, + .. + } => build_union_fields_for_direct_tag_enum( + cx, + enum_adt_def, + enum_type_and_layout, + enum_type_di_node, + &mut variants.indices(), + tag_field, + ), + Variants::Multiple { + tag_encoding: TagEncoding::Niche { dataful_variant, .. }, + ref variants, + tag_field, + .. + } => build_union_fields_for_niche_tag_enum( + cx, + enum_adt_def, + enum_type_and_layout, + enum_type_di_node, + dataful_variant, + &mut variants.indices(), + tag_field, + ), + } + }, + NO_GENERICS, + ) +} + +/// A generator debuginfo node looks the same as a that of an enum type. +/// +/// See [build_enum_type_di_node] for more information. +pub(super) fn build_generator_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + let generator_type = unique_type_id.expect_ty(); + let generator_type_and_layout = cx.layout_of(generator_type); + let generator_type_name = compute_debuginfo_type_name(cx.tcx, generator_type, false); + + debug_assert!(!wants_c_like_enum_debuginfo(generator_type_and_layout)); + + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + type_map::Stub::Union, + unique_type_id, + &generator_type_name, + size_and_align_of(generator_type_and_layout), + NO_SCOPE_METADATA, + DIFlags::FlagZero, + ), + |cx, generator_type_di_node| match generator_type_and_layout.variants { + Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => { + build_union_fields_for_direct_tag_generator( + cx, + generator_type_and_layout, + generator_type_di_node, + ) + } + Variants::Single { .. } + | Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, .. } => { + bug!( + "Encountered generator with non-direct-tag layout: {:?}", + generator_type_and_layout + ) + } + }, + NO_GENERICS, + ) +} + +fn build_single_variant_union_fields<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + enum_adt_def: AdtDef<'tcx>, + enum_type_and_layout: TyAndLayout<'tcx>, + enum_type_di_node: &'ll DIType, + variant_index: VariantIdx, +) -> SmallVec<&'ll DIType> { + let variant_layout = enum_type_and_layout.for_variant(cx, variant_index); + let variant_struct_type_di_node = super::build_enum_variant_struct_type_di_node( + cx, + enum_type_and_layout.ty, + enum_type_di_node, + variant_index, + enum_adt_def.variant(variant_index), + variant_layout, + ); + + // NOTE: The field name of the union is the same as the variant name, not "variant0". + let variant_name = enum_adt_def.variant(variant_index).name.as_str(); + + smallvec![build_field_di_node( + cx, + enum_type_di_node, + variant_name, + // NOTE: We use the size and align of the entire type, not from variant_layout + // since the later is sometimes smaller (if it has fewer fields). + size_and_align_of(enum_type_and_layout), + Size::ZERO, + DIFlags::FlagZero, + variant_struct_type_di_node, + )] +} + +fn build_union_fields_for_direct_tag_enum<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + enum_adt_def: AdtDef<'tcx>, + enum_type_and_layout: TyAndLayout<'tcx>, + enum_type_di_node: &'ll DIType, + variant_indices: &mut dyn Iterator<Item = VariantIdx>, + tag_field: usize, +) -> SmallVec<&'ll DIType> { + let variant_field_infos: SmallVec<VariantFieldInfo<'ll>> = variant_indices + .map(|variant_index| { + let variant_layout = enum_type_and_layout.for_variant(cx, variant_index); + + VariantFieldInfo { + variant_index, + variant_struct_type_di_node: super::build_enum_variant_struct_type_di_node( + cx, + enum_type_and_layout.ty, + enum_type_di_node, + variant_index, + enum_adt_def.variant(variant_index), + variant_layout, + ), + source_info: None, + } + }) + .collect(); + + let discr_type_name = cx.tcx.item_name(enum_adt_def.did()); + let tag_base_type = super::tag_base_type(cx, enum_type_and_layout); + let discr_type_di_node = super::build_enumeration_type_di_node( + cx, + discr_type_name.as_str(), + tag_base_type, + &mut enum_adt_def.discriminants(cx.tcx).map(|(variant_index, discr)| { + (discr, Cow::from(enum_adt_def.variant(variant_index).name.as_str())) + }), + enum_type_di_node, + ); + + build_union_fields_for_direct_tag_enum_or_generator( + cx, + enum_type_and_layout, + enum_type_di_node, + &variant_field_infos, + discr_type_di_node, + tag_field, + ) +} + +fn build_union_fields_for_niche_tag_enum<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + enum_adt_def: AdtDef<'tcx>, + enum_type_and_layout: TyAndLayout<'tcx>, + enum_type_di_node: &'ll DIType, + dataful_variant_index: VariantIdx, + variant_indices: &mut dyn Iterator<Item = VariantIdx>, + tag_field: usize, +) -> SmallVec<&'ll DIType> { + let dataful_variant_struct_type_di_node = super::build_enum_variant_struct_type_di_node( + cx, + enum_type_and_layout.ty, + enum_type_di_node, + dataful_variant_index, + &enum_adt_def.variant(dataful_variant_index), + enum_type_and_layout.for_variant(cx, dataful_variant_index), + ); + + let tag_base_type = super::tag_base_type(cx, enum_type_and_layout); + // Create an DW_TAG_enumerator for each variant except the dataful one. + let discr_type_di_node = super::build_enumeration_type_di_node( + cx, + "Discriminant$", + tag_base_type, + &mut variant_indices.filter_map(|variant_index| { + if let Some(discr_val) = + super::compute_discriminant_value(cx, enum_type_and_layout, variant_index) + { + let discr = Discr { val: discr_val as u128, ty: tag_base_type }; + let variant_name = Cow::from(enum_adt_def.variant(variant_index).name.as_str()); + Some((discr, variant_name)) + } else { + debug_assert_eq!(variant_index, dataful_variant_index); + None + } + }), + enum_type_di_node, + ); + + smallvec![ + build_field_di_node( + cx, + enum_type_di_node, + "dataful_variant", + size_and_align_of(enum_type_and_layout), + Size::ZERO, + DIFlags::FlagZero, + dataful_variant_struct_type_di_node, + ), + build_field_di_node( + cx, + enum_type_di_node, + "discriminant", + cx.size_and_align_of(tag_base_type), + enum_type_and_layout.fields.offset(tag_field), + DIFlags::FlagZero, + discr_type_di_node, + ), + ] +} + +fn build_union_fields_for_direct_tag_generator<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + generator_type_and_layout: TyAndLayout<'tcx>, + generator_type_di_node: &'ll DIType, +) -> SmallVec<&'ll DIType> { + let Variants::Multiple { tag_encoding: TagEncoding::Direct, tag_field, .. } = generator_type_and_layout.variants else { + bug!("This function only supports layouts with directly encoded tags.") + }; + + let (generator_def_id, generator_substs) = match generator_type_and_layout.ty.kind() { + &ty::Generator(def_id, substs, _) => (def_id, substs.as_generator()), + _ => unreachable!(), + }; + + let (generator_layout, state_specific_upvar_names) = + generator_layout_and_saved_local_names(cx.tcx, generator_def_id); + + let common_upvar_names = closure_saved_names_of_captured_variables(cx.tcx, generator_def_id); + let variant_range = generator_substs.variant_range(generator_def_id, cx.tcx); + + // Build the type node for each field. + let variant_field_infos: SmallVec<VariantFieldInfo<'ll>> = variant_range + .map(|variant_index| { + let variant_struct_type_di_node = super::build_generator_variant_struct_type_di_node( + cx, + variant_index, + generator_type_and_layout, + generator_type_di_node, + generator_layout, + &state_specific_upvar_names, + &common_upvar_names, + ); + + let span = generator_layout.variant_source_info[variant_index].span; + let source_info = if !span.is_dummy() { + let loc = cx.lookup_debug_loc(span.lo()); + Some((file_metadata(cx, &loc.file), loc.line as c_uint)) + } else { + None + }; + + VariantFieldInfo { variant_index, variant_struct_type_di_node, source_info } + }) + .collect(); + + let tag_base_type = tag_base_type(cx, generator_type_and_layout); + let discr_type_name = "Discriminant$"; + let discr_type_di_node = super::build_enumeration_type_di_node( + cx, + discr_type_name, + tag_base_type, + &mut generator_substs + .discriminants(generator_def_id, cx.tcx) + .map(|(variant_index, discr)| (discr, GeneratorSubsts::variant_name(variant_index))), + generator_type_di_node, + ); + + build_union_fields_for_direct_tag_enum_or_generator( + cx, + generator_type_and_layout, + generator_type_di_node, + &variant_field_infos[..], + discr_type_di_node, + tag_field, + ) +} + +/// This is a helper function shared between enums and generators that makes sure fields have the +/// expect names. +fn build_union_fields_for_direct_tag_enum_or_generator<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + enum_type_and_layout: TyAndLayout<'tcx>, + enum_type_di_node: &'ll DIType, + variant_field_infos: &[VariantFieldInfo<'ll>], + discr_type_di_node: &'ll DIType, + tag_field: usize, +) -> SmallVec<&'ll DIType> { + let mut unions_fields = SmallVec::with_capacity(variant_field_infos.len() + 1); + + // We create a field in the union for each variant ... + unions_fields.extend(variant_field_infos.into_iter().map(|variant_member_info| { + let (file_di_node, line_number) = variant_member_info + .source_info + .unwrap_or_else(|| (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER)); + + let field_name = variant_union_field_name(variant_member_info.variant_index); + let (size, align) = size_and_align_of(enum_type_and_layout); + + // We use LLVMRustDIBuilderCreateMemberType() member type directly because + // the build_field_di_node() function does not support specifying a source location, + // which is something that we don't do anywhere else. + unsafe { + llvm::LLVMRustDIBuilderCreateMemberType( + DIB(cx), + enum_type_di_node, + field_name.as_ptr().cast(), + field_name.len(), + file_di_node, + line_number, + // NOTE: We use the size and align of the entire type, not from variant_layout + // since the later is sometimes smaller (if it has fewer fields). + size.bits(), + align.bits() as u32, + // Union fields are always at offset zero + Size::ZERO.bits(), + DIFlags::FlagZero, + variant_member_info.variant_struct_type_di_node, + ) + } + })); + + debug_assert_eq!( + cx.size_and_align_of(enum_type_and_layout.field(cx, tag_field).ty), + cx.size_and_align_of(super::tag_base_type(cx, enum_type_and_layout)) + ); + + // ... and a field for the discriminant. + unions_fields.push(build_field_di_node( + cx, + enum_type_di_node, + "discriminant", + cx.size_and_align_of(enum_type_and_layout.field(cx, tag_field).ty), + enum_type_and_layout.fields.offset(tag_field), + DIFlags::FlagZero, + discr_type_di_node, + )); + + unions_fields +} + +/// Information about a single field of the top-level DW_TAG_union_type. +struct VariantFieldInfo<'ll> { + variant_index: VariantIdx, + variant_struct_type_di_node: &'ll DIType, + source_info: Option<(&'ll DIFile, c_uint)>, +} + +fn variant_union_field_name(variant_index: VariantIdx) -> Cow<'static, str> { + const PRE_ALLOCATED: [&str; 16] = [ + "variant0", + "variant1", + "variant2", + "variant3", + "variant4", + "variant5", + "variant6", + "variant7", + "variant8", + "variant9", + "variant10", + "variant11", + "variant12", + "variant13", + "variant14", + "variant15", + ]; + + PRE_ALLOCATED + .get(variant_index.as_usize()) + .map(|&s| Cow::from(s)) + .unwrap_or_else(|| format!("variant{}", variant_index.as_usize()).into()) +} diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs new file mode 100644 index 000000000..73e01d045 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs @@ -0,0 +1,437 @@ +use rustc_codegen_ssa::debuginfo::{ + type_names::{compute_debuginfo_type_name, cpp_like_debuginfo}, + wants_c_like_enum_debuginfo, +}; +use rustc_hir::def::CtorKind; +use rustc_index::vec::IndexVec; +use rustc_middle::{ + bug, + mir::{Field, GeneratorLayout, GeneratorSavedLocal}, + ty::{ + self, + layout::{IntegerExt, LayoutOf, PrimitiveExt, TyAndLayout}, + util::Discr, + AdtDef, GeneratorSubsts, Ty, VariantDef, + }, +}; +use rustc_span::Symbol; +use rustc_target::abi::{HasDataLayout, Integer, Primitive, TagEncoding, VariantIdx, Variants}; +use std::borrow::Cow; + +use crate::{ + common::CodegenCx, + debuginfo::{ + metadata::{ + build_field_di_node, build_generic_type_param_di_nodes, type_di_node, + type_map::{self, Stub}, + unknown_file_metadata, UNKNOWN_LINE_NUMBER, + }, + utils::{create_DIArray, get_namespace_for_item, DIB}, + }, + llvm::{ + self, + debuginfo::{DIFlags, DIType}, + }, +}; + +use super::{ + size_and_align_of, + type_map::{DINodeCreationResult, UniqueTypeId}, + SmallVec, +}; + +mod cpp_like; +mod native; + +pub(super) fn build_enum_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + let enum_type = unique_type_id.expect_ty(); + let &ty::Adt(enum_adt_def, _) = enum_type.kind() else { + bug!("build_enum_type_di_node() called with non-enum type: `{:?}`", enum_type) + }; + + let enum_type_and_layout = cx.layout_of(enum_type); + + if wants_c_like_enum_debuginfo(enum_type_and_layout) { + return build_c_style_enum_di_node(cx, enum_adt_def, enum_type_and_layout); + } + + if cpp_like_debuginfo(cx.tcx) { + cpp_like::build_enum_type_di_node(cx, unique_type_id) + } else { + native::build_enum_type_di_node(cx, unique_type_id) + } +} + +pub(super) fn build_generator_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + if cpp_like_debuginfo(cx.tcx) { + cpp_like::build_generator_di_node(cx, unique_type_id) + } else { + native::build_generator_di_node(cx, unique_type_id) + } +} + +/// Build the debuginfo node for a C-style enum, i.e. an enum the variants of which have no fields. +/// +/// The resulting debuginfo will be a DW_TAG_enumeration_type. +fn build_c_style_enum_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + enum_adt_def: AdtDef<'tcx>, + enum_type_and_layout: TyAndLayout<'tcx>, +) -> DINodeCreationResult<'ll> { + let containing_scope = get_namespace_for_item(cx, enum_adt_def.did()); + DINodeCreationResult { + di_node: build_enumeration_type_di_node( + cx, + &compute_debuginfo_type_name(cx.tcx, enum_type_and_layout.ty, false), + tag_base_type(cx, enum_type_and_layout), + &mut enum_adt_def.discriminants(cx.tcx).map(|(variant_index, discr)| { + (discr, Cow::from(enum_adt_def.variant(variant_index).name.as_str())) + }), + containing_scope, + ), + already_stored_in_typemap: false, + } +} + +/// Extract the type with which we want to describe the tag of the given enum or generator. +fn tag_base_type<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + enum_type_and_layout: TyAndLayout<'tcx>, +) -> Ty<'tcx> { + debug_assert!(match enum_type_and_layout.ty.kind() { + ty::Generator(..) => true, + ty::Adt(adt_def, _) => adt_def.is_enum(), + _ => false, + }); + + match enum_type_and_layout.layout.variants() { + // A single-variant enum has no discriminant. + Variants::Single { .. } => { + bug!("tag_base_type() called for enum without tag: {:?}", enum_type_and_layout) + } + + Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, tag, .. } => { + // Niche tags are always normalized to unsized integers of the correct size. + match tag.primitive() { + Primitive::Int(t, _) => t, + Primitive::F32 => Integer::I32, + Primitive::F64 => Integer::I64, + Primitive::Pointer => { + // If the niche is the NULL value of a reference, then `discr_enum_ty` will be + // a RawPtr. CodeView doesn't know what to do with enums whose base type is a + // pointer so we fix this up to just be `usize`. + // DWARF might be able to deal with this but with an integer type we are on + // the safe side there too. + cx.data_layout().ptr_sized_integer() + } + } + .to_ty(cx.tcx, false) + } + + Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, .. } => { + // Direct tags preserve the sign. + tag.primitive().to_ty(cx.tcx) + } + } +} + +/// Build a DW_TAG_enumeration_type debuginfo node, with the given base type and variants. +/// This is a helper function and does not register anything in the type map by itself. +/// +/// `variants` is an iterator of (discr-value, variant-name). +/// +// NOTE: Handling of discriminant values is somewhat inconsistent. They can appear as u128, +// u64, and i64. Here everything gets mapped to i64 because that's what LLVM's API expects. +fn build_enumeration_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + type_name: &str, + base_type: Ty<'tcx>, + variants: &mut dyn Iterator<Item = (Discr<'tcx>, Cow<'tcx, str>)>, + containing_scope: &'ll DIType, +) -> &'ll DIType { + let is_unsigned = match base_type.kind() { + ty::Int(_) => false, + ty::Uint(_) => true, + _ => bug!("build_enumeration_type_di_node() called with non-integer tag type."), + }; + + let enumerator_di_nodes: SmallVec<Option<&'ll DIType>> = variants + .map(|(discr, variant_name)| { + unsafe { + Some(llvm::LLVMRustDIBuilderCreateEnumerator( + DIB(cx), + variant_name.as_ptr().cast(), + variant_name.len(), + // FIXME: what if enumeration has i128 discriminant? + discr.val as i64, + is_unsigned, + )) + } + }) + .collect(); + + let (size, align) = cx.size_and_align_of(base_type); + + unsafe { + llvm::LLVMRustDIBuilderCreateEnumerationType( + DIB(cx), + containing_scope, + type_name.as_ptr().cast(), + type_name.len(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + size.bits(), + align.bits() as u32, + create_DIArray(DIB(cx), &enumerator_di_nodes[..]), + type_di_node(cx, base_type), + true, + ) + } +} + +/// Build the debuginfo node for the struct type describing a single variant of an enum. +/// +/// ```txt +/// DW_TAG_structure_type (top-level type for enum) +/// DW_TAG_variant_part (variant part) +/// DW_AT_discr (reference to discriminant DW_TAG_member) +/// DW_TAG_member (discriminant member) +/// DW_TAG_variant (variant 1) +/// DW_TAG_variant (variant 2) +/// DW_TAG_variant (variant 3) +/// ---> DW_TAG_structure_type (type of variant 1) +/// ---> DW_TAG_structure_type (type of variant 2) +/// ---> DW_TAG_structure_type (type of variant 3) +/// ``` +/// +/// In CPP-like mode, we have the exact same descriptions for each variant too: +/// +/// ```txt +/// DW_TAG_union_type (top-level type for enum) +/// DW_TAG_member (member for variant 1) +/// DW_TAG_member (member for variant 2) +/// DW_TAG_member (member for variant 3) +/// ---> DW_TAG_structure_type (type of variant 1) +/// ---> DW_TAG_structure_type (type of variant 2) +/// ---> DW_TAG_structure_type (type of variant 3) +/// DW_TAG_enumeration_type (type of tag) +/// ``` +/// +/// The node looks like: +/// +/// ```txt +/// DW_TAG_structure_type +/// DW_AT_name <name-of-variant> +/// DW_AT_byte_size 0x00000010 +/// DW_AT_alignment 0x00000008 +/// DW_TAG_member +/// DW_AT_name <name-of-field-0> +/// DW_AT_type <0x0000018e> +/// DW_AT_alignment 0x00000004 +/// DW_AT_data_member_location 4 +/// DW_TAG_member +/// DW_AT_name <name-of-field-1> +/// DW_AT_type <0x00000195> +/// DW_AT_alignment 0x00000008 +/// DW_AT_data_member_location 8 +/// ... +/// ``` +/// +/// The type of a variant is always a struct type with the name of the variant +/// and a DW_TAG_member for each field (but not the discriminant). +fn build_enum_variant_struct_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + enum_type: Ty<'tcx>, + enum_type_di_node: &'ll DIType, + variant_index: VariantIdx, + variant_def: &VariantDef, + variant_layout: TyAndLayout<'tcx>, +) -> &'ll DIType { + debug_assert_eq!(variant_layout.ty, enum_type); + + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + Stub::Struct, + UniqueTypeId::for_enum_variant_struct_type(cx.tcx, enum_type, variant_index), + variant_def.name.as_str(), + // NOTE: We use size and align of enum_type, not from variant_layout: + cx.size_and_align_of(enum_type), + Some(enum_type_di_node), + DIFlags::FlagZero, + ), + |cx, struct_type_di_node| { + (0..variant_layout.fields.count()) + .map(|field_index| { + let field_name = if variant_def.ctor_kind != CtorKind::Fn { + // Fields have names + Cow::from(variant_def.fields[field_index].name.as_str()) + } else { + // Tuple-like + super::tuple_field_name(field_index) + }; + + let field_layout = variant_layout.field(cx, field_index); + + build_field_di_node( + cx, + struct_type_di_node, + &field_name, + (field_layout.size, field_layout.align.abi), + variant_layout.fields.offset(field_index), + DIFlags::FlagZero, + type_di_node(cx, field_layout.ty), + ) + }) + .collect() + }, + |cx| build_generic_type_param_di_nodes(cx, enum_type), + ) + .di_node +} + +/// Build the struct type for describing a single generator state. +/// See [build_generator_variant_struct_type_di_node]. +/// +/// ```txt +/// +/// DW_TAG_structure_type (top-level type for enum) +/// DW_TAG_variant_part (variant part) +/// DW_AT_discr (reference to discriminant DW_TAG_member) +/// DW_TAG_member (discriminant member) +/// DW_TAG_variant (variant 1) +/// DW_TAG_variant (variant 2) +/// DW_TAG_variant (variant 3) +/// ---> DW_TAG_structure_type (type of variant 1) +/// ---> DW_TAG_structure_type (type of variant 2) +/// ---> DW_TAG_structure_type (type of variant 3) +/// +/// ``` +pub fn build_generator_variant_struct_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + variant_index: VariantIdx, + generator_type_and_layout: TyAndLayout<'tcx>, + generator_type_di_node: &'ll DIType, + generator_layout: &GeneratorLayout<'tcx>, + state_specific_upvar_names: &IndexVec<GeneratorSavedLocal, Option<Symbol>>, + common_upvar_names: &[String], +) -> &'ll DIType { + let variant_name = GeneratorSubsts::variant_name(variant_index); + let unique_type_id = UniqueTypeId::for_enum_variant_struct_type( + cx.tcx, + generator_type_and_layout.ty, + variant_index, + ); + + let variant_layout = generator_type_and_layout.for_variant(cx, variant_index); + + let generator_substs = match generator_type_and_layout.ty.kind() { + ty::Generator(_, substs, _) => substs.as_generator(), + _ => unreachable!(), + }; + + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + Stub::Struct, + unique_type_id, + &variant_name, + size_and_align_of(generator_type_and_layout), + Some(generator_type_di_node), + DIFlags::FlagZero, + ), + |cx, variant_struct_type_di_node| { + // Fields that just belong to this variant/state + let state_specific_fields: SmallVec<_> = (0..variant_layout.fields.count()) + .map(|field_index| { + let generator_saved_local = generator_layout.variant_fields[variant_index] + [Field::from_usize(field_index)]; + let field_name_maybe = state_specific_upvar_names[generator_saved_local]; + let field_name = field_name_maybe + .as_ref() + .map(|s| Cow::from(s.as_str())) + .unwrap_or_else(|| super::tuple_field_name(field_index)); + + let field_type = variant_layout.field(cx, field_index).ty; + + build_field_di_node( + cx, + variant_struct_type_di_node, + &field_name, + cx.size_and_align_of(field_type), + variant_layout.fields.offset(field_index), + DIFlags::FlagZero, + type_di_node(cx, field_type), + ) + }) + .collect(); + + // Fields that are common to all states + let common_fields: SmallVec<_> = generator_substs + .prefix_tys() + .enumerate() + .map(|(index, upvar_ty)| { + build_field_di_node( + cx, + variant_struct_type_di_node, + &common_upvar_names[index], + cx.size_and_align_of(upvar_ty), + generator_type_and_layout.fields.offset(index), + DIFlags::FlagZero, + type_di_node(cx, upvar_ty), + ) + }) + .collect(); + + state_specific_fields.into_iter().chain(common_fields.into_iter()).collect() + }, + |cx| build_generic_type_param_di_nodes(cx, generator_type_and_layout.ty), + ) + .di_node +} + +/// Returns the discriminant value corresponding to the variant index. +/// +/// Will return `None` if there is less than two variants (because then the enum won't have) +/// a tag, and if this is the dataful variant of a niche-layout enum (because then there is no +/// single discriminant value). +fn compute_discriminant_value<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + enum_type_and_layout: TyAndLayout<'tcx>, + variant_index: VariantIdx, +) -> Option<u64> { + match enum_type_and_layout.layout.variants() { + &Variants::Single { .. } => None, + &Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => Some( + enum_type_and_layout.ty.discriminant_for_variant(cx.tcx, variant_index).unwrap().val + as u64, + ), + &Variants::Multiple { + tag_encoding: TagEncoding::Niche { ref niche_variants, niche_start, dataful_variant }, + tag, + .. + } => { + if variant_index == dataful_variant { + None + } else { + let value = (variant_index.as_u32() as u128) + .wrapping_sub(niche_variants.start().as_u32() as u128) + .wrapping_add(niche_start); + let value = tag.size(cx).truncate(value); + // NOTE(eddyb) do *NOT* remove this assert, until + // we pass the full 128-bit value to LLVM, otherwise + // truncation will be silent and remain undetected. + assert_eq!(value as u64 as u128, value); + Some(value as u64) + } + } + } +} diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs new file mode 100644 index 000000000..f1935e0ec --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs @@ -0,0 +1,441 @@ +use std::borrow::Cow; + +use crate::{ + common::CodegenCx, + debuginfo::{ + metadata::{ + closure_saved_names_of_captured_variables, + enums::tag_base_type, + file_metadata, generator_layout_and_saved_local_names, size_and_align_of, type_di_node, + type_map::{self, Stub, StubInfo, UniqueTypeId}, + unknown_file_metadata, DINodeCreationResult, SmallVec, NO_GENERICS, + UNKNOWN_LINE_NUMBER, + }, + utils::{create_DIArray, get_namespace_for_item, DIB}, + }, + llvm::{ + self, + debuginfo::{DIFile, DIFlags, DIType}, + }, +}; +use libc::c_uint; +use rustc_codegen_ssa::{ + debuginfo::{type_names::compute_debuginfo_type_name, wants_c_like_enum_debuginfo}, + traits::ConstMethods, +}; +use rustc_middle::{ + bug, + ty::{ + self, + layout::{LayoutOf, TyAndLayout}, + }, +}; +use rustc_target::abi::{Size, TagEncoding, VariantIdx, Variants}; +use smallvec::smallvec; + +/// Build the debuginfo node for an enum type. The listing below shows how such a +/// type looks like at the LLVM IR/DWARF level. It is a `DW_TAG_structure_type` +/// with a single `DW_TAG_variant_part` that in turn contains a `DW_TAG_variant` +/// for each variant of the enum. The variant-part also contains a single member +/// describing the discriminant, and a nested struct type for each of the variants. +/// +/// ```txt +/// ---> DW_TAG_structure_type (top-level type for enum) +/// DW_TAG_variant_part (variant part) +/// DW_AT_discr (reference to discriminant DW_TAG_member) +/// DW_TAG_member (discriminant member) +/// DW_TAG_variant (variant 1) +/// DW_TAG_variant (variant 2) +/// DW_TAG_variant (variant 3) +/// DW_TAG_structure_type (type of variant 1) +/// DW_TAG_structure_type (type of variant 2) +/// DW_TAG_structure_type (type of variant 3) +/// ``` +pub(super) fn build_enum_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + let enum_type = unique_type_id.expect_ty(); + let &ty::Adt(enum_adt_def, _) = enum_type.kind() else { + bug!("build_enum_type_di_node() called with non-enum type: `{:?}`", enum_type) + }; + + let containing_scope = get_namespace_for_item(cx, enum_adt_def.did()); + let enum_type_and_layout = cx.layout_of(enum_type); + let enum_type_name = compute_debuginfo_type_name(cx.tcx, enum_type, false); + + debug_assert!(!wants_c_like_enum_debuginfo(enum_type_and_layout)); + + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + Stub::Struct, + unique_type_id, + &enum_type_name, + size_and_align_of(enum_type_and_layout), + Some(containing_scope), + DIFlags::FlagZero, + ), + |cx, enum_type_di_node| { + // Build the struct type for each variant. These will be referenced by the + // DW_TAG_variant DIEs inside of the DW_TAG_variant_part DIE. + // We also called the names for the corresponding DW_TAG_variant DIEs here. + let variant_member_infos: SmallVec<_> = enum_adt_def + .variant_range() + .map(|variant_index| VariantMemberInfo { + variant_index, + variant_name: Cow::from(enum_adt_def.variant(variant_index).name.as_str()), + variant_struct_type_di_node: super::build_enum_variant_struct_type_di_node( + cx, + enum_type, + enum_type_di_node, + variant_index, + enum_adt_def.variant(variant_index), + enum_type_and_layout.for_variant(cx, variant_index), + ), + source_info: None, + }) + .collect(); + + smallvec![build_enum_variant_part_di_node( + cx, + enum_type_and_layout, + enum_type_di_node, + &variant_member_infos[..], + )] + }, + // We don't seem to be emitting generic args on the enum type, it seems. Rather + // they get attached to the struct type of each variant. + NO_GENERICS, + ) +} + +/// Build the debuginfo node for a generator environment. It looks the same as the debuginfo for +/// an enum. See [build_enum_type_di_node] for more information. +/// +/// ```txt +/// +/// ---> DW_TAG_structure_type (top-level type for the generator) +/// DW_TAG_variant_part (variant part) +/// DW_AT_discr (reference to discriminant DW_TAG_member) +/// DW_TAG_member (discriminant member) +/// DW_TAG_variant (variant 1) +/// DW_TAG_variant (variant 2) +/// DW_TAG_variant (variant 3) +/// DW_TAG_structure_type (type of variant 1) +/// DW_TAG_structure_type (type of variant 2) +/// DW_TAG_structure_type (type of variant 3) +/// +/// ``` +pub(super) fn build_generator_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + let generator_type = unique_type_id.expect_ty(); + let &ty::Generator(generator_def_id, _, _ ) = generator_type.kind() else { + bug!("build_generator_di_node() called with non-generator type: `{:?}`", generator_type) + }; + + let containing_scope = get_namespace_for_item(cx, generator_def_id); + let generator_type_and_layout = cx.layout_of(generator_type); + + debug_assert!(!wants_c_like_enum_debuginfo(generator_type_and_layout)); + + let generator_type_name = compute_debuginfo_type_name(cx.tcx, generator_type, false); + + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + Stub::Struct, + unique_type_id, + &generator_type_name, + size_and_align_of(generator_type_and_layout), + Some(containing_scope), + DIFlags::FlagZero, + ), + |cx, generator_type_di_node| { + let (generator_layout, state_specific_upvar_names) = + generator_layout_and_saved_local_names(cx.tcx, generator_def_id); + + let Variants::Multiple { tag_encoding: TagEncoding::Direct, ref variants, .. } = generator_type_and_layout.variants else { + bug!( + "Encountered generator with non-direct-tag layout: {:?}", + generator_type_and_layout + ) + }; + + let common_upvar_names = + closure_saved_names_of_captured_variables(cx.tcx, generator_def_id); + + // Build variant struct types + let variant_struct_type_di_nodes: SmallVec<_> = variants + .indices() + .map(|variant_index| { + // FIXME: This is problematic because just a number is not a valid identifier. + // GeneratorSubsts::variant_name(variant_index), would be consistent + // with enums? + let variant_name = format!("{}", variant_index.as_usize()).into(); + + let span = generator_layout.variant_source_info[variant_index].span; + let source_info = if !span.is_dummy() { + let loc = cx.lookup_debug_loc(span.lo()); + Some((file_metadata(cx, &loc.file), loc.line)) + } else { + None + }; + + VariantMemberInfo { + variant_index, + variant_name, + variant_struct_type_di_node: + super::build_generator_variant_struct_type_di_node( + cx, + variant_index, + generator_type_and_layout, + generator_type_di_node, + generator_layout, + &state_specific_upvar_names, + &common_upvar_names, + ), + source_info, + } + }) + .collect(); + + smallvec![build_enum_variant_part_di_node( + cx, + generator_type_and_layout, + generator_type_di_node, + &variant_struct_type_di_nodes[..], + )] + }, + // We don't seem to be emitting generic args on the generator type, it seems. Rather + // they get attached to the struct type of each variant. + NO_GENERICS, + ) +} + +/// Builds the DW_TAG_variant_part of an enum or generator debuginfo node: +/// +/// ```txt +/// DW_TAG_structure_type (top-level type for enum) +/// ---> DW_TAG_variant_part (variant part) +/// DW_AT_discr (reference to discriminant DW_TAG_member) +/// DW_TAG_member (discriminant member) +/// DW_TAG_variant (variant 1) +/// DW_TAG_variant (variant 2) +/// DW_TAG_variant (variant 3) +/// DW_TAG_structure_type (type of variant 1) +/// DW_TAG_structure_type (type of variant 2) +/// DW_TAG_structure_type (type of variant 3) +/// ``` +fn build_enum_variant_part_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + enum_type_and_layout: TyAndLayout<'tcx>, + enum_type_di_node: &'ll DIType, + variant_member_infos: &[VariantMemberInfo<'_, 'll>], +) -> &'ll DIType { + let tag_member_di_node = + build_discr_member_di_node(cx, enum_type_and_layout, enum_type_di_node); + + let variant_part_unique_type_id = + UniqueTypeId::for_enum_variant_part(cx.tcx, enum_type_and_layout.ty); + + let stub = StubInfo::new( + cx, + variant_part_unique_type_id, + |cx, variant_part_unique_type_id_str| unsafe { + let variant_part_name = ""; + llvm::LLVMRustDIBuilderCreateVariantPart( + DIB(cx), + enum_type_di_node, + variant_part_name.as_ptr().cast(), + variant_part_name.len(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + enum_type_and_layout.size.bits(), + enum_type_and_layout.align.abi.bits() as u32, + DIFlags::FlagZero, + tag_member_di_node, + create_DIArray(DIB(cx), &[]), + variant_part_unique_type_id_str.as_ptr().cast(), + variant_part_unique_type_id_str.len(), + ) + }, + ); + + type_map::build_type_with_children( + cx, + stub, + |cx, variant_part_di_node| { + variant_member_infos + .iter() + .map(|variant_member_info| { + build_enum_variant_member_di_node( + cx, + enum_type_and_layout, + variant_part_di_node, + variant_member_info, + ) + }) + .collect() + }, + NO_GENERICS, + ) + .di_node +} + +/// Builds the DW_TAG_member describing where we can find the tag of an enum. +/// Returns `None` if the enum does not have a tag. +/// +/// ```txt +/// +/// DW_TAG_structure_type (top-level type for enum) +/// DW_TAG_variant_part (variant part) +/// DW_AT_discr (reference to discriminant DW_TAG_member) +/// ---> DW_TAG_member (discriminant member) +/// DW_TAG_variant (variant 1) +/// DW_TAG_variant (variant 2) +/// DW_TAG_variant (variant 3) +/// DW_TAG_structure_type (type of variant 1) +/// DW_TAG_structure_type (type of variant 2) +/// DW_TAG_structure_type (type of variant 3) +/// +/// ``` +fn build_discr_member_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + enum_or_generator_type_and_layout: TyAndLayout<'tcx>, + enum_or_generator_type_di_node: &'ll DIType, +) -> Option<&'ll DIType> { + let tag_name = match enum_or_generator_type_and_layout.ty.kind() { + ty::Generator(..) => "__state", + _ => "", + }; + + // NOTE: This is actually wrong. This will become a member of + // of the DW_TAG_variant_part. But, due to LLVM's API, that + // can only be constructed with this DW_TAG_member already in created. + // In LLVM IR the wrong scope will be listed but when DWARF is + // generated from it, the DW_TAG_member will be a child the + // DW_TAG_variant_part. + let containing_scope = enum_or_generator_type_di_node; + + match enum_or_generator_type_and_layout.layout.variants() { + // A single-variant enum has no discriminant. + &Variants::Single { .. } => None, + + &Variants::Multiple { tag_field, .. } => { + let tag_base_type = tag_base_type(cx, enum_or_generator_type_and_layout); + let (size, align) = cx.size_and_align_of(tag_base_type); + + unsafe { + Some(llvm::LLVMRustDIBuilderCreateMemberType( + DIB(cx), + containing_scope, + tag_name.as_ptr().cast(), + tag_name.len(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + size.bits(), + align.bits() as u32, + enum_or_generator_type_and_layout.fields.offset(tag_field).bits(), + DIFlags::FlagArtificial, + type_di_node(cx, tag_base_type), + )) + } + } + } +} + +/// Build the debuginfo node for `DW_TAG_variant`: +/// +/// ```txt +/// DW_TAG_structure_type (top-level type for enum) +/// DW_TAG_variant_part (variant part) +/// DW_AT_discr (reference to discriminant DW_TAG_member) +/// DW_TAG_member (discriminant member) +/// ---> DW_TAG_variant (variant 1) +/// ---> DW_TAG_variant (variant 2) +/// ---> DW_TAG_variant (variant 3) +/// DW_TAG_structure_type (type of variant 1) +/// DW_TAG_structure_type (type of variant 2) +/// DW_TAG_structure_type (type of variant 3) +/// ``` +/// +/// This node looks like: +/// +/// ```txt +/// DW_TAG_variant +/// DW_AT_discr_value 0 +/// DW_TAG_member +/// DW_AT_name None +/// DW_AT_type <0x000002a1> +/// DW_AT_alignment 0x00000002 +/// DW_AT_data_member_location 0 +/// ``` +/// +/// The DW_AT_discr_value is optional, and is omitted if +/// - This is the only variant of a univariant enum (i.e. their is no discriminant) +/// - This is the "dataful" variant of a niche-layout enum +/// (where only the other variants are identified by a single value) +/// +/// There is only ever a single member, the type of which is a struct that describes the +/// fields of the variant (excluding the discriminant). The name of the member is the name +/// of the variant as given in the source code. The DW_AT_data_member_location is always +/// zero. +/// +/// Note that the LLVM DIBuilder API is a bit unintuitive here. The DW_TAG_variant subtree +/// (including the DW_TAG_member) is built by a single call to +/// `LLVMRustDIBuilderCreateVariantMemberType()`. +fn build_enum_variant_member_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + enum_type_and_layout: TyAndLayout<'tcx>, + variant_part_di_node: &'ll DIType, + variant_member_info: &VariantMemberInfo<'_, 'll>, +) -> &'ll DIType { + let variant_index = variant_member_info.variant_index; + let discr_value = super::compute_discriminant_value(cx, enum_type_and_layout, variant_index); + + let (file_di_node, line_number) = variant_member_info + .source_info + .unwrap_or_else(|| (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER)); + + unsafe { + llvm::LLVMRustDIBuilderCreateVariantMemberType( + DIB(cx), + variant_part_di_node, + variant_member_info.variant_name.as_ptr().cast(), + variant_member_info.variant_name.len(), + file_di_node, + line_number, + enum_type_and_layout.size.bits(), + enum_type_and_layout.align.abi.bits() as u32, + Size::ZERO.bits(), + discr_value.map(|v| cx.const_u64(v)), + DIFlags::FlagZero, + variant_member_info.variant_struct_type_di_node, + ) + } +} + +/// Information needed for building a `DW_TAG_variant`: +/// +/// ```txt +/// DW_TAG_structure_type (top-level type for enum) +/// DW_TAG_variant_part (variant part) +/// DW_AT_discr (reference to discriminant DW_TAG_member) +/// DW_TAG_member (discriminant member) +/// ---> DW_TAG_variant (variant 1) +/// ---> DW_TAG_variant (variant 2) +/// ---> DW_TAG_variant (variant 3) +/// DW_TAG_structure_type (type of variant 1) +/// DW_TAG_structure_type (type of variant 2) +/// DW_TAG_structure_type (type of variant 3) +struct VariantMemberInfo<'a, 'll> { + variant_index: VariantIdx, + variant_name: Cow<'a, str>, + variant_struct_type_di_node: &'ll DIType, + source_info: Option<(&'ll DIFile, c_uint)>, +} diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs new file mode 100644 index 000000000..ce2f419c4 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -0,0 +1,267 @@ +use std::cell::RefCell; + +use rustc_data_structures::{ + fingerprint::Fingerprint, + fx::FxHashMap, + stable_hasher::{HashStable, StableHasher}, +}; +use rustc_middle::{ + bug, + ty::{ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt}, +}; +use rustc_target::abi::{Align, Size, VariantIdx}; + +use crate::{ + common::CodegenCx, + debuginfo::utils::{create_DIArray, debug_context, DIB}, + llvm::{ + self, + debuginfo::{DIFlags, DIScope, DIType}, + }, +}; + +use super::{unknown_file_metadata, SmallVec, UNKNOWN_LINE_NUMBER}; + +mod private { + // This type cannot be constructed outside of this module because + // it has a private field. We make use of this in order to prevent + // `UniqueTypeId` from being constructed directly, without asserting + // the preconditions. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, HashStable)] + pub struct HiddenZst; +} + +/// A unique identifier for anything that we create a debuginfo node for. +/// The types it contains are expected to already be normalized (which +/// is debug_asserted in the constructors). +/// +/// Note that there are some things that only show up in debuginfo, like +/// the separate type descriptions for each enum variant. These get an ID +/// too because they have their own debuginfo node in LLVM IR. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, HashStable)] +pub(super) enum UniqueTypeId<'tcx> { + /// The ID of a regular type as it shows up at the language level. + Ty(Ty<'tcx>, private::HiddenZst), + /// The ID for the single DW_TAG_variant_part nested inside the top-level + /// DW_TAG_structure_type that describes enums and generators. + VariantPart(Ty<'tcx>, private::HiddenZst), + /// The ID for the artificial struct type describing a single enum variant. + VariantStructType(Ty<'tcx>, VariantIdx, private::HiddenZst), + /// The ID of the artificial type we create for VTables. + VTableTy(Ty<'tcx>, Option<PolyExistentialTraitRef<'tcx>>, private::HiddenZst), +} + +impl<'tcx> UniqueTypeId<'tcx> { + pub fn for_ty(tcx: TyCtxt<'tcx>, t: Ty<'tcx>) -> Self { + debug_assert_eq!(t, tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t)); + UniqueTypeId::Ty(t, private::HiddenZst) + } + + pub fn for_enum_variant_part(tcx: TyCtxt<'tcx>, enum_ty: Ty<'tcx>) -> Self { + debug_assert_eq!(enum_ty, tcx.normalize_erasing_regions(ParamEnv::reveal_all(), enum_ty)); + UniqueTypeId::VariantPart(enum_ty, private::HiddenZst) + } + + pub fn for_enum_variant_struct_type( + tcx: TyCtxt<'tcx>, + enum_ty: Ty<'tcx>, + variant_idx: VariantIdx, + ) -> Self { + debug_assert_eq!(enum_ty, tcx.normalize_erasing_regions(ParamEnv::reveal_all(), enum_ty)); + UniqueTypeId::VariantStructType(enum_ty, variant_idx, private::HiddenZst) + } + + pub fn for_vtable_ty( + tcx: TyCtxt<'tcx>, + self_type: Ty<'tcx>, + implemented_trait: Option<PolyExistentialTraitRef<'tcx>>, + ) -> Self { + debug_assert_eq!( + self_type, + tcx.normalize_erasing_regions(ParamEnv::reveal_all(), self_type) + ); + debug_assert_eq!( + implemented_trait, + tcx.normalize_erasing_regions(ParamEnv::reveal_all(), implemented_trait) + ); + UniqueTypeId::VTableTy(self_type, implemented_trait, private::HiddenZst) + } + + /// Generates a string version of this [UniqueTypeId], which can be used as the `UniqueId` + /// argument of the various `LLVMRustDIBuilderCreate*Type()` methods. + /// + /// Right now this takes the form of a hex-encoded opaque hash value. + pub fn generate_unique_id_string(self, tcx: TyCtxt<'tcx>) -> String { + let mut hasher = StableHasher::new(); + tcx.with_stable_hashing_context(|mut hcx| { + hcx.while_hashing_spans(false, |hcx| self.hash_stable(hcx, &mut hasher)) + }); + hasher.finish::<Fingerprint>().to_hex() + } + + pub fn expect_ty(self) -> Ty<'tcx> { + match self { + UniqueTypeId::Ty(ty, _) => ty, + _ => bug!("Expected `UniqueTypeId::Ty` but found `{:?}`", self), + } + } +} + +/// The `TypeMap` is where the debug context holds the type metadata nodes +/// created so far. The debuginfo nodes are identified by `UniqueTypeId`. +#[derive(Default)] +pub(crate) struct TypeMap<'ll, 'tcx> { + pub(super) unique_id_to_di_node: RefCell<FxHashMap<UniqueTypeId<'tcx>, &'ll DIType>>, +} + +impl<'ll, 'tcx> TypeMap<'ll, 'tcx> { + /// Adds a `UniqueTypeId` to metadata mapping to the `TypeMap`. The method will + /// fail if the mapping already exists. + pub(super) fn insert(&self, unique_type_id: UniqueTypeId<'tcx>, metadata: &'ll DIType) { + if self.unique_id_to_di_node.borrow_mut().insert(unique_type_id, metadata).is_some() { + bug!("type metadata for unique ID '{:?}' is already in the `TypeMap`!", unique_type_id); + } + } + + pub(super) fn di_node_for_unique_id( + &self, + unique_type_id: UniqueTypeId<'tcx>, + ) -> Option<&'ll DIType> { + self.unique_id_to_di_node.borrow().get(&unique_type_id).cloned() + } +} + +pub struct DINodeCreationResult<'ll> { + pub di_node: &'ll DIType, + pub already_stored_in_typemap: bool, +} + +impl<'ll> DINodeCreationResult<'ll> { + pub fn new(di_node: &'ll DIType, already_stored_in_typemap: bool) -> Self { + DINodeCreationResult { di_node, already_stored_in_typemap } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum Stub<'ll> { + Struct, + Union, + VTableTy { vtable_holder: &'ll DIType }, +} + +pub struct StubInfo<'ll, 'tcx> { + metadata: &'ll DIType, + unique_type_id: UniqueTypeId<'tcx>, +} + +impl<'ll, 'tcx> StubInfo<'ll, 'tcx> { + pub(super) fn new( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, + build: impl FnOnce(&CodegenCx<'ll, 'tcx>, /* unique_type_id_str: */ &str) -> &'ll DIType, + ) -> StubInfo<'ll, 'tcx> { + let unique_type_id_str = unique_type_id.generate_unique_id_string(cx.tcx); + let di_node = build(cx, &unique_type_id_str); + StubInfo { metadata: di_node, unique_type_id } + } +} + +/// Create a stub debuginfo node onto which fields and nested types can be attached. +pub(super) fn stub<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + kind: Stub<'ll>, + unique_type_id: UniqueTypeId<'tcx>, + name: &str, + (size, align): (Size, Align), + containing_scope: Option<&'ll DIScope>, + flags: DIFlags, +) -> StubInfo<'ll, 'tcx> { + let empty_array = create_DIArray(DIB(cx), &[]); + let unique_type_id_str = unique_type_id.generate_unique_id_string(cx.tcx); + + let metadata = match kind { + Stub::Struct | Stub::VTableTy { .. } => { + let vtable_holder = match kind { + Stub::VTableTy { vtable_holder } => Some(vtable_holder), + _ => None, + }; + unsafe { + llvm::LLVMRustDIBuilderCreateStructType( + DIB(cx), + containing_scope, + name.as_ptr().cast(), + name.len(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + size.bits(), + align.bits() as u32, + flags, + None, + empty_array, + 0, + vtable_holder, + unique_type_id_str.as_ptr().cast(), + unique_type_id_str.len(), + ) + } + } + Stub::Union => unsafe { + llvm::LLVMRustDIBuilderCreateUnionType( + DIB(cx), + containing_scope, + name.as_ptr().cast(), + name.len(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + size.bits(), + align.bits() as u32, + flags, + Some(empty_array), + 0, + unique_type_id_str.as_ptr().cast(), + unique_type_id_str.len(), + ) + }, + }; + StubInfo { metadata, unique_type_id } +} + +/// This function enables creating debuginfo nodes that can recursively refer to themselves. +/// It will first insert the given stub into the type map and only then execute the `members` +/// and `generics` closures passed in. These closures have access to the stub so they can +/// directly attach fields to them. If the type of a field transitively refers back +/// to the type currently being built, the stub will already be found in the type map, +/// which effectively breaks the recursion cycle. +pub(super) fn build_type_with_children<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + stub_info: StubInfo<'ll, 'tcx>, + members: impl FnOnce(&CodegenCx<'ll, 'tcx>, &'ll DIType) -> SmallVec<&'ll DIType>, + generics: impl FnOnce(&CodegenCx<'ll, 'tcx>) -> SmallVec<&'ll DIType>, +) -> DINodeCreationResult<'ll> { + debug_assert_eq!( + debug_context(cx).type_map.di_node_for_unique_id(stub_info.unique_type_id), + None + ); + + debug_context(cx).type_map.insert(stub_info.unique_type_id, stub_info.metadata); + + let members: SmallVec<_> = + members(cx, stub_info.metadata).into_iter().map(|node| Some(node)).collect(); + let generics: SmallVec<Option<&'ll DIType>> = + generics(cx).into_iter().map(|node| Some(node)).collect(); + + if !(members.is_empty() && generics.is_empty()) { + unsafe { + let members_array = create_DIArray(DIB(cx), &members[..]); + let generics_array = create_DIArray(DIB(cx), &generics[..]); + llvm::LLVMRustDICompositeTypeReplaceArrays( + DIB(cx), + stub_info.metadata, + Some(members_array), + Some(generics_array), + ); + } + } + + DINodeCreationResult { di_node: stub_info.metadata, already_stored_in_typemap: true } +} diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs new file mode 100644 index 000000000..cf591295b --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -0,0 +1,614 @@ +#![doc = include_str!("doc.md")] + +use rustc_codegen_ssa::mir::debuginfo::VariableKind::*; + +use self::metadata::{file_metadata, type_di_node}; +use self::metadata::{UNKNOWN_COLUMN_NUMBER, UNKNOWN_LINE_NUMBER}; +use self::namespace::mangled_name_of_instance; +use self::utils::{create_DIArray, is_node_local_to_unit, DIB}; + +use crate::abi::FnAbi; +use crate::builder::Builder; +use crate::common::CodegenCx; +use crate::llvm; +use crate::llvm::debuginfo::{ + DIArray, DIBuilder, DIFile, DIFlags, DILexicalBlock, DILocation, DISPFlags, DIScope, DIType, + DIVariable, +}; +use crate::value::Value; + +use rustc_codegen_ssa::debuginfo::type_names; +use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext, VariableKind}; +use rustc_codegen_ssa::traits::*; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sync::Lrc; +use rustc_hir::def_id::{DefId, DefIdMap}; +use rustc_index::vec::IndexVec; +use rustc_middle::mir; +use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; +use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TypeVisitable}; +use rustc_session::config::{self, DebugInfo}; +use rustc_session::Session; +use rustc_span::symbol::Symbol; +use rustc_span::{self, BytePos, Pos, SourceFile, SourceFileAndLine, SourceFileHash, Span}; +use rustc_target::abi::Size; + +use libc::c_uint; +use smallvec::SmallVec; +use std::cell::OnceCell; +use std::cell::RefCell; +use std::iter; +use tracing::debug; + +mod create_scope_map; +pub mod gdb; +pub mod metadata; +mod namespace; +mod utils; + +pub use self::create_scope_map::compute_mir_scopes; +pub use self::metadata::build_global_var_di_node; +pub use self::metadata::extend_scope_to_file; + +#[allow(non_upper_case_globals)] +const DW_TAG_auto_variable: c_uint = 0x100; +#[allow(non_upper_case_globals)] +const DW_TAG_arg_variable: c_uint = 0x101; + +/// A context object for maintaining all state needed by the debuginfo module. +pub struct CodegenUnitDebugContext<'ll, 'tcx> { + llcontext: &'ll llvm::Context, + llmod: &'ll llvm::Module, + builder: &'ll mut DIBuilder<'ll>, + created_files: RefCell<FxHashMap<Option<(u128, SourceFileHash)>, &'ll DIFile>>, + + type_map: metadata::TypeMap<'ll, 'tcx>, + namespace_map: RefCell<DefIdMap<&'ll DIScope>>, + recursion_marker_type: OnceCell<&'ll DIType>, +} + +impl Drop for CodegenUnitDebugContext<'_, '_> { + fn drop(&mut self) { + unsafe { + llvm::LLVMRustDIBuilderDispose(&mut *(self.builder as *mut _)); + } + } +} + +impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { + pub fn new(llmod: &'ll llvm::Module) -> Self { + debug!("CodegenUnitDebugContext::new"); + let builder = unsafe { llvm::LLVMRustDIBuilderCreate(llmod) }; + // DIBuilder inherits context from the module, so we'd better use the same one + let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) }; + CodegenUnitDebugContext { + llcontext, + llmod, + builder, + created_files: Default::default(), + type_map: Default::default(), + namespace_map: RefCell::new(Default::default()), + recursion_marker_type: OnceCell::new(), + } + } + + pub fn finalize(&self, sess: &Session) { + unsafe { + llvm::LLVMRustDIBuilderFinalize(self.builder); + + if !sess.target.is_like_msvc { + // Debuginfo generation in LLVM by default uses a higher + // version of dwarf than macOS currently understands. We can + // instruct LLVM to emit an older version of dwarf, however, + // for macOS to understand. For more info see #11352 + // This can be overridden using --llvm-opts -dwarf-version,N. + // Android has the same issue (#22398) + let dwarf_version = sess + .opts + .unstable_opts + .dwarf_version + .unwrap_or(sess.target.default_dwarf_version); + llvm::LLVMRustAddModuleFlag( + self.llmod, + llvm::LLVMModFlagBehavior::Warning, + "Dwarf Version\0".as_ptr().cast(), + dwarf_version, + ); + } else { + // Indicate that we want CodeView debug information on MSVC + llvm::LLVMRustAddModuleFlag( + self.llmod, + llvm::LLVMModFlagBehavior::Warning, + "CodeView\0".as_ptr().cast(), + 1, + ) + } + + // Prevent bitcode readers from deleting the debug info. + let ptr = "Debug Info Version\0".as_ptr(); + llvm::LLVMRustAddModuleFlag( + self.llmod, + llvm::LLVMModFlagBehavior::Warning, + ptr.cast(), + llvm::LLVMRustDebugMetadataVersion(), + ); + } + } +} + +/// Creates any deferred debug metadata nodes +pub fn finalize(cx: &CodegenCx<'_, '_>) { + if let Some(dbg_cx) = &cx.dbg_cx { + debug!("finalize"); + + if gdb::needs_gdb_debug_scripts_section(cx) { + // Add a .debug_gdb_scripts section to this compile-unit. This will + // cause GDB to try and load the gdb_load_rust_pretty_printers.py file, + // which activates the Rust pretty printers for binary this section is + // contained in. + gdb::get_or_insert_gdb_debug_scripts_section_global(cx); + } + + dbg_cx.finalize(cx.sess()); + } +} + +impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> { + // FIXME(eddyb) find a common convention for all of the debuginfo-related + // names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.). + fn dbg_var_addr( + &mut self, + dbg_var: &'ll DIVariable, + dbg_loc: &'ll DILocation, + variable_alloca: Self::Value, + direct_offset: Size, + indirect_offsets: &[Size], + ) { + // Convert the direct and indirect offsets to address ops. + // FIXME(eddyb) use `const`s instead of getting the values via FFI, + // the values should match the ones in the DWARF standard anyway. + let op_deref = || unsafe { llvm::LLVMRustDIBuilderCreateOpDeref() }; + let op_plus_uconst = || unsafe { llvm::LLVMRustDIBuilderCreateOpPlusUconst() }; + let mut addr_ops = SmallVec::<[u64; 8]>::new(); + + if direct_offset.bytes() > 0 { + addr_ops.push(op_plus_uconst()); + addr_ops.push(direct_offset.bytes() as u64); + } + for &offset in indirect_offsets { + addr_ops.push(op_deref()); + if offset.bytes() > 0 { + addr_ops.push(op_plus_uconst()); + addr_ops.push(offset.bytes() as u64); + } + } + + unsafe { + // FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`. + llvm::LLVMRustDIBuilderInsertDeclareAtEnd( + DIB(self.cx()), + variable_alloca, + 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 { + let dbg_loc_as_llval = llvm::LLVMRustMetadataAsValue(self.cx().llcx, dbg_loc); + llvm::LLVMSetCurrentDebugLocation(self.llbuilder, dbg_loc_as_llval); + } + } + + fn insert_reference_to_gdb_debug_scripts_section_global(&mut self) { + gdb::insert_reference_to_gdb_debug_scripts_section_global(self) + } + + fn set_var_name(&mut self, value: &'ll Value, name: &str) { + // Avoid wasting time if LLVM value names aren't even enabled. + if self.sess().fewer_names() { + return; + } + + // Only function parameters and instructions are local to a function, + // don't change the name of anything else (e.g. globals). + let param_or_inst = unsafe { + llvm::LLVMIsAArgument(value).is_some() || llvm::LLVMIsAInstruction(value).is_some() + }; + if !param_or_inst { + return; + } + + // Avoid replacing the name if it already exists. + // While we could combine the names somehow, it'd + // get noisy quick, and the usefulness is dubious. + if llvm::get_value_name(value).is_empty() { + llvm::set_value_name(value, name.as_bytes()); + } + } +} + +/// A source code location used to generate debug information. +// FIXME(eddyb) rename this to better indicate it's a duplicate of +// `rustc_span::Loc` rather than `DILocation`, perhaps by making +// `lookup_char_pos` return the right information instead. +pub struct DebugLoc { + /// Information about the original source file. + pub file: Lrc<SourceFile>, + /// The (1-based) line number. + pub line: u32, + /// The (1-based) column number. + pub col: u32, +} + +impl CodegenCx<'_, '_> { + /// Looks up debug source information about a `BytePos`. + // FIXME(eddyb) rename this to better indicate it's a duplicate of + // `lookup_char_pos` rather than `dbg_loc`, perhaps by making + // `lookup_char_pos` return the right information instead. + pub fn lookup_debug_loc(&self, pos: BytePos) -> DebugLoc { + let (file, line, col) = match self.sess().source_map().lookup_line(pos) { + Ok(SourceFileAndLine { sf: file, line }) => { + let line_pos = file.line_begin_pos(pos); + + // Use 1-based indexing. + let line = (line + 1) as u32; + let col = (pos - line_pos).to_u32() + 1; + + (file, line, col) + } + Err(file) => (file, UNKNOWN_LINE_NUMBER, UNKNOWN_COLUMN_NUMBER), + }; + + // For MSVC, omit the column number. + // Otherwise, emit it. This mimics clang behaviour. + // See discussion in https://github.com/rust-lang/rust/issues/42921 + if self.sess().target.is_like_msvc { + DebugLoc { file, line, col: UNKNOWN_COLUMN_NUMBER } + } else { + DebugLoc { file, line, col } + } + } +} + +impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { + fn create_function_debug_context( + &self, + instance: Instance<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + llfn: &'ll Value, + mir: &mir::Body<'tcx>, + ) -> Option<FunctionDebugContext<&'ll DIScope, &'ll DILocation>> { + if self.sess().opts.debuginfo == DebugInfo::None { + return None; + } + + // Initialize fn debug context (including scopes). + let empty_scope = DebugScope { + dbg_scope: self.dbg_scope_fn(instance, fn_abi, Some(llfn)), + inlined_at: None, + file_start_pos: BytePos(0), + file_end_pos: BytePos(0), + }; + let mut fn_debug_context = + FunctionDebugContext { scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes) }; + + // Fill in all the scopes, with the information from the MIR body. + compute_mir_scopes(self, instance, mir, &mut fn_debug_context); + + Some(fn_debug_context) + } + + fn dbg_scope_fn( + &self, + instance: Instance<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + maybe_definition_llfn: Option<&'ll Value>, + ) -> &'ll DIScope { + let tcx = self.tcx; + + let def_id = instance.def_id(); + let containing_scope = get_containing_scope(self, instance); + let span = tcx.def_span(def_id); + let loc = self.lookup_debug_loc(span.lo()); + let file_metadata = file_metadata(self, &loc.file); + + let function_type_metadata = unsafe { + let fn_signature = get_function_signature(self, fn_abi); + llvm::LLVMRustDIBuilderCreateSubroutineType(DIB(self), fn_signature) + }; + + let mut name = String::new(); + type_names::push_item_name(tcx, def_id, false, &mut name); + + // Find the enclosing function, in case this is a closure. + let enclosing_fn_def_id = tcx.typeck_root_def_id(def_id); + + // We look up the generics of the enclosing function and truncate the substs + // to their length in order to cut off extra stuff that might be in there for + // closures or generators. + let generics = tcx.generics_of(enclosing_fn_def_id); + let substs = instance.substs.truncate_to(tcx, generics); + + type_names::push_generic_params( + tcx, + tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substs), + &mut name, + ); + + let template_parameters = get_template_parameters(self, generics, substs); + + let linkage_name = &mangled_name_of_instance(self, instance).name; + // Omit the linkage_name if it is the same as subprogram name. + let linkage_name = if &name == linkage_name { "" } else { linkage_name }; + + // FIXME(eddyb) does this need to be separate from `loc.line` for some reason? + let scope_line = loc.line; + + let mut flags = DIFlags::FlagPrototyped; + + if fn_abi.ret.layout.abi.is_uninhabited() { + flags |= DIFlags::FlagNoReturn; + } + + let mut spflags = DISPFlags::SPFlagDefinition; + if is_node_local_to_unit(self, def_id) { + spflags |= DISPFlags::SPFlagLocalToUnit; + } + if self.sess().opts.optimize != config::OptLevel::No { + spflags |= DISPFlags::SPFlagOptimized; + } + if let Some((id, _)) = tcx.entry_fn(()) { + if id == def_id { + spflags |= DISPFlags::SPFlagMainSubprogram; + } + } + + unsafe { + return llvm::LLVMRustDIBuilderCreateFunction( + DIB(self), + containing_scope, + name.as_ptr().cast(), + name.len(), + linkage_name.as_ptr().cast(), + linkage_name.len(), + file_metadata, + loc.line, + function_type_metadata, + scope_line, + flags, + spflags, + maybe_definition_llfn, + template_parameters, + None, + ); + } + + fn get_function_signature<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + ) -> &'ll DIArray { + if cx.sess().opts.debuginfo == DebugInfo::Limited { + return create_DIArray(DIB(cx), &[]); + } + + let mut signature = Vec::with_capacity(fn_abi.args.len() + 1); + + // Return type -- llvm::DIBuilder wants this at index 0 + signature.push(if fn_abi.ret.is_ignore() { + None + } else { + Some(type_di_node(cx, fn_abi.ret.layout.ty)) + }); + + // Arguments types + if cx.sess().target.is_like_msvc { + // FIXME(#42800): + // There is a bug in MSDIA that leads to a crash when it encounters + // a fixed-size array of `u8` or something zero-sized in a + // function-type (see #40477). + // As a workaround, we replace those fixed-size arrays with a + // pointer-type. So a function `fn foo(a: u8, b: [u8; 4])` would + // appear as `fn foo(a: u8, b: *const u8)` in debuginfo, + // and a function `fn bar(x: [(); 7])` as `fn bar(x: *const ())`. + // This transformed type is wrong, but these function types are + // already inaccurate due to ABI adjustments (see #42800). + signature.extend(fn_abi.args.iter().map(|arg| { + let t = arg.layout.ty; + let t = match t.kind() { + ty::Array(ct, _) + if (*ct == cx.tcx.types.u8) || cx.layout_of(*ct).is_zst() => + { + cx.tcx.mk_imm_ptr(*ct) + } + _ => t, + }; + Some(type_di_node(cx, t)) + })); + } else { + signature + .extend(fn_abi.args.iter().map(|arg| Some(type_di_node(cx, arg.layout.ty)))); + } + + create_DIArray(DIB(cx), &signature[..]) + } + + fn get_template_parameters<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + generics: &ty::Generics, + substs: SubstsRef<'tcx>, + ) -> &'ll DIArray { + if substs.types().next().is_none() { + return create_DIArray(DIB(cx), &[]); + } + + // Again, only create type information if full debuginfo is enabled + let template_params: Vec<_> = if cx.sess().opts.debuginfo == DebugInfo::Full { + let names = get_parameter_names(cx, generics); + iter::zip(substs, names) + .filter_map(|(kind, name)| { + if let GenericArgKind::Type(ty) = kind.unpack() { + let actual_type = + cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty); + let actual_type_metadata = type_di_node(cx, actual_type); + let name = name.as_str(); + Some(unsafe { + Some(llvm::LLVMRustDIBuilderCreateTemplateTypeParameter( + DIB(cx), + None, + name.as_ptr().cast(), + name.len(), + actual_type_metadata, + )) + }) + } else { + None + } + }) + .collect() + } else { + vec![] + }; + + create_DIArray(DIB(cx), &template_params) + } + + fn get_parameter_names(cx: &CodegenCx<'_, '_>, generics: &ty::Generics) -> Vec<Symbol> { + let mut names = generics.parent.map_or_else(Vec::new, |def_id| { + get_parameter_names(cx, cx.tcx.generics_of(def_id)) + }); + names.extend(generics.params.iter().map(|param| param.name)); + names + } + + fn get_containing_scope<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + instance: Instance<'tcx>, + ) -> &'ll DIScope { + // First, let's see if this is a method within an inherent impl. Because + // if yes, we want to make the result subroutine DIE a child of the + // subroutine's self-type. + let self_type = cx.tcx.impl_of_method(instance.def_id()).and_then(|impl_def_id| { + // If the method does *not* belong to a trait, proceed + if cx.tcx.trait_id_of_impl(impl_def_id).is_none() { + let impl_self_ty = cx.tcx.subst_and_normalize_erasing_regions( + instance.substs, + ty::ParamEnv::reveal_all(), + cx.tcx.type_of(impl_def_id), + ); + + // Only "class" methods are generally understood by LLVM, + // so avoid methods on other types (e.g., `<*mut T>::null`). + match impl_self_ty.kind() { + ty::Adt(def, ..) if !def.is_box() => { + // Again, only create type information if full debuginfo is enabled + if cx.sess().opts.debuginfo == DebugInfo::Full + && !impl_self_ty.needs_subst() + { + Some(type_di_node(cx, impl_self_ty)) + } else { + Some(namespace::item_namespace(cx, def.did())) + } + } + _ => None, + } + } else { + // For trait method impls we still use the "parallel namespace" + // strategy + None + } + }); + + self_type.unwrap_or_else(|| { + namespace::item_namespace( + cx, + DefId { + krate: instance.def_id().krate, + index: cx + .tcx + .def_key(instance.def_id()) + .parent + .expect("get_containing_scope: missing parent?"), + }, + ) + }) + } + } + + fn dbg_loc( + &self, + scope: &'ll DIScope, + inlined_at: Option<&'ll DILocation>, + span: Span, + ) -> &'ll DILocation { + let DebugLoc { line, col, .. } = self.lookup_debug_loc(span.lo()); + + unsafe { llvm::LLVMRustDIBuilderCreateDebugLocation(line, col, scope, inlined_at) } + } + + fn create_vtable_debuginfo( + &self, + ty: Ty<'tcx>, + trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>, + vtable: Self::Value, + ) { + metadata::create_vtable_di_node(self, ty, trait_ref, vtable) + } + + fn extend_scope_to_file( + &self, + scope_metadata: &'ll DIScope, + file: &rustc_span::SourceFile, + ) -> &'ll DILexicalBlock { + metadata::extend_scope_to_file(self, scope_metadata, file) + } + + fn debuginfo_finalize(&self) { + finalize(self) + } + + // FIXME(eddyb) find a common convention for all of the debuginfo-related + // names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.). + fn create_dbg_var( + &self, + variable_name: Symbol, + variable_type: Ty<'tcx>, + scope_metadata: &'ll DIScope, + variable_kind: VariableKind, + span: Span, + ) -> &'ll DIVariable { + let loc = self.lookup_debug_loc(span.lo()); + let file_metadata = file_metadata(self, &loc.file); + + let type_metadata = type_di_node(self, variable_type); + + let (argument_index, dwarf_tag) = match variable_kind { + ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable), + LocalVariable => (0, DW_TAG_auto_variable), + }; + let align = self.align_of(variable_type); + + let name = variable_name.as_str(); + unsafe { + llvm::LLVMRustDIBuilderCreateVariable( + DIB(self), + dwarf_tag, + scope_metadata, + name.as_ptr().cast(), + name.len(), + file_metadata, + loc.line, + type_metadata, + true, + DIFlags::FlagZero, + argument_index, + align.bytes() as u32, + ) + } + } +} diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs b/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs new file mode 100644 index 000000000..d5ea48c31 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs @@ -0,0 +1,48 @@ +// Namespace Handling. + +use super::utils::{debug_context, DIB}; +use rustc_codegen_ssa::debuginfo::type_names; +use rustc_middle::ty::{self, Instance}; + +use crate::common::CodegenCx; +use crate::llvm; +use crate::llvm::debuginfo::DIScope; +use rustc_hir::def_id::DefId; + +pub fn mangled_name_of_instance<'a, 'tcx>( + cx: &CodegenCx<'a, 'tcx>, + instance: Instance<'tcx>, +) -> ty::SymbolName<'tcx> { + let tcx = cx.tcx; + tcx.symbol_name(instance) +} + +pub fn item_namespace<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId) -> &'ll DIScope { + if let Some(&scope) = debug_context(cx).namespace_map.borrow().get(&def_id) { + return scope; + } + + let def_key = cx.tcx.def_key(def_id); + let parent_scope = def_key + .parent + .map(|parent| item_namespace(cx, DefId { krate: def_id.krate, index: parent })); + + let namespace_name_string = { + let mut output = String::new(); + type_names::push_item_name(cx.tcx, def_id, false, &mut output); + output + }; + + let scope = unsafe { + llvm::LLVMRustDIBuilderCreateNameSpace( + DIB(cx), + parent_scope, + namespace_name_string.as_ptr().cast(), + namespace_name_string.len(), + false, // ExportSymbols (only relevant for C++ anonymous namespaces) + ) + }; + + debug_context(cx).namespace_map.borrow_mut().insert(def_id, scope); + scope +} diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs new file mode 100644 index 000000000..8f2436739 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs @@ -0,0 +1,99 @@ +// Utility Functions. + +use super::namespace::item_namespace; +use super::CodegenUnitDebugContext; + +use rustc_hir::def_id::DefId; +use rustc_middle::ty::layout::{HasParamEnv, LayoutOf}; +use rustc_middle::ty::{self, DefIdTree, Ty}; +use tracing::trace; + +use crate::common::CodegenCx; +use crate::llvm; +use crate::llvm::debuginfo::{DIArray, DIBuilder, DIDescriptor, DIScope}; + +pub fn is_node_local_to_unit(cx: &CodegenCx<'_, '_>, def_id: DefId) -> bool { + // The is_local_to_unit flag indicates whether a function is local to the + // current compilation unit (i.e., if it is *static* in the C-sense). The + // *reachable* set should provide a good approximation of this, as it + // contains everything that might leak out of the current crate (by being + // externally visible or by being inlined into something externally + // visible). It might better to use the `exported_items` set from + // `driver::CrateAnalysis` in the future, but (atm) this set is not + // available in the codegen pass. + !cx.tcx.is_reachable_non_generic(def_id) +} + +#[allow(non_snake_case)] +pub fn create_DIArray<'ll>( + builder: &DIBuilder<'ll>, + arr: &[Option<&'ll DIDescriptor>], +) -> &'ll DIArray { + unsafe { llvm::LLVMRustDIBuilderGetOrCreateArray(builder, arr.as_ptr(), arr.len() as u32) } +} + +#[inline] +pub fn debug_context<'a, 'll, 'tcx>( + cx: &'a CodegenCx<'ll, 'tcx>, +) -> &'a CodegenUnitDebugContext<'ll, 'tcx> { + cx.dbg_cx.as_ref().unwrap() +} + +#[inline] +#[allow(non_snake_case)] +pub fn DIB<'a, 'll>(cx: &'a CodegenCx<'ll, '_>) -> &'a DIBuilder<'ll> { + cx.dbg_cx.as_ref().unwrap().builder +} + +pub fn get_namespace_for_item<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId) -> &'ll DIScope { + item_namespace(cx, cx.tcx.parent(def_id)) +} + +#[derive(Debug, PartialEq, Eq)] +pub(crate) enum FatPtrKind { + Slice, + Dyn, +} + +/// Determines if `pointee_ty` is slice-like or trait-object-like, i.e. +/// if the second field of the fat pointer is a length or a vtable-pointer. +/// If `pointee_ty` does not require a fat pointer (because it is Sized) then +/// the function returns `None`. +pub(crate) fn fat_pointer_kind<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + pointee_ty: Ty<'tcx>, +) -> Option<FatPtrKind> { + let pointee_tail_ty = cx.tcx.struct_tail_erasing_lifetimes(pointee_ty, cx.param_env()); + let layout = cx.layout_of(pointee_tail_ty); + trace!( + "fat_pointer_kind: {:?} has layout {:?} (is_unsized? {})", + pointee_tail_ty, + layout, + layout.is_unsized() + ); + + if !layout.is_unsized() { + return None; + } + + match *pointee_tail_ty.kind() { + ty::Str | ty::Slice(_) => Some(FatPtrKind::Slice), + ty::Dynamic(..) => Some(FatPtrKind::Dyn), + ty::Foreign(_) => { + // Assert that pointers to foreign types really are thin: + debug_assert_eq!( + cx.size_of(cx.tcx.mk_imm_ptr(pointee_tail_ty)), + cx.size_of(cx.tcx.mk_imm_ptr(cx.tcx.types.u8)) + ); + None + } + _ => { + // For all other pointee types we should already have returned None + // at the beginning of the function. + panic!( + "fat_pointer_kind() - Encountered unexpected `pointee_tail_ty`: {:?}", + pointee_tail_ty + ) + } + } +} |