Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DWARFLinkerParallel] Add support for -odr mode. #68721

Merged

Conversation

avl-llvm
Copy link
Collaborator

This patch is extracted from D96035, it adds support for the type deduplication mode. With this patch DWARFLinkerParallel handles --odr option. It also processes clang modules.

run-time performance and memory requirements for clang binary --num-threads 16 :

------------------------------------------------------------------------------
                                |  time, sec   |  mem, GB  | .debug_info, MB |
------------------------------------------------------------------------------
dsymutil --odr --linker llvm    |      45      |     13    |       192       |
------------------------------------------------------------------------------
dsymutil --odr --linker apple   |     115      |     11    |       561       |
------------------------------------------------------------------------------

run-time performance and memory requirements for clang binary --num-threads 1 :

------------------------------------------------------------------------------
                                |  time, sec   |  mem, GB  | .debug_info, MB |
------------------------------------------------------------------------------
dsymutil --odr --linker llvm    |     236      |    12.6   |       192       |
------------------------------------------------------------------------------
dsymutil --odr --linker apple   |     187      |    10.0   |       561       |
------------------------------------------------------------------------------

Note: Sometimes DWARFLinkerParallel may produce non-deterministic results.
The reason for that is ambiguous input DWARF. That problem is assumed
to be solved with separate patches(most probably not for DWARFLinkerParallel
but for generated DWARF).

Note: The dependency tracking algorithm handles DW_TAG_imported_module and
DW_TAG_imported_declaration differently than current DWARFLinker. Current
DWARFLinker keeps all content referenced by DW_AT_import attribute despite
the fact whether it references live code or not. This patch keeps only DIEs
referencing live addresses(and all their dependencies)

@llvmbot
Copy link
Collaborator

llvmbot commented Oct 10, 2023

@llvm/pr-subscribers-debuginfo

Author: None (avl-llvm)

Changes

This patch is extracted from D96035, it adds support for the type deduplication mode. With this patch DWARFLinkerParallel handles --odr option. It also processes clang modules.

run-time performance and memory requirements for clang binary --num-threads 16 :

------------------------------------------------------------------------------
                                |  time, sec   |  mem, GB  | .debug_info, MB |
------------------------------------------------------------------------------
dsymutil --odr --linker llvm    |      45      |     13    |       192       |
------------------------------------------------------------------------------
dsymutil --odr --linker apple   |     115      |     11    |       561       |
------------------------------------------------------------------------------

run-time performance and memory requirements for clang binary --num-threads 1 :

------------------------------------------------------------------------------
                                |  time, sec   |  mem, GB  | .debug_info, MB |
------------------------------------------------------------------------------
dsymutil --odr --linker llvm    |     236      |    12.6   |       192       |
------------------------------------------------------------------------------
dsymutil --odr --linker apple   |     187      |    10.0   |       561       |
------------------------------------------------------------------------------

Note: Sometimes DWARFLinkerParallel may produce non-deterministic results.
The reason for that is ambiguous input DWARF. That problem is assumed
to be solved with separate patches(most probably not for DWARFLinkerParallel
but for generated DWARF).

Note: The dependency tracking algorithm handles DW_TAG_imported_module and
DW_TAG_imported_declaration differently than current DWARFLinker. Current
DWARFLinker keeps all content referenced by DW_AT_import attribute despite
the fact whether it references live code or not. This patch keeps only DIEs
referencing live addresses(and all their dependencies)


Patch is 469.13 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/68721.diff

