summaryrefslogtreecommitdiff
path: root/llvm/lib/LTO
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/LTO')
-rw-r--r--llvm/lib/LTO/Caching.cpp150
-rw-r--r--llvm/lib/LTO/LTO.cpp1412
-rw-r--r--llvm/lib/LTO/LTOBackend.cpp547
-rw-r--r--llvm/lib/LTO/LTOCodeGenerator.cpp722
-rw-r--r--llvm/lib/LTO/LTOModule.cpp678
-rw-r--r--llvm/lib/LTO/SummaryBasedOptimizations.cpp85
-rw-r--r--llvm/lib/LTO/ThinLTOCodeGenerator.cpp1135
-rw-r--r--llvm/lib/LTO/UpdateCompilerUsed.cpp132
8 files changed, 4861 insertions, 0 deletions
diff --git a/llvm/lib/LTO/Caching.cpp b/llvm/lib/LTO/Caching.cpp
new file mode 100644
index 0000000000000..12dcd182de2d0
--- /dev/null
+++ b/llvm/lib/LTO/Caching.cpp
@@ -0,0 +1,150 @@
+//===-Caching.cpp - LLVM Link Time Optimizer Cache Handling ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the Caching for ThinLTO.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/LTO/Caching.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/raw_ostream.h"
+
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+#include <unistd.h>
+#else
+#include <io.h>
+#endif
+
+using namespace llvm;
+using namespace llvm::lto;
+
+Expected<NativeObjectCache> lto::localCache(StringRef CacheDirectoryPath,
+ AddBufferFn AddBuffer) {
+ if (std::error_code EC = sys::fs::create_directories(CacheDirectoryPath))
+ return errorCodeToError(EC);
+
+ return [=](unsigned Task, StringRef Key) -> AddStreamFn {
+ // This choice of file name allows the cache to be pruned (see pruneCache()
+ // in include/llvm/Support/CachePruning.h).
+ SmallString<64> EntryPath;
+ sys::path::append(EntryPath, CacheDirectoryPath, "llvmcache-" + Key);
+ // First, see if we have a cache hit.
+ SmallString<64> ResultPath;
+ Expected<sys::fs::file_t> FDOrErr = sys::fs::openNativeFileForRead(
+ Twine(EntryPath), sys::fs::OF_UpdateAtime, &ResultPath);
+ std::error_code EC;
+ if (FDOrErr) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
+ MemoryBuffer::getOpenFile(*FDOrErr, EntryPath,
+ /*FileSize=*/-1,
+ /*RequiresNullTerminator=*/false);
+ sys::fs::closeFile(*FDOrErr);
+ if (MBOrErr) {
+ AddBuffer(Task, std::move(*MBOrErr));
+ return AddStreamFn();
+ }
+ EC = MBOrErr.getError();
+ } else {
+ EC = errorToErrorCode(FDOrErr.takeError());
+ }
+
+ // On Windows we can fail to open a cache file with a permission denied
+ // error. This generally means that another process has requested to delete
+ // the file while it is still open, but it could also mean that another
+ // process has opened the file without the sharing permissions we need.
+ // Since the file is probably being deleted we handle it in the same way as
+ // if the file did not exist at all.
+ if (EC != errc::no_such_file_or_directory && EC != errc::permission_denied)
+ report_fatal_error(Twine("Failed to open cache file ") + EntryPath +
+ ": " + EC.message() + "\n");
+
+ // This native object stream is responsible for commiting the resulting
+ // file to the cache and calling AddBuffer to add it to the link.
+ struct CacheStream : NativeObjectStream {
+ AddBufferFn AddBuffer;
+ sys::fs::TempFile TempFile;
+ std::string EntryPath;
+ unsigned Task;
+
+ CacheStream(std::unique_ptr<raw_pwrite_stream> OS, AddBufferFn AddBuffer,
+ sys::fs::TempFile TempFile, std::string EntryPath,
+ unsigned Task)
+ : NativeObjectStream(std::move(OS)), AddBuffer(std::move(AddBuffer)),
+ TempFile(std::move(TempFile)), EntryPath(std::move(EntryPath)),
+ Task(Task) {}
+
+ ~CacheStream() {
+ // Make sure the stream is closed before committing it.
+ OS.reset();
+
+ // Open the file first to avoid racing with a cache pruner.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
+ MemoryBuffer::getOpenFile(
+ sys::fs::convertFDToNativeFile(TempFile.FD), TempFile.TmpName,
+ /*FileSize=*/-1, /*RequiresNullTerminator=*/false);
+ if (!MBOrErr)
+ report_fatal_error(Twine("Failed to open new cache file ") +
+ TempFile.TmpName + ": " +
+ MBOrErr.getError().message() + "\n");
+
+ // On POSIX systems, this will atomically replace the destination if
+ // it already exists. We try to emulate this on Windows, but this may
+ // fail with a permission denied error (for example, if the destination
+ // is currently opened by another process that does not give us the
+ // sharing permissions we need). Since the existing file should be
+ // semantically equivalent to the one we are trying to write, we give
+ // AddBuffer a copy of the bytes we wrote in that case. We do this
+ // instead of just using the existing file, because the pruner might
+ // delete the file before we get a chance to use it.
+ Error E = TempFile.keep(EntryPath);
+ E = handleErrors(std::move(E), [&](const ECError &E) -> Error {
+ std::error_code EC = E.convertToErrorCode();
+ if (EC != errc::permission_denied)
+ return errorCodeToError(EC);
+
+ auto MBCopy = MemoryBuffer::getMemBufferCopy((*MBOrErr)->getBuffer(),
+ EntryPath);
+ MBOrErr = std::move(MBCopy);
+
+ // FIXME: should we consume the discard error?
+ consumeError(TempFile.discard());
+
+ return Error::success();
+ });
+
+ if (E)
+ report_fatal_error(Twine("Failed to rename temporary file ") +
+ TempFile.TmpName + " to " + EntryPath + ": " +
+ toString(std::move(E)) + "\n");
+
+ AddBuffer(Task, std::move(*MBOrErr));
+ }
+ };
+
+ return [=](size_t Task) -> std::unique_ptr<NativeObjectStream> {
+ // Write to a temporary to avoid race condition
+ SmallString<64> TempFilenameModel;
+ sys::path::append(TempFilenameModel, CacheDirectoryPath, "Thin-%%%%%%.tmp.o");
+ Expected<sys::fs::TempFile> Temp = sys::fs::TempFile::create(
+ TempFilenameModel, sys::fs::owner_read | sys::fs::owner_write);
+ if (!Temp) {
+ errs() << "Error: " << toString(Temp.takeError()) << "\n";
+ report_fatal_error("ThinLTO: Can't get a temporary file");
+ }
+
+ // This CacheStream will move the temporary file into the cache when done.
+ return std::make_unique<CacheStream>(
+ std::make_unique<raw_fd_ostream>(Temp->FD, /* ShouldClose */ false),
+ AddBuffer, std::move(*Temp), EntryPath.str(), Task);
+ };
+ };
+}
diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
new file mode 100644
index 0000000000000..1e345e7dd89e5
--- /dev/null
+++ b/llvm/lib/LTO/LTO.cpp
@@ -0,0 +1,1412 @@
+//===-LTO.cpp - LLVM Link Time Optimizer ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements functions and classes used to support LTO.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/LTO/LTO.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/Analysis/TargetTransformInfo.h"
+#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
+#include "llvm/CodeGen/Analysis.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/IR/AutoUpgrade.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Mangler.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/IR/RemarkStreamer.h"
+#include "llvm/LTO/LTOBackend.h"
+#include "llvm/LTO/SummaryBasedOptimizations.h"
+#include "llvm/Linker/IRMover.h"
+#include "llvm/Object/IRObjectFile.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/SHA1.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/ThreadPool.h"
+#include "llvm/Support/Threading.h"
+#include "llvm/Support/VCSRevision.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Target/TargetOptions.h"
+#include "llvm/Transforms/IPO.h"
+#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#include "llvm/Transforms/IPO/WholeProgramDevirt.h"
+#include "llvm/Transforms/Utils/FunctionImportUtils.h"
+#include "llvm/Transforms/Utils/SplitModule.h"
+
+#include <set>
+
+using namespace llvm;
+using namespace lto;
+using namespace object;
+
+#define DEBUG_TYPE "lto"
+
+static cl::opt<bool>
+ DumpThinCGSCCs("dump-thin-cg-sccs", cl::init(false), cl::Hidden,
+ cl::desc("Dump the SCCs in the ThinLTO index's callgraph"));
+
+/// Enable global value internalization in LTO.
+cl::opt<bool> EnableLTOInternalization(
+ "enable-lto-internalization", cl::init(true), cl::Hidden,
+ cl::desc("Enable global value internalization in LTO"));
+
+// Computes a unique hash for the Module considering the current list of
+// export/import and other global analysis results.
+// The hash is produced in \p Key.
+void llvm::computeLTOCacheKey(
+ SmallString<40> &Key, const Config &Conf, const ModuleSummaryIndex &Index,
+ StringRef ModuleID, const FunctionImporter::ImportMapTy &ImportList,
+ const FunctionImporter::ExportSetTy &ExportList,
+ const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
+ const GVSummaryMapTy &DefinedGlobals,
+ const std::set<GlobalValue::GUID> &CfiFunctionDefs,
+ const std::set<GlobalValue::GUID> &CfiFunctionDecls) {
+ // Compute the unique hash for this entry.
+ // This is based on the current compiler version, the module itself, the
+ // export list, the hash for every single module in the import list, the
+ // list of ResolvedODR for the module, and the list of preserved symbols.
+ SHA1 Hasher;
+
+ // Start with the compiler revision
+ Hasher.update(LLVM_VERSION_STRING);
+#ifdef LLVM_REVISION
+ Hasher.update(LLVM_REVISION);
+#endif
+
+ // Include the parts of the LTO configuration that affect code generation.
+ auto AddString = [&](StringRef Str) {
+ Hasher.update(Str);
+ Hasher.update(ArrayRef<uint8_t>{0});
+ };
+ auto AddUnsigned = [&](unsigned I) {
+ uint8_t Data[4];
+ Data[0] = I;
+ Data[1] = I >> 8;
+ Data[2] = I >> 16;
+ Data[3] = I >> 24;
+ Hasher.update(ArrayRef<uint8_t>{Data, 4});
+ };
+ auto AddUint64 = [&](uint64_t I) {
+ uint8_t Data[8];
+ Data[0] = I;
+ Data[1] = I >> 8;
+ Data[2] = I >> 16;
+ Data[3] = I >> 24;
+ Data[4] = I >> 32;
+ Data[5] = I >> 40;
+ Data[6] = I >> 48;
+ Data[7] = I >> 56;
+ Hasher.update(ArrayRef<uint8_t>{Data, 8});
+ };
+ AddString(Conf.CPU);
+ // FIXME: Hash more of Options. For now all clients initialize Options from
+ // command-line flags (which is unsupported in production), but may set
+ // RelaxELFRelocations. The clang driver can also pass FunctionSections,
+ // DataSections and DebuggerTuning via command line flags.
+ AddUnsigned(Conf.Options.RelaxELFRelocations);
+ AddUnsigned(Conf.Options.FunctionSections);
+ AddUnsigned(Conf.Options.DataSections);
+ AddUnsigned((unsigned)Conf.Options.DebuggerTuning);
+ for (auto &A : Conf.MAttrs)
+ AddString(A);
+ if (Conf.RelocModel)
+ AddUnsigned(*Conf.RelocModel);
+ else
+ AddUnsigned(-1);
+ if (Conf.CodeModel)
+ AddUnsigned(*Conf.CodeModel);
+ else
+ AddUnsigned(-1);
+ AddUnsigned(Conf.CGOptLevel);
+ AddUnsigned(Conf.CGFileType);
+ AddUnsigned(Conf.OptLevel);
+ AddUnsigned(Conf.UseNewPM);
+ AddUnsigned(Conf.Freestanding);
+ AddString(Conf.OptPipeline);
+ AddString(Conf.AAPipeline);
+ AddString(Conf.OverrideTriple);
+ AddString(Conf.DefaultTriple);
+ AddString(Conf.DwoDir);
+
+ // Include the hash for the current module
+ auto ModHash = Index.getModuleHash(ModuleID);
+ Hasher.update(ArrayRef<uint8_t>((uint8_t *)&ModHash[0], sizeof(ModHash)));
+ for (auto F : ExportList)
+ // The export list can impact the internalization, be conservative here
+ Hasher.update(ArrayRef<uint8_t>((uint8_t *)&F, sizeof(F)));
+
+ // Include the hash for every module we import functions from. The set of
+ // imported symbols for each module may affect code generation and is
+ // sensitive to link order, so include that as well.
+ for (auto &Entry : ImportList) {
+ auto ModHash = Index.getModuleHash(Entry.first());
+ Hasher.update(ArrayRef<uint8_t>((uint8_t *)&ModHash[0], sizeof(ModHash)));
+
+ AddUint64(Entry.second.size());
+ for (auto &Fn : Entry.second)
+ AddUint64(Fn);
+ }
+
+ // Include the hash for the resolved ODR.
+ for (auto &Entry : ResolvedODR) {
+ Hasher.update(ArrayRef<uint8_t>((const uint8_t *)&Entry.first,
+ sizeof(GlobalValue::GUID)));
+ Hasher.update(ArrayRef<uint8_t>((const uint8_t *)&Entry.second,
+ sizeof(GlobalValue::LinkageTypes)));
+ }
+
+ // Members of CfiFunctionDefs and CfiFunctionDecls that are referenced or
+ // defined in this module.
+ std::set<GlobalValue::GUID> UsedCfiDefs;
+ std::set<GlobalValue::GUID> UsedCfiDecls;
+
+ // Typeids used in this module.
+ std::set<GlobalValue::GUID> UsedTypeIds;
+
+ auto AddUsedCfiGlobal = [&](GlobalValue::GUID ValueGUID) {
+ if (CfiFunctionDefs.count(ValueGUID))
+ UsedCfiDefs.insert(ValueGUID);
+ if (CfiFunctionDecls.count(ValueGUID))
+ UsedCfiDecls.insert(ValueGUID);
+ };
+
+ auto AddUsedThings = [&](GlobalValueSummary *GS) {
+ if (!GS) return;
+ AddUnsigned(GS->isLive());
+ AddUnsigned(GS->canAutoHide());
+ for (const ValueInfo &VI : GS->refs()) {
+ AddUnsigned(VI.isDSOLocal());
+ AddUsedCfiGlobal(VI.getGUID());
+ }
+ if (auto *GVS = dyn_cast<GlobalVarSummary>(GS)) {
+ AddUnsigned(GVS->maybeReadOnly());
+ AddUnsigned(GVS->maybeWriteOnly());
+ }
+ if (auto *FS = dyn_cast<FunctionSummary>(GS)) {
+ for (auto &TT : FS->type_tests())
+ UsedTypeIds.insert(TT);
+ for (auto &TT : FS->type_test_assume_vcalls())
+ UsedTypeIds.insert(TT.GUID);
+ for (auto &TT : FS->type_checked_load_vcalls())
+ UsedTypeIds.insert(TT.GUID);
+ for (auto &TT : FS->type_test_assume_const_vcalls())
+ UsedTypeIds.insert(TT.VFunc.GUID);
+ for (auto &TT : FS->type_checked_load_const_vcalls())
+ UsedTypeIds.insert(TT.VFunc.GUID);
+ for (auto &ET : FS->calls()) {
+ AddUnsigned(ET.first.isDSOLocal());
+ AddUsedCfiGlobal(ET.first.getGUID());
+ }
+ }
+ };
+
+ // Include the hash for the linkage type to reflect internalization and weak
+ // resolution, and collect any used type identifier resolutions.
+ for (auto &GS : DefinedGlobals) {
+ GlobalValue::LinkageTypes Linkage = GS.second->linkage();
+ Hasher.update(
+ ArrayRef<uint8_t>((const uint8_t *)&Linkage, sizeof(Linkage)));
+ AddUsedCfiGlobal(GS.first);
+ AddUsedThings(GS.second);
+ }
+
+ // Imported functions may introduce new uses of type identifier resolutions,
+ // so we need to collect their used resolutions as well.
+ for (auto &ImpM : ImportList)
+ for (auto &ImpF : ImpM.second) {
+ GlobalValueSummary *S = Index.findSummaryInModule(ImpF, ImpM.first());
+ AddUsedThings(S);
+ // If this is an alias, we also care about any types/etc. that the aliasee
+ // may reference.
+ if (auto *AS = dyn_cast_or_null<AliasSummary>(S))
+ AddUsedThings(AS->getBaseObject());
+ }
+
+ auto AddTypeIdSummary = [&](StringRef TId, const TypeIdSummary &S) {
+ AddString(TId);
+
+ AddUnsigned(S.TTRes.TheKind);
+ AddUnsigned(S.TTRes.SizeM1BitWidth);
+
+ AddUint64(S.TTRes.AlignLog2);
+ AddUint64(S.TTRes.SizeM1);
+ AddUint64(S.TTRes.BitMask);
+ AddUint64(S.TTRes.InlineBits);
+
+ AddUint64(S.WPDRes.size());
+ for (auto &WPD : S.WPDRes) {
+ AddUnsigned(WPD.first);
+ AddUnsigned(WPD.second.TheKind);
+ AddString(WPD.second.SingleImplName);
+
+ AddUint64(WPD.second.ResByArg.size());
+ for (auto &ByArg : WPD.second.ResByArg) {
+ AddUint64(ByArg.first.size());
+ for (uint64_t Arg : ByArg.first)
+ AddUint64(Arg);
+ AddUnsigned(ByArg.second.TheKind);
+ AddUint64(ByArg.second.Info);
+ AddUnsigned(ByArg.second.Byte);
+ AddUnsigned(ByArg.second.Bit);
+ }
+ }
+ };
+
+ // Include the hash for all type identifiers used by this module.
+ for (GlobalValue::GUID TId : UsedTypeIds) {
+ auto TidIter = Index.typeIds().equal_range(TId);
+ for (auto It = TidIter.first; It != TidIter.second; ++It)
+ AddTypeIdSummary(It->second.first, It->second.second);
+ }
+
+ AddUnsigned(UsedCfiDefs.size());
+ for (auto &V : UsedCfiDefs)
+ AddUint64(V);
+
+ AddUnsigned(UsedCfiDecls.size());
+ for (auto &V : UsedCfiDecls)
+ AddUint64(V);
+
+ if (!Conf.SampleProfile.empty()) {
+ auto FileOrErr = MemoryBuffer::getFile(Conf.SampleProfile);
+ if (FileOrErr) {
+ Hasher.update(FileOrErr.get()->getBuffer());
+
+ if (!Conf.ProfileRemapping.empty()) {
+ FileOrErr = MemoryBuffer::getFile(Conf.ProfileRemapping);
+ if (FileOrErr)
+ Hasher.update(FileOrErr.get()->getBuffer());
+ }
+ }
+ }
+
+ Key = toHex(Hasher.result());
+}
+
+static void thinLTOResolvePrevailingGUID(
+ ValueInfo VI, DenseSet<GlobalValueSummary *> &GlobalInvolvedWithAlias,
+ function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
+ isPrevailing,
+ function_ref<void(StringRef, GlobalValue::GUID, GlobalValue::LinkageTypes)>
+ recordNewLinkage,
+ const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) {
+ for (auto &S : VI.getSummaryList()) {
+ GlobalValue::LinkageTypes OriginalLinkage = S->linkage();
+ // Ignore local and appending linkage values since the linker
+ // doesn't resolve them.
+ if (GlobalValue::isLocalLinkage(OriginalLinkage) ||
+ GlobalValue::isAppendingLinkage(S->linkage()))
+ continue;
+ // We need to emit only one of these. The prevailing module will keep it,
+ // but turned into a weak, while the others will drop it when possible.
+ // This is both a compile-time optimization and a correctness
+ // transformation. This is necessary for correctness when we have exported
+ // a reference - we need to convert the linkonce to weak to
+ // ensure a copy is kept to satisfy the exported reference.
+ // FIXME: We may want to split the compile time and correctness
+ // aspects into separate routines.
+ if (isPrevailing(VI.getGUID(), S.get())) {
+ if (GlobalValue::isLinkOnceLinkage(OriginalLinkage)) {
+ S->setLinkage(GlobalValue::getWeakLinkage(
+ GlobalValue::isLinkOnceODRLinkage(OriginalLinkage)));
+ // The kept copy is eligible for auto-hiding (hidden visibility) if all
+ // copies were (i.e. they were all linkonce_odr global unnamed addr).
+ // If any copy is not (e.g. it was originally weak_odr), then the symbol
+ // must remain externally available (e.g. a weak_odr from an explicitly
+ // instantiated template). Additionally, if it is in the
+ // GUIDPreservedSymbols set, that means that it is visibile outside
+ // the summary (e.g. in a native object or a bitcode file without
+ // summary), and in that case we cannot hide it as it isn't possible to
+ // check all copies.
+ S->setCanAutoHide(VI.canAutoHide() &&
+ !GUIDPreservedSymbols.count(VI.getGUID()));
+ }
+ }
+ // Alias and aliasee can't be turned into available_externally.
+ else if (!isa<AliasSummary>(S.get()) &&
+ !GlobalInvolvedWithAlias.count(S.get()))
+ S->setLinkage(GlobalValue::AvailableExternallyLinkage);
+ if (S->linkage() != OriginalLinkage)
+ recordNewLinkage(S->modulePath(), VI.getGUID(), S->linkage());
+ }
+}
+
+/// Resolve linkage for prevailing symbols in the \p Index.
+//
+// We'd like to drop these functions if they are no longer referenced in the
+// current module. However there is a chance that another module is still
+// referencing them because of the import. We make sure we always emit at least
+// one copy.
+void llvm::thinLTOResolvePrevailingInIndex(
+ ModuleSummaryIndex &Index,
+ function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
+ isPrevailing,
+ function_ref<void(StringRef, GlobalValue::GUID, GlobalValue::LinkageTypes)>
+ recordNewLinkage,
+ const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) {
+ // We won't optimize the globals that are referenced by an alias for now
+ // Ideally we should turn the alias into a global and duplicate the definition
+ // when needed.
+ DenseSet<GlobalValueSummary *> GlobalInvolvedWithAlias;
+ for (auto &I : Index)
+ for (auto &S : I.second.SummaryList)
+ if (auto AS = dyn_cast<AliasSummary>(S.get()))
+ GlobalInvolvedWithAlias.insert(&AS->getAliasee());
+
+ for (auto &I : Index)
+ thinLTOResolvePrevailingGUID(Index.getValueInfo(I), GlobalInvolvedWithAlias,
+ isPrevailing, recordNewLinkage,
+ GUIDPreservedSymbols);
+}
+
+static bool isWeakObjectWithRWAccess(GlobalValueSummary *GVS) {
+ if (auto *VarSummary = dyn_cast<GlobalVarSummary>(GVS->getBaseObject()))
+ return !VarSummary->maybeReadOnly() && !VarSummary->maybeWriteOnly() &&
+ (VarSummary->linkage() == GlobalValue::WeakODRLinkage ||
+ VarSummary->linkage() == GlobalValue::LinkOnceODRLinkage);
+ return false;
+}
+
+static void thinLTOInternalizeAndPromoteGUID(
+ GlobalValueSummaryList &GVSummaryList, GlobalValue::GUID GUID,
+ function_ref<bool(StringRef, GlobalValue::GUID)> isExported,
+ function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
+ isPrevailing) {
+ for (auto &S : GVSummaryList) {
+ if (isExported(S->modulePath(), GUID)) {
+ if (GlobalValue::isLocalLinkage(S->linkage()))
+ S->setLinkage(GlobalValue::ExternalLinkage);
+ } else if (EnableLTOInternalization &&
+ // Ignore local and appending linkage values since the linker
+ // doesn't resolve them.
+ !GlobalValue::isLocalLinkage(S->linkage()) &&
+ (!GlobalValue::isInterposableLinkage(S->linkage()) ||
+ isPrevailing(GUID, S.get())) &&
+ S->linkage() != GlobalValue::AppendingLinkage &&
+ // We can't internalize available_externally globals because this
+ // can break function pointer equality.
+ S->linkage() != GlobalValue::AvailableExternallyLinkage &&
+ // Functions and read-only variables with linkonce_odr and
+ // weak_odr linkage can be internalized. We can't internalize
+ // linkonce_odr and weak_odr variables which are both modified
+ // and read somewhere in the program because reads and writes
+ // will become inconsistent.
+ !isWeakObjectWithRWAccess(S.get()))
+ S->setLinkage(GlobalValue::InternalLinkage);
+ }
+}
+
+// Update the linkages in the given \p Index to mark exported values
+// as external and non-exported values as internal.
+void llvm::thinLTOInternalizeAndPromoteInIndex(
+ ModuleSummaryIndex &Index,
+ function_ref<bool(StringRef, GlobalValue::GUID)> isExported,
+ function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
+ isPrevailing) {
+ for (auto &I : Index)
+ thinLTOInternalizeAndPromoteGUID(I.second.SummaryList, I.first, isExported,
+ isPrevailing);
+}
+
+// Requires a destructor for std::vector<InputModule>.
+InputFile::~InputFile() = default;
+
+Expected<std::unique_ptr<InputFile>> InputFile::create(MemoryBufferRef Object) {
+ std::unique_ptr<InputFile> File(new InputFile);
+
+ Expected<IRSymtabFile> FOrErr = readIRSymtab(Object);
+ if (!FOrErr)
+ return FOrErr.takeError();
+
+ File->TargetTriple = FOrErr->TheReader.getTargetTriple();
+ File->SourceFileName = FOrErr->TheReader.getSourceFileName();
+ File->COFFLinkerOpts = FOrErr->TheReader.getCOFFLinkerOpts();
+ File->DependentLibraries = FOrErr->TheReader.getDependentLibraries();
+ File->ComdatTable = FOrErr->TheReader.getComdatTable();
+
+ for (unsigned I = 0; I != FOrErr->Mods.size(); ++I) {
+ size_t Begin = File->Symbols.size();
+ for (const irsymtab::Reader::SymbolRef &Sym :
+ FOrErr->TheReader.module_symbols(I))
+ // Skip symbols that are irrelevant to LTO. Note that this condition needs
+ // to match the one in Skip() in LTO::addRegularLTO().
+ if (Sym.isGlobal() && !Sym.isFormatSpecific())
+ File->Symbols.push_back(Sym);
+ File->ModuleSymIndices.push_back({Begin, File->Symbols.size()});
+ }
+
+ File->Mods = FOrErr->Mods;
+ File->Strtab = std::move(FOrErr->Strtab);
+ return std::move(File);
+}
+
+StringRef InputFile::getName() const {
+ return Mods[0].getModuleIdentifier();
+}
+
+BitcodeModule &InputFile::getSingleBitcodeModule() {
+ assert(Mods.size() == 1 && "Expect only one bitcode module");
+ return Mods[0];
+}
+
+LTO::RegularLTOState::RegularLTOState(unsigned ParallelCodeGenParallelismLevel,
+ Config &Conf)
+ : ParallelCodeGenParallelismLevel(ParallelCodeGenParallelismLevel),
+ Ctx(Conf), CombinedModule(std::make_unique<Module>("ld-temp.o", Ctx)),
+ Mover(std::make_unique<IRMover>(*CombinedModule)) {}
+
+LTO::ThinLTOState::ThinLTOState(ThinBackend Backend)
+ : Backend(Backend), CombinedIndex(/*HaveGVs*/ false) {
+ if (!Backend)
+ this->Backend =
+ createInProcessThinBackend(llvm::heavyweight_hardware_concurrency());
+}
+
+LTO::LTO(Config Conf, ThinBackend Backend,
+ unsigned ParallelCodeGenParallelismLevel)
+ : Conf(std::move(Conf)),
+ RegularLTO(ParallelCodeGenParallelismLevel, this->Conf),
+ ThinLTO(std::move(Backend)) {}
+
+// Requires a destructor for MapVector<BitcodeModule>.
+LTO::~LTO() = default;
+
+// Add the symbols in the given module to the GlobalResolutions map, and resolve
+// their partitions.
+void LTO::addModuleToGlobalRes(ArrayRef<InputFile::Symbol> Syms,
+ ArrayRef<SymbolResolution> Res,
+ unsigned Partition, bool InSummary) {
+ auto *ResI = Res.begin();
+ auto *ResE = Res.end();
+ (void)ResE;
+ for (const InputFile::Symbol &Sym : Syms) {
+ assert(ResI != ResE);
+ SymbolResolution Res = *ResI++;
+
+ StringRef Name = Sym.getName();
+ Triple TT(RegularLTO.CombinedModule->getTargetTriple());
+ // Strip the __imp_ prefix from COFF dllimport symbols (similar to the
+ // way they are handled by lld), otherwise we can end up with two
+ // global resolutions (one with and one for a copy of the symbol without).
+ if (TT.isOSBinFormatCOFF() && Name.startswith("__imp_"))
+ Name = Name.substr(strlen("__imp_"));
+ auto &GlobalRes = GlobalResolutions[Name];
+ GlobalRes.UnnamedAddr &= Sym.isUnnamedAddr();
+ if (Res.Prevailing) {
+ assert(!GlobalRes.Prevailing &&
+ "Multiple prevailing defs are not allowed");
+ GlobalRes.Prevailing = true;
+ GlobalRes.IRName = Sym.getIRName();
+ } else if (!GlobalRes.Prevailing && GlobalRes.IRName.empty()) {
+ // Sometimes it can be two copies of symbol in a module and prevailing
+ // symbol can have no IR name. That might happen if symbol is defined in
+ // module level inline asm block. In case we have multiple modules with
+ // the same symbol we want to use IR name of the prevailing symbol.
+ // Otherwise, if we haven't seen a prevailing symbol, set the name so that
+ // we can later use it to check if there is any prevailing copy in IR.
+ GlobalRes.IRName = Sym.getIRName();
+ }
+
+ // 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
+ // regular object, is referenced from llvm.compiler_used, or was already
+ // recorded as being referenced from a different partition.
+ if (Res.LinkerRedefined || Res.VisibleToRegularObj || Sym.isUsed() ||
+ (GlobalRes.Partition != GlobalResolution::Unknown &&
+ GlobalRes.Partition != Partition)) {
+ GlobalRes.Partition = GlobalResolution::External;
+ } else
+ // First recorded reference, save the current partition.
+ GlobalRes.Partition = Partition;
+
+ // Flag as visible outside of summary if visible from a regular object or
+ // from a module that does not have a summary.
+ GlobalRes.VisibleOutsideSummary |=
+ (Res.VisibleToRegularObj || Sym.isUsed() || !InSummary);
+ }
+}
+
+static void writeToResolutionFile(raw_ostream &OS, InputFile *Input,
+ ArrayRef<SymbolResolution> Res) {
+ StringRef Path = Input->getName();
+ OS << Path << '\n';
+ auto ResI = Res.begin();
+ for (const InputFile::Symbol &Sym : Input->symbols()) {
+ assert(ResI != Res.end());
+ SymbolResolution Res = *ResI++;
+
+ OS << "-r=" << Path << ',' << Sym.getName() << ',';
+ if (Res.Prevailing)
+ OS << 'p';
+ if (Res.FinalDefinitionInLinkageUnit)
+ OS << 'l';
+ if (Res.VisibleToRegularObj)
+ OS << 'x';
+ if (Res.LinkerRedefined)
+ OS << 'r';
+ OS << '\n';
+ }
+ OS.flush();
+ assert(ResI == Res.end());
+}
+
+Error LTO::add(std::unique_ptr<InputFile> Input,
+ ArrayRef<SymbolResolution> Res) {
+ assert(!CalledGetMaxTasks);
+
+ if (Conf.ResolutionFile)
+ writeToResolutionFile(*Conf.ResolutionFile, Input.get(), Res);
+
+ if (RegularLTO.CombinedModule->getTargetTriple().empty())
+ RegularLTO.CombinedModule->setTargetTriple(Input->getTargetTriple());
+
+ const SymbolResolution *ResI = Res.begin();
+ for (unsigned I = 0; I != Input->Mods.size(); ++I)
+ if (Error Err = addModule(*Input, I, ResI, Res.end()))
+ return Err;
+
+ assert(ResI == Res.end());
+ return Error::success();
+}
+
+Error LTO::addModule(InputFile &Input, unsigned ModI,
+ const SymbolResolution *&ResI,
+ const SymbolResolution *ResE) {
+ Expected<BitcodeLTOInfo> LTOInfo = Input.Mods[ModI].getLTOInfo();
+ if (!LTOInfo)
+ return LTOInfo.takeError();
+
+ if (EnableSplitLTOUnit.hasValue()) {
+ // If only some modules were split, flag this in the index so that
+ // we can skip or error on optimizations that need consistently split
+ // modules (whole program devirt and lower type tests).
+ if (EnableSplitLTOUnit.getValue() != LTOInfo->EnableSplitLTOUnit)
+ ThinLTO.CombinedIndex.setPartiallySplitLTOUnits();
+ } else
+ EnableSplitLTOUnit = LTOInfo->EnableSplitLTOUnit;
+
+ BitcodeModule BM = Input.Mods[ModI];
+ auto ModSyms = Input.module_symbols(ModI);
+ addModuleToGlobalRes(ModSyms, {ResI, ResE},
+ LTOInfo->IsThinLTO ? ThinLTO.ModuleMap.size() + 1 : 0,
+ LTOInfo->HasSummary);
+
+ if (LTOInfo->IsThinLTO)
+ return addThinLTO(BM, ModSyms, ResI, ResE);
+
+ Expected<RegularLTOState::AddedModule> ModOrErr =
+ addRegularLTO(BM, ModSyms, ResI, ResE);
+ if (!ModOrErr)
+ return ModOrErr.takeError();
+
+ if (!LTOInfo->HasSummary)
+ return linkRegularLTO(std::move(*ModOrErr), /*LivenessFromIndex=*/false);
+
+ // Regular LTO module summaries are added to a dummy module that represents
+ // the combined regular LTO module.
+ if (Error Err = BM.readSummary(ThinLTO.CombinedIndex, "", -1ull))
+ return Err;
+ RegularLTO.ModsWithSummaries.push_back(std::move(*ModOrErr));
+ return Error::success();
+}
+
+// Checks whether the given global value is in a non-prevailing comdat
+// (comdat containing values the linker indicated were not prevailing,
+// which we then dropped to available_externally), and if so, removes
+// it from the comdat. This is called for all global values to ensure the
+// comdat is empty rather than leaving an incomplete comdat. It is needed for
+// regular LTO modules, in case we are in a mixed-LTO mode (both regular
+// and thin LTO modules) compilation. Since the regular LTO module will be
+// linked first in the final native link, we want to make sure the linker
+// doesn't select any of these incomplete comdats that would be left
+// in the regular LTO module without this cleanup.
+static void
+handleNonPrevailingComdat(GlobalValue &GV,
+ std::set<const Comdat *> &NonPrevailingComdats) {
+ Comdat *C = GV.getComdat();
+ if (!C)
+ return;
+
+ if (!NonPrevailingComdats.count(C))
+ return;
+
+ // Additionally need to drop externally visible global values from the comdat
+ // to available_externally, so that there aren't multiply defined linker
+ // errors.
+ if (!GV.hasLocalLinkage())
+ GV.setLinkage(GlobalValue::AvailableExternallyLinkage);
+
+ if (auto GO = dyn_cast<GlobalObject>(&GV))
+ GO->setComdat(nullptr);
+}
+
+// Add a regular LTO object to the link.
+// The resulting module needs to be linked into the combined LTO module with
+// linkRegularLTO.
+Expected<LTO::RegularLTOState::AddedModule>
+LTO::addRegularLTO(BitcodeModule BM, ArrayRef<InputFile::Symbol> Syms,
+ const SymbolResolution *&ResI,
+ const SymbolResolution *ResE) {
+ RegularLTOState::AddedModule Mod;
+ Expected<std::unique_ptr<Module>> MOrErr =
+ BM.getLazyModule(RegularLTO.Ctx, /*ShouldLazyLoadMetadata*/ true,
+ /*IsImporting*/ false);
+ if (!MOrErr)
+ return MOrErr.takeError();
+ Module &M = **MOrErr;
+ Mod.M = std::move(*MOrErr);
+
+ if (Error Err = M.materializeMetadata())
+ return std::move(Err);
+ UpgradeDebugInfo(M);
+
+ ModuleSymbolTable SymTab;
+ SymTab.addModule(&M);
+
+ for (GlobalVariable &GV : M.globals())
+ if (GV.hasAppendingLinkage())
+ Mod.Keep.push_back(&GV);
+
+ DenseSet<GlobalObject *> AliasedGlobals;
+ for (auto &GA : M.aliases())
+ if (GlobalObject *GO = GA.getBaseObject())
+ AliasedGlobals.insert(GO);
+
+ // In this function we need IR GlobalValues matching the symbols in Syms
+ // (which is not backed by a module), so we need to enumerate them in the same
+ // order. The symbol enumeration order of a ModuleSymbolTable intentionally
+ // matches the order of an irsymtab, but when we read the irsymtab in
+ // InputFile::create we omit some symbols that are irrelevant to LTO. The
+ // Skip() function skips the same symbols from the module as InputFile does
+ // from the symbol table.
+ auto MsymI = SymTab.symbols().begin(), MsymE = SymTab.symbols().end();
+ auto Skip = [&]() {
+ while (MsymI != MsymE) {
+ auto Flags = SymTab.getSymbolFlags(*MsymI);
+ if ((Flags & object::BasicSymbolRef::SF_Global) &&
+ !(Flags & object::BasicSymbolRef::SF_FormatSpecific))
+ return;
+ ++MsymI;
+ }
+ };
+ Skip();
+
+ std::set<const Comdat *> NonPrevailingComdats;
+ for (const InputFile::Symbol &Sym : Syms) {
+ assert(ResI != ResE);
+ SymbolResolution Res = *ResI++;
+
+ assert(MsymI != MsymE);
+ ModuleSymbolTable::Symbol Msym = *MsymI++;
+ Skip();
+
+ if (GlobalValue *GV = Msym.dyn_cast<GlobalValue *>()) {
+ if (Res.Prevailing) {
+ if (Sym.isUndefined())
+ continue;
+ Mod.Keep.push_back(GV);
+ // For symbols re-defined with linker -wrap and -defsym options,
+ // set the linkage to weak to inhibit IPO. The linkage will be
+ // restored by the linker.
+ if (Res.LinkerRedefined)
+ GV->setLinkage(GlobalValue::WeakAnyLinkage);
+
+ GlobalValue::LinkageTypes OriginalLinkage = GV->getLinkage();
+ if (GlobalValue::isLinkOnceLinkage(OriginalLinkage))
+ GV->setLinkage(GlobalValue::getWeakLinkage(
+ GlobalValue::isLinkOnceODRLinkage(OriginalLinkage)));
+ } else if (isa<GlobalObject>(GV) &&
+ (GV->hasLinkOnceODRLinkage() || GV->hasWeakODRLinkage() ||
+ GV->hasAvailableExternallyLinkage()) &&
+ !AliasedGlobals.count(cast<GlobalObject>(GV))) {
+ // Any of the above three types of linkage indicates that the
+ // chosen prevailing symbol will have the same semantics as this copy of
+ // the symbol, so we may be able to link it with available_externally
+ // linkage. We will decide later whether to do that when we link this
+ // module (in linkRegularLTO), based on whether it is undefined.
+ Mod.Keep.push_back(GV);
+ GV->setLinkage(GlobalValue::AvailableExternallyLinkage);
+ if (GV->hasComdat())
+ NonPrevailingComdats.insert(GV->getComdat());
+ cast<GlobalObject>(GV)->setComdat(nullptr);
+ }
+
+ // Set the 'local' flag based on the linker resolution for this symbol.
+ if (Res.FinalDefinitionInLinkageUnit) {
+ GV->setDSOLocal(true);
+ if (GV->hasDLLImportStorageClass())
+ GV->setDLLStorageClass(GlobalValue::DLLStorageClassTypes::
+ DefaultStorageClass);
+ }
+ }
+ // Common resolution: collect the maximum size/alignment over all commons.
+ // We also record if we see an instance of a common as prevailing, so that
+ // if none is prevailing we can ignore it later.
+ if (Sym.isCommon()) {
+ // FIXME: We should figure out what to do about commons defined by asm.
+ // For now they aren't reported correctly by ModuleSymbolTable.
+ auto &CommonRes = RegularLTO.Commons[Sym.getIRName()];
+ CommonRes.Size = std::max(CommonRes.Size, Sym.getCommonSize());
+ CommonRes.Align =
+ std::max(CommonRes.Align, MaybeAlign(Sym.getCommonAlignment()));
+ CommonRes.Prevailing |= Res.Prevailing;
+ }
+
+ }
+ if (!M.getComdatSymbolTable().empty())
+ for (GlobalValue &GV : M.global_values())
+ handleNonPrevailingComdat(GV, NonPrevailingComdats);
+ assert(MsymI == MsymE);
+ return std::move(Mod);
+}
+
+Error LTO::linkRegularLTO(RegularLTOState::AddedModule Mod,
+ bool LivenessFromIndex) {
+ std::vector<GlobalValue *> Keep;
+ for (GlobalValue *GV : Mod.Keep) {
+ if (LivenessFromIndex && !ThinLTO.CombinedIndex.isGUIDLive(GV->getGUID()))
+ continue;
+
+ if (!GV->hasAvailableExternallyLinkage()) {
+ Keep.push_back(GV);
+ continue;
+ }
+
+ // Only link available_externally definitions if we don't already have a
+ // definition.
+ GlobalValue *CombinedGV =
+ RegularLTO.CombinedModule->getNamedValue(GV->getName());
+ if (CombinedGV && !CombinedGV->isDeclaration())
+ continue;
+
+ Keep.push_back(GV);
+ }
+
+ return RegularLTO.Mover->move(std::move(Mod.M), Keep,
+ [](GlobalValue &, IRMover::ValueAdder) {},
+ /* IsPerformingImport */ false);
+}
+
+// Add a ThinLTO module to the link.
+Error LTO::addThinLTO(BitcodeModule BM, ArrayRef<InputFile::Symbol> Syms,
+ const SymbolResolution *&ResI,
+ const SymbolResolution *ResE) {
+ if (Error Err =
+ BM.readSummary(ThinLTO.CombinedIndex, BM.getModuleIdentifier(),
+ ThinLTO.ModuleMap.size()))
+ return Err;
+
+ for (const InputFile::Symbol &Sym : Syms) {
+ assert(ResI != ResE);
+ SymbolResolution Res = *ResI++;
+
+ if (!Sym.getIRName().empty()) {
+ auto GUID = GlobalValue::getGUID(GlobalValue::getGlobalIdentifier(
+ Sym.getIRName(), GlobalValue::ExternalLinkage, ""));
+ if (Res.Prevailing) {
+ ThinLTO.PrevailingModuleForGUID[GUID] = BM.getModuleIdentifier();
+
+ // For linker redefined symbols (via --wrap or --defsym) we want to
+ // switch the linkage to `weak` to prevent IPOs from happening.
+ // Find the summary in the module for this very GV and record the new
+ // linkage so that we can switch it when we import the GV.
+ if (Res.LinkerRedefined)
+ if (auto S = ThinLTO.CombinedIndex.findSummaryInModule(
+ GUID, BM.getModuleIdentifier()))
+ S->setLinkage(GlobalValue::WeakAnyLinkage);
+ }
+
+ // If the linker resolved the symbol to a local definition then mark it
+ // as local in the summary for the module we are adding.
+ if (Res.FinalDefinitionInLinkageUnit) {
+ if (auto S = ThinLTO.CombinedIndex.findSummaryInModule(
+ GUID, BM.getModuleIdentifier())) {
+ S->setDSOLocal(true);
+ }
+ }
+ }
+ }
+
+ if (!ThinLTO.ModuleMap.insert({BM.getModuleIdentifier(), BM}).second)
+ return make_error<StringError>(
+ "Expected at most one ThinLTO module per bitcode file",
+ inconvertibleErrorCode());
+
+ return Error::success();
+}
+
+unsigned LTO::getMaxTasks() const {
+ CalledGetMaxTasks = true;
+ return RegularLTO.ParallelCodeGenParallelismLevel + ThinLTO.ModuleMap.size();
+}
+
+// If only some of the modules were split, we cannot correctly handle
+// code that contains type tests or type checked loads.
+Error LTO::checkPartiallySplit() {
+ if (!ThinLTO.CombinedIndex.partiallySplitLTOUnits())
+ return Error::success();
+
+ Function *TypeTestFunc = RegularLTO.CombinedModule->getFunction(
+ Intrinsic::getName(Intrinsic::type_test));
+ Function *TypeCheckedLoadFunc = RegularLTO.CombinedModule->getFunction(
+ Intrinsic::getName(Intrinsic::type_checked_load));
+
+ // First check if there are type tests / type checked loads in the
+ // merged regular LTO module IR.
+ if ((TypeTestFunc && !TypeTestFunc->use_empty()) ||
+ (TypeCheckedLoadFunc && !TypeCheckedLoadFunc->use_empty()))
+ return make_error<StringError>(
+ "inconsistent LTO Unit splitting (recompile with -fsplit-lto-unit)",
+ inconvertibleErrorCode());
+
+ // Otherwise check if there are any recorded in the combined summary from the
+ // ThinLTO modules.
+ for (auto &P : ThinLTO.CombinedIndex) {
+ for (auto &S : P.second.SummaryList) {
+ auto *FS = dyn_cast<FunctionSummary>(S.get());
+ if (!FS)
+ continue;
+ if (!FS->type_test_assume_vcalls().empty() ||
+ !FS->type_checked_load_vcalls().empty() ||
+ !FS->type_test_assume_const_vcalls().empty() ||
+ !FS->type_checked_load_const_vcalls().empty() ||
+ !FS->type_tests().empty())
+ return make_error<StringError>(
+ "inconsistent LTO Unit splitting (recompile with -fsplit-lto-unit)",
+ inconvertibleErrorCode());
+ }
+ }
+ return Error::success();
+}
+
+Error LTO::run(AddStreamFn AddStream, NativeObjectCache Cache) {
+ // Compute "dead" symbols, we don't want to import/export these!
+ DenseSet<GlobalValue::GUID> GUIDPreservedSymbols;
+ DenseMap<GlobalValue::GUID, PrevailingType> GUIDPrevailingResolutions;
+ for (auto &Res : GlobalResolutions) {
+ // Normally resolution have IR name of symbol. We can do nothing here
+ // otherwise. See comments in GlobalResolution struct for more details.
+ if (Res.second.IRName.empty())
+ continue;
+
+ GlobalValue::GUID GUID = GlobalValue::getGUID(
+ GlobalValue::dropLLVMManglingEscape(Res.second.IRName));
+
+ if (Res.second.VisibleOutsideSummary && Res.second.Prevailing)
+ GUIDPreservedSymbols.insert(GUID);
+
+ GUIDPrevailingResolutions[GUID] =
+ Res.second.Prevailing ? PrevailingType::Yes : PrevailingType::No;
+ }
+
+ auto isPrevailing = [&](GlobalValue::GUID G) {
+ auto It = GUIDPrevailingResolutions.find(G);
+ if (It == GUIDPrevailingResolutions.end())
+ return PrevailingType::Unknown;
+ return It->second;
+ };
+ computeDeadSymbolsWithConstProp(ThinLTO.CombinedIndex, GUIDPreservedSymbols,
+ isPrevailing, Conf.OptLevel > 0);
+
+ // Setup output file to emit statistics.
+ auto StatsFileOrErr = setupStatsFile(Conf.StatsFile);
+ if (!StatsFileOrErr)
+ return StatsFileOrErr.takeError();
+ std::unique_ptr<ToolOutputFile> StatsFile = std::move(StatsFileOrErr.get());
+
+ // Finalize linking of regular LTO modules containing summaries now that
+ // we have computed liveness information.
+ for (auto &M : RegularLTO.ModsWithSummaries)
+ if (Error Err = linkRegularLTO(std::move(M),
+ /*LivenessFromIndex=*/true))
+ return Err;
+
+ // Ensure we don't have inconsistently split LTO units with type tests.
+ if (Error Err = checkPartiallySplit())
+ return Err;
+
+ Error Result = runRegularLTO(AddStream);
+ if (!Result)
+ Result = runThinLTO(AddStream, Cache, GUIDPreservedSymbols);
+
+ if (StatsFile)
+ PrintStatisticsJSON(StatsFile->os());
+
+ return Result;
+}
+
+Error LTO::runRegularLTO(AddStreamFn AddStream) {
+ // Make sure commons have the right size/alignment: we kept the largest from
+ // all the prevailing when adding the inputs, and we apply it here.
+ const DataLayout &DL = RegularLTO.CombinedModule->getDataLayout();
+ for (auto &I : RegularLTO.Commons) {
+ if (!I.second.Prevailing)
+ // Don't do anything if no instance of this common was prevailing.
+ continue;
+ GlobalVariable *OldGV = RegularLTO.CombinedModule->getNamedGlobal(I.first);
+ if (OldGV && DL.getTypeAllocSize(OldGV->getValueType()) == I.second.Size) {
+ // Don't create a new global if the type is already correct, just make
+ // sure the alignment is correct.
+ OldGV->setAlignment(I.second.Align);
+ continue;
+ }
+ ArrayType *Ty =
+ ArrayType::get(Type::getInt8Ty(RegularLTO.Ctx), I.second.Size);
+ auto *GV = new GlobalVariable(*RegularLTO.CombinedModule, Ty, false,
+ GlobalValue::CommonLinkage,
+ ConstantAggregateZero::get(Ty), "");
+ GV->setAlignment(I.second.Align);
+ if (OldGV) {
+ OldGV->replaceAllUsesWith(ConstantExpr::getBitCast(GV, OldGV->getType()));
+ GV->takeName(OldGV);
+ OldGV->eraseFromParent();
+ } else {
+ GV->setName(I.first);
+ }
+ }
+
+ if (Conf.PreOptModuleHook &&
+ !Conf.PreOptModuleHook(0, *RegularLTO.CombinedModule))
+ return Error::success();
+
+ if (!Conf.CodeGenOnly) {
+ for (const auto &R : GlobalResolutions) {
+ if (!R.second.isPrevailingIRSymbol())
+ continue;
+ if (R.second.Partition != 0 &&
+ R.second.Partition != GlobalResolution::External)
+ continue;
+
+ GlobalValue *GV =
+ RegularLTO.CombinedModule->getNamedValue(R.second.IRName);
+ // Ignore symbols defined in other partitions.
+ // Also skip declarations, which are not allowed to have internal linkage.
+ if (!GV || GV->hasLocalLinkage() || GV->isDeclaration())
+ continue;
+ GV->setUnnamedAddr(R.second.UnnamedAddr ? GlobalValue::UnnamedAddr::Global
+ : GlobalValue::UnnamedAddr::None);
+ if (EnableLTOInternalization && R.second.Partition == 0)
+ GV->setLinkage(GlobalValue::InternalLinkage);
+ }
+
+ RegularLTO.CombinedModule->addModuleFlag(Module::Error, "LTOPostLink", 1);
+
+ if (Conf.PostInternalizeModuleHook &&
+ !Conf.PostInternalizeModuleHook(0, *RegularLTO.CombinedModule))
+ return Error::success();
+ }
+ return backend(Conf, AddStream, RegularLTO.ParallelCodeGenParallelismLevel,
+ std::move(RegularLTO.CombinedModule), ThinLTO.CombinedIndex);
+}
+
+static const char *libcallRoutineNames[] = {
+#define HANDLE_LIBCALL(code, name) name,
+#include "llvm/IR/RuntimeLibcalls.def"
+#undef HANDLE_LIBCALL
+};
+
+ArrayRef<const char*> LTO::getRuntimeLibcallSymbols() {
+ return makeArrayRef(libcallRoutineNames);
+}
+
+/// This class defines the interface to the ThinLTO backend.
+class lto::ThinBackendProc {
+protected:
+ Config &Conf;
+ ModuleSummaryIndex &CombinedIndex;
+ const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries;
+
+public:
+ ThinBackendProc(Config &Conf, ModuleSummaryIndex &CombinedIndex,
+ const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries)
+ : Conf(Conf), CombinedIndex(CombinedIndex),
+ ModuleToDefinedGVSummaries(ModuleToDefinedGVSummaries) {}
+
+ virtual ~ThinBackendProc() {}
+ virtual Error start(
+ unsigned Task, BitcodeModule BM,
+ const FunctionImporter::ImportMapTy &ImportList,
+ const FunctionImporter::ExportSetTy &ExportList,
+ const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
+ MapVector<StringRef, BitcodeModule> &ModuleMap) = 0;
+ virtual Error wait() = 0;
+};
+
+namespace {
+class InProcessThinBackend : public ThinBackendProc {
+ ThreadPool BackendThreadPool;
+ AddStreamFn AddStream;
+ NativeObjectCache Cache;
+ std::set<GlobalValue::GUID> CfiFunctionDefs;
+ std::set<GlobalValue::GUID> CfiFunctionDecls;
+
+ Optional<Error> Err;
+ std::mutex ErrMu;
+
+public:
+ InProcessThinBackend(
+ Config &Conf, ModuleSummaryIndex &CombinedIndex,
+ unsigned ThinLTOParallelismLevel,
+ const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
+ AddStreamFn AddStream, NativeObjectCache Cache)
+ : ThinBackendProc(Conf, CombinedIndex, ModuleToDefinedGVSummaries),
+ BackendThreadPool(ThinLTOParallelismLevel),
+ AddStream(std::move(AddStream)), Cache(std::move(Cache)) {
+ for (auto &Name : CombinedIndex.cfiFunctionDefs())
+ CfiFunctionDefs.insert(
+ GlobalValue::getGUID(GlobalValue::dropLLVMManglingEscape(Name)));
+ for (auto &Name : CombinedIndex.cfiFunctionDecls())
+ CfiFunctionDecls.insert(
+ GlobalValue::getGUID(GlobalValue::dropLLVMManglingEscape(Name)));
+ }
+
+ Error runThinLTOBackendThread(
+ AddStreamFn AddStream, NativeObjectCache Cache, unsigned Task,
+ BitcodeModule BM, ModuleSummaryIndex &CombinedIndex,
+ const FunctionImporter::ImportMapTy &ImportList,
+ const FunctionImporter::ExportSetTy &ExportList,
+ const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
+ const GVSummaryMapTy &DefinedGlobals,
+ MapVector<StringRef, BitcodeModule> &ModuleMap) {
+ auto RunThinBackend = [&](AddStreamFn AddStream) {
+ LTOLLVMContext BackendContext(Conf);
+ Expected<std::unique_ptr<Module>> MOrErr = BM.parseModule(BackendContext);
+ if (!MOrErr)
+ return MOrErr.takeError();
+
+ return thinBackend(Conf, Task, AddStream, **MOrErr, CombinedIndex,
+ ImportList, DefinedGlobals, ModuleMap);
+ };
+
+ auto ModuleID = BM.getModuleIdentifier();
+
+ if (!Cache || !CombinedIndex.modulePaths().count(ModuleID) ||
+ all_of(CombinedIndex.getModuleHash(ModuleID),
+ [](uint32_t V) { return V == 0; }))
+ // Cache disabled or no entry for this module in the combined index or
+ // no module hash.
+ return RunThinBackend(AddStream);
+
+ SmallString<40> Key;
+ // The module may be cached, this helps handling it.
+ computeLTOCacheKey(Key, Conf, CombinedIndex, ModuleID, ImportList,
+ ExportList, ResolvedODR, DefinedGlobals, CfiFunctionDefs,
+ CfiFunctionDecls);
+ if (AddStreamFn CacheAddStream = Cache(Task, Key))
+ return RunThinBackend(CacheAddStream);
+
+ return Error::success();
+ }
+
+ Error start(
+ unsigned Task, BitcodeModule BM,
+ const FunctionImporter::ImportMapTy &ImportList,
+ const FunctionImporter::ExportSetTy &ExportList,
+ const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
+ MapVector<StringRef, BitcodeModule> &ModuleMap) override {
+ StringRef ModulePath = BM.getModuleIdentifier();
+ assert(ModuleToDefinedGVSummaries.count(ModulePath));
+ const GVSummaryMapTy &DefinedGlobals =
+ ModuleToDefinedGVSummaries.find(ModulePath)->second;
+ BackendThreadPool.async(
+ [=](BitcodeModule BM, ModuleSummaryIndex &CombinedIndex,
+ const FunctionImporter::ImportMapTy &ImportList,
+ const FunctionImporter::ExportSetTy &ExportList,
+ const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>
+ &ResolvedODR,
+ const GVSummaryMapTy &DefinedGlobals,
+ MapVector<StringRef, BitcodeModule> &ModuleMap) {
+ Error E = runThinLTOBackendThread(
+ AddStream, Cache, Task, BM, CombinedIndex, ImportList, ExportList,
+ ResolvedODR, DefinedGlobals, ModuleMap);
+ if (E) {
+ std::unique_lock<std::mutex> L(ErrMu);
+ if (Err)
+ Err = joinErrors(std::move(*Err), std::move(E));
+ else
+ Err = std::move(E);
+ }
+ },
+ BM, std::ref(CombinedIndex), std::ref(ImportList), std::ref(ExportList),
+ std::ref(ResolvedODR), std::ref(DefinedGlobals), std::ref(ModuleMap));
+ return Error::success();
+ }
+
+ Error wait() override {
+ BackendThreadPool.wait();
+ if (Err)
+ return std::move(*Err);
+ else
+ return Error::success();
+ }
+};
+} // end anonymous namespace
+
+ThinBackend lto::createInProcessThinBackend(unsigned ParallelismLevel) {
+ return [=](Config &Conf, ModuleSummaryIndex &CombinedIndex,
+ const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
+ AddStreamFn AddStream, NativeObjectCache Cache) {
+ return std::make_unique<InProcessThinBackend>(
+ Conf, CombinedIndex, ParallelismLevel, ModuleToDefinedGVSummaries,
+ AddStream, Cache);
+ };
+}
+
+// Given the original \p Path to an output file, replace any path
+// prefix matching \p OldPrefix with \p NewPrefix. Also, create the
+// resulting directory if it does not yet exist.
+std::string lto::getThinLTOOutputFile(const std::string &Path,
+ const std::string &OldPrefix,
+ const std::string &NewPrefix) {
+ if (OldPrefix.empty() && NewPrefix.empty())
+ return Path;
+ SmallString<128> NewPath(Path);
+ llvm::sys::path::replace_path_prefix(NewPath, OldPrefix, NewPrefix);
+ StringRef ParentPath = llvm::sys::path::parent_path(NewPath.str());
+ if (!ParentPath.empty()) {
+ // Make sure the new directory exists, creating it if necessary.
+ if (std::error_code EC = llvm::sys::fs::create_directories(ParentPath))
+ llvm::errs() << "warning: could not create directory '" << ParentPath
+ << "': " << EC.message() << '\n';
+ }
+ return NewPath.str();
+}
+
+namespace {
+class WriteIndexesThinBackend : public ThinBackendProc {
+ std::string OldPrefix, NewPrefix;
+ bool ShouldEmitImportsFiles;
+ raw_fd_ostream *LinkedObjectsFile;
+ lto::IndexWriteCallback OnWrite;
+
+public:
+ WriteIndexesThinBackend(
+ Config &Conf, ModuleSummaryIndex &CombinedIndex,
+ const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
+ std::string OldPrefix, std::string NewPrefix, bool ShouldEmitImportsFiles,
+ raw_fd_ostream *LinkedObjectsFile, lto::IndexWriteCallback OnWrite)
+ : ThinBackendProc(Conf, CombinedIndex, ModuleToDefinedGVSummaries),
+ OldPrefix(OldPrefix), NewPrefix(NewPrefix),
+ ShouldEmitImportsFiles(ShouldEmitImportsFiles),
+ LinkedObjectsFile(LinkedObjectsFile), OnWrite(OnWrite) {}
+
+ Error start(
+ unsigned Task, BitcodeModule BM,
+ const FunctionImporter::ImportMapTy &ImportList,
+ const FunctionImporter::ExportSetTy &ExportList,
+ const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
+ MapVector<StringRef, BitcodeModule> &ModuleMap) override {
+ StringRef ModulePath = BM.getModuleIdentifier();
+ std::string NewModulePath =
+ getThinLTOOutputFile(ModulePath, OldPrefix, NewPrefix);
+
+ if (LinkedObjectsFile)
+ *LinkedObjectsFile << NewModulePath << '\n';
+
+ std::map<std::string, GVSummaryMapTy> ModuleToSummariesForIndex;
+ gatherImportedSummariesForModule(ModulePath, ModuleToDefinedGVSummaries,
+ ImportList, ModuleToSummariesForIndex);
+
+ std::error_code EC;
+ raw_fd_ostream OS(NewModulePath + ".thinlto.bc", EC,
+ sys::fs::OpenFlags::OF_None);
+ if (EC)
+ return errorCodeToError(EC);
+ WriteIndexToFile(CombinedIndex, OS, &ModuleToSummariesForIndex);
+
+ if (ShouldEmitImportsFiles) {
+ EC = EmitImportsFiles(ModulePath, NewModulePath + ".imports",
+ ModuleToSummariesForIndex);
+ if (EC)
+ return errorCodeToError(EC);
+ }
+
+ if (OnWrite)
+ OnWrite(ModulePath);
+ return Error::success();
+ }
+
+ Error wait() override { return Error::success(); }
+};
+} // end anonymous namespace
+
+ThinBackend lto::createWriteIndexesThinBackend(
+ std::string OldPrefix, std::string NewPrefix, bool ShouldEmitImportsFiles,
+ raw_fd_ostream *LinkedObjectsFile, IndexWriteCallback OnWrite) {
+ return [=](Config &Conf, ModuleSummaryIndex &CombinedIndex,
+ const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
+ AddStreamFn AddStream, NativeObjectCache Cache) {
+ return std::make_unique<WriteIndexesThinBackend>(
+ Conf, CombinedIndex, ModuleToDefinedGVSummaries, OldPrefix, NewPrefix,
+ ShouldEmitImportsFiles, LinkedObjectsFile, OnWrite);
+ };
+}
+
+Error LTO::runThinLTO(AddStreamFn AddStream, NativeObjectCache Cache,
+ const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) {
+ if (ThinLTO.ModuleMap.empty())
+ return Error::success();
+
+ if (Conf.CombinedIndexHook && !Conf.CombinedIndexHook(ThinLTO.CombinedIndex))
+ return Error::success();
+
+ // Collect for each module the list of function it defines (GUID ->
+ // Summary).
+ StringMap<GVSummaryMapTy>
+ ModuleToDefinedGVSummaries(ThinLTO.ModuleMap.size());
+ ThinLTO.CombinedIndex.collectDefinedGVSummariesPerModule(
+ ModuleToDefinedGVSummaries);
+ // Create entries for any modules that didn't have any GV summaries
+ // (either they didn't have any GVs to start with, or we suppressed
+ // generation of the summaries because they e.g. had inline assembly
+ // uses that couldn't be promoted/renamed on export). This is so
+ // InProcessThinBackend::start can still launch a backend thread, which
+ // is passed the map of summaries for the module, without any special
+ // handling for this case.
+ for (auto &Mod : ThinLTO.ModuleMap)
+ if (!ModuleToDefinedGVSummaries.count(Mod.first))
+ ModuleToDefinedGVSummaries.try_emplace(Mod.first);
+
+ // Synthesize entry counts for functions in the CombinedIndex.
+ computeSyntheticCounts(ThinLTO.CombinedIndex);
+
+ StringMap<FunctionImporter::ImportMapTy> ImportLists(
+ ThinLTO.ModuleMap.size());
+ StringMap<FunctionImporter::ExportSetTy> ExportLists(
+ ThinLTO.ModuleMap.size());
+ StringMap<std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>> ResolvedODR;
+
+ if (DumpThinCGSCCs)
+ ThinLTO.CombinedIndex.dumpSCCs(outs());
+
+ std::set<GlobalValue::GUID> ExportedGUIDs;
+
+ // Perform index-based WPD. This will return immediately if there are
+ // no index entries in the typeIdMetadata map (e.g. if we are instead
+ // performing IR-based WPD in hybrid regular/thin LTO mode).
+ std::map<ValueInfo, std::vector<VTableSlotSummary>> LocalWPDTargetsMap;
+ runWholeProgramDevirtOnIndex(ThinLTO.CombinedIndex, ExportedGUIDs,
+ LocalWPDTargetsMap);
+
+ if (Conf.OptLevel > 0)
+ ComputeCrossModuleImport(ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries,
+ ImportLists, ExportLists);
+
+ // Figure out which symbols need to be internalized. This also needs to happen
+ // at -O0 because summary-based DCE is implemented using internalization, and
+ // we must apply DCE consistently with the full LTO module in order to avoid
+ // undefined references during the final link.
+ for (auto &Res : GlobalResolutions) {
+ // If the symbol does not have external references or it is not prevailing,
+ // then not need to mark it as exported from a ThinLTO partition.
+ if (Res.second.Partition != GlobalResolution::External ||
+ !Res.second.isPrevailingIRSymbol())
+ continue;
+ auto GUID = GlobalValue::getGUID(
+ GlobalValue::dropLLVMManglingEscape(Res.second.IRName));
+ // Mark exported unless index-based analysis determined it to be dead.
+ if (ThinLTO.CombinedIndex.isGUIDLive(GUID))
+ ExportedGUIDs.insert(GUID);
+ }
+
+ // Any functions referenced by the jump table in the regular LTO object must
+ // be exported.
+ for (auto &Def : ThinLTO.CombinedIndex.cfiFunctionDefs())
+ ExportedGUIDs.insert(
+ GlobalValue::getGUID(GlobalValue::dropLLVMManglingEscape(Def)));
+
+ auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) {
+ const auto &ExportList = ExportLists.find(ModuleIdentifier);
+ return (ExportList != ExportLists.end() &&
+ ExportList->second.count(GUID)) ||
+ ExportedGUIDs.count(GUID);
+ };
+
+ // Update local devirtualized targets that were exported by cross-module
+ // importing or by other devirtualizations marked in the ExportedGUIDs set.
+ updateIndexWPDForExports(ThinLTO.CombinedIndex, isExported,
+ LocalWPDTargetsMap);
+
+ auto isPrevailing = [&](GlobalValue::GUID GUID,
+ const GlobalValueSummary *S) {
+ return ThinLTO.PrevailingModuleForGUID[GUID] == S->modulePath();
+ };
+ thinLTOInternalizeAndPromoteInIndex(ThinLTO.CombinedIndex, isExported,
+ isPrevailing);
+
+ auto recordNewLinkage = [&](StringRef ModuleIdentifier,
+ GlobalValue::GUID GUID,
+ GlobalValue::LinkageTypes NewLinkage) {
+ ResolvedODR[ModuleIdentifier][GUID] = NewLinkage;
+ };
+ thinLTOResolvePrevailingInIndex(ThinLTO.CombinedIndex, isPrevailing,
+ recordNewLinkage, GUIDPreservedSymbols);
+
+ std::unique_ptr<ThinBackendProc> BackendProc =
+ ThinLTO.Backend(Conf, ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries,
+ AddStream, Cache);
+
+ // Tasks 0 through ParallelCodeGenParallelismLevel-1 are reserved for combined
+ // module and parallel code generation partitions.
+ unsigned Task = RegularLTO.ParallelCodeGenParallelismLevel;
+ for (auto &Mod : ThinLTO.ModuleMap) {
+ if (Error E = BackendProc->start(Task, Mod.second, ImportLists[Mod.first],
+ ExportLists[Mod.first],
+ ResolvedODR[Mod.first], ThinLTO.ModuleMap))
+ return E;
+ ++Task;
+ }
+
+ return BackendProc->wait();
+}
+
+Expected<std::unique_ptr<ToolOutputFile>>
+lto::setupOptimizationRemarks(LLVMContext &Context, StringRef RemarksFilename,
+ StringRef RemarksPasses, StringRef RemarksFormat,
+ bool RemarksWithHotness, int Count) {
+ std::string Filename = RemarksFilename;
+ if (!Filename.empty() && Count != -1)
+ Filename += ".thin." + llvm::utostr(Count) + ".yaml";
+
+ auto ResultOrErr = llvm::setupOptimizationRemarks(
+ Context, Filename, RemarksPasses, RemarksFormat, RemarksWithHotness);
+ if (Error E = ResultOrErr.takeError())
+ return std::move(E);
+
+ if (*ResultOrErr)
+ (*ResultOrErr)->keep();
+
+ return ResultOrErr;
+}
+
+Expected<std::unique_ptr<ToolOutputFile>>
+lto::setupStatsFile(StringRef StatsFilename) {
+ // Setup output file to emit statistics.
+ if (StatsFilename.empty())
+ return nullptr;
+
+ llvm::EnableStatistics(false);
+ std::error_code EC;
+ auto StatsFile =
+ std::make_unique<ToolOutputFile>(StatsFilename, EC, sys::fs::OF_None);
+ if (EC)
+ return errorCodeToError(EC);
+
+ StatsFile->keep();
+ return std::move(StatsFile);
+}
diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp
new file mode 100644
index 0000000000000..2761f8367b0da
--- /dev/null
+++ b/llvm/lib/LTO/LTOBackend.cpp
@@ -0,0 +1,547 @@
+//===-LTOBackend.cpp - LLVM Link Time Optimizer Backend -------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the "backend" phase of LTO, i.e. it performs
+// optimization and code generation on a loaded module. It is generally used
+// internally by the LTO class but can also be used independently, for example
+// to implement a standalone ThinLTO backend.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/LTO/LTOBackend.h"
+#include "llvm/Analysis/AliasAnalysis.h"
+#include "llvm/Analysis/CGSCCPassManager.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/Analysis/TargetTransformInfo.h"
+#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/IR/RemarkStreamer.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/LTO/LTO.h"
+#include "llvm/MC/SubtargetFeature.h"
+#include "llvm/Object/ModuleSymbolTable.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Passes/StandardInstrumentations.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/ThreadPool.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Transforms/IPO.h"
+#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#include "llvm/Transforms/Scalar/LoopPassManager.h"
+#include "llvm/Transforms/Utils/FunctionImportUtils.h"
+#include "llvm/Transforms/Utils/SplitModule.h"
+
+using namespace llvm;
+using namespace lto;
+
+LLVM_ATTRIBUTE_NORETURN static void reportOpenError(StringRef Path, Twine Msg) {
+ errs() << "failed to open " << Path << ": " << Msg << '\n';
+ errs().flush();
+ exit(1);
+}
+
+Error Config::addSaveTemps(std::string OutputFileName,
+ bool UseInputModulePath) {
+ ShouldDiscardValueNames = false;
+
+ std::error_code EC;
+ ResolutionFile = std::make_unique<raw_fd_ostream>(
+ OutputFileName + "resolution.txt", EC, sys::fs::OpenFlags::OF_Text);
+ if (EC)
+ return errorCodeToError(EC);
+
+ auto setHook = [&](std::string PathSuffix, ModuleHookFn &Hook) {
+ // Keep track of the hook provided by the linker, which also needs to run.
+ ModuleHookFn LinkerHook = Hook;
+ Hook = [=](unsigned Task, const Module &M) {
+ // If the linker's hook returned false, we need to pass that result
+ // through.
+ if (LinkerHook && !LinkerHook(Task, M))
+ return false;
+
+ std::string PathPrefix;
+ // If this is the combined module (not a ThinLTO backend compile) or the
+ // user hasn't requested using the input module's path, emit to a file
+ // named from the provided OutputFileName with the Task ID appended.
+ if (M.getModuleIdentifier() == "ld-temp.o" || !UseInputModulePath) {
+ PathPrefix = OutputFileName;
+ if (Task != (unsigned)-1)
+ PathPrefix += utostr(Task) + ".";
+ } else
+ PathPrefix = M.getModuleIdentifier() + ".";
+ std::string Path = PathPrefix + PathSuffix + ".bc";
+ std::error_code EC;
+ raw_fd_ostream OS(Path, EC, sys::fs::OpenFlags::OF_None);
+ // Because -save-temps is a debugging feature, we report the error
+ // directly and exit.
+ if (EC)
+ reportOpenError(Path, EC.message());
+ WriteBitcodeToFile(M, OS, /*ShouldPreserveUseListOrder=*/false);
+ return true;
+ };
+ };
+
+ setHook("0.preopt", PreOptModuleHook);
+ setHook("1.promote", PostPromoteModuleHook);
+ setHook("2.internalize", PostInternalizeModuleHook);
+ setHook("3.import", PostImportModuleHook);
+ setHook("4.opt", PostOptModuleHook);
+ setHook("5.precodegen", PreCodeGenModuleHook);
+
+ CombinedIndexHook = [=](const ModuleSummaryIndex &Index) {
+ std::string Path = OutputFileName + "index.bc";
+ std::error_code EC;
+ raw_fd_ostream OS(Path, EC, sys::fs::OpenFlags::OF_None);
+ // Because -save-temps is a debugging feature, we report the error
+ // directly and exit.
+ if (EC)
+ reportOpenError(Path, EC.message());
+ WriteIndexToFile(Index, OS);
+
+ Path = OutputFileName + "index.dot";
+ raw_fd_ostream OSDot(Path, EC, sys::fs::OpenFlags::OF_None);
+ if (EC)
+ reportOpenError(Path, EC.message());
+ Index.exportToDot(OSDot);
+ return true;
+ };
+
+ return Error::success();
+}
+
+namespace {
+
+std::unique_ptr<TargetMachine>
+createTargetMachine(Config &Conf, const Target *TheTarget, Module &M) {
+ StringRef TheTriple = M.getTargetTriple();
+ SubtargetFeatures Features;
+ Features.getDefaultSubtargetFeatures(Triple(TheTriple));
+ for (const std::string &A : Conf.MAttrs)
+ Features.AddFeature(A);
+
+ Reloc::Model RelocModel;
+ if (Conf.RelocModel)
+ RelocModel = *Conf.RelocModel;
+ else
+ RelocModel =
+ M.getPICLevel() == PICLevel::NotPIC ? Reloc::Static : Reloc::PIC_;
+
+ Optional<CodeModel::Model> CodeModel;
+ if (Conf.CodeModel)
+ CodeModel = *Conf.CodeModel;
+ else
+ CodeModel = M.getCodeModel();
+
+ return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
+ TheTriple, Conf.CPU, Features.getString(), Conf.Options, RelocModel,
+ CodeModel, Conf.CGOptLevel));
+}
+
+static void runNewPMPasses(Config &Conf, Module &Mod, TargetMachine *TM,
+ unsigned OptLevel, bool IsThinLTO,
+ ModuleSummaryIndex *ExportSummary,
+ const ModuleSummaryIndex *ImportSummary) {
+ Optional<PGOOptions> PGOOpt;
+ if (!Conf.SampleProfile.empty())
+ PGOOpt = PGOOptions(Conf.SampleProfile, "", Conf.ProfileRemapping,
+ PGOOptions::SampleUse, PGOOptions::NoCSAction, true);
+ else if (Conf.RunCSIRInstr) {
+ PGOOpt = PGOOptions("", Conf.CSIRProfile, Conf.ProfileRemapping,
+ PGOOptions::IRUse, PGOOptions::CSIRInstr);
+ } else if (!Conf.CSIRProfile.empty()) {
+ PGOOpt = PGOOptions(Conf.CSIRProfile, "", Conf.ProfileRemapping,
+ PGOOptions::IRUse, PGOOptions::CSIRUse);
+ }
+
+ PassInstrumentationCallbacks PIC;
+ StandardInstrumentations SI;
+ SI.registerCallbacks(PIC);
+ PassBuilder PB(TM, PipelineTuningOptions(),PGOOpt, &PIC);
+ AAManager AA;
+
+ // Parse a custom AA pipeline if asked to.
+ if (auto Err = PB.parseAAPipeline(AA, "default"))
+ report_fatal_error("Error parsing default AA pipeline");
+
+ LoopAnalysisManager LAM(Conf.DebugPassManager);
+ FunctionAnalysisManager FAM(Conf.DebugPassManager);
+ CGSCCAnalysisManager CGAM(Conf.DebugPassManager);
+ ModuleAnalysisManager MAM(Conf.DebugPassManager);
+
+ // Register the AA manager first so that our version is the one used.
+ FAM.registerPass([&] { return std::move(AA); });
+
+ // Register all the basic analyses with the managers.
+ PB.registerModuleAnalyses(MAM);
+ PB.registerCGSCCAnalyses(CGAM);
+ PB.registerFunctionAnalyses(FAM);
+ PB.registerLoopAnalyses(LAM);
+ PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
+
+ ModulePassManager MPM(Conf.DebugPassManager);
+ // FIXME (davide): verify the input.
+
+ PassBuilder::OptimizationLevel OL;
+
+ switch (OptLevel) {
+ default:
+ llvm_unreachable("Invalid optimization level");
+ case 0:
+ OL = PassBuilder::O0;
+ break;
+ case 1:
+ OL = PassBuilder::O1;
+ break;
+ case 2:
+ OL = PassBuilder::O2;
+ break;
+ case 3:
+ OL = PassBuilder::O3;
+ break;
+ }
+
+ if (IsThinLTO)
+ MPM = PB.buildThinLTODefaultPipeline(OL, Conf.DebugPassManager,
+ ImportSummary);
+ else
+ MPM = PB.buildLTODefaultPipeline(OL, Conf.DebugPassManager, ExportSummary);
+ MPM.run(Mod, MAM);
+
+ // FIXME (davide): verify the output.
+}
+
+static void runNewPMCustomPasses(Module &Mod, TargetMachine *TM,
+ std::string PipelineDesc,
+ std::string AAPipelineDesc,
+ bool DisableVerify) {
+ PassBuilder PB(TM);
+ AAManager AA;
+
+ // Parse a custom AA pipeline if asked to.
+ if (!AAPipelineDesc.empty())
+ if (auto Err = PB.parseAAPipeline(AA, AAPipelineDesc))
+ report_fatal_error("unable to parse AA pipeline description '" +
+ AAPipelineDesc + "': " + toString(std::move(Err)));
+
+ LoopAnalysisManager LAM;
+ FunctionAnalysisManager FAM;
+ CGSCCAnalysisManager CGAM;
+ ModuleAnalysisManager MAM;
+
+ // Register the AA manager first so that our version is the one used.
+ FAM.registerPass([&] { return std::move(AA); });
+
+ // Register all the basic analyses with the managers.
+ PB.registerModuleAnalyses(MAM);
+ PB.registerCGSCCAnalyses(CGAM);
+ PB.registerFunctionAnalyses(FAM);
+ PB.registerLoopAnalyses(LAM);
+ PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
+
+ ModulePassManager MPM;
+
+ // Always verify the input.
+ MPM.addPass(VerifierPass());
+
+ // Now, add all the passes we've been requested to.
+ if (auto Err = PB.parsePassPipeline(MPM, PipelineDesc))
+ report_fatal_error("unable to parse pass pipeline description '" +
+ PipelineDesc + "': " + toString(std::move(Err)));
+
+ if (!DisableVerify)
+ MPM.addPass(VerifierPass());
+ MPM.run(Mod, MAM);
+}
+
+static void runOldPMPasses(Config &Conf, Module &Mod, TargetMachine *TM,
+ bool IsThinLTO, ModuleSummaryIndex *ExportSummary,
+ const ModuleSummaryIndex *ImportSummary) {
+ legacy::PassManager passes;
+ passes.add(createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis()));
+
+ PassManagerBuilder PMB;
+ PMB.LibraryInfo = new TargetLibraryInfoImpl(Triple(TM->getTargetTriple()));
+ PMB.Inliner = createFunctionInliningPass();
+ PMB.ExportSummary = ExportSummary;
+ PMB.ImportSummary = ImportSummary;
+ // Unconditionally verify input since it is not verified before this
+ // point and has unknown origin.
+ PMB.VerifyInput = true;
+ PMB.VerifyOutput = !Conf.DisableVerify;
+ PMB.LoopVectorize = true;
+ PMB.SLPVectorize = true;
+ PMB.OptLevel = Conf.OptLevel;
+ PMB.PGOSampleUse = Conf.SampleProfile;
+ PMB.EnablePGOCSInstrGen = Conf.RunCSIRInstr;
+ if (!Conf.RunCSIRInstr && !Conf.CSIRProfile.empty()) {
+ PMB.EnablePGOCSInstrUse = true;
+ PMB.PGOInstrUse = Conf.CSIRProfile;
+ }
+ if (IsThinLTO)
+ PMB.populateThinLTOPassManager(passes);
+ else
+ PMB.populateLTOPassManager(passes);
+ passes.run(Mod);
+}
+
+bool opt(Config &Conf, TargetMachine *TM, unsigned Task, Module &Mod,
+ bool IsThinLTO, ModuleSummaryIndex *ExportSummary,
+ const ModuleSummaryIndex *ImportSummary) {
+ // FIXME: Plumb the combined index into the new pass manager.
+ if (!Conf.OptPipeline.empty())
+ runNewPMCustomPasses(Mod, TM, Conf.OptPipeline, Conf.AAPipeline,
+ Conf.DisableVerify);
+ else if (Conf.UseNewPM)
+ runNewPMPasses(Conf, Mod, TM, Conf.OptLevel, IsThinLTO, ExportSummary,
+ ImportSummary);
+ else
+ runOldPMPasses(Conf, Mod, TM, IsThinLTO, ExportSummary, ImportSummary);
+ return !Conf.PostOptModuleHook || Conf.PostOptModuleHook(Task, Mod);
+}
+
+void codegen(Config &Conf, TargetMachine *TM, AddStreamFn AddStream,
+ unsigned Task, Module &Mod) {
+ if (Conf.PreCodeGenModuleHook && !Conf.PreCodeGenModuleHook(Task, Mod))
+ return;
+
+ std::unique_ptr<ToolOutputFile> DwoOut;
+ SmallString<1024> DwoFile(Conf.SplitDwarfOutput);
+ if (!Conf.DwoDir.empty()) {
+ std::error_code EC;
+ if (auto EC = llvm::sys::fs::create_directories(Conf.DwoDir))
+ report_fatal_error("Failed to create directory " + Conf.DwoDir + ": " +
+ EC.message());
+
+ DwoFile = Conf.DwoDir;
+ sys::path::append(DwoFile, std::to_string(Task) + ".dwo");
+ TM->Options.MCOptions.SplitDwarfFile = DwoFile.str().str();
+ } else
+ TM->Options.MCOptions.SplitDwarfFile = Conf.SplitDwarfFile;
+
+ if (!DwoFile.empty()) {
+ std::error_code EC;
+ DwoOut = std::make_unique<ToolOutputFile>(DwoFile, EC, sys::fs::OF_None);
+ if (EC)
+ report_fatal_error("Failed to open " + DwoFile + ": " + EC.message());
+ }
+
+ auto Stream = AddStream(Task);
+ legacy::PassManager CodeGenPasses;
+ if (TM->addPassesToEmitFile(CodeGenPasses, *Stream->OS,
+ DwoOut ? &DwoOut->os() : nullptr,
+ Conf.CGFileType))
+ report_fatal_error("Failed to setup codegen");
+ CodeGenPasses.run(Mod);
+
+ if (DwoOut)
+ DwoOut->keep();
+}
+
+void splitCodeGen(Config &C, TargetMachine *TM, AddStreamFn AddStream,
+ unsigned ParallelCodeGenParallelismLevel,
+ std::unique_ptr<Module> Mod) {
+ ThreadPool CodegenThreadPool(ParallelCodeGenParallelismLevel);
+ unsigned ThreadCount = 0;
+ const Target *T = &TM->getTarget();
+
+ SplitModule(
+ std::move(Mod), ParallelCodeGenParallelismLevel,
+ [&](std::unique_ptr<Module> MPart) {
+ // We want to clone the module in a new context to multi-thread the
+ // codegen. We do it by serializing partition modules to bitcode
+ // (while still on the main thread, in order to avoid data races) and
+ // spinning up new threads which deserialize the partitions into
+ // separate contexts.
+ // FIXME: Provide a more direct way to do this in LLVM.
+ SmallString<0> BC;
+ raw_svector_ostream BCOS(BC);
+ WriteBitcodeToFile(*MPart, BCOS);
+
+ // Enqueue the task
+ CodegenThreadPool.async(
+ [&](const SmallString<0> &BC, unsigned ThreadId) {
+ LTOLLVMContext Ctx(C);
+ Expected<std::unique_ptr<Module>> MOrErr = parseBitcodeFile(
+ MemoryBufferRef(StringRef(BC.data(), BC.size()), "ld-temp.o"),
+ Ctx);
+ if (!MOrErr)
+ report_fatal_error("Failed to read bitcode");
+ std::unique_ptr<Module> MPartInCtx = std::move(MOrErr.get());
+
+ std::unique_ptr<TargetMachine> TM =
+ createTargetMachine(C, T, *MPartInCtx);
+
+ codegen(C, TM.get(), AddStream, ThreadId, *MPartInCtx);
+ },
+ // Pass BC using std::move to ensure that it get moved rather than
+ // copied into the thread's context.
+ std::move(BC), ThreadCount++);
+ },
+ false);
+
+ // Because the inner lambda (which runs in a worker thread) captures our local
+ // variables, we need to wait for the worker threads to terminate before we
+ // can leave the function scope.
+ CodegenThreadPool.wait();
+}
+
+Expected<const Target *> initAndLookupTarget(Config &C, Module &Mod) {
+ if (!C.OverrideTriple.empty())
+ Mod.setTargetTriple(C.OverrideTriple);
+ else if (Mod.getTargetTriple().empty())
+ Mod.setTargetTriple(C.DefaultTriple);
+
+ std::string Msg;
+ const Target *T = TargetRegistry::lookupTarget(Mod.getTargetTriple(), Msg);
+ if (!T)
+ return make_error<StringError>(Msg, inconvertibleErrorCode());
+ return T;
+}
+
+}
+
+static Error
+finalizeOptimizationRemarks(std::unique_ptr<ToolOutputFile> DiagOutputFile) {
+ // Make sure we flush the diagnostic remarks file in case the linker doesn't
+ // call the global destructors before exiting.
+ if (!DiagOutputFile)
+ return Error::success();
+ DiagOutputFile->keep();
+ DiagOutputFile->os().flush();
+ return Error::success();
+}
+
+Error lto::backend(Config &C, AddStreamFn AddStream,
+ unsigned ParallelCodeGenParallelismLevel,
+ std::unique_ptr<Module> Mod,
+ ModuleSummaryIndex &CombinedIndex) {
+ Expected<const Target *> TOrErr = initAndLookupTarget(C, *Mod);
+ if (!TOrErr)
+ return TOrErr.takeError();
+
+ std::unique_ptr<TargetMachine> TM = createTargetMachine(C, *TOrErr, *Mod);
+
+ // Setup optimization remarks.
+ auto DiagFileOrErr = lto::setupOptimizationRemarks(
+ Mod->getContext(), C.RemarksFilename, C.RemarksPasses, C.RemarksFormat,
+ C.RemarksWithHotness);
+ if (!DiagFileOrErr)
+ return DiagFileOrErr.takeError();
+ auto DiagnosticOutputFile = std::move(*DiagFileOrErr);
+
+ if (!C.CodeGenOnly) {
+ if (!opt(C, TM.get(), 0, *Mod, /*IsThinLTO=*/false,
+ /*ExportSummary=*/&CombinedIndex, /*ImportSummary=*/nullptr))
+ return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));
+ }
+
+ if (ParallelCodeGenParallelismLevel == 1) {
+ codegen(C, TM.get(), AddStream, 0, *Mod);
+ } else {
+ splitCodeGen(C, TM.get(), AddStream, ParallelCodeGenParallelismLevel,
+ std::move(Mod));
+ }
+ return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));
+}
+
+static void dropDeadSymbols(Module &Mod, const GVSummaryMapTy &DefinedGlobals,
+ const ModuleSummaryIndex &Index) {
+ std::vector<GlobalValue*> DeadGVs;
+ for (auto &GV : Mod.global_values())
+ if (GlobalValueSummary *GVS = DefinedGlobals.lookup(GV.getGUID()))
+ if (!Index.isGlobalValueLive(GVS)) {
+ DeadGVs.push_back(&GV);
+ convertToDeclaration(GV);
+ }
+
+ // Now that all dead bodies have been dropped, delete the actual objects
+ // themselves when possible.
+ for (GlobalValue *GV : DeadGVs) {
+ GV->removeDeadConstantUsers();
+ // Might reference something defined in native object (i.e. dropped a
+ // non-prevailing IR def, but we need to keep the declaration).
+ if (GV->use_empty())
+ GV->eraseFromParent();
+ }
+}
+
+Error lto::thinBackend(Config &Conf, unsigned Task, AddStreamFn AddStream,
+ Module &Mod, const ModuleSummaryIndex &CombinedIndex,
+ const FunctionImporter::ImportMapTy &ImportList,
+ const GVSummaryMapTy &DefinedGlobals,
+ MapVector<StringRef, BitcodeModule> &ModuleMap) {
+ Expected<const Target *> TOrErr = initAndLookupTarget(Conf, Mod);
+ if (!TOrErr)
+ return TOrErr.takeError();
+
+ std::unique_ptr<TargetMachine> TM = createTargetMachine(Conf, *TOrErr, Mod);
+
+ // Setup optimization remarks.
+ auto DiagFileOrErr = lto::setupOptimizationRemarks(
+ Mod.getContext(), Conf.RemarksFilename, Conf.RemarksPasses,
+ Conf.RemarksFormat, Conf.RemarksWithHotness, Task);
+ if (!DiagFileOrErr)
+ return DiagFileOrErr.takeError();
+ auto DiagnosticOutputFile = std::move(*DiagFileOrErr);
+
+ if (Conf.CodeGenOnly) {
+ codegen(Conf, TM.get(), AddStream, Task, Mod);
+ return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));
+ }
+
+ if (Conf.PreOptModuleHook && !Conf.PreOptModuleHook(Task, Mod))
+ return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));
+
+ renameModuleForThinLTO(Mod, CombinedIndex);
+
+ dropDeadSymbols(Mod, DefinedGlobals, CombinedIndex);
+
+ thinLTOResolvePrevailingInModule(Mod, DefinedGlobals);
+
+ if (Conf.PostPromoteModuleHook && !Conf.PostPromoteModuleHook(Task, Mod))
+ return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));
+
+ if (!DefinedGlobals.empty())
+ thinLTOInternalizeModule(Mod, DefinedGlobals);
+
+ if (Conf.PostInternalizeModuleHook &&
+ !Conf.PostInternalizeModuleHook(Task, Mod))
+ return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));
+
+ auto ModuleLoader = [&](StringRef Identifier) {
+ assert(Mod.getContext().isODRUniquingDebugTypes() &&
+ "ODR Type uniquing should be enabled on the context");
+ auto I = ModuleMap.find(Identifier);
+ assert(I != ModuleMap.end());
+ return I->second.getLazyModule(Mod.getContext(),
+ /*ShouldLazyLoadMetadata=*/true,
+ /*IsImporting*/ true);
+ };
+
+ FunctionImporter Importer(CombinedIndex, ModuleLoader);
+ if (Error Err = Importer.importFunctions(Mod, ImportList).takeError())
+ return Err;
+
+ if (Conf.PostImportModuleHook && !Conf.PostImportModuleHook(Task, Mod))
+ return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));
+
+ if (!opt(Conf, TM.get(), Task, Mod, /*IsThinLTO=*/true,
+ /*ExportSummary=*/nullptr, /*ImportSummary=*/&CombinedIndex))
+ return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));
+
+ codegen(Conf, TM.get(), AddStream, Task, Mod);
+ return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));
+}
diff --git a/llvm/lib/LTO/LTOCodeGenerator.cpp b/llvm/lib/LTO/LTOCodeGenerator.cpp
new file mode 100644
index 0000000000000..8821928928672
--- /dev/null
+++ b/llvm/lib/LTO/LTOCodeGenerator.cpp
@@ -0,0 +1,722 @@
+//===-LTOCodeGenerator.cpp - LLVM Link Time Optimizer ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the Link Time Optimization library. This library is
+// intended to be used by linker to optimize code at link time.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/LTO/legacy/LTOCodeGenerator.h"
+
+#include "llvm/ADT/Statistic.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Analysis/Passes.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/Analysis/TargetTransformInfo.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
+#include "llvm/CodeGen/ParallelCG.h"
+#include "llvm/CodeGen/TargetSubtargetInfo.h"
+#include "llvm/Config/config.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/DebugInfo.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Mangler.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/PassTimingInfo.h"
+#include "llvm/IR/RemarkStreamer.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/LTO/LTO.h"
+#include "llvm/LTO/legacy/LTOModule.h"
+#include "llvm/LTO/legacy/UpdateCompilerUsed.h"
+#include "llvm/Linker/Linker.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/SubtargetFeature.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Target/TargetOptions.h"
+#include "llvm/Transforms/IPO.h"
+#include "llvm/Transforms/IPO/Internalize.h"
+#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#include "llvm/Transforms/ObjCARC.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
+#include <system_error>
+using namespace llvm;
+
+const char* LTOCodeGenerator::getVersionString() {
+#ifdef LLVM_VERSION_INFO
+ return PACKAGE_NAME " version " PACKAGE_VERSION ", " LLVM_VERSION_INFO;
+#else
+ return PACKAGE_NAME " version " PACKAGE_VERSION;
+#endif
+}
+
+namespace llvm {
+cl::opt<bool> LTODiscardValueNames(
+ "lto-discard-value-names",
+ cl::desc("Strip names from Value during LTO (other than GlobalValue)."),
+#ifdef NDEBUG
+ cl::init(true),
+#else
+ cl::init(false),
+#endif
+ cl::Hidden);
+
+cl::opt<bool> RemarksWithHotness(
+ "lto-pass-remarks-with-hotness",
+ cl::desc("With PGO, include profile count in optimization remarks"),
+ cl::Hidden);
+
+cl::opt<std::string>
+ RemarksFilename("lto-pass-remarks-output",
+ cl::desc("Output filename for pass remarks"),
+ cl::value_desc("filename"));
+
+cl::opt<std::string>
+ RemarksPasses("lto-pass-remarks-filter",
+ cl::desc("Only record optimization remarks from passes whose "
+ "names match the given regular expression"),
+ cl::value_desc("regex"));
+
+cl::opt<std::string> RemarksFormat(
+ "lto-pass-remarks-format",
+ cl::desc("The format used for serializing remarks (default: YAML)"),
+ cl::value_desc("format"), cl::init("yaml"));
+
+cl::opt<std::string> LTOStatsFile(
+ "lto-stats-file",
+ cl::desc("Save statistics to the specified file"),
+ cl::Hidden);
+}
+
+LTOCodeGenerator::LTOCodeGenerator(LLVMContext &Context)
+ : Context(Context), MergedModule(new Module("ld-temp.o", Context)),
+ TheLinker(new Linker(*MergedModule)) {
+ Context.setDiscardValueNames(LTODiscardValueNames);
+ Context.enableDebugTypeODRUniquing();
+ initializeLTOPasses();
+}
+
+LTOCodeGenerator::~LTOCodeGenerator() {}
+
+// Initialize LTO passes. Please keep this function in sync with
+// PassManagerBuilder::populateLTOPassManager(), and make sure all LTO
+// passes are initialized.
+void LTOCodeGenerator::initializeLTOPasses() {
+ PassRegistry &R = *PassRegistry::getPassRegistry();
+
+ initializeInternalizeLegacyPassPass(R);
+ initializeIPSCCPLegacyPassPass(R);
+ initializeGlobalOptLegacyPassPass(R);
+ initializeConstantMergeLegacyPassPass(R);
+ initializeDAHPass(R);
+ initializeInstructionCombiningPassPass(R);
+ initializeSimpleInlinerPass(R);
+ initializePruneEHPass(R);
+ initializeGlobalDCELegacyPassPass(R);
+ initializeArgPromotionPass(R);
+ initializeJumpThreadingPass(R);
+ initializeSROALegacyPassPass(R);
+ initializeAttributorLegacyPassPass(R);
+ initializePostOrderFunctionAttrsLegacyPassPass(R);
+ initializeReversePostOrderFunctionAttrsLegacyPassPass(R);
+ initializeGlobalsAAWrapperPassPass(R);
+ initializeLegacyLICMPassPass(R);
+ initializeMergedLoadStoreMotionLegacyPassPass(R);
+ initializeGVNLegacyPassPass(R);
+ initializeMemCpyOptLegacyPassPass(R);
+ initializeDCELegacyPassPass(R);
+ initializeCFGSimplifyPassPass(R);
+}
+
+void LTOCodeGenerator::setAsmUndefinedRefs(LTOModule *Mod) {
+ const std::vector<StringRef> &undefs = Mod->getAsmUndefinedRefs();
+ for (int i = 0, e = undefs.size(); i != e; ++i)
+ AsmUndefinedRefs.insert(undefs[i]);
+}
+
+bool LTOCodeGenerator::addModule(LTOModule *Mod) {
+ assert(&Mod->getModule().getContext() == &Context &&
+ "Expected module in same context");
+
+ bool ret = TheLinker->linkInModule(Mod->takeModule());
+ setAsmUndefinedRefs(Mod);
+
+ // We've just changed the input, so let's make sure we verify it.
+ HasVerifiedInput = false;
+
+ return !ret;
+}
+
+void LTOCodeGenerator::setModule(std::unique_ptr<LTOModule> Mod) {
+ assert(&Mod->getModule().getContext() == &Context &&
+ "Expected module in same context");
+
+ AsmUndefinedRefs.clear();
+
+ MergedModule = Mod->takeModule();
+ TheLinker = std::make_unique<Linker>(*MergedModule);
+ setAsmUndefinedRefs(&*Mod);
+
+ // We've just changed the input, so let's make sure we verify it.
+ HasVerifiedInput = false;
+}
+
+void LTOCodeGenerator::setTargetOptions(const TargetOptions &Options) {
+ this->Options = Options;
+}
+
+void LTOCodeGenerator::setDebugInfo(lto_debug_model Debug) {
+ switch (Debug) {
+ case LTO_DEBUG_MODEL_NONE:
+ EmitDwarfDebugInfo = false;
+ return;
+
+ case LTO_DEBUG_MODEL_DWARF:
+ EmitDwarfDebugInfo = true;
+ return;
+ }
+ llvm_unreachable("Unknown debug format!");
+}
+
+void LTOCodeGenerator::setOptLevel(unsigned Level) {
+ OptLevel = Level;
+ switch (OptLevel) {
+ case 0:
+ CGOptLevel = CodeGenOpt::None;
+ return;
+ case 1:
+ CGOptLevel = CodeGenOpt::Less;
+ return;
+ case 2:
+ CGOptLevel = CodeGenOpt::Default;
+ return;
+ case 3:
+ CGOptLevel = CodeGenOpt::Aggressive;
+ return;
+ }
+ llvm_unreachable("Unknown optimization level!");
+}
+
+bool LTOCodeGenerator::writeMergedModules(StringRef Path) {
+ if (!determineTarget())
+ return false;
+
+ // We always run the verifier once on the merged module.
+ verifyMergedModuleOnce();
+
+ // mark which symbols can not be internalized
+ applyScopeRestrictions();
+
+ // create output file
+ std::error_code EC;
+ ToolOutputFile Out(Path, EC, sys::fs::OF_None);
+ if (EC) {
+ std::string ErrMsg = "could not open bitcode file for writing: ";
+ ErrMsg += Path.str() + ": " + EC.message();
+ emitError(ErrMsg);
+ return false;
+ }
+
+ // write bitcode to it
+ WriteBitcodeToFile(*MergedModule, Out.os(), ShouldEmbedUselists);
+ Out.os().close();
+
+ if (Out.os().has_error()) {
+ std::string ErrMsg = "could not write bitcode file: ";
+ ErrMsg += Path.str() + ": " + Out.os().error().message();
+ emitError(ErrMsg);
+ Out.os().clear_error();
+ return false;
+ }
+
+ Out.keep();
+ return true;
+}
+
+bool LTOCodeGenerator::compileOptimizedToFile(const char **Name) {
+ // make unique temp output file to put generated code
+ SmallString<128> Filename;
+ int FD;
+
+ StringRef Extension
+ (FileType == TargetMachine::CGFT_AssemblyFile ? "s" : "o");
+
+ std::error_code EC =
+ sys::fs::createTemporaryFile("lto-llvm", Extension, FD, Filename);
+ if (EC) {
+ emitError(EC.message());
+ return false;
+ }
+
+ // generate object file
+ ToolOutputFile objFile(Filename, FD);
+
+ bool genResult = compileOptimized(&objFile.os());
+ objFile.os().close();
+ if (objFile.os().has_error()) {
+ emitError((Twine("could not write object file: ") + Filename + ": " +
+ objFile.os().error().message())
+ .str());
+ objFile.os().clear_error();
+ sys::fs::remove(Twine(Filename));
+ return false;
+ }
+
+ objFile.keep();
+ if (!genResult) {
+ sys::fs::remove(Twine(Filename));
+ return false;
+ }
+
+ NativeObjectPath = Filename.c_str();
+ *Name = NativeObjectPath.c_str();
+ return true;
+}
+
+std::unique_ptr<MemoryBuffer>
+LTOCodeGenerator::compileOptimized() {
+ const char *name;
+ if (!compileOptimizedToFile(&name))
+ return nullptr;
+
+ // read .o file into memory buffer
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
+ MemoryBuffer::getFile(name, -1, false);
+ if (std::error_code EC = BufferOrErr.getError()) {
+ emitError(EC.message());
+ sys::fs::remove(NativeObjectPath);
+ return nullptr;
+ }
+
+ // remove temp files
+ sys::fs::remove(NativeObjectPath);
+
+ return std::move(*BufferOrErr);
+}
+
+bool LTOCodeGenerator::compile_to_file(const char **Name, bool DisableVerify,
+ bool DisableInline,
+ bool DisableGVNLoadPRE,
+ bool DisableVectorization) {
+ if (!optimize(DisableVerify, DisableInline, DisableGVNLoadPRE,
+ DisableVectorization))
+ return false;
+
+ return compileOptimizedToFile(Name);
+}
+
+std::unique_ptr<MemoryBuffer>
+LTOCodeGenerator::compile(bool DisableVerify, bool DisableInline,
+ bool DisableGVNLoadPRE, bool DisableVectorization) {
+ if (!optimize(DisableVerify, DisableInline, DisableGVNLoadPRE,
+ DisableVectorization))
+ return nullptr;
+
+ return compileOptimized();
+}
+
+bool LTOCodeGenerator::determineTarget() {
+ if (TargetMach)
+ return true;
+
+ TripleStr = MergedModule->getTargetTriple();
+ if (TripleStr.empty()) {
+ TripleStr = sys::getDefaultTargetTriple();
+ MergedModule->setTargetTriple(TripleStr);
+ }
+ llvm::Triple Triple(TripleStr);
+
+ // create target machine from info for merged modules
+ std::string ErrMsg;
+ MArch = TargetRegistry::lookupTarget(TripleStr, ErrMsg);
+ if (!MArch) {
+ emitError(ErrMsg);
+ return false;
+ }
+
+ // Construct LTOModule, hand over ownership of module and target. Use MAttr as
+ // the default set of features.
+ SubtargetFeatures Features(MAttr);
+ Features.getDefaultSubtargetFeatures(Triple);
+ FeatureStr = Features.getString();
+ // Set a default CPU for Darwin triples.
+ if (MCpu.empty() && Triple.isOSDarwin()) {
+ if (Triple.getArch() == llvm::Triple::x86_64)
+ MCpu = "core2";
+ else if (Triple.getArch() == llvm::Triple::x86)
+ MCpu = "yonah";
+ else if (Triple.getArch() == llvm::Triple::aarch64 ||
+ Triple.getArch() == llvm::Triple::aarch64_32)
+ MCpu = "cyclone";
+ }
+
+ TargetMach = createTargetMachine();
+ return true;
+}
+
+std::unique_ptr<TargetMachine> LTOCodeGenerator::createTargetMachine() {
+ return std::unique_ptr<TargetMachine>(MArch->createTargetMachine(
+ TripleStr, MCpu, FeatureStr, Options, RelocModel, None, CGOptLevel));
+}
+
+// If a linkonce global is present in the MustPreserveSymbols, we need to make
+// sure we honor this. To force the compiler to not drop it, we add it to the
+// "llvm.compiler.used" global.
+void LTOCodeGenerator::preserveDiscardableGVs(
+ Module &TheModule,
+ llvm::function_ref<bool(const GlobalValue &)> mustPreserveGV) {
+ std::vector<GlobalValue *> Used;
+ auto mayPreserveGlobal = [&](GlobalValue &GV) {
+ if (!GV.isDiscardableIfUnused() || GV.isDeclaration() ||
+ !mustPreserveGV(GV))
+ return;
+ if (GV.hasAvailableExternallyLinkage())
+ return emitWarning(
+ (Twine("Linker asked to preserve available_externally global: '") +
+ GV.getName() + "'").str());
+ if (GV.hasInternalLinkage())
+ return emitWarning((Twine("Linker asked to preserve internal global: '") +
+ GV.getName() + "'").str());
+ Used.push_back(&GV);
+ };
+ for (auto &GV : TheModule)
+ mayPreserveGlobal(GV);
+ for (auto &GV : TheModule.globals())
+ mayPreserveGlobal(GV);
+ for (auto &GV : TheModule.aliases())
+ mayPreserveGlobal(GV);
+
+ if (Used.empty())
+ return;
+
+ appendToCompilerUsed(TheModule, Used);
+}
+
+void LTOCodeGenerator::applyScopeRestrictions() {
+ if (ScopeRestrictionsDone)
+ return;
+
+ // Declare a callback for the internalize pass that will ask for every
+ // candidate GlobalValue if it can be internalized or not.
+ Mangler Mang;
+ SmallString<64> MangledName;
+ auto mustPreserveGV = [&](const GlobalValue &GV) -> bool {
+ // Unnamed globals can't be mangled, but they can't be preserved either.
+ if (!GV.hasName())
+ return false;
+
+ // Need to mangle the GV as the "MustPreserveSymbols" StringSet is filled
+ // with the linker supplied name, which on Darwin includes a leading
+ // underscore.
+ MangledName.clear();
+ MangledName.reserve(GV.getName().size() + 1);
+ Mang.getNameWithPrefix(MangledName, &GV, /*CannotUsePrivateLabel=*/false);
+ return MustPreserveSymbols.count(MangledName);
+ };
+
+ // Preserve linkonce value on linker request
+ preserveDiscardableGVs(*MergedModule, mustPreserveGV);
+
+ if (!ShouldInternalize)
+ return;
+
+ if (ShouldRestoreGlobalsLinkage) {
+ // Record the linkage type of non-local symbols so they can be restored
+ // prior
+ // to module splitting.
+ auto RecordLinkage = [&](const GlobalValue &GV) {
+ if (!GV.hasAvailableExternallyLinkage() && !GV.hasLocalLinkage() &&
+ GV.hasName())
+ ExternalSymbols.insert(std::make_pair(GV.getName(), GV.getLinkage()));
+ };
+ for (auto &GV : *MergedModule)
+ RecordLinkage(GV);
+ for (auto &GV : MergedModule->globals())
+ RecordLinkage(GV);
+ for (auto &GV : MergedModule->aliases())
+ RecordLinkage(GV);
+ }
+
+ // Update the llvm.compiler_used globals to force preserving libcalls and
+ // symbols referenced from asm
+ updateCompilerUsed(*MergedModule, *TargetMach, AsmUndefinedRefs);
+
+ internalizeModule(*MergedModule, mustPreserveGV);
+
+ MergedModule->addModuleFlag(Module::Error, "LTOPostLink", 1);
+
+ ScopeRestrictionsDone = true;
+}
+
+/// Restore original linkage for symbols that may have been internalized
+void LTOCodeGenerator::restoreLinkageForExternals() {
+ if (!ShouldInternalize || !ShouldRestoreGlobalsLinkage)
+ return;
+
+ assert(ScopeRestrictionsDone &&
+ "Cannot externalize without internalization!");
+
+ if (ExternalSymbols.empty())
+ return;
+
+ auto externalize = [this](GlobalValue &GV) {
+ if (!GV.hasLocalLinkage() || !GV.hasName())
+ return;
+
+ auto I = ExternalSymbols.find(GV.getName());
+ if (I == ExternalSymbols.end())
+ return;
+
+ GV.setLinkage(I->second);
+ };
+
+ llvm::for_each(MergedModule->functions(), externalize);
+ llvm::for_each(MergedModule->globals(), externalize);
+ llvm::for_each(MergedModule->aliases(), externalize);
+}
+
+void LTOCodeGenerator::verifyMergedModuleOnce() {
+ // Only run on the first call.
+ if (HasVerifiedInput)
+ return;
+ HasVerifiedInput = true;
+
+ bool BrokenDebugInfo = false;
+ if (verifyModule(*MergedModule, &dbgs(), &BrokenDebugInfo))
+ report_fatal_error("Broken module found, compilation aborted!");
+ if (BrokenDebugInfo) {
+ emitWarning("Invalid debug info found, debug info will be stripped");
+ StripDebugInfo(*MergedModule);
+ }
+}
+
+void LTOCodeGenerator::finishOptimizationRemarks() {
+ if (DiagnosticOutputFile) {
+ DiagnosticOutputFile->keep();
+ // FIXME: LTOCodeGenerator dtor is not invoked on Darwin
+ DiagnosticOutputFile->os().flush();
+ }
+}
+
+/// Optimize merged modules using various IPO passes
+bool LTOCodeGenerator::optimize(bool DisableVerify, bool DisableInline,
+ bool DisableGVNLoadPRE,
+ bool DisableVectorization) {
+ if (!this->determineTarget())
+ return false;
+
+ auto DiagFileOrErr =
+ lto::setupOptimizationRemarks(Context, RemarksFilename, RemarksPasses,
+ RemarksFormat, RemarksWithHotness);
+ if (!DiagFileOrErr) {
+ errs() << "Error: " << toString(DiagFileOrErr.takeError()) << "\n";
+ report_fatal_error("Can't get an output file for the remarks");
+ }
+ DiagnosticOutputFile = std::move(*DiagFileOrErr);
+
+ // Setup output file to emit statistics.
+ auto StatsFileOrErr = lto::setupStatsFile(LTOStatsFile);
+ if (!StatsFileOrErr) {
+ errs() << "Error: " << toString(StatsFileOrErr.takeError()) << "\n";
+ report_fatal_error("Can't get an output file for the statistics");
+ }
+ StatsFile = std::move(StatsFileOrErr.get());
+
+ // We always run the verifier once on the merged module, the `DisableVerify`
+ // parameter only applies to subsequent verify.
+ verifyMergedModuleOnce();
+
+ // Mark which symbols can not be internalized
+ this->applyScopeRestrictions();
+
+ // Instantiate the pass manager to organize the passes.
+ legacy::PassManager passes;
+
+ // Add an appropriate DataLayout instance for this module...
+ MergedModule->setDataLayout(TargetMach->createDataLayout());
+
+ passes.add(
+ createTargetTransformInfoWrapperPass(TargetMach->getTargetIRAnalysis()));
+
+ Triple TargetTriple(TargetMach->getTargetTriple());
+ PassManagerBuilder PMB;
+ PMB.DisableGVNLoadPRE = DisableGVNLoadPRE;
+ PMB.LoopVectorize = !DisableVectorization;
+ PMB.SLPVectorize = !DisableVectorization;
+ if (!DisableInline)
+ PMB.Inliner = createFunctionInliningPass();
+ PMB.LibraryInfo = new TargetLibraryInfoImpl(TargetTriple);
+ if (Freestanding)
+ PMB.LibraryInfo->disableAllFunctions();
+ PMB.OptLevel = OptLevel;
+ PMB.VerifyInput = !DisableVerify;
+ PMB.VerifyOutput = !DisableVerify;
+
+ PMB.populateLTOPassManager(passes);
+
+ // Run our queue of passes all at once now, efficiently.
+ passes.run(*MergedModule);
+
+ return true;
+}
+
+bool LTOCodeGenerator::compileOptimized(ArrayRef<raw_pwrite_stream *> Out) {
+ if (!this->determineTarget())
+ return false;
+
+ // We always run the verifier once on the merged module. If it has already
+ // been called in optimize(), this call will return early.
+ verifyMergedModuleOnce();
+
+ legacy::PassManager preCodeGenPasses;
+
+ // If the bitcode files contain ARC code and were compiled with optimization,
+ // the ObjCARCContractPass must be run, so do it unconditionally here.
+ preCodeGenPasses.add(createObjCARCContractPass());
+ preCodeGenPasses.run(*MergedModule);
+
+ // Re-externalize globals that may have been internalized to increase scope
+ // for splitting
+ restoreLinkageForExternals();
+
+ // Do code generation. We need to preserve the module in case the client calls
+ // writeMergedModules() after compilation, but we only need to allow this at
+ // parallelism level 1. This is achieved by having splitCodeGen return the
+ // original module at parallelism level 1 which we then assign back to
+ // MergedModule.
+ MergedModule = splitCodeGen(std::move(MergedModule), Out, {},
+ [&]() { return createTargetMachine(); }, FileType,
+ ShouldRestoreGlobalsLinkage);
+
+ // If statistics were requested, save them to the specified file or
+ // print them out after codegen.
+ if (StatsFile)
+ PrintStatisticsJSON(StatsFile->os());
+ else if (AreStatisticsEnabled())
+ PrintStatistics();
+
+ reportAndResetTimings();
+
+ finishOptimizationRemarks();
+
+ return true;
+}
+
+/// setCodeGenDebugOptions - Set codegen debugging options to aid in debugging
+/// LTO problems.
+void LTOCodeGenerator::setCodeGenDebugOptions(StringRef Options) {
+ for (std::pair<StringRef, StringRef> o = getToken(Options); !o.first.empty();
+ o = getToken(o.second))
+ CodegenOptions.push_back(o.first);
+}
+
+void LTOCodeGenerator::parseCodeGenDebugOptions() {
+ // if options were requested, set them
+ if (!CodegenOptions.empty()) {
+ // ParseCommandLineOptions() expects argv[0] to be program name.
+ std::vector<const char *> CodegenArgv(1, "libLLVMLTO");
+ for (std::string &Arg : CodegenOptions)
+ CodegenArgv.push_back(Arg.c_str());
+ cl::ParseCommandLineOptions(CodegenArgv.size(), CodegenArgv.data());
+ }
+}
+
+
+void LTOCodeGenerator::DiagnosticHandler(const DiagnosticInfo &DI) {
+ // Map the LLVM internal diagnostic severity to the LTO diagnostic severity.
+ lto_codegen_diagnostic_severity_t Severity;
+ switch (DI.getSeverity()) {
+ case DS_Error:
+ Severity = LTO_DS_ERROR;
+ break;
+ case DS_Warning:
+ Severity = LTO_DS_WARNING;
+ break;
+ case DS_Remark:
+ Severity = LTO_DS_REMARK;
+ break;
+ case DS_Note:
+ Severity = LTO_DS_NOTE;
+ break;
+ }
+ // Create the string that will be reported to the external diagnostic handler.
+ std::string MsgStorage;
+ raw_string_ostream Stream(MsgStorage);
+ DiagnosticPrinterRawOStream DP(Stream);
+ DI.print(DP);
+ Stream.flush();
+
+ // If this method has been called it means someone has set up an external
+ // diagnostic handler. Assert on that.
+ assert(DiagHandler && "Invalid diagnostic handler");
+ (*DiagHandler)(Severity, MsgStorage.c_str(), DiagContext);
+}
+
+namespace {
+struct LTODiagnosticHandler : public DiagnosticHandler {
+ LTOCodeGenerator *CodeGenerator;
+ LTODiagnosticHandler(LTOCodeGenerator *CodeGenPtr)
+ : CodeGenerator(CodeGenPtr) {}
+ bool handleDiagnostics(const DiagnosticInfo &DI) override {
+ CodeGenerator->DiagnosticHandler(DI);
+ return true;
+ }
+};
+}
+
+void
+LTOCodeGenerator::setDiagnosticHandler(lto_diagnostic_handler_t DiagHandler,
+ void *Ctxt) {
+ this->DiagHandler = DiagHandler;
+ this->DiagContext = Ctxt;
+ if (!DiagHandler)
+ return Context.setDiagnosticHandler(nullptr);
+ // Register the LTOCodeGenerator stub in the LLVMContext to forward the
+ // diagnostic to the external DiagHandler.
+ Context.setDiagnosticHandler(std::make_unique<LTODiagnosticHandler>(this),
+ true);
+}
+
+namespace {
+class LTODiagnosticInfo : public DiagnosticInfo {
+ const Twine &Msg;
+public:
+ LTODiagnosticInfo(const Twine &DiagMsg, DiagnosticSeverity Severity=DS_Error)
+ : DiagnosticInfo(DK_Linker, Severity), Msg(DiagMsg) {}
+ void print(DiagnosticPrinter &DP) const override { DP << Msg; }
+};
+}
+
+void LTOCodeGenerator::emitError(const std::string &ErrMsg) {
+ if (DiagHandler)
+ (*DiagHandler)(LTO_DS_ERROR, ErrMsg.c_str(), DiagContext);
+ else
+ Context.diagnose(LTODiagnosticInfo(ErrMsg));
+}
+
+void LTOCodeGenerator::emitWarning(const std::string &ErrMsg) {
+ if (DiagHandler)
+ (*DiagHandler)(LTO_DS_WARNING, ErrMsg.c_str(), DiagContext);
+ else
+ Context.diagnose(LTODiagnosticInfo(ErrMsg, DS_Warning));
+}
diff --git a/llvm/lib/LTO/LTOModule.cpp b/llvm/lib/LTO/LTOModule.cpp
new file mode 100644
index 0000000000000..587b332e70649
--- /dev/null
+++ b/llvm/lib/LTO/LTOModule.cpp
@@ -0,0 +1,678 @@
+//===-- LTOModule.cpp - LLVM Link Time Optimizer --------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the Link Time Optimization library. This library is
+// intended to be used by linker to optimize code at link time.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/LTO/legacy/LTOModule.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/CodeGen/TargetSubtargetInfo.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Mangler.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/IR/Module.h"
+#include "llvm/MC/MCExpr.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCParser/MCAsmParser.h"
+#include "llvm/MC/MCSection.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCSymbol.h"
+#include "llvm/MC/SubtargetFeature.h"
+#include "llvm/Object/IRObjectFile.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Target/TargetLoweringObjectFile.h"
+#include "llvm/Transforms/Utils/GlobalStatus.h"
+#include <system_error>
+using namespace llvm;
+using namespace llvm::object;
+
+LTOModule::LTOModule(std::unique_ptr<Module> M, MemoryBufferRef MBRef,
+ llvm::TargetMachine *TM)
+ : Mod(std::move(M)), MBRef(MBRef), _target(TM) {
+ SymTab.addModule(Mod.get());
+}
+
+LTOModule::~LTOModule() {}
+
+/// isBitcodeFile - Returns 'true' if the file (or memory contents) is LLVM
+/// bitcode.
+bool LTOModule::isBitcodeFile(const void *Mem, size_t Length) {
+ Expected<MemoryBufferRef> BCData = IRObjectFile::findBitcodeInMemBuffer(
+ MemoryBufferRef(StringRef((const char *)Mem, Length), "<mem>"));
+ return !errorToBool(BCData.takeError());
+}
+
+bool LTOModule::isBitcodeFile(StringRef Path) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
+ MemoryBuffer::getFile(Path);
+ if (!BufferOrErr)
+ return false;
+
+ Expected<MemoryBufferRef> BCData = IRObjectFile::findBitcodeInMemBuffer(
+ BufferOrErr.get()->getMemBufferRef());
+ return !errorToBool(BCData.takeError());
+}
+
+bool LTOModule::isThinLTO() {
+ Expected<BitcodeLTOInfo> Result = getBitcodeLTOInfo(MBRef);
+ if (!Result) {
+ logAllUnhandledErrors(Result.takeError(), errs());
+ return false;
+ }
+ return Result->IsThinLTO;
+}
+
+bool LTOModule::isBitcodeForTarget(MemoryBuffer *Buffer,
+ StringRef TriplePrefix) {
+ Expected<MemoryBufferRef> BCOrErr =
+ IRObjectFile::findBitcodeInMemBuffer(Buffer->getMemBufferRef());
+ if (errorToBool(BCOrErr.takeError()))
+ return false;
+ LLVMContext Context;
+ ErrorOr<std::string> TripleOrErr =
+ expectedToErrorOrAndEmitErrors(Context, getBitcodeTargetTriple(*BCOrErr));
+ if (!TripleOrErr)
+ return false;
+ return StringRef(*TripleOrErr).startswith(TriplePrefix);
+}
+
+std::string LTOModule::getProducerString(MemoryBuffer *Buffer) {
+ Expected<MemoryBufferRef> BCOrErr =
+ IRObjectFile::findBitcodeInMemBuffer(Buffer->getMemBufferRef());
+ if (errorToBool(BCOrErr.takeError()))
+ return "";
+ LLVMContext Context;
+ ErrorOr<std::string> ProducerOrErr = expectedToErrorOrAndEmitErrors(
+ Context, getBitcodeProducerString(*BCOrErr));
+ if (!ProducerOrErr)
+ return "";
+ return *ProducerOrErr;
+}
+
+ErrorOr<std::unique_ptr<LTOModule>>
+LTOModule::createFromFile(LLVMContext &Context, StringRef path,
+ const TargetOptions &options) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
+ MemoryBuffer::getFile(path);
+ if (std::error_code EC = BufferOrErr.getError()) {
+ Context.emitError(EC.message());
+ return EC;
+ }
+ std::unique_ptr<MemoryBuffer> Buffer = std::move(BufferOrErr.get());
+ return makeLTOModule(Buffer->getMemBufferRef(), options, Context,
+ /* ShouldBeLazy*/ false);
+}
+
+ErrorOr<std::unique_ptr<LTOModule>>
+LTOModule::createFromOpenFile(LLVMContext &Context, int fd, StringRef path,
+ size_t size, const TargetOptions &options) {
+ return createFromOpenFileSlice(Context, fd, path, size, 0, options);
+}
+
+ErrorOr<std::unique_ptr<LTOModule>>
+LTOModule::createFromOpenFileSlice(LLVMContext &Context, int fd, StringRef path,
+ size_t map_size, off_t offset,
+ const TargetOptions &options) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
+ MemoryBuffer::getOpenFileSlice(sys::fs::convertFDToNativeFile(fd), path,
+ map_size, offset);
+ if (std::error_code EC = BufferOrErr.getError()) {
+ Context.emitError(EC.message());
+ return EC;
+ }
+ std::unique_ptr<MemoryBuffer> Buffer = std::move(BufferOrErr.get());
+ return makeLTOModule(Buffer->getMemBufferRef(), options, Context,
+ /* ShouldBeLazy */ false);
+}
+
+ErrorOr<std::unique_ptr<LTOModule>>
+LTOModule::createFromBuffer(LLVMContext &Context, const void *mem,
+ size_t length, const TargetOptions &options,
+ StringRef path) {
+ StringRef Data((const char *)mem, length);
+ MemoryBufferRef Buffer(Data, path);
+ return makeLTOModule(Buffer, options, Context, /* ShouldBeLazy */ false);
+}
+
+ErrorOr<std::unique_ptr<LTOModule>>
+LTOModule::createInLocalContext(std::unique_ptr<LLVMContext> Context,
+ const void *mem, size_t length,
+ const TargetOptions &options, StringRef path) {
+ StringRef Data((const char *)mem, length);
+ MemoryBufferRef Buffer(Data, path);
+ // If we own a context, we know this is being used only for symbol extraction,
+ // not linking. Be lazy in that case.
+ ErrorOr<std::unique_ptr<LTOModule>> Ret =
+ makeLTOModule(Buffer, options, *Context, /* ShouldBeLazy */ true);
+ if (Ret)
+ (*Ret)->OwnedContext = std::move(Context);
+ return Ret;
+}
+
+static ErrorOr<std::unique_ptr<Module>>
+parseBitcodeFileImpl(MemoryBufferRef Buffer, LLVMContext &Context,
+ bool ShouldBeLazy) {
+ // Find the buffer.
+ Expected<MemoryBufferRef> MBOrErr =
+ IRObjectFile::findBitcodeInMemBuffer(Buffer);
+ if (Error E = MBOrErr.takeError()) {
+ std::error_code EC = errorToErrorCode(std::move(E));
+ Context.emitError(EC.message());
+ return EC;
+ }
+
+ if (!ShouldBeLazy) {
+ // Parse the full file.
+ return expectedToErrorOrAndEmitErrors(Context,
+ parseBitcodeFile(*MBOrErr, Context));
+ }
+
+ // Parse lazily.
+ return expectedToErrorOrAndEmitErrors(
+ Context,
+ getLazyBitcodeModule(*MBOrErr, Context, true /*ShouldLazyLoadMetadata*/));
+}
+
+ErrorOr<std::unique_ptr<LTOModule>>
+LTOModule::makeLTOModule(MemoryBufferRef Buffer, const TargetOptions &options,
+ LLVMContext &Context, bool ShouldBeLazy) {
+ ErrorOr<std::unique_ptr<Module>> MOrErr =
+ parseBitcodeFileImpl(Buffer, Context, ShouldBeLazy);
+ if (std::error_code EC = MOrErr.getError())
+ return EC;
+ std::unique_ptr<Module> &M = *MOrErr;
+
+ std::string TripleStr = M->getTargetTriple();
+ if (TripleStr.empty())
+ TripleStr = sys::getDefaultTargetTriple();
+ llvm::Triple Triple(TripleStr);
+
+ // find machine architecture for this module
+ std::string errMsg;
+ const Target *march = TargetRegistry::lookupTarget(TripleStr, errMsg);
+ if (!march)
+ return make_error_code(object::object_error::arch_not_found);
+
+ // construct LTOModule, hand over ownership of module and target
+ SubtargetFeatures Features;
+ Features.getDefaultSubtargetFeatures(Triple);
+ std::string FeatureStr = Features.getString();
+ // Set a default CPU for Darwin triples.
+ std::string CPU;
+ if (Triple.isOSDarwin()) {
+ if (Triple.getArch() == llvm::Triple::x86_64)
+ CPU = "core2";
+ else if (Triple.getArch() == llvm::Triple::x86)
+ CPU = "yonah";
+ else if (Triple.getArch() == llvm::Triple::aarch64 ||
+ Triple.getArch() == llvm::Triple::aarch64_32)
+ CPU = "cyclone";
+ }
+
+ TargetMachine *target =
+ march->createTargetMachine(TripleStr, CPU, FeatureStr, options, None);
+
+ std::unique_ptr<LTOModule> Ret(new LTOModule(std::move(M), Buffer, target));
+ Ret->parseSymbols();
+ Ret->parseMetadata();
+
+ return std::move(Ret);
+}
+
+/// Create a MemoryBuffer from a memory range with an optional name.
+std::unique_ptr<MemoryBuffer>
+LTOModule::makeBuffer(const void *mem, size_t length, StringRef name) {
+ const char *startPtr = (const char*)mem;
+ return MemoryBuffer::getMemBuffer(StringRef(startPtr, length), name, false);
+}
+
+/// objcClassNameFromExpression - Get string that the data pointer points to.
+bool
+LTOModule::objcClassNameFromExpression(const Constant *c, std::string &name) {
+ if (const ConstantExpr *ce = dyn_cast<ConstantExpr>(c)) {
+ Constant *op = ce->getOperand(0);
+ if (GlobalVariable *gvn = dyn_cast<GlobalVariable>(op)) {
+ Constant *cn = gvn->getInitializer();
+ if (ConstantDataArray *ca = dyn_cast<ConstantDataArray>(cn)) {
+ if (ca->isCString()) {
+ name = (".objc_class_name_" + ca->getAsCString()).str();
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/// addObjCClass - Parse i386/ppc ObjC class data structure.
+void LTOModule::addObjCClass(const GlobalVariable *clgv) {
+ const ConstantStruct *c = dyn_cast<ConstantStruct>(clgv->getInitializer());
+ if (!c) return;
+
+ // second slot in __OBJC,__class is pointer to superclass name
+ std::string superclassName;
+ if (objcClassNameFromExpression(c->getOperand(1), superclassName)) {
+ auto IterBool =
+ _undefines.insert(std::make_pair(superclassName, NameAndAttributes()));
+ if (IterBool.second) {
+ NameAndAttributes &info = IterBool.first->second;
+ info.name = IterBool.first->first();
+ info.attributes = LTO_SYMBOL_DEFINITION_UNDEFINED;
+ info.isFunction = false;
+ info.symbol = clgv;
+ }
+ }
+
+ // third slot in __OBJC,__class is pointer to class name
+ std::string className;
+ if (objcClassNameFromExpression(c->getOperand(2), className)) {
+ auto Iter = _defines.insert(className).first;
+
+ NameAndAttributes info;
+ info.name = Iter->first();
+ info.attributes = LTO_SYMBOL_PERMISSIONS_DATA |
+ LTO_SYMBOL_DEFINITION_REGULAR | LTO_SYMBOL_SCOPE_DEFAULT;
+ info.isFunction = false;
+ info.symbol = clgv;
+ _symbols.push_back(info);
+ }
+}
+
+/// addObjCCategory - Parse i386/ppc ObjC category data structure.
+void LTOModule::addObjCCategory(const GlobalVariable *clgv) {
+ const ConstantStruct *c = dyn_cast<ConstantStruct>(clgv->getInitializer());
+ if (!c) return;
+
+ // second slot in __OBJC,__category is pointer to target class name
+ std::string targetclassName;
+ if (!objcClassNameFromExpression(c->getOperand(1), targetclassName))
+ return;
+
+ auto IterBool =
+ _undefines.insert(std::make_pair(targetclassName, NameAndAttributes()));
+
+ if (!IterBool.second)
+ return;
+
+ NameAndAttributes &info = IterBool.first->second;
+ info.name = IterBool.first->first();
+ info.attributes = LTO_SYMBOL_DEFINITION_UNDEFINED;
+ info.isFunction = false;
+ info.symbol = clgv;
+}
+
+/// addObjCClassRef - Parse i386/ppc ObjC class list data structure.
+void LTOModule::addObjCClassRef(const GlobalVariable *clgv) {
+ std::string targetclassName;
+ if (!objcClassNameFromExpression(clgv->getInitializer(), targetclassName))
+ return;
+
+ auto IterBool =
+ _undefines.insert(std::make_pair(targetclassName, NameAndAttributes()));
+
+ if (!IterBool.second)
+ return;
+
+ NameAndAttributes &info = IterBool.first->second;
+ info.name = IterBool.first->first();
+ info.attributes = LTO_SYMBOL_DEFINITION_UNDEFINED;
+ info.isFunction = false;
+ info.symbol = clgv;
+}
+
+void LTOModule::addDefinedDataSymbol(ModuleSymbolTable::Symbol Sym) {
+ SmallString<64> Buffer;
+ {
+ raw_svector_ostream OS(Buffer);
+ SymTab.printSymbolName(OS, Sym);
+ Buffer.c_str();
+ }
+
+ const GlobalValue *V = Sym.get<GlobalValue *>();
+ addDefinedDataSymbol(Buffer, V);
+}
+
+void LTOModule::addDefinedDataSymbol(StringRef Name, const GlobalValue *v) {
+ // Add to list of defined symbols.
+ addDefinedSymbol(Name, v, false);
+
+ if (!v->hasSection() /* || !isTargetDarwin */)
+ return;
+
+ // Special case i386/ppc ObjC data structures in magic sections:
+ // The issue is that the old ObjC object format did some strange
+ // contortions to avoid real linker symbols. For instance, the
+ // ObjC class data structure is allocated statically in the executable
+ // that defines that class. That data structures contains a pointer to
+ // its superclass. But instead of just initializing that part of the
+ // struct to the address of its superclass, and letting the static and
+ // dynamic linkers do the rest, the runtime works by having that field
+ // instead point to a C-string that is the name of the superclass.
+ // At runtime the objc initialization updates that pointer and sets
+ // it to point to the actual super class. As far as the linker
+ // knows it is just a pointer to a string. But then someone wanted the
+ // linker to issue errors at build time if the superclass was not found.
+ // So they figured out a way in mach-o object format to use an absolute
+ // symbols (.objc_class_name_Foo = 0) and a floating reference
+ // (.reference .objc_class_name_Bar) to cause the linker into erroring when
+ // a class was missing.
+ // The following synthesizes the implicit .objc_* symbols for the linker
+ // from the ObjC data structures generated by the front end.
+
+ // special case if this data blob is an ObjC class definition
+ if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(v)) {
+ StringRef Section = GV->getSection();
+ if (Section.startswith("__OBJC,__class,")) {
+ addObjCClass(GV);
+ }
+
+ // special case if this data blob is an ObjC category definition
+ else if (Section.startswith("__OBJC,__category,")) {
+ addObjCCategory(GV);
+ }
+
+ // special case if this data blob is the list of referenced classes
+ else if (Section.startswith("__OBJC,__cls_refs,")) {
+ addObjCClassRef(GV);
+ }
+ }
+}
+
+void LTOModule::addDefinedFunctionSymbol(ModuleSymbolTable::Symbol Sym) {
+ SmallString<64> Buffer;
+ {
+ raw_svector_ostream OS(Buffer);
+ SymTab.printSymbolName(OS, Sym);
+ Buffer.c_str();
+ }
+
+ const Function *F = cast<Function>(Sym.get<GlobalValue *>());
+ addDefinedFunctionSymbol(Buffer, F);
+}
+
+void LTOModule::addDefinedFunctionSymbol(StringRef Name, const Function *F) {
+ // add to list of defined symbols
+ addDefinedSymbol(Name, F, true);
+}
+
+void LTOModule::addDefinedSymbol(StringRef Name, const GlobalValue *def,
+ bool isFunction) {
+ // set alignment part log2() can have rounding errors
+ uint32_t align = def->getAlignment();
+ uint32_t attr = align ? countTrailingZeros(align) : 0;
+
+ // set permissions part
+ if (isFunction) {
+ attr |= LTO_SYMBOL_PERMISSIONS_CODE;
+ } else {
+ const GlobalVariable *gv = dyn_cast<GlobalVariable>(def);
+ if (gv && gv->isConstant())
+ attr |= LTO_SYMBOL_PERMISSIONS_RODATA;
+ else
+ attr |= LTO_SYMBOL_PERMISSIONS_DATA;
+ }
+
+ // set definition part
+ if (def->hasWeakLinkage() || def->hasLinkOnceLinkage())
+ attr |= LTO_SYMBOL_DEFINITION_WEAK;
+ else if (def->hasCommonLinkage())
+ attr |= LTO_SYMBOL_DEFINITION_TENTATIVE;
+ else
+ attr |= LTO_SYMBOL_DEFINITION_REGULAR;
+
+ // set scope part
+ if (def->hasLocalLinkage())
+ // Ignore visibility if linkage is local.
+ attr |= LTO_SYMBOL_SCOPE_INTERNAL;
+ else if (def->hasHiddenVisibility())
+ attr |= LTO_SYMBOL_SCOPE_HIDDEN;
+ else if (def->hasProtectedVisibility())
+ attr |= LTO_SYMBOL_SCOPE_PROTECTED;
+ else if (def->canBeOmittedFromSymbolTable())
+ attr |= LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN;
+ else
+ attr |= LTO_SYMBOL_SCOPE_DEFAULT;
+
+ if (def->hasComdat())
+ attr |= LTO_SYMBOL_COMDAT;
+
+ if (isa<GlobalAlias>(def))
+ attr |= LTO_SYMBOL_ALIAS;
+
+ auto Iter = _defines.insert(Name).first;
+
+ // fill information structure
+ NameAndAttributes info;
+ StringRef NameRef = Iter->first();
+ info.name = NameRef;
+ assert(NameRef.data()[NameRef.size()] == '\0');
+ info.attributes = attr;
+ info.isFunction = isFunction;
+ info.symbol = def;
+
+ // add to table of symbols
+ _symbols.push_back(info);
+}
+
+/// addAsmGlobalSymbol - Add a global symbol from module-level ASM to the
+/// defined list.
+void LTOModule::addAsmGlobalSymbol(StringRef name,
+ lto_symbol_attributes scope) {
+ auto IterBool = _defines.insert(name);
+
+ // only add new define if not already defined
+ if (!IterBool.second)
+ return;
+
+ NameAndAttributes &info = _undefines[IterBool.first->first()];
+
+ if (info.symbol == nullptr) {
+ // FIXME: This is trying to take care of module ASM like this:
+ //
+ // module asm ".zerofill __FOO, __foo, _bar_baz_qux, 0"
+ //
+ // but is gross and its mother dresses it funny. Have the ASM parser give us
+ // more details for this type of situation so that we're not guessing so
+ // much.
+
+ // fill information structure
+ info.name = IterBool.first->first();
+ info.attributes =
+ LTO_SYMBOL_PERMISSIONS_DATA | LTO_SYMBOL_DEFINITION_REGULAR | scope;
+ info.isFunction = false;
+ info.symbol = nullptr;
+
+ // add to table of symbols
+ _symbols.push_back(info);
+ return;
+ }
+
+ if (info.isFunction)
+ addDefinedFunctionSymbol(info.name, cast<Function>(info.symbol));
+ else
+ addDefinedDataSymbol(info.name, info.symbol);
+
+ _symbols.back().attributes &= ~LTO_SYMBOL_SCOPE_MASK;
+ _symbols.back().attributes |= scope;
+}
+
+/// addAsmGlobalSymbolUndef - Add a global symbol from module-level ASM to the
+/// undefined list.
+void LTOModule::addAsmGlobalSymbolUndef(StringRef name) {
+ auto IterBool = _undefines.insert(std::make_pair(name, NameAndAttributes()));
+
+ _asm_undefines.push_back(IterBool.first->first());
+
+ // we already have the symbol
+ if (!IterBool.second)
+ return;
+
+ uint32_t attr = LTO_SYMBOL_DEFINITION_UNDEFINED;
+ attr |= LTO_SYMBOL_SCOPE_DEFAULT;
+ NameAndAttributes &info = IterBool.first->second;
+ info.name = IterBool.first->first();
+ info.attributes = attr;
+ info.isFunction = false;
+ info.symbol = nullptr;
+}
+
+/// Add a symbol which isn't defined just yet to a list to be resolved later.
+void LTOModule::addPotentialUndefinedSymbol(ModuleSymbolTable::Symbol Sym,
+ bool isFunc) {
+ SmallString<64> name;
+ {
+ raw_svector_ostream OS(name);
+ SymTab.printSymbolName(OS, Sym);
+ name.c_str();
+ }
+
+ auto IterBool = _undefines.insert(std::make_pair(name, NameAndAttributes()));
+
+ // we already have the symbol
+ if (!IterBool.second)
+ return;
+
+ NameAndAttributes &info = IterBool.first->second;
+
+ info.name = IterBool.first->first();
+
+ const GlobalValue *decl = Sym.dyn_cast<GlobalValue *>();
+
+ if (decl->hasExternalWeakLinkage())
+ info.attributes = LTO_SYMBOL_DEFINITION_WEAKUNDEF;
+ else
+ info.attributes = LTO_SYMBOL_DEFINITION_UNDEFINED;
+
+ info.isFunction = isFunc;
+ info.symbol = decl;
+}
+
+void LTOModule::parseSymbols() {
+ for (auto Sym : SymTab.symbols()) {
+ auto *GV = Sym.dyn_cast<GlobalValue *>();
+ uint32_t Flags = SymTab.getSymbolFlags(Sym);
+ if (Flags & object::BasicSymbolRef::SF_FormatSpecific)
+ continue;
+
+ bool IsUndefined = Flags & object::BasicSymbolRef::SF_Undefined;
+
+ if (!GV) {
+ SmallString<64> Buffer;
+ {
+ raw_svector_ostream OS(Buffer);
+ SymTab.printSymbolName(OS, Sym);
+ Buffer.c_str();
+ }
+ StringRef Name(Buffer);
+
+ if (IsUndefined)
+ addAsmGlobalSymbolUndef(Name);
+ else if (Flags & object::BasicSymbolRef::SF_Global)
+ addAsmGlobalSymbol(Name, LTO_SYMBOL_SCOPE_DEFAULT);
+ else
+ addAsmGlobalSymbol(Name, LTO_SYMBOL_SCOPE_INTERNAL);
+ continue;
+ }
+
+ auto *F = dyn_cast<Function>(GV);
+ if (IsUndefined) {
+ addPotentialUndefinedSymbol(Sym, F != nullptr);
+ continue;
+ }
+
+ if (F) {
+ addDefinedFunctionSymbol(Sym);
+ continue;
+ }
+
+ if (isa<GlobalVariable>(GV)) {
+ addDefinedDataSymbol(Sym);
+ continue;
+ }
+
+ assert(isa<GlobalAlias>(GV));
+ addDefinedDataSymbol(Sym);
+ }
+
+ // make symbols for all undefines
+ for (StringMap<NameAndAttributes>::iterator u =_undefines.begin(),
+ e = _undefines.end(); u != e; ++u) {
+ // If this symbol also has a definition, then don't make an undefine because
+ // it is a tentative definition.
+ if (_defines.count(u->getKey())) continue;
+ NameAndAttributes info = u->getValue();
+ _symbols.push_back(info);
+ }
+}
+
+/// parseMetadata - Parse metadata from the module
+void LTOModule::parseMetadata() {
+ raw_string_ostream OS(LinkerOpts);
+
+ // Linker Options
+ if (NamedMDNode *LinkerOptions =
+ getModule().getNamedMetadata("llvm.linker.options")) {
+ for (unsigned i = 0, e = LinkerOptions->getNumOperands(); i != e; ++i) {
+ MDNode *MDOptions = LinkerOptions->getOperand(i);
+ for (unsigned ii = 0, ie = MDOptions->getNumOperands(); ii != ie; ++ii) {
+ MDString *MDOption = cast<MDString>(MDOptions->getOperand(ii));
+ OS << " " << MDOption->getString();
+ }
+ }
+ }
+
+ // Globals - we only need to do this for COFF.
+ const Triple TT(_target->getTargetTriple());
+ if (!TT.isOSBinFormatCOFF())
+ return;
+ Mangler M;
+ for (const NameAndAttributes &Sym : _symbols) {
+ if (!Sym.symbol)
+ continue;
+ emitLinkerFlagsForGlobalCOFF(OS, Sym.symbol, TT, M);
+ }
+}
+
+lto::InputFile *LTOModule::createInputFile(const void *buffer,
+ size_t buffer_size, const char *path,
+ std::string &outErr) {
+ StringRef Data((const char *)buffer, buffer_size);
+ MemoryBufferRef BufferRef(Data, path);
+
+ Expected<std::unique_ptr<lto::InputFile>> ObjOrErr =
+ lto::InputFile::create(BufferRef);
+
+ if (ObjOrErr)
+ return ObjOrErr->release();
+
+ outErr = std::string(path) +
+ ": Could not read LTO input file: " + toString(ObjOrErr.takeError());
+ return nullptr;
+}
+
+size_t LTOModule::getDependentLibraryCount(lto::InputFile *input) {
+ return input->getDependentLibraries().size();
+}
+
+const char *LTOModule::getDependentLibrary(lto::InputFile *input, size_t index,
+ size_t *size) {
+ StringRef S = input->getDependentLibraries()[index];
+ *size = S.size();
+ return S.data();
+}
diff --git a/llvm/lib/LTO/SummaryBasedOptimizations.cpp b/llvm/lib/LTO/SummaryBasedOptimizations.cpp
new file mode 100644
index 0000000000000..6db495de003b6
--- /dev/null
+++ b/llvm/lib/LTO/SummaryBasedOptimizations.cpp
@@ -0,0 +1,85 @@
+//==-SummaryBasedOptimizations.cpp - Optimizations based on ThinLTO summary-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements optimizations that are based on the module summaries.
+// These optimizations are performed during the thinlink phase of the
+// compilation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/LTO/SummaryBasedOptimizations.h"
+#include "llvm/Analysis/SyntheticCountsUtils.h"
+#include "llvm/IR/ModuleSummaryIndex.h"
+
+using namespace llvm;
+
+static cl::opt<bool> ThinLTOSynthesizeEntryCounts(
+ "thinlto-synthesize-entry-counts", cl::init(false), cl::Hidden,
+ cl::desc("Synthesize entry counts based on the summary"));
+
+extern cl::opt<int> InitialSyntheticCount;
+
+static void initializeCounts(ModuleSummaryIndex &Index) {
+ auto Root = Index.calculateCallGraphRoot();
+ // Root is a fake node. All its successors are the actual roots of the
+ // callgraph.
+ // FIXME: This initializes the entry counts of only the root nodes. This makes
+ // sense when compiling a binary with ThinLTO, but for libraries any of the
+ // non-root nodes could be called from outside.
+ for (auto &C : Root.calls()) {
+ auto &V = C.first;
+ for (auto &GVS : V.getSummaryList()) {
+ auto S = GVS.get()->getBaseObject();
+ auto *F = cast<FunctionSummary>(S);
+ F->setEntryCount(InitialSyntheticCount);
+ }
+ }
+}
+
+void llvm::computeSyntheticCounts(ModuleSummaryIndex &Index) {
+ if (!ThinLTOSynthesizeEntryCounts)
+ return;
+
+ using Scaled64 = ScaledNumber<uint64_t>;
+ initializeCounts(Index);
+ auto GetCallSiteRelFreq = [](FunctionSummary::EdgeTy &Edge) {
+ return Scaled64(Edge.second.RelBlockFreq, -CalleeInfo::ScaleShift);
+ };
+ auto GetEntryCount = [](ValueInfo V) {
+ if (V.getSummaryList().size()) {
+ auto S = V.getSummaryList().front().get()->getBaseObject();
+ auto *F = cast<FunctionSummary>(S);
+ return F->entryCount();
+ } else {
+ return UINT64_C(0);
+ }
+ };
+ auto AddToEntryCount = [](ValueInfo V, Scaled64 New) {
+ if (!V.getSummaryList().size())
+ return;
+ for (auto &GVS : V.getSummaryList()) {
+ auto S = GVS.get()->getBaseObject();
+ auto *F = cast<FunctionSummary>(S);
+ F->setEntryCount(
+ SaturatingAdd(F->entryCount(), New.template toInt<uint64_t>()));
+ }
+ };
+
+ auto GetProfileCount = [&](ValueInfo V, FunctionSummary::EdgeTy &Edge) {
+ auto RelFreq = GetCallSiteRelFreq(Edge);
+ Scaled64 EC(GetEntryCount(V), 0);
+ return RelFreq * EC;
+ };
+ // After initializing the counts in initializeCounts above, the counts have to
+ // be propagated across the combined callgraph.
+ // SyntheticCountsUtils::propagate takes care of this propagation on any
+ // callgraph that specialized GraphTraits.
+ SyntheticCountsUtils<ModuleSummaryIndex *>::propagate(&Index, GetProfileCount,
+ AddToEntryCount);
+ Index.setHasSyntheticEntryCounts();
+}
diff --git a/llvm/lib/LTO/ThinLTOCodeGenerator.cpp b/llvm/lib/LTO/ThinLTOCodeGenerator.cpp
new file mode 100644
index 0000000000000..d151de17896f6
--- /dev/null
+++ b/llvm/lib/LTO/ThinLTOCodeGenerator.cpp
@@ -0,0 +1,1135 @@
+//===-ThinLTOCodeGenerator.cpp - LLVM Link Time Optimizer -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the Thin Link Time Optimization library. This library is
+// intended to be used by linker to optimize code at link time.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/LTO/legacy/ThinLTOCodeGenerator.h"
+
+#include "llvm/ADT/Statistic.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Analysis/ModuleSummaryAnalysis.h"
+#include "llvm/Analysis/ProfileSummaryInfo.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/Analysis/TargetTransformInfo.h"
+#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
+#include "llvm/Bitcode/BitcodeWriterPass.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/IR/DebugInfo.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Mangler.h"
+#include "llvm/IR/PassTimingInfo.h"
+#include "llvm/IR/RemarkStreamer.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/LTO/LTO.h"
+#include "llvm/LTO/SummaryBasedOptimizations.h"
+#include "llvm/MC/SubtargetFeature.h"
+#include "llvm/Object/IRObjectFile.h"
+#include "llvm/Support/CachePruning.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/SHA1.h"
+#include "llvm/Support/SmallVectorMemoryBuffer.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/ThreadPool.h"
+#include "llvm/Support/Threading.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/VCSRevision.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Transforms/IPO.h"
+#include "llvm/Transforms/IPO/FunctionImport.h"
+#include "llvm/Transforms/IPO/Internalize.h"
+#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+#include "llvm/Transforms/IPO/WholeProgramDevirt.h"
+#include "llvm/Transforms/ObjCARC.h"
+#include "llvm/Transforms/Utils/FunctionImportUtils.h"
+
+#include <numeric>
+
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+#include <unistd.h>
+#else
+#include <io.h>
+#endif
+
+using namespace llvm;
+
+#define DEBUG_TYPE "thinlto"
+
+namespace llvm {
+// Flags -discard-value-names, defined in LTOCodeGenerator.cpp
+extern cl::opt<bool> LTODiscardValueNames;
+extern cl::opt<std::string> RemarksFilename;
+extern cl::opt<std::string> RemarksPasses;
+extern cl::opt<bool> RemarksWithHotness;
+extern cl::opt<std::string> RemarksFormat;
+}
+
+namespace {
+
+static cl::opt<int>
+ ThreadCount("threads", cl::init(llvm::heavyweight_hardware_concurrency()));
+
+// Simple helper to save temporary files for debug.
+static void saveTempBitcode(const Module &TheModule, StringRef TempDir,
+ unsigned count, StringRef Suffix) {
+ if (TempDir.empty())
+ return;
+ // User asked to save temps, let dump the bitcode file after import.
+ std::string SaveTempPath = (TempDir + llvm::Twine(count) + Suffix).str();
+ std::error_code EC;
+ raw_fd_ostream OS(SaveTempPath, EC, sys::fs::OF_None);
+ if (EC)
+ report_fatal_error(Twine("Failed to open ") + SaveTempPath +
+ " to save optimized bitcode\n");
+ WriteBitcodeToFile(TheModule, OS, /* ShouldPreserveUseListOrder */ true);
+}
+
+static const GlobalValueSummary *
+getFirstDefinitionForLinker(const GlobalValueSummaryList &GVSummaryList) {
+ // If there is any strong definition anywhere, get it.
+ auto StrongDefForLinker = llvm::find_if(
+ GVSummaryList, [](const std::unique_ptr<GlobalValueSummary> &Summary) {
+ auto Linkage = Summary->linkage();
+ return !GlobalValue::isAvailableExternallyLinkage(Linkage) &&
+ !GlobalValue::isWeakForLinker(Linkage);
+ });
+ if (StrongDefForLinker != GVSummaryList.end())
+ return StrongDefForLinker->get();
+ // Get the first *linker visible* definition for this global in the summary
+ // list.
+ auto FirstDefForLinker = llvm::find_if(
+ GVSummaryList, [](const std::unique_ptr<GlobalValueSummary> &Summary) {
+ auto Linkage = Summary->linkage();
+ return !GlobalValue::isAvailableExternallyLinkage(Linkage);
+ });
+ // Extern templates can be emitted as available_externally.
+ if (FirstDefForLinker == GVSummaryList.end())
+ return nullptr;
+ return FirstDefForLinker->get();
+}
+
+// Populate map of GUID to the prevailing copy for any multiply defined
+// symbols. Currently assume first copy is prevailing, or any strong
+// definition. Can be refined with Linker information in the future.
+static void computePrevailingCopies(
+ const ModuleSummaryIndex &Index,
+ DenseMap<GlobalValue::GUID, const GlobalValueSummary *> &PrevailingCopy) {
+ auto HasMultipleCopies = [&](const GlobalValueSummaryList &GVSummaryList) {
+ return GVSummaryList.size() > 1;
+ };
+
+ for (auto &I : Index) {
+ if (HasMultipleCopies(I.second.SummaryList))
+ PrevailingCopy[I.first] =
+ getFirstDefinitionForLinker(I.second.SummaryList);
+ }
+}
+
+static StringMap<lto::InputFile *>
+generateModuleMap(std::vector<std::unique_ptr<lto::InputFile>> &Modules) {
+ StringMap<lto::InputFile *> ModuleMap;
+ for (auto &M : Modules) {
+ assert(ModuleMap.find(M->getName()) == ModuleMap.end() &&
+ "Expect unique Buffer Identifier");
+ ModuleMap[M->getName()] = M.get();
+ }
+ return ModuleMap;
+}
+
+static void promoteModule(Module &TheModule, const ModuleSummaryIndex &Index) {
+ if (renameModuleForThinLTO(TheModule, Index))
+ report_fatal_error("renameModuleForThinLTO failed");
+}
+
+namespace {
+class ThinLTODiagnosticInfo : public DiagnosticInfo {
+ const Twine &Msg;
+public:
+ ThinLTODiagnosticInfo(const Twine &DiagMsg,
+ DiagnosticSeverity Severity = DS_Error)
+ : DiagnosticInfo(DK_Linker, Severity), Msg(DiagMsg) {}
+ void print(DiagnosticPrinter &DP) const override { DP << Msg; }
+};
+}
+
+/// Verify the module and strip broken debug info.
+static void verifyLoadedModule(Module &TheModule) {
+ bool BrokenDebugInfo = false;
+ if (verifyModule(TheModule, &dbgs(), &BrokenDebugInfo))
+ report_fatal_error("Broken module found, compilation aborted!");
+ if (BrokenDebugInfo) {
+ TheModule.getContext().diagnose(ThinLTODiagnosticInfo(
+ "Invalid debug info found, debug info will be stripped", DS_Warning));
+ StripDebugInfo(TheModule);
+ }
+}
+
+static std::unique_ptr<Module> loadModuleFromInput(lto::InputFile *Input,
+ LLVMContext &Context,
+ bool Lazy,
+ bool IsImporting) {
+ auto &Mod = Input->getSingleBitcodeModule();
+ SMDiagnostic Err;
+ Expected<std::unique_ptr<Module>> ModuleOrErr =
+ Lazy ? Mod.getLazyModule(Context,
+ /* ShouldLazyLoadMetadata */ true, IsImporting)
+ : Mod.parseModule(Context);
+ if (!ModuleOrErr) {
+ handleAllErrors(ModuleOrErr.takeError(), [&](ErrorInfoBase &EIB) {
+ SMDiagnostic Err = SMDiagnostic(Mod.getModuleIdentifier(),
+ SourceMgr::DK_Error, EIB.message());
+ Err.print("ThinLTO", errs());
+ });
+ report_fatal_error("Can't load module, abort.");
+ }
+ if (!Lazy)
+ verifyLoadedModule(*ModuleOrErr.get());
+ return std::move(*ModuleOrErr);
+}
+
+static void
+crossImportIntoModule(Module &TheModule, const ModuleSummaryIndex &Index,
+ StringMap<lto::InputFile*> &ModuleMap,
+ const FunctionImporter::ImportMapTy &ImportList) {
+ auto Loader = [&](StringRef Identifier) {
+ auto &Input = ModuleMap[Identifier];
+ return loadModuleFromInput(Input, TheModule.getContext(),
+ /*Lazy=*/true, /*IsImporting*/ true);
+ };
+
+ FunctionImporter Importer(Index, Loader);
+ Expected<bool> Result = Importer.importFunctions(TheModule, ImportList);
+ if (!Result) {
+ handleAllErrors(Result.takeError(), [&](ErrorInfoBase &EIB) {
+ SMDiagnostic Err = SMDiagnostic(TheModule.getModuleIdentifier(),
+ SourceMgr::DK_Error, EIB.message());
+ Err.print("ThinLTO", errs());
+ });
+ report_fatal_error("importFunctions failed");
+ }
+ // Verify again after cross-importing.
+ verifyLoadedModule(TheModule);
+}
+
+static void optimizeModule(Module &TheModule, TargetMachine &TM,
+ unsigned OptLevel, bool Freestanding,
+ ModuleSummaryIndex *Index) {
+ // Populate the PassManager
+ PassManagerBuilder PMB;
+ PMB.LibraryInfo = new TargetLibraryInfoImpl(TM.getTargetTriple());
+ if (Freestanding)
+ PMB.LibraryInfo->disableAllFunctions();
+ PMB.Inliner = createFunctionInliningPass();
+ // FIXME: should get it from the bitcode?
+ PMB.OptLevel = OptLevel;
+ PMB.LoopVectorize = true;
+ PMB.SLPVectorize = true;
+ // Already did this in verifyLoadedModule().
+ PMB.VerifyInput = false;
+ PMB.VerifyOutput = false;
+ PMB.ImportSummary = Index;
+
+ legacy::PassManager PM;
+
+ // Add the TTI (required to inform the vectorizer about register size for
+ // instance)
+ PM.add(createTargetTransformInfoWrapperPass(TM.getTargetIRAnalysis()));
+
+ // Add optimizations
+ PMB.populateThinLTOPassManager(PM);
+
+ PM.run(TheModule);
+}
+
+static void
+addUsedSymbolToPreservedGUID(const lto::InputFile &File,
+ DenseSet<GlobalValue::GUID> &PreservedGUID) {
+ for (const auto &Sym : File.symbols()) {
+ if (Sym.isUsed())
+ PreservedGUID.insert(GlobalValue::getGUID(Sym.getIRName()));
+ }
+}
+
+// Convert the PreservedSymbols map from "Name" based to "GUID" based.
+static DenseSet<GlobalValue::GUID>
+computeGUIDPreservedSymbols(const StringSet<> &PreservedSymbols,
+ const Triple &TheTriple) {
+ DenseSet<GlobalValue::GUID> GUIDPreservedSymbols(PreservedSymbols.size());
+ for (auto &Entry : PreservedSymbols) {
+ StringRef Name = Entry.first();
+ if (TheTriple.isOSBinFormatMachO() && Name.size() > 0 && Name[0] == '_')
+ Name = Name.drop_front();
+ GUIDPreservedSymbols.insert(GlobalValue::getGUID(Name));
+ }
+ return GUIDPreservedSymbols;
+}
+
+std::unique_ptr<MemoryBuffer> codegenModule(Module &TheModule,
+ TargetMachine &TM) {
+ SmallVector<char, 128> OutputBuffer;
+
+ // CodeGen
+ {
+ raw_svector_ostream OS(OutputBuffer);
+ legacy::PassManager PM;
+
+ // If the bitcode files contain ARC code and were compiled with optimization,
+ // the ObjCARCContractPass must be run, so do it unconditionally here.
+ PM.add(createObjCARCContractPass());
+
+ // Setup the codegen now.
+ if (TM.addPassesToEmitFile(PM, OS, nullptr, TargetMachine::CGFT_ObjectFile,
+ /* DisableVerify */ true))
+ report_fatal_error("Failed to setup codegen");
+
+ // Run codegen now. resulting binary is in OutputBuffer.
+ PM.run(TheModule);
+ }
+ return std::make_unique<SmallVectorMemoryBuffer>(std::move(OutputBuffer));
+}
+
+/// Manage caching for a single Module.
+class ModuleCacheEntry {
+ SmallString<128> EntryPath;
+
+public:
+ // Create a cache entry. This compute a unique hash for the Module considering
+ // the current list of export/import, and offer an interface to query to
+ // access the content in the cache.
+ ModuleCacheEntry(
+ StringRef CachePath, const ModuleSummaryIndex &Index, StringRef ModuleID,
+ const FunctionImporter::ImportMapTy &ImportList,
+ const FunctionImporter::ExportSetTy &ExportList,
+ const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR,
+ const GVSummaryMapTy &DefinedGVSummaries, unsigned OptLevel,
+ bool Freestanding, const TargetMachineBuilder &TMBuilder) {
+ if (CachePath.empty())
+ return;
+
+ if (!Index.modulePaths().count(ModuleID))
+ // The module does not have an entry, it can't have a hash at all
+ return;
+
+ if (all_of(Index.getModuleHash(ModuleID),
+ [](uint32_t V) { return V == 0; }))
+ // No hash entry, no caching!
+ return;
+
+ llvm::lto::Config Conf;
+ Conf.OptLevel = OptLevel;
+ Conf.Options = TMBuilder.Options;
+ Conf.CPU = TMBuilder.MCpu;
+ Conf.MAttrs.push_back(TMBuilder.MAttr);
+ Conf.RelocModel = TMBuilder.RelocModel;
+ Conf.CGOptLevel = TMBuilder.CGOptLevel;
+ Conf.Freestanding = Freestanding;
+ SmallString<40> Key;
+ computeLTOCacheKey(Key, Conf, Index, ModuleID, ImportList, ExportList,
+ ResolvedODR, DefinedGVSummaries);
+
+ // This choice of file name allows the cache to be pruned (see pruneCache()
+ // in include/llvm/Support/CachePruning.h).
+ sys::path::append(EntryPath, CachePath, "llvmcache-" + Key);
+ }
+
+ // Access the path to this entry in the cache.
+ StringRef getEntryPath() { return EntryPath; }
+
+ // Try loading the buffer for this cache entry.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> tryLoadingBuffer() {
+ if (EntryPath.empty())
+ return std::error_code();
+ SmallString<64> ResultPath;
+ Expected<sys::fs::file_t> FDOrErr = sys::fs::openNativeFileForRead(
+ Twine(EntryPath), sys::fs::OF_UpdateAtime, &ResultPath);
+ if (!FDOrErr)
+ return errorToErrorCode(FDOrErr.takeError());
+ ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = MemoryBuffer::getOpenFile(
+ *FDOrErr, EntryPath, /*FileSize=*/-1, /*RequiresNullTerminator=*/false);
+ sys::fs::closeFile(*FDOrErr);
+ return MBOrErr;
+ }
+
+ // Cache the Produced object file
+ void write(const MemoryBuffer &OutputBuffer) {
+ if (EntryPath.empty())
+ return;
+
+ // Write to a temporary to avoid race condition
+ SmallString<128> TempFilename;
+ SmallString<128> CachePath(EntryPath);
+ llvm::sys::path::remove_filename(CachePath);
+ sys::path::append(TempFilename, CachePath, "Thin-%%%%%%.tmp.o");
+
+ if (auto Err = handleErrors(
+ llvm::writeFileAtomically(TempFilename, EntryPath,
+ OutputBuffer.getBuffer()),
+ [](const llvm::AtomicFileWriteError &E) {
+ std::string ErrorMsgBuffer;
+ llvm::raw_string_ostream S(ErrorMsgBuffer);
+ E.log(S);
+
+ if (E.Error ==
+ llvm::atomic_write_error::failed_to_create_uniq_file) {
+ errs() << "Error: " << ErrorMsgBuffer << "\n";
+ report_fatal_error("ThinLTO: Can't get a temporary file");
+ }
+ })) {
+ // FIXME
+ consumeError(std::move(Err));
+ }
+ }
+};
+
+static std::unique_ptr<MemoryBuffer>
+ProcessThinLTOModule(Module &TheModule, ModuleSummaryIndex &Index,
+ StringMap<lto::InputFile *> &ModuleMap, TargetMachine &TM,
+ const FunctionImporter::ImportMapTy &ImportList,
+ const FunctionImporter::ExportSetTy &ExportList,
+ const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols,
+ const GVSummaryMapTy &DefinedGlobals,
+ const ThinLTOCodeGenerator::CachingOptions &CacheOptions,
+ bool DisableCodeGen, StringRef SaveTempsDir,
+ bool Freestanding, unsigned OptLevel, unsigned count) {
+
+ // "Benchmark"-like optimization: single-source case
+ bool SingleModule = (ModuleMap.size() == 1);
+
+ if (!SingleModule) {
+ promoteModule(TheModule, Index);
+
+ // Apply summary-based prevailing-symbol resolution decisions.
+ thinLTOResolvePrevailingInModule(TheModule, DefinedGlobals);
+
+ // Save temps: after promotion.
+ saveTempBitcode(TheModule, SaveTempsDir, count, ".1.promoted.bc");
+ }
+
+ // Be friendly and don't nuke totally the module when the client didn't
+ // supply anything to preserve.
+ if (!ExportList.empty() || !GUIDPreservedSymbols.empty()) {
+ // Apply summary-based internalization decisions.
+ thinLTOInternalizeModule(TheModule, DefinedGlobals);
+ }
+
+ // Save internalized bitcode
+ saveTempBitcode(TheModule, SaveTempsDir, count, ".2.internalized.bc");
+
+ if (!SingleModule) {
+ crossImportIntoModule(TheModule, Index, ModuleMap, ImportList);
+
+ // Save temps: after cross-module import.
+ saveTempBitcode(TheModule, SaveTempsDir, count, ".3.imported.bc");
+ }
+
+ optimizeModule(TheModule, TM, OptLevel, Freestanding, &Index);
+
+ saveTempBitcode(TheModule, SaveTempsDir, count, ".4.opt.bc");
+
+ if (DisableCodeGen) {
+ // Configured to stop before CodeGen, serialize the bitcode and return.
+ SmallVector<char, 128> OutputBuffer;
+ {
+ raw_svector_ostream OS(OutputBuffer);
+ ProfileSummaryInfo PSI(TheModule);
+ auto Index = buildModuleSummaryIndex(TheModule, nullptr, &PSI);
+ WriteBitcodeToFile(TheModule, OS, true, &Index);
+ }
+ return std::make_unique<SmallVectorMemoryBuffer>(std::move(OutputBuffer));
+ }
+
+ return codegenModule(TheModule, TM);
+}
+
+/// Resolve prevailing symbols. Record resolutions in the \p ResolvedODR map
+/// for caching, and in the \p Index for application during the ThinLTO
+/// backends. This is needed for correctness for exported symbols (ensure
+/// at least one copy kept) and a compile-time optimization (to drop duplicate
+/// copies when possible).
+static void resolvePrevailingInIndex(
+ ModuleSummaryIndex &Index,
+ StringMap<std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>>
+ &ResolvedODR,
+ const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols,
+ const DenseMap<GlobalValue::GUID, const GlobalValueSummary *>
+ &PrevailingCopy) {
+
+ auto isPrevailing = [&](GlobalValue::GUID GUID, const GlobalValueSummary *S) {
+ const auto &Prevailing = PrevailingCopy.find(GUID);
+ // Not in map means that there was only one copy, which must be prevailing.
+ if (Prevailing == PrevailingCopy.end())
+ return true;
+ return Prevailing->second == S;
+ };
+
+ auto recordNewLinkage = [&](StringRef ModuleIdentifier,
+ GlobalValue::GUID GUID,
+ GlobalValue::LinkageTypes NewLinkage) {
+ ResolvedODR[ModuleIdentifier][GUID] = NewLinkage;
+ };
+
+ thinLTOResolvePrevailingInIndex(Index, isPrevailing, recordNewLinkage,
+ GUIDPreservedSymbols);
+}
+
+// Initialize the TargetMachine builder for a given Triple
+static void initTMBuilder(TargetMachineBuilder &TMBuilder,
+ const Triple &TheTriple) {
+ // Set a default CPU for Darwin triples (copied from LTOCodeGenerator).
+ // FIXME this looks pretty terrible...
+ if (TMBuilder.MCpu.empty() && TheTriple.isOSDarwin()) {
+ if (TheTriple.getArch() == llvm::Triple::x86_64)
+ TMBuilder.MCpu = "core2";
+ else if (TheTriple.getArch() == llvm::Triple::x86)
+ TMBuilder.MCpu = "yonah";
+ else if (TheTriple.getArch() == llvm::Triple::aarch64 ||
+ TheTriple.getArch() == llvm::Triple::aarch64_32)
+ TMBuilder.MCpu = "cyclone";
+ }
+ TMBuilder.TheTriple = std::move(TheTriple);
+}
+
+} // end anonymous namespace
+
+void ThinLTOCodeGenerator::addModule(StringRef Identifier, StringRef Data) {
+ MemoryBufferRef Buffer(Data, Identifier);
+
+ auto InputOrError = lto::InputFile::create(Buffer);
+ if (!InputOrError)
+ report_fatal_error("ThinLTO cannot create input file: " +
+ toString(InputOrError.takeError()));
+
+ auto TripleStr = (*InputOrError)->getTargetTriple();
+ Triple TheTriple(TripleStr);
+
+ if (Modules.empty())
+ initTMBuilder(TMBuilder, Triple(TheTriple));
+ else if (TMBuilder.TheTriple != TheTriple) {
+ if (!TMBuilder.TheTriple.isCompatibleWith(TheTriple))
+ report_fatal_error("ThinLTO modules with incompatible triples not "
+ "supported");
+ initTMBuilder(TMBuilder, Triple(TMBuilder.TheTriple.merge(TheTriple)));
+ }
+
+ Modules.emplace_back(std::move(*InputOrError));
+}
+
+void ThinLTOCodeGenerator::preserveSymbol(StringRef Name) {
+ PreservedSymbols.insert(Name);
+}
+
+void ThinLTOCodeGenerator::crossReferenceSymbol(StringRef Name) {
+ // FIXME: At the moment, we don't take advantage of this extra information,
+ // we're conservatively considering cross-references as preserved.
+ // CrossReferencedSymbols.insert(Name);
+ PreservedSymbols.insert(Name);
+}
+
+// TargetMachine factory
+std::unique_ptr<TargetMachine> TargetMachineBuilder::create() const {
+ std::string ErrMsg;
+ const Target *TheTarget =
+ TargetRegistry::lookupTarget(TheTriple.str(), ErrMsg);
+ if (!TheTarget) {
+ report_fatal_error("Can't load target for this Triple: " + ErrMsg);
+ }
+
+ // Use MAttr as the default set of features.
+ SubtargetFeatures Features(MAttr);
+ Features.getDefaultSubtargetFeatures(TheTriple);
+ std::string FeatureStr = Features.getString();
+
+ return std::unique_ptr<TargetMachine>(
+ TheTarget->createTargetMachine(TheTriple.str(), MCpu, FeatureStr, Options,
+ RelocModel, None, CGOptLevel));
+}
+
+/**
+ * Produce the combined summary index from all the bitcode files:
+ * "thin-link".
+ */
+std::unique_ptr<ModuleSummaryIndex> ThinLTOCodeGenerator::linkCombinedIndex() {
+ std::unique_ptr<ModuleSummaryIndex> CombinedIndex =
+ std::make_unique<ModuleSummaryIndex>(/*HaveGVs=*/false);
+ uint64_t NextModuleId = 0;
+ for (auto &Mod : Modules) {
+ auto &M = Mod->getSingleBitcodeModule();
+ if (Error Err =
+ M.readSummary(*CombinedIndex, Mod->getName(), NextModuleId++)) {
+ // FIXME diagnose
+ logAllUnhandledErrors(
+ std::move(Err), errs(),
+ "error: can't create module summary index for buffer: ");
+ return nullptr;
+ }
+ }
+ return CombinedIndex;
+}
+
+struct IsExported {
+ const StringMap<FunctionImporter::ExportSetTy> &ExportLists;
+ const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols;
+
+ IsExported(const StringMap<FunctionImporter::ExportSetTy> &ExportLists,
+ const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols)
+ : ExportLists(ExportLists), GUIDPreservedSymbols(GUIDPreservedSymbols) {}
+
+ bool operator()(StringRef ModuleIdentifier, GlobalValue::GUID GUID) const {
+ const auto &ExportList = ExportLists.find(ModuleIdentifier);
+ return (ExportList != ExportLists.end() &&
+ ExportList->second.count(GUID)) ||
+ GUIDPreservedSymbols.count(GUID);
+ }
+};
+
+struct IsPrevailing {
+ const DenseMap<GlobalValue::GUID, const GlobalValueSummary *> &PrevailingCopy;
+ IsPrevailing(const DenseMap<GlobalValue::GUID, const GlobalValueSummary *>
+ &PrevailingCopy)
+ : PrevailingCopy(PrevailingCopy) {}
+
+ bool operator()(GlobalValue::GUID GUID, const GlobalValueSummary *S) const {
+ const auto &Prevailing = PrevailingCopy.find(GUID);
+ // Not in map means that there was only one copy, which must be prevailing.
+ if (Prevailing == PrevailingCopy.end())
+ return true;
+ return Prevailing->second == S;
+ };
+};
+
+static void computeDeadSymbolsInIndex(
+ ModuleSummaryIndex &Index,
+ const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) {
+ // We have no symbols resolution available. And can't do any better now in the
+ // case where the prevailing symbol is in a native object. It can be refined
+ // with linker information in the future.
+ auto isPrevailing = [&](GlobalValue::GUID G) {
+ return PrevailingType::Unknown;
+ };
+ computeDeadSymbolsWithConstProp(Index, GUIDPreservedSymbols, isPrevailing,
+ /* ImportEnabled = */ true);
+}
+
+/**
+ * Perform promotion and renaming of exported internal functions.
+ * Index is updated to reflect linkage changes from weak resolution.
+ */
+void ThinLTOCodeGenerator::promote(Module &TheModule, ModuleSummaryIndex &Index,
+ const lto::InputFile &File) {
+ auto ModuleCount = Index.modulePaths().size();
+ auto ModuleIdentifier = TheModule.getModuleIdentifier();
+
+ // Collect for each module the list of function it defines (GUID -> Summary).
+ StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries;
+ Index.collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
+
+ // Convert the preserved symbols set from string to GUID
+ auto GUIDPreservedSymbols = computeGUIDPreservedSymbols(
+ PreservedSymbols, Triple(TheModule.getTargetTriple()));
+
+ // Add used symbol to the preserved symbols.
+ addUsedSymbolToPreservedGUID(File, GUIDPreservedSymbols);
+
+ // Compute "dead" symbols, we don't want to import/export these!
+ computeDeadSymbolsInIndex(Index, GUIDPreservedSymbols);
+
+ // Generate import/export list
+ StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
+ StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
+ ComputeCrossModuleImport(Index, ModuleToDefinedGVSummaries, ImportLists,
+ ExportLists);
+
+ DenseMap<GlobalValue::GUID, const GlobalValueSummary *> PrevailingCopy;
+ computePrevailingCopies(Index, PrevailingCopy);
+
+ // Resolve prevailing symbols
+ StringMap<std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>> ResolvedODR;
+ resolvePrevailingInIndex(Index, ResolvedODR, GUIDPreservedSymbols,
+ PrevailingCopy);
+
+ thinLTOResolvePrevailingInModule(
+ TheModule, ModuleToDefinedGVSummaries[ModuleIdentifier]);
+
+ // Promote the exported values in the index, so that they are promoted
+ // in the module.
+ thinLTOInternalizeAndPromoteInIndex(
+ Index, IsExported(ExportLists, GUIDPreservedSymbols),
+ IsPrevailing(PrevailingCopy));
+
+ promoteModule(TheModule, Index);
+}
+
+/**
+ * Perform cross-module importing for the module identified by ModuleIdentifier.
+ */
+void ThinLTOCodeGenerator::crossModuleImport(Module &TheModule,
+ ModuleSummaryIndex &Index,
+ const lto::InputFile &File) {
+ auto ModuleMap = generateModuleMap(Modules);
+ auto ModuleCount = Index.modulePaths().size();
+
+ // Collect for each module the list of function it defines (GUID -> Summary).
+ StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries(ModuleCount);
+ Index.collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
+
+ // Convert the preserved symbols set from string to GUID
+ auto GUIDPreservedSymbols = computeGUIDPreservedSymbols(
+ PreservedSymbols, Triple(TheModule.getTargetTriple()));
+
+ addUsedSymbolToPreservedGUID(File, GUIDPreservedSymbols);
+
+ // Compute "dead" symbols, we don't want to import/export these!
+ computeDeadSymbolsInIndex(Index, GUIDPreservedSymbols);
+
+ // Generate import/export list
+ StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
+ StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
+ ComputeCrossModuleImport(Index, ModuleToDefinedGVSummaries, ImportLists,
+ ExportLists);
+ auto &ImportList = ImportLists[TheModule.getModuleIdentifier()];
+
+ crossImportIntoModule(TheModule, Index, ModuleMap, ImportList);
+}
+
+/**
+ * Compute the list of summaries needed for importing into module.
+ */
+void ThinLTOCodeGenerator::gatherImportedSummariesForModule(
+ Module &TheModule, ModuleSummaryIndex &Index,
+ std::map<std::string, GVSummaryMapTy> &ModuleToSummariesForIndex,
+ const lto::InputFile &File) {
+ auto ModuleCount = Index.modulePaths().size();
+ auto ModuleIdentifier = TheModule.getModuleIdentifier();
+
+ // Collect for each module the list of function it defines (GUID -> Summary).
+ StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries(ModuleCount);
+ Index.collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
+
+ // Convert the preserved symbols set from string to GUID
+ auto GUIDPreservedSymbols = computeGUIDPreservedSymbols(
+ PreservedSymbols, Triple(TheModule.getTargetTriple()));
+
+ addUsedSymbolToPreservedGUID(File, GUIDPreservedSymbols);
+
+ // Compute "dead" symbols, we don't want to import/export these!
+ computeDeadSymbolsInIndex(Index, GUIDPreservedSymbols);
+
+ // Generate import/export list
+ StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
+ StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
+ ComputeCrossModuleImport(Index, ModuleToDefinedGVSummaries, ImportLists,
+ ExportLists);
+
+ llvm::gatherImportedSummariesForModule(
+ ModuleIdentifier, ModuleToDefinedGVSummaries,
+ ImportLists[ModuleIdentifier], ModuleToSummariesForIndex);
+}
+
+/**
+ * Emit the list of files needed for importing into module.
+ */
+void ThinLTOCodeGenerator::emitImports(Module &TheModule, StringRef OutputName,
+ ModuleSummaryIndex &Index,
+ const lto::InputFile &File) {
+ auto ModuleCount = Index.modulePaths().size();
+ auto ModuleIdentifier = TheModule.getModuleIdentifier();
+
+ // Collect for each module the list of function it defines (GUID -> Summary).
+ StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries(ModuleCount);
+ Index.collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
+
+ // Convert the preserved symbols set from string to GUID
+ auto GUIDPreservedSymbols = computeGUIDPreservedSymbols(
+ PreservedSymbols, Triple(TheModule.getTargetTriple()));
+
+ addUsedSymbolToPreservedGUID(File, GUIDPreservedSymbols);
+
+ // Compute "dead" symbols, we don't want to import/export these!
+ computeDeadSymbolsInIndex(Index, GUIDPreservedSymbols);
+
+ // Generate import/export list
+ StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
+ StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
+ ComputeCrossModuleImport(Index, ModuleToDefinedGVSummaries, ImportLists,
+ ExportLists);
+
+ std::map<std::string, GVSummaryMapTy> ModuleToSummariesForIndex;
+ llvm::gatherImportedSummariesForModule(
+ ModuleIdentifier, ModuleToDefinedGVSummaries,
+ ImportLists[ModuleIdentifier], ModuleToSummariesForIndex);
+
+ std::error_code EC;
+ if ((EC = EmitImportsFiles(ModuleIdentifier, OutputName,
+ ModuleToSummariesForIndex)))
+ report_fatal_error(Twine("Failed to open ") + OutputName +
+ " to save imports lists\n");
+}
+
+/**
+ * Perform internalization. Runs promote and internalization together.
+ * Index is updated to reflect linkage changes.
+ */
+void ThinLTOCodeGenerator::internalize(Module &TheModule,
+ ModuleSummaryIndex &Index,
+ const lto::InputFile &File) {
+ initTMBuilder(TMBuilder, Triple(TheModule.getTargetTriple()));
+ auto ModuleCount = Index.modulePaths().size();
+ auto ModuleIdentifier = TheModule.getModuleIdentifier();
+
+ // Convert the preserved symbols set from string to GUID
+ auto GUIDPreservedSymbols =
+ computeGUIDPreservedSymbols(PreservedSymbols, TMBuilder.TheTriple);
+
+ addUsedSymbolToPreservedGUID(File, GUIDPreservedSymbols);
+
+ // Collect for each module the list of function it defines (GUID -> Summary).
+ StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries(ModuleCount);
+ Index.collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
+
+ // Compute "dead" symbols, we don't want to import/export these!
+ computeDeadSymbolsInIndex(Index, GUIDPreservedSymbols);
+
+ // Generate import/export list
+ StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
+ StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
+ ComputeCrossModuleImport(Index, ModuleToDefinedGVSummaries, ImportLists,
+ ExportLists);
+ auto &ExportList = ExportLists[ModuleIdentifier];
+
+ // Be friendly and don't nuke totally the module when the client didn't
+ // supply anything to preserve.
+ if (ExportList.empty() && GUIDPreservedSymbols.empty())
+ return;
+
+ DenseMap<GlobalValue::GUID, const GlobalValueSummary *> PrevailingCopy;
+ computePrevailingCopies(Index, PrevailingCopy);
+
+ // Resolve prevailing symbols
+ StringMap<std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>> ResolvedODR;
+ resolvePrevailingInIndex(Index, ResolvedODR, GUIDPreservedSymbols,
+ PrevailingCopy);
+
+ // Promote the exported values in the index, so that they are promoted
+ // in the module.
+ thinLTOInternalizeAndPromoteInIndex(
+ Index, IsExported(ExportLists, GUIDPreservedSymbols),
+ IsPrevailing(PrevailingCopy));
+
+ promoteModule(TheModule, Index);
+
+ // Internalization
+ thinLTOResolvePrevailingInModule(
+ TheModule, ModuleToDefinedGVSummaries[ModuleIdentifier]);
+
+ thinLTOInternalizeModule(TheModule,
+ ModuleToDefinedGVSummaries[ModuleIdentifier]);
+}
+
+/**
+ * Perform post-importing ThinLTO optimizations.
+ */
+void ThinLTOCodeGenerator::optimize(Module &TheModule) {
+ initTMBuilder(TMBuilder, Triple(TheModule.getTargetTriple()));
+
+ // Optimize now
+ optimizeModule(TheModule, *TMBuilder.create(), OptLevel, Freestanding,
+ nullptr);
+}
+
+/// Write out the generated object file, either from CacheEntryPath or from
+/// OutputBuffer, preferring hard-link when possible.
+/// Returns the path to the generated file in SavedObjectsDirectoryPath.
+std::string
+ThinLTOCodeGenerator::writeGeneratedObject(int count, StringRef CacheEntryPath,
+ const MemoryBuffer &OutputBuffer) {
+ auto ArchName = TMBuilder.TheTriple.getArchName();
+ SmallString<128> OutputPath(SavedObjectsDirectoryPath);
+ llvm::sys::path::append(OutputPath,
+ Twine(count) + "." + ArchName + ".thinlto.o");
+ OutputPath.c_str(); // Ensure the string is null terminated.
+ if (sys::fs::exists(OutputPath))
+ sys::fs::remove(OutputPath);
+
+ // We don't return a memory buffer to the linker, just a list of files.
+ if (!CacheEntryPath.empty()) {
+ // Cache is enabled, hard-link the entry (or copy if hard-link fails).
+ auto Err = sys::fs::create_hard_link(CacheEntryPath, OutputPath);
+ if (!Err)
+ return OutputPath.str();
+ // Hard linking failed, try to copy.
+ Err = sys::fs::copy_file(CacheEntryPath, OutputPath);
+ if (!Err)
+ return OutputPath.str();
+ // Copy failed (could be because the CacheEntry was removed from the cache
+ // in the meantime by another process), fall back and try to write down the
+ // buffer to the output.
+ errs() << "error: can't link or copy from cached entry '" << CacheEntryPath
+ << "' to '" << OutputPath << "'\n";
+ }
+ // No cache entry, just write out the buffer.
+ std::error_code Err;
+ raw_fd_ostream OS(OutputPath, Err, sys::fs::OF_None);
+ if (Err)
+ report_fatal_error("Can't open output '" + OutputPath + "'\n");
+ OS << OutputBuffer.getBuffer();
+ return OutputPath.str();
+}
+
+// Main entry point for the ThinLTO processing
+void ThinLTOCodeGenerator::run() {
+ // Prepare the resulting object vector
+ assert(ProducedBinaries.empty() && "The generator should not be reused");
+ if (SavedObjectsDirectoryPath.empty())
+ ProducedBinaries.resize(Modules.size());
+ else {
+ sys::fs::create_directories(SavedObjectsDirectoryPath);
+ bool IsDir;
+ sys::fs::is_directory(SavedObjectsDirectoryPath, IsDir);
+ if (!IsDir)
+ report_fatal_error("Unexistent dir: '" + SavedObjectsDirectoryPath + "'");
+ ProducedBinaryFiles.resize(Modules.size());
+ }
+
+ if (CodeGenOnly) {
+ // Perform only parallel codegen and return.
+ ThreadPool Pool;
+ int count = 0;
+ for (auto &Mod : Modules) {
+ Pool.async([&](int count) {
+ LLVMContext Context;
+ Context.setDiscardValueNames(LTODiscardValueNames);
+
+ // Parse module now
+ auto TheModule = loadModuleFromInput(Mod.get(), Context, false,
+ /*IsImporting*/ false);
+
+ // CodeGen
+ auto OutputBuffer = codegenModule(*TheModule, *TMBuilder.create());
+ if (SavedObjectsDirectoryPath.empty())
+ ProducedBinaries[count] = std::move(OutputBuffer);
+ else
+ ProducedBinaryFiles[count] =
+ writeGeneratedObject(count, "", *OutputBuffer);
+ }, count++);
+ }
+
+ return;
+ }
+
+ // Sequential linking phase
+ auto Index = linkCombinedIndex();
+
+ // Save temps: index.
+ if (!SaveTempsDir.empty()) {
+ auto SaveTempPath = SaveTempsDir + "index.bc";
+ std::error_code EC;
+ raw_fd_ostream OS(SaveTempPath, EC, sys::fs::OF_None);
+ if (EC)
+ report_fatal_error(Twine("Failed to open ") + SaveTempPath +
+ " to save optimized bitcode\n");
+ WriteIndexToFile(*Index, OS);
+ }
+
+
+ // Prepare the module map.
+ auto ModuleMap = generateModuleMap(Modules);
+ auto ModuleCount = Modules.size();
+
+ // Collect for each module the list of function it defines (GUID -> Summary).
+ StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries(ModuleCount);
+ Index->collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
+
+ // Convert the preserved symbols set from string to GUID, this is needed for
+ // computing the caching hash and the internalization.
+ auto GUIDPreservedSymbols =
+ computeGUIDPreservedSymbols(PreservedSymbols, TMBuilder.TheTriple);
+
+ // Add used symbol from inputs to the preserved symbols.
+ for (const auto &M : Modules)
+ addUsedSymbolToPreservedGUID(*M, GUIDPreservedSymbols);
+
+ // Compute "dead" symbols, we don't want to import/export these!
+ computeDeadSymbolsInIndex(*Index, GUIDPreservedSymbols);
+
+ // Synthesize entry counts for functions in the combined index.
+ computeSyntheticCounts(*Index);
+
+ // Perform index-based WPD. This will return immediately if there are
+ // no index entries in the typeIdMetadata map (e.g. if we are instead
+ // performing IR-based WPD in hybrid regular/thin LTO mode).
+ std::map<ValueInfo, std::vector<VTableSlotSummary>> LocalWPDTargetsMap;
+ std::set<GlobalValue::GUID> ExportedGUIDs;
+ runWholeProgramDevirtOnIndex(*Index, ExportedGUIDs, LocalWPDTargetsMap);
+ for (auto GUID : ExportedGUIDs)
+ GUIDPreservedSymbols.insert(GUID);
+
+ // Collect the import/export lists for all modules from the call-graph in the
+ // combined index.
+ StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
+ StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
+ ComputeCrossModuleImport(*Index, ModuleToDefinedGVSummaries, ImportLists,
+ ExportLists);
+
+ // We use a std::map here to be able to have a defined ordering when
+ // producing a hash for the cache entry.
+ // FIXME: we should be able to compute the caching hash for the entry based
+ // on the index, and nuke this map.
+ StringMap<std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>> ResolvedODR;
+
+ DenseMap<GlobalValue::GUID, const GlobalValueSummary *> PrevailingCopy;
+ computePrevailingCopies(*Index, PrevailingCopy);
+
+ // Resolve prevailing symbols, this has to be computed early because it
+ // impacts the caching.
+ resolvePrevailingInIndex(*Index, ResolvedODR, GUIDPreservedSymbols,
+ PrevailingCopy);
+
+ // Use global summary-based analysis to identify symbols that can be
+ // internalized (because they aren't exported or preserved as per callback).
+ // Changes are made in the index, consumed in the ThinLTO backends.
+ updateIndexWPDForExports(*Index,
+ IsExported(ExportLists, GUIDPreservedSymbols),
+ LocalWPDTargetsMap);
+ thinLTOInternalizeAndPromoteInIndex(
+ *Index, IsExported(ExportLists, GUIDPreservedSymbols),
+ IsPrevailing(PrevailingCopy));
+
+ // Make sure that every module has an entry in the ExportLists, ImportList,
+ // GVSummary and ResolvedODR maps to enable threaded access to these maps
+ // below.
+ for (auto &Module : Modules) {
+ auto ModuleIdentifier = Module->getName();
+ ExportLists[ModuleIdentifier];
+ ImportLists[ModuleIdentifier];
+ ResolvedODR[ModuleIdentifier];
+ ModuleToDefinedGVSummaries[ModuleIdentifier];
+ }
+
+ // Compute the ordering we will process the inputs: the rough heuristic here
+ // is to sort them per size so that the largest module get schedule as soon as
+ // possible. This is purely a compile-time optimization.
+ std::vector<int> ModulesOrdering;
+ ModulesOrdering.resize(Modules.size());
+ std::iota(ModulesOrdering.begin(), ModulesOrdering.end(), 0);
+ llvm::sort(ModulesOrdering, [&](int LeftIndex, int RightIndex) {
+ auto LSize =
+ Modules[LeftIndex]->getSingleBitcodeModule().getBuffer().size();
+ auto RSize =
+ Modules[RightIndex]->getSingleBitcodeModule().getBuffer().size();
+ return LSize > RSize;
+ });
+
+ // Parallel optimizer + codegen
+ {
+ ThreadPool Pool(ThreadCount);
+ for (auto IndexCount : ModulesOrdering) {
+ auto &Mod = Modules[IndexCount];
+ Pool.async([&](int count) {
+ auto ModuleIdentifier = Mod->getName();
+ auto &ExportList = ExportLists[ModuleIdentifier];
+
+ auto &DefinedGVSummaries = ModuleToDefinedGVSummaries[ModuleIdentifier];
+
+ // The module may be cached, this helps handling it.
+ ModuleCacheEntry CacheEntry(CacheOptions.Path, *Index, ModuleIdentifier,
+ ImportLists[ModuleIdentifier], ExportList,
+ ResolvedODR[ModuleIdentifier],
+ DefinedGVSummaries, OptLevel, Freestanding,
+ TMBuilder);
+ auto CacheEntryPath = CacheEntry.getEntryPath();
+
+ {
+ auto ErrOrBuffer = CacheEntry.tryLoadingBuffer();
+ LLVM_DEBUG(dbgs() << "Cache " << (ErrOrBuffer ? "hit" : "miss")
+ << " '" << CacheEntryPath << "' for buffer "
+ << count << " " << ModuleIdentifier << "\n");
+
+ if (ErrOrBuffer) {
+ // Cache Hit!
+ if (SavedObjectsDirectoryPath.empty())
+ ProducedBinaries[count] = std::move(ErrOrBuffer.get());
+ else
+ ProducedBinaryFiles[count] = writeGeneratedObject(
+ count, CacheEntryPath, *ErrOrBuffer.get());
+ return;
+ }
+ }
+
+ LLVMContext Context;
+ Context.setDiscardValueNames(LTODiscardValueNames);
+ Context.enableDebugTypeODRUniquing();
+ auto DiagFileOrErr = lto::setupOptimizationRemarks(
+ Context, RemarksFilename, RemarksPasses, RemarksFormat,
+ RemarksWithHotness, count);
+ if (!DiagFileOrErr) {
+ errs() << "Error: " << toString(DiagFileOrErr.takeError()) << "\n";
+ report_fatal_error("ThinLTO: Can't get an output file for the "
+ "remarks");
+ }
+
+ // Parse module now
+ auto TheModule = loadModuleFromInput(Mod.get(), Context, false,
+ /*IsImporting*/ false);
+
+ // Save temps: original file.
+ saveTempBitcode(*TheModule, SaveTempsDir, count, ".0.original.bc");
+
+ auto &ImportList = ImportLists[ModuleIdentifier];
+ // Run the main process now, and generates a binary
+ auto OutputBuffer = ProcessThinLTOModule(
+ *TheModule, *Index, ModuleMap, *TMBuilder.create(), ImportList,
+ ExportList, GUIDPreservedSymbols,
+ ModuleToDefinedGVSummaries[ModuleIdentifier], CacheOptions,
+ DisableCodeGen, SaveTempsDir, Freestanding, OptLevel, count);
+
+ // Commit to the cache (if enabled)
+ CacheEntry.write(*OutputBuffer);
+
+ if (SavedObjectsDirectoryPath.empty()) {
+ // We need to generated a memory buffer for the linker.
+ if (!CacheEntryPath.empty()) {
+ // When cache is enabled, reload from the cache if possible.
+ // Releasing the buffer from the heap and reloading it from the
+ // cache file with mmap helps us to lower memory pressure.
+ // The freed memory can be used for the next input file.
+ // The final binary link will read from the VFS cache (hopefully!)
+ // or from disk (if the memory pressure was too high).
+ auto ReloadedBufferOrErr = CacheEntry.tryLoadingBuffer();
+ if (auto EC = ReloadedBufferOrErr.getError()) {
+ // On error, keep the preexisting buffer and print a diagnostic.
+ errs() << "error: can't reload cached file '" << CacheEntryPath
+ << "': " << EC.message() << "\n";
+ } else {
+ OutputBuffer = std::move(*ReloadedBufferOrErr);
+ }
+ }
+ ProducedBinaries[count] = std::move(OutputBuffer);
+ return;
+ }
+ ProducedBinaryFiles[count] = writeGeneratedObject(
+ count, CacheEntryPath, *OutputBuffer);
+ }, IndexCount);
+ }
+ }
+
+ pruneCache(CacheOptions.Path, CacheOptions.Policy);
+
+ // If statistics were requested, print them out now.
+ if (llvm::AreStatisticsEnabled())
+ llvm::PrintStatistics();
+ reportAndResetTimings();
+}
diff --git a/llvm/lib/LTO/UpdateCompilerUsed.cpp b/llvm/lib/LTO/UpdateCompilerUsed.cpp
new file mode 100644
index 0000000000000..6434f902088d1
--- /dev/null
+++ b/llvm/lib/LTO/UpdateCompilerUsed.cpp
@@ -0,0 +1,132 @@
+//==-LTOInternalize.cpp - LLVM Link Time Optimizer Internalization Utility -==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a helper to run the internalization part of LTO.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/LTO/legacy/UpdateCompilerUsed.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/CodeGen/TargetLowering.h"
+#include "llvm/CodeGen/TargetSubtargetInfo.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Mangler.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
+
+using namespace llvm;
+
+namespace {
+
+// Helper class that collects AsmUsed and user supplied libcalls.
+class PreserveLibCallsAndAsmUsed {
+public:
+ PreserveLibCallsAndAsmUsed(const StringSet<> &AsmUndefinedRefs,
+ const TargetMachine &TM,
+ std::vector<GlobalValue *> &LLVMUsed)
+ : AsmUndefinedRefs(AsmUndefinedRefs), TM(TM), LLVMUsed(LLVMUsed) {}
+
+ void findInModule(Module &TheModule) {
+ initializeLibCalls(TheModule);
+ for (Function &F : TheModule)
+ findLibCallsAndAsm(F);
+ for (GlobalVariable &GV : TheModule.globals())
+ findLibCallsAndAsm(GV);
+ for (GlobalAlias &GA : TheModule.aliases())
+ findLibCallsAndAsm(GA);
+ }
+
+private:
+ // Inputs
+ const StringSet<> &AsmUndefinedRefs;
+ const TargetMachine &TM;
+
+ // Temps
+ llvm::Mangler Mangler;
+ StringSet<> Libcalls;
+
+ // Output
+ std::vector<GlobalValue *> &LLVMUsed;
+
+ // Collect names of runtime library functions. User-defined functions with the
+ // same names are added to llvm.compiler.used to prevent them from being
+ // deleted by optimizations.
+ void initializeLibCalls(const Module &TheModule) {
+ TargetLibraryInfoImpl TLII(Triple(TM.getTargetTriple()));
+ TargetLibraryInfo TLI(TLII);
+
+ // TargetLibraryInfo has info on C runtime library calls on the current
+ // target.
+ for (unsigned I = 0, E = static_cast<unsigned>(LibFunc::NumLibFuncs);
+ I != E; ++I) {
+ LibFunc F = static_cast<LibFunc>(I);
+ if (TLI.has(F))
+ Libcalls.insert(TLI.getName(F));
+ }
+
+ SmallPtrSet<const TargetLowering *, 1> TLSet;
+
+ for (const Function &F : TheModule) {
+ const TargetLowering *Lowering =
+ TM.getSubtargetImpl(F)->getTargetLowering();
+
+ if (Lowering && TLSet.insert(Lowering).second)
+ // TargetLowering has info on library calls that CodeGen expects to be
+ // available, both from the C runtime and compiler-rt.
+ for (unsigned I = 0, E = static_cast<unsigned>(RTLIB::UNKNOWN_LIBCALL);
+ I != E; ++I)
+ if (const char *Name =
+ Lowering->getLibcallName(static_cast<RTLIB::Libcall>(I)))
+ Libcalls.insert(Name);
+ }
+ }
+
+ void findLibCallsAndAsm(GlobalValue &GV) {
+ // There are no restrictions to apply to declarations.
+ if (GV.isDeclaration())
+ return;
+
+ // There is nothing more restrictive than private linkage.
+ if (GV.hasPrivateLinkage())
+ return;
+
+ // Conservatively append user-supplied runtime library functions (supplied
+ // either directly, or via a function alias) to llvm.compiler.used. These
+ // could be internalized and deleted by optimizations like -globalopt,
+ // causing problems when later optimizations add new library calls (e.g.,
+ // llvm.memset => memset and printf => puts).
+ // Leave it to the linker to remove any dead code (e.g. with -dead_strip).
+ GlobalValue *FuncAliasee = nullptr;
+ if (isa<GlobalAlias>(GV)) {
+ auto *A = cast<GlobalAlias>(&GV);
+ FuncAliasee = dyn_cast<Function>(A->getAliasee());
+ }
+ if ((isa<Function>(GV) || FuncAliasee) && Libcalls.count(GV.getName())) {
+ LLVMUsed.push_back(&GV);
+ return;
+ }
+
+ SmallString<64> Buffer;
+ TM.getNameWithPrefix(Buffer, &GV, Mangler);
+ if (AsmUndefinedRefs.count(Buffer))
+ LLVMUsed.push_back(&GV);
+ }
+};
+
+} // namespace anonymous
+
+void llvm::updateCompilerUsed(Module &TheModule, const TargetMachine &TM,
+ const StringSet<> &AsmUndefinedRefs) {
+ std::vector<GlobalValue *> UsedValues;
+ PreserveLibCallsAndAsmUsed(AsmUndefinedRefs, TM, UsedValues)
+ .findInModule(TheModule);
+
+ if (UsedValues.empty())
+ return;
+
+ appendToCompilerUsed(TheModule, UsedValues);
+}