[llvm] [llvm-link] Add the `--ignore-if-conflict` option (PR #75696)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Dec 16 02:47:22 PST 2023
https://github.com/DianQK created https://github.com/llvm/llvm-project/pull/75696
Adding the `--ignore-if-conflict` option supports the following usage case.
The user code (user.ll):
```llvm
define void __addsf3() { ... }
define void foo() { ... }
```
The builtin function provided by the compiler (builtin.ll):
```llvm
define void __addsf3() { ... }
```
I want to allow user to override builtin functions under LTO. I can't use `--override`, which might cause the `foo` symbol to accidentally override a symbol of the same name.
>From ff2a4f2de05db73af7e90eb0204536707cf884fd Mon Sep 17 00:00:00 2001
From: DianQK <dianqk at dianqk.net>
Date: Wed, 13 Dec 2023 08:17:53 +0800
Subject: [PATCH] [llvm-link] Add the `--ignore-if-conflict` option
---
llvm/docs/CommandGuide/llvm-link.rst | 8 ++++++++
llvm/include/llvm/Linker/Linker.h | 1 +
llvm/lib/Linker/LinkModules.cpp | 8 ++++++++
llvm/test/tools/llvm-link/Inputs/f_1.ll | 9 +++++++++
llvm/test/tools/llvm-link/ignore.test | 8 ++++++++
llvm/tools/llvm-link/llvm-link.cpp | 14 +++++++++++++-
llvm/unittests/Linker/LinkModulesTest.cpp | 10 ++++++++++
7 files changed, 57 insertions(+), 1 deletion(-)
create mode 100644 llvm/test/tools/llvm-link/Inputs/f_1.ll
create mode 100644 llvm/test/tools/llvm-link/ignore.test
diff --git a/llvm/docs/CommandGuide/llvm-link.rst b/llvm/docs/CommandGuide/llvm-link.rst
index 1cc1376becf9df..dd06cf4a99e5aa 100644
--- a/llvm/docs/CommandGuide/llvm-link.rst
+++ b/llvm/docs/CommandGuide/llvm-link.rst
@@ -57,6 +57,14 @@ OPTIONS
a symbol is declared more than twice, the definition from the file declared
last takes precedence.
+.. option:: --ignore-if-conflict <filename>
+
+ Adds the passed-in file to the link and ignores symbols that have already
+ been declared with the definitions in the file that is passed in. This flag
+ can be specified multiple times to have multiple files act as ignores. If
+ a symbol is declared more than twice, the definition from the file declared
+ first takes precedence.
+
.. option:: --import <function:filename>
Specify a function that should be imported from the specified file for
diff --git a/llvm/include/llvm/Linker/Linker.h b/llvm/include/llvm/Linker/Linker.h
index ac8041d8df1afa..5556e863c15884 100644
--- a/llvm/include/llvm/Linker/Linker.h
+++ b/llvm/include/llvm/Linker/Linker.h
@@ -27,6 +27,7 @@ class Linker {
None = 0,
OverrideFromSrc = (1 << 0),
LinkOnlyNeeded = (1 << 1),
+ IgnoreFromSrcIfConflict = (1 << 2),
};
Linker(Module &M);
diff --git a/llvm/lib/Linker/LinkModules.cpp b/llvm/lib/Linker/LinkModules.cpp
index 4fe1f1a0f51833..8195e68539e652 100644
--- a/llvm/lib/Linker/LinkModules.cpp
+++ b/llvm/lib/Linker/LinkModules.cpp
@@ -52,6 +52,9 @@ class ModuleLinker {
bool shouldOverrideFromSrc() { return Flags & Linker::OverrideFromSrc; }
bool shouldLinkOnlyNeeded() { return Flags & Linker::LinkOnlyNeeded; }
+ bool shouldIgnoreFromSrcIfConflict() {
+ return Flags & Linker::IgnoreFromSrcIfConflict;
+ }
bool shouldLinkFromSource(bool &LinkFromSrc, const GlobalValue &Dest,
const GlobalValue &Src);
@@ -317,6 +320,11 @@ bool ModuleLinker::shouldLinkFromSource(bool &LinkFromSrc,
return false;
}
+ if (shouldIgnoreFromSrcIfConflict()) {
+ LinkFromSrc = false;
+ return false;
+ }
+
assert(!Src.hasExternalWeakLinkage());
assert(!Dest.hasExternalWeakLinkage());
assert(Dest.hasExternalLinkage() && Src.hasExternalLinkage() &&
diff --git a/llvm/test/tools/llvm-link/Inputs/f_1.ll b/llvm/test/tools/llvm-link/Inputs/f_1.ll
new file mode 100644
index 00000000000000..a0b85a4563dc49
--- /dev/null
+++ b/llvm/test/tools/llvm-link/Inputs/f_1.ll
@@ -0,0 +1,9 @@
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+
+define void @f() {
+entry:
+ call void @f_1();
+ ret void
+}
+
+declare void @f_1();
diff --git a/llvm/test/tools/llvm-link/ignore.test b/llvm/test/tools/llvm-link/ignore.test
new file mode 100644
index 00000000000000..eac9c3b548afc5
--- /dev/null
+++ b/llvm/test/tools/llvm-link/ignore.test
@@ -0,0 +1,8 @@
+# RUN: not llvm-link %S/Inputs/f.ll %S/Inputs/f_1.ll 2>&1 | FileCheck %s
+# RUN: llvm-link %S/Inputs/f.ll --ignore-if-conflict %S/Inputs/f_1.ll -S | FileCheck -check-prefix=IGNORE %s
+
+# CHECK: error: Linking globals named 'f': symbol multiply defined!
+
+# IGNORE: define void @f() {
+# IGNORE-NEXT: entry:
+# IGNORE-NEXT: ret void
diff --git a/llvm/tools/llvm-link/llvm-link.cpp b/llvm/tools/llvm-link/llvm-link.cpp
index a476b50a1ed90b..33d39e6baf8eff 100644
--- a/llvm/tools/llvm-link/llvm-link.cpp
+++ b/llvm/tools/llvm-link/llvm-link.cpp
@@ -53,6 +53,12 @@ static cl::list<std::string> OverridingInputs(
"input bitcode file which can override previously defined symbol(s)"),
cl::cat(LinkCategory));
+static cl::list<std::string> IgnoreIfConflictInputs(
+ "ignore-if-conflict", cl::value_desc("filename"),
+ cl::desc("defined symbol(s) of input bitcode file which can be ignore if "
+ "previously same defined symbol(s)"),
+ cl::cat(LinkCategory));
+
// Option to simulate function importing for testing. This enables using
// llvm-link to simulate ThinLTO backend processes.
static cl::list<std::string> Imports(
@@ -382,7 +388,8 @@ static bool importFunctions(const char *argv0, Module &DestModule) {
static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L,
const cl::list<std::string> &Files, unsigned Flags) {
// Filter out flags that don't apply to the first file we load.
- unsigned ApplicableFlags = Flags & Linker::Flags::OverrideFromSrc;
+ unsigned ApplicableFlags = Flags & (Linker::Flags::OverrideFromSrc |
+ Linker::Flags::IgnoreFromSrcIfConflict);
// Similar to some flags, internalization doesn't apply to the first file.
bool InternalizeLinkedSymbols = false;
for (const auto &File : Files) {
@@ -488,6 +495,11 @@ int main(int argc, char **argv) {
Flags | Linker::Flags::OverrideFromSrc))
return 1;
+ // Next the -ignore-if-conflict ones.
+ if (!linkFiles(argv[0], Context, L, IgnoreIfConflictInputs,
+ Flags | Linker::Flags::IgnoreFromSrcIfConflict))
+ return 1;
+
// Import any functions requested via -import
if (!importFunctions(argv[0], *Composite))
return 1;
diff --git a/llvm/unittests/Linker/LinkModulesTest.cpp b/llvm/unittests/Linker/LinkModulesTest.cpp
index 182ce73178c1d4..25adf74db8c1db 100644
--- a/llvm/unittests/Linker/LinkModulesTest.cpp
+++ b/llvm/unittests/Linker/LinkModulesTest.cpp
@@ -233,6 +233,16 @@ TEST_F(LinkModuleTest, NewCAPIFailure) {
EXPECT_EQ("Linking globals named 'foo': symbol multiply defined!", Err);
}
+TEST_F(LinkModuleTest, IgnoreFromSrcIfConflict) {
+ LLVMContext Ctx;
+ std::unique_ptr<Module> DestM(getExternal(Ctx, "foo"));
+ std::unique_ptr<Module> SourceM(getExternal(Ctx, "foo"));
+ Ctx.setDiagnosticHandlerCallBack(expectNoDiags);
+ bool Failed = Linker::linkModules(*DestM, std::move(SourceM),
+ Linker::Flags::IgnoreFromSrcIfConflict);
+ ASSERT_FALSE(Failed);
+}
+
TEST_F(LinkModuleTest, MoveDistinctMDs) {
LLVMContext C;
SMDiagnostic Err;
More information about the llvm-commits
mailing list