Unverified Commit b58f4ce4 authored by Jonas Devlieghere's avatar Jonas Devlieghere Committed by GitHub
Browse files

[DWARFLinker] Emit DW_IDX_parent in the accelerator table (#195403)

.debug_names entries produced by the parallel linker were always emitted
with std::nullopt for ParentDIEOffset, resulting in a missing
DW_IDX_parent. The classic linker emits it via
DWARF5AccelTableData::getDefiningParentDieOffset on the output DIE tree.

The parallel linker can't use the same approach because the records are
saved during cloneDIE, before the output DIE has been linked into its
parent, so DIE::getParent() is nullptr at that time time. Fix that by
computing the parent offset from the input-side DIE tree instead. We
look up InputDieEntry's parent via getParentIdx, skip parents marked
DW_AT_declaration, and translate them to the output offset through
CompileUnit::getDieOutOffset. Since no real DIE can live at offset 0, we
can use that to unambiguously mark input DIEs that were not cloned into
this CU's plain DWARF (e.g. routed only into the artificial type unit)
and is treated as "no parent".

Only compile-unit accelerator entries are covered. Type-unit entries
(artificial type unit) still emit no DW_IDX_parent, tracked by a TODO.
parent 9aa55b29
Loading
Loading
Loading
Loading
+66 −28
Original line number Diff line number Diff line
@@ -116,8 +116,9 @@ void AcceleratorRecordsSaver::save(const DWARFDebugInfoEntry *InputDieEntry,
              InputDIE.find(dwarf::DW_AT_APPLE_objc_complete_type))
              .value_or(0);

      saveTypeRecord(AttrInfo.Name, OutDIE, InputDieEntry->getTag(), Hash,
                     ObjCClassIsImplementation, TypeEntry);
      saveTypeRecord(InputDieEntry, AttrInfo.Name, OutDIE,
                     InputDieEntry->getTag(), Hash, ObjCClassIsImplementation,
                     TypeEntry);
    }
  } break;
  case dwarf::DW_TAG_namespace: {
@@ -125,13 +126,13 @@ void AcceleratorRecordsSaver::save(const DWARFDebugInfoEntry *InputDieEntry,
      AttrInfo.Name =
          GlobalData.getStringPool().insert("(anonymous namespace)").first;

    saveNamespaceRecord(AttrInfo.Name, OutDIE, InputDieEntry->getTag(),
                        TypeEntry);
    saveNamespaceRecord(InputDieEntry, AttrInfo.Name, OutDIE,
                        InputDieEntry->getTag(), TypeEntry);
  } break;
  case dwarf::DW_TAG_imported_declaration: {
    if (AttrInfo.Name != nullptr)
      saveNamespaceRecord(AttrInfo.Name, OutDIE, InputDieEntry->getTag(),
                          TypeEntry);
      saveNamespaceRecord(InputDieEntry, AttrInfo.Name, OutDIE,
                          InputDieEntry->getTag(), TypeEntry);
  } break;
  case dwarf::DW_TAG_compile_unit:
  case dwarf::DW_TAG_lexical_block: {
@@ -144,9 +145,9 @@ void AcceleratorRecordsSaver::save(const DWARFDebugInfoEntry *InputDieEntry,

    if (AttrInfo.HasLiveAddress || AttrInfo.HasRanges) {
      if (AttrInfo.Name)
        saveNameRecord(AttrInfo.Name, OutDIE, InputDieEntry->getTag(),
                       InputDieEntry->getTag() ==
                           dwarf::DW_TAG_inlined_subroutine);
        saveNameRecord(
            InputDieEntry, AttrInfo.Name, OutDIE, InputDieEntry->getTag(),
            InputDieEntry->getTag() == dwarf::DW_TAG_inlined_subroutine);

      // Look for mangled name recursively if mangled name is not known yet.
      if (!AttrInfo.MangledName)
@@ -155,7 +156,8 @@ void AcceleratorRecordsSaver::save(const DWARFDebugInfoEntry *InputDieEntry,
              GlobalData.getStringPool().insert(LinkageName).first;

      if (AttrInfo.MangledName && AttrInfo.MangledName != AttrInfo.Name)
        saveNameRecord(AttrInfo.MangledName, OutDIE, InputDieEntry->getTag(),
        saveNameRecord(InputDieEntry, AttrInfo.MangledName, OutDIE,
                       InputDieEntry->getTag(),
                       InputDieEntry->getTag() ==
                           dwarf::DW_TAG_inlined_subroutine);

@@ -167,7 +169,7 @@ void AcceleratorRecordsSaver::save(const DWARFDebugInfoEntry *InputDieEntry,
          StringEntry *NameWithoutTemplateParams =
              GlobalData.getStringPool().insert(*Name).first;

          saveNameRecord(NameWithoutTemplateParams, OutDIE,
          saveNameRecord(InputDieEntry, NameWithoutTemplateParams, OutDIE,
                         InputDieEntry->getTag(), true);
        }
      }
@@ -188,38 +190,64 @@ void AcceleratorRecordsSaver::saveObjC(const DWARFDebugInfoEntry *InputDieEntry,

  StringEntry *Selector =
      GlobalData.getStringPool().insert(Names->Selector).first;
  saveNameRecord(Selector, OutDIE, InputDieEntry->getTag(), true);
  saveNameRecord(InputDieEntry, Selector, OutDIE, InputDieEntry->getTag(),
                 true);
  StringEntry *ClassName =
      GlobalData.getStringPool().insert(Names->ClassName).first;
  saveObjCNameRecord(ClassName, OutDIE, InputDieEntry->getTag());
  saveObjCNameRecord(InputDieEntry, ClassName, OutDIE, InputDieEntry->getTag());
  if (Names->ClassNameNoCategory) {
    StringEntry *ClassNameNoCategory =
        GlobalData.getStringPool().insert(*Names->ClassNameNoCategory).first;
    saveObjCNameRecord(ClassNameNoCategory, OutDIE, InputDieEntry->getTag());
    saveObjCNameRecord(InputDieEntry, ClassNameNoCategory, OutDIE,
                       InputDieEntry->getTag());
  }
  if (Names->MethodNameNoCategory) {
    StringEntry *MethodNameNoCategory =
        GlobalData.getStringPool().insert(*Names->MethodNameNoCategory).first;
    saveNameRecord(MethodNameNoCategory, OutDIE, InputDieEntry->getTag(), true);
    saveNameRecord(InputDieEntry, MethodNameNoCategory, OutDIE,
                   InputDieEntry->getTag(), true);
  }
}

void AcceleratorRecordsSaver::saveNameRecord(StringEntry *Name, DIE *OutDIE,
                                             dwarf::Tag Tag,
                                             bool AvoidForPubSections) {
std::optional<uint64_t> AcceleratorRecordsSaver::getDefiningParentOutOffset(
    const DWARFDebugInfoEntry *InputDieEntry) {
  // getDieOutOffset returns this for input DIEs that were not cloned into
  // this CU's plain DWARF (e.g. routed only into the artificial type unit).
  // OutDieOffsetArray is zero-initialized and a real DIE never lives at
  // offset 0 (the CU header occupies the first bytes of the unit), so 0 is
  // an unambiguous "no plain-DWARF copy" sentinel.
  constexpr uint64_t NotClonedInPlainDWARF = 0;

  std::optional<uint32_t> ParentIdx = InputDieEntry->getParentIdx();
  if (!ParentIdx)
    return std::nullopt;
  // Skip parents marked as declarations; the name table should only reference
  // definitions.
  if (dwarf::toUnsigned(InUnit.find(*ParentIdx, dwarf::DW_AT_declaration), 0))
    return std::nullopt;
  uint64_t ParentOutOffset = InUnit.getDieOutOffset(*ParentIdx);
  if (ParentOutOffset == NotClonedInPlainDWARF)
    return std::nullopt;
  return ParentOutOffset;
}

void AcceleratorRecordsSaver::saveNameRecord(
    const DWARFDebugInfoEntry *InputDieEntry, StringEntry *Name, DIE *OutDIE,
    dwarf::Tag Tag, bool AvoidForPubSections) {
  DwarfUnit::AccelInfo Info;

  Info.Type = DwarfUnit::AccelType::Name;
  Info.String = Name;
  Info.OutOffset = OutDIE->getOffset();
  Info.ParentOffset = getDefiningParentOutOffset(InputDieEntry);
  Info.Tag = Tag;
  Info.AvoidForPubSections = AvoidForPubSections;

  OutUnit.getAsCompileUnit()->saveAcceleratorInfo(Info);
}
void AcceleratorRecordsSaver::saveNamespaceRecord(StringEntry *Name,
                                                  DIE *OutDIE, dwarf::Tag Tag,
                                                  TypeEntry *TypeEntry) {
void AcceleratorRecordsSaver::saveNamespaceRecord(
    const DWARFDebugInfoEntry *InputDieEntry, StringEntry *Name, DIE *OutDIE,
    dwarf::Tag Tag, TypeEntry *TypeEntry) {
  if (OutUnit.isCompileUnit()) {
    assert(TypeEntry == nullptr);
    DwarfUnit::AccelInfo Info;
@@ -227,12 +255,17 @@ void AcceleratorRecordsSaver::saveNamespaceRecord(StringEntry *Name,
    Info.Type = DwarfUnit::AccelType::Namespace;
    Info.String = Name;
    Info.OutOffset = OutDIE->getOffset();
    Info.ParentOffset = getDefiningParentOutOffset(InputDieEntry);
    Info.Tag = Tag;

    OutUnit.getAsCompileUnit()->saveAcceleratorInfo(Info);
    return;
  }

  // TODO: compute DW_IDX_parent for entries emitted into the artificial type
  // unit. The parent lookup via the input-side DIE tree is only valid for
  // DIEs cloned into this CU's plain DWARF.

  assert(TypeEntry != nullptr);
  TypeUnit::TypeUnitAccelInfo Info;
  Info.Type = DwarfUnit::AccelType::Namespace;
@@ -245,23 +278,24 @@ void AcceleratorRecordsSaver::saveNamespaceRecord(StringEntry *Name,
  OutUnit.getAsTypeUnit()->saveAcceleratorInfo(Info);
}

void AcceleratorRecordsSaver::saveObjCNameRecord(StringEntry *Name, DIE *OutDIE,
void AcceleratorRecordsSaver::saveObjCNameRecord(
    const DWARFDebugInfoEntry *InputDieEntry, StringEntry *Name, DIE *OutDIE,
    dwarf::Tag Tag) {
  DwarfUnit::AccelInfo Info;

  Info.Type = DwarfUnit::AccelType::ObjC;
  Info.String = Name;
  Info.OutOffset = OutDIE->getOffset();
  Info.ParentOffset = getDefiningParentOutOffset(InputDieEntry);
  Info.Tag = Tag;
  Info.AvoidForPubSections = true;

  OutUnit.getAsCompileUnit()->saveAcceleratorInfo(Info);
}

void AcceleratorRecordsSaver::saveTypeRecord(StringEntry *Name, DIE *OutDIE,
                                             dwarf::Tag Tag,
                                             uint32_t QualifiedNameHash,
                                             bool ObjcClassImplementation,
void AcceleratorRecordsSaver::saveTypeRecord(
    const DWARFDebugInfoEntry *InputDieEntry, StringEntry *Name, DIE *OutDIE,
    dwarf::Tag Tag, uint32_t QualifiedNameHash, bool ObjcClassImplementation,
    TypeEntry *TypeEntry) {
  if (OutUnit.isCompileUnit()) {
    assert(TypeEntry == nullptr);
@@ -270,6 +304,7 @@ void AcceleratorRecordsSaver::saveTypeRecord(StringEntry *Name, DIE *OutDIE,
    Info.Type = DwarfUnit::AccelType::Type;
    Info.String = Name;
    Info.OutOffset = OutDIE->getOffset();
    Info.ParentOffset = getDefiningParentOutOffset(InputDieEntry);
    Info.Tag = Tag;
    Info.QualifiedNameHash = QualifiedNameHash;
    Info.ObjcClassImplementation = ObjcClassImplementation;
@@ -278,6 +313,9 @@ void AcceleratorRecordsSaver::saveTypeRecord(StringEntry *Name, DIE *OutDIE,
    return;
  }

  // TODO: compute DW_IDX_parent for entries emitted into the artificial type
  // unit (see saveNamespaceRecord).

  assert(TypeEntry != nullptr);
  TypeUnit::TypeUnitAccelInfo Info;

+16 −4
Original line number Diff line number Diff line
@@ -46,15 +46,27 @@ protected:
  void saveObjC(const DWARFDebugInfoEntry *InputDieEntry, DIE *OutDIE,
                AttributesInfo &AttrInfo);

  void saveNameRecord(StringEntry *Name, DIE *OutDIE, dwarf::Tag Tag,
  void saveNameRecord(const DWARFDebugInfoEntry *InputDieEntry,
                      StringEntry *Name, DIE *OutDIE, dwarf::Tag Tag,
                      bool AvoidForPubSections);
  void saveNamespaceRecord(StringEntry *Name, DIE *OutDIE, dwarf::Tag Tag,
  void saveNamespaceRecord(const DWARFDebugInfoEntry *InputDieEntry,
                           StringEntry *Name, DIE *OutDIE, dwarf::Tag Tag,
                           TypeEntry *TypeEntry);
  void saveObjCNameRecord(StringEntry *Name, DIE *OutDIE, dwarf::Tag Tag);
  void saveTypeRecord(StringEntry *Name, DIE *OutDIE, dwarf::Tag Tag,
  void saveObjCNameRecord(const DWARFDebugInfoEntry *InputDieEntry,
                          StringEntry *Name, DIE *OutDIE, dwarf::Tag Tag);
  void saveTypeRecord(const DWARFDebugInfoEntry *InputDieEntry,
                      StringEntry *Name, DIE *OutDIE, dwarf::Tag Tag,
                      uint32_t QualifiedNameHash, bool ObjcClassImplementation,
                      TypeEntry *TypeEntry);

  /// Return the output offset of \p InputDieEntry's immediate
  /// non-declaration parent, for use as the DW_IDX_parent field of a name
  /// index entry. Matches classic's one-level lookup: does not walk past a
  /// pruned or declaration parent to find a surviving ancestor. Returns
  /// std::nullopt if there is no usable parent.
  std::optional<uint64_t>
  getDefiningParentOutOffset(const DWARFDebugInfoEntry *InputDieEntry);

  /// Global linking data.
  LinkingGlobalData &GlobalData;

+2 −2
Original line number Diff line number Diff line
@@ -1374,8 +1374,8 @@ void DWARFLinkerImpl::emitDWARFv5DebugNamesSection(const Triple &TargetTriple) {
      case DwarfUnit::AccelType::Namespace:
      case DwarfUnit::AccelType::Type: {
        DebugNames->addName(*DebugStrStrings.getExistingEntry(Info.String),
                            Info.OutOffset, std::nullopt /*ParentDIEOffset*/,
                            Info.Tag, CU->getUniqueID(),
                            Info.OutOffset, Info.ParentOffset, Info.Tag,
                            CU->getUniqueID(),
                            CU->getTag() == dwarf::DW_TAG_type_unit);
      } break;

+4 −0
Original line number Diff line number Diff line
@@ -131,6 +131,10 @@ public:
    /// Output offset of the DIE this entry describes.
    uint64_t OutOffset;

    /// Output offset of the enclosing non-declaration DIE, used for the
    /// DW_IDX_parent field of DWARF 5 name index entries.
    std::optional<uint64_t> ParentOffset;

    /// Hash of the fully qualified name.
    uint32_t QualifiedNameHash = 0;

+4 −0
Original line number Diff line number Diff line
@@ -43,6 +43,9 @@ DWARF: Bucket 0 [
DWARF-NEXT:   Name {{.*}} {
DWARF-NEXT:     Hash: {{.*}}
DWARF-NEXT:     String: {{.*}} "C"
## The first two entries below are emitted into the artificial type unit.
## The parallel linker currently does not emit DW_IDX_parent for TU entries
## (TODO in AcceleratorRecordsSaver.cpp) while the third entry (real CU) does.
DWARF-NEXT:     Entry {{.*}} {
DWARF-NEXT:       Abbrev: {{.*}}
DWARF-NEXT:       Tag: DW_TAG_namespace
@@ -57,6 +60,7 @@ DWARF-NEXT: Entry {{.*}} {
DWARF-NEXT:       Abbrev: {{.*}}
DWARF-NEXT:       Tag: DW_TAG_namespace
DWARF:       DW_IDX_die_offset: 0x0000003c
DWARF-NEXT:  DW_IDX_parent: Entry @ 0x{{0*[1-9a-f][0-9a-f]*}}
DWARF-NEXT:     }

DWARF-NEXT:   }
Loading