-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[LTO] Setup LTO pipeline and swift-lto tool #32233
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
//===--- LTO.cpp - Swift LTO ----------------------------------------------===// | ||
// | ||
// This source file is part of the Swift.org open source project | ||
// | ||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors | ||
// Licensed under Apache License v2.0 with Runtime Library Exception | ||
// | ||
// See https://swift.org/LICENSE.txt for license information | ||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef SWIFT_LTO_H | ||
#define SWIFT_LTO_H | ||
|
||
#include "llvm/ADT/SmallVector.h" | ||
#include "llvm/ADT/StringRef.h" | ||
#include "llvm/Support/MemoryBuffer.h" | ||
#include <functional> | ||
#include <memory> | ||
|
||
#include "swift/AST/ASTContext.h" | ||
#include "swift/AST/IRGenOptions.h" | ||
#include "swift/ClangImporter/ClangImporterOptions.h" | ||
#include "swift/Frontend/ModuleInterfaceLoader.h" | ||
#include "swift/Frontend/PrintingDiagnosticConsumer.h" | ||
#include "swift/Serialization/Validation.h" | ||
|
||
namespace swift { | ||
|
||
class ASTContext; | ||
|
||
namespace lto { | ||
|
||
using GetStreamFn = std::function<std::unique_ptr<llvm::raw_ostream>( | ||
llvm::StringRef ModuleName)>; | ||
|
||
class LTOPipeline { | ||
llvm::SmallVector<llvm::StringRef, 4> RuntimeLibraryPaths; | ||
llvm::SmallVector<llvm::StringRef, 4> RuntimeLibraryImportPaths; | ||
llvm::StringRef RuntimeResourcePath; | ||
llvm::SmallVector<Identifier, 2> ModuleNames; | ||
LangOptions LangOpts; | ||
ClangImporterOptions ClangOpts; | ||
TypeCheckerOptions TCOpts; | ||
SearchPathOptions SearchPathOpts; | ||
SourceManager SM; | ||
DiagnosticEngine Diags; | ||
PrintingDiagnosticConsumer PrintDiags; | ||
std::unique_ptr<ASTContext> Ctx; | ||
MemoryBufferSerializedModuleLoader *MBL; | ||
|
||
public: | ||
LTOPipeline(llvm::SmallVector<llvm::StringRef, 4> RuntimeLibraryPaths, | ||
llvm::SmallVector<llvm::StringRef, 4> RuntimeLibraryImportPaths, | ||
llvm::StringRef RuntimeResourcePath) | ||
: RuntimeLibraryPaths(RuntimeLibraryPaths), | ||
RuntimeLibraryImportPaths(RuntimeLibraryImportPaths), | ||
RuntimeResourcePath(RuntimeResourcePath), Diags(SM) {} | ||
bool addModule(std::unique_ptr<llvm::MemoryBuffer> Buffer); | ||
bool emitLLVMModules(GetStreamFn GetStream); | ||
|
||
private: | ||
ASTContext *createASTContext(serialization::ValidationInfo info, | ||
serialization::ExtendedValidationInfo extInfo); | ||
}; | ||
|
||
} // namespace lto | ||
} // namespace swift | ||
|
||
#endif // SWIFT_LTO_H |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
add_swift_host_library(swiftLTO STATIC | ||
LTO.cpp) | ||
target_link_libraries(swiftLTO PRIVATE | ||
swiftFrontend | ||
swiftIRGen | ||
swiftSILGen | ||
swiftSILOptimizer) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
//===--- LTO.cpp - Swift LTO ----------------------------------------------===// | ||
// | ||
// This source file is part of the Swift.org open source project | ||
// | ||
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors | ||
// Licensed under Apache License v2.0 with Runtime Library Exception | ||
// | ||
// See https://swift.org/LICENSE.txt for license information | ||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "swift/LTO/LTO.h" | ||
#include "swift/AST/DiagnosticsFrontend.h" | ||
#include "swift/AST/IRGenRequests.h" | ||
#include "swift/ClangImporter/ClangImporter.h" | ||
#include "swift/SIL/SILModule.h" | ||
#include "swift/Serialization/SerializedModuleLoader.h" | ||
#include "llvm/Bitcode/BitcodeWriter.h" | ||
#include "llvm/IR/Module.h" | ||
|
||
namespace swift { | ||
|
||
namespace lto { | ||
|
||
using namespace llvm; | ||
|
||
bool LTOPipeline::addModule(std::unique_ptr<MemoryBuffer> Buffer) { | ||
serialization::ExtendedValidationInfo extendedInfo; | ||
serialization::ValidationInfo info = | ||
serialization::validateSerializedAST(Buffer->getBuffer(), &extendedInfo); | ||
if (info.status != serialization::Status::Valid) { | ||
Diags.diagnose(SourceLoc(), diag::invalid_serialized_module); | ||
return true; | ||
} | ||
|
||
if (!Ctx) { | ||
Ctx.reset(createASTContext(info, extendedInfo)); | ||
} | ||
|
||
MBL->registerMemoryBuffer(info.name, std::move(Buffer)); | ||
|
||
ModuleNames.emplace_back(Ctx->getIdentifier(info.name)); | ||
return false; | ||
} | ||
|
||
bool LTOPipeline::emitLLVMModules(GetStreamFn GetStream) { | ||
IRGenOptions Opts = {}; | ||
Opts.OutputKind = IRGenOutputKind::Module; | ||
|
||
for (auto &ModuleName : ModuleNames) { | ||
std::vector<swift::Located<swift::Identifier>> AccessPath; | ||
AccessPath.emplace_back(ModuleName, SourceLoc()); | ||
auto SwiftModule = Ctx->getModule(AccessPath); | ||
if (!SwiftModule) { | ||
Diags.diagnose(SourceLoc(), diag::unable_to_load_serialized_module, | ||
ModuleName.get()); | ||
return true; | ||
} | ||
kateinoigakukun marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Lowering::TypeConverter Types(*SwiftModule); | ||
SILOptions SILOpts = {}; | ||
auto SM = performASTLowering(SwiftModule, Types, SILOpts); | ||
// TODO: Propagate input file name through SIB to enable debug info | ||
const PrimarySpecificPaths PSPs; | ||
auto GeneratedMod = | ||
performIRGeneration(Opts, SM->getSwiftModule(), std::move(SM), | ||
ModuleName.get(), PSPs, ArrayRef<std::string>()); | ||
auto LLVMMod = GeneratedMod.getModule(); | ||
if (auto OS = GetStream(LLVMMod->getName())) { | ||
WriteBitcodeToFile(*LLVMMod, *OS); | ||
} | ||
kateinoigakukun marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
return false; | ||
} | ||
|
||
ASTContext * | ||
LTOPipeline::createASTContext(serialization::ValidationInfo info, | ||
serialization::ExtendedValidationInfo extInfo) { | ||
auto Ctx = ASTContext::get(LangOpts, TCOpts, SearchPathOpts, SM, Diags); | ||
Diags.addConsumer(PrintDiags); | ||
LangOpts.setTarget(Triple(info.targetTriple)); | ||
SearchPathOpts.SDKPath = extInfo.getSDKPath(); | ||
|
||
SearchPathOpts.RuntimeLibraryPaths.insert( | ||
SearchPathOpts.RuntimeLibraryPaths.end(), RuntimeLibraryPaths.begin(), | ||
RuntimeLibraryPaths.end()); | ||
SearchPathOpts.RuntimeLibraryImportPaths.insert( | ||
SearchPathOpts.RuntimeLibraryImportPaths.end(), | ||
RuntimeLibraryImportPaths.begin(), RuntimeLibraryImportPaths.end()); | ||
SearchPathOpts.RuntimeResourcePath = RuntimeResourcePath; | ||
|
||
// MARK: Setup module loaders | ||
std::unique_ptr<ClangImporter> clangImporter = | ||
ClangImporter::create(*Ctx, ClangOpts, "", nullptr); | ||
auto const &Clang = clangImporter->getClangInstance(); | ||
std::string ModuleCachePath = getModuleCachePathFromClang(Clang); | ||
|
||
auto MIL = ModuleInterfaceLoader::create(*Ctx, ModuleCachePath, "", nullptr, | ||
ModuleLoadingMode::PreferSerialized); | ||
Ctx->addModuleLoader(std::move(MIL)); | ||
auto MBL = MemoryBufferSerializedModuleLoader::create( | ||
*Ctx, nullptr, ModuleLoadingMode::OnlySerialized, true); | ||
this->MBL = MBL.get(); | ||
|
||
auto SML = SerializedModuleLoader::create( | ||
*Ctx, nullptr, ModuleLoadingMode::OnlySerialized, true); | ||
|
||
Ctx->addModuleLoader(std::move(MBL)); | ||
Ctx->addModuleLoader(std::move(SML)); | ||
Ctx->addModuleLoader(std::move(clangImporter), /*isClang*/ true); | ||
|
||
registerIRGenRequestFunctions(Ctx->evaluator); | ||
registerSILOptimizerRequestFunctions(Ctx->evaluator); | ||
registerParseRequestFunctions(Ctx->evaluator); | ||
registerTypeCheckerRequestFunctions(Ctx->evaluator); | ||
registerSILGenRequestFunctions(Ctx->evaluator); | ||
registerIRGenSILTransforms(*Ctx); | ||
return Ctx; | ||
} | ||
|
||
} // namespace lto | ||
} // namespace swift |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
public struct LibX {} | ||
|
||
public func getLibX() -> LibX { return LibX() } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import lib | ||
|
||
_ = getLibX() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// RUN: %empty-directory(%t) | ||
// RUN: cd %t && %target-swiftc_driver -emit-module %S/Inputs/lib.swift | ||
// RUN: cd %t && %target-swift-frontend -emit-sib %S/Inputs/lib.swift -parse-as-library | ||
// RUN: cd %t && %target-swift-frontend -emit-sib -I%t %S/Inputs/main.swift | ||
|
||
// Examine loading order | ||
// RUN: cd %t && %swift-lto main.sib lib.sib | ||
// RUN: cd %t && %llvm-dis lib.bc -o - | %FileCheck %s -check-prefix=CHECK-LIB | ||
// RUN: cd %t && %llvm-dis main.bc -o - | %FileCheck %s -check-prefix=CHECK-MAIN | ||
|
||
// RUN: cd %t && %swift-lto lib.sib main.sib | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test doesn't actually check anything ... what is the purpose of the test? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test checks that modules can be deserialized using the pipeline and ensure that the pipeline can resolve dependent modules regardless of input order. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think Im not understanding something then. The test doesn't verify that the module are deserialized and the dependency modules are resolved. It just assumes that if the process exited everything is correct. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see. added more checks |
||
// RUN: cd %t && %llvm-dis lib.bc -o - | %FileCheck %s -check-prefix=CHECK-LIB | ||
// RUN: cd %t && %llvm-dis main.bc -o - | %FileCheck %s -check-prefix=CHECK-MAIN | ||
|
||
// CHECK-LIB: ModuleID = 'lib.bc' | ||
// CHECK-LIB: define hidden swiftcc void @"$s3lib4LibXVACycfC"() | ||
// CHECK-LIB: define swiftcc void @"$s3lib7getLibXAA0C1XVyF"() | ||
// CHECK-LIB: call swiftcc void @"$s3lib4LibXVACycfC" | ||
|
||
// CHECK-MAIN: ModuleID = 'main.bc' | ||
// CHECK-MAIN: define i32 @main | ||
// CHECK-MAIN: call swiftcc void @"$s3lib7getLibXAA0C1XVyF" | ||
// CHECK-MAIN: declare swiftcc void @"$s3lib7getLibXAA0C1XVyF"() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
//===--- swift_lto_main.cpp -----------------------------------------------===// | ||
// | ||
// This source file is part of the Swift.org open source project | ||
// | ||
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors | ||
// Licensed under Apache License v2.0 with Runtime Library Exception | ||
// | ||
// See https://swift.org/LICENSE.txt for license information | ||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
// | ||
//===----------------------------------------------------------------------===// | ||
/// | ||
/// \file | ||
/// | ||
/// This is a tool for reading sib files and running LTO passes upon them. | ||
/// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "swift/Basic/LLVMInitialize.h" | ||
#include "swift/Frontend/Frontend.h" | ||
#include "swift/LTO/LTO.h" | ||
#include "swift/Strings.h" | ||
#include "llvm/Support/CommandLine.h" | ||
#include "llvm/Support/MemoryBuffer.h" | ||
#include <cstdio> | ||
#include <memory> | ||
using namespace swift; | ||
|
||
llvm::cl::SubCommand LTOSubcommand("lto", "Swift LTO Tool"); | ||
|
||
static llvm::cl::list<std::string> | ||
InputFilenames(llvm::cl::Positional, llvm::cl::desc("[input files...]"), | ||
llvm::cl::OneOrMore, llvm::cl::sub(LTOSubcommand)); | ||
|
||
// This function isn't referenced outside its translation unit, but it | ||
// can't use the "static" keyword because its address is used for | ||
// getMainExecutable (since some platforms don't support taking the | ||
// address of main, and some platforms can't implement getMainExecutable | ||
// without being given the address of a function in the main executable). | ||
void anchorForGetMainExecutableInSwiftLTO() {} | ||
|
||
// This tool is combined with sil-llvm-gen to reduce link time. | ||
// This entrypoint is invoked from SILLLVMGen.cpp when user invoke | ||
// lto subcommand. | ||
int swift_lto_main(int argc, char **argv) { | ||
CompilerInvocation Invocation; | ||
|
||
Invocation.setMainExecutablePath(llvm::sys::fs::getMainExecutable( | ||
argv[0], | ||
reinterpret_cast<void *>(&anchorForGetMainExecutableInSwiftLTO))); | ||
|
||
auto SearchPathOpts = Invocation.getSearchPathOptions(); | ||
llvm::SmallVector<llvm::StringRef, 4> RuntimeLibraryPaths; | ||
llvm::SmallVector<llvm::StringRef, 4> RuntimeLibraryImportPaths; | ||
RuntimeLibraryPaths.insert(RuntimeLibraryPaths.begin(), | ||
SearchPathOpts.RuntimeLibraryPaths.begin(), | ||
SearchPathOpts.RuntimeLibraryPaths.end()); | ||
RuntimeLibraryImportPaths.insert( | ||
RuntimeLibraryImportPaths.begin(), | ||
SearchPathOpts.RuntimeLibraryImportPaths.begin(), | ||
SearchPathOpts.RuntimeLibraryImportPaths.end()); | ||
lto::LTOPipeline Pipeline(RuntimeLibraryPaths, RuntimeLibraryImportPaths, | ||
SearchPathOpts.RuntimeResourcePath); | ||
|
||
for (auto InputFilename : InputFilenames) { | ||
// Load the input file. | ||
auto FileBufOrErr = llvm::MemoryBuffer::getFileOrSTDIN(InputFilename); | ||
if (!FileBufOrErr) { | ||
fprintf(stderr, "Error! Failed to open file: %s\n", | ||
InputFilename.c_str()); | ||
exit(-1); | ||
} | ||
|
||
if (Pipeline.addModule(std::move(FileBufOrErr.get()))) { | ||
fprintf(stderr, "Error! Failed to load serialized module: %s\n", | ||
InputFilename.c_str()); | ||
exit(-1); | ||
} | ||
} | ||
|
||
Pipeline.emitLLVMModules([&](StringRef ModuleName) { | ||
std::error_code EC; | ||
std::unique_ptr<llvm::raw_ostream> RawOS = | ||
std::make_unique<llvm::raw_fd_ostream>(ModuleName.str() + ".bc", EC); | ||
if (EC) | ||
return std::unique_ptr<llvm::raw_ostream>(nullptr); | ||
return RawOS; | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you clarify why exact this tool is needed? This really feels like its executing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The tool accepts SIB files for multiple modules and optimizes them at once. This is something swift-llvm-gen does not do. If piping sil-opt and swift-llvm-gen, it's impossible to optimize across modules. And the main purpose of this tool is simulating linker integration without linker. So it doesn't match sil-opt and swift-llvm-gen's purposes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But you can load the SIB file in
I think Im not understanding the missing piece in the tools. Can you explain that and write that up in the commit message as well? I think its critical to explain the deficiency.
I disagree on this. Its not simulating linker integration - its running the SIL pipeline on a set of SIB files. That is not related to linking. Running the SIL pipeline is precisely what There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As you say, this swift-lto and sil-opt have the same part of roles in optimizing SIBs. But the sil-opt also accept SIL as input, whereas the LTO pipeline only accepts SIBs. The LTO pipeline accepts inputs only from linkers, so SILs will never be inputted. If LTO pipeline accepts SILs as input, the LTO pipeline becomes more complex only for testing. For example, SIL doesn't serialize SDKPath, etc., so we have to assemble those missing pieces of information before passing input them into the pipeline. So creating new tool that only accepts only SIBs helps keep the tool simple. And sil-opt really depends on Frontend module that assumes that input module is always one. But LTO pipeline is independent from the module because LTO pipeline needs to be acceptable for multi-modules. This difference makes it difficult to merge these tools. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, The flags are precisely what all the LTO work is about - its how to thread all the appropriate flags through the driver into the frontend and the linker. I think that is expected. That will also be needed for this tool. So given that the tools can accomplish what is needed, the question is why is the new tool better than building on the existing tools? Is it going to be far more code to just add support for multiple files to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps I've made a big misunderstanding. Do you mean that the linker will run sil-opt directly? I was thinking that LTOPipeline class will be linked to the linker. And swift-lto tool was only going to be used for testing. |
||
return 0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll refine after #32237 merged. I'll serialize options only when
-lto=swift
is given.