Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
243 changes: 243 additions & 0 deletions Plugins/AdvancedLTO/AdvancedLTO.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
//===- AdvancedLTO.cpp----------------------------------------------------===//
// Part of the eld Project, under the BSD License
// See https://github.com/qualcomm/eld/LICENSE.txt for license information.
// SPDX-License-Identifier: BSD-3-Clause
//===----------------------------------------------------------------------===//

#include "Defines.h"
#include "LinkerPlugin.h"
#include "LinkerWrapper.h"
#include "PluginADT.h"
#include "PluginVersion.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/LTO/LTO.h"
#include <cassert>
#include <cerrno>
#include <cstdint>
#include <cstdlib>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>

using namespace eld::plugin;

namespace {

struct ModuleData {
explicit ModuleData(BitcodeFile BCF) : BCFile(BCF) {}

BitcodeFile BCFile;
std::unordered_map<std::string, Section> SectionsByName;
};

class AdvancedLTO : public LinkerPlugin {
public:
AdvancedLTO() : LinkerPlugin("AdvancedLTO") {}

void Init(const std::string &Options) override {
(void)Options;
EnableLTOLinkerScripts =
getLinker()->getLinkerConfig().hasLTOLinkerScripts();
TraceLTO = getLinker()->getLinkerConfig().shouldTraceLTO();
}

LTOModule *CreateLTOModule(BitcodeFile BCF, LTOModuleHash Hash) override {
LastBitcodeFile = BCF;
auto Data = std::make_unique<ModuleData>(BCF);
LTOModule *M = reinterpret_cast<LTOModule *>(Data.get());
Modules[M] = std::move(Data);
FilesByHash.insert_or_assign(Hash, &BCF.getBitcodeFile());
return M;
}

void ReadSymbols(LTOModule &M) override {
ModuleData *Data = importModule(M);
if (!Data)
return;

auto getKind = [](const llvm::lto::InputFile::Symbol &Sym) {
if (Sym.isUndefined())
return Symbol::Undefined;
if (Sym.isCommon())
return Symbol::Common;
return Symbol::Define;
};

auto getBinding = [](const llvm::lto::InputFile::Symbol &Sym) {
if (!Sym.isGlobal())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the symbol is weak, then wouldn't !Sym.isGlobal() return true and the function will incorrectly return Symbol::Local?

return Symbol::Local;
if (Sym.isWeak())
return Symbol::Weak;
return Symbol::Global;
};

auto getVisibility = [](const llvm::lto::InputFile::Symbol &Sym) {
switch (Sym.getVisibility()) {
case llvm::GlobalValue::DefaultVisibility:
return Symbol::Default;
case llvm::GlobalValue::HiddenVisibility:
return Symbol::Hidden;
case llvm::GlobalValue::ProtectedVisibility:
return Symbol::Protected;
}
return Symbol::Default;
};

auto getType = [](const llvm::lto::InputFile::Symbol &Sym) {
if (Sym.isExecutable())
return static_cast<unsigned>(llvm::ELF::STT_FUNC);
if (Sym.isTLS())
return static_cast<unsigned>(llvm::ELF::STT_TLS);
return static_cast<unsigned>(llvm::ELF::STT_OBJECT);
};

llvm::ArrayRef<llvm::lto::InputFile::Symbol> Symbols =
Data->BCFile.getInputFile().symbols();

for (const auto &Sym : llvm::enumerate(Symbols)) {
unsigned SymbolIndex = Sym.index();
const llvm::lto::InputFile::Symbol &LtoSym = Sym.value();
bool KeepComdat = Data->BCFile.findIfKeptComdat(LtoSym.getComdatIndex());
Symbol::Kind Kind = KeepComdat ? getKind(LtoSym) : Symbol::Undefined;

Section InputSection;
llvm::StringRef SectionName = LtoSym.getSectionName();
if (SectionName.empty() && LtoSym.isCommon())
SectionName = "COMMON";

if (!SectionName.empty()) {
auto ExpSection = getOrCreateSection(*Data, SectionName);
ELDEXP_REPORT_AND_RETURN_VOID_IF_ERROR(getLinker(), ExpSection);
InputSection = *ExpSection;
}

auto ExpSymbol = getLinker()->addSymbol(
Data->BCFile, LtoSym.getName().str(), getBinding(LtoSym),
InputSection, Kind, getVisibility(LtoSym), getType(LtoSym),
LtoSym.isCommon() ? LtoSym.getCommonSize() : 0, SymbolIndex);
ELDEXP_REPORT_AND_RETURN_VOID_IF_ERROR(getLinker(), ExpSymbol);

if (!EnableLTOLinkerScripts || !InputSection || Kind == Symbol::Undefined)
continue;

auto Rule = InputSection.getLinkerScriptRule();
if (!Rule || !Rule.isKeep())
continue;

auto ExpSetPreserve = getLinker()->setPreserveSymbol(*ExpSymbol);
ELDEXP_REPORT_AND_RETURN_VOID_IF_ERROR(getLinker(), ExpSetPreserve);
}
}

void VisitSections(InputFile IF) override {
if (!EnableLTOLinkerScripts)
return;

for (Section S : IF.getSections()) {
std::string Name = S.getName();
size_t FirstPos = Name.find("^^");
if (FirstPos == std::string::npos)
continue;
std::string BaseName = Name.substr(0, FirstPos);
std::string HashStr = Name.substr(FirstPos + 2);
size_t SecondPos = HashStr.find("^^");
if (SecondPos != std::string::npos)
HashStr = HashStr.substr(SecondPos + 2);
if (HashStr.empty())
continue;

if (!getLinker()->isPostLTOPhase())
continue;

errno = 0;
char *End = nullptr;
unsigned long long Parsed = std::strtoull(HashStr.c_str(), &End, 16);
if (errno != 0 || End == HashStr.c_str() || *End != '\0')
continue;
uint64_t Hash = Parsed;

auto It = FilesByHash.find(Hash);
if (It == FilesByHash.end())
continue;

getLinker()->setSectionName(S, BaseName);
getLinker()->setRuleMatchingInput(S, BitcodeFile(*It->second));
if (TraceLTO) {
(void)getLinker()->reportDiag(
getLinker()->getNoteDiagID(
"Applying section namespace override %0"),
Name);
}
}
}

void ActBeforeSectionMerging() override {
if (!getLinker()->getLinkerScript().hasSectionsCommand() ||
getLinker()->getLinkMode() == LinkerWrapper::PartialLink ||
FilesByHash.empty())
return;

auto ExpSort = getLinker()->sortInputSectionsForSectionMerging(
[&](const Section &A, const Section &B) {
InputFile RMInputA = A.getRuleMatchingInput();
InputFile RMInputB = B.getRuleMatchingInput();
assert(RMInputA.getInputFile() && RMInputB.getInputFile() &&
"rule-matching inputs must be valid");

if (RMInputA.isInternal() && !RMInputB.isInternal())
return false;
if (!RMInputA.isInternal() && RMInputB.isInternal())
return true;

auto getEffectiveOrdinal = [&](const Section &S) -> uint32_t {
InputFile IF = S.getInputFile();
if (IF.isLTOGeneratedObject() && !S.hasOldInputFile()) {
assert(LastBitcodeFile.has_value() &&
"LTO-generated objects require a last bitcode input");
return LastBitcodeFile->getOrdinal();
}
return S.getRuleMatchingInput().getOrdinal();
};

return getEffectiveOrdinal(A) < getEffectiveOrdinal(B);
},
"AdvancedLTO: sort input sections by original input ordinal");
ELDEXP_REPORT_AND_RETURN_VOID_IF_ERROR(getLinker(), ExpSort);
}

private:
ModuleData *importModule(LTOModule &M) {
auto *ID = &M;
auto It = Modules.find(ID);
if (It == Modules.end())
return nullptr;
return It->second.get();
}

Expected<Section> getOrCreateSection(ModuleData &Data,
llvm::StringRef SectionName) {
std::string Key = SectionName.str();
auto It = Data.SectionsByName.find(Key);
if (It != Data.SectionsByName.end())
return It->second;

auto ExpSection = getLinker()->createBitcodeSection(Key, Data.BCFile);
ELDEXP_RETURN_DIAGENTRY_IF_ERROR(ExpSection);
Data.SectionsByName.emplace(std::move(Key), *ExpSection);
return *ExpSection;
}

private:
bool EnableLTOLinkerScripts = false;
bool TraceLTO = false;
std::unordered_map<LTOModule *, std::unique_ptr<ModuleData>> Modules;
std::unordered_map<uint64_t, eld::BitcodeFile *> FilesByHash;
std::optional<BitcodeFile> LastBitcodeFile;
};

} // namespace

