summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs')
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs267
1 files changed, 267 insertions, 0 deletions
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 }
+}