rust/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1620 lines
60 KiB
Rust
Raw Normal View History

use self::type_map::DINodeCreationResult;
use self::type_map::Stub;
use self::type_map::UniqueTypeId;
2015-04-29 06:14:37 +00:00
use super::namespace::mangled_name_of_instance;
use super::type_names::{compute_debuginfo_type_name, compute_debuginfo_vtable_name};
2019-12-24 22:38:22 +00:00
use super::utils::{
create_DIArray, debug_context, get_namespace_for_item, is_node_local_to_unit, DIB,
2019-12-24 22:38:22 +00:00
};
use super::CodegenUnitDebugContext;
2015-04-29 06:14:37 +00:00
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;
2019-02-17 18:58:58 +00:00
use crate::llvm;
2019-12-24 22:38:22 +00:00
use crate::llvm::debuginfo::{
DIDescriptor, DIFile, DIFlags, DILexicalBlock, DIScope, DIType, DebugEmissionKind,
2019-12-24 22:38:22 +00:00
};
use crate::value::Value;
2015-04-29 06:14:37 +00:00
2021-02-13 11:17:15 +00:00
use cstr::cstr;
use rustc_codegen_ssa::debuginfo::type_names::cpp_like_debuginfo;
use rustc_codegen_ssa::debuginfo::type_names::VTableNameKind;
2019-12-24 22:38:22 +00:00
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};
2019-12-24 22:38:22 +00:00
use rustc_index::vec::{Idx, IndexVec};
use rustc_middle::bug;
use rustc_middle::mir::{self, GeneratorLayout};
Add metadata generation for vtables when using VFE This adds the typeid and `vcall_visibility` metadata to vtables when the -Cvirtual-function-elimination flag is set. The typeid is generated in the same way as for the `llvm.type.checked.load` intrinsic from the trait_ref. The offset that is added to the typeid is always 0. This is because LLVM assumes that vtables are constructed according to the definition in the Itanium ABI. This includes an "address point" of the vtable. In C++ this is the offset in the vtable where information for RTTI is placed. Since there is no RTTI information in Rust's vtables, this "address point" is always 0. This "address point" in combination with the offset passed to the `llvm.type.checked.load` intrinsic determines the final function that should be loaded from the vtable in the `WholeProgramDevirtualization` pass in LLVM. That's why the `llvm.type.checked.load` intrinsics are generated with the typeid of the trait, rather than with that of the function that is called. This matches what `clang` does for C++. The vcall_visibility metadata depends on three factors: 1. LTO level: Currently this is always fat LTO, because LLVM only supports this optimization with fat LTO. 2. Visibility of the trait: If the trait is publicly visible, VFE can only act on its vtables after linking. 3. Number of CGUs: if there is more than one CGU, also vtables with restricted visibility could be seen outside of the CGU, so VFE can only act on them after linking. To reflect this, there are three visibility levels: Public, LinkageUnit, and TranslationUnit.
2022-04-21 13:02:54 +00:00
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::subst::GenericArgKind;
Add metadata generation for vtables when using VFE This adds the typeid and `vcall_visibility` metadata to vtables when the -Cvirtual-function-elimination flag is set. The typeid is generated in the same way as for the `llvm.type.checked.load` intrinsic from the trait_ref. The offset that is added to the typeid is always 0. This is because LLVM assumes that vtables are constructed according to the definition in the Itanium ABI. This includes an "address point" of the vtable. In C++ this is the offset in the vtable where information for RTTI is placed. Since there is no RTTI information in Rust's vtables, this "address point" is always 0. This "address point" in combination with the offset passed to the `llvm.type.checked.load` intrinsic determines the final function that should be loaded from the vtable in the `WholeProgramDevirtualization` pass in LLVM. That's why the `llvm.type.checked.load` intrinsics are generated with the typeid of the trait, rather than with that of the function that is called. This matches what `clang` does for C++. The vcall_visibility metadata depends on three factors: 1. LTO level: Currently this is always fat LTO, because LLVM only supports this optimization with fat LTO. 2. Visibility of the trait: If the trait is publicly visible, VFE can only act on its vtables after linking. 3. Number of CGUs: if there is more than one CGU, also vtables with restricted visibility could be seen outside of the CGU, so VFE can only act on them after linking. To reflect this, there are three visibility levels: Public, LinkageUnit, and TranslationUnit.
2022-04-21 13:02:54 +00:00
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;
Add metadata generation for vtables when using VFE This adds the typeid and `vcall_visibility` metadata to vtables when the -Cvirtual-function-elimination flag is set. The typeid is generated in the same way as for the `llvm.type.checked.load` intrinsic from the trait_ref. The offset that is added to the typeid is always 0. This is because LLVM assumes that vtables are constructed according to the definition in the Itanium ABI. This includes an "address point" of the vtable. In C++ this is the offset in the vtable where information for RTTI is placed. Since there is no RTTI information in Rust's vtables, this "address point" is always 0. This "address point" in combination with the offset passed to the `llvm.type.checked.load` intrinsic determines the final function that should be loaded from the vtable in the `WholeProgramDevirtualization` pass in LLVM. That's why the `llvm.type.checked.load` intrinsics are generated with the typeid of the trait, rather than with that of the function that is called. This matches what `clang` does for C++. The vcall_visibility metadata depends on three factors: 1. LTO level: Currently this is always fat LTO, because LLVM only supports this optimization with fat LTO. 2. Visibility of the trait: If the trait is publicly visible, VFE can only act on its vtables after linking. 3. Number of CGUs: if there is more than one CGU, also vtables with restricted visibility could be seen outside of the CGU, so VFE can only act on them after linking. To reflect this, there are three visibility levels: Public, LinkageUnit, and TranslationUnit.
2022-04-21 13:02:54 +00:00
use rustc_span::{self, FileNameDisplayPreference, SourceFile};
use rustc_symbol_mangling::typeid_for_trait_ref;
use rustc_target::abi::{Align, Size};
use smallvec::smallvec;
2020-08-05 11:35:53 +00:00
use tracing::debug;
2015-04-24 04:48:10 +00:00
Add metadata generation for vtables when using VFE This adds the typeid and `vcall_visibility` metadata to vtables when the -Cvirtual-function-elimination flag is set. The typeid is generated in the same way as for the `llvm.type.checked.load` intrinsic from the trait_ref. The offset that is added to the typeid is always 0. This is because LLVM assumes that vtables are constructed according to the definition in the Itanium ABI. This includes an "address point" of the vtable. In C++ this is the offset in the vtable where information for RTTI is placed. Since there is no RTTI information in Rust's vtables, this "address point" is always 0. This "address point" in combination with the offset passed to the `llvm.type.checked.load` intrinsic determines the final function that should be loaded from the vtable in the `WholeProgramDevirtualization` pass in LLVM. That's why the `llvm.type.checked.load` intrinsics are generated with the typeid of the trait, rather than with that of the function that is called. This matches what `clang` does for C++. The vcall_visibility metadata depends on three factors: 1. LTO level: Currently this is always fat LTO, because LLVM only supports this optimization with fat LTO. 2. Visibility of the trait: If the trait is publicly visible, VFE can only act on its vtables after linking. 3. Number of CGUs: if there is more than one CGU, also vtables with restricted visibility could be seen outside of the CGU, so VFE can only act on them after linking. To reflect this, there are three visibility levels: Public, LinkageUnit, and TranslationUnit.
2022-04-21 13:02:54 +00:00
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};
2019-12-24 22:38:22 +00:00
use std::ptr;
use tracing::instrument;
2015-04-24 04:48:10 +00:00
impl PartialEq for llvm::Metadata {
fn eq(&self, other: &Self) -> bool {
2019-01-12 15:13:33 +00:00
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)
}
}
2016-03-18 21:30:15 +00:00
// From DWARF 5.
// See http://www.dwarfstd.org/ShowIssue.php?issue=140129.1.
2016-03-18 21:30:15 +00:00
const DW_LANG_RUST: c_uint = 0x1c;
2015-04-24 04:48:10 +00:00
#[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;
2015-04-24 04:48:10 +00:00
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();
2015-04-29 06:14:37 +00:00
// 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]>;
2015-04-29 06:14:37 +00:00
mod enums;
mod type_map;
2015-04-29 06:14:37 +00:00
pub(crate) use type_map::TypeMap;
2015-04-29 06:14:37 +00:00
/// 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 {
2019-12-24 22:38:22 +00:00
($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);
2015-10-21 16:20:46 +00:00
}
2019-12-24 22:38:22 +00:00
};
2015-04-24 04:48:10 +00:00
}
/// Extract size and alignment from a TyAndLayout.
#[inline]
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);
2015-04-24 04:48:10 +00:00
return_if_di_node_created_in_meantime!(cx, unique_type_id);
2015-04-24 04:48:10 +00:00
let (size, align) = cx.size_and_align_of(array_type);
2015-04-24 04:48:10 +00:00
let upper_bound = len.eval_usize(cx.tcx, ty::ParamEnv::reveal_all()) as c_longlong;
2015-04-24 04:48:10 +00:00
2019-12-24 22:38:22 +00:00
let subrange =
unsafe { Some(llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound)) };
2015-04-24 04:48:10 +00:00
let subscripts = create_DIArray(DIB(cx), &[subrange]);
let di_node = unsafe {
llvm::LLVMRustDIBuilderCreateArrayType(
2015-04-24 04:48:10 +00:00
DIB(cx),
size.bits(),
align.bits() as u32,
element_type_di_node,
2019-12-24 22:38:22 +00:00
subscripts,
)
2015-04-24 04:48:10 +00:00
};
DINodeCreationResult::new(di_node, false)
2015-04-24 04:48:10 +00:00
}
/// 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);
2015-04-24 04:48:10 +00:00
return_if_di_node_created_in_meantime!(cx, unique_type_id);
2015-04-24 04:48:10 +00:00
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);
2015-04-24 04:48:10 +00:00
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,
)
}
}
2015-04-24 04:48:10 +00:00
}
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));
2015-04-24 04:48:10 +00:00
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));
2015-04-24 04:48:10 +00:00
let signature_di_nodes: SmallVec<_> = iter::once(
// return type
2020-08-02 22:49:11 +00:00
match signature.output().kind() {
ty::Tuple(tys) if tys.is_empty() => {
// this is a "void" function
None
}
_ => Some(type_di_node(cx, signature.output())),
2019-12-24 22:38:22 +00:00
},
)
.chain(
// regular arguments
signature.inputs().iter().map(|&argument_type| Some(type_di_node(cx, argument_type))),
2019-12-24 22:38:22 +00:00
)
.collect();
2015-04-24 04:48:10 +00:00
debug_context(cx).type_map.unique_id_to_di_node.borrow_mut().remove(&unique_type_id);
2015-04-24 04:48:10 +00:00
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)
2015-04-24 04:48:10 +00:00
}
/// 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
)
}
}
2015-04-24 04:48:10 +00:00
/// 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() {
2022-01-25 03:13:38 +00:00
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
)
}
Improve debug symbol names to avoid ambiguity and work better with MSVC's debugger There are several cases where names of types and functions in the debug info are either ambiguous, or not helpful, such as including ambiguous placeholders (e.g., `{{impl}}`, `{{closure}}` or `dyn _'`) or dropping qualifications (e.g., for dynamic types). Instead, each debug symbol name should be unique and useful: * Include disambiguators for anonymous `DefPathDataName` (closures and generators), and unify their formatting when used as a path-qualifier vs item being qualified. * Qualify the principal trait for dynamic types. * If there is no principal trait for a dynamic type, emit all other traits instead. * Respect the `qualified` argument when emitting ref and pointer types. * For implementations, emit the disambiguator. * Print const generics when emitting generic parameters or arguments. Additionally, when targeting MSVC, its debugger treats many command arguments as C++ expressions, even when the argument is defined to be a symbol name. As such names in the debug info need to be more C++-like to be parsed correctly: * Avoid characters with special meaning (`#`, `[`, `"`, `+`). * Never start a name with `<` or `{` as this is treated as an operator. * `>>` is always treated as a right-shift, even when parsing generic arguments (so add a space to avoid this). * Emit function declarations using C/C++ style syntax (e.g., leading return type). * Emit arrays as a synthetic `array$<type, size>` type. * Include a `$` in all synthetic types as this is a legal character for C++, but not Rust (thus we avoid collisions with user types).
2021-06-24 17:36:28 +00:00
};
2015-04-24 04:48:10 +00:00
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 }
2015-04-24 04:48:10 +00:00
}
/// 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;
}
2015-04-24 04:48:10 +00:00
debug!("type_di_node: {:?}", t);
2015-04-24 04:48:10 +00:00
let DINodeCreationResult { di_node, already_stored_in_typemap } = match *t.kind() {
2019-12-24 22:38:22 +00:00
ty::Never | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => {
build_basic_type_di_node(cx, t)
2015-04-24 04:48:10 +00:00
}
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)
2016-12-26 13:34:03 +00:00
}
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),
2015-04-24 04:48:10 +00:00
};
{
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,
2019-12-24 22:38:22 +00:00
);
2015-04-24 04:48:10 +00:00
}
};
debug_assert_eq!(di_node_for_uid as *const _, di_node as *const _);
2015-04-24 04:48:10 +00:00
} else {
debug_context(cx).type_map.insert(unique_type_id, di_node);
2015-04-24 04:48:10 +00:00
}
}
di_node
2015-04-24 04:48:10 +00:00
}
// 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(),
)
}
}
2015-04-24 04:48:10 +00:00
}
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);
2015-04-24 04:48:10 +00:00
// 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);
2020-08-02 22:49:11 +00:00
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),
2019-12-24 22:38:22 +00:00
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"),
2015-04-24 04:48:10 +00:00
};
let ty_di_node = unsafe {
llvm::LLVMRustDIBuilderCreateBasicType(
2015-04-24 04:48:10 +00:00
DIB(cx),
name.as_ptr().cast(),
name.len(),
cx.size_of(t).bits(),
2019-12-24 22:38:22 +00:00
encoding,
)
2015-04-24 04:48:10 +00:00
};
if !cpp_like_debuginfo {
return DINodeCreationResult::new(ty_di_node, false);
}
2020-08-02 22:49:11 +00:00
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)
2015-04-24 04:48:10 +00:00
}
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());
};
2017-09-03 18:53:58 +00:00
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,
)
2017-09-03 18:53:58 +00:00
}
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()),
2015-04-24 04:48:10 +00:00
};
// 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);
2019-12-24 22:38:22 +00:00
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.
2019-07-19 14:08:37 +00:00
let producer = format!("clang LLVM ({})", rustc_producer);
2015-04-24 04:48:10 +00:00
let name_in_debuginfo = name_in_debuginfo.to_string_lossy();
let work_dir = tcx.sess.opts.working_dir.to_string_lossy(FileNameDisplayPreference::Remapped);
2015-04-24 04:48:10 +00:00
let flags = "\0";
2021-05-11 12:39:04 +00:00
let output_filenames = tcx.output_filenames(());
rustc: Stabilize `-Zrun-dsymutil` as `-Csplit-debuginfo` This commit adds a new stable codegen option to rustc, `-Csplit-debuginfo`. The old `-Zrun-dsymutil` flag is deleted and now subsumed by this stable flag. Additionally `-Zsplit-dwarf` is also subsumed by this flag but still requires `-Zunstable-options` to actually activate. The `-Csplit-debuginfo` flag takes one of three values: * `off` - This indicates that split-debuginfo from the final artifact is not desired. This is not supported on Windows and is the default on Unix platforms except macOS. On macOS this means that `dsymutil` is not executed. * `packed` - This means that debuginfo is desired in one location separate from the main executable. This is the default on Windows (`*.pdb`) and macOS (`*.dSYM`). On other Unix platforms this subsumes `-Zsplit-dwarf=single` and produces a `*.dwp` file. * `unpacked` - This means that debuginfo will be roughly equivalent to object files, meaning that it's throughout the build directory rather than in one location (often the fastest for local development). This is not the default on any platform and is not supported on Windows. Each target can indicate its own default preference for how debuginfo is handled. Almost all platforms default to `off` except for Windows and macOS which default to `packed` for historical reasons. Some equivalencies for previous unstable flags with the new flags are: * `-Zrun-dsymutil=yes` -> `-Csplit-debuginfo=packed` * `-Zrun-dsymutil=no` -> `-Csplit-debuginfo=unpacked` * `-Zsplit-dwarf=single` -> `-Csplit-debuginfo=packed` * `-Zsplit-dwarf=split` -> `-Csplit-debuginfo=unpacked` Note that `-Csplit-debuginfo` still requires `-Zunstable-options` for non-macOS platforms since split-dwarf support was *just* implemented in rustc. There's some more rationale listed on #79361, but the main gist of the motivation for this commit is that `dsymutil` can take quite a long time to execute in debug builds and provides little benefit. This means that incremental compile times appear that much worse on macOS because the compiler is constantly running `dsymutil` over every single binary it produces during `cargo build` (even build scripts!). Ideally rustc would switch to not running `dsymutil` by default, but that's a problem left to get tackled another day. Closes #79361
2020-11-30 16:39:08 +00:00
let split_name = if tcx.sess.target_can_use_split_dwarf() {
2021-05-11 12:39:04 +00:00
output_filenames
.split_dwarf_path(
tcx.sess.split_debuginfo(),
tcx.sess.opts.unstable_opts.split_dwarf_kind,
Some(codegen_unit_name),
)
2021-12-06 23:59:59 +00:00
// 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)
rustc: Stabilize `-Zrun-dsymutil` as `-Csplit-debuginfo` This commit adds a new stable codegen option to rustc, `-Csplit-debuginfo`. The old `-Zrun-dsymutil` flag is deleted and now subsumed by this stable flag. Additionally `-Zsplit-dwarf` is also subsumed by this flag but still requires `-Zunstable-options` to actually activate. The `-Csplit-debuginfo` flag takes one of three values: * `off` - This indicates that split-debuginfo from the final artifact is not desired. This is not supported on Windows and is the default on Unix platforms except macOS. On macOS this means that `dsymutil` is not executed. * `packed` - This means that debuginfo is desired in one location separate from the main executable. This is the default on Windows (`*.pdb`) and macOS (`*.dSYM`). On other Unix platforms this subsumes `-Zsplit-dwarf=single` and produces a `*.dwp` file. * `unpacked` - This means that debuginfo will be roughly equivalent to object files, meaning that it's throughout the build directory rather than in one location (often the fastest for local development). This is not the default on any platform and is not supported on Windows. Each target can indicate its own default preference for how debuginfo is handled. Almost all platforms default to `off` except for Windows and macOS which default to `packed` for historical reasons. Some equivalencies for previous unstable flags with the new flags are: * `-Zrun-dsymutil=yes` -> `-Csplit-debuginfo=packed` * `-Zrun-dsymutil=no` -> `-Csplit-debuginfo=unpacked` * `-Zsplit-dwarf=single` -> `-Csplit-debuginfo=packed` * `-Zsplit-dwarf=split` -> `-Csplit-debuginfo=unpacked` Note that `-Csplit-debuginfo` still requires `-Zunstable-options` for non-macOS platforms since split-dwarf support was *just* implemented in rustc. There's some more rationale listed on #79361, but the main gist of the motivation for this commit is that `dsymutil` can take quite a long time to execute in debug builds and provides little benefit. This means that incremental compile times appear that much worse on macOS because the compiler is constantly running `dsymutil` over every single binary it produces during `cargo build` (even build scripts!). Ideally rustc would switch to not running `dsymutil` by default, but that's a problem left to get tackled another day. Closes #79361
2020-11-30 16:39:08 +00:00
} 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);
2017-02-11 21:01:25 +00:00
unsafe {
let compile_unit_file = llvm::LLVMRustDIBuilderCreateFile(
2019-12-24 22:38:22 +00:00
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,
2019-12-24 22:38:22 +00:00
);
2017-02-11 21:01:25 +00:00
let unit_metadata = llvm::LLVMRustDIBuilderCreateCompileUnit(
debug_context.builder,
2015-04-24 04:48:10 +00:00
DW_LANG_RUST,
compile_unit_file,
producer.as_ptr().cast(),
producer.len(),
tcx.sess.opts.optimize != config::OptLevel::No,
flags.as_ptr().cast(),
2015-04-24 04:48:10 +00:00
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(),
2019-12-24 22:38:22 +00:00
kind,
0,
tcx.sess.opts.unstable_opts.split_dwarf_inlining,
2019-12-24 22:38:22 +00:00
);
if tcx.sess.opts.unstable_opts.profile {
2019-12-24 22:38:22 +00:00
let cu_desc_metadata =
llvm::LLVMRustMetadataAsValue(debug_context.llcontext, unit_metadata);
2021-05-11 12:39:04 +00:00
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 = [
2021-05-11 12:39:04 +00:00
path_to_mdstring(debug_context.llcontext, &output_filenames.with_extension("gcno")),
2021-09-30 17:38:50 +00:00
path_to_mdstring(debug_context.llcontext, gcda_path),
cu_desc_metadata,
];
2019-12-24 22:38:22 +00:00
let gcov_metadata = llvm::LLVMMDNodeInContext(
debug_context.llcontext,
gcov_cu_info.as_ptr(),
gcov_cu_info.len() as c_uint,
);
2021-02-13 11:17:15 +00:00
let llvm_gcov_ident = cstr!("llvm.gcov");
2019-12-24 22:38:22 +00:00
llvm::LLVMAddNamedMetadataOperand(
debug_context.llmod,
llvm_gcov_ident.as_ptr(),
gcov_metadata,
);
}
2020-12-30 18:52:21 +00:00
// Insert `llvm.ident` metadata on the wasm targets since that will
2019-07-19 14:08:37 +00:00
// get hooked up to the "producer" sections `processed-by` information.
2020-12-30 18:52:21 +00:00
if tcx.sess.target.is_like_wasm {
2019-07-19 14:08:37 +00:00
let name_metadata = llvm::LLVMMDStringInContext(
debug_context.llcontext,
rustc_producer.as_ptr().cast(),
2019-07-19 14:08:37 +00:00
rustc_producer.as_bytes().len() as c_uint,
);
llvm::LLVMAddNamedMetadataOperand(
debug_context.llmod,
2021-02-13 11:17:15 +00:00
cstr!("llvm.ident").as_ptr(),
2019-07-19 14:08:37 +00:00
llvm::LLVMMDNodeInContext(debug_context.llcontext, &name_metadata, 1),
);
}
return unit_metadata;
2015-04-24 04:48:10 +00:00
};
fn path_to_mdstring<'ll>(llcx: &'ll llvm::Context, path: &Path) -> &'ll Value {
let path_str = path_to_c_string(path);
unsafe {
2019-12-24 22:38:22 +00:00
llvm::LLVMMDStringInContext(
llcx,
path_str.as_ptr(),
path_str.as_bytes().len() as c_uint,
)
}
}
2015-04-24 04:48:10 +00:00
}
/// 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,
)
2015-04-29 06:14:37 +00:00
}
}
/// 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),
2015-04-29 06:14:37 +00:00
)
}
//=-----------------------------------------------------------------------------
// 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
)
}
};
2015-04-29 06:14:37 +00:00
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()
2015-04-29 06:14:37 +00:00
}
/// 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)
};
2015-04-29 06:14:37 +00:00
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(
2015-04-29 06:14:37 +00:00
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,
2015-04-29 06:14:37 +00:00
)
}
/// 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);
2016-08-22 18:11:22 +00:00
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,
)
2016-08-22 18:11:22 +00:00
}
/// 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"),
2016-08-22 18:11:22 +00:00
};
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);
2016-08-22 18:11:22 +00:00
type_map::build_type_with_children(
2016-08-22 18:11:22 +00:00
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),
2016-08-22 18:11:22 +00:00
)
}
2015-04-29 06:14:37 +00:00
// 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,
2020-04-19 11:00:18 +00:00
) -> (&'tcx GeneratorLayout<'tcx>, IndexVec<mir::GeneratorSavedLocal, Option<Symbol>>) {
let body = tcx.optimized_mir(def_id);
2021-01-17 12:27:05 +00:00
let generator_layout = body.generator_layout().unwrap();
2019-12-24 22:38:22 +00:00
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 {
2022-01-18 19:26:13 +00:00
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> {
2020-08-02 22:49:11 +00:00
if let ty::Adt(def, substs) = *ty.kind() {
2020-02-29 00:56:37 +00:00
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)
2019-12-24 22:38:22 +00:00
.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();
2019-12-24 22:38:22 +00:00
Some(unsafe {
llvm::LLVMRustDIBuilderCreateTemplateTypeParameter(
2019-12-24 22:38:22 +00:00
DIB(cx),
None,
name.as_ptr().cast(),
name.len(),
actual_type_di_node,
)
2019-12-24 22:38:22 +00:00
})
} else {
None
}
})
.collect();
return template_params;
}
}
return smallvec![];
2019-12-24 22:38:22 +00:00
fn get_parameter_names(cx: &CodegenCx<'_, '_>, generics: &ty::Generics) -> Vec<Symbol> {
let mut names = generics
.parent
2021-02-25 01:13:42 +00:00
.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
2015-04-29 06:14:37 +00:00
}
}
/// 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() {
2015-04-29 06:14:37 +00:00
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);
2015-04-29 06:14:37 +00:00
2018-06-24 22:00:21 +00:00
let (file_metadata, line_number) = if !span.is_dummy() {
let loc = cx.lookup_debug_loc(span.lo());
(file_metadata(cx, &loc.file), loc.line)
2015-04-29 06:14:37 +00:00
} else {
2021-02-19 20:50:15 +00:00
(unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER)
2015-04-29 06:14:37 +00:00
};
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);
2015-04-29 06:14:37 +00:00
unsafe {
2019-12-24 22:38:22 +00:00
llvm::LLVMRustDIBuilderCreateStaticVariable(
DIB(cx),
Some(var_scope),
var_name.as_ptr().cast(),
var_name.len(),
linkage_name.as_ptr().cast(),
linkage_name.len(),
2019-12-24 22:38:22 +00:00
file_metadata,
2021-02-19 20:50:15 +00:00
line_number,
type_di_node,
2019-12-24 22:38:22 +00:00
is_local_to_unit,
global,
None,
global_align.bits() as u32,
);
2015-04-29 06:14:37 +00:00
}
}
/// 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
}
Add metadata generation for vtables when using VFE This adds the typeid and `vcall_visibility` metadata to vtables when the -Cvirtual-function-elimination flag is set. The typeid is generated in the same way as for the `llvm.type.checked.load` intrinsic from the trait_ref. The offset that is added to the typeid is always 0. This is because LLVM assumes that vtables are constructed according to the definition in the Itanium ABI. This includes an "address point" of the vtable. In C++ this is the offset in the vtable where information for RTTI is placed. Since there is no RTTI information in Rust's vtables, this "address point" is always 0. This "address point" in combination with the offset passed to the `llvm.type.checked.load` intrinsic determines the final function that should be loaded from the vtable in the `WholeProgramDevirtualization` pass in LLVM. That's why the `llvm.type.checked.load` intrinsics are generated with the typeid of the trait, rather than with that of the function that is called. This matches what `clang` does for C++. The vcall_visibility metadata depends on three factors: 1. LTO level: Currently this is always fat LTO, because LLVM only supports this optimization with fat LTO. 2. Visibility of the trait: If the trait is publicly visible, VFE can only act on its vtables after linking. 3. Number of CGUs: if there is more than one CGU, also vtables with restricted visibility could be seen outside of the CGU, so VFE can only act on them after linking. To reflect this, there are three visibility levels: Public, LinkageUnit, and TranslationUnit.
2022-04-21 13:02:54 +00:00
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,
) {
Add metadata generation for vtables when using VFE This adds the typeid and `vcall_visibility` metadata to vtables when the -Cvirtual-function-elimination flag is set. The typeid is generated in the same way as for the `llvm.type.checked.load` intrinsic from the trait_ref. The offset that is added to the typeid is always 0. This is because LLVM assumes that vtables are constructed according to the definition in the Itanium ABI. This includes an "address point" of the vtable. In C++ this is the offset in the vtable where information for RTTI is placed. Since there is no RTTI information in Rust's vtables, this "address point" is always 0. This "address point" in combination with the offset passed to the `llvm.type.checked.load` intrinsic determines the final function that should be loaded from the vtable in the `WholeProgramDevirtualization` pass in LLVM. That's why the `llvm.type.checked.load` intrinsics are generated with the typeid of the trait, rather than with that of the function that is called. This matches what `clang` does for C++. The vcall_visibility metadata depends on three factors: 1. LTO level: Currently this is always fat LTO, because LLVM only supports this optimization with fat LTO. 2. Visibility of the trait: If the trait is publicly visible, VFE can only act on its vtables after linking. 3. Number of CGUs: if there is more than one CGU, also vtables with restricted visibility could be seen outside of the CGU, so VFE can only act on them after linking. To reflect this, there are three visibility levels: Public, LinkageUnit, and TranslationUnit.
2022-04-21 13:02:54 +00:00
// 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 {
Add metadata generation for vtables when using VFE This adds the typeid and `vcall_visibility` metadata to vtables when the -Cvirtual-function-elimination flag is set. The typeid is generated in the same way as for the `llvm.type.checked.load` intrinsic from the trait_ref. The offset that is added to the typeid is always 0. This is because LLVM assumes that vtables are constructed according to the definition in the Itanium ABI. This includes an "address point" of the vtable. In C++ this is the offset in the vtable where information for RTTI is placed. Since there is no RTTI information in Rust's vtables, this "address point" is always 0. This "address point" in combination with the offset passed to the `llvm.type.checked.load` intrinsic determines the final function that should be loaded from the vtable in the `WholeProgramDevirtualization` pass in LLVM. That's why the `llvm.type.checked.load` intrinsics are generated with the typeid of the trait, rather than with that of the function that is called. This matches what `clang` does for C++. The vcall_visibility metadata depends on three factors: 1. LTO level: Currently this is always fat LTO, because LLVM only supports this optimization with fat LTO. 2. Visibility of the trait: If the trait is publicly visible, VFE can only act on its vtables after linking. 3. Number of CGUs: if there is more than one CGU, also vtables with restricted visibility could be seen outside of the CGU, so VFE can only act on them after linking. To reflect this, there are three visibility levels: Public, LinkageUnit, and TranslationUnit.
2022-04-21 13:02:54 +00:00
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 {
2019-12-24 22:38:22 +00:00
llvm::LLVMRustDIBuilderCreateStaticVariable(
DIB(cx),
NO_SCOPE_METADATA,
vtable_name.as_ptr().cast(),
vtable_name.len(),
linkage_name.as_ptr().cast(),
linkage_name.len(),
2019-12-24 22:38:22 +00:00
unknown_file_metadata(cx),
UNKNOWN_LINE_NUMBER,
vtable_type_di_node,
2019-12-24 22:38:22 +00:00
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);
2019-12-24 22:38:22 +00:00
unsafe { llvm::LLVMRustDIBuilderCreateLexicalBlockFile(DIB(cx), scope_metadata, file_metadata) }
2016-08-26 16:23:42 +00:00
}
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)))
}