[llvm] 1506e4c - [DWARFLinkerParallel] Add limited functionality to DWARFLinkerParallel.
Alexey Lapshin via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 17 00:49:17 PDT 2023
Author: Alexey Lapshin
Date: 2023-08-17T09:47:34+02:00
New Revision: 1506e4c77624aa3d208658fd356b9caccc1a713f
URL: https://github.com/llvm/llvm-project/commit/1506e4c77624aa3d208658fd356b9caccc1a713f
DIFF: https://github.com/llvm/llvm-project/commit/1506e4c77624aa3d208658fd356b9caccc1a713f.diff
LOG: [DWARFLinkerParallel] Add limited functionality to DWARFLinkerParallel.
This patch is extracted from D96035, it adds support for the existing
DWARFLinker functionality. What is not supported yet:
1. Types deduplication(--odr mode).
2. Modules deduplication.
3. Generation of index tables.
Differential Revision: https://reviews.llvm.org/D153268
Added:
llvm/lib/DWARFLinkerParallel/ArrayList.h
llvm/lib/DWARFLinkerParallel/DIEAttributeCloner.cpp
llvm/lib/DWARFLinkerParallel/DIEAttributeCloner.h
llvm/lib/DWARFLinkerParallel/DIEGenerator.h
llvm/lib/DWARFLinkerParallel/DWARFFile.cpp
llvm/lib/DWARFLinkerParallel/DWARFLinkerCompileUnit.cpp
llvm/lib/DWARFLinkerParallel/DWARFLinkerGlobalData.h
llvm/lib/DWARFLinkerParallel/DWARFLinkerUnit.cpp
llvm/lib/DWARFLinkerParallel/DebugLineSectionEmitter.h
llvm/lib/DWARFLinkerParallel/DependencyTracker.cpp
llvm/lib/DWARFLinkerParallel/DependencyTracker.h
llvm/lib/DWARFLinkerParallel/IndexedValuesMap.h
llvm/lib/DWARFLinkerParallel/StringEntryToDwarfStringPoolEntryMap.h
llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/dwarf5-dwarf4-combination-macho.test
llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/dwarf5-macho.test
llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/fat-dylib-update.test
llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/obfuscated.test
llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-linking-bundle.test
llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-lto-dw4-linking-x86.test
llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-lto-linking-x86.test
llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-with-libfat-test.test
llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/empty_range.s
llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/frame-1.test
llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/frame-2.test
llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/multiple-inputs.test
Modified:
llvm/include/llvm/CodeGen/DwarfStringPoolEntry.h
llvm/include/llvm/DWARFLinker/DWARFLinker.h
llvm/include/llvm/DWARFLinkerParallel/AddressesMap.h
llvm/include/llvm/DWARFLinkerParallel/DWARFFile.h
llvm/include/llvm/DWARFLinkerParallel/StringPool.h
llvm/include/llvm/DebugInfo/DWARF/DWARFDebugMacro.h
llvm/lib/DWARFLinker/DWARFLinker.cpp
llvm/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp
llvm/lib/DWARFLinkerParallel/CMakeLists.txt
llvm/lib/DWARFLinkerParallel/DWARFEmitterImpl.cpp
llvm/lib/DWARFLinkerParallel/DWARFEmitterImpl.h
llvm/lib/DWARFLinkerParallel/DWARFLinkerCompileUnit.h
llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.cpp
llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.h
llvm/lib/DWARFLinkerParallel/DWARFLinkerUnit.h
llvm/lib/DWARFLinkerParallel/OutputSections.cpp
llvm/lib/DWARFLinkerParallel/OutputSections.h
llvm/test/tools/dsymutil/ARM/call-pc-reloc.test
llvm/test/tools/dsymutil/ARM/dwarf5-addr-base.test
llvm/test/tools/dsymutil/ARM/dwarf5-addrx-0x0-last.test
llvm/test/tools/dsymutil/ARM/dwarf5-str-offsets-base-strx.test
llvm/test/tools/dsymutil/ARM/empty-map.test
llvm/test/tools/dsymutil/ARM/extern-alias.test
llvm/test/tools/dsymutil/ARM/fat-arch-name.test
llvm/test/tools/dsymutil/ARM/fat-arch-not-found.test
llvm/test/tools/dsymutil/ARM/inlined-low_pc.c
llvm/test/tools/dsymutil/ARM/preload.test
llvm/test/tools/dsymutil/ARM/scattered.c
llvm/test/tools/dsymutil/ARM/thumb.c
llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-linking-x86.test
llvm/test/tools/dsymutil/X86/alias.test
llvm/test/tools/dsymutil/X86/call-site-entry-linking.test
llvm/test/tools/dsymutil/X86/call-site-entry-reloc.test
llvm/test/tools/dsymutil/X86/common-sym-multi.test
llvm/test/tools/dsymutil/X86/common-sym.test
llvm/test/tools/dsymutil/X86/custom-line-table.test
llvm/test/tools/dsymutil/X86/darwin-bundle.test
llvm/test/tools/dsymutil/X86/dead-stripped.cpp
llvm/test/tools/dsymutil/X86/debug-loc-base-addr.test
llvm/test/tools/dsymutil/X86/dwarf4-linetable.test
llvm/test/tools/dsymutil/X86/dwarf5-addrx.test
llvm/test/tools/dsymutil/X86/dwarf5-call-site-entry-reloc.test
llvm/test/tools/dsymutil/X86/dwarf5-dw-op-addrx.test
llvm/test/tools/dsymutil/X86/dwarf5-linetable.test
llvm/test/tools/dsymutil/X86/dwarf5-loclists.test
llvm/test/tools/dsymutil/X86/dwarf5-rnglists.test
llvm/test/tools/dsymutil/X86/eh_frame.test
llvm/test/tools/dsymutil/X86/empty-CU.test
llvm/test/tools/dsymutil/X86/fat-archive-input-i386.test
llvm/test/tools/dsymutil/X86/fat-object-input-x86_64.test
llvm/test/tools/dsymutil/X86/fat-object-input-x86_64h.test
llvm/test/tools/dsymutil/X86/generate-empty-CU.test
llvm/test/tools/dsymutil/X86/global_downgraded_to_static.c
llvm/test/tools/dsymutil/X86/inlined-static-variable.cpp
llvm/test/tools/dsymutil/X86/keep-func.test
llvm/test/tools/dsymutil/X86/label.test
llvm/test/tools/dsymutil/X86/label2.test
llvm/test/tools/dsymutil/X86/lc_build_version.test
llvm/test/tools/dsymutil/X86/location-expression.test
llvm/test/tools/dsymutil/X86/mismatch.m
llvm/test/tools/dsymutil/X86/modules-dwarf-version.m
llvm/test/tools/dsymutil/X86/modules-empty.m
llvm/test/tools/dsymutil/X86/multiple-inputs.test
llvm/test/tools/dsymutil/X86/object-prefix-path.test
llvm/test/tools/dsymutil/X86/op-convert-offset.test
llvm/test/tools/dsymutil/X86/op-convert.test
llvm/test/tools/dsymutil/X86/papertrail-warnings.test
llvm/test/tools/dsymutil/X86/reflection-dump.test
llvm/test/tools/dsymutil/X86/remarks-linking-archive.text
llvm/test/tools/dsymutil/X86/remarks-linking-bundle-empty.test
llvm/test/tools/dsymutil/X86/remarks-linking-bundle.test
llvm/test/tools/dsymutil/X86/remarks-linking-fat-bundle.test
llvm/test/tools/dsymutil/X86/reproducer.test
llvm/test/tools/dsymutil/X86/statistics.test
llvm/test/tools/dsymutil/X86/swift-ast-x86_64.test
llvm/test/tools/dsymutil/X86/swift-dwarf-loc.test
llvm/test/tools/dsymutil/X86/tail-call-linking.test
llvm/test/tools/dsymutil/X86/thinlto.test
llvm/test/tools/dsymutil/X86/timestamp-mismatch.test
llvm/test/tools/dsymutil/X86/tls-variable.test
llvm/test/tools/dsymutil/X86/union-fwd-decl.test
llvm/test/tools/dsymutil/X86/verify.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf4-macro-short.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf4-macro.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-addresses.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-attributes.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-line-str.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-loclists.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro-opcodeop.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro-short.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-rnglists.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-default.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-func-overlapping-address-ranges.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-maxpc.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-no-garbage.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-unit-overlapping-address-ranges.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/verify.test
llvm/tools/dsymutil/BinaryHolder.cpp
llvm/tools/dsymutil/BinaryHolder.h
llvm/tools/dsymutil/DwarfLinkerForBinary.cpp
llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp
llvm/unittests/CodeGen/DwarfStringPoolEntryRefTest.cpp
llvm/unittests/DWARFLinkerParallel/CMakeLists.txt
llvm/unittests/DWARFLinkerParallel/StringPoolTest.cpp
Removed:
llvm/include/llvm/DWARFLinkerParallel/StringTable.h
llvm/test/tools/llvm-dwarfutil/ELF/X86/DWARFLinkerParallel/gc-default.test
llvm/unittests/DWARFLinkerParallel/StringTableTest.cpp
################################################################################
diff --git a/llvm/include/llvm/CodeGen/DwarfStringPoolEntry.h b/llvm/include/llvm/CodeGen/DwarfStringPoolEntry.h
index 7822ebf2eb0999..188dfbe790e2ce 100644
--- a/llvm/include/llvm/CodeGen/DwarfStringPoolEntry.h
+++ b/llvm/include/llvm/CodeGen/DwarfStringPoolEntry.h
@@ -27,29 +27,34 @@ struct DwarfStringPoolEntry {
bool isIndexed() const { return Index != NotIndexed; }
};
+/// DwarfStringPoolEntry with string keeping externally.
+struct DwarfStringPoolEntryWithExtString : public DwarfStringPoolEntry {
+ StringRef String;
+};
+
/// DwarfStringPoolEntryRef: Dwarf string pool entry reference.
///
/// Dwarf string pool entry keeps string value and its data.
/// There are two variants how data are represented:
///
-/// 1. By value - StringMapEntry<DwarfStringPoolEntry>.
-/// 2. By pointer - StringMapEntry<DwarfStringPoolEntry *>.
+/// 1. String data in pool - StringMapEntry<DwarfStringPoolEntry>.
+/// 2. External string data - DwarfStringPoolEntryWithExtString.
///
-/// The "By pointer" variant allows for reducing memory usage for the case
-/// when string pool entry does not have data: it keeps the null pointer
-/// and so no need to waste space for the full DwarfStringPoolEntry.
-/// It is recommended to use "By pointer" variant if not all entries
-/// of dwarf string pool have corresponding DwarfStringPoolEntry.
+/// The external data variant allows reducing memory usage for the case
+/// when string pool entry does not have data: string entry does not
+/// keep any data and so no need to waste space for the full
+/// DwarfStringPoolEntry. It is recommended to use external variant if not all
+/// entries of dwarf string pool have corresponding DwarfStringPoolEntry.
class DwarfStringPoolEntryRef {
/// Pointer type for "By value" string entry.
using ByValStringEntryPtr = const StringMapEntry<DwarfStringPoolEntry> *;
- /// Pointer type for "By pointer" string entry.
- using ByPtrStringEntryPtr = const StringMapEntry<DwarfStringPoolEntry *> *;
+ /// Pointer type for external string entry.
+ using ExtStringEntryPtr = const DwarfStringPoolEntryWithExtString *;
/// Pointer to the dwarf string pool Entry.
- PointerUnion<ByValStringEntryPtr, ByPtrStringEntryPtr> MapEntry = nullptr;
+ PointerUnion<ByValStringEntryPtr, ExtStringEntryPtr> MapEntry = nullptr;
public:
DwarfStringPoolEntryRef() = default;
@@ -61,10 +66,8 @@ class DwarfStringPoolEntryRef {
/// ASSUMPTION: DwarfStringPoolEntryRef keeps pointer to \p Entry,
/// thus specified entry mustn`t be reallocated.
- DwarfStringPoolEntryRef(const StringMapEntry<DwarfStringPoolEntry *> &Entry)
- : MapEntry(&Entry) {
- assert(cast<ByPtrStringEntryPtr>(MapEntry)->second != nullptr);
- }
+ DwarfStringPoolEntryRef(const DwarfStringPoolEntryWithExtString &Entry)
+ : MapEntry(&Entry) {}
explicit operator bool() const { return !MapEntry.isNull(); }
@@ -88,7 +91,7 @@ class DwarfStringPoolEntryRef {
if (isa<ByValStringEntryPtr>(MapEntry))
return cast<ByValStringEntryPtr>(MapEntry)->first();
- return cast<ByPtrStringEntryPtr>(MapEntry)->first();
+ return cast<ExtStringEntryPtr>(MapEntry)->String;
}
/// \returns the entire string pool entry for convenience.
@@ -96,7 +99,7 @@ class DwarfStringPoolEntryRef {
if (isa<ByValStringEntryPtr>(MapEntry))
return cast<ByValStringEntryPtr>(MapEntry)->second;
- return *cast<ByPtrStringEntryPtr>(MapEntry)->second;
+ return *cast<ExtStringEntryPtr>(MapEntry);
}
bool operator==(const DwarfStringPoolEntryRef &X) const {
diff --git a/llvm/include/llvm/DWARFLinker/DWARFLinker.h b/llvm/include/llvm/DWARFLinker/DWARFLinker.h
index 7a19858f426792..e615f7bbef0212 100644
--- a/llvm/include/llvm/DWARFLinker/DWARFLinker.h
+++ b/llvm/include/llvm/DWARFLinker/DWARFLinker.h
@@ -270,9 +270,11 @@ using UnitListTy = std::vector<std::unique_ptr<CompileUnit>>;
/// and its address map.
class DWARFFile {
public:
+ using UnloadCallbackTy = std::function<void(StringRef FileName)>;
DWARFFile(StringRef Name, std::unique_ptr<DWARFContext> Dwarf,
std::unique_ptr<AddressesMap> Addresses,
- const std::vector<std::string> &Warnings)
+ const std::vector<std::string> &Warnings,
+ UnloadCallbackTy = nullptr)
: FileName(Name), Dwarf(std::move(Dwarf)),
Addresses(std::move(Addresses)), Warnings(Warnings) {}
diff --git a/llvm/include/llvm/DWARFLinkerParallel/AddressesMap.h b/llvm/include/llvm/DWARFLinkerParallel/AddressesMap.h
index 5d735abab419e6..22fbec20d7d376 100644
--- a/llvm/include/llvm/DWARFLinkerParallel/AddressesMap.h
+++ b/llvm/include/llvm/DWARFLinkerParallel/AddressesMap.h
@@ -10,8 +10,10 @@
#define LLVM_DWARFLINKERPARALLEL_ADDRESSESMAP_H
#include "llvm/ADT/AddressRanges.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFDie.h"
#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
+#include "llvm/DebugInfo/DWARF/DWARFUnit.h"
#include <cstdint>
namespace llvm {
@@ -62,6 +64,108 @@ class AddressesMap {
/// Erases all data.
virtual void clear() = 0;
+
+ /// This function checks whether variable has DWARF expression containing
+ /// operation referencing live address(f.e. DW_OP_addr, DW_OP_addrx...).
+ /// \returns first is true if the expression has an operation referencing an
+ /// address.
+ /// second is the relocation adjustment value if the live address is
+ /// referenced.
+ std::pair<bool, std::optional<int64_t>>
+ getVariableRelocAdjustment(const DWARFDie &DIE) {
+ assert((DIE.getTag() == dwarf::DW_TAG_variable ||
+ DIE.getTag() == dwarf::DW_TAG_constant) &&
+ "Wrong type of input die");
+
+ const auto *Abbrev = DIE.getAbbreviationDeclarationPtr();
+
+ // Check if DIE has DW_AT_location attribute.
+ DWARFUnit *U = DIE.getDwarfUnit();
+ std::optional<uint32_t> LocationIdx =
+ Abbrev->findAttributeIndex(dwarf::DW_AT_location);
+ if (!LocationIdx)
+ return std::make_pair(false, std::nullopt);
+
+ // Get offset to the DW_AT_location attribute.
+ uint64_t AttrOffset =
+ Abbrev->getAttributeOffsetFromIndex(*LocationIdx, DIE.getOffset(), *U);
+
+ // Get value of the DW_AT_location attribute.
+ std::optional<DWARFFormValue> LocationValue =
+ Abbrev->getAttributeValueFromOffset(*LocationIdx, AttrOffset, *U);
+ if (!LocationValue)
+ return std::make_pair(false, std::nullopt);
+
+ // Check that DW_AT_location attribute is of 'exprloc' class.
+ // Handling value of location expressions for attributes of 'loclist'
+ // class is not implemented yet.
+ std::optional<ArrayRef<uint8_t>> Expr = LocationValue->getAsBlock();
+ if (!Expr)
+ return std::make_pair(false, std::nullopt);
+
+ // Parse 'exprloc' expression.
+ DataExtractor Data(toStringRef(*Expr), U->getContext().isLittleEndian(),
+ U->getAddressByteSize());
+ DWARFExpression Expression(Data, U->getAddressByteSize(),
+ U->getFormParams().Format);
+
+ bool HasLocationAddress = false;
+ uint64_t CurExprOffset = 0;
+ for (DWARFExpression::iterator It = Expression.begin();
+ It != Expression.end(); ++It) {
+ DWARFExpression::iterator NextIt = It;
+ ++NextIt;
+
+ const DWARFExpression::Operation &Op = *It;
+ switch (Op.getCode()) {
+ case dwarf::DW_OP_const2u:
+ case dwarf::DW_OP_const4u:
+ case dwarf::DW_OP_const8u:
+ case dwarf::DW_OP_const2s:
+ case dwarf::DW_OP_const4s:
+ case dwarf::DW_OP_const8s:
+ if (NextIt == Expression.end() || !isTlsAddressCode(NextIt->getCode()))
+ break;
+ [[fallthrough]];
+ case dwarf::DW_OP_addr: {
+ HasLocationAddress = true;
+ // Check relocation for the address.
+ if (std::optional<int64_t> RelocAdjustment =
+ getExprOpAddressRelocAdjustment(*U, Op,
+ AttrOffset + CurExprOffset,
+ AttrOffset + Op.getEndOffset()))
+ return std::make_pair(HasLocationAddress, *RelocAdjustment);
+ } break;
+ case dwarf::DW_OP_constx:
+ case dwarf::DW_OP_addrx: {
+ HasLocationAddress = true;
+ if (std::optional<uint64_t> AddressOffset =
+ DIE.getDwarfUnit()->getIndexedAddressOffset(
+ Op.getRawOperand(0))) {
+ // Check relocation for the address.
+ if (std::optional<int64_t> RelocAdjustment =
+ getExprOpAddressRelocAdjustment(
+ *U, Op, *AddressOffset,
+ *AddressOffset +
+ DIE.getDwarfUnit()->getAddressByteSize()))
+ return std::make_pair(HasLocationAddress, *RelocAdjustment);
+ }
+ } break;
+ default: {
+ // Nothing to do.
+ } break;
+ }
+ CurExprOffset = Op.getEndOffset();
+ }
+
+ return std::make_pair(HasLocationAddress, std::nullopt);
+ }
+
+protected:
+ inline bool isTlsAddressCode(uint8_t DW_OP_Code) {
+ return DW_OP_Code == dwarf::DW_OP_form_tls_address ||
+ DW_OP_Code == dwarf::DW_OP_GNU_push_tls_address;
+ }
};
} // end of namespace dwarflinker_parallel
diff --git a/llvm/include/llvm/DWARFLinkerParallel/DWARFFile.h b/llvm/include/llvm/DWARFLinkerParallel/DWARFFile.h
index c20d59f9771d9d..e48150ef2339e2 100644
--- a/llvm/include/llvm/DWARFLinkerParallel/DWARFFile.h
+++ b/llvm/include/llvm/DWARFLinkerParallel/DWARFFile.h
@@ -30,14 +30,7 @@ class DWARFFile {
DWARFFile(StringRef Name, std::unique_ptr<DWARFContext> Dwarf,
std::unique_ptr<AddressesMap> Addresses,
const std::vector<std::string> &Warnings,
- UnloadCallbackTy UnloadFunc = nullptr)
- : FileName(Name), Dwarf(std::move(Dwarf)),
- Addresses(std::move(Addresses)), Warnings(Warnings),
- UnloadFunc(UnloadFunc) {
- if (this->Dwarf)
- Endianess = this->Dwarf->isLittleEndian() ? support::endianness::little
- : support::endianness::big;
- }
+ UnloadCallbackTy UnloadFunc = nullptr);
/// Object file name.
StringRef FileName;
@@ -51,9 +44,6 @@ class DWARFFile {
/// Warnings for object file.
const std::vector<std::string> &Warnings;
- /// Endiannes of source DWARF information.
- support::endianness Endianess = support::endianness::little;
-
/// Callback to the module keeping object file to unload.
UnloadCallbackTy UnloadFunc;
diff --git a/llvm/include/llvm/DWARFLinkerParallel/StringPool.h b/llvm/include/llvm/DWARFLinkerParallel/StringPool.h
index 44383ed6c7ddcc..8608476a8dc794 100644
--- a/llvm/include/llvm/DWARFLinkerParallel/StringPool.h
+++ b/llvm/include/llvm/DWARFLinkerParallel/StringPool.h
@@ -21,7 +21,7 @@ namespace dwarflinker_parallel {
/// StringEntry keeps data of the string: the length, external offset
/// and a string body which is placed right after StringEntry.
-using StringEntry = StringMapEntry<DwarfStringPoolEntry *>;
+using StringEntry = StringMapEntry<std::nullopt_t>;
class StringPoolEntryInfo {
public:
@@ -64,6 +64,8 @@ class StringPool
parallel::PerThreadBumpPtrAllocator &getAllocatorRef() { return Allocator; }
+ void clear() { Allocator.Reset(); }
+
private:
parallel::PerThreadBumpPtrAllocator Allocator;
};
diff --git a/llvm/include/llvm/DWARFLinkerParallel/StringTable.h b/llvm/include/llvm/DWARFLinkerParallel/StringTable.h
deleted file mode 100644
index 4f8aece521d85d..00000000000000
--- a/llvm/include/llvm/DWARFLinkerParallel/StringTable.h
+++ /dev/null
@@ -1,88 +0,0 @@
-//===- StringTable.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_DWARFLINKERPARALLEL_STRINGTABLE_H
-#define LLVM_DWARFLINKERPARALLEL_STRINGTABLE_H
-
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/DWARFLinkerParallel/StringPool.h"
-
-namespace llvm {
-namespace dwarflinker_parallel {
-
-using StringsVector = SmallVector<StringEntry *>;
-
-/// This class prepares strings for emission into .debug_str table:
-/// translates string if necessary, assigns index and offset, keeps in order.
-class StringTable {
-public:
- StringTable(StringPool &Strings,
- std::function<StringRef(StringRef)> StringsTranslator)
- : Strings(Strings), StringsTranslator(StringsTranslator) {}
- ~StringTable() {}
-
- /// Add string to the vector of strings which should be emitted.
- /// Translate input string if neccessary, assign index and offset.
- /// \returns updated string entry.
- StringEntry *add(StringEntry *String) {
- // Translate string if necessary.
- if (StringsTranslator)
- String = Strings.insert(StringsTranslator(String->first())).first;
-
- // Store String for emission and assign index and offset.
- if (String->getValue() == nullptr) {
- DwarfStringPoolEntry *NewEntry =
- Strings.getAllocatorRef().Allocate<DwarfStringPoolEntry>();
-
- NewEntry->Symbol = nullptr;
- NewEntry->Index = StringEntriesForEmission.size();
-
- if (StringEntriesForEmission.empty())
- NewEntry->Offset = 0;
- else {
- StringEntry *PrevString = StringEntriesForEmission.back();
- NewEntry->Offset =
- PrevString->getValue()->Offset + PrevString->getKeyLength() + 1;
- }
-
- String->getValue() = NewEntry;
- StringEntriesForEmission.push_back(String);
- }
-
- return String;
- }
-
- /// Erase contents of StringsForEmission.
- void clear() { StringEntriesForEmission.clear(); }
-
- /// Enumerate all strings in sequential order and call \p Handler for each
- /// string.
- void forEach(function_ref<void(DwarfStringPoolEntryRef)> Handler) const {
- for (const StringEntry *Entry : StringEntriesForEmission)
- Handler(*Entry);
- }
-
- std::function<StringRef(StringRef)> getTranslator() {
- return StringsTranslator;
- }
-
-protected:
- /// List of strings for emission.
- StringsVector StringEntriesForEmission;
-
- /// String pool for the translated strings.
- StringPool &Strings;
-
- /// Translator for the strings.
- std::function<StringRef(StringRef)> StringsTranslator;
-};
-
-} // end of namespace dwarflinker_parallel
-} // end namespace llvm
-
-#endif // LLVM_DWARFLINKERPARALLEL_STRINGTABLE_H
diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugMacro.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugMacro.h
index 10d4eb25a921b1..6b1b2ae6d7e049 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugMacro.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugMacro.h
@@ -22,6 +22,7 @@ class DwarfStreamer;
class DWARFDebugMacro {
friend DwarfStreamer;
+ friend dwarflinker_parallel::CompileUnit;
/// DWARFv5 section 6.3.1 Macro Information Header.
enum HeaderFlagMask {
diff --git a/llvm/lib/DWARFLinker/DWARFLinker.cpp b/llvm/lib/DWARFLinker/DWARFLinker.cpp
index 58c52534f05f45..3beb20b59baaaf 100644
--- a/llvm/lib/DWARFLinker/DWARFLinker.cpp
+++ b/llvm/lib/DWARFLinker/DWARFLinker.cpp
@@ -444,8 +444,10 @@ DWARFLinker::getVariableRelocAdjustment(AddressesMap &RelocMgr,
const DWARFExpression::Operation &Op = *It;
switch (Op.getCode()) {
+ case dwarf::DW_OP_const2u:
case dwarf::DW_OP_const4u:
case dwarf::DW_OP_const8u:
+ case dwarf::DW_OP_const2s:
case dwarf::DW_OP_const4s:
case dwarf::DW_OP_const8s:
if (NextIt == Expression.end() || !isTlsAddressCode(NextIt->getCode()))
diff --git a/llvm/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp b/llvm/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp
index add0d94da73fc8..06559bc38c86cc 100644
--- a/llvm/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp
+++ b/llvm/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp
@@ -97,8 +97,10 @@ void CompileUnit::markEverythingAsKept() {
++NextIt;
switch (It->getCode()) {
+ case dwarf::DW_OP_const2u:
case dwarf::DW_OP_const4u:
case dwarf::DW_OP_const8u:
+ case dwarf::DW_OP_const2s:
case dwarf::DW_OP_const4s:
case dwarf::DW_OP_const8s:
if (NextIt == Expression.end() ||
diff --git a/llvm/lib/DWARFLinkerParallel/ArrayList.h b/llvm/lib/DWARFLinkerParallel/ArrayList.h
new file mode 100644
index 00000000000000..67aea161f510e9
--- /dev/null
+++ b/llvm/lib/DWARFLinkerParallel/ArrayList.h
@@ -0,0 +1,92 @@
+//===- ArrayList.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_ARRAYLIST_H
+#define LLVM_LIB_DWARFLINKERPARALLEL_ARRAYLIST_H
+
+#include "DWARFLinkerGlobalData.h"
+#include "llvm/Support/PerThreadBumpPtrAllocator.h"
+
+namespace llvm {
+namespace dwarflinker_parallel {
+
+/// This class is a simple list of T structures. It keeps elements as
+/// pre-allocated groups to save memory for each element's next pointer.
+/// It allocates internal data using specified per-thread BumpPtrAllocator.
+template <typename T, size_t ItemsGroupSize = 512> class ArrayList {
+public:
+ /// Copy specified \p Item into the list.
+ T ¬eItem(const T &Item) {
+ assert(Allocator != nullptr);
+
+ ItemsGroup *CurGroup = LastGroup;
+
+ if (CurGroup == nullptr) {
+ // Allocate first ItemsGroup.
+ LastGroup = Allocator->Allocate<ItemsGroup>();
+ LastGroup->ItemsCount = 0;
+ LastGroup->Next = nullptr;
+ GroupsHead = LastGroup;
+ CurGroup = LastGroup;
+ }
+
+ if (CurGroup->ItemsCount == ItemsGroupSize) {
+ // Allocate next ItemsGroup if current one is full.
+ LastGroup = Allocator->Allocate<ItemsGroup>();
+ LastGroup->ItemsCount = 0;
+ LastGroup->Next = nullptr;
+ CurGroup->Next = LastGroup;
+ CurGroup = LastGroup;
+ }
+
+ // Copy item into the next position inside current ItemsGroup.
+ CurGroup->Items[CurGroup->ItemsCount] = Item;
+ return CurGroup->Items[CurGroup->ItemsCount++];
+ }
+
+ using ItemHandlerTy = function_ref<void(T &)>;
+
+ /// Enumerate all items and apply specified \p Handler to each.
+ void forEach(ItemHandlerTy Handler) {
+ for (ItemsGroup *CurGroup = GroupsHead; CurGroup != nullptr;
+ CurGroup = CurGroup->Next) {
+ for (size_t Idx = 0; Idx < CurGroup->ItemsCount; Idx++) {
+ Handler(CurGroup->Items[Idx]);
+ }
+ }
+ }
+
+ /// Check whether list is empty.
+ bool empty() { return GroupsHead == nullptr; }
+
+ /// Erase list.
+ void erase() {
+ GroupsHead = nullptr;
+ LastGroup = nullptr;
+ }
+
+ void setAllocator(parallel::PerThreadBumpPtrAllocator *Allocator) {
+ this->Allocator = Allocator;
+ }
+
+protected:
+ struct ItemsGroup {
+ std::array<T, ItemsGroupSize> Items;
+ ItemsGroup *Next = nullptr;
+ size_t ItemsCount = 0;
+ };
+
+ ItemsGroup *GroupsHead = nullptr;
+ ItemsGroup *LastGroup = nullptr;
+ parallel::PerThreadBumpPtrAllocator *Allocator = nullptr;
+};
+
+} // end of namespace dwarflinker_parallel
+} // end namespace llvm
+
+#endif // LLVM_LIB_DWARFLINKERPARALLEL_ARRAYLIST_H
diff --git a/llvm/lib/DWARFLinkerParallel/CMakeLists.txt b/llvm/lib/DWARFLinkerParallel/CMakeLists.txt
index 8a1481c9df3e44..d321ecf8d5ce84 100644
--- a/llvm/lib/DWARFLinkerParallel/CMakeLists.txt
+++ b/llvm/lib/DWARFLinkerParallel/CMakeLists.txt
@@ -1,7 +1,12 @@
add_llvm_component_library(LLVMDWARFLinkerParallel
+ DependencyTracker.cpp
+ DIEAttributeCloner.cpp
DWARFEmitterImpl.cpp
+ DWARFFile.cpp
DWARFLinker.cpp
+ DWARFLinkerCompileUnit.cpp
DWARFLinkerImpl.cpp
+ DWARFLinkerUnit.cpp
OutputSections.cpp
StringPool.cpp
diff --git a/llvm/lib/DWARFLinkerParallel/DIEAttributeCloner.cpp b/llvm/lib/DWARFLinkerParallel/DIEAttributeCloner.cpp
new file mode 100644
index 00000000000000..25a55026dd6755
--- /dev/null
+++ b/llvm/lib/DWARFLinkerParallel/DIEAttributeCloner.cpp
@@ -0,0 +1,568 @@
+//=== DIEAttributeCloner.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 "DIEAttributeCloner.h"
+#include "llvm/DebugInfo/DWARF/DWARFDebugMacro.h"
+
+namespace llvm {
+namespace dwarflinker_parallel {
+
+void DIEAttributeCloner::clone() {
+ DWARFUnit &U = CU.getOrigUnit();
+
+ // Extract and clone every attribute.
+ DWARFDataExtractor Data = U.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();
+
+ // 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
+ // unconditionally, and it makes the code simpler.
+ SmallString<40> DIECopy(Data.getData().substr(Offset, NextOffset - Offset));
+ Data =
+ DWARFDataExtractor(DIECopy, Data.isLittleEndian(), Data.getAddressSize());
+
+ // Modify the copy with relocated addresses.
+ CU.getContaingFile().Addresses->applyValidRelocs(DIECopy, Offset,
+ Data.isLittleEndian());
+
+ // Reset the Offset to 0 as we will be working on the local copy of
+ // the data.
+ Offset = 0;
+
+ const auto *Abbrev = InputDieEntry->getAbbreviationDeclarationPtr();
+ Offset += getULEB128Size(Abbrev->getCode());
+
+ // Set current output offset.
+ AttrOutOffset = OutDIE->getOffset();
+ for (const auto &AttrSpec : Abbrev->attributes()) {
+ // Check whether current attribute should be skipped.
+ if (shouldSkipAttribute(AttrSpec)) {
+ DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset,
+ U.getFormParams());
+ continue;
+ }
+
+ DWARFFormValue Val = AttrSpec.getFormValue();
+ Val.extractValue(Data, &Offset, U.getFormParams(), &U);
+
+ // Clone current attribute.
+ switch (AttrSpec.Form) {
+ case dwarf::DW_FORM_strp:
+ case dwarf::DW_FORM_line_strp:
+ case dwarf::DW_FORM_string:
+ case dwarf::DW_FORM_strx:
+ case dwarf::DW_FORM_strx1:
+ case dwarf::DW_FORM_strx2:
+ case dwarf::DW_FORM_strx3:
+ case dwarf::DW_FORM_strx4:
+ AttrOutOffset += cloneStringAttr(Val, AttrSpec);
+ break;
+ case dwarf::DW_FORM_ref_addr:
+ case dwarf::DW_FORM_ref1:
+ case dwarf::DW_FORM_ref2:
+ case dwarf::DW_FORM_ref4:
+ case dwarf::DW_FORM_ref8:
+ case dwarf::DW_FORM_ref_udata:
+ AttrOutOffset += cloneDieRefAttr(Val, AttrSpec);
+ break;
+ case dwarf::DW_FORM_data1:
+ case dwarf::DW_FORM_data2:
+ case dwarf::DW_FORM_data4:
+ case dwarf::DW_FORM_data8:
+ case dwarf::DW_FORM_udata:
+ case dwarf::DW_FORM_sdata:
+ case dwarf::DW_FORM_sec_offset:
+ case dwarf::DW_FORM_flag:
+ case dwarf::DW_FORM_flag_present:
+ case dwarf::DW_FORM_rnglistx:
+ case dwarf::DW_FORM_loclistx:
+ case dwarf::DW_FORM_implicit_const:
+ AttrOutOffset += cloneScalarAttr(Val, AttrSpec);
+ break;
+ case dwarf::DW_FORM_block:
+ case dwarf::DW_FORM_block1:
+ case dwarf::DW_FORM_block2:
+ case dwarf::DW_FORM_block4:
+ case dwarf::DW_FORM_exprloc:
+ AttrOutOffset += cloneBlockAttr(Val, AttrSpec);
+ break;
+ case dwarf::DW_FORM_addr:
+ case dwarf::DW_FORM_addrx:
+ case dwarf::DW_FORM_addrx1:
+ case dwarf::DW_FORM_addrx2:
+ case dwarf::DW_FORM_addrx3:
+ case dwarf::DW_FORM_addrx4:
+ AttrOutOffset += cloneAddressAttr(Val, AttrSpec);
+ break;
+ default:
+ CU.warn("unsupported attribute form " +
+ dwarf::FormEncodingString(AttrSpec.Form) +
+ " in DieAttributeCloner::clone(). Dropping.",
+ InputDieEntry);
+ }
+ }
+
+ // We convert source strings into the indexed form for DWARFv5.
+ // 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) {
+ DebugInfoOutputSection.notePatchWithOffsetUpdate(
+ DebugOffsetPatch{
+ AttrOutOffset,
+ &CU.getOrCreateSectionDescriptor(DebugSectionKind::DebugStrOffsets),
+ true},
+ PatchesOffsets);
+
+ AttrOutOffset += Generator
+ .addScalarAttribute(dwarf::DW_AT_str_offsets_base,
+ dwarf::DW_FORM_sec_offset,
+ CU.getDebugStrOffsetsHeaderSize())
+ .second;
+ }
+}
+
+bool DIEAttributeCloner::shouldSkipAttribute(
+ DWARFAbbreviationDeclaration::AttributeSpec AttrSpec) {
+ switch (AttrSpec.Attr) {
+ default:
+ return false;
+ case dwarf::DW_AT_low_pc:
+ case dwarf::DW_AT_high_pc:
+ case dwarf::DW_AT_ranges:
+ if (CU.getGlobalData().getOptions().UpdateIndexTablesOnly)
+ return false;
+
+ // Skip address attribute if we are in function scope and function does not
+ // reference live address.
+ return CU.getDIEInfo(InputDIEIdx).getIsInFunctionScope() &&
+ !FuncAddressAdjustment.has_value();
+ case dwarf::DW_AT_rnglists_base:
+ // In case !Update the .debug_addr table is not generated/preserved.
+ // Thus instead of DW_FORM_rnglistx the DW_FORM_sec_offset is used.
+ // Since DW_AT_rnglists_base is used for only DW_FORM_rnglistx the
+ // DW_AT_rnglists_base is removed.
+ return !CU.getGlobalData().getOptions().UpdateIndexTablesOnly;
+ case dwarf::DW_AT_loclists_base:
+ // In case !Update the .debug_addr table is not generated/preserved.
+ // Thus instead of DW_FORM_loclistx the DW_FORM_sec_offset is used.
+ // Since DW_AT_loclists_base is used for only DW_FORM_loclistx the
+ // DW_AT_loclists_base is removed.
+ return !CU.getGlobalData().getOptions().UpdateIndexTablesOnly;
+ case dwarf::DW_AT_location:
+ case dwarf::DW_AT_frame_base:
+ if (CU.getGlobalData().getOptions().UpdateIndexTablesOnly)
+ return false;
+
+ // When location expression contains an address: skip this attribute
+ // if it does not reference live address.
+ if (HasLocationExpressionAddress)
+ return !VarAddressAdjustment.has_value();
+
+ // Skip location attribute if we are in function scope and function does not
+ // reference live address.
+ return CU.getDIEInfo(InputDIEIdx).getIsInFunctionScope() &&
+ !FuncAddressAdjustment.has_value();
+ }
+}
+
+size_t DIEAttributeCloner::cloneStringAttr(
+ const DWARFFormValue &Val,
+ const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec) {
+ std::optional<const char *> String = dwarf::toString(Val);
+ if (!String) {
+ CU.warn("cann't read string attribute.");
+ return 0;
+ }
+
+ StringEntry *StringInPool =
+ CU.getGlobalData().getStringPool().insert(*String).first;
+ if (AttrSpec.Form == dwarf::DW_FORM_line_strp) {
+ DebugInfoOutputSection.notePatchWithOffsetUpdate(
+ DebugLineStrPatch{{AttrOutOffset}, StringInPool}, PatchesOffsets);
+ return Generator
+ .addStringPlaceholderAttribute(AttrSpec.Attr, dwarf::DW_FORM_line_strp)
+ .second;
+ }
+
+ // Update attributes info.
+ if (AttrSpec.Attr == dwarf::DW_AT_name)
+ AttrInfo.Name = StringInPool;
+ else if (AttrSpec.Attr == dwarf::DW_AT_MIPS_linkage_name ||
+ AttrSpec.Attr == dwarf::DW_AT_linkage_name)
+ AttrInfo.MangledName = StringInPool;
+
+ if (CU.getVersion() < 5) {
+ DebugInfoOutputSection.notePatchWithOffsetUpdate(
+ DebugStrPatch{{AttrOutOffset}, StringInPool}, PatchesOffsets);
+
+ return Generator
+ .addStringPlaceholderAttribute(AttrSpec.Attr, dwarf::DW_FORM_strp)
+ .second;
+ }
+
+ return Generator
+ .addIndexedStringAttribute(AttrSpec.Attr, dwarf::DW_FORM_strx,
+ CU.getDebugStrIndex(StringInPool))
+ .second;
+}
+
+size_t DIEAttributeCloner::cloneDieRefAttr(
+ const DWARFFormValue &Val,
+ const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec) {
+ if (AttrSpec.Attr == dwarf::DW_AT_sibling)
+ return 0;
+
+ std::optional<std::pair<CompileUnit *, uint32_t>> RefDiePair =
+ CU.resolveDIEReference(Val);
+ if (!RefDiePair) {
+ // If the referenced DIE is not found, drop the attribute.
+ CU.warn("cann't find referenced DIE.", InputDieEntry);
+ return 0;
+ }
+ assert(RefDiePair->first->getStage() >= CompileUnit::Stage::Loaded);
+ assert(RefDiePair->second != 0);
+
+ // Get output offset for referenced DIE.
+ uint64_t OutDieOffset =
+ RefDiePair->first->getDieOutOffset(RefDiePair->second);
+
+ // Examine whether referenced DIE is in current compile unit.
+ bool IsLocal = CU.getUniqueID() == RefDiePair->first->getUniqueID();
+
+ // Set attribute form basing on the kind of referenced DIE(local or not?).
+ dwarf::Form NewForm = IsLocal ? dwarf::DW_FORM_ref4 : dwarf::DW_FORM_ref_addr;
+
+ // Check whether current attribute references already cloned DIE inside
+ // the same compilation unit. If true - write the already known offset value.
+ if (IsLocal && (OutDieOffset != 0))
+ return Generator.addScalarAttribute(AttrSpec.Attr, NewForm, OutDieOffset)
+ .second;
+
+ // If offset value is not known at this point then create patch for the
+ // reference value and write dummy value into the attribute.
+ DebugInfoOutputSection.notePatchWithOffsetUpdate(
+ DebugDieRefPatch{AttrOutOffset, &CU, RefDiePair->first,
+ RefDiePair->second},
+ PatchesOffsets);
+ return Generator.addScalarAttribute(AttrSpec.Attr, NewForm, 0xBADDEF).second;
+}
+
+size_t DIEAttributeCloner::cloneScalarAttr(
+ const DWARFFormValue &Val,
+ const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec) {
+
+ // Create patches for attribute referencing other non invariant section.
+ // Invariant section could not be updated here as this section and
+ // reference to it do not change value in case --update.
+ if (AttrSpec.Attr == dwarf::DW_AT_macro_info) {
+ if (std::optional<uint64_t> Offset = Val.getAsSectionOffset()) {
+ const DWARFDebugMacro *Macro =
+ CU.getContaingFile().Dwarf->getDebugMacinfo();
+ if (Macro == nullptr || !Macro->hasEntryForOffset(*Offset))
+ return 0;
+
+ DebugInfoOutputSection.notePatchWithOffsetUpdate(
+ DebugOffsetPatch{AttrOutOffset, &CU.getOrCreateSectionDescriptor(
+ DebugSectionKind::DebugMacinfo)},
+ PatchesOffsets);
+ }
+ } else if (AttrSpec.Attr == dwarf::DW_AT_macros) {
+ if (std::optional<uint64_t> Offset = Val.getAsSectionOffset()) {
+ const DWARFDebugMacro *Macro =
+ CU.getContaingFile().Dwarf->getDebugMacro();
+ if (Macro == nullptr || !Macro->hasEntryForOffset(*Offset))
+ return 0;
+
+ DebugInfoOutputSection.notePatchWithOffsetUpdate(
+ DebugOffsetPatch{AttrOutOffset, &CU.getOrCreateSectionDescriptor(
+ DebugSectionKind::DebugMacro)},
+ PatchesOffsets);
+ }
+ } else if (AttrSpec.Attr == dwarf::DW_AT_stmt_list) {
+ DebugInfoOutputSection.notePatchWithOffsetUpdate(
+ DebugOffsetPatch{AttrOutOffset, &CU.getOrCreateSectionDescriptor(
+ DebugSectionKind::DebugLine)},
+ PatchesOffsets);
+ } else if (AttrSpec.Attr == dwarf::DW_AT_str_offsets_base) {
+ DebugInfoOutputSection.notePatchWithOffsetUpdate(
+ DebugOffsetPatch{
+ AttrOutOffset,
+ &CU.getOrCreateSectionDescriptor(DebugSectionKind::DebugStrOffsets),
+ true},
+ PatchesOffsets);
+
+ // Use size of .debug_str_offsets header as attribute value. The offset
+ // to .debug_str_offsets would be added later while patching.
+ AttrInfo.HasStringOffsetBaseAttr = true;
+ return Generator
+ .addScalarAttribute(AttrSpec.Attr, AttrSpec.Form,
+ CU.getDebugStrOffsetsHeaderSize())
+ .second;
+ }
+
+ uint64_t Value;
+ if (CU.getGlobalData().getOptions().UpdateIndexTablesOnly) {
+ if (auto OptionalValue = Val.getAsUnsignedConstant())
+ Value = *OptionalValue;
+ else if (auto OptionalValue = Val.getAsSignedConstant())
+ Value = *OptionalValue;
+ else if (auto OptionalValue = Val.getAsSectionOffset())
+ Value = *OptionalValue;
+ else {
+ CU.warn("unsupported scalar attribute form. Dropping attribute.",
+ InputDieEntry);
+ return 0;
+ }
+
+ if (AttrSpec.Attr == dwarf::DW_AT_declaration && Value)
+ AttrInfo.IsDeclaration = true;
+
+ if (AttrSpec.Form == dwarf::DW_FORM_loclistx)
+ return Generator.addLocListAttribute(AttrSpec.Attr, AttrSpec.Form, Value)
+ .second;
+
+ return Generator.addScalarAttribute(AttrSpec.Attr, AttrSpec.Form, Value)
+ .second;
+ }
+
+ dwarf::Form ResultingForm = AttrSpec.Form;
+ if (AttrSpec.Form == dwarf::DW_FORM_rnglistx) {
+ // DWARFLinker does not generate .debug_addr table. Thus we need to change
+ // all "addrx" related forms to "addr" version. Change DW_FORM_rnglistx
+ // to DW_FORM_sec_offset here.
+ std::optional<uint64_t> Index = Val.getAsSectionOffset();
+ if (!Index) {
+ CU.warn("cann't read the attribute. Dropping.", InputDieEntry);
+ return 0;
+ }
+ std::optional<uint64_t> Offset = CU.getOrigUnit().getRnglistOffset(*Index);
+ if (!Offset) {
+ CU.warn("cann't read the attribute. Dropping.", InputDieEntry);
+ return 0;
+ }
+
+ Value = *Offset;
+ ResultingForm = dwarf::DW_FORM_sec_offset;
+ } else if (AttrSpec.Form == dwarf::DW_FORM_loclistx) {
+ // DWARFLinker does not generate .debug_addr table. Thus we need to change
+ // all "addrx" related forms to "addr" version. Change DW_FORM_loclistx
+ // to DW_FORM_sec_offset here.
+ std::optional<uint64_t> Index = Val.getAsSectionOffset();
+ if (!Index) {
+ CU.warn("cann't read the attribute. Dropping.", InputDieEntry);
+ return 0;
+ }
+ std::optional<uint64_t> Offset = CU.getOrigUnit().getLoclistOffset(*Index);
+ if (!Offset) {
+ CU.warn("cann't read the attribute. Dropping.", InputDieEntry);
+ return 0;
+ }
+
+ Value = *Offset;
+ ResultingForm = dwarf::DW_FORM_sec_offset;
+ } else if (AttrSpec.Attr == dwarf::DW_AT_high_pc &&
+ InputDieEntry->getTag() == dwarf::DW_TAG_compile_unit) {
+ std::optional<uint64_t> LowPC = CU.getLowPc();
+ if (!LowPC)
+ return 0;
+ // Dwarf >= 4 high_pc is an size, not an address.
+ Value = CU.getHighPc() - *LowPC;
+ } else if (AttrSpec.Form == dwarf::DW_FORM_sec_offset)
+ Value = *Val.getAsSectionOffset();
+ else if (AttrSpec.Form == dwarf::DW_FORM_sdata)
+ Value = *Val.getAsSignedConstant();
+ else if (auto OptionalValue = Val.getAsUnsignedConstant())
+ Value = *OptionalValue;
+ else {
+ CU.warn("unsupported scalar attribute form. Dropping attribute.",
+ InputDieEntry);
+ return 0;
+ }
+
+ if (AttrSpec.Attr == dwarf::DW_AT_ranges ||
+ AttrSpec.Attr == dwarf::DW_AT_start_scope) {
+ // Create patch for the range offset value.
+ DebugInfoOutputSection.notePatchWithOffsetUpdate(
+ DebugRangePatch{{AttrOutOffset},
+ InputDieEntry->getTag() == dwarf::DW_TAG_compile_unit},
+ PatchesOffsets);
+ AttrInfo.HasRanges = true;
+ } else if (DWARFAttribute::mayHaveLocationList(AttrSpec.Attr) &&
+ dwarf::doesFormBelongToClass(AttrSpec.Form,
+ DWARFFormValue::FC_SectionOffset,
+ CU.getOrigUnit().getVersion())) {
+ int64_t AddrAdjustmentValue = 0;
+ if (VarAddressAdjustment)
+ AddrAdjustmentValue = *VarAddressAdjustment;
+ else if (FuncAddressAdjustment)
+ AddrAdjustmentValue = *FuncAddressAdjustment;
+
+ // Create patch for the location offset value.
+ DebugInfoOutputSection.notePatchWithOffsetUpdate(
+ DebugLocPatch{{AttrOutOffset}, AddrAdjustmentValue}, PatchesOffsets);
+ } else if (AttrSpec.Attr == dwarf::DW_AT_addr_base) {
+ DebugInfoOutputSection.notePatchWithOffsetUpdate(
+ DebugOffsetPatch{
+ AttrOutOffset,
+ &CU.getOrCreateSectionDescriptor(DebugSectionKind::DebugAddr),
+ true},
+ PatchesOffsets);
+
+ // Use size of .debug_addr header as attribute value. The offset to
+ // .debug_addr would be added later while patching.
+ return Generator
+ .addScalarAttribute(AttrSpec.Attr, AttrSpec.Form,
+ CU.getDebugAddrHeaderSize())
+ .second;
+ } else if (AttrSpec.Attr == dwarf::DW_AT_declaration && Value)
+ AttrInfo.IsDeclaration = true;
+
+ return Generator.addScalarAttribute(AttrSpec.Attr, ResultingForm, Value)
+ .second;
+}
+
+size_t DIEAttributeCloner::cloneBlockAttr(
+ const DWARFFormValue &Val,
+ const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec) {
+
+ size_t NumberOfPatchesAtStart = PatchesOffsets.size();
+
+ // If the block is a DWARF Expression, clone it into the temporary
+ // buffer using cloneExpression(), otherwise copy the data directly.
+ SmallVector<uint8_t, 32> Buffer;
+ ArrayRef<uint8_t> Bytes = *Val.getAsBlock();
+ if (DWARFAttribute::mayHaveLocationExpr(AttrSpec.Attr) &&
+ (Val.isFormClass(DWARFFormValue::FC_Block) ||
+ Val.isFormClass(DWARFFormValue::FC_Exprloc))) {
+ DWARFUnit &OrigUnit = CU.getOrigUnit();
+ DataExtractor Data(StringRef((const char *)Bytes.data(), Bytes.size()),
+ OrigUnit.isLittleEndian(),
+ OrigUnit.getAddressByteSize());
+ DWARFExpression Expr(Data, OrigUnit.getAddressByteSize(),
+ OrigUnit.getFormParams().Format);
+
+ CU.cloneDieAttrExpression(Expr, Buffer, DebugInfoOutputSection,
+ VarAddressAdjustment, PatchesOffsets);
+ Bytes = Buffer;
+ }
+
+ // The expression location data might be updated and exceed the original size.
+ // Check whether the new data fits into the original form.
+ dwarf::Form ResultForm = AttrSpec.Form;
+ if ((ResultForm == dwarf::DW_FORM_block1 && Bytes.size() > UINT8_MAX) ||
+ (ResultForm == dwarf::DW_FORM_block2 && Bytes.size() > UINT16_MAX) ||
+ (ResultForm == dwarf::DW_FORM_block4 && Bytes.size() > UINT32_MAX))
+ ResultForm = dwarf::DW_FORM_block;
+
+ size_t FinalAttributeSize;
+ if (AttrSpec.Form == dwarf::DW_FORM_exprloc)
+ FinalAttributeSize =
+ Generator.addLocationAttribute(AttrSpec.Attr, ResultForm, Bytes).second;
+ else
+ FinalAttributeSize =
+ Generator.addBlockAttribute(AttrSpec.Attr, ResultForm, Bytes).second;
+
+ // Update patches offsets with the size of length field for Bytes.
+ for (size_t Idx = NumberOfPatchesAtStart; Idx < PatchesOffsets.size();
+ Idx++) {
+ assert(FinalAttributeSize > Bytes.size());
+ *PatchesOffsets[Idx] +=
+ (AttrOutOffset + (FinalAttributeSize - Bytes.size()));
+ }
+
+ return FinalAttributeSize;
+}
+
+size_t DIEAttributeCloner::cloneAddressAttr(
+ const DWARFFormValue &Val,
+ const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec) {
+ if (AttrSpec.Attr == dwarf::DW_AT_low_pc)
+ AttrInfo.HasLowPc = true;
+
+ if (CU.getGlobalData().getOptions().UpdateIndexTablesOnly)
+ return Generator
+ .addScalarAttribute(AttrSpec.Attr, AttrSpec.Form, Val.getRawUValue())
+ .second;
+
+ // Cloned Die may have address attributes relocated to a
+ // totally unrelated value. This can happen:
+ // - If high_pc is an address (Dwarf version == 2), then it might have been
+ // relocated to a totally unrelated value (because the end address in the
+ // object file might be start address of another function which got moved
+ // independently by the linker).
+ // - If address relocated in an inline_subprogram that happens at the
+ // beginning of its inlining function.
+ // To avoid above cases and to not apply relocation twice (in
+ // applyValidRelocs and here), read address attribute from InputDIE and apply
+ // Info.PCOffset here.
+
+ std::optional<DWARFFormValue> AddrAttribute =
+ CU.find(InputDieEntry, AttrSpec.Attr);
+ if (!AddrAttribute)
+ llvm_unreachable("Cann't find attribute");
+
+ std::optional<uint64_t> Addr = AddrAttribute->getAsAddress();
+ if (!Addr) {
+ CU.warn("cann't read address attribute value.");
+ return 0;
+ }
+
+ if (InputDieEntry->getTag() == dwarf::DW_TAG_compile_unit &&
+ AttrSpec.Attr == dwarf::DW_AT_low_pc) {
+ if (std::optional<uint64_t> LowPC = CU.getLowPc())
+ Addr = *LowPC;
+ else
+ return 0;
+ } else if (InputDieEntry->getTag() == dwarf::DW_TAG_compile_unit &&
+ AttrSpec.Attr == dwarf::DW_AT_high_pc) {
+ if (uint64_t HighPc = CU.getHighPc())
+ Addr = HighPc;
+ else
+ return 0;
+ } else {
+ if (VarAddressAdjustment)
+ *Addr += *VarAddressAdjustment;
+ else if (FuncAddressAdjustment)
+ *Addr += *FuncAddressAdjustment;
+ }
+
+ if (AttrSpec.Form == dwarf::DW_FORM_addr) {
+ return Generator.addScalarAttribute(AttrSpec.Attr, AttrSpec.Form, *Addr)
+ .second;
+ }
+
+ return Generator
+ .addScalarAttribute(AttrSpec.Attr, dwarf::Form::DW_FORM_addrx,
+ CU.getDebugAddrIndex(*Addr))
+ .second;
+}
+
+unsigned DIEAttributeCloner::finalizeAbbreviations(bool HasChildrenToClone) {
+ size_t SizeOfAbbreviationNumber =
+ Generator.finalizeAbbreviations(HasChildrenToClone);
+
+ // We need to update patches offsets after we know the size of the
+ // abbreviation number.
+ updatePatchesWithSizeOfAbbreviationNumber(SizeOfAbbreviationNumber);
+
+ // Add the size of the abbreviation number to the output offset.
+ AttrOutOffset += SizeOfAbbreviationNumber;
+
+ return AttrOutOffset;
+}
+
+} // end of namespace dwarflinker_parallel
+} // namespace llvm
diff --git a/llvm/lib/DWARFLinkerParallel/DIEAttributeCloner.h b/llvm/lib/DWARFLinkerParallel/DIEAttributeCloner.h
new file mode 100644
index 00000000000000..d590b1805e8efd
--- /dev/null
+++ b/llvm/lib/DWARFLinkerParallel/DIEAttributeCloner.h
@@ -0,0 +1,147 @@
+//===- DIEAttributeCloner.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_DIEATTRIBUTECLONER_H
+#define LLVM_LIB_DWARFLINKERPARALLEL_DIEATTRIBUTECLONER_H
+
+#include "ArrayList.h"
+#include "DIEGenerator.h"
+#include "DWARFLinkerCompileUnit.h"
+#include "DWARFLinkerGlobalData.h"
+
+namespace llvm {
+namespace dwarflinker_parallel {
+
+/// This class creates clones of input DIE attributes.
+/// It enumerates attributes of input DIE, creates clone for each
+/// attribute, adds cloned attribute to the output DIE.
+class DIEAttributeCloner {
+public:
+ DIEAttributeCloner(DIE *OutDIE, CompileUnit &CU,
+ const DWARFDebugInfoEntry *InputDieEntry,
+ DIEGenerator &Generator,
+ std::optional<int64_t> FuncAddressAdjustment,
+ std::optional<int64_t> VarAddressAdjustment,
+ bool HasLocationExpressionAddress)
+ : OutDIE(OutDIE), CU(CU),
+ DebugInfoOutputSection(
+ CU.getOrCreateSectionDescriptor(DebugSectionKind::DebugInfo)),
+ InputDieEntry(InputDieEntry), Generator(Generator),
+ FuncAddressAdjustment(FuncAddressAdjustment),
+ VarAddressAdjustment(VarAddressAdjustment),
+ HasLocationExpressionAddress(HasLocationExpressionAddress) {
+ InputDIEIdx = CU.getDIEIndex(InputDieEntry);
+ }
+
+ /// Clone attributes of input DIE.
+ void clone();
+
+ /// Create abbreviations for the output DIE after all attributes are cloned.
+ unsigned finalizeAbbreviations(bool HasChildrenToClone);
+
+ /// Information gathered and exchanged between the various
+ /// clone*Attr helpers about the attributes of a particular DIE.
+ ///
+ /// Cannot be used concurrently.
+ struct AttributesInfo {
+ /// Names.
+ StringEntry *Name = nullptr;
+ StringEntry *MangledName = nullptr;
+ StringEntry *NameWithoutTemplate = nullptr;
+
+ /// Does the DIE have a low_pc attribute?
+ bool HasLowPc = false;
+
+ /// Is this DIE only a declaration?
+ bool IsDeclaration = false;
+
+ /// Does the DIE have a ranges attribute?
+ bool HasRanges = false;
+
+ /// Does the DIE have a string offset attribute?
+ bool HasStringOffsetBaseAttr = false;
+ };
+
+ AttributesInfo AttrInfo;
+
+protected:
+ /// Clone string attribute.
+ size_t
+ cloneStringAttr(const DWARFFormValue &Val,
+ const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec);
+
+ /// Clone attribute referencing another DIE.
+ size_t
+ cloneDieRefAttr(const DWARFFormValue &Val,
+ const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec);
+
+ /// Clone scalar attribute.
+ size_t
+ cloneScalarAttr(const DWARFFormValue &Val,
+ const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec);
+
+ /// Clone block or exprloc attribute.
+ size_t
+ cloneBlockAttr(const DWARFFormValue &Val,
+ const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec);
+
+ /// Clone address attribute.
+ size_t
+ cloneAddressAttr(const DWARFFormValue &Val,
+ const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec);
+
+ /// Returns true if attribute should be skipped.
+ bool
+ shouldSkipAttribute(DWARFAbbreviationDeclaration::AttributeSpec AttrSpec);
+
+ /// Update patches offsets with the size of abbreviation number.
+ void
+ updatePatchesWithSizeOfAbbreviationNumber(unsigned SizeOfAbbreviationNumber) {
+ for (uint64_t *OffsetPtr : PatchesOffsets)
+ *OffsetPtr += SizeOfAbbreviationNumber;
+ }
+
+ /// Output DIE.
+ DIE *OutDIE = nullptr;
+
+ /// Compile unit for the output DIE.
+ CompileUnit &CU;
+
+ /// .debug_info section descriptor.
+ SectionDescriptor &DebugInfoOutputSection;
+
+ /// Input DIE entry.
+ const DWARFDebugInfoEntry *InputDieEntry = nullptr;
+
+ /// Input DIE index.
+ uint32_t InputDIEIdx = 0;
+
+ /// Output DIE generator.
+ DIEGenerator &Generator;
+
+ /// Relocation adjustment for the function address ranges.
+ std::optional<int64_t> FuncAddressAdjustment;
+
+ /// Relocation adjustment for the variable locations.
+ std::optional<int64_t> VarAddressAdjustment;
+
+ /// Indicates whether InputDieEntry has an location attribute
+ /// containg address expression.
+ bool HasLocationExpressionAddress = false;
+
+ /// Output offset after all attributes.
+ unsigned AttrOutOffset = 0;
+
+ /// Patches for the cloned attributes.
+ OffsetsPtrVector PatchesOffsets;
+};
+
+} // end of namespace dwarflinker_parallel
+} // end namespace llvm
+
+#endif // LLVM_LIB_DWARFLINKERPARALLEL_DIEATTRIBUTECLONER_H
diff --git a/llvm/lib/DWARFLinkerParallel/DIEGenerator.h b/llvm/lib/DWARFLinkerParallel/DIEGenerator.h
new file mode 100644
index 00000000000000..c0bbce0f520143
--- /dev/null
+++ b/llvm/lib/DWARFLinkerParallel/DIEGenerator.h
@@ -0,0 +1,165 @@
+//===- DIEGenerator.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_DIEGENERATOR_H
+#define LLVM_LIB_DWARFLINKERPARALLEL_DIEGENERATOR_H
+
+#include "DWARFLinkerGlobalData.h"
+#include "DWARFLinkerUnit.h"
+#include "llvm/CodeGen/DIE.h"
+#include "llvm/Support/LEB128.h"
+
+namespace llvm {
+namespace dwarflinker_parallel {
+
+/// This class is a helper to create output DIE tree.
+class DIEGenerator {
+public:
+ DIEGenerator(BumpPtrAllocator &Allocator, DwarfUnit &CU)
+ : Allocator(Allocator), CU(CU) {}
+
+ /// Creates a DIE of specified tag \p DieTag and \p OutOffset.
+ DIE *createDIE(dwarf::Tag DieTag, uint32_t OutOffset) {
+ OutputDIE = DIE::get(Allocator, DieTag);
+
+ OutputDIE->setOffset(OutOffset);
+
+ return OutputDIE;
+ }
+
+ /// Adds a specified \p Child to the current DIE.
+ void addChild(DIE *Child) {
+ assert(Child != nullptr);
+ assert(OutputDIE != nullptr);
+
+ OutputDIE->addChild(Child);
+ }
+
+ /// Adds specified scalar attribute to the current DIE.
+ std::pair<DIEValue &, size_t> addScalarAttribute(dwarf::Attribute Attr,
+ dwarf::Form AttrForm,
+ uint64_t Value) {
+ return addAttribute(Attr, AttrForm, DIEInteger(Value));
+ }
+
+ /// Adds specified location attribute to the current DIE.
+ std::pair<DIEValue &, size_t> addLocationAttribute(dwarf::Attribute Attr,
+ dwarf::Form AttrForm,
+ ArrayRef<uint8_t> Bytes) {
+ DIELoc *Loc = new (Allocator) DIELoc;
+ for (auto Byte : Bytes)
+ static_cast<DIEValueList *>(Loc)->addValue(
+ Allocator, static_cast<dwarf::Attribute>(0), dwarf::DW_FORM_data1,
+ DIEInteger(Byte));
+ Loc->setSize(Bytes.size());
+
+ return addAttribute(Attr, AttrForm, Loc);
+ }
+
+ /// Adds specified block or exprloc attribute to the current DIE.
+ std::pair<DIEValue &, size_t> addBlockAttribute(dwarf::Attribute Attr,
+ dwarf::Form AttrForm,
+ ArrayRef<uint8_t> Bytes) {
+ // The expression location data might be updated and exceed the original
+ // size. Check whether the new data fits into the original form.
+ assert((AttrForm == dwarf::DW_FORM_block) ||
+ (AttrForm == dwarf::DW_FORM_exprloc) ||
+ (AttrForm == dwarf::DW_FORM_block1 && Bytes.size() <= UINT8_MAX) ||
+ (AttrForm == dwarf::DW_FORM_block2 && Bytes.size() <= UINT16_MAX) ||
+ (AttrForm == dwarf::DW_FORM_block4 && Bytes.size() <= UINT32_MAX));
+
+ DIEBlock *Block = new (Allocator) DIEBlock;
+ for (auto Byte : Bytes)
+ static_cast<DIEValueList *>(Block)->addValue(
+ Allocator, static_cast<dwarf::Attribute>(0), dwarf::DW_FORM_data1,
+ DIEInteger(Byte));
+ Block->setSize(Bytes.size());
+
+ return addAttribute(Attr, AttrForm, Block);
+ }
+
+ /// Adds specified location list attribute to the current DIE.
+ std::pair<DIEValue &, size_t> addLocListAttribute(dwarf::Attribute Attr,
+ dwarf::Form AttrForm,
+ uint64_t Value) {
+ return addAttribute(Attr, AttrForm, DIELocList(Value));
+ }
+
+ /// Adds indexed string attribute.
+ std::pair<DIEValue &, size_t> addIndexedStringAttribute(dwarf::Attribute Attr,
+ dwarf::Form AttrForm,
+ uint64_t Idx) {
+ assert(AttrForm == dwarf::DW_FORM_strx);
+ return addAttribute(Attr, AttrForm, DIEInteger(Idx));
+ }
+
+ /// Adds string attribute with dummy offset to the current DIE.
+ std::pair<DIEValue &, size_t>
+ addStringPlaceholderAttribute(dwarf::Attribute Attr, dwarf::Form AttrForm) {
+ assert(AttrForm == dwarf::DW_FORM_strp ||
+ AttrForm == dwarf::DW_FORM_line_strp);
+ return addAttribute(Attr, AttrForm, DIEInteger(0xBADDEF));
+ }
+
+ /// Adds inplace string attribute to the current DIE.
+ std::pair<DIEValue &, size_t> addInplaceString(dwarf::Attribute Attr,
+ StringRef String) {
+ DIEBlock *Block = new (Allocator) DIEBlock;
+ for (auto Byte : String.bytes())
+ static_cast<DIEValueList *>(Block)->addValue(
+ Allocator, static_cast<dwarf::Attribute>(0), dwarf::DW_FORM_data1,
+ DIEInteger(Byte));
+
+ static_cast<DIEValueList *>(Block)->addValue(
+ Allocator, static_cast<dwarf::Attribute>(0), dwarf::DW_FORM_data1,
+ DIEInteger(0));
+ Block->setSize(String.size() + 1);
+
+ DIEValue &ValueRef =
+ *OutputDIE->addValue(Allocator, Attr, dwarf::DW_FORM_string, Block);
+ return std::pair<DIEValue &, size_t>(ValueRef, String.size() + 1);
+ }
+
+ /// Creates appreviations for the current DIE. Returns value of
+ /// abbreviation number.
+ size_t finalizeAbbreviations(bool CHILDREN_yes) {
+ // Create abbreviations for output DIE.
+ DIEAbbrev NewAbbrev = OutputDIE->generateAbbrev();
+ if (CHILDREN_yes)
+ NewAbbrev.setChildrenFlag(dwarf::DW_CHILDREN_yes);
+
+ CU.assignAbbrev(NewAbbrev);
+ OutputDIE->setAbbrevNumber(NewAbbrev.getNumber());
+
+ return getULEB128Size(OutputDIE->getAbbrevNumber());
+ }
+
+protected:
+ template <typename T>
+ std::pair<DIEValue &, size_t> addAttribute(dwarf::Attribute Attr,
+ dwarf::Form AttrForm, T &&Value) {
+ DIEValue &ValueRef =
+ *OutputDIE->addValue(Allocator, Attr, AttrForm, std::forward<T>(Value));
+ unsigned ValueSize = ValueRef.sizeOf(CU.getFormParams());
+ return std::pair<DIEValue &, size_t>(ValueRef, ValueSize);
+ }
+
+ // Allocator for output DIEs and values.
+ BumpPtrAllocator &Allocator;
+
+ // Unit for the output DIE.
+ DwarfUnit &CU;
+
+ // OutputDIE.
+ DIE *OutputDIE = nullptr;
+};
+
+} // end of namespace dwarflinker_parallel
+} // end namespace llvm
+
+#endif // LLVM_LIB_DWARFLINKERPARALLEL_DIEGENERATOR_H
diff --git a/llvm/lib/DWARFLinkerParallel/DWARFEmitterImpl.cpp b/llvm/lib/DWARFLinkerParallel/DWARFEmitterImpl.cpp
index 50909c0ba66937..38898ac6f2fa2f 100644
--- a/llvm/lib/DWARFLinkerParallel/DWARFEmitterImpl.cpp
+++ b/llvm/lib/DWARFLinkerParallel/DWARFEmitterImpl.cpp
@@ -114,18 +114,115 @@ Error DwarfEmitterImpl::init(Triple TheTriple,
TripleName.c_str());
Asm->setDwarfUsesRelocationsAcrossSections(false);
- RangesSectionSize = 0;
- RngListsSectionSize = 0;
- LocSectionSize = 0;
- LocListsSectionSize = 0;
- LineSectionSize = 0;
- FrameSectionSize = 0;
DebugInfoSectionSize = 0;
- MacInfoSectionSize = 0;
- MacroSectionSize = 0;
return Error::success();
}
+void DwarfEmitterImpl::emitSwiftAST(StringRef Buffer) {
+ MCSection *SwiftASTSection = MOFI->getDwarfSwiftASTSection();
+ SwiftASTSection->setAlignment(Align(32));
+ MS->switchSection(SwiftASTSection);
+ MS->emitBytes(Buffer);
+}
+
+/// Emit the swift reflection section stored in \p Buffer.
+void DwarfEmitterImpl::emitSwiftReflectionSection(
+ llvm::binaryformat::Swift5ReflectionSectionKind ReflSectionKind,
+ StringRef Buffer, uint32_t Alignment, uint32_t) {
+ MCSection *ReflectionSection =
+ MOFI->getSwift5ReflectionSection(ReflSectionKind);
+ if (ReflectionSection == nullptr)
+ return;
+ ReflectionSection->setAlignment(Align(Alignment));
+ MS->switchSection(ReflectionSection);
+ MS->emitBytes(Buffer);
+}
+
+void DwarfEmitterImpl::emitSectionContents(StringRef SecData,
+ StringRef SecName) {
+ if (SecData.empty())
+ return;
+
+ if (MCSection *Section = switchSection(SecName)) {
+ MS->switchSection(Section);
+
+ MS->emitBytes(SecData);
+ }
+}
+
+MCSection *DwarfEmitterImpl::switchSection(StringRef SecName) {
+ return StringSwitch<MCSection *>(SecName)
+ .Case("debug_info", MC->getObjectFileInfo()->getDwarfInfoSection())
+ .Case("debug_abbrev", MC->getObjectFileInfo()->getDwarfAbbrevSection())
+ .Case("debug_line", MC->getObjectFileInfo()->getDwarfLineSection())
+ .Case("debug_loc", MC->getObjectFileInfo()->getDwarfLocSection())
+ .Case("debug_ranges", MC->getObjectFileInfo()->getDwarfRangesSection())
+ .Case("debug_frame", MC->getObjectFileInfo()->getDwarfFrameSection())
+ .Case("debug_aranges", MC->getObjectFileInfo()->getDwarfARangesSection())
+ .Case("debug_rnglists",
+ MC->getObjectFileInfo()->getDwarfRnglistsSection())
+ .Case("debug_loclists",
+ MC->getObjectFileInfo()->getDwarfLoclistsSection())
+ .Case("debug_macro", MC->getObjectFileInfo()->getDwarfMacroSection())
+ .Case("debug_macinfo", MC->getObjectFileInfo()->getDwarfMacinfoSection())
+ .Case("debug_addr", MC->getObjectFileInfo()->getDwarfAddrSection())
+ .Case("debug_str", MC->getObjectFileInfo()->getDwarfStrSection())
+ .Case("debug_line_str", MC->getObjectFileInfo()->getDwarfLineStrSection())
+ .Case("debug_str_offsets",
+ MC->getObjectFileInfo()->getDwarfStrOffSection())
+ .Default(nullptr);
+}
+
+MCSymbol *DwarfEmitterImpl::emitTempSym(StringRef SecName, StringRef SymName) {
+ if (MCSection *Section = switchSection(SecName)) {
+ MS->switchSection(Section);
+ MCSymbol *Res = Asm->createTempSymbol(SymName);
+ Asm->OutStreamer->emitLabel(Res);
+ return Res;
+ }
+
+ return nullptr;
+}
+
+void DwarfEmitterImpl::emitAbbrevs(
+ const SmallVector<std::unique_ptr<DIEAbbrev>> &Abbrevs,
+ unsigned DwarfVersion) {
+ MS->switchSection(MOFI->getDwarfAbbrevSection());
+ MC->setDwarfVersion(DwarfVersion);
+ Asm->emitDwarfAbbrevs(Abbrevs);
+}
+
+void DwarfEmitterImpl::emitCompileUnitHeader(DwarfUnit &Unit) {
+ MS->switchSection(MOFI->getDwarfInfoSection());
+ MC->setDwarfVersion(Unit.getVersion());
+
+ // Emit size of content not including length itself. The size has already
+ // been computed in CompileUnit::computeOffsets(). Subtract 4 to that size to
+ // account for the length field.
+ Asm->emitInt32(Unit.getDebugInfoHeaderSize() +
+ Unit.getOutUnitDIE()->getSize() - 4);
+ Asm->emitInt16(Unit.getVersion());
+
+ if (Unit.getVersion() >= 5) {
+ Asm->emitInt8(dwarf::DW_UT_compile);
+ Asm->emitInt8(Unit.getFormParams().AddrSize);
+ // Proper offset to the abbreviations table will be set later.
+ Asm->emitInt32(0);
+ DebugInfoSectionSize += 12;
+ } else {
+ // Proper offset to the abbreviations table will be set later.
+ Asm->emitInt32(0);
+ Asm->emitInt8(Unit.getFormParams().AddrSize);
+ DebugInfoSectionSize += 11;
+ }
+}
+
+void DwarfEmitterImpl::emitDIE(DIE &Die) {
+ MS->switchSection(MOFI->getDwarfInfoSection());
+ Asm->emitDwarfDIE(Die);
+ DebugInfoSectionSize += Die.getSize();
+}
+
} // end of namespace dwarflinker_parallel
} // namespace llvm
diff --git a/llvm/lib/DWARFLinkerParallel/DWARFEmitterImpl.h b/llvm/lib/DWARFLinkerParallel/DWARFEmitterImpl.h
index d07397a304196e..3a8aab1ca83dc9 100644
--- a/llvm/lib/DWARFLinkerParallel/DWARFEmitterImpl.h
+++ b/llvm/lib/DWARFLinkerParallel/DWARFEmitterImpl.h
@@ -14,7 +14,6 @@
#include "llvm/CodeGen/AccelTable.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/DWARFLinkerParallel/DWARFLinker.h"
-#include "llvm/DWARFLinkerParallel/StringTable.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCInstrInfo.h"
@@ -40,186 +39,66 @@ class DWARFDebugMacro;
namespace dwarflinker_parallel {
-struct UnitStartSymbol {
- unsigned UnitID = 0;
- MCSymbol *Symbol = 0;
-};
-using UnitStartSymbolsTy = SmallVector<UnitStartSymbol>;
-using Offset2UnitMapTy = DenseMap<uint64_t, CompileUnit *>;
-
-struct RangeAttrPatch;
-struct LocAttrPatch;
-
-/// The Dwarf emission logic.
-///
-/// All interactions with the MC layer that is used to build the debug
-/// information binary representation are handled in this class.
+/// This class emits DWARF data to the output stream. It emits already
+/// generated section data and specific data, which could not be generated
+/// by CompileUnit.
class DwarfEmitterImpl : public ExtraDwarfEmitter {
public:
DwarfEmitterImpl(DWARFLinker::OutputFileType OutFileType,
- raw_pwrite_stream &OutFile,
- std::function<StringRef(StringRef Input)> Translator,
- DWARFLinker::MessageHandlerTy Warning)
- : OutFile(OutFile), OutFileType(OutFileType), Translator(Translator),
- WarningHandler(Warning) {}
+ raw_pwrite_stream &OutFile)
+ : OutFile(OutFile), OutFileType(OutFileType) {}
+ /// Initialize AsmPrinter data.
Error init(Triple TheTriple, StringRef Swift5ReflectionSegmentName);
+ /// Returns triple of output stream.
+ const Triple &getTargetTriple() { return MC->getTargetTriple(); }
+
/// Dump the file to the disk.
void finish() override { MS->finish(); }
+ /// Returns AsmPrinter.
AsmPrinter &getAsmPrinter() const override { return *Asm; }
- /// Set the current output section to debug_info and change
- /// the MC Dwarf version to \p DwarfVersion.
- void switchToDebugInfoSection(unsigned DwarfVersion) {}
-
/// Emit the swift_ast section stored in \p Buffer.
- void emitSwiftAST(StringRef Buffer) override {}
+ void emitSwiftAST(StringRef Buffer) override;
/// Emit the swift reflection section stored in \p Buffer.
void emitSwiftReflectionSection(
llvm::binaryformat::Swift5ReflectionSectionKind ReflSectionKind,
- StringRef Buffer, uint32_t Alignment, uint32_t Size) override {}
+ StringRef Buffer, uint32_t Alignment, uint32_t) override;
- void emitPaperTrailWarningsDie(DIE &Die) {}
+ /// Emit specified section data.
+ void emitSectionContents(StringRef SecData, StringRef SecName) override;
- void emitSectionContents(StringRef SecData, StringRef SecName) override {}
-
- MCSymbol *emitTempSym(StringRef SecName, StringRef SymName) override {
- return nullptr;
- }
+ /// Emit temporary symbol.
+ MCSymbol *emitTempSym(StringRef SecName, StringRef SymName) override;
+ /// Emit abbreviations.
void emitAbbrevs(const SmallVector<std::unique_ptr<DIEAbbrev>> &Abbrevs,
- unsigned DwarfVersion) {}
-
- void emitStrings(const StringTable &Strings) {}
-
- void emitLineStrings(const StringTable &Strings) {}
-
- void emitDebugNames(AccelTable<DWARF5AccelTableStaticData> &,
- UnitStartSymbolsTy &UnitOffsets) {}
-
- void emitAppleNamespaces(AccelTable<AppleAccelTableStaticOffsetData> &) {}
-
- void emitAppleNames(AccelTable<AppleAccelTableStaticOffsetData> &) {}
-
- void emitAppleObjc(AccelTable<AppleAccelTableStaticOffsetData> &) {}
-
- void emitAppleTypes(AccelTable<AppleAccelTableStaticTypeData> &) {}
-
- MCSymbol *emitDwarfDebugRangeListHeader(const CompileUnit &Unit) {
- return nullptr;
- }
-
- void emitDwarfDebugRangeListFragment(const CompileUnit &Unit,
- const AddressRanges &LinkedRanges,
- RangeAttrPatch &Patch) {}
-
- void emitDwarfDebugRangeListFooter(const CompileUnit &Unit,
- MCSymbol *EndLabel) {}
-
- MCSymbol *emitDwarfDebugLocListHeader(const CompileUnit &Unit) {
- return nullptr;
- }
-
- void emitDwarfDebugLocListFragment(
- const CompileUnit &Unit,
- const DWARFLocationExpressionsVector &LinkedLocationExpression,
- LocAttrPatch &Patch) {}
-
- void emitDwarfDebugLocListFooter(const CompileUnit &Unit,
- MCSymbol *EndLabel) {}
-
- void emitDwarfDebugArangesTable(const CompileUnit &Unit,
- const AddressRanges &LinkedRanges) {}
-
- void translateLineTable(DataExtractor LineData, uint64_t Offset) {}
-
- void emitLineTableForUnit(MCDwarfLineTableParams Params,
- StringRef PrologueBytes, unsigned MinInstLength,
- std::vector<DWARFDebugLine::Row> &Rows,
- unsigned AdddressSize) {}
+ unsigned DwarfVersion);
- void emitLineTableForUnit(const DWARFDebugLine::LineTable &LineTable,
- const CompileUnit &Unit, const StringTable &Strings,
- const StringTable &LineTableStrings) {}
+ /// Emit compile unit header.
+ void emitCompileUnitHeader(DwarfUnit &Unit);
- void emitPubNamesForUnit(const CompileUnit &Unit) {}
-
- void emitPubTypesForUnit(const CompileUnit &Unit) {}
-
- void emitCIE(StringRef CIEBytes) {}
-
- void emitFDE(uint32_t CIEOffset, uint32_t AddreSize, uint64_t Address,
- StringRef Bytes) {}
-
- void emitCompileUnitHeader(CompileUnit &Unit, unsigned DwarfVersion) {}
-
- void emitDIE(DIE &Die) {}
-
- void emitMacroTables(DWARFContext *Context,
- const Offset2UnitMapTy &UnitMacroMap,
- StringTable &Strings) {}
-
- /// Returns size of generated .debug_line section.
- uint64_t getDebugLineSectionSize() const { return LineSectionSize; }
-
- /// Returns size of generated .debug_frame section.
- uint64_t getDebugFrameSectionSize() const { return FrameSectionSize; }
-
- /// Returns size of generated .debug_ranges section.
- uint64_t getDebugRangesSectionSize() const { return RangesSectionSize; }
-
- /// Returns size of generated .debug_rnglists section.
- uint64_t getDebugRngListsSectionSize() const { return RngListsSectionSize; }
+ /// Emit DIE recursively.
+ void emitDIE(DIE &Die);
/// Returns size of generated .debug_info section.
uint64_t getDebugInfoSectionSize() const { return DebugInfoSectionSize; }
- /// Returns size of generated .debug_macinfo section.
- uint64_t getDebugMacInfoSectionSize() const { return MacInfoSectionSize; }
-
- /// Returns size of generated .debug_macro section.
- uint64_t getDebugMacroSectionSize() const { return MacroSectionSize; }
-
- /// Returns size of generated .debug_loc section.
- uint64_t getDebugLocSectionSize() const { return LocSectionSize; }
-
- /// Returns size of generated .debug_loclists section.
- uint64_t getDebugLocListsSectionSize() const { return LocListsSectionSize; }
-
private:
- inline void warn(const Twine &Warning, StringRef Context = "") {
- if (WarningHandler)
- WarningHandler(Warning, Context, nullptr);
- }
-
- void emitMacroTableImpl(const DWARFDebugMacro *MacroTable,
- const Offset2UnitMapTy &UnitMacroMap,
- StringPool &StringPool, uint64_t &OutOffset) {}
-
- /// Emit piece of .debug_ranges for \p LinkedRanges.
- void emitDwarfDebugRangesTableFragment(const CompileUnit &Unit,
- const AddressRanges &LinkedRanges,
- RangeAttrPatch &Patch) {}
-
- /// Emit piece of .debug_rnglists for \p LinkedRanges.
- void emitDwarfDebugRngListsTableFragment(const CompileUnit &Unit,
- const AddressRanges &LinkedRanges,
- RangeAttrPatch &Patch) {}
-
- /// Emit piece of .debug_loc for \p LinkedRanges.
- void emitDwarfDebugLocTableFragment(
- const CompileUnit &Unit,
- const DWARFLocationExpressionsVector &LinkedLocationExpression,
- LocAttrPatch &Patch) {}
-
- /// Emit piece of .debug_loclists for \p LinkedRanges.
- void emitDwarfDebugLocListsTableFragment(
- const CompileUnit &Unit,
- const DWARFLocationExpressionsVector &LinkedLocationExpression,
- LocAttrPatch &Patch) {}
+ // Enumerate all string patches and write them into the destination section.
+ // Order of patches is the same as in original input file. To avoid emitting
+ // the same string twice we accumulate NextOffset value. Thus if string
+ // offset smaller than NextOffset value then the patch is skipped (as that
+ // string was emitted earlier).
+ template <typename PatchTy>
+ void emitStringsImpl(ArrayList<PatchTy> &StringPatches,
+ const StringEntryToDwarfStringPoolEntryMap &Strings,
+ uint64_t &NextOffset, MCSection *OutSection);
+
+ MCSection *switchSection(StringRef SecName);
/// \defgroup MCObjects MC layer objects constructed by the streamer
/// @{
@@ -240,32 +119,8 @@ class DwarfEmitterImpl : public ExtraDwarfEmitter {
/// The output file we stream the linked Dwarf to.
raw_pwrite_stream &OutFile;
DWARFLinker::OutputFileType OutFileType = DWARFLinker::OutputFileType::Object;
- std::function<StringRef(StringRef Input)> Translator;
- uint64_t RangesSectionSize = 0;
- uint64_t RngListsSectionSize = 0;
- uint64_t LocSectionSize = 0;
- uint64_t LocListsSectionSize = 0;
- uint64_t LineSectionSize = 0;
- uint64_t FrameSectionSize = 0;
uint64_t DebugInfoSectionSize = 0;
- uint64_t MacInfoSectionSize = 0;
- uint64_t MacroSectionSize = 0;
-
- /// Keep track of emitted CUs and their Unique ID.
- struct EmittedUnit {
- unsigned ID;
- MCSymbol *LabelBegin;
- };
- std::vector<EmittedUnit> EmittedUnitsTy;
-
- /// Emit the pubnames or pubtypes section contribution for \p
- /// Unit into \p Sec. The data is provided in \p Names.
- void emitPubSectionForUnit(MCSection *Sec, StringRef Name,
- const CompileUnit &Unit,
- const std::vector<CompileUnit::AccelInfo> &Names);
-
- DWARFLinker::MessageHandlerTy WarningHandler = nullptr;
};
} // end namespace dwarflinker_parallel
diff --git a/llvm/lib/DWARFLinkerParallel/DWARFFile.cpp b/llvm/lib/DWARFLinkerParallel/DWARFFile.cpp
new file mode 100644
index 00000000000000..86c7e816ead8fe
--- /dev/null
+++ b/llvm/lib/DWARFLinkerParallel/DWARFFile.cpp
@@ -0,0 +1,18 @@
+//=== DWARFFile.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 "llvm/DWARFLinkerParallel/DWARFFile.h"
+#include "DWARFLinkerGlobalData.h"
+
+llvm::dwarflinker_parallel::DWARFFile::DWARFFile(
+ StringRef Name, std::unique_ptr<DWARFContext> Dwarf,
+ std::unique_ptr<AddressesMap> Addresses,
+ const std::vector<std::string> &Warnings,
+ DWARFFile::UnloadCallbackTy UnloadFunc)
+ : FileName(Name), Dwarf(std::move(Dwarf)), Addresses(std::move(Addresses)),
+ Warnings(Warnings), UnloadFunc(UnloadFunc) {}
diff --git a/llvm/lib/DWARFLinkerParallel/DWARFLinkerCompileUnit.cpp b/llvm/lib/DWARFLinkerParallel/DWARFLinkerCompileUnit.cpp
new file mode 100644
index 00000000000000..783d9ea0d4954d
--- /dev/null
+++ b/llvm/lib/DWARFLinkerParallel/DWARFLinkerCompileUnit.cpp
@@ -0,0 +1,1396 @@
+//=== DWARFLinkerCompileUnit.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 "DWARFLinkerCompileUnit.h"
+#include "DIEAttributeCloner.h"
+#include "DIEGenerator.h"
+#include "llvm/DebugInfo/DWARF/DWARFDebugMacro.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormatVariadic.h"
+
+using namespace llvm;
+using namespace llvm::dwarflinker_parallel;
+
+void CompileUnit::loadLineTable() {
+ LineTablePtr = File.Dwarf->getLineTableForUnit(&getOrigUnit());
+}
+
+void CompileUnit::maybeResetToLoadedStage() {
+ if (getStage() <= Stage::Loaded)
+ return;
+
+ for (DIEInfo &DieInfo : DieInfoArray)
+ DieInfo.unsetFlagsWhichSetDuringLiveAnalysis();
+
+ LowPc = std::nullopt;
+ HighPc = 0;
+ Labels.clear();
+ Ranges.clear();
+
+ if (getStage() < Stage::Cloned) {
+ setStage(Stage::Loaded);
+ return;
+ }
+
+ AbbreviationsSet.clear();
+ Abbreviations.clear();
+ OutUnitDIE = nullptr;
+ DebugAddrIndexMap.clear();
+
+ for (uint64_t &Offset : OutDieOffsetArray)
+ Offset = 0;
+ eraseSections();
+
+ setStage(Stage::CreatedNotLoaded);
+}
+
+bool CompileUnit::loadInputDIEs() {
+ DWARFDie InputUnitDIE = getUnitDIE(false);
+ if (!InputUnitDIE)
+ return false;
+
+ // load input dies, resize Info structures array.
+ DieInfoArray.resize(getOrigUnit().getNumDIEs());
+ OutDieOffsetArray.resize(getOrigUnit().getNumDIEs(), 0);
+ return true;
+}
+
+void CompileUnit::analyzeDWARFStructureRec(const DWARFDebugInfoEntry *DieEntry,
+ bool IsInModule, bool IsInFunction) {
+ for (const DWARFDebugInfoEntry *CurChild = getFirstChildEntry(DieEntry);
+ CurChild && CurChild->getAbbreviationDeclarationPtr();
+ CurChild = getSiblingEntry(CurChild)) {
+ CompileUnit::DIEInfo &ChildInfo = getDIEInfo(CurChild);
+
+ if (IsInModule)
+ ChildInfo.setIsInMouduleScope();
+ if (IsInFunction)
+ ChildInfo.setIsInFunctionScope();
+
+ switch (CurChild->getTag()) {
+ case dwarf::DW_TAG_module:
+ ChildInfo.setIsInMouduleScope();
+ if (DieEntry->getTag() == dwarf::DW_TAG_compile_unit &&
+ dwarf::toString(find(CurChild, dwarf::DW_AT_name), "") !=
+ getClangModuleName())
+ analyzeImportedModule(CurChild);
+ break;
+ case dwarf::DW_TAG_subprogram:
+ ChildInfo.setIsInFunctionScope();
+ break;
+ default:
+ break;
+ }
+
+ if (IsInModule)
+ ChildInfo.setIsInMouduleScope();
+ if (IsInFunction)
+ ChildInfo.setIsInFunctionScope();
+
+ if (CurChild->hasChildren())
+ analyzeDWARFStructureRec(CurChild, ChildInfo.getIsInMouduleScope(),
+ ChildInfo.getIsInFunctionScope());
+ }
+}
+
+StringEntry *CompileUnit::getFileName(unsigned FileIdx,
+ StringPool &GlobalStrings) {
+ if (LineTablePtr) {
+ if (LineTablePtr->hasFileAtIndex(FileIdx)) {
+ // Cache the resolved paths based on the index in the line table,
+ // because calling realpath is expensive.
+ ResolvedPathsMap::const_iterator It = ResolvedFullPaths.find(FileIdx);
+ if (It == ResolvedFullPaths.end()) {
+ std::string OrigFileName;
+ bool FoundFileName = LineTablePtr->getFileNameByIndex(
+ FileIdx, getOrigUnit().getCompilationDir(),
+ DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,
+ OrigFileName);
+ (void)FoundFileName;
+ assert(FoundFileName && "Must get file name from line table");
+
+ // Second level of caching, this time based on the file's parent
+ // path.
+ StringRef FileName = sys::path::filename(OrigFileName);
+ StringRef ParentPath = sys::path::parent_path(OrigFileName);
+
+ // If the ParentPath has not yet been resolved, resolve and cache it for
+ // future look-ups.
+ StringMap<StringEntry *>::iterator ParentIt =
+ ResolvedParentPaths.find(ParentPath);
+ if (ParentIt == ResolvedParentPaths.end()) {
+ SmallString<256> RealPath;
+ sys::fs::real_path(ParentPath, RealPath);
+ ParentIt =
+ ResolvedParentPaths
+ .insert({ParentPath, GlobalStrings.insert(RealPath).first})
+ .first;
+ }
+
+ // Join the file name again with the resolved path.
+ SmallString<256> ResolvedPath(ParentIt->second->first());
+ sys::path::append(ResolvedPath, FileName);
+
+ It = ResolvedFullPaths
+ .insert(std::make_pair(
+ FileIdx, GlobalStrings.insert(ResolvedPath).first))
+ .first;
+ }
+
+ return It->second;
+ }
+ }
+
+ return nullptr;
+}
+
+void CompileUnit::cleanupDataAfterClonning() {
+ AbbreviationsSet.clear();
+ ResolvedFullPaths.shrink_and_clear();
+ ResolvedParentPaths.clear();
+ DieInfoArray = SmallVector<DIEInfo>();
+ OutDieOffsetArray = SmallVector<uint64_t>();
+ getOrigUnit().clear();
+}
+
+/// Collect references to parseable Swift interfaces in imported
+/// DW_TAG_module blocks.
+void CompileUnit::analyzeImportedModule(const DWARFDebugInfoEntry *DieEntry) {
+ if (getLanguage() != dwarf::DW_LANG_Swift)
+ return;
+
+ if (!GlobalData.getOptions().ParseableSwiftInterfaces)
+ return;
+
+ StringRef Path =
+ dwarf::toStringRef(find(DieEntry, dwarf::DW_AT_LLVM_include_path));
+ if (!Path.endswith(".swiftinterface"))
+ return;
+ // Don't track interfaces that are part of the SDK.
+ StringRef SysRoot =
+ dwarf::toStringRef(find(DieEntry, dwarf::DW_AT_LLVM_sysroot));
+ if (SysRoot.empty())
+ SysRoot = getSysRoot();
+ if (!SysRoot.empty() && Path.startswith(SysRoot))
+ return;
+ if (std::optional<DWARFFormValue> Val = find(DieEntry, dwarf::DW_AT_name)) {
+ Expected<const char *> Name = Val->getAsCString();
+ if (!Name) {
+ warn(Name.takeError());
+ return;
+ }
+
+ auto &Entry = (*GlobalData.getOptions().ParseableSwiftInterfaces)[*Name];
+ // The prepend path is applied later when copying.
+ SmallString<128> ResolvedPath;
+ if (sys::path::is_relative(Path))
+ sys::path::append(
+ ResolvedPath,
+ dwarf::toString(getUnitDIE().find(dwarf::DW_AT_comp_dir), ""));
+ sys::path::append(ResolvedPath, Path);
+ if (!Entry.empty() && Entry != ResolvedPath) {
+ DWARFDie Die = getDIE(DieEntry);
+ warn(Twine("conflicting parseable interfaces for Swift Module ") + *Name +
+ ": " + Entry + " and " + Path + ".",
+ &Die);
+ }
+ Entry = std::string(ResolvedPath.str());
+ }
+}
+
+void CompileUnit::updateDieRefPatchesWithClonedOffsets() {
+ if (std::optional<SectionDescriptor *> DebugInfoSection =
+ getSectionDescriptor(DebugSectionKind::DebugInfo)) {
+
+ (*DebugInfoSection)
+ ->ListDebugDieRefPatch.forEach([](DebugDieRefPatch &Patch) {
+ Patch.RefDieIdxOrClonedOffset =
+ Patch.RefCU.getPointer()->getDieOutOffset(
+ Patch.RefDieIdxOrClonedOffset);
+ });
+
+ (*DebugInfoSection)
+ ->ListDebugULEB128DieRefPatch.forEach(
+ [](DebugULEB128DieRefPatch &Patch) {
+ Patch.RefDieIdxOrClonedOffset =
+ Patch.RefCU.getPointer()->getDieOutOffset(
+ Patch.RefDieIdxOrClonedOffset);
+ });
+ }
+
+ if (std::optional<SectionDescriptor *> DebugLocSection =
+ getSectionDescriptor(DebugSectionKind::DebugLoc)) {
+ (*DebugLocSection)
+ ->ListDebugULEB128DieRefPatch.forEach(
+ [](DebugULEB128DieRefPatch &Patch) {
+ Patch.RefDieIdxOrClonedOffset =
+ Patch.RefCU.getPointer()->getDieOutOffset(
+ Patch.RefDieIdxOrClonedOffset);
+ });
+ }
+
+ if (std::optional<SectionDescriptor *> DebugLocListsSection =
+ getSectionDescriptor(DebugSectionKind::DebugLocLists)) {
+ (*DebugLocListsSection)
+ ->ListDebugULEB128DieRefPatch.forEach(
+ [](DebugULEB128DieRefPatch &Patch) {
+ Patch.RefDieIdxOrClonedOffset =
+ Patch.RefCU.getPointer()->getDieOutOffset(
+ Patch.RefDieIdxOrClonedOffset);
+ });
+ }
+}
+
+std::optional<std::pair<CompileUnit *, uint32_t>>
+CompileUnit::resolveDIEReference(const DWARFFormValue &RefValue) {
+ if (std::optional<DWARFFormValue::UnitOffset> Ref =
+ *RefValue.getAsRelativeReference()) {
+ if (Ref->Unit != nullptr) {
+ // Referenced DIE is in current compile unit.
+
+ if (std::optional<uint32_t> RefDieIdx =
+ getDIEIndexForOffset(Ref->Unit->getOffset() + Ref->Offset))
+ return std::make_pair(this, *RefDieIdx);
+ } else if (CompileUnit *RefCU = getUnitFromOffset(Ref->Offset)) {
+ // Referenced DIE is in other compile unit.
+
+ // Check whether DIEs are loaded for that compile unit.
+ enum Stage ReferredCUStage = RefCU->getStage();
+ if (ReferredCUStage < Stage::Loaded || ReferredCUStage > Stage::Cloned)
+ return std::make_pair(RefCU, 0);
+
+ if (std::optional<uint32_t> RefDieIdx =
+ RefCU->getDIEIndexForOffset(Ref->Offset))
+ return std::make_pair(RefCU, *RefDieIdx);
+ }
+ }
+
+ return std::nullopt;
+}
+
+void CompileUnit::addFunctionRange(uint64_t FuncLowPc, uint64_t FuncHighPc,
+ int64_t PcOffset) {
+ std::lock_guard<std::mutex> Guard(RangesMutex);
+
+ Ranges.insert({FuncLowPc, FuncHighPc}, PcOffset);
+ if (LowPc)
+ LowPc = std::min(*LowPc, FuncLowPc + PcOffset);
+ else
+ LowPc = FuncLowPc + PcOffset;
+ this->HighPc = std::max(HighPc, FuncHighPc + PcOffset);
+}
+
+void CompileUnit::addLabelLowPc(uint64_t LabelLowPc, int64_t PcOffset) {
+ std::lock_guard<std::mutex> Guard(LabelsMutex);
+ Labels.insert({LabelLowPc, PcOffset});
+}
+
+Error CompileUnit::cloneAndEmitDebugLocations() {
+ if (getGlobalData().getOptions().UpdateIndexTablesOnly)
+ return Error::success();
+
+ if (getOrigUnit().getVersion() < 5) {
+ emitLocations(DebugSectionKind::DebugLoc);
+ return Error::success();
+ }
+
+ emitLocations(DebugSectionKind::DebugLocLists);
+ return Error::success();
+}
+
+void CompileUnit::emitLocations(DebugSectionKind LocationSectionKind) {
+ SectionDescriptor &DebugInfoSection =
+ getOrCreateSectionDescriptor(DebugSectionKind::DebugInfo);
+
+ if (!DebugInfoSection.ListDebugLocPatch.empty()) {
+ SectionDescriptor &OutLocationSection =
+ getOrCreateSectionDescriptor(LocationSectionKind);
+ DWARFUnit &OrigUnit = getOrigUnit();
+
+ uint64_t OffsetAfterUnitLength = emitLocListHeader(OutLocationSection);
+
+ DebugInfoSection.ListDebugLocPatch.forEach([&](DebugLocPatch &Patch) {
+ // Get location expressions vector corresponding to the current
+ // attribute from the source DWARF.
+ uint64_t InputDebugLocSectionOffset = DebugInfoSection.getIntVal(
+ Patch.PatchOffset,
+ DebugInfoSection.getFormParams().getDwarfOffsetByteSize());
+ Expected<DWARFLocationExpressionsVector> OriginalLocations =
+ OrigUnit.findLoclistFromOffset(InputDebugLocSectionOffset);
+
+ if (!OriginalLocations) {
+ warn(OriginalLocations.takeError());
+ return;
+ }
+
+ LinkedLocationExpressionsVector LinkedLocationExpressions;
+ for (DWARFLocationExpression &CurExpression : *OriginalLocations) {
+ LinkedLocationExpressionsWithOffsetPatches LinkedExpression;
+
+ if (CurExpression.Range) {
+ // Relocate address range.
+ LinkedExpression.Expression.Range = {
+ CurExpression.Range->LowPC + Patch.AddrAdjustmentValue,
+ CurExpression.Range->HighPC + Patch.AddrAdjustmentValue};
+ }
+
+ DataExtractor Data(CurExpression.Expr, OrigUnit.isLittleEndian(),
+ OrigUnit.getAddressByteSize());
+
+ DWARFExpression InputExpression(Data, OrigUnit.getAddressByteSize(),
+ OrigUnit.getFormParams().Format);
+ cloneDieAttrExpression(InputExpression,
+ LinkedExpression.Expression.Expr,
+ OutLocationSection, Patch.AddrAdjustmentValue,
+ LinkedExpression.Patches);
+
+ LinkedLocationExpressions.push_back({LinkedExpression});
+ }
+
+ // Emit locations list table fragment corresponding to the CurLocAttr.
+ DebugInfoSection.apply(Patch.PatchOffset, dwarf::DW_FORM_sec_offset,
+ OutLocationSection.OS.tell());
+ emitLocListFragment(LinkedLocationExpressions, OutLocationSection);
+ });
+
+ if (OffsetAfterUnitLength > 0) {
+ assert(OffsetAfterUnitLength -
+ OutLocationSection.getFormParams().getDwarfOffsetByteSize() <
+ OffsetAfterUnitLength);
+ OutLocationSection.apply(
+ OffsetAfterUnitLength -
+ OutLocationSection.getFormParams().getDwarfOffsetByteSize(),
+ dwarf::DW_FORM_sec_offset,
+ OutLocationSection.OS.tell() - OffsetAfterUnitLength);
+ }
+ }
+}
+
+/// Emit debug locations(.debug_loc, .debug_loclists) header.
+uint64_t CompileUnit::emitLocListHeader(SectionDescriptor &OutLocationSection) {
+ if (getOrigUnit().getVersion() < 5)
+ return 0;
+
+ // unit_length.
+ OutLocationSection.emitUnitLength(0xBADDEF);
+ uint64_t OffsetAfterUnitLength = OutLocationSection.OS.tell();
+
+ // Version.
+ OutLocationSection.emitIntVal(5, 2);
+
+ // Address size.
+ OutLocationSection.emitIntVal(OutLocationSection.getFormParams().AddrSize, 1);
+
+ // Seg_size
+ OutLocationSection.emitIntVal(0, 1);
+
+ // Offset entry count
+ OutLocationSection.emitIntVal(0, 4);
+
+ return OffsetAfterUnitLength;
+}
+
+/// Emit debug locations(.debug_loc, .debug_loclists) fragment.
+uint64_t CompileUnit::emitLocListFragment(
+ const LinkedLocationExpressionsVector &LinkedLocationExpression,
+ SectionDescriptor &OutLocationSection) {
+ uint64_t OffsetBeforeLocationExpression = 0;
+
+ if (getOrigUnit().getVersion() < 5) {
+ uint64_t BaseAddress = 0;
+ if (std::optional<uint64_t> LowPC = getLowPc())
+ BaseAddress = *LowPC;
+
+ for (const LinkedLocationExpressionsWithOffsetPatches &LocExpression :
+ LinkedLocationExpression) {
+ if (LocExpression.Expression.Range) {
+ OutLocationSection.emitIntVal(
+ LocExpression.Expression.Range->LowPC - BaseAddress,
+ OutLocationSection.getFormParams().AddrSize);
+ OutLocationSection.emitIntVal(
+ LocExpression.Expression.Range->HighPC - BaseAddress,
+ OutLocationSection.getFormParams().AddrSize);
+ }
+
+ OutLocationSection.emitIntVal(LocExpression.Expression.Expr.size(), 2);
+ OffsetBeforeLocationExpression = OutLocationSection.OS.tell();
+ for (uint64_t *OffsetPtr : LocExpression.Patches)
+ *OffsetPtr += OffsetBeforeLocationExpression;
+
+ OutLocationSection.OS
+ << StringRef((const char *)LocExpression.Expression.Expr.data(),
+ LocExpression.Expression.Expr.size());
+ }
+
+ // Emit the terminator entry.
+ OutLocationSection.emitIntVal(0,
+ OutLocationSection.getFormParams().AddrSize);
+ OutLocationSection.emitIntVal(0,
+ OutLocationSection.getFormParams().AddrSize);
+ return OffsetBeforeLocationExpression;
+ }
+
+ std::optional<uint64_t> BaseAddress;
+ for (const LinkedLocationExpressionsWithOffsetPatches &LocExpression :
+ LinkedLocationExpression) {
+ if (LocExpression.Expression.Range) {
+ // Check whether base address is set. If it is not set yet
+ // then set current base address and emit base address selection entry.
+ if (!BaseAddress) {
+ BaseAddress = LocExpression.Expression.Range->LowPC;
+
+ // Emit base address.
+ OutLocationSection.emitIntVal(dwarf::DW_LLE_base_addressx, 1);
+ encodeULEB128(DebugAddrIndexMap.getValueIndex(*BaseAddress),
+ OutLocationSection.OS);
+ }
+
+ // Emit type of entry.
+ OutLocationSection.emitIntVal(dwarf::DW_LLE_offset_pair, 1);
+
+ // Emit start offset relative to base address.
+ encodeULEB128(LocExpression.Expression.Range->LowPC - *BaseAddress,
+ OutLocationSection.OS);
+
+ // Emit end offset relative to base address.
+ encodeULEB128(LocExpression.Expression.Range->HighPC - *BaseAddress,
+ OutLocationSection.OS);
+ } else
+ // Emit type of entry.
+ OutLocationSection.emitIntVal(dwarf::DW_LLE_default_location, 1);
+
+ encodeULEB128(LocExpression.Expression.Expr.size(), OutLocationSection.OS);
+ OffsetBeforeLocationExpression = OutLocationSection.OS.tell();
+ for (uint64_t *OffsetPtr : LocExpression.Patches)
+ *OffsetPtr += OffsetBeforeLocationExpression;
+
+ OutLocationSection.OS << StringRef(
+ (const char *)LocExpression.Expression.Expr.data(),
+ LocExpression.Expression.Expr.size());
+ }
+
+ // Emit the terminator entry.
+ OutLocationSection.emitIntVal(dwarf::DW_LLE_end_of_list, 1);
+ return OffsetBeforeLocationExpression;
+}
+
+Error CompileUnit::emitDebugAddrSection() {
+ if (GlobalData.getOptions().UpdateIndexTablesOnly)
+ return Error::success();
+
+ if (getVersion() < 5)
+ return Error::success();
+
+ if (DebugAddrIndexMap.empty())
+ return Error::success();
+
+ SectionDescriptor &OutAddrSection =
+ getOrCreateSectionDescriptor(DebugSectionKind::DebugAddr);
+
+ // Emit section header.
+
+ // Emit length.
+ OutAddrSection.emitUnitLength(0xBADDEF);
+ uint64_t OffsetAfterSectionLength = OutAddrSection.OS.tell();
+
+ // Emit version.
+ OutAddrSection.emitIntVal(5, 2);
+
+ // Emit address size.
+ OutAddrSection.emitIntVal(getFormParams().AddrSize, 1);
+
+ // Emit segment size.
+ OutAddrSection.emitIntVal(0, 1);
+
+ // Emit addresses.
+ for (uint64_t AddrValue : DebugAddrIndexMap.getValues())
+ OutAddrSection.emitIntVal(AddrValue, getFormParams().AddrSize);
+
+ // Patch section length.
+ OutAddrSection.apply(
+ OffsetAfterSectionLength -
+ OutAddrSection.getFormParams().getDwarfOffsetByteSize(),
+ dwarf::DW_FORM_sec_offset,
+ OutAddrSection.OS.tell() - OffsetAfterSectionLength);
+
+ return Error::success();
+}
+
+Error CompileUnit::emitDebugStringOffsetSection() {
+ if (getVersion() < 5)
+ return Error::success();
+
+ if (DebugStringIndexMap.empty())
+ return Error::success();
+
+ SectionDescriptor &OutDebugStrOffsetsSection =
+ getOrCreateSectionDescriptor(DebugSectionKind::DebugStrOffsets);
+
+ // Emit section header.
+
+ // Emit length.
+ OutDebugStrOffsetsSection.emitUnitLength(0xBADDEF);
+ uint64_t OffsetAfterSectionLength = OutDebugStrOffsetsSection.OS.tell();
+
+ // Emit version.
+ OutDebugStrOffsetsSection.emitIntVal(5, 2);
+
+ // Emit padding.
+ OutDebugStrOffsetsSection.emitIntVal(0, 2);
+
+ // Emit index to offset map.
+ for (const StringEntry *String : DebugStringIndexMap.getValues()) {
+ // Note patch for string offset value.
+ OutDebugStrOffsetsSection.notePatch(
+ DebugStrPatch{{OutDebugStrOffsetsSection.OS.tell()}, String});
+
+ // Emit placeholder for offset value.
+ OutDebugStrOffsetsSection.emitOffset(0xBADDEF);
+ }
+
+ // Patch section length.
+ OutDebugStrOffsetsSection.apply(
+ OffsetAfterSectionLength -
+ OutDebugStrOffsetsSection.getFormParams().getDwarfOffsetByteSize(),
+ dwarf::DW_FORM_sec_offset,
+ OutDebugStrOffsetsSection.OS.tell() - OffsetAfterSectionLength);
+
+ return Error::success();
+}
+
+Error CompileUnit::cloneAndEmitRanges() {
+ if (getGlobalData().getOptions().UpdateIndexTablesOnly)
+ return Error::success();
+
+ // Build set of linked address ranges for unit function ranges.
+ AddressRanges LinkedFunctionRanges;
+ for (const AddressRangeValuePair &Range : getFunctionRanges())
+ LinkedFunctionRanges.insert(
+ {Range.Range.start() + Range.Value, Range.Range.end() + Range.Value});
+
+ emitAranges(LinkedFunctionRanges);
+
+ if (getOrigUnit().getVersion() < 5) {
+ cloneAndEmitRangeList(DebugSectionKind::DebugRange, LinkedFunctionRanges);
+ return Error::success();
+ }
+
+ cloneAndEmitRangeList(DebugSectionKind::DebugRngLists, LinkedFunctionRanges);
+ return Error::success();
+}
+
+void CompileUnit::cloneAndEmitRangeList(DebugSectionKind RngSectionKind,
+ AddressRanges &LinkedFunctionRanges) {
+ SectionDescriptor &DebugInfoSection =
+ getOrCreateSectionDescriptor(DebugSectionKind::DebugInfo);
+ SectionDescriptor &OutRangeSection =
+ getOrCreateSectionDescriptor(RngSectionKind);
+
+ if (!DebugInfoSection.ListDebugRangePatch.empty()) {
+ std::optional<AddressRangeValuePair> CachedRange;
+ uint64_t OffsetAfterUnitLength = emitRangeListHeader(OutRangeSection);
+
+ DebugRangePatch *CompileUnitRangePtr = nullptr;
+ DebugInfoSection.ListDebugRangePatch.forEach([&](DebugRangePatch &Patch) {
+ if (Patch.IsCompileUnitRanges) {
+ CompileUnitRangePtr = &Patch;
+ } else {
+ // Get ranges from the source DWARF corresponding to the current
+ // attribute.
+ AddressRanges LinkedRanges;
+ uint64_t InputDebugRangesSectionOffset = DebugInfoSection.getIntVal(
+ Patch.PatchOffset,
+ DebugInfoSection.getFormParams().getDwarfOffsetByteSize());
+ if (Expected<DWARFAddressRangesVector> InputRanges =
+ getOrigUnit().findRnglistFromOffset(
+ InputDebugRangesSectionOffset)) {
+ // Apply relocation adjustment.
+ for (const auto &Range : *InputRanges) {
+ if (!CachedRange || !CachedRange->Range.contains(Range.LowPC))
+ CachedRange =
+ getFunctionRanges().getRangeThatContains(Range.LowPC);
+
+ // All range entries should lie in the function range.
+ if (!CachedRange) {
+ warn("inconsistent range data.");
+ continue;
+ }
+
+ // Store range for emiting.
+ LinkedRanges.insert({Range.LowPC + CachedRange->Value,
+ Range.HighPC + CachedRange->Value});
+ }
+ } else {
+ llvm::consumeError(InputRanges.takeError());
+ warn("invalid range list ignored.");
+ }
+
+ // Emit linked ranges.
+ DebugInfoSection.apply(Patch.PatchOffset, dwarf::DW_FORM_sec_offset,
+ OutRangeSection.OS.tell());
+ emitRangeListFragment(LinkedRanges, OutRangeSection);
+ }
+ });
+
+ if (CompileUnitRangePtr != nullptr) {
+ // Emit compile unit ranges last to be binary compatible with classic
+ // dsymutil.
+ DebugInfoSection.apply(CompileUnitRangePtr->PatchOffset,
+ dwarf::DW_FORM_sec_offset,
+ OutRangeSection.OS.tell());
+ emitRangeListFragment(LinkedFunctionRanges, OutRangeSection);
+ }
+
+ if (OffsetAfterUnitLength > 0) {
+ assert(OffsetAfterUnitLength -
+ OutRangeSection.getFormParams().getDwarfOffsetByteSize() <
+ OffsetAfterUnitLength);
+ OutRangeSection.apply(
+ OffsetAfterUnitLength -
+ OutRangeSection.getFormParams().getDwarfOffsetByteSize(),
+ dwarf::DW_FORM_sec_offset,
+ OutRangeSection.OS.tell() - OffsetAfterUnitLength);
+ }
+ }
+}
+
+uint64_t CompileUnit::emitRangeListHeader(SectionDescriptor &OutRangeSection) {
+ if (OutRangeSection.getFormParams().Version < 5)
+ return 0;
+
+ // unit_length.
+ OutRangeSection.emitUnitLength(0xBADDEF);
+ uint64_t OffsetAfterUnitLength = OutRangeSection.OS.tell();
+
+ // Version.
+ OutRangeSection.emitIntVal(5, 2);
+
+ // Address size.
+ OutRangeSection.emitIntVal(OutRangeSection.getFormParams().AddrSize, 1);
+
+ // Seg_size
+ OutRangeSection.emitIntVal(0, 1);
+
+ // Offset entry count
+ OutRangeSection.emitIntVal(0, 4);
+
+ return OffsetAfterUnitLength;
+}
+
+void CompileUnit::emitRangeListFragment(const AddressRanges &LinkedRanges,
+ SectionDescriptor &OutRangeSection) {
+ if (OutRangeSection.getFormParams().Version < 5) {
+ // Emit ranges.
+ uint64_t BaseAddress = 0;
+ if (std::optional<uint64_t> LowPC = getLowPc())
+ BaseAddress = *LowPC;
+
+ for (const AddressRange &Range : LinkedRanges) {
+ OutRangeSection.emitIntVal(Range.start() - BaseAddress,
+ OutRangeSection.getFormParams().AddrSize);
+ OutRangeSection.emitIntVal(Range.end() - BaseAddress,
+ OutRangeSection.getFormParams().AddrSize);
+ }
+
+ // Add the terminator entry.
+ OutRangeSection.emitIntVal(0, OutRangeSection.getFormParams().AddrSize);
+ OutRangeSection.emitIntVal(0, OutRangeSection.getFormParams().AddrSize);
+ return;
+ }
+
+ std::optional<uint64_t> BaseAddress;
+ for (const AddressRange &Range : LinkedRanges) {
+ if (!BaseAddress) {
+ BaseAddress = Range.start();
+
+ // Emit base address.
+ OutRangeSection.emitIntVal(dwarf::DW_RLE_base_addressx, 1);
+ encodeULEB128(getDebugAddrIndex(*BaseAddress), OutRangeSection.OS);
+ }
+
+ // Emit type of entry.
+ OutRangeSection.emitIntVal(dwarf::DW_RLE_offset_pair, 1);
+
+ // Emit start offset relative to base address.
+ encodeULEB128(Range.start() - *BaseAddress, OutRangeSection.OS);
+
+ // Emit end offset relative to base address.
+ encodeULEB128(Range.end() - *BaseAddress, OutRangeSection.OS);
+ }
+
+ // Emit the terminator entry.
+ OutRangeSection.emitIntVal(dwarf::DW_RLE_end_of_list, 1);
+}
+
+void CompileUnit::emitAranges(AddressRanges &LinkedFunctionRanges) {
+ if (LinkedFunctionRanges.empty())
+ return;
+
+ SectionDescriptor &DebugInfoSection =
+ getOrCreateSectionDescriptor(DebugSectionKind::DebugInfo);
+ SectionDescriptor &OutArangesSection =
+ getOrCreateSectionDescriptor(DebugSectionKind::DebugARanges);
+
+ // Emit Header.
+ unsigned HeaderSize =
+ sizeof(int32_t) + // Size of contents (w/o this field
+ sizeof(int16_t) + // DWARF ARange version number
+ sizeof(int32_t) + // Offset of CU in the .debug_info section
+ sizeof(int8_t) + // Pointer Size (in bytes)
+ sizeof(int8_t); // Segment Size (in bytes)
+
+ unsigned TupleSize = OutArangesSection.getFormParams().AddrSize * 2;
+ unsigned Padding = offsetToAlignment(HeaderSize, Align(TupleSize));
+
+ OutArangesSection.emitOffset(0xBADDEF); // Aranges length
+ uint64_t OffsetAfterArangesLengthField = OutArangesSection.OS.tell();
+
+ OutArangesSection.emitIntVal(dwarf::DW_ARANGES_VERSION, 2); // Version number
+ OutArangesSection.notePatch(
+ DebugOffsetPatch{OutArangesSection.OS.tell(), &DebugInfoSection});
+ OutArangesSection.emitOffset(0xBADDEF); // Corresponding unit's offset
+ OutArangesSection.emitIntVal(OutArangesSection.getFormParams().AddrSize,
+ 1); // Address size
+ OutArangesSection.emitIntVal(0, 1); // Segment size
+
+ for (size_t Idx = 0; Idx < Padding; Idx++)
+ OutArangesSection.emitIntVal(0, 1); // Padding
+
+ // Emit linked ranges.
+ for (const AddressRange &Range : LinkedFunctionRanges) {
+ OutArangesSection.emitIntVal(Range.start(),
+ OutArangesSection.getFormParams().AddrSize);
+ OutArangesSection.emitIntVal(Range.end() - Range.start(),
+ OutArangesSection.getFormParams().AddrSize);
+ }
+
+ // Emit terminator.
+ OutArangesSection.emitIntVal(0, OutArangesSection.getFormParams().AddrSize);
+ OutArangesSection.emitIntVal(0, OutArangesSection.getFormParams().AddrSize);
+
+ uint64_t OffsetAfterArangesEnd = OutArangesSection.OS.tell();
+
+ // Update Aranges lentgh.
+ OutArangesSection.apply(
+ OffsetAfterArangesLengthField -
+ OutArangesSection.getFormParams().getDwarfOffsetByteSize(),
+ dwarf::DW_FORM_sec_offset,
+ OffsetAfterArangesEnd - OffsetAfterArangesLengthField);
+}
+
+Error CompileUnit::cloneAndEmitDebugMacro() {
+ if (getOutUnitDIE() == nullptr)
+ return Error::success();
+
+ DWARFUnit &OrigUnit = getOrigUnit();
+ DWARFDie OrigUnitDie = OrigUnit.getUnitDIE();
+
+ // Check for .debug_macro table.
+ if (std::optional<uint64_t> MacroAttr =
+ dwarf::toSectionOffset(OrigUnitDie.find(dwarf::DW_AT_macros))) {
+ if (const DWARFDebugMacro *Table =
+ getContaingFile().Dwarf->getDebugMacro()) {
+ emitMacroTableImpl(Table, *MacroAttr, true);
+ }
+ }
+
+ // Check for .debug_macinfo table.
+ if (std::optional<uint64_t> MacroAttr =
+ dwarf::toSectionOffset(OrigUnitDie.find(dwarf::DW_AT_macro_info))) {
+ if (const DWARFDebugMacro *Table =
+ getContaingFile().Dwarf->getDebugMacinfo()) {
+ emitMacroTableImpl(Table, *MacroAttr, false);
+ }
+ }
+
+ return Error::success();
+}
+
+void CompileUnit::emitMacroTableImpl(const DWARFDebugMacro *MacroTable,
+ uint64_t OffsetToMacroTable,
+ bool hasDWARFv5Header) {
+ SectionDescriptor &OutSection =
+ hasDWARFv5Header
+ ? getOrCreateSectionDescriptor(DebugSectionKind::DebugMacro)
+ : getOrCreateSectionDescriptor(DebugSectionKind::DebugMacinfo);
+
+ bool DefAttributeIsReported = false;
+ bool UndefAttributeIsReported = false;
+ bool ImportAttributeIsReported = false;
+
+ for (const DWARFDebugMacro::MacroList &List : MacroTable->MacroLists) {
+ if (OffsetToMacroTable == List.Offset) {
+ // Write DWARFv5 header.
+ if (hasDWARFv5Header) {
+ // Write header version.
+ OutSection.emitIntVal(List.Header.Version, sizeof(List.Header.Version));
+
+ uint8_t Flags = List.Header.Flags;
+
+ // Check for OPCODE_OPERANDS_TABLE.
+ if (Flags &
+ DWARFDebugMacro::HeaderFlagMask::MACRO_OPCODE_OPERANDS_TABLE) {
+ Flags &=
+ ~DWARFDebugMacro::HeaderFlagMask::MACRO_OPCODE_OPERANDS_TABLE;
+ warn("opcode_operands_table is not supported yet.");
+ }
+
+ // Check for DEBUG_LINE_OFFSET.
+ std::optional<uint64_t> StmtListOffset;
+ if (Flags & DWARFDebugMacro::HeaderFlagMask::MACRO_DEBUG_LINE_OFFSET) {
+ // Get offset to the line table from the cloned compile unit.
+ for (auto &V : getOutUnitDIE()->values()) {
+ if (V.getAttribute() == dwarf::DW_AT_stmt_list) {
+ StmtListOffset = V.getDIEInteger().getValue();
+ break;
+ }
+ }
+
+ if (!StmtListOffset) {
+ Flags &= ~DWARFDebugMacro::HeaderFlagMask::MACRO_DEBUG_LINE_OFFSET;
+ warn("couldn`t find line table for macro table.");
+ }
+ }
+
+ // Write flags.
+ OutSection.emitIntVal(Flags, sizeof(Flags));
+
+ // Write offset to line table.
+ if (StmtListOffset) {
+ OutSection.notePatch(DebugOffsetPatch{
+ OutSection.OS.tell(),
+ &getOrCreateSectionDescriptor(DebugSectionKind::DebugLine)});
+ // TODO: check that List.Header.getOffsetByteSize() and
+ // DebugOffsetPatch agree on size.
+ OutSection.emitIntVal(0xBADDEF, List.Header.getOffsetByteSize());
+ }
+ }
+
+ // Write macro entries.
+ for (const DWARFDebugMacro::Entry &MacroEntry : List.Macros) {
+ if (MacroEntry.Type == 0) {
+ encodeULEB128(MacroEntry.Type, OutSection.OS);
+ continue;
+ }
+
+ uint8_t MacroType = MacroEntry.Type;
+ switch (MacroType) {
+ default: {
+ bool HasVendorSpecificExtension =
+ (!hasDWARFv5Header &&
+ MacroType == dwarf::DW_MACINFO_vendor_ext) ||
+ (hasDWARFv5Header && (MacroType >= dwarf::DW_MACRO_lo_user &&
+ MacroType <= dwarf::DW_MACRO_hi_user));
+
+ if (HasVendorSpecificExtension) {
+ // Write macinfo type.
+ OutSection.emitIntVal(MacroType, 1);
+
+ // Write vendor extension constant.
+ encodeULEB128(MacroEntry.ExtConstant, OutSection.OS);
+
+ // Write vendor extension string.
+ OutSection.emitString(dwarf::DW_FORM_string, MacroEntry.ExtStr);
+ } else
+ warn("unknown macro type. skip.");
+ } break;
+ // debug_macro and debug_macinfo share some common encodings.
+ // DW_MACRO_define == DW_MACINFO_define
+ // DW_MACRO_undef == DW_MACINFO_undef
+ // DW_MACRO_start_file == DW_MACINFO_start_file
+ // DW_MACRO_end_file == DW_MACINFO_end_file
+ // For readibility/uniformity we are using DW_MACRO_*.
+ case dwarf::DW_MACRO_define:
+ case dwarf::DW_MACRO_undef: {
+ // Write macinfo type.
+ OutSection.emitIntVal(MacroType, 1);
+
+ // Write source line.
+ encodeULEB128(MacroEntry.Line, OutSection.OS);
+
+ // Write macro string.
+ OutSection.emitString(dwarf::DW_FORM_string, MacroEntry.MacroStr);
+ } break;
+ case dwarf::DW_MACRO_define_strp:
+ case dwarf::DW_MACRO_undef_strp:
+ case dwarf::DW_MACRO_define_strx:
+ case dwarf::DW_MACRO_undef_strx: {
+ // DW_MACRO_*_strx forms are not supported currently.
+ // Convert to *_strp.
+ switch (MacroType) {
+ case dwarf::DW_MACRO_define_strx: {
+ MacroType = dwarf::DW_MACRO_define_strp;
+ if (!DefAttributeIsReported) {
+ warn("DW_MACRO_define_strx unsupported yet. Convert to "
+ "DW_MACRO_define_strp.");
+ DefAttributeIsReported = true;
+ }
+ } break;
+ case dwarf::DW_MACRO_undef_strx: {
+ MacroType = dwarf::DW_MACRO_undef_strp;
+ if (!UndefAttributeIsReported) {
+ warn("DW_MACRO_undef_strx unsupported yet. Convert to "
+ "DW_MACRO_undef_strp.");
+ UndefAttributeIsReported = true;
+ }
+ } break;
+ default:
+ // Nothing to do.
+ break;
+ }
+
+ // Write macinfo type.
+ OutSection.emitIntVal(MacroType, 1);
+
+ // Write source line.
+ encodeULEB128(MacroEntry.Line, OutSection.OS);
+
+ // Write macro string.
+ OutSection.emitString(dwarf::DW_FORM_strp, MacroEntry.MacroStr);
+ break;
+ }
+ case dwarf::DW_MACRO_start_file: {
+ // Write macinfo type.
+ OutSection.emitIntVal(MacroType, 1);
+ // Write source line.
+ encodeULEB128(MacroEntry.Line, OutSection.OS);
+ // Write source file id.
+ encodeULEB128(MacroEntry.File, OutSection.OS);
+ } break;
+ case dwarf::DW_MACRO_end_file: {
+ // Write macinfo type.
+ OutSection.emitIntVal(MacroType, 1);
+ } break;
+ case dwarf::DW_MACRO_import:
+ case dwarf::DW_MACRO_import_sup: {
+ if (!ImportAttributeIsReported) {
+ warn("DW_MACRO_import and DW_MACRO_import_sup are unsupported "
+ "yet. remove.");
+ ImportAttributeIsReported = true;
+ }
+ } break;
+ }
+ }
+
+ return;
+ }
+ }
+}
+
+void CompileUnit::cloneDieAttrExpression(
+ const DWARFExpression &InputExpression,
+ SmallVectorImpl<uint8_t> &OutputExpression, SectionDescriptor &Section,
+ std::optional<int64_t> VarAddressAdjustment,
+ OffsetsPtrVector &PatchesOffsets) {
+ using Encoding = DWARFExpression::Operation::Encoding;
+
+ DWARFUnit &OrigUnit = getOrigUnit();
+ uint8_t OrigAddressByteSize = OrigUnit.getAddressByteSize();
+
+ uint64_t OpOffset = 0;
+ for (auto &Op : InputExpression) {
+ auto Desc = Op.getDescription();
+ // DW_OP_const_type is variable-length and has 3
+ // operands. Thus far we only support 2.
+ if ((Desc.Op.size() == 2 && Desc.Op[0] == Encoding::BaseTypeRef) ||
+ (Desc.Op.size() == 2 && Desc.Op[1] == Encoding::BaseTypeRef &&
+ Desc.Op[0] != Encoding::Size1))
+ warn("unsupported DW_OP encoding.");
+
+ if ((Desc.Op.size() == 1 && Desc.Op[0] == Encoding::BaseTypeRef) ||
+ (Desc.Op.size() == 2 && Desc.Op[1] == Encoding::BaseTypeRef &&
+ Desc.Op[0] == Encoding::Size1)) {
+ // This code assumes that the other non-typeref operand fits into 1 byte.
+ assert(OpOffset < Op.getEndOffset());
+ uint32_t ULEBsize = Op.getEndOffset() - OpOffset - 1;
+ assert(ULEBsize <= 16);
+
+ // Copy over the operation.
+ assert(!Op.getSubCode() && "SubOps not yet supported");
+ OutputExpression.push_back(Op.getCode());
+ uint64_t RefOffset;
+ if (Desc.Op.size() == 1) {
+ RefOffset = Op.getRawOperand(0);
+ } else {
+ OutputExpression.push_back(Op.getRawOperand(0));
+ RefOffset = Op.getRawOperand(1);
+ }
+ uint8_t ULEB[16];
+ uint32_t Offset = 0;
+ unsigned RealSize = 0;
+ // Look up the base type. For DW_OP_convert, the operand may be 0 to
+ // instead indicate the generic type. The same holds for
+ // DW_OP_reinterpret, which is currently not supported.
+ if (RefOffset > 0 || Op.getCode() != dwarf::DW_OP_convert) {
+ RefOffset += OrigUnit.getOffset();
+ uint32_t RefDieIdx = 0;
+ if (std::optional<uint32_t> Idx =
+ OrigUnit.getDIEIndexForOffset(RefOffset))
+ RefDieIdx = *Idx;
+
+ // Use fixed size for ULEB128 data, since we need to update that size
+ // later with the proper offsets. Use 5 for DWARF32, 9 for DWARF64.
+ ULEBsize = getFormParams().getDwarfOffsetByteSize() + 1;
+
+ RealSize = encodeULEB128(0xBADDEF, ULEB, ULEBsize);
+
+ Section.notePatchWithOffsetUpdate(
+ DebugULEB128DieRefPatch(OutputExpression.size(), this, this,
+ RefDieIdx),
+ PatchesOffsets);
+ } else
+ RealSize = encodeULEB128(Offset, ULEB, ULEBsize);
+
+ if (RealSize > ULEBsize) {
+ // Emit the generic type as a fallback.
+ RealSize = encodeULEB128(0, ULEB, ULEBsize);
+ warn("base type ref doesn't fit.");
+ }
+ assert(RealSize == ULEBsize && "padding failed");
+ ArrayRef<uint8_t> ULEBbytes(ULEB, ULEBsize);
+ OutputExpression.append(ULEBbytes.begin(), ULEBbytes.end());
+ } else if (!getGlobalData().getOptions().UpdateIndexTablesOnly &&
+ Op.getCode() == dwarf::DW_OP_addrx) {
+ if (std::optional<object::SectionedAddress> SA =
+ OrigUnit.getAddrOffsetSectionItem(Op.getRawOperand(0))) {
+ // DWARFLinker does not use addrx forms since it generates relocated
+ // addresses. Replace DW_OP_addrx with DW_OP_addr here.
+ // Argument of DW_OP_addrx should be relocated here as it is not
+ // processed by applyValidRelocs.
+ OutputExpression.push_back(dwarf::DW_OP_addr);
+ uint64_t LinkedAddress =
+ SA->Address + (VarAddressAdjustment ? *VarAddressAdjustment : 0);
+ if ((getEndianness() == support::endianness::little) !=
+ sys::IsLittleEndianHost)
+ sys::swapByteOrder(LinkedAddress);
+ ArrayRef<uint8_t> AddressBytes(
+ reinterpret_cast<const uint8_t *>(&LinkedAddress),
+ OrigAddressByteSize);
+ OutputExpression.append(AddressBytes.begin(), AddressBytes.end());
+ } else
+ warn("cann't read DW_OP_addrx operand.");
+ } else if (!getGlobalData().getOptions().UpdateIndexTablesOnly &&
+ Op.getCode() == dwarf::DW_OP_constx) {
+ if (std::optional<object::SectionedAddress> SA =
+ OrigUnit.getAddrOffsetSectionItem(Op.getRawOperand(0))) {
+ // DWARFLinker does not use constx forms since it generates relocated
+ // addresses. Replace DW_OP_constx with DW_OP_const[*]u here.
+ // Argument of DW_OP_constx should be relocated here as it is not
+ // processed by applyValidRelocs.
+ std::optional<uint8_t> OutOperandKind;
+ switch (OrigAddressByteSize) {
+ case 2:
+ OutOperandKind = dwarf::DW_OP_const2u;
+ break;
+ case 4:
+ OutOperandKind = dwarf::DW_OP_const4u;
+ break;
+ case 8:
+ OutOperandKind = dwarf::DW_OP_const8u;
+ break;
+ default:
+ warn(
+ formatv(("unsupported address size: {0}."), OrigAddressByteSize));
+ break;
+ }
+
+ if (OutOperandKind) {
+ OutputExpression.push_back(*OutOperandKind);
+ uint64_t LinkedAddress =
+ SA->Address + (VarAddressAdjustment ? *VarAddressAdjustment : 0);
+ if ((getEndianness() == support::endianness::little) !=
+ sys::IsLittleEndianHost)
+ sys::swapByteOrder(LinkedAddress);
+ ArrayRef<uint8_t> AddressBytes(
+ reinterpret_cast<const uint8_t *>(&LinkedAddress),
+ OrigAddressByteSize);
+ OutputExpression.append(AddressBytes.begin(), AddressBytes.end());
+ }
+ } else
+ warn("cann't read DW_OP_constx operand.");
+ } else {
+ // Copy over everything else unmodified.
+ StringRef Bytes =
+ InputExpression.getData().slice(OpOffset, Op.getEndOffset());
+ OutputExpression.append(Bytes.begin(), Bytes.end());
+ }
+ OpOffset = Op.getEndOffset();
+ }
+}
+
+Error CompileUnit::cloneAndEmit(std::optional<Triple> TargetTriple) {
+ BumpPtrAllocator Allocator;
+
+ DWARFDie OrigUnitDIE = getOrigUnit().getUnitDIE();
+ if (!OrigUnitDIE.isValid())
+ return Error::success();
+
+ CanStripTemplateName =
+ llvm::is_contained(getGlobalData().getOptions().AccelTables,
+ DWARFLinker::AccelTableKind::Apple);
+
+ // Clone input DIE entry recursively.
+ DIE *OutCUDie =
+ cloneDIE(OrigUnitDIE.getDebugInfoEntry(), getDebugInfoHeaderSize(),
+ std::nullopt, std::nullopt, Allocator);
+ setOutUnitDIE(OutCUDie);
+
+ if (getGlobalData().getOptions().NoOutput || (OutCUDie == nullptr))
+ return Error::success();
+
+ assert(TargetTriple.has_value());
+ if (Error Err = cloneAndEmitLineTable(*TargetTriple))
+ return Err;
+
+ if (Error Err = cloneAndEmitDebugMacro())
+ return Err;
+
+ if (Error Err = emitDebugInfo(*TargetTriple))
+ return Err;
+
+ // ASSUMPTION: .debug_info section should already be emitted at this point.
+ // cloneAndEmitRanges & cloneAndEmitDebugLocations use .debug_info section
+ // data.
+
+ if (Error Err = cloneAndEmitRanges())
+ return Err;
+
+ if (Error Err = cloneAndEmitDebugLocations())
+ return Err;
+
+ if (Error Err = emitDebugAddrSection())
+ return Err;
+
+ if (Error Err = emitDebugStringOffsetSection())
+ return Err;
+
+ return emitAbbreviations();
+}
+
+bool needToClone(CompileUnit::DIEInfo &Info) {
+ return Info.getKeep() || Info.getKeepChildren();
+}
+
+DIE *CompileUnit::cloneDIE(const DWARFDebugInfoEntry *InputDieEntry,
+ uint64_t OutOffset,
+ std::optional<int64_t> FuncAddressAdjustment,
+ std::optional<int64_t> VarAddressAdjustment,
+ BumpPtrAllocator &Allocator) {
+ uint32_t InputDieIdx = getDIEIndex(InputDieEntry);
+ CompileUnit::DIEInfo &Info = getDIEInfo(InputDieIdx);
+
+ if (!needToClone(Info))
+ return nullptr;
+
+ bool HasLocationExpressionAddress = false;
+ if (InputDieEntry->getTag() == dwarf::DW_TAG_subprogram) {
+ // Get relocation adjustment value for the current function.
+ FuncAddressAdjustment =
+ getContaingFile().Addresses->getSubprogramRelocAdjustment(
+ getDIE(InputDieEntry));
+ } else if (InputDieEntry->getTag() == dwarf::DW_TAG_variable) {
+ // Get relocation adjustment value for the current variable.
+ std::pair<bool, std::optional<int64_t>> LocExprAddrAndRelocAdjustment =
+ getContaingFile().Addresses->getVariableRelocAdjustment(
+ getDIE(InputDieEntry));
+
+ HasLocationExpressionAddress = LocExprAddrAndRelocAdjustment.first;
+ if (LocExprAddrAndRelocAdjustment.first &&
+ LocExprAddrAndRelocAdjustment.second)
+ VarAddressAdjustment = *LocExprAddrAndRelocAdjustment.second;
+ }
+
+ DIEGenerator DIEGenerator(Allocator, *this);
+ DIE *ClonedDIE = DIEGenerator.createDIE(InputDieEntry->getTag(), OutOffset);
+ rememberDieOutOffset(InputDieIdx, OutOffset);
+
+ // Clone Attributes.
+ DIEAttributeCloner AttributesCloner(
+ ClonedDIE, *this, InputDieEntry, DIEGenerator, FuncAddressAdjustment,
+ VarAddressAdjustment, HasLocationExpressionAddress);
+ AttributesCloner.clone();
+
+ bool HasChildrenToClone = Info.getKeepChildren();
+ OutOffset = AttributesCloner.finalizeAbbreviations(HasChildrenToClone);
+
+ if (HasChildrenToClone) {
+ // Recursively clone children.
+ for (const DWARFDebugInfoEntry *CurChild =
+ getFirstChildEntry(InputDieEntry);
+ CurChild && CurChild->getAbbreviationDeclarationPtr();
+ CurChild = getSiblingEntry(CurChild)) {
+ if (DIE *ClonedChild =
+ cloneDIE(CurChild, OutOffset, FuncAddressAdjustment,
+ VarAddressAdjustment, Allocator)) {
+ OutOffset = ClonedChild->getOffset() + ClonedChild->getSize();
+ DIEGenerator.addChild(ClonedChild);
+ }
+ }
+
+ // Account for the end of children marker.
+ OutOffset += sizeof(int8_t);
+ }
+
+ // Update our size.
+ ClonedDIE->setSize(OutOffset - ClonedDIE->getOffset());
+ return ClonedDIE;
+}
+
+Error CompileUnit::cloneAndEmitLineTable(Triple &TargetTriple) {
+ const DWARFDebugLine::LineTable *InputLineTable =
+ getContaingFile().Dwarf->getLineTableForUnit(&getOrigUnit());
+ if (InputLineTable == nullptr) {
+ warn("cann't load line table.");
+ return Error::success();
+ }
+
+ DWARFDebugLine::LineTable OutLineTable;
+
+ // Set Line Table header.
+ OutLineTable.Prologue = InputLineTable->Prologue;
+ OutLineTable.Prologue.FormParams.AddrSize = getFormParams().AddrSize;
+
+ // Set Line Table Rows.
+ if (getGlobalData().getOptions().UpdateIndexTablesOnly) {
+ OutLineTable.Rows = InputLineTable->Rows;
+ // If all the line table contains is a DW_LNE_end_sequence, clear the line
+ // table rows, it will be inserted again in the DWARFStreamer.
+ if (OutLineTable.Rows.size() == 1 && OutLineTable.Rows[0].EndSequence)
+ OutLineTable.Rows.clear();
+
+ OutLineTable.Sequences = InputLineTable->Sequences;
+ } else {
+ // This vector is the output line table.
+ std::vector<DWARFDebugLine::Row> NewRows;
+ NewRows.reserve(InputLineTable->Rows.size());
+
+ // Current sequence of rows being extracted, before being inserted
+ // in NewRows.
+ std::vector<DWARFDebugLine::Row> Seq;
+
+ const auto &FunctionRanges = getFunctionRanges();
+ std::optional<AddressRangeValuePair> CurrRange;
+
+ // FIXME: This logic is meant to generate exactly the same output as
+ // Darwin's classic dsymutil. There is a nicer way to implement this
+ // by simply putting all the relocated line info in NewRows and simply
+ // sorting NewRows before passing it to emitLineTableForUnit. This
+ // should be correct as sequences for a function should stay
+ // together in the sorted output. There are a few corner cases that
+ // look suspicious though, and that required to implement the logic
+ // this way. Revisit that once initial validation is finished.
+
+ // Iterate over the object file line info and extract the sequences
+ // that correspond to linked functions.
+ for (DWARFDebugLine::Row Row : InputLineTable->Rows) {
+ // Check whether we stepped out of the range. The range is
+ // half-open, but consider accept the end address of the range if
+ // it is marked as end_sequence in the input (because in that
+ // case, the relocation offset is accurate and that entry won't
+ // serve as the start of another function).
+ if (!CurrRange || !CurrRange->Range.contains(Row.Address.Address)) {
+ // We just stepped out of a known range. Insert a end_sequence
+ // corresponding to the end of the range.
+ uint64_t StopAddress =
+ CurrRange ? CurrRange->Range.end() + CurrRange->Value : -1ULL;
+ CurrRange = FunctionRanges.getRangeThatContains(Row.Address.Address);
+ if (StopAddress != -1ULL && !Seq.empty()) {
+ // Insert end sequence row with the computed end address, but
+ // the same line as the previous one.
+ auto NextLine = Seq.back();
+ NextLine.Address.Address = StopAddress;
+ NextLine.EndSequence = 1;
+ NextLine.PrologueEnd = 0;
+ NextLine.BasicBlock = 0;
+ NextLine.EpilogueBegin = 0;
+ Seq.push_back(NextLine);
+ insertLineSequence(Seq, NewRows);
+ }
+
+ if (!CurrRange)
+ continue;
+ }
+
+ // Ignore empty sequences.
+ if (Row.EndSequence && Seq.empty())
+ continue;
+
+ // Relocate row address and add it to the current sequence.
+ Row.Address.Address += CurrRange->Value;
+ Seq.emplace_back(Row);
+
+ if (Row.EndSequence)
+ insertLineSequence(Seq, NewRows);
+ }
+
+ OutLineTable.Rows = std::move(NewRows);
+ }
+
+ return emitDebugLine(TargetTriple, OutLineTable);
+}
+
+void CompileUnit::insertLineSequence(std::vector<DWARFDebugLine::Row> &Seq,
+ std::vector<DWARFDebugLine::Row> &Rows) {
+ if (Seq.empty())
+ return;
+
+ if (!Rows.empty() && Rows.back().Address < Seq.front().Address) {
+ llvm::append_range(Rows, Seq);
+ Seq.clear();
+ return;
+ }
+
+ object::SectionedAddress Front = Seq.front().Address;
+ auto InsertPoint = partition_point(
+ Rows, [=](const DWARFDebugLine::Row &O) { return O.Address < Front; });
+
+ // FIXME: this only removes the unneeded end_sequence if the
+ // sequences have been inserted in order. Using a global sort like
+ // described in cloneAndEmitLineTable() and delaying the end_sequene
+ // elimination to DebugLineEmitter::emit() we can get rid of all of them.
+ if (InsertPoint != Rows.end() && InsertPoint->Address == Front &&
+ InsertPoint->EndSequence) {
+ *InsertPoint = Seq.front();
+ Rows.insert(InsertPoint + 1, Seq.begin() + 1, Seq.end());
+ } else {
+ Rows.insert(InsertPoint, Seq.begin(), Seq.end());
+ }
+
+ Seq.clear();
+}
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+LLVM_DUMP_METHOD void CompileUnit::DIEInfo::dump() {
+ llvm::errs() << "{\n";
+ llvm::errs() << " Placement: ";
+ switch (getPlacement()) {
+ case NotSet:
+ llvm::errs() << "NotSet\n";
+ break;
+ case TypeTable:
+ llvm::errs() << "TypeTable\n";
+ break;
+ case PlainDwarf:
+ llvm::errs() << "PlainDwarf\n";
+ break;
+ case Both:
+ llvm::errs() << "Both\n";
+ break;
+ case Parent:
+ llvm::errs() << "Parent\n";
+ break;
+ }
+
+ llvm::errs() << " Keep: " << getKeep();
+ llvm::errs() << " KeepChildren: " << getKeepChildren();
+ llvm::errs() << " ReferrencedBy: " << getReferrencedBy();
+ llvm::errs() << " IsInMouduleScope: " << getIsInMouduleScope();
+ llvm::errs() << " IsInFunctionScope: " << getIsInFunctionScope();
+ llvm::errs() << "}\n";
+}
+#endif // if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
diff --git a/llvm/lib/DWARFLinkerParallel/DWARFLinkerCompileUnit.h b/llvm/lib/DWARFLinkerParallel/DWARFLinkerCompileUnit.h
index 1617a848512d5f..24cbcf4d6f1e2d 100644
--- a/llvm/lib/DWARFLinkerParallel/DWARFLinkerCompileUnit.h
+++ b/llvm/lib/DWARFLinkerParallel/DWARFLinkerCompileUnit.h
@@ -10,54 +10,334 @@
#define LLVM_LIB_DWARFLINKERPARALLEL_DWARFLINKERCOMPILEUNIT_H
#include "DWARFLinkerUnit.h"
+#include "IndexedValuesMap.h"
#include "llvm/DWARFLinkerParallel/DWARFFile.h"
-#include "llvm/DWARFLinkerParallel/DWARFLinker.h"
#include <optional>
namespace llvm {
namespace dwarflinker_parallel {
-struct LinkContext;
-class DWARFFile;
+using OffsetToUnitTy = function_ref<CompileUnit *(uint64_t Offset)>;
/// Stores all information related to a compile unit, be it in its original
/// instance of the object file or its brand new cloned and generated DIE tree.
class CompileUnit : public DwarfUnit {
public:
- CompileUnit(LinkContext &, unsigned ID, StringRef ClangModuleName,
- DWARFFile &File,
- DWARFLinker::SwiftInterfacesMapTy *,
- UnitMessageHandlerTy WarningHandler)
- : DwarfUnit(ID, ClangModuleName, WarningHandler), ContaingFile(File) {
- FormParams.Version = 4;
- FormParams.Format = dwarf::DWARF32;
- FormParams.AddrSize = 4;
- UnitName = ContaingFile.FileName;
- }
-
- CompileUnit(LinkContext &, DWARFUnit &OrigUnit, unsigned ID,
+ /// The stages of new compile unit processing.
+ enum class Stage : uint8_t {
+ /// Created, linked with input DWARF file.
+ CreatedNotLoaded = 0,
+
+ /// Input DWARF is loaded.
+ Loaded,
+
+ /// Input DWARF is analysed(DIEs pointing to the real code section are
+ /// discovered, type names are assigned if ODR is requested).
+ LivenessAnalysisDone,
+
+ /// Output DWARF is generated.
+ Cloned,
+
+ /// Offsets inside patch records are updated.
+ PatchesUpdated,
+
+ /// Resources(Input DWARF, Output DWARF tree) are released.
+ Cleaned,
+ };
+
+ CompileUnit(LinkingGlobalData &GlobalData, unsigned ID,
+ StringRef ClangModuleName, DWARFFile &File,
+ OffsetToUnitTy UnitFromOffset)
+ : DwarfUnit(GlobalData, ID, ClangModuleName), File(File),
+ getUnitFromOffset(UnitFromOffset), Stage(Stage::CreatedNotLoaded) {
+ UnitName = File.FileName;
+ }
+
+ CompileUnit(LinkingGlobalData &GlobalData, DWARFUnit &OrigUnit, unsigned ID,
StringRef ClangModuleName, DWARFFile &File,
- UnitMessageHandlerTy WarningHandler)
- : DwarfUnit(ID, ClangModuleName, WarningHandler),
- ContaingFile(File), OrigUnit(&OrigUnit) {
+ OffsetToUnitTy UnitFromOffset)
+ : DwarfUnit(GlobalData, ID, ClangModuleName), File(File),
+ OrigUnit(&OrigUnit), getUnitFromOffset(UnitFromOffset),
+ Stage(Stage::CreatedNotLoaded) {
DWARFDie CUDie = OrigUnit.getUnitDIE();
if (!CUDie)
return;
- if (File.Dwarf)
- Endianess = File.Dwarf->isLittleEndian() ? support::endianness::little
- : support::endianness::big;
-
- FormParams.Version = OrigUnit.getVersion();
- FormParams.Format = dwarf::DWARF32;
- FormParams.AddrSize = OrigUnit.getAddressByteSize();
+ setOutputFormat(OrigUnit);
Language = dwarf::toUnsigned(CUDie.find(dwarf::DW_AT_language), 0);
-
- UnitName = ContaingFile.FileName;
+ if (const char *CUName = CUDie.getName(DINameKind::ShortName))
+ UnitName = CUName;
+ else
+ UnitName = File.FileName;
SysRoot = dwarf::toStringRef(CUDie.find(dwarf::DW_AT_LLVM_sysroot)).str();
}
+ /// Returns stage of overall processing.
+ Stage getStage() const { return Stage; }
+
+ /// Set stage of overall processing.
+ void setStage(Stage Stage) { this->Stage = Stage; }
+
+ /// Loads unit line table.
+ void loadLineTable();
+
+ /// Returns name of the file for the \p FileIdx
+ /// from the unit`s line table.
+ StringEntry *getFileName(unsigned FileIdx, StringPool &GlobalStrings);
+
+ /// Returns DWARFFile containing this compile unit.
+ const DWARFFile &getContaingFile() const { return File; }
+
+ /// Load DIEs of input compilation unit. \returns true if input DIEs
+ /// successfully loaded.
+ bool loadInputDIEs();
+
+ /// Reset compile units data(results of liveness analysis, clonning)
+ /// if current stage greater than Stage::Loaded. We need to reset data
+ /// as we are going to repeat stages.
+ void maybeResetToLoadedStage();
+
+ /// Collect references to parseable Swift interfaces in imported
+ /// DW_TAG_module blocks.
+ void analyzeImportedModule(const DWARFDebugInfoEntry *DieEntry);
+
+ /// Navigate DWARF tree and set die properties.
+ void analyzeDWARFStructure() {
+ analyzeDWARFStructureRec(getUnitDIE().getDebugInfoEntry(), false, false);
+ }
+
+ /// Cleanup unneeded resources after compile unit is cloned.
+ void cleanupDataAfterClonning();
+
+ /// After cloning stage the output DIEs offsets are deallocated.
+ /// This method copies output offsets for referenced DIEs into DIEs patches.
+ void updateDieRefPatchesWithClonedOffsets();
+
+ /// Kinds of placement for the output die.
+ enum DieOutputPlacement : uint8_t {
+ NotSet = 0,
+
+ /// Corresponding DIE goes to the type table only.
+ /// NOTE: Not used yet.
+ TypeTable = 1,
+
+ /// Corresponding DIE goes to the plain dwarf only.
+ PlainDwarf = 2,
+
+ /// Corresponding DIE goes to type table and to plain dwarf.
+ /// NOTE: Not used yet.
+ Both = 3,
+
+ /// Corresponding DIE needs to examine parent to determine
+ /// the point of placement.
+ /// NOTE: Not used yet.
+ Parent = 4
+ };
+
+ /// Information gathered about source DIEs.
+ struct DIEInfo {
+ DIEInfo() = default;
+ DIEInfo(const DIEInfo &Other) { Flags = Other.Flags.load(); }
+ DIEInfo &operator=(const DIEInfo &Other) {
+ Flags = Other.Flags.load();
+ return *this;
+ }
+
+ /// Data member keeping various flags.
+ std::atomic<uint16_t> Flags = {0};
+
+ /// \returns Placement kind for the corresponding die.
+ DieOutputPlacement getPlacement() const {
+ return DieOutputPlacement(Flags & 0x7);
+ }
+
+ /// Sets Placement kind for the corresponding die.
+ void setPlacement(DieOutputPlacement Placement) {
+ auto InputData = Flags.load();
+ while (!Flags.compare_exchange_weak(InputData,
+ ((InputData & ~0x7) | Placement))) {
+ }
+ }
+
+ /// Unsets Placement kind for the corresponding die.
+ void unsetPlacement() {
+ auto InputData = Flags.load();
+ while (!Flags.compare_exchange_weak(InputData, (InputData & ~0x7))) {
+ }
+ }
+
+ /// Sets Placement kind for the corresponding die.
+ bool setPlacementIfUnset(DieOutputPlacement Placement) {
+ auto InputData = Flags.load();
+ if ((InputData & 0x7) == NotSet)
+ if (Flags.compare_exchange_weak(InputData, (InputData | Placement)))
+ return true;
+
+ return false;
+ }
+
+#define SINGLE_FLAG_METHODS_SET(Name, Value) \
+ bool get##Name() const { return Flags & Value; } \
+ void set##Name() { \
+ auto InputData = Flags.load(); \
+ while (!Flags.compare_exchange_weak(InputData, InputData | Value)) { \
+ } \
+ } \
+ void unset##Name() { \
+ auto InputData = Flags.load(); \
+ while (!Flags.compare_exchange_weak(InputData, InputData & ~Value)) { \
+ } \
+ }
+
+ /// DIE is a part of the linked output.
+ SINGLE_FLAG_METHODS_SET(Keep, 0x08)
+
+ /// DIE has children which are part of the linked output.
+ SINGLE_FLAG_METHODS_SET(KeepChildren, 0x10)
+
+ /// DIE is referenced by other DIE.
+ SINGLE_FLAG_METHODS_SET(ReferrencedBy, 0x20)
+
+ /// DIE is in module scope.
+ SINGLE_FLAG_METHODS_SET(IsInMouduleScope, 0x40)
+
+ /// DIE is in function scope.
+ SINGLE_FLAG_METHODS_SET(IsInFunctionScope, 0x80)
+
+ void unsetFlagsWhichSetDuringLiveAnalysis() {
+ auto InputData = Flags.load();
+ while (!Flags.compare_exchange_weak(
+ InputData, InputData & ~(0x7 | 0x8 | 0x10 | 0x20))) {
+ }
+ }
+
+ /// Erase all flags.
+ void eraseData() { Flags = 0; }
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+ LLVM_DUMP_METHOD void dump();
+#endif
+ };
+
+ /// \defgroup Group of functions returning DIE info.
+ ///
+ /// @{
+
+ /// \p Idx index of the DIE.
+ /// \returns DieInfo descriptor.
+ DIEInfo &getDIEInfo(unsigned Idx) { return DieInfoArray[Idx]; }
+
+ /// \p Idx index of the DIE.
+ /// \returns DieInfo descriptor.
+ const DIEInfo &getDIEInfo(unsigned Idx) const { return DieInfoArray[Idx]; }
+
+ /// \p Idx index of the DIE.
+ /// \returns DieInfo descriptor.
+ DIEInfo &getDIEInfo(const DWARFDebugInfoEntry *Entry) {
+ return DieInfoArray[getOrigUnit().getDIEIndex(Entry)];
+ }
+
+ /// \p Idx index of the DIE.
+ /// \returns DieInfo descriptor.
+ const DIEInfo &getDIEInfo(const DWARFDebugInfoEntry *Entry) const {
+ return DieInfoArray[getOrigUnit().getDIEIndex(Entry)];
+ }
+
+ /// \p Die
+ /// \returns PlainDieInfo descriptor.
+ DIEInfo &getDIEInfo(const DWARFDie &Die) {
+ return DieInfoArray[getOrigUnit().getDIEIndex(Die)];
+ }
+
+ /// \p Die
+ /// \returns PlainDieInfo descriptor.
+ const DIEInfo &getDIEInfo(const DWARFDie &Die) const {
+ return DieInfoArray[getOrigUnit().getDIEIndex(Die)];
+ }
+
+ /// \p Idx index of the DIE.
+ /// \returns DieInfo descriptor.
+ uint64_t getDieOutOffset(uint32_t Idx) {
+ return reinterpret_cast<std::atomic<uint64_t> *>(&OutDieOffsetArray[Idx])
+ ->load();
+ }
+
+ /// \p Idx index of the DIE.
+ /// \returns DieInfo descriptor.
+ void rememberDieOutOffset(uint32_t Idx, uint64_t Offset) {
+ reinterpret_cast<std::atomic<uint64_t> *>(&OutDieOffsetArray[Idx])
+ ->store(Offset);
+ }
+
+ /// @}
+
+ /// Returns value of DW_AT_low_pc attribute.
+ std::optional<uint64_t> getLowPc() const { return LowPc; }
+
+ /// Returns value of DW_AT_high_pc attribute.
+ uint64_t getHighPc() const { return HighPc; }
+
+ /// Returns true if there is a label corresponding to the specified \p Addr.
+ bool hasLabelAt(uint64_t Addr) const { return Labels.count(Addr); }
+
+ /// Add the low_pc of a label that is relocated by applying
+ /// offset \p PCOffset.
+ void addLabelLowPc(uint64_t LabelLowPc, int64_t PcOffset);
+
+ /// Resolve the DIE attribute reference that has been extracted in \p
+ /// RefValue. The resulting DIE might be in another CompileUnit.
+ /// \returns referenced die and corresponding compilation unit.
+ /// compilation unit is null if reference could not be resolved.
+ std::optional<std::pair<CompileUnit *, uint32_t>>
+ resolveDIEReference(const DWARFFormValue &RefValue);
+ /// @}
+
+ /// Add a function range [\p LowPC, \p HighPC) that is relocated by applying
+ /// offset \p PCOffset.
+ void addFunctionRange(uint64_t LowPC, uint64_t HighPC, int64_t PCOffset);
+
+ /// Returns function ranges of this unit.
+ const RangesTy &getFunctionRanges() const { return Ranges; }
+
+ /// Clone and emit this compilation unit.
+ Error cloneAndEmit(std::optional<Triple> TargetTriple);
+
+ /// Clone and emit debug locations(.debug_loc/.debug_loclists).
+ Error cloneAndEmitDebugLocations();
+
+ /// Clone and emit ranges.
+ Error cloneAndEmitRanges();
+
+ /// Clone and emit debug macros(.debug_macinfo/.debug_macro).
+ Error cloneAndEmitDebugMacro();
+
+ // Clone input DIE entry.
+ DIE *cloneDIE(const DWARFDebugInfoEntry *InputDieEntry, uint64_t OutOffset,
+ std::optional<int64_t> FuncAddressAdjustment,
+ std::optional<int64_t> VarAddressAdjustment,
+ BumpPtrAllocator &Allocator);
+
+ // Clone and emit line table.
+ Error cloneAndEmitLineTable(Triple &TargetTriple);
+
+ /// Clone attribute location axpression.
+ void cloneDieAttrExpression(const DWARFExpression &InputExpression,
+ SmallVectorImpl<uint8_t> &OutputExpression,
+ SectionDescriptor &Section,
+ std::optional<int64_t> VarAddressAdjustment,
+ OffsetsPtrVector &PatchesOffsets);
+
+ /// Returns index(inside .debug_addr) of an address.
+ uint64_t getDebugAddrIndex(uint64_t Addr) {
+ return DebugAddrIndexMap.getValueIndex(Addr);
+ }
+
+ /// Returns index(inside .debug_str_offsets) of specified string.
+ uint64_t getDebugStrIndex(const StringEntry *String) {
+ return DebugStringIndexMap.getValueIndex(String);
+ }
+
/// \defgroup Helper methods to access OrigUnit.
///
/// @{
@@ -142,12 +422,144 @@ class CompileUnit : public DwarfUnit {
/// @}
+ /// \defgroup Methods used for reporting warnings and errors:
+ ///
+ /// @{
+
+ void warn(const Twine &Warning, const DWARFDie *DIE = nullptr) {
+ GlobalData.warn(Warning, getUnitName(), DIE);
+ }
+
+ void warn(Error Warning, const DWARFDie *DIE = nullptr) {
+ handleAllErrors(std::move(Warning), [&](ErrorInfoBase &Info) {
+ GlobalData.warn(Info.message(), getUnitName(), DIE);
+ });
+ }
+
+ void warn(const Twine &Warning, const DWARFDebugInfoEntry *DieEntry) {
+ if (DieEntry != nullptr) {
+ DWARFDie DIE(&getOrigUnit(), DieEntry);
+ GlobalData.warn(Warning, getUnitName(), &DIE);
+ return;
+ }
+
+ GlobalData.warn(Warning, getUnitName());
+ }
+
+ void error(const Twine &Err, const DWARFDie *DIE = nullptr) {
+ GlobalData.warn(Err, getUnitName(), DIE);
+ }
+
+ void error(Error Err, const DWARFDie *DIE = nullptr) {
+ handleAllErrors(std::move(Err), [&](ErrorInfoBase &Info) {
+ GlobalData.error(Info.message(), getUnitName(), DIE);
+ });
+ }
+
+ /// @}
+
private:
+ /// Navigate DWARF tree recursively and set die properties.
+ void analyzeDWARFStructureRec(const DWARFDebugInfoEntry *DieEntry,
+ bool IsInModule, bool IsInFunction);
+
+ struct LinkedLocationExpressionsWithOffsetPatches {
+ DWARFLocationExpression Expression;
+ OffsetsPtrVector Patches;
+ };
+ using LinkedLocationExpressionsVector =
+ SmallVector<LinkedLocationExpressionsWithOffsetPatches>;
+
+ /// Emit debug locations.
+ void emitLocations(DebugSectionKind LocationSectionKind);
+
+ /// Emit location list header.
+ uint64_t emitLocListHeader(SectionDescriptor &OutLocationSection);
+
+ /// Emit location list fragment.
+ uint64_t emitLocListFragment(
+ const LinkedLocationExpressionsVector &LinkedLocationExpression,
+ SectionDescriptor &OutLocationSection);
+
+ /// Emit the .debug_addr section fragment for current unit.
+ Error emitDebugAddrSection();
+
+ /// Emit the .debug_str_offsets section for current unit.
+ Error emitDebugStringOffsetSection();
+
+ /// Emit .debug_aranges.
+ void emitAranges(AddressRanges &LinkedFunctionRanges);
+
+ /// Clone and emit .debug_ranges/.debug_rnglists.
+ void cloneAndEmitRangeList(DebugSectionKind RngSectionKind,
+ AddressRanges &LinkedFunctionRanges);
+
+ /// Emit range list header.
+ uint64_t emitRangeListHeader(SectionDescriptor &OutRangeSection);
+
+ /// Emit range list fragment.
+ void emitRangeListFragment(const AddressRanges &LinkedRanges,
+ SectionDescriptor &OutRangeSection);
+
+ /// Insert the new line info sequence \p Seq into the current
+ /// set of already linked line info \p Rows.
+ void insertLineSequence(std::vector<DWARFDebugLine::Row> &Seq,
+ std::vector<DWARFDebugLine::Row> &Rows);
+
+ /// Emits body for both macro sections.
+ void emitMacroTableImpl(const DWARFDebugMacro *MacroTable,
+ uint64_t OffsetToMacroTable, bool hasDWARFv5Header);
+
/// DWARFFile containing this compile unit.
- DWARFFile &ContaingFile;
+ DWARFFile &File;
/// Pointer to the paired compile unit from the input DWARF.
DWARFUnit *OrigUnit = nullptr;
+
+ /// Line table for this unit.
+ const DWARFDebugLine::LineTable *LineTablePtr = nullptr;
+
+ /// Cached resolved paths from the line table.
+ /// The key is <UniqueUnitID, FileIdx>.
+ using ResolvedPathsMap = DenseMap<unsigned, StringEntry *>;
+ ResolvedPathsMap ResolvedFullPaths;
+ StringMap<StringEntry *> ResolvedParentPaths;
+
+ /// This field instructs compile unit to store DIE name with stripped
+ /// template parameters into the accelerator table.
+ bool CanStripTemplateName = false;
+
+ /// Maps an address into the index inside .debug_addr section.
+ IndexedValuesMap<uint64_t> DebugAddrIndexMap;
+
+ /// Maps a string into the index inside .debug_str_offsets section.
+ IndexedValuesMap<const StringEntry *> DebugStringIndexMap;
+
+ /// \defgroup Data Members accessed asinchroniously.
+ ///
+ /// @{
+ OffsetToUnitTy getUnitFromOffset;
+
+ std::optional<uint64_t> LowPc;
+ uint64_t HighPc = 0;
+
+ /// The ranges in that map are the PC ranges for functions in this unit,
+ /// associated with the PC offset to apply to the addresses to get
+ /// the linked address.
+ RangesTy Ranges;
+ std::mutex RangesMutex;
+
+ /// The DW_AT_low_pc of each DW_TAG_label.
+ SmallDenseMap<uint64_t, uint64_t, 1> Labels;
+ std::mutex LabelsMutex;
+
+ /// This field keeps current stage of overall compile unit processing.
+ std::atomic<Stage> Stage;
+
+ /// DIE info indexed by DIE index.
+ SmallVector<DIEInfo> DieInfoArray;
+ SmallVector<uint64_t> OutDieOffsetArray;
+ /// @}
};
} // end of namespace dwarflinker_parallel
diff --git a/llvm/lib/DWARFLinkerParallel/DWARFLinkerGlobalData.h b/llvm/lib/DWARFLinkerParallel/DWARFLinkerGlobalData.h
new file mode 100644
index 00000000000000..78d0b5468d0495
--- /dev/null
+++ b/llvm/lib/DWARFLinkerParallel/DWARFLinkerGlobalData.h
@@ -0,0 +1,159 @@
+//===- DWARFLinkerGlobalData.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_DWARFLINKERGLOBALDATA_H
+#define LLVM_LIB_DWARFLINKERPARALLEL_DWARFLINKERGLOBALDATA_H
+
+#include "llvm/DWARFLinkerParallel/DWARFLinker.h"
+#include "llvm/DWARFLinkerParallel/StringPool.h"
+#include "llvm/Support/PerThreadBumpPtrAllocator.h"
+#include <map>
+
+namespace llvm {
+
+class DWARFDie;
+
+namespace dwarflinker_parallel {
+
+using TranslatorFuncTy = std::function<StringRef(StringRef)>;
+using MessageHandlerTy = std::function<void(
+ const Twine &Warning, StringRef Context, const DWARFDie *DIE)>;
+
+/// linking options
+struct DWARFLinkerOptions {
+ /// DWARF version for the output.
+ uint16_t TargetDWARFVersion = 0;
+
+ /// Generate processing log to the standard output.
+ bool Verbose = false;
+
+ /// Print statistics.
+ bool Statistics = false;
+
+ /// Verify the input DWARF.
+ bool VerifyInputDWARF = false;
+
+ /// Do not emit output.
+ bool NoOutput = false;
+
+ /// Do not unique types according to ODR
+ bool NoODR = false;
+
+ /// Update index tables.
+ bool UpdateIndexTablesOnly = false;
+
+ /// Whether we want a static variable to force us to keep its enclosing
+ /// function.
+ bool KeepFunctionForStatic = false;
+
+ /// Allow to generate valid, but non deterministic output.
+ bool AllowNonDeterministicOutput = false;
+
+ /// Number of threads.
+ unsigned Threads = 1;
+
+ /// The accelerator table kinds
+ SmallVector<DWARFLinker::AccelTableKind, 1> AccelTables;
+
+ /// Prepend path for the clang modules.
+ std::string PrependPath;
+
+ /// input verification handler(it might be called asynchronously).
+ DWARFLinker::InputVerificationHandlerTy InputVerificationHandler = nullptr;
+
+ /// A list of all .swiftinterface files referenced by the debug
+ /// info, mapping Module name to path on disk. The entries need to
+ /// be uniqued and sorted and there are only few entries expected
+ /// per compile unit, which is why this is a std::map.
+ /// this is dsymutil specific fag.
+ ///
+ /// (it might be called asynchronously).
+ DWARFLinker::SwiftInterfacesMapTy *ParseableSwiftInterfaces = nullptr;
+
+ /// A list of remappings to apply to file paths.
+ ///
+ /// (it might be called asynchronously).
+ DWARFLinker::ObjectPrefixMapTy *ObjectPrefixMap = nullptr;
+};
+
+class DWARFLinkerImpl;
+
+/// This class keeps data and services common for the whole linking process.
+class LinkingGlobalData {
+ friend DWARFLinkerImpl;
+
+public:
+ /// Returns global per-thread allocator.
+ parallel::PerThreadBumpPtrAllocator &getAllocator() { return Allocator; }
+
+ /// Returns global string pool.
+ StringPool &getStringPool() { return Strings; }
+
+ /// Set translation function.
+ void setTranslator(TranslatorFuncTy Translator) {
+ this->Translator = Translator;
+ }
+
+ /// Translate specified string.
+ StringRef translateString(StringRef String) {
+ if (Translator)
+ return Translator(String);
+
+ return String;
+ }
+
+ /// Returns linking options.
+ const DWARFLinkerOptions &getOptions() const { return Options; }
+
+ /// Set warning handler.
+ void setWarningHandler(MessageHandlerTy Handler) { WarningHandler = Handler; }
+
+ /// Set error handler.
+ void setErrorHandler(MessageHandlerTy Handler) { ErrorHandler = Handler; }
+
+ /// Report warning.
+ void warn(const Twine &Warning, StringRef Context,
+ const DWARFDie *DIE = nullptr) {
+ if (WarningHandler)
+ (WarningHandler)(Warning, Context, DIE);
+ }
+
+ /// Report warning.
+ void warn(Error Warning, StringRef Context, const DWARFDie *DIE = nullptr) {
+ handleAllErrors(std::move(Warning), [&](ErrorInfoBase &Info) {
+ warn(Info.message(), Context, DIE);
+ });
+ }
+
+ /// Report error.
+ void error(const Twine &Err, StringRef Context,
+ const DWARFDie *DIE = nullptr) {
+ if (ErrorHandler)
+ (ErrorHandler)(Err, Context, DIE);
+ }
+
+ /// Report error.
+ void error(Error Err, StringRef Context, const DWARFDie *DIE = nullptr) {
+ handleAllErrors(std::move(Err), [&](ErrorInfoBase &Info) {
+ error(Info.message(), Context, DIE);
+ });
+ }
+
+protected:
+ parallel::PerThreadBumpPtrAllocator Allocator;
+ StringPool Strings;
+ TranslatorFuncTy Translator;
+ DWARFLinkerOptions Options;
+ MessageHandlerTy WarningHandler;
+ MessageHandlerTy ErrorHandler;
+};
+
+} // end of namespace dwarflinker_parallel
+} // end namespace llvm
+
+#endif // LLVM_LIB_DWARFLINKERPARALLEL_DWARFLINKERGLOBALDATA_H
diff --git a/llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.cpp b/llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.cpp
index dfd77af92f2727..04d8024bcd7635 100644
--- a/llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.cpp
+++ b/llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.cpp
@@ -7,33 +7,20 @@
//===----------------------------------------------------------------------===//
#include "DWARFLinkerImpl.h"
+#include "DIEGenerator.h"
+#include "DependencyTracker.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/Parallel.h"
+#include "llvm/Support/ThreadPool.h"
namespace llvm {
namespace dwarflinker_parallel {
-/// Similar to DWARFUnitSection::getUnitForOffset(), but returning our
-/// CompileUnit object instead.
-CompileUnit *
-DWARFLinkerImpl::LinkContext::getUnitForOffset(CompileUnit &CurrentCU,
- uint64_t Offset) const {
- if (CurrentCU.isClangModule())
- return &CurrentCU;
-
- auto CU = llvm::upper_bound(
- CompileUnits, Offset,
- [](uint64_t LHS, const std::unique_ptr<CompileUnit> &RHS) {
- return LHS < RHS->getOrigUnit().getNextUnitOffset();
- });
-
- return CU != CompileUnits.end() ? CU->get() : nullptr;
-}
-
Error DWARFLinkerImpl::createEmitter(const Triple &TheTriple,
OutputFileType FileType,
raw_pwrite_stream &OutFile) {
- TheDwarfEmitter = std::make_unique<DwarfEmitterImpl>(
- FileType, OutFile, OutputStrings.getTranslator(), WarningHandler);
+ TheDwarfEmitter = std::make_unique<DwarfEmitterImpl>(FileType, OutFile);
return TheDwarfEmitter->init(TheTriple, "__DWARF");
}
@@ -42,5 +29,1058 @@ ExtraDwarfEmitter *DWARFLinkerImpl::getEmitter() {
return TheDwarfEmitter.get();
}
+void DWARFLinkerImpl::addObjectFile(DWARFFile &File, ObjFileLoaderTy Loader,
+ CompileUnitHandlerTy OnCUDieLoaded) {
+ ObjectContexts.emplace_back(std::make_unique<LinkContext>(
+ GlobalData, File, ClangModules, UniqueUnitID,
+ (TheDwarfEmitter.get() == nullptr ? std::optional<Triple>(std::nullopt)
+ : TheDwarfEmitter->getTargetTriple())));
+
+ if (ObjectContexts.back()->InputDWARFFile.Dwarf) {
+ for (const std::unique_ptr<DWARFUnit> &CU :
+ ObjectContexts.back()->InputDWARFFile.Dwarf->compile_units()) {
+ DWARFDie CUDie = CU->getUnitDIE();
+ OverallNumberOfCU++;
+
+ if (!CUDie)
+ continue;
+
+ OnCUDieLoaded(*CU);
+
+ // Register mofule reference.
+ if (!GlobalData.getOptions().UpdateIndexTablesOnly)
+ ObjectContexts.back()->registerModuleReference(CUDie, Loader,
+ OnCUDieLoaded);
+ }
+ }
+}
+
+Error DWARFLinkerImpl::link() {
+ // reset compile unit unique ID counter.
+ UniqueUnitID = 0;
+
+ if (Error Err = validateAndUpdateOptions())
+ return Err;
+
+ std::optional<dwarf::FormParams> Format;
+ std::optional<support::endianness> Endianess;
+
+ for (std::unique_ptr<LinkContext> &Context : ObjectContexts) {
+ if (Context->InputDWARFFile.Dwarf.get() == nullptr)
+ continue;
+
+ if (GlobalData.getOptions().Verbose) {
+ outs() << "OBJECT: " << Context->InputDWARFFile.FileName << "\n";
+
+ for (const std::unique_ptr<DWARFUnit> &OrigCU :
+ Context->InputDWARFFile.Dwarf->compile_units()) {
+ outs() << "Input compilation unit:";
+ DIDumpOptions DumpOpts;
+ DumpOpts.ChildRecurseDepth = 0;
+ DumpOpts.Verbose = GlobalData.getOptions().Verbose;
+ OrigCU->getUnitDIE().dump(outs(), 0, DumpOpts);
+ }
+ }
+
+ // Verify input DWARF if requested.
+ if (GlobalData.getOptions().VerifyInputDWARF)
+ verifyInput(Context->InputDWARFFile);
+
+ for (const std::unique_ptr<DWARFUnit> &OrigCU :
+ Context->InputDWARFFile.Dwarf->compile_units()) {
+ if (!Format)
+ Format = OrigCU.get()->getFormParams();
+ }
+
+ if (!Endianess)
+ Endianess = Context->InputDWARFFile.Dwarf->isLittleEndian()
+ ? support::endianness::little
+ : support::endianness::big;
+ }
+
+ if (!Format)
+ Format = {GlobalData.getOptions().TargetDWARFVersion, 8,
+ dwarf::DwarfFormat::DWARF32};
+ Format->Version = GlobalData.getOptions().TargetDWARFVersion;
+ if (!Endianess)
+ Endianess = support::endianness::little;
+
+ CommonSections.setOutputFormat(*Format, *Endianess);
+
+ // Set parallel options.
+ if (GlobalData.getOptions().Threads == 0)
+ parallel::strategy = optimal_concurrency(OverallNumberOfCU);
+ else
+ parallel::strategy = hardware_concurrency(GlobalData.getOptions().Threads);
+
+ // Link object files.
+ if (GlobalData.getOptions().Threads == 1) {
+ for (std::unique_ptr<LinkContext> &Context : ObjectContexts) {
+ // Link object file.
+ if (Error Err = Context->link())
+ GlobalData.error(std::move(Err), Context->InputDWARFFile.FileName);
+
+ Context->InputDWARFFile.unload();
+ }
+ } else {
+ ThreadPool Pool(parallel::strategy);
+ for (std::unique_ptr<LinkContext> &Context : ObjectContexts)
+ Pool.async([&]() {
+ // Link object file.
+ if (Error Err = Context->link())
+ GlobalData.error(std::move(Err), Context->InputDWARFFile.FileName);
+
+ Context->InputDWARFFile.unload();
+ });
+
+ Pool.wait();
+ }
+
+ // At this stage each compile units are cloned to their own set of debug
+ // sections. Now, update patches, assign offsets and assemble final file
+ // glueing debug tables from each compile unit.
+ glueCompileUnitsAndWriteToTheOutput();
+
+ return Error::success();
+}
+
+void DWARFLinkerImpl::verifyInput(const DWARFFile &File) {
+ assert(File.Dwarf);
+
+ raw_ostream &os = GlobalData.getOptions().Verbose ? errs() : nulls();
+ DIDumpOptions DumpOpts;
+ if (!File.Dwarf->verify(os, DumpOpts.noImplicitRecursion())) {
+ if (GlobalData.getOptions().InputVerificationHandler)
+ GlobalData.getOptions().InputVerificationHandler(File);
+ }
+}
+
+Error DWARFLinkerImpl::validateAndUpdateOptions() {
+ if (GlobalData.getOptions().TargetDWARFVersion == 0)
+ return createStringError(std::errc::invalid_argument,
+ "target DWARF version is not set");
+
+ GlobalData.Options.NoOutput = TheDwarfEmitter.get() == nullptr;
+
+ if (GlobalData.getOptions().Verbose && GlobalData.getOptions().Threads != 1) {
+ GlobalData.Options.Threads = 1;
+ GlobalData.warn(
+ "set number of threads to 1 to make --verbose to work properly.", "");
+ }
+
+ return Error::success();
+}
+
+/// Resolve the relative path to a build artifact referenced by DWARF by
+/// applying DW_AT_comp_dir.
+static void resolveRelativeObjectPath(SmallVectorImpl<char> &Buf, DWARFDie CU) {
+ sys::path::append(Buf, dwarf::toString(CU.find(dwarf::DW_AT_comp_dir), ""));
+}
+
+static uint64_t getDwoId(const DWARFDie &CUDie) {
+ auto DwoId = dwarf::toUnsigned(
+ CUDie.find({dwarf::DW_AT_dwo_id, dwarf::DW_AT_GNU_dwo_id}));
+ if (DwoId)
+ return *DwoId;
+ return 0;
+}
+
+static std::string
+remapPath(StringRef Path,
+ const DWARFLinker::ObjectPrefixMapTy &ObjectPrefixMap) {
+ if (ObjectPrefixMap.empty())
+ return Path.str();
+
+ SmallString<256> p = Path;
+ for (const auto &Entry : ObjectPrefixMap)
+ if (llvm::sys::path::replace_path_prefix(p, Entry.first, Entry.second))
+ break;
+ return p.str().str();
+}
+
+static std::string getPCMFile(const DWARFDie &CUDie,
+ DWARFLinker::ObjectPrefixMapTy *ObjectPrefixMap) {
+ std::string PCMFile = dwarf::toString(
+ CUDie.find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), "");
+
+ if (PCMFile.empty())
+ return PCMFile;
+
+ if (ObjectPrefixMap)
+ PCMFile = remapPath(PCMFile, *ObjectPrefixMap);
+
+ return PCMFile;
+}
+
+std::pair<bool, bool> DWARFLinkerImpl::LinkContext::isClangModuleRef(
+ const DWARFDie &CUDie, std::string &PCMFile, unsigned Indent, bool Quiet) {
+ if (PCMFile.empty())
+ return std::make_pair(false, false);
+
+ // Clang module DWARF skeleton CUs abuse this for the path to the module.
+ uint64_t DwoId = getDwoId(CUDie);
+
+ std::string Name = dwarf::toString(CUDie.find(dwarf::DW_AT_name), "");
+ if (Name.empty()) {
+ if (!Quiet)
+ GlobalData.warn("anonymous module skeleton CU for " + PCMFile + ".",
+ InputDWARFFile.FileName);
+ return std::make_pair(true, true);
+ }
+
+ if (!Quiet && GlobalData.getOptions().Verbose) {
+ outs().indent(Indent);
+ outs() << "Found clang module reference " << PCMFile;
+ }
+
+ auto Cached = ClangModules.find(PCMFile);
+ if (Cached != ClangModules.end()) {
+ // FIXME: Until PR27449 (https://llvm.org/bugs/show_bug.cgi?id=27449) is
+ // fixed in clang, only warn about DWO_id mismatches in verbose mode.
+ // ASTFileSignatures will change randomly when a module is rebuilt.
+ if (!Quiet && GlobalData.getOptions().Verbose && (Cached->second != DwoId))
+ GlobalData.warn(
+ Twine("hash mismatch: this object file was built against a "
+ "
diff erent version of the module ") +
+ PCMFile + ".",
+ InputDWARFFile.FileName);
+ if (!Quiet && GlobalData.getOptions().Verbose)
+ outs() << " [cached].\n";
+ return std::make_pair(true, true);
+ }
+
+ return std::make_pair(true, false);
+}
+
+/// If this compile unit is really a skeleton CU that points to a
+/// clang module, register it in ClangModules and return true.
+///
+/// A skeleton CU is a CU without children, a DW_AT_gnu_dwo_name
+/// pointing to the module, and a DW_AT_gnu_dwo_id with the module
+/// hash.
+bool DWARFLinkerImpl::LinkContext::registerModuleReference(
+ const DWARFDie &CUDie, ObjFileLoaderTy Loader,
+ CompileUnitHandlerTy OnCUDieLoaded, unsigned Indent) {
+ std::string PCMFile =
+ getPCMFile(CUDie, GlobalData.getOptions().ObjectPrefixMap);
+ std::pair<bool, bool> IsClangModuleRef =
+ isClangModuleRef(CUDie, PCMFile, Indent, false);
+
+ if (!IsClangModuleRef.first)
+ return false;
+
+ if (IsClangModuleRef.second)
+ return true;
+
+ if (GlobalData.getOptions().Verbose)
+ outs() << " ...\n";
+
+ // Cyclic dependencies are disallowed by Clang, but we still
+ // shouldn't run into an infinite loop, so mark it as processed now.
+ ClangModules.insert({PCMFile, getDwoId(CUDie)});
+
+ if (Error E =
+ loadClangModule(Loader, CUDie, PCMFile, OnCUDieLoaded, Indent + 2)) {
+ consumeError(std::move(E));
+ return false;
+ }
+ return true;
+}
+
+Error DWARFLinkerImpl::LinkContext::loadClangModule(
+ ObjFileLoaderTy Loader, const DWARFDie &CUDie, const std::string &PCMFile,
+ CompileUnitHandlerTy OnCUDieLoaded, unsigned Indent) {
+
+ uint64_t DwoId = getDwoId(CUDie);
+ std::string ModuleName = dwarf::toString(CUDie.find(dwarf::DW_AT_name), "");
+
+ /// Using a SmallString<0> because loadClangModule() is recursive.
+ SmallString<0> Path(GlobalData.getOptions().PrependPath);
+ if (sys::path::is_relative(PCMFile))
+ resolveRelativeObjectPath(Path, CUDie);
+ sys::path::append(Path, PCMFile);
+ // Don't use the cached binary holder because we have no thread-safety
+ // guarantee and the lifetime is limited.
+
+ if (Loader == nullptr) {
+ GlobalData.error("cann't load clang module: loader is not specified.",
+ InputDWARFFile.FileName);
+ return Error::success();
+ }
+
+ auto ErrOrObj = Loader(InputDWARFFile.FileName, Path);
+ if (!ErrOrObj)
+ return Error::success();
+
+ std::unique_ptr<CompileUnit> Unit;
+ for (const auto &CU : ErrOrObj->Dwarf->compile_units()) {
+ OnCUDieLoaded(*CU);
+ // Recursively get all modules imported by this one.
+ auto ChildCUDie = CU->getUnitDIE();
+ if (!ChildCUDie)
+ continue;
+ if (!registerModuleReference(ChildCUDie, Loader, OnCUDieLoaded, Indent)) {
+ if (Unit) {
+ std::string Err =
+ (PCMFile +
+ ": Clang modules are expected to have exactly 1 compile unit.\n");
+ GlobalData.error(Err, InputDWARFFile.FileName);
+ return make_error<StringError>(Err, inconvertibleErrorCode());
+ }
+ // FIXME: Until PR27449 (https://llvm.org/bugs/show_bug.cgi?id=27449) is
+ // fixed in clang, only warn about DWO_id mismatches in verbose mode.
+ // ASTFileSignatures will change randomly when a module is rebuilt.
+ uint64_t PCMDwoId = getDwoId(ChildCUDie);
+ if (PCMDwoId != DwoId) {
+ if (GlobalData.getOptions().Verbose)
+ GlobalData.warn(
+ Twine("hash mismatch: this object file was built against a "
+ "
diff erent version of the module ") +
+ PCMFile + ".",
+ InputDWARFFile.FileName);
+ // Update the cache entry with the DwoId of the module loaded from disk.
+ ClangModules[PCMFile] = PCMDwoId;
+ }
+
+ // Empty modules units should not be cloned.
+ if (!ChildCUDie.hasChildren())
+ continue;
+
+ // Add this module.
+ Unit = std::make_unique<CompileUnit>(
+ GlobalData, *CU, UniqueUnitID.fetch_add(1), ModuleName, *ErrOrObj,
+ getUnitForOffset);
+ }
+ }
+
+ if (Unit) {
+ ModulesCompileUnits.emplace_back(RefModuleUnit{*ErrOrObj, std::move(Unit)});
+ // Preload line table, as it can't be loaded asynchronously.
+ ModulesCompileUnits.back().Unit->loadLineTable();
+ }
+
+ return Error::success();
+}
+
+Error DWARFLinkerImpl::LinkContext::link() {
+ InterCUProcessingStarted = false;
+ if (InputDWARFFile.Warnings.empty()) {
+ if (!InputDWARFFile.Dwarf)
+ return Error::success();
+
+ // Preload macro tables, as they can't be loaded asynchronously.
+ InputDWARFFile.Dwarf->getDebugMacinfo();
+ InputDWARFFile.Dwarf->getDebugMacro();
+
+ // Link modules compile units first.
+ parallelForEach(ModulesCompileUnits, [&](RefModuleUnit &RefModule) {
+ linkSingleCompileUnit(*RefModule.Unit);
+ });
+
+ // Check for live relocations. If there is no any live relocation then we
+ // can skip entire object file.
+ if (!GlobalData.getOptions().UpdateIndexTablesOnly &&
+ !InputDWARFFile.Addresses->hasValidRelocs()) {
+ if (GlobalData.getOptions().Verbose)
+ outs() << "No valid relocations found. Skipping.\n";
+ return Error::success();
+ }
+
+ OriginalDebugInfoSize = getInputDebugInfoSize();
+
+ // Create CompileUnit structures to keep information about source
+ // DWARFUnit`s, load line tables.
+ for (const auto &OrigCU : InputDWARFFile.Dwarf->compile_units()) {
+ // Load only unit DIE at this stage.
+ auto CUDie = OrigCU->getUnitDIE();
+ std::string PCMFile =
+ getPCMFile(CUDie, GlobalData.getOptions().ObjectPrefixMap);
+
+ // The !isClangModuleRef condition effectively skips over fully resolved
+ // skeleton units.
+ if (!CUDie || GlobalData.getOptions().UpdateIndexTablesOnly ||
+ !isClangModuleRef(CUDie, PCMFile, 0, true).first) {
+ CompileUnits.emplace_back(std::make_unique<CompileUnit>(
+ GlobalData, *OrigCU, UniqueUnitID.fetch_add(1), "", InputDWARFFile,
+ getUnitForOffset));
+
+ // Preload line table, as it can't be loaded asynchronously.
+ CompileUnits.back()->loadLineTable();
+ }
+ };
+
+ HasNewInterconnectedCUs = false;
+
+ // Link self-sufficient compile units and discover inter-connected compile
+ // units.
+ parallelForEach(CompileUnits, [&](std::unique_ptr<CompileUnit> &CU) {
+ linkSingleCompileUnit(*CU);
+ });
+
+ // Link all inter-connected units.
+ if (HasNewInterconnectedCUs) {
+ InterCUProcessingStarted = true;
+
+ do {
+ HasNewInterconnectedCUs = false;
+
+ // Load inter-connected units.
+ parallelForEach(CompileUnits, [&](std::unique_ptr<CompileUnit> &CU) {
+ if (CU->isInterconnectedCU()) {
+ CU->maybeResetToLoadedStage();
+ linkSingleCompileUnit(*CU, CompileUnit::Stage::Loaded);
+ }
+ });
+
+ // Do liveness analysis for inter-connected units.
+ parallelForEach(CompileUnits, [&](std::unique_ptr<CompileUnit> &CU) {
+ linkSingleCompileUnit(*CU, CompileUnit::Stage::LivenessAnalysisDone);
+ });
+ } while (HasNewInterconnectedCUs);
+
+ // Clone inter-connected units.
+ parallelForEach(CompileUnits, [&](std::unique_ptr<CompileUnit> &CU) {
+ linkSingleCompileUnit(*CU, CompileUnit::Stage::Cloned);
+ });
+
+ // Update patches for inter-connected units.
+ parallelForEach(CompileUnits, [&](std::unique_ptr<CompileUnit> &CU) {
+ linkSingleCompileUnit(*CU, CompileUnit::Stage::PatchesUpdated);
+ });
+
+ // Release data.
+ parallelForEach(CompileUnits, [&](std::unique_ptr<CompileUnit> &CU) {
+ linkSingleCompileUnit(*CU, CompileUnit::Stage::Cleaned);
+ });
+ }
+ }
+
+ if (!InputDWARFFile.Warnings.empty()) {
+ // Create compile unit with paper trail warnings.
+
+ Error ResultErr = Error::success();
+ // We use task group here as PerThreadBumpPtrAllocator should be called from
+ // the threads created by ThreadPoolExecutor.
+ parallel::TaskGroup TGroup;
+ TGroup.spawn([&]() {
+ if (Error Err = cloneAndEmitPaperTrails())
+ ResultErr = std::move(Err);
+ });
+ return ResultErr;
+ } else if (GlobalData.getOptions().UpdateIndexTablesOnly) {
+ // Emit Invariant sections.
+
+ if (Error Err = emitInvariantSections())
+ return Err;
+ } else if (!CompileUnits.empty()) {
+ // Emit .debug_frame section.
+
+ Error ResultErr = Error::success();
+ parallel::TaskGroup TGroup;
+ // We use task group here as PerThreadBumpPtrAllocator should be called from
+ // the threads created by ThreadPoolExecutor.
+ TGroup.spawn([&]() {
+ if (Error Err = cloneAndEmitDebugFrame())
+ ResultErr = std::move(Err);
+ });
+ return ResultErr;
+ }
+
+ return Error::success();
+}
+
+void DWARFLinkerImpl::LinkContext::linkSingleCompileUnit(
+ CompileUnit &CU, enum CompileUnit::Stage DoUntilStage) {
+ while (CU.getStage() < DoUntilStage) {
+ if (InterCUProcessingStarted != CU.isInterconnectedCU())
+ return;
+
+ switch (CU.getStage()) {
+ case CompileUnit::Stage::CreatedNotLoaded: {
+ // Load input compilation unit DIEs.
+ // Analyze properties of DIEs.
+ if (!CU.loadInputDIEs()) {
+ // We do not need to do liveness analysis for invalud compilation unit.
+ CU.setStage(CompileUnit::Stage::LivenessAnalysisDone);
+ } else {
+ CU.analyzeDWARFStructure();
+
+ // The registerModuleReference() condition effectively skips
+ // over fully resolved skeleton units. This second pass of
+ // registerModuleReferences doesn't do any new work, but it
+ // will collect top-level errors, which are suppressed. Module
+ // warnings were already displayed in the first iteration.
+ if (registerModuleReference(
+ CU.getOrigUnit().getUnitDIE(), nullptr,
+ [](const DWARFUnit &) {}, 0))
+ CU.setStage(CompileUnit::Stage::PatchesUpdated);
+ else
+ CU.setStage(CompileUnit::Stage::Loaded);
+ }
+ } break;
+
+ case CompileUnit::Stage::Loaded: {
+ // Mark all the DIEs that need to be present in the generated output.
+ // If ODR requested, build type names.
+ if (!DependencyTracker(*this).resolveDependenciesAndMarkLiveness(CU)) {
+ assert(HasNewInterconnectedCUs);
+ return;
+ }
+
+ CU.setStage(CompileUnit::Stage::LivenessAnalysisDone);
+ } break;
+
+ case CompileUnit::Stage::LivenessAnalysisDone:
+
+#ifndef NDEBUG
+ DependencyTracker::verifyKeepChain(CU);
+#endif
+
+ // Clone input compile unit.
+ if (CU.isClangModule() || GlobalData.getOptions().UpdateIndexTablesOnly ||
+ CU.getContaingFile().Addresses->hasValidRelocs()) {
+ if (Error Err = CU.cloneAndEmit(TargetTriple))
+ CU.error(std::move(Err));
+ }
+
+ CU.setStage(CompileUnit::Stage::Cloned);
+ break;
+
+ case CompileUnit::Stage::Cloned:
+ // Update DIEs referencies.
+ CU.updateDieRefPatchesWithClonedOffsets();
+ CU.setStage(CompileUnit::Stage::PatchesUpdated);
+ break;
+
+ case CompileUnit::Stage::PatchesUpdated:
+ // Cleanup resources.
+ CU.cleanupDataAfterClonning();
+ CU.setStage(CompileUnit::Stage::Cleaned);
+ break;
+
+ case CompileUnit::Stage::Cleaned:
+ assert(false);
+ break;
+ }
+ }
+}
+
+Error DWARFLinkerImpl::LinkContext::emitInvariantSections() {
+ if (GlobalData.getOptions().NoOutput)
+ return Error::success();
+
+ getOrCreateSectionDescriptor(DebugSectionKind::DebugLoc).OS
+ << InputDWARFFile.Dwarf->getDWARFObj().getLocSection().Data;
+ getOrCreateSectionDescriptor(DebugSectionKind::DebugLocLists).OS
+ << InputDWARFFile.Dwarf->getDWARFObj().getLoclistsSection().Data;
+ getOrCreateSectionDescriptor(DebugSectionKind::DebugRange).OS
+ << InputDWARFFile.Dwarf->getDWARFObj().getRangesSection().Data;
+ getOrCreateSectionDescriptor(DebugSectionKind::DebugRngLists).OS
+ << InputDWARFFile.Dwarf->getDWARFObj().getRnglistsSection().Data;
+ getOrCreateSectionDescriptor(DebugSectionKind::DebugARanges).OS
+ << InputDWARFFile.Dwarf->getDWARFObj().getArangesSection();
+ getOrCreateSectionDescriptor(DebugSectionKind::DebugFrame).OS
+ << InputDWARFFile.Dwarf->getDWARFObj().getFrameSection().Data;
+ getOrCreateSectionDescriptor(DebugSectionKind::DebugAddr).OS
+ << InputDWARFFile.Dwarf->getDWARFObj().getAddrSection().Data;
+
+ return Error::success();
+}
+
+Error DWARFLinkerImpl::LinkContext::cloneAndEmitDebugFrame() {
+ if (GlobalData.getOptions().NoOutput)
+ return Error::success();
+
+ if (InputDWARFFile.Dwarf.get() == nullptr)
+ return Error::success();
+
+ const DWARFObject &InputDWARFObj = InputDWARFFile.Dwarf->getDWARFObj();
+
+ StringRef OrigFrameData = InputDWARFObj.getFrameSection().Data;
+ if (OrigFrameData.empty())
+ return Error::success();
+
+ RangesTy AllUnitsRanges;
+ for (std::unique_ptr<CompileUnit> &Unit : CompileUnits) {
+ for (auto CurRange : Unit->getFunctionRanges())
+ AllUnitsRanges.insert(CurRange.Range, CurRange.Value);
+ }
+
+ unsigned SrcAddrSize = InputDWARFObj.getAddressSize();
+
+ SectionDescriptor &OutSection =
+ getOrCreateSectionDescriptor(DebugSectionKind::DebugFrame);
+
+ DataExtractor Data(OrigFrameData, InputDWARFObj.isLittleEndian(), 0);
+ uint64_t InputOffset = 0;
+
+ // Store the data of the CIEs defined in this object, keyed by their
+ // offsets.
+ DenseMap<uint64_t, StringRef> LocalCIES;
+
+ /// The CIEs that have been emitted in the output section. The actual CIE
+ /// data serves a the key to this StringMap.
+ StringMap<uint32_t> EmittedCIEs;
+
+ while (Data.isValidOffset(InputOffset)) {
+ uint64_t EntryOffset = InputOffset;
+ uint32_t InitialLength = Data.getU32(&InputOffset);
+ if (InitialLength == 0xFFFFFFFF)
+ return createFileError(InputDWARFObj.getFileName(),
+ createStringError(std::errc::invalid_argument,
+ "Dwarf64 bits no supported"));
+
+ uint32_t CIEId = Data.getU32(&InputOffset);
+ if (CIEId == 0xFFFFFFFF) {
+ // This is a CIE, store it.
+ StringRef CIEData = OrigFrameData.substr(EntryOffset, InitialLength + 4);
+ LocalCIES[EntryOffset] = CIEData;
+ // The -4 is to account for the CIEId we just read.
+ InputOffset += InitialLength - 4;
+ continue;
+ }
+
+ uint64_t Loc = Data.getUnsigned(&InputOffset, SrcAddrSize);
+
+ // Some compilers seem to emit frame info that doesn't start at
+ // the function entry point, thus we can't just lookup the address
+ // in the debug map. Use the AddressInfo's range map to see if the FDE
+ // describes something that we can relocate.
+ std::optional<AddressRangeValuePair> Range =
+ AllUnitsRanges.getRangeThatContains(Loc);
+ if (!Range) {
+ // The +4 is to account for the size of the InitialLength field itself.
+ InputOffset = EntryOffset + InitialLength + 4;
+ continue;
+ }
+
+ // This is an FDE, and we have a mapping.
+ // Have we already emitted a corresponding CIE?
+ StringRef CIEData = LocalCIES[CIEId];
+ if (CIEData.empty())
+ return createFileError(
+ InputDWARFObj.getFileName(),
+ createStringError(std::errc::invalid_argument,
+ "Inconsistent debug_frame content. Dropping."));
+
+ uint64_t OffsetToCIERecord = OutSection.OS.tell();
+
+ // Look if we already emitted a CIE that corresponds to the
+ // referenced one (the CIE data is the key of that lookup).
+ auto IteratorInserted =
+ EmittedCIEs.insert(std::make_pair(CIEData, OffsetToCIERecord));
+ OffsetToCIERecord = IteratorInserted.first->getValue();
+
+ // Emit CIE for this ID if it is not emitted yet.
+ if (IteratorInserted.second)
+ OutSection.OS << CIEData;
+
+ // Remember offset to the FDE record, so that we might update
+ // field referencing CIE record(containing OffsetToCIERecord),
+ // when final offsets are known. OffsetToCIERecord(which is written later)
+ // is local to the current .debug_frame section, it should be updated
+ // with final offset of the .debug_frame section.
+ OutSection.notePatch(
+ DebugOffsetPatch{OutSection.OS.tell() + 4, &OutSection, true});
+
+ // Emit the FDE with updated address and CIE pointer.
+ // (4 + AddrSize) is the size of the CIEId + initial_location
+ // fields that will get reconstructed by emitFDE().
+ unsigned FDERemainingBytes = InitialLength - (4 + SrcAddrSize);
+ emitFDE(OffsetToCIERecord, SrcAddrSize, Loc + Range->Value,
+ OrigFrameData.substr(InputOffset, FDERemainingBytes), OutSection);
+ InputOffset += FDERemainingBytes;
+ }
+
+ return Error::success();
+}
+
+/// Emit a FDE into the debug_frame section. \p FDEBytes
+/// contains the FDE data without the length, CIE offset and address
+/// which will be replaced with the parameter values.
+void DWARFLinkerImpl::LinkContext::emitFDE(uint32_t CIEOffset,
+ uint32_t AddrSize, uint64_t Address,
+ StringRef FDEBytes,
+ SectionDescriptor &Section) {
+ Section.emitIntVal(FDEBytes.size() + 4 + AddrSize, 4);
+ Section.emitIntVal(CIEOffset, 4);
+ Section.emitIntVal(Address, AddrSize);
+ Section.OS.write(FDEBytes.data(), FDEBytes.size());
+}
+
+Error DWARFLinkerImpl::LinkContext::cloneAndEmitPaperTrails() {
+
+ CompileUnits.emplace_back(
+ std::make_unique<CompileUnit>(GlobalData, UniqueUnitID.fetch_add(1), "",
+ InputDWARFFile, getUnitForOffset));
+
+ CompileUnit &CU = *CompileUnits.back();
+
+ BumpPtrAllocator Allocator;
+
+ DIEGenerator ParentGenerator(Allocator, CU);
+
+ SectionDescriptor &DebugInfoSection =
+ CU.getOrCreateSectionDescriptor(DebugSectionKind::DebugInfo);
+ OffsetsPtrVector PatchesOffsets;
+
+ uint64_t CurrentOffset = CU.getDebugInfoHeaderSize();
+ DIE *CUDie =
+ ParentGenerator.createDIE(dwarf::DW_TAG_compile_unit, CurrentOffset);
+ CU.setOutUnitDIE(CUDie);
+
+ DebugInfoSection.notePatchWithOffsetUpdate(
+ DebugStrPatch{{CurrentOffset},
+ GlobalData.getStringPool().insert("dsymutil").first},
+ PatchesOffsets);
+ CurrentOffset += ParentGenerator
+ .addStringPlaceholderAttribute(dwarf::DW_AT_producer,
+ dwarf::DW_FORM_strp)
+ .second;
+
+ CurrentOffset +=
+ ParentGenerator
+ .addInplaceString(dwarf::DW_AT_name, InputDWARFFile.FileName)
+ .second;
+
+ size_t SizeAbbrevNumber = ParentGenerator.finalizeAbbreviations(true);
+ CurrentOffset += SizeAbbrevNumber;
+ for (uint64_t *OffsetPtr : PatchesOffsets)
+ *OffsetPtr += SizeAbbrevNumber;
+ for (const auto &Warning : CU.getContaingFile().Warnings) {
+ PatchesOffsets.clear();
+ DIEGenerator ChildGenerator(Allocator, CU);
+
+ DIE *ChildDie =
+ ChildGenerator.createDIE(dwarf::DW_TAG_constant, CurrentOffset);
+ ParentGenerator.addChild(ChildDie);
+
+ DebugInfoSection.notePatchWithOffsetUpdate(
+ DebugStrPatch{
+ {CurrentOffset},
+ GlobalData.getStringPool().insert("dsymutil_warning").first},
+ PatchesOffsets);
+ CurrentOffset += ChildGenerator
+ .addStringPlaceholderAttribute(dwarf::DW_AT_name,
+ dwarf::DW_FORM_strp)
+ .second;
+
+ CurrentOffset +=
+ ChildGenerator
+ .addScalarAttribute(dwarf::DW_AT_artificial, dwarf::DW_FORM_flag, 1)
+ .second;
+
+ DebugInfoSection.notePatchWithOffsetUpdate(
+ DebugStrPatch{{CurrentOffset},
+ GlobalData.getStringPool().insert(Warning).first},
+ PatchesOffsets);
+ CurrentOffset += ChildGenerator
+ .addStringPlaceholderAttribute(
+ dwarf::DW_AT_const_value, dwarf::DW_FORM_strp)
+ .second;
+
+ SizeAbbrevNumber = ChildGenerator.finalizeAbbreviations(false);
+
+ CurrentOffset += SizeAbbrevNumber;
+ for (uint64_t *OffsetPtr : PatchesOffsets)
+ *OffsetPtr += SizeAbbrevNumber;
+
+ ChildDie->setSize(CurrentOffset - ChildDie->getOffset());
+ }
+
+ CurrentOffset += 1; // End of children
+ CUDie->setSize(CurrentOffset - CUDie->getOffset());
+
+ uint64_t UnitSize = 0;
+ UnitSize += CU.getDebugInfoHeaderSize();
+ UnitSize += CUDie->getSize();
+ CU.setUnitSize(UnitSize);
+
+ if (GlobalData.getOptions().NoOutput)
+ return Error::success();
+
+ if (Error Err = CU.emitDebugInfo(*TargetTriple))
+ return Err;
+
+ return CU.emitAbbreviations();
+}
+
+void DWARFLinkerImpl::glueCompileUnitsAndWriteToTheOutput() {
+ if (GlobalData.getOptions().NoOutput)
+ return;
+
+ // Go through all object files, all compile units and assign
+ // offsets to them.
+ assignOffsets();
+
+ // Patch size/offsets fields according to the assigned CU offsets.
+ patchOffsetsAndSizes();
+
+ // FIXME: Build accelerator tables.
+
+ // Emit common sections.
+ emitCommonSections();
+
+ // Cleanup data.
+ cleanupDataAfterOutputSectionsAreGenerated();
+
+ // Write debug tables from all object files/compile units into the
+ // resulting file.
+ writeDWARFToTheOutput();
+
+ if (GlobalData.getOptions().Statistics)
+ printStatistic();
+}
+
+void DWARFLinkerImpl::printStatistic() {
+
+ // For each object file map how many bytes were emitted.
+ StringMap<DebugInfoSize> SizeByObject;
+
+ for (const std::unique_ptr<LinkContext> &Context : ObjectContexts) {
+ uint64_t AllDebugInfoSectionsSize = 0;
+
+ for (std::unique_ptr<CompileUnit> &CU : Context->CompileUnits)
+ if (std::optional<SectionDescriptor *> DebugInfo =
+ CU->getSectionDescriptor(DebugSectionKind::DebugInfo))
+ AllDebugInfoSectionsSize += (*DebugInfo)->getContents().size();
+
+ SizeByObject[Context->InputDWARFFile.FileName].Input =
+ Context->OriginalDebugInfoSize;
+ SizeByObject[Context->InputDWARFFile.FileName].Output =
+ AllDebugInfoSectionsSize;
+ }
+
+ // Create a vector sorted in descending order by output size.
+ std::vector<std::pair<StringRef, DebugInfoSize>> Sorted;
+ for (auto &E : SizeByObject)
+ Sorted.emplace_back(E.first(), E.second);
+ llvm::sort(Sorted, [](auto &LHS, auto &RHS) {
+ return LHS.second.Output > RHS.second.Output;
+ });
+
+ auto ComputePercentange = [](int64_t Input, int64_t Output) -> float {
+ const float Difference = Output - Input;
+ const float Sum = Input + Output;
+ if (Sum == 0)
+ return 0;
+ return (Difference / (Sum / 2));
+ };
+
+ int64_t InputTotal = 0;
+ int64_t OutputTotal = 0;
+ const char *FormatStr = "{0,-45} {1,10}b {2,10}b {3,8:P}\n";
+
+ // Print header.
+ outs() << ".debug_info section size (in bytes)\n";
+ outs() << "----------------------------------------------------------------"
+ "---------------\n";
+ outs() << "Filename Object "
+ " dSYM Change\n";
+ outs() << "----------------------------------------------------------------"
+ "---------------\n";
+
+ // Print body.
+ for (auto &E : Sorted) {
+ InputTotal += E.second.Input;
+ OutputTotal += E.second.Output;
+ llvm::outs() << formatv(
+ FormatStr, sys::path::filename(E.first).take_back(45), E.second.Input,
+ E.second.Output, ComputePercentange(E.second.Input, E.second.Output));
+ }
+ // Print total and footer.
+ outs() << "----------------------------------------------------------------"
+ "---------------\n";
+ llvm::outs() << formatv(FormatStr, "Total", InputTotal, OutputTotal,
+ ComputePercentange(InputTotal, OutputTotal));
+ outs() << "----------------------------------------------------------------"
+ "---------------\n\n";
+}
+
+void DWARFLinkerImpl::assignOffsets() {
+ parallel::TaskGroup TGroup;
+ TGroup.spawn([&]() { assignOffsetsToStrings(); });
+ TGroup.spawn([&]() { assignOffsetsToSections(); });
+}
+
+void DWARFLinkerImpl::assignOffsetsToStrings() {
+ size_t CurDebugStrIndex = 1; // start from 1 to take into account zero entry.
+ uint64_t CurDebugStrOffset =
+ 1; // start from 1 to take into account zero entry.
+ size_t CurDebugLineStrIndex = 0;
+ uint64_t CurDebugLineStrOffset = 0;
+
+ // To save space we do not create any separate string table.
+ // We use already allocated string patches and assign offsets
+ // to them in the natural order.
+ // ASSUMPTION: strings should be stored into .debug_str/.debug_line_str
+ // sections in the same order as they were assigned offsets.
+
+ forEachObjectSectionsSet([&](OutputSections &SectionsSet) {
+ SectionsSet.forEach([&](SectionDescriptor &OutSection) {
+ assignOffsetsToStringsImpl(OutSection.ListDebugStrPatch, CurDebugStrIndex,
+ CurDebugStrOffset, DebugStrStrings);
+
+ assignOffsetsToStringsImpl(OutSection.ListDebugLineStrPatch,
+ CurDebugLineStrIndex, CurDebugLineStrOffset,
+ DebugLineStrStrings);
+ });
+ });
+}
+
+template <typename PatchTy>
+void DWARFLinkerImpl::assignOffsetsToStringsImpl(
+ ArrayList<PatchTy> &Patches, size_t &IndexAccumulator,
+ uint64_t &OffsetAccumulator,
+ StringEntryToDwarfStringPoolEntryMap &StringsForEmission) {
+
+ // Enumerates all patches, adds string into the
+ // StringEntry->DwarfStringPoolEntry map, assign offset and index to the
+ // string if it is not indexed yet.
+ Patches.forEach([&](PatchTy &Patch) {
+ DwarfStringPoolEntryWithExtString *Entry =
+ StringsForEmission.add(Patch.String);
+ assert(Entry != nullptr);
+
+ if (!Entry->isIndexed()) {
+ Entry->Offset = OffsetAccumulator;
+ OffsetAccumulator += Entry->String.size() + 1;
+ Entry->Index = IndexAccumulator++;
+ }
+ });
+}
+
+void DWARFLinkerImpl::assignOffsetsToSections() {
+ std::array<uint64_t, SectionKindsNum> SectionSizesAccumulator = {0};
+
+ forEachObjectSectionsSet([&](OutputSections &UnitSections) {
+ UnitSections.assignSectionsOffsetAndAccumulateSize(SectionSizesAccumulator);
+ });
+}
+
+void DWARFLinkerImpl::forEachObjectSectionsSet(
+ function_ref<void(OutputSections &)> SectionsSetHandler) {
+ // Handle all modules first(before regular compilation units).
+ for (const std::unique_ptr<LinkContext> &Context : ObjectContexts)
+ for (LinkContext::RefModuleUnit &ModuleUnit : Context->ModulesCompileUnits)
+ SectionsSetHandler(*ModuleUnit.Unit);
+
+ for (const std::unique_ptr<LinkContext> &Context : ObjectContexts) {
+ // Handle object file common sections.
+ SectionsSetHandler(*Context);
+
+ // Handle compilation units.
+ for (std::unique_ptr<CompileUnit> &CU : Context->CompileUnits)
+ SectionsSetHandler(*CU);
+ }
+}
+
+void DWARFLinkerImpl::patchOffsetsAndSizes() {
+ forEachObjectSectionsSet([&](OutputSections &SectionsSet) {
+ SectionsSet.forEach([&](SectionDescriptor &OutSection) {
+ SectionsSet.applyPatches(OutSection, DebugStrStrings,
+ DebugLineStrStrings);
+ });
+ });
+}
+
+template <typename PatchTy>
+void DWARFLinkerImpl::emitStringsImpl(
+ ArrayList<PatchTy> &StringPatches,
+ const StringEntryToDwarfStringPoolEntryMap &Strings, uint64_t &NextOffset,
+ SectionDescriptor &OutSection) {
+ // Enumerate all string patches and write strings into the destination
+ // section. We enumerate patches to have a predictable order of strings(i.e.
+ // strings are emitted in the order as they appear in the patches).
+ StringPatches.forEach([&](const PatchTy &Patch) {
+ DwarfStringPoolEntryWithExtString *StringToEmit =
+ Strings.getExistingEntry(Patch.String);
+ assert(StringToEmit->isIndexed());
+
+ // Patches can refer the same strings. We use accumulated NextOffset
+ // to understand whether corresponding string is already emitted.
+ // Skip patch if string is already emitted.
+ if (StringToEmit->Offset >= NextOffset) {
+ NextOffset = StringToEmit->Offset + StringToEmit->String.size() + 1;
+ // Emit the string itself.
+ OutSection.emitInplaceString(StringToEmit->String);
+ }
+ });
+}
+
+void DWARFLinkerImpl::emitCommonSections() {
+ parallel::TaskGroup TG;
+
+ SectionDescriptor &OutDebugStrSection =
+ CommonSections.getOrCreateSectionDescriptor(DebugSectionKind::DebugStr);
+ SectionDescriptor &OutDebugLineStrSection =
+ CommonSections.getOrCreateSectionDescriptor(
+ DebugSectionKind::DebugLineStr);
+
+ // Emit .debug_str section.
+ TG.spawn([&]() {
+ uint64_t DebugStrNextOffset = 0;
+
+ // Emit zero length string. Accelerator tables does not work correctly
+ // if the first string is not zero length string.
+ OutDebugStrSection.emitInplaceString("");
+ DebugStrNextOffset++;
+
+ forEachObjectSectionsSet([&](OutputSections &Sections) {
+ Sections.forEach([&](SectionDescriptor &Section) {
+ emitStringsImpl(Section.ListDebugStrPatch, DebugStrStrings,
+ DebugStrNextOffset, OutDebugStrSection);
+ });
+ });
+ });
+
+ // Emit .debug_line_str section.
+ TG.spawn([&]() {
+ uint64_t DebugLineStrNextOffset = 0;
+
+ forEachObjectSectionsSet([&](OutputSections &Sections) {
+ Sections.forEach([&](SectionDescriptor &Section) {
+ emitStringsImpl(Section.ListDebugLineStrPatch, DebugLineStrStrings,
+ DebugLineStrNextOffset, OutDebugLineStrSection);
+ });
+ });
+ });
+}
+
+void DWARFLinkerImpl::cleanupDataAfterOutputSectionsAreGenerated() {
+ GlobalData.getStringPool().clear();
+ DebugStrStrings.clear();
+ DebugLineStrStrings.clear();
+}
+
+void DWARFLinkerImpl::writeDWARFToTheOutput() {
+ bool HasAbbreviations = false;
+
+ forEachObjectSectionsSet([&](OutputSections &Sections) {
+ Sections.forEach([&](SectionDescriptor &OutSection) {
+ if (!HasAbbreviations && !OutSection.getContents().empty() &&
+ OutSection.getKind() == DebugSectionKind::DebugAbbrev)
+ HasAbbreviations = true;
+
+ // Emit section content.
+ TheDwarfEmitter->emitSectionContents(OutSection.getContents(),
+ OutSection.getName());
+ OutSection.erase();
+ });
+ });
+
+ CommonSections.forEach([&](SectionDescriptor &OutSection) {
+ // Emit section content.
+ TheDwarfEmitter->emitSectionContents(OutSection.getContents(),
+ OutSection.getName());
+ OutSection.erase();
+ });
+
+ if (!HasAbbreviations) {
+ const SmallVector<std::unique_ptr<DIEAbbrev>> Abbreviations;
+ TheDwarfEmitter->emitAbbrevs(Abbreviations, 3);
+ }
+}
+
} // end of namespace dwarflinker_parallel
} // namespace llvm
diff --git a/llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.h b/llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.h
index a8fa9b4b46d8a6..7e41eb11f239ea 100644
--- a/llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.h
+++ b/llvm/lib/DWARFLinkerParallel/DWARFLinkerImpl.h
@@ -11,29 +11,29 @@
#include "DWARFEmitterImpl.h"
#include "DWARFLinkerCompileUnit.h"
+#include "StringEntryToDwarfStringPoolEntryMap.h"
#include "llvm/ADT/AddressRanges.h"
#include "llvm/CodeGen/AccelTable.h"
#include "llvm/DWARFLinkerParallel/DWARFLinker.h"
#include "llvm/DWARFLinkerParallel/StringPool.h"
-#include "llvm/DWARFLinkerParallel/StringTable.h"
namespace llvm {
namespace dwarflinker_parallel {
-using Offset2UnitMapTy = DenseMap<uint64_t, CompileUnit *>;
-
-struct RangeAttrPatch;
-struct LocAttrPatch;
-
+/// This class links debug info.
class DWARFLinkerImpl : public DWARFLinker {
public:
DWARFLinkerImpl(MessageHandlerTy ErrorHandler,
MessageHandlerTy WarningHandler,
TranslatorFuncTy StringsTranslator)
- : UniqueUnitID(0), ErrorHandler(ErrorHandler),
- WarningHandler(WarningHandler),
- OutputStrings(Strings, StringsTranslator) {}
+ : UniqueUnitID(0), DebugStrStrings(GlobalData),
+ DebugLineStrStrings(GlobalData), CommonSections(GlobalData) {
+ GlobalData.setTranslator(StringsTranslator);
+ GlobalData.setErrorHandler(ErrorHandler);
+ GlobalData.setWarningHandler(WarningHandler);
+ }
+ /// Create debug info emitter.
Error createEmitter(const Triple &TheTriple, OutputFileType FileType,
raw_pwrite_stream &OutFile) override;
@@ -47,13 +47,11 @@ class DWARFLinkerImpl : public DWARFLinker {
/// \pre NoODR, Update options should be set before call to addObjectFile.
void addObjectFile(
DWARFFile &File, ObjFileLoaderTy Loader = nullptr,
- CompileUnitHandlerTy OnCUDieLoaded = [](const DWARFUnit &) {}) override {}
+
+ CompileUnitHandlerTy OnCUDieLoaded = [](const DWARFUnit &) {}) override;
/// Link debug info for added files.
- Error link() override {
- reportWarning("LLVM parallel dwarflinker is not implemented yet.", "");
- return Error::success();
- }
+ Error link() override;
/// \defgroup Methods setting various linking options:
///
@@ -61,51 +59,58 @@ class DWARFLinkerImpl : public DWARFLinker {
///
/// Allows to generate log of linking process to the standard output.
- void setVerbosity(bool Verbose) override { Options.Verbose = Verbose; }
+ void setVerbosity(bool Verbose) override {
+ GlobalData.Options.Verbose = Verbose;
+ }
/// Print statistics to standard output.
void setStatistics(bool Statistics) override {
- Options.Statistics = Statistics;
+ GlobalData.Options.Statistics = Statistics;
}
/// Verify the input DWARF.
void setVerifyInputDWARF(bool Verify) override {
- Options.VerifyInputDWARF = Verify;
+ GlobalData.Options.VerifyInputDWARF = Verify;
}
/// Do not unique types according to ODR.
- void setNoODR(bool NoODR) override { Options.NoODR = NoODR; }
+ void setNoODR(bool) override {
+ // FIXME: set option when ODR mode will be supported.
+ // getOptions().NoODR = NoODR;
+ GlobalData.Options.NoODR = true;
+ }
/// Update index tables only(do not modify rest of DWARF).
void setUpdateIndexTablesOnly(bool UpdateIndexTablesOnly) override {
- Options.UpdateIndexTablesOnly = UpdateIndexTablesOnly;
+ GlobalData.Options.UpdateIndexTablesOnly = UpdateIndexTablesOnly;
}
/// Allow generating valid, but non-deterministic output.
void
setAllowNonDeterministicOutput(bool AllowNonDeterministicOutput) override {
- Options.AllowNonDeterministicOutput = AllowNonDeterministicOutput;
+ GlobalData.Options.AllowNonDeterministicOutput =
+ AllowNonDeterministicOutput;
}
/// Set to keep the enclosing function for a static variable.
void setKeepFunctionForStatic(bool KeepFunctionForStatic) override {
- Options.KeepFunctionForStatic = KeepFunctionForStatic;
+ GlobalData.Options.KeepFunctionForStatic = KeepFunctionForStatic;
}
/// Use specified number of threads for parallel files linking.
void setNumThreads(unsigned NumThreads) override {
- Options.Threads = NumThreads;
+ GlobalData.Options.Threads = NumThreads;
}
/// Add kind of accelerator tables to be generated.
void addAccelTableKind(AccelTableKind Kind) override {
- assert(!llvm::is_contained(Options.AccelTables, Kind));
- Options.AccelTables.emplace_back(Kind);
+ assert(!llvm::is_contained(GlobalData.getOptions().AccelTables, Kind));
+ GlobalData.Options.AccelTables.emplace_back(Kind);
}
/// Set prepend path for clang modules.
void setPrependPath(const std::string &Ppath) override {
- Options.PrependPath = Ppath;
+ GlobalData.Options.PrependPath = Ppath;
}
/// Set estimated objects files amount, for preliminary data allocation.
@@ -117,17 +122,17 @@ class DWARFLinkerImpl : public DWARFLinker {
/// errors.
void
setInputVerificationHandler(InputVerificationHandlerTy Handler) override {
- Options.InputVerificationHandler = Handler;
+ GlobalData.Options.InputVerificationHandler = Handler;
}
/// Set map for Swift interfaces.
void setSwiftInterfacesMap(SwiftInterfacesMapTy *Map) override {
- Options.ParseableSwiftInterfaces = Map;
+ GlobalData.Options.ParseableSwiftInterfaces = Map;
}
/// Set prefix map for objects.
void setObjectPrefixMap(ObjectPrefixMapTy *Map) override {
- Options.ObjectPrefixMap = Map;
+ GlobalData.Options.ObjectPrefixMap = Map;
}
/// Set target DWARF version.
@@ -137,36 +142,28 @@ class DWARFLinkerImpl : public DWARFLinker {
"unsupported DWARF version: %d",
TargetDWARFVersion);
- Options.TargetDWARFVersion = TargetDWARFVersion;
+ GlobalData.Options.TargetDWARFVersion = TargetDWARFVersion;
return Error::success();
}
/// @}
protected:
- /// Reports Warning.
- void reportWarning(const Twine &Warning, const DWARFFile &File,
- const DWARFDie *DIE = nullptr) const {
- if (WarningHandler != nullptr)
- WarningHandler(Warning, File.FileName, DIE);
- }
+ /// Verify input DWARF file.
+ void verifyInput(const DWARFFile &File);
- /// Reports Warning.
- void reportWarning(const Twine &Warning, StringRef FileName,
- const DWARFDie *DIE = nullptr) const {
- if (WarningHandler != nullptr)
- WarningHandler(Warning, FileName, DIE);
- }
+ /// Validate specified options.
+ Error validateAndUpdateOptions();
- /// Reports Error.
- void reportError(const Twine &Warning, StringRef FileName,
- const DWARFDie *DIE = nullptr) const {
- if (ErrorHandler != nullptr)
- ErrorHandler(Warning, FileName, DIE);
- }
+ /// Take already linked compile units and glue them into single file.
+ void glueCompileUnitsAndWriteToTheOutput();
- /// Returns next available unique Compile Unit ID.
- unsigned getNextUniqueUnitID() { return UniqueUnitID.fetch_add(1); }
+ /// Hold the input and output of the debug info size in bytes.
+ struct DebugInfoSize {
+ uint64_t Input;
+ uint64_t Output;
+ };
+ friend class DependencyTracker;
/// Keeps track of data associated with one object during linking.
/// i.e. source file descriptor, compilation units, output data
/// for compilation units common tables.
@@ -188,7 +185,7 @@ class DWARFLinkerImpl : public DWARFLinker {
using ModuleUnitListTy = SmallVector<RefModuleUnit>;
/// Object file descriptor.
- DWARFFile &File;
+ DWARFFile &InputDWARFFile;
/// Set of Compilation Units(may be accessed asynchroniously for reading).
UnitListTy CompileUnits;
@@ -199,117 +196,190 @@ class DWARFLinkerImpl : public DWARFLinker {
/// Size of Debug info before optimizing.
uint64_t OriginalDebugInfoSize = 0;
- /// Output sections, common for all compilation units.
- OutTablesFileTy OutDebugInfoBytes;
+ /// Flag indicating that all inter-connected units are loaded
+ /// and the dwarf linking process for these units is started.
+ bool InterCUProcessingStarted = false;
+
+ StringMap<uint64_t> &ClangModules;
+
+ std::optional<Triple> TargetTriple;
+
+ /// Flag indicating that new inter-connected compilation units were
+ /// discovered. It is used for restarting units processing
+ /// if new inter-connected units were found.
+ std::atomic<bool> HasNewInterconnectedCUs = {false};
- /// Endianness for the final file.
- support::endianness Endianess = support::endianness::little;
+ /// Counter for compile units ID.
+ std::atomic<size_t> &UniqueUnitID;
+
+ LinkContext(LinkingGlobalData &GlobalData, DWARFFile &File,
+ StringMap<uint64_t> &ClangModules,
+ std::atomic<size_t> &UniqueUnitID,
+ std::optional<Triple> TargetTriple)
+ : OutputSections(GlobalData), InputDWARFFile(File),
+ ClangModules(ClangModules), TargetTriple(TargetTriple),
+ UniqueUnitID(UniqueUnitID) {
- LinkContext(DWARFFile &File) : File(File) {
if (File.Dwarf) {
if (!File.Dwarf->compile_units().empty())
CompileUnits.reserve(File.Dwarf->getNumCompileUnits());
-
- Endianess = File.Dwarf->isLittleEndian() ? support::endianness::little
- : support::endianness::big;
}
}
+ /// Check whether specified \p CUDie is a Clang module reference.
+ /// if \p Quiet is false then display error messages.
+ /// \return first == true if CUDie is a Clang module reference.
+ /// second == true if module is already loaded.
+ std::pair<bool, bool> isClangModuleRef(const DWARFDie &CUDie,
+ std::string &PCMFile,
+ unsigned Indent, bool Quiet);
+
+ /// If this compile unit is really a skeleton CU that points to a
+ /// clang module, register it in ClangModules and return true.
+ ///
+ /// A skeleton CU is a CU without children, a DW_AT_gnu_dwo_name
+ /// pointing to the module, and a DW_AT_gnu_dwo_id with the module
+ /// hash.
+ bool registerModuleReference(const DWARFDie &CUDie, ObjFileLoaderTy Loader,
+ CompileUnitHandlerTy OnCUDieLoaded,
+ unsigned Indent = 0);
+
+ /// Recursively add the debug info in this clang module .pcm
+ /// file (and all the modules imported by it in a bottom-up fashion)
+ /// to ModuleUnits.
+ Error loadClangModule(ObjFileLoaderTy Loader, const DWARFDie &CUDie,
+ const std::string &PCMFile,
+ CompileUnitHandlerTy OnCUDieLoaded,
+ unsigned Indent = 0);
+
/// Add Compile Unit corresponding to the module.
void addModulesCompileUnit(RefModuleUnit &&Unit) {
ModulesCompileUnits.emplace_back(std::move(Unit));
}
- /// Return Endiannes of the source DWARF information.
- support::endianness getEndianness() { return Endianess; }
+ /// Computes the total size of the debug info.
+ uint64_t getInputDebugInfoSize() const {
+ uint64_t Size = 0;
- /// \returns pointer to compilation unit which corresponds \p Offset.
- CompileUnit *getUnitForOffset(CompileUnit &CU, uint64_t Offset) const;
- };
+ if (InputDWARFFile.Dwarf == nullptr)
+ return Size;
+
+ for (auto &Unit : InputDWARFFile.Dwarf->compile_units())
+ Size += Unit->getLength();
+
+ return Size;
+ }
- /// linking options
- struct DWARFLinkerOptions {
- /// DWARF version for the output.
- uint16_t TargetDWARFVersion = 0;
+ /// Link compile units for this context.
+ Error link();
- /// Generate processing log to the standard output.
- bool Verbose = false;
+ /// Link specified compile unit until specified stage.
+ void linkSingleCompileUnit(
+ CompileUnit &CU,
+ enum CompileUnit::Stage DoUntilStage = CompileUnit::Stage::Cleaned);
- /// Print statistics.
- bool Statistics = false;
+ /// Emit invariant sections.
+ Error emitInvariantSections();
- /// Verify the input DWARF.
- bool VerifyInputDWARF = false;
+ /// Clone and emit .debug_frame.
+ Error cloneAndEmitDebugFrame();
- /// Do not unique types according to ODR
- bool NoODR = false;
+ /// Emit FDE record.
+ void emitFDE(uint32_t CIEOffset, uint32_t AddrSize, uint64_t Address,
+ StringRef FDEBytes, SectionDescriptor &Section);
- /// Update index tables.
- bool UpdateIndexTablesOnly = false;
+ /// Clone and emit paper trails.
+ Error cloneAndEmitPaperTrails();
- /// Whether we want a static variable to force us to keep its enclosing
- /// function.
- bool KeepFunctionForStatic = false;
+ std::function<CompileUnit *(uint64_t)> getUnitForOffset =
+ [&](uint64_t Offset) -> CompileUnit * {
+ auto CU = llvm::upper_bound(
+ CompileUnits, Offset,
+ [](uint64_t LHS, const std::unique_ptr<CompileUnit> &RHS) {
+ return LHS < RHS->getOrigUnit().getNextUnitOffset();
+ });
- /// Allow to generate valid, but non deterministic output.
- bool AllowNonDeterministicOutput = false;
+ return CU != CompileUnits.end() ? CU->get() : nullptr;
+ };
+ };
- /// Number of threads.
- unsigned Threads = 1;
+ /// Enumerate all compile units and assign offsets to their sections and
+ /// strings.
+ void assignOffsets();
- /// The accelerator table kinds
- SmallVector<AccelTableKind, 1> AccelTables;
+ /// Enumerate all compile units and assign offsets to their sections.
+ void assignOffsetsToSections();
- /// Prepend path for the clang modules.
- std::string PrependPath;
+ /// Enumerate all compile units and assign offsets to their strings.
+ void assignOffsetsToStrings();
- /// input verification handler(it might be called asynchronously).
- InputVerificationHandlerTy InputVerificationHandler = nullptr;
+ /// Enumerates specified string patches, assigns offset and index.
+ template <typename PatchTy>
+ void assignOffsetsToStringsImpl(
+ ArrayList<PatchTy> &Section, size_t &IndexAccumulator,
+ uint64_t &OffsetAccumulator,
+ StringEntryToDwarfStringPoolEntryMap &StringsForEmission);
- /// A list of all .swiftinterface files referenced by the debug
- /// info, mapping Module name to path on disk. The entries need to
- /// be uniqued and sorted and there are only few entries expected
- /// per compile unit, which is why this is a std::map.
- /// this is dsymutil specific fag.
- ///
- /// (it might be called asynchronously).
- SwiftInterfacesMapTy *ParseableSwiftInterfaces = nullptr;
+ /// Print statistic for processed Debug Info.
+ void printStatistic();
- /// A list of remappings to apply to file paths.
- ///
- /// (it might be called asynchronously).
- ObjectPrefixMapTy *ObjectPrefixMap = nullptr;
- } Options;
+ /// Enumerates sections for modules, invariant for object files, compile
+ /// units.
+ void forEachObjectSectionsSet(
+ function_ref<void(OutputSections &SectionsSet)> SectionsSetHandler);
+
+ /// Enumerates all patches and update them with the correct values.
+ void patchOffsetsAndSizes();
+
+ /// Emit debug sections common for all input files.
+ void emitCommonSections();
+
+ /// Cleanup data(string pools) after output sections are generated.
+ void cleanupDataAfterOutputSectionsAreGenerated();
+
+ /// Enumerate all compile units and put their data into the output stream.
+ void writeDWARFToTheOutput();
+
+ template <typename PatchTy>
+ void emitStringsImpl(ArrayList<PatchTy> &StringPatches,
+ const StringEntryToDwarfStringPoolEntryMap &Strings,
+ uint64_t &NextOffset, SectionDescriptor &OutSection);
/// \defgroup Data members accessed asinchroniously.
///
/// @{
/// Unique ID for compile unit.
- std::atomic<unsigned> UniqueUnitID;
-
- /// Strings pool. Keeps all strings.
- StringPool Strings;
+ std::atomic<size_t> UniqueUnitID;
- /// error handler(it might be called asynchronously).
- MessageHandlerTy ErrorHandler = nullptr;
-
- /// warning handler(it might be called asynchronously).
- MessageHandlerTy WarningHandler = nullptr;
+ /// Mapping the PCM filename to the DwoId.
+ StringMap<uint64_t> ClangModules;
+ std::mutex ClangModulesMutex;
/// @}
/// \defgroup Data members accessed sequentially.
///
/// @{
+ /// DwarfStringPoolEntries for .debug_str section.
+ StringEntryToDwarfStringPoolEntryMap DebugStrStrings;
- /// Set of strings which should be emitted.
- StringTable OutputStrings;
+ /// DwarfStringPoolEntries for .debug_line_str section.
+ StringEntryToDwarfStringPoolEntryMap DebugLineStrStrings;
/// Keeps all linking contexts.
SmallVector<std::unique_ptr<LinkContext>> ObjectContexts;
+ /// Common sections.
+ OutputSections CommonSections;
+
/// The emitter of final dwarf file.
std::unique_ptr<DwarfEmitterImpl> TheDwarfEmitter;
+
+ /// Overall compile units number.
+ uint64_t OverallNumberOfCU = 0;
+
+ /// Data global for the whole linking process.
+ LinkingGlobalData GlobalData;
/// @}
};
diff --git a/llvm/lib/DWARFLinkerParallel/DWARFLinkerUnit.cpp b/llvm/lib/DWARFLinkerParallel/DWARFLinkerUnit.cpp
new file mode 100644
index 00000000000000..e16c237d1a43f7
--- /dev/null
+++ b/llvm/lib/DWARFLinkerParallel/DWARFLinkerUnit.cpp
@@ -0,0 +1,130 @@
+//===- DWARFLinkerUnit.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 "DWARFLinkerUnit.h"
+#include "DWARFEmitterImpl.h"
+#include "DebugLineSectionEmitter.h"
+
+namespace llvm {
+namespace dwarflinker_parallel {
+
+void DwarfUnit::assignAbbrev(DIEAbbrev &Abbrev) {
+ // Check the set for priors.
+ FoldingSetNodeID ID;
+ Abbrev.Profile(ID);
+ void *InsertToken;
+
+ DIEAbbrev *InSet = AbbreviationsSet.FindNodeOrInsertPos(ID, InsertToken);
+ // If it's newly added.
+ if (InSet) {
+ // Assign existing abbreviation number.
+ Abbrev.setNumber(InSet->getNumber());
+ } else {
+ // Add to abbreviation list.
+ Abbreviations.push_back(
+ std::make_unique<DIEAbbrev>(Abbrev.getTag(), Abbrev.hasChildren()));
+ for (const auto &Attr : Abbrev.getData())
+ Abbreviations.back()->AddAttribute(Attr);
+ AbbreviationsSet.InsertNode(Abbreviations.back().get(), InsertToken);
+ // Assign the unique abbreviation number.
+ Abbrev.setNumber(Abbreviations.size());
+ Abbreviations.back()->setNumber(Abbreviations.size());
+ }
+}
+
+Error DwarfUnit::emitAbbreviations() {
+ const std::vector<std::unique_ptr<DIEAbbrev>> &Abbrevs = getAbbreviations();
+ if (Abbrevs.empty())
+ return Error::success();
+
+ SectionDescriptor &AbbrevSection =
+ getOrCreateSectionDescriptor(DebugSectionKind::DebugAbbrev);
+
+ // For each abbreviation.
+ for (const auto &Abbrev : Abbrevs)
+ emitDwarfAbbrevEntry(*Abbrev, AbbrevSection);
+
+ // Mark end of abbreviations.
+ encodeULEB128(0, AbbrevSection.OS);
+
+ return Error::success();
+}
+
+void DwarfUnit::emitDwarfAbbrevEntry(const DIEAbbrev &Abbrev,
+ SectionDescriptor &AbbrevSection) {
+ // Emit the abbreviations code (base 1 index.)
+ encodeULEB128(Abbrev.getNumber(), AbbrevSection.OS);
+
+ // Emit the abbreviations data.
+ // Emit its Dwarf tag type.
+ encodeULEB128(Abbrev.getTag(), AbbrevSection.OS);
+
+ // Emit whether it has children DIEs.
+ encodeULEB128((unsigned)Abbrev.hasChildren(), AbbrevSection.OS);
+
+ // For each attribute description.
+ const SmallVectorImpl<DIEAbbrevData> &Data = Abbrev.getData();
+ for (unsigned i = 0, N = Data.size(); i < N; ++i) {
+ const DIEAbbrevData &AttrData = Data[i];
+
+ // Emit attribute type.
+ encodeULEB128(AttrData.getAttribute(), AbbrevSection.OS);
+
+ // Emit form type.
+ encodeULEB128(AttrData.getForm(), AbbrevSection.OS);
+
+ // Emit value for DW_FORM_implicit_const.
+ if (AttrData.getForm() == dwarf::DW_FORM_implicit_const)
+ encodeSLEB128(AttrData.getValue(), AbbrevSection.OS);
+ }
+
+ // Mark end of abbreviation.
+ encodeULEB128(0, AbbrevSection.OS);
+ encodeULEB128(0, AbbrevSection.OS);
+}
+
+Error DwarfUnit::emitDebugInfo(Triple &TargetTriple) {
+ DIE *OutUnitDIE = getOutUnitDIE();
+ if (OutUnitDIE == nullptr)
+ return Error::success();
+
+ // FIXME: Remove dependence on DwarfEmitterImpl/AsmPrinter and emit DIEs
+ // directly.
+
+ SectionDescriptor &OutSection =
+ getOrCreateSectionDescriptor(DebugSectionKind::DebugInfo);
+ DwarfEmitterImpl Emitter(DWARFLinker::OutputFileType::Object, OutSection.OS);
+ if (Error Err = Emitter.init(TargetTriple, "__DWARF"))
+ return Err;
+
+ // Emit compile unit header.
+ Emitter.emitCompileUnitHeader(*this);
+ size_t OffsetToAbbreviationTableOffset =
+ (getFormParams().Version >= 5) ? 8 : 6;
+ OutSection.notePatch(DebugOffsetPatch{
+ OffsetToAbbreviationTableOffset,
+ &getOrCreateSectionDescriptor(DebugSectionKind::DebugAbbrev)});
+
+ // Emit DIEs.
+ Emitter.emitDIE(*OutUnitDIE);
+ Emitter.finish();
+
+ // Set start offset ans size for .debug_info section.
+ OutSection.setSizesForSectionCreatedByAsmPrinter();
+ return Error::success();
+}
+
+Error DwarfUnit::emitDebugLine(Triple &TargetTriple,
+ const DWARFDebugLine::LineTable &OutLineTable) {
+ DebugLineSectionEmitter DebugLineEmitter(TargetTriple, *this);
+
+ return DebugLineEmitter.emit(OutLineTable);
+}
+
+} // end of namespace dwarflinker_parallel
+} // end of namespace llvm
diff --git a/llvm/lib/DWARFLinkerParallel/DWARFLinkerUnit.h b/llvm/lib/DWARFLinkerParallel/DWARFLinkerUnit.h
index 78e8d82ea06158..50aa6957b3dc28 100644
--- a/llvm/lib/DWARFLinkerParallel/DWARFLinkerUnit.h
+++ b/llvm/lib/DWARFLinkerParallel/DWARFLinkerUnit.h
@@ -9,9 +9,11 @@
#ifndef LLVM_LIB_DWARFLINKERPARALLEL_DWARFLINKERUNIT_H
#define LLVM_LIB_DWARFLINKERPARALLEL_DWARFLINKERUNIT_H
+#include "DWARFLinkerGlobalData.h"
#include "OutputSections.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/CodeGen/DIE.h"
+#include "llvm/DWARFLinkerParallel/DWARFLinker.h"
#include "llvm/DWARFLinkerParallel/StringPool.h"
#include "llvm/DebugInfo/DWARF/DWARFUnit.h"
#include "llvm/Support/LEB128.h"
@@ -19,44 +21,17 @@
namespace llvm {
namespace dwarflinker_parallel {
-using UnitMessageHandlerTy = function_ref<void(
- const Twine &Error, StringRef Context, const DWARFDie *DIE)>;
-
-/// Each unit keeps output data as a file with debug tables
-/// corresponding to the concrete unit.
-using OutTablesFileTy = SmallString<0>;
+class DwarfUnit;
+using MacroOffset2UnitMapTy = DenseMap<uint64_t, DwarfUnit *>;
/// Base class for all Dwarf units(Compile unit/Type table unit).
class DwarfUnit : public OutputSections {
public:
virtual ~DwarfUnit() {}
- DwarfUnit(unsigned ID, StringRef ClangModuleName,
- UnitMessageHandlerTy WarningHandler)
- : ID(ID), ClangModuleName(ClangModuleName),
- WarningHandler(WarningHandler) {
- FormParams.Version = 4;
- FormParams.Format = dwarf::DWARF32;
- FormParams.AddrSize = 4;
- }
-
- /// Endiannes for the compile unit.
- support::endianness getEndianness() const { return Endianess; }
-
- /// Return DWARF version.
- uint16_t getVersion() const { return FormParams.Version; }
-
- /// Return size of header of debug_info table.
- uint16_t getHeaderSize() const { return FormParams.Version >= 5 ? 12 : 11; }
-
- /// Return size of address.
- uint8_t getAddressByteSize() const { return FormParams.AddrSize; }
-
- /// Return size of reference.
- uint8_t getRefAddrByteSize() const { return FormParams.getRefAddrByteSize(); }
-
- /// Return format of the Dwarf(DWARF32 or DWARF64).
- /// TODO: DWARF64 is not currently supported.
- dwarf::DwarfFormat getDwarfFormat() const { return FormParams.Format; }
+ DwarfUnit(LinkingGlobalData &GlobalData, unsigned ID,
+ StringRef ClangModuleName)
+ : OutputSections(GlobalData), ID(ID), ClangModuleName(ClangModuleName),
+ OutUnitDIE(nullptr) {}
/// Unique id of the unit.
unsigned getUniqueID() const { return ID; }
@@ -76,41 +51,48 @@ class DwarfUnit : public OutputSections {
/// Return the DW_AT_LLVM_sysroot of the compile unit or an empty StringRef.
StringRef getSysRoot() { return SysRoot; }
- /// Create a Die for this unit.
- void setOutputDIE(DIE *UnitDie) { NewUnit = UnitDie; }
-
- /// Return Die for this compile unit.
- DIE *getOutputUnitDIE() const { return NewUnit; }
-
/// Return true if this compile unit is from Clang module.
bool isClangModule() const { return !ClangModuleName.empty(); }
/// Return Clang module name;
const std::string &getClangModuleName() const { return ClangModuleName; }
- /// Returns generated file keeping debug tables for this compile unit.
- OutTablesFileTy &getOutDwarfBits() { return OutDebugInfoBits; }
+ /// Return global data.
+ LinkingGlobalData &getGlobalData() { return GlobalData; }
- /// Erases generated file keeping debug tables for this compile unit.
- void eraseDwarfBits() { OutDebugInfoBits = OutTablesFileTy(); }
+ /// Returns true if unit is inter-connected(it references/referenced by other
+ /// unit).
+ bool isInterconnectedCU() const { return IsInterconnectedCU; }
- MCSymbol *getLabelBegin() { return LabelBegin; }
- void setLabelBegin(MCSymbol *S) { LabelBegin = S; }
+ /// Mark this unit as inter-connected(it references/referenced by other unit).
+ void setInterconnectedCU() { IsInterconnectedCU = true; }
- /// Error reporting methods.
- /// @{
+ /// Adds \p Abbrev into unit`s abbreviation table.
+ void assignAbbrev(DIEAbbrev &Abbrev);
- void reportWarning(const Twine &Warning,
- const DWARFDie *Die = nullptr) const {
- if (WarningHandler)
- WarningHandler(Warning, getUnitName(), Die);
- }
- void reportWarning(Error Warning) const {
- handleAllErrors(std::move(Warning), [&](ErrorInfoBase &Info) {
- if (WarningHandler)
- WarningHandler(Info.message(), getUnitName(), nullptr);
- });
+ /// Returns abbreviations for this compile unit.
+ const std::vector<std::unique_ptr<DIEAbbrev>> &getAbbreviations() const {
+ return Abbreviations;
}
+
+ /// Returns output unit DIE.
+ DIE *getOutUnitDIE() { return OutUnitDIE; }
+
+ /// Set output unit DIE.
+ void setOutUnitDIE(DIE *UnitDie) { OutUnitDIE = UnitDie; }
+
+ /// \defgroup Methods used to emit unit's debug info:
+ ///
+ /// @{
+ /// Emit unit's abbreviations.
+ Error emitAbbreviations();
+
+ /// Emit .debug_info section for unit DIEs.
+ Error emitDebugInfo(Triple &TargetTriple);
+
+ /// Emit .debug_line section.
+ Error emitDebugLine(Triple &TargetTriple,
+ const DWARFDebugLine::LineTable &OutLineTable);
/// @}
/// This structure keeps fields which would be used for creating accelerator
@@ -142,16 +124,22 @@ class DwarfUnit : public OutputSections {
const DIE *Die = nullptr;
};
+ /// \defgroup Methods used for reporting warnings and errors:
+ ///
+ /// @{
+ void warn(const Twine &Warning) { GlobalData.warn(Warning, getUnitName()); }
+
+ void error(const Twine &Err) { GlobalData.warn(Err, getUnitName()); }
+ /// @}
+
protected:
+ /// Emit single abbreviation entry.
+ void emitDwarfAbbrevEntry(const DIEAbbrev &Abbrev,
+ SectionDescriptor &AbbrevSection);
+
/// Unique ID for the unit.
unsigned ID = 0;
- /// Properties of the unit.
- dwarf::FormParams FormParams;
-
- /// DIE for newly generated compile unit.
- DIE *NewUnit = nullptr;
-
/// The DW_AT_language of this unit.
uint16_t Language = 0;
@@ -166,18 +154,17 @@ class DwarfUnit : public OutputSections {
uint64_t UnitSize = 0;
- /// Elf file containg generated debug tables for this compile unit.
- OutTablesFileTy OutDebugInfoBits;
-
- /// Endiannes for this compile unit.
- support::endianness Endianess = support::endianness::little;
-
- MCSymbol *LabelBegin = nullptr;
-
/// true if current unit references_to/is_referenced by other unit.
std::atomic<bool> IsInterconnectedCU = {false};
- UnitMessageHandlerTy WarningHandler;
+ /// FoldingSet that uniques the abbreviations.
+ FoldingSet<DIEAbbrev> AbbreviationsSet;
+
+ /// Storage for the unique Abbreviations.
+ std::vector<std::unique_ptr<DIEAbbrev>> Abbreviations;
+
+ /// Output unit DIE.
+ DIE *OutUnitDIE = nullptr;
};
} // end of namespace dwarflinker_parallel
diff --git a/llvm/lib/DWARFLinkerParallel/DebugLineSectionEmitter.h b/llvm/lib/DWARFLinkerParallel/DebugLineSectionEmitter.h
new file mode 100644
index 00000000000000..f1fcd91753900d
--- /dev/null
+++ b/llvm/lib/DWARFLinkerParallel/DebugLineSectionEmitter.h
@@ -0,0 +1,384 @@
+//===- DebugLineSectionEmitter.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_DEBUGLINESECTIONEMITTER_H
+#define LLVM_LIB_DWARFLINKERPARALLEL_DEBUGLINESECTIONEMITTER_H
+
+#include "DWARFEmitterImpl.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/DWARFLinkerParallel/AddressesMap.h"
+#include "llvm/DWARFLinkerParallel/DWARFLinker.h"
+#include "llvm/DebugInfo/DWARF/DWARFObject.h"
+#include "llvm/MC/MCTargetOptionsCommandFlags.h"
+#include "llvm/MC/TargetRegistry.h"
+
+namespace llvm {
+namespace dwarflinker_parallel {
+
+/// This class emits specified line table into the .debug_line section.
+class DebugLineSectionEmitter {
+public:
+ DebugLineSectionEmitter(const Triple &TheTriple, DwarfUnit &U)
+ : TheTriple(TheTriple), U(U) {}
+
+ Error emit(const DWARFDebugLine::LineTable &LineTable) {
+ // FIXME: remove dependence on MCDwarfLineAddr::encode.
+ // As we reuse MCDwarfLineAddr::encode, we need to create/initialize
+ // some MC* classes.
+ if (Error Err = init(TheTriple))
+ return Err;
+
+ // Get descriptor for output .debug_line section.
+ SectionDescriptor &OutSection =
+ U.getOrCreateSectionDescriptor(DebugSectionKind::DebugLine);
+
+ // unit_length.
+ OutSection.emitUnitLength(0xBADDEF);
+ uint64_t OffsetAfterUnitLength = OutSection.OS.tell();
+
+ // Emit prologue.
+ emitLineTablePrologue(LineTable.Prologue, OutSection);
+
+ // Emit rows.
+ emitLineTableRows(LineTable, OutSection);
+ uint64_t OffsetAfterEnd = OutSection.OS.tell();
+
+ // Update unit length field with actual length value.
+ assert(OffsetAfterUnitLength -
+ OutSection.getFormParams().getDwarfOffsetByteSize() <
+ OffsetAfterUnitLength);
+ OutSection.apply(OffsetAfterUnitLength -
+ OutSection.getFormParams().getDwarfOffsetByteSize(),
+ dwarf::DW_FORM_sec_offset,
+ OffsetAfterEnd - OffsetAfterUnitLength);
+
+ return Error::success();
+ }
+
+private:
+ Error init(Triple TheTriple) {
+ std::string ErrorStr;
+ std::string TripleName;
+
+ // Get the target.
+ const Target *TheTarget =
+ TargetRegistry::lookupTarget(TripleName, TheTriple, ErrorStr);
+ if (!TheTarget)
+ return createStringError(std::errc::invalid_argument, ErrorStr.c_str());
+ TripleName = TheTriple.getTriple();
+
+ // Create all the MC Objects.
+ MRI.reset(TheTarget->createMCRegInfo(TripleName));
+ if (!MRI)
+ return createStringError(std::errc::invalid_argument,
+ "no register info for target %s",
+ TripleName.c_str());
+
+ MCTargetOptions MCOptions = mc::InitMCTargetOptionsFromFlags();
+ MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
+ if (!MAI)
+ return createStringError(std::errc::invalid_argument,
+ "no asm info for target %s", TripleName.c_str());
+
+ MSTI.reset(TheTarget->createMCSubtargetInfo(TripleName, "", ""));
+ if (!MSTI)
+ return createStringError(std::errc::invalid_argument,
+ "no subtarget info for target %s",
+ TripleName.c_str());
+
+ MC.reset(new MCContext(TheTriple, MAI.get(), MRI.get(), MSTI.get(), nullptr,
+ nullptr, true, "__DWARF"));
+
+ return Error::success();
+ }
+
+ void emitLineTablePrologue(const DWARFDebugLine::Prologue &P,
+ SectionDescriptor &Section) {
+ // version (uhalf).
+ Section.emitIntVal(P.getVersion(), 2);
+ if (P.getVersion() == 5) {
+ // address_size (ubyte).
+ Section.emitIntVal(P.getAddressSize(), 1);
+
+ // segment_selector_size (ubyte).
+ Section.emitIntVal(P.SegSelectorSize, 1);
+ }
+
+ // header_length.
+ Section.emitOffset(0xBADDEF);
+
+ uint64_t OffsetAfterPrologueLength = Section.OS.tell();
+ emitLineTableProloguePayload(P, Section);
+ uint64_t OffsetAfterPrologueEnd = Section.OS.tell();
+
+ // Update prologue length field with actual length value.
+ Section.apply(OffsetAfterPrologueLength -
+ Section.getFormParams().getDwarfOffsetByteSize(),
+ dwarf::DW_FORM_sec_offset,
+ OffsetAfterPrologueEnd - OffsetAfterPrologueLength);
+ }
+
+ void
+ emitLineTablePrologueV2IncludeAndFileTable(const DWARFDebugLine::Prologue &P,
+ SectionDescriptor &Section) {
+ // include_directories (sequence of path names).
+ for (const DWARFFormValue &Include : P.IncludeDirectories) {
+ std::optional<const char *> IncludeStr = dwarf::toString(Include);
+ if (!IncludeStr) {
+ U.warn("cann't read string from line table.");
+ return;
+ }
+
+ Section.emitString(Include.getForm(), *IncludeStr);
+ }
+ // The last entry is followed by a single null byte.
+ Section.emitIntVal(0, 1);
+
+ // file_names (sequence of file entries).
+ for (const DWARFDebugLine::FileNameEntry &File : P.FileNames) {
+ std::optional<const char *> FileNameStr = dwarf::toString(File.Name);
+ if (!FileNameStr) {
+ U.warn("cann't read string from line table.");
+ return;
+ }
+
+ // A null-terminated string containing the full or relative path name of a
+ // source file.
+ Section.emitString(File.Name.getForm(), *FileNameStr);
+ // An unsigned LEB128 number representing the directory index of a
+ // directory in the include_directories section.
+ encodeULEB128(File.DirIdx, Section.OS);
+ // An unsigned LEB128 number representing the (implementation-defined)
+ // time of last modification for the file, or 0 if not available.
+ encodeULEB128(File.ModTime, Section.OS);
+ // An unsigned LEB128 number representing the length in bytes of the file,
+ // or 0 if not available.
+ encodeULEB128(File.Length, Section.OS);
+ }
+ // The last entry is followed by a single null byte.
+ Section.emitIntVal(0, 1);
+ }
+
+ void
+ emitLineTablePrologueV5IncludeAndFileTable(const DWARFDebugLine::Prologue &P,
+ SectionDescriptor &Section) {
+ if (P.IncludeDirectories.empty()) {
+ // directory_entry_format_count(ubyte).
+ Section.emitIntVal(0, 1);
+ } else {
+ // directory_entry_format_count(ubyte).
+ Section.emitIntVal(1, 1);
+
+ // directory_entry_format (sequence of ULEB128 pairs).
+ encodeULEB128(dwarf::DW_LNCT_path, Section.OS);
+ encodeULEB128(P.IncludeDirectories[0].getForm(), Section.OS);
+ }
+
+ // directories_count (ULEB128).
+ encodeULEB128(P.IncludeDirectories.size(), Section.OS);
+ // directories (sequence of directory names).
+ for (auto Include : P.IncludeDirectories) {
+ std::optional<const char *> IncludeStr = dwarf::toString(Include);
+ if (!IncludeStr) {
+ U.warn("cann't read string from line table.");
+ return;
+ }
+
+ Section.emitString(Include.getForm(), *IncludeStr);
+ }
+
+ if (P.FileNames.empty()) {
+ // file_name_entry_format_count (ubyte).
+ Section.emitIntVal(0, 1);
+ } else {
+ // file_name_entry_format_count (ubyte).
+ Section.emitIntVal(2, 1);
+
+ // file_name_entry_format (sequence of ULEB128 pairs).
+ encodeULEB128(dwarf::DW_LNCT_path, Section.OS);
+ encodeULEB128(P.FileNames[0].Name.getForm(), Section.OS);
+
+ encodeULEB128(dwarf::DW_LNCT_directory_index, Section.OS);
+ encodeULEB128(dwarf::DW_FORM_data1, Section.OS);
+ }
+
+ // file_names_count (ULEB128).
+ encodeULEB128(P.FileNames.size(), Section.OS);
+
+ // file_names (sequence of file name entries).
+ for (auto File : P.FileNames) {
+ std::optional<const char *> FileNameStr = dwarf::toString(File.Name);
+ if (!FileNameStr) {
+ U.warn("cann't read string from line table.");
+ return;
+ }
+
+ // A null-terminated string containing the full or relative path name of a
+ // source file.
+ Section.emitString(File.Name.getForm(), *FileNameStr);
+ Section.emitIntVal(File.DirIdx, 1);
+ }
+ }
+
+ void emitLineTableProloguePayload(const DWARFDebugLine::Prologue &P,
+ SectionDescriptor &Section) {
+ // minimum_instruction_length (ubyte).
+ Section.emitIntVal(P.MinInstLength, 1);
+ if (P.FormParams.Version >= 4) {
+ // maximum_operations_per_instruction (ubyte).
+ Section.emitIntVal(P.MaxOpsPerInst, 1);
+ }
+ // default_is_stmt (ubyte).
+ Section.emitIntVal(P.DefaultIsStmt, 1);
+ // line_base (sbyte).
+ Section.emitIntVal(P.LineBase, 1);
+ // line_range (ubyte).
+ Section.emitIntVal(P.LineRange, 1);
+ // opcode_base (ubyte).
+ Section.emitIntVal(P.OpcodeBase, 1);
+
+ // standard_opcode_lengths (array of ubyte).
+ for (auto Length : P.StandardOpcodeLengths)
+ Section.emitIntVal(Length, 1);
+
+ if (P.FormParams.Version < 5)
+ emitLineTablePrologueV2IncludeAndFileTable(P, Section);
+ else
+ emitLineTablePrologueV5IncludeAndFileTable(P, Section);
+ }
+
+ void emitLineTableRows(const DWARFDebugLine::LineTable &LineTable,
+ SectionDescriptor &Section) {
+
+ MCDwarfLineTableParams Params;
+ Params.DWARF2LineOpcodeBase = LineTable.Prologue.OpcodeBase;
+ Params.DWARF2LineBase = LineTable.Prologue.LineBase;
+ Params.DWARF2LineRange = LineTable.Prologue.LineRange;
+
+ SmallString<128> EncodingBuffer;
+
+ if (LineTable.Rows.empty()) {
+ // We only have the dummy entry, dsymutil emits an entry with a 0
+ // address in that case.
+ MCDwarfLineAddr::encode(*MC, Params, std::numeric_limits<int64_t>::max(),
+ 0, EncodingBuffer);
+ Section.OS.write(EncodingBuffer.c_str(), EncodingBuffer.size());
+ return;
+ }
+
+ // Line table state machine fields
+ unsigned FileNum = 1;
+ unsigned LastLine = 1;
+ unsigned Column = 0;
+ unsigned IsStatement = 1;
+ unsigned Isa = 0;
+ uint64_t Address = -1ULL;
+
+ unsigned RowsSinceLastSequence = 0;
+
+ for (const DWARFDebugLine::Row &Row : LineTable.Rows) {
+ int64_t AddressDelta;
+ if (Address == -1ULL) {
+ Section.emitIntVal(dwarf::DW_LNS_extended_op, 1);
+ encodeULEB128(Section.getFormParams().AddrSize + 1, Section.OS);
+ Section.emitIntVal(dwarf::DW_LNE_set_address, 1);
+ Section.emitIntVal(Row.Address.Address,
+ Section.getFormParams().AddrSize);
+ AddressDelta = 0;
+ } else {
+ AddressDelta =
+ (Row.Address.Address - Address) / LineTable.Prologue.MinInstLength;
+ }
+
+ // FIXME: code copied and transformed from
+ // MCDwarf.cpp::EmitDwarfLineTable. We should find a way to share this
+ // code, but the current compatibility requirement with classic dsymutil
+ // makes it hard. Revisit that once this requirement is dropped.
+
+ if (FileNum != Row.File) {
+ FileNum = Row.File;
+ Section.emitIntVal(dwarf::DW_LNS_set_file, 1);
+ encodeULEB128(FileNum, Section.OS);
+ }
+ if (Column != Row.Column) {
+ Column = Row.Column;
+ Section.emitIntVal(dwarf::DW_LNS_set_column, 1);
+ encodeULEB128(Column, Section.OS);
+ }
+
+ // FIXME: We should handle the discriminator here, but dsymutil doesn't
+ // consider it, thus ignore it for now.
+
+ if (Isa != Row.Isa) {
+ Isa = Row.Isa;
+ Section.emitIntVal(dwarf::DW_LNS_set_isa, 1);
+ encodeULEB128(Isa, Section.OS);
+ }
+ if (IsStatement != Row.IsStmt) {
+ IsStatement = Row.IsStmt;
+ Section.emitIntVal(dwarf::DW_LNS_negate_stmt, 1);
+ }
+ if (Row.BasicBlock)
+ Section.emitIntVal(dwarf::DW_LNS_set_basic_block, 1);
+
+ if (Row.PrologueEnd)
+ Section.emitIntVal(dwarf::DW_LNS_set_prologue_end, 1);
+
+ if (Row.EpilogueBegin)
+ Section.emitIntVal(dwarf::DW_LNS_set_epilogue_begin, 1);
+
+ int64_t LineDelta = int64_t(Row.Line) - LastLine;
+ if (!Row.EndSequence) {
+ MCDwarfLineAddr::encode(*MC, Params, LineDelta, AddressDelta,
+ EncodingBuffer);
+ Section.OS.write(EncodingBuffer.c_str(), EncodingBuffer.size());
+ EncodingBuffer.resize(0);
+ Address = Row.Address.Address;
+ LastLine = Row.Line;
+ RowsSinceLastSequence++;
+ } else {
+ if (LineDelta) {
+ Section.emitIntVal(dwarf::DW_LNS_advance_line, 1);
+ encodeSLEB128(LineDelta, Section.OS);
+ }
+ if (AddressDelta) {
+ Section.emitIntVal(dwarf::DW_LNS_advance_pc, 1);
+ encodeULEB128(AddressDelta, Section.OS);
+ }
+ MCDwarfLineAddr::encode(*MC, Params,
+ std::numeric_limits<int64_t>::max(), 0,
+ EncodingBuffer);
+ Section.OS.write(EncodingBuffer.c_str(), EncodingBuffer.size());
+ EncodingBuffer.resize(0);
+ Address = -1ULL;
+ LastLine = FileNum = IsStatement = 1;
+ RowsSinceLastSequence = Column = Isa = 0;
+ }
+ }
+
+ if (RowsSinceLastSequence) {
+ MCDwarfLineAddr::encode(*MC, Params, std::numeric_limits<int64_t>::max(),
+ 0, EncodingBuffer);
+ Section.OS.write(EncodingBuffer.c_str(), EncodingBuffer.size());
+ EncodingBuffer.resize(0);
+ }
+ }
+
+ Triple TheTriple;
+ DwarfUnit &U;
+
+ std::unique_ptr<MCRegisterInfo> MRI;
+ std::unique_ptr<MCAsmInfo> MAI;
+ std::unique_ptr<MCContext> MC;
+ std::unique_ptr<MCSubtargetInfo> MSTI;
+};
+
+} // end of namespace dwarflinker_parallel
+} // end namespace llvm
+
+#endif // LLVM_LIB_DWARFLINKERPARALLEL_DEBUGLINESECTIONEMITTER_H
diff --git a/llvm/lib/DWARFLinkerParallel/DependencyTracker.cpp b/llvm/lib/DWARFLinkerParallel/DependencyTracker.cpp
new file mode 100644
index 00000000000000..4bf353afbe42b0
--- /dev/null
+++ b/llvm/lib/DWARFLinkerParallel/DependencyTracker.cpp
@@ -0,0 +1,428 @@
+//=== DependencyTracker.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 "DependencyTracker.h"
+#include "llvm/Support/FormatVariadic.h"
+
+namespace llvm {
+namespace dwarflinker_parallel {
+
+#ifndef NDEBUG
+/// A broken link in the keep chain. By recording both the parent and the child
+/// we can show only broken links for DIEs with multiple children.
+struct BrokenLink {
+ BrokenLink(DWARFDie Parent, DWARFDie Child) : Parent(Parent), Child(Child) {}
+ DWARFDie Parent;
+ DWARFDie Child;
+};
+
+/// Verify the keep chain by looking for DIEs that are kept but who's parent
+/// isn't.
+void DependencyTracker::verifyKeepChain(CompileUnit &CU) {
+ SmallVector<DWARFDie> Worklist;
+ Worklist.push_back(CU.getOrigUnit().getUnitDIE());
+
+ // List of broken links.
+ SmallVector<BrokenLink> BrokenLinks;
+
+ while (!Worklist.empty()) {
+ const DWARFDie Current = Worklist.back();
+ Worklist.pop_back();
+
+ if (!Current.isValid())
+ continue;
+
+ const bool CurrentDieIsKept = CU.getDIEInfo(Current).getKeep() ||
+ CU.getDIEInfo(Current).getKeepChildren();
+
+ for (DWARFDie Child : reverse(Current.children())) {
+ Worklist.push_back(Child);
+
+ const bool ChildDieIsKept = CU.getDIEInfo(Child).getKeep() ||
+ CU.getDIEInfo(Child).getKeepChildren();
+ if (!CurrentDieIsKept && ChildDieIsKept)
+ BrokenLinks.emplace_back(Current, Child);
+ }
+ }
+
+ if (!BrokenLinks.empty()) {
+ for (BrokenLink Link : BrokenLinks) {
+ WithColor::error() << formatv(
+ "Found invalid link in keep chain between {0:x} and {1:x}\n",
+ Link.Parent.getOffset(), Link.Child.getOffset());
+
+ errs() << "Parent:";
+ Link.Parent.dump(errs(), 0, {});
+ CU.getDIEInfo(Link.Parent).dump();
+
+ errs() << "Child:";
+ Link.Child.dump(errs(), 2, {});
+ CU.getDIEInfo(Link.Child).dump();
+ }
+ report_fatal_error("invalid keep chain");
+ }
+}
+#endif
+
+bool DependencyTracker::resolveDependenciesAndMarkLiveness(CompileUnit &CU) {
+ // We do not track liveness inside Clang modules. We also do not track
+ // liveness if UpdateIndexTablesOnly is requested.
+ TrackLiveness = !(CU.isClangModule() ||
+ CU.getGlobalData().getOptions().UpdateIndexTablesOnly);
+ RootEntriesWorkList.clear();
+
+ // Search for live root DIEs.
+ collectRootsToKeep(CU, CU.getDebugInfoEntry(0));
+
+ // Mark live DIEs as kept.
+ return markLiveRootsAsKept();
+}
+
+void DependencyTracker::collectRootsToKeep(CompileUnit &CU,
+ const DWARFDebugInfoEntry *Entry) {
+ if (!TrackLiveness) {
+ addItemToWorklist(CU, Entry);
+ return;
+ }
+
+ switch (Entry->getTag()) {
+ case dwarf::DW_TAG_subprogram:
+ case dwarf::DW_TAG_label:
+ if (isLiveSubprogramEntry(CU, Entry)) {
+ addItemToWorklist(CU, Entry);
+ break;
+ }
+ [[fallthrough]];
+ case dwarf::DW_TAG_compile_unit:
+ case dwarf::DW_TAG_namespace:
+ case dwarf::DW_TAG_module:
+ case dwarf::DW_TAG_lexical_block: {
+ for (const DWARFDebugInfoEntry *CurChild = CU.getFirstChildEntry(Entry);
+ CurChild && CurChild->getAbbreviationDeclarationPtr();
+ CurChild = CU.getSiblingEntry(CurChild))
+ collectRootsToKeep(CU, CurChild);
+ } break;
+ case dwarf::DW_TAG_constant:
+ case dwarf::DW_TAG_variable: {
+ if (isLiveVariableEntry(CU, Entry))
+ addItemToWorklist(CU, Entry);
+ } break;
+ case dwarf::DW_TAG_base_type: {
+ addItemToWorklist(CU, Entry);
+ } break;
+ case dwarf::DW_TAG_imported_module:
+ case dwarf::DW_TAG_imported_declaration:
+ case dwarf::DW_TAG_imported_unit: {
+ addItemToWorklist(CU, Entry);
+ } break;
+ default:
+ // Nothing to do.
+ break;
+ }
+}
+
+bool DependencyTracker::markLiveRootsAsKept() {
+ bool Res = true;
+
+ while (!RootEntriesWorkList.empty()) {
+ RootEntryTy CurrentItem = RootEntriesWorkList.pop_back_val();
+
+ if (!markDIEEntryAsKeptRec(CurrentItem, CurrentItem.CU,
+ CurrentItem.RootEntry))
+ Res = false;
+ }
+
+ return Res;
+}
+
+bool DependencyTracker::markDIEEntryAsKeptRec(
+ const RootEntryTy &RootItem, CompileUnit &CU,
+ const DWARFDebugInfoEntry *Entry) {
+ if (Entry->getAbbreviationDeclarationPtr() == nullptr)
+ return true;
+
+ CompileUnit::DIEInfo &Info = CU.getDIEInfo(Entry);
+
+ if (Info.getKeep())
+ return true;
+
+ // Mark parents as 'KeepChildren'.
+ std::optional<uint32_t> ParentIdx = Entry->getParentIdx();
+ while (ParentIdx) {
+ const DWARFDebugInfoEntry *ParentEntry = CU.getDebugInfoEntry(*ParentIdx);
+ CompileUnit::DIEInfo &ParentInfo = CU.getDIEInfo(*ParentIdx);
+ if (ParentInfo.getKeepChildren())
+ break;
+ ParentInfo.setKeepChildren();
+ ParentIdx = ParentEntry->getParentIdx();
+ }
+
+ // Mark current DIE as kept.
+ Info.setKeep();
+ setDIEPlacementAndTypename(Info);
+
+ // Set liveness information.
+ switch (Entry->getTag()) {
+ case dwarf::DW_TAG_constant:
+ case dwarf::DW_TAG_variable: {
+ isLiveVariableEntry(CU, Entry);
+ } break;
+ case dwarf::DW_TAG_subprogram:
+ case dwarf::DW_TAG_label: {
+ isLiveSubprogramEntry(CU, Entry);
+ } break;
+ default:
+ // Nothing to do.
+ break;
+ }
+
+ // Analyse referenced DIEs.
+ bool Res = true;
+ if (!maybeAddReferencedRoots(RootItem, CU, Entry))
+ Res = false;
+
+ // Navigate children.
+ for (const DWARFDebugInfoEntry *CurChild = CU.getFirstChildEntry(Entry);
+ CurChild && CurChild->getAbbreviationDeclarationPtr();
+ CurChild = CU.getSiblingEntry(CurChild)) {
+ if (!markDIEEntryAsKeptRec(RootItem, CU, CurChild))
+ Res = false;
+ }
+
+ return Res;
+}
+
+bool DependencyTracker::maybeAddReferencedRoots(
+ const RootEntryTy &RootItem, CompileUnit &CU,
+ const DWARFDebugInfoEntry *Entry) {
+ const auto *Abbrev = Entry->getAbbreviationDeclarationPtr();
+ if (Abbrev == nullptr)
+ return true;
+
+ DWARFUnit &Unit = CU.getOrigUnit();
+ DWARFDataExtractor Data = Unit.getDebugInfoExtractor();
+ uint64_t Offset = Entry->getOffset() + getULEB128Size(Abbrev->getCode());
+
+ // For each DIE attribute...
+ for (const auto &AttrSpec : Abbrev->attributes()) {
+ DWARFFormValue Val(AttrSpec.Form);
+ if (!Val.isFormClass(DWARFFormValue::FC_Reference) ||
+ AttrSpec.Attr == dwarf::DW_AT_sibling) {
+ DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset,
+ Unit.getFormParams());
+ continue;
+ }
+ Val.extractValue(Data, &Offset, Unit.getFormParams(), &Unit);
+
+ // Resolve reference.
+ std::optional<std::pair<CompileUnit *, uint32_t>> RefDie =
+ CU.resolveDIEReference(Val);
+ if (!RefDie) {
+ CU.warn("cann't find referenced DIE", Entry);
+ continue;
+ }
+
+ if (CU.getUniqueID() == RefDie->first->getUniqueID()) {
+ // Check if referenced DIE entry is already kept.
+ if (RefDie->first->getDIEInfo(RefDie->second).getKeep())
+ continue;
+
+ // If referenced DIE is inside current compilation unit.
+ const DWARFDebugInfoEntry *RefEntry =
+ RefDie->first->getDebugInfoEntry(RefDie->second);
+
+ if (RootItem.RootEntry->getTag() == dwarf::DW_TAG_compile_unit)
+ addItemToWorklist(*RefDie->first, RefEntry);
+ else {
+ uint64_t RootStartOffset = RootItem.RootEntry->getOffset();
+ uint64_t RootEndOffset;
+ if (std::optional<uint32_t> SiblingIdx =
+ RootItem.RootEntry->getSiblingIdx()) {
+ RootEndOffset =
+ RootItem.CU.getDebugInfoEntry(*SiblingIdx)->getOffset();
+ } else {
+ RootEndOffset = RootItem.CU.getOrigUnit().getNextUnitOffset();
+ }
+
+ // Do not put item in work list if it is an ancestor of RootItem.
+ // (since we will visit and mark it as kept during normal traversing of
+ // RootItem children)
+ if (RootStartOffset > RefEntry->getOffset() ||
+ RefEntry->getOffset() >= RootEndOffset)
+ addItemToWorklist(*RefDie->first, RefEntry);
+ }
+ } else if (Context.InterCUProcessingStarted && RefDie->second != 0) {
+ // If referenced DIE is in other compilation unit and
+ // it is safe to navigate other units DIEs.
+ addItemToWorklist(*RefDie->first,
+ RefDie->first->getDebugInfoEntry(RefDie->second));
+ } else {
+ // Delay resolving reference.
+ RefDie->first->setInterconnectedCU();
+ CU.setInterconnectedCU();
+ Context.HasNewInterconnectedCUs = true;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Returns true if the specified DIE type allows removing children.
+static bool childrenCanBeRemoved(uint32_t Tag) {
+ switch (Tag) {
+ default:
+ return true;
+ case dwarf::DW_TAG_class_type:
+ case dwarf::DW_TAG_common_block:
+ case dwarf::DW_TAG_lexical_block:
+ case dwarf::DW_TAG_structure_type:
+ case dwarf::DW_TAG_subprogram:
+ case dwarf::DW_TAG_subroutine_type:
+ case dwarf::DW_TAG_union_type:
+ case dwarf::DW_TAG_array_type:
+ return false;
+ }
+ llvm_unreachable("Invalid Tag");
+}
+
+void DependencyTracker::addItemToWorklist(CompileUnit &CU,
+ const DWARFDebugInfoEntry *Entry) {
+ if (Entry->getAbbreviationDeclarationPtr() == nullptr)
+ return;
+
+ const DWARFDebugInfoEntry *EntryToAdd = Entry;
+
+ // If parent does not allow children removing then use that parent as a root
+ // DIE.
+ std::optional<uint32_t> ParentIdx = Entry->getParentIdx();
+ while (ParentIdx) {
+ const DWARFDebugInfoEntry *ParentEntry = CU.getDebugInfoEntry(*ParentIdx);
+ if (childrenCanBeRemoved(ParentEntry->getTag()))
+ break;
+ EntryToAdd = ParentEntry;
+ ParentIdx = ParentEntry->getParentIdx();
+ }
+
+ // Check if the DIE entry is already kept.
+ if (CU.getDIEInfo(EntryToAdd).getKeep())
+ return;
+
+ RootEntriesWorkList.emplace_back(CU, EntryToAdd);
+}
+
+bool DependencyTracker::isLiveVariableEntry(CompileUnit &CU,
+ const DWARFDebugInfoEntry *Entry) {
+ DWARFDie DIE = CU.getDIE(Entry);
+ CompileUnit::DIEInfo &Info = CU.getDIEInfo(DIE);
+
+ if (TrackLiveness) {
+ const auto *Abbrev = DIE.getAbbreviationDeclarationPtr();
+
+ // Global variables with constant value can always be kept.
+ if (!Info.getIsInFunctionScope() &&
+ Abbrev->findAttributeIndex(dwarf::DW_AT_const_value))
+ return true;
+
+ // See if there is a relocation to a valid debug map entry inside this
+ // variable's location. The order is important here. We want to always check
+ // if the variable has a location expression address.
+ // However, we don't want a static variable in a function to force us to
+ // keep the enclosing function, unless requested explicitly.
+ std::pair<bool, std::optional<int64_t>> LocExprAddrAndRelocAdjustment =
+ CU.getContaingFile().Addresses->getVariableRelocAdjustment(DIE);
+
+ if (!LocExprAddrAndRelocAdjustment.second)
+ return false;
+
+ if ((Info.getIsInFunctionScope()) &&
+ !LLVM_UNLIKELY(CU.getGlobalData().getOptions().KeepFunctionForStatic))
+ return false;
+ }
+
+ if (CU.getGlobalData().getOptions().Verbose) {
+ outs() << "Keeping variable DIE:";
+ DIDumpOptions DumpOpts;
+ DumpOpts.ChildRecurseDepth = 0;
+ DumpOpts.Verbose = CU.getGlobalData().getOptions().Verbose;
+ DIE.dump(outs(), 8 /* Indent */, DumpOpts);
+ }
+
+ return true;
+}
+
+bool DependencyTracker::isLiveSubprogramEntry(
+ CompileUnit &CU, const DWARFDebugInfoEntry *Entry) {
+ DWARFDie DIE = CU.getDIE(Entry);
+
+ std::optional<uint64_t> LowPc;
+ std::optional<uint64_t> HighPc;
+ std::optional<int64_t> RelocAdjustment;
+
+ if (TrackLiveness) {
+ LowPc = dwarf::toAddress(DIE.find(dwarf::DW_AT_low_pc));
+ if (!LowPc)
+ return false;
+
+ RelocAdjustment =
+ CU.getContaingFile().Addresses->getSubprogramRelocAdjustment(DIE);
+ if (!RelocAdjustment)
+ return false;
+
+ if (DIE.getTag() == dwarf::DW_TAG_subprogram) {
+ // Validate subprogram address range.
+
+ HighPc = DIE.getHighPC(*LowPc);
+ if (!HighPc) {
+ CU.warn("function without high_pc. Range will be discarded.", &DIE);
+ return false;
+ }
+
+ if (*LowPc > *HighPc) {
+ CU.warn("low_pc greater than high_pc. Range will be discarded.", &DIE);
+ return false;
+ }
+ } else if (DIE.getTag() == dwarf::DW_TAG_variable) {
+ if (CU.hasLabelAt(*LowPc))
+ return false;
+
+ // FIXME: dsymutil-classic compat. dsymutil-classic doesn't consider
+ // labels that don't fall into the CU's aranges. This is wrong IMO. Debug
+ // info generation bugs aside, this is really wrong in the case of labels,
+ // where a label marking the end of a function will have a PC == CU's
+ // high_pc.
+ if (dwarf::toAddress(
+ CU.getOrigUnit().getUnitDIE().find(dwarf::DW_AT_high_pc))
+ .value_or(UINT64_MAX) <= LowPc)
+ return false;
+
+ CU.addLabelLowPc(*LowPc, *RelocAdjustment);
+ }
+ }
+
+ if (CU.getGlobalData().getOptions().Verbose) {
+ outs() << "Keeping subprogram DIE:";
+ DIDumpOptions DumpOpts;
+ DumpOpts.ChildRecurseDepth = 0;
+ DumpOpts.Verbose = CU.getGlobalData().getOptions().Verbose;
+ DIE.dump(outs(), 8 /* Indent */, DumpOpts);
+ }
+
+ if (!TrackLiveness || DIE.getTag() == dwarf::DW_TAG_label)
+ return true;
+
+ CU.addFunctionRange(*LowPc, *HighPc, *RelocAdjustment);
+ return true;
+}
+
+void DependencyTracker::setDIEPlacementAndTypename(CompileUnit::DIEInfo &Info) {
+ Info.setPlacement(CompileUnit::PlainDwarf);
+}
+
+} // end of namespace dwarflinker_parallel
+} // namespace llvm
diff --git a/llvm/lib/DWARFLinkerParallel/DependencyTracker.h b/llvm/lib/DWARFLinkerParallel/DependencyTracker.h
new file mode 100644
index 00000000000000..69e57bc3ea4d26
--- /dev/null
+++ b/llvm/lib/DWARFLinkerParallel/DependencyTracker.h
@@ -0,0 +1,102 @@
+//===- "DependencyTracker.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_DEPENDENCYTRACKER_H
+#define LLVM_LIB_DWARFLINKERPARALLEL_DEPENDENCYTRACKER_H
+
+#include "DWARFLinkerCompileUnit.h"
+#include "DWARFLinkerImpl.h"
+#include "llvm/ADT/SmallVector.h"
+
+namespace llvm {
+class DWARFDebugInfoEntry;
+class DWARFDie;
+
+namespace dwarflinker_parallel {
+
+/// This class discovers DIEs dependencies and marks "live" DIEs.
+class DependencyTracker {
+public:
+ DependencyTracker(DWARFLinkerImpl::LinkContext &Context) : Context(Context) {}
+
+ /// Recursively walk the \p DIE tree and look for DIEs to keep. Store that
+ /// information in \p CU's DIEInfo.
+ ///
+ /// This function is the entry point of the DIE selection algorithm. It is
+ /// expected to walk the DIE tree and(through the mediation of
+ /// Context.File.Addresses) ask for relocation adjustment value on each
+ /// DIE that might be a 'root DIE'.
+ ///
+ /// Returns true if all dependencies are correctly discovered. Inter-CU
+ /// dependencies cannot be discovered if referenced CU is not analyzed yet.
+ /// If that is the case this method returns false.
+ bool resolveDependenciesAndMarkLiveness(CompileUnit &CU);
+
+ /// Recursively walk the \p DIE tree and check "keepness" information.
+ /// It is an error if parent node does not have "keep" flag, while
+ /// child have one. This function dump error at stderr in that case.
+#ifndef NDEBUG
+ static void verifyKeepChain(CompileUnit &CU);
+#endif
+
+protected:
+ struct RootEntryTy {
+ RootEntryTy(CompileUnit &CU, const DWARFDebugInfoEntry *RootEntry)
+ : CU(CU), RootEntry(RootEntry) {}
+
+ // Compile unit keeping root entry.
+ CompileUnit &CU;
+
+ // Root entry.
+ const DWARFDebugInfoEntry *RootEntry;
+ };
+
+ using RootEntriesListTy = SmallVector<RootEntryTy>;
+
+ /// This function navigates DIEs tree starting from specified \p Entry.
+ /// It puts 'root DIE' into the worklist.
+ void collectRootsToKeep(CompileUnit &CU, const DWARFDebugInfoEntry *Entry);
+
+ /// Returns true if specified variable references live code section.
+ bool isLiveVariableEntry(CompileUnit &CU, const DWARFDebugInfoEntry *Entry);
+
+ /// Returns true if specified subprogram references live code section.
+ bool isLiveSubprogramEntry(CompileUnit &CU, const DWARFDebugInfoEntry *Entry);
+
+ /// Examine worklist and mark all 'root DIE's as kept.
+ bool markLiveRootsAsKept();
+
+ /// Mark whole DIE tree as kept recursively.
+ bool markDIEEntryAsKeptRec(const RootEntryTy &RootItem, CompileUnit &CU,
+ const DWARFDebugInfoEntry *Entry);
+
+ /// Check referenced DIEs and add them into the worklist if neccessary.
+ bool maybeAddReferencedRoots(const RootEntryTy &RootItem, CompileUnit &CU,
+ const DWARFDebugInfoEntry *Entry);
+
+ /// Add 'root DIE' into the worklist.
+ void addItemToWorklist(CompileUnit &CU, const DWARFDebugInfoEntry *Entry);
+
+ /// Set kind of placement(whether it goes into type table, plain dwarf or
+ /// both) for the specified die \p DieIdx.
+ void setDIEPlacementAndTypename(CompileUnit::DIEInfo &Info);
+
+ /// Flag indicating whether liveness information should be examined.
+ bool TrackLiveness = false;
+
+ /// List of CU, Entry pairs which are 'root DIE's.
+ RootEntriesListTy RootEntriesWorkList;
+
+ /// Link context for the analyzed CU.
+ DWARFLinkerImpl::LinkContext &Context;
+};
+
+} // end namespace dwarflinker_parallel
+} // end namespace llvm
+
+#endif // LLVM_LIB_DWARFLINKERPARALLEL_DEPENDENCYTRACKER_H
diff --git a/llvm/lib/DWARFLinkerParallel/IndexedValuesMap.h b/llvm/lib/DWARFLinkerParallel/IndexedValuesMap.h
new file mode 100644
index 00000000000000..0dc8de860a42e8
--- /dev/null
+++ b/llvm/lib/DWARFLinkerParallel/IndexedValuesMap.h
@@ -0,0 +1,49 @@
+//===- IndexedValuesMap.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_INDEXEDVALUESMAP_H
+#define LLVM_LIB_DWARFLINKERPARALLEL_INDEXEDVALUESMAP_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
+#include <cstdint>
+#include <utility>
+
+namespace llvm {
+namespace dwarflinker_parallel {
+
+template <typename T> class IndexedValuesMap {
+public:
+ uint64_t getValueIndex(T Value) {
+ typename ValueToIndexMapTy::iterator It = ValueToIndexMap.find(Value);
+ if (It == ValueToIndexMap.end()) {
+ It = ValueToIndexMap.insert(std::make_pair(Value, Values.size())).first;
+ Values.push_back(Value);
+ }
+ return It->second;
+ }
+
+ const SmallVector<T> &getValues() { return Values; }
+
+ void clear() {
+ ValueToIndexMap.clear();
+ Values.clear();
+ }
+
+ bool empty() { return Values.empty(); }
+
+protected:
+ using ValueToIndexMapTy = DenseMap<T, uint64_t>;
+ ValueToIndexMapTy ValueToIndexMap;
+ SmallVector<T> Values;
+};
+
+} // end of namespace dwarflinker_parallel
+} // end namespace llvm
+
+#endif // LLVM_LIB_DWARFLINKERPARALLEL_INDEXEDVALUESMAP_H
diff --git a/llvm/lib/DWARFLinkerParallel/OutputSections.cpp b/llvm/lib/DWARFLinkerParallel/OutputSections.cpp
index 69c5bfaa7bdf84..e623f4a54c5181 100644
--- a/llvm/lib/DWARFLinkerParallel/OutputSections.cpp
+++ b/llvm/lib/DWARFLinkerParallel/OutputSections.cpp
@@ -7,30 +7,393 @@
//===----------------------------------------------------------------------===//
#include "OutputSections.h"
+#include "DWARFLinkerCompileUnit.h"
#include "llvm/ADT/StringSwitch.h"
namespace llvm {
namespace dwarflinker_parallel {
-std::optional<OutputSections::DebugSectionKind>
-OutputSections::parseDebugSectionName(llvm::StringRef SecName) {
- return llvm::StringSwitch<std::optional<OutputSections::DebugSectionKind>>(
- SecName)
- .Case("debug_info", DebugSectionKind::DebugInfo)
- .Case("debug_line", DebugSectionKind::DebugLine)
- .Case("debug_frame", DebugSectionKind::DebugFrame)
- .Case("debug_ranges", DebugSectionKind::DebugRange)
- .Case("debug_rnglists", DebugSectionKind::DebugRngLists)
- .Case("debug_loc", DebugSectionKind::DebugLoc)
- .Case("debug_loclists", DebugSectionKind::DebugLocLists)
- .Case("debug_aranges", DebugSectionKind::DebugARanges)
- .Case("debug_abbrev", DebugSectionKind::DebugAbbrev)
- .Case("debug_macinfo", DebugSectionKind::DebugMacinfo)
- .Case("debug_macro", DebugSectionKind::DebugMacro)
+static constexpr StringLiteral SectionNames[SectionKindsNum] = {
+ "debug_info", "debug_line", "debug_frame", "debug_ranges",
+ "debug_rnglists", "debug_loc", "debug_loclists", "debug_aranges",
+ "debug_abbrev", "debug_macinfo", "debug_macro", "debug_addr",
+ "debug_str", "debug_line_str", "debug_str_offsets"};
+
+const StringLiteral &getSectionName(DebugSectionKind SectionKind) {
+ return SectionNames[static_cast<uint8_t>(SectionKind)];
+}
+
+std::optional<DebugSectionKind> parseDebugTableName(llvm::StringRef SecName) {
+ return llvm::StringSwitch<std::optional<DebugSectionKind>>(
+ SecName.substr(SecName.find_first_not_of("._")))
+ .Case(getSectionName(DebugSectionKind::DebugInfo),
+ DebugSectionKind::DebugInfo)
+ .Case(getSectionName(DebugSectionKind::DebugLine),
+ DebugSectionKind::DebugLine)
+ .Case(getSectionName(DebugSectionKind::DebugFrame),
+ DebugSectionKind::DebugFrame)
+ .Case(getSectionName(DebugSectionKind::DebugRange),
+ DebugSectionKind::DebugRange)
+ .Case(getSectionName(DebugSectionKind::DebugRngLists),
+ DebugSectionKind::DebugRngLists)
+ .Case(getSectionName(DebugSectionKind::DebugLoc),
+ DebugSectionKind::DebugLoc)
+ .Case(getSectionName(DebugSectionKind::DebugLocLists),
+ DebugSectionKind::DebugLocLists)
+ .Case(getSectionName(DebugSectionKind::DebugARanges),
+ DebugSectionKind::DebugARanges)
+ .Case(getSectionName(DebugSectionKind::DebugAbbrev),
+ DebugSectionKind::DebugAbbrev)
+ .Case(getSectionName(DebugSectionKind::DebugMacinfo),
+ DebugSectionKind::DebugMacinfo)
+ .Case(getSectionName(DebugSectionKind::DebugMacro),
+ DebugSectionKind::DebugMacro)
+ .Case(getSectionName(DebugSectionKind::DebugAddr),
+ DebugSectionKind::DebugAddr)
+ .Case(getSectionName(DebugSectionKind::DebugStr),
+ DebugSectionKind::DebugStr)
+ .Case(getSectionName(DebugSectionKind::DebugLineStr),
+ DebugSectionKind::DebugLineStr)
+ .Case(getSectionName(DebugSectionKind::DebugStrOffsets),
+ DebugSectionKind::DebugStrOffsets)
.Default(std::nullopt);
return std::nullopt;
}
+DebugDieRefPatch::DebugDieRefPatch(uint64_t PatchOffset, CompileUnit *SrcCU,
+ CompileUnit *RefCU, uint32_t RefIdx)
+ : SectionPatch({PatchOffset}),
+ RefCU(RefCU, (SrcCU != nullptr) &&
+ (SrcCU->getUniqueID() == RefCU->getUniqueID())),
+ RefDieIdxOrClonedOffset(RefIdx) {}
+
+DebugULEB128DieRefPatch::DebugULEB128DieRefPatch(uint64_t PatchOffset,
+ CompileUnit *SrcCU,
+ CompileUnit *RefCU,
+ uint32_t RefIdx)
+ : SectionPatch({PatchOffset}),
+ RefCU(RefCU, SrcCU->getUniqueID() == RefCU->getUniqueID()),
+ RefDieIdxOrClonedOffset(RefIdx) {}
+
+void SectionDescriptor::erase() {
+ StartOffset = 0;
+ Contents = OutSectionDataTy();
+ ListDebugStrPatch.erase();
+ ListDebugLineStrPatch.erase();
+ ListDebugRangePatch.erase();
+ ListDebugLocPatch.erase();
+ ListDebugDieRefPatch.erase();
+ ListDebugULEB128DieRefPatch.erase();
+ ListDebugOffsetPatch.erase();
+}
+
+void SectionDescriptor::setSizesForSectionCreatedByAsmPrinter() {
+ if (Contents.empty())
+ return;
+
+ MemoryBufferRef Mem(Contents, "obj");
+ Expected<std::unique_ptr<object::ObjectFile>> Obj =
+ object::ObjectFile::createObjectFile(Mem);
+ if (!Obj) {
+ consumeError(Obj.takeError());
+ Contents.clear();
+ return;
+ }
+
+ for (const object::SectionRef &Sect : (*Obj).get()->sections()) {
+ Expected<StringRef> SectNameOrErr = Sect.getName();
+ if (!SectNameOrErr) {
+ consumeError(SectNameOrErr.takeError());
+ continue;
+ }
+
+ if (std::optional<DebugSectionKind> SectKind =
+ parseDebugTableName(*SectNameOrErr)) {
+ if (*SectKind == SectionKind) {
+ Expected<StringRef> Data = Sect.getContents();
+ if (!Data) {
+ consumeError(SectNameOrErr.takeError());
+ Contents.clear();
+ return;
+ }
+
+ SectionOffsetInsideAsmPrinterOutputStart =
+ Data->data() - Contents.data();
+ SectionOffsetInsideAsmPrinterOutputEnd =
+ SectionOffsetInsideAsmPrinterOutputStart + Data->size();
+ }
+ }
+ }
+
+ return;
+}
+
+void SectionDescriptor::emitIntVal(uint64_t Val, unsigned Size) {
+ switch (Size) {
+ case 1: {
+ uint8_t ShortVal = static_cast<uint8_t>(Val);
+ OS.write(reinterpret_cast<const char *>(&ShortVal), Size);
+ } break;
+ case 2: {
+ uint16_t ShortVal = static_cast<uint16_t>(Val);
+ ShortVal = support::endian::byte_swap(ShortVal, getEndianess());
+ OS.write(reinterpret_cast<const char *>(&ShortVal), Size);
+ } break;
+ case 4: {
+ uint32_t ShortVal = static_cast<uint32_t>(Val);
+ ShortVal = support::endian::byte_swap(ShortVal, getEndianess());
+ OS.write(reinterpret_cast<const char *>(&ShortVal), Size);
+ } break;
+ case 8: {
+ uint64_t ShortVal = static_cast<uint64_t>(Val);
+ ShortVal = support::endian::byte_swap(ShortVal, getEndianess());
+ OS.write(reinterpret_cast<const char *>(&ShortVal), Size);
+ } break;
+ default:
+ llvm_unreachable("Unsupported integer type size");
+ }
+}
+
+void SectionDescriptor::emitString(dwarf::Form StringForm,
+ const char *StringVal) {
+ assert(StringVal != nullptr);
+
+ switch (StringForm) {
+ case dwarf::DW_FORM_string: {
+ emitInplaceString(GlobalData.translateString(StringVal));
+ } break;
+ case dwarf::DW_FORM_strp: {
+ notePatch(DebugStrPatch{
+ {OS.tell()}, GlobalData.getStringPool().insert(StringVal).first});
+ emitStringPlaceholder();
+ } break;
+ case dwarf::DW_FORM_line_strp: {
+ notePatch(DebugLineStrPatch{
+ {OS.tell()}, GlobalData.getStringPool().insert(StringVal).first});
+ emitStringPlaceholder();
+ } break;
+ default:
+ llvm_unreachable("Unsupported string form");
+ break;
+ };
+}
+
+void SectionDescriptor::apply(uint64_t PatchOffset, dwarf::Form AttrForm,
+ uint64_t Val) {
+ switch (AttrForm) {
+ case dwarf::DW_FORM_strp:
+ case dwarf::DW_FORM_line_strp: {
+ applyIntVal(PatchOffset, Val, Format.getDwarfOffsetByteSize());
+ } break;
+
+ case dwarf::DW_FORM_ref_addr: {
+ applyIntVal(PatchOffset, Val, Format.getRefAddrByteSize());
+ } break;
+ case dwarf::DW_FORM_ref1: {
+ applyIntVal(PatchOffset, Val, 1);
+ } break;
+ case dwarf::DW_FORM_ref2: {
+ applyIntVal(PatchOffset, Val, 2);
+ } break;
+ case dwarf::DW_FORM_ref4: {
+ applyIntVal(PatchOffset, Val, 4);
+ } break;
+ case dwarf::DW_FORM_ref8: {
+ applyIntVal(PatchOffset, Val, 8);
+ } break;
+
+ case dwarf::DW_FORM_data1: {
+ applyIntVal(PatchOffset, Val, 1);
+ } break;
+ case dwarf::DW_FORM_data2: {
+ applyIntVal(PatchOffset, Val, 2);
+ } break;
+ case dwarf::DW_FORM_data4: {
+ applyIntVal(PatchOffset, Val, 4);
+ } break;
+ case dwarf::DW_FORM_data8: {
+ applyIntVal(PatchOffset, Val, 8);
+ } break;
+ case dwarf::DW_FORM_udata: {
+ applyULEB128(PatchOffset, Val);
+ } break;
+ case dwarf::DW_FORM_sdata: {
+ applySLEB128(PatchOffset, Val);
+ } break;
+ case dwarf::DW_FORM_sec_offset: {
+ applyIntVal(PatchOffset, Val, Format.getDwarfOffsetByteSize());
+ } break;
+ case dwarf::DW_FORM_flag: {
+ applyIntVal(PatchOffset, Val, 1);
+ } break;
+
+ default:
+ llvm_unreachable("Unsupported attribute form");
+ break;
+ }
+}
+
+uint64_t SectionDescriptor::getIntVal(uint64_t PatchOffset, unsigned Size) {
+ assert(PatchOffset < getContents().size());
+ switch (Size) {
+ case 1: {
+ return support::endian::read<uint8_t>(
+ const_cast<char *>(getContents().data() + PatchOffset), Endianess);
+ }
+ case 2: {
+ return support::endian::read<uint16_t>(
+ const_cast<char *>(getContents().data() + PatchOffset), Endianess);
+ }
+ case 4: {
+ return support::endian::read<uint32_t>(
+ const_cast<char *>(getContents().data() + PatchOffset), Endianess);
+ }
+ case 8: {
+ return support::endian::read<uint64_t>(
+ const_cast<char *>(getContents().data() + PatchOffset), Endianess);
+ }
+ }
+ llvm_unreachable("Unsupported integer type size");
+ return 0;
+}
+
+void SectionDescriptor::applyIntVal(uint64_t PatchOffset, uint64_t Val,
+ unsigned Size) {
+ assert(PatchOffset < getContents().size());
+
+ switch (Size) {
+ case 1: {
+ support::endian::write(
+ const_cast<char *>(getContents().data() + PatchOffset),
+ static_cast<uint8_t>(Val), Endianess);
+ } break;
+ case 2: {
+ support::endian::write(
+ const_cast<char *>(getContents().data() + PatchOffset),
+ static_cast<uint16_t>(Val), Endianess);
+ } break;
+ case 4: {
+ support::endian::write(
+ const_cast<char *>(getContents().data() + PatchOffset),
+ static_cast<uint32_t>(Val), Endianess);
+ } break;
+ case 8: {
+ support::endian::write(
+ const_cast<char *>(getContents().data() + PatchOffset),
+ static_cast<uint64_t>(Val), Endianess);
+ } break;
+ default:
+ llvm_unreachable("Unsupported integer type size");
+ }
+}
+
+void SectionDescriptor::applyULEB128(uint64_t PatchOffset, uint64_t Val) {
+ assert(PatchOffset < getContents().size());
+
+ uint8_t ULEB[16];
+ uint8_t DestSize = Format.getDwarfOffsetByteSize() + 1;
+ uint8_t RealSize = encodeULEB128(Val, ULEB, DestSize);
+
+ memcpy(const_cast<char *>(getContents().data() + PatchOffset), ULEB,
+ RealSize);
+}
+
+/// Writes integer value \p Val of SLEB128 format by specified \p PatchOffset.
+void SectionDescriptor::applySLEB128(uint64_t PatchOffset, uint64_t Val) {
+ assert(PatchOffset < getContents().size());
+
+ uint8_t SLEB[16];
+ uint8_t DestSize = Format.getDwarfOffsetByteSize() + 1;
+ uint8_t RealSize = encodeSLEB128(Val, SLEB, DestSize);
+
+ memcpy(const_cast<char *>(getContents().data() + PatchOffset), SLEB,
+ RealSize);
+}
+
+void OutputSections::applyPatches(
+ SectionDescriptor &Section,
+ StringEntryToDwarfStringPoolEntryMap &DebugStrStrings,
+ StringEntryToDwarfStringPoolEntryMap &DebugLineStrStrings) {
+
+ Section.ListDebugStrPatch.forEach([&](DebugStrPatch &Patch) {
+ DwarfStringPoolEntryWithExtString *Entry =
+ DebugStrStrings.getExistingEntry(Patch.String);
+ assert(Entry != nullptr);
+
+ Section.apply(Patch.PatchOffset, dwarf::DW_FORM_strp, Entry->Offset);
+ });
+
+ Section.ListDebugLineStrPatch.forEach([&](DebugLineStrPatch &Patch) {
+ DwarfStringPoolEntryWithExtString *Entry =
+ DebugLineStrStrings.getExistingEntry(Patch.String);
+ assert(Entry != nullptr);
+
+ Section.apply(Patch.PatchOffset, dwarf::DW_FORM_line_strp, Entry->Offset);
+ });
+
+ std::optional<SectionDescriptor *> RangeSection;
+ if (Format.Version >= 5)
+ RangeSection = getSectionDescriptor(DebugSectionKind::DebugRngLists);
+ else
+ RangeSection = getSectionDescriptor(DebugSectionKind::DebugRange);
+
+ if (RangeSection) {
+ Section.ListDebugRangePatch.forEach([&](DebugRangePatch &Patch) {
+ uint64_t FinalValue =
+ Section.getIntVal(Patch.PatchOffset, Format.getDwarfOffsetByteSize());
+ FinalValue += (*RangeSection)->StartOffset;
+
+ Section.apply(Patch.PatchOffset, dwarf::DW_FORM_sec_offset, FinalValue);
+ });
+ }
+
+ std::optional<SectionDescriptor *> LocationSection;
+ if (Format.Version >= 5)
+ LocationSection = getSectionDescriptor(DebugSectionKind::DebugLocLists);
+ else
+ LocationSection = getSectionDescriptor(DebugSectionKind::DebugLoc);
+
+ if (LocationSection) {
+ Section.ListDebugLocPatch.forEach([&](DebugLocPatch &Patch) {
+ uint64_t FinalValue =
+ Section.getIntVal(Patch.PatchOffset, Format.getDwarfOffsetByteSize());
+ FinalValue += (*LocationSection)->StartOffset;
+
+ Section.apply(Patch.PatchOffset, dwarf::DW_FORM_sec_offset, FinalValue);
+ });
+ }
+
+ Section.ListDebugDieRefPatch.forEach([&](DebugDieRefPatch &Patch) {
+ uint64_t FinalOffset = Patch.RefDieIdxOrClonedOffset;
+ dwarf::Form FinalForm = dwarf::DW_FORM_ref4;
+
+ if (!Patch.RefCU.getInt()) {
+ FinalForm = dwarf::DW_FORM_ref_addr;
+ FinalOffset +=
+ Patch.RefCU.getPointer()
+ ->getOrCreateSectionDescriptor(DebugSectionKind::DebugInfo)
+ .StartOffset;
+ }
+ Section.apply(Patch.PatchOffset, FinalForm, FinalOffset);
+ });
+
+ Section.ListDebugULEB128DieRefPatch.forEach(
+ [&](DebugULEB128DieRefPatch &Patch) {
+ assert(Patch.RefCU.getInt());
+ Section.apply(Patch.PatchOffset, dwarf::DW_FORM_udata,
+ Patch.RefDieIdxOrClonedOffset);
+ });
+
+ Section.ListDebugOffsetPatch.forEach([&](DebugOffsetPatch &Patch) {
+ uint64_t FinalValue = Patch.SectionPtr.getPointer()->StartOffset;
+ if (Patch.SectionPtr.getInt())
+ FinalValue +=
+ Section.getIntVal(Patch.PatchOffset, Format.getDwarfOffsetByteSize());
+
+ Section.apply(Patch.PatchOffset, dwarf::DW_FORM_sec_offset, FinalValue);
+ });
+}
+
} // end of namespace dwarflinker_parallel
} // end of namespace llvm
diff --git a/llvm/lib/DWARFLinkerParallel/OutputSections.h b/llvm/lib/DWARFLinkerParallel/OutputSections.h
index 15ab4cc1167a3c..00e00645a89e93 100644
--- a/llvm/lib/DWARFLinkerParallel/OutputSections.h
+++ b/llvm/lib/DWARFLinkerParallel/OutputSections.h
@@ -9,56 +9,397 @@
#ifndef LLVM_LIB_DWARFLINKERPARALLEL_OUTPUTSECTIONS_H
#define LLVM_LIB_DWARFLINKERPARALLEL_OUTPUTSECTIONS_H
+#include "ArrayList.h"
+#include "StringEntryToDwarfStringPoolEntryMap.h"
+#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/CodeGen/DwarfStringPoolEntry.h"
+#include "llvm/DWARFLinkerParallel/StringPool.h"
+#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
+#include "llvm/DebugInfo/DWARF/DWARFObject.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/LEB128.h"
+#include "llvm/Support/MemoryBufferRef.h"
+#include "llvm/Support/raw_ostream.h"
#include <array>
#include <cstdint>
namespace llvm {
namespace dwarflinker_parallel {
-/// This class keeps offsets to the debug sections. Any object which is
-/// supposed to be emitted into the debug section should use this class to
-/// track debug sections offsets.
-class OutputSections {
-public:
- /// List of tracked debug sections.
- enum class DebugSectionKind : uint8_t {
- DebugInfo = 0,
- DebugLine,
- DebugFrame,
- DebugRange,
- DebugRngLists,
- DebugLoc,
- DebugLocLists,
- DebugARanges,
- DebugAbbrev,
- DebugMacinfo,
- DebugMacro,
- };
- constexpr static size_t SectionKindsNum = 11;
-
- /// Recognise the section name and match it with the DebugSectionKind.
- static std::optional<DebugSectionKind> parseDebugSectionName(StringRef Name);
+/// List of tracked debug tables.
+enum class DebugSectionKind : uint8_t {
+ DebugInfo = 0,
+ DebugLine,
+ DebugFrame,
+ DebugRange,
+ DebugRngLists,
+ DebugLoc,
+ DebugLocLists,
+ DebugARanges,
+ DebugAbbrev,
+ DebugMacinfo,
+ DebugMacro,
+ DebugAddr,
+ DebugStr,
+ DebugLineStr,
+ DebugStrOffsets,
+ NumberOfEnumEntries // must be last
+};
+constexpr static size_t SectionKindsNum =
+ static_cast<size_t>(DebugSectionKind::NumberOfEnumEntries);
+
+/// Recognise the table name and match it with the DebugSectionKind.
+std::optional<DebugSectionKind> parseDebugTableName(StringRef Name);
+
+/// Return the name of the section.
+const StringLiteral &getSectionName(DebugSectionKind SectionKind);
+
+/// There are fields(sizes, offsets) which should be updated after
+/// sections are generated. To remember offsets and related data
+/// the descendants of SectionPatch structure should be used.
+
+struct SectionPatch {
+ uint64_t PatchOffset = 0;
+};
+
+/// This structure is used to update strings offsets into .debug_str.
+struct DebugStrPatch : SectionPatch {
+ const StringEntry *String = nullptr;
+};
+
+/// This structure is used to update strings offsets into .debug_line_str.
+struct DebugLineStrPatch : SectionPatch {
+ const StringEntry *String = nullptr;
+};
+
+/// This structure is used to update range list offset into
+/// .debug_ranges/.debug_rnglists.
+struct DebugRangePatch : SectionPatch {
+ /// Indicates patch which points to immediate compile unit's attribute.
+ bool IsCompileUnitRanges = false;
+};
+
+/// This structure is used to update location list offset into
+/// .debug_loc/.debug_loclists.
+struct DebugLocPatch : SectionPatch {
+ int64_t AddrAdjustmentValue = 0;
+};
+
+/// This structure is used to update offset with start of another section.
+struct SectionDescriptor;
+struct DebugOffsetPatch : SectionPatch {
+ DebugOffsetPatch(uint64_t PatchOffset, SectionDescriptor *SectionPtr,
+ bool AddLocalValue = false)
+ : SectionPatch({PatchOffset}), SectionPtr(SectionPtr, AddLocalValue) {}
+
+ PointerIntPair<SectionDescriptor *, 1> SectionPtr;
+};
+
+/// This structure is used to update reference to the DIE.
+struct DebugDieRefPatch : SectionPatch {
+ DebugDieRefPatch(uint64_t PatchOffset, CompileUnit *SrcCU, CompileUnit *RefCU,
+ uint32_t RefIdx);
+
+ PointerIntPair<CompileUnit *, 1> RefCU;
+ uint64_t RefDieIdxOrClonedOffset;
+};
+
+/// This structure is used to update reference to the DIE of ULEB128 form.
+struct DebugULEB128DieRefPatch : SectionPatch {
+ DebugULEB128DieRefPatch(uint64_t PatchOffset, CompileUnit *SrcCU,
+ CompileUnit *RefCU, uint32_t RefIdx);
+
+ PointerIntPair<CompileUnit *, 1> RefCU;
+ uint64_t RefDieIdxOrClonedOffset;
+};
+
+/// Type for section data.
+using OutSectionDataTy = SmallString<0>;
+
+/// Type for list of pointers to patches offsets.
+using OffsetsPtrVector = SmallVector<uint64_t *>;
+
+class OutputSections;
+
+/// This structure is used to keep data of the concrete section.
+/// Like data bits, list of patches, format.
+struct SectionDescriptor {
+ friend OutputSections;
+
+ SectionDescriptor(DebugSectionKind SectionKind, LinkingGlobalData &GlobalData,
+ dwarf::FormParams Format, support::endianness Endianess)
+ : OS(Contents), GlobalData(GlobalData), SectionKind(SectionKind),
+ Format(Format), Endianess(Endianess) {
+ ListDebugStrPatch.setAllocator(&GlobalData.getAllocator());
+ ListDebugLineStrPatch.setAllocator(&GlobalData.getAllocator());
+ ListDebugRangePatch.setAllocator(&GlobalData.getAllocator());
+ ListDebugLocPatch.setAllocator(&GlobalData.getAllocator());
+ ListDebugDieRefPatch.setAllocator(&GlobalData.getAllocator());
+ ListDebugULEB128DieRefPatch.setAllocator(&GlobalData.getAllocator());
+ ListDebugOffsetPatch.setAllocator(&GlobalData.getAllocator());
+ }
+
+ /// Erase whole section contents(data bits, list of patches, format).
+ void erase();
/// When objects(f.e. compile units) are glued into the single file,
/// the debug sections corresponding to the concrete object are assigned
- /// with offsets inside the whole file. This method returns offset
- /// to the \p SectionKind debug section, corresponding to this object.
- uint64_t getStartOffset(DebugSectionKind SectionKind) const {
- return Offsets[static_cast<
- typename std::underlying_type<DebugSectionKind>::type>(SectionKind)];
+ /// with offsets inside the whole file. This field keeps offset
+ /// to the debug section, corresponding to this object.
+ uint64_t StartOffset;
+
+ /// Stream which stores data to the Contents.
+ raw_svector_ostream OS;
+
+ /// Section patches.
+#define ADD_PATCHES_LIST(T) \
+ T ¬ePatch(const T &Patch) { return List##T.noteItem(Patch); } \
+ ArrayList<T> List##T;
+
+ ADD_PATCHES_LIST(DebugStrPatch)
+ ADD_PATCHES_LIST(DebugLineStrPatch)
+ ADD_PATCHES_LIST(DebugRangePatch)
+ ADD_PATCHES_LIST(DebugLocPatch)
+ ADD_PATCHES_LIST(DebugDieRefPatch)
+ ADD_PATCHES_LIST(DebugULEB128DieRefPatch)
+ ADD_PATCHES_LIST(DebugOffsetPatch)
+
+ /// Offsets to some fields are not known at the moment of noting patch.
+ /// In that case we remember pointers to patch offset to update them later.
+ template <typename T>
+ void notePatchWithOffsetUpdate(const T &Patch,
+ OffsetsPtrVector &PatchesOffsetsList) {
+ PatchesOffsetsList.emplace_back(¬ePatch(Patch).PatchOffset);
+ }
+
+ /// Some sections are emitted using AsmPrinter. In that case "Contents"
+ /// member of SectionDescriptor contains elf file. This method searches
+ /// for section data inside elf file and remember offset to it.
+ void setSizesForSectionCreatedByAsmPrinter();
+
+ /// Returns section content.
+ StringRef getContents() {
+ if (SectionOffsetInsideAsmPrinterOutputStart == 0)
+ return StringRef(Contents.data(), Contents.size());
+
+ return Contents.slice(SectionOffsetInsideAsmPrinterOutputStart,
+ SectionOffsetInsideAsmPrinterOutputEnd);
+ }
+
+ /// Emit unit length into the current section contents.
+ void emitUnitLength(uint64_t Length) {
+ maybeEmitDwarf64Mark();
+ emitIntVal(Length, getFormParams().getDwarfOffsetByteSize());
+ }
+
+ /// Emit DWARF64 mark into the current section contents.
+ void maybeEmitDwarf64Mark() {
+ if (getFormParams().Format != dwarf::DWARF64)
+ return;
+ emitIntVal(dwarf::DW_LENGTH_DWARF64, 4);
+ }
+
+ /// Emit specified offset value into the current section contents.
+ void emitOffset(uint64_t Val) {
+ emitIntVal(Val, getFormParams().getDwarfOffsetByteSize());
+ }
+
+ /// Emit specified integer value into the current section contents.
+ void emitIntVal(uint64_t Val, unsigned Size);
+
+ /// Emit specified string value into the current section contents.
+ void emitString(dwarf::Form StringForm, const char *StringVal);
+
+ /// Emit specified inplace string value into the current section contents.
+ void emitInplaceString(StringRef String) {
+ OS << String;
+ emitIntVal(0, 1);
+ }
+
+ /// Emit string placeholder into the current section contents.
+ void emitStringPlaceholder() {
+ // emit bad offset which should be updated later.
+ emitOffset(0xBADDEF);
}
- /// Set offset to the start of specified \p SectionKind debug section,
- /// corresponding to this object.
- void setStartOffset(DebugSectionKind SectionKind, uint64_t Offset) {
- Offsets[static_cast<typename std::underlying_type<DebugSectionKind>::type>(
- SectionKind)] = Offset;
+ /// Write specified \p Value of \p AttrForm to the \p PatchOffset.
+ void apply(uint64_t PatchOffset, dwarf::Form AttrForm, uint64_t Val);
+
+ /// Returns section kind.
+ DebugSectionKind getKind() { return SectionKind; }
+
+ /// Returns section name.
+ const StringLiteral &getName() const { return getSectionName(SectionKind); }
+
+ /// Returns endianess used by section.
+ support::endianness getEndianess() const { return Endianess; };
+
+ /// Returns FormParams used by section.
+ dwarf::FormParams getFormParams() const { return Format; }
+
+ /// Returns integer value of \p Size located by specified \p PatchOffset.
+ uint64_t getIntVal(uint64_t PatchOffset, unsigned Size);
+
+protected:
+ /// Writes integer value \p Val of \p Size by specified \p PatchOffset.
+ void applyIntVal(uint64_t PatchOffset, uint64_t Val, unsigned Size);
+
+ /// Writes integer value \p Val of ULEB128 format by specified \p PatchOffset.
+ void applyULEB128(uint64_t PatchOffset, uint64_t Val);
+
+ /// Writes integer value \p Val of SLEB128 format by specified \p PatchOffset.
+ void applySLEB128(uint64_t PatchOffset, uint64_t Val);
+
+ /// Sets output format.
+ void setOutputFormat(dwarf::FormParams Format,
+ support::endianness Endianess) {
+ this->Format = Format;
+ this->Endianess = Endianess;
+ }
+
+ LinkingGlobalData &GlobalData;
+
+ /// The section kind.
+ DebugSectionKind SectionKind;
+
+ /// Section data bits.
+ OutSectionDataTy Contents;
+
+ /// Some sections are generated using AsmPrinter. The real section data
+ /// located inside elf file in that case. Following fields points to the
+ /// real section content inside elf file.
+ size_t SectionOffsetInsideAsmPrinterOutputStart = 0;
+ size_t SectionOffsetInsideAsmPrinterOutputEnd = 0;
+
+ /// Output format.
+ dwarf::FormParams Format = {4, 4, dwarf::DWARF32};
+ support::endianness Endianess = support::endianness::little;
+};
+
+/// This class keeps contents and offsets to the debug sections. Any objects
+/// which is supposed to be emitted into the debug sections should use this
+/// class to track debug sections offsets and keep sections data.
+class OutputSections {
+public:
+ OutputSections(LinkingGlobalData &GlobalData) : GlobalData(GlobalData) {}
+
+ /// Sets output format for all keeping sections.
+ void setOutputFormat(DWARFUnit &OriginalUnit) {
+ setOutputFormat(OriginalUnit.getFormParams(),
+ OriginalUnit.isLittleEndian() ? support::endianness::little
+ : support::endianness::big);
+ }
+
+ /// Sets output format for all keeping sections.
+ void setOutputFormat(dwarf::FormParams Format,
+ support::endianness Endianess) {
+ this->Format = Format;
+ this->Endianess = Endianess;
+ }
+
+ /// Returns descriptor for the specified section of \p SectionKind.
+ std::optional<const SectionDescriptor *>
+ getSectionDescriptor(DebugSectionKind SectionKind) const {
+ SectionsSetTy::const_iterator It = SectionDescriptors.find(SectionKind);
+
+ if (It == SectionDescriptors.end())
+ return std::nullopt;
+
+ return &It->second;
+ }
+
+ /// Returns descriptor for the specified section of \p SectionKind.
+ std::optional<SectionDescriptor *>
+ getSectionDescriptor(DebugSectionKind SectionKind) {
+ SectionsSetTy::iterator It = SectionDescriptors.find(SectionKind);
+
+ if (It == SectionDescriptors.end())
+ return std::nullopt;
+
+ return &It->second;
+ }
+
+ /// Returns descriptor for the specified section of \p SectionKind.
+ /// If descriptor does not exist then create it.
+ SectionDescriptor &
+ getOrCreateSectionDescriptor(DebugSectionKind SectionKind) {
+ return SectionDescriptors
+ .try_emplace(SectionKind, SectionKind, GlobalData, Format, Endianess)
+ .first->second;
+ }
+
+ /// Erases data of all sections.
+ void eraseSections() {
+ for (auto &Section : SectionDescriptors)
+ Section.second.erase();
+ }
+
+ /// Enumerate all sections and call \p Handler for each.
+ void forEach(function_ref<void(SectionDescriptor &)> Handler) {
+ for (auto &Section : SectionDescriptors)
+ Handler(Section.second);
+ }
+
+ /// Enumerate all sections, for each section set current offset
+ /// (kept by \p SectionSizesAccumulator), update current offset with section
+ /// length.
+ void assignSectionsOffsetAndAccumulateSize(
+ std::array<uint64_t, SectionKindsNum> &SectionSizesAccumulator) {
+ for (auto &Section : SectionDescriptors) {
+ Section.second.StartOffset = SectionSizesAccumulator[static_cast<uint8_t>(
+ Section.second.getKind())];
+ SectionSizesAccumulator[static_cast<uint8_t>(Section.second.getKind())] +=
+ Section.second.getContents().size();
+ }
}
+ /// Enumerate all sections, for each section apply all section patches.
+ void applyPatches(SectionDescriptor &Section,
+ StringEntryToDwarfStringPoolEntryMap &DebugStrStrings,
+ StringEntryToDwarfStringPoolEntryMap &DebugLineStrStrings);
+
+ /// Endiannes for the sections.
+ support::endianness getEndianness() const { return Endianess; }
+
+ /// Return DWARF version.
+ uint16_t getVersion() const { return Format.Version; }
+
+ /// Return size of header of debug_info table.
+ uint16_t getDebugInfoHeaderSize() const {
+ return Format.Version >= 5 ? 12 : 11;
+ }
+
+ /// Return size of header of debug_ table.
+ uint16_t getDebugAddrHeaderSize() const {
+ assert(Format.Version >= 5);
+ return Format.Format == dwarf::DwarfFormat::DWARF32 ? 8 : 16;
+ }
+
+ /// Return size of header of debug_str_offsets table.
+ uint16_t getDebugStrOffsetsHeaderSize() const {
+ assert(Format.Version >= 5);
+ return Format.Format == dwarf::DwarfFormat::DWARF32 ? 8 : 16;
+ }
+
+ /// Return size of address.
+ const dwarf::FormParams &getFormParams() const { return Format; }
+
protected:
- /// Offsets to the debug sections composing this object.
- std::array<uint64_t, SectionKindsNum> Offsets = {0};
+ LinkingGlobalData &GlobalData;
+
+ /// Format for sections.
+ dwarf::FormParams Format = {4, 4, dwarf::DWARF32};
+
+ /// Endiannes for sections.
+ support::endianness Endianess = support::endianness::little;
+
+ /// All keeping sections.
+ using SectionsSetTy = std::map<DebugSectionKind, SectionDescriptor>;
+ SectionsSetTy SectionDescriptors;
};
} // end of namespace dwarflinker_parallel
diff --git a/llvm/lib/DWARFLinkerParallel/StringEntryToDwarfStringPoolEntryMap.h b/llvm/lib/DWARFLinkerParallel/StringEntryToDwarfStringPoolEntryMap.h
new file mode 100644
index 00000000000000..b4c74d0adba970
--- /dev/null
+++ b/llvm/lib/DWARFLinkerParallel/StringEntryToDwarfStringPoolEntryMap.h
@@ -0,0 +1,72 @@
+//===- StringEntryToDwarfStringPoolEntryMap.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_STRINGENTRYTODWARFSTRINGPOOLENTRYMAP_H
+#define LLVM_LIB_DWARFLINKERPARALLEL_STRINGENTRYTODWARFSTRINGPOOLENTRYMAP_H
+
+#include "DWARFLinkerGlobalData.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/DWARFLinkerParallel/StringPool.h"
+
+namespace llvm {
+namespace dwarflinker_parallel {
+
+/// This class creates a DwarfStringPoolEntry for the corresponding StringEntry.
+class StringEntryToDwarfStringPoolEntryMap {
+public:
+ StringEntryToDwarfStringPoolEntryMap(LinkingGlobalData &GlobalData)
+ : GlobalData(GlobalData) {}
+ ~StringEntryToDwarfStringPoolEntryMap() {}
+
+ /// Create DwarfStringPoolEntry for specified StringEntry if necessary.
+ /// Initialize DwarfStringPoolEntry with initial values.
+ DwarfStringPoolEntryWithExtString *add(const StringEntry *String) {
+ DwarfStringPoolEntriesTy::iterator it = DwarfStringPoolEntries.find(String);
+
+ if (it == DwarfStringPoolEntries.end()) {
+ DwarfStringPoolEntryWithExtString *DataPtr =
+ GlobalData.getAllocator()
+ .Allocate<DwarfStringPoolEntryWithExtString>();
+ DataPtr->String = GlobalData.translateString(String->getKey());
+ DataPtr->Index = DwarfStringPoolEntry::NotIndexed;
+ DataPtr->Offset = 0;
+ DataPtr->Symbol = nullptr;
+ it = DwarfStringPoolEntries.insert(std::make_pair(String, DataPtr)).first;
+ }
+
+ assert(it->second != nullptr);
+ return it->second;
+ }
+
+ /// Returns already existed DwarfStringPoolEntry for the specified
+ /// StringEntry.
+ DwarfStringPoolEntryWithExtString *
+ getExistingEntry(const StringEntry *String) const {
+ DwarfStringPoolEntriesTy::const_iterator it =
+ DwarfStringPoolEntries.find(String);
+
+ assert(it != DwarfStringPoolEntries.end());
+ assert(it->second != nullptr);
+ return it->second;
+ }
+
+ /// Erase contents of StringsForEmission.
+ void clear() { DwarfStringPoolEntries.clear(); }
+
+protected:
+ using DwarfStringPoolEntriesTy =
+ DenseMap<const StringEntry *, DwarfStringPoolEntryWithExtString *>;
+ DwarfStringPoolEntriesTy DwarfStringPoolEntries;
+
+ LinkingGlobalData &GlobalData;
+};
+
+} // end of namespace dwarflinker_parallel
+} // end namespace llvm
+
+#endif // LLVM_LIB_DWARFLINKERPARALLEL_STRINGENTRYTODWARFSTRINGPOOLENTRYMAP_H
diff --git a/llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/dwarf5-dwarf4-combination-macho.test b/llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/dwarf5-dwarf4-combination-macho.test
new file mode 100644
index 00000000000000..96febc4fb1b870
--- /dev/null
+++ b/llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/dwarf5-dwarf4-combination-macho.test
@@ -0,0 +1,200 @@
+; This test checks to ensure that if a DWARF v5 and DWARF v4 object file is used to
+; generate a dsym, dsymutil correctly outputs the debug information, by keeping
+; the DWARF v5 and DWARF v4 debug info distinct, and that all the section headers
+; have the correct format.
+
+; 1.o was produced with the source file:
+
+; a.cpp
+; __attribute__((section("1,__text_foo"))) void foo() {}
+;
+; int foo2(int a) {
+; return a+5;
+; }
+; int main () {
+; return 1;
+; }
+
+; clang -g -c -O1 a.cpp -Xclang -gdwarf-5 -o 1.o
+
+; 2.o was produced with the following source file:
+
+; b.cpp
+; __attribute__((section("1,__text_foo2"))) void foo2() {}
+;
+; int bar(int x) {
+; int y = x + 2;
+; return y;
+; }
+
+; clang -g -c -O1 b.cpp -gdwarf-4 -o 2.o
+
+
+RUN: rm -rf %t.dir && mkdir -p %t.dir
+RUN: dsymutil --linker llvm -y %p/../dummy-debug-map-amr64.map \
+RUN: -oso-prepend-path=%p/../../Inputs/DWARF5-DWARF4-combination \
+RUN: -o %t.dir/dwarf5-dwarf4-combination-macho.dSYM
+RUN: llvm-dwarfdump %t.dir/dwarf5-dwarf4-combination-macho.dSYM -a --verbose | FileCheck %s
+
+
+CHECK:.debug_abbrev contents:
+CHECK-NEXT: Abbrev table for offset: 0x00000000
+
+CHECK: .debug_info contents:
+CHECK: 0x00000000: Compile Unit: length = 0x0000004a, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x0000, addr_size = 0x08
+CHECK: DW_AT_producer [DW_FORM_strx] (indexed (00000000) string = "Apple clang version 14.0.3 (clang-1403.0.22.14.1)")
+CHECK: DW_AT_name [DW_FORM_strx] (indexed (00000001) string = "a.cpp")
+CHECK: DW_AT_LLVM_sysroot [DW_FORM_strx] (indexed (00000002) string = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk")
+CHECK: DW_AT_APPLE_sdk [DW_FORM_strx] (indexed (00000003) string = "MacOSX.sdk")
+CHECK: DW_AT_str_offsets_base [DW_FORM_sec_offset] (0x00000008)
+CHECK: DW_AT_comp_dir [DW_FORM_strx] (indexed (00000004) string = "/Users/shubham/Development/test109275485")
+CHECK: DW_AT_ranges [DW_FORM_sec_offset] (0x[[RANGELIST_OFFSET:[0-9a-f]+]]
+CHECK-NEXT: [0x[[RANGELIST_OFFSET_START:[0-9a-f]+]], 0x[[RANGELIST_OFFSET_END:[0-9a-f]+]]))
+CHECK-NEXT: DW_AT_addr_base [DW_FORM_sec_offset] (0x00000008)
+CHECK: 0x0000002c: DW_TAG_subprogram [2] * (0x0000000c)
+CHECK-NEXT: DW_AT_low_pc [DW_FORM_addrx] (indexed (00000000) address = 0x[[#%.16x,LOCLIST_LOWPC:]])
+CHECK: DW_AT_linkage_name [DW_FORM_strx] (indexed (00000005) string = "_Z4foo2i")
+CHECK: DW_AT_name [DW_FORM_strx] (indexed (00000006) string = "foo2")
+CHECK: 0x0000003c: DW_TAG_formal_parameter [3] (0x0000002c)
+CHECK-NEXT: DW_AT_location [DW_FORM_sec_offset] (0x[[LOCLIST_OFFSET:[0-9a-f]+]]:
+CHECK-NEXT: [0x[[#%.16x,LOCLIST_PAIR_START:]], 0x[[#%.16x,LOCLIST_PAIR_END:]]): [[LOCLIST_EXPR:.*]]
+CHECK-NEXT: [0x[[#%.16x,LOCLIST_PAIR_START2:]], 0x[[#%.16x,LOCLIST_PAIR_END2:]]): [[LOCLIST_EXPR2:.*]])
+CHECK: DW_AT_name [DW_FORM_strx] (indexed (00000007) string = "a")
+
+CHECK: 0x0000004e: Compile Unit: length = 0x00000072, format = DWARF32, version = 0x0004, abbr_offset = 0x005a, addr_size = 0x08
+CHECK: DW_AT_producer [DW_FORM_strp] ( .debug_str[0x00000001] = "Apple clang version 14.0.3 (clang-1403.0.22.14.1)")
+CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x000000e0] = "b.cpp")
+CHECK: DW_AT_LLVM_sysroot [DW_FORM_strp] ( .debug_str[0x00000039] = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk")
+CHECK: DW_AT_APPLE_sdk [DW_FORM_strp] ( .debug_str[0x00000098] = "MacOSX.sdk")
+CHECK-NOT: DW_AT_str_offsets_base
+CHECK: DW_AT_comp_dir [DW_FORM_strp] ( .debug_str[0x000000a3] = "/Users/shubham/Development/test109275485")
+CHECK: DW_AT_low_pc [DW_FORM_addr] (0x[[#%.16x,RANGE_LOWPC:]])
+CHECK-NEXT: DW_AT_ranges [DW_FORM_sec_offset] (0x00000000
+CHECK-NEXT: [0x[[#%.16x,RANGE_START:]], 0x[[#%.16x,RANGE_END:]]))
+CHECK: 0x00000080: DW_TAG_subprogram {{.*}} * (0x00000059)
+CHECK-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x[[#%.16x,LOC_LOWPC:]])
+CHECK: DW_AT_linkage_name [DW_FORM_strp] ( .debug_str[0x000000e6] = "_Z3bari")
+CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x000000ee] = "bar")
+CHECK: 0x0000009d: DW_TAG_formal_parameter {{.*}} (0x00000080)
+CHECK-NEXT: DW_AT_location [DW_FORM_sec_offset] (0x[[LOC_OFFSET:[0-9a-f]+]]:
+CHECK-NEXT: [0x[[#%.16x,LOC_PAIR_START:]], 0x[[#%.16x,LOC_PAIR_END:]]): [[LOC_EXPR:.*]]
+CHECK-NEXT: [0x[[#%.16x,LOC_PAIR_START2:]], 0x[[#%.16x,LOC_PAIR_END2:]]): [[LOC_EXPR2:.*]])
+CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x000000f2] = "x")
+
+CHECK: .debug_loc contents:
+CHECK-NEXT: 0x[[LOC_OFFSET]]:
+CHECK-NEXT: (0x[[#sub(LOC_PAIR_START,LOC_LOWPC)]], 0x[[#sub(LOC_PAIR_END,LOC_LOWPC)]]): [[LOC_EXPR:.*]]
+CHECK-NEXT: (0x[[#sub(LOC_PAIR_START2,LOC_LOWPC)]], 0x[[#sub(LOC_PAIR_END2,LOC_LOWPC)]]): [[LOC_EXPR2:.*]]
+
+CHECK: .debug_loclists contents:
+CHECK-NEXT: 0x00000000: locations list header: length = 0x00000018, format = DWARF32, version = 0x0005, addr_size = 0x08, seg_size = 0x00, offset_entry_count = 0x00000000
+CHECK-NEXT: 0x[[LOCLIST_OFFSET]]:
+CHECK-NEXT: DW_LLE_base_addressx (0x0000000000000000)
+CHECK-NEXT: DW_LLE_offset_pair (0x[[#sub(LOCLIST_PAIR_START,LOCLIST_LOWPC)]], 0x[[#sub(LOCLIST_PAIR_END,LOCLIST_LOWPC)]])
+CHECK-NEXT: DW_LLE_offset_pair (0x[[#sub(LOCLIST_PAIR_START2,LOCLIST_LOWPC)]], 0x[[#sub(LOCLIST_PAIR_END2,LOCLIST_LOWPC)]])
+CHECK-NEXT: DW_LLE_end_of_list ()
+
+CHECK: .debug_line contents:
+CHECK-NEXT: debug_line[0x00000000]
+CHECK-NEXT: Line table prologue:
+CHECK-NEXT: total_length: 0x00000048
+CHECK-NEXT: format: DWARF32
+CHECK-NEXT: version: 5
+CHECK-NEXT: address_size: 8
+CHECK-NEXT: seg_select_size: 0
+CHECK-NEXT: prologue_length: 0x00000025
+CHECK-NEXT: min_inst_length: 1
+CHECK-NEXT: max_ops_per_inst: 1
+CHECK-NEXT: default_is_stmt: 1
+CHECK-NEXT: line_base: -5
+CHECK-NEXT: line_range: 14
+CHECK-NEXT: opcode_base: 13
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_copy] = 0
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_advance_pc] = 1
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_advance_line] = 1
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_set_file] = 1
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_set_column] = 1
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_negate_stmt] = 0
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_set_basic_block] = 0
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_const_add_pc] = 0
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_fixed_advance_pc] = 1
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_set_prologue_end] = 0
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_set_epilogue_begin] = 0
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_set_isa] = 1
+CHECK-NEXT: include_directories[ 0] = .debug_line_str[0x00000000] = "/Users/shubham/Development/test109275485"
+CHECK-NEXT: file_names[ 0]:
+CHECK-NEXT: name: .debug_line_str[0x00000029] = "a.cpp"
+CHECK-NEXT: dir_index: 0
+
+CHECK: debug_line[0x0000004c]
+CHECK-NEXT: Line table prologue:
+CHECK-NEXT: total_length: 0x0000003b
+CHECK-NEXT: format: DWARF32
+CHECK-NEXT: version: 4
+CHECK-NEXT: prologue_length: 0x0000001d
+CHECK-NEXT: min_inst_length: 1
+CHECK-NEXT: max_ops_per_inst: 1
+CHECK-NEXT: default_is_stmt: 1
+CHECK-NEXT: line_base: -5
+CHECK-NEXT: line_range: 14
+CHECK-NEXT: opcode_base: 13
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_copy] = 0
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_advance_pc] = 1
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_advance_line] = 1
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_set_file] = 1
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_set_column] = 1
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_negate_stmt] = 0
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_set_basic_block] = 0
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_const_add_pc] = 0
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_fixed_advance_pc] = 1
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_set_prologue_end] = 0
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_set_epilogue_begin] = 0
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_set_isa] = 1
+CHECK-NEXT: file_names[ 1]:
+CHECK-NEXT: name: "b.cpp"
+CHECK-NEXT: dir_index: 0
+CHECK-NEXT: mod_time: 0x00000000
+CHECK-NEXT: length: 0x00000000
+
+CHECK: .debug_str contents:
+CHECK-NEXT: 0x00000000: ""
+CHECK-NEXT: 0x00000001: "Apple clang version 14.0.3 (clang-1403.0.22.14.1)"
+CHECK-NEXT: 0x00000033: "a.cpp"
+CHECK-NEXT: 0x00000039: "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk"
+CHECK-NEXT: 0x00000098: "MacOSX.sdk"
+CHECK-NEXT: 0x000000a3: "/Users/shubham/Development/test109275485"
+CHECK-NEXT: 0x000000cc: "_Z4foo2i"
+CHECK-NEXT: 0x000000d5: "foo2"
+CHECK-NEXT: 0x000000da: "a"
+CHECK-NEXT: 0x000000dc: "int"
+CHECK-NEXT: 0x000000e0: "b.cpp"
+CHECK-NEXT: 0x000000e6: "_Z3bari"
+CHECK-NEXT: 0x000000ee: "bar"
+CHECK-NEXT: 0x000000f2: "x"
+CHECK-NEXT: 0x000000f4: "y"
+
+CHECK: .debug_line_str contents:
+CHECK-NEXT: 0x00000000: "/Users/shubham/Development/test109275485"
+CHECK-NEXT: 0x00000029: "a.cpp"
+
+CHECK: .debug_ranges contents:
+CHECK-NEXT: 00000000 [[#sub(RANGE_START,RANGE_LOWPC)]] [[#sub(RANGE_END,RANGE_LOWPC)]]
+
+CHECK: .debug_rnglists contents:
+CHECK-NEXT: 0x00000000: range list header: length = 0x0000000e, format = DWARF32, version = 0x0005, addr_size = 0x08, seg_size = 0x00, offset_entry_count = 0x00000000
+CHECK-NEXT: ranges:
+CHECK-NEXT: [[RANGELIST_OFFSET]]: [DW_RLE_base_addressx]: 0x0000000000000000
+CHECK-NEXT: 0x0000000e: [DW_RLE_offset_pair ]: {{.*}}[0x[[RANGELIST_OFFSET_START]], 0x[[RANGELIST_OFFSET_END]])
+CHECK-NEXT: 0x00000011: [DW_RLE_end_of_list ]
+
+CHECK: .debug_str_offsets contents:
+CHECK-NEXT: 0x00000000: Contribution size = 40, Format = DWARF32, Version = 5
+CHECK-NEXT: 0x00000008: 00000001 "Apple clang version 14.0.3 (clang-1403.0.22.14.1)"
+CHECK-NEXT: 0x0000000c: 00000033 "a.cpp"
+CHECK-NEXT: 0x00000010: 00000039 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk"
+CHECK-NEXT: 0x00000014: 00000098 "MacOSX.sdk"
+CHECK-NEXT: 0x00000018: 000000a3 "/Users/shubham/Development/test109275485"
+CHECK-NEXT: 0x0000001c: 000000cc "_Z4foo2i"
+CHECK-NEXT: 0x00000020: 000000d5 "foo2"
+CHECK-NEXT: 0x00000024: 000000da "a"
+CHECK-NEXT: 0x00000028: 000000dc "int"
diff --git a/llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/dwarf5-macho.test b/llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/dwarf5-macho.test
new file mode 100644
index 00000000000000..0e1e427be67c2c
--- /dev/null
+++ b/llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/dwarf5-macho.test
@@ -0,0 +1,92 @@
+; This test checks to that DWARF v5 debug info can be correctly linked
+; into a dSYM bundle by dsymutil, with the correct section names and DWARF v5
+; headers for the
diff erent sections.
+
+; 1.o was produced with the source file:
+
+; a.cpp
+; __attribute__((section("1,__text_foo"))) void foo() {}
+;
+; int foo2(int a) {
+; return a+5;
+; }
+; int main () {
+; return 1;
+; }
+
+; clang -g -c -O1 a.cpp -Xclang -gdwarf-5 -o 1.o
+
+
+RUN: rm -rf %t.dir && mkdir -p %t.dir
+RUN: dsymutil --linker llvm -y %p/../dummy-debug-map-amr64.map \
+RUN: -oso-prepend-path=%p/../../Inputs/DWARF5 -o %t.dir/dwarf5-macho.dSYM
+RUN: llvm-dwarfdump %t.dir/dwarf5-macho.dSYM -a --verbose | FileCheck %s
+
+CHECK:.debug_abbrev contents:
+CHECK-NEXT: Abbrev table for offset: 0x00000000
+
+CHECK: .debug_info contents:
+CHECK-NEXT: Compile Unit: length = 0x0000004a, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x0000, addr_size = 0x08
+CHECK: DW_AT_ranges [DW_FORM_sec_offset] (0x[[RANGELIST_OFFSET:[0-9a-f]+]]
+CHECK-NEXT: [0x[[RANGELIST_OFFSET_START:[0-9a-f]+]], 0x[[RANGELIST_OFFSET_END:[0-9a-f]+]]))
+CHECK-NEXT: DW_AT_addr_base [DW_FORM_sec_offset] (0x00000008)
+CHECK: 0x0000002c: DW_TAG_subprogram [2] * (0x0000000c)
+CHECK-NEXT: DW_AT_low_pc [DW_FORM_addrx] (indexed (00000000) address = 0x[[#%.16x,LOCLIST_LOWPC:]])
+CHECK: 0x0000003c: DW_TAG_formal_parameter [3] (0x0000002c)
+CHECK-NEXT: DW_AT_location [DW_FORM_sec_offset] (0x[[LOC_OFFSET:[0-9a-f]+]]:
+CHECK-NEXT: [0x[[#%.16x,LOCLIST_PAIR_START:]], 0x[[#%.16x,LOCLIST_PAIR_END:]]): [[LOCLIST_EXPR:.*]]
+CHECK-NEXT: [0x[[#%.16x,LOCLIST_PAIR_START2:]], 0x[[#%.16x,LOCLIST_PAIR_END2:]]): [[LOCLIST_EXPR2:.*]])
+
+CHECK: .debug_loclists contents:
+CHECK-NEXT: 0x00000000: locations list header: length = 0x00000018, format = DWARF32, version = 0x0005, addr_size = 0x08, seg_size = 0x00, offset_entry_count = 0x00000000
+CHECK-NEXT: 0x[[LOC_OFFSET]]:
+CHECK-NEXT: DW_LLE_base_addressx (0x0000000000000000)
+CHECK-NEXT: DW_LLE_offset_pair (0x[[#sub(LOCLIST_PAIR_START,LOCLIST_LOWPC)]], 0x[[#sub(LOCLIST_PAIR_END,LOCLIST_LOWPC)]])
+CHECK-NEXT: DW_LLE_offset_pair (0x[[#sub(LOCLIST_PAIR_START2,LOCLIST_LOWPC)]], 0x[[#sub(LOCLIST_PAIR_END2,LOCLIST_LOWPC)]])
+CHECK-NEXT: DW_LLE_end_of_list ()
+
+CHECK: .debug_line contents:
+CHECK-NEXT: debug_line[0x00000000]
+CHECK-NEXT: Line table prologue:
+CHECK-NEXT: total_length: 0x00000048
+CHECK-NEXT: format: DWARF32
+CHECK-NEXT: version: 5
+CHECK-NEXT: address_size: 8
+CHECK-NEXT: seg_select_size: 0
+CHECK-NEXT: prologue_length: 0x00000025
+CHECK-NEXT: min_inst_length: 1
+CHECK-NEXT: max_ops_per_inst: 1
+CHECK-NEXT: default_is_stmt: 1
+CHECK-NEXT: line_base: -5
+CHECK-NEXT: line_range: 14
+CHECK-NEXT: opcode_base: 13
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_copy] = 0
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_advance_pc] = 1
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_advance_line] = 1
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_set_file] = 1
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_set_column] = 1
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_negate_stmt] = 0
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_set_basic_block] = 0
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_const_add_pc] = 0
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_fixed_advance_pc] = 1
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_set_prologue_end] = 0
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_set_epilogue_begin] = 0
+CHECK-NEXT: standard_opcode_lengths[DW_LNS_set_isa] = 1
+CHECK-NEXT: include_directories[ 0] = .debug_line_str[0x00000000] = "/Users/shubham/Development/test109275485"
+CHECK-NEXT: file_names[ 0]:
+CHECK-NEXT: name: .debug_line_str[0x00000029] = "a.cpp"
+CHECK-NEXT: dir_index: 0
+
+
+CHECK: .debug_str contents:
+
+CHECK: .debug_line_str contents:
+CHECK-NEXT: 0x00000000: "/Users/shubham/Development/test109275485"
+CHECK-NEXT: 0x00000029: "a.cpp"
+
+CHECK: .debug_rnglists contents:
+CHECK-NEXT: 0x00000000: range list header: length = 0x0000000e, format = DWARF32, version = 0x0005, addr_size = 0x08, seg_size = 0x00, offset_entry_count = 0x00000000
+CHECK-NEXT: ranges:
+CHECK-NEXT: [[RANGELIST_OFFSET]]: [DW_RLE_base_addressx]: 0x0000000000000000
+CHECK-NEXT: 0x0000000e: [DW_RLE_offset_pair ]: {{.*}}[0x[[RANGELIST_OFFSET_START]], 0x[[RANGELIST_OFFSET_END]])
+CHECK-NEXT: 0x00000011: [DW_RLE_end_of_list ]
diff --git a/llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/fat-dylib-update.test b/llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/fat-dylib-update.test
new file mode 100644
index 00000000000000..4947f6005d3fe5
--- /dev/null
+++ b/llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/fat-dylib-update.test
@@ -0,0 +1,110 @@
+# REQUIRES: object-emission,system-darwin
+# RUN: dsymutil --linker llvm -oso-prepend-path %p/../.. %p/../../Inputs/fat-test.arm.dylib -o %t.dSYM
+# RUN: llvm-dwarfdump -a -v %t.dSYM/Contents/Resources/DWARF/fat-test.arm.dylib | FileCheck %s
+# RUN: dsymutil --linker llvm -u %t.dSYM
+# RUN: llvm-dwarfdump -a -v %t.dSYM/Contents/Resources/DWARF/fat-test.arm.dylib | FileCheck %s
+# RUN: dsymutil --linker llvm -u %t.dSYM -o %t1.dSYM
+# RUN: llvm-dwarfdump -a -v %t1.dSYM/Contents/Resources/DWARF/fat-test.arm.dylib | FileCheck %s
+
+CHECK: /Contents/Resources/DWARF/fat-test.arm.dylib(armv7): file format Mach-O arm
+
+CHECK: .debug_info contents:
+CHECK: Compile Unit: length = 0x00000034, format = DWARF32, version = 0x0002, abbr_offset = 0x0000, addr_size = 0x04 (next unit at 0x00000038)
+CHECK: DW_TAG_compile_unit [1] *
+CHECK: DW_AT_producer [DW_FORM_strp] ( .debug_str[0x00000001] = "clang version 3.8.0 (trunk 243776)")
+CHECK: DW_AT_language [DW_FORM_data2] (DW_LANG_C99)
+CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000024] = "fat-test.c")
+CHECK: DW_AT_stmt_list [DW_FORM_data4] (0x00000000)
+CHECK: DW_AT_comp_dir [DW_FORM_strp] ( .debug_str[0x0000002f] = "/Inputs")
+CHECK: DW_TAG_variable [2]
+CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000037] = "armv7_var")
+CHECK: DW_AT_type [DW_FORM_ref4] (cu + 0x0030 => {0x00000030}
+CHECK: DW_AT_external [DW_FORM_flag] (0x01)
+CHECK: DW_AT_decl_file [DW_FORM_data1] ("/Inputs/fat-test.c")
+CHECK: DW_AT_decl_line [DW_FORM_data1] (23)
+CHECK: DW_AT_location [DW_FORM_block1] (DW_OP_addr 0x1000)
+CHECK: DW_TAG_base_type [3]
+CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000041] = "int")
+CHECK: DW_AT_encoding [DW_FORM_data1] (DW_ATE_signed)
+CHECK: DW_AT_byte_size [DW_FORM_data1] (0x04)
+CHECK: NULL
+
+
+CHECK: .debug_line contents:
+CHECK: Line table prologue:
+CHECK: total_length: 0x0000002a
+CHECK: version: 2
+CHECK: prologue_length: 0x00000021
+CHECK: min_inst_length: 1
+CHECK: default_is_stmt: 1
+CHECK: line_base: -5
+CHECK: line_range: 14
+CHECK: opcode_base: 13
+
+CHECK: /Contents/Resources/DWARF/fat-test.arm.dylib(armv7s): file format Mach-O arm
+
+CHECK: .debug_info contents:
+CHECK: Compile Unit: length = 0x00000034, format = DWARF32, version = 0x0002, abbr_offset = 0x0000, addr_size = 0x04 (next unit at 0x00000038)
+CHECK: DW_TAG_compile_unit [1] *
+CHECK: DW_AT_producer [DW_FORM_strp] ( .debug_str[0x00000001] = "clang version 3.8.0 (trunk 243776)")
+CHECK: DW_AT_language [DW_FORM_data2] (DW_LANG_C99)
+CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000024] = "fat-test.c")
+CHECK: DW_AT_stmt_list [DW_FORM_data4] (0x00000000)
+CHECK: DW_AT_comp_dir [DW_FORM_strp] ( .debug_str[0x0000002f] = "/Inputs")
+CHECK: DW_TAG_variable [2]
+CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000037] = "armv7s_var")
+CHECK: DW_AT_type [DW_FORM_ref4] (cu + 0x0030 => {0x00000030}
+CHECK: DW_AT_external [DW_FORM_flag] (0x01)
+CHECK: DW_AT_decl_file [DW_FORM_data1] ("/Inputs/fat-test.c")
+CHECK: DW_AT_decl_line [DW_FORM_data1] (21)
+CHECK: DW_AT_location [DW_FORM_block1] (DW_OP_addr 0x1000)
+CHECK: DW_TAG_base_type [3]
+CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000042] = "int")
+CHECK: DW_AT_encoding [DW_FORM_data1] (DW_ATE_signed)
+CHECK: DW_AT_byte_size [DW_FORM_data1] (0x04)
+CHECK: NULL
+
+CHECK: .debug_line contents:
+CHECK: Line table prologue:
+CHECK: total_length: 0x0000002a
+CHECK: version: 2
+CHECK: prologue_length: 0x00000021
+CHECK: min_inst_length: 1
+CHECK: default_is_stmt: 1
+CHECK: line_base: -5
+CHECK: line_range: 14
+CHECK: opcode_base: 13
+
+CHECK: /Contents/Resources/DWARF/fat-test.arm.dylib(arm64): file format Mach-O arm64
+
+CHECK: .debug_info contents:
+CHECK: Compile Unit: length = 0x00000038, format = DWARF32, version = 0x0002, abbr_offset = 0x0000, addr_size = 0x08 (next unit at 0x0000003c)
+CHECK: DW_TAG_compile_unit [1] *
+CHECK: DW_AT_producer [DW_FORM_strp] ( .debug_str[0x00000001] = "clang version 3.8.0 (trunk 243776)")
+CHECK: DW_AT_language [DW_FORM_data2] (DW_LANG_C99)
+CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000024] = "fat-test.c")
+CHECK: DW_AT_stmt_list [DW_FORM_data4] (0x00000000)
+CHECK: DW_AT_comp_dir [DW_FORM_strp] ( .debug_str[0x0000002f] = "/Inputs")
+CHECK: DW_TAG_variable [2]
+CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000037] = "arm64_var")
+CHECK: DW_AT_type [DW_FORM_ref4] (cu + 0x0034 => {0x00000034}
+CHECK: DW_AT_external [DW_FORM_flag] (0x01)
+CHECK: DW_AT_decl_file [DW_FORM_data1] ("/Inputs/fat-test.c")
+CHECK: DW_AT_decl_line [DW_FORM_data1] (25)
+CHECK: DW_AT_location [DW_FORM_block1] (DW_OP_addr 0x4000)
+CHECK: DW_TAG_base_type [3]
+CHECK: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000041] = "int")
+CHECK: DW_AT_encoding [DW_FORM_data1] (DW_ATE_signed)
+CHECK: DW_AT_byte_size [DW_FORM_data1] (0x04)
+CHECK: NULL
+
+CHECK: .debug_line contents:
+CHECK: Line table prologue:
+CHECK: total_length: 0x0000002a
+CHECK: version: 2
+CHECK: prologue_length: 0x00000021
+CHECK: min_inst_length: 1
+CHECK: default_is_stmt: 1
+CHECK: line_base: -5
+CHECK: line_range: 14
+CHECK: opcode_base: 13
diff --git a/llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/obfuscated.test b/llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/obfuscated.test
new file mode 100644
index 00000000000000..640b5484b2c4e2
--- /dev/null
+++ b/llvm/test/tools/dsymutil/ARM/DWARFLinkerParallel/obfuscated.test
@@ -0,0 +1,119 @@
+REQUIRES: system-darwin
+
+RUN: dsymutil --linker llvm --symbol-map %p/../../Inputs/obfuscated.map %p/../../Inputs/obfuscated.arm64 -f -o - \
+RUN: | llvm-dwarfdump -v - \
+RUN: | FileCheck %s
+
+RUN: dsymutil --linker llvm --symbol-map %p/../../Inputs/obfuscated.map %p/../../Inputs/obfuscated.arm64 -f -o - \
+RUN: | llvm-dwarfdump -v - \
+RUN: | FileCheck --check-prefix=NOHIDDEN %s
+
+RUN: dsymutil --linker llvm --symbol-map %p/../../Inputs/obfuscated.2.map %p/../../Inputs/obfuscated.2.arm64 -f -o - \
+RUN: | llvm-dwarfdump -v - \
+RUN: | FileCheck --check-prefix=NOHIDDEN %s
+
+// Run with plist and make sure dsymutil finds it.
+RUN: mkdir -p %t.dSYM/Contents/Resources/DWARF/
+RUN: mkdir -p %t.mapdir
+RUN: cp %p/../../Inputs/obfuscated.arm64 %t.dSYM/Contents/Resources/DWARF/
+RUN: cp %p/../../Inputs/E828A486-8433-3A5E-B6DB-A6294D28133D.plist %t.dSYM/Contents/Resources/
+RUN: cp %p/../../Inputs/obfuscated.map %t.mapdir/506AA50A-6B26-3B37-86D2-DC6EBD57B720.bcsymbolmap
+RUN: dsymutil --linker llvm --symbol-map %t.mapdir %t.dSYM 2>&1 | FileCheck --check-prefix=OBFUSCATING %s
+
+// Run without plist and make sure dsymutil doesn't crash.
+RUN: rm %t.dSYM/Contents/Resources/E828A486-8433-3A5E-B6DB-A6294D28133D.plist
+RUN: dsymutil --linker llvm --symbol-map %t.mapdir %t.dSYM 2>&1 | FileCheck --check-prefix=NOTOBFUSCATING %s
+
+OBFUSCATING-NOT: not unobfuscating
+
+NOTOBFUSCATING: not unobfuscating
+
+NOHIDDEN-NOT: __hidden#
+
+CHECK: .debug_info contents:
+
+CHECK: DW_TAG_compile_unit [1] *
+CHECK: DW_AT_producer [DW_FORM_strp] ( {{.*}} "Apple LLVM version 7.0.0 (clang-700.2.38.2)")
+CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "main.c")
+CHECK: DW_AT_comp_dir [DW_FORM_strp] ( {{.*}} "/Users/steven/dev/alpena/tests/src")
+CHECK: DW_TAG_subprogram [2]
+CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "main")
+
+CHECK: DW_TAG_compile_unit [1] *
+CHECK: DW_AT_producer [DW_FORM_strp] ( {{.*}} "Apple LLVM version 7.0.0 (clang-700.2.38.2)")
+CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "one.c")
+CHECK: DW_AT_comp_dir [DW_FORM_strp] ( {{.*}} "/Users/steven/dev/alpena/tests/src")
+CHECK: DW_TAG_subprogram [2]
+CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "one")
+
+CHECK: DW_TAG_compile_unit [1] *
+CHECK: DW_AT_producer [DW_FORM_strp] ( {{.*}} "Apple LLVM version 7.0.0 (clang-700.2.38.2)")
+CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "two.c")
+CHECK: DW_AT_comp_dir [DW_FORM_strp] ( {{.*}} "/Users/steven/dev/alpena/tests/src")
+CHECK: DW_TAG_subprogram [2]
+CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "two")
+
+CHECK: DW_TAG_compile_unit [1] *
+CHECK: DW_AT_producer [DW_FORM_strp] ( {{.*}} "Apple LLVM version 7.0.0 (clang-700.2.38.2)")
+CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "three.c")
+CHECK: DW_AT_comp_dir [DW_FORM_strp] ( {{.*}} "/Users/steven/dev/alpena/tests/src")
+CHECK: DW_TAG_subprogram [2]
+CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "three")
+
+CHECK: DW_TAG_compile_unit [1] *
+CHECK: DW_AT_producer [DW_FORM_strp] ( {{.*}} "Apple LLVM version 7.0.0 (clang-700.2.38.2)")
+CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "four.c")
+CHECK: DW_AT_stmt_list [DW_FORM_data4] (0x0000011e)
+CHECK: DW_AT_comp_dir [DW_FORM_strp] ( {{.*}} "/Users/steven/dev/alpena/tests/src")
+CHECK: DW_TAG_subprogram [2]
+CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "four")
+
+CHECK: DW_TAG_compile_unit [1] *
+CHECK: DW_AT_producer [DW_FORM_strp] ( {{.*}} "Apple LLVM version 7.0.0 (clang-700.2.38.2)")
+CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "five.c")
+CHECK: DW_AT_comp_dir [DW_FORM_strp] ( {{.*}} "/Users/steven/dev/alpena/tests/src")
+CHECK: DW_TAG_subprogram [2]
+CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "five")
+
+CHECK: DW_TAG_compile_unit [1] *
+CHECK: DW_AT_producer [DW_FORM_strp] ( {{.*}} "Apple LLVM version 7.0.0 (clang-700.2.38.2)")
+CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "six.c")
+CHECK: DW_AT_comp_dir [DW_FORM_strp] ( {{.*}} "/Users/steven/dev/alpena/tests/src")
+CHECK: DW_TAG_subprogram [2]
+CHECK: DW_AT_name [DW_FORM_strp] ( {{.*}} "six")
+
+CHECK: .debug_line contents:
+CHECK: file_names[ 1]:
+CHECK: name: "main.c"
+CHECK: dir_index: 0
+CHECK: mod_time: 0x00000000
+CHECK: file_names[ 1]:
+CHECK: name: "one.c"
+CHECK: dir_index: 0
+CHECK: mod_time: 0x00000000
+CHECK: length: 0x00000000
+CHECK: file_names[ 1]:
+CHECK: name: "two.c"
+CHECK: dir_index: 0
+CHECK: mod_time: 0x00000000
+CHECK: length: 0x00000000
+CHECK: file_names[ 1]:
+CHECK: name: "three.c"
+CHECK: dir_index: 0
+CHECK: mod_time: 0x00000000
+CHECK: length: 0x00000000
+CHECK: file_names[ 1]:
+CHECK: name: "four.c"
+CHECK: dir_index: 0
+CHECK: mod_time: 0x00000000
+CHECK: length: 0x00000000
+CHECK: file_names[ 1]:
+CHECK: name: "five.c"
+CHECK: dir_index: 0
+CHECK: mod_time: 0x00000000
+CHECK: length: 0x00000000
+CHECK: file_names[ 1]:
+CHECK: name: "six.c"
+CHECK: dir_index: 0
+CHECK: mod_time: 0x00000000
+CHECK: length: 0x00000000
diff --git a/llvm/test/tools/dsymutil/ARM/call-pc-reloc.test b/llvm/test/tools/dsymutil/ARM/call-pc-reloc.test
index f34e682697b017..8c2b2622a36588 100644
--- a/llvm/test/tools/dsymutil/ARM/call-pc-reloc.test
+++ b/llvm/test/tools/dsymutil/ARM/call-pc-reloc.test
@@ -17,4 +17,7 @@ $ clang -arch arm64 main.arm64.o -o main.arm64 -g
RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/call_pc/main.arm64 -o %t.dSYM
RUN: llvm-dwarfdump %t.dSYM | FileCheck %s -implicit-check-not=DW_AT_call_pc
+RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/call_pc/main.arm64 -o %t.dSYM
+RUN: llvm-dwarfdump %t.dSYM | FileCheck %s -implicit-check-not=DW_AT_call_pc
+
CHECK: DW_AT_call_pc (0x0000000100007f94)
diff --git a/llvm/test/tools/dsymutil/ARM/dwarf5-addr-base.test b/llvm/test/tools/dsymutil/ARM/dwarf5-addr-base.test
index 96973fe122c8c4..00dcde718e3622 100644
--- a/llvm/test/tools/dsymutil/ARM/dwarf5-addr-base.test
+++ b/llvm/test/tools/dsymutil/ARM/dwarf5-addr-base.test
@@ -49,9 +49,23 @@ RUN: rm -rf %t.dir && mkdir -p %t.dir
RUN: dsymutil -y %p/dummy-debug-map-amr64.map -oso-prepend-path=%p/../Inputs/DWARF5-addr-base-str-off-base -o %t.dir/dwarf5-addr-base.dSYM
RUN: llvm-dwarfdump %t.dir/dwarf5-addr-base.dSYM -a --verbose | FileCheck %s
+RUN: rm -rf %t.dir && mkdir -p %t.dir
+RUN: dsymutil --linker llvm -y %p/dummy-debug-map-amr64.map \
+RUN: -oso-prepend-path=%p/../Inputs/DWARF5-addr-base-str-off-base \
+RUN: -o %t.dir/dwarf5-addr-base.dSYM
+RUN: llvm-dwarfdump %t.dir/dwarf5-addr-base.dSYM -a --verbose | FileCheck %s
+
+RUN: rm -rf %t.dir && mkdir -p %t.dir
RUN: dsymutil --update -y %p/dummy-debug-map-amr64.map -oso-prepend-path=%p/../Inputs/DWARF5-addr-base-str-off-base -o %t.dir/dwarf5-addr-base.dSYM
RUN: llvm-dwarfdump %t.dir/dwarf5-addr-base.dSYM -a --verbose | FileCheck %s --check-prefix=UPD
+RUN: rm -rf %t.dir && mkdir -p %t.dir
+RUN: dsymutil --linker llvm --update -y %p/dummy-debug-map-amr64.map \
+RUN: -oso-prepend-path=%p/../Inputs/DWARF5-addr-base-str-off-base \
+RUN: -o %t.dir/dwarf5-addr-base.dSYM
+RUN: llvm-dwarfdump %t.dir/dwarf5-addr-base.dSYM -a --verbose | \
+RUN: FileCheck %s --check-prefix=UPD
+
CHECK: .debug_info contents:
CHECK-NEXT: 0x00000000: Compile Unit: length = 0x0000004a, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x0000, addr_size = 0x08
@@ -62,7 +76,7 @@ CHECK: 0x0000002c: DW_TAG_subprogram [2] * (0x0000000c)
CHECK-NEXT: DW_AT_low_pc [DW_FORM_addrx] (indexed (00000000) address = 0x[[ADDR1:[0-9a-f]+]])
-CHECK: 0x0000004e: Compile Unit: length = 0x0000004a, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x0000, addr_size = 0x08
+CHECK: 0x0000004e: Compile Unit: length = 0x0000004a, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x00{{00|5a}}, addr_size = 0x08
CHECK: 0x0000005a: DW_TAG_compile_unit [1] *
CHECK: DW_AT_addr_base [DW_FORM_sec_offset] (0x00000018)
@@ -70,9 +84,9 @@ CHECK: DW_AT_addr_base [DW_FORM_sec_offset] (0x00000018)
CHECK: 0x0000007a: DW_TAG_subprogram [2] * (0x0000005a)
CHECK-NEXT: DW_AT_low_pc [DW_FORM_addrx] (indexed (00000000) address = 0x[[ADDR2:[0-9a-f]+]])
-CHECK: 0x0000009c: Compile Unit: length = 0x00000043, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x0000, addr_size = 0x08
+CHECK: 0x0000009c: Compile Unit: length = 0x00000043, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x00{{00|b4}}, addr_size = 0x08
-CHECK: 0x000000a8: DW_TAG_compile_unit [5] *
+CHECK: 0x000000a8: DW_TAG_compile_unit {{.*}} *
CHECK: DW_AT_addr_base [DW_FORM_sec_offset] (0x00000028)
CHECK: 0x000000c1: DW_TAG_subprogram [2] * (0x000000a8)
@@ -111,7 +125,7 @@ UPD: 0x00000071: DW_TAG_subprogram [5] (0x0000000c)
UPD-NEXT: DW_AT_low_pc [DW_FORM_addrx] (indexed (00000003) address = 0x0000000000000010)
-UPD: 0x00000085: Compile Unit: length = 0x00000072, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x0000, addr_size = 0x08
+UPD: 0x00000085: Compile Unit: length = 0x00000072, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x00{{00|99}}, addr_size = 0x08
UPD: 0x00000091: DW_TAG_compile_unit [1] *
UPD: DW_AT_addr_base [DW_FORM_sec_offset] (0x00000008)
@@ -125,12 +139,12 @@ UPD-NEXT: DW_AT_low_pc [DW_FORM_addrx] (indexed (00000001) ad
UPD: 0x000000dc: DW_TAG_subprogram [3] * (0x00000091)
UPD-NEXT: DW_AT_low_pc [DW_FORM_addrx] (indexed (00000002) address = 0x0000000000000008)
-UPD: 0x000000fb: Compile Unit: length = 0x00000044, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x0000, addr_size = 0x08
+UPD: 0x000000fb: Compile Unit: length = 0x00000044, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x0{{000|115}}, addr_size = 0x08
-UPD: 0x00000107: DW_TAG_compile_unit [7] *
+UPD: 0x00000107: DW_TAG_compile_unit {{.*}} *
UPD: DW_AT_addr_base [DW_FORM_sec_offset] (0x00000008)
-UPD: 0x00000124: DW_TAG_subprogram [3] * (0x00000107)
+UPD: 0x00000124: DW_TAG_subprogram {{.*}} * (0x00000107)
UPD-NEXT: DW_AT_low_pc [DW_FORM_addrx] (indexed (00000000) address = 0x0000000000000018)
UPD: .debug_addr contents:
diff --git a/llvm/test/tools/dsymutil/ARM/dwarf5-addrx-0x0-last.test b/llvm/test/tools/dsymutil/ARM/dwarf5-addrx-0x0-last.test
index f74c514ab51697..e54d05f941162c 100644
--- a/llvm/test/tools/dsymutil/ARM/dwarf5-addrx-0x0-last.test
+++ b/llvm/test/tools/dsymutil/ARM/dwarf5-addrx-0x0-last.test
@@ -28,6 +28,14 @@ RUN: llvm-dwarfdump --verify %t.dSYM 2>&1 | FileCheck %s
RUN: llvm-dwarfdump --verbose -debug-info %t.dSYM | FileCheck %s --check-prefix DEBUGINFO
RUN: llvm-dwarfdump --verbose -debug-line %t.dSYM | FileCheck %s --check-prefix DEBUGLINE
+RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs \
+RUN: %p/../Inputs/private/tmp/dwarf5/dwarf5-addrx-0x0-last.out \
+RUN: -o %t.dSYM 2>&1 | FileCheck %s --allow-empty
+RUN: llvm-dwarfdump --verify %t.dSYM 2>&1 | FileCheck %s
+RUN: llvm-dwarfdump --verbose -debug-info %t.dSYM | FileCheck %s --check-prefix DEBUGINFO
+RUN: llvm-dwarfdump --verbose -debug-line %t.dSYM | FileCheck %s --check-prefix DEBUGLINE
+
+
CHECK-NOT: error:
DEBUGINFO: DW_TAG_subprogram
diff --git a/llvm/test/tools/dsymutil/ARM/dwarf5-str-offsets-base-strx.test b/llvm/test/tools/dsymutil/ARM/dwarf5-str-offsets-base-strx.test
index 4765d02a13f1aa..ac813fc6203708 100644
--- a/llvm/test/tools/dsymutil/ARM/dwarf5-str-offsets-base-strx.test
+++ b/llvm/test/tools/dsymutil/ARM/dwarf5-str-offsets-base-strx.test
@@ -1,4 +1,8 @@
; This test checks to ensure that if three DWARFv5 object files have correct values for the DW_AT_str_offsets_base and DW_FORM_strx for strings in their compile units.
+; DWARFLinkerParallel specific : DWARFLinkerParallel uses .debug_str_offsets
+; table local to the compile unit. That leads to
diff erent string indexes.
+; In such cases test contains both variants for indexes value:
+; (indexed (0000000{{9|1}}) string = "b.cpp".
; 1.o was produced with the source file:
@@ -47,10 +51,21 @@
RUN: rm -rf %t.dir && mkdir -p %t.dir
RUN: dsymutil -y %p/dummy-debug-map-amr64.map -oso-prepend-path=%p/../Inputs/DWARF5-addr-base-str-off-base -o %t.dir/dwarf5-addr-base.dSYM
-RUN: llvm-dwarfdump %t.dir/dwarf5-addr-base.dSYM -a --verbose | FileCheck %s
+RUN: llvm-dwarfdump %t.dir/dwarf5-addr-base.dSYM -a --verbose | FileCheck %s --check-prefixes=CHECK,GLOBAL
RUN: dsymutil --update -y %p/dummy-debug-map-amr64.map -oso-prepend-path=%p/../Inputs/DWARF5-addr-base-str-off-base -o %t.dir/dwarf5-addr-base.dSYM
-RUN: llvm-dwarfdump %t.dir/dwarf5-addr-base.dSYM -a --verbose | FileCheck %s --check-prefix=UPD
+RUN: llvm-dwarfdump %t.dir/dwarf5-addr-base.dSYM -a --verbose | FileCheck %s --check-prefixes=UPD,GLOBALUPD
+
+RUN: rm -rf %t.dir && mkdir -p %t.dir
+RUN: dsymutil --linker llvm -y %p/dummy-debug-map-amr64.map \
+RUN: -oso-prepend-path=%p/../Inputs/DWARF5-addr-base-str-off-base \
+RUN: -o %t.dir/dwarf5-addr-base.dSYM
+RUN: llvm-dwarfdump %t.dir/dwarf5-addr-base.dSYM -a --verbose | FileCheck %s --check-prefixes=CHECK,LOCAL
+
+RUN: dsymutil --linker llvm --update -y %p/dummy-debug-map-amr64.map \
+RUN: -oso-prepend-path=%p/../Inputs/DWARF5-addr-base-str-off-base \
+RUN: -o %t.dir/dwarf5-addr-base.dSYM
+RUN: llvm-dwarfdump %t.dir/dwarf5-addr-base.dSYM -a --verbose | FileCheck %s --check-prefixes=UPD,LOCALUPD
CHECK: .debug_info contents:
CHECK: 0x00000000: Compile Unit: length = 0x0000004a, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x0000, addr_size = 0x08 (next unit at 0x0000004e)
@@ -78,19 +93,19 @@ CHECK: DW_AT_name [DW_FORM_strx] (indexed (00000008) strin
CHECK: 0x0000004d: NULL
-CHECK: 0x0000004e: Compile Unit: length = 0x0000004a, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x0000, addr_size = 0x08 (next unit at 0x0000009c)
+CHECK: 0x0000004e: Compile Unit: length = 0x0000004a, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x00{{00|5a}}, addr_size = 0x08 (next unit at 0x0000009c)
CHECK: 0x0000005a: DW_TAG_compile_unit [1] *
CHECK: DW_AT_producer [DW_FORM_strx] (indexed (00000000) string = "Apple clang version 15.0.0 (clang-1500.0.31.1)")
-CHECK: DW_AT_name [DW_FORM_strx] (indexed (00000009) string = "b.cpp")
+CHECK: DW_AT_name [DW_FORM_strx] (indexed (0000000{{9|1}}) string = "b.cpp")
CHECK: DW_AT_LLVM_sysroot [DW_FORM_strx] (indexed (00000002) string = "/Users/shubham/apple-internal/Xcode-Rainbow/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk")
CHECK: DW_AT_APPLE_sdk [DW_FORM_strx] (indexed (00000003) string = "MacOSX14.0.sdk")
-CHECK: DW_AT_str_offsets_base [DW_FORM_sec_offset] (0x00000008)
+CHECK: DW_AT_str_offsets_base [DW_FORM_sec_offset] (0x000000{{08|34}})
CHECK: DW_AT_comp_dir [DW_FORM_strx] (indexed (00000004) string = "/Users/shubham/Development/test109275485")
CHECK: 0x0000007a: DW_TAG_subprogram [2] * (0x0000005a)
-CHECK: DW_AT_linkage_name [DW_FORM_strx] (indexed (0000000a) string = "_Z4bar2i")
-CHECK: DW_AT_name [DW_FORM_strx] (indexed (0000000b) string = "bar2")
+CHECK: DW_AT_linkage_name [DW_FORM_strx] (indexed (0000000{{a|5}}) string = "_Z4bar2i")
+CHECK: DW_AT_name [DW_FORM_strx] (indexed (0000000{{b|6}}) string = "bar2")
CHECK: 0x0000008a: DW_TAG_formal_parameter [3] (0x0000007a)
CHECK: DW_AT_name [DW_FORM_strx] (indexed (00000007) string = "a")
@@ -102,22 +117,22 @@ CHECK: DW_AT_name [DW_FORM_strx] (indexed (00000008) strin
CHECK: 0x0000009b: NULL
-CHECK: 0x0000009c: Compile Unit: length = 0x00000043, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x0000, addr_size = 0x08 (next unit at 0x000000e3)
+CHECK: 0x0000009c: Compile Unit: length = 0x00000043, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x00{{00|b4}}, addr_size = 0x08 (next unit at 0x000000e3)
-CHECK: 0x000000a8: DW_TAG_compile_unit [5] *
+CHECK: 0x000000a8: DW_TAG_compile_unit {{.*}} *
CHECK: DW_AT_producer [DW_FORM_strx] (indexed (00000000) string = "Apple clang version 15.0.0 (clang-1500.0.31.1)")
-CHECK: DW_AT_name [DW_FORM_strx] (indexed (0000000c) string = "c.cpp")
+CHECK: DW_AT_name [DW_FORM_strx] (indexed (0000000{{c|1}}) string = "c.cpp")
CHECK: DW_AT_LLVM_sysroot [DW_FORM_strx] (indexed (00000002) string = "/Users/shubham/apple-internal/Xcode-Rainbow/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk")
CHECK: DW_AT_APPLE_sdk [DW_FORM_strx] (indexed (00000003) string = "MacOSX14.0.sdk")
-CHECK: DW_AT_str_offsets_base [DW_FORM_sec_offset] (0x00000008)
+CHECK: DW_AT_str_offsets_base [DW_FORM_sec_offset] (0x000000{{08|60}})
CHECK: DW_AT_comp_dir [DW_FORM_strx] (indexed (00000004) string = "/Users/shubham/Development/test109275485")
CHECK: 0x000000c1: DW_TAG_subprogram [2] * (0x000000a8)
-CHECK: DW_AT_linkage_name [DW_FORM_strx] (indexed (0000000d) string = "_Z3bazi")
-CHECK: DW_AT_name [DW_FORM_strx] (indexed (0000000e) string = "baz")
+CHECK: DW_AT_linkage_name [DW_FORM_strx] (indexed (0000000{{d|5}}) string = "_Z3bazi")
+CHECK: DW_AT_name [DW_FORM_strx] (indexed (0000000{{e|6}}) string = "baz")
CHECK: 0x000000d1: DW_TAG_formal_parameter [3] (0x000000c1)
-CHECK: DW_AT_name [DW_FORM_strx] (indexed (0000000f) string = "x")
+CHECK: DW_AT_name [DW_FORM_strx] (indexed (0000000{{f|7}}) string = "x")
CHECK: 0x000000dd: NULL
@@ -145,24 +160,57 @@ CHECK-NEXT: 0x0000011d: "_Z3bazi"
CHECK-NEXT: 0x00000125: "baz"
CHECK-NEXT: 0x00000129: "x"
-CHECK: .debug_str_offsets contents:
-CHECK-NEXT: 0x00000000: Contribution size = 68, Format = DWARF32, Version = 5
-CHECK-NEXT: 0x00000008: 00000001 "Apple clang version 15.0.0 (clang-1500.0.31.1)"
-CHECK-NEXT: 0x0000000c: 00000030 "a.cpp"
-CHECK-NEXT: 0x00000010: 00000036 "/Users/shubham/apple-internal/Xcode-Rainbow/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk"
-CHECK-NEXT: 0x00000014: 000000b7 "MacOSX14.0.sdk"
-CHECK-NEXT: 0x00000018: 000000c6 "/Users/shubham/Development/test109275485"
-CHECK-NEXT: 0x0000001c: 000000ef "_Z4foo2i"
-CHECK-NEXT: 0x00000020: 000000f8 "foo2"
-CHECK-NEXT: 0x00000024: 000000fd "a"
-CHECK-NEXT: 0x00000028: 000000ff "int"
-CHECK-NEXT: 0x0000002c: 00000103 "b.cpp"
-CHECK-NEXT: 0x00000030: 00000109 "_Z4bar2i"
-CHECK-NEXT: 0x00000034: 00000112 "bar2"
-CHECK-NEXT: 0x00000038: 00000117 "c.cpp"
-CHECK-NEXT: 0x0000003c: 0000011d "_Z3bazi"
-CHECK-NEXT: 0x00000040: 00000125 "baz"
-CHECK-NEXT: 0x00000044: 00000129 "x"
+GLOBAL: .debug_str_offsets contents:
+GLOBAL-NEXT: 0x00000000: Contribution size = 68, Format = DWARF32, Version = 5
+GLOBAL-NEXT: 0x00000008: 00000001 "Apple clang version 15.0.0 (clang-1500.0.31.1)"
+GLOBAL-NEXT: 0x0000000c: 00000030 "a.cpp"
+GLOBAL-NEXT: 0x00000010: 00000036 "/Users/shubham/apple-internal/Xcode-Rainbow/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk"
+GLOBAL-NEXT: 0x00000014: 000000b7 "MacOSX14.0.sdk"
+GLOBAL-NEXT: 0x00000018: 000000c6 "/Users/shubham/Development/test109275485"
+GLOBAL-NEXT: 0x0000001c: 000000ef "_Z4foo2i"
+GLOBAL-NEXT: 0x00000020: 000000f8 "foo2"
+GLOBAL-NEXT: 0x00000024: 000000fd "a"
+GLOBAL-NEXT: 0x00000028: 000000ff "int"
+GLOBAL-NEXT: 0x0000002c: 00000103 "b.cpp"
+GLOBAL-NEXT: 0x00000030: 00000109 "_Z4bar2i"
+GLOBAL-NEXT: 0x00000034: 00000112 "bar2"
+GLOBAL-NEXT: 0x00000038: 00000117 "c.cpp"
+GLOBAL-NEXT: 0x0000003c: 0000011d "_Z3bazi"
+GLOBAL-NEXT: 0x00000040: 00000125 "baz"
+GLOBAL-NEXT: 0x00000044: 00000129 "x"
+
+LOCAL: .debug_str_offsets contents:
+LOCAL-NEXT: 0x00000000: Contribution size = 40, Format = DWARF32, Version = 5
+LOCAL-NEXT: 0x00000008: 00000001 "Apple clang version 15.0.0 (clang-1500.0.31.1)"
+LOCAL-NEXT: 0x0000000c: 00000030 "a.cpp"
+LOCAL-NEXT: 0x00000010: 00000036 "/Users/shubham/apple-internal/Xcode-Rainbow/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk"
+LOCAL-NEXT: 0x00000014: 000000b7 "MacOSX14.0.sdk"
+LOCAL-NEXT: 0x00000018: 000000c6 "/Users/shubham/Development/test109275485"
+LOCAL-NEXT: 0x0000001c: 000000ef "_Z4foo2i"
+LOCAL-NEXT: 0x00000020: 000000f8 "foo2"
+LOCAL-NEXT: 0x00000024: 000000fd "a"
+LOCAL-NEXT: 0x00000028: 000000ff "int"
+LOCAL-NEXT: 0x0000002c: Contribution size = 40, Format = DWARF32, Version = 5
+LOCAL-NEXT: 0x00000034: 00000001 "Apple clang version 15.0.0 (clang-1500.0.31.1)"
+LOCAL-NEXT: 0x00000038: 00000103 "b.cpp"
+LOCAL-NEXT: 0x0000003c: 00000036 "/Users/shubham/apple-internal/Xcode-Rainbow/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk"
+LOCAL-NEXT: 0x00000040: 000000b7 "MacOSX14.0.sdk"
+LOCAL-NEXT: 0x00000044: 000000c6 "/Users/shubham/Development/test109275485"
+LOCAL-NEXT: 0x00000048: 00000109 "_Z4bar2i"
+LOCAL-NEXT: 0x0000004c: 00000112 "bar2"
+LOCAL-NEXT: 0x00000050: 000000fd "a"
+LOCAL-NEXT: 0x00000054: 000000ff "int"
+LOCAL-NEXT: 0x00000058: Contribution size = 40, Format = DWARF32, Version = 5
+LOCAL-NEXT: 0x00000060: 00000001 "Apple clang version 15.0.0 (clang-1500.0.31.1)"
+LOCAL-NEXT: 0x00000064: 00000117 "c.cpp"
+LOCAL-NEXT: 0x00000068: 00000036 "/Users/shubham/apple-internal/Xcode-Rainbow/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk"
+LOCAL-NEXT: 0x0000006c: 000000b7 "MacOSX14.0.sdk"
+LOCAL-NEXT: 0x00000070: 000000c6 "/Users/shubham/Development/test109275485"
+LOCAL-NEXT: 0x00000074: 0000011d "_Z3bazi"
+LOCAL-NEXT: 0x00000078: 00000125 "baz"
+LOCAL-NEXT: 0x0000007c: 00000129 "x"
+LOCAL-NEXT: 0x00000080: 000000ff "int"
+
UPD: .debug_info contents:
UPD: 0x00000000: Compile Unit: length = 0x00000081, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x0000, addr_size = 0x08 (next unit at 0x00000085)
@@ -205,23 +253,23 @@ UPD: DW_AT_name [DW_FORM_strx] (indexed (0000000e) string
UPD: 0x00000084: NULL
-UPD: 0x00000085: Compile Unit: length = 0x00000072, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x0000, addr_size = 0x08 (next unit at 0x000000fb)
+UPD: 0x00000085: Compile Unit: length = 0x00000072, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x00{{00|99}}, addr_size = 0x08 (next unit at 0x000000fb)
UPD: 0x00000091: DW_TAG_compile_unit [1] *
UPD: DW_AT_producer [DW_FORM_strx] (indexed (00000000) string = "Apple clang version 15.0.0 (clang-1500.0.31.1)")
-UPD: DW_AT_name [DW_FORM_strx] (indexed (0000000f) string = "b.cpp")
+UPD: DW_AT_name [DW_FORM_strx] (indexed (0000000{{f|1}}) string = "b.cpp")
UPD: DW_AT_LLVM_sysroot [DW_FORM_strx] (indexed (00000002) string = "/Users/shubham/apple-internal/Xcode-Rainbow/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk")
UPD: DW_AT_APPLE_sdk [DW_FORM_strx] (indexed (00000003) string = "MacOSX14.0.sdk")
-UPD: DW_AT_str_offsets_base [DW_FORM_sec_offset] (0x00000008)
+UPD: DW_AT_str_offsets_base [DW_FORM_sec_offset] (0x000000{{08|4c}})
UPD: DW_AT_comp_dir [DW_FORM_strx] (indexed (00000004) string = "/Users/shubham/Development/test109275485")
UPD: 0x000000b6: DW_TAG_subprogram [2] (0x00000091)
-UPD: DW_AT_linkage_name [DW_FORM_strx] (indexed (00000010) string = "_Z3barv")
-UPD: DW_AT_name [DW_FORM_strx] (indexed (00000011) string = "bar")
+UPD: DW_AT_linkage_name [DW_FORM_strx] (indexed (000000{{10|05}}) string = "_Z3barv")
+UPD: DW_AT_name [DW_FORM_strx] (indexed (000000{{11|06}}) string = "bar")
UPD: 0x000000c2: DW_TAG_subprogram [3] * (0x00000091)
-UPD: DW_AT_linkage_name [DW_FORM_strx] (indexed (00000012) string = "_Z4bar2i")
-UPD: DW_AT_name [DW_FORM_strx] (indexed (00000013) string = "bar2")
+UPD: DW_AT_linkage_name [DW_FORM_strx] (indexed (000000{{12|07}}) string = "_Z4bar2i")
+UPD: DW_AT_name [DW_FORM_strx] (indexed (000000{{13|08}}) string = "bar2")
UPD: 0x000000d2: DW_TAG_formal_parameter [4] (0x000000c2)
UPD: DW_AT_name [DW_FORM_strx] (indexed (00000009) string = "a")
@@ -229,40 +277,40 @@ UPD: DW_AT_name [DW_FORM_strx] (indexed (00000009) string
UPD: 0x000000db: NULL
UPD: 0x000000dc: DW_TAG_subprogram [3] * (0x00000091)
-UPD: DW_AT_linkage_name [DW_FORM_strx] (indexed (00000014) string = "_Z4bar3i")
-UPD: DW_AT_name [DW_FORM_strx] (indexed (00000015) string = "bar3")
+UPD: DW_AT_linkage_name [DW_FORM_strx] (indexed (000000{{14|0a}}) string = "_Z4bar3i")
+UPD: DW_AT_name [DW_FORM_strx] (indexed (000000{{15|0b}}) string = "bar3")
UPD: 0x000000ec: DW_TAG_formal_parameter [4] (0x000000dc)
UPD: DW_AT_name [DW_FORM_strx] (indexed (0000000c) string = "x")
UPD: 0x000000f5: NULL
-UPD: 0x000000f6: DW_TAG_base_type [6] (0x00000091)
-UPD: DW_AT_name [DW_FORM_strx] (indexed (0000000e) string = "int")
+UPD: 0x000000f6: DW_TAG_base_type {{.*}} (0x00000091)
+UPD: DW_AT_name [DW_FORM_strx] (indexed (0000000{{e|d}}) string = "int")
UPD: 0x000000fa: NULL
-UPD: 0x000000fb: Compile Unit: length = 0x00000044, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x0000, addr_size = 0x08 (next unit at 0x00000143)
+UPD: 0x000000fb: Compile Unit: length = 0x00000044, format = DWARF32, version = 0x0005, unit_type = DW_UT_compile, abbr_offset = 0x0{{000|115}}, addr_size = 0x08 (next unit at 0x00000143)
-UPD: 0x00000107: DW_TAG_compile_unit [7] *
+UPD: 0x00000107: DW_TAG_compile_unit {{.*}} *
UPD: DW_AT_producer [DW_FORM_strx] (indexed (00000000) string = "Apple clang version 15.0.0 (clang-1500.0.31.1)")
-UPD: DW_AT_name [DW_FORM_strx] (indexed (00000016) string = "c.cpp")
+UPD: DW_AT_name [DW_FORM_strx] (indexed (000000{{16|01}}) string = "c.cpp")
UPD: DW_AT_LLVM_sysroot [DW_FORM_strx] (indexed (00000002) string = "/Users/shubham/apple-internal/Xcode-Rainbow/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk")
UPD: DW_AT_APPLE_sdk [DW_FORM_strx] (indexed (00000003) string = "MacOSX14.0.sdk")
-UPD: DW_AT_str_offsets_base [DW_FORM_sec_offset] (0x00000008)
+UPD: DW_AT_str_offsets_base [DW_FORM_sec_offset] (0x000000{{08|8c}})
UPD: DW_AT_comp_dir [DW_FORM_strx] (indexed (00000004) string = "/Users/shubham/Development/test109275485")
-UPD: 0x00000124: DW_TAG_subprogram [3] * (0x00000107)
-UPD: DW_AT_linkage_name [DW_FORM_strx] (indexed (00000017) string = "_Z3bazi")
-UPD: DW_AT_name [DW_FORM_strx] (indexed (00000018) string = "baz")
+UPD: 0x00000124: DW_TAG_subprogram {{.*}} * (0x00000107)
+UPD: DW_AT_linkage_name [DW_FORM_strx] (indexed (000000{{17|05}}) string = "_Z3bazi")
+UPD: DW_AT_name [DW_FORM_strx] (indexed (000000{{18|06}}) string = "baz")
-UPD: 0x00000134: DW_TAG_formal_parameter [4] (0x00000124)
-UPD: DW_AT_name [DW_FORM_strx] (indexed (0000000c) string = "x")
+UPD: 0x00000134: DW_TAG_formal_parameter {{.*}} (0x00000124)
+UPD: DW_AT_name [DW_FORM_strx] (indexed (0000000{{c|7}}) string = "x")
UPD: 0x0000013d: NULL
-UPD: 0x0000013e: DW_TAG_base_type [6] (0x00000107)
-UPD: DW_AT_name [DW_FORM_strx] (indexed (0000000e) string = "int")
+UPD: 0x0000013e: DW_TAG_base_type {{.*}} (0x00000107)
+UPD: DW_AT_name [DW_FORM_strx] (indexed (0000000{{e|8}}) string = "int")
UPD: 0x00000142: NULL
@@ -294,30 +342,73 @@ UPD-NEXT: 0x00000152: "c.cpp"
UPD-NEXT: 0x00000158: "_Z3bazi"
UPD-NEXT: 0x00000160: "baz"
-UPD: .debug_str_offsets contents:
-UPD-NEXT: 0x00000000: Contribution size = 104, Format = DWARF32, Version = 5
-UPD-NEXT: 0x00000008: 00000001 "Apple clang version 15.0.0 (clang-1500.0.31.1)"
-UPD-NEXT: 0x0000000c: 00000030 "a.cpp"
-UPD-NEXT: 0x00000010: 00000036 "/Users/shubham/apple-internal/Xcode-Rainbow/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk"
-UPD-NEXT: 0x00000014: 000000b7 "MacOSX14.0.sdk"
-UPD-NEXT: 0x00000018: 000000c6 "/Users/shubham/Development/test109275485"
-UPD-NEXT: 0x0000001c: 000000ef "_Z3foov"
-UPD-NEXT: 0x00000020: 000000f7 "foo"
-UPD-NEXT: 0x00000024: 000000fb "_Z4foo2i"
-UPD-NEXT: 0x00000028: 00000104 "foo2"
-UPD-NEXT: 0x0000002c: 00000109 "a"
-UPD-NEXT: 0x00000030: 0000010b "_Z4foo3i"
-UPD-NEXT: 0x00000034: 00000114 "foo3"
-UPD-NEXT: 0x00000038: 00000119 "x"
-UPD-NEXT: 0x0000003c: 0000011b "main"
-UPD-NEXT: 0x00000040: 00000120 "int"
-UPD-NEXT: 0x00000044: 00000124 "b.cpp"
-UPD-NEXT: 0x00000048: 0000012a "_Z3barv"
-UPD-NEXT: 0x0000004c: 00000132 "bar"
-UPD-NEXT: 0x00000050: 00000136 "_Z4bar2i"
-UPD-NEXT: 0x00000054: 0000013f "bar2"
-UPD-NEXT: 0x00000058: 00000144 "_Z4bar3i"
-UPD-NEXT: 0x0000005c: 0000014d "bar3"
-UPD-NEXT: 0x00000060: 00000152 "c.cpp"
-UPD-NEXT: 0x00000064: 00000158 "_Z3bazi"
-UPD-NEXT: 0x00000068: 00000160 "baz"
+GLOBALUPD: .debug_str_offsets contents:
+GLOBALUPD-NEXT: 0x00000000: Contribution size = 104, Format = DWARF32, Version = 5
+GLOBALUPD-NEXT: 0x00000008: 00000001 "Apple clang version 15.0.0 (clang-1500.0.31.1)"
+GLOBALUPD-NEXT: 0x0000000c: 00000030 "a.cpp"
+GLOBALUPD-NEXT: 0x00000010: 00000036 "/Users/shubham/apple-internal/Xcode-Rainbow/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk"
+GLOBALUPD-NEXT: 0x00000014: 000000b7 "MacOSX14.0.sdk"
+GLOBALUPD-NEXT: 0x00000018: 000000c6 "/Users/shubham/Development/test109275485"
+GLOBALUPD-NEXT: 0x0000001c: 000000ef "_Z3foov"
+GLOBALUPD-NEXT: 0x00000020: 000000f7 "foo"
+GLOBALUPD-NEXT: 0x00000024: 000000fb "_Z4foo2i"
+GLOBALUPD-NEXT: 0x00000028: 00000104 "foo2"
+GLOBALUPD-NEXT: 0x0000002c: 00000109 "a"
+GLOBALUPD-NEXT: 0x00000030: 0000010b "_Z4foo3i"
+GLOBALUPD-NEXT: 0x00000034: 00000114 "foo3"
+GLOBALUPD-NEXT: 0x00000038: 00000119 "x"
+GLOBALUPD-NEXT: 0x0000003c: 0000011b "main"
+GLOBALUPD-NEXT: 0x00000040: 00000120 "int"
+GLOBALUPD-NEXT: 0x00000044: 00000124 "b.cpp"
+GLOBALUPD-NEXT: 0x00000048: 0000012a "_Z3barv"
+GLOBALUPD-NEXT: 0x0000004c: 00000132 "bar"
+GLOBALUPD-NEXT: 0x00000050: 00000136 "_Z4bar2i"
+GLOBALUPD-NEXT: 0x00000054: 0000013f "bar2"
+GLOBALUPD-NEXT: 0x00000058: 00000144 "_Z4bar3i"
+GLOBALUPD-NEXT: 0x0000005c: 0000014d "bar3"
+GLOBALUPD-NEXT: 0x00000060: 00000152 "c.cpp"
+GLOBALUPD-NEXT: 0x00000064: 00000158 "_Z3bazi"
+GLOBALUPD-NEXT: 0x00000068: 00000160 "baz"
+
+LOCALUPD: .debug_str_offsets contents:
+LOCALUPD-NEXT: 0x00000000: Contribution size = 64, Format = DWARF32, Version = 5
+LOCALUPD-NEXT: 0x00000008: 00000001 "Apple clang version 15.0.0 (clang-1500.0.31.1)"
+LOCALUPD-NEXT: 0x0000000c: 00000030 "a.cpp"
+LOCALUPD-NEXT: 0x00000010: 00000036 "/Users/shubham/apple-internal/Xcode-Rainbow/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk"
+LOCALUPD-NEXT: 0x00000014: 000000b7 "MacOSX14.0.sdk"
+LOCALUPD-NEXT: 0x00000018: 000000c6 "/Users/shubham/Development/test109275485"
+LOCALUPD-NEXT: 0x0000001c: 000000ef "_Z3foov"
+LOCALUPD-NEXT: 0x00000020: 000000f7 "foo"
+LOCALUPD-NEXT: 0x00000024: 000000fb "_Z4foo2i"
+LOCALUPD-NEXT: 0x00000028: 00000104 "foo2"
+LOCALUPD-NEXT: 0x0000002c: 00000109 "a"
+LOCALUPD-NEXT: 0x00000030: 0000010b "_Z4foo3i"
+LOCALUPD-NEXT: 0x00000034: 00000114 "foo3"
+LOCALUPD-NEXT: 0x00000038: 00000119 "x"
+LOCALUPD-NEXT: 0x0000003c: 0000011b "main"
+LOCALUPD-NEXT: 0x00000040: 00000120 "int"
+LOCALUPD-NEXT: 0x00000044: Contribution size = 60, Format = DWARF32, Version = 5
+LOCALUPD-NEXT: 0x0000004c: 00000001 "Apple clang version 15.0.0 (clang-1500.0.31.1)"
+LOCALUPD-NEXT: 0x00000050: 00000124 "b.cpp"
+LOCALUPD-NEXT: 0x00000054: 00000036 "/Users/shubham/apple-internal/Xcode-Rainbow/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk"
+LOCALUPD-NEXT: 0x00000058: 000000b7 "MacOSX14.0.sdk"
+LOCALUPD-NEXT: 0x0000005c: 000000c6 "/Users/shubham/Development/test109275485"
+LOCALUPD-NEXT: 0x00000060: 0000012a "_Z3barv"
+LOCALUPD-NEXT: 0x00000064: 00000132 "bar"
+LOCALUPD-NEXT: 0x00000068: 00000136 "_Z4bar2i"
+LOCALUPD-NEXT: 0x0000006c: 0000013f "bar2"
+LOCALUPD-NEXT: 0x00000070: 00000109 "a"
+LOCALUPD-NEXT: 0x00000074: 00000144 "_Z4bar3i"
+LOCALUPD-NEXT: 0x00000078: 0000014d "bar3"
+LOCALUPD-NEXT: 0x0000007c: 00000119 "x"
+LOCALUPD-NEXT: 0x00000080: 00000120 "int"
+LOCALUPD-NEXT: 0x00000084: Contribution size = 40, Format = DWARF32, Version = 5
+LOCALUPD-NEXT: 0x0000008c: 00000001 "Apple clang version 15.0.0 (clang-1500.0.31.1)"
+LOCALUPD-NEXT: 0x00000090: 00000152 "c.cpp"
+LOCALUPD-NEXT: 0x00000094: 00000036 "/Users/shubham/apple-internal/Xcode-Rainbow/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk"
+LOCALUPD-NEXT: 0x00000098: 000000b7 "MacOSX14.0.sdk"
+LOCALUPD-NEXT: 0x0000009c: 000000c6 "/Users/shubham/Development/test109275485"
+LOCALUPD-NEXT: 0x000000a0: 00000158 "_Z3bazi"
+LOCALUPD-NEXT: 0x000000a4: 00000160 "baz"
+LOCALUPD-NEXT: 0x000000a8: 00000119 "x"
+LOCALUPD-NEXT: 0x000000ac: 00000120 "int"
diff --git a/llvm/test/tools/dsymutil/ARM/empty-map.test b/llvm/test/tools/dsymutil/ARM/empty-map.test
index d80311e5f5c31e..2668d46ed6d7d5 100644
--- a/llvm/test/tools/dsymutil/ARM/empty-map.test
+++ b/llvm/test/tools/dsymutil/ARM/empty-map.test
@@ -1,5 +1,7 @@
# RUN: dsymutil -f -oso-prepend-path=%p/../Inputs -y %s -o - 2>&1 | FileCheck %s
+# RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/../Inputs -y %s -o - 2>&1 | FileCheck %s
+
---
triple: 'thumbv7-apple-darwin'
...
diff --git a/llvm/test/tools/dsymutil/ARM/extern-alias.test b/llvm/test/tools/dsymutil/ARM/extern-alias.test
index cb459fbd662899..f8b59584d2b465 100644
--- a/llvm/test/tools/dsymutil/ARM/extern-alias.test
+++ b/llvm/test/tools/dsymutil/ARM/extern-alias.test
@@ -37,6 +37,10 @@ $ xcrun --sdk iphoneos clang private_extern.o main.o -target arm64-apple-ios14.0
RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/private_extern/private_extern.out -o %t.dSYM --verbose | FileCheck %s
RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/extern/extern.out -o %t.dSYM --verbose | FileCheck %s
+
+RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/private_extern/private_extern.out -o %t.dSYM --verbose | FileCheck %s
+RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/extern/extern.out -o %t.dSYM --verbose | FileCheck %s
+
CHECK-NOT: could not find object file symbol for symbol _baz
CHECK: { sym: _baz, objAddr: 0x0, binAddr: 0x100007F58, size: 0x0 }
CHECK: { sym: _foo, objAddr: 0x0, binAddr: 0x100007F58, size: 0x20 }
diff --git a/llvm/test/tools/dsymutil/ARM/fat-arch-name.test b/llvm/test/tools/dsymutil/ARM/fat-arch-name.test
index 4f7866d4d22de4..4840de13af582f 100644
--- a/llvm/test/tools/dsymutil/ARM/fat-arch-name.test
+++ b/llvm/test/tools/dsymutil/ARM/fat-arch-name.test
@@ -1,5 +1,7 @@
# RUN: dsymutil -no-output %p/../Inputs/fat-test.arm.dylib -o /dev/null -verbose 2>&1 | FileCheck %s
+# RUN: dsymutil --linker llvm -no-output %p/../Inputs/fat-test.arm.dylib -o /dev/null -verbose 2>&1 | FileCheck %s
+
# We detect thumb triples from the binaries, because those are the only ones
# that are guaranteed to be able to generate a Target instance (for example
# we would detect armv7m-apple-darwin as non-thumb triple, but you can't
diff --git a/llvm/test/tools/dsymutil/ARM/fat-arch-not-found.test b/llvm/test/tools/dsymutil/ARM/fat-arch-not-found.test
index b223299204aab6..59d2ccc783840f 100644
--- a/llvm/test/tools/dsymutil/ARM/fat-arch-not-found.test
+++ b/llvm/test/tools/dsymutil/ARM/fat-arch-not-found.test
@@ -1,4 +1,6 @@
# RUN: dsymutil -f -oso-prepend-path=%p/../Inputs -y %s -o - 2>&1 | FileCheck %s
+#
+# RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/../Inputs -y %s -o - 2>&1 | FileCheck %s
---
triple: 'armv7-apple-darwin'
diff --git a/llvm/test/tools/dsymutil/ARM/inlined-low_pc.c b/llvm/test/tools/dsymutil/ARM/inlined-low_pc.c
index 99fe0450dc8348..9cd10be1ec3cf4 100644
--- a/llvm/test/tools/dsymutil/ARM/inlined-low_pc.c
+++ b/llvm/test/tools/dsymutil/ARM/inlined-low_pc.c
@@ -5,6 +5,10 @@ int bar(int a) { return foo(a); }
// RUN: dsymutil -f -y %p/dummy-debug-map-amr64.map -oso-prepend-path %p/../Inputs/inlined-low_pc -o - | llvm-dwarfdump - | FileCheck %s
+// RUN: dsymutil --linker llvm -f -y %p/dummy-debug-map-amr64.map \
+// RUN: -oso-prepend-path %p/../Inputs/inlined-low_pc -o - | \
+// RUN: llvm-dwarfdump - | FileCheck %s
+
// CHECK: DW_TAG_subprogram
// CHECK: DW_AT_low_pc{{.*}}0x0000000000010000
// CHECK: DW_AT_name{{.*}}"bar"
diff --git a/llvm/test/tools/dsymutil/ARM/preload.test b/llvm/test/tools/dsymutil/ARM/preload.test
index 3caa4a22c4a03f..2d1ac9b455d1b5 100644
--- a/llvm/test/tools/dsymutil/ARM/preload.test
+++ b/llvm/test/tools/dsymutil/ARM/preload.test
@@ -7,4 +7,9 @@ $ xcrun clang -o foo foo.o -g3 -Wl,-preload -nodefaultlibs
RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/preload/foo -o %t.dSYM
RUN: llvm-nm %p/../Inputs/private/tmp/preload/foo | FileCheck %s
RUN: llvm-nm %t.dSYM/Contents/Resources/DWARF/foo | FileCheck %s
+
+RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/preload/foo -o %t.dSYM
+RUN: llvm-nm %p/../Inputs/private/tmp/preload/foo | FileCheck %s
+RUN: llvm-nm %t.dSYM/Contents/Resources/DWARF/foo | FileCheck %s
+
CHECK: start
diff --git a/llvm/test/tools/dsymutil/ARM/scattered.c b/llvm/test/tools/dsymutil/ARM/scattered.c
index 81082bcd736bfe..75076f96cadd83 100644
--- a/llvm/test/tools/dsymutil/ARM/scattered.c
+++ b/llvm/test/tools/dsymutil/ARM/scattered.c
@@ -1,4 +1,10 @@
-RUN: dsymutil -y %p/dummy-debug-map.map -oso-prepend-path %p/../Inputs/scattered-reloc/ -f -o - | llvm-dwarfdump -debug-info - | FileCheck %s
+RUN: dsymutil -y %p/dummy-debug-map.map -oso-prepend-path \
+RUN: %p/../Inputs/scattered-reloc/ -f -o - | \
+RUN: llvm-dwarfdump -debug-info - | FileCheck %s
+
+RUN: dsymutil --linker llvm -y %p/dummy-debug-map.map \
+RUN: -oso-prepend-path %p/../Inputs/scattered-reloc/ -f -o - \
+RUN: | llvm-dwarfdump -debug-info - | FileCheck %s
// See Inputs/scattered-reloc/scattered.s to see how this test
// actually works.
diff --git a/llvm/test/tools/dsymutil/ARM/thumb.c b/llvm/test/tools/dsymutil/ARM/thumb.c
index 81602d17e8776d..205ddd07106849 100644
--- a/llvm/test/tools/dsymutil/ARM/thumb.c
+++ b/llvm/test/tools/dsymutil/ARM/thumb.c
@@ -1,6 +1,11 @@
// RUN: dsymutil -f -oso-prepend-path=%p/.. %p/../Inputs/thumb.armv7m -o - | llvm-dwarfdump - | FileCheck %s
// RUN: dsymutil -arch armv7m -f -oso-prepend-path=%p/.. %p/../Inputs/thumb.armv7m -o - | llvm-dwarfdump - | FileCheck %s
+// RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/.. \
+// RUN: %p/../Inputs/thumb.armv7m -o - | llvm-dwarfdump - | FileCheck %s
+// RUN: dsymutil --linker llvm -arch armv7m -f -oso-prepend-path=%p/.. \
+// RUN: %p/../Inputs/thumb.armv7m -o - | llvm-dwarfdump - | FileCheck %s
+
/* Compile with:
clang -c thumb.c -arch armv7m -g
clang thumb.o -o thumb.armv7m -arch armv7m -nostdlib -static -Wl,-e,_start
diff --git a/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-linking-bundle.test b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-linking-bundle.test
new file mode 100644
index 00000000000000..ae92280840e792
--- /dev/null
+++ b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-linking-bundle.test
@@ -0,0 +1,41 @@
+RUN: rm -rf %t
+RUN: mkdir -p %t/dsymdest
+RUN: cat %p/../../Inputs/basic.macho.x86_64 > %t/basic.macho.x86_64
+
+RUN: dsymutil --linker llvm -accelerator=Pub -oso-prepend-path=%p/../.. %t/basic.macho.x86_64
+
+Check that the object file in the bundle exists and is sane:
+RUN: llvm-dwarfdump -a %t/basic.macho.x86_64.dSYM/Contents/Resources/DWARF/basic.macho.x86_64 | FileCheck %S/basic-linking-x86.test
+
+Check that we don't create an empty Remarks directory if there are no remarks.
+RUN: not ls %t/basic.macho.x86_64.dSYM/Contents/Resources/Remarks
+
+Check that llvm-dwarfdump -a recognizes the bundle as a dSYM:
+RUN: llvm-dwarfdump -a %t/basic.macho.x86_64.dSYM | FileCheck %S/basic-linking-x86.test
+
+RUN: FileCheck %s --input-file %t/basic.macho.x86_64.dSYM/Contents/Info.plist
+
+RUN: dsymutil --linker llvm -oso-prepend-path=%p/../.. %t/basic.macho.x86_64 -o %t/dsymdest/basic.macho.x86_64.dSYM
+RUN: llvm-dwarfdump -a %t/dsymdest/basic.macho.x86_64.dSYM/Contents/Resources/DWARF/basic.macho.x86_64 | FileCheck %S/basic-linking-x86.test
+RUN: FileCheck %s --input-file %t/dsymdest/basic.macho.x86_64.dSYM/Contents/Info.plist
+
+CHECK: <?xml version="1.0" encoding="UTF-8"?>
+CHECK-NEXT: <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+CHECK-NEXT: <plist version="1.0">
+CHECK-NEXT: <dict>
+CHECK-NEXT: <key>CFBundleDevelopmentRegion</key>
+CHECK-NEXT: <string>English</string>
+CHECK-NEXT: <key>CFBundleIdentifier</key>
+CHECK-NEXT: <string>com.apple.xcode.dsym.basic.macho.x86_64</string>
+CHECK-NEXT: <key>CFBundleInfoDictionaryVersion</key>
+CHECK-NEXT: <string>6.0</string>
+CHECK-NEXT: <key>CFBundlePackageType</key>
+CHECK-NEXT: <string>dSYM</string>
+CHECK-NEXT: <key>CFBundleSignature</key>
+CHECK-NEXT: <string>????</string>
+CHECK-NEXT: <key>CFBundleShortVersionString</key>
+CHECK-NEXT: <string>1.0</string>
+CHECK-NEXT: <key>CFBundleVersion</key>
+CHECK-NEXT: <string>1</string>
+CHECK-NEXT: </dict>
+CHECK-NEXT: </plist>
diff --git a/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-linking-x86.test b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-linking-x86.test
index ba5bc6f4a3e504..b65529e487237e 100644
--- a/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-linking-x86.test
+++ b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-linking-x86.test
@@ -1,4 +1,190 @@
RUN: cat %p/../../Inputs/basic.macho.x86_64 > %t1
-RUN: dsymutil --linker llvm --no-output -accelerator=Pub -f -oso-prepend-path=%p/../.. %t1 2>&1 | FileCheck %s --allow-empty
+RUN: dsymutil --linker llvm -accelerator=Pub -f -oso-prepend-path=%p/../.. %t1
+RUN: llvm-dwarfdump -a %t1.dwarf | FileCheck %s
+RUN: dsymutil --linker llvm -accelerator=Pub -f -o %t2 -oso-prepend-path=%p/../.. %p/../../Inputs/basic.macho.x86_64
+RUN: llvm-dwarfdump -a %t2 | FileCheck %s
+RUN: dsymutil --linker llvm -accelerator=Pub -f -o - -oso-prepend-path=%p/../.. %p/../../Inputs/basic.macho.x86_64 | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,BASIC
+RUN: dsymutil --linker llvm -accelerator=Pub -f -o - -oso-prepend-path=%p/../.. %p/../../Inputs/basic-archive.macho.x86_64 | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,ARCHIVE
+RUN: dsymutil --linker llvm -accelerator=Pub -dump-debug-map -oso-prepend-path=%p/../.. %p/../../Inputs/basic.macho.x86_64 | dsymutil -accelerator=Pub -f -y -o - - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,BASIC
+RUN: dsymutil --linker llvm -accelerator=Pub -dump-debug-map -oso-prepend-path=%p/../.. %p/../../Inputs/basic-archive.macho.x86_64 | dsymutil -accelerator=Pub -f -o - -y - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,ARCHIVE
-#CHECK: LLVM parallel dwarflinker is not implemented yet.
+CHECK: file format Mach-O 64-bit x86-64
+
+CHECK: debug_info contents
+
+CHECK: Compile Unit:
+
+CHECK: DW_TAG_compile_unit
+CHECK: DW_AT_producer ("Apple LLVM version 6.0 (clang-600.0.39) (based on LLVM 3.5svn)")
+CHECK: DW_AT_language (DW_LANG_C99)
+CHECK: DW_AT_name ("basic1.c")
+CHECK: DW_AT_stmt_list (0x00000000)
+CHECK: DW_AT_comp_dir ("/Inputs")
+CHECK: DW_AT_low_pc (0x0000000100000ea0)
+CHECK: DW_TAG_subprogram
+CHECK: DW_AT_name ("main")
+CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c")
+CHECK: DW_AT_decl_line (23)
+CHECK: DW_AT_prototyped (0x01)
+CHECK: DW_AT_type (0x00000063
+CHECK: DW_AT_external (0x01)
+CHECK: DW_AT_accessibility (DW_ACCESS_public)
+CHECK: DW_AT_low_pc (0x0000000100000ea0)
+CHECK: DW_AT_high_pc (0x0000000100000ec4)
+CHECK: DW_AT_frame_base (DW_OP_reg6 RBP)
+CHECK: DW_TAG_formal_parameter
+CHECK: DW_AT_name ("argc")
+CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c")
+CHECK: DW_AT_decl_line (23)
+CHECK: DW_AT_type (0x00000063
+CHECK: DW_AT_location (DW_OP_fbreg -8)
+CHECK: DW_TAG_formal_parameter
+CHECK: DW_AT_name ("argv")
+CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c")
+CHECK: DW_AT_decl_line (23)
+CHECK: DW_AT_type (0x0000006a
+CHECK: DW_AT_location (DW_OP_fbreg -16)
+CHECK: NULL
+CHECK: DW_TAG_base_type
+CHECK: DW_AT_name ("int")
+CHECK: DW_AT_encoding (DW_ATE_signed)
+CHECK: DW_AT_byte_size (0x04)
+CHECK: DW_TAG_pointer_type
+CHECK: DW_AT_type (0x0000006f
+CHECK: DW_TAG_pointer_type
+CHECK: DW_AT_type (0x00000074
+CHECK: DW_TAG_const_type
+CHECK: DW_AT_type (0x00000079
+CHECK: DW_TAG_base_type
+CHECK: DW_AT_name ("char")
+CHECK: DW_AT_encoding (DW_ATE_signed_char)
+CHECK: DW_AT_byte_size (0x01)
+CHECK: NULL
+
+CHECK: Compile Unit:
+
+CHECK: DW_TAG_compile_unit
+CHECK: DW_AT_producer ("Apple LLVM version 6.0 (clang-600.0.39) (based on LLVM 3.5svn)")
+CHECK: DW_AT_name ("basic2.c")
+CHECK: DW_AT_stmt_list (0x0000003f)
+CHECK: DW_AT_comp_dir ("/Inputs")
+CHECK: DW_AT_low_pc (0x0000000100000ed0)
+CHECK: DW_TAG_base_type
+CHECK: DW_AT_name ("int")
+CHECK: DW_TAG_variable
+CHECK: DW_AT_name ("private_int")
+CHECK: DW_AT_type (0x000000a7
+CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic2.c")
+BASIC: DW_AT_location (DW_OP_addr 0x100001008)
+ARCHIVE: DW_AT_location (DW_OP_addr 0x100001004)
+CHECK: DW_TAG_variable
+CHECK: DW_AT_name ("baz")
+CHECK: DW_AT_type (0x000000a7
+CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic2.c")
+CHECK: DW_AT_location (DW_OP_addr 0x100001000)
+CHECK: DW_TAG_subprogram
+CHECK: DW_AT_name ("foo")
+CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic2.c")
+CHECK: DW_AT_type (0x000000a7
+CHECK: DW_AT_low_pc (0x0000000100000ed0)
+CHECK: DW_AT_high_pc (0x0000000100000f19)
+CHECK: DW_AT_frame_base (DW_OP_reg6 RBP)
+CHECK: DW_TAG_formal_parameter
+CHECK: DW_AT_name ("arg")
+CHECK: DW_AT_type (0x000000a7
+CHECK: DW_AT_location (DW_OP_fbreg -4)
+CHECK: NULL
+CHECK: DW_TAG_subprogram
+CHECK: DW_AT_name ("inc")
+CHECK: DW_AT_type (0x000000a7
+CHECK: DW_AT_low_pc (0x0000000100000f20)
+CHECK: DW_AT_high_pc (0x0000000100000f37)
+CHECK: DW_AT_frame_base (DW_OP_reg6 RBP)
+CHECK: NULL
+
+CHECK: Compile Unit:
+
+CHECK: DW_TAG_compile_unit
+CHECK: DW_AT_producer ("Apple LLVM version 6.0 (clang-600.0.39) (based on LLVM 3.5svn)")
+CHECK: DW_AT_name ("basic3.c")
+CHECK: DW_AT_stmt_list (0x00000093)
+CHECK: DW_AT_comp_dir ("/Inputs")
+CHECK: DW_AT_low_pc (0x0000000100000f40)
+CHECK: DW_TAG_variable
+CHECK: DW_AT_name ("val")
+CHECK: DW_AT_type (0x00000162
+CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic3.c")
+BASIC: DW_AT_location (DW_OP_addr 0x100001004)
+ARCHIVE: DW_AT_location (DW_OP_addr 0x100001008)
+CHECK: DW_TAG_volatile_type
+CHECK: DW_AT_type (0x00000167
+CHECK: DW_TAG_base_type
+CHECK: DW_AT_name ("int")
+CHECK: DW_TAG_subprogram
+CHECK: DW_AT_name ("bar")
+CHECK: DW_AT_type (0x00000167
+CHECK: DW_AT_low_pc (0x0000000100000f40)
+CHECK: DW_AT_high_pc (0x0000000100000f84)
+CHECK: DW_AT_frame_base (DW_OP_reg6 RBP)
+CHECK: DW_TAG_formal_parameter
+CHECK: DW_AT_name ("arg")
+CHECK: DW_AT_type (0x00000167
+CHECK: DW_AT_location (DW_OP_fbreg -8)
+CHECK: NULL
+CHECK: DW_TAG_subprogram
+CHECK: DW_AT_name ("inc")
+CHECK: DW_AT_type (0x00000167
+CHECK: DW_AT_low_pc (0x0000000100000f90)
+CHECK: DW_AT_high_pc (0x0000000100000fa9)
+CHECK: DW_AT_frame_base (DW_OP_reg6 RBP)
+
+CHECK: NULL
+
+CHECK-NOT: .debug_loc contents
+
+CHECK:.debug_aranges contents:
+CHECK-NEXT:Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x00000000, addr_size = 0x08, seg_size = 0x00
+CHECK-NEXT:[0x0000000100000ea0, 0x0000000100000ec4)
+CHECK-NEXT:Address Range Header: length = 0x0000003c, format = DWARF32, version = 0x0002, cu_offset = 0x00000081, addr_size = 0x08, seg_size = 0x00
+CHECK-NEXT:[0x0000000100000ed0, 0x0000000100000f19)
+CHECK-NEXT:[0x0000000100000f20, 0x0000000100000f37)
+CHECK-NEXT:Address Range Header: length = 0x0000003c, format = DWARF32, version = 0x0002, cu_offset = 0x00000126, addr_size = 0x08, seg_size = 0x00
+CHECK-NEXT:[0x0000000100000f40, 0x0000000100000f84)
+CHECK-NEXT:[0x0000000100000f90, 0x0000000100000fa9)
+
+CHECK: .debug_line contents:
+CHECK: file_names[ 1]:
+CHECK-NEXT: name: "basic1.c"
+CHECK-NEXT: dir_index: 0
+CHECK: Address Line Column File ISA Discriminator OpIndex Flags
+CHECK-NEXT: ------------------ ------ ------ ------ --- ------------- ------- -------------
+CHECK-NEXT: 0x0000000100000ea0 23 0 1 0 0 0 is_stmt
+CHECK-NEXT: 0x0000000100000eb6 24 0 1 0 0 0 is_stmt prologue_end
+CHECK-NEXT: 0x0000000100000ec4 24 0 1 0 0 0 is_stmt end_sequence
+
+CHECK: file_names[ 1]:
+CHECK-NEXT: name: "basic2.c"
+CHECK-NEXT: dir_index: 0
+CHECK: Address Line Column File ISA Discriminator OpIndex Flags
+CHECK-NEXT: ------------------ ------ ------ ------ --- ------------- ------- -------------
+CHECK-NEXT: 0x0000000100000ed0 19 0 1 0 0 0 is_stmt
+CHECK-NEXT: 0x0000000100000ee2 20 0 1 0 0 0 is_stmt prologue_end
+CHECK-NEXT: 0x0000000100000f19 20 0 1 0 0 0 is_stmt end_sequence
+CHECK-NEXT: 0x0000000100000f20 14 0 1 0 0 0 is_stmt
+CHECK-NEXT: 0x0000000100000f24 15 0 1 0 0 0 is_stmt prologue_end
+CHECK-NEXT: 0x0000000100000f37 15 0 1 0 0 0 is_stmt end_sequence
+
+CHECK: file_names[ 1]:
+CHECK-NEXT: name: "basic3.c"
+CHECK-NEXT: dir_index: 0
+CHECK: Address Line Column File ISA Discriminator OpIndex Flags
+CHECK-NEXT: ------------------ ------ ------ ------ --- ------------- ------- -------------
+CHECK-NEXT: 0x0000000100000f40 16 0 1 0 0 0 is_stmt
+CHECK-NEXT: 0x0000000100000f4b 17 0 1 0 0 0 is_stmt prologue_end
+CHECK-NEXT: 0x0000000100000f58 18 0 1 0 0 0 is_stmt
+CHECK-NEXT: 0x0000000100000f6c 19 0 1 0 0 0 is_stmt
+CHECK-NEXT: 0x0000000100000f7b 20 0 1 0 0 0 is_stmt
+CHECK-NEXT: 0x0000000100000f84 20 0 1 0 0 0 is_stmt end_sequence
+CHECK-NEXT: 0x0000000100000f90 11 0 1 0 0 0 is_stmt
+CHECK-NEXT: 0x0000000100000f9b 12 0 1 0 0 0 is_stmt prologue_end
+CHECK-NEXT: 0x0000000100000fa9 12 0 1 0 0 0 is_stmt end_sequence
diff --git a/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-lto-dw4-linking-x86.test b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-lto-dw4-linking-x86.test
new file mode 100644
index 00000000000000..75a85d82b4ef28
--- /dev/null
+++ b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-lto-dw4-linking-x86.test
@@ -0,0 +1,183 @@
+RUN: dsymutil --linker llvm -f -o - -oso-prepend-path=%p/../.. %p/../../Inputs/basic-lto-dw4.macho.x86_64 | llvm-dwarfdump -a - | FileCheck %s
+
+CHECK: file format Mach-O 64-bit x86-64
+
+CHECK: debug_info contents
+
+CHECK: Compile Unit: {{.*}} version = 0x0004
+CHECK: DW_TAG_compile_unit
+CHECK: DW_AT_producer ("clang version 3.7.0 ")
+CHECK: DW_AT_language (DW_LANG_C99)
+CHECK: DW_AT_name ("basic1.c")
+CHECK: DW_AT_stmt_list (0x00000000)
+CHECK: DW_AT_comp_dir ("/Inputs")
+CHECK: DW_AT_low_pc (0x0000000100000f40)
+CHECK: DW_AT_high_pc (0x0000000100000f4b)
+CHECK: DW_TAG_subprogram
+CHECK: DW_AT_low_pc (0x0000000100000f40)
+CHECK: DW_AT_high_pc (0x0000000100000f4b)
+CHECK: DW_AT_frame_base (DW_OP_reg6 RBP)
+CHECK: DW_AT_name ("main")
+CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c")
+CHECK: DW_AT_prototyped (true)
+CHECK: DW_AT_type (0x00000000000000a1
+CHECK: DW_AT_external (true)
+CHECK: DW_TAG_formal_parameter
+CHECK: DW_AT_location (DW_OP_reg5 RDI, DW_OP_piece 0x4)
+CHECK: DW_AT_name ("argc")
+CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c")
+CHECK: DW_AT_type (0x00000000000000a1
+CHECK: DW_TAG_formal_parameter
+CHECK: DW_AT_location (DW_OP_reg4 RSI)
+CHECK: DW_AT_name ("argv")
+CHECK: DW_AT_type (0x00000060
+CHECK: NULL
+CHECK: DW_TAG_pointer_type
+CHECK: DW_AT_type (0x00000065
+CHECK: DW_TAG_pointer_type
+CHECK: DW_TAG_const_type
+CHECK: DW_TAG_base_type
+CHECK: DW_AT_name ("char")
+CHECK: DW_AT_encoding (DW_ATE_signed_char)
+CHECK: DW_AT_byte_size (0x01)
+CHECK: NULL
+
+CHECK: Compile Unit:{{.*}} version = 0x0004
+
+CHECK: DW_TAG_compile_unit
+CHECK: DW_AT_producer ("clang version 3.7.0 ")
+CHECK: DW_AT_language (DW_LANG_C99)
+CHECK: DW_AT_name ("basic2.c")
+CHECK: DW_AT_stmt_list (0x00000044)
+CHECK: DW_AT_low_pc (0x0000000100000f50)
+CHECK: DW_AT_high_pc (0x0000000100000f87)
+CHECK: DW_TAG_base_type
+CHECK: DW_AT_name ("int")
+CHECK: DW_TAG_variable
+CHECK: DW_AT_name ("baz")
+CHECK: DW_AT_location (DW_OP_addr 0x100001000)
+CHECK: DW_TAG_variable
+CHECK: DW_AT_name ("private_int")
+CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic2.c")
+CHECK: DW_AT_location (DW_OP_addr 0x100001008)
+CHECK: DW_TAG_subprogram
+CHECK: DW_AT_name ("inc")
+CHECK: DW_AT_type (0x000000a1
+CHECK: DW_AT_inline (DW_INL_inlined)
+CHECK: DW_TAG_subprogram
+CHECK: DW_AT_low_pc (0x0000000100000f50)
+CHECK: DW_AT_high_pc (0x0000000100000f87)
+CHECK: DW_AT_frame_base (DW_OP_reg6 RBP)
+CHECK: DW_AT_name ("foo")
+CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic2.c")
+CHECK: DW_AT_prototyped (true)
+CHECK: DW_AT_type (0x000000a1
+CHECK: DW_TAG_formal_parameter
+CHECK: DW_AT_location (0x00000000
+CHECK: [0x0000000100000f50, 0x0000000100000f5c): DW_OP_reg5 RDI, DW_OP_piece 0x4)
+CHECK: DW_AT_name ("arg")
+CHECK: DW_AT_type (0x000000a1
+CHECK: DW_TAG_inlined_subroutine
+CHECK: DW_AT_abstract_origin (0x000000d2 "inc")
+CHECK: DW_AT_low_pc (0x0000000100000f61)
+CHECK: DW_AT_high_pc (0x0000000100000f70)
+CHECK: NULL
+CHECK: NULL
+
+CHECK: Compile Unit: {{.*}} version = 0x0004
+
+CHECK: DW_TAG_compile_unit
+CHECK: DW_AT_producer ("clang version 3.7.0 ")
+CHECK: DW_AT_name ("basic3.c")
+CHECK: DW_AT_stmt_list (0x0000009a)
+CHECK: DW_AT_low_pc (0x0000000100000f90)
+CHECK: DW_AT_high_pc (0x0000000100000fb4)
+CHECK: DW_TAG_variable
+CHECK: DW_AT_name ("val")
+CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic3.c")
+CHECK: DW_AT_location (DW_OP_addr 0x100001004)
+CHECK: DW_TAG_volatile_type
+CHECK: DW_TAG_subprogram
+CHECK: DW_AT_name ("inc")
+CHECK: DW_AT_inline (DW_INL_inlined)
+CHECK: DW_TAG_subprogram
+CHECK: DW_AT_low_pc (0x0000000100000f90)
+CHECK: DW_AT_high_pc (0x0000000100000fb4)
+CHECK: DW_AT_frame_base (DW_OP_reg6 RBP)
+CHECK: DW_AT_name ("bar")
+CHECK: DW_TAG_formal_parameter
+CHECK: DW_AT_location (0x00000025
+CHECK: [0x0000000100000f90, 0x0000000100000f9f): DW_OP_reg5 RDI, DW_OP_piece 0x4
+CHECK: [0x0000000100000fa9, 0x0000000100000fad): DW_OP_reg5 RDI, DW_OP_piece 0x4)
+CHECK: DW_AT_name ("arg")
+CHECK: DW_TAG_inlined_subroutine
+CHECK: DW_AT_abstract_origin (0x0000015f "inc")
+CHECK: DW_AT_ranges (0x00000000
+CHECK: [0x0000000100000f94, 0x0000000100000f9a)
+CHECK: [0x0000000100000f9f, 0x0000000100000fa7))
+
+CHECK: NULL
+CHECK: NULL
+
+
+CHECK: .debug_loc contents:
+CHECK-NEXT: 0x00000000:
+CHECK-NEXT: (0x0000000000000000, 0x000000000000000c): DW_OP_reg5 RDI, DW_OP_piece 0x4
+CHECK-NOT: :
+CHECK: 0x00000025:
+CHECK-NEXT: (0x0000000000000000, 0x000000000000000f): DW_OP_reg5 RDI, DW_OP_piece 0x4
+CHECK-NEXT: (0x0000000000000019, 0x000000000000001d): DW_OP_reg5 RDI, DW_OP_piece 0x4
+
+
+CHECK: .debug_aranges contents:
+CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x00000000, addr_size = 0x08, seg_size = 0x00
+CHECK-NEXT: [0x0000000100000f40, 0x0000000100000f4b)
+CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x00000077, addr_size = 0x08, seg_size = 0x00
+CHECK-NEXT: [0x0000000100000f50, 0x0000000100000f87)
+CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x0000011b, addr_size = 0x08, seg_size = 0x00
+CHECK-NEXT: [0x0000000100000f90, 0x0000000100000fb4)
+
+CHECK: .debug_line contents:
+CHECK: file_names[ 1]:
+CHECK-NEXT: name: "basic1.c"
+CHECK-NEXT: dir_index: 0
+CHECK: Address Line Column File ISA Discriminator OpIndex Flags
+CHECK-NEXT: ------------------ ------ ------ ------ --- ------------- ------- -------------
+CHECK-NEXT: 0x0000000100000f40 26 0 1 0 0 0 is_stmt
+CHECK-NEXT: 0x0000000100000f44 27 10 1 0 0 0 is_stmt prologue_end
+CHECK-NEXT: 0x0000000100000f49 27 3 1 0 0 0
+CHECK-NEXT: 0x0000000100000f4b 27 3 1 0 0 0 end_sequence
+
+CHECK: file_names[ 1]:
+CHECK-NEXT: name: "basic2.c"
+CHECK-NEXT: dir_index: 0
+CHECK: Address Line Column File ISA Discriminator OpIndex Flags
+CHECK-NEXT: ------------------ ------ ------ ------ --- ------------- ------- -------------
+CHECK-NEXT: 0x0000000100000f50 19 0 1 0 0 0 is_stmt
+CHECK-NEXT: 0x0000000100000f54 20 18 1 0 0 0 is_stmt prologue_end
+CHECK-NEXT: 0x0000000100000f5a 20 17 1 0 0 0
+CHECK-NEXT: 0x0000000100000f5c 20 10 1 0 0 0
+CHECK-NEXT: 0x0000000100000f61 15 10 1 0 0 0 is_stmt
+CHECK-NEXT: 0x0000000100000f70 20 23 1 0 0 0 is_stmt
+CHECK-NEXT: 0x0000000100000f74 20 36 1 0 0 0
+CHECK-NEXT: 0x0000000100000f83 20 31 1 0 0 0
+CHECK-NEXT: 0x0000000100000f85 20 3 1 0 0 0
+CHECK-NEXT: 0x0000000100000f87 20 3 1 0 0 0 end_sequence
+
+CHECK: file_names[ 1]:
+CHECK-NEXT: name: "basic3.c"
+CHECK-NEXT: dir_index: 0
+CHECK: Address Line Column File ISA Discriminator OpIndex Flags
+CHECK-NEXT: ------------------ ------ ------ ------ --- ------------- ------- -------------
+CHECK-NEXT: 0x0000000100000f90 16 0 1 0 0 0 is_stmt
+CHECK-NEXT: 0x0000000100000f94 12 10 1 0 0 0 is_stmt prologue_end
+CHECK-NEXT: 0x0000000100000f9a 17 7 1 0 0 0 is_stmt
+CHECK-NEXT: 0x0000000100000f9f 12 10 1 0 0 0 is_stmt
+CHECK-NEXT: 0x0000000100000fa7 20 1 1 0 0 0 is_stmt
+CHECK-NEXT: 0x0000000100000fa9 19 18 1 0 0 0 is_stmt
+CHECK-NEXT: 0x0000000100000fab 19 10 1 0 0 0
+CHECK-NEXT: 0x0000000100000fb2 20 1 1 0 0 0 is_stmt
+CHECK-NEXT: 0x0000000100000fb4 20 1 1 0 0 0 is_stmt end_sequence
+
+CHECK-NOT: .debug_pubnames contents:
+CHECK-NOT: .debug_pubtypes contents:
diff --git a/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-lto-linking-x86.test b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-lto-linking-x86.test
new file mode 100644
index 00000000000000..2c936530a85a10
--- /dev/null
+++ b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-lto-linking-x86.test
@@ -0,0 +1,182 @@
+RUN: dsymutil --linker llvm -f -o - -oso-prepend-path=%p/../.. %p/../../Inputs/basic-lto.macho.x86_64 | llvm-dwarfdump -a - | FileCheck %s
+RUN: dsymutil --linker llvm -oso-prepend-path=%p/../.. -dump-debug-map %p/../../Inputs/basic-lto.macho.x86_64 | dsymutil -f -o - -y - | llvm-dwarfdump -a - | FileCheck %s
+
+CHECK: file format Mach-O 64-bit x86-64
+
+CHECK: debug_info contents
+
+CHECK: Compile Unit:
+
+CHECK: DW_TAG_compile_unit
+CHECK: DW_AT_producer ("Apple LLVM version 6.0 (clang-600.0.39) (based on LLVM 3.5svn)")
+CHECK: DW_AT_language (DW_LANG_C99)
+CHECK: DW_AT_name ("basic1.c")
+CHECK: DW_AT_stmt_list (0x00000000)
+CHECK: DW_AT_comp_dir ("/Inputs")
+CHECK: DW_AT_low_pc (0x0000000100000f40)
+CHECK: DW_TAG_subprogram
+CHECK: DW_AT_name ("main")
+CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic1.c")
+CHECK: DW_AT_decl_line (23)
+CHECK: DW_AT_prototyped (0x01)
+CHECK: DW_AT_type (0x00000063
+CHECK: DW_AT_external (0x01)
+CHECK: DW_AT_accessibility (DW_ACCESS_public)
+CHECK: DW_AT_low_pc (0x0000000100000f40)
+CHECK: DW_AT_high_pc (0x0000000100000f4b)
+CHECK: DW_AT_frame_base (DW_OP_reg6 RBP)
+CHECK: DW_TAG_formal_parameter
+CHECK: DW_AT_name ("argc")
+CHECK: DW_AT_type (0x00000063
+CHECK: DW_AT_location (DW_OP_reg5 RDI, DW_OP_piece 0x4)
+CHECK: DW_TAG_formal_parameter
+CHECK: DW_AT_name ("argv")
+CHECK: DW_AT_type (0x0000006a
+CHECK: DW_AT_location (DW_OP_reg4 RSI)
+CHECK: NULL
+CHECK: DW_TAG_base_type
+CHECK: DW_AT_name ("int")
+CHECK: DW_AT_encoding (DW_ATE_signed)
+CHECK: DW_AT_byte_size (0x04)
+CHECK: DW_TAG_pointer_type
+CHECK: DW_AT_type (0x0000006f
+CHECK: DW_TAG_pointer_type
+CHECK: DW_AT_type (0x00000074
+CHECK: DW_TAG_const_type
+CHECK: DW_AT_type (0x00000079
+CHECK: DW_TAG_base_type
+CHECK: DW_AT_name ("char")
+CHECK: DW_AT_encoding (DW_ATE_signed_char)
+CHECK: DW_AT_byte_size (0x01)
+CHECK: NULL
+
+CHECK: Compile Unit:
+
+CHECK: DW_TAG_compile_unit
+CHECK: DW_AT_producer ("Apple LLVM version 6.0 (clang-600.0.39) (based on LLVM 3.5svn)")
+CHECK: DW_AT_name ("basic2.c")
+CHECK: DW_AT_stmt_list (0x0000003e)
+CHECK: DW_AT_comp_dir ("/Inputs")
+CHECK: DW_AT_low_pc (0x0000000100000f50)
+CHECK: DW_TAG_variable
+CHECK: DW_AT_name ("private_int")
+CHECK: DW_AT_type (0x0000000000000063
+CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic2.c")
+CHECK: DW_AT_location (DW_OP_addr 0x100001008)
+CHECK: DW_TAG_variable
+CHECK: DW_AT_name ("baz")
+CHECK: DW_AT_type (0x0000000000000063
+CHECK: DW_AT_location (DW_OP_addr 0x100001000)
+CHECK: DW_TAG_subprogram
+CHECK: DW_AT_name ("foo")
+CHECK: DW_AT_type (0x0000000000000063
+CHECK: DW_AT_low_pc (0x0000000100000f50)
+CHECK: DW_AT_high_pc (0x0000000100000f89)
+CHECK: DW_AT_frame_base (DW_OP_reg6 RBP)
+CHECK: DW_TAG_formal_parameter
+CHECK: DW_AT_name ("arg")
+CHECK: DW_AT_type (0x0000000000000063
+CHECK: DW_AT_location (0x00000000
+CHECK: [0x0000000100000f50, 0x0000000100000f5e): DW_OP_reg5 RDI, DW_OP_piece 0x4)
+CHECK:[[INC1:0x[0-9a-f]*]]{{.*}}DW_TAG_inlined_subroutine
+CHECK: DW_AT_abstract_origin (0x00000128 "inc")
+CHECK: DW_AT_low_pc (0x0000000100000f63)
+CHECK: DW_AT_high_pc (0x0000000100000f72)
+CHECK: DW_AT_call_line (20)
+CHECK: NULL
+CHECK: DW_TAG_subprogram
+CHECK: DW_AT_name ("inc")
+CHECK: DW_AT_type (0x0000000000000063
+CHECK: DW_AT_inline (DW_INL_inlined)
+CHECK: NULL
+
+CHECK: Compile Unit:
+
+CHECK: DW_TAG_compile_unit
+CHECK: DW_AT_producer ("Apple LLVM version 6.0 (clang-600.0.39) (based on LLVM 3.5svn)")
+CHECK: DW_AT_name ("basic3.c")
+CHECK: DW_AT_stmt_list (0x0000007e)
+CHECK: DW_AT_comp_dir ("/Inputs")
+CHECK: DW_AT_low_pc (0x0000000100000f90)
+CHECK: DW_TAG_variable
+CHECK: DW_AT_name ("val")
+CHECK: DW_AT_type (0x00000176
+CHECK: DW_AT_decl_file ("/Inputs{{[/\\]}}basic3.c")
+CHECK: DW_AT_location (DW_OP_addr 0x100001004)
+CHECK: DW_TAG_volatile_type
+CHECK: DW_AT_type (0x0000000000000063
+CHECK: DW_TAG_subprogram
+CHECK: DW_AT_name ("bar")
+CHECK: DW_AT_type (0x0000000000000063
+CHECK: DW_AT_low_pc (0x0000000100000f90)
+CHECK: DW_AT_high_pc (0x0000000100000fb4)
+CHECK: DW_AT_frame_base (DW_OP_reg6 RBP)
+CHECK: DW_TAG_formal_parameter
+CHECK: DW_AT_name ("arg")
+CHECK: DW_AT_type (0x0000000000000063
+CHECK: DW_AT_location (0x00000025
+CHECK: [0x0000000100000f90, 0x0000000100000f9f): DW_OP_reg5 RDI, DW_OP_piece 0x4
+CHECK: [0x0000000100000fa9, 0x0000000100000fad): DW_OP_reg5 RDI, DW_OP_piece 0x4)
+CHECK: DW_TAG_lexical_block
+CHECK: DW_AT_low_pc (0x0000000100000f94)
+CHECK: DW_AT_high_pc (0x0000000100000fa7)
+CHECK:[[INC2:0x[0-9a-f]*]]{{.*}}DW_TAG_inlined_subroutine
+CHECK: DW_AT_abstract_origin (0x000001d4 "inc")
+CHECK: DW_AT_ranges (0x00000000
+CHECK: [0x0000000100000f94, 0x0000000100000f9a)
+CHECK: [0x0000000100000f9f, 0x0000000100000fa7))
+CHECK: NULL
+CHECK: NULL
+CHECK: DW_TAG_subprogram
+CHECK: DW_AT_name ("inc")
+CHECK: DW_AT_type (0x0000000000000063
+CHECK: NULL
+
+CHECK: .debug_loc contents:
+CHECK-NEXT: 0x00000000:
+CHECK-NEXT: (0x0000000000000000, 0x000000000000000e): DW_OP_reg5 RDI, DW_OP_piece 0x4
+CHECK-NOT: :
+CHECK: 0x00000025:
+CHECK-NEXT: (0x0000000000000000, 0x000000000000000f): DW_OP_reg5 RDI, DW_OP_piece 0x4
+CHECK-NEXT: (0x0000000000000019, 0x000000000000001d): DW_OP_reg5 RDI, DW_OP_piece 0x4
+
+CHECK: .debug_aranges contents:
+CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x00000000, addr_size = 0x08, seg_size = 0x00
+CHECK-NEXT: [0x0000000100000f40, 0x0000000100000f4b)
+CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x00000081, addr_size = 0x08, seg_size = 0x00
+CHECK-NEXT: [0x0000000100000f50, 0x0000000100000f89)
+CHECK-NEXT: Address Range Header: length = 0x0000002c, format = DWARF32, version = 0x0002, cu_offset = 0x0000013a, addr_size = 0x08, seg_size = 0x00
+CHECK-NEXT: [0x0000000100000f90, 0x0000000100000fb4)
+
+
+CHECK: .debug_line contents
+CHECK: file_names[ 1]:
+CHECK-NEXT: name: "basic1.c"
+CHECK-NEXT: dir_index: 0
+CHECK: 0x0000000100000f40 23 0 1 0 0 0 is_stmt
+CHECK: 0x0000000100000f44 24 0 1 0 0 0 is_stmt prologue_end
+CHECK: 0x0000000100000f4b 24 0 1 0 0 0 is_stmt end_sequence
+
+CHECK: file_names[ 1]:
+CHECK-NEXT: name: "basic2.c"
+CHECK-NEXT: dir_index: 0
+CHECK: 0x0000000100000f50 19 0 1 0 0 0 is_stmt
+CHECK: 0x0000000100000f54 20 0 1 0 0 0 is_stmt prologue_end
+CHECK: 0x0000000100000f63 15 0 1 0 0 0 is_stmt
+CHECK: 0x0000000100000f72 20 0 1 0 0 0 is_stmt
+CHECK: 0x0000000100000f89 20 0 1 0 0 0 is_stmt end_sequence
+
+CHECK: file_names[ 1]:
+CHECK-NEXT: name: "basic3.c"
+CHECK-NEXT: dir_index: 0
+CHECK: 0x0000000100000f90 16 0 1 0 0 0 is_stmt
+CHECK: 0x0000000100000f94 12 0 1 0 0 0 is_stmt prologue_end
+CHECK: 0x0000000100000f9a 17 0 1 0 0 0 is_stmt
+CHECK: 0x0000000100000f9f 12 0 1 0 0 0 is_stmt
+CHECK: 0x0000000100000fa7 20 0 1 0 0 0 is_stmt
+CHECK: 0x0000000100000fa9 19 0 1 0 0 0 is_stmt
+CHECK: 0x0000000100000fb2 20 0 1 0 0 0 is_stmt
+CHECK: 0x0000000100000fb4 20 0 1 0 0 0 is_stmt end_sequence
+
+CHECK-NOT: .debug_pubnames contents:
+CHECK-NOT: .debug_pubtypes contents:
diff --git a/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-with-libfat-test.test b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-with-libfat-test.test
new file mode 100644
index 00000000000000..21ddd3b5d29517
--- /dev/null
+++ b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/basic-with-libfat-test.test
@@ -0,0 +1,12 @@
+RUN: cat %p/../../Inputs/basic-with-libfat-test.macho.x86_64 > %t1
+RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/../.. %t1
+RUN: llvm-dwarfdump %t1.dwarf | FileCheck %s
+
+The test binary was created by force-linking the libfat-test.a fat archive
+with the basic linking test archive, like so:
+$ clang -all_load libfat-test.a libbasic.a basic1.macho.x86_64.o -Wl,-dead_strip -u _x86_64_var
+
+CHECK: DW_AT_name{{.*}}"x86_64_var"
+CHECK: DW_AT_name{{.*}}"basic2.c"
+CHECK: DW_AT_name{{.*}}"basic3.c"
+CHECK: DW_AT_name{{.*}}"basic1.c"
diff --git a/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/empty_range.s b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/empty_range.s
new file mode 100644
index 00000000000000..603867250d3b74
--- /dev/null
+++ b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/empty_range.s
@@ -0,0 +1,53 @@
+# This test verifies that an empty range list in the .debug_ranges section
+# doesn't crash dsymutil. As clang does not produce this kind of debug
+# info anymore, we used this hand-crafted assembly file to produce a testcase
+# Compile with:
+# llvm-mc -triple x86_64-apple-darwin -filetype=obj -o 1.o empty_range.o
+
+# RUN: dsymutil --linker llvm -f -y %p/../dummy-debug-map.map -oso-prepend-path %p/../../Inputs/empty_range -o - | llvm-dwarfdump -debug-info - | FileCheck %s
+
+ .section __TEXT,__text,regular,pure_instructions
+ .macosx_version_min 10, 11
+ .globl __Z3foov
+ .align 4, 0x90
+__Z3foov: ## @_Z3foov
+Lfunc_begin0:
+ pushq %rbp
+ movq %rsp, %rbp
+ popq %rbp
+ retq
+Lfunc_end0:
+ .section __DWARF,__debug_abbrev,regular,debug
+Lsection_abbrev:
+ .byte 1 ## Abbreviation Code
+ .byte 17 ## DW_TAG_compile_unit
+ .byte 1 ## DW_CHILDREN_yes
+ .byte 0 ## EOM(1)
+ .byte 0 ## EOM(2)
+ .byte 2 ## Abbreviation Code
+ .byte 46 ## DW_TAG_subprogram
+ .byte 0 ## DW_CHILDREN_no
+ .byte 17 ## DW_AT_low_pc
+ .byte 1 ## DW_FORM_addr
+ .byte 0x55 ## DW_AT_ranges
+ .byte 6 ## DW_FORM_data4
+ .byte 0 ## EOM(1)
+ .byte 0 ## EOM(2)
+ .byte 0 ## EOM(3)
+ .section __DWARF,__debug_info,regular,debug
+Lsection_info:
+ .long 22 ## Length of Unit
+ .short 2 ## DWARF version number
+ .long 0 ## Offset Into Abbrev. Section
+ .byte 8 ## Address Size (in bytes)
+ .byte 1 ## Abbrev [1] DW_TAG_compile_unit
+ .byte 2 ## Abbrev [2] DW_TAG_subprogram
+ .quad Lfunc_begin0 ## DW_AT_low_pc
+ .long 0 ## DW_AT_ranges (pointing at an empty entry)
+ .byte 0 ## End Of Children Mark
+ .section __DWARF,__debug_ranges,regular,debug
+Ldebug_range:
+ .long 0
+ .long 0
+
+# CHECK-NOT: DW_TAG_compile_unit
diff --git a/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/frame-1.test b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/frame-1.test
new file mode 100644
index 00000000000000..dbdbb455eab16a
--- /dev/null
+++ b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/frame-1.test
@@ -0,0 +1,36 @@
+# RUN: rm -rf %t
+# RUN: mkdir -p %t
+# RUN: llc -filetype=obj %p/../../Inputs/frame-dw2.ll -o %t/frame-dw2.o
+# RUN: dsymutil --linker llvm -f -oso-prepend-path=%t -y %s -o - | \
+# RUN: llvm-dwarfdump -debug-frame - | FileCheck %s
+
+# This test is meant to verify that identical CIEs will get reused
+# in the same file but not inbetween files. For this to happen, we
+# link twice the same file using this made-up debug map:
+
+---
+triple: 'i386-apple-darwin'
+objects:
+ - filename: frame-dw2.o
+ symbols:
+ - { sym: _bar, objAddr: 0x0, binAddr: 0x1000, size: 0x12 }
+ - { sym: _baz, objAddr: 0x0, binAddr: 0x2000, size: 0x12 }
+ - filename: frame-dw2.o
+ symbols:
+ - { sym: _bar, objAddr: 0x0, binAddr: 0x3000, size: 0x12 }
+ - { sym: _baz, objAddr: 0x0, binAddr: 0x4000, size: 0x12 }
+...
+
+# CHECK: .debug_frame contents:
+# CHECK: 00000000 {{[0-9a-f]*}} ffffffff CIE
+# CHECK-NOT: FDE
+# CHECK: FDE cie=00000000 pc=00001000...00001
+# CHECK-NOT: FDE
+# CHECK: FDE cie=00000000 pc=00002000...00002
+# CHECK: [[CIECU2:[0-9a-f]*]] {{[0-9a-f]*}} ffffffff CIE
+# CHECK-NOT: FDE
+# CHECK: FDE cie=[[CIECU2]] pc=00003000...00003
+# CHECK-NOT: FDE
+# CHECK: FDE cie=[[CIECU2]] pc=00004000...00004
+# CHECK-NOT: FDE
+# CHECK: .eh_frame contents:
diff --git a/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/frame-2.test b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/frame-2.test
new file mode 100644
index 00000000000000..ffb559050dabbd
--- /dev/null
+++ b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/frame-2.test
@@ -0,0 +1,46 @@
+# RUN: rm -rf %t
+# RUN: mkdir -p %t
+# RUN: llc -filetype=obj %p/../../Inputs/frame-dw2.ll -o %t/frame-dw2.o
+# RUN: llc -filetype=obj %p/../../Inputs/frame-dw4.ll -o %t/frame-dw4.o
+# RUN: dsymutil --linker llvm -f -oso-prepend-path=%t -y %s -o - | \
+# RUN: llvm-dwarfdump -debug-frame - | FileCheck %s
+
+# Check the handling of multiple
diff erent CIEs. To have CIEs that
+# appear to be
diff erent, use a dwarf2 version of the file along with
+# a dwarf 4 version. The CIE header version (and layout) will be
diff erent.
+---
+triple: 'i386-apple-darwin'
+objects:
+ - filename: frame-dw2.o
+ symbols:
+ - { sym: _bar, objAddr: 0x0, binAddr: 0x1000, size: 0x12 }
+ - { sym: _baz, objAddr: 0x0, binAddr: 0x2000, size: 0x12 }
+ - filename: frame-dw4.o
+ symbols:
+ - { sym: _baz, objAddr: 0x0, binAddr: 0x3000, size: 0x12 }
+ - filename: frame-dw2.o
+ symbols:
+ - { sym: _bar, objAddr: 0x0, binAddr: 0x4000, size: 0x12 }
+...
+
+# CHECK: .debug_frame contents:
+# CHECK: 00000000 {{[0-9a-f]*}} ffffffff CIE
+# CHECK-NEXT: Format: DWARF32
+# CHECK-NEXT: Version:{{.*}}1
+# CHECK-NOT: FDE
+# CHECK: FDE cie=00000000 pc=00001000...00001
+# CHECK-NOT: FDE
+# CHECK: FDE cie=00000000 pc=00002000...00002
+# CHECK-NOT: FDE
+# CHECK: [[CIEDW4:[0-9a-f]*]] 00000010 ffffffff CIE
+# CHECK-NEXT: Format: DWARF32
+# CHECK-NEXT: Version:{{.*}}4
+# CHECK-NOT: FDE
+# CHECK: FDE cie=[[CIEDW4]] pc=00003000...00003
+# CHECK: [[CIEDW1_2:[0-9a-f]*]] 00000010 ffffffff CIE
+# CHECK-NEXT: Format: DWARF32
+# CHECK-NEXT: Version:{{.*}}1
+# CHECK-NOT: FDE
+# CHECK: FDE cie=[[CIEDW1_2]] pc=00004000...00004
+# CHECK-NOT: FDE
+# CHECK: .eh_frame contents:
diff --git a/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/multiple-inputs.test b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/multiple-inputs.test
new file mode 100644
index 00000000000000..9adcdcbc4ba719
--- /dev/null
+++ b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/multiple-inputs.test
@@ -0,0 +1,30 @@
+RUN: rm -rf %t
+RUN: mkdir -p %t
+
+RUN: cat %p/../../Inputs/basic.macho.x86_64 > %t/basic.macho.x86_64
+RUN: cat %p/../../Inputs/basic-archive.macho.x86_64 > %t/basic-archive.macho.x86_64
+RUN: cat %p/../../Inputs/basic-lto.macho.x86_64 > %t/basic-lto.macho.x86_64
+RUN: cat %p/../../Inputs/basic-lto-dw4.macho.x86_64 > %t/basic-lto-dw4.macho.x86_64
+
+# Multiple inputs in flat mode
+RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/../.. %t/basic.macho.x86_64 %t/basic-archive.macho.x86_64 %t/basic-lto.macho.x86_64 %t/basic-lto-dw4.macho.x86_64
+RUN: llvm-dwarfdump -a %t/basic.macho.x86_64.dwarf \
+RUN: | FileCheck %S/basic-linking-x86.test --check-prefixes=CHECK,BASIC
+RUN: llvm-dwarfdump -a %t/basic-archive.macho.x86_64.dwarf \
+RUN: | FileCheck %S/basic-linking-x86.test --check-prefixes=CHECK,ARCHIVE
+RUN: llvm-dwarfdump -a %t/basic-lto.macho.x86_64.dwarf | FileCheck %S/basic-lto-linking-x86.test
+RUN: llvm-dwarfdump -a %t/basic-lto-dw4.macho.x86_64.dwarf | FileCheck %S/basic-lto-dw4-linking-x86.test
+
+# Multiple inputs that end up in the same named bundle
+RUN: dsymutil --linker llvm -oso-prepend-path=%p/../.. %t/basic.macho.x86_64 %t/basic-archive.macho.x86_64 %t/basic-lto.macho.x86_64 %t/basic-lto-dw4.macho.x86_64 -o %t.dSYM
+RUN: llvm-dwarfdump -a %t.dSYM/Contents/Resources/DWARF/basic.macho.x86_64 \
+RUN: | FileCheck %S/basic-linking-x86.test --check-prefixes=CHECK,BASIC
+RUN: llvm-dwarfdump -a %t.dSYM/Contents/Resources/DWARF/basic-archive.macho.x86_64 \
+RUN: | FileCheck %S/basic-linking-x86.test --check-prefixes=CHECK,ARCHIVE
+RUN: llvm-dwarfdump -a %t.dSYM/Contents/Resources/DWARF/basic-lto.macho.x86_64 | FileCheck %S/basic-lto-linking-x86.test
+RUN: llvm-dwarfdump -a %t.dSYM/Contents/Resources/DWARF/basic-lto-dw4.macho.x86_64 | FileCheck %S/basic-lto-dw4-linking-x86.test
+
+# Multiple inputs in a named bundle in flat mode... impossible.
+RUN: not dsymutil --linker llvm -f -oso-prepend-path=%p/../.. %t/basic.macho.x86_64 %t/basic-archive.macho.x86_64 %t/basic-lto.macho.x86_64 %t/basic-lto-dw4.macho.x86_64 -o %t.dSYM 2>&1 | FileCheck %s
+
+CHECK: error: cannot use -o with multiple inputs in flat mode
diff --git a/llvm/test/tools/dsymutil/X86/alias.test b/llvm/test/tools/dsymutil/X86/alias.test
index 2e56ba5719b158..4c1e8168706098 100644
--- a/llvm/test/tools/dsymutil/X86/alias.test
+++ b/llvm/test/tools/dsymutil/X86/alias.test
@@ -1,5 +1,9 @@
# RUN: dsymutil -f -oso-prepend-path=%p/../Inputs/alias \
# RUN: %p/../Inputs/alias/foobar -o - | llvm-dwarfdump - 2>&1 | FileCheck %s
+
+# RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/../Inputs/alias \
+# RUN: %p/../Inputs/alias/foobar -o - | llvm-dwarfdump - 2>&1 | FileCheck %s
+
# CHECK-NOT: could not find object file symbol for symbol
# CHECK: DW_AT_name ("foo.c")
# CHECK: DW_AT_name ("bar.c")
diff --git a/llvm/test/tools/dsymutil/X86/call-site-entry-linking.test b/llvm/test/tools/dsymutil/X86/call-site-entry-linking.test
index 7eef7af41f5252..2ef400f5d7120f 100644
--- a/llvm/test/tools/dsymutil/X86/call-site-entry-linking.test
+++ b/llvm/test/tools/dsymutil/X86/call-site-entry-linking.test
@@ -1,4 +1,7 @@
RUN: dsymutil -oso-prepend-path=%p %p/Inputs/call-site-entry.macho.x86_64 -o %t.dSYM
RUN: llvm-dwarfdump %t.dSYM | FileCheck %s -implicit-check-not=DW_AT_call_return_pc
+RUN: dsymutil --linker llvm -oso-prepend-path=%p %p/Inputs/call-site-entry.macho.x86_64 -o %t.dSYM
+RUN: llvm-dwarfdump %t.dSYM | FileCheck %s -implicit-check-not=DW_AT_call_return_pc
+
CHECK: DW_AT_call_return_pc (0x0000000100000fa4)
diff --git a/llvm/test/tools/dsymutil/X86/call-site-entry-reloc.test b/llvm/test/tools/dsymutil/X86/call-site-entry-reloc.test
index 46e947044ffcaa..a03ce06d6545b7 100644
--- a/llvm/test/tools/dsymutil/X86/call-site-entry-reloc.test
+++ b/llvm/test/tools/dsymutil/X86/call-site-entry-reloc.test
@@ -22,5 +22,8 @@ twice.
RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/call_return_pc/call -o %t.dSYM
RUN: llvm-dwarfdump %t.dSYM | FileCheck %s -implicit-check-not=DW_AT_call_return_pc
+RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/call_return_pc/call -o %t.dSYM
+RUN: llvm-dwarfdump %t.dSYM | FileCheck %s -implicit-check-not=DW_AT_call_return_pc
+
CHECK: DW_AT_call_return_pc (0x0000000100000f72)
CHECK: DW_AT_call_return_pc (0x0000000100000f78)
diff --git a/llvm/test/tools/dsymutil/X86/common-sym-multi.test b/llvm/test/tools/dsymutil/X86/common-sym-multi.test
index ff970a53bfecf0..9ce14bb541c2ee 100644
--- a/llvm/test/tools/dsymutil/X86/common-sym-multi.test
+++ b/llvm/test/tools/dsymutil/X86/common-sym-multi.test
@@ -1,6 +1,9 @@
RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/common/common.x86_64 -f -o - | llvm-dwarfdump -debug-info - | FileCheck %s
RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/common/common.x86_64 -dump-debug-map | FileCheck %s --check-prefix DEBUGMAP
+RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/common/common.x86_64 -f -o - | llvm-dwarfdump -debug-info - | FileCheck %s
+RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/common/common.x86_64 -dump-debug-map | FileCheck %s --check-prefix DEBUGMAP
+
The test was compiled from two source files:
$ cd /private/tmp/common
$ cat common1.c
diff --git a/llvm/test/tools/dsymutil/X86/common-sym.test b/llvm/test/tools/dsymutil/X86/common-sym.test
index 0f07c8f39b81aa..796866c4743caa 100644
--- a/llvm/test/tools/dsymutil/X86/common-sym.test
+++ b/llvm/test/tools/dsymutil/X86/common-sym.test
@@ -1,5 +1,7 @@
RUN: dsymutil -oso-prepend-path %p/.. %p/../Inputs/common.macho.x86_64 -f -o - | llvm-dwarfdump -v -debug-info - | FileCheck %s
+RUN: dsymutil --linker llvm -oso-prepend-path %p/.. %p/../Inputs/common.macho.x86_64 -f -o - | llvm-dwarfdump -v -debug-info - | FileCheck %s
+
The test was compiled from a single source:
$ cat common.c
char common[16];
diff --git a/llvm/test/tools/dsymutil/X86/custom-line-table.test b/llvm/test/tools/dsymutil/X86/custom-line-table.test
index f75e01a12fba9c..8a164655ec21a3 100644
--- a/llvm/test/tools/dsymutil/X86/custom-line-table.test
+++ b/llvm/test/tools/dsymutil/X86/custom-line-table.test
@@ -1,4 +1,6 @@
# RUN: dsymutil -oso-prepend-path %p/../Inputs -y %s -f -o - | llvm-dwarfdump - --debug-line | FileCheck %s
+#
+# RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs -y %s -f -o - | llvm-dwarfdump - --debug-line | FileCheck %s
# This test runs dsymutil on an object file with non-standard (as far
# as llvm is concerned) line table settings.
diff --git a/llvm/test/tools/dsymutil/X86/darwin-bundle.test b/llvm/test/tools/dsymutil/X86/darwin-bundle.test
index d44b25e4870545..80a42217edff19 100644
--- a/llvm/test/tools/dsymutil/X86/darwin-bundle.test
+++ b/llvm/test/tools/dsymutil/X86/darwin-bundle.test
@@ -11,6 +11,13 @@ RUN: FileCheck %s --input-file %t/dsymdest/basic.macho.x86_64.dSYM/Contents/Info
RUN: dsymutil -oso-prepend-path=%p/.. %t/basic.macho.x86_64 -toolchain "toolchain&and'some<symbols" -o %t/dsymdest/basic.macho.x86_64.dSYM
RUN: FileCheck %s --input-file %t/dsymdest/basic.macho.x86_64.dSYM/Contents/Info.plist --check-prefix=CHECK --check-prefix=TOOLCHAIN
+RUN: dsymutil --linker llvm -oso-prepend-path=%p/.. %t/basic.macho.x86_64 -o %t/dsymdest/basic.macho.x86_64.dSYM
+RUN: FileCheck %s --input-file %t/dsymdest/basic.macho.x86_64.dSYM/Contents/Info.plist
+
+RUN: dsymutil --linker llvm -oso-prepend-path=%p/.. %t/basic.macho.x86_64 -toolchain "toolchain&and'some<symbols" -o %t/dsymdest/basic.macho.x86_64.dSYM
+RUN: FileCheck %s --input-file %t/dsymdest/basic.macho.x86_64.dSYM/Contents/Info.plist --check-prefix=CHECK --check-prefix=TOOLCHAIN
+
+
CHECK: <?xml version="1.0" encoding="UTF-8"?>
CHECK-NEXT: <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
CHECK-NEXT: <plist version="1.0">
diff --git a/llvm/test/tools/dsymutil/X86/dead-stripped.cpp b/llvm/test/tools/dsymutil/X86/dead-stripped.cpp
index 6ea5ef22a65acb..8a72aee6fb38fb 100644
--- a/llvm/test/tools/dsymutil/X86/dead-stripped.cpp
+++ b/llvm/test/tools/dsymutil/X86/dead-stripped.cpp
@@ -1,5 +1,10 @@
// RUN: dsymutil -f -y %p/dummy-debug-map.map -oso-prepend-path %p/../Inputs/dead-stripped -o - | llvm-dwarfdump - --debug-info | FileCheck %s --implicit-check-not "{{DW_AT_low_pc|DW_AT_high_pc|DW_AT_location|DW_TAG|NULL}}"
+// RUN: dsymutil --linker llvm -f -y %p/dummy-debug-map.map -oso-prepend-path \
+// RUN: %p/../Inputs/dead-stripped -o - | llvm-dwarfdump - --debug-info | \
+// RUN: FileCheck %s --implicit-check-not \
+// RUN: "{{DW_AT_low_pc|DW_AT_high_pc|DW_AT_location|DW_TAG|NULL}}"
+
// The test was compiled with:
// clang++ -O2 -g -c dead-strip.cpp -o 1.o
diff --git a/llvm/test/tools/dsymutil/X86/debug-loc-base-addr.test b/llvm/test/tools/dsymutil/X86/debug-loc-base-addr.test
index a36e9139bcf0b7..341f64dec4090e 100644
--- a/llvm/test/tools/dsymutil/X86/debug-loc-base-addr.test
+++ b/llvm/test/tools/dsymutil/X86/debug-loc-base-addr.test
@@ -1,5 +1,7 @@
RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/baseaddr/loc1.x86_64 -f -o - | llvm-dwarfdump --debug-info - | FileCheck %s
+RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/baseaddr/loc1.x86_64 -f -o - | llvm-dwarfdump --debug-info - | FileCheck %s
+
The test was compiled from a single source:
$ cat loc1.cpp
int f1(int i, int j) {
diff --git a/llvm/test/tools/dsymutil/X86/dwarf4-linetable.test b/llvm/test/tools/dsymutil/X86/dwarf4-linetable.test
index 4de68abbf062ed..eb3e8fb2a714b9 100644
--- a/llvm/test/tools/dsymutil/X86/dwarf4-linetable.test
+++ b/llvm/test/tools/dsymutil/X86/dwarf4-linetable.test
@@ -1,5 +1,7 @@
# RUN: dsymutil -f -oso-prepend-path=%p/../Inputs/ -y %s -o - | llvm-dwarfdump -debug-line - | FileCheck %s
+# RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/../Inputs/ -y %s -o - | llvm-dwarfdump -debug-line - | FileCheck %s
+
# Source:
# int main() {
# return 0;
diff --git a/llvm/test/tools/dsymutil/X86/dwarf5-addrx.test b/llvm/test/tools/dsymutil/X86/dwarf5-addrx.test
index 463c51a1b068ee..fd6197bdc5fb42 100644
--- a/llvm/test/tools/dsymutil/X86/dwarf5-addrx.test
+++ b/llvm/test/tools/dsymutil/X86/dwarf5-addrx.test
@@ -52,6 +52,17 @@ RUN: llvm-dwarfdump --verify %t.dSYM 2>&1 | FileCheck %s
RUN: llvm-dwarfdump --verbose %t.dSYM | FileCheck %s --check-prefix UPDATE-DWARF
+RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/dwarf5/dwarf5-addrx.out -o %t.dSYM 2>&1 | FileCheck %s --allow-empty
+RUN: llvm-dwarfdump --verify %t.dSYM 2>&1 | FileCheck %s
+
+RUN: llvm-dwarfdump --verbose %t.dSYM | FileCheck %s --check-prefix DWARF
+
+RUN: dsymutil --linker llvm --update -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/dwarf5/dwarf5-addrx.out -o %t.dSYM 2>&1 | FileCheck %s --allow-empty
+RUN: llvm-dwarfdump --verify %t.dSYM 2>&1 | FileCheck %s
+
+RUN: llvm-dwarfdump --verbose %t.dSYM | FileCheck %s --check-prefix UPDATE-DWARF
+
+
CHECK-NOT: error:
DWARF: DW_TAG_compile_unit
diff --git a/llvm/test/tools/dsymutil/X86/dwarf5-call-site-entry-reloc.test b/llvm/test/tools/dsymutil/X86/dwarf5-call-site-entry-reloc.test
index b960d4496ce7d7..bc17a456b2b4c0 100644
--- a/llvm/test/tools/dsymutil/X86/dwarf5-call-site-entry-reloc.test
+++ b/llvm/test/tools/dsymutil/X86/dwarf5-call-site-entry-reloc.test
@@ -22,6 +22,9 @@
#RUN: dsymutil -oso-prepend-path %p/../Inputs -y %s -o %t.dSYM
#RUN: llvm-dwarfdump %t.dSYM | FileCheck %s -implicit-check-not=DW_AT_call_return_pc
+#RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs -y %s -o %t.dSYM
+#RUN: llvm-dwarfdump %t.dSYM | FileCheck %s -implicit-check-not=DW_AT_call_return_pc
+
#CHECK: DW_AT_call_return_pc (0x0000000100000f72)
#CHECK: DW_AT_call_return_pc (0x0000000100000f78)
diff --git a/llvm/test/tools/dsymutil/X86/dwarf5-dw-op-addrx.test b/llvm/test/tools/dsymutil/X86/dwarf5-dw-op-addrx.test
index 34100145b17de6..f78a3102e18a5e 100644
--- a/llvm/test/tools/dsymutil/X86/dwarf5-dw-op-addrx.test
+++ b/llvm/test/tools/dsymutil/X86/dwarf5-dw-op-addrx.test
@@ -25,6 +25,14 @@
#RUN: llvm-dwarfdump --verify %t.dSYM | FileCheck %s
#RUN: llvm-dwarfdump -a --verbose %t.dSYM | FileCheck %s --check-prefix UPD-DWARF-CHECK
+#RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs -y %s -o %t.dSYM
+#RUN: llvm-dwarfdump --verify %t.dSYM | FileCheck %s
+#RUN: llvm-dwarfdump -a --verbose %t.dSYM | FileCheck %s --check-prefix DWARF-CHECK
+
+#RUN: dsymutil --linker llvm --update -oso-prepend-path %p/../Inputs -y %s -o %t.dSYM
+#RUN: llvm-dwarfdump --verify %t.dSYM | FileCheck %s
+#RUN: llvm-dwarfdump -a --verbose %t.dSYM | FileCheck %s --check-prefix UPD-DWARF-CHECK
+
#CHECK: No errors.
#DWARF-CHECK: DW_TAG_compile_unit
diff --git a/llvm/test/tools/dsymutil/X86/dwarf5-linetable.test b/llvm/test/tools/dsymutil/X86/dwarf5-linetable.test
index 008e133ec63015..a07e13038e94ba 100644
--- a/llvm/test/tools/dsymutil/X86/dwarf5-linetable.test
+++ b/llvm/test/tools/dsymutil/X86/dwarf5-linetable.test
@@ -1,5 +1,7 @@
# RUN: dsymutil -f -oso-prepend-path=%p/../Inputs/ -y %s -o - | llvm-dwarfdump -debug-line -debug-line-str --verbose - | FileCheck %s
+# RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/../Inputs/ -y %s -o - | llvm-dwarfdump -debug-line -debug-line-str --verbose - | FileCheck %s
+
# Source:
# int main() {
# return 0;
diff --git a/llvm/test/tools/dsymutil/X86/dwarf5-loclists.test b/llvm/test/tools/dsymutil/X86/dwarf5-loclists.test
index ccb458c35c41aa..f7133d638d32b6 100644
--- a/llvm/test/tools/dsymutil/X86/dwarf5-loclists.test
+++ b/llvm/test/tools/dsymutil/X86/dwarf5-loclists.test
@@ -24,6 +24,14 @@
#RUN: llvm-dwarfdump --verify %t.dSYM | FileCheck %s
#RUN: llvm-dwarfdump -a --verbose %t.dSYM | FileCheck %s --check-prefix UPD-DWARF-CHECK
+#RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs -y %s -o %t.dSYM
+#RUN: llvm-dwarfdump --verify %t.dSYM | FileCheck %s
+#RUN: llvm-dwarfdump -a --verbose %t.dSYM | FileCheck %s --check-prefix DWARF-CHECK
+
+#RUN: dsymutil --linker llvm --update -oso-prepend-path %p/../Inputs -y %s -o %t.dSYM
+#RUN: llvm-dwarfdump --verify %t.dSYM | FileCheck %s
+#RUN: llvm-dwarfdump -a --verbose %t.dSYM | FileCheck %s --check-prefix UPD-DWARF-CHECK
+
#CHECK: No errors.
#DWARF-CHECK: DW_TAG_formal_parameter
diff --git a/llvm/test/tools/dsymutil/X86/dwarf5-rnglists.test b/llvm/test/tools/dsymutil/X86/dwarf5-rnglists.test
index 7603ae738f8e8a..b6d2f4391c70e3 100644
--- a/llvm/test/tools/dsymutil/X86/dwarf5-rnglists.test
+++ b/llvm/test/tools/dsymutil/X86/dwarf5-rnglists.test
@@ -32,11 +32,19 @@
#RUN: dsymutil -oso-prepend-path %p/../Inputs -y %s -o %t.dSYM
#RUN: llvm-dwarfdump --verify %t.dSYM | FileCheck %s
#RUN: llvm-dwarfdump -a --verbose %t.dSYM | FileCheck %s --check-prefix DWARF-CHECK
-#
+
#RUN: dsymutil --update -oso-prepend-path %p/../Inputs -y %s -o %t.dSYM
#RUN: llvm-dwarfdump --verify %t.dSYM | FileCheck %s
#RUN: llvm-dwarfdump -a --verbose %t.dSYM | FileCheck %s --check-prefix UPD-DWARF-CHECK
+#RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs -y %s -o %t.dSYM
+#RUN: llvm-dwarfdump --verify %t.dSYM | FileCheck %s
+#RUN: llvm-dwarfdump -a --verbose %t.dSYM | FileCheck %s --check-prefix DWARF-CHECK
+
+#RUN: dsymutil --linker llvm --update -oso-prepend-path %p/../Inputs -y %s -o %t.dSYM
+#RUN: llvm-dwarfdump --verify %t.dSYM | FileCheck %s
+#RUN: llvm-dwarfdump -a --verbose %t.dSYM | FileCheck %s --check-prefix UPD-DWARF-CHECK
+
#CHECK: No errors.
#DWARF-CHECK: DW_TAG_compile_unit
diff --git a/llvm/test/tools/dsymutil/X86/eh_frame.test b/llvm/test/tools/dsymutil/X86/eh_frame.test
index c4be843fff597c..08e403eedfb41c 100644
--- a/llvm/test/tools/dsymutil/X86/eh_frame.test
+++ b/llvm/test/tools/dsymutil/X86/eh_frame.test
@@ -18,5 +18,10 @@ RUN: llvm-dwarfdump --verify %t.dSYM
RUN: llvm-otool -s __TEXT __eh_frame %p/../Inputs/private/tmp/eh_frame/eh_frame.out | FileCheck %s
RUN: llvm-otool -s __TEXT __eh_frame %t.dSYM/Contents/Resources/DWARF/eh_frame.out | FileCheck %s
+RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/eh_frame/eh_frame.out -o %t.dSYM
+RUN: llvm-dwarfdump --verify %t.dSYM
+RUN: llvm-otool -s __TEXT __eh_frame %p/../Inputs/private/tmp/eh_frame/eh_frame.out | FileCheck %s
+RUN: llvm-otool -s __TEXT __eh_frame %t.dSYM/Contents/Resources/DWARF/eh_frame.out | FileCheck %s
+
CHECK: 14 00 00 00 00 00 00 00 01 7a 52 00 01 78 10 01
CHECK: 10 0c 07 08 90 01 00 00
diff --git a/llvm/test/tools/dsymutil/X86/empty-CU.test b/llvm/test/tools/dsymutil/X86/empty-CU.test
index 7b06607a74cbc0..db9f70874971b3 100644
--- a/llvm/test/tools/dsymutil/X86/empty-CU.test
+++ b/llvm/test/tools/dsymutil/X86/empty-CU.test
@@ -1,6 +1,8 @@
RUN: llvm-mc %p/../Inputs/empty-CU.s -filetype obj -triple x86_64-apple-darwin -o %t.o
RUN: dsymutil --update -f %t.o -o - | llvm-dwarfdump -v - -debug-info | FileCheck %s
+RUN: dsymutil --linker llvm --update -f %t.o -o - | llvm-dwarfdump -v - -debug-info | FileCheck %s
+
CHECK: .debug_info contents:
CHECK: 0x00000000: Compile Unit: length = 0x00000008, format = DWARF32, version = 0x0003, abbr_offset = 0x0000, addr_size = 0x04 (next unit at 0x0000000c)
diff --git a/llvm/test/tools/dsymutil/X86/fat-archive-input-i386.test b/llvm/test/tools/dsymutil/X86/fat-archive-input-i386.test
index 5c71f130a7f70f..a2d165500b9fe2 100644
--- a/llvm/test/tools/dsymutil/X86/fat-archive-input-i386.test
+++ b/llvm/test/tools/dsymutil/X86/fat-archive-input-i386.test
@@ -1,5 +1,7 @@
# RUN: dsymutil -f -oso-prepend-path=%p/../Inputs -y %s -o - | llvm-dwarfdump -debug-info - | FileCheck %s
+# RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/../Inputs -y %s -o - | llvm-dwarfdump -debug-info - | FileCheck %s
+
---
triple: 'i386-apple-darwin'
objects:
diff --git a/llvm/test/tools/dsymutil/X86/fat-object-input-x86_64.test b/llvm/test/tools/dsymutil/X86/fat-object-input-x86_64.test
index d6cb6714be6e5a..6259d11a3358fa 100644
--- a/llvm/test/tools/dsymutil/X86/fat-object-input-x86_64.test
+++ b/llvm/test/tools/dsymutil/X86/fat-object-input-x86_64.test
@@ -1,5 +1,7 @@
# RUN: dsymutil -f -oso-prepend-path=%p/../Inputs -y %s -o - | llvm-dwarfdump -debug-info - | FileCheck %s
+# RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/../Inputs -y %s -o - | llvm-dwarfdump -debug-info - | FileCheck %s
+
---
triple: 'x86_64-apple-darwin'
objects:
diff --git a/llvm/test/tools/dsymutil/X86/fat-object-input-x86_64h.test b/llvm/test/tools/dsymutil/X86/fat-object-input-x86_64h.test
index d7200c8647cf04..588fee9bb10928 100644
--- a/llvm/test/tools/dsymutil/X86/fat-object-input-x86_64h.test
+++ b/llvm/test/tools/dsymutil/X86/fat-object-input-x86_64h.test
@@ -1,5 +1,7 @@
# RUN: dsymutil -f -oso-prepend-path=%p/../Inputs -y %s -o - | llvm-dwarfdump -debug-info - | FileCheck %s
+# RUN: dsymutil --linker llvm -f -oso-prepend-path=%p/../Inputs -y %s -o - | llvm-dwarfdump -debug-info - | FileCheck %s
+
---
triple: 'x86_64h-apple-darwin'
objects:
diff --git a/llvm/test/tools/dsymutil/X86/generate-empty-CU.test b/llvm/test/tools/dsymutil/X86/generate-empty-CU.test
index 58afea183a2f05..aa70ca7de98b75 100644
--- a/llvm/test/tools/dsymutil/X86/generate-empty-CU.test
+++ b/llvm/test/tools/dsymutil/X86/generate-empty-CU.test
@@ -1,7 +1,9 @@
# RUN: dsymutil -f -o - -oso-prepend-path=%p/.. -y %s | llvm-dwarfdump -v - | FileCheck %s
+# RUN: dsymutil --linker llvm -f -o - -oso-prepend-path=%p/.. -y %s | llvm-dwarfdump -v - | FileCheck %s
+
# This test on links the Dwarf for an LTO binary and on purpose doesn't retain
-# any symbol in the second CU out of 3. To be valid DWARF ssymutil must not
+# any symbol in the second CU out of 3. To be valid DWARF dsymutil must not
# generate an empty CU but omit it.
---
@@ -22,10 +24,12 @@ CHECK: DW_AT_name {{.*}} "basic1.c"
CHECK: DW_TAG_subprogram
DW_AT_name {{.*}} "main"
-CHECK: 0x00000081: Compile Unit: length = 0x00000089, format = DWARF32, version = 0x0002, abbr_offset = 0x0000, addr_size = 0x08 (next unit at 0x0000010e)
+CHECK: 0x00000081: Compile Unit: length = 0x00000089, format = DWARF32, version = 0x0002, abbr_offset = 0x00{{00|53}}, addr_size = 0x08 (next unit at 0x0000010e)
CHECK: DW_TAG_compile_unit
CHECK: DW_AT_name {{.*}} "basic3.c"
-CHECK: DW_TAG_subprogram [7] *
+CHECK: DW_TAG_subprogram
CHECK: DW_AT_name {{.*}} = "bar"
+
+CHECK-NOT: DW_TAG_compile_unit
diff --git a/llvm/test/tools/dsymutil/X86/global_downgraded_to_static.c b/llvm/test/tools/dsymutil/X86/global_downgraded_to_static.c
index 0c808a208fe9cb..28910cb0d34147 100644
--- a/llvm/test/tools/dsymutil/X86/global_downgraded_to_static.c
+++ b/llvm/test/tools/dsymutil/X86/global_downgraded_to_static.c
@@ -1,5 +1,9 @@
// REQUIRES : system-darwin
-// RUN: dsymutil -oso-prepend-path %p/.. -dump-debug-map %p/../Inputs/global_downgraded_to_static.x86_64 2>&1 | FileCheck %s
+// RUN: dsymutil -oso-prepend-path %p/.. -dump-debug-map \
+// RUN: %p/../Inputs/global_downgraded_to_static.x86_64 2>&1 | FileCheck %s
+//
+// RUN: dsymutil --linker llvm -oso-prepend-path %p/.. -dump-debug-map \
+// RUN: %p/../Inputs/global_downgraded_to_static.x86_64 2>&1 | FileCheck %s
//
// To build:
// clang -g -c -DFILE1 global_downgraded_to_static.c -o 1.o
diff --git a/llvm/test/tools/dsymutil/X86/inlined-static-variable.cpp b/llvm/test/tools/dsymutil/X86/inlined-static-variable.cpp
index 5593b2638147e4..e933be8fd9bdb0 100644
--- a/llvm/test/tools/dsymutil/X86/inlined-static-variable.cpp
+++ b/llvm/test/tools/dsymutil/X86/inlined-static-variable.cpp
@@ -1,5 +1,10 @@
// RUN: dsymutil -f -y %p/dummy-debug-map.map -oso-prepend-path %p/../Inputs/inlined-static-variable -o - | llvm-dwarfdump - | FileCheck %s --implicit-check-not "{{DW_AT_low_pc|DW_AT_high_pc|DW_AT_location|DW_TAG|NULL}}"
+// RUN: dsymutil --linker llvm -f -y %p/dummy-debug-map.map -oso-prepend-path \
+// RUN: %p/../Inputs/inlined-static-variable -o - | llvm-dwarfdump - | \
+// RUN: FileCheck %s --implicit-check-not \
+// RUN: "{{DW_AT_low_pc|DW_AT_high_pc|DW_AT_location|DW_TAG|NULL}}"
+
// clang -g -c inlined-static-variable.cpp -o 4.o
// The functions removed and not_removed are not in the debug map and are
diff --git a/llvm/test/tools/dsymutil/X86/keep-func.test b/llvm/test/tools/dsymutil/X86/keep-func.test
index 021c7212bd8319..f307f5ad89900d 100644
--- a/llvm/test/tools/dsymutil/X86/keep-func.test
+++ b/llvm/test/tools/dsymutil/X86/keep-func.test
@@ -25,6 +25,11 @@ RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/keep_func/
RUN: llvm-dwarfdump %t.omit.dSYM | FileCheck %s --check-prefix OMIT
RUN: llvm-dwarfdump %t.keep.dSYM | FileCheck %s --check-prefix KEEP
+RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/keep_func/main.out -o %t.omit.dSYM
+RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/keep_func/main.out -o %t.keep.dSYM -keep-function-for-static
+RUN: llvm-dwarfdump %t.omit.dSYM | FileCheck %s --check-prefix OMIT
+RUN: llvm-dwarfdump %t.keep.dSYM | FileCheck %s --check-prefix KEEP
+
KEEP: DW_AT_name ("MyDummyVar")
KEEP: DW_AT_name ("FOO_VAR_TYPE")
KEEP: DW_AT_name ("x1")
diff --git a/llvm/test/tools/dsymutil/X86/label.test b/llvm/test/tools/dsymutil/X86/label.test
index 62c9d62b76b176..129f603781e51e 100644
--- a/llvm/test/tools/dsymutil/X86/label.test
+++ b/llvm/test/tools/dsymutil/X86/label.test
@@ -1,5 +1,7 @@
# RUN: dsymutil -oso-prepend-path %p/../Inputs -y %s -f -o - | llvm-dwarfdump - --debug-info | FileCheck %s
+# RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs -y %s -f -o - | llvm-dwarfdump - --debug-info | FileCheck %s
+
# Compile with:
# echo -e ".global _foo;\nfoo:\nnop" | clang -x assembler -g - -c -o /tmp/label.o
diff --git a/llvm/test/tools/dsymutil/X86/label2.test b/llvm/test/tools/dsymutil/X86/label2.test
index 0517d1e7135b50..9e0f39e5f337b4 100644
--- a/llvm/test/tools/dsymutil/X86/label2.test
+++ b/llvm/test/tools/dsymutil/X86/label2.test
@@ -14,6 +14,9 @@ $ clang label.o -o label.out
RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/label/label.out -o %t.dSYM
RUN: llvm-dwarfdump %t.dSYM | FileCheck %s
+RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/label/label.out -o %t.dSYM
+RUN: llvm-dwarfdump %t.dSYM | FileCheck %s
+
CHECK: DW_TAG_label
CHECK-NEXT: DW_AT_name ("foobar")
CHECK-NEXT: DW_AT_decl_file ("/tmp/label{{[/\\]}}label.c")
diff --git a/llvm/test/tools/dsymutil/X86/lc_build_version.test b/llvm/test/tools/dsymutil/X86/lc_build_version.test
index 2c91cbb0aa0e5c..f0d0e803b7a3b2 100644
--- a/llvm/test/tools/dsymutil/X86/lc_build_version.test
+++ b/llvm/test/tools/dsymutil/X86/lc_build_version.test
@@ -1,6 +1,9 @@
# RUN: dsymutil -f %p/../Inputs/lc_build_version.x86_64 -o - \
# RUN: -oso-prepend-path=%p/.. | obj2yaml | FileCheck %s
+# RUN: dsymutil --linker llvm -f %p/../Inputs/lc_build_version.x86_64 -o - \
+# RUN: -oso-prepend-path=%p/.. | obj2yaml | FileCheck %s
+
CHECK: LoadCommands:
CHECK: - cmd: LC_BUILD_VERSION
CHECK-NEXT: cmdsize: 24
diff --git a/llvm/test/tools/dsymutil/X86/location-expression.test b/llvm/test/tools/dsymutil/X86/location-expression.test
index a991edb6b75f75..5414dff3745b28 100644
--- a/llvm/test/tools/dsymutil/X86/location-expression.test
+++ b/llvm/test/tools/dsymutil/X86/location-expression.test
@@ -11,6 +11,7 @@
# RUN: echo ' - { sym: __Z3foov, objAddr: 0x0, binAddr: 0x10000, size: 0x10 }' >> %t2.map
# RUN: echo '...' >> %t2.map
# RUN: dsymutil -y %t2.map -f -o - | llvm-dwarfdump -a --verbose - | FileCheck %s
+# RUN: dsymutil --linker llvm -y %t2.map -f -o - | llvm-dwarfdump -a --verbose - | FileCheck %s
# CHECK: file format Mach-O 64-bit x86-64
# CHECK: .debug_info contents:
@@ -20,11 +21,11 @@
# CHECK: 0x0000001b: DW_TAG_variable
# CHECK: DW_AT_name {{.*}}"var1"
# CHECK: DW_AT_type {{.*}}"class1"
-# CHECK: DW_AT_location [DW_FORM_block1] (DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address)
+# CHECK: DW_AT_location [DW_FORM_block1] (DW_OP_const8u 0x{{.*}}, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address)
# CHECK: 0x000000ab: DW_TAG_variable
# CHECK: DW_AT_name {{.*}}"var2"
# CHECK: DW_AT_type {{.*}}"class1"
-# CHECK: DW_AT_location [DW_FORM_block] (DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address)
+# CHECK: DW_AT_location [DW_FORM_block] (DW_OP_const8u 0x{{.*}}, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address, DW_OP_const8u 0x2000, DW_OP_form_tls_address)
# CHECK: 0x00000146: DW_TAG_variable
# CHECK: DW_AT_name {{.*}}"var3"
# CHECK: DW_AT_type {{.*}}"class1"
@@ -72,13 +73,21 @@ LoadCommands:
offset: 0x00000410
align: 0
reloff: 0x00000600
- nreloc: 1
+ nreloc: 2
flags: 0x02000000
reserved1: 0x00000000
reserved2: 0x00000000
reserved3: 0x00000000
relocations:
- - address: 0x1FC
+ - address: 0x30
+ symbolnum: 1
+ pcrel: true
+ length: 3
+ extern: true
+ type: 0
+ scattered: false
+ value: 0
+ - address: 0xc2
symbolnum: 1
pcrel: true
length: 3
diff --git a/llvm/test/tools/dsymutil/X86/mismatch.m b/llvm/test/tools/dsymutil/X86/mismatch.m
index 2e907530c5b1c8..d2d257a54c777b 100644
--- a/llvm/test/tools/dsymutil/X86/mismatch.m
+++ b/llvm/test/tools/dsymutil/X86/mismatch.m
@@ -19,6 +19,8 @@
// RUN: cp %p/../Inputs/mismatch/1.o %t.dir/2.o
// RUN: dsymutil --verbose -f -oso-prepend-path=%t.dir \
// RUN: -y %p/dummy-debug-map.map -o %t.bin 2>&1 | FileCheck %s
+// RUN: dsymutil --linker llvm --verbose -f -oso-prepend-path=%t.dir \
+// RUN: -y %p/dummy-debug-map.map -o %t.bin 2>&1 | FileCheck %s
@import mismatch;
diff --git a/llvm/test/tools/dsymutil/X86/modules-dwarf-version.m b/llvm/test/tools/dsymutil/X86/modules-dwarf-version.m
index 7c395f22e5168d..b57e91adb8b86a 100644
--- a/llvm/test/tools/dsymutil/X86/modules-dwarf-version.m
+++ b/llvm/test/tools/dsymutil/X86/modules-dwarf-version.m
@@ -11,6 +11,9 @@
// RUN: dsymutil -verify -f -oso-prepend-path=%t.dir \
// RUN: -y %p/dummy-debug-map.map -o - \
// RUN: | llvm-dwarfdump --debug-info - | FileCheck %s
+// RUN: dsymutil --linker llvm -verify -f -oso-prepend-path=%t.dir \
+// RUN: -y %p/dummy-debug-map.map -o - \
+// RUN: | llvm-dwarfdump --debug-info - | FileCheck %s
@import Bar;
int main(int argc, char **argv) {
diff --git a/llvm/test/tools/dsymutil/X86/modules-empty.m b/llvm/test/tools/dsymutil/X86/modules-empty.m
index ce5ab427af57d3..fc415acc59f6d6 100644
--- a/llvm/test/tools/dsymutil/X86/modules-empty.m
+++ b/llvm/test/tools/dsymutil/X86/modules-empty.m
@@ -12,11 +12,16 @@
// RUN: rm -rf %t.dir
// RUN: mkdir %t.dir
-// RUN: cp %p/../Inputs/modules-empty/1.o %p/../Inputs/modules-empty/Empty.pcm %t.dir
+// RUN: cp %p/../Inputs/modules-empty/1.o %p/../Inputs/modules-empty/Empty.pcm \
+// RUN: %t.dir
// RUN: dsymutil -f -oso-prepend-path=%t.dir \
// RUN: -verify \
// RUN: -y %p/dummy-debug-map.map -o - \
// RUN: | llvm-dwarfdump --debug-info - | FileCheck %s
+// RUN: dsymutil --linker llvm -f -oso-prepend-path=%t.dir \
+// RUN: -verify \
+// RUN: -y %p/dummy-debug-map.map -o - \
+// RUN: | llvm-dwarfdump --debug-info - | FileCheck %s
#include "Empty.h"
int main() {
diff --git a/llvm/test/tools/dsymutil/X86/multiple-inputs.test b/llvm/test/tools/dsymutil/X86/multiple-inputs.test
index ea541426eb17e1..6e1ed2e48c6d70 100644
--- a/llvm/test/tools/dsymutil/X86/multiple-inputs.test
+++ b/llvm/test/tools/dsymutil/X86/multiple-inputs.test
@@ -28,4 +28,3 @@ RUN: llvm-dwarfdump -a %t.dSYM/Contents/Resources/DWARF/basic-lto-dw4.macho.x86_
RUN: not dsymutil -f -oso-prepend-path=%p/.. %t/basic.macho.x86_64 %t/basic-archive.macho.x86_64 %t/basic-lto.macho.x86_64 %t/basic-lto-dw4.macho.x86_64 -o %t.dSYM 2>&1 | FileCheck %s
CHECK: error: cannot use -o with multiple inputs in flat mode
-
diff --git a/llvm/test/tools/dsymutil/X86/object-prefix-path.test b/llvm/test/tools/dsymutil/X86/object-prefix-path.test
index 16956e0f94521e..9ac5ad755ec132 100644
--- a/llvm/test/tools/dsymutil/X86/object-prefix-path.test
+++ b/llvm/test/tools/dsymutil/X86/object-prefix-path.test
@@ -7,5 +7,10 @@ RUN: %p/dummy-debug-map.map -o %t \
RUN: -object-prefix-map=/ModuleCache=/ModuleCacheRenamed \
RUN: 2>&1 | FileCheck %s
+RUN: dsymutil --linker llvm -verify -f -oso-prepend-path=%t.dir -y \
+RUN: %p/dummy-debug-map.map -o %t \
+RUN: -object-prefix-map=/ModuleCache=/ModuleCacheRenamed \
+RUN: 2>&1 | FileCheck %s
+
CHECK: warning: {{.*}}Bar.pcm:
CHECK-NOT: warning: {{.*}}Foo.pcm:
diff --git a/llvm/test/tools/dsymutil/X86/op-convert-offset.test b/llvm/test/tools/dsymutil/X86/op-convert-offset.test
index 80cfc867b1d92f..fa780993bd0d6d 100644
--- a/llvm/test/tools/dsymutil/X86/op-convert-offset.test
+++ b/llvm/test/tools/dsymutil/X86/op-convert-offset.test
@@ -24,6 +24,14 @@ RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/op-convert
RUN: llvm-dwarfdump %p/../Inputs/private/tmp/op-convert-offset/op-convert-offset.o 2>&1 | FileCheck %s --check-prefix OBJ
RUN: llvm-dwarfdump %t.dSYM 2>&1 | FileCheck %s --check-prefix DSYM
+RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs \
+RUN: %p/../Inputs/private/tmp/op-convert-offset/op-convert-offset \
+RUN: -o %t.dSYM 2>&1
+RUN: llvm-dwarfdump \
+RUN: %p/../Inputs/private/tmp/op-convert-offset/op-convert-offset.o 2>&1 \
+RUN: | FileCheck %s --check-prefix OBJ
+RUN: llvm-dwarfdump %t.dSYM 2>&1 | FileCheck %s --check-prefix DSYM
+
OBJ: 0x0000007d: DW_TAG_base_type
OBJ: DW_AT_name ("DW_ATE_unsigned_1")
OBJ: DW_AT_encoding (DW_ATE_unsigned)
@@ -42,4 +50,4 @@ DSYM: DW_AT_byte_size (0x01)
DSYM: 0x0000009b: DW_TAG_formal_parameter
DSYM: DW_AT_location (DW_OP_breg2 RCX+0, DW_OP_constu 0xff, DW_OP_and, DW_OP_convert (0x00000084) "DW_ATE_unsigned_1", DW_OP_convert (0x00000088) "DW_ATE_unsigned_8", DW_OP_stack_value)
DSYM: DW_AT_name ("b")
-DSYM: DW_AT_type (0x000000b6 "_Bool")
+DSYM: DW_AT_type ({{.*}} "_Bool")
diff --git a/llvm/test/tools/dsymutil/X86/op-convert.test b/llvm/test/tools/dsymutil/X86/op-convert.test
index a03869865599db..15725a0435d488 100644
--- a/llvm/test/tools/dsymutil/X86/op-convert.test
+++ b/llvm/test/tools/dsymutil/X86/op-convert.test
@@ -1,6 +1,9 @@
# RUN: dsymutil -f -o %t --verify -oso-prepend-path=%p/../Inputs -y %s
# RUN: llvm-dwarfdump %t | FileCheck %s
+# RUN: dsymutil --linker llvm -f -o %t --verify -oso-prepend-path=%p/../Inputs -y %s
+# RUN: llvm-dwarfdump %t | FileCheck %s
+
---
triple: 'x86_64-apple-darwin'
objects:
diff --git a/llvm/test/tools/dsymutil/X86/papertrail-warnings.test b/llvm/test/tools/dsymutil/X86/papertrail-warnings.test
index 1678adb8ae7b7a..69c851a335ad69 100644
--- a/llvm/test/tools/dsymutil/X86/papertrail-warnings.test
+++ b/llvm/test/tools/dsymutil/X86/papertrail-warnings.test
@@ -1,5 +1,7 @@
RUN: env RC_DEBUG_OPTIONS=1 dsymutil -f %p/../Inputs/basic.macho.x86_64 -o - | llvm-dwarfdump -v - | FileCheck -DMSG=%errc_ENOENT %s
+RUN: env RC_DEBUG_OPTIONS=1 dsymutil --linker llvm -f %p/../Inputs/basic.macho.x86_64 -o - | llvm-dwarfdump -v - | FileCheck -DMSG=%errc_ENOENT %s
+
CHECK: .debug_info contents:
CHECK: Compile Unit:
CHECK: DW_TAG_compile_unit [1] *
diff --git a/llvm/test/tools/dsymutil/X86/reflection-dump.test b/llvm/test/tools/dsymutil/X86/reflection-dump.test
index bcee07d5929eb6..77694773ab0f97 100644
--- a/llvm/test/tools/dsymutil/X86/reflection-dump.test
+++ b/llvm/test/tools/dsymutil/X86/reflection-dump.test
@@ -9,6 +9,9 @@ RUN: yaml2obj %p/../Inputs/reflection_metadata.yaml -o %t.dir/tmp/reflection_met
RUN: dsymutil -oso-prepend-path=%t.dir %t.dir/main -o %t.dir/main.dSYM
RUN: llvm-objdump -s %t.dir/main.dSYM/Contents/Resources/DWARF/main | FileCheck %s
+RUN: dsymutil --linker llvm -oso-prepend-path=%t.dir %t.dir/main -o %t.dir/main.dSYM
+RUN: llvm-objdump -s %t.dir/main.dSYM/Contents/Resources/DWARF/main | FileCheck %s
+
REQUIRES: host-byteorder-little-endian
diff --git a/llvm/test/tools/dsymutil/X86/remarks-linking-archive.text b/llvm/test/tools/dsymutil/X86/remarks-linking-archive.text
index 117349c78396d9..5578fd28b89a8a 100644
--- a/llvm/test/tools/dsymutil/X86/remarks-linking-archive.text
+++ b/llvm/test/tools/dsymutil/X86/remarks-linking-archive.text
@@ -7,10 +7,17 @@ RUN: dsymutil -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs
Check that the remark file in the bundle exists and is sane:
RUN: llvm-bcanalyzer -dump %t/basic.macho.remarks.archive.x86_64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.archive.x86_64 | FileCheck %s
+RUN: dsymutil --linker llvm -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs %t/basic.macho.remarks.archive.x86_64
+
+Check that the remark file in the bundle exists and is sane:
+RUN: llvm-bcanalyzer -dump %t/basic.macho.remarks.archive.x86_64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.archive.x86_64 | FileCheck %s
+
Check that we don't error if we're missing remark files from an archive, but we warn instead.
Instead of creating a new binary, just remove the remarks prepend path.
RUN: dsymutil -oso-prepend-path=%p/../Inputs %t/basic.macho.remarks.archive.x86_64 2>&1 | FileCheck %s --check-prefix=CHECK-MISSING
+RUN: dsymutil --linker llvm -oso-prepend-path=%p/../Inputs %t/basic.macho.remarks.archive.x86_64 2>&1 | FileCheck %s --check-prefix=CHECK-MISSING
+
CHECK: <Meta
CHECK: <Remark Num
CHECK: <Remark Num
diff --git a/llvm/test/tools/dsymutil/X86/remarks-linking-bundle-empty.test b/llvm/test/tools/dsymutil/X86/remarks-linking-bundle-empty.test
index 12418694f144ee..ad8accf24530d1 100644
--- a/llvm/test/tools/dsymutil/X86/remarks-linking-bundle-empty.test
+++ b/llvm/test/tools/dsymutil/X86/remarks-linking-bundle-empty.test
@@ -6,3 +6,8 @@ RUN: dsymutil -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs
Check that the remark file in the bundle does not exist:
RUN: not cat %t/basic.macho.remarks.empty.x86_64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.empty.x86_64 2>&1
+
+RUN: dsymutil --linker llvm -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs %t/basic.macho.remarks.empty.x86_64
+
+Check that the remark file in the bundle does not exist:
+RUN: not cat %t/basic.macho.remarks.empty.x86_64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.empty.x86_64 2>&1
diff --git a/llvm/test/tools/dsymutil/X86/remarks-linking-bundle.test b/llvm/test/tools/dsymutil/X86/remarks-linking-bundle.test
index 11b9a6f9d5eab7..3fb7fd59d44bc1 100644
--- a/llvm/test/tools/dsymutil/X86/remarks-linking-bundle.test
+++ b/llvm/test/tools/dsymutil/X86/remarks-linking-bundle.test
@@ -7,10 +7,18 @@ RUN: dsymutil -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs
Check that the remark file in the bundle exists and is sane:
RUN: llvm-bcanalyzer -dump %t/basic.macho.remarks.x86_64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.x86_64 | FileCheck %s
+RUN: dsymutil --linker llvm -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs %t/basic.macho.remarks.x86_64
+
+Check that the remark file in the bundle exists and is sane:
+RUN: llvm-bcanalyzer -dump %t/basic.macho.remarks.x86_64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.x86_64 | FileCheck %s
+
Now emit it in a
diff erent format: YAML.
RUN: dsymutil -remarks-output-format=yaml -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs %t/basic.macho.remarks.x86_64
RUN: cat %t/basic.macho.remarks.x86_64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.x86_64 | FileCheck %s --check-prefix=CHECK-YAML
+RUN: dsymutil --linker llvm -remarks-output-format=yaml -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs %t/basic.macho.remarks.x86_64
+RUN: cat %t/basic.macho.remarks.x86_64.dSYM/Contents/Resources/Remarks/basic.macho.remarks.x86_64 | FileCheck %s --check-prefix=CHECK-YAML
+
CHECK: <Meta
CHECK: <Remark Num
CHECK: <Remark Num
diff --git a/llvm/test/tools/dsymutil/X86/remarks-linking-fat-bundle.test b/llvm/test/tools/dsymutil/X86/remarks-linking-fat-bundle.test
index c0b4beffee4fee..632da020607146 100644
--- a/llvm/test/tools/dsymutil/X86/remarks-linking-fat-bundle.test
+++ b/llvm/test/tools/dsymutil/X86/remarks-linking-fat-bundle.test
@@ -11,6 +11,13 @@ RUN: llvm-bcanalyzer -dump %t/fat.macho.remarks.x86.dSYM/Contents/Resources/Rema
RUN: llvm-bcanalyzer -dump %t/fat.macho.remarks.x86.dSYM/Contents/Resources/Remarks/fat.macho.remarks.x86-x86_64 | FileCheck %s
RUN: llvm-bcanalyzer -dump %t/fat.macho.remarks.x86.dSYM/Contents/Resources/Remarks/fat.macho.remarks.x86-i386 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-i386
+RUN: dsymutil --linker llvm -oso-prepend-path=%p/../Inputs -remarks-prepend-path=%p/../Inputs %t/fat.macho.remarks.x86
+
+Check that the remark files in the bundle exist and are all sane:
+RUN: llvm-bcanalyzer -dump %t/fat.macho.remarks.x86.dSYM/Contents/Resources/Remarks/fat.macho.remarks.x86-x86_64h | FileCheck %s
+RUN: llvm-bcanalyzer -dump %t/fat.macho.remarks.x86.dSYM/Contents/Resources/Remarks/fat.macho.remarks.x86-x86_64 | FileCheck %s
+RUN: llvm-bcanalyzer -dump %t/fat.macho.remarks.x86.dSYM/Contents/Resources/Remarks/fat.macho.remarks.x86-i386 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-i386
+
CHECK: <Meta
CHECK: <Remark Num
CHECK: <Remark Num
diff --git a/llvm/test/tools/dsymutil/X86/reproducer.test b/llvm/test/tools/dsymutil/X86/reproducer.test
index 7e65d4ee2d77b2..128cd5388011c2 100644
--- a/llvm/test/tools/dsymutil/X86/reproducer.test
+++ b/llvm/test/tools/dsymutil/X86/reproducer.test
@@ -28,12 +28,18 @@ RUN: llvm-dwarfdump -a %t.generate | FileCheck %s
RUN: rm -rf %t
RUN: not dsymutil -f -o %t.error -oso-prepend-path=%t %t/Inputs/basic.macho.x86_64 2>&1 | FileCheck %s --check-prefix=ERROR
+RUN: not dsymutil --linker llvm -f -o %t.error -oso-prepend-path=%t %t/Inputs/basic.macho.x86_64 2>&1 | FileCheck %s --check-prefix=ERROR
+
# Use the reproducer.
RUN: dsymutil -use-reproducer %t.repro -f -o - -oso-prepend-path=%t %t/Inputs/basic.macho.x86_64 | llvm-dwarfdump -a - | FileCheck %s
+RUN: dsymutil --linker llvm -use-reproducer %t.repro -f -o - -oso-prepend-path=%t %t/Inputs/basic.macho.x86_64 | llvm-dwarfdump -a - | FileCheck %s
+
# Using a reproducer takes precedence.
RUN: dsymutil -gen-reproducer -use-reproducer %t.repro -f -o - -oso-prepend-path=%t %t/Inputs/basic.macho.x86_64 | llvm-dwarfdump -a - | FileCheck %s
+RUN: dsymutil --linker llvm -gen-reproducer -use-reproducer %t.repro -f -o - -oso-prepend-path=%t %t/Inputs/basic.macho.x86_64 | llvm-dwarfdump -a - | FileCheck %s
+
CHECK: .debug_info
CHECK: DW_TAG_compile_unit
CHECK-NEXT: DW_AT_producer ("Apple LLVM version 6.0 (clang-600.0.39) (based on LLVM 3.5svn)")
diff --git a/llvm/test/tools/dsymutil/X86/statistics.test b/llvm/test/tools/dsymutil/X86/statistics.test
index 0237aede280011..082d20b1c53714 100644
--- a/llvm/test/tools/dsymutil/X86/statistics.test
+++ b/llvm/test/tools/dsymutil/X86/statistics.test
@@ -1,4 +1,5 @@
# RUN: dsymutil -statistics -oso-prepend-path=%p/.. %p/../Inputs/basic.macho.x86_64 %p/../Inputs/basic-archive.macho.x86_64 %p/../Inputs/basic-lto.macho.x86_64 %p/../Inputs/basic-lto-dw4.macho.x86_64 -o %t 2>&1 | FileCheck %s
+# RUN: dsymutil --linker llvm -statistics -oso-prepend-path=%p/.. %p/../Inputs/basic.macho.x86_64 %p/../Inputs/basic-archive.macho.x86_64 %p/../Inputs/basic-lto.macho.x86_64 %p/../Inputs/basic-lto-dw4.macho.x86_64 -o %t 2>&1 | FileCheck %s
#
# CHECK: -------------------------------------------------------------------------------
# CHECK-NEXT: Filename Object dSYM Change
diff --git a/llvm/test/tools/dsymutil/X86/swift-ast-x86_64.test b/llvm/test/tools/dsymutil/X86/swift-ast-x86_64.test
index a16330d8c1b054..9d959e06fb958f 100644
--- a/llvm/test/tools/dsymutil/X86/swift-ast-x86_64.test
+++ b/llvm/test/tools/dsymutil/X86/swift-ast-x86_64.test
@@ -3,6 +3,12 @@ RUN: dsymutil -oso-prepend-path %p/.. %p/../Inputs/swift-ast.macho.x86_64 -o %T/
RUN: llvm-readobj --sections --section-data %T/swift-ast.dSYM/Contents/Resources/DWARF/swift-ast.macho.x86_64 | FileCheck %s --check-prefix=READOBJ
RUN: llvm-dwarfdump --show-section-sizes %T/swift-ast.dSYM/Contents/Resources/DWARF/swift-ast.macho.x86_64 | FileCheck %s --check-prefix=DWARFDUMP
+RUN: dsymutil --linker llvm -oso-prepend-path %p/.. %p/../Inputs/swift-ast.macho.x86_64 -o %T/swift-ast.dSYM -verbose -no-swiftmodule-timestamp | FileCheck %s --check-prefix=DSYMUTIL
+RUN: dsymutil --linker llvm -oso-prepend-path %p/.. %p/../Inputs/swift-ast.macho.x86_64 -o %T/swift-ast.dSYM -verbose | FileCheck %s --check-prefix=DSYMUTIL
+RUN: llvm-readobj --sections --section-data %T/swift-ast.dSYM/Contents/Resources/DWARF/swift-ast.macho.x86_64 | FileCheck %s --check-prefix=READOBJ
+RUN: llvm-dwarfdump --show-section-sizes %T/swift-ast.dSYM/Contents/Resources/DWARF/swift-ast.macho.x86_64 | FileCheck %s --check-prefix=DWARFDUMP
+
+
The tested object file has been created by the dummy Swift code:
let x = 1
diff --git a/llvm/test/tools/dsymutil/X86/swift-dwarf-loc.test b/llvm/test/tools/dsymutil/X86/swift-dwarf-loc.test
index 65282223a30338..d12e2e137d7e49 100644
--- a/llvm/test/tools/dsymutil/X86/swift-dwarf-loc.test
+++ b/llvm/test/tools/dsymutil/X86/swift-dwarf-loc.test
@@ -1,5 +1,7 @@
RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/swift-dwarf-loc.macho.x86_64 -no-output -verbose | FileCheck %s
+RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/swift-dwarf-loc.macho.x86_64 -no-output -verbose | FileCheck %s
+
This test checks that dsymutil generates a valid dwarf location for a symbol with no flags set.
The following IR was compiled for x86_64-apple:
diff --git a/llvm/test/tools/dsymutil/X86/tail-call-linking.test b/llvm/test/tools/dsymutil/X86/tail-call-linking.test
index 29ae2cc544cf62..f4e2afe808a09d 100644
--- a/llvm/test/tools/dsymutil/X86/tail-call-linking.test
+++ b/llvm/test/tools/dsymutil/X86/tail-call-linking.test
@@ -1,4 +1,7 @@
RUN: dsymutil -oso-prepend-path=%p %p/Inputs/tail-call.macho.x86_64 -o %t.dSYM
RUN: llvm-dwarfdump %t.dSYM | FileCheck %s -implicit-check-not=DW_AT_call_pc
+RUN: dsymutil --linker llvm -oso-prepend-path=%p %p/Inputs/tail-call.macho.x86_64 -o %t.dSYM
+RUN: llvm-dwarfdump %t.dSYM | FileCheck %s -implicit-check-not=DW_AT_call_pc
+
CHECK: DW_AT_call_pc (0x0000000100000f95)
diff --git a/llvm/test/tools/dsymutil/X86/thinlto.test b/llvm/test/tools/dsymutil/X86/thinlto.test
index ebd4068a5c1275..2d2195805f3aab 100644
--- a/llvm/test/tools/dsymutil/X86/thinlto.test
+++ b/llvm/test/tools/dsymutil/X86/thinlto.test
@@ -20,5 +20,8 @@ $ xcrun clang++ -g -flto=thin -O2 foo.cpp bar.cpp -c
$ xcrun clang++ -flto=thin foo.o bar.o -Xlinker -object_path_lto -Xlinker lto -shared -o foobar.dylib
RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/thinlto/foobar.dylib -o %t.dSYM 2>&1 | FileCheck %s --allow-empty
+
+RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/thinlto/foobar.dylib -o %t.dSYM 2>&1 | FileCheck %s --allow-empty
+
CHECK-NOT: could not find object file symbol for symbol __ZZ9function2vE12magic_static
CHECK-NOT: could not find object file symbol for symbol __ZGVZ9function2vE12magic_static
diff --git a/llvm/test/tools/dsymutil/X86/timestamp-mismatch.test b/llvm/test/tools/dsymutil/X86/timestamp-mismatch.test
index 7d41905e79514d..f41fe31f5549b7 100644
--- a/llvm/test/tools/dsymutil/X86/timestamp-mismatch.test
+++ b/llvm/test/tools/dsymutil/X86/timestamp-mismatch.test
@@ -5,6 +5,8 @@ RUN: cp %p/../Inputs/basic2.macho.x86_64.o %t/Inputs
RUN: cp %p/../Inputs/basic3.macho.x86_64.o %t/Inputs
RUN: dsymutil -oso-prepend-path=%t %t/Inputs/basic.macho.x86_64 -o %t.dSYM 2>&1 | FileCheck %s
+RUN: dsymutil --linker llvm -oso-prepend-path=%t %t/Inputs/basic.macho.x86_64 -o %t.dSYM 2>&1 | FileCheck %s
+
CHECK: warning: {{.*}}/Inputs/basic1.macho.x86_64.o: timestamp mismatch between object file ({{.*}}) and debug map ({{.*}})
CHECK: warning: {{.*}}/Inputs/basic2.macho.x86_64.o: timestamp mismatch between object file ({{.*}}) and debug map ({{.*}})
CHECK: warning: {{.*}}/Inputs/basic3.macho.x86_64.o: timestamp mismatch between object file ({{.*}}) and debug map ({{.*}})
diff --git a/llvm/test/tools/dsymutil/X86/tls-variable.test b/llvm/test/tools/dsymutil/X86/tls-variable.test
index 19f9e380f22be7..48027e4f98714d 100644
--- a/llvm/test/tools/dsymutil/X86/tls-variable.test
+++ b/llvm/test/tools/dsymutil/X86/tls-variable.test
@@ -10,6 +10,7 @@
# RUN: echo ' - { sym: __Z3foov, objAddr: 0x0, binAddr: 0x10000, size: 0x10 }' >> %t2.map
# RUN: echo '...' >> %t2.map
# RUN: dsymutil -y %t2.map --keep-function-for-static -f -o - | llvm-dwarfdump -a - | FileCheck %s
+# RUN: dsymutil --linker llvm -y %t2.map --keep-function-for-static -f -o - | llvm-dwarfdump -a - | FileCheck %s
# CHECK: file format Mach-O 64-bit x86-64
# CHECK: .debug_info contents:
diff --git a/llvm/test/tools/dsymutil/X86/union-fwd-decl.test b/llvm/test/tools/dsymutil/X86/union-fwd-decl.test
index c73db0e3f5532e..c5c8c9e93e0a06 100644
--- a/llvm/test/tools/dsymutil/X86/union-fwd-decl.test
+++ b/llvm/test/tools/dsymutil/X86/union-fwd-decl.test
@@ -48,6 +48,9 @@ Note that the link order in the last command matters for this test.
RUN: dsymutil -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/union/a.out -o %t.dSYM
RUN: llvm-dwarfdump %t.dSYM | FileCheck %s
+RUN: dsymutil --linker llvm -oso-prepend-path %p/../Inputs %p/../Inputs/private/tmp/union/a.out -o %t.dSYM
+RUN: llvm-dwarfdump %t.dSYM | FileCheck %s
+
CHECK: DW_TAG_compile_unit
CHECK: DW_AT_name ("Container_ivars")
diff --git a/llvm/test/tools/dsymutil/X86/verify.test b/llvm/test/tools/dsymutil/X86/verify.test
index 2a7b1938e843f7..b48af48473b635 100644
--- a/llvm/test/tools/dsymutil/X86/verify.test
+++ b/llvm/test/tools/dsymutil/X86/verify.test
@@ -25,6 +25,25 @@
# QUIET-SUCCESS-NOT: input verification failed
# QUIET-SUCCESS-NOT: output verification failed
# BOGUS: error: invalid verify type specified: 'bogus'
+#
+# Positive tests in regular and verbose mode.
+# RUN: dsymutil --linker llvm -verify -oso-prepend-path=%p/.. %p/../Inputs/basic.macho.x86_64 %p/../Inputs/basic-archive.macho.x86_64 %p/../Inputs/basic-lto.macho.x86_64 %p/../Inputs/basic-lto-dw4.macho.x86_64 -o %t 2>&1 | FileCheck %s --allow-empty --check-prefix=QUIET-SUCCESS
+# RUN: dsymutil --linker llvm -verify -verbose -oso-prepend-path=%p/.. %p/../Inputs/basic.macho.x86_64 %p/../Inputs/basic-archive.macho.x86_64 %p/../Inputs/basic-lto.macho.x86_64 %p/../Inputs/basic-lto-dw4.macho.x86_64 -o %t 2>&1 | FileCheck %s --check-prefixes=QUIET-SUCCESS,VERBOSE
+#
+# # Negative output tests in regular and verbose mode.
+# (Invalid object generated from ../Inputs/invalid.s by modified the low PC.)
+# RUN: not dsymutil --linker llvm -verify -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefix=QUIET-OUTPUT-FAIL
+# RUN: not dsymutil --linker llvm -verify-dwarf=output -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefix=QUIET-OUTPUT-FAIL
+# RUN: not dsymutil --linker llvm -verify -verbose -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefixes=QUIET-OUTPUT-FAIL,VERBOSE
+#
+# # Negative input & output tests in regular and verbose mode. Only output failures result in a non-zero exit code.
+# RUN: dsymutil --linker llvm -verify-dwarf=input -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefix=QUIET-INPUT-FAIL
+# RUN: dsymutil --linker llvm -verify-dwarf=input -verbose -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefix=QUIET-INPUT-FAIL
+# RUN: dsymutil --linker llvm -verify-dwarf=none -verbose -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefixes=QUIET-SUCCESS
+# RUN: not dsymutil --linker llvm -verify-dwarf=bogus -verbose -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefixes=BOGUS
+# RUN: not dsymutil --linker llvm -verify-dwarf=all -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefixes=QUIET-OUTPUT-FAIL,QUIET-INPUT-FAIL
+# RUN: not dsymutil --linker llvm -verify-dwarf=all -verbose -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefixes=VERBOSE-INPUT-FAIL
+
---
triple: 'x86_64-apple-darwin'
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/DWARFLinkerParallel/gc-default.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/DWARFLinkerParallel/gc-default.test
deleted file mode 100644
index 5083fd54c136e3..00000000000000
--- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/DWARFLinkerParallel/gc-default.test
+++ /dev/null
@@ -1,134 +0,0 @@
-## This test checks that debug info related to deleted code (marked with
-## default tombstone value) is removed.
-
-# RUN: yaml2obj %s -o %t.o
-# RUN: llvm-dwarfutil --linker llvm %t.o %t1.out 2>&1 | FileCheck %s --allow-empty
-
-#CHECK: LLVM parallel dwarflinker is not implemented yet.
-
---- !ELF
-FileHeader:
- Class: ELFCLASS64
- Data: ELFDATA2LSB
- Type: ET_REL
- Machine: EM_X86_64
-Sections:
- - Name: .text
- Type: SHT_PROGBITS
- Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
- Address: 0x1000
- Size: 0x1b
-DWARF:
- debug_abbrev:
- - Table:
- - Tag: DW_TAG_compile_unit
- Children: DW_CHILDREN_yes
- Attributes:
- - Attribute: DW_AT_producer
- Form: DW_FORM_string
- - Attribute: DW_AT_language
- Form: DW_FORM_data2
- - Attribute: DW_AT_name
- Form: DW_FORM_string
- - Attribute: DW_AT_low_pc
- Form: DW_FORM_addr
- - Attribute: DW_AT_high_pc
- Form: DW_FORM_data8
- - Tag: DW_TAG_subprogram
- Children: DW_CHILDREN_no
- Attributes:
- - Attribute: DW_AT_name
- Form: DW_FORM_string
- - Attribute: DW_AT_low_pc
- Form: DW_FORM_addr
- - Attribute: DW_AT_high_pc
- Form: DW_FORM_data8
- - Attribute: DW_AT_type
- Form: DW_FORM_ref4
- - Tag: DW_TAG_class_type
- Children: DW_CHILDREN_yes
- Attributes:
- - Attribute: DW_AT_name
- Form: DW_FORM_string
- - Tag: DW_TAG_member
- Children: DW_CHILDREN_no
- Attributes:
- - Attribute: DW_AT_type
- Form: DW_FORM_ref4
- - Attribute: DW_AT_name
- Form: DW_FORM_string
- - Tag: DW_TAG_class_type
- Children: DW_CHILDREN_no
- Attributes:
- - Attribute: DW_AT_name
- Form: DW_FORM_string
- - Attribute: DW_AT_declaration
- Form: DW_FORM_flag_present
- - Tag: DW_TAG_class_type
- Children: DW_CHILDREN_yes
- Attributes:
- - Attribute: DW_AT_name
- Form: DW_FORM_string
- - Attribute: DW_AT_declaration
- Form: DW_FORM_flag_present
- - Tag: DW_TAG_template_type_parameter
- Children: DW_CHILDREN_no
- Attributes:
- - Attribute: DW_AT_type
- Form: DW_FORM_ref4
- - Tag: DW_TAG_base_type
- Children: DW_CHILDREN_no
- Attributes:
- - Attribute: DW_AT_name
- Form: DW_FORM_string
- debug_info:
- - Version: 4
- Entries:
- - AbbrCode: 1
- Values:
- - CStr: by_hand
- - Value: 0x04
- - CStr: CU1
- - Value: 0x1000
- - Value: 0x1b
- - AbbrCode: 3
- Values:
- - CStr: class1
- - AbbrCode: 4
- Values:
- - Value: 0x0000006c
- - CStr: member1
- - AbbrCode: 0
- - AbbrCode: 3
- Values:
- - CStr: class2
- - AbbrCode: 4
- Values:
- - Value: 0x0000006c
- - CStr: member1
- - AbbrCode: 0
- - AbbrCode: 3
- Values:
- - CStr: class3
- - AbbrCode: 4
- Values:
- - Value: 0x0000006c
- - CStr: member1
- - AbbrCode: 0
- - AbbrCode: 8
- Values:
- - CStr: int
- - AbbrCode: 2
- Values:
- - CStr: foo1
- - Value: 0x1000
- - Value: 0x10
- - Value: 0x0000002a
- - AbbrCode: 2
- Values:
- - CStr: foo2
- - Value: 0x0
- - Value: 0x100
- - Value: 0x00000040
- - AbbrCode: 0
-...
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf4-macro-short.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf4-macro-short.test
index f500ad1d1232d1..95def093e66a2f 100644
--- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf4-macro-short.test
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf4-macro-short.test
@@ -7,6 +7,10 @@
# RUN: llvm-dwarfdump --verify %t1 | FileCheck --check-prefix=VERIFY %s
# RUN: llvm-dwarfdump -a %t1 | FileCheck %s
+# RUN: llvm-dwarfutil --linker llvm %t.o %t1
+# RUN: llvm-dwarfdump --verify %t1 | FileCheck --check-prefix=VERIFY %s
+# RUN: llvm-dwarfdump -a %t1 | FileCheck %s
+
# VERIFY: No errors.
## Content: "03000201064400020744000400":
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf4-macro.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf4-macro.test
index a84653c612cb73..6797af9d578cf4 100644
--- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf4-macro.test
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf4-macro.test
@@ -39,16 +39,28 @@
#RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefix=MACINFO
+#RUN: llvm-dwarfutil --linker llvm --garbage-collection %p/Inputs/dwarf4-macro.out %t1
+#RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
+#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefix=MACINFO
+
## Check that macro table preserved during simple copying.
#
#RUN: llvm-dwarfutil --no-garbage-collection %p/Inputs/dwarf4-macro.out %t1
#RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefix=MACINFO
+#RUN: llvm-dwarfutil --linker llvm --no-garbage-collection %p/Inputs/dwarf4-macro.out %t1
+#RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
+#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefix=MACINFO
+
## Check that macro table preserved during updating accelerator tables.
#RUN: llvm-dwarfutil --no-garbage-collection --build-accelerator=DWARF %p/Inputs/dwarf4-macro.out %t1
#RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
-#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefixes=MACINFO,NAMES
+#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefixes=MACINFO
+
+#RUN: llvm-dwarfutil --linker llvm --no-garbage-collection --build-accelerator=DWARF %p/Inputs/dwarf4-macro.out %t1
+#RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
+#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefixes=MACINFO
#CHECK: No errors.
@@ -2228,6 +2240,3 @@
#MACINFO-NEXT: DW_MACINFO_define - lineno: 0 macro: __STDC_UTF_32__ 1
#MACINFO-NEXT: DW_MACINFO_define - lineno: 0 macro: __GCC_HAVE_DWARF2_CFI_ASM 1
-
-#NAMES: .debug_names contents
-#NAMES: Name Index
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-addresses.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-addresses.test
index 50dd070633bc78..90c40fd1e4ab62 100644
--- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-addresses.test
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-addresses.test
@@ -12,15 +12,27 @@
# RUN: llvm-dwarfutil %t.o %t1
# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix DWARF-CHECK
-#
+
+# RUN: llvm-dwarfutil --linker llvm %t.o %t1
+# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
+# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix DWARF-CHECK
+
# RUN: llvm-dwarfutil --no-garbage-collection %t.o %t1
# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix UPD-DWARF-CHECK
-#
+
+# RUN: llvm-dwarfutil --linker llvm --no-garbage-collection %t.o %t1
+# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
+# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix UPD-DWARF-CHECK
+
# RUN: llvm-dwarfutil --no-garbage-collection --build-accelerator=DWARF %t.o %t1
# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix UPD-DWARF-CHECK
+# RUN: llvm-dwarfutil --linker llvm --no-garbage-collection --build-accelerator=DWARF %t.o %t1
+# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
+# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix UPD-DWARF-CHECK
+
#CHECK: No errors.
#DWARF-CHECK: DW_TAG_compile_unit
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-attributes.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-attributes.test
index 779e165ab72b85..bf880c643a4dcd 100644
--- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-attributes.test
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-attributes.test
@@ -6,15 +6,27 @@
# RUN: llvm-dwarfutil %t.o %t1
# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK
# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s
-#
+
+# RUN: llvm-dwarfutil --linker llvm %t.o %t1
+# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK
+# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s
+
# RUN: llvm-dwarfutil --no-garbage-collection %t.o %t1
# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK
# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s
-#
+
+# RUN: llvm-dwarfutil --linker llvm --no-garbage-collection %t.o %t1
+# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK
+# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s
+
# RUN: llvm-dwarfutil --no-garbage-collection --build-accelerator=DWARF %t.o %t1
# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK
# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s
+# RUN: llvm-dwarfutil --linker llvm --no-garbage-collection --build-accelerator=DWARF %t.o %t1
+# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK
+# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s
+
#VERIFY-CHECK: No errors.
#CHECK: .debug_abbrev
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-line-str.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-line-str.test
index 8f2957350484ea..52bf01c3a7130b 100644
--- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-line-str.test
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-line-str.test
@@ -6,15 +6,27 @@
# RUN: llvm-dwarfutil %t.o %t1
# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK
# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s
-#
+
# RUN: llvm-dwarfutil --no-garbage-collection %t.o %t1
# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK
# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s
-#
+
# RUN: llvm-dwarfutil --no-garbage-collection --build-accelerator=DWARF %t.o %t1
# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK
# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s
+# RUN: llvm-dwarfutil --linker llvm %t.o %t1
+# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK
+# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s
+
+# RUN: llvm-dwarfutil --linker llvm --no-garbage-collection %t.o %t1
+# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK
+# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s
+
+# RUN: llvm-dwarfutil --linker llvm --no-garbage-collection --build-accelerator=DWARF %t.o %t1
+# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s --check-prefix VERIFY-CHECK
+# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s
+
#VERIFY-CHECK: No errors.
#CHECK: .debug_info
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-loclists.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-loclists.test
index b02c76e3367ec1..719944b3d224e5 100644
--- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-loclists.test
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-loclists.test
@@ -16,6 +16,18 @@
# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix UPD-DWARF-CHECK
+# RUN: llvm-dwarfutil --linker llvm %t.o %t1
+# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
+# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix DWARF-CHECK
+
+# RUN: llvm-dwarfutil --linker llvm --no-garbage-collection %t.o %t1
+# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
+# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix UPD-DWARF-CHECK
+
+# RUN: llvm-dwarfutil --linker llvm --no-garbage-collection --build-accelerator=DWARF %t.o %t1
+# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
+# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix UPD-DWARF-CHECK
+
#CHECK: No errors.
#DWARF-CHECK: DW_TAG_compile_unit
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro-opcodeop.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro-opcodeop.test
index c19462c309e06f..c9e89b2d1d1258 100644
--- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro-opcodeop.test
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro-opcodeop.test
@@ -6,6 +6,10 @@
# RUN: llvm-dwarfdump --verify %t1 | FileCheck --check-prefix=VERIFY %s
# RUN: llvm-dwarfdump -a %t1 | FileCheck %s
+# RUN: llvm-dwarfutil --linker llvm %t.o %t1 2>&1 | FileCheck --check-prefix=ERR %s
+# RUN: llvm-dwarfdump --verify %t1 | FileCheck --check-prefix=VERIFY %s
+# RUN: llvm-dwarfdump -a %t1 | FileCheck %s
+
# ERR: error: opcode_operands_table is not supported
# VERIFY: No errors.
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro-short.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro-short.test
index ecafd814baf3ce..b38aae6f816d9e 100644
--- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro-short.test
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro-short.test
@@ -8,6 +8,10 @@
# RUN: llvm-dwarfdump --verify %t1 | FileCheck --check-prefix=VERIFY %s
# RUN: llvm-dwarfdump -a %t1 | FileCheck %s
+# RUN: llvm-dwarfutil --linker llvm %t.o %t1 2>&1 | FileCheck --check-prefix=WARN %s
+# RUN: llvm-dwarfdump --verify %t1 | FileCheck --check-prefix=VERIFY %s
+# RUN: llvm-dwarfdump -a %t1 | FileCheck %s
+
# WARN: warning: DW_MACRO_define_strx unsupported yet. Convert to DW_MACRO_define_strp.
# WARN: warning: DW_MACRO_undef_strx unsupported yet. Convert to DW_MACRO_undef_strp.
# WARN: warning: DW_MACRO_import and DW_MACRO_import_sup are unsupported yet. remove.
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro.test
index 57487be9d42707..053d55871d4553 100644
--- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro.test
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-macro.test
@@ -39,16 +39,28 @@
#RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefix=MACRO
+#RUN: llvm-dwarfutil --linker llvm --garbage-collection %p/Inputs/dwarf5-macro.out %t1
+#RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
+#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefix=MACRO
+
## Check that macro table preserved during simple copying.
#
#RUN: llvm-dwarfutil --no-garbage-collection %p/Inputs/dwarf5-macro.out %t1
#RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefix=MACRO
+#RUN: llvm-dwarfutil --linker llvm --no-garbage-collection %p/Inputs/dwarf5-macro.out %t1
+#RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
+#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefix=MACRO
+
## Check that macro table preserved during updating accelerator tables.
#RUN: llvm-dwarfutil --no-garbage-collection --build-accelerator=DWARF %p/Inputs/dwarf5-macro.out %t1
#RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
-#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefixes=MACRO,NAMES
+#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefixes=MACRO
+
+#RUN: llvm-dwarfutil --linker llvm --no-garbage-collection --build-accelerator=DWARF %p/Inputs/dwarf5-macro.out %t1
+#RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
+#RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefixes=MACRO
#CHECK: No errors.
@@ -2233,6 +2245,3 @@
#MACRO-NEXT: DW_MACRO_define_str{{[px]}} - lineno: 0 macro: __STDC_UTF_16__ 1
#MACRO-NEXT: DW_MACRO_define_str{{[px]}} - lineno: 0 macro: __STDC_UTF_32__ 1
#MACRO-NEXT: DW_MACRO_define_str{{[px]}} - lineno: 0 macro: __GCC_HAVE_DWARF2_CFI_ASM 1
-
-#NAMES: .debug_names contents
-#NAMES: Name Index
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-rnglists.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-rnglists.test
index d9458e901944fa..78b53897346c48 100644
--- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-rnglists.test
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/dwarf5-rnglists.test
@@ -8,14 +8,26 @@
# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix DWARF-CHECK
+# RUN: llvm-dwarfutil --linker llvm %t.o %t1
+# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
+# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix DWARF-CHECK
+
# RUN: llvm-dwarfutil --no-garbage-collection %t.o %t1
# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix UPD-DWARF-CHECK
+# RUN: llvm-dwarfutil --linker llvm --no-garbage-collection %t.o %t1
+# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
+# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix UPD-DWARF-CHECK
+
# RUN: llvm-dwarfutil --no-garbage-collection --build-accelerator=DWARF %t.o %t1
# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix UPD-DWARF-CHECK
+# RUN: llvm-dwarfutil --linker llvm --no-garbage-collection --build-accelerator=DWARF %t.o %t1
+# RUN: llvm-dwarfdump -verify %t1 | FileCheck %s
+# RUN: llvm-dwarfdump -a --verbose %t1 | FileCheck %s --check-prefix UPD-DWARF-CHECK
+
#CHECK: No errors.
#DWARF-CHECK: DW_TAG_compile_unit
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-default.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-default.test
index b364a751a7c7cd..90cb99858c9efc 100644
--- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-default.test
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-default.test
@@ -7,14 +7,20 @@
# RUN: llvm-dwarfutil --linker apple %t.o - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,CHECK-GC
+# RUN: llvm-dwarfutil --linker llvm %t.o - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,CHECK-GC
+
# RUN: llvm-dwarfutil --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,CHECK-GC
# RUN: llvm-dwarfutil --no-garbage-collection --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,CHECK-GC
# RUN: llvm-dwarfutil --garbage-collection --no-garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,CHECK-NOGC
+# RUN: llvm-dwarfutil --linker llvm --garbage-collection --no-garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,CHECK-NOGC
+
# RUN: llvm-dwarfutil %t.o --tombstone=universal - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,CHECK-GC
+# RUN: llvm-dwarfutil --linker llvm %t.o --tombstone=universal - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,CHECK-GC
+
# CHECK: DW_TAG_compile_unit
# CHECK: DW_AT_name{{.*}}"CU1"
# CHECK: DW_TAG_class_type
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-func-overlapping-address-ranges.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-func-overlapping-address-ranges.test
index cfbb7cb105586e..232f6a5553e571 100644
--- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-func-overlapping-address-ranges.test
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-func-overlapping-address-ranges.test
@@ -5,6 +5,9 @@
# RUN: llvm-dwarfutil --garbage-collection %t.o %t1
# RUN: llvm-dwarfdump -a %t1 | FileCheck %s
+# RUN: llvm-dwarfutil --linker llvm --garbage-collection %t.o %t1
+# RUN: llvm-dwarfdump -a %t1 | FileCheck %s
+
# CHECK: DW_TAG_compile_unit
# CHECK: DW_AT_name{{.*}}"CU1"
# CHECK: DW_AT_low_pc{{.*}}0000000000001000
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-maxpc.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-maxpc.test
index 6184f724961296..cb3b435ea9aa4c 100644
--- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-maxpc.test
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-maxpc.test
@@ -6,6 +6,9 @@
# RUN: llvm-dwarfutil --tombstone=maxpc --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s
# RUN: llvm-dwarfutil --tombstone=universal --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s
+# RUN: llvm-dwarfutil --linker llvm --tombstone=maxpc --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s
+# RUN: llvm-dwarfutil --linker llvm --tombstone=universal --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s
+
# CHECK: DW_TAG_compile_unit
# CHECK: DW_AT_name{{.*}}"CU1"
# CHECK: DW_TAG_class_type
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-no-garbage.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-no-garbage.test
index 11252295617d7a..1f5ddd78adaa24 100644
--- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-no-garbage.test
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-no-garbage.test
@@ -4,6 +4,8 @@
# RUN: yaml2obj %s -o %t.o
# RUN: llvm-dwarfutil --tombstone=maxpc --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s
+# RUN: llvm-dwarfutil --linker llvm --tombstone=maxpc --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s
+
# CHECK: DW_TAG_compile_unit
# CHECK: DW_AT_name{{.*}}"CU1"
# CHECK: DW_TAG_class_type
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-unit-overlapping-address-ranges.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-unit-overlapping-address-ranges.test
index f9d42c28d42098..2ee1d73b6f92f0 100644
--- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-unit-overlapping-address-ranges.test
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-unit-overlapping-address-ranges.test
@@ -6,6 +6,9 @@
# RUN: llvm-dwarfutil --garbage-collection %t.o %t1
# RUN: llvm-dwarfdump -a %t1 | FileCheck %s
+# RUN: llvm-dwarfutil --linker llvm --garbage-collection %t.o %t1
+# RUN: llvm-dwarfdump -a %t1 | FileCheck %s
+
# CHECK: DW_TAG_compile_unit
# CHECK: DW_AT_name{{.*}}"CU1"
# CHECK: DW_TAG_class_type
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/verify.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/verify.test
index 844e8fb671af3e..e9557edddde427 100644
--- a/llvm/test/tools/llvm-dwarfutil/ELF/X86/verify.test
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/verify.test
@@ -7,18 +7,23 @@
## Verify resulting debug info after --garbage-collection optimisation.
# RUN: llvm-dwarfutil %t.o %t1 --verify
+# RUN: llvm-dwarfutil --linker llvm %t.o %t1 --verify
## Verify separate debug file after --garbage-collection optimisation.
# RUN: llvm-dwarfutil %t.o --separate-debug-file %t1 --verify
+# RUN: llvm-dwarfutil --linker llvm %t.o --separate-debug-file %t1 --verify
## Verify not optimised resulting debug info.
# RUN: not llvm-dwarfutil --no-garbage-collection %t.o %t1 --verify 2>&1 | FileCheck %s -DFILE=%t1
+# RUN: not llvm-dwarfutil --linker llvm --no-garbage-collection %t.o %t1 --verify 2>&1 | FileCheck %s -DFILE=%t1
## Verify not optimised resulting separate debug file.
# RUN: not llvm-dwarfutil --no-garbage-collection %t.o --separate-debug-file %t1 --verify 2>&1 | FileCheck %s -DFILE=%t1.debug
+# RUN: not llvm-dwarfutil --linker llvm --no-garbage-collection %t.o --separate-debug-file %t1 --verify 2>&1 | FileCheck %s -DFILE=%t1.debug
## Check that verification is disabled when destination is stdout.
# RUN: llvm-dwarfutil %t.o - --verify 2>&1 | FileCheck %s --check-prefix=CHECK-STDOUT
+# RUN: llvm-dwarfutil --linker llvm %t.o - --verify 2>&1 | FileCheck %s --check-prefix=CHECK-STDOUT
# CHECK: error: '[[FILE]]': output verification failed
# CHECK-STDOUT: warning: verification skipped because writing to stdout
diff --git a/llvm/tools/dsymutil/BinaryHolder.cpp b/llvm/tools/dsymutil/BinaryHolder.cpp
index d8293f7f354f2d..c6661f4058f102 100644
--- a/llvm/tools/dsymutil/BinaryHolder.cpp
+++ b/llvm/tools/dsymutil/BinaryHolder.cpp
@@ -238,6 +238,7 @@ BinaryHolder::getObjectEntry(StringRef Filename, TimestampTy Timestamp) {
if (isArchive(Filename)) {
StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;
std::lock_guard<std::mutex> Lock(ArchiveCacheMutex);
+ ArchiveRefCounter[ArchiveFilename]++;
if (ArchiveCache.count(ArchiveFilename)) {
return ArchiveCache[ArchiveFilename]->getObjectEntry(Filename, Timestamp,
Verbose);
@@ -258,6 +259,7 @@ BinaryHolder::getObjectEntry(StringRef Filename, TimestampTy Timestamp) {
// If this is an object, we might have it cached. If not we'll have to load
// it from the file system and cache it now.
std::lock_guard<std::mutex> Lock(ObjectCacheMutex);
+ ObjectRefCounter[Filename]++;
if (!ObjectCache.count(Filename)) {
auto OE = std::make_unique<ObjectEntry>();
auto Err = OE->load(VFS, Filename, Timestamp, Verbose);
@@ -276,5 +278,24 @@ void BinaryHolder::clear() {
ObjectCache.clear();
}
+void BinaryHolder::eraseObjectEntry(StringRef Filename) {
+ if (Verbose)
+ WithColor::note() << "erasing '" << Filename << "' from cache\n";
+
+ if (isArchive(Filename)) {
+ StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first;
+ std::lock_guard<std::mutex> Lock(ArchiveCacheMutex);
+ ArchiveRefCounter[ArchiveFilename]--;
+ if (ArchiveRefCounter[ArchiveFilename] == 0)
+ ArchiveCache.erase(ArchiveFilename);
+ return;
+ }
+
+ std::lock_guard<std::mutex> Lock(ObjectCacheMutex);
+ ObjectRefCounter[Filename]--;
+ if (ObjectRefCounter[Filename] == 0)
+ ObjectCache.erase(Filename);
+}
+
} // namespace dsymutil
} // namespace llvm
diff --git a/llvm/tools/dsymutil/BinaryHolder.h b/llvm/tools/dsymutil/BinaryHolder.h
index 84084c568cbea3..b70bf338936fa4 100644
--- a/llvm/tools/dsymutil/BinaryHolder.h
+++ b/llvm/tools/dsymutil/BinaryHolder.h
@@ -126,15 +126,18 @@ class BinaryHolder {
getObjectEntry(StringRef Filename, TimestampTy Timestamp = TimestampTy());
void clear();
+ void eraseObjectEntry(StringRef Filename);
private:
/// Cache of static archives. Objects that are part of a static archive are
/// stored under this object, rather than in the map below.
StringMap<std::unique_ptr<ArchiveEntry>> ArchiveCache;
+ DenseMap<StringRef, uint32_t> ArchiveRefCounter;
std::mutex ArchiveCacheMutex;
/// Object entries for objects that are not in a static archive.
StringMap<std::unique_ptr<ObjectEntry>> ObjectCache;
+ DenseMap<StringRef, uint32_t> ObjectRefCounter;
std::mutex ObjectCacheMutex;
/// Virtual File System instance.
diff --git a/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp b/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp
index 4b281d5f19801a..5e94901c0babc4 100644
--- a/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp
+++ b/llvm/tools/dsymutil/DwarfLinkerForBinary.cpp
@@ -123,16 +123,22 @@ static void dumpDIE(const DWARFDie *DIE, bool Verbose) {
/// specific \p DIE related to the warning.
void DwarfLinkerForBinary::reportWarning(Twine Warning, Twine Context,
const DWARFDie *DIE) const {
- std::lock_guard<std::mutex> Guard(ErrorHandlerMutex);
- warn(Warning, Context);
- dumpDIE(DIE, Options.Verbose);
+ // FIXME: implement warning logging which does not block other threads.
+ if (ErrorHandlerMutex.try_lock()) {
+ warn(Warning, Context);
+ dumpDIE(DIE, Options.Verbose);
+ ErrorHandlerMutex.unlock();
+ }
}
void DwarfLinkerForBinary::reportError(Twine Error, Twine Context,
const DWARFDie *DIE) const {
- std::lock_guard<std::mutex> Guard(ErrorHandlerMutex);
- error(Error, Context);
- dumpDIE(DIE, Options.Verbose);
+ // FIXME: implement error logging which does not block other threads.
+ if (ErrorHandlerMutex.try_lock()) {
+ error(Error, Context);
+ dumpDIE(DIE, Options.Verbose);
+ ErrorHandlerMutex.unlock();
+ }
}
ErrorOr<const object::ObjectFile &>
@@ -233,9 +239,23 @@ DwarfLinkerForBinary::loadObject(const DebugMapObject &Obj,
if (ErrorOrObj) {
Res = std::make_unique<OutDWARFFile>(
- Obj.getObjectFilename(), DWARFContext::create(*ErrorOrObj),
+ Obj.getObjectFilename(),
+ DWARFContext::create(
+ *ErrorOrObj, DWARFContext::ProcessDebugRelocations::Process,
+ nullptr, "",
+ [&](Error Err) {
+ handleAllErrors(std::move(Err), [&](ErrorInfoBase &Info) {
+ reportError(Info.message());
+ });
+ },
+ [&](Error Warning) {
+ handleAllErrors(std::move(Warning), [&](ErrorInfoBase &Info) {
+ reportWarning(Info.message());
+ });
+ }),
std::make_unique<AddressesMap>(*this, *ErrorOrObj, Obj),
- Obj.empty() ? Obj.getWarnings() : EmptyWarnings);
+ Obj.empty() ? Obj.getWarnings() : EmptyWarnings,
+ [&](StringRef FileName) { BinHolder.eraseObjectEntry(FileName); });
Error E = RL.link(*ErrorOrObj);
if (Error NewE = handleErrors(
@@ -1036,8 +1056,10 @@ std::optional<int64_t> DwarfLinkerForBinary::AddressManager<AddressesMapBase>::
default: {
assert(false && "Specified operation does not have address operand");
} break;
+ case dwarf::DW_OP_const2u:
case dwarf::DW_OP_const4u:
case dwarf::DW_OP_const8u:
+ case dwarf::DW_OP_const2s:
case dwarf::DW_OP_const4s:
case dwarf::DW_OP_const8s:
case dwarf::DW_OP_addr: {
diff --git a/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp b/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp
index 47a23e8448cc49..fbbead16a03709 100644
--- a/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp
+++ b/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp
@@ -98,15 +98,18 @@ class ObjFileAddressMap : public AddressMapBase {
return std::nullopt;
}
- std::optional<int64_t> getExprOpAddressRelocAdjustment(
- DWARFUnit &U, const DWARFExpression::Operation &Op, uint64_t StartOffset,
- uint64_t EndOffset) override {
+ std::optional<int64_t>
+ getExprOpAddressRelocAdjustment(DWARFUnit &U,
+ const DWARFExpression::Operation &Op,
+ uint64_t, uint64_t) override {
switch (Op.getCode()) {
default: {
assert(false && "Specified operation does not have address operand");
} break;
+ case dwarf::DW_OP_const2u:
case dwarf::DW_OP_const4u:
case dwarf::DW_OP_const8u:
+ case dwarf::DW_OP_const2s:
case dwarf::DW_OP_const4s:
case dwarf::DW_OP_const8s:
case dwarf::DW_OP_addr: {
@@ -287,23 +290,33 @@ static std::string getMessageForDeletedAcceleratorTables(
template <typename Linker, typename OutDwarfFile, typename AddressMapBase>
Error linkDebugInfoImpl(object::ObjectFile &File, const Options &Options,
raw_pwrite_stream &OutStream) {
+ std::mutex ErrorHandlerMutex;
+
auto ReportWarn = [&](const Twine &Message, StringRef Context,
const DWARFDie *Die) {
- warning(Message, Context);
-
- if (!Options.Verbose || !Die)
+ // FIXME: implement warning logging which does not block other threads.
+ if (!ErrorHandlerMutex.try_lock())
return;
- DIDumpOptions DumpOpts;
- DumpOpts.ChildRecurseDepth = 0;
- DumpOpts.Verbose = Options.Verbose;
+ warning(Message, Context);
+ if (Options.Verbose && Die) {
+ DIDumpOptions DumpOpts;
+ DumpOpts.ChildRecurseDepth = 0;
+ DumpOpts.Verbose = Options.Verbose;
- WithColor::note() << " in DIE:\n";
- Die->dump(errs(), /*Indent=*/6, DumpOpts);
+ WithColor::note() << " in DIE:\n";
+ Die->dump(errs(), /*Indent=*/6, DumpOpts);
+ }
+ ErrorHandlerMutex.unlock();
};
auto ReportErr = [&](const Twine &Message, StringRef Context,
const DWARFDie *) {
+ // FIXME: implement error logging which does not block other threads.
+ if (!ErrorHandlerMutex.try_lock())
+ return;
+
WithColor::error(errs(), Context) << Message << '\n';
+ ErrorHandlerMutex.unlock();
};
// Create DWARF linker.
@@ -325,7 +338,18 @@ Error linkDebugInfoImpl(object::ObjectFile &File, const Options &Options,
std::vector<std::string> EmptyWarnings;
// Add object files to the DWARFLinker.
- std::unique_ptr<DWARFContext> Context = DWARFContext::create(File);
+ std::unique_ptr<DWARFContext> Context = DWARFContext::create(
+ File, DWARFContext::ProcessDebugRelocations::Process, nullptr, "",
+ [&](Error Err) {
+ handleAllErrors(std::move(Err), [&](ErrorInfoBase &Info) {
+ ReportErr(Info.message(), "", nullptr);
+ });
+ },
+ [&](Error Warning) {
+ handleAllErrors(std::move(Warning), [&](ErrorInfoBase &Info) {
+ ReportWarn(Info.message(), "", nullptr);
+ });
+ });
std::unique_ptr<ObjFileAddressMap<AddressMapBase>> AddressesMap(
std::make_unique<ObjFileAddressMap<AddressMapBase>>(*Context, Options,
File));
diff --git a/llvm/unittests/CodeGen/DwarfStringPoolEntryRefTest.cpp b/llvm/unittests/CodeGen/DwarfStringPoolEntryRefTest.cpp
index 6e901f7a18f27d..25db003ea81deb 100644
--- a/llvm/unittests/CodeGen/DwarfStringPoolEntryRefTest.cpp
+++ b/llvm/unittests/CodeGen/DwarfStringPoolEntryRefTest.cpp
@@ -61,40 +61,25 @@ bool isEntryEqual(const DwarfStringPoolEntry &LHS,
}
TEST(DwarfStringPoolEntryRefTest, TestShortEntry) {
- BumpPtrAllocator Allocator;
- DwarfStringPoolEntry DwarfEntry1 = {nullptr, 0, 0};
- StringMapEntry<DwarfStringPoolEntry *> *StringEntry1 =
- StringMapEntry<DwarfStringPoolEntry *>::create("Key1", Allocator,
- &DwarfEntry1);
-
- EXPECT_TRUE(StringEntry1->getKey() == "Key1");
- EXPECT_TRUE(StringEntry1->second->Symbol == nullptr);
- EXPECT_TRUE(StringEntry1->second->Offset == 0);
- EXPECT_TRUE(StringEntry1->second->Index == 0);
+ DwarfStringPoolEntryWithExtString DwarfEntry1 = {{nullptr, 0, 0}, "Key1"};
- DwarfStringPoolEntryRef Ref1(*StringEntry1);
+ DwarfStringPoolEntryRef Ref1(DwarfEntry1);
EXPECT_TRUE(Ref1.getString() == "Key1");
EXPECT_TRUE(Ref1.getOffset() == 0);
EXPECT_TRUE(Ref1.getIndex() == 0);
EXPECT_TRUE(isEntryEqual(Ref1.getEntry(), DwarfEntry1));
- DwarfStringPoolEntryRef Ref2(*StringEntry1);
+ DwarfStringPoolEntryRef Ref2(DwarfEntry1);
EXPECT_TRUE(Ref2.getString() == "Key1");
EXPECT_TRUE(Ref2.getOffset() == 0);
EXPECT_TRUE(isEntryEqual(Ref2.getEntry(), DwarfEntry1));
EXPECT_TRUE(Ref1 == Ref2);
EXPECT_FALSE(Ref1 != Ref2);
- DwarfStringPoolEntry DwarfEntry2 = {nullptr, 0x1000, 1};
- StringMapEntry<DwarfStringPoolEntry *> *StringEntry2 =
- StringMapEntry<DwarfStringPoolEntry *>::create("Key2", Allocator,
- &DwarfEntry2);
- EXPECT_TRUE(StringEntry2->getKey() == "Key2");
- EXPECT_TRUE(StringEntry2->second->Symbol == nullptr);
- EXPECT_TRUE(StringEntry2->second->Offset == 0x1000);
- EXPECT_TRUE(StringEntry2->second->Index == 1);
+ DwarfStringPoolEntryWithExtString DwarfEntry2 = {{nullptr, 0x1000, 1},
+ "Key2"};
- DwarfStringPoolEntryRef Ref3(*StringEntry2);
+ DwarfStringPoolEntryRef Ref3(DwarfEntry2);
EXPECT_TRUE(Ref3.getString() == "Key2");
EXPECT_TRUE(Ref3.getOffset() == 0x1000);
EXPECT_TRUE(Ref3.getIndex() == 1);
@@ -105,11 +90,8 @@ TEST(DwarfStringPoolEntryRefTest, TestShortEntry) {
TEST(DwarfStringPoolEntryRefTest, CompareFullAndShort) {
BumpPtrAllocator Allocator;
- DwarfStringPoolEntry DwarfEntry1 = {nullptr, 0, 0};
- StringMapEntry<DwarfStringPoolEntry *> *StringEntry1 =
- StringMapEntry<DwarfStringPoolEntry *>::create("Key1", Allocator,
- &DwarfEntry1);
- DwarfStringPoolEntryRef Ref1(*StringEntry1);
+ DwarfStringPoolEntryWithExtString DwarfEntry1 = {{nullptr, 0, 0}, "Key1"};
+ DwarfStringPoolEntryRef Ref1(DwarfEntry1);
StringMapEntry<DwarfStringPoolEntry> *StringEntry2 =
StringMapEntry<DwarfStringPoolEntry>::create(
diff --git a/llvm/unittests/DWARFLinkerParallel/CMakeLists.txt b/llvm/unittests/DWARFLinkerParallel/CMakeLists.txt
index e06806103b313c..5eeec23df4edba 100644
--- a/llvm/unittests/DWARFLinkerParallel/CMakeLists.txt
+++ b/llvm/unittests/DWARFLinkerParallel/CMakeLists.txt
@@ -6,7 +6,6 @@ set(LLVM_LINK_COMPONENTS
add_llvm_unittest(DWARFLinkerParallelTests
DWARFLinkerTest.cpp
StringPoolTest.cpp
- StringTableTest.cpp
)
target_link_libraries(DWARFLinkerParallelTests PRIVATE LLVMTestingSupport)
diff --git a/llvm/unittests/DWARFLinkerParallel/StringPoolTest.cpp b/llvm/unittests/DWARFLinkerParallel/StringPoolTest.cpp
index 11ae18c9e8621f..84199f696a59de 100644
--- a/llvm/unittests/DWARFLinkerParallel/StringPoolTest.cpp
+++ b/llvm/unittests/DWARFLinkerParallel/StringPoolTest.cpp
@@ -28,20 +28,17 @@ TEST(StringPoolTest, TestStringPool) {
std::pair<StringEntry *, bool> Entry = Strings.insert("test");
EXPECT_TRUE(Entry.second);
EXPECT_TRUE(Entry.first->getKey() == "test");
- EXPECT_TRUE(Entry.first->second == nullptr);
StringEntry *EntryPtr = Entry.first;
Entry = Strings.insert("test");
EXPECT_FALSE(Entry.second);
EXPECT_TRUE(Entry.first->getKey() == "test");
- EXPECT_TRUE(Entry.first->second == nullptr);
EXPECT_TRUE(EntryPtr == Entry.first);
Entry = Strings.insert("test2");
EXPECT_TRUE(Entry.second);
EXPECT_TRUE(Entry.first->getKey() == "test2");
- EXPECT_TRUE(Entry.first->second == nullptr);
EXPECT_TRUE(EntryPtr != Entry.first);
});
}
@@ -54,7 +51,6 @@ TEST(StringPoolTest, TestStringPoolParallel) {
std::pair<StringEntry *, bool> Entry = Strings.insert(std::to_string(Idx));
EXPECT_TRUE(Entry.second);
EXPECT_TRUE(Entry.first->getKey() == std::to_string(Idx));
- EXPECT_TRUE(Entry.first->second == nullptr);
});
// Check data.
@@ -62,7 +58,6 @@ TEST(StringPoolTest, TestStringPoolParallel) {
std::pair<StringEntry *, bool> Entry = Strings.insert(std::to_string(Idx));
EXPECT_FALSE(Entry.second);
EXPECT_TRUE(Entry.first->getKey() == std::to_string(Idx));
- EXPECT_TRUE(Entry.first->second == nullptr);
});
}
diff --git a/llvm/unittests/DWARFLinkerParallel/StringTableTest.cpp b/llvm/unittests/DWARFLinkerParallel/StringTableTest.cpp
deleted file mode 100644
index 02bb95001acd42..00000000000000
--- a/llvm/unittests/DWARFLinkerParallel/StringTableTest.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-//===- llvm/unittest/DWARFLinkerParallel/StringTableTest.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 "llvm/DWARFLinkerParallel/StringTable.h"
-#include "llvm/Support/Parallel.h"
-#include "gtest/gtest.h"
-#include <cstdlib>
-
-using namespace llvm;
-using namespace dwarflinker_parallel;
-
-namespace {
-
-TEST(StringPoolTest, TestStringTable) {
- struct StringDescription {
- const char *Str = nullptr;
- uint64_t Idx = 0;
- uint64_t Offset = 0;
- };
-
- SmallVector<StringDescription> InputStrings = {
- {"first", 0, 0}, {"second", 1, 6}, {"third", 2, 13}};
-
- StringPool Strings;
- StringTable OutStrings(Strings, nullptr);
-
- // StringPool uses PerThreadBumpPtrAllocator which should be accessed from
- // threads created by ThreadPoolExecutor. Use TaskGroup to run on
- // ThreadPoolExecutor threads.
- parallel::TaskGroup tg;
-
- tg.spawn([&]() {
- // Check string insertion.
- StringEntry *FirstPtr = Strings.insert(InputStrings[0].Str).first;
- StringEntry *SecondPtr = Strings.insert(InputStrings[1].Str).first;
- StringEntry *ThirdPtr = Strings.insert(InputStrings[2].Str).first;
-
- FirstPtr = OutStrings.add(FirstPtr);
- SecondPtr = OutStrings.add(SecondPtr);
- ThirdPtr = OutStrings.add(ThirdPtr);
-
- // Check fields of inserted strings.
- EXPECT_TRUE(FirstPtr->getKey() == InputStrings[0].Str);
- EXPECT_TRUE(FirstPtr->getValue()->Offset == InputStrings[0].Offset);
- EXPECT_TRUE(FirstPtr->getValue()->Index == InputStrings[0].Idx);
-
- EXPECT_TRUE(SecondPtr->getKey() == InputStrings[1].Str);
- EXPECT_TRUE(SecondPtr->getValue()->Offset == InputStrings[1].Offset);
- EXPECT_TRUE(SecondPtr->getValue()->Index == InputStrings[1].Idx);
-
- EXPECT_TRUE(ThirdPtr->getKey() == InputStrings[2].Str);
- EXPECT_TRUE(ThirdPtr->getValue()->Offset == InputStrings[2].Offset);
- EXPECT_TRUE(ThirdPtr->getValue()->Index == InputStrings[2].Idx);
-
- // Check order enumerated strings.
- uint64_t CurIdx = 0;
- std::function<void(DwarfStringPoolEntryRef)> checkStr =
- [&](DwarfStringPoolEntryRef Entry) {
- EXPECT_TRUE(Entry.getEntry().isIndexed());
- EXPECT_TRUE(Entry.getIndex() == CurIdx);
- EXPECT_TRUE(Entry.getOffset() == InputStrings[CurIdx].Offset);
- EXPECT_TRUE(Entry.getString() == InputStrings[CurIdx].Str);
-
- CurIdx++;
- };
-
- OutStrings.forEach(checkStr);
- });
-}
-
-TEST(StringPoolTest, TestStringTableWithTranslator) {
- std::string Word;
- std::function<StringRef(StringRef)> TranslatorFunc =
- [&](StringRef InputString) -> StringRef {
- Word.clear();
- for (auto Sym : InputString)
- Word.insert(Word.begin(), Sym);
- Word += '0';
- return Word;
- };
-
- StringPool Strings;
- StringTable OutStrings(Strings, TranslatorFunc);
-
- // StringPool uses PerThreadBumpPtrAllocator which should be accessed from
- // threads created by ThreadPoolExecutor. Use TaskGroup to run on
- // ThreadPoolExecutor threads.
- parallel::TaskGroup tg;
-
- tg.spawn([&]() {
- StringEntry *FirstPtr = Strings.insert("first").first;
- StringEntry *SecondPtr = Strings.insert("second").first;
- StringEntry *ThirdPtr = Strings.insert("third").first;
-
- FirstPtr = OutStrings.add(FirstPtr);
- SecondPtr = OutStrings.add(SecondPtr);
- ThirdPtr = OutStrings.add(ThirdPtr);
-
- EXPECT_TRUE(FirstPtr->getKey() == "tsrif0");
- EXPECT_TRUE(FirstPtr->getValue()->Offset == 0);
- EXPECT_TRUE(FirstPtr->getValue()->Index == 0);
-
- EXPECT_TRUE(SecondPtr->getKey() == "dnoces0");
- EXPECT_TRUE(SecondPtr->getValue()->Offset == 7);
- EXPECT_TRUE(SecondPtr->getValue()->Index == 1);
-
- EXPECT_TRUE(ThirdPtr->getKey() == "driht0");
- EXPECT_TRUE(ThirdPtr->getValue()->Offset == 15);
- EXPECT_TRUE(ThirdPtr->getValue()->Index == 2);
- });
-}
-
-} // anonymous namespace
More information about the llvm-commits
mailing list