77 Files Affected:

  • (added) llvm/lib/DWARFLinkerParallel/AcceleratorRecordsSaver.cpp (+295)
  • (added) llvm/lib/DWARFLinkerParallel/AcceleratorRecordsSaver.h (+70)
  • (modified) llvm/lib/DWARFLinkerParallel/ArrayList.h (+24-2)
  • (modified) llvm/lib/DWARFLinkerParallel/CMakeLists.txt (+3)
  • (modified) llvm/lib/DWARFLinkerParallel/DIEAttributeCloner.cpp (+187-110)
  • (modified) llvm/lib/DWARFLinkerParallel/DIEAttributeCloner.h (+55-18)
  • (modified) llvm/lib/DWARFLinkerParallel/DIEGenerator.h (+18-3)
  • (modified) llvm/lib/DWARFLinkerParallel/DWARFLinker.cpp (+1)
  • (modified) llvm/lib/DWARFLinkerParallel/DWARFLinkerCompileUnit.cpp (+542-303)
  • (modified) llvm/lib/DWARFLinkerParallel/DWARFLinkerCompileUnit.h (+210-59)
  • (modified) llvm/lib/DWARFLinkerParallel/DWARFLinkerGlobalData.h (+1)
  • (modified) llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.cpp (+336-104)
  • (modified) llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.h (+18-40)
  • (added) llvm/lib/DWARFLinkerParallel/DWARFLinkerTypeUnit.cpp (+391)
  • (added) llvm/lib/DWARFLinkerParallel/DWARFLinkerTypeUnit.h (+138)
  • (modified) llvm/lib/DWARFLinkerParallel/DWARFLinkerUnit.cpp (+46-4)
  • (modified) llvm/lib/DWARFLinkerParallel/DWARFLinkerUnit.h (+41-67)
  • (modified) llvm/lib/DWARFLinkerParallel/DebugLineSectionEmitter.h (+1)
  • (modified) llvm/lib/DWARFLinkerParallel/DependencyTracker.cpp (+632-198)
  • (modified) llvm/lib/DWARFLinkerParallel/DependencyTracker.h (+195-40)
  • (modified) llvm/lib/DWARFLinkerParallel/OutputSections.cpp (+138-26)
  • (modified) llvm/lib/DWARFLinkerParallel/OutputSections.h (+76-17)
  • (added) llvm/lib/DWARFLinkerParallel/SyntheticTypeNameBuilder.cpp (+767)
  • (added) llvm/lib/DWARFLinkerParallel/SyntheticTypeNameBuilder.h (+155)
  • (added) llvm/lib/DWARFLinkerParallel/TypePool.h (+177)
  • (added) llvm/lib/DWARFLinkerParallel/Utils.h (+40)
  • (added) llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/accel-imported-declarations.test (+80)
  • (modified) llvm/test/tools/dsymutil/ARM/accel-imported-declarations.test (+6-14)
  • (modified) llvm/test/tools/dsymutil/ARM/dwarf5-addr-base.test (+32-11)
  • (modified) llvm/test/tools/dsymutil/ARM/dwarf5-dwarf4-combination-macho.test (+21-7)
  • (modified) llvm/test/tools/dsymutil/ARM/dwarf5-macho.test (+16-4)
  • (modified) llvm/test/tools/dsymutil/ARM/dwarf5-str-offsets-base-strx.test (+42-23)
  • (added) llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/dead-stripped.cpp (+67)
  • (added) llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/empty-CU.test (+5)
  • (added) llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/inlined-static-variable.cpp (+74)
  • (added) llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/keep-func.test (+36)
  • (added) llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/odr-anon-namespace.cpp (+68)
  • (added) llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/odr-fwd-declaration.test (+101)
  • (added) llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/odr-fwd-declaration2.test (+131)
  • (added) llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/odr-fwd-declaration3.test (+381)
  • (added) llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/odr-member-functions.cpp (+192)
  • (added) llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/odr-namespace-extension.test (+157)
  • (added) llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/odr-nested-types1.test (+384)
  • (added) llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/odr-nested-types2.test (+374)
  • (added) llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/odr-parents.test (+238)
  • (added) llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/odr-predictable-output.test (+117)
  • (added) llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/odr-predictable-output2.test (+120)
  • (added) llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/odr-recursive-dependence.test (+245)
  • (added) llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/odr-string.test (+180)
  • (added) llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/odr-template-parameters.test (+201)
  • (added) llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/odr-two-units-in-single-file.test (+205)
  • (added) llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/odr-types-in-subprogram1.test (+431)
  • (added) llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/odr-uniquing.cpp (+475)
  • (added) llvm/test/tools/dsymutil/X86/Inputs/String/foo1.o ()
  • (added) llvm/test/tools/dsymutil/X86/Inputs/String/foo2.o ()
  • (added) llvm/test/tools/dsymutil/X86/Inputs/String/foo3.o ()
  • (added) llvm/test/tools/dsymutil/X86/Inputs/String/main.o ()
  • (modified) llvm/test/tools/dsymutil/X86/dead-stripped.cpp (-5)
  • (modified) llvm/test/tools/dsymutil/X86/dummy-debug-map.map (+2-2)
  • (modified) llvm/test/tools/dsymutil/X86/dwarf5-rnglists.test (+2-2)
  • (modified) llvm/test/tools/dsymutil/X86/empty-CU.test (-2)
  • (modified) llvm/test/tools/dsymutil/X86/inlined-static-variable.cpp (+12-6)
  • (modified) llvm/test/tools/dsymutil/X86/keep-func.test (-5)
  • (added) llvm/test/tools/dsymutil/X86/linker-llvm-union-fwd-decl.test (+65)
  • (modified) llvm/test/tools/dsymutil/X86/location-expression.test (+3-3)
  • (modified) llvm/test/tools/dsymutil/X86/modules-empty.m (+3-3)
  • (modified) llvm/test/tools/dsymutil/X86/odr-uniquing.cpp (+2)
  • (modified) llvm/test/tools/dsymutil/X86/op-convert.test (+7-5)
  • (modified) llvm/test/tools/dsymutil/X86/union-fwd-decl.test (-3)
  • (modified) llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-addresses.test (+1-1)
  • (modified) llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-rnglists.test (+12-12)
  • (modified) llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-default.test (+9-9)
  • (modified) llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-func-overlapping-address-ranges.test (+2-2)
  • (modified) llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-maxpc.test (+4-4)
  • (modified) llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-no-garbage.test (+2-2)
  • (modified) llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-unit-overlapping-address-ranges.test (+2-2)
  • (modified) llvm/tools/dsymutil/DwarfLinkerForBinary.cpp (-1)
