Reland "[LTO][LLD] Prevent invalid LTO libfunc transforms (#164916)" (#190642)

This reverts commit 1ec7e86b3a779df2a0af3f37e58c8f5b3a398d7f after issue
#190072 was fixed.
This commit is contained in:
Daniel Thornburgh 2026-04-06 12:20:45 -07:00 committed by GitHub
parent 412d6941e3
commit fecf609998
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 488 additions and 52 deletions

View File

@ -1410,12 +1410,14 @@ runThinLTOBackend(CompilerInstance &CI, ModuleSummaryIndex *CombinedIndex,
// FIXME: Both ExecuteAction and thinBackend set up optimization remarks for // FIXME: Both ExecuteAction and thinBackend set up optimization remarks for
// the same context. // 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()); finalizeLLVMOptimizationRemarks(M->getContext());
if (Error E = if (Error E = thinBackend(
thinBackend(Conf, -1, AddStream, *M, *CombinedIndex, ImportList, Conf, -1, AddStream, *M, *CombinedIndex, ImportList,
ModuleToDefinedGVSummaries[M->getModuleIdentifier()], ModuleToDefinedGVSummaries[M->getModuleIdentifier()],
/*ModuleMap=*/nullptr, Conf.CodeGenOnly, /*ModuleMap=*/nullptr, Conf.CodeGenOnly, /*BitcodeLibFuncs=*/{},
/*IRAddStream=*/nullptr, CGOpts.CmdArgs)) { /*IRAddStream=*/nullptr, CGOpts.CmdArgs)) {
handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) { handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) {
errs() << "Error running ThinLTO backend: " << EIB.message() << '\n'; errs() << "Error running ThinLTO backend: " << EIB.message() << '\n';
}); });

View File

@ -25,7 +25,6 @@
#include "llvm/DebugInfo/PDB/Native/NativeSession.h" #include "llvm/DebugInfo/PDB/Native/NativeSession.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/IR/Mangler.h" #include "llvm/IR/Mangler.h"
#include "llvm/IR/RuntimeLibcalls.h"
#include "llvm/LTO/LTO.h" #include "llvm/LTO/LTO.h"
#include "llvm/Object/Binary.h" #include "llvm/Object/Binary.h"
#include "llvm/Object/COFF.h" #include "llvm/Object/COFF.h"
@ -1394,8 +1393,6 @@ void BitcodeFile::parse() {
// FIXME: Check nodeduplicate // FIXME: Check nodeduplicate
comdat[i] = comdat[i] =
symtab.addComdat(this, saver.save(obj->getComdatTable()[i].first)); 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()) { for (const lto::InputFile::Symbol &objSym : obj->symbols()) {
StringRef symName = saver.save(objSym.getName()); StringRef symName = saver.save(objSym.getName());
int comdatIndex = objSym.getComdatIndex(); int comdatIndex = objSym.getComdatIndex();
@ -1445,7 +1442,7 @@ void BitcodeFile::parse() {
symtab.addRegular(this, symName, nullptr, fakeSC, 0, objSym.isWeak()); symtab.addRegular(this, symName, nullptr, fakeSC, 0, objSym.isWeak());
} }
symbols.push_back(sym); symbols.push_back(sym);
if (objSym.isUsed() || objSym.isLibcall(libcalls)) if (objSym.isUsed())
symtab.ctx.config.gcroot.push_back(sym); symtab.ctx.config.gcroot.push_back(sym);
} }
directives = saver.save(obj->getCOFFLinkerOpts()); directives = saver.save(obj->getCOFFLinkerOpts());

View File

@ -288,3 +288,7 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
return ret; return ret;
} }
void BitcodeCompiler::setBitcodeLibFuncs(ArrayRef<StringRef> bitcodeLibFuncs) {
ltoObj->setBitcodeLibFuncs(bitcodeLibFuncs);
}

View File

@ -45,6 +45,7 @@ public:
void add(BitcodeFile &f); void add(BitcodeFile &f);
std::vector<InputFile *> compile(); std::vector<InputFile *> compile();
void setBitcodeLibFuncs(ArrayRef<StringRef> bitcodeLibFuncs);
private: private:
std::unique_ptr<llvm::lto::LTO> ltoObj; std::unique_ptr<llvm::lto::LTO> ltoObj;

View File

