[lld] [llvm] [LLD][COFF] Make unresolved symbol search behavior compliant with MSVC link.exe (PR #85290)

Alexandre Ganea via llvm-commits llvm-commits at lists.llvm.org
Mon Apr 8 16:52:25 PDT 2024


https://github.com/aganea updated https://github.com/llvm/llvm-project/pull/85290

>From d5c4adfb5e85e80758cb51a91a246630924c7472 Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Thu, 14 Mar 2024 13:31:51 -0400
Subject: [PATCH 01/12] [LLD][COFF] Align unresolved symbols search behavior
 with MSVC link.exe

---
 lld/COFF/Driver.cpp                    |  53 +++++++++---
 lld/COFF/Driver.h                      |  21 +++--
 lld/COFF/InputFiles.cpp                |  13 +--
 lld/COFF/InputFiles.h                  |  26 ++++--
 lld/COFF/SymbolTable.cpp               | 110 +++++++++++++++++++++++--
 lld/COFF/Symbols.h                     |  11 +++
 lld/test/COFF/duplicate-imp-func.s     |   6 +-
 lld/test/COFF/lib-searching-behavior.s |  67 +++++++++++++++
 llvm/include/llvm/Support/Allocator.h  |  28 ++++++-
 9 files changed, 292 insertions(+), 43 deletions(-)
 create mode 100644 lld/test/COFF/lib-searching-behavior.s

diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 22ee2f133be98a..38e0392a876307 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -187,7 +187,8 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> mb) {
 }
 
 void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
-                             bool wholeArchive, bool lazy) {
+                             bool wholeArchive, bool lazy,
+                             ArchiveFile *parent) {
   StringRef filename = mb->getBufferIdentifier();
 
   MemoryBufferRef mbref = takeBuffer(std::move(mb));
@@ -213,11 +214,11 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
     ctx.symtab.addFile(make<ArchiveFile>(ctx, mbref));
     break;
   case file_magic::bitcode:
-    ctx.symtab.addFile(make<BitcodeFile>(ctx, mbref, "", 0, lazy));
+    ctx.symtab.addFile(make<BitcodeFile>(ctx, mbref, "", 0, lazy, parent));
     break;
   case file_magic::coff_object:
   case file_magic::coff_import_library:
-    ctx.symtab.addFile(make<ObjFile>(ctx, mbref, lazy));
+    ctx.symtab.addFile(make<ObjFile>(ctx, mbref, lazy, parent));
     break;
   case file_magic::pdb:
     ctx.symtab.addFile(make<PDBInputFile>(ctx, mbref));
@@ -242,7 +243,9 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
   }
 }
 
-void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) {
+void LinkerDriver::enqueuePath(
+    StringRef path, bool wholeArchive, bool lazy,
+    std::optional<std::shared_future<ArchiveFile *>> parent) {
   auto future = std::make_shared<std::future<MBErrPair>>(
       createFutureForFile(std::string(path)));
   std::string pathStr = std::string(path);
@@ -281,13 +284,15 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) {
       else
         error(msg + "; did you mean '" + nearest + "'");
     } else
-      ctx.driver.addBuffer(std::move(mb), wholeArchive, lazy);
+      ctx.driver.addBuffer(std::move(mb), wholeArchive, lazy,
+                           parent ? parent->get() : nullptr);
   });
 }
 
 void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName,
                                     StringRef parentName,
-                                    uint64_t offsetInArchive) {
+                                    uint64_t offsetInArchive,
+                                    ArchiveFile *parent) {
   file_magic magic = identify_magic(mb.getBuffer());
   if (magic == file_magic::coff_import_library) {
     InputFile *imp = make<ImportFile>(ctx, mb);
@@ -298,10 +303,10 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName,
 
   InputFile *obj;
   if (magic == file_magic::coff_object) {
-    obj = make<ObjFile>(ctx, mb);
+    obj = make<ObjFile>(ctx, mb, /*lazy=*/false, parent);
   } else if (magic == file_magic::bitcode) {
-    obj =
-        make<BitcodeFile>(ctx, mb, parentName, offsetInArchive, /*lazy=*/false);
+    obj = make<BitcodeFile>(ctx, mb, parentName, offsetInArchive,
+                            /*lazy=*/false, parent);
   } else if (magic == file_magic::coff_cl_gl_object) {
     error(mb.getBufferIdentifier() +
           ": is not a native COFF file. Recompile without /GL?");
@@ -318,7 +323,8 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName,
 
 void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
                                         const Archive::Symbol &sym,
-                                        StringRef parentName) {
+                                        StringRef parentName,
+                                        ArchiveFile *parent) {
 
   auto reportBufferError = [=](Error &&e, StringRef childName) {
     fatal("could not get the buffer for the member defining symbol " +
@@ -335,7 +341,7 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
     enqueueTask([=]() {
       llvm::TimeTraceScope timeScope("Archive: ", mb.getBufferIdentifier());
       ctx.driver.addArchiveBuffer(mb, toCOFFString(ctx, sym), parentName,
-                                  offsetInArchive);
+                                  offsetInArchive, parent);
     });
     return;
   }
@@ -356,7 +362,15 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
     // used as the buffer identifier.
     ctx.driver.addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)),
                                 toCOFFString(ctx, sym), "",
-                                /*OffsetInArchive=*/0);
+                                /*OffsetInArchive=*/0, parent);
+  });
+}
+
+void LinkerDriver::enqueueLazyFile(InputFile *file) {
+  enqueueTask([=]() {
+    // Once it has been enqued, it cannot be lazy anymore.
+    file->lazy = false;
+    ctx.symtab.addFile(file);
   });
 }
 
@@ -2111,17 +2125,30 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   {
     llvm::TimeTraceScope timeScope2("Parse & queue inputs");
     bool inLib = false;
+    std::optional<std::shared_future<ArchiveFile *>> inLibArchive;
     for (auto *arg : args) {
       switch (arg->getOption().getID()) {
       case OPT_end_lib:
         if (!inLib)
           error("stray " + arg->getSpelling());
         inLib = false;
+        inLibArchive = std::nullopt;
         break;
       case OPT_start_lib:
         if (inLib)
           error("nested " + arg->getSpelling());
         inLib = true;
+        // In is important to create a fake archive here so that we remember its
+        // placement on the command-line. This will be later needed to resolve
+        // symbols in the archive order required by the MSVC specification.
+        {
+          auto a = std::make_shared<std::promise<ArchiveFile *>>();
+          inLibArchive = a->get_future().share();
+          enqueueTask([=] {
+            a->set_value(
+                make<ArchiveFile>(ctx, MemoryBufferRef({}, "<cmdline-lib>")));
+          });
+        }
         break;
       case OPT_wholearchive_file:
         if (std::optional<StringRef> path = findFileIfNew(arg->getValue()))
@@ -2129,7 +2156,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
         break;
       case OPT_INPUT:
         if (std::optional<StringRef> path = findFileIfNew(arg->getValue()))
-          enqueuePath(*path, isWholeArchive(*path), inLib);
+          enqueuePath(*path, isWholeArchive(*path), inLib, inLibArchive);
         break;
       default:
         // Ignore other options.
diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h
index fa54de05befb58..da3c41e1bca734 100644
--- a/lld/COFF/Driver.h
+++ b/lld/COFF/Driver.h
@@ -22,6 +22,7 @@
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/TarWriter.h"
 #include "llvm/WindowsDriver/MSVCPaths.h"
+#include <future>
 #include <memory>
 #include <optional>
 #include <set>
@@ -91,13 +92,20 @@ class LinkerDriver {
 
   // Used by ArchiveFile to enqueue members.
   void enqueueArchiveMember(const Archive::Child &c, const Archive::Symbol &sym,
-                            StringRef parentName);
+                            StringRef parentName,
+                            ArchiveFile *parent = nullptr);
 
-  void enqueuePDB(StringRef Path) { enqueuePath(Path, false, false); }
+  void enqueuePDB(StringRef Path) {
+    enqueuePath(Path, false, false, /*parent=*/std::nullopt);
+  }
 
   MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb);
 
-  void enqueuePath(StringRef path, bool wholeArchive, bool lazy);
+  void enqueuePath(
+      StringRef path, bool wholeArchive, bool lazy,
+      std::optional<std::shared_future<ArchiveFile *>> parent = std::nullopt);
+
+  void enqueueLazyFile(InputFile *file);
 
   std::unique_ptr<llvm::TarWriter> tar; // for /linkrepro
 
@@ -182,10 +190,11 @@ class LinkerDriver {
   StringRef findDefaultEntry();
   WindowsSubsystem inferSubsystem();
 
-  void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive,
-                 bool lazy);
+  void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive, bool lazy,
+                 ArchiveFile *parent = nullptr);
   void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName,
-                        StringRef parentName, uint64_t offsetInArchive);
+                        StringRef parentName, uint64_t offsetInArchive,
+                        ArchiveFile *parent = nullptr);
 
   void enqueueTask(std::function<void()> task);
   bool run();
diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp
index 037fae45242c6f..42cdd1cf3b6c2c 100644
--- a/lld/COFF/InputFiles.cpp
+++ b/lld/COFF/InputFiles.cpp
@@ -94,9 +94,12 @@ static bool ignoredSymbolName(StringRef name) {
 }
 
 ArchiveFile::ArchiveFile(COFFLinkerContext &ctx, MemoryBufferRef m)
-    : InputFile(ctx, ArchiveKind, m) {}
+    : InputFile(ctx, ArchiveKind, m, /*lazy=*/true) {
+  static unsigned Order = 0;
+  CmdLineIndex = Order++;
+}
 
-void ArchiveFile::parse() {
+void ArchiveFile::parseLazy() {
   // Parse a MemoryBufferRef as an archive file.
   file = CHECK(Archive::create(mb), this);
 
@@ -115,7 +118,7 @@ void ArchiveFile::addMember(const Archive::Symbol &sym) {
   if (!seen.insert(c.getChildOffset()).second)
     return;
 
-  ctx.driver.enqueueArchiveMember(c, sym, getName());
+  ctx.driver.enqueueArchiveMember(c, sym, getName(), this);
 }
 
 std::vector<MemoryBufferRef> lld::coff::getArchiveMembers(Archive *file) {
@@ -1000,8 +1003,8 @@ void ImportFile::parse() {
 
 BitcodeFile::BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef mb,
                          StringRef archiveName, uint64_t offsetInArchive,
-                         bool lazy)
-    : InputFile(ctx, BitcodeKind, mb, lazy) {
+                         bool lazy, ArchiveFile *parent)
+    : InputFile(ctx, BitcodeKind, mb, lazy), parent(parent) {
   std::string path = mb.getBufferIdentifier().str();
   if (ctx.config.thinLTOIndexOnly)
     path = replaceThinLTOSuffix(mb.getBufferIdentifier(),
diff --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h
index 3b55cd791bfda2..7070f51fdf78ac 100644
--- a/lld/COFF/InputFiles.h
+++ b/lld/COFF/InputFiles.h
@@ -66,7 +66,6 @@ class InputFile {
   enum Kind {
     ArchiveKind,
     ObjectKind,
-    LazyObjectKind,
     PDBKind,
     ImportKind,
     BitcodeKind,
@@ -105,7 +104,7 @@ class InputFile {
 
 public:
   // True if this is a lazy ObjFile or BitcodeFile.
-  bool lazy = false;
+  bool lazy;
 };
 
 // .lib or .a file.
@@ -113,23 +112,30 @@ class ArchiveFile : public InputFile {
 public:
   explicit ArchiveFile(COFFLinkerContext &ctx, MemoryBufferRef m);
   static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; }
-  void parse() override;
+  void parse() override{};
+  void parseLazy();
 
   // Enqueues an archive member load for the given symbol. If we've already
   // enqueued a load for the same archive member, this function does nothing,
   // which ensures that we don't load the same member more than once.
   void addMember(const Archive::Symbol &sym);
 
-private:
   std::unique_ptr<Archive> file;
+
+  // The order this archive was seen on the cmd-line. This is later needed for
+  // resolving undefined symbols in archive OBJs.
+  uint32_t CmdLineIndex;
+
+private:
   llvm::DenseSet<uint64_t> seen;
 };
 
 // .obj or .o file. This may be a member of an archive file.
 class ObjFile : public InputFile {
 public:
-  explicit ObjFile(COFFLinkerContext &ctx, MemoryBufferRef m, bool lazy = false)
-      : InputFile(ctx, ObjectKind, m, lazy) {}
+  explicit ObjFile(COFFLinkerContext &ctx, MemoryBufferRef m, bool lazy = false,
+                   ArchiveFile *parent = nullptr)
+      : InputFile(ctx, ObjectKind, m, lazy), parent(parent) {}
   static bool classof(const InputFile *f) { return f->kind() == ObjectKind; }
   void parse() override;
   void parseLazy();
@@ -182,6 +188,9 @@ class ObjFile : public InputFile {
   // True if this file was compiled with /guard:ehcont.
   bool hasGuardEHCont() { return feat00Flags & 0x4000; }
 
+  // Whether this Obj buffer is part of an archive.
+  ArchiveFile *parent;
+
   // Pointer to the PDB module descriptor builder. Various debug info records
   // will reference object files by "module index", which is here. Things like
   // source files and section contributions are also recorded here. Will be null
@@ -369,7 +378,7 @@ class BitcodeFile : public InputFile {
 public:
   explicit BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef mb,
                        StringRef archiveName, uint64_t offsetInArchive,
-                       bool lazy);
+                       bool lazy = false, ArchiveFile *parent = nullptr);
   ~BitcodeFile();
   static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
   ArrayRef<Symbol *> getSymbols() { return symbols; }
@@ -377,6 +386,9 @@ class BitcodeFile : public InputFile {
   void parseLazy();
   std::unique_ptr<llvm::lto::InputFile> obj;
 
+  // Whether this bitcode buffer is part of an archive.
+  ArchiveFile *parent;
+
 private:
   void parse() override;
 
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index 44aa506d2c35da..f570e8c211f43d 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -54,8 +54,10 @@ void SymbolTable::addFile(InputFile *file) {
   if (file->lazy) {
     if (auto *f = dyn_cast<BitcodeFile>(file))
       f->parseLazy();
-    else
-      cast<ObjFile>(file)->parseLazy();
+    else if (auto *o = dyn_cast<ObjFile>(file))
+      o->parseLazy();
+    else if (auto *a = dyn_cast<ArchiveFile>(file))
+      a->parseLazy();
   } else {
     file->parse();
     if (auto *f = dyn_cast<ObjFile>(file)) {
@@ -102,7 +104,7 @@ static void forceLazy(Symbol *s) {
   }
   case Symbol::Kind::LazyObjectKind: {
     InputFile *file = cast<LazyObject>(s)->file;
-    file->ctx.symtab.addFile(file);
+    file->ctx.driver.enqueueLazyFile(file);
     break;
   }
   case Symbol::Kind::LazyDLLSymbolKind: {
@@ -562,6 +564,57 @@ std::pair<Symbol *, bool> SymbolTable::insert(StringRef name, InputFile *file) {
   return result;
 }
 
+static LazyIntrusiveNode *lazyNode(Symbol *s) {
+  if (auto *sym = dyn_cast<LazyArchive>(s))
+    return &sym->node;
+  if (auto *sym = dyn_cast<LazyObject>(s))
+    return &sym->node;
+  return nullptr;
+}
+
+static ArchiveFile *lazyParent(InputFile *f) {
+  if (!f)
+    return nullptr;
+  if (auto *obj = dyn_cast<ObjFile>(f))
+    return obj->parent;
+  if (auto *obj = dyn_cast<BitcodeFile>(f))
+    return obj->parent;
+  return nullptr;
+}
+
+static ArchiveFile *lazyArchive(Symbol *s) {
+  if (auto *sym = dyn_cast<LazyArchive>(s))
+    return sym->file;
+  if (auto *sym = dyn_cast<LazyObject>(s))
+    return lazyParent(sym->file);
+  return nullptr;
+}
+
+// The search behavior for undefined symbols is different when the OBJ
+// was pulled from an archive (LIB). This is documented here:
+// https://learn.microsoft.com/en-us/cpp/build/reference/link-input-files?view=msvc-170
+// "Object files on the command line are processed in the order they
+// appear on the command line. Libraries are searched in command line
+// order as well, with the following caveat: Symbols that are unresolved
+// when bringing in an object file from a library are searched for in
+// that library first, and then the following libraries from the command
+// line and /DEFAULTLIB (Specify default library) directives, and then
+// to any libraries at the beginning of the command line."
+static Symbol *searchArchiveSymbol(Symbol *s, ArchiveFile *pivot) {
+  auto &Alloc = getSpecificAllocSingleton<SymbolUnion>().Allocator;
+  Symbol *curr = s;
+  for (;;) {
+    if (lazyArchive(curr)->CmdLineIndex >= pivot->CmdLineIndex)
+      return curr;
+    uint32_t next = lazyNode(curr)->next;
+    if (!next)
+      break;
+    curr = reinterpret_cast<LazyArchive *>(
+        Alloc.fromAlignedIndex<SymbolUnion>(next));
+  }
+  return s;
+}
+
 Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
                                   bool isWeakAlias) {
   auto [s, wasInserted] = insert(name, f);
@@ -569,11 +622,43 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
     replaceSymbol<Undefined>(s, name);
     return s;
   }
-  if (s->isLazy())
+  if (s->isLazy()) {
+    if (ArchiveFile *parent = lazyParent(f)) {
+      Symbol *selected = searchArchiveSymbol(s, parent);
+      forceLazy(selected);
+      // Now that we have selected a symbol, we don't need the linked list of
+      // `LazyArchive`s anymore. Collapse to the selected symbol.
+      memcpy(s, selected, sizeof(SymbolUnion));
+      return s;
+    }
     forceLazy(s);
+  }
   return s;
 }
 
+// This creates a linked list of archives where a specific symbol was seen.
+// We later walk that list if a undefined symbol needs to be resolved from an
+// archive OBJ.
+template <typename T, typename... ArgT>
+static void chainLazy(LazyIntrusiveNode *front, ArgT &&...arg) {
+  // Chain with symbols defined in other archives
+  Symbol *newSym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
+  newSym->canInline = true;
+  replaceSymbol<T>(newSym, std::forward<ArgT>(arg)...);
+
+  auto &Alloc = getSpecificAllocSingleton<SymbolUnion>().Allocator;
+  uint32_t index = Alloc.identifyKnownAlignedObject<SymbolUnion>(newSym);
+
+  if (!front->next)
+    front->next = index;
+  if (front->last) {
+    Symbol *last = reinterpret_cast<Symbol *>(
+        Alloc.fromAlignedIndex<SymbolUnion>(front->last));
+    lazyNode(last)->next = index;
+  }
+  front->last = index;
+}
+
 void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) {
   StringRef name = sym.getName();
   auto [s, wasInserted] = insert(name);
@@ -581,6 +666,10 @@ void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) {
     replaceSymbol<LazyArchive>(s, f, sym);
     return;
   }
+  if (auto *n = lazyNode(s)) {
+    chainLazy<LazyArchive>(n, f, sym);
+    return;
+  }
   auto *u = dyn_cast<Undefined>(s);
   if (!u || u->weakAlias || s->pendingArchiveLoad)
     return;
@@ -588,19 +677,22 @@ void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) {
   f->addMember(sym);
 }
 
-void SymbolTable::addLazyObject(InputFile *f, StringRef n) {
+void SymbolTable::addLazyObject(InputFile *f, StringRef name) {
   assert(f->lazy);
-  auto [s, wasInserted] = insert(n, f);
+  auto [s, wasInserted] = insert(name, f);
   if (wasInserted) {
-    replaceSymbol<LazyObject>(s, f, n);
+    replaceSymbol<LazyObject>(s, f, name);
+    return;
+  }
+  if (auto *n = lazyNode(s)) {
+    chainLazy<LazyObject>(n, f, name);
     return;
   }
   auto *u = dyn_cast<Undefined>(s);
   if (!u || u->weakAlias || s->pendingArchiveLoad)
     return;
   s->pendingArchiveLoad = true;
-  f->lazy = false;
-  addFile(f);
+  f->ctx.driver.enqueueLazyFile(f);
 }
 
 void SymbolTable::addLazyDLLSymbol(DLLFile *f, DLLFile::Symbol *sym,
diff --git a/lld/COFF/Symbols.h b/lld/COFF/Symbols.h
index ca69fb2d052706..1577406c8626cf 100644
--- a/lld/COFF/Symbols.h
+++ b/lld/COFF/Symbols.h
@@ -286,6 +286,15 @@ class DefinedSynthetic : public Defined {
   uint32_t offset;
 };
 
+// Keep track of symbols with the same name exposed by archives. This is
+// required to later resolve unresolved symbols in the same order as required
+// by the MSVC spec. These are indexes in the specific bump allocator for
+// SymbolUnion.
+struct LazyIntrusiveNode {
+  uint32_t next = 0;
+  uint32_t last = 0;
+};
+
 // This class represents a symbol defined in an archive file. It is
 // created from an archive file header, and it knows how to load an
 // object file from an archive to replace itself with a defined
@@ -302,6 +311,7 @@ class LazyArchive : public Symbol {
 
   ArchiveFile *file;
   const Archive::Symbol sym;
+  LazyIntrusiveNode node;
 };
 
 class LazyObject : public Symbol {
@@ -309,6 +319,7 @@ class LazyObject : public Symbol {
   LazyObject(InputFile *f, StringRef n) : Symbol(LazyObjectKind, n), file(f) {}
   static bool classof(const Symbol *s) { return s->kind() == LazyObjectKind; }
   InputFile *file;
+  LazyIntrusiveNode node;
 };
 
 // MinGW only.
diff --git a/lld/test/COFF/duplicate-imp-func.s b/lld/test/COFF/duplicate-imp-func.s
index fc0cf1ef6ae051..631c714c951f77 100644
--- a/lld/test/COFF/duplicate-imp-func.s
+++ b/lld/test/COFF/duplicate-imp-func.s
@@ -28,8 +28,10 @@
 # Once the import library member from %t.lib.dll.a gets loaded, libfunc
 # and __imp_libfunc already are defined.
 
-# Just check that this fails cleanly (doesn't crash).
-# RUN: not lld-link -lldmingw -out:%t.main.exe -entry:main %t.main.o %t.lib.dll.a %t.helper.a
+# This test should now succeed since we're following the MSVC symbol searching behvior described in:
+# https://learn.microsoft.com/en-us/cpp/build/reference/link-input-files?view=msvc-170
+# In this case, the linker will select the libfunc symbol in %t.helper.a
+# RUN: lld-link -lldmingw -out:%t.main.exe -entry:main %t.main.o %t.lib.dll.a %t.helper.a
 
 # Test with %t.helper.a on the command line; in this case we won't try to
 # include libfunc from %t.lib.dll.a and everything works fine.
diff --git a/lld/test/COFF/lib-searching-behavior.s b/lld/test/COFF/lib-searching-behavior.s
new file mode 100644
index 00000000000000..eb4ba55c397534
--- /dev/null
+++ b/lld/test/COFF/lib-searching-behavior.s
@@ -0,0 +1,67 @@
+# REQUIRES: x86
+
+# This test ensures that we're following the MSVC symbol searching behvior described in:
+# https://learn.microsoft.com/en-us/cpp/build/reference/link-input-files?view=msvc-170
+# "Object files on the command line are processed in the order they appear on the command line.
+# Libraries are searched in command line order as well, with the following caveat: Symbols that
+# are unresolved when bringing in an object file from a library are searched for in that library
+# first, and then the following libraries from the command line and /DEFAULTLIB (Specify default
+# library) directives, and then to any libraries at the beginning of the command line."
+
+# RUN: echo -e ".intel_syntax noprefix\n.globl libfunc\n.text\nlibfunc:\nmov eax, 1\nret\n.section .drectve\n.ascii \"/EXPORT:libfunc\"" > %t.lib.s
+# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %t.lib.s -filetype=obj -o %t.lib.o
+# RUN: lld-link -dll -out:%t.lib.dll -entry:libfunc %t.lib.o -implib:%t.lib.dll.a
+
+# RUN: echo -e ".globl helper\n.text\nhelper:\ncall libfunc\nret" > %t.helper1.s
+# RUN: echo -e ".intel_syntax noprefix\n.globl libfunc\n.text\nlibfunc:\nxor eax, eax\nret" > %t.helper2.s
+# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %t.helper1.s -filetype=obj -o %t.helper1.o
+# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %t.helper2.s -filetype=obj -o %t.helper2.o
+
+# RUN: llvm-ar rcs %t.helper.a %t.helper1.o %t.helper2.o
+
+# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %s -filetype=obj -o %t.main.o
+
+# Simulate a setup, where two libraries provide the same function;
+# %t.lib.dll.a is a pure import library which provides a import symbol "libfunc".
+# %t.helper.a is a static library which contains "helper1" and "helper2".
+#
+# helper1 contains an undefined reference to libfunc. helper2 contains an
+# implementation of libfunc.
+#
+# First %t.main.o is processed and pushes a undefined symbol 'helper'.
+# Then %t.lib.dll.a is processed a pushes the lazy archive symbol 'libfunc' in the symbol table.
+# Then comes %t.helper.a and it pushes 'helper' and 'libfunc' as lazy symbols. Then 'helper' is
+# resolved and that pushes 'libfunc' as a undefined symbol. That pulls on %t.helper.a(%t.helper2.o)
+# which contains the 'libfunc' symbol, resolving it. This is illustrative of the MSVC library searching
+# behavior which starts with the current library object which requested the unresolved symbol.
+# RUN: lld-link -out:%t.main.exe -entry:main %t.main.o %t.lib.dll.a %t.helper.a
+# RUN: llvm-objdump --no-print-imm-hex -d %t.main.exe | FileCheck --check-prefix=LIB %s
+
+# In this case, the symbol in %t.helper.a(%t.helper2.o) is still considered first.
+# RUN: lld-link -out:%t.main.exe -entry:main %t.main.o %t.helper.a %t.lib.dll.a
+# RUN: llvm-objdump --no-print-imm-hex -d %t.main.exe | FileCheck --check-prefix=LIB %s
+
+# In this test we're defining libfunc in a third library that comes after all the others. The symbol should be pulled
+# now from that third library.
+# RUN: llvm-ar rcs %t.helper1.a %t.helper1.o
+# RUN: llvm-ar rcs %t.helper2.a %t.helper2.o
+# RUN: lld-link -out:%t.main.exe -entry:main %t.main.o %t.lib.dll.a %t.helper1.a %t.helper2.a
+# RUN: llvm-objdump --no-print-imm-hex -d %t.main.exe | FileCheck --check-prefix=LIB %s
+
+# LIB: 140001000 <.text>:
+# LIB: 140001000: e8 03 00 00 00                   callq   0x140001008 <.text+0x8>
+# LIB: 140001008: e8 03 00 00 00                   callq   0x140001010 <.text+0x10>
+# LIB: 140001010: 31 c0                            xorl    %eax, %eax
+
+# In this last test, we should pick up the import symbol from %t.lib.dll.a since it isn't defined anywhere else.
+# RUN: lld-link -out:%t.main.exe -entry:main %t.main.o %t.lib.dll.a %t.helper1.a
+# RUN: llvm-objdump --no-print-imm-hex -d %t.main.exe | FileCheck --check-prefix=LIB-IMP %s
+
+# LIB-IMP: 140001000 <.text>:
+# LIB-IMP: 140001010: ff 25 22 10 00 00            jmpq    *4130(%rip)
+
+    .globl main
+    .text
+main:
+    call helper
+    ret
diff --git a/llvm/include/llvm/Support/Allocator.h b/llvm/include/llvm/Support/Allocator.h
index c1e5c6d2853bd5..8c061b72d65f72 100644
--- a/llvm/include/llvm/Support/Allocator.h
+++ b/llvm/include/llvm/Support/Allocator.h
@@ -278,6 +278,32 @@ class BumpPtrAllocatorImpl
     return Out / alignof(T);
   }
 
+  /// Gets an already allocated object from an index that was previously
+  /// retrieved with `identifyKnownAlignedObject`.
+  template <typename T> T *fromAlignedIndex(int64_t Index) {
+    Index *= alignof(T);
+
+    int64_t InSlabIdx = 0;
+    for (size_t Idx = 0, E = Slabs.size(); Idx < E; Idx++) {
+      char *S = static_cast<char *>(Slabs[Idx]);
+      if (Index >= InSlabIdx &&
+          Index < InSlabIdx + static_cast<int64_t>(computeSlabSize(Idx)))
+        return reinterpret_cast<T *>(S + (Index - InSlabIdx));
+      InSlabIdx += static_cast<int64_t>(computeSlabSize(Idx));
+    }
+
+    // Use negative index to denote custom sized slabs.
+    int64_t InCustomSizedSlabIdx = -1;
+    for (size_t Idx = 0, E = CustomSizedSlabs.size(); Idx < E; Idx++) {
+      char *S = static_cast<char *>(CustomSizedSlabs[Idx].first);
+      int64_t Size = static_cast<int64_t>(CustomSizedSlabs[Idx].second);
+      if (Index <= InCustomSizedSlabIdx && Index > InCustomSizedSlabIdx - Size)
+        return reinterpret_cast<T *>(S - (Index - InCustomSizedSlabIdx));
+      InCustomSizedSlabIdx -= static_cast<int64_t>(Size);
+    }
+    return nullptr;
+  }
+
   size_t getTotalMemory() const {
     size_t TotalMemory = 0;
     for (auto I = Slabs.begin(), E = Slabs.end(); I != E; ++I)
@@ -380,9 +406,9 @@ typedef BumpPtrAllocatorImpl<> BumpPtrAllocator;
 /// This allows calling the destructor in DestroyAll() and when the allocator is
 /// destroyed.
 template <typename T> class SpecificBumpPtrAllocator {
+public:
   BumpPtrAllocator Allocator;
 
-public:
   SpecificBumpPtrAllocator() {
     // Because SpecificBumpPtrAllocator walks the memory to call destructors,
     // it can't have red zones between allocations.

>From b1149e14bed66742286093dcb68399029980cdaf Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Thu, 14 Mar 2024 14:29:25 -0400
Subject: [PATCH 02/12] Revert unneeded changes

---
 lld/COFF/SymbolTable.cpp | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index f570e8c211f43d..1b0e073ac00526 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -677,15 +677,15 @@ void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) {
   f->addMember(sym);
 }
 
-void SymbolTable::addLazyObject(InputFile *f, StringRef name) {
+void SymbolTable::addLazyObject(InputFile *f, StringRef n) {
   assert(f->lazy);
-  auto [s, wasInserted] = insert(name, f);
+  auto [s, wasInserted] = insert(n, f);
   if (wasInserted) {
-    replaceSymbol<LazyObject>(s, f, name);
+    replaceSymbol<LazyObject>(s, f, n);
     return;
   }
-  if (auto *n = lazyNode(s)) {
-    chainLazy<LazyObject>(n, f, name);
+  if (auto *node = lazyNode(s)) {
+    chainLazy<LazyObject>(node, f, n);
     return;
   }
   auto *u = dyn_cast<Undefined>(s);

>From 6c2a7770dccde79e0b0b61b004cec4447d24848f Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Thu, 14 Mar 2024 14:29:48 -0400
Subject: [PATCH 03/12] Test cmd-line libraries

---
 lld/test/COFF/lib-searching-behavior.s | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/lld/test/COFF/lib-searching-behavior.s b/lld/test/COFF/lib-searching-behavior.s
index eb4ba55c397534..4ba786f015f449 100644
--- a/lld/test/COFF/lib-searching-behavior.s
+++ b/lld/test/COFF/lib-searching-behavior.s
@@ -53,13 +53,17 @@
 # LIB: 140001008: e8 03 00 00 00                   callq   0x140001010 <.text+0x10>
 # LIB: 140001010: 31 c0                            xorl    %eax, %eax
 
-# In this last test, we should pick up the import symbol from %t.lib.dll.a since it isn't defined anywhere else.
+# Here, we should pick up the import symbol from %t.lib.dll.a since it isn't defined anywhere else.
 # RUN: lld-link -out:%t.main.exe -entry:main %t.main.o %t.lib.dll.a %t.helper1.a
 # RUN: llvm-objdump --no-print-imm-hex -d %t.main.exe | FileCheck --check-prefix=LIB-IMP %s
 
 # LIB-IMP: 140001000 <.text>:
 # LIB-IMP: 140001010: ff 25 22 10 00 00            jmpq    *4130(%rip)
 
+# Test cmd-line archives
+# RUN: lld-link -out:%t.main.exe -entry:main %t.main.o %t.lib.dll.a -start-lib %t.helper1.o %t.helper2.o -end-lib
+# RUN: llvm-objdump --no-print-imm-hex -d %t.main.exe | FileCheck --check-prefix=LIB %s
+
     .globl main
     .text
 main:

>From ad59e483aae69af21cdcecb81a55630361f8e93a Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Fri, 15 Mar 2024 09:32:42 -0400
Subject: [PATCH 04/12] LLD release notes

---
 lld/docs/ReleaseNotes.rst | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst
index 97ed0604891005..4b83d078e21e80 100644
--- a/lld/docs/ReleaseNotes.rst
+++ b/lld/docs/ReleaseNotes.rst
@@ -36,6 +36,10 @@ Breaking changes
 COFF Improvements
 -----------------
 
+* Symbols pulled from archives/LIBs are now resolved in the same way (archive
+  order) as MSVC link.exe. For more information see:
+  https://learn.microsoft.com/en-us/cpp/build/reference/link-input-files?view=msvc-170
+
 MinGW Improvements
 ------------------
 

>From b115c60988183be6a15aaa2a9ad969a0072cb9cb Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Fri, 15 Mar 2024 09:48:33 -0400
Subject: [PATCH 05/12] Add another egde case where symbols could be
 pulled/resolved from two archives

---
 lld/COFF/SymbolTable.cpp               | 11 ++++++++++-
 lld/test/COFF/lib-searching-behavior.s | 20 ++++++++++++++++++++
 2 files changed, 30 insertions(+), 1 deletion(-)

diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index 1b0e073ac00526..7c26e91ec42c25 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -95,6 +95,7 @@ static void errorOrWarn(const Twine &s, bool forceUnresolved) {
 
 // Causes the file associated with a lazy symbol to be linked in.
 static void forceLazy(Symbol *s) {
+  assert(!s->pendingArchiveLoad);
   s->pendingArchiveLoad = true;
   switch (s->kind()) {
   case Symbol::Kind::LazyArchiveKind: {
@@ -623,14 +624,21 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
     return s;
   }
   if (s->isLazy()) {
+    if (s->pendingArchiveLoad)
+      return s;
     if (ArchiveFile *parent = lazyParent(f)) {
+      // We're placing a undefined symbol from an archive OBJ. The rules are
+      // different than regular OBJs on the command-line.
       Symbol *selected = searchArchiveSymbol(s, parent);
       forceLazy(selected);
       // Now that we have selected a symbol, we don't need the linked list of
       // `LazyArchive`s anymore. Collapse to the selected symbol.
-      memcpy(s, selected, sizeof(SymbolUnion));
+      if (s != selected)
+        memcpy(s, selected, sizeof(SymbolUnion));
+      *lazyNode(s) = LazyIntrusiveNode();
       return s;
     }
+    // We're placing a undefined symbol from a command-line OBJ.
     forceLazy(s);
   }
   return s;
@@ -667,6 +675,7 @@ void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) {
     return;
   }
   if (auto *n = lazyNode(s)) {
+    assert(!s->pendingArchiveLoad);
     chainLazy<LazyArchive>(n, f, sym);
     return;
   }
diff --git a/lld/test/COFF/lib-searching-behavior.s b/lld/test/COFF/lib-searching-behavior.s
index 4ba786f015f449..95bc580b3da4a9 100644
--- a/lld/test/COFF/lib-searching-behavior.s
+++ b/lld/test/COFF/lib-searching-behavior.s
@@ -37,10 +37,12 @@
 # RUN: lld-link -out:%t.main.exe -entry:main %t.main.o %t.lib.dll.a %t.helper.a
 # RUN: llvm-objdump --no-print-imm-hex -d %t.main.exe | FileCheck --check-prefix=LIB %s
 
+
 # In this case, the symbol in %t.helper.a(%t.helper2.o) is still considered first.
 # RUN: lld-link -out:%t.main.exe -entry:main %t.main.o %t.helper.a %t.lib.dll.a
 # RUN: llvm-objdump --no-print-imm-hex -d %t.main.exe | FileCheck --check-prefix=LIB %s
 
+
 # In this test we're defining libfunc in a third library that comes after all the others. The symbol should be pulled
 # now from that third library.
 # RUN: llvm-ar rcs %t.helper1.a %t.helper1.o
@@ -53,6 +55,7 @@
 # LIB: 140001008: e8 03 00 00 00                   callq   0x140001010 <.text+0x10>
 # LIB: 140001010: 31 c0                            xorl    %eax, %eax
 
+
 # Here, we should pick up the import symbol from %t.lib.dll.a since it isn't defined anywhere else.
 # RUN: lld-link -out:%t.main.exe -entry:main %t.main.o %t.lib.dll.a %t.helper1.a
 # RUN: llvm-objdump --no-print-imm-hex -d %t.main.exe | FileCheck --check-prefix=LIB-IMP %s
@@ -60,10 +63,27 @@
 # LIB-IMP: 140001000 <.text>:
 # LIB-IMP: 140001010: ff 25 22 10 00 00            jmpq    *4130(%rip)
 
+
 # Test cmd-line archives
 # RUN: lld-link -out:%t.main.exe -entry:main %t.main.o %t.lib.dll.a -start-lib %t.helper1.o %t.helper2.o -end-lib
 # RUN: llvm-objdump --no-print-imm-hex -d %t.main.exe | FileCheck --check-prefix=LIB %s
 
+
+# Test pulling two different OBJ from two archives, which themselves both define the same symbol 'libfunc'.
+# Ensure that we resolve the symbol only once.
+
+# RUN: echo -e ".globl test\n.text\ntest:\ncall libfunc\nret" > %t.test1.s
+# RUN: echo -e ".intel_syntax noprefix\n.globl libfunc\n.text\nlibfunc:\nmov eax, 2\nret" > %t.test2.s
+# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %t.test1.s -filetype=obj -o %t.test1.o
+# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %t.test2.s -filetype=obj -o %t.test2.o
+# RUN: llvm-ar rcs %t.test.a %t.test1.o %t.test2.o
+
+# RUN: echo -e ".globl main\n.text\nmain:\ncall test\ncall helper\nret" > %t.main2.s
+# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %s -filetype=obj -o %t.main2.o
+
+# RUN: lld-link -out:%t.main.exe -entry:main %t.main2.o %t.helper.a %t.test.a 2>&1 | FileCheck --allow-empty --check-prefix=LIB-TWO %s
+# LIB-TWO-NOT: duplicate symbol:
+
     .globl main
     .text
 main:

>From 9d910c7cb83cbf0cc861b06847e8f38f04aa7e57 Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Fri, 15 Mar 2024 09:54:19 -0400
Subject: [PATCH 06/12] Missing assert for cmd-line archives

---
 lld/COFF/SymbolTable.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index 7c26e91ec42c25..414040dd67c0e2 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -694,6 +694,7 @@ void SymbolTable::addLazyObject(InputFile *f, StringRef n) {
     return;
   }
   if (auto *node = lazyNode(s)) {
+    assert(!s->pendingArchiveLoad);
     chainLazy<LazyObject>(node, f, n);
     return;
   }

>From 865dc264ca00b5c2b1decf632d53ca5a7c74c83d Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Mon, 18 Mar 2024 19:02:36 -0400
Subject: [PATCH 07/12] Support the new behavior with /DEFAULTLIB on the
 cmd-line or in directive section

---
 lld/COFF/Driver.cpp      | 55 ++++++++++++++++++++++++++++++----------
 lld/COFF/Driver.h        | 14 +++++++---
 lld/COFF/InputFiles.cpp  | 36 +++++++++++++++++++++++++-
 lld/COFF/SymbolTable.cpp | 30 ++--------------------
 4 files changed, 89 insertions(+), 46 deletions(-)

diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 38e0392a876307..071c9d1bdaf590 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -43,6 +43,7 @@
 #include "llvm/Support/Parallel.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Process.h"
+#include "llvm/Support/SaveAndRestore.h"
 #include "llvm/Support/TarWriter.h"
 #include "llvm/Support/TargetSelect.h"
 #include "llvm/Support/VirtualFileSystem.h"
@@ -338,7 +339,7 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
     if (!mbOrErr)
       reportBufferError(mbOrErr.takeError(), check(c.getFullName()));
     MemoryBufferRef mb = mbOrErr.get();
-    enqueueTask([=]() {
+    enqueueSecondaryTask([=]() {
       llvm::TimeTraceScope timeScope("Archive: ", mb.getBufferIdentifier());
       ctx.driver.addArchiveBuffer(mb, toCOFFString(ctx, sym), parentName,
                                   offsetInArchive, parent);
@@ -352,7 +353,7 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
                 toCOFFString(ctx, sym));
   auto future =
       std::make_shared<std::future<MBErrPair>>(createFutureForFile(childName));
-  enqueueTask([=]() {
+  enqueueSecondaryTask([=]() {
     auto mbOrErr = future->get();
     if (mbOrErr.second)
       reportBufferError(errorCodeToError(mbOrErr.second), childName);
@@ -367,7 +368,7 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
 }
 
 void LinkerDriver::enqueueLazyFile(InputFile *file) {
-  enqueueTask([=]() {
+  enqueueSecondaryTask([=]() {
     // Once it has been enqued, it cannot be lazy anymore.
     file->lazy = false;
     ctx.symtab.addFile(file);
@@ -379,6 +380,24 @@ bool LinkerDriver::isDecorated(StringRef sym) {
          (!ctx.config.mingw && sym.contains('@'));
 }
 
+static LLVM_THREAD_LOCAL bool executingFirstQueue;
+
+static bool executeQueue(std::list<std::function<void()>> &queue) {
+  bool didWork = !queue.empty();
+  while (!queue.empty()) {
+    queue.front()();
+    queue.pop_front();
+  }
+  return didWork;
+}
+
+static bool executeFirstQueue(std::list<std::function<void()>> &queue) {
+  if (executingFirstQueue)
+    return false;
+  SaveAndRestore s(executingFirstQueue, true);
+  return executeQueue(queue);
+}
+
 // Parses .drectve section contents and returns a list of files
 // specified by /defaultlib.
 void LinkerDriver::parseDirectives(InputFile *file) {
@@ -489,6 +508,11 @@ void LinkerDriver::parseDirectives(InputFile *file) {
             toString(file) + ")");
     }
   }
+
+  // If we are running off the low-priority task list, execute and drain the
+  // high priority task list before going any further. This is to ensure symbols
+  // provided by /DEFAULTLIB archives are linked property in the symbol table.
+  executeFirstQueue(firstTaskQueue);
 }
 
 // Find file from search paths. You can omit ".obj", this function takes
@@ -1071,18 +1095,19 @@ void LinkerDriver::parseModuleDefs(StringRef path) {
 }
 
 void LinkerDriver::enqueueTask(std::function<void()> task) {
-  taskQueue.push_back(std::move(task));
+  firstTaskQueue.push_back(std::move(task));
+}
+
+void LinkerDriver::enqueueSecondaryTask(std::function<void()> task) {
+  secondaryTaskQueue.push_back(std::move(task));
 }
 
 bool LinkerDriver::run() {
   llvm::TimeTraceScope timeScope("Read input files");
   ScopedTimer t(ctx.inputFileTimer);
 
-  bool didWork = !taskQueue.empty();
-  while (!taskQueue.empty()) {
-    taskQueue.front()();
-    taskQueue.pop_front();
-  }
+  bool didWork = executeFirstQueue(firstTaskQueue);
+  didWork |= executeQueue(secondaryTaskQueue);
   return didWork;
 }
 
@@ -2165,8 +2190,10 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     }
   }
 
-  // Read all input files given via the command line.
-  run();
+  // Read all input files given via the command line. Do not process the
+  // dependent OBJs pulled from archives just yet, since we need to push the
+  // default libs first.
+  executeFirstQueue(firstTaskQueue);
   if (errorCount())
     return;
 
@@ -2470,9 +2497,11 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
 
       if (args.hasArg(OPT_include_optional)) {
         // Handle /includeoptional
-        for (auto *arg : args.filtered(OPT_include_optional))
-          if (isa_and_nonnull<LazyArchive>(ctx.symtab.find(arg->getValue())))
+        for (auto *arg : args.filtered(OPT_include_optional)) {
+          Symbol *sym = ctx.symtab.find(arg->getValue());
+          if (sym && (isa<LazyArchive>(sym) || isa<LazyObject>(sym)))
             addUndefined(arg->getValue());
+        }
       }
     } while (run());
   }
diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h
index da3c41e1bca734..c38e1cffe77f87 100644
--- a/lld/COFF/Driver.h
+++ b/lld/COFF/Driver.h
@@ -95,9 +95,7 @@ class LinkerDriver {
                             StringRef parentName,
                             ArchiveFile *parent = nullptr);
 
-  void enqueuePDB(StringRef Path) {
-    enqueuePath(Path, false, false, /*parent=*/std::nullopt);
-  }
+  void enqueuePDB(StringRef Path) { enqueuePath(Path, false, false); }
 
   MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb);
 
@@ -197,9 +195,17 @@ class LinkerDriver {
                         ArchiveFile *parent = nullptr);
 
   void enqueueTask(std::function<void()> task);
+  void enqueueSecondaryTask(std::function<void()> task);
   bool run();
 
-  std::list<std::function<void()>> taskQueue;
+  // The first queue contains all direct command-line inputs, all /defaultlib
+  // LIBs, provided on the command-line or in a directives section. The second
+  // queue is meant for lower-priority dependent OBJs pulled by a symbol from an
+  // archive. If there are items in both queues, the first one must be fully
+  // executed first before the second queue. This is important to ensure we pull
+  // on archives symbols in the order specified by the MSVC spec.
+  std::list<std::function<void()>> firstTaskQueue;
+  std::list<std::function<void()>> secondaryTaskQueue;
   std::vector<StringRef> filePaths;
   std::vector<MemoryBufferRef> resources;
 
diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp
index 42cdd1cf3b6c2c..f7639729d86ca9 100644
--- a/lld/COFF/InputFiles.cpp
+++ b/lld/COFF/InputFiles.cpp
@@ -28,6 +28,7 @@
 #include "llvm/LTO/LTO.h"
 #include "llvm/Object/Binary.h"
 #include "llvm/Object/COFF.h"
+#include "llvm/Object/WindowsMachineFlag.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/Endian.h"
 #include "llvm/Support/Error.h"
@@ -93,6 +94,33 @@ static bool ignoredSymbolName(StringRef name) {
   return name == "@feat.00" || name == "@comp.id";
 }
 
+static bool compatibleMachineType(COFFLinkerContext &ctx, MachineTypes mt) {
+  if (mt == IMAGE_FILE_MACHINE_UNKNOWN)
+    return true;
+  switch (ctx.config.machine) {
+  case ARM64:
+    return mt == ARM64 || mt == ARM64X;
+  case ARM64EC:
+    return COFF::isArm64EC(mt) || mt == AMD64;
+  case ARM64X:
+    return COFF::isAnyArm64(mt) || mt == AMD64;
+  default:
+    return ctx.config.machine == mt;
+  }
+}
+
+static void setMachine(InputFile *file, COFFLinkerContext &ctx) {
+  MachineTypes mt = file->getMachineType();
+  if (ctx.config.machine == IMAGE_FILE_MACHINE_UNKNOWN) {
+    ctx.config.machine = mt;
+    ctx.driver.addWinSysRootLibSearchPaths();
+  } else if (!compatibleMachineType(ctx, mt)) {
+    error(toString(file) + ": machine type " + machineToStr(mt) +
+          " conflicts with " + machineToStr(ctx.config.machine));
+    return;
+  }
+}
+
 ArchiveFile::ArchiveFile(COFFLinkerContext &ctx, MemoryBufferRef m)
     : InputFile(ctx, ArchiveKind, m, /*lazy=*/true) {
   static unsigned Order = 0;
@@ -165,6 +193,7 @@ void ObjFile::parse() {
   } else {
     fatal(toString(this) + " is not a COFF file");
   }
+  setMachine(this, ctx);
 
   // Read section and symbol tables.
   initializeChunks();
@@ -216,6 +245,7 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
     ArrayRef<uint8_t> data;
     cantFail(coffObj->getSectionContents(sec, data));
     directives = StringRef((const char *)data.data(), data.size());
+    ctx.driver.parseDirectives(this);
     return nullptr;
   }
 
@@ -1025,6 +1055,7 @@ BitcodeFile::BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef mb,
                                                utostr(offsetInArchive)));
 
   obj = check(lto::InputFile::create(mbref));
+  setMachine(this, ctx);
 }
 
 BitcodeFile::~BitcodeFile() = default;
@@ -1032,6 +1063,9 @@ BitcodeFile::~BitcodeFile() = default;
 void BitcodeFile::parse() {
   llvm::StringSaver &saver = lld::saver();
 
+  directives = saver.save(obj->getCOFFLinkerOpts());
+  ctx.driver.parseDirectives(this);
+
   std::vector<std::pair<Symbol *, bool>> comdat(obj->getComdatTable().size());
   for (size_t i = 0; i != obj->getComdatTable().size(); ++i)
     // FIXME: Check nodeduplicate
@@ -1089,7 +1123,6 @@ void BitcodeFile::parse() {
     if (objSym.isUsed())
       ctx.config.gcroot.push_back(sym);
   }
-  directives = saver.save(obj->getCOFFLinkerOpts());
 }
 
 void BitcodeFile::parseLazy() {
@@ -1143,6 +1176,7 @@ void DLLFile::parse() {
     error(toString(this) + " is not a COFF file");
     return;
   }
+  setMachine(this, ctx);
 
   if (!coffObj->getPE32Header() && !coffObj->getPE32PlusHeader()) {
     error(toString(this) + " is not a PE-COFF executable");
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index 414040dd67c0e2..f45f68e22153f0 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -19,7 +19,6 @@
 #include "llvm/DebugInfo/DIContext.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/LTO/LTO.h"
-#include "llvm/Object/WindowsMachineFlag.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/raw_ostream.h"
 #include <utility>
@@ -34,21 +33,6 @@ StringRef ltrim1(StringRef s, const char *chars) {
   return s;
 }
 
-static bool compatibleMachineType(COFFLinkerContext &ctx, MachineTypes mt) {
-  if (mt == IMAGE_FILE_MACHINE_UNKNOWN)
-    return true;
-  switch (ctx.config.machine) {
-  case ARM64:
-    return mt == ARM64 || mt == ARM64X;
-  case ARM64EC:
-    return COFF::isArm64EC(mt) || mt == AMD64;
-  case ARM64X:
-    return COFF::isAnyArm64(mt) || mt == AMD64;
-  default:
-    return ctx.config.machine == mt;
-  }
-}
-
 void SymbolTable::addFile(InputFile *file) {
   log("Reading " + toString(file));
   if (file->lazy) {
@@ -72,18 +56,6 @@ void SymbolTable::addFile(InputFile *file) {
       ctx.importFileInstances.push_back(f);
     }
   }
-
-  MachineTypes mt = file->getMachineType();
-  if (ctx.config.machine == IMAGE_FILE_MACHINE_UNKNOWN) {
-    ctx.config.machine = mt;
-    ctx.driver.addWinSysRootLibSearchPaths();
-  } else if (!compatibleMachineType(ctx, mt)) {
-    error(toString(file) + ": machine type " + machineToStr(mt) +
-          " conflicts with " + machineToStr(ctx.config.machine));
-    return;
-  }
-
-  ctx.driver.parseDirectives(file);
 }
 
 static void errorOrWarn(const Twine &s, bool forceUnresolved) {
@@ -636,6 +608,8 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
       if (s != selected)
         memcpy(s, selected, sizeof(SymbolUnion));
       *lazyNode(s) = LazyIntrusiveNode();
+      if (!isa<BitcodeFile>(f))
+        s->isUsedInRegularObj = true;
       return s;
     }
     // We're placing a undefined symbol from a command-line OBJ.

>From 114870b6b8e676319c0abb745202e93cc9c547eb Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Tue, 19 Mar 2024 16:13:30 -0400
Subject: [PATCH 08/12] Fix chaining with pending load symbols

---
 lld/COFF/SymbolTable.cpp | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index f45f68e22153f0..673b730235443c 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -648,9 +648,9 @@ void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) {
     replaceSymbol<LazyArchive>(s, f, sym);
     return;
   }
-  if (auto *n = lazyNode(s)) {
-    assert(!s->pendingArchiveLoad);
-    chainLazy<LazyArchive>(n, f, sym);
+  if (auto *node = lazyNode(s)) {
+    if (!s->pendingArchiveLoad)
+      chainLazy<LazyArchive>(node, f, sym);
     return;
   }
   auto *u = dyn_cast<Undefined>(s);
@@ -668,8 +668,8 @@ void SymbolTable::addLazyObject(InputFile *f, StringRef n) {
     return;
   }
   if (auto *node = lazyNode(s)) {
-    assert(!s->pendingArchiveLoad);
-    chainLazy<LazyObject>(node, f, n);
+    if (!s->pendingArchiveLoad)
+      chainLazy<LazyObject>(node, f, n);
     return;
   }
   auto *u = dyn_cast<Undefined>(s);

>From 0ece3207b52b2462f6e86b50c8418c24a42a4b5c Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Mon, 8 Apr 2024 19:43:04 -0400
Subject: [PATCH 09/12] Add /DEFAULTLIB tests

---
 lld/test/COFF/lib-searching-behavior.s | 34 +++++++++++++++++++++++---
 1 file changed, 31 insertions(+), 3 deletions(-)

diff --git a/lld/test/COFF/lib-searching-behavior.s b/lld/test/COFF/lib-searching-behavior.s
index 95bc580b3da4a9..b9f49739016f69 100644
--- a/lld/test/COFF/lib-searching-behavior.s
+++ b/lld/test/COFF/lib-searching-behavior.s
@@ -1,6 +1,6 @@
 # REQUIRES: x86
 
-# This test ensures that we're following the MSVC symbol searching behvior described in:
+# This test ensures that we're following the MSVC symbol searching behavior described in:
 # https://learn.microsoft.com/en-us/cpp/build/reference/link-input-files?view=msvc-170
 # "Object files on the command line are processed in the order they appear on the command line.
 # Libraries are searched in command line order as well, with the following caveat: Symbols that
@@ -8,7 +8,7 @@
 # first, and then the following libraries from the command line and /DEFAULTLIB (Specify default
 # library) directives, and then to any libraries at the beginning of the command line."
 
-# RUN: echo -e ".intel_syntax noprefix\n.globl libfunc\n.text\nlibfunc:\nmov eax, 1\nret\n.section .drectve\n.ascii \"/EXPORT:libfunc\"" > %t.lib.s
+# RUN: echo -e ".intel_syntax noprefix\n.globl libfunc\n.text\nlibfunc:\nmov eax, 1\nret\n.section .drectve\n.ascii \" /EXPORT:libfunc\"" > %t.lib.s
 # RUN: llvm-mc -triple=x86_64-pc-windows-msvc %t.lib.s -filetype=obj -o %t.lib.o
 # RUN: lld-link -dll -out:%t.lib.dll -entry:libfunc %t.lib.o -implib:%t.lib.dll.a
 
@@ -79,11 +79,39 @@
 # RUN: llvm-ar rcs %t.test.a %t.test1.o %t.test2.o
 
 # RUN: echo -e ".globl main\n.text\nmain:\ncall test\ncall helper\nret" > %t.main2.s
-# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %s -filetype=obj -o %t.main2.o
+# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %t.main2.s -filetype=obj -o %t.main2.o
 
 # RUN: lld-link -out:%t.main.exe -entry:main %t.main2.o %t.helper.a %t.test.a 2>&1 | FileCheck --allow-empty --check-prefix=LIB-TWO %s
 # LIB-TWO-NOT: duplicate symbol:
 
+
+# Test pulling symbols from /DEFAULTLIB archives. These archives should come
+# after all the other archives passed explictly on the command-line.
+
+# RUN: echo -e ".intel_syntax noprefix\n.globl libfunc\n.text\nlibfunc:\nmov eax, 3\nret" > %t.deflib.s
+# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %t.deflib.s -filetype=obj -o %t.deflib.o
+# RUN: llvm-ar rcs %t.deflib.a %t.deflib.o
+
+# RUN: lld-link -out:%t.main.exe -entry:main %t.main2.o %t.helper1.a /DEFAULTLIB:%t.deflib.a %t.test.a 2>&1 | FileCheck --allow-empty --check-prefix=LIB-TWO %s
+
+# RUN: llvm-ar rcs %t.test1.a %t.test1.o
+# RUN: lld-link -out:%t.main.exe -entry:main %t.main2.o %t.test1.a /DEFAULTLIB:%t.deflib.a %t.helper.a 2>&1 | FileCheck --allow-empty --check-prefix=LIB-TWO %s
+
+
+# Test implicit /DEFAULTLIB from .drectve sections. These archives should come
+# after all the other archives passed explictly on the command-line, and are
+# added dynamically while possibly parsing an existing OBJ file.
+
+# RUN: echo -e -n ".intel_syntax noprefix\n.globl test\n.text\ntest:\ncall libfunc\nret\n.section .drectve\n.ascii \" /DEFAULTLIB:" > %t.lib.s
+# RUN: echo -n "%/t.deflib.a" >> %t.lib.s
+# RUN: echo -e "\"" >> %t.lib.s
+# RUN: llvm-mc -triple=x86_64-pc-windows-msvc %t.lib.s -filetype=obj -o %t.lib.o
+# RUN: llvm-ar rcs %t.lib.a %t.lib.o
+
+# RUN: lld-link -out:%t.main.exe -entry:main %t.main2.o %t.helper.a %t.lib.a 2>&1 | FileCheck --allow-empty --check-prefix=LIB-TWO %s
+# RUN: lld-link -out:%t.main.exe -entry:main %t.main2.o %t.lib.a %t.helper1.a 2>&1 | FileCheck --allow-empty --check-prefix=LIB-TWO %s
+
+
     .globl main
     .text
 main:

>From 4d6fcd842a4c6a7886971c4b193ed6d649032b45 Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Mon, 8 Apr 2024 19:43:31 -0400
Subject: [PATCH 10/12] Review fixes

---
 lld/COFF/SymbolTable.cpp           | 67 +++++++++++++++++-------------
 lld/test/COFF/duplicate-imp-func.s |  2 +-
 2 files changed, 40 insertions(+), 29 deletions(-)

diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index 673b730235443c..49e5e4a92f4c69 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -537,7 +537,7 @@ std::pair<Symbol *, bool> SymbolTable::insert(StringRef name, InputFile *file) {
   return result;
 }
 
-static LazyIntrusiveNode *lazyNode(Symbol *s) {
+static LazyIntrusiveNode *getLazyNode(Symbol *s) {
   if (auto *sym = dyn_cast<LazyArchive>(s))
     return &sym->node;
   if (auto *sym = dyn_cast<LazyObject>(s))
@@ -545,9 +545,7 @@ static LazyIntrusiveNode *lazyNode(Symbol *s) {
   return nullptr;
 }
 
-static ArchiveFile *lazyParent(InputFile *f) {
-  if (!f)
-    return nullptr;
+static ArchiveFile *getLazyParent(InputFile *f) {
   if (auto *obj = dyn_cast<ObjFile>(f))
     return obj->parent;
   if (auto *obj = dyn_cast<BitcodeFile>(f))
@@ -555,11 +553,11 @@ static ArchiveFile *lazyParent(InputFile *f) {
   return nullptr;
 }
 
-static ArchiveFile *lazyArchive(Symbol *s) {
+static ArchiveFile *getLazyArchive(Symbol *s) {
   if (auto *sym = dyn_cast<LazyArchive>(s))
     return sym->file;
   if (auto *sym = dyn_cast<LazyObject>(s))
-    return lazyParent(sym->file);
+    return getLazyParent(sym->file);
   return nullptr;
 }
 
@@ -573,19 +571,32 @@ static ArchiveFile *lazyArchive(Symbol *s) {
 // that library first, and then the following libraries from the command
 // line and /DEFAULTLIB (Specify default library) directives, and then
 // to any libraries at the beginning of the command line."
-static Symbol *searchArchiveSymbol(Symbol *s, ArchiveFile *pivot) {
+static Symbol *lookupLazy(Symbol *frontSym, ArchiveFile *requestingArchive) {
   auto &Alloc = getSpecificAllocSingleton<SymbolUnion>().Allocator;
-  Symbol *curr = s;
+  Symbol *currentSym = frontSym;
   for (;;) {
-    if (lazyArchive(curr)->CmdLineIndex >= pivot->CmdLineIndex)
-      return curr;
-    uint32_t next = lazyNode(curr)->next;
-    if (!next)
+    ArchiveFile *currentArchive = getLazyArchive(currentSym);
+    assert(currentArchive && "Unhandled lazy archive");
+    if (currentArchive->CmdLineIndex >= requestingArchive->CmdLineIndex)
+      return currentSym;
+    LazyIntrusiveNode *node = getLazyNode(currentSym);
+    assert(node && "Unhandled lazy node");
+    if (!node->next)
       break;
-    curr = reinterpret_cast<LazyArchive *>(
-        Alloc.fromAlignedIndex<SymbolUnion>(next));
+    currentSym = reinterpret_cast<LazyArchive *>(
+        Alloc.fromAlignedIndex<SymbolUnion>(node->next));
   }
-  return s;
+  return frontSym;
+}
+
+static void collapseLazy(Symbol *frontSym, Symbol *selected, InputFile *f) {
+  if (frontSym != selected)
+    memcpy(frontSym, selected, sizeof(SymbolUnion));
+  LazyIntrusiveNode *node = getLazyNode(frontSym);
+  assert(node && "Unhandled lazy node");
+  *node = LazyIntrusiveNode();
+  if (!isa<BitcodeFile>(f))
+    frontSym->isUsedInRegularObj = true;
 }
 
 Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
@@ -598,18 +609,16 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
   if (s->isLazy()) {
     if (s->pendingArchiveLoad)
       return s;
-    if (ArchiveFile *parent = lazyParent(f)) {
-      // We're placing a undefined symbol from an archive OBJ. The rules are
-      // different than regular OBJs on the command-line.
-      Symbol *selected = searchArchiveSymbol(s, parent);
+    if (ArchiveFile *parent = getLazyParent(f)) {
+      // Lookup the most suitable undefined symbol exposed by an archive OBJ.
+      // The rules are different than regular OBJs on the command-line (see
+      // above).
+      Symbol *selected = lookupLazy(s, parent);
       forceLazy(selected);
+
       // Now that we have selected a symbol, we don't need the linked list of
       // `LazyArchive`s anymore. Collapse to the selected symbol.
-      if (s != selected)
-        memcpy(s, selected, sizeof(SymbolUnion));
-      *lazyNode(s) = LazyIntrusiveNode();
-      if (!isa<BitcodeFile>(f))
-        s->isUsedInRegularObj = true;
+      collapseLazy(s, selected, f);
       return s;
     }
     // We're placing a undefined symbol from a command-line OBJ.
@@ -619,7 +628,7 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
 }
 
 // This creates a linked list of archives where a specific symbol was seen.
-// We later walk that list if a undefined symbol needs to be resolved from an
+// We later walk that list if an undefined symbol needs to be resolved from an
 // archive OBJ.
 template <typename T, typename... ArgT>
 static void chainLazy(LazyIntrusiveNode *front, ArgT &&...arg) {
@@ -636,7 +645,9 @@ static void chainLazy(LazyIntrusiveNode *front, ArgT &&...arg) {
   if (front->last) {
     Symbol *last = reinterpret_cast<Symbol *>(
         Alloc.fromAlignedIndex<SymbolUnion>(front->last));
-    lazyNode(last)->next = index;
+    LazyIntrusiveNode *node = getLazyNode(last);
+    assert(node && "Unhandled lazy node");
+    node->next = index;
   }
   front->last = index;
 }
@@ -648,7 +659,7 @@ void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) {
     replaceSymbol<LazyArchive>(s, f, sym);
     return;
   }
-  if (auto *node = lazyNode(s)) {
+  if (auto *node = getLazyNode(s)) {
     if (!s->pendingArchiveLoad)
       chainLazy<LazyArchive>(node, f, sym);
     return;
@@ -667,7 +678,7 @@ void SymbolTable::addLazyObject(InputFile *f, StringRef n) {
     replaceSymbol<LazyObject>(s, f, n);
     return;
   }
-  if (auto *node = lazyNode(s)) {
+  if (auto *node = getLazyNode(s)) {
     if (!s->pendingArchiveLoad)
       chainLazy<LazyObject>(node, f, n);
     return;
diff --git a/lld/test/COFF/duplicate-imp-func.s b/lld/test/COFF/duplicate-imp-func.s
index 631c714c951f77..bd173483a9cc38 100644
--- a/lld/test/COFF/duplicate-imp-func.s
+++ b/lld/test/COFF/duplicate-imp-func.s
@@ -28,7 +28,7 @@
 # Once the import library member from %t.lib.dll.a gets loaded, libfunc
 # and __imp_libfunc already are defined.
 
-# This test should now succeed since we're following the MSVC symbol searching behvior described in:
+# This test should now succeed since we're following the MSVC symbol searching behavior described in:
 # https://learn.microsoft.com/en-us/cpp/build/reference/link-input-files?view=msvc-170
 # In this case, the linker will select the libfunc symbol in %t.helper.a
 # RUN: lld-link -lldmingw -out:%t.main.exe -entry:main %t.main.o %t.lib.dll.a %t.helper.a

>From 87a90c3a1a03c4dad02874310425b5f2e83ad60d Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Mon, 8 Apr 2024 19:46:11 -0400
Subject: [PATCH 11/12] More review fixes

---
 lld/COFF/Driver.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 071c9d1bdaf590..da6ccbd3c86813 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -369,7 +369,7 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
 
 void LinkerDriver::enqueueLazyFile(InputFile *file) {
   enqueueSecondaryTask([=]() {
-    // Once it has been enqued, it cannot be lazy anymore.
+    // Once it has been enqueued, it cannot be lazy anymore.
     file->lazy = false;
     ctx.symtab.addFile(file);
   });

>From c2e7c7856894d48b4a7900cab936e8cc1ab2e26c Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Mon, 8 Apr 2024 19:52:01 -0400
Subject: [PATCH 12/12] clang-format

---
 lld/COFF/InputFiles.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h
index 7070f51fdf78ac..77cba8b0eac054 100644
--- a/lld/COFF/InputFiles.h
+++ b/lld/COFF/InputFiles.h
@@ -112,7 +112,7 @@ class ArchiveFile : public InputFile {
 public:
   explicit ArchiveFile(COFFLinkerContext &ctx, MemoryBufferRef m);
   static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; }
-  void parse() override{};
+  void parse() override {};
   void parseLazy();
 
   // Enqueues an archive member load for the given symbol. If we've already



More information about the llvm-commits mailing list