[llvm] 81d3c19 - [DWARFLinker] Support parseable Swift interfaces in the parallel linker (#195475)

via llvm-commits llvm-commits at lists.llvm.org
Tue May 5 10:38:52 PDT 2026


Author: Jonas Devlieghere
Date: 2026-05-05T10:38:47-07:00
New Revision: 81d3c19137011a8f46495c8a3a24cf8a8c0a20df

URL: https://github.com/llvm/llvm-project/commit/81d3c19137011a8f46495c8a3a24cf8a8c0a20df
DIFF: https://github.com/llvm/llvm-project/commit/81d3c19137011a8f46495c8a3a24cf8a8c0a20df.diff

LOG: [DWARFLinker] Support parseable Swift interfaces in the parallel linker (#195475)

CompileUnit::Language was filtered through isODRLanguage at construction,
so non-ODR languages landed as std::nullopt and analyzeImportedModule's
Swift branch was never reached under --linker parallel. Store the raw
DW_AT_language and apply isODRLanguage only where ODR is actually
decided.

Reaching that branch then exposes a shared-map write from
parallelForEach workers. A mutex would make it safe but not
deterministic: conflict warnings and last-writer-wins contents would
depend on thread scheduling and diverge from the classic linker. Stage
entries per-CU and merge them serially in input order after analysis, so
results match classic regardless of schedule.

Because the diagnostics are emitted after unloading the input, we do
lose the originating DW_TAG_module. The warning still names the module
and both conflicting paths, which carries the load-bearing information.

Added: 
    

Modified: 
    llvm/lib/DWARFLinker/Parallel/DWARFLinkerCompileUnit.cpp
    llvm/lib/DWARFLinker/Parallel/DWARFLinkerCompileUnit.h
    llvm/lib/DWARFLinker/Parallel/DWARFLinkerImpl.cpp
    llvm/test/tools/dsymutil/X86/swift-interface.test

Removed: 
    


################################################################################
diff  --git a/llvm/lib/DWARFLinker/Parallel/DWARFLinkerCompileUnit.cpp b/llvm/lib/DWARFLinker/Parallel/DWARFLinkerCompileUnit.cpp
index ebe914c4f8c52..eb9b3b87f2d39 100644
--- a/llvm/lib/DWARFLinker/Parallel/DWARFLinkerCompileUnit.cpp
+++ b/llvm/lib/DWARFLinker/Parallel/DWARFLinkerCompileUnit.cpp
@@ -51,13 +51,11 @@ CompileUnit::CompileUnit(LinkingGlobalData &GlobalData, DWARFUnit &OrigUnit,
   if (!CUDie)
     return;
 
-  if (std::optional<DWARFFormValue> Val = CUDie.find(dwarf::DW_AT_language)) {
-    uint16_t LangVal = dwarf::toUnsigned(Val, 0);
-    if (isODRLanguage(LangVal))
-      Language = LangVal;
-  }
+  if (std::optional<DWARFFormValue> Val = CUDie.find(dwarf::DW_AT_language))
+    Language = dwarf::toUnsigned(Val, 0);
 
-  if (!GlobalData.getOptions().NoODR && Language.has_value())
+  if (!GlobalData.getOptions().NoODR && Language.has_value() &&
+      isODRLanguage(*Language))
     NoODR = false;
 
   if (const char *CUName = CUDie.getName(DINameKind::ShortName))
@@ -293,7 +291,6 @@ void CompileUnit::analyzeImportedModule(const DWARFDebugInfoEntry *DieEntry) {
       return;
     }
 
-    auto &Entry = (*GlobalData.getOptions().ParseableSwiftInterfaces)[*Name];
     // The prepend path is applied later when copying.
     SmallString<128> ResolvedPath;
     if (sys::path::is_relative(Path))
@@ -301,14 +298,25 @@ void CompileUnit::analyzeImportedModule(const DWARFDebugInfoEntry *DieEntry) {
           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);
+
+    // Stage the entry. It will be merged into the shared
+    // ParseableSwiftInterfaces map after the parallel analysis phase so that
+    // the final contents and any conflict warnings are deterministic.
+    PendingSwiftInterfaces.emplace_back(*Name, ResolvedPath);
+  }
+}
+
+void CompileUnit::mergeSwiftInterfaces(
+    DWARFLinkerBase::SwiftInterfacesMapTy &Map) {
+  for (auto &Pending : PendingSwiftInterfaces) {
+    auto &Entry = Map[Pending.ModuleName];
+    if (!Entry.empty() && Entry != Pending.ResolvedPath)
+      warn(Twine("conflicting parseable interfaces for Swift Module ") +
+           Pending.ModuleName + ": " + Entry + " and " + Pending.ResolvedPath +
+           ".");
+    Entry = Pending.ResolvedPath;
   }
+  PendingSwiftInterfaces.clear();
 }
 
 Error CompileUnit::assignTypeNames(TypePool &TypePoolRef) {

diff  --git a/llvm/lib/DWARFLinker/Parallel/DWARFLinkerCompileUnit.h b/llvm/lib/DWARFLinker/Parallel/DWARFLinkerCompileUnit.h
index 9edea8a0509ac..f57adcea5bd71 100644
--- a/llvm/lib/DWARFLinker/Parallel/DWARFLinkerCompileUnit.h
+++ b/llvm/lib/DWARFLinker/Parallel/DWARFLinkerCompileUnit.h
@@ -127,9 +127,15 @@ class alignas(8) CompileUnit : public DwarfUnit {
   void maybeResetToLoadedStage();
 
   /// Collect references to parseable Swift interfaces in imported
-  /// DW_TAG_module blocks.
+  /// DW_TAG_module blocks. The entries are staged on the CompileUnit and
+  /// merged into the shared map after the parallel analysis phase.
   void analyzeImportedModule(const DWARFDebugInfoEntry *DieEntry);
 
+  /// Merge the Swift interface entries collected by analyzeImportedModule
+  /// into \p Map, emitting a warning for each conflicting path. Must be
+  /// called serially after analysis has completed.
+  void mergeSwiftInterfaces(DWARFLinkerBase::SwiftInterfacesMapTy &Map);
+
   /// Navigate DWARF tree and set die properties.
   void analyzeDWARFStructure() {
     analyzeDWARFStructureRec(getUnitDIE().getDebugInfoEntry(), false);
@@ -731,9 +737,19 @@ class alignas(8) CompileUnit : public DwarfUnit {
   /// Pointer to the paired compile unit from the input DWARF.
   DWARFUnit *OrigUnit = nullptr;
 
-  /// The DW_AT_language of this unit.
+  /// Raw DW_AT_language from the input (not ODR-filtered).
   std::optional<uint16_t> Language;
 
+  /// Parseable Swift interface entries staged during the parallel analysis
+  /// phase. Merged serially afterwards.
+  struct PendingSwiftInterface {
+    PendingSwiftInterface(StringRef ModuleName, StringRef ResolvedPath)
+        : ModuleName(ModuleName), ResolvedPath(ResolvedPath) {}
+    std::string ModuleName;
+    std::string ResolvedPath;
+  };
+  SmallVector<PendingSwiftInterface> PendingSwiftInterfaces;
+
   /// Line table for this unit.
   const DWARFDebugLine::LineTable *LineTablePtr = nullptr;
 

diff  --git a/llvm/lib/DWARFLinker/Parallel/DWARFLinkerImpl.cpp b/llvm/lib/DWARFLinker/Parallel/DWARFLinkerImpl.cpp
index 7230767ab0563..5595cf9d9cbc5 100644
--- a/llvm/lib/DWARFLinker/Parallel/DWARFLinkerImpl.cpp
+++ b/llvm/lib/DWARFLinker/Parallel/DWARFLinkerImpl.cpp
@@ -203,6 +203,20 @@ Error DWARFLinkerImpl::link() {
     Pool.wait();
   }
 
+  // Merge staged parseable Swift interface entries into the shared map. Done
+  // serially so that the final map contents and any conflict warnings are
+  // deterministic.
+  if (DWARFLinkerBase::SwiftInterfacesMapTy *SwiftInterfaces =
+          GlobalData.Options.ParseableSwiftInterfaces) {
+    for (std::unique_ptr<LinkContext> &Context : ObjectContexts) {
+      for (LinkContext::RefModuleUnit &ModuleUnit :
+           Context->ModulesCompileUnits)
+        ModuleUnit.Unit->mergeSwiftInterfaces(*SwiftInterfaces);
+      for (std::unique_ptr<CompileUnit> &CU : Context->CompileUnits)
+        CU->mergeSwiftInterfaces(*SwiftInterfaces);
+    }
+  }
+
   if (ArtificialTypeUnit != nullptr && !ArtificialTypeUnit->getTypePool()
                                             .getRoot()
                                             ->getValue()

diff  --git a/llvm/test/tools/dsymutil/X86/swift-interface.test b/llvm/test/tools/dsymutil/X86/swift-interface.test
index d64b61070567e..bf03b5a804bfd 100644
--- a/llvm/test/tools/dsymutil/X86/swift-interface.test
+++ b/llvm/test/tools/dsymutil/X86/swift-interface.test
@@ -6,11 +6,20 @@
 # RUN: dsymutil --linker classic -oso-prepend-path %t.dir -y %s \
 # RUN:    -o %t.dir/swift-interface.dSYM 2>&1 \
 # RUN:    | FileCheck %s --check-prefix=WARNINGS
+# RUN: rm -rf %t.dir/swift-interface.dSYM
+# RUN: dsymutil --linker parallel -oso-prepend-path %t.dir -y %s \
+# RUN:    -o %t.dir/swift-interface.dSYM 2>&1 \
+# RUN:    | FileCheck %s --check-prefix=WARNINGS
 # RUN: echo "// module Foo" >%t.dir/Foo/x86_64.swiftinterface
 # RUN: dsymutil --linker classic -oso-prepend-path %t.dir -y %s \
 # RUN:    -o %t.dir/swift-interface.dSYM
 # RUN: cat %t.dir/swift-interface.dSYM/Contents/Resources/Swift/x86_64/Foo.swiftinterface \
 # RUN:   | FileCheck %s --check-prefix=INTERFACE
+# RUN: rm -rf %t.dir/swift-interface.dSYM
+# RUN: dsymutil --linker parallel -oso-prepend-path %t.dir -y %s \
+# RUN:    -o %t.dir/swift-interface.dSYM
+# RUN: cat %t.dir/swift-interface.dSYM/Contents/Resources/Swift/x86_64/Foo.swiftinterface \
+# RUN:   | FileCheck %s --check-prefix=INTERFACE
 
 # WARNINGS-NOT: cannot copy parseable Swift interface {{.*}}{{Swift|Foundation|_Concurrency}}
 # WARNINGS: cannot copy parseable Swift interface {{.*}}Foo
@@ -24,5 +33,3 @@ objects:
     symbols:
       - { sym: _main, objAddr: 0x0, binAddr: 0x10000, size: 0x10 }
 ...
-
-## FIXME: Support --linker parallel


        


More information about the llvm-commits mailing list