@ -1437,13 +1437,37 @@ void SymbolTable::compileBitcodeFiles() {
if (bitcodeFileInstances.empty()) if (bitcodeFileInstances.empty())
return; 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); ScopedTimer t(ctx.ltoTimer);
lto.reset(new BitcodeCompiler(ctx)); lto.reset(new BitcodeCompiler(ctx));
lto->setBitcodeLibFuncs(bitcodeLibFuncs);
{ {
llvm::TimeTraceScope addScope("Add bitcode file instances"); llvm::TimeTraceScope addScope("Add bitcode file instances");
for (BitcodeFile *f : bitcodeFileInstances) for (BitcodeFile *f : bitcodeFileInstances)
lto->add(*f); lto->add(*f);
} }
for (InputFile *newObj : lto->compile()) { for (InputFile *newObj : lto->compile()) {
ObjFile *obj = cast<ObjFile>(newObj); ObjFile *obj = cast<ObjFile>(newObj);
obj->parse(); obj->parse();

View File

@ -2774,8 +2774,27 @@ static void markBuffersAsDontNeed(Ctx &ctx, bool skipLinkedOutput) {
template <class ELFT> template <class ELFT>
void LinkerDriver::compileBitcodeFiles(bool skipLinkedOutput) { void LinkerDriver::compileBitcodeFiles(bool skipLinkedOutput) {
llvm::TimeTraceScope timeScope("LTO"); 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. // Compile bitcode files and replace bitcode symbols.
lto.reset(new BitcodeCompiler(ctx)); lto.reset(new BitcodeCompiler(ctx));
lto->setBitcodeLibFuncs(bitcodeLibFuncs);
for (BitcodeFile *file : ctx.bitcodeFiles) for (BitcodeFile *file : ctx.bitcodeFiles)
lto->add(*file); lto->add(*file);

View File

@ -436,3 +436,7 @@ SmallVector<std::unique_ptr<InputFile>, 0> BitcodeCompiler::compile() {
} }
return ret; return ret;
} }
void BitcodeCompiler::setBitcodeLibFuncs(ArrayRef<StringRef> bitcodeLibFuncs) {
ltoObj->setBitcodeLibFuncs(bitcodeLibFuncs);
}

View File

@ -43,6 +43,7 @@ public:
void add(BitcodeFile &f); void add(BitcodeFile &f);
SmallVector<std::unique_ptr<InputFile>, 0> compile(); SmallVector<std::unique_ptr<InputFile>, 0> compile();
void setBitcodeLibFuncs(ArrayRef<StringRef> bitcodeLibFuncs);
private: private:
Ctx &ctx; Ctx &ctx;

View File

@ -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"
@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, ...)

View File

@ -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"
@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, ...)

View File

@ -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"
@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, ...)

View File

