https://github.com/MaskRay updated 
https://github.com/llvm/llvm-project/pull/231407

>From 1556c94736f8744755ab28636ba30919673a545c Mon Sep 17 00:00:00 2001
From: Fangrui Song <[email protected]>
Date: Mon, 12 Oct 2026 21:43:33 -0700
Subject: [PATCH 1/4] [Option] Let an LLVMContext override library options

`OptionsStruct<..., perContext = true>` lets each LLVMContext hold its
own values of a library's options, so that two contexts in one process
can compile with different values. The static member `Global` becomes
private, and readers call `Ctx.getLibraryOptions().get<T>()`, which
returns the context's copy, (temporary fallback) or `Global` if it has
none. InstCombine is the first such library.

The new cl::parseCommandLineOptions sets per-context libraries' options
in the given cl::LibraryOptionsContext instead of Global. opt parses
into its LLVMContext; clang -cc1 parses -mllvm into its CompilerInstance
and replays the arguments into each code generation LLVMContext. An
embedder calls set({"-x=v"}), which dispatches through the cl::
registry.

https://discourse.llvm.org/t/rfc-declare-library-command-line-options-in-tablegen-one-struct-per-library/91877
LLM-aided
---
 .../include/clang/Frontend/CompilerInstance.h |   9 ++
 clang/lib/CIR/FrontendAction/CIRGenAction.cpp |   2 +
 clang/lib/CodeGen/CodeGenAction.cpp           |   2 +
 clang/lib/Frontend/CompilerInstance.cpp       |   5 +-
 .../test/CIR/CodeGen/mllvm-library-options.c  |  17 +++
 clang/test/CodeGen/mllvm-library-options.ll   |  21 ++++
 llvm/include/llvm/IR/LLVMContext.h            |   7 ++
 llvm/include/llvm/Option/LibraryOptions.h     |  36 +++---
 llvm/include/llvm/Option/LibraryOptions.td    |   4 +-
 llvm/include/llvm/Support/CommandLine.h       |  35 +++++-
 .../llvm/Support/LibraryOptionsContext.h      |  64 +++++++++++
 llvm/lib/IR/LLVMContext.cpp                   |   4 +
 llvm/lib/IR/LLVMContextImpl.h                 |   3 +
 llvm/lib/Option/LibraryOptions.cpp            |   4 +-
 llvm/lib/Support/CommandLine.cpp              | 103 ++++++++++++++----
 .../InstCombine/InstCombineCLOptions.td       |   2 +-
 .../InstCombine/InstructionCombining.cpp      |   4 +-
 llvm/tools/opt/optdriver.cpp                  |   8 +-
 llvm/unittests/IR/CMakeLists.txt              |   2 +
 .../IR/LibraryOptionsContextTest.cpp          |  69 ++++++++++++
 llvm/unittests/Option/LibraryOptionsTest.cpp  |  56 ++++++++--
 llvm/unittests/Support/CommandLineTest.cpp    |   4 +-
 .../TableGen/Basic/OptionParserEmitter.cpp    |  15 ++-
 .../gn/secondary/llvm/unittests/IR/BUILD.gn   |   2 +
 .../llvm/unittests/BUILD.bazel                |   1 +
 25 files changed, 417 insertions(+), 62 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/mllvm-library-options.c
 create mode 100644 clang/test/CodeGen/mllvm-library-options.ll
 create mode 100644 llvm/include/llvm/Support/LibraryOptionsContext.h
 create mode 100644 llvm/unittests/IR/LibraryOptionsContextTest.cpp

diff --git a/clang/include/clang/Frontend/CompilerInstance.h 
b/clang/include/clang/Frontend/CompilerInstance.h
index b87dd037480d6c..bceb85f6e923bf 100644
--- a/clang/include/clang/Frontend/CompilerInstance.h
+++ b/clang/include/clang/Frontend/CompilerInstance.h
@@ -26,6 +26,7 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/BuryPointer.h"
 #include "llvm/Support/FileSystem.h"
+#include "llvm/Support/LibraryOptionsContext.h"
 #include "llvm/Support/VirtualFileSystem.h"
 #include "llvm/Support/VirtualOutputBackend.h"
 #include <cassert>