ELD_REGISTER_PLUGIN(AdvancedLTO)
30 changes: 30 additions & 0 deletions Plugins/AdvancedLTO/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
set(SOURCES AdvancedLTO.cpp)

if(NOT CYGWIN AND LLVM_ENABLE_PIC)
set(SHARED_LIB_SOURCES ${SOURCES})

set(bsl ${BUILD_SHARED_LIBS})
set(BUILD_SHARED_LIBS ON)

add_llvm_library(AdvancedLTO BUILDTREE_ONLY ${SHARED_LIB_SOURCES} LINK_LIBS LW)

set(BUILD_SHARED_LIBS ${bsl})

install(TARGETS AdvancedLTO
COMPONENT ld.eld
LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX}
ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX}
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")

if(TARGET ld.eld)
add_dependencies(ld.eld AdvancedLTO)
endif()

if(TARGET install-ld.eld)
add_dependencies(install-ld.eld AdvancedLTO)
endif()

if(TARGET check-eld)
add_dependencies(check-eld AdvancedLTO)
endif()
endif()
3 changes: 2 additions & 1 deletion Plugins/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
add_subdirectory(HelloWorldPlugin)
add_subdirectory(HelloWorldPlugin)
add_subdirectory(AdvancedLTO)
7 changes: 6 additions & 1 deletion include/eld/Config/GeneralOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,10 @@ class GeneralOptions {

bool hasFatLTOObjects() const { return FatLTOObjects; }

void setLTOLinkerScripts(bool V = true) { LTOLinkerScripts = V; }

bool hasLTOLinkerScripts() const { return LTOLinkerScripts; }

void setLTOOptions(llvm::StringRef OptionType);

void addLTOCodeGenOptions(std::string O);
Expand Down Expand Up @@ -1289,7 +1293,8 @@ class GeneralOptions {
uint32_t GPSize = 8; // -G, --gpsize
bool Lto = false;
bool FatLTOObjects = false; // --fat-lto-objects
bool LTOUseAs = false; // --flto-use-as
bool LTOLinkerScripts = false; // --lto-linker-scripts
bool LTOUseAs = false; // -flto-use-as
StripSymbolMode StripSymbols = KeepAllSymbols; // Strip symbols ?
bool BPageAlignSegments = true; // Does the linker need to align segments to
// a page.
Expand Down
2 changes: 1 addition & 1 deletion include/eld/Core/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ class Module {

void addSymbol(ResolveInfo *R);

LDSymbol *addSymbolFromBitCode(ObjectFile &CurInput, const std::string &Name,
LDSymbol *addSymbolFromBitCode(BitcodeFile &CurInput, const std::string &Name,
ResolveInfo::Type Type, ResolveInfo::Desc Desc,
ResolveInfo::Binding Binding,
ResolveInfo::SizeType Size,
Expand Down
18 changes: 18 additions & 0 deletions include/eld/Driver/GnuLinkerOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -1272,6 +1272,14 @@ defm no_fat_lto_objects
: mDashDeprWithOpt<"no-fat-lto-objects", "no_fat_lto_objects", "Ignore "
".llvm.lto section in fat LTO objects">,
Group<grp_ltooptions>;
def lto_linker_scripts : Flag<["--"], "lto-linker-scripts">,
HelpText<"Make LTO respect section mappings "
"specified in linker scripts">,
Group<grp_ltooptions>;
def no_lto_linker_scripts : Flag<["--"], "no-lto-linker-scripts">,
HelpText<"Ignore section mappings specified in "
"linker scripts during LTO (default)">,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about making --lto-linker-scripts as the default, instead of --no-lto-linker-scripts?

Group<grp_ltooptions>;
defm ffat_lto_objects : mDashDeprAlias<"ffat-lto-objects", "fat_lto_objects",
"Alias for --fat-lto-objects">,
Group<grp_ltooptions>;
Expand Down Expand Up @@ -1430,6 +1438,16 @@ def : J<"plugin-opt=jobs=">,
HelpText<"Alias for --thinlto-jobs=">,
Group<grp_ltooptions>;

def thinlto_cache_dir_eq
: JJ<"thinlto-cache-dir=">,
HelpText<"Path to ThinLTO cached object file directory">,
MetaVarName<"<directory>">,
Group<grp_ltooptions>;
def : J<"plugin-opt=cache-dir=">,
Alias<thinlto_cache_dir_eq>,
HelpText<"Alias for --thinlto-cache-dir=">,
Group<grp_ltooptions>;

def lto_O : JJ<"lto-O">,
MetaVarName<"<opt-level>">,
HelpText<"Optimization level for LTO">,
Expand Down
8 changes: 7 additions & 1 deletion include/eld/Input/BitcodeFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ class BitcodeFile : public ObjectFile {
return (I->getKind() == InputFile::BitcodeFileKind);
}

bool createLTOInputFile(const std::string &ModuleID);
bool createLTOInputFile(const std::string &ModuleID,
bool IncludeLocalSymbols = false);

llvm::lto::InputFile &getInputFile() const { return *LTOInputFile; }

Expand Down Expand Up @@ -77,6 +78,10 @@ class BitcodeFile : public ObjectFile {

Section *getInputSectionForSymbol(const ResolveInfo &) const;

void setResolveInfoForLTOSymbol(unsigned Index, ResolveInfo &Info);

ResolveInfo *getResolveInfoForLTOSymbol(unsigned Index) const;

private:
void inferObjectInfo();

Expand All @@ -93,6 +98,7 @@ class BitcodeFile : public ObjectFile {
// Marked by comdat index in Module if accepted: (true if not rejected)
llvm::DenseMap<int, bool> BCComdats;
std::unordered_map<const ResolveInfo *, Section *> InputSectionForSymbol;
std::vector<ResolveInfo *> ResolveInfoForLTOSymbol;
plugin::LTOModule *PluginModule;
};

Expand Down
2 changes: 1 addition & 1 deletion include/eld/PluginAPI/LinkerWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ class DLL_A_EXPORT LinkerWrapper {
plugin::Symbol::Binding Binding, Section InputSection,
plugin::Symbol::Kind Kind,
plugin::Symbol::Visibility Visibility = plugin::Symbol::Default,
unsigned Type = 0, uint64_t Size = 0);
unsigned Type = 0, uint64_t Size = 0, unsigned SymbolIndex = 0);

/// Override the output section to OutputSection for Section S.
/// \param S Section for which the output section needs to be overriden.
Expand Down
1 change: 1 addition & 0 deletions include/eld/PluginAPI/PluginADT.h
Original file line number Diff line number Diff line change
Expand Up @@ -1464,6 +1464,7 @@ struct DLL_A_EXPORT LinkerConfig {
bool hasBSymbolic() const;
bool hasGCSections() const;
bool hasUniqueOutputSections() const;
bool hasLTOLinkerScripts() const;

/// Return options set by --flto-options.
const std::vector<std::string> &getLTOOptions() const;
Expand Down
Loading
Loading