diff --git a/llvm/docs/CommandGuide/llvm-link.rst b/llvm/docs/CommandGuide/llvm-link.rst index 1cc1376becf9d..dd06cf4a99e5a 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 + + 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 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 ac8041d8df1af..5556e863c1588 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 4fe1f1a0f5183..8195e68539e65 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 0000000000000..a0b85a4563dc4 --- /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 0000000000000..eac9c3b548afc --- /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 a476b50a1ed90..33d39e6baf8ef 100644 --- a/llvm/tools/llvm-link/llvm-link.cpp +++ b/llvm/tools/llvm-link/llvm-link.cpp @@ -53,6 +53,12 @@ static cl::list OverridingInputs( "input bitcode file which can override previously defined symbol(s)"), cl::cat(LinkCategory)); +static cl::list 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 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 &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 182ce73178c1d..25adf74db8c1d 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 DestM(getExternal(Ctx, "foo")); + std::unique_ptr 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;