@@ -147,6 +148,10 @@ class CompilerInstance : public ModuleLoader {
   /// The frontend timer.
   std::unique_ptr<llvm::Timer> FrontendTimer;
 
+  /// The -mllvm options of per-context libraries, which every LLVMContext that
+  /// compiles for this instance receives.
+  llvm::cl::LibraryOptionsContext LibraryOptions;
+
   /// The ASTReader, if one exists.
   IntrusiveRefCntPtr<ASTReader> TheASTReader;
 
@@ -269,6 +274,10 @@ class CompilerInstance : public ModuleLoader {
   /// This should be called after plugins are loaded and before ExecuteAction.
   void parseLLVMArgs();
 
+  const llvm::cl::LibraryOptionsContext &getLibraryOptions() const {
+    return LibraryOptions;
+  }
+
   /// @}
   /// @name Compiler Invocation and Options
   /// @{
diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp 
b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
index 71a7a17146af3d..a04aa51e163af3 100644
--- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
+++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
@@ -35,6 +35,7 @@
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Module.h"
 #include "llvm/Linker/Linker.h"
+#include "llvm/Support/LibraryOptionsContext.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/raw_ostream.h"
@@ -367,6 +368,7 @@ CIRGenAction::CIRGenAction(OutputType Act, 
mlir::MLIRContext *MLIRCtx)
 CIRGenAction::~CIRGenAction() { MLIRMod.release(); }
 
 bool CIRGenAction::BeginSourceFileAction(CompilerInstance &CI) {
+  cantFail(Ctx->getLibraryOptions().set(CI.getLibraryOptions().args()));
   if (clang::loadLinkModules(CI, *Ctx, LinkModules))
     return false;
   return ASTFrontendAction::BeginSourceFileAction(CI);
diff --git a/clang/lib/CodeGen/CodeGenAction.cpp 
b/clang/lib/CodeGen/CodeGenAction.cpp
index 1abd8b0455a19b..22925cc55f495f 100644
--- a/clang/lib/CodeGen/CodeGenAction.cpp
+++ b/clang/lib/CodeGen/CodeGenAction.cpp
@@ -42,6 +42,7 @@
 #include "llvm/LTO/LTOBackend.h"
 #include "llvm/Linker/Linker.h"
 #include "llvm/Pass.h"
+#include "llvm/Support/LibraryOptionsContext.h"
 #include "llvm/Support/ManagedStatic.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Mutex.h"
@@ -366,6 +367,7 @@ CodeGenerator *CodeGenAction::getCodeGenerator() const {
 }
 
 bool CodeGenAction::BeginSourceFileAction(CompilerInstance &CI) {
+  cantFail(VMContext->getLibraryOptions().set(CI.getLibraryOptions().args()));
   if (CI.getFrontendOpts().GenReducedBMI)
     CI.getLangOpts().setCompilingModule(LangOptions::CMK_ModuleInterface);
   return ASTFrontendAction::BeginSourceFileAction(CI);
diff --git a/clang/lib/Frontend/CompilerInstance.cpp 
b/clang/lib/Frontend/CompilerInstance.cpp
index b09c9a9f5a5bed..f122208d6b4c8b 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -1143,8 +1143,9 @@ void CompilerInstance::parseLLVMArgs() {
     for (unsigned i = 0; i != NumArgs; ++i)
       Args[i + 1] = getFrontendOpts().LLVMArgs[i].c_str();
     Args[NumArgs + 1] = nullptr;
-    llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get(), /*Overview=*/"",
-                                      /*Errs=*/nullptr,
+    llvm::cl::parseCommandLineOptions(LibraryOptions,
+                                      ArrayRef(Args.get(), NumArgs + 1),
+                                      /*Overview=*/"", /*Errs=*/nullptr,
                                       /*VFS=*/&getVirtualFileSystem());
   }
 }
diff --git a/clang/test/CIR/CodeGen/mllvm-library-options.c 
b/clang/test/CIR/CodeGen/mllvm-library-options.c
new file mode 100644
index 00000000000000..4f584b4058a81c
--- /dev/null
+++ b/clang/test/CIR/CodeGen/mllvm-library-options.c
@@ -0,0 +1,17 @@
+// -mllvm options of per-context libraries reach the LLVMContext that CIR is
+// lowered into.
+// RUN: %clang_cc1 -triple x86_64 -O1 -fclangir -emit-llvm %s -o - | FileCheck 
%s --check-prefixes=CHECK,SINK
+// RUN: %clang_cc1 -triple x86_64 -O1 -fclangir -emit-llvm -mllvm 
-instcombine-code-sinking=0 %s -o - | FileCheck %s --check-prefixes=CHECK,NOSINK
+
+void use(int);
+
+// CHECK-LABEL: define {{.*}} @f(
+// SINK-NOT:      sdiv
+// NOSINK:        sdiv
+// CHECK:         br i1
+// SINK:          sdiv
+void f(int c, int a, int b) {
+  int d = a / b;
+  if (c)
+    use(d);
+}
diff --git a/clang/test/CodeGen/mllvm-library-options.ll 
b/clang/test/CodeGen/mllvm-library-options.ll
new file mode 100644
index 00000000000000..a4938f811537cf
--- /dev/null
+++ b/clang/test/CodeGen/mllvm-library-options.ll
@@ -0,0 +1,21 @@
+;; -mllvm options of per-context libraries reach the LLVMContext that compiles.
+; RUN: %clang_cc1 -O1 -emit-llvm %s -o - | FileCheck %s 
--check-prefixes=CHECK,SINK
+; RUN: %clang_cc1 -O1 -emit-llvm -mllvm -instcombine-code-sinking=0 %s -o - | 
FileCheck %s --check-prefixes=CHECK,NOSINK
+
+; CHECK-LABEL: define {{.*}} @f(
+; NOSINK-NEXT: entry:
+; NOSINK-NEXT:  = sdiv
+; CHECK:       {{^}}t:
+; SINK-NEXT:     = sdiv
+define void @f(i1 %c, i32 %a, i32 %b) {
+entry:
+  %d = sdiv i32 %a, %b
+  br i1 %c, label %t, label %e
+t:
+  call void @use(i32 %d)
+  ret void
+e:
+  ret void
+}
+
+declare void @use(i32)
diff --git a/llvm/include/llvm/IR/LLVMContext.h 
b/llvm/include/llvm/IR/LLVMContext.h
index ced9b9227538e4..be0d27bacf3e4a 100644
--- a/llvm/include/llvm/IR/LLVMContext.h
+++ b/llvm/include/llvm/IR/LLVMContext.h
@@ -32,6 +32,9 @@ class Instruction;
 class LLVMContextImpl;
 class Module;
 class OptPassGate;
+namespace cl {
+class LibraryOptionsContext;
+} // namespace cl
 template <typename T> class SmallVectorImpl;
 template <typename T> class StringMapEntry;
 class StringRef;
@@ -329,6 +332,10 @@ class LLVMContext {
   /// LLVMContext is used by compilation.
   LLVM_ABI void setOptPassGate(OptPassGate &);
 
+  /// The library options this context reads in place of their process-wide
+  /// values.
+  LLVM_ABI cl::LibraryOptionsContext &getLibraryOptions() const;
+
   /// Get or set the current "default" target CPU (target-cpu function
   /// attribute). The intent is that compiler frontends will set this to a 
value
   /// that reflects the attribute that a function would get "by default" 
without
diff --git a/llvm/include/llvm/Option/LibraryOptions.h 
b/llvm/include/llvm/Option/LibraryOptions.h
index 8b5c34525fa7c6..19df88f77e586a 100644
--- a/llvm/include/llvm/Option/LibraryOptions.h
+++ b/llvm/include/llvm/Option/LibraryOptions.h
@@ -27,6 +27,7 @@
 #include "llvm/Support/Compiler.h"
 #include <optional>
 #include <type_traits>
+#include <utility>
 
 namespace llvm {
 namespace opt {
@@ -118,23 +119,36 @@ class LLVM_ABI LibraryOptTable : public OptTable {
   ~LibraryOptTable() override;
 };
 
-/// Connects an options struct to cl::ParseCommandLineOptions.
+/// Connects options struct T to cl::ParseCommandLineOptions and
+/// cl::LibraryOptionsContext.
 class LLVM_ABI LibraryOptionsParser final : public cl::LibraryOptions {
 public:
-  using ApplyFn = bool (*)(const Arg &, BumpPtrAllocator &);
-  using TableFn = const OptTable &(*)();
-  LibraryOptionsParser(TableFn Table, ApplyFn Apply, void (*Reset)())
-      : Table(Table), Apply(Apply), Reset(Reset) {}
+  template <typename T>
+  explicit LibraryOptionsParser(std::in_place_type_t<T>)
+      : LibraryOptions(T::PerContext), Table(T::optTable), Global(&T::Global),
+        Apply([](void *Opts, const Arg &A, BumpPtrAllocator &Alloc) {
+          return static_cast<T *>(Opts)->apply(A, Alloc);
+        }),
+        Copy([](BumpPtrAllocator &Alloc) -> void * {
+          return new (Alloc) T(T::Global);
+        }),
+        Reset([] { T::Global = T(); }) {}
 
   void forEachOption(
       function_ref<void(StringRef, StringRef, StringRef)> Fn) const override;
-  Error parse(ArrayRef<const char *> Args, unsigned &Consumed,
+  Error parse(void *Opts, ArrayRef<const char *> Args, unsigned &Consumed,
               BumpPtrAllocator &Alloc) override;
+  void *global() override { return Global; }
+  void *copyGlobal(BumpPtrAllocator &Alloc) const override {
+    return Copy(Alloc);
+  }
   void reset() override { Reset(); }
 
 private:
-  TableFn Table;
-  ApplyFn Apply;
+  const OptTable &(*Table)();
+  void *Global;
+  bool (*Apply)(void *Opts, const Arg &A, BumpPtrAllocator &Alloc);
+  void *(*Copy)(BumpPtrAllocator &Alloc);
   void (*Reset)();
 };
 
@@ -144,11 +158,7 @@ class LLVM_ABI LibraryOptionsParser final : public 
cl::LibraryOptions {
 template <typename T> class RegisterLibraryOptions {
   static_assert(std::is_trivially_destructible_v<T>,
                 "an options struct must not need an exit-time destructor");
-  LibraryOptionsParser Parser{T::optTable,
-                              [](const Arg &A, BumpPtrAllocator &Alloc) {
-                                return T::Global.apply(A, Alloc);
-                              },
-                              [] { T::Global = T(); }};
+  LibraryOptionsParser Parser{std::in_place_type<T>};
 
 public:
   RegisterLibraryOptions() { cl::addLibraryOptions(Parser); }
diff --git a/llvm/include/llvm/Option/LibraryOptions.td 
b/llvm/include/llvm/Option/LibraryOptions.td
index d901f8027f7028..ab027c056831ea 100644
--- a/llvm/include/llvm/Option/LibraryOptions.td
+++ b/llvm/include/llvm/Option/LibraryOptions.td
@@ -17,9 +17,11 @@
 include "llvm/Option/OptParser.td"
 
 // A row whose spelling starts with prefix names its member without it.
-class OptionsStruct<string namespace = "llvm", string prefix = ""> {
+// perContext makes Global private; read through 
cl::LibraryOptionsContext::get.
+class OptionsStruct<string namespace = "llvm", string prefix = "", bit 
perContext = false> {
   string Namespace = namespace;
   string MemberPrefix = prefix;
+  bit PerContext = perContext;
 }
 
 // The builds an option exists in, as a preprocessor expression.
diff --git a/llvm/include/llvm/Support/CommandLine.h 
b/llvm/include/llvm/Support/CommandLine.h
index bc7059244461e3..1b3e3745ce7246 100644
--- a/llvm/include/llvm/Support/CommandLine.h
+++ b/llvm/include/llvm/Support/CommandLine.h
@@ -50,6 +50,8 @@ class Error;
 /// It is intentionally a short name to make qualified usage concise.
 namespace cl {
 
+class LibraryOptionsContext;
+
 
//===----------------------------------------------------------------------===//
 // Command line option processing entry point.
 //
@@ -70,6 +72,15 @@ LLVM_ABI bool ParseCommandLineOptions(int argc, const char 
*const *argv,
                                       vfs::FileSystem *VFS = nullptr,
                                       const char *EnvVar = nullptr);
 
+// Like ParseCommandLineOptions, but sets the library registered options
+// as per-context in LibraryOptions rather than in their global().
+LLVM_ABI bool parseCommandLineOptions(LibraryOptionsContext &LibraryOptions,
+                                      ArrayRef<const char *> Args,
+                                      StringRef Overview = "",
+                                      raw_ostream *Errs = nullptr,
+                                      vfs::FileSystem *VFS = nullptr,
+                                      const char *EnvVar = nullptr);
+
 // Function pointer type for printing version information.
 using VersionPrinterTy = std::function<void(raw_ostream &)>;
 
@@ -2176,18 +2187,32 @@ class LLVM_ABI LibraryOptions {
           Fn) const = 0;
 
   /// Parses the option spelled by Args[0], which may take Args[1] as its
-  /// value, and sets \p Consumed to the number of arguments it spans. The
-  /// strings in \p Args and the memory from \p Alloc remain valid until
-  /// reset().
-  virtual Error parse(ArrayRef<const char *> Args, unsigned &Consumed,
-                      BumpPtrAllocator &Alloc) = 0;
+  /// value, into \p Opts, which is global() or a copy of it, and sets
+  /// \p Consumed to the number of arguments it spans. The strings in \p Args
+  /// and the memory from \p Alloc remain valid as long as \p Opts is used.
+  virtual Error parse(void *Opts, ArrayRef<const char *> Args,
+                      unsigned &Consumed, BumpPtrAllocator &Alloc) = 0;
+
+  /// Returns the values that ParseCommandLineOptions sets.
+  virtual void *global() = 0;
+
+  /// Returns a copy of global() allocated from \p Alloc.
+  virtual void *copyGlobal(BumpPtrAllocator &Alloc) const = 0;
 
   /// Restores the default values.
   virtual void reset() = 0;
 
+  /// Whether every reader of the options goes through a context's
+  /// LibraryOptionsContext, so that they need not be set in global().
+  bool isPerContext() const { return PerContext; }
+
 protected:
+  explicit LibraryOptions(bool PerContext = false) : PerContext(PerContext) {}
   // Registrations are static and never destroyed through this class.
   ~LibraryOptions() = default;
+
+private:
+  bool PerContext;
 };
 
 /// Makes ParseCommandLineOptions recognize \p L's options. A name that is also
diff --git a/llvm/include/llvm/Support/LibraryOptionsContext.h 
b/llvm/include/llvm/Support/LibraryOptionsContext.h
new file mode 100644
index 00000000000000..150a8c1d5a7719
--- /dev/null
+++ b/llvm/include/llvm/Support/LibraryOptionsContext.h
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_SUPPORT_LIBRARYOPTIONSCONTEXT_H
+#define LLVM_SUPPORT_LIBRARYOPTIONSCONTEXT_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/Compiler.h"
+
+namespace llvm {
+
+class Error;
+
+namespace cl {
+
+class LibraryOptions;
+
+// Values of library options that override their global() in one compilation
+// context, such as an LLVMContext.
+class LLVM_ABI LibraryOptionsContext {
+public:
+  // Sets the library options that Args spell, e.g. {"-x=v"}. A library's 
values
+  // start as a copy of its global(), and later calls add to them. Fails at the
+  // first argument that is not a library option or has an invalid value.
+  //
+  // Strings copied from global() die with ResetAllOptionOccurrences. The first
+  // call (or ParseCommandLineOptions) indexes the registry, which is not
+  // thread-safe.
+  Error set(ArrayRef<const char *> Args);
+
+  // Parses the option of L that Args[0] spells, as set() does.
+  Error parse(LibraryOptions &L, ArrayRef<const char *> Args,
+              unsigned &Consumed);
+
+  // The arguments set so far, for replaying into another context.
+  ArrayRef<const char *> args() const { return Args; }
+
+  // T's values, or T::Global if there are none.
+  template <typename T> const T &get() const {
+    if (const void *Opts = Values.lookup(&T::Global))
+      return *static_cast<const T *>(Opts);
+    // TODO: Return the defaults once cl::ParseCommandLineOptions callers have
+    // migrated to parseCommandLineOptions and no longer set T::Global.
+    return T::Global;
+  }
+
+private:
+  DenseMap<const void *, void *> Values;
+  SmallVector<const char *, 0> Args;
+  BumpPtrAllocator Alloc;
+};
+
+} // namespace cl
+} // namespace llvm
+
+#endif // LLVM_SUPPORT_LIBRARYOPTIONSCONTEXT_H
diff --git a/llvm/lib/IR/LLVMContext.cpp b/llvm/lib/IR/LLVMContext.cpp
index 1fcdd3bc4c142a..fb3b4bd8b206f3 100644
--- a/llvm/lib/IR/LLVMContext.cpp
+++ b/llvm/lib/IR/LLVMContext.cpp
@@ -357,6 +357,10 @@ void LLVMContext::setOptPassGate(OptPassGate& OPG) {
   pImpl->setOptPassGate(OPG);
 }
 
+cl::LibraryOptionsContext &LLVMContext::getLibraryOptions() const {
+  return pImpl->LibraryOptions;
+}
+
 const DiagnosticHandler *LLVMContext::getDiagHandlerPtr() const {
   return pImpl->DiagHandler.get();
 }
diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h
index 74a73664473b03..d02d57e4c0197f 100644
--- a/llvm/lib/IR/LLVMContextImpl.h
+++ b/llvm/lib/IR/LLVMContextImpl.h
@@ -40,6 +40,7 @@
 #include "llvm/IR/Value.h"
 #include "llvm/Support/Allocator.h"
 #include "llvm/Support/Casting.h"
+#include "llvm/Support/LibraryOptionsContext.h"
 #include "llvm/Support/StringSaver.h"
 #include <algorithm>
 #include <cassert>
@@ -1951,6 +1952,8 @@ class LLVMContextImpl {
   std::string DefaultTargetCPU;
   std::string DefaultTargetFeatures;
 
+  cl::LibraryOptionsContext LibraryOptions;
+
   /// The next available source atom group number. The front end is responsible
   /// for assigning source atom numbers, but certain optimisations need to
   /// assign new group numbers to a set of instructions. Most often code
diff --git a/llvm/lib/Option/LibraryOptions.cpp 
b/llvm/lib/Option/LibraryOptions.cpp
index cfcb82273eb6d8..e5848a0498f23c 100644
--- a/llvm/lib/Option/LibraryOptions.cpp
+++ b/llvm/lib/Option/LibraryOptions.cpp
@@ -34,7 +34,7 @@ void LibraryOptionsParser::forEachOption(
   }
 }
 
-Error LibraryOptionsParser::parse(ArrayRef<const char *> Args,
+Error LibraryOptionsParser::parse(void *Opts, ArrayRef<const char *> Args,
                                   unsigned &Consumed, BumpPtrAllocator &Alloc) 
{
   InputArgList List(Args.begin(), Args.end());
   Consumed = 0;
@@ -45,7 +45,7 @@ Error LibraryOptionsParser::parse(ArrayRef<const char *> Args,
                              "' requires an argument");
   if (A->getOption().getKind() == Option::UnknownClass)
     return createStringError("unknown argument '" + Twine(Args[0]) + "'");
-  if (!Apply(*A, Alloc))
+  if (!Apply(Opts, *A, Alloc))
     return createStringError("invalid value '" + Twine(A->getValue()) +
                              "' in '" + A->getAsString(List) + "'");
   return Error::success();
diff --git a/llvm/lib/Support/CommandLine.cpp b/llvm/lib/Support/CommandLine.cpp
index 1cb5fccb07a1be..31f07e114cace1 100644
--- a/llvm/lib/Support/CommandLine.cpp
+++ b/llvm/lib/Support/CommandLine.cpp
@@ -35,6 +35,7 @@
 #include "llvm/Support/Error.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/FileSystem.h"
+#include "llvm/Support/LibraryOptionsContext.h"
 #include "llvm/Support/ManagedStatic.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Path.h"
@@ -189,7 +190,8 @@ class CommandLineParser {
 
   bool ParseCommandLineOptions(int argc, const char *const *argv,
                                StringRef Overview, raw_ostream *Errs = nullptr,
-                               vfs::FileSystem *VFS = nullptr);
+                               vfs::FileSystem *VFS = nullptr,
+                               LibraryOptionsContext *Context = nullptr);
 
   void forEachSubCommand(Option &Opt, function_ref<void(SubCommand &)> Action) 
{
     if (Opt.Subs.empty()) {
@@ -440,6 +442,26 @@ static CommandLineParser &globalParser() {
   return *GlobalParser;
 }
 
+// Parses the library option spelled by Args[0], which takes at most one
+// separate value, into Opts. Opts may refer to the arguments, so they are
+// copied into Alloc, and appended to Record if given.
+static Error
+parseLibraryOption(LibraryOptions &L, void *Opts, ArrayRef<const char *> Args,
+                   unsigned &Consumed, BumpPtrAllocator &Alloc,
+                   SmallVectorImpl<const char *> *Record = nullptr) {
+  StringSaver Saver(Alloc);
+  const char *Saved[2];
+  unsigned NumArgs = std::min<size_t>(Args.size(), 2);
+  for (unsigned J = 0; J != NumArgs; ++J)
+    Saved[J] = Saver.save(Args[J]).data();
+  Consumed = 1;
+  if (Error E = L.parse(Opts, ArrayRef(Saved, NumArgs), Consumed, Alloc))
+    return E;
+  if (Record)
+    Record->append(Saved, Saved + Consumed);
+  return Error::success();
+}
+
 template <typename T, T TrueVal, T FalseVal>
 static bool parseBool(Option &O, StringRef ArgName, StringRef Arg, T &Value) {
   // ProvideOption passes a null Arg for a bare -flag (treated as true) and an
@@ -1424,14 +1446,15 @@ Error ExpansionContext::readConfigFile(StringRef 
CfgFile,
 }
 
 static void initCommonOptions();
-bool cl::ParseCommandLineOptions(int argc, const char *const *argv,
-                                 StringRef Overview, raw_ostream *Errs,
-                                 vfs::FileSystem *VFS, const char *EnvVar) {
+static bool parseCommandLineOptionsImpl(LibraryOptionsContext *Context, 
ArrayRef<const char *> Args,
+                                        StringRef Overview, raw_ostream *Errs,
+                                        vfs::FileSystem *VFS,
+                                        const char *EnvVar) {
   initCommonOptions();
   SmallVector<const char *, 20> NewArgv;
   BumpPtrAllocator A;
   StringSaver Saver(A);
-  NewArgv.push_back(argv[0]);
+  NewArgv.push_back(Args[0]);
 
   // Parse options from environment variable.
   if (EnvVar) {
@@ -1441,13 +1464,27 @@ bool cl::ParseCommandLineOptions(int argc, const char 
*const *argv,
   }
 
   // Append options from command line.
-  for (int I = 1; I < argc; ++I)
-    NewArgv.push_back(argv[I]);
+  append_range(NewArgv, Args.drop_front());
   int NewArgc = static_cast<int>(NewArgv.size());
 
   // Parse all options.
   return globalParser().ParseCommandLineOptions(NewArgc, &NewArgv[0], Overview,
-                                                Errs, VFS);
+                                                Errs, VFS, Context);
+}
+
+bool cl::ParseCommandLineOptions(int argc, const char *const *argv,
+                                 StringRef Overview, raw_ostream *Errs,
+                                 vfs::FileSystem *VFS, const char *EnvVar) {
+  return parseCommandLineOptionsImpl(nullptr, ArrayRef(argv, argc), Overview,
+                                     Errs, VFS, EnvVar);
+}
+
+bool cl::parseCommandLineOptions(LibraryOptionsContext &LibraryOptions,
+                                 ArrayRef<const char *> Args,
+                                 StringRef Overview, raw_ostream *Errs,
+                                 vfs::FileSystem *VFS, const char *EnvVar) {
+  return parseCommandLineOptionsImpl(&LibraryOptions, Args, Overview, Errs, 
VFS,
+                                     EnvVar);
 }
 
 /// Reset all options at least once, so that we can parse different options.
@@ -1474,11 +1511,9 @@ void CommandLineParser::ResetAllOptionOccurrences() {
   LibraryArgAlloc.Reset();
 }
 
-bool CommandLineParser::ParseCommandLineOptions(int argc,
-                                                const char *const *argv,
-                                                StringRef Overview,
-                                                raw_ostream *Errs,
-                                                vfs::FileSystem *VFS) {
+bool CommandLineParser::ParseCommandLineOptions(
+    int argc, const char *const *argv, StringRef Overview, raw_ostream *Errs,
+    vfs::FileSystem *VFS, LibraryOptionsContext *Context) {
   assert(hasOptions() && "No options specified!");
 
   ProgramOverview = Overview;
@@ -1654,14 +1689,12 @@ bool CommandLineParser::ParseCommandLineOptions(int 
argc,
 
       if (!Handler) {
         if (LibraryOptions *L = lookupLibraryOption(ArgName.split('=').first)) 
{
-          // An option takes at most one separate value.
-          StringSaver Saver(LibraryArgAlloc);
-          const char *Args[2];
-          unsigned NumArgs = std::min(argc - i, 2);
-          for (unsigned J = 0; J != NumArgs; ++J)
-            Args[J] = Saver.save(argv[i + J]).data();
-          unsigned N = 1;
-          if (Error E = L->parse(ArrayRef(Args, NumArgs), N, LibraryArgAlloc)) 
{
+          ArrayRef<const char *> Rest(argv + i, argc - i);
+          unsigned N;
+          if (Error E = Context && L->isPerContext()
+                            ? Context->parse(*L, Rest, N)
+                            : parseLibraryOption(*L, L->global(), Rest, N,
+                                                 LibraryArgAlloc)) {
             *Errs << ProgramName << ": " << toString(std::move(E)) << '\n';
             ErrorParsing = true;
           }
@@ -2909,6 +2942,34 @@ void cl::addLibraryOptions(LibraryOptions &L) {
   globalParser().Libraries.push_back(&L);
 }
 
+Error LibraryOptionsContext::set(ArrayRef<const char *> Args) {
+  for (size_t I = 0, E = Args.size(); I != E;) {
+    StringRef Name = Args[I];
+    LibraryOptions *L = nullptr;
+    if (Name.consume_front("-")) {
+      Name.consume_front("-");
+      L = globalParser().lookupLibraryOption(Name.split('=').first);
+    }
+    if (!L)
+      return createStringError("'" + Twine(Args[I]) +
+                               "' is not a library option");
+    unsigned N;
+    if (Error Err = parse(*L, Args.drop_front(I), N))
+      return Err;
+    I += N;
+  }
+  return Error::success();
+}
+
+Error LibraryOptionsContext::parse(LibraryOptions &L,
+                                   ArrayRef<const char *> Args,
+                                   unsigned &Consumed) {
+  auto [It, Inserted] = Values.try_emplace(L.global());
+  if (Inserted)
+    It->second = L.copyGlobal(Alloc);
+  return parseLibraryOption(L, It->second, Args, Consumed, Alloc, &this->Args);
+}
+
 void cl::ResetAllOptionOccurrences() {
   globalParser().ResetAllOptionOccurrences();
 }
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCLOptions.td 
b/llvm/lib/Transforms/InstCombine/InstCombineCLOptions.td
index 9fc098796f9e45..d2f850f5972eff 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCLOptions.td
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCLOptions.td
@@ -8,7 +8,7 @@
 
 include "llvm/Option/LibraryOptions.td"
 
-def InstCombineCLOptions : OptionsStruct<prefix = "instcombine-">;
+def InstCombineCLOptions : OptionsStruct<prefix = "instcombine-", perContext = 
true>;
 
 defm : BoolField<"instcombine-code-sinking", "true",
   "Enable code sinking">;
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp 
b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 6986fb324bfeef..7158dec4277b4e 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -96,6 +96,7 @@
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/KnownBits.h"
 #include "llvm/Support/KnownFPClass.h"
+#include "llvm/Support/LibraryOptionsContext.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Transforms/InstCombine/InstCombine.h"
 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
@@ -6269,7 +6270,8 @@ static bool combineInstructionsOverFunction(
 
   // Lower dbg.declare intrinsics otherwise their value may be clobbered
   // by instcombiner.
-  const InstCombineCLOptions &CLOpts = InstCombineCLOptions::Global;
+  const auto &CLOpts =
+      F.getContext().getLibraryOptions().get<InstCombineCLOptions>();
   bool MadeIRChange = false;
   if (CLOpts.lower_dbg_declare)
     MadeIRChange = LowerDbgDeclare(F);
diff --git a/llvm/tools/opt/optdriver.cpp b/llvm/tools/opt/optdriver.cpp
index 068e227328c168..da87ef049c7d4e 100644
--- a/llvm/tools/opt/optdriver.cpp
+++ b/llvm/tools/opt/optdriver.cpp
@@ -456,8 +456,10 @@ optMain(int argc, char **argv,
   // Register the Target and CPU printer for --version.
   cl::AddExtraVersionPrinter(sys::printDefaultTargetAndDetectedCPU);
 
-  cl::ParseCommandLineOptions(
-      argc, argv, "llvm .bc -> .bc modular optimizer and analysis printer\n");
+  LLVMContext Context;
+  cl::parseCommandLineOptions(
+      Context.getLibraryOptions(), ArrayRef(argv, argc),
+      "llvm .bc -> .bc modular optimizer and analysis printer\n");
 
   SmallVector<PassPluginLibraryInfo, 0> Extensions;
   for (const std::string &Path : PassPlugins) {
@@ -471,8 +473,6 @@ optMain(int argc, char **argv,
   if (Error E = passPluginArguments(Extensions, PluginArgs))
     reportFatalUsageError(std::move(E));
 
-  LLVMContext Context;
-
   // TODO: remove shouldForceLegacyPM().
   const bool UseNPM =
       !shouldForceLegacyPM() || PassPipeline.getNumOccurrences() > 0;
diff --git a/llvm/unittests/IR/CMakeLists.txt b/llvm/unittests/IR/CMakeLists.txt
index df83992fadd1e4..ad94dcc32a880b 100644
--- a/llvm/unittests/IR/CMakeLists.txt
+++ b/llvm/unittests/IR/CMakeLists.txt
@@ -2,6 +2,7 @@ set(LLVM_LINK_COMPONENTS
   Analysis
   AsmParser
   Core
+  InstCombine
   Support
   Passes
   TargetParser
@@ -35,6 +36,7 @@ add_llvm_unittest(IRTests
   InstructionsTest.cpp
   IntrinsicsTest.cpp
   LegacyPassManagerTest.cpp
+  LibraryOptionsContextTest.cpp
   MDBuilderTest.cpp
   MemoryModelRelaxationAnnotationsTest.cpp
   ManglerTest.cpp
diff --git a/llvm/unittests/IR/LibraryOptionsContextTest.cpp 
b/llvm/unittests/IR/LibraryOptionsContextTest.cpp
new file mode 100644
index 00000000000000..2588bdcda41cb7
--- /dev/null
+++ b/llvm/unittests/IR/LibraryOptionsContextTest.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/AsmParser/Parser.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/ValueSymbolTable.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Transforms/InstCombine/InstCombine.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+
+// InstCombine sinks %a into its only user's block unless
+// -instcombine-code-sinking=0.
+const char *IR = R"(
+define i32 @f(i1 %c, i32 %x) {
+entry:
+  %a = add i32 %x, 1
+  br i1 %c, label %t, label %e
+t:
+  ret i32 %a
+e:
+  ret i32 0
+}
+)";
+
+// Runs InstCombine on IR in Ctx and returns the block that holds %a.
+std::string runInstCombine(LLVMContext &Ctx) {
+  SMDiagnostic Err;
+  std::unique_ptr<Module> M = parseAssemblyString(IR, Err, Ctx);
+  if (!M)
+    return Err.getMessage().str();
+  LoopAnalysisManager LAM;
+  FunctionAnalysisManager FAM;
+  CGSCCAnalysisManager CGAM;
+  ModuleAnalysisManager MAM;
+  PassBuilder PB;
+  PB.registerModuleAnalyses(MAM);
+  PB.registerCGSCCAnalyses(CGAM);
+  PB.registerFunctionAnalyses(FAM);
+  PB.registerLoopAnalyses(LAM);
+  PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
+  Function &F = *M->getFunction("f");
+  InstCombinePass().run(F, FAM);
+  auto *A = cast<Instruction>(F.getValueSymbolTable()->lookup("a"));
+  return A->getParent()->getName().str();
+}
+
+// parseCommandLineOptions sets InstCombine's options in the given context
+// only, so two contexts in one process run InstCombine with different options.
+TEST(LibraryOptionsContextTest, InstCombine) {
+  LLVMContext C1, C2;
+  const char *Args[] = {"prog", "-instcombine-code-sinking=0"};
+  ASSERT_TRUE(
+      cl::parseCommandLineOptions(C2.getLibraryOptions(), Args, "", &nulls()));
+  EXPECT_EQ(runInstCombine(C1), "t");
+  EXPECT_EQ(runInstCombine(C2), "entry");
+}
+
+} // namespace
diff --git a/llvm/unittests/Option/LibraryOptionsTest.cpp 
b/llvm/unittests/Option/LibraryOptionsTest.cpp
index dff1d179845b7e..0120f105353323 100644
--- a/llvm/unittests/Option/LibraryOptionsTest.cpp
+++ b/llvm/unittests/Option/LibraryOptionsTest.cpp
@@ -17,6 +17,7 @@ enum class Mode { A, B };
 
 #include "llvm/Option/ArgList.h"
 #include "llvm/Option/LibraryOptions.h"
+#include "llvm/Support/LibraryOptionsContext.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
@@ -115,12 +116,7 @@ TEST(LibraryOptionsTest, BoolOrDefault) {
 
 // What cl:: sees of the struct, without cl::.
 TEST(LibraryOptionsTest, Parser) {
-  opt::LibraryOptionsParser P(
-      TestLibraryOptions::optTable,
-      [](const opt::Arg &A, BumpPtrAllocator &Alloc) {
-        return TestLibraryOptions::Global.apply(A, Alloc);
-      },
-      [] { TestLibraryOptions::Global = TestLibraryOptions(); });
+  opt::LibraryOptionsParser P(std::in_place_type<TestLibraryOptions>);
 
   std::vector<std::string> Rows;
   P.forEachOption([&](StringRef Spelling, StringRef MetaVar, StringRef Help) {
@@ -140,7 +136,7 @@ TEST(LibraryOptionsTest, Parser) {
   BumpPtrAllocator Alloc;
   auto Parse = [&](std::initializer_list<const char *> Argv) {
     unsigned Consumed = 0;
-    std::string Err = toString(P.parse(Argv, Consumed, Alloc));
+    std::string Err = toString(P.parse(P.global(), Argv, Consumed, Alloc));
     return std::to_string(Consumed) + " " + Err;
   };
   EXPECT_EQ(Parse({"-lib-count", "5"}), "2 ");
@@ -193,4 +189,50 @@ TEST(LibraryOptionsTest, Register) {
   cl::ResetCommandLineParser();
 }
 
+// parseCommandLineOptions sets the options of a library that is not
+// per-context in its Global, which a context without values reads.
+TEST(LibraryOptionsTest, ParseIntoGlobal) {
+  cl::ResetCommandLineParser();
+  opt::RegisterLibraryOptions<TestLibraryOptions> Registration;
+  cl::LibraryOptionsContext V;
+  const char *Args[] = {"prog", "-lib-count=5"};
+  EXPECT_TRUE(cl::parseCommandLineOptions(V, Args, "", &nulls()));
+  EXPECT_EQ(TestLibraryOptions::Global.count, 5u);
+  EXPECT_EQ(&V.get<TestLibraryOptions>(), &TestLibraryOptions::Global);
+  cl::ResetCommandLineParser();
+}
+
+// Values that override Global, as an LLVMContext holds them.
+TEST(LibraryOptionsTest, Values) {
+  cl::ResetCommandLineParser();
+  opt::RegisterLibraryOptions<TestLibraryOptions> Registration;
+  cl::opt<bool> NotLibrary("lib-cl-opt");
+  cl::LibraryOptionsContext V;
+  auto Set = [&](std::initializer_list<const char *> Args) {
+    return toString(V.set(Args));
+  };
+  std::string Path = "-lib-path=q";
+  EXPECT_EQ(Set({"-lib-count", "7", Path.c_str(), "--lib-list=1,2"}), "");
+  Path.assign(Path.size(), ' ');
+  EXPECT_EQ(TestLibraryOptions::Global.count, 3u);
+
+  // A later call keeps the earlier values.
+  EXPECT_EQ(Set({"-lib-list=3"}), "");
+  EXPECT_THAT(V.get<TestLibraryOptions>().list,
+              testing::ElementsAre(1u, 2u, 3u));
+
+  // Another context replays the arguments, which outlive the caller's.
+  cl::LibraryOptionsContext Copy;
+  EXPECT_EQ(toString(Copy.set(V.args())), "");
+  const TestLibraryOptions &C = Copy.get<TestLibraryOptions>();
+  EXPECT_EQ(C.count, 7u);
+  EXPECT_EQ(C.Path, "q");
+  EXPECT_THAT(C.list, testing::ElementsAre(1u, 2u, 3u));
+
+  EXPECT_EQ(Set({"-lib-count=x"}), "invalid value 'x' in '-lib-count=x'");
+  EXPECT_EQ(Set({"-lib-cl-opt"}), "'-lib-cl-opt' is not a library option");
+  EXPECT_EQ(Set({"lib-count=1"}), "'lib-count=1' is not a library option");
+  cl::ResetCommandLineParser();
+}
+
 } // namespace
diff --git a/llvm/unittests/Support/CommandLineTest.cpp 
b/llvm/unittests/Support/CommandLineTest.cpp
index ccf45962ba04fc..ae9e0969d15407 100644
--- a/llvm/unittests/Support/CommandLineTest.cpp
+++ b/llvm/unittests/Support/CommandLineTest.cpp
@@ -2346,7 +2346,7 @@ struct TestLibrary final : cl::LibraryOptions {
     Fn("library-flag", "", "A flag");
     Fn("library-int", " <int>", "");
   }
-  Error parse(ArrayRef<const char *> Argv, unsigned &Consumed,
+  Error parse(void *, ArrayRef<const char *> Argv, unsigned &Consumed,
               BumpPtrAllocator &) override {
     Consumed = 1;
     Args.push_back(Argv[0]);
@@ -2360,6 +2360,8 @@ struct TestLibrary final : cl::LibraryOptions {
       return createStringError("bad value");
     return Error::success();
   }
+  void *global() override { return this; }
+  void *copyGlobal(BumpPtrAllocator &) const override { return nullptr; }
   void reset() override { Args.clear(); }
 };
 
diff --git a/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp 
b/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
index c4b23c046e7186..8df952eb8664e0 100644
--- a/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
@@ -353,8 +353,10 @@ static void emitOptionsStruct(const Record &Struct,
   OS << "#include \"llvm/ADT/StringRef.h\"\n";
   OS << "#include \"llvm/Support/Allocator.h\"\n\n";
   StringRef Namespace = Struct.getValueAsString("Namespace");
-  OS << "namespace llvm {\nnamespace opt {\nclass Arg;\n"
-        "class OptTable;\n} // namespace opt\n} // namespace llvm\n\n";
+  OS << "namespace llvm {\nnamespace cl {\nclass LibraryOptionsContext;\n"
+        "} // namespace cl\nnamespace opt {\nclass Arg;\n"
+        "class LibraryOptionsParser;\nclass OptTable;\n} // namespace opt\n"
+        "} // namespace llvm\n\n";
   OS << "namespace " << Namespace << " {\n";
   OS << "struct " << Name << " {\n";
   if (Namespace != "llvm")
@@ -363,13 +365,18 @@ static void emitOptionsStruct(const Record &Struct,
     ConditionScope CS(OS, *R);
     OS << "  " << M.Type << " " << M.Name << "{" << M.Default << "};\n";
   }
-  OS << "\n  /// The instance cl::ParseCommandLineOptions sets.\n";
-  OS << "  static " << Name << " Global;\n\n";
+  bool PerContext = Struct.getValueAsBit("PerContext");
+  OS << "\n  static constexpr bool PerContext = "
+     << (PerContext ? "true" : "false") << ";\n";
   OS << "  static const llvm::opt::OptTable &optTable();\n";
   OS << "  /// Sets the member that \\p A names, allocating a list from \\p "
         "Alloc.\n  /// Returns false if the value is invalid.\n";
   OS << "  bool apply(const llvm::opt::Arg &A, llvm::BumpPtrAllocator "
         "&Alloc);\n";
+  if (PerContext)
+    OS << "\nprivate:\n  friend class llvm::cl::LibraryOptionsContext;\n"
+          "  friend class llvm::opt::LibraryOptionsParser;\n";
+  OS << "\n  static " << Name << " Global;\n";
   OS << "};\n} // namespace " << Namespace << "\n";
   OS << "#endif // OPTIONS_STRUCT_DECL\n";
 
diff --git a/llvm/utils/gn/secondary/llvm/unittests/IR/BUILD.gn 
b/llvm/utils/gn/secondary/llvm/unittests/IR/BUILD.gn
index 547d82187b87f1..21d7a82a713b4c 100644
--- a/llvm/utils/gn/secondary/llvm/unittests/IR/BUILD.gn
+++ b/llvm/utils/gn/secondary/llvm/unittests/IR/BUILD.gn
@@ -8,6 +8,7 @@ unittest("IRTests") {
     "//llvm/lib/Passes",
     "//llvm/lib/Support",
     "//llvm/lib/Testing/Support",
+    "//llvm/lib/Transforms/InstCombine",
     "//llvm/lib/Transforms/Utils",
   ]
   sources = [
@@ -36,6 +37,7 @@ unittest("IRTests") {
     "InstructionsTest.cpp",
     "IntrinsicsTest.cpp",
     "LegacyPassManagerTest.cpp",
+    "LibraryOptionsContextTest.cpp",
     "MDBuilderTest.cpp",
     "ManglerTest.cpp",
     "MemoryModelRelaxationAnnotationsTest.cpp",
diff --git a/utils/bazel/llvm-project-overlay/llvm/unittests/BUILD.bazel 
b/utils/bazel/llvm-project-overlay/llvm/unittests/BUILD.bazel
index 89c25df2d1d92e..05e3760c588cf5 100644
--- a/utils/bazel/llvm-project-overlay/llvm/unittests/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/llvm/unittests/BUILD.bazel
@@ -474,6 +474,7 @@ cc_test(
         "//llvm:BinaryFormat",
         "//llvm:CodeGen",
         "//llvm:Core",
+        "//llvm:InstCombine",
         "//llvm:Passes",
         "//llvm:Plugins",
         "//llvm:Scalar",

>From 7719060711312ca95463b6c197a929723d412840 Mon Sep 17 00:00:00 2001
From: Fangrui Song <[email protected]>
Date: Tue, 13 Oct 2026 22:50:17 -0700
Subject: [PATCH 2/4] port Vectorize; replace set() with assign()

---
 clang/lib/CIR/FrontendAction/CIRGenAction.cpp |  2 +-
 clang/lib/CodeGen/CodeGenAction.cpp           |  3 ++-
 .../unittests/Frontend/CodeGenActionTest.cpp  | 24 +++++++++++++++++++
 llvm/include/llvm/Option/LibraryOptions.h     |  2 +-
 llvm/include/llvm/SandboxIR/Context.h         |  3 +++
 .../llvm/Support/LibraryOptionsContext.h      | 20 ++++++++--------
 .../SandboxVectorizer/Passes/BundleVec.h      |  5 ++++
 .../SandboxVectorizer/SeedCollector.h         |  3 ++-
 llvm/lib/Support/CommandLine.cpp              |  7 ++++--
 .../InstCombine/InstructionCombining.cpp      |  1 -
 .../Vectorize/LoopIdiomVectorize.cpp          |  3 ++-
 .../Transforms/Vectorize/LoopVectorize.cpp    |  7 ++++--
 .../Transforms/Vectorize/SLPVectorizer.cpp    |  2 +-
 .../SandboxVectorizer/Passes/BundleVec.cpp    | 13 +++++-----
 .../SandboxVectorizer/Passes/LoadStoreVec.cpp |  4 +++-
 .../Passes/SeedCollection.cpp                 |  3 ++-
 .../Passes/TransactionAcceptOrRevert.cpp      |  3 ++-
 .../SandboxVectorizer/SandboxVectorizer.cpp   |  2 +-
 .../SandboxVectorizer/SeedCollector.cpp       | 14 +++++++----
 .../Vectorize/SandboxVectorizer/VecUtils.cpp  |  5 ++--
 .../Transforms/Vectorize/VPlanTransforms.cpp  | 12 ++++++----
 .../Transforms/Vectorize/VPlanTransforms.h    |  4 ++--
 .../Transforms/Vectorize/VectorCombine.cpp    |  5 ++--
 .../Transforms/Vectorize/VectorizeOptions.td  |  2 +-
 llvm/unittests/Option/LibraryOptionsTest.cpp  | 24 +++++++++----------
 .../Vectorize/VPlanVerifierTest.cpp           |  4 +---
 .../TableGen/Basic/OptionParserEmitter.cpp    | 15 +++++++-----
 27 files changed, 123 insertions(+), 69 deletions(-)

diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp 
b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
index a04aa51e163af3..54a6b5336662bb 100644
--- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
+++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
@@ -368,7 +368,7 @@ CIRGenAction::CIRGenAction(OutputType Act, 
mlir::MLIRContext *MLIRCtx)
 CIRGenAction::~CIRGenAction() { MLIRMod.release(); }
 
 bool CIRGenAction::BeginSourceFileAction(CompilerInstance &CI) {
-  cantFail(Ctx->getLibraryOptions().set(CI.getLibraryOptions().args()));
+  cantFail(Ctx->getLibraryOptions().assign(CI.getLibraryOptions().args()));
   if (clang::loadLinkModules(CI, *Ctx, LinkModules))
     return false;
   return ASTFrontendAction::BeginSourceFileAction(CI);
diff --git a/clang/lib/CodeGen/CodeGenAction.cpp 
b/clang/lib/CodeGen/CodeGenAction.cpp
index 22925cc55f495f..cd5047659eef7a 100644
--- a/clang/lib/CodeGen/CodeGenAction.cpp
+++ b/clang/lib/CodeGen/CodeGenAction.cpp
@@ -367,7 +367,8 @@ CodeGenerator *CodeGenAction::getCodeGenerator() const {
 }
 
 bool CodeGenAction::BeginSourceFileAction(CompilerInstance &CI) {
-  cantFail(VMContext->getLibraryOptions().set(CI.getLibraryOptions().args()));
+  cantFail(
+      VMContext->getLibraryOptions().assign(CI.getLibraryOptions().args()));
   if (CI.getFrontendOpts().GenReducedBMI)
     CI.getLangOpts().setCompilingModule(LangOptions::CMK_ModuleInterface);
   return ASTFrontendAction::BeginSourceFileAction(CI);
diff --git a/clang/unittests/Frontend/CodeGenActionTest.cpp 
b/clang/unittests/Frontend/CodeGenActionTest.cpp
index d2befe1af4a591..f119c51128796c 100644
--- a/clang/unittests/Frontend/CodeGenActionTest.cpp
+++ b/clang/unittests/Frontend/CodeGenActionTest.cpp
@@ -15,7 +15,9 @@
 #include "clang/CodeGen/BackendUtil.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Lex/PreprocessorOptions.h"
+#include "llvm/IR/LLVMContext.h"
 #include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/LibraryOptionsContext.h"
 #include "llvm/Support/VirtualFileSystem.h"
 #include "gtest/gtest.h"
 
@@ -76,4 +78,26 @@ TEST(CodeGenTest, CodeGenFromIRMemBuffer) {
   bool Success = Compiler.ExecuteAction(Action);
   EXPECT_TRUE(Success);
 }
+
+// Each input assigns -mllvm library options to the LLVMContext anew.
+TEST(CodeGenTest, LibraryOptionsPerInput) {
+  auto Invocation = std::make_shared<CompilerInvocation>();
+  std::unique_ptr<MemoryBuffer> MemBuffer =
+      MemoryBuffer::getMemBuffer("", "test.ll");
+  for (int I = 0; I != 2; ++I)
+    Invocation->getFrontendOpts().Inputs.push_back(
+        FrontendInputFile(*MemBuffer, Language::LLVM_IR));
+  Invocation->getFrontendOpts().LLVMArgs = {"-instcombine-code-sinking=0"};
+  Invocation->getFrontendOpts().ProgramAction = frontend::EmitLLVMOnly;
+  Invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
+  CompilerInstance Compiler(std::move(Invocation));
+  Compiler.setVirtualFileSystem(llvm::vfs::getRealFileSystem());
+  Compiler.createDiagnostics();
+  Compiler.parseLLVMArgs();
+
+  LLVMContext Ctx;
+  EmitLLVMOnlyAction Action(&Ctx);
+  EXPECT_TRUE(Compiler.ExecuteAction(Action));
+  EXPECT_EQ(Ctx.getLibraryOptions().args().size(), 1u);
+}
 }
diff --git a/llvm/include/llvm/Option/LibraryOptions.h 
b/llvm/include/llvm/Option/LibraryOptions.h
index 19df88f77e586a..830cc52fd14fbc 100644
--- a/llvm/include/llvm/Option/LibraryOptions.h
+++ b/llvm/include/llvm/Option/LibraryOptions.h
@@ -119,7 +119,7 @@ class LLVM_ABI LibraryOptTable : public OptTable {
   ~LibraryOptTable() override;
 };
 
-/// Connects options struct T to cl::ParseCommandLineOptions and
+/// Connects options struct T to cl::parseCommandLineOptions and
 /// cl::LibraryOptionsContext.
 class LLVM_ABI LibraryOptionsParser final : public cl::LibraryOptions {
 public:
diff --git a/llvm/include/llvm/SandboxIR/Context.h 
b/llvm/include/llvm/SandboxIR/Context.h
index 2467c5d7fb3d51..4d1cd45612f41e 100644
--- a/llvm/include/llvm/SandboxIR/Context.h
+++ b/llvm/include/llvm/SandboxIR/Context.h
@@ -237,6 +237,9 @@ class LLVM_ABI Context {
   /// Clears function-level state.
   void clear();
 
+  cl::LibraryOptionsContext &getLibraryOptions() const {
+    return LLVMCtx.getLibraryOptions();
+  }
   Tracker &getTracker() { return IRTracker; }
   /// Convenience function for `getTracker().save()`
   void save() { IRTracker.save(); }
diff --git a/llvm/include/llvm/Support/LibraryOptionsContext.h 
b/llvm/include/llvm/Support/LibraryOptionsContext.h
index 150a8c1d5a7719..f9b74ea181ef07 100644
--- a/llvm/include/llvm/Support/LibraryOptionsContext.h
+++ b/llvm/include/llvm/Support/LibraryOptionsContext.h
@@ -23,20 +23,20 @@ namespace cl {
 
 class LibraryOptions;
 
-// Values of library options that override their global() in one compilation
-// context, such as an LLVMContext.
+// The library options of one compilation context, such as an LLVMContext.
+// Libraries declared with perContext read their options here.
 class LLVM_ABI LibraryOptionsContext {
 public:
-  // Sets the library options that Args spell, e.g. {"-x=v"}. A library's 
values
-  // start as a copy of its global(), and later calls add to them. Fails at the
-  // first argument that is not a library option or has an invalid value.
+  // Replaces the library options with those that Args spell, e.g. {"-x=v"}.
+  // Fails at the first argument that is not a library option or has an invalid
+  // value.
   //
-  // Strings copied from global() die with ResetAllOptionOccurrences. The first
-  // call (or ParseCommandLineOptions) indexes the registry, which is not
-  // thread-safe.
-  Error set(ArrayRef<const char *> Args);
+  // A library's values start as a copy of its global(), which may refer to
+  // strings that ResetAllOptionOccurrences frees.
+  // Indexing libraries registered since the last parse is not thread-safe.
+  Error assign(ArrayRef<const char *> Args);
 
-  // Parses the option of L that Args[0] spells, as set() does.
+  // Parses the option of L that Args[0] spells, keeping the other values.
   Error parse(LibraryOptions &L, ArrayRef<const char *> Args,
               unsigned &Consumed);
 
diff --git 
a/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/Passes/BundleVec.h 
b/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/Passes/BundleVec.h
index 08d7c8981f092a..04d03ada87b247 100644
--- 
a/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/Passes/BundleVec.h
+++ 
b/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/Passes/BundleVec.h
@@ -23,6 +23,10 @@
 #include "llvm/Transforms/Vectorize/SandboxVectorizer/Legality.h"
 #include "llvm/Transforms/Vectorize/SandboxVectorizer/Scheduler.h"
 
+namespace llvm {
+struct VectorizeOptions;
+} // namespace llvm
+
 namespace llvm::sandboxir {
 
 /// This is a simple bottom-up (top-down) vectorizer Region pass.
@@ -38,6 +42,7 @@ namespace llvm::sandboxir {
 /// accepts or rejects the IR transaction, depending on the cost.
 class LLVM_ABI BundleVec final : public RegionPass {
 private:
+  const VectorizeOptions *Opts = nullptr;
   /// Set to true whenever the pass modifies the IR.
   bool Change = false;
   AuxPassArgsRegistry ArgsRegistry;
diff --git 
a/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/SeedCollector.h 
b/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/SeedCollector.h
index 3f8b49af7b1bdd..d63c0276a6e9c6 100644
--- a/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/SeedCollector.h
+++ b/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/SeedCollector.h
@@ -197,12 +197,13 @@ class SeedContainer {
   DenseMap<Instruction *, SeedBundle *> SeedLookupMap;
 
   ScalarEvolution &SE;
+  unsigned BundleSizeLimit;
 
   template <typename LoadOrStoreT>
   KeyT getKey(LoadOrStoreT *LSI, bool AllowDiffTypes) const;
 
 public:
-  SeedContainer(ScalarEvolution &SE) : SE(SE) {}
+  LLVM_ABI SeedContainer(ScalarEvolution &SE);
 
   class iterator {
     BundleMapT *Map = nullptr;
diff --git a/llvm/lib/Support/CommandLine.cpp b/llvm/lib/Support/CommandLine.cpp
index 31f07e114cace1..6e304b28da6c49 100644
--- a/llvm/lib/Support/CommandLine.cpp
+++ b/llvm/lib/Support/CommandLine.cpp
@@ -1446,7 +1446,8 @@ Error ExpansionContext::readConfigFile(StringRef CfgFile,
 }
 
 static void initCommonOptions();
-static bool parseCommandLineOptionsImpl(LibraryOptionsContext *Context, 
ArrayRef<const char *> Args,
+static bool parseCommandLineOptionsImpl(LibraryOptionsContext *Context,
+                                        ArrayRef<const char *> Args,
                                         StringRef Overview, raw_ostream *Errs,
                                         vfs::FileSystem *VFS,
                                         const char *EnvVar) {
@@ -2942,7 +2943,9 @@ void cl::addLibraryOptions(LibraryOptions &L) {
   globalParser().Libraries.push_back(&L);
 }
 
-Error LibraryOptionsContext::set(ArrayRef<const char *> Args) {
+Error LibraryOptionsContext::assign(ArrayRef<const char *> Args) {
+  Values.clear();
+  this->Args.clear();
   for (size_t I = 0, E = Args.size(); I != E;) {
     StringRef Name = Args[I];
     LibraryOptions *L = nullptr;
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp 
b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 7158dec4277b4e..36293a73ebdb9e 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -96,7 +96,6 @@
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/KnownBits.h"
 #include "llvm/Support/KnownFPClass.h"
-#include "llvm/Support/LibraryOptionsContext.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Transforms/InstCombine/InstCombine.h"
 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
diff --git a/llvm/lib/Transforms/Vectorize/LoopIdiomVectorize.cpp 
b/llvm/lib/Transforms/Vectorize/LoopIdiomVectorize.cpp
index 67fc06e8c5ad7e..3801e4f9eb02cb 100644
--- a/llvm/lib/Transforms/Vectorize/LoopIdiomVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopIdiomVectorize.cpp
@@ -160,7 +160,8 @@ class LoopIdiomVectorize {
 PreservedAnalyses LoopIdiomVectorizePass::run(Loop &L, LoopAnalysisManager &AM,
                                               LoopStandardAnalysisResults &AR,
                                               LPMUpdater &) {
-  const VectorizeOptions &Opts = VectorizeOptions::Global;
+  const VectorizeOptions &Opts =
+      L.getHeader()->getContext().getLibraryOptions().get<VectorizeOptions>();
   if (Opts.disable_loop_idiom_vectorize_all)
     return PreservedAnalyses::all();
 
diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp 
b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index 24f50d2120b0c5..0619a0e36c678d 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -7400,7 +7400,8 @@ static void connectEpilogueVectorLoop(VPlan &EpiPlan, 
DominatorTree *DT,
 }
 
 bool LoopVectorizePass::processLoop(Loop *L) {
-  const VectorizeOptions &Opts = VectorizeOptions::Global;
+  const VectorizeOptions &Opts =
+      L->getHeader()->getContext().getLibraryOptions().get<VectorizeOptions>();
   assert((Opts.enable_vplan_native_path || L->isInnermost()) &&
          "VPlan-native path is not enabled. Only process inner loops.");
 
@@ -7963,8 +7964,10 @@ LoopVectorizeResult LoopVectorizePass::runImpl(Function 
&F) {
   // and can invalidate iterators across the loops.
   SmallVector<Loop *, 8> Worklist;
 
+  const VectorizeOptions &Opts =
+      F.getContext().getLibraryOptions().get<VectorizeOptions>();
   for (Loop *L : *LI)
-    collectSupportedLoops(VectorizeOptions::Global, *L, LI, ORE, Worklist);
+    collectSupportedLoops(Opts, *L, LI, ORE, Worklist);
 
   LoopsAnalyzed += Worklist.size();
 
diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp 
b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index 1ced55cea8d724..9f37ebea74f475 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -29450,7 +29450,7 @@ bool SLPVectorizerPass::runImpl(Function &F, 
ScalarEvolution *SE_,
                                 LoopInfo *LI_, DominatorTree *DT_,
                                 AssumptionCache *AC_, DemandedBits *DB_,
                                 OptimizationRemarkEmitter *ORE_) {
-  Opts = &VectorizeOptions::Global;
+  Opts = &F.getContext().getLibraryOptions().get<VectorizeOptions>();
   if (!Opts->vectorize_slp)
     return false;
   SE = SE_;
diff --git 
a/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/BundleVec.cpp 
b/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/BundleVec.cpp
index 28e2877cc77aa8..f6bb1abc0454b3 100644
--- a/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/BundleVec.cpp
+++ b/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/BundleVec.cpp
@@ -205,9 +205,8 @@ Value *BundleVec::createPack(BndlRef<Value *> ToPack, 
BasicBlock *UserBB) {
 Action *BundleVec::vectorizeRec(BndlRef<Value *> Bndl,
                                 BndlRef<Value *> UserBndl, unsigned Depth,
                                 LegalityAnalysis &Legality) {
-  const VectorizeOptions &Opts = VectorizeOptions::Global;
-  bool StopForDebug = DebugBndlCnt++ >= Opts.sbvec_stop_bndl &&
-                      Opts.sbvec_stop_bndl != SBVecStopDisabled;
+  bool StopForDebug = DebugBndlCnt++ >= Opts->sbvec_stop_bndl &&
+                      Opts->sbvec_stop_bndl != SBVecStopDisabled;
   LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "canVectorize() Bundle:\n";
              VecUtils::dump(Bndl));
   const auto &LegalityRes = StopForDebug ? Legality.getForcedPackForDebugging()
@@ -479,7 +478,7 @@ Value *BundleVec::emitVectors() {
       ActionPtr->Vec = NewVec;
     }
 #ifndef NDEBUG
-    if (VectorizeOptions::Global.sbvec_always_verify) {
+    if (Opts->sbvec_always_verify) {
       // This helps find broken IR by constantly verifying the function. Note
       // that this is very expensive and should only be used for debugging.
       Instruction *I0 = isa<Instruction>(Bndl[0])
@@ -495,10 +494,9 @@ Value *BundleVec::emitVectors() {
 
 bool BundleVec::tryVectorize(BndlRef<Value *> Bndl,
                              LegalityAnalysis &Legality) {
-  const VectorizeOptions &Opts = VectorizeOptions::Global;
   Change = false;
-  if (LLVM_UNLIKELY(InvocationCnt++ >= Opts.sbvec_stop_at &&
-                    Opts.sbvec_stop_at != SBVecStopDisabled))
+  if (LLVM_UNLIKELY(InvocationCnt++ >= Opts->sbvec_stop_at &&
+                    Opts->sbvec_stop_at != SBVecStopDisabled))
     return false;
   Legality.clear();
   Actions.clear();
@@ -513,6 +511,7 @@ bool BundleVec::tryVectorize(BndlRef<Value *> Bndl,
 }
 
 bool BundleVec::runOnRegion(Region &Rgn, const Analyses &A) {
+  Opts = &Rgn.getContext().getLibraryOptions().get<VectorizeOptions>();
   const auto &SeedSlice = Rgn.getAux();
   if (SeedSlice.size() < 2)
     return false;
diff --git 
a/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/LoadStoreVec.cpp 
b/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/LoadStoreVec.cpp
index d4898ff577471a..f5f4c12c7ad0d5 100644
--- a/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/LoadStoreVec.cpp
+++ b/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/LoadStoreVec.cpp
@@ -55,7 +55,9 @@ bool LoadStoreVec::acceptOrRevert() {
   LLVM_DEBUG(dbgs() << DEBUG_PREFIX_LOCAL << "CostGain=" << CostGain
                     << " (After=" << CostAfter << " Before=" << CostBefore
                     << ")\n");
-  if (CostGain > VectorizeOptions::Global.sbvec_cost_threshold) {
+  const VectorizeOptions &Opts =
+      Ctx->getLibraryOptions().get<VectorizeOptions>();
+  if (CostGain > Opts.sbvec_cost_threshold) {
     LLVM_DEBUG(dbgs() << DEBUG_PREFIX_LOCAL << "Not profitable, reverting.\n");
     Ctx->revert();
     return false;
diff --git 
a/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/SeedCollection.cpp 
b/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/SeedCollection.cpp
index 27fd775ee20777..97822a64e4b1c5 100644
--- a/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/SeedCollection.cpp
+++ b/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/SeedCollection.cpp
@@ -30,7 +30,8 @@ SeedCollection::SeedCollection(StringRef Pipeline, StringRef 
AuxArg)
 }
 
 bool SeedCollection::runOnFunction(Function &F, const Analyses &A) {
-  const VectorizeOptions &Opts = VectorizeOptions::Global;
+  const VectorizeOptions &Opts =
+      F.getContext().getLibraryOptions().get<VectorizeOptions>();
   bool Change = false;
   const auto &DL = F.getParent()->getDataLayout();
 
diff --git 
a/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/TransactionAcceptOrRevert.cpp
 
b/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/TransactionAcceptOrRevert.cpp
index b4157cbcf232b7..394b511605eb15 100644
--- 
a/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/TransactionAcceptOrRevert.cpp
+++ 
b/llvm/lib/Transforms/Vectorize/SandboxVectorizer/Passes/TransactionAcceptOrRevert.cpp
@@ -17,7 +17,8 @@ namespace llvm {
 namespace sandboxir {
 
 bool TransactionAcceptOrRevert::runOnRegion(Region &Rgn, const Analyses &A) {
-  const VectorizeOptions &Opts = VectorizeOptions::Global;
+  const VectorizeOptions &Opts =
+      Rgn.getContext().getLibraryOptions().get<VectorizeOptions>();
   const auto &SB = cast<RegionWithScore>(Rgn).getScoreboard();
   [[maybe_unused]] auto CostBefore = SB.getBeforeCost();
   [[maybe_unused]] auto CostAfter = SB.getAfterCost();
diff --git 
a/llvm/lib/Transforms/Vectorize/SandboxVectorizer/SandboxVectorizer.cpp 
b/llvm/lib/Transforms/Vectorize/SandboxVectorizer/SandboxVectorizer.cpp
index 5d3369cae02565..7310a39421b07b 100644
--- a/llvm/lib/Transforms/Vectorize/SandboxVectorizer/SandboxVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SandboxVectorizer/SandboxVectorizer.cpp
@@ -83,7 +83,7 @@ bool SandboxVectorizerPass::allowFile(const std::string 
&SrcFilePath) {
 }
 
 bool SandboxVectorizerPass::runImpl(Function &LLVMF) {
-  Opts = &VectorizeOptions::Global;
+  Opts = &LLVMF.getContext().getLibraryOptions().get<VectorizeOptions>();
   if (Ctx == nullptr) {
     Ctx = std::make_unique<sandboxir::Context>(LLVMF.getContext());
     buildPipeline();
diff --git a/llvm/lib/Transforms/Vectorize/SandboxVectorizer/SeedCollector.cpp 
b/llvm/lib/Transforms/Vectorize/SandboxVectorizer/SeedCollector.cpp
index 48e7a7fa99002d..ccab5e105bf0f2 100644
--- a/llvm/lib/Transforms/Vectorize/SandboxVectorizer/SeedCollector.cpp
+++ b/llvm/lib/Transforms/Vectorize/SandboxVectorizer/SeedCollector.cpp
@@ -98,6 +98,12 @@ bool SeedContainer::erase(Instruction *I) {
   return true;
 }
 
+SeedContainer::SeedContainer(ScalarEvolution &SE) : SE(SE) {
+  const VectorizeOptions &Opts =
+      SE.getContext().getLibraryOptions().get<VectorizeOptions>();
+  BundleSizeLimit = Opts.sbvec_seed_bundle_size_limit;
+}
+
 template <typename LoadOrStoreT>
 void SeedContainer::insert(LoadOrStoreT *LSI, bool AllowDiffTypes) {
   // Find the bundle containing seeds for this symbol and type-of-access.
@@ -105,9 +111,7 @@ void SeedContainer::insert(LoadOrStoreT *LSI, bool 
AllowDiffTypes) {
   // Fill this vector of bundles front to back so that only the last bundle in
   // the vector may have available space. This avoids iteration to find one 
with
   // space.
-  if (BundleVec.empty() ||
-      BundleVec.back()->size() ==
-          VectorizeOptions::Global.sbvec_seed_bundle_size_limit)
+  if (BundleVec.empty() || BundleVec.back()->size() == BundleSizeLimit)
     BundleVec.emplace_back(std::make_unique<MemSeedBundle<LoadOrStoreT>>(LSI));
   else
     BundleVec.back()->tryInsert(LSI, SE);
@@ -175,6 +179,8 @@ SeedCollector::SeedCollector(BasicBlock *BB, 
ScalarEvolution &SE,
   });
 
   // Actually collect the seeds.
+  const VectorizeOptions &Opts =
+      Ctx.getLibraryOptions().get<VectorizeOptions>();
   for (auto &I : *BB) {
     if (StoreInst *SI = dyn_cast<StoreInst>(&I))
       if (CollectStores && isValidMemSeed(SI))
@@ -183,7 +189,7 @@ SeedCollector::SeedCollector(BasicBlock *BB, 
ScalarEvolution &SE,
       if (CollectLoads && isValidMemSeed(LI))
         LoadSeeds.insert(LI, AllowDiffTypes);
     // Cap compilation time.
-    if (totalNumSeedGroups() > 
VectorizeOptions::Global.sbvec_seed_groups_limit)
+    if (totalNumSeedGroups() > Opts.sbvec_seed_groups_limit)
       break;
   }
 }
diff --git a/llvm/lib/Transforms/Vectorize/SandboxVectorizer/VecUtils.cpp 
b/llvm/lib/Transforms/Vectorize/SandboxVectorizer/VecUtils.cpp
index 4299bbac1bf11a..7592749d1fc09d 100644
--- a/llvm/lib/Transforms/Vectorize/SandboxVectorizer/VecUtils.cpp
+++ b/llvm/lib/Transforms/Vectorize/SandboxVectorizer/VecUtils.cpp
@@ -69,13 +69,14 @@ VecUtils::getNextUserBundles(ArrayRef<Value *> Bndl, const 
InstrMaps &IMaps,
 
   Value *V0 = Bndl[0];
   DenseSet<User *> SeenUsers;
+  const VectorizeOptions &Opts =
+      V0->getContext().getLibraryOptions().get<VectorizeOptions>();
   // For each user U0 of lane 0, try to form a bundle of matching users across
   // all lanes. Cap the number of users considered to bound compilation time,
   // since each one may trigger an O(Bndl.size()) search across the other
   // lanes' users.
   for (User *U0 : V0->users()) {
-    if (SeenUsers.size() >=
-        VectorizeOptions::Global.sbvec_max_users_to_consider)
+    if (SeenUsers.size() >= Opts.sbvec_max_users_to_consider)
       break;
     if (!SeenUsers.insert(U0).second)
       continue;
diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp 
b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
index e042eb1063761c..3f34669819e342 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
@@ -49,20 +49,24 @@ using namespace VPlanPatternMatch;
 using namespace SCEVPatternMatch;
 
 void VPlanTransforms::verifyIfRequested(VPlan &Plan) {
-  if (VectorizeOptions::Global.vplan_verify_each && !verifyVPlanIsValid(Plan))
+  const VectorizeOptions &Opts =
+      Plan.getContext().getLibraryOptions().get<VectorizeOptions>();
+  if (Opts.vplan_verify_each && !verifyVPlanIsValid(Plan))
     report_fatal_error("Broken VPlan found, compilation aborted!");
 }
 
 #ifndef NDEBUG
-bool VPlanTransforms::isPrintingRequested() {
-  const VectorizeOptions &Opts = VectorizeOptions::Global;
+bool VPlanTransforms::isPrintingRequested(const VPlan &Plan) {
+  const VectorizeOptions &Opts =
+      Plan.getContext().getLibraryOptions().get<VectorizeOptions>();
   return Opts.vplan_print_before_all || Opts.vplan_print_after_all ||
          !Opts.vplan_print_before.empty() || !Opts.vplan_print_after.empty();
 }
 
 void VPlanTransforms::printIfRequested(VPlan &Plan, Function *Fn,
                                        StringRef NumberedPassName, bool After) 
{
-  const VectorizeOptions &Opts = VectorizeOptions::Global;
+  const VectorizeOptions &Opts =
+      Plan.getContext().getLibraryOptions().get<VectorizeOptions>();
   bool All = After ? Opts.vplan_print_after_all : Opts.vplan_print_before_all;
   ArrayRef<StringRef> Passes =
       After ? Opts.vplan_print_after : Opts.vplan_print_before;
diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.h 
b/llvm/lib/Transforms/Vectorize/VPlanTransforms.h
index 927ffb76486ea1..9144b2e1a8fda9 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.h
@@ -41,7 +41,7 @@ struct VPlanTransforms {
   LLVM_ABI_FOR_TEST static void verifyIfRequested(VPlan &Plan);
 
 #ifndef NDEBUG
-  LLVM_ABI_FOR_TEST static bool isPrintingRequested();
+  LLVM_ABI_FOR_TEST static bool isPrintingRequested(const VPlan &Plan);
 
   /// Print \p Plan of \p Fn before or after the pass \p NumberedPassName if
   /// requested.
@@ -65,7 +65,7 @@ struct VPlanTransforms {
     unsigned Instance;
     std::string NumberedPassName;
 
-    if (isPrintingRequested()) {
+    if (isPrintingRequested(Plan)) {
       Instance = ++PassCounter[{Fn, PassName}];
 
       NumberedPassName = Instance == 1
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp 
b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index 6a75a0f3d66ee8..e7927ea6b5198e 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -7252,8 +7252,9 @@ PreservedAnalyses VectorCombinePass::run(Function &F,
   const DataLayout *DL = &F.getDataLayout();
   TTI::TargetCostKind CostKind =
       F.hasOptSize() ? TTI::TCK_CodeSize : TTI::TCK_RecipThroughput;
-  VectorCombine Combiner(VectorizeOptions::Global, F, TTI, DT, AA, AC, DL,
-                         CostKind, TryEarlyFoldsOnly);
+  VectorCombine Combiner(
+      F.getContext().getLibraryOptions().get<VectorizeOptions>(), F, TTI, DT,
+      AA, AC, DL, CostKind, TryEarlyFoldsOnly);
   if (!Combiner.run())
     return PreservedAnalyses::all();
   PreservedAnalyses PA;
diff --git a/llvm/lib/Transforms/Vectorize/VectorizeOptions.td 
b/llvm/lib/Transforms/Vectorize/VectorizeOptions.td
index e3bb555306f387..9a43436ee057a3 100644
--- a/llvm/lib/Transforms/Vectorize/VectorizeOptions.td
+++ b/llvm/lib/Transforms/Vectorize/VectorizeOptions.td
@@ -8,7 +8,7 @@
 
 include "llvm/Option/LibraryOptions.td"
 
-def VectorizeOptions : OptionsStruct;
+def VectorizeOptions : OptionsStruct<perContext = true>;
 
 // LoopIdiomVectorize.cpp
 defm : BoolField<"disable-loop-idiom-vectorize-all", "false",
diff --git a/llvm/unittests/Option/LibraryOptionsTest.cpp 
b/llvm/unittests/Option/LibraryOptionsTest.cpp
index 0120f105353323..c360f73270379f 100644
--- a/llvm/unittests/Option/LibraryOptionsTest.cpp
+++ b/llvm/unittests/Option/LibraryOptionsTest.cpp
@@ -208,30 +208,28 @@ TEST(LibraryOptionsTest, Values) {
   opt::RegisterLibraryOptions<TestLibraryOptions> Registration;
   cl::opt<bool> NotLibrary("lib-cl-opt");
   cl::LibraryOptionsContext V;
-  auto Set = [&](std::initializer_list<const char *> Args) {
-    return toString(V.set(Args));
+  auto Assign = [&](std::initializer_list<const char *> Args) {
+    return toString(V.assign(Args));
   };
+  EXPECT_EQ(Assign({"-lib-list=9"}), "");
   std::string Path = "-lib-path=q";
-  EXPECT_EQ(Set({"-lib-count", "7", Path.c_str(), "--lib-list=1,2"}), "");
+  EXPECT_EQ(Assign({"-lib-count", "7", Path.c_str(), "--lib-list=1,2"}), "");
   Path.assign(Path.size(), ' ');
   EXPECT_EQ(TestLibraryOptions::Global.count, 3u);
-
-  // A later call keeps the earlier values.
-  EXPECT_EQ(Set({"-lib-list=3"}), "");
-  EXPECT_THAT(V.get<TestLibraryOptions>().list,
-              testing::ElementsAre(1u, 2u, 3u));
+  // The earlier call's values are discarded.
+  EXPECT_THAT(V.get<TestLibraryOptions>().list, testing::ElementsAre(1u, 2u));
 
   // Another context replays the arguments, which outlive the caller's.
   cl::LibraryOptionsContext Copy;
-  EXPECT_EQ(toString(Copy.set(V.args())), "");
+  EXPECT_EQ(toString(Copy.assign(V.args())), "");
   const TestLibraryOptions &C = Copy.get<TestLibraryOptions>();
   EXPECT_EQ(C.count, 7u);
   EXPECT_EQ(C.Path, "q");
-  EXPECT_THAT(C.list, testing::ElementsAre(1u, 2u, 3u));
+  EXPECT_THAT(C.list, testing::ElementsAre(1u, 2u));
 
-  EXPECT_EQ(Set({"-lib-count=x"}), "invalid value 'x' in '-lib-count=x'");
-  EXPECT_EQ(Set({"-lib-cl-opt"}), "'-lib-cl-opt' is not a library option");
-  EXPECT_EQ(Set({"lib-count=1"}), "'lib-count=1' is not a library option");
+  EXPECT_EQ(Assign({"-lib-count=x"}), "invalid value 'x' in '-lib-count=x'");
+  EXPECT_EQ(Assign({"-lib-cl-opt"}), "'-lib-cl-opt' is not a library option");
+  EXPECT_EQ(Assign({"lib-count=1"}), "'lib-count=1' is not a library option");
   cl::ResetCommandLineParser();
 }
 
diff --git a/llvm/unittests/Transforms/Vectorize/VPlanVerifierTest.cpp 
b/llvm/unittests/Transforms/Vectorize/VPlanVerifierTest.cpp
index d1d971285eb781..2100f558b24c35 100644
--- a/llvm/unittests/Transforms/Vectorize/VPlanVerifierTest.cpp
+++ b/llvm/unittests/Transforms/Vectorize/VPlanVerifierTest.cpp
@@ -13,7 +13,6 @@
 #include "VPlanTestBase.h"
 #include "llvm/IR/Instruction.h"
 #include "llvm/IR/Instructions.h"
-#include "llvm/Support/SaveAndRestore.h"
 #include "gtest/gtest.h"
 
 using namespace llvm;
@@ -485,8 +484,7 @@ TEST_F(VPVerifierTest, testRUN_VPLAN_PASS) {
   VPBB1->appendRecipe(UseI);
   VPBB1->appendRecipe(DefI);
 
-  llvm::SaveAndRestore<bool> RestoreVerifyEachVPlan(
-      VectorizeOptions::Global.vplan_verify_each, true);
+  cantFail(C.getLibraryOptions().assign({"-vplan-verify-each"}));
   auto NopPass = [](VPlan &Plan) {};
   EXPECT_DEATH(
       { VPlanTransforms::runPass("simplifyRecipes", NopPass, Plan); },
diff --git a/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp 
b/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
index 8df952eb8664e0..08b94a68c4e985 100644
--- a/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/OptionParserEmitter.cpp
@@ -351,12 +351,16 @@ static void emitOptionsStruct(const Record &Struct,
   OS << "#include \"llvm/ADT/ArrayRef.h\"\n";
   OS << "#include \"llvm/ADT/BoolOrDefault.h\"\n";
   OS << "#include \"llvm/ADT/StringRef.h\"\n";
-  OS << "#include \"llvm/Support/Allocator.h\"\n\n";
+  OS << "#include \"llvm/Support/Allocator.h\"\n";
+  bool PerContext = Struct.getValueAsBit("PerContext");
+  if (PerContext)
+    OS << "#include \"llvm/Support/LibraryOptionsContext.h\"\n";
+  OS << "\n";
   StringRef Namespace = Struct.getValueAsString("Namespace");
-  OS << "namespace llvm {\nnamespace cl {\nclass LibraryOptionsContext;\n"
-        "} // namespace cl\nnamespace opt {\nclass Arg;\n"
-        "class LibraryOptionsParser;\nclass OptTable;\n} // namespace opt\n"
-        "} // namespace llvm\n\n";
+  OS << "namespace llvm {\nnamespace opt {\nclass Arg;\n";
+  if (PerContext)
+    OS << "class LibraryOptionsParser;\n";
+  OS << "class OptTable;\n} // namespace opt\n} // namespace llvm\n\n";
   OS << "namespace " << Namespace << " {\n";
   OS << "struct " << Name << " {\n";
   if (Namespace != "llvm")
@@ -365,7 +369,6 @@ static void emitOptionsStruct(const Record &Struct,
     ConditionScope CS(OS, *R);
     OS << "  " << M.Type << " " << M.Name << "{" << M.Default << "};\n";
   }
-  bool PerContext = Struct.getValueAsBit("PerContext");
   OS << "\n  static constexpr bool PerContext = "
      << (PerContext ? "true" : "false") << ";\n";
   OS << "  static const llvm::opt::OptTable &optTable();\n";

>From c383f9b28650ba338f20f47bab85fff04145b209 Mon Sep 17 00:00:00 2001
From: Fangrui Song <[email protected]>
Date: Tue, 13 Oct 2026 23:29:07 -0700
Subject: [PATCH 3/4] simplify assign()

---
 clang/lib/CIR/FrontendAction/CIRGenAction.cpp |  2 +-
 clang/lib/CodeGen/CodeGenAction.cpp           |  3 +-
 llvm/include/llvm/Support/CommandLine.h       |  6 ++--
 .../llvm/Support/LibraryOptionsContext.h      | 12 ++++----
 llvm/lib/Support/CommandLine.cpp              | 28 ++++++++-----------
 .../IR/LibraryOptionsContextTest.cpp          |  8 ++----
 llvm/unittests/Option/LibraryOptionsTest.cpp  | 28 +++++++++----------
 .../Vectorize/VPlanVerifierTest.cpp           |  6 ++--
 8 files changed, 42 insertions(+), 51 deletions(-)

diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp 
b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
index 54a6b5336662bb..442562a4fffff0 100644
--- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
+++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
@@ -368,7 +368,7 @@ CIRGenAction::CIRGenAction(OutputType Act, 
mlir::MLIRContext *MLIRCtx)
 CIRGenAction::~CIRGenAction() { MLIRMod.release(); }
 
 bool CIRGenAction::BeginSourceFileAction(CompilerInstance &CI) {
-  cantFail(Ctx->getLibraryOptions().assign(CI.getLibraryOptions().args()));
+  Ctx->getLibraryOptions().assign(CI.getLibraryOptions());
   if (clang::loadLinkModules(CI, *Ctx, LinkModules))
     return false;
   return ASTFrontendAction::BeginSourceFileAction(CI);
diff --git a/clang/lib/CodeGen/CodeGenAction.cpp 
b/clang/lib/CodeGen/CodeGenAction.cpp
index cd5047659eef7a..772bb8872517b0 100644
--- a/clang/lib/CodeGen/CodeGenAction.cpp
+++ b/clang/lib/CodeGen/CodeGenAction.cpp
@@ -367,8 +367,7 @@ CodeGenerator *CodeGenAction::getCodeGenerator() const {
 }
 
 bool CodeGenAction::BeginSourceFileAction(CompilerInstance &CI) {
-  cantFail(
-      VMContext->getLibraryOptions().assign(CI.getLibraryOptions().args()));
+  VMContext->getLibraryOptions().assign(CI.getLibraryOptions());
   if (CI.getFrontendOpts().GenReducedBMI)
     CI.getLangOpts().setCompilingModule(LangOptions::CMK_ModuleInterface);
   return ASTFrontendAction::BeginSourceFileAction(CI);
diff --git a/llvm/include/llvm/Support/CommandLine.h 
b/llvm/include/llvm/Support/CommandLine.h
index 1b3e3745ce7246..f51cd4879eafdb 100644
--- a/llvm/include/llvm/Support/CommandLine.h
+++ b/llvm/include/llvm/Support/CommandLine.h
@@ -72,9 +72,9 @@ LLVM_ABI bool ParseCommandLineOptions(int argc, const char 
*const *argv,
                                       vfs::FileSystem *VFS = nullptr,
                                       const char *EnvVar = nullptr);
 
-// Like ParseCommandLineOptions, but sets the library registered options
-// as per-context in LibraryOptions rather than in their global().
-LLVM_ABI bool parseCommandLineOptions(LibraryOptionsContext &LibraryOptions,
+// Like ParseCommandLineOptions, but sets per-context libraries' options in
+// Context instead of their global().
+LLVM_ABI bool parseCommandLineOptions(LibraryOptionsContext &Context,
                                       ArrayRef<const char *> Args,
                                       StringRef Overview = "",
                                       raw_ostream *Errs = nullptr,
diff --git a/llvm/include/llvm/Support/LibraryOptionsContext.h 
b/llvm/include/llvm/Support/LibraryOptionsContext.h
index f9b74ea181ef07..16312dc4ba32b1 100644
--- a/llvm/include/llvm/Support/LibraryOptionsContext.h
+++ b/llvm/include/llvm/Support/LibraryOptionsContext.h
@@ -27,21 +27,19 @@ class LibraryOptions;
 // Libraries declared with perContext read their options here.
 class LLVM_ABI LibraryOptionsContext {
 public:
-  // Replaces the library options with those that Args spell, e.g. {"-x=v"}.
-  // Fails at the first argument that is not a library option or has an invalid
-  // value.
+  // Replaces the library options with Other's by replaying its arguments.
   //
   // A library's values start as a copy of its global(), which may refer to
   // strings that ResetAllOptionOccurrences frees.
   // Indexing libraries registered since the last parse is not thread-safe.
-  Error assign(ArrayRef<const char *> Args);
+  void assign(const LibraryOptionsContext &Other);
 
   // Parses the option of L that Args[0] spells, keeping the other values.
   Error parse(LibraryOptions &L, ArrayRef<const char *> Args,
               unsigned &Consumed);
 
-  // The arguments set so far, for replaying into another context.
-  ArrayRef<const char *> args() const { return Args; }
+  // The arguments assigned so far.
+  ArrayRef<const char *> args() const { return SavedArgs; }
 
   // T's values, or T::Global if there are none.
   template <typename T> const T &get() const {
@@ -54,7 +52,7 @@ class LLVM_ABI LibraryOptionsContext {
 
 private:
   DenseMap<const void *, void *> Values;
-  SmallVector<const char *, 0> Args;
+  SmallVector<const char *, 0> SavedArgs;
   BumpPtrAllocator Alloc;
 };
 
diff --git a/llvm/lib/Support/CommandLine.cpp b/llvm/lib/Support/CommandLine.cpp
index 6e304b28da6c49..12cef93255e485 100644
--- a/llvm/lib/Support/CommandLine.cpp
+++ b/llvm/lib/Support/CommandLine.cpp
@@ -1480,11 +1480,11 @@ bool cl::ParseCommandLineOptions(int argc, const char 
*const *argv,
                                      Errs, VFS, EnvVar);
 }
 
-bool cl::parseCommandLineOptions(LibraryOptionsContext &LibraryOptions,
+bool cl::parseCommandLineOptions(LibraryOptionsContext &Context,
                                  ArrayRef<const char *> Args,
                                  StringRef Overview, raw_ostream *Errs,
                                  vfs::FileSystem *VFS, const char *EnvVar) {
-  return parseCommandLineOptionsImpl(&LibraryOptions, Args, Overview, Errs, 
VFS,
+  return parseCommandLineOptionsImpl(&Context, Args, Overview, Errs, VFS,
                                      EnvVar);
 }
 
@@ -2943,25 +2943,21 @@ void cl::addLibraryOptions(LibraryOptions &L) {
   globalParser().Libraries.push_back(&L);
 }
 
-Error LibraryOptionsContext::assign(ArrayRef<const char *> Args) {
+void LibraryOptionsContext::assign(const LibraryOptionsContext &Other) {
+  assert(this != &Other);
   Values.clear();
-  this->Args.clear();
+  SavedArgs.clear();
+  ArrayRef<const char *> Args = Other.SavedArgs;
   for (size_t I = 0, E = Args.size(); I != E;) {
     StringRef Name = Args[I];
-    LibraryOptions *L = nullptr;
-    if (Name.consume_front("-")) {
-      Name.consume_front("-");
-      L = globalParser().lookupLibraryOption(Name.split('=').first);
-    }
-    if (!L)
-      return createStringError("'" + Twine(Args[I]) +
-                               "' is not a library option");
+    Name.consume_front("-");
+    Name.consume_front("-");
+    LibraryOptions *L =
+        globalParser().lookupLibraryOption(Name.split('=').first);
     unsigned N;
-    if (Error Err = parse(*L, Args.drop_front(I), N))
-      return Err;
+    cantFail(parse(*L, Args.drop_front(I), N));
     I += N;
   }
-  return Error::success();
 }
 
 Error LibraryOptionsContext::parse(LibraryOptions &L,
@@ -2970,7 +2966,7 @@ Error LibraryOptionsContext::parse(LibraryOptions &L,
   auto [It, Inserted] = Values.try_emplace(L.global());
   if (Inserted)
     It->second = L.copyGlobal(Alloc);
-  return parseLibraryOption(L, It->second, Args, Consumed, Alloc, &this->Args);
+  return parseLibraryOption(L, It->second, Args, Consumed, Alloc, &SavedArgs);
 }
 
 void cl::ResetAllOptionOccurrences() {
diff --git a/llvm/unittests/IR/LibraryOptionsContextTest.cpp 
b/llvm/unittests/IR/LibraryOptionsContextTest.cpp
index 2588bdcda41cb7..87fd1a2378b3b0 100644
--- a/llvm/unittests/IR/LibraryOptionsContextTest.cpp
+++ b/llvm/unittests/IR/LibraryOptionsContextTest.cpp
@@ -17,11 +17,9 @@
 
 using namespace llvm;
 
-namespace {
-
 // InstCombine sinks %a into its only user's block unless
 // -instcombine-code-sinking=0.
-const char *IR = R"(
+const char IR[] = R"(
 define i32 @f(i1 %c, i32 %x) {
 entry:
   %a = add i32 %x, 1
@@ -34,7 +32,7 @@ define i32 @f(i1 %c, i32 %x) {
 )";
 
 // Runs InstCombine on IR in Ctx and returns the block that holds %a.
-std::string runInstCombine(LLVMContext &Ctx) {
+static std::string runInstCombine(LLVMContext &Ctx) {
   SMDiagnostic Err;
   std::unique_ptr<Module> M = parseAssemblyString(IR, Err, Ctx);
   if (!M)
@@ -65,5 +63,3 @@ TEST(LibraryOptionsContextTest, InstCombine) {
   EXPECT_EQ(runInstCombine(C1), "t");
   EXPECT_EQ(runInstCombine(C2), "entry");
 }
-
-} // namespace
diff --git a/llvm/unittests/Option/LibraryOptionsTest.cpp 
b/llvm/unittests/Option/LibraryOptionsTest.cpp
index c360f73270379f..558a78c2c390bb 100644
--- a/llvm/unittests/Option/LibraryOptionsTest.cpp
+++ b/llvm/unittests/Option/LibraryOptionsTest.cpp
@@ -206,30 +206,30 @@ TEST(LibraryOptionsTest, ParseIntoGlobal) {
 TEST(LibraryOptionsTest, Values) {
   cl::ResetCommandLineParser();
   opt::RegisterLibraryOptions<TestLibraryOptions> Registration;
-  cl::opt<bool> NotLibrary("lib-cl-opt");
-  cl::LibraryOptionsContext V;
-  auto Assign = [&](std::initializer_list<const char *> Args) {
-    return toString(V.assign(Args));
+  opt::LibraryOptionsParser L{std::in_place_type<TestLibraryOptions>};
+  auto Parse = [&](cl::LibraryOptionsContext &V, ArrayRef<const char *> Args) {
+    unsigned N;
+    return toString(V.parse(L, Args, N));
   };
-  EXPECT_EQ(Assign({"-lib-list=9"}), "");
+  cl::LibraryOptionsContext V;
   std::string Path = "-lib-path=q";
-  EXPECT_EQ(Assign({"-lib-count", "7", Path.c_str(), "--lib-list=1,2"}), "");
+  EXPECT_EQ(Parse(V, {"-lib-count", "7"}), "");
+  EXPECT_EQ(Parse(V, Path.c_str()), "");
+  EXPECT_EQ(Parse(V, "--lib-list=1,2"), "");
   Path.assign(Path.size(), ' ');
   EXPECT_EQ(TestLibraryOptions::Global.count, 3u);
-  // The earlier call's values are discarded.
-  EXPECT_THAT(V.get<TestLibraryOptions>().list, testing::ElementsAre(1u, 2u));
+  EXPECT_EQ(Parse(V, "-lib-count=x"), "invalid value 'x' in '-lib-count=x'");
 
-  // Another context replays the arguments, which outlive the caller's.
+  // assign() replays the arguments, which outlive the caller's, and discards
+  // the earlier values.
   cl::LibraryOptionsContext Copy;
-  EXPECT_EQ(toString(Copy.assign(V.args())), "");
+  EXPECT_EQ(Parse(Copy, "-lib-list=9"), "");
+  Copy.assign(V);
   const TestLibraryOptions &C = Copy.get<TestLibraryOptions>();
   EXPECT_EQ(C.count, 7u);
   EXPECT_EQ(C.Path, "q");
   EXPECT_THAT(C.list, testing::ElementsAre(1u, 2u));
-
-  EXPECT_EQ(Assign({"-lib-count=x"}), "invalid value 'x' in '-lib-count=x'");
-  EXPECT_EQ(Assign({"-lib-cl-opt"}), "'-lib-cl-opt' is not a library option");
-  EXPECT_EQ(Assign({"lib-count=1"}), "'lib-count=1' is not a library option");
+  EXPECT_EQ(Copy.args().size(), V.args().size());
   cl::ResetCommandLineParser();
 }
 
diff --git a/llvm/unittests/Transforms/Vectorize/VPlanVerifierTest.cpp 
b/llvm/unittests/Transforms/Vectorize/VPlanVerifierTest.cpp
index 2100f558b24c35..6eed6c30538198 100644
--- a/llvm/unittests/Transforms/Vectorize/VPlanVerifierTest.cpp
+++ b/llvm/unittests/Transforms/Vectorize/VPlanVerifierTest.cpp
@@ -9,10 +9,10 @@
 #include "../lib/Transforms/Vectorize/VPlanVerifier.h"
 #include "../lib/Transforms/Vectorize/LoopVectorizationPlanner.h"
 #include "../lib/Transforms/Vectorize/VPlan.h"
-#include "../lib/Transforms/Vectorize/VectorizeOptions.h"
 #include "VPlanTestBase.h"
 #include "llvm/IR/Instruction.h"
 #include "llvm/IR/Instructions.h"
+#include "llvm/Support/CommandLine.h"
 #include "gtest/gtest.h"
 
 using namespace llvm;
@@ -484,7 +484,9 @@ TEST_F(VPVerifierTest, testRUN_VPLAN_PASS) {
   VPBB1->appendRecipe(UseI);
   VPBB1->appendRecipe(DefI);
 
-  cantFail(C.getLibraryOptions().assign({"-vplan-verify-each"}));
+  const char *Args[] = {"prog", "-vplan-verify-each"};
+  ASSERT_TRUE(
+      cl::parseCommandLineOptions(C.getLibraryOptions(), Args, "", &nulls()));
   auto NopPass = [](VPlan &Plan) {};
   EXPECT_DEATH(
       { VPlanTransforms::runPass("simplifyRecipes", NopPass, Plan); },

>From 9eada30679fd2647e618f4e167c351d04d5e4d74 Mon Sep 17 00:00:00 2001
From: Fangrui Song <[email protected]>
Date: Wed, 14 Oct 2026 10:53:27 -0700
Subject: [PATCH 4/4] fix bazel build

---
 clang/unittests/Frontend/CMakeLists.txt                      | 1 +
 llvm/utils/gn/secondary/clang/unittests/Frontend/BUILD.gn    | 1 +
 utils/bazel/llvm-project-overlay/clang/unittests/BUILD.bazel | 1 +
 3 files changed, 3 insertions(+)

diff --git a/clang/unittests/Frontend/CMakeLists.txt 
b/clang/unittests/Frontend/CMakeLists.txt
index 0d9fc714212d50..7fca6348ae9da3 100644
--- a/clang/unittests/Frontend/CMakeLists.txt
+++ b/clang/unittests/Frontend/CMakeLists.txt
@@ -24,6 +24,7 @@ add_clang_unittest(FrontendTests
   clangSerialization
   clangTooling
   LLVM_COMPONENTS
+  Core
   Support
   TargetParser
   )
diff --git a/llvm/utils/gn/secondary/clang/unittests/Frontend/BUILD.gn 
b/llvm/utils/gn/secondary/clang/unittests/Frontend/BUILD.gn
index 6f1e5c8c593ca3..c7244ea9f15158 100644
--- a/llvm/utils/gn/secondary/clang/unittests/Frontend/BUILD.gn
+++ b/llvm/utils/gn/secondary/clang/unittests/Frontend/BUILD.gn
@@ -11,6 +11,7 @@ unittest("FrontendTests") {
     "//clang/lib/Lex",
     "//clang/lib/Sema",
     "//clang/lib/Tooling",
+    "//llvm/lib/IR",
     "//llvm/lib/Support",
     "//llvm/lib/Target:AllTargetsDescs",
     "//llvm/lib/TargetParser",
diff --git a/utils/bazel/llvm-project-overlay/clang/unittests/BUILD.bazel 
b/utils/bazel/llvm-project-overlay/clang/unittests/BUILD.bazel
index 1996fa21d7e4eb..e5a749e160f02d 100644
--- a/utils/bazel/llvm-project-overlay/clang/unittests/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/clang/unittests/BUILD.bazel
@@ -310,6 +310,7 @@ cc_test(
         "//clang:serialization",
         "//clang:static_analyzer_core_options",
         "//clang:tooling",
+        "//llvm:Core",
         "//llvm:Option",
         "//llvm:Support",
         "//llvm:TargetParser",

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to