[clang] [lld] [llvm] Reland "[LTO][LLD] Prevent invalid LTO libfunc transforms (#164916)" (PR #190642)
Daniel Thornburgh via llvm-commits
llvm-commits at lists.llvm.org
Mon Apr 6 11:41:42 PDT 2026
https://github.com/mysterymath created https://github.com/llvm/llvm-project/pull/190642
This reverts commit 1ec7e86b3a779df2a0af3f37e58c8f5b3a398d7f after issue #190072 was fixed.
>From 73f4aeb49f6125a081f73f459471797e70b1150e Mon Sep 17 00:00:00 2001
From: Daniel Thornburgh <dthorn at google.com>
Date: Mon, 6 Apr 2026 11:20:46 -0700
Subject: [PATCH] Reland "[LTO][LLD] Prevent invalid LTO libfunc transforms
(#164916)"
This reverts commit 1ec7e86b3a779df2a0af3f37e58c8f5b3a398d7f after issue
#190072 was fixed.
---
clang/lib/CodeGen/BackendUtil.cpp | 12 ++--
lld/COFF/InputFiles.cpp | 5 +-
lld/COFF/LTO.cpp | 4 ++
lld/COFF/LTO.h | 1 +
lld/COFF/SymbolTable.cpp | 24 +++++++
lld/ELF/Driver.cpp | 19 +++++
lld/ELF/LTO.cpp | 4 ++
lld/ELF/LTO.h | 1 +
.../COFF/lto-libcall-archive-bitcode.test | 51 +++++++++++++
lld/test/ELF/lto/libcall-archive-bitcode.test | 54 ++++++++++++++
lld/test/wasm/lto/libcall-archive-bitcode.ll | 56 +++++++++++++++
lld/wasm/LTO.cpp | 4 ++
lld/wasm/LTO.h | 1 +
lld/wasm/SymbolTable.cpp | 23 ++++++
llvm/docs/ReleaseNotes.md | 7 ++
llvm/include/llvm/LTO/LTO.h | 29 ++++++--
llvm/include/llvm/LTO/LTOBackend.h | 22 +++---
llvm/lib/LTO/LTO.cpp | 71 ++++++++++++++-----
llvm/lib/LTO/LTOBackend.cpp | 34 +++++++--
llvm/lib/LTO/LTOCodeGenerator.cpp | 4 +-
llvm/lib/LTO/ThinLTOCodeGenerator.cpp | 4 +-
.../LTO/Resolution/X86/libcall-external.ll | 25 +++++++
.../Resolution/X86/libcall-in-thin-link.ll | 35 +++++++++
llvm/test/LTO/Resolution/X86/libcall-in-tu.ll | 34 +++++++++
llvm/tools/llvm-lto2/llvm-lto2.cpp | 16 +++++
25 files changed, 488 insertions(+), 52 deletions(-)
create mode 100644 lld/test/COFF/lto-libcall-archive-bitcode.test
create mode 100644 lld/test/ELF/lto/libcall-archive-bitcode.test
create mode 100644 lld/test/wasm/lto/libcall-archive-bitcode.ll
create mode 100644 llvm/test/LTO/Resolution/X86/libcall-external.ll
create mode 100644 llvm/test/LTO/Resolution/X86/libcall-in-thin-link.ll
create mode 100644 llvm/test/LTO/Resolution/X86/libcall-in-tu.ll
diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 5b8b4083c2ac0..74c8bf1c670da 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -1410,12 +1410,14 @@ runThinLTOBackend(CompilerInstance &CI, ModuleSummaryIndex *CombinedIndex,
// FIXME: Both ExecuteAction and thinBackend set up optimization remarks for
// the same context.
+ // FIXME: This does not yet set the list of bitcode libfuncs that it isn't
+ // safe to call. This precludes bitcode libc in distributed ThinLTO.
finalizeLLVMOptimizationRemarks(M->getContext());
- if (Error E =
- thinBackend(Conf, -1, AddStream, *M, *CombinedIndex, ImportList,
- ModuleToDefinedGVSummaries[M->getModuleIdentifier()],
- /*ModuleMap=*/nullptr, Conf.CodeGenOnly,
- /*IRAddStream=*/nullptr, CGOpts.CmdArgs)) {
+ if (Error E = thinBackend(
+ Conf, -1, AddStream, *M, *CombinedIndex, ImportList,
+ ModuleToDefinedGVSummaries[M->getModuleIdentifier()],
+ /*ModuleMap=*/nullptr, Conf.CodeGenOnly, /*BitcodeLibFuncs=*/{},
+ /*IRAddStream=*/nullptr, CGOpts.CmdArgs)) {
handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) {
errs() << "Error running ThinLTO backend: " << EIB.message() << '\n';
});
diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp
index e797da6effb35..9040d9653d2b2 100644
--- a/lld/COFF/InputFiles.cpp
+++ b/lld/COFF/InputFiles.cpp
@@ -25,7 +25,6 @@
#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/IR/Mangler.h"
-#include "llvm/IR/RuntimeLibcalls.h"
#include "llvm/LTO/LTO.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/COFF.h"
@@ -1394,8 +1393,6 @@ void BitcodeFile::parse() {
// FIXME: Check nodeduplicate
comdat[i] =
symtab.addComdat(this, saver.save(obj->getComdatTable()[i].first));
- Triple tt(obj->getTargetTriple());
- RTLIB::RuntimeLibcallsInfo libcalls(tt);
for (const lto::InputFile::Symbol &objSym : obj->symbols()) {
StringRef symName = saver.save(objSym.getName());
int comdatIndex = objSym.getComdatIndex();
@@ -1445,7 +1442,7 @@ void BitcodeFile::parse() {
symtab.addRegular(this, symName, nullptr, fakeSC, 0, objSym.isWeak());
}
symbols.push_back(sym);
- if (objSym.isUsed() || objSym.isLibcall(libcalls))
+ if (objSym.isUsed())
symtab.ctx.config.gcroot.push_back(sym);
}
directives = saver.save(obj->getCOFFLinkerOpts());
diff --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp
index 2d38a13830dad..445ea52e995da 100644
--- a/lld/COFF/LTO.cpp
+++ b/lld/COFF/LTO.cpp
@@ -288,3 +288,7 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
return ret;
}
+
+void BitcodeCompiler::setBitcodeLibFuncs(ArrayRef<StringRef> bitcodeLibFuncs) {
+ ltoObj->setBitcodeLibFuncs(bitcodeLibFuncs);
+}
diff --git a/lld/COFF/LTO.h b/lld/COFF/LTO.h
index 6826251b5ffa7..73e855e567b09 100644
--- a/lld/COFF/LTO.h
+++ b/lld/COFF/LTO.h
@@ -45,6 +45,7 @@ class BitcodeCompiler {
void add(BitcodeFile &f);
std::vector<InputFile *> compile();
+ void setBitcodeLibFuncs(ArrayRef<StringRef> bitcodeLibFuncs);
private:
std::unique_ptr<llvm::lto::LTO> ltoObj;
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index 38a43390c15ab..fef9bc54fd8a6 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -1437,13 +1437,37 @@ void SymbolTable::compileBitcodeFiles() {
if (bitcodeFileInstances.empty())
return;
+ // Collect the bitcode library functions that are not safe to call because
+ // they were not yet brought in the link. (Such symbols are lazy.)
+ llvm::BumpPtrAllocator alloc;
+ llvm::StringSaver saver(alloc);
+ SmallVector<StringRef> bitcodeLibFuncs;
+ // Triple must be captured before the bitcode is moved into the compiler.
+ // Note that the below assumes that the set of possible libfuncs is roughly
+ // equivalent for all bitcode translation units.
+ llvm::Triple tt =
+ llvm::Triple(bitcodeFileInstances.front()->obj->getTargetTriple());
+ for (StringRef libFunc : lto::LTO::getLibFuncSymbols(tt, saver)) {
+ if (Symbol *sym = find(libFunc)) {
+ if (auto *l = dyn_cast<LazyArchive>(sym)) {
+ if (isBitcode(l->getMemberBuffer()))
+ bitcodeLibFuncs.push_back(libFunc);
+ } else if (auto *o = dyn_cast<LazyObject>(sym)) {
+ if (isBitcode(o->file->mb))
+ bitcodeLibFuncs.push_back(libFunc);
+ }
+ }
+ }
+
ScopedTimer t(ctx.ltoTimer);
lto.reset(new BitcodeCompiler(ctx));
+ lto->setBitcodeLibFuncs(bitcodeLibFuncs);
{
llvm::TimeTraceScope addScope("Add bitcode file instances");
for (BitcodeFile *f : bitcodeFileInstances)
lto->add(*f);
}
+
for (InputFile *newObj : lto->compile()) {
ObjFile *obj = cast<ObjFile>(newObj);
obj->parse();
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 949314fd70ed2..09aa6af5ee2c7 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -2774,8 +2774,27 @@ static void markBuffersAsDontNeed(Ctx &ctx, bool skipLinkedOutput) {
template <class ELFT>
void LinkerDriver::compileBitcodeFiles(bool skipLinkedOutput) {
llvm::TimeTraceScope timeScope("LTO");
+
+ // Collect the bitcode library functions that are not safe to call because
+ // they were not yet brought in the link. (Such symbols are lazy.)
+ llvm::BumpPtrAllocator alloc;
+ llvm::StringSaver saver(alloc);
+ SmallVector<StringRef> bitcodeLibFuncs;
+ if (!ctx.bitcodeFiles.empty()) {
+ // Triple must be captured before the bitcode is moved into the compiler.
+ // Note that the below assumes that the set of possible libfuncs is roughly
+ // equivalent for all bitcode translation units.
+ llvm::Triple tt =
+ llvm::Triple(ctx.bitcodeFiles.front()->obj->getTargetTriple());
+ for (StringRef libFunc : lto::LTO::getLibFuncSymbols(tt, saver))
+ if (Symbol *sym = ctx.symtab->find(libFunc);
+ sym && sym->isLazy() && isa<BitcodeFile>(sym->file))
+ bitcodeLibFuncs.push_back(libFunc);
+ }
+
// Compile bitcode files and replace bitcode symbols.
lto.reset(new BitcodeCompiler(ctx));
+ lto->setBitcodeLibFuncs(bitcodeLibFuncs);
for (BitcodeFile *file : ctx.bitcodeFiles)
lto->add(*file);
diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index 352848af1551d..7b0fe2001439e 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -436,3 +436,7 @@ SmallVector<std::unique_ptr<InputFile>, 0> BitcodeCompiler::compile() {
}
return ret;
}
+
+void BitcodeCompiler::setBitcodeLibFuncs(ArrayRef<StringRef> bitcodeLibFuncs) {
+ ltoObj->setBitcodeLibFuncs(bitcodeLibFuncs);
+}
diff --git a/lld/ELF/LTO.h b/lld/ELF/LTO.h
index acf3bcff7f2f1..c8cb2156d90ca 100644
--- a/lld/ELF/LTO.h
+++ b/lld/ELF/LTO.h
@@ -43,6 +43,7 @@ class BitcodeCompiler {
void add(BitcodeFile &f);
SmallVector<std::unique_ptr<InputFile>, 0> compile();
+ void setBitcodeLibFuncs(ArrayRef<StringRef> bitcodeLibFuncs);
private:
Ctx &ctx;
diff --git a/lld/test/COFF/lto-libcall-archive-bitcode.test b/lld/test/COFF/lto-libcall-archive-bitcode.test
new file mode 100644
index 0000000000000..bd172f1ff3955
--- /dev/null
+++ b/lld/test/COFF/lto-libcall-archive-bitcode.test
@@ -0,0 +1,51 @@
+; REQUIRES: x86
+
+; RUN: rm -rf %t && split-file %s %t && cd %t
+; RUN: llvm-as main.ll -o main.obj
+; RUN: llvm-as puts.ll -o puts.obj
+; RUN: llvm-mc -filetype=obj -triple=x86_64-pc-windows-msvc printf.s -o printf.obj
+; RUN: llvm-ar rcs libc.lib puts.obj printf.obj
+
+;; Ensure that no printf->puts translation occurs during LTO because puts is in
+;; bitcode, but was not brought into the link. This would fail the link by
+;; extracting bitcode after LTO.
+; RUN: lld-link -out:out.exe -entry:main -subsystem:console -lldmap:- -nodefaultlib main.obj libc.lib | FileCheck %s
+
+;; Test the same behavior with lazy objects.
+; RUN: lld-link -out:out-lazy.exe -entry:main -subsystem:console -lldmap:- -nodefaultlib main.obj /start-lib puts.obj /end-lib printf.obj | FileCheck %s
+
+;; Test that translation DOES occur when puts is extracted and brought into the link.
+; RUN: lld-link -out:out-extracted.exe -entry:main -subsystem:console -lldmap:- -nodefaultlib main.obj puts.obj printf.obj | FileCheck %s --check-prefix=EXTRACTED
+
+; CHECK-NOT: puts
+; CHECK: printf
+
+; EXTRACTED: printf
+; EXTRACTED: puts
+
+;--- puts.ll
+target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc"
+
+define i32 @puts(ptr nocapture readonly %0) noinline {
+ call void asm sideeffect "", ""()
+ ret i32 0
+}
+
+;--- printf.s
+.globl printf
+printf:
+ ret
+
+;--- main.ll
+target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc"
+
+ at str = constant [5 x i8] c"foo\0A\00"
+
+define i32 @main() {
+ %call = call i32 (ptr, ...) @printf(ptr @str)
+ ret i32 0
+}
+
+declare i32 @printf(ptr, ...)
diff --git a/lld/test/ELF/lto/libcall-archive-bitcode.test b/lld/test/ELF/lto/libcall-archive-bitcode.test
new file mode 100644
index 0000000000000..00726a2e6aeb5
--- /dev/null
+++ b/lld/test/ELF/lto/libcall-archive-bitcode.test
@@ -0,0 +1,54 @@
+; REQUIRES: x86
+
+; RUN: rm -rf %t && split-file %s %t && cd %t
+; RUN: llvm-as main.ll -o main.o
+; RUN: llvm-as puts.ll -o puts.o
+; RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux-gnu printf.s -o printf.o
+; RUN: llvm-ar rcs libc.a puts.o printf.o
+
+;; Ensure that no printf->puts translation occurs during LTO because puts is in
+;; bitcode, but was not brought into the link. This would fail the link by
+;; extracting bitcode after LTO.
+; RUN: ld.lld -o out main.o libc.a
+; RUN: llvm-nm out | FileCheck %s
+
+;; Test the same behavior with lazy objects.
+; RUN: ld.lld -o out-lazy main.o --start-lib puts.o --end-lib printf.o
+; RUN: llvm-nm out-lazy | FileCheck %s
+
+;; Test that translation DOES occur when puts is extracted and brought into the link.
+; RUN: ld.lld -o out-extracted main.o puts.o printf.o
+; RUN: llvm-nm out-extracted | FileCheck %s --check-prefix=EXTRACTED
+
+; CHECK-NOT: puts
+; CHECK: printf
+
+; EXTRACTED: printf
+; EXTRACTED: puts
+
+;--- puts.ll
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define i32 @puts(ptr nocapture readonly %0) noinline {
+ call void asm sideeffect "", ""()
+ ret i32 0
+}
+
+;--- printf.s
+.globl printf
+printf:
+ ret
+
+;--- main.ll
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+ at str = constant [5 x i8] c"foo\0A\00"
+
+define i32 @_start() {
+ %call = call i32 (ptr, ...) @printf(ptr @str)
+ ret i32 0
+}
+
+declare i32 @printf(ptr, ...)
diff --git a/lld/test/wasm/lto/libcall-archive-bitcode.ll b/lld/test/wasm/lto/libcall-archive-bitcode.ll
new file mode 100644
index 0000000000000..3b640efc1091f
--- /dev/null
+++ b/lld/test/wasm/lto/libcall-archive-bitcode.ll
@@ -0,0 +1,56 @@
+; REQUIRES: x86
+
+; RUN: rm -rf %t && split-file %s %t && cd %t
+; RUN: llvm-as main.ll -o main.o
+; RUN: llvm-as puts.ll -o puts.o
+; RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown printf.s -o printf.o
+; RUN: llvm-ar rcs libc.a puts.o printf.o
+
+;; Ensure that no printf->puts translation occurs during LTO because puts is in
+;; bitcode, but was not brought into the link. This would fail the link by
+;; extracting bitcode after LTO.
+; RUN: wasm-ld -o out.wasm main.o libc.a
+; RUN: obj2yaml out.wasm | FileCheck %s
+
+;; Test the same behavior with lazy objects.
+; RUN: wasm-ld -o out-lazy.wasm main.o --start-lib puts.o --end-lib printf.o
+; RUN: obj2yaml out-lazy.wasm | FileCheck %s
+
+;; Test that translation DOES occur when puts is extracted and brought into the link.
+; RUN: wasm-ld -o out-extracted.wasm main.o puts.o printf.o
+; RUN: obj2yaml out-extracted.wasm | FileCheck %s --check-prefix=EXTRACTED
+
+; CHECK-NOT: Name: puts
+; CHECK: Name: printf
+
+; EXTRACTED: Name: puts
+; EXTRACTED-NOT: Name: printf
+
+;--- puts.ll
+target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
+target triple = "wasm32-unknown-unknown"
+
+define i32 @puts(ptr nocapture readonly %0) noinline {
+ call void asm sideeffect "", ""()
+ ret i32 0
+}
+
+;--- printf.s
+.globl printf
+printf:
+ .functype printf (i32, i32) -> (i32)
+ i32.const 0
+ end_function
+
+;--- main.ll
+target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
+target triple = "wasm32-unknown-unknown"
+
+ at str = constant [5 x i8] c"foo\0A\00"
+
+define i32 @_start() {
+ %call = call i32 (ptr, ...) @printf(ptr @str)
+ ret i32 0
+}
+
+declare i32 @printf(ptr, ...)
\ No newline at end of file
diff --git a/lld/wasm/LTO.cpp b/lld/wasm/LTO.cpp
index 9fcb95b84b545..2c068bf518905 100644
--- a/lld/wasm/LTO.cpp
+++ b/lld/wasm/LTO.cpp
@@ -195,6 +195,10 @@ static void thinLTOCreateEmptyIndexFiles() {
}
}
+void BitcodeCompiler::setBitcodeLibFuncs(ArrayRef<StringRef> bitcodeLibFuncs) {
+ ltoObj->setBitcodeLibFuncs(bitcodeLibFuncs);
+}
+
// Merge all the bitcode files we have seen, codegen the result
// and return the resulting objects.
SmallVector<InputFile *, 0> BitcodeCompiler::compile() {
diff --git a/lld/wasm/LTO.h b/lld/wasm/LTO.h
index 21b1d59024663..6fa110d6099bf 100644
--- a/lld/wasm/LTO.h
+++ b/lld/wasm/LTO.h
@@ -46,6 +46,7 @@ class BitcodeCompiler {
void add(BitcodeFile &f);
SmallVector<InputFile *, 0> compile();
+ void setBitcodeLibFuncs(ArrayRef<StringRef> bitcodeLibFuncs);
private:
std::unique_ptr<llvm::lto::LTO> ltoObj;
diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp
index 05e653ea23eda..93d3e5f71a3e5 100644
--- a/lld/wasm/SymbolTable.cpp
+++ b/lld/wasm/SymbolTable.cpp
@@ -82,8 +82,31 @@ void SymbolTable::compileBitcodeFiles() {
// Prevent further LTO objects being included
BitcodeFile::doneLTO = true;
+ // Collect the bitcode library functions that are not safe to call because
+ // they were not yet brought in the link. (Such symbols are lazy.)
+ llvm::BumpPtrAllocator alloc;
+ llvm::StringSaver saver(alloc);
+ SmallVector<StringRef> bitcodeLibFuncs;
+ if (!ctx.bitcodeFiles.empty()) {
+ // Triple must be captured before the bitcode is moved into the compiler.
+ // Note that the below assumes that the set of possible libfuncs is
+ // equivalent for all bitcode translation units.
+ llvm::Triple tt =
+ llvm::Triple(ctx.bitcodeFiles.front()->obj->getTargetTriple());
+ for (StringRef libFunc : llvm::lto::LTO::getLibFuncSymbols(tt, saver)) {
+ if (Symbol *sym = find(libFunc)) {
+ if (auto *lazy = dyn_cast<LazySymbol>(sym)) {
+ if (isa<BitcodeFile>(lazy->getFile()))
+ bitcodeLibFuncs.push_back(libFunc);
+ }
+ }
+ }
+ }
+
// Compile bitcode files and replace bitcode symbols.
lto.reset(new BitcodeCompiler);
+ lto->setBitcodeLibFuncs(bitcodeLibFuncs);
+
for (BitcodeFile *f : ctx.bitcodeFiles)
lto->add(*f);
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index d0d8b9f4ddabc..701b386dba73e 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -101,6 +101,13 @@ Changes to LLVM infrastructure
this may fail if symlink permissions are not available.
* Added ``readlink``, which reads the target of a symbolic link.
+* Bitcode libraries can now implement compiler-managed library functions
+ (libcalls) without causing incorrect API manipulation or undefined references
+ ([#177046](https://github.com/llvm/llvm-project/pull/125687)). Note that
+ there are still issues with invalid compiler reasoning about some functions
+ in bitcode, e.g. `malloc`. Not yet supported on MachO or when using
+ distributed ThinLTO.
+
Changes to building LLVM
------------------------
diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h
index 3fec06b8bd78d..aba2661e81c47 100644
--- a/llvm/include/llvm/LTO/LTO.h
+++ b/llvm/include/llvm/LTO/LTO.h
@@ -179,7 +179,8 @@ class InputFile {
// may emit references to. Such symbols must be considered external, as
// removing them or modifying their interfaces would invalidate the code
// generator's knowledge about them.
- bool isLibcall(const RTLIB::RuntimeLibcallsInfo &Libcalls) const;
+ bool isLibcall(const TargetLibraryInfo &TLI,
+ const RTLIB::RuntimeLibcallsInfo &Libcalls) const;
};
/// A range over the symbols in this InputFile.
@@ -308,7 +309,8 @@ class ThinBackendProc {
using ThinBackendFunction = std::function<std::unique_ptr<ThinBackendProc>(
const Config &C, ModuleSummaryIndex &CombinedIndex,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
- AddStreamFn AddStream, FileCache Cache)>;
+ AddStreamFn AddStream, FileCache Cache,
+ ArrayRef<StringRef> BitcodeLibFuncs)>;
/// This type defines the behavior following the thin-link phase during ThinLTO.
/// It encapsulates a backend function and a strategy for thread pool
@@ -323,10 +325,11 @@ struct ThinBackend {
std::unique_ptr<ThinBackendProc> operator()(
const Config &Conf, ModuleSummaryIndex &CombinedIndex,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
- AddStreamFn AddStream, FileCache Cache) {
+ AddStreamFn AddStream, FileCache Cache,
+ ArrayRef<StringRef> BitcodeLibFuncs) {
assert(isValid() && "Invalid backend function");
return Func(Conf, CombinedIndex, ModuleToDefinedGVSummaries,
- std::move(AddStream), std::move(Cache));
+ std::move(AddStream), std::move(Cache), BitcodeLibFuncs);
}
ThreadPoolStrategy getParallelism() const { return Parallelism; }
bool isValid() const { return static_cast<bool>(Func); }
@@ -446,6 +449,11 @@ class LTO {
LLVM_ABI Error add(std::unique_ptr<InputFile> Obj,
ArrayRef<SymbolResolution> Res);
+ /// Set the list of functions implemented in bitcode that were not extracted
+ /// from an archive. Such functions may not be referenced, as they have
+ /// lost their opportunity to be defined.
+ LLVM_ABI void setBitcodeLibFuncs(ArrayRef<StringRef> BitcodeLibFuncs);
+
/// Returns an upper bound on the number of tasks that the client may expect.
/// This may only be called after all IR object files have been added. For a
/// full description of tasks see LTOBackend.h.
@@ -466,6 +474,14 @@ class LTO {
LLVM_ABI static SmallVector<const char *>
getRuntimeLibcallSymbols(const Triple &TT);
+ /// Static method that returns a list of library function symbols that can be
+ /// generated by LTO but might not be visible from bitcode symbol table.
+ /// Unlike the runtime libcalls, the linker can report to the code generator
+ /// which of these are actually available in the link, and the code generator
+ /// can then only reference that set of symbols.
+ LLVM_ABI static SmallVector<StringRef>
+ getLibFuncSymbols(const Triple &TT, llvm::StringSaver &Saver);
+
protected:
// Called at the start of run().
virtual Error serializeInputsForDistribution() { return Error::success(); }
@@ -657,6 +673,11 @@ class LTO {
// Setup optimization remarks according to the provided configuration.
Error setupOptimizationRemarks();
+ // LibFuncs that were implemented in bitcode but were not extracted
+ // from their libraries. Such functions cannot safely be called, since
+ // they have lost their opportunity to be defined.
+ SmallVector<StringRef> BitcodeLibFuncs;
+
public:
/// Helper to emit an optimization remark during the LTO link when outside of
/// the standard optimization pass pipeline.
diff --git a/llvm/include/llvm/LTO/LTOBackend.h b/llvm/include/llvm/LTO/LTOBackend.h
index 48ad5aa64f61f..4bb38529ec754 100644
--- a/llvm/include/llvm/LTO/LTOBackend.h
+++ b/llvm/include/llvm/LTO/LTOBackend.h
@@ -39,13 +39,15 @@ LLVM_ABI bool opt(const Config &Conf, TargetMachine *TM, unsigned Task,
Module &Mod, bool IsThinLTO,
ModuleSummaryIndex *ExportSummary,
const ModuleSummaryIndex *ImportSummary,
- const std::vector<uint8_t> &CmdArgs);
+ const std::vector<uint8_t> &CmdArgs,
+ ArrayRef<StringRef> BitcodeLibFuncs);
/// Runs a regular LTO backend. The regular LTO backend can also act as the
/// regular LTO phase of ThinLTO, which may need to access the combined index.
LLVM_ABI Error backend(const Config &C, AddStreamFn AddStream,
unsigned ParallelCodeGenParallelismLevel, Module &M,
- ModuleSummaryIndex &CombinedIndex);
+ ModuleSummaryIndex &CombinedIndex,
+ ArrayRef<StringRef> BitcodeLibFuncs);
/// Runs a ThinLTO backend.
/// If \p ModuleMap is not nullptr, all the module files to be imported have
@@ -56,14 +58,14 @@ LLVM_ABI Error backend(const Config &C, AddStreamFn AddStream,
/// the backend will skip optimization and only perform code generation. If
/// \p IRAddStream is not nullptr, it will be called just before code generation
/// to serialize the optimized IR.
-LLVM_ABI Error
-thinBackend(const Config &C, unsigned Task, AddStreamFn AddStream, Module &M,
- const ModuleSummaryIndex &CombinedIndex,
- const FunctionImporter::ImportMapTy &ImportList,
- const GVSummaryMapTy &DefinedGlobals,
- MapVector<StringRef, BitcodeModule> *ModuleMap, bool CodeGenOnly,
- AddStreamFn IRAddStream = nullptr,
- const std::vector<uint8_t> &CmdArgs = std::vector<uint8_t>());
+LLVM_ABI Error thinBackend(
+ const Config &C, unsigned Task, AddStreamFn AddStream, Module &M,
+ const ModuleSummaryIndex &CombinedIndex,
+ const FunctionImporter::ImportMapTy &ImportList,
+ const GVSummaryMapTy &DefinedGlobals,
+ MapVector<StringRef, BitcodeModule> *ModuleMap, bool CodeGenOnly,
+ ArrayRef<StringRef> BitcodeLibFuncs, AddStreamFn IRAddStream = nullptr,
+ const std::vector<uint8_t> &CmdArgs = std::vector<uint8_t>());
LLVM_ABI Error finalizeOptimizationRemarks(LLVMRemarkFileHandle DiagOutputFile);
diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index 1a6976eea8088..95faf3484c456 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -653,7 +653,11 @@ Expected<std::unique_ptr<InputFile>> InputFile::create(MemoryBufferRef Object) {
}
bool InputFile::Symbol::isLibcall(
+ const TargetLibraryInfo &TLI,
const RTLIB::RuntimeLibcallsInfo &Libcalls) const {
+ LibFunc F;
+ if (TLI.getLibFunc(IRName, F) && TLI.has(F))
+ return true;
return Libcalls.getSupportedLibcallImpl(IRName) != RTLIB::Unsupported;
}
@@ -715,6 +719,8 @@ void LTO::addModuleToGlobalRes(ArrayRef<InputFile::Symbol> Syms,
auto *ResE = Res.end();
(void)ResE;
RTLIB::RuntimeLibcallsInfo Libcalls(TT);
+ TargetLibraryInfoImpl TLII(TT);
+ TargetLibraryInfo TLI(TLII);
for (const InputFile::Symbol &Sym : Syms) {
assert(ResI != ResE);
SymbolResolution Res = *ResI++;
@@ -757,7 +763,7 @@ void LTO::addModuleToGlobalRes(ArrayRef<InputFile::Symbol> Syms,
GlobalRes.VisibleOutsideSummary = true;
}
- bool IsLibcall = Sym.isLibcall(Libcalls);
+ bool IsLibcall = Sym.isLibcall(TLI, Libcalls);
// Set the partition to external if we know it is re-defined by the linker
// with -defsym or -wrap options, used elsewhere, e.g. it is visible to a
@@ -844,6 +850,12 @@ Error LTO::add(std::unique_ptr<InputFile> InputPtr,
return Error::success();
}
+void LTO::setBitcodeLibFuncs(ArrayRef<StringRef> BitcodeLibFuncs) {
+ assert(this->BitcodeLibFuncs.empty() &&
+ "bitcode libfuncs were set twice; maybe accidentally clobbered?");
+ this->BitcodeLibFuncs.append(BitcodeLibFuncs.begin(), BitcodeLibFuncs.end());
+}
+
Expected<ArrayRef<SymbolResolution>>
LTO::addModule(InputFile &Input, ArrayRef<SymbolResolution> InputRes,
unsigned ModI, ArrayRef<SymbolResolution> Res) {
@@ -1469,9 +1481,9 @@ Error LTO::runRegularLTO(AddStreamFn AddStream) {
}
if (!RegularLTO.EmptyCombinedModule || Conf.AlwaysEmitRegularLTOObj) {
- if (Error Err =
- backend(Conf, AddStream, RegularLTO.ParallelCodeGenParallelismLevel,
- *RegularLTO.CombinedModule, ThinLTO.CombinedIndex))
+ if (Error Err = backend(
+ Conf, AddStream, RegularLTO.ParallelCodeGenParallelismLevel,
+ *RegularLTO.CombinedModule, ThinLTO.CombinedIndex, BitcodeLibFuncs))
return Err;
}
@@ -1491,6 +1503,20 @@ SmallVector<const char *> LTO::getRuntimeLibcallSymbols(const Triple &TT) {
return LibcallSymbols;
}
+SmallVector<StringRef> LTO::getLibFuncSymbols(const Triple &TT,
+ StringSaver &Saver) {
+ auto TLII = std::make_unique<TargetLibraryInfoImpl>(TT);
+ TargetLibraryInfo TLI(*TLII);
+ SmallVector<StringRef> LibFuncSymbols;
+ LibFuncSymbols.reserve(LibFunc::NumLibFuncs);
+ for (unsigned I = LibFunc::Begin_LibFunc; I != LibFunc::End_LibFunc; ++I) {
+ LibFunc F = static_cast<LibFunc>(I);
+ if (TLI.has(F))
+ LibFuncSymbols.push_back(Saver.save(TLI.getName(F)).data());
+ }
+ return LibFuncSymbols;
+}
+
Error ThinBackendProc::emitFiles(
const FunctionImporter::ImportMapTy &ImportList, llvm::StringRef ModulePath,
const std::string &NewModulePath) const {
@@ -1568,6 +1594,7 @@ class InProcessThinBackend : public CGThinBackend {
// generating directly into the returned output stream.
AddStreamFn AddStream;
FileCache Cache;
+ ArrayRef<StringRef> BitcodeLibFuncs;
public:
InProcessThinBackend(
@@ -1575,11 +1602,13 @@ class InProcessThinBackend : public CGThinBackend {
ThreadPoolStrategy ThinLTOParallelism,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddStreamFn AddStream, FileCache Cache, lto::IndexWriteCallback OnWrite,
- bool ShouldEmitIndexFiles, bool ShouldEmitImportsFiles)
+ bool ShouldEmitIndexFiles, bool ShouldEmitImportsFiles,
+ ArrayRef<StringRef> BitcodeLibFuncs)
: CGThinBackend(Conf, CombinedIndex, ModuleToDefinedGVSummaries, OnWrite,
ShouldEmitIndexFiles, ShouldEmitImportsFiles,
ThinLTOParallelism),
- AddStream(std::move(AddStream)), Cache(std::move(Cache)) {}
+ AddStream(std::move(AddStream)), Cache(std::move(Cache)),
+ BitcodeLibFuncs(BitcodeLibFuncs) {}
virtual Error runThinLTOBackendThread(
AddStreamFn AddStream, FileCache Cache, unsigned Task, BitcodeModule BM,
@@ -1600,7 +1629,7 @@ class InProcessThinBackend : public CGThinBackend {
return thinBackend(Conf, Task, AddStream, **MOrErr, CombinedIndex,
ImportList, DefinedGlobals, &ModuleMap,
- Conf.CodeGenOnly);
+ Conf.CodeGenOnly, BitcodeLibFuncs);
};
if (ShouldEmitIndexFiles) {
if (auto E = emitFiles(ImportList, ModuleID, ModuleID.str()))
@@ -1685,13 +1714,14 @@ class FirstRoundThinBackend : public InProcessThinBackend {
const Config &Conf, ModuleSummaryIndex &CombinedIndex,
ThreadPoolStrategy ThinLTOParallelism,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
- AddStreamFn CGAddStream, FileCache CGCache, AddStreamFn IRAddStream,
+ AddStreamFn CGAddStream, FileCache CGCache,
+ ArrayRef<StringRef> BitcodeLibFuncs, AddStreamFn IRAddStream,
FileCache IRCache)
: InProcessThinBackend(Conf, CombinedIndex, ThinLTOParallelism,
ModuleToDefinedGVSummaries, std::move(CGAddStream),
std::move(CGCache), /*OnWrite=*/nullptr,
/*ShouldEmitIndexFiles=*/false,
- /*ShouldEmitImportsFiles=*/false),
+ /*ShouldEmitImportsFiles=*/false, BitcodeLibFuncs),
IRAddStream(std::move(IRAddStream)), IRCache(std::move(IRCache)) {}
Error runThinLTOBackendThread(
@@ -1714,7 +1744,7 @@ class FirstRoundThinBackend : public InProcessThinBackend {
return thinBackend(Conf, Task, CGAddStream, **MOrErr, CombinedIndex,
ImportList, DefinedGlobals, &ModuleMap,
- Conf.CodeGenOnly, IRAddStream);
+ Conf.CodeGenOnly, BitcodeLibFuncs, IRAddStream);
};
// Like InProcessThinBackend, we produce index files as needed for
// FirstRoundThinBackend. However, these files are not generated for
@@ -1781,6 +1811,7 @@ class SecondRoundThinBackend : public InProcessThinBackend {
ThreadPoolStrategy ThinLTOParallelism,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddStreamFn AddStream, FileCache Cache,
+ ArrayRef<StringRef> BitcodeLibFuncs,
std::unique_ptr<SmallVector<StringRef>> IRFiles,
stable_hash CombinedCGDataHash)
: InProcessThinBackend(Conf, CombinedIndex, ThinLTOParallelism,
@@ -1788,7 +1819,7 @@ class SecondRoundThinBackend : public InProcessThinBackend {
std::move(Cache),
/*OnWrite=*/nullptr,
/*ShouldEmitIndexFiles=*/false,
- /*ShouldEmitImportsFiles=*/false),
+ /*ShouldEmitImportsFiles=*/false, BitcodeLibFuncs),
IRFiles(std::move(IRFiles)), CombinedCGDataHash(CombinedCGDataHash) {}
Error runThinLTOBackendThread(
@@ -1809,7 +1840,7 @@ class SecondRoundThinBackend : public InProcessThinBackend {
return thinBackend(Conf, Task, AddStream, *LoadedModule, CombinedIndex,
ImportList, DefinedGlobals, &ModuleMap,
- /*CodeGenOnly=*/true);
+ /*CodeGenOnly=*/true, BitcodeLibFuncs);
};
if (!Cache.isValid() || !CombinedIndex.modulePaths().count(ModuleID) ||
all_of(CombinedIndex.getModuleHash(ModuleID),
@@ -1848,11 +1879,12 @@ ThinBackend lto::createInProcessThinBackend(ThreadPoolStrategy Parallelism,
auto Func =
[=](const Config &Conf, ModuleSummaryIndex &CombinedIndex,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
- AddStreamFn AddStream, FileCache Cache) {
+ AddStreamFn AddStream, FileCache Cache,
+ ArrayRef<StringRef> BitcodeLibFuncs) {
return std::make_unique<InProcessThinBackend>(
Conf, CombinedIndex, Parallelism, ModuleToDefinedGVSummaries,
AddStream, Cache, OnWrite, ShouldEmitIndexFiles,
- ShouldEmitImportsFiles);
+ ShouldEmitImportsFiles, BitcodeLibFuncs);
};
return ThinBackend(Func, Parallelism);
}
@@ -1969,7 +2001,8 @@ ThinBackend lto::createWriteIndexesThinBackend(
auto Func =
[=](const Config &Conf, ModuleSummaryIndex &CombinedIndex,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
- AddStreamFn AddStream, FileCache Cache) {
+ AddStreamFn AddStream, FileCache Cache,
+ ArrayRef<StringRef> BitcodeLibFuncs) {
return std::make_unique<WriteIndexesThinBackend>(
Conf, CombinedIndex, Parallelism, ModuleToDefinedGVSummaries,
OldPrefix, NewPrefix, NativeObjectPrefix, ShouldEmitImportsFiles,
@@ -2222,7 +2255,7 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache,
if (!CodeGenDataThinLTOTwoRounds) {
std::unique_ptr<ThinBackendProc> BackendProc =
ThinLTO.Backend(Conf, ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries,
- AddStream, Cache);
+ AddStream, Cache, BitcodeLibFuncs);
return RunBackends(BackendProc.get());
}
@@ -2245,7 +2278,7 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache,
LLVM_DEBUG(dbgs() << "[TwoRounds] Running the first round of codegen\n");
auto FirstRoundLTO = std::make_unique<FirstRoundThinBackend>(
Conf, ThinLTO.CombinedIndex, Parallelism, ModuleToDefinedGVSummaries,
- CG.AddStream, CG.Cache, IR.AddStream, IR.Cache);
+ CG.AddStream, CG.Cache, BitcodeLibFuncs, IR.AddStream, IR.Cache);
if (Error E = RunBackends(FirstRoundLTO.get()))
return E;
@@ -2261,7 +2294,7 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache,
LLVM_DEBUG(dbgs() << "[TwoRounds] Running the second round of codegen\n");
auto SecondRoundLTO = std::make_unique<SecondRoundThinBackend>(
Conf, ThinLTO.CombinedIndex, Parallelism, ModuleToDefinedGVSummaries,
- AddStream, Cache, IR.getResult(), CombinedHash);
+ AddStream, Cache, BitcodeLibFuncs, IR.getResult(), CombinedHash);
return RunBackends(SecondRoundLTO.get());
}
@@ -2768,7 +2801,7 @@ ThinBackend lto::createOutOfProcessThinBackend(
auto Func =
[=](const Config &Conf, ModuleSummaryIndex &CombinedIndex,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
- AddStreamFn, FileCache Cache) {
+ AddStreamFn, FileCache Cache, ArrayRef<StringRef> BitcodeLibFuncs) {
return std::make_unique<OutOfProcessThinBackend>(
Conf, CombinedIndex, Parallelism, ModuleToDefinedGVSummaries, Cache,
OnWrite, ShouldEmitIndexFiles, ShouldEmitImportsFiles,
diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp
index 7ee2557a68bd5..16a42e526eb8a 100644
--- a/llvm/lib/LTO/LTOBackend.cpp
+++ b/llvm/lib/LTO/LTOBackend.cpp
@@ -259,7 +259,8 @@ createTargetMachine(const Config &Conf, const Target *TheTarget, Module &M) {
static void runNewPMPasses(const Config &Conf, Module &Mod, TargetMachine *TM,
unsigned OptLevel, bool IsThinLTO,
ModuleSummaryIndex *ExportSummary,
- const ModuleSummaryIndex *ImportSummary) {
+ const ModuleSummaryIndex *ImportSummary,
+ const DenseSet<StringRef> &BitcodeLibFuncs) {
std::optional<PGOOptions> PGOOpt;
if (!Conf.SampleProfile.empty())
PGOOpt = PGOOptions(Conf.SampleProfile, "", Conf.ProfileRemapping,
@@ -301,6 +302,20 @@ static void runNewPMPasses(const Config &Conf, Module &Mod, TargetMachine *TM,
new TargetLibraryInfoImpl(TM->getTargetTriple(), TM->Options.VecLib));
if (Conf.Freestanding)
TLII->disableAllFunctions();
+
+ // Determine whether or not its safe to emit calls to each libfunc. Libfuncs
+ // that might have been present in the current LTO unit, but are not, have
+ // lost their only opportunity to be defined, and calls must not be emitted to
+ // them.
+ // FIXME: BitcodeLibFuncs isn't yet set for distributed ThinLTO.
+ TargetLibraryInfo TLI(*TLII);
+ for (unsigned I = 0, E = static_cast<unsigned>(LibFunc::NumLibFuncs); I != E;
+ ++I) {
+ LibFunc F = static_cast<LibFunc>(I);
+ if (BitcodeLibFuncs.contains(TLI.getName(F)))
+ TLII->setUnavailable(F);
+ }
+
FAM.registerPass([&] { return TargetLibraryAnalysis(*TLII); });
// Parse a custom AA pipeline if asked to.
@@ -384,7 +399,8 @@ static bool isEmptyModule(const Module &Mod) {
bool lto::opt(const Config &Conf, TargetMachine *TM, unsigned Task, Module &Mod,
bool IsThinLTO, ModuleSummaryIndex *ExportSummary,
const ModuleSummaryIndex *ImportSummary,
- const std::vector<uint8_t> &CmdArgs) {
+ const std::vector<uint8_t> &CmdArgs,
+ ArrayRef<StringRef> BitcodeLibFuncs) {
llvm::TimeTraceScope timeScope("opt");
if (EmbedBitcode == LTOBitcodeEmbedding::EmbedPostMergePreOptimized) {
// FIXME: the motivation for capturing post-merge bitcode and command line
@@ -409,9 +425,11 @@ bool lto::opt(const Config &Conf, TargetMachine *TM, unsigned Task, Module &Mod,
// analysis in the case of a ThinLTO build where this might be an empty
// regular LTO combined module, with a large combined index from ThinLTO.
if (!isEmptyModule(Mod)) {
+ DenseSet<StringRef> BitcodeLibFuncsSet(BitcodeLibFuncs.begin(),
+ BitcodeLibFuncs.end());
// FIXME: Plumb the combined index into the new pass manager.
runNewPMPasses(Conf, Mod, TM, Conf.OptLevel, IsThinLTO, ExportSummary,
- ImportSummary);
+ ImportSummary, BitcodeLibFuncsSet);
}
return !Conf.PostOptModuleHook || Conf.PostOptModuleHook(Task, Mod);
}
@@ -577,7 +595,8 @@ Error lto::finalizeOptimizationRemarks(LLVMRemarkFileHandle DiagOutputFile) {
Error lto::backend(const Config &C, AddStreamFn AddStream,
unsigned ParallelCodeGenParallelismLevel, Module &Mod,
- ModuleSummaryIndex &CombinedIndex) {
+ ModuleSummaryIndex &CombinedIndex,
+ ArrayRef<StringRef> BitcodeLibFuncs) {
llvm::TimeTraceScope timeScope("LTO backend");
Expected<const Target *> TOrErr = initAndLookupTarget(C, Mod);
if (!TOrErr)
@@ -589,7 +608,7 @@ Error lto::backend(const Config &C, AddStreamFn AddStream,
if (!C.CodeGenOnly) {
if (!opt(C, TM.get(), 0, Mod, /*IsThinLTO=*/false,
/*ExportSummary=*/&CombinedIndex, /*ImportSummary=*/nullptr,
- /*CmdArgs*/ std::vector<uint8_t>()))
+ /*CmdArgs*/ std::vector<uint8_t>(), BitcodeLibFuncs))
return Error::success();
}
@@ -629,7 +648,8 @@ Error lto::thinBackend(const Config &Conf, unsigned Task, AddStreamFn AddStream,
const FunctionImporter::ImportMapTy &ImportList,
const GVSummaryMapTy &DefinedGlobals,
MapVector<StringRef, BitcodeModule> *ModuleMap,
- bool CodeGenOnly, AddStreamFn IRAddStream,
+ bool CodeGenOnly, ArrayRef<StringRef> BitcodeLibFuncs,
+ AddStreamFn IRAddStream,
const std::vector<uint8_t> &CmdArgs) {
llvm::TimeTraceScope timeScope("Thin backend", Mod.getModuleIdentifier());
Expected<const Target *> TOrErr = initAndLookupTarget(Conf, Mod);
@@ -668,7 +688,7 @@ Error lto::thinBackend(const Config &Conf, unsigned Task, AddStreamFn AddStream,
// Perform optimization and code generation for ThinLTO.
if (!opt(Conf, TM, Task, Mod, /*IsThinLTO=*/true,
/*ExportSummary=*/nullptr, /*ImportSummary=*/&CombinedIndex,
- CmdArgs))
+ CmdArgs, BitcodeLibFuncs))
return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));
// Save the current module before the first codegen round.
diff --git a/llvm/lib/LTO/LTOCodeGenerator.cpp b/llvm/lib/LTO/LTOCodeGenerator.cpp
index 46be71da5a092..8b9bcfb1d4736 100644
--- a/llvm/lib/LTO/LTOCodeGenerator.cpp
+++ b/llvm/lib/LTO/LTOCodeGenerator.cpp
@@ -614,7 +614,7 @@ bool LTOCodeGenerator::optimize() {
TargetMach = createTargetMachine();
if (!opt(Config, TargetMach.get(), 0, *MergedModule, /*IsThinLTO=*/false,
/*ExportSummary=*/&CombinedIndex, /*ImportSummary=*/nullptr,
- /*CmdArgs*/ std::vector<uint8_t>())) {
+ /*CmdArgs*/ std::vector<uint8_t>(), /*BitcodeLibFuncs=*/{})) {
emitError("LTO middle-end optimizations failed");
return false;
}
@@ -639,7 +639,7 @@ bool LTOCodeGenerator::compileOptimized(AddStreamFn AddStream,
Config.CodeGenOnly = true;
Error Err = backend(Config, AddStream, ParallelismLevel, *MergedModule,
- CombinedIndex);
+ CombinedIndex, /*BitcodeLibFuncs=*/{});
assert(!Err && "unexpected code-generation failure");
(void)Err;
diff --git a/llvm/lib/LTO/ThinLTOCodeGenerator.cpp b/llvm/lib/LTO/ThinLTOCodeGenerator.cpp
index 93b672ae7840c..6397fdffed687 100644
--- a/llvm/lib/LTO/ThinLTOCodeGenerator.cpp
+++ b/llvm/lib/LTO/ThinLTOCodeGenerator.cpp
@@ -292,8 +292,10 @@ addUsedSymbolToPreservedGUID(const lto::InputFile &File,
DenseSet<GlobalValue::GUID> &PreservedGUID) {
Triple TT(File.getTargetTriple());
RTLIB::RuntimeLibcallsInfo Libcalls(TT);
+ TargetLibraryInfoImpl TLII(TT);
+ TargetLibraryInfo TLI(TLII);
for (const auto &Sym : File.symbols())
- if (Sym.isUsed() || Sym.isLibcall(Libcalls))
+ if (Sym.isUsed() || Sym.isLibcall(TLI, Libcalls))
PreservedGUID.insert(
GlobalValue::getGUIDAssumingExternalLinkage(Sym.getIRName()));
}
diff --git a/llvm/test/LTO/Resolution/X86/libcall-external.ll b/llvm/test/LTO/Resolution/X86/libcall-external.ll
new file mode 100644
index 0000000000000..46756b387668c
--- /dev/null
+++ b/llvm/test/LTO/Resolution/X86/libcall-external.ll
@@ -0,0 +1,25 @@
+;; When a libcall was not brought into the link, it can be used iff it is
+;; defined in native code, not bitcode.
+; RUN: opt %s -o %t.o -mtriple x86_64-unknown-linux-musl
+; RUN: llvm-lto2 run -o %t.bitcode.o \
+; RUN: -r %t.o,foo,plx -r %t.o,memcmp,x -save-temps %t.o \
+; RUN: --bitcode-libfuncs=bcmp
+; RUN: llvm-dis %t.bitcode.o.0.4.opt.bc -o - | FileCheck --check-prefixes=CHECK,BITCODE %s
+; RUN: llvm-lto2 run -o %t.native.o \
+; RUN: -r %t.o,foo,plx -r %t.o,memcmp,x -save-temps %t.o
+; RUN: llvm-dis %t.native.o.0.4.opt.bc -o - | FileCheck --check-prefixes=CHECK,NATIVE %s
+define i1 @foo(ptr %0, ptr %1, i64 %2) {
+ ; CHECK-LABEL: define{{.*}}i1 @foo
+ ; BITCODE-NEXT: %cmp = {{.*}}call i32 @memcmp
+ ; BITCODE-NEXT: %eq = icmp eq i32 %cmp, 0
+ ; NATIVE-NEXT: %bcmp = {{.*}}call i32 @bcmp
+ ; NATIVE-NEXT: %eq = icmp eq i32 %bcmp, 0
+ ; CHECK-NEXT: ret i1 %eq
+
+
+ %cmp = call i32 @memcmp(ptr %0, ptr %1, i64 %2)
+ %eq = icmp eq i32 %cmp, 0
+ ret i1 %eq
+}
+
+declare i32 @memcmp(ptr, ptr, i64)
diff --git a/llvm/test/LTO/Resolution/X86/libcall-in-thin-link.ll b/llvm/test/LTO/Resolution/X86/libcall-in-thin-link.ll
new file mode 100644
index 0000000000000..fe8e492580d42
--- /dev/null
+++ b/llvm/test/LTO/Resolution/X86/libcall-in-thin-link.ll
@@ -0,0 +1,35 @@
+;; If a libcall was extracted in a thin link, it can be used even if not
+;; present in the current TU.
+
+; RUN: rm -rf %t && split-file %s %t && cd %t
+; RUN: opt foo.ll -o foo.o -module-summary -mtriple x86_64-unknown-linux-musl
+; RUN: opt bcmp.ll -o bcmp.o -module-summary -mtriple x86_64-unknown-linux-musl
+; RUN: llvm-lto2 run -o lto.o \
+; RUN: -r foo.o,foo,plx \
+; RUN: -r foo.o,memcmp,x \
+; RUN: -r bcmp.o,bcmp,pl \
+; RUN: -r bcmp.o,bcmp_impl,x foo.o bcmp.o -save-temps
+; RUN: llvm-dis lto.o.1.4.opt.bc -o - | FileCheck %s
+
+;--- foo.ll
+define i1 @foo(ptr %0, ptr %1, i64 %2) {
+ ; CHECK-LABEL: define{{.*}}i1 @foo
+ ; CHECK-NEXT: %bcmp = {{.*}}call i32 @bcmp
+ ; CHECK-NEXT: %eq = icmp eq i32 %bcmp, 0
+ ; CHECK-NEXT: ret i1 %eq
+
+ %cmp = call i32 @memcmp(ptr %0, ptr %1, i64 %2)
+ %eq = icmp eq i32 %cmp, 0
+ ret i1 %eq
+}
+
+declare i32 @memcmp(ptr, ptr, i64)
+
+;--- bcmp.ll
+define i32 @bcmp(ptr %0, ptr %1, i64 %2) noinline {
+ %r = call i32 @bcmp_impl(ptr %0, ptr %1, i64 %2)
+ ret i32 %r
+}
+
+declare i32 @bcmp_impl(ptr, ptr, i64)
+
diff --git a/llvm/test/LTO/Resolution/X86/libcall-in-tu.ll b/llvm/test/LTO/Resolution/X86/libcall-in-tu.ll
new file mode 100644
index 0000000000000..cddf5d30697d0
--- /dev/null
+++ b/llvm/test/LTO/Resolution/X86/libcall-in-tu.ll
@@ -0,0 +1,34 @@
+;; This test comes from a real world scenario in LTO, where the definition of
+;; bcmp was deleted because it has no uses, but later instcombine re-introduced
+;; a call to bcmp() as part of SimplifyLibCalls. Such deletions must not be
+;; allowed.
+
+; RUN: opt %s -o %t.o -mtriple x86_64-unknown-linux-musl
+; RUN: llvm-lto2 run -o %t.lto.o \
+; RUN: -r %t.o,foo,plx \
+; RUN: -r %t.o,memcmp,x \
+; RUN: -r %t.o,bcmp,pl \
+; RUN: -r %t.o,bcmp_impl,x %t.o -save-temps
+; RUN: llvm-dis %t.lto.o.0.4.opt.bc -o - | FileCheck %s
+
+define i1 @foo(ptr %0, ptr %1, i64 %2) {
+ ; CHECK-LABEL: define{{.*}}i1 @foo
+ ; CHECK-NEXT: %bcmp = {{.*}}call i32 @bcmp
+ ; CHECK-NEXT: %eq = icmp eq i32 %bcmp, 0
+ ; CHECK-NEXT: ret i1 %eq
+
+ %cmp = call i32 @memcmp(ptr %0, ptr %1, i64 %2)
+ %eq = icmp eq i32 %cmp, 0
+ ret i1 %eq
+}
+
+declare i32 @memcmp(ptr, ptr, i64)
+declare i32 @bcmp_impl(ptr, ptr, i64)
+
+;; Ensure bcmp is not removed from module because it is external.
+; CHECK: define dso_local i32 @bcmp
+define i32 @bcmp(ptr %0, ptr %1, i64 %2) noinline {
+ %r = call i32 @bcmp_impl(ptr %0, ptr %1, i64 %2)
+ ret i32 %r
+}
+
diff --git a/llvm/tools/llvm-lto2/llvm-lto2.cpp b/llvm/tools/llvm-lto2/llvm-lto2.cpp
index 8c676251cb391..839324e396850 100644
--- a/llvm/tools/llvm-lto2/llvm-lto2.cpp
+++ b/llvm/tools/llvm-lto2/llvm-lto2.cpp
@@ -234,6 +234,19 @@ static cl::opt<bool>
AllVtablesHaveTypeInfos("all-vtables-have-type-infos", cl::Hidden,
cl::desc("All vtables have type infos"));
+// Specifying a symbol here states that it is a library symbol that had a
+// definition in bitcode, but was not extracted. Such symbols cannot safely
+// be referenced, since they have already lost their opportunity to be defined.
+//
+// FIXME: Listing all bitcode libfunc symbols here is clunky. A higher-level way
+// to indicate which TUs made it into the link might be better, but this would
+// require more detailed tracking of the sources of constructs in the IR.
+// Alternatively, there may be some other data structure that could hold this
+// information.
+static cl::list<std::string> BitcodeLibFuncs(
+ "bitcode-libfuncs", cl::Hidden,
+ cl::desc("set of unextracted libfuncs implemented in bitcode"));
+
static cl::opt<bool> TimeTrace("time-trace", cl::desc("Record time trace"));
static cl::opt<unsigned> TimeTraceGranularity(
@@ -514,6 +527,9 @@ static int run(int argc, char **argv) {
if (HasErrors)
return 1;
+ Lto.setBitcodeLibFuncs(
+ SmallVector<StringRef>(BitcodeLibFuncs.begin(), BitcodeLibFuncs.end()));
+
FileCache Cache;
if (!CacheDir.empty())
Cache = check(localCache("ThinLTO", "Thin", CacheDir, AddBuffer),
More information about the llvm-commits
mailing list