https://github.com/MaskRay created
https://github.com/llvm/llvm-project/pull/231407
`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
>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] [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 b87dd037480d6..bceb85f6e923b 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 71a7a17146af3..a04aa51e163af 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 1abd8b0455a19..22925cc55f495 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 b09c9a9f5a5be..f122208d6b4c8 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 0000000000000..4f584b4058a81
--- /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 0000000000000..a4938f811537c
--- /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 ced9b9227538e..be0d27bacf3e4 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 8b5c34525fa7c..19df88f77e586 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 d901f8027f702..ab027c056831e 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 bc7059244461e..1b3e3745ce724 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 0000000000000..150a8c1d5a771
--- /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 1fcdd3bc4c142..fb3b4bd8b206f 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 74a73664473b0..d02d57e4c0197 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 cfcb82273eb6d..e5848a0498f23 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 1cb5fccb07a1b..31f07e114cace 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 9fc098796f9e4..d2f850f5972ef 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 6986fb324bfee..7158dec4277b4 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 068e227328c16..da87ef049c7d4 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 df83992fadd1e..ad94dcc32a880 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 0000000000000..2588bdcda41cb
--- /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 dff1d179845b7..0120f10535332 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 ccf45962ba04f..ae9e0969d1540 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 c4b23c046e718..8df952eb8664e 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 547d82187b87f..21d7a82a713b4 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 89c25df2d1d92..05e3760c588cf 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",
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits