Skip to content

Commit

Permalink
[LTO] Support LLVM LTO for IRGen and frontend
Browse files Browse the repository at this point in the history
This commit adds -lto flag for frontend to enable LTO at LLVM level.
When -lto=llvm given, compiler emits LLVM bitcode file instead of object
file and adds index summary for LTO.
In addition for ELF format, emit llvm.dependent-libraries section to
embed auto linking information
  • Loading branch information
kateinoigakukun committed Jul 1, 2020
1 parent 3da8595 commit 20bc0af
Show file tree
Hide file tree
Showing 14 changed files with 325 additions and 64 deletions.
32 changes: 20 additions & 12 deletions include/swift/AST/IRGenOptions.h
Expand Up @@ -65,6 +65,12 @@ enum class IRGenDebugInfoFormat : unsigned {
CodeView
};

enum class IRGenLLVMLTOKind : unsigned {
None,
Thin,
Full,
};

enum class IRGenEmbedMode : unsigned {
None,
EmbedMarker,
Expand Down Expand Up @@ -220,6 +226,8 @@ class IRGenOptions {
/// Whether we should embed the bitcode file.
IRGenEmbedMode EmbedMode : 2;

IRGenLLVMLTOKind LLVMLTOKind : 2;

/// Add names to LLVM values.
unsigned HasValueNamesSetting : 1;
unsigned ValueNames : 1;
Expand Down Expand Up @@ -320,21 +328,21 @@ class IRGenOptions {
DebugInfoLevel(IRGenDebugInfoLevel::None),
DebugInfoFormat(IRGenDebugInfoFormat::None),
DisableClangModuleSkeletonCUs(false), UseJIT(false),
DisableLLVMOptzns(false),
DisableSwiftSpecificLLVMOptzns(false), DisableLLVMSLPVectorizer(false),
Playground(false), EmitStackPromotionChecks(false),
FunctionSections(false), PrintInlineTree(false), EmbedMode(IRGenEmbedMode::None),
HasValueNamesSetting(false), ValueNames(false),
EnableReflectionMetadata(true), EnableReflectionNames(true),
EnableAnonymousContextMangledNames(false), ForcePublicLinkage(false),
LazyInitializeClassMetadata(false),
DisableLLVMOptzns(false), DisableSwiftSpecificLLVMOptzns(false),
DisableLLVMSLPVectorizer(false), Playground(false),
EmitStackPromotionChecks(false), FunctionSections(false),
PrintInlineTree(false), EmbedMode(IRGenEmbedMode::None),
LLVMLTOKind(IRGenLLVMLTOKind::None), HasValueNamesSetting(false),
ValueNames(false), EnableReflectionMetadata(true),
EnableReflectionNames(true), EnableAnonymousContextMangledNames(false),
ForcePublicLinkage(false), LazyInitializeClassMetadata(false),
LazyInitializeProtocolConformances(false), DisableLegacyTypeInfo(false),
PrespecializeGenericMetadata(false), UseIncrementalLLVMCodeGen(true),
UseSwiftCall(false), UseTypeLayoutValueHandling(true), GenerateProfile(false),
EnableDynamicReplacementChaining(false),
UseSwiftCall(false), UseTypeLayoutValueHandling(true),
GenerateProfile(false), EnableDynamicReplacementChaining(false),
DisableRoundTripDebugTypes(false), DisableDebuggerShadowCopies(false),
DisableConcreteTypeMetadataMangledNameAccessors(false),
CmdArgs(), SanitizeCoverage(llvm::SanitizerCoverageOptions()),
DisableConcreteTypeMetadataMangledNameAccessors(false), CmdArgs(),
SanitizeCoverage(llvm::SanitizerCoverageOptions()),
TypeInfoFilter(TypeInfoDumpFilter::All) {}

/// Appends to \p os an arbitrary string representing all options which
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Option/Options.td
Expand Up @@ -523,6 +523,10 @@ def disable_bridging_pch : Flag<["-"], "disable-bridging-pch">,
Flags<[HelpHidden]>,
HelpText<"Disable automatic generation of bridging PCH files">;

def lto : Joined<["-"], "lto=">,
Flags<[FrontendOption, NoInteractiveOption]>,
HelpText<"Specify the LTO type to either 'llvm-thin' or 'llvm-full'">;

// Experimental feature options

// Note: this flag will be removed when JVP/differential generation in the
Expand Down
12 changes: 12 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Expand Up @@ -1474,6 +1474,18 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
}
}

if (const Arg *A = Args.getLastArg(options::OPT_lto)) {
auto LLVMLTOKind =
llvm::StringSwitch<Optional<IRGenLLVMLTOKind>>(A->getValue())
.Case("llvm-thin", IRGenLLVMLTOKind::Thin)
.Case("llvm-full", IRGenLLVMLTOKind::Full)
.Default(llvm::None);
if (LLVMLTOKind)
Opts.LLVMLTOKind = LLVMLTOKind.getValue();
else
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
A->getAsString(Args), A->getValue());
}

if (const Arg *A = Args.getLastArg(options::OPT_sanitize_coverage_EQ)) {
Opts.SanitizeCoverage =
Expand Down
9 changes: 7 additions & 2 deletions lib/IRGen/IRGen.cpp
Expand Up @@ -548,9 +548,14 @@ bool swift::performLLVM(const IRGenOptions &Opts,
case IRGenOutputKind::LLVMAssembly:
EmitPasses.add(createPrintModulePass(*RawOS));
break;
case IRGenOutputKind::LLVMBitcode:
EmitPasses.add(createBitcodeWriterPass(*RawOS));
case IRGenOutputKind::LLVMBitcode: {
if (Opts.LLVMLTOKind == IRGenLLVMLTOKind::Thin) {
EmitPasses.add(createWriteThinLTOBitcodePass(*RawOS));
} else {
EmitPasses.add(createBitcodeWriterPass(*RawOS));
}
break;
}
case IRGenOutputKind::NativeAssembly:
case IRGenOutputKind::ObjectFile: {
CodeGenFileType FileType;
Expand Down
222 changes: 173 additions & 49 deletions lib/IRGen/IRGenModule.cpp
Expand Up @@ -1108,8 +1108,6 @@ llvm::SmallString<32> getTargetDependentLibraryOption(const llvm::Triple &T,
}

void IRGenModule::addLinkLibrary(const LinkLibrary &linkLib) {
llvm::LLVMContext &ctx = Module.getContext();

// The debugger gets the autolink information directly from
// the LinkLibraries of the module, so there's no reason to
// emit it into the IR of debugger expressions.
Expand All @@ -1118,10 +1116,7 @@ void IRGenModule::addLinkLibrary(const LinkLibrary &linkLib) {

switch (linkLib.getKind()) {
case LibraryKind::Library: {
llvm::SmallString<32> opt =
getTargetDependentLibraryOption(Triple, linkLib.getName());
AutolinkEntries.push_back(
llvm::MDNode::get(ctx, llvm::MDString::get(ctx, opt)));
AutolinkEntries.emplace_back(linkLib);
break;
}
case LibraryKind::Framework: {
Expand All @@ -1130,12 +1125,7 @@ void IRGenModule::addLinkLibrary(const LinkLibrary &linkLib) {
if (std::find(frameworks.begin(), frameworks.end(), linkLib.getName())
!= frameworks.end())
return;

llvm::Metadata *args[] = {
llvm::MDString::get(ctx, "-framework"),
llvm::MDString::get(ctx, linkLib.getName())
};
AutolinkEntries.push_back(llvm::MDNode::get(ctx, args));
AutolinkEntries.emplace_back(linkLib);
break;
}
}
Expand Down Expand Up @@ -1205,63 +1195,197 @@ static bool isFirstObjectFileInModule(IRGenModule &IGM) {
return containingModule->getFiles().front() == file;
}

void IRGenModule::emitAutolinkInfo() {
// Collect the linker options already in the module (from ClangCodeGen).
static bool
doesTargetAutolinkUsingAutolinkExtract(const SwiftTargetInfo &TargetInfo,
const llvm::Triple &Triple) {
if (TargetInfo.OutputObjectFormat == llvm::Triple::ELF && !Triple.isPS4())
return true;

if (TargetInfo.OutputObjectFormat == llvm::Triple::Wasm)
return true;

if (Triple.isOSCygMing())
return true;

return false;
}

namespace {

struct AutolinkKind {
enum ValueTy {
LLVMLinkerOptions,
LLVMDependentLibraries,
SwiftAutoLinkExtract,
};

ValueTy Value;

AutolinkKind(ValueTy value) : Value(value) {}
AutolinkKind(const AutolinkKind &kind) : Value(kind.Value) {}

StringRef getSectionNameMetadata();

template <typename Vector, typename Set>
void collectEntriesFromLibraries(llvm::SetVector<llvm::MDNode *, Vector, Set> &Entries,
ArrayRef<LinkLibrary> AutolinkEntries,
IRGenModule &IGM);

template <typename Vector, typename Set>
void writeEntries(llvm::SetVector<llvm::MDNode *, Vector, Set> Entries,
llvm::NamedMDNode *Metadata, IRGenModule &IGM);

static AutolinkKind create(const SwiftTargetInfo &TargetInfo,
llvm::Triple Triple, IRGenLLVMLTOKind LLVMLTOKind);
};

} // anonymous namespace

StringRef AutolinkKind::getSectionNameMetadata() {
// FIXME: This constant should be vended by LLVM somewhere.
auto *Metadata = Module.getOrInsertNamedMetadata("llvm.linker.options");
for (llvm::MDNode *LinkOption : Metadata->operands())
AutolinkEntries.push_back(LinkOption);

// Remove duplicates.
llvm::SmallPtrSet<llvm::MDNode *, 4> knownAutolinkEntries;
AutolinkEntries.erase(std::remove_if(AutolinkEntries.begin(),
AutolinkEntries.end(),
[&](llvm::MDNode *entry) -> bool {
return !knownAutolinkEntries.insert(
entry).second;
}),
AutolinkEntries.end());

const bool AutolinkExtractRequired =
(TargetInfo.OutputObjectFormat == llvm::Triple::ELF && !Triple.isPS4()) ||
TargetInfo.OutputObjectFormat == llvm::Triple::Wasm ||
Triple.isOSCygMing();

if (!AutolinkExtractRequired) {
switch (Value) {
case AutolinkKind::LLVMDependentLibraries:
return "llvm.dependent-libraries";
case AutolinkKind::LLVMLinkerOptions:
case AutolinkKind::SwiftAutoLinkExtract:
return "llvm.linker.options";
}

llvm_unreachable("Unhandled AutolinkKind in switch.");
}

template <typename Vector, typename Set>
void AutolinkKind::collectEntriesFromLibraries(
llvm::SetVector<llvm::MDNode *, Vector, Set> &Entries,
ArrayRef<LinkLibrary> AutolinkEntries, IRGenModule &IGM) {
llvm::LLVMContext &ctx = IGM.getLLVMContext();

switch (Value) {
case AutolinkKind::LLVMLinkerOptions:
case AutolinkKind::SwiftAutoLinkExtract: {
// On platforms that support autolinking, continue to use the metadata.
for (LinkLibrary linkLib : AutolinkEntries) {
switch (linkLib.getKind()) {
case LibraryKind::Library: {
llvm::SmallString<32> opt =
getTargetDependentLibraryOption(IGM.Triple, linkLib.getName());
Entries.insert(llvm::MDNode::get(ctx, llvm::MDString::get(ctx, opt)));
continue;
}
case LibraryKind::Framework: {
llvm::Metadata *args[] = {llvm::MDString::get(ctx, "-framework"),
llvm::MDString::get(ctx, linkLib.getName())};
Entries.insert(llvm::MDNode::get(ctx, args));
continue;
}
}
llvm_unreachable("Unhandled LibraryKind in switch.");
}
return;
}
case AutolinkKind::LLVMDependentLibraries: {
for (LinkLibrary linkLib : AutolinkEntries) {
switch (linkLib.getKind()) {
case LibraryKind::Library: {
Entries.insert(llvm::MDNode::get(
ctx, llvm::MDString::get(ctx, linkLib.getName())));
continue;
}
case LibraryKind::Framework: {
llvm_unreachable(
"llvm.dependent-libraries doesn't support framework dependency");
}
}
llvm_unreachable("Unhandled LibraryKind in switch.");
}
return;
}
}
llvm_unreachable("Unhandled AutolinkKind in switch.");
}

template <typename Vector, typename Set>
void AutolinkKind::writeEntries(llvm::SetVector<llvm::MDNode *, Vector, Set> Entries,
llvm::NamedMDNode *Metadata, IRGenModule &IGM) {
switch (Value) {
case AutolinkKind::LLVMLinkerOptions:
case AutolinkKind::LLVMDependentLibraries: {
// On platforms that support autolinking, continue to use the metadata.
Metadata->clearOperands();
for (auto *Entry : AutolinkEntries)
for (auto *Entry : Entries)
Metadata->addOperand(Entry);
} else {
return;
}
case AutolinkKind::SwiftAutoLinkExtract: {
// Merge the entries into null-separated string.
llvm::SmallString<64> EntriesString;
for (auto &EntryNode : AutolinkEntries) {
const llvm::MDNode *MD = cast<llvm::MDNode>(EntryNode);
for (auto EntryNode : Entries) {
const auto *MD = cast<llvm::MDNode>(EntryNode);
for (auto &Entry : MD->operands()) {
const llvm::MDString *MS = cast<llvm::MDString>(Entry);
EntriesString += MS->getString();
EntriesString += '\0';
}
}
auto EntriesConstant = llvm::ConstantDataArray::getString(
getLLVMContext(), EntriesString, /*AddNull=*/false);
IGM.getLLVMContext(), EntriesString, /*AddNull=*/false);
// Mark the swift1_autolink_entries section with the SHF_EXCLUDE attribute
// to get the linker to drop it in the final linked binary.
// LLVM doesn't provide an interface to specify section attributs in the IR
// so we pass the attribute with inline assembly.
if (TargetInfo.OutputObjectFormat == llvm::Triple::ELF)
Module.appendModuleInlineAsm(".section .swift1_autolink_entries,"
"\"0x80000000\"");
// LLVM doesn't provide an interface to specify section attributs in the
// IR so we pass the attribute with inline assembly.
if (IGM.TargetInfo.OutputObjectFormat == llvm::Triple::ELF)
IGM.Module.appendModuleInlineAsm(".section .swift1_autolink_entries,"
"\"0x80000000\"");
auto var =
new llvm::GlobalVariable(*getModule(), EntriesConstant->getType(), true,
llvm::GlobalValue::PrivateLinkage,
new llvm::GlobalVariable(*IGM.getModule(), EntriesConstant->getType(),
true, llvm::GlobalValue::PrivateLinkage,
EntriesConstant, "_swift1_autolink_entries");
var->setSection(".swift1_autolink_entries");
var->setAlignment(llvm::MaybeAlign(getPointerAlignment().getValue()));
var->setAlignment(llvm::MaybeAlign(IGM.getPointerAlignment().getValue()));

disableAddressSanitizer(*this, var);
addUsedGlobal(var);
disableAddressSanitizer(IGM, var);
IGM.addUsedGlobal(var);
return;
}
}
llvm_unreachable("Unhandled AutolinkKind in switch.");
}

AutolinkKind AutolinkKind::create(const SwiftTargetInfo &TargetInfo,
llvm::Triple Triple,
IRGenLLVMLTOKind LLVMLTOKind) {
// When performing LTO, we always use lld that supports auto linking
// mechanism with ELF. So embed dependent libraries names in
// "llvm.dependent-libraries" instead of "llvm.linker.options".
if (TargetInfo.OutputObjectFormat == llvm::Triple::ELF &&
LLVMLTOKind != IRGenLLVMLTOKind::None) {
return AutolinkKind::LLVMDependentLibraries;
}

if (doesTargetAutolinkUsingAutolinkExtract(TargetInfo, Triple)) {
return AutolinkKind::SwiftAutoLinkExtract;
}

return AutolinkKind::LLVMLinkerOptions;
}

void IRGenModule::emitAutolinkInfo() {
auto Autolink =
AutolinkKind::create(TargetInfo, Triple, IRGen.Opts.LLVMLTOKind);

StringRef AutolinkSectionName = Autolink.getSectionNameMetadata();

auto *Metadata = Module.getOrInsertNamedMetadata(AutolinkSectionName);
llvm::SmallSetVector<llvm::MDNode *, 4> Entries;

// Collect the linker options already in the module (from ClangCodeGen).
for (auto Entry : Metadata->operands()) {
Entries.insert(Entry);
}

Autolink.collectEntriesFromLibraries(Entries, AutolinkEntries, *this);

Autolink.writeEntries(Entries, Metadata, *this);

if (!IRGen.Opts.ForceLoadSymbolName.empty() &&
(Triple.supportsCOMDAT() || isFirstObjectFileInModule(*this))) {
Expand Down
3 changes: 2 additions & 1 deletion lib/IRGen/IRGenModule.h
Expand Up @@ -22,6 +22,7 @@
#include "SwiftTargetInfo.h"
#include "TypeLayout.h"
#include "swift/AST/Decl.h"
#include "swift/AST/LinkLibrary.h"
#include "swift/AST/Module.h"
#include "swift/AST/ReferenceCounting.h"
#include "swift/AST/SourceFile.h"
Expand Down Expand Up @@ -1076,7 +1077,7 @@ class IRGenModule {
SmallVector<llvm::WeakTrackingVH, 4> LLVMCompilerUsed;

/// Metadata nodes for autolinking info.
SmallVector<llvm::MDNode *, 32> AutolinkEntries;
SmallVector<LinkLibrary, 32> AutolinkEntries;

/// List of Objective-C classes, bitcast to i8*.
SmallVector<llvm::WeakTrackingVH, 4> ObjCClasses;
Expand Down
@@ -0,0 +1,2 @@

extern int IComeFromLinkFramework;
Empty file.
@@ -0,0 +1,4 @@
framework module LinkFramework {
header "LinkFramework.h"
export *
}
1 change: 1 addition & 0 deletions test/IRGen/Inputs/autolink-elf-c-pragma-transitive.h
@@ -0,0 +1 @@
#pragma comment(lib, "transitive-module")

0 comments on commit 20bc0af

Please sign in to comment.