Skip to content

[LTO] Support LLVM LTO for IRGen and frontend #32429

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
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
32 changes: 20 additions & 12 deletions include/swift/AST/IRGenOptions.h
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

extern int IComeFromLinkFramework;
Empty file.
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#pragma comment(lib, "transitive-module")
Loading