@ -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 // Merge all the bitcode files we have seen, codegen the result
// and return the resulting objects. // and return the resulting objects.
SmallVector<InputFile *, 0> BitcodeCompiler::compile() { SmallVector<InputFile *, 0> BitcodeCompiler::compile() {

View File

@ -46,6 +46,7 @@ public:
void add(BitcodeFile &f); void add(BitcodeFile &f);
SmallVector<InputFile *, 0> compile(); SmallVector<InputFile *, 0> compile();
void setBitcodeLibFuncs(ArrayRef<StringRef> bitcodeLibFuncs);
private: private:
std::unique_ptr<llvm::lto::LTO> ltoObj; std::unique_ptr<llvm::lto::LTO> ltoObj;

View File

@ -82,8 +82,31 @@ void SymbolTable::compileBitcodeFiles() {
// Prevent further LTO objects being included // Prevent further LTO objects being included
BitcodeFile::doneLTO = true; 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. // Compile bitcode files and replace bitcode symbols.
lto.reset(new BitcodeCompiler); lto.reset(new BitcodeCompiler);
lto->setBitcodeLibFuncs(bitcodeLibFuncs);
for (BitcodeFile *f : ctx.bitcodeFiles) for (BitcodeFile *f : ctx.bitcodeFiles)
lto->add(*f); lto->add(*f);

View File

@ -101,6 +101,13 @@ Changes to LLVM infrastructure
this may fail if symlink permissions are not available. this may fail if symlink permissions are not available.
* Added ``readlink``, which reads the target of a symbolic link. * 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 Changes to building LLVM
------------------------ ------------------------

View File

@ -179,7 +179,8 @@ public:
// may emit references to. Such symbols must be considered external, as // may emit references to. Such symbols must be considered external, as
// removing them or modifying their interfaces would invalidate the code // removing them or modifying their interfaces would invalidate the code
// generator's knowledge about them. // 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. /// A range over the symbols in this InputFile.
@ -308,7 +309,8 @@ public:
using ThinBackendFunction = std::function<std::unique_ptr<ThinBackendProc>( using ThinBackendFunction = std::function<std::unique_ptr<ThinBackendProc>(
const Config &C, ModuleSummaryIndex &CombinedIndex, const Config &C, ModuleSummaryIndex &CombinedIndex,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, 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. /// This type defines the behavior following the thin-link phase during ThinLTO.
/// It encapsulates a backend function and a strategy for thread pool /// It encapsulates a backend function and a strategy for thread pool
@ -323,10 +325,11 @@ struct ThinBackend {
std::unique_ptr<ThinBackendProc> operator()( std::unique_ptr<ThinBackendProc> operator()(
const Config &Conf, ModuleSummaryIndex &CombinedIndex, const Config &Conf, ModuleSummaryIndex &CombinedIndex,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddStreamFn AddStream, FileCache Cache) { AddStreamFn AddStream, FileCache Cache,
ArrayRef<StringRef> BitcodeLibFuncs) {
assert(isValid() && "Invalid backend function"); assert(isValid() && "Invalid backend function");
return Func(Conf, CombinedIndex, ModuleToDefinedGVSummaries, return Func(Conf, CombinedIndex, ModuleToDefinedGVSummaries,
std::move(AddStream), std::move(Cache)); std::move(AddStream), std::move(Cache), BitcodeLibFuncs);
} }
ThreadPoolStrategy getParallelism() const { return Parallelism; } ThreadPoolStrategy getParallelism() const { return Parallelism; }
bool isValid() const { return static_cast<bool>(Func); } bool isValid() const { return static_cast<bool>(Func); }
@ -446,6 +449,11 @@ public:
LLVM_ABI Error add(std::unique_ptr<InputFile> Obj, LLVM_ABI Error add(std::unique_ptr<InputFile> Obj,
ArrayRef<SymbolResolution> Res); 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. /// 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 /// This may only be called after all IR object files have been added. For a
/// full description of tasks see LTOBackend.h. /// full description of tasks see LTOBackend.h.
@ -466,6 +474,14 @@ public:
LLVM_ABI static SmallVector<const char *> LLVM_ABI static SmallVector<const char *>
getRuntimeLibcallSymbols(const Triple &TT); 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: protected:
// Called at the start of run(). // Called at the start of run().
virtual Error serializeInputsForDistribution() { return Error::success(); } virtual Error serializeInputsForDistribution() { return Error::success(); }
@ -657,6 +673,11 @@ private:
// Setup optimization remarks according to the provided configuration. // Setup optimization remarks according to the provided configuration.
Error setupOptimizationRemarks(); 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: public:
/// Helper to emit an optimization remark during the LTO link when outside of /// Helper to emit an optimization remark during the LTO link when outside of
/// the standard optimization pass pipeline. /// the standard optimization pass pipeline.

View File

@ -39,13 +39,15 @@ LLVM_ABI bool opt(const Config &Conf, TargetMachine *TM, unsigned Task,
Module &Mod, bool IsThinLTO, Module &Mod, bool IsThinLTO,
ModuleSummaryIndex *ExportSummary, ModuleSummaryIndex *ExportSummary,
const ModuleSummaryIndex *ImportSummary, 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 /// 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. /// regular LTO phase of ThinLTO, which may need to access the combined index.
LLVM_ABI Error backend(const Config &C, AddStreamFn AddStream, LLVM_ABI Error backend(const Config &C, AddStreamFn AddStream,
unsigned ParallelCodeGenParallelismLevel, Module &M, unsigned ParallelCodeGenParallelismLevel, Module &M,
ModuleSummaryIndex &CombinedIndex); ModuleSummaryIndex &CombinedIndex,
ArrayRef<StringRef> BitcodeLibFuncs);
/// Runs a ThinLTO backend. /// Runs a ThinLTO backend.
/// If \p ModuleMap is not nullptr, all the module files to be imported have /// 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 /// the backend will skip optimization and only perform code generation. If
/// \p IRAddStream is not nullptr, it will be called just before code generation /// \p IRAddStream is not nullptr, it will be called just before code generation
/// to serialize the optimized IR. /// to serialize the optimized IR.
LLVM_ABI Error LLVM_ABI Error thinBackend(
thinBackend(const Config &C, unsigned Task, AddStreamFn AddStream, Module &M, const Config &C, unsigned Task, AddStreamFn AddStream, Module &M,
const ModuleSummaryIndex &CombinedIndex, const ModuleSummaryIndex &CombinedIndex,
const FunctionImporter::ImportMapTy &ImportList, const FunctionImporter::ImportMapTy &ImportList,
const GVSummaryMapTy &DefinedGlobals, const GVSummaryMapTy &DefinedGlobals,
MapVector<StringRef, BitcodeModule> *ModuleMap, bool CodeGenOnly, MapVector<StringRef, BitcodeModule> *ModuleMap, bool CodeGenOnly,
AddStreamFn IRAddStream = nullptr, ArrayRef<StringRef> BitcodeLibFuncs, AddStreamFn IRAddStream = nullptr,
const std::vector<uint8_t> &CmdArgs = std::vector<uint8_t>()); const std::vector<uint8_t> &CmdArgs = std::vector<uint8_t>());
LLVM_ABI Error finalizeOptimizationRemarks(LLVMRemarkFileHandle DiagOutputFile); LLVM_ABI Error finalizeOptimizationRemarks(LLVMRemarkFileHandle DiagOutputFile);

View File

@ -653,7 +653,11 @@ Expected<std::unique_ptr<InputFile>> InputFile::create(MemoryBufferRef Object) {
} }
bool InputFile::Symbol::isLibcall( bool InputFile::Symbol::isLibcall(
const TargetLibraryInfo &TLI,
const RTLIB::RuntimeLibcallsInfo &Libcalls) const { const RTLIB::RuntimeLibcallsInfo &Libcalls) const {
LibFunc F;
if (TLI.getLibFunc(IRName, F) && TLI.has(F))
return true;
return Libcalls.getSupportedLibcallImpl(IRName) != RTLIB::Unsupported; return Libcalls.getSupportedLibcallImpl(IRName) != RTLIB::Unsupported;
} }
@ -715,6 +719,8 @@ void LTO::addModuleToGlobalRes(ArrayRef<InputFile::Symbol> Syms,
auto *ResE = Res.end(); auto *ResE = Res.end();
(void)ResE; (void)ResE;
RTLIB::RuntimeLibcallsInfo Libcalls(TT); RTLIB::RuntimeLibcallsInfo Libcalls(TT);
TargetLibraryInfoImpl TLII(TT);
TargetLibraryInfo TLI(TLII);
for (const InputFile::Symbol &Sym : Syms) { for (const InputFile::Symbol &Sym : Syms) {
assert(ResI != ResE); assert(ResI != ResE);
SymbolResolution Res = *ResI++; SymbolResolution Res = *ResI++;
@ -757,7 +763,7 @@ void LTO::addModuleToGlobalRes(ArrayRef<InputFile::Symbol> Syms,
GlobalRes.VisibleOutsideSummary = true; 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 // 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 // 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(); 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>> Expected<ArrayRef<SymbolResolution>>
LTO::addModule(InputFile &Input, ArrayRef<SymbolResolution> InputRes, LTO::addModule(InputFile &Input, ArrayRef<SymbolResolution> InputRes,
unsigned ModI, ArrayRef<SymbolResolution> Res) { unsigned ModI, ArrayRef<SymbolResolution> Res) {
@ -1469,9 +1481,9 @@ Error LTO::runRegularLTO(AddStreamFn AddStream) {
} }
if (!RegularLTO.EmptyCombinedModule || Conf.AlwaysEmitRegularLTOObj) { if (!RegularLTO.EmptyCombinedModule || Conf.AlwaysEmitRegularLTOObj) {
if (Error Err = if (Error Err = backend(
backend(Conf, AddStream, RegularLTO.ParallelCodeGenParallelismLevel, Conf, AddStream, RegularLTO.ParallelCodeGenParallelismLevel,
*RegularLTO.CombinedModule, ThinLTO.CombinedIndex)) *RegularLTO.CombinedModule, ThinLTO.CombinedIndex, BitcodeLibFuncs))
return Err; return Err;
} }
@ -1491,6 +1503,20 @@ SmallVector<const char *> LTO::getRuntimeLibcallSymbols(const Triple &TT) {
return LibcallSymbols; 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( Error ThinBackendProc::emitFiles(
const FunctionImporter::ImportMapTy &ImportList, llvm::StringRef ModulePath, const FunctionImporter::ImportMapTy &ImportList, llvm::StringRef ModulePath,
const std::string &NewModulePath) const { const std::string &NewModulePath) const {
@ -1568,6 +1594,7 @@ protected:
// generating directly into the returned output stream. // generating directly into the returned output stream.
AddStreamFn AddStream; AddStreamFn AddStream;
FileCache Cache; FileCache Cache;
ArrayRef<StringRef> BitcodeLibFuncs;
public: public:
InProcessThinBackend( InProcessThinBackend(
@ -1575,11 +1602,13 @@ public:
ThreadPoolStrategy ThinLTOParallelism, ThreadPoolStrategy ThinLTOParallelism,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddStreamFn AddStream, FileCache Cache, lto::IndexWriteCallback OnWrite, AddStreamFn AddStream, FileCache Cache, lto::IndexWriteCallback OnWrite,
bool ShouldEmitIndexFiles, bool ShouldEmitImportsFiles) bool ShouldEmitIndexFiles, bool ShouldEmitImportsFiles,
ArrayRef<StringRef> BitcodeLibFuncs)
: CGThinBackend(Conf, CombinedIndex, ModuleToDefinedGVSummaries, OnWrite, : CGThinBackend(Conf, CombinedIndex, ModuleToDefinedGVSummaries, OnWrite,
ShouldEmitIndexFiles, ShouldEmitImportsFiles, ShouldEmitIndexFiles, ShouldEmitImportsFiles,
ThinLTOParallelism), ThinLTOParallelism),
AddStream(std::move(AddStream)), Cache(std::move(Cache)) {} AddStream(std::move(AddStream)), Cache(std::move(Cache)),
BitcodeLibFuncs(BitcodeLibFuncs) {}
virtual Error runThinLTOBackendThread( virtual Error runThinLTOBackendThread(
AddStreamFn AddStream, FileCache Cache, unsigned Task, BitcodeModule BM, AddStreamFn AddStream, FileCache Cache, unsigned Task, BitcodeModule BM,
@ -1600,7 +1629,7 @@ public:
return thinBackend(Conf, Task, AddStream, **MOrErr, CombinedIndex, return thinBackend(Conf, Task, AddStream, **MOrErr, CombinedIndex,
ImportList, DefinedGlobals, &ModuleMap, ImportList, DefinedGlobals, &ModuleMap,
Conf.CodeGenOnly); Conf.CodeGenOnly, BitcodeLibFuncs);
}; };
if (ShouldEmitIndexFiles) { if (ShouldEmitIndexFiles) {
if (auto E = emitFiles(ImportList, ModuleID, ModuleID.str())) if (auto E = emitFiles(ImportList, ModuleID, ModuleID.str()))
@ -1685,13 +1714,14 @@ public:
const Config &Conf, ModuleSummaryIndex &CombinedIndex, const Config &Conf, ModuleSummaryIndex &CombinedIndex,
ThreadPoolStrategy ThinLTOParallelism, ThreadPoolStrategy ThinLTOParallelism,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddStreamFn CGAddStream, FileCache CGCache, AddStreamFn IRAddStream, AddStreamFn CGAddStream, FileCache CGCache,
ArrayRef<StringRef> BitcodeLibFuncs, AddStreamFn IRAddStream,
FileCache IRCache) FileCache IRCache)
: InProcessThinBackend(Conf, CombinedIndex, ThinLTOParallelism, : InProcessThinBackend(Conf, CombinedIndex, ThinLTOParallelism,
ModuleToDefinedGVSummaries, std::move(CGAddStream), ModuleToDefinedGVSummaries, std::move(CGAddStream),
std::move(CGCache), /*OnWrite=*/nullptr, std::move(CGCache), /*OnWrite=*/nullptr,
/*ShouldEmitIndexFiles=*/false, /*ShouldEmitIndexFiles=*/false,
/*ShouldEmitImportsFiles=*/false), /*ShouldEmitImportsFiles=*/false, BitcodeLibFuncs),
IRAddStream(std::move(IRAddStream)), IRCache(std::move(IRCache)) {} IRAddStream(std::move(IRAddStream)), IRCache(std::move(IRCache)) {}
Error runThinLTOBackendThread( Error runThinLTOBackendThread(
@ -1714,7 +1744,7 @@ public:
return thinBackend(Conf, Task, CGAddStream, **MOrErr, CombinedIndex, return thinBackend(Conf, Task, CGAddStream, **MOrErr, CombinedIndex,
ImportList, DefinedGlobals, &ModuleMap, ImportList, DefinedGlobals, &ModuleMap,
Conf.CodeGenOnly, IRAddStream); Conf.CodeGenOnly, BitcodeLibFuncs, IRAddStream);
}; };
// Like InProcessThinBackend, we produce index files as needed for // Like InProcessThinBackend, we produce index files as needed for
// FirstRoundThinBackend. However, these files are not generated for // FirstRoundThinBackend. However, these files are not generated for
@ -1781,6 +1811,7 @@ public:
ThreadPoolStrategy ThinLTOParallelism, ThreadPoolStrategy ThinLTOParallelism,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddStreamFn AddStream, FileCache Cache, AddStreamFn AddStream, FileCache Cache,
ArrayRef<StringRef> BitcodeLibFuncs,
std::unique_ptr<SmallVector<StringRef>> IRFiles, std::unique_ptr<SmallVector<StringRef>> IRFiles,
stable_hash CombinedCGDataHash) stable_hash CombinedCGDataHash)
: InProcessThinBackend(Conf, CombinedIndex, ThinLTOParallelism, : InProcessThinBackend(Conf, CombinedIndex, ThinLTOParallelism,
@ -1788,7 +1819,7 @@ public:
std::move(Cache), std::move(Cache),
/*OnWrite=*/nullptr, /*OnWrite=*/nullptr,
/*ShouldEmitIndexFiles=*/false, /*ShouldEmitIndexFiles=*/false,
/*ShouldEmitImportsFiles=*/false), /*ShouldEmitImportsFiles=*/false, BitcodeLibFuncs),
IRFiles(std::move(IRFiles)), CombinedCGDataHash(CombinedCGDataHash) {} IRFiles(std::move(IRFiles)), CombinedCGDataHash(CombinedCGDataHash) {}
Error runThinLTOBackendThread( Error runThinLTOBackendThread(
@ -1809,7 +1840,7 @@ public:
return thinBackend(Conf, Task, AddStream, *LoadedModule, CombinedIndex, return thinBackend(Conf, Task, AddStream, *LoadedModule, CombinedIndex,
ImportList, DefinedGlobals, &ModuleMap, ImportList, DefinedGlobals, &ModuleMap,
/*CodeGenOnly=*/true); /*CodeGenOnly=*/true, BitcodeLibFuncs);
}; };
if (!Cache.isValid() || !CombinedIndex.modulePaths().count(ModuleID) || if (!Cache.isValid() || !CombinedIndex.modulePaths().count(ModuleID) ||
all_of(CombinedIndex.getModuleHash(ModuleID), all_of(CombinedIndex.getModuleHash(ModuleID),
@ -1848,11 +1879,12 @@ ThinBackend lto::createInProcessThinBackend(ThreadPoolStrategy Parallelism,
auto Func = auto Func =
[=](const Config &Conf, ModuleSummaryIndex &CombinedIndex, [=](const Config &Conf, ModuleSummaryIndex &CombinedIndex,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddStreamFn AddStream, FileCache Cache) { AddStreamFn AddStream, FileCache Cache,
ArrayRef<StringRef> BitcodeLibFuncs) {
return std::make_unique<InProcessThinBackend>( return std::make_unique<InProcessThinBackend>(
Conf, CombinedIndex, Parallelism, ModuleToDefinedGVSummaries, Conf, CombinedIndex, Parallelism, ModuleToDefinedGVSummaries,
AddStream, Cache, OnWrite, ShouldEmitIndexFiles, AddStream, Cache, OnWrite, ShouldEmitIndexFiles,
ShouldEmitImportsFiles); ShouldEmitImportsFiles, BitcodeLibFuncs);
}; };
return ThinBackend(Func, Parallelism); return ThinBackend(Func, Parallelism);
} }
@ -1969,7 +2001,8 @@ ThinBackend lto::createWriteIndexesThinBackend(
auto Func = auto Func =
[=](const Config &Conf, ModuleSummaryIndex &CombinedIndex, [=](const Config &Conf, ModuleSummaryIndex &CombinedIndex,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddStreamFn AddStream, FileCache Cache) { AddStreamFn AddStream, FileCache Cache,
ArrayRef<StringRef> BitcodeLibFuncs) {
return std::make_unique<WriteIndexesThinBackend>( return std::make_unique<WriteIndexesThinBackend>(
Conf, CombinedIndex, Parallelism, ModuleToDefinedGVSummaries, Conf, CombinedIndex, Parallelism, ModuleToDefinedGVSummaries,
OldPrefix, NewPrefix, NativeObjectPrefix, ShouldEmitImportsFiles, OldPrefix, NewPrefix, NativeObjectPrefix, ShouldEmitImportsFiles,
@ -2222,7 +2255,7 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache,
if (!CodeGenDataThinLTOTwoRounds) { if (!CodeGenDataThinLTOTwoRounds) {
std::unique_ptr<ThinBackendProc> BackendProc = std::unique_ptr<ThinBackendProc> BackendProc =
ThinLTO.Backend(Conf, ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries, ThinLTO.Backend(Conf, ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries,
AddStream, Cache); AddStream, Cache, BitcodeLibFuncs);
return RunBackends(BackendProc.get()); 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"); LLVM_DEBUG(dbgs() << "[TwoRounds] Running the first round of codegen\n");
auto FirstRoundLTO = std::make_unique<FirstRoundThinBackend>( auto FirstRoundLTO = std::make_unique<FirstRoundThinBackend>(
Conf, ThinLTO.CombinedIndex, Parallelism, ModuleToDefinedGVSummaries, 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())) if (Error E = RunBackends(FirstRoundLTO.get()))
return E; return E;
@ -2261,7 +2294,7 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache,
LLVM_DEBUG(dbgs() << "[TwoRounds] Running the second round of codegen\n"); LLVM_DEBUG(dbgs() << "[TwoRounds] Running the second round of codegen\n");
auto SecondRoundLTO = std::make_unique<SecondRoundThinBackend>( auto SecondRoundLTO = std::make_unique<SecondRoundThinBackend>(
Conf, ThinLTO.CombinedIndex, Parallelism, ModuleToDefinedGVSummaries, Conf, ThinLTO.CombinedIndex, Parallelism, ModuleToDefinedGVSummaries,
AddStream, Cache, IR.getResult(), CombinedHash); AddStream, Cache, BitcodeLibFuncs, IR.getResult(), CombinedHash);
return RunBackends(SecondRoundLTO.get()); return RunBackends(SecondRoundLTO.get());
} }
@ -2768,7 +2801,7 @@ ThinBackend lto::createOutOfProcessThinBackend(
auto Func = auto Func =
[=](const Config &Conf, ModuleSummaryIndex &CombinedIndex, [=](const Config &Conf, ModuleSummaryIndex &CombinedIndex,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddStreamFn, FileCache Cache) { AddStreamFn, FileCache Cache, ArrayRef<StringRef> BitcodeLibFuncs) {
return std::make_unique<OutOfProcessThinBackend>( return std::make_unique<OutOfProcessThinBackend>(
Conf, CombinedIndex, Parallelism, ModuleToDefinedGVSummaries, Cache, Conf, CombinedIndex, Parallelism, ModuleToDefinedGVSummaries, Cache,
OnWrite, ShouldEmitIndexFiles, ShouldEmitImportsFiles, OnWrite, ShouldEmitIndexFiles, ShouldEmitImportsFiles,

View File

@ -259,7 +259,8 @@ createTargetMachine(const Config &Conf, const Target *TheTarget, Module &M) {
static void runNewPMPasses(const Config &Conf, Module &Mod, TargetMachine *TM, static void runNewPMPasses(const Config &Conf, Module &Mod, TargetMachine *TM,
unsigned OptLevel, bool IsThinLTO, unsigned OptLevel, bool IsThinLTO,
ModuleSummaryIndex *ExportSummary, ModuleSummaryIndex *ExportSummary,
const ModuleSummaryIndex *ImportSummary) { const ModuleSummaryIndex *ImportSummary,
const DenseSet<StringRef> &BitcodeLibFuncs) {
std::optional<PGOOptions> PGOOpt; std::optional<PGOOptions> PGOOpt;
if (!Conf.SampleProfile.empty()) if (!Conf.SampleProfile.empty())
PGOOpt = PGOOptions(Conf.SampleProfile, "", Conf.ProfileRemapping, 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)); new TargetLibraryInfoImpl(TM->getTargetTriple(), TM->Options.VecLib));
if (Conf.Freestanding) if (Conf.Freestanding)
TLII->disableAllFunctions(); 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); }); FAM.registerPass([&] { return TargetLibraryAnalysis(*TLII); });
// Parse a custom AA pipeline if asked to. // 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 lto::opt(const Config &Conf, TargetMachine *TM, unsigned Task, Module &Mod,
bool IsThinLTO, ModuleSummaryIndex *ExportSummary, bool IsThinLTO, ModuleSummaryIndex *ExportSummary,
const ModuleSummaryIndex *ImportSummary, const ModuleSummaryIndex *ImportSummary,
const std::vector<uint8_t> &CmdArgs) { const std::vector<uint8_t> &CmdArgs,
ArrayRef<StringRef> BitcodeLibFuncs) {
llvm::TimeTraceScope timeScope("opt"); llvm::TimeTraceScope timeScope("opt");
if (EmbedBitcode == LTOBitcodeEmbedding::EmbedPostMergePreOptimized) { if (EmbedBitcode == LTOBitcodeEmbedding::EmbedPostMergePreOptimized) {
// FIXME: the motivation for capturing post-merge bitcode and command line // 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 // 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. // regular LTO combined module, with a large combined index from ThinLTO.
if (!isEmptyModule(Mod)) { if (!isEmptyModule(Mod)) {
DenseSet<StringRef> BitcodeLibFuncsSet(BitcodeLibFuncs.begin(),
BitcodeLibFuncs.end());
// FIXME: Plumb the combined index into the new pass manager. // FIXME: Plumb the combined index into the new pass manager.
runNewPMPasses(Conf, Mod, TM, Conf.OptLevel, IsThinLTO, ExportSummary, runNewPMPasses(Conf, Mod, TM, Conf.OptLevel, IsThinLTO, ExportSummary,
ImportSummary); ImportSummary, BitcodeLibFuncsSet);
} }
return !Conf.PostOptModuleHook || Conf.PostOptModuleHook(Task, Mod); return !Conf.PostOptModuleHook || Conf.PostOptModuleHook(Task, Mod);
} }
@ -577,7 +595,8 @@ Error lto::finalizeOptimizationRemarks(LLVMRemarkFileHandle DiagOutputFile) {
Error lto::backend(const Config &C, AddStreamFn AddStream, Error lto::backend(const Config &C, AddStreamFn AddStream,
unsigned ParallelCodeGenParallelismLevel, Module &Mod, unsigned ParallelCodeGenParallelismLevel, Module &Mod,
ModuleSummaryIndex &CombinedIndex) { ModuleSummaryIndex &CombinedIndex,
ArrayRef<StringRef> BitcodeLibFuncs) {
llvm::TimeTraceScope timeScope("LTO backend"); llvm::TimeTraceScope timeScope("LTO backend");
Expected<const Target *> TOrErr = initAndLookupTarget(C, Mod); Expected<const Target *> TOrErr = initAndLookupTarget(C, Mod);
if (!TOrErr) if (!TOrErr)
@ -589,7 +608,7 @@ Error lto::backend(const Config &C, AddStreamFn AddStream,
if (!C.CodeGenOnly) { if (!C.CodeGenOnly) {
if (!opt(C, TM.get(), 0, Mod, /*IsThinLTO=*/false, if (!opt(C, TM.get(), 0, Mod, /*IsThinLTO=*/false,
/*ExportSummary=*/&CombinedIndex, /*ImportSummary=*/nullptr, /*ExportSummary=*/&CombinedIndex, /*ImportSummary=*/nullptr,
/*CmdArgs*/ std::vector<uint8_t>())) /*CmdArgs*/ std::vector<uint8_t>(), BitcodeLibFuncs))
return Error::success(); return Error::success();
} }
@ -629,7 +648,8 @@ Error lto::thinBackend(const Config &Conf, unsigned Task, AddStreamFn AddStream,
const FunctionImporter::ImportMapTy &ImportList, const FunctionImporter::ImportMapTy &ImportList,
const GVSummaryMapTy &DefinedGlobals, const GVSummaryMapTy &DefinedGlobals,
MapVector<StringRef, BitcodeModule> *ModuleMap, MapVector<StringRef, BitcodeModule> *ModuleMap,
bool CodeGenOnly, AddStreamFn IRAddStream, bool CodeGenOnly, ArrayRef<StringRef> BitcodeLibFuncs,
AddStreamFn IRAddStream,
const std::vector<uint8_t> &CmdArgs) { const std::vector<uint8_t> &CmdArgs) {
llvm::TimeTraceScope timeScope("Thin backend", Mod.getModuleIdentifier()); llvm::TimeTraceScope timeScope("Thin backend", Mod.getModuleIdentifier());
Expected<const Target *> TOrErr = initAndLookupTarget(Conf, Mod); 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. // Perform optimization and code generation for ThinLTO.
if (!opt(Conf, TM, Task, Mod, /*IsThinLTO=*/true, if (!opt(Conf, TM, Task, Mod, /*IsThinLTO=*/true,
/*ExportSummary=*/nullptr, /*ImportSummary=*/&CombinedIndex, /*ExportSummary=*/nullptr, /*ImportSummary=*/&CombinedIndex,
CmdArgs)) CmdArgs, BitcodeLibFuncs))
return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile)); return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));
// Save the current module before the first codegen round. // Save the current module before the first codegen round.

View File

@ -614,7 +614,7 @@ bool LTOCodeGenerator::optimize() {
TargetMach = createTargetMachine(); TargetMach = createTargetMachine();
if (!opt(Config, TargetMach.get(), 0, *MergedModule, /*IsThinLTO=*/false, if (!opt(Config, TargetMach.get(), 0, *MergedModule, /*IsThinLTO=*/false,
/*ExportSummary=*/&CombinedIndex, /*ImportSummary=*/nullptr, /*ExportSummary=*/&CombinedIndex, /*ImportSummary=*/nullptr,
/*CmdArgs*/ std::vector<uint8_t>())) { /*CmdArgs*/ std::vector<uint8_t>(), /*BitcodeLibFuncs=*/{})) {
emitError("LTO middle-end optimizations failed"); emitError("LTO middle-end optimizations failed");
return false; return false;
} }
@ -639,7 +639,7 @@ bool LTOCodeGenerator::compileOptimized(AddStreamFn AddStream,
Config.CodeGenOnly = true; Config.CodeGenOnly = true;
Error Err = backend(Config, AddStream, ParallelismLevel, *MergedModule, Error Err = backend(Config, AddStream, ParallelismLevel, *MergedModule,
CombinedIndex); CombinedIndex, /*BitcodeLibFuncs=*/{});
assert(!Err && "unexpected code-generation failure"); assert(!Err && "unexpected code-generation failure");
(void)Err; (void)Err;

View File

@ -292,8 +292,10 @@ addUsedSymbolToPreservedGUID(const lto::InputFile &File,
DenseSet<GlobalValue::GUID> &PreservedGUID) { DenseSet<GlobalValue::GUID> &PreservedGUID) {
Triple TT(File.getTargetTriple()); Triple TT(File.getTargetTriple());
RTLIB::RuntimeLibcallsInfo Libcalls(TT); RTLIB::RuntimeLibcallsInfo Libcalls(TT);
TargetLibraryInfoImpl TLII(TT);
TargetLibraryInfo TLI(TLII);
for (const auto &Sym : File.symbols()) for (const auto &Sym : File.symbols())
if (Sym.isUsed() || Sym.isLibcall(Libcalls)) if (Sym.isUsed() || Sym.isLibcall(TLI, Libcalls))
PreservedGUID.insert( PreservedGUID.insert(
GlobalValue::getGUIDAssumingExternalLinkage(Sym.getIRName())); GlobalValue::getGUIDAssumingExternalLinkage(Sym.getIRName()));
} }

View File

@ -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)

View File

@ -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)

View File

@ -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
}

View File

@ -234,6 +234,19 @@ static cl::opt<bool>
AllVtablesHaveTypeInfos("all-vtables-have-type-infos", cl::Hidden, AllVtablesHaveTypeInfos("all-vtables-have-type-infos", cl::Hidden,
cl::desc("All vtables have type infos")); 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<bool> TimeTrace("time-trace", cl::desc("Record time trace"));
static cl::opt<unsigned> TimeTraceGranularity( static cl::opt<unsigned> TimeTraceGranularity(
@ -514,6 +527,9 @@ static int run(int argc, char **argv) {
if (HasErrors) if (HasErrors)
return 1; return 1;
Lto.setBitcodeLibFuncs(
SmallVector<StringRef>(BitcodeLibFuncs.begin(), BitcodeLibFuncs.end()));
FileCache Cache; FileCache Cache;
if (!CacheDir.empty()) if (!CacheDir.empty())
Cache = check(localCache("ThinLTO", "Thin", CacheDir, AddBuffer), Cache = check(localCache("ThinLTO", "Thin", CacheDir, AddBuffer),