diff --git a/llvm/lib/DWARFLinkerParallel/AcceleratorRecordsSaver.cpp b/llvm/lib/DWARFLinkerParallel/AcceleratorRecordsSaver.cpp
new file mode 100644
index 000000000000000..5ec25cfe5fd26e1
--- /dev/null
+++ b/llvm/lib/DWARFLinkerParallel/AcceleratorRecordsSaver.cpp
@@ -0,0 +1,295 @@
+//=== AcceleratorRecordsSaver.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "AcceleratorRecordsSaver.h"
+#include "Utils.h"
+#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h"
+#include "llvm/Support/DJB.h"
+
+namespace llvm {
+namespace dwarflinker_parallel {
+
+static uint32_t hashFullyQualifiedName(CompileUnit &InputCU, DWARFDie &InputDIE,
+                                       int ChildRecurseDepth = 0) {
+  const char *Name = nullptr;
+  CompileUnit *CU = &InputCU;
+  std::optional<DWARFFormValue> RefVal;
+
+  if (Error Err = finiteLoop([&]() -> Expected<bool> {
+        if (const char *CurrentName = InputDIE.getName(DINameKind::ShortName))
+          Name = CurrentName;
+
+        if (!(RefVal = InputDIE.find(dwarf::DW_AT_specification)) &&
+            !(RefVal = InputDIE.find(dwarf::DW_AT_abstract_origin)))
+          return false;
+
+        if (!RefVal->isFormClass(DWARFFormValue::FC_Reference))
+          return false;
+
+        std::optional<UnitEntryPairTy> RefDie = CU->resolveDIEReference(
+            *RefVal, ResolveInterCUReferencesMode::Resolve);
+        if (!RefDie)
+          return false;
+
+        if (!RefDie->DieEntry)
+          return false;
+
+        CU = RefDie->CU;
+        InputDIE = RefDie->CU->getDIE(RefDie->DieEntry);
+        return true;
+      })) {
+    consumeError(std::move(Err));
+  }
+
+  if (!Name && InputDIE.getTag() == dwarf::DW_TAG_namespace)
+    Name = "(anonymous namespace)";
+
+  DWARFDie ParentDie = InputDIE.getParent();
+  if (!ParentDie.isValid() || ParentDie.getTag() == dwarf::DW_TAG_compile_unit)
+    return djbHash(Name ? Name : "", djbHash(ChildRecurseDepth ? "" : "::"));
+
+  return djbHash(
+      (Name ? Name : ""),
+      djbHash((Name ? "::" : ""),
+              hashFullyQualifiedName(*CU, ParentDie, ++ChildRecurseDepth)));
+}
+
+void AcceleratorRecordsSaver::save(const DWARFDebugInfoEntry *InputDieEntry,
+                                   DIE *OutDIE, AttributesInfo &AttrInfo,
+                                   TypeEntry *TypeEntry) {
+  if (GlobalData.getOptions().AccelTables.empty())
+    return;
+
+  DWARFDie InputDIE = InUnit.getDIE(InputDieEntry);
+
+  // Look for short name recursively if short name is not known yet.
+  if (AttrInfo.Name == nullptr)
+    if (const char *ShortName = InputDIE.getShortName())
+      AttrInfo.Name = GlobalData.getStringPool().insert(ShortName).first;
+
+  switch (InputDieEntry->getTag()) {
+  case dwarf::DW_TAG_array_type:
+  case dwarf::DW_TAG_class_type:
+  case dwarf::DW_TAG_enumeration_type:
+  case dwarf::DW_TAG_pointer_type:
+  case dwarf::DW_TAG_reference_type:
+  case dwarf::DW_TAG_string_type:
+  case dwarf::DW_TAG_structure_type:
+  case dwarf::DW_TAG_subroutine_type:
+  case dwarf::DW_TAG_typedef:
+  case dwarf::DW_TAG_union_type:
+  case dwarf::DW_TAG_ptr_to_member_type:
+  case dwarf::DW_TAG_set_type:
+  case dwarf::DW_TAG_subrange_type:
+  case dwarf::DW_TAG_base_type:
+  case dwarf::DW_TAG_const_type:
+  case dwarf::DW_TAG_constant:
+  case dwarf::DW_TAG_file_type:
+  case dwarf::DW_TAG_namelist:
+  case dwarf::DW_TAG_packed_type:
+  case dwarf::DW_TAG_volatile_type:
+  case dwarf::DW_TAG_restrict_type:
+  case dwarf::DW_TAG_atomic_type:
+  case dwarf::DW_TAG_interface_type:
+  case dwarf::DW_TAG_unspecified_type:
+  case dwarf::DW_TAG_shared_type:
+  case dwarf::DW_TAG_immutable_type:
+  case dwarf::DW_TAG_rvalue_reference_type: {
+    if (!AttrInfo.IsDeclaration && AttrInfo.Name != nullptr &&
+        !AttrInfo.Name->getKey().empty()) {
+      uint32_t Hash = hashFullyQualifiedName(InUnit, InputDIE);
+
+      uint64_t RuntimeLang =
+          dwarf::toUnsigned(InputDIE.find(dwarf::DW_AT_APPLE_runtime_class))
+              .value_or(0);
+
+      bool ObjCClassIsImplementation =
+          (RuntimeLang == dwarf::DW_LANG_ObjC ||
+           RuntimeLang == dwarf::DW_LANG_ObjC_plus_plus) &&
+          dwarf::toUnsigned(
+              InputDIE.find(dwarf::DW_AT_APPLE_objc_complete_type))
+              .value_or(0);
+
+      saveTypeRecord(AttrInfo.Name, OutDIE, InputDieEntry->getTag(), Hash,
+                     ObjCClassIsImplementation, TypeEntry);
+    }
+  } break;
+  case dwarf::DW_TAG_namespace: {
+    if (AttrInfo.Name == nullptr)
+      AttrInfo.Name =
+          GlobalData.getStringPool().insert("(anonymous namespace)").first;
+
+    saveNamespaceRecord(AttrInfo.Name, OutDIE, InputDieEntry->getTag(),
+                        TypeEntry);
+  } break;
+  case dwarf::DW_TAG_imported_declaration: {
+    if (AttrInfo.Name != nullptr)
+      saveNamespaceRecord(AttrInfo.Name, OutDIE, InputDieEntry->getTag(),
+                          TypeEntry);
+  } break;
+  case dwarf::DW_TAG_compile_unit:
+  case dwarf::DW_TAG_lexical_block: {
+    // Nothing to do.
+  } break;
+  default:
+    if (TypeEntry)
+      // Do not store this kind of accelerator entries for type entries.
+      return;
+
+    if (AttrInfo.HasLiveAddress || AttrInfo.HasRanges) {
+      if (AttrInfo.Name)
+        saveNameRecord(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)
+        if (const char *LinkageName = InputDIE.getLinkageName())
+          AttrInfo.MangledName =
+              GlobalData.getStringPool().insert(LinkageName).first;
+
+      if (AttrInfo.MangledName && AttrInfo.MangledName != AttrInfo.Name)
+        saveNameRecord(AttrInfo.MangledName, OutDIE, InputDieEntry->getTag(),
+                       InputDieEntry->getTag() ==
+                           dwarf::DW_TAG_inlined_subroutine);
+
+      // Strip template parameters from the short name.
+      if (AttrInfo.Name && AttrInfo.MangledName != AttrInfo.Name &&
+          (InputDieEntry->getTag() != dwarf::DW_TAG_inlined_subroutine)) {
+        if (std::optional<StringRef> Name =
+                StripTemplateParameters(AttrInfo.Name->getKey())) {
+          StringEntry *NameWithoutTemplateParams =
+              GlobalData.getStringPool().insert(*Name).first;
+
+          saveNameRecord(NameWithoutTemplateParams, OutDIE,
+                         InputDieEntry->getTag(), true);
+        }
+      }
+
+      if (AttrInfo.Name)
+        saveObjC(InputDieEntry, OutDIE, AttrInfo);
+    }
+    break;
+  }
+}
+
+void AcceleratorRecordsSaver::saveObjC(const DWARFDebugInfoEntry *InputDieEntry,
+                                       DIE *OutDIE, AttributesInfo &AttrInfo) {
+  std::optional<ObjCSelectorNames> Names =
+      getObjCNamesIfSelector(AttrInfo.Name->getKey());
+  if (!Names)
+    return;
+
+  StringEntry *Selector =
+      GlobalData.getStringPool().insert(Names->Selector).first;
+  saveNameRecord(Selector, OutDIE, InputDieEntry->getTag(), true);
+  StringEntry *ClassName =
+      GlobalData.getStringPool().insert(Names->ClassName).first;
+  saveObjCNameRecord(ClassName, OutDIE, InputDieEntry->getTag());
+  if (Names->ClassNameNoCategory) {
+    StringEntry *ClassNameNoCategory =
+        GlobalData.getStringPool().insert(*Names->ClassNameNoCategory).first;
+    saveObjCNameRecord(ClassNameNoCategory, OutDIE, InputDieEntry->getTag());
+  }
+  if (Names->MethodNameNoCategory) {
+    StringEntry *MethodNameNoCategory =
+        GlobalData.getStringPool().insert(*Names->MethodNameNoCategory).first;
+    saveNameRecord(MethodNameNoCategory, OutDIE, InputDieEntry->getTag(), true);
+  }
+}
+
+void AcceleratorRecordsSaver::saveNameRecord(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.Tag = Tag;
+  Info.AvoidForPubSections = AvoidForPubSections;
+
+  OutUnit.getAsCompileUnit()->saveAcceleratorInfo(Info);
+}
+void AcceleratorRecordsSaver::saveNamespaceRecord(StringEntry *Name,
+                                                  DIE *OutDIE, dwarf::Tag Tag,
+                                                  TypeEntry *TypeEntry) {
+  if (OutUnit.isCompileUnit()) {
+    assert(TypeEntry == nullptr);
+    DwarfUnit::AccelInfo Info;
+
+    Info.Type = DwarfUnit::AccelType::Namespace;
+    Info.String = Name;
+    Info.OutOffset = OutDIE->getOffset();
+    Info.Tag = Tag;
+
+    OutUnit.getAsCompileUnit()->saveAcceleratorInfo(Info);
+    return;
+  }
+
+  assert(TypeEntry != nullptr);
+  TypeUnit::TypeUnitAccelInfo Info;
+  Info.Type = DwarfUnit::AccelType::Namespace;
+  Info.String = Name;
+  Info.OutOffset = 0xbaddef;
+  Info.Tag = Tag;
+  Info.OutDIE = OutDIE;
+  Info.TypeEntryBodyPtr = TypeEntry->getValue().load();
+
+  OutUnit.getAsTypeUnit()->saveAcceleratorInfo(Info);
+}
+
+void AcceleratorRecordsSaver::saveObjCNameRecord(StringEntry *Name, DIE *OutDIE,
+                                                 dwarf::Tag Tag) {
+  DwarfUnit::AccelInfo Info;
+
+  Info.Type = DwarfUnit::AccelType::ObjC;
+  Info.String = Name;
+  Info.OutOffset = OutDIE->getOffset();
+  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,
+                                             TypeEntry *TypeEntry) {
+  if (OutUnit.isCompileUnit()) {
+    assert(TypeEntry == nullptr);
+    DwarfUnit::AccelInfo Info;
+
+    Info.Type = DwarfUnit::AccelType::Type;
+    Info.String = Name;
+    Info.OutOffset = OutDIE->getOffset();
+    Info.Tag = Tag;
+    Info.QualifiedNameHash = QualifiedNameHash;
+    Info.ObjcClassImplementation = ObjcClassImplementation;
+
+    OutUnit.getAsCompileUnit()->saveAcceleratorInfo(Info);
+    return;
+  }
+
+  assert(TypeEntry != nullptr);
+  TypeUnit::TypeUnitAccelInfo Info;
+
+  Info.Type = DwarfUnit::AccelType::Type;
+  Info.String = Name;
+  Info.OutOffset = 0xbaddef;
+  Info.Tag = Tag;
+  Info.QualifiedNameHash = QualifiedNameHash;
+  Info.ObjcClassImplementation = ObjcClassImplementation;
+  Info.OutDIE = OutDIE;
+  Info.TypeEntryBodyPtr = TypeEntry->getValue().load();
+  OutUnit.getAsTypeUnit()->saveAcceleratorInfo(Info);
+}
+
+} // end of namespace dwarflinker_parallel
+} // namespace llvm
diff --git a/llvm/lib/DWARFLinkerParallel/AcceleratorRecordsSaver.h b/llvm/lib/DWARFLinkerParallel/AcceleratorRecordsSaver.h
new file mode 100644
index 000000000000000..5e7f4d0c3166fd1
--- /dev/null
+++ b/llvm/lib/DWARFLinkerParallel/AcceleratorRecordsSaver.h
@@ -0,0 +1,70 @@
+//===- AcceleratorRecordsSaver.h --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_DWARFLINKERPARALLEL_ACCELERATORRECORDSSAVER_H
+#define LLVM_LIB_DWARFLINKERPARALLEL_ACCELERATORRECORDSSAVER_H
+
+#include "DIEAttributeCloner.h"
+#include "DWARFLinkerCompileUnit.h"
+#include "DWARFLinkerGlobalData.h"
+#include "DWARFLinkerTypeUnit.h"
+
+namespace llvm {
+namespace dwarflinker_parallel {
+
+/// This class helps to store information for accelerator entries.
+/// It prepares accelerator info for the certain DIE and store it inside
+/// OutUnit.
+class AcceleratorRecordsSaver {
+public:
+  AcceleratorRecordsSaver(LinkingGlobalData &GlobalData, CompileUnit &InUnit,
+                          CompileUnit *OutUnit)
+      : AcceleratorRecordsSaver(GlobalData, InUnit,
+                                CompileUnit::OutputUnitVariantPtr(OutUnit)) {}
+
+  AcceleratorRecordsSaver(LinkingGlobalData &GlobalData, CompileUnit &InUnit,
+                          TypeUnit *OutUnit)
+      : AcceleratorRecordsSaver(GlobalData, InUnit,
+                                CompileUnit::OutputUnitVariantPtr(OutUnit)) {}
+
+  /// Save accelerator info for the specified \p OutDIE inside OutUnit.
+  /// Side effects: set attributes in \p AttrInfo.
+  void save(const DWARFDebugInfoEntry *InputDieEntry, DIE *OutDIE,
+            AttributesInfo &AttrInfo, TypeEntry *TypeEntry);
+
+protected:
+  AcceleratorRecordsSaver(LinkingGlobalData &GlobalData, CompileUnit &InUnit,
+                          CompileUnit::OutputUnitVariantPtr OutUnit)
+      : GlobalData(GlobalData), InUnit(InUnit), OutUnit(OutUnit) {}
+
+  void saveObjC(const DWARFDebugInfoEntry *InputDieEntry, DIE *OutDIE,
+                AttributesInfo &AttrInfo);
+
+  void saveNameRecord(StringEntry *Name, DIE *OutDIE, dwarf::Tag Tag,
+                      bool AvoidForPubSections);
+  void saveNamespaceRecord(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,
+                      uint32_t QualifiedNameHash, bool ObjcClassImplementation,
+                      TypeEntry *TypeEntry);
+
+  /// Global linking data.
+  LinkingGlobalData &GlobalData;
+
+  /// Comiple unit corresponding to input DWARF.
+  CompileUnit &InUnit;
+
+  /// Compile unit or Artificial type unit corresponding to the output DWARF.
+  CompileUnit::OutputUnitVariantPtr OutUnit;
+};
+
+} // end of namespace dwarflinker_parallel
+} // end namespace llvm
+
+#endif // LLVM_LIB_DWARFLINKERPARALLEL_ACCELERATORRECORDSSAVER_H
diff --git a/llvm/lib/DWARFLinkerParallel/ArrayList.h b/llvm/lib/DWARFLinkerParallel/ArrayList.h
index 58d550982c8dfc0..def83f91bc6f319 100644
--- a/llvm/lib/DWARFLinkerParallel/ArrayList.h
+++ b/llvm/lib/DWARFLinkerParallel/ArrayList.h
@@ -21,6 +21,9 @@ namespace dwarflinker_parallel {
 /// Method add() can be called asynchronously.
 template <typename T, size_t ItemsGroupSize = 512> class ArrayList {
 public:
+  ArrayList(parallel::PerThreadBumpPtrAllocator *Allocator)
+      : Allocator(Allocator) {}
+
   /// Add specified \p Item to the list.
   T &add(const T &Item) {
     assert(Allocator);
@@ -73,8 +76,27 @@ template <typename T, size_t ItemsGroupSize = 512> class ArrayList {
     LastGroup = nullptr;
   }
 
-  void setAllocator(parallel::PerThreadBumpPtrAllocator *Allocator) {
-    this->Allocator = Allocator;
+  void sort(function_ref<bool(const T &LHS, const T &RHS)> Comparator) {
+    SmallVector<T> SortedItems;
+    forEach([&](T &Item) { SortedItems.push_back(Item); });
+
+    if (SortedItems.size()) {
+      std::sort(SortedItems.begin(), SortedItems.end(), Comparator);
+
+      size_t SortedItemIdx = 0;
+      forEach([&](T &Item) { Item = SortedItems[SortedItemIdx++]; });
+      assert(SortedItemIdx == SortedItems.size());
+    }
+  }
+
+  size_t size() {
+    size_t Result = 0;
+
+    for (ItemsGroup *CurGroup = GroupsHead; CurGroup != nullptr;
+         CurGroup = CurGroup->Next)
+      Result += CurGroup->getItemsCount();
+
+    return Result;
   }
 
 protected:
diff --git a/llvm/lib/DWARFLinkerParallel/CMakeLists.txt b/llvm/lib/DWARFLinkerParallel/CMakeLists.txt
index d321ecf8d5ce847..b0f0b3910e586ab 100644
--- a/llvm/lib/DWARFLinkerParallel/CMakeLists.txt
+++ b/llvm/lib/DWARFLinkerParallel/CMakeLists.txt
@@ -1,14 +1,17 @@
 add_llvm_component_library(LLVMDWARFLinkerParallel
+  AcceleratorRecordsSaver.cpp
   DependencyTracker.cpp
   DIEAttributeCloner.cpp
   DWARFEmitterImpl.cpp
   DWARFFile.cpp
   DWARFLinker.cpp
   DWARFLinkerCompileUnit.cpp
+  DWARFLinkerTypeUnit.cpp
   DWARFLinkerImpl.cpp
   DWARFLinkerUnit.cpp
   OutputSections.cpp
   StringPool.cpp
+  SyntheticTypeNameBuilder.cpp
 
   ADDITIONAL_HEADER_DIRS
   ${LLVM_MAIN_INCLUDE_DIR}/llvm/DWARFLinkerParallel
diff --git a/llvm/lib/DWARFLinkerParallel/DIEAttributeCloner.cpp b/llvm/lib/DWARFLinkerParallel/DIEAttributeCloner.cpp
index d05fd8d61b85743..81fc57f7cabbb79 100644
--- a/llvm/lib/DWARFLinkerParallel/DIEAttributeCloner.cpp
+++ b/llvm/lib/DWARFLinkerParallel/DIEAttributeCloner.cpp
@@ -13,18 +13,16 @@ namespace llvm {
 namespace dwarflinker_parallel {
 
 void DIEAttributeCloner::clone() {
-  DWARFUnit &U = CU.getOrigUnit();
-
   // Extract and clone every attribute.
-  DWARFDataExtractor Data = U.getDebugInfoExtractor();
+  DWARFDataExtractor Data = InUnit.getOrigUnit().getDebugInfoExtractor();
 
   uint64_t Offset = InputDieEntry->getOffset();
   // Point to the next DIE (generally there is always at least a NULL
   // entry after the current one). If this is a lone
   // DW_TAG_compile_unit without any children, point to the next unit.
-  uint64_t NextOffset = (InputDIEIdx + 1 < U.getNumDIEs())
-                            ? U.getDIEAtIndex(InputDIEIdx + 1).getOffset()
-                            : U.getNextUnitOffset();
+  uint64_t NextOffset = (InputDIEIdx + 1 < InUnit.getOrigUnit().getNumDIEs())
+                            ? InUnit.getDIEAtIndex(InputDIEIdx + 1).getOffset()
+                            : InUnit.getOrigUnit().getNextUnitOffset();
 
   // We could copy the data only if we need to apply a relocation to it. After
   // testing, it seems there is no performance downside to doing the copy
@@ -34,8 +32,8 @@ void DIEAttributeCloner::clone() {
       DWARFDataExtractor(DIECopy, Data.isLittleEndian(), Data.getAddressSize());
 
   // Modify the copy with relocated addresses.
-  CU.getContaingFile().Addresses->applyValidRelocs(DIECopy, Offset,
-                                                   Data.isLittleEndian());
+  InUnit.getContaingFile().Addresses->applyValidRelocs(DIECopy, Offset,
+                                                       Data.isLittleEndian());
 
   // Reset the Offset to 0 as we will be working on the local copy of
   // the data.
@@ -45,17 +43,18 @@ void DIEAttributeCloner::clone() {
   Offset += getULEB128Size(Abbrev->getCode());
 
   // Set current output offset.
-  AttrOutOffset = OutDIE->getOffset();
+  AttrOutOffset = OutUnit.isCompileUnit() ? OutDIE->getOffset() : 0;
   for (const auto &AttrSpec : Abbrev->attributes()) {
     // Check whether current attribute should be skipped.
     if (shouldSkipAttribute(AttrSpec)) {
       DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset,
-                                U.getFormParams());
+                                InUnit.getFormParams());
       continue;
     }
 
     DWARFFormValue Val = AttrSpec.getFormValue();
-    Val.extractValue(Data, &Offset, U.getFormParams(), &U);
+    Val.extractValue(Data, &Offset, InUnit.getFormParams(),
+                     &InUnit.getOrigUnit());
 
     // Clone current attribute.
     switch (AttrSpec.Form) {
@@ -107,10 +106,10 @@ void DIEAttributeCloner::clone() {
       AttrOutOffset += cloneAddressAttr(Val, AttrSpec);
       break;
     default:
-      CU.warn("unsupported attribute form " +
-                  dwarf::FormEncodingString(AttrSpec.Form) +
-                  " in DieAttributeCloner::clone(). Dropping.",
-              InputDieEntry);
+      InUnit.warn("unsupported attribute form " +
+                      dwarf::FormEncodingString(AttrSpec.Form) +
+                      " in DieAttributeCloner::clone(). Dropping.",
+                  InputDieEntry);
     }
   }
 
@@ -118,19 +117,20 @@ void DIEAttributeCloner::clone() {
   // Check if original compile unit already has DW_AT_str_offsets_base
   // attribute.
   if (InputDieEntry->getTag() == dwarf::DW_TAG_compile_unit &&
-      CU.getVersion() >= 5 && !AttrInfo.HasStringOffsetBaseAttr) {
+      InUnit.getVersion() >= 5 && !AttrInfo.HasStringOffsetBaseAttr) {
     DebugInfoOutput...
[truncated]

@JDevlieghere
Copy link
Member

Why is the debug info section so much smaller when using the parallel linker? Is it really that much more efficient than the Apple one, and if so, where are the gains coming from? Are we eliminating types that the dsymutil isn't, like base types? I'm a little skeptical, but I can also imagine the new approach is a lot better because it doesn't need to keep things like forward declarations until it sees a definition, etc.

Note: Sometimes DWARFLinkerParallel may produce non-deterministic results.

I know you're aware but I'll emphasize again that that's a non-starter for us. It sounds like you have patches in the works so that's great!

@avl-llvm
Copy link
Collaborator Author

Why is the debug info section so much smaller when using the parallel linker? Is it really that much more efficient than the Apple one, and if so, where are the gains coming from? Are we eliminating types that the dsymutil isn't, like base types? I'm a little skeptical, but I can also imagine the new approach is a lot better because it doesn't need to keep things like forward declarations until it sees a definition, etc.

yes, the size of debug info is smaller because the parallel linker does ODR deduplication differently:

  1. The most space-saving thing is types merging(*). Parallel linker creates
    an artificial compile unit and moves all types into this unit. All variations
    of partially defined type are finally combined into the single type description.
    This saves most of the space. f.e.

llvm-xray built with llvm linker:

llvm-dwarfdump --debug-info llvm-xray.dSYM/Contents/Resources/DWARF/llvm-xray | grep -c "\(\"vector<unsigned char, std::__1::allocator<unsigned char> >\"\)"
1

llvm-xray built with apple linker:

llvm-dwarfdump --debug-info llvm-xray.dSYM/Contents/Resources/DWARF/llvm-xray | grep -c "\(\"vector<unsigned char, std::__1::allocator<unsigned char> >\"\)"
140

  1. Forward declarations are removed. The type(if the definition is found) or single type declaration
    is moved into the artificial compile unit. All other copies of the declaration are removed.

  2. Not used content of imported namespaces is removed.

Note: Sometimes DWARFLinkerParallel may produce non-deterministic results.

I know you're aware but I'll emphasize again that that's a non-starter for us. It sounds like you have patches in the works so that's great!

yes, I intend to have deterministic output. I think it would be better to achieve this goal with separate patches as the current patch is already huge. The problem that leads to non-deterministic output is caused by inconsistent DWARF(**). It contains different definitions for the same type. So the definition that would be put in the result depends on which definition was handled earlier(which is non-deterministic). I think the proper way to fix that problem would be to fix Clang's DWARF generation to produce consistent results.

(*) Example of types merging:

Lets assume we have following types:


CU1 {
  DW_TAG_class_type {
      DW_TAG_name "vector"
      DW_AT_declaration (true)
      
      DW_TAG_subprogram {
          DW_TAG_name "push_back"
      }
  }
  
 DW_TAG_typedef
   DW_AT_Name "vector"
   DW_AT_type CU1::vector
}

CU2 {
  DW_TAG_class_type {
      DW_TAG_name "vector"
      DW_AT_declaration (true)
      
      DW_TAG_subprogram {
          DW_TAG_name "empty"
      }
  }

  
 DW_TAG_typedef
   DW_AT_Name "vector"
   DW_AT_type CU2::vector  
}

Above partial definitions would be united into single one:

TYPE_CU {
  DW_TAG_class_type {
      DW_TAG_name "vector"
      DW_AT_declaration (true)
      
      DW_TAG_subprogram {
          DW_TAG_name "push_back"
      }
      DW_TAG_subprogram {
          DW_TAG_name "empty"
      }      
  }
}

CU1 {
 DW_TAG_typedef
   DW_AT_Name "vector"
   DW_AT_type TYPE_CU::vector
}

CU2 { 
 DW_TAG_typedef
   DW_AT_Name "vector"
   DW_AT_type TYPE_CU::vector  
}

(**) Example of inconsistent DWARF:

  1. opt binary.

compile unit "llvm/tools/opt/opt.cpp" has following definition:

0x001c4067:     DW_TAG_class_type
                  DW_AT_calling_convention      (DW_CC_pass_by_reference)
                  DW_AT_name    ("DominatorTreeBase<llvm::BasicBlock, false>")
                  DW_AT_byte_size       (0x50)
                  DW_AT_decl_file       ("llvm-project/llvm/include/llvm/IR/Dominators.h")
                  DW_AT_decl_line       (51)

0x001c4090:       DW_TAG_member
                    DW_AT_name  ("Insert")
                    DW_AT_type  (0x002aa3e4 "const UpdateKind")
                    DW_AT_decl_file     ("llvm-project/llvm/include/llvm/Support/GenericDomTree.h")
                    DW_AT_decl_line     (258)
                    DW_AT_external      (true)
                    DW_AT_declaration   (true)
                    DW_AT_accessibility (DW_ACCESS_public)

while compile unit "llvm/lib/Target/X86/X86LowerAMXIntrinsics.cpp" has a bit different:

0x010f6214:     DW_TAG_class_type
                  DW_AT_calling_convention      (DW_CC_pass_by_reference)
                  DW_AT_name    ("DominatorTreeBase<llvm::BasicBlock, false>")
                  DW_AT_byte_size       (0x50)
                  DW_AT_decl_file       ("llvm-project/llvm/include/llvm/IR/Dominators.h")
                  DW_AT_decl_line       (51)

0x010f623d:       DW_TAG_member
                    DW_AT_name  ("Insert")
                    DW_AT_type  (0x011774c9 "const UpdateKind")
                    DW_AT_decl_file     ("llvm-project/llvm/include/llvm/Support/GenericDomTree.h")
                    DW_AT_decl_line     (258)
                    DW_AT_external      (true)
                    DW_AT_declaration   (true)
                    DW_AT_accessibility (DW_ACCESS_public)
                    DW_AT_const_value   (0)   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<

  1. llvm-xray binary.

compile unit "llvm/lib/Bitcode/Reader/BitcodeReader.cpp" has following definition:

0x0260e77a:     DW_TAG_class_type
                  DW_AT_calling_convention      (DW_CC_pass_by_reference)
                  DW_AT_name    ("SmallVectorImpl<llvm::Instruction *>")
                  DW_AT_byte_size       (0x10)
                  DW_AT_decl_file       ("llvm-project/llvm/include/llvm/ADT/SmallVector.h")
                  DW_AT_decl_line       (577)

0x0260e784:       DW_TAG_template_type_parameter
                    DW_AT_type  (0x026cb21f "llvm::Instruction *")
                    DW_AT_name  ("T")   <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

while compile unit "llvm/lib/IR/SSAContext.cpp" has a bit different:

0x03659f68:     DW_TAG_class_type
                  DW_AT_calling_convention      (DW_CC_pass_by_reference)
                  DW_AT_name    ("SmallVectorImpl<llvm::Instruction *>")
                  DW_AT_byte_size       (0x10)
                  DW_AT_decl_file       ("llvm-project/llvm/include/llvm/ADT/SmallVector.h")
                  DW_AT_decl_line       (577)

0x03659f72:       DW_TAG_template_type_parameter
                    DW_AT_type  (0x036716fa "llvm::Instruction *")

@dwblaikie
Copy link
Collaborator

DWARFLinker would still need to be deterministic, even on old/bad/different input - though maybe Apple & other folks would be open to it having an error on "bad" input rather than having to figure out a way to have a deterministic successful output.

That said - I'm always here for a conversation about type consistency in DWARF... so let's take a look:

DominatorTreeBase<llvm::BasicBlock, false> example - Insert is a static constexpr SomeEnum member with an in-class initializer.
Hmm, that might be tricky to do - because technically this isn't a definition (there may be no storage behind it) so I'm not sure we could move the const value out into a separate definition DIE. I guess a definition DIE without a DW_AT_location would be the nearest DWARF-ish way to express it. We /might/ be able to always instantiate the definition value, so put it on the declaration in all cases, but would need some language expertise to check if that's do-able.

SmallVectorImpl<llvm::Instruction *> example - I'm not sure why we'd sometimes put the parameter name in and sometimes not. That seems unlikely to be intentional/particularly deeply thought out, but of uncertain difficulty to fix.

@JDevlieghere
Copy link
Member

DWARFLinker would still need to be deterministic, even on old/bad/different input

+1

though maybe Apple & other folks would be open to it having an error on "bad" input rather than having to figure out a way to have a deterministic successful output.

As long as the input DWARF is valid, we need to be able to link it. Maybe a slow path would be okay if we can guarantee our toolchain doesn't trigger it (i.e. clang & Swift).

@avl-llvm
Copy link
Collaborator Author

DWARFLinker would still need to be deterministic, even on old/bad/different input - though maybe Apple & other folks would be open to it having an error on "bad" input rather than having to figure out a way to have a deterministic successful output.

that could be done with the cost of slower execution.
Currently, first met definition is copied into the resulting DWARF and all other declarations/definitions are skipped.
Instead all met declarations/definitions could be united. i.e. final type description would have attributes from all type copies. In that case final DWARF would be the same despite of the order of compile units handling.

That said - I'm always here for a conversation about type consistency in DWARF... so let's take a look:

DominatorTreeBase<llvm::BasicBlock, false> example - Insert is a static constexpr SomeEnum member with an in-class initializer. Hmm, that might be tricky to do - because technically this isn't a definition (there may be no storage behind it) so I'm not sure we could move the const value out into a separate definition DIE. I guess a definition DIE without a DW_AT_location would be the nearest DWARF-ish way to express it. We /might/ be able to always instantiate the definition value, so put it on the declaration in all cases, but would need some language expertise to check if that's do-able.

Speaking of original example, Do I correctly understand that "DW_AT_const_value (0)" should be in both type definition copies(0x001c4090 and 0x010f623d) in currently generated DWARF?

SmallVectorImpl<llvm::Instruction *> example - I'm not sure why we'd sometimes put the parameter name in and sometimes not. That seems unlikely to be intentional/particularly deeply thought out, but of uncertain difficulty to fix.

@avl-llvm
Copy link
Collaborator Author

DWARFLinker would still need to be deterministic, even on old/bad/different input

+1

though maybe Apple & other folks would be open to it having an error on "bad" input rather than having to figure out a way to have a deterministic successful output.

As long as the input DWARF is valid, we need to be able to link it. Maybe a slow path would be okay if we can guarantee our toolchain doesn't trigger it (i.e. clang & Swift).

yes, more slower but deterministic handling is in my future plans. And we can use slower version with unknown sources and faster version with DWARF generated by clang&Swift (if inconsistent cases would be resolved).

@dwblaikie
Copy link
Collaborator

That said - I'm always here for a conversation about type consistency in DWARF... so let's take a look:
DominatorTreeBase<llvm::BasicBlock, false> example - Insert is a static constexpr SomeEnum member with an in-class initializer. Hmm, that might be tricky to do - because technically this isn't a definition (there may be no storage behind it) so I'm not sure we could move the const value out into a separate definition DIE. I guess a definition DIE without a DW_AT_location would be the nearest DWARF-ish way to express it. We /might/ be able to always instantiate the definition value, so put it on the declaration in all cases, but would need some language expertise to check if that's do-able.

Speaking of original example, Do I correctly understand that "DW_AT_const_value (0)" should be in both type definition copies(0x001c4090 and 0x010f623d) in currently generated DWARF?

I'm not actually sure. I'm not sure if we can always compute the value reliably in every context (some parts of templates we're not allowed to instantiate only in certain contexts).

If we couldn't do it in all cases, maybe we could not put the value on the declaration in all cases - and use a definition DIE to attach the value to (& not put a DW_AT_location on that definition, because it might not have storage in translation units, etc).

@avl-llvm
Copy link
Collaborator Author

avl-llvm commented Oct 17, 2023

Speaking of original example, Do I correctly understand that "DW_AT_const_value (0)" should be in both type definition copies(0x001c4090 and 0x010f623d) in currently generated DWARF?

I'm not actually sure. I'm not sure if we can always compute the value reliably in every context (some parts of templates we're not allowed to instantiate only in certain contexts).

If we couldn't do it in all cases, maybe we could not put the value on the declaration in all cases - and use a definition DIE to attach the value to (& not put a DW_AT_location on that definition, because it might not have storage in translation units, etc).

I see. Thank you for explanations.

@avl-llvm
Copy link
Collaborator Author

friendly ping.

Michael137 added a commit to Michael137/llvm-project that referenced this pull request Nov 6, 2023
…eclarations

Since `f07cf5aaf96a70123530239d07c1e655dc91b98d` we emit a definition
for any static data member with a constant initializer. That definition
will either have a location or constant attached to it. So having the
constant on the declaration is redundant. It also helps with the
type merging inconsistencies encountered in
llvm#68721
@avl-llvm
Copy link
Collaborator Author

friendly ping.

@avl-llvm
Copy link
Collaborator Author

@JDevlieghere @adrian-prantl are there anything which should be done with this patch before integration?

@JDevlieghere
Copy link
Member

Did we come up with a solution for determinism that doesn't depend on the generator? My understanding was that we're okay with taking a penalty in terms of performance, but does the code reflect this, or will the current version of this patch still generate non-deterministic results with other producers or older version of clang that don't have Michael's changes?

@avl-llvm
Copy link
Collaborator Author

Did we come up with a solution for determinism that doesn't depend on the generator? My understanding was that we're okay with taking a penalty in terms of performance, but does the code reflect this, or will the current version of this patch still generate non-deterministic results with other producers or older version of clang that don't have Michael's changes?

This version of patch will produce non-deterministic results if input have non-consistent DWARF(these are old-producers and current clang(even with Michael's patch)). I proposed to implement full-deterministic version as a follow-up patch. Current patch is already huge and it would be good to reduce it's size. I suggest following order of patches:

  1. this patch.
  2. patch refactoring directory structure. So that DWARFLinker directory contains code for common library, Apple linker, LLVM linker.
  3. add --deterministic cl option.
  4. implement full deterministic mode which will produce deterministic output for current clang and old-producers.

What do you think?

Copy link
Member

@JDevlieghere JDevlieghere left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works for me 👍

@avl-llvm
Copy link
Collaborator Author

Thanks!

This patch is extracted from D96035, it adds support for the type
deduplication mode. With this patch DWARFLinkerParallel handles
--odr option. It also processes clang modules.
@avl-llvm avl-llvm force-pushed the avl-llvm/add-odr-to-dwarflinkerparallel branch from bef2dd8 to 66b03b2 Compare November 22, 2023 23:17
@avl-llvm avl-llvm merged commit b61ac4a into llvm:main Nov 23, 2023
3 checks passed
@avl-llvm avl-llvm deleted the avl-llvm/add-odr-to-dwarflinkerparallel branch November 23, 2023 10:56
@mgorny
Copy link
Member

mgorny commented Nov 25, 2023

This change broke building on 32-bit platforms, see #73267.

@avl-llvm
Copy link
Collaborator Author

This change broke building on 32-bit platforms, see #73267.

The fix for that problem #73388

Michael137 added a commit that referenced this pull request Nov 28, 2023
…mber declarations (#73626)

In #71780 we started emitting
definitions for all static data-members with constant initialisers, even
if they were constants (i.e., didn't have a location). We also dropped
the DW_AT_const_value from the declaration to [help resolve
inconsistencies during type merging in the
DWARFParallelLinker](#68721).
However, for static data members that do have locations, we wouldn't
emit a DW_AT_const_value on it, assuming that the consumer knows how to
read the value using the location. This broke some consumers that really
wanted to find a DW_AT_const_value. Ultimately we want to attach a
DW_AT_const_value to definitions that have a location too. But to fix
consumers broken by said change, this patch adds the constant back onto
the declaration. This is what we used to do prior to
#71780
felipepiovezan pushed a commit to felipepiovezan/llvm-project that referenced this pull request Feb 2, 2024
This patch is extracted from D96035, it adds support for the type
deduplication mode. With this patch DWARFLinkerParallel handles --odr
option. It also processes clang modules.

(cherry picked from commit b61ac4a)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants