Files
swift-mirror/lib/Frontend/Frontend.cpp
David Zarzycki 017ee7bf04 [SIL] NFC: Simplify SILVTable and save 8 bytes per SILVTable
We were not using the primary benefits of an intrusive list, namely the
ability to insert or remove from the middle of the list, so let's switch
to a plain vector. This also avoids linked-list pointer chasing.
2020-06-10 07:54:23 -04:00

1070 lines
40 KiB
C++

//===--- Frontend.cpp - frontend utility methods --------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file contains utility methods for parsing and performing semantic
// on modules.
//
//===----------------------------------------------------------------------===//
#include "swift/Frontend/Frontend.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/AST/DiagnosticsSema.h"
#include "swift/AST/FileSystem.h"
#include "swift/AST/IncrementalRanges.h"
#include "swift/AST/Module.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/Basic/FileTypes.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Basic/Statistic.h"
#include "swift/Frontend/ModuleInterfaceLoader.h"
#include "swift/Parse/Lexer.h"
#include "swift/SIL/SILModule.h"
#include "swift/SILOptimizer/PassManager/Passes.h"
#include "swift/SILOptimizer/Utils/Generics.h"
#include "swift/Serialization/SerializationOptions.h"
#include "swift/Serialization/SerializedModuleLoader.h"
#include "swift/Strings.h"
#include "swift/Subsystems.h"
#include "clang/AST/ASTContext.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
using namespace swift;
CompilerInstance::CompilerInstance() = default;
CompilerInstance::~CompilerInstance() = default;
std::string CompilerInvocation::getPCHHash() const {
using llvm::hash_combine;
auto Code = hash_combine(LangOpts.getPCHHashComponents(),
FrontendOpts.getPCHHashComponents(),
ClangImporterOpts.getPCHHashComponents(),
SearchPathOpts.getPCHHashComponents(),
DiagnosticOpts.getPCHHashComponents(),
SILOpts.getPCHHashComponents(),
IRGenOpts.getPCHHashComponents());
return llvm::APInt(64, Code).toString(36, /*Signed=*/false);
}
const PrimarySpecificPaths &
CompilerInvocation::getPrimarySpecificPathsForAtMostOnePrimary() const {
return getFrontendOptions().getPrimarySpecificPathsForAtMostOnePrimary();
}
const PrimarySpecificPaths &
CompilerInvocation::getPrimarySpecificPathsForPrimary(
StringRef filename) const {
return getFrontendOptions().getPrimarySpecificPathsForPrimary(filename);
}
const PrimarySpecificPaths &
CompilerInvocation::getPrimarySpecificPathsForSourceFile(
const SourceFile &SF) const {
return getPrimarySpecificPathsForPrimary(SF.getFilename());
}
std::string CompilerInvocation::getOutputFilenameForAtMostOnePrimary() const {
return getPrimarySpecificPathsForAtMostOnePrimary().OutputFilename;
}
std::string
CompilerInvocation::getMainInputFilenameForDebugInfoForAtMostOnePrimary()
const {
return getPrimarySpecificPathsForAtMostOnePrimary()
.MainInputFilenameForDebugInfo;
}
std::string
CompilerInvocation::getObjCHeaderOutputPathForAtMostOnePrimary() const {
return getPrimarySpecificPathsForAtMostOnePrimary()
.SupplementaryOutputs.ObjCHeaderOutputPath;
}
std::string CompilerInvocation::getModuleOutputPathForAtMostOnePrimary() const {
return getPrimarySpecificPathsForAtMostOnePrimary()
.SupplementaryOutputs.ModuleOutputPath;
}
std::string CompilerInvocation::getReferenceDependenciesFilePathForPrimary(
StringRef filename) const {
return getPrimarySpecificPathsForPrimary(filename)
.SupplementaryOutputs.ReferenceDependenciesFilePath;
}
std::string
CompilerInvocation::getSwiftRangesFilePathForPrimary(StringRef filename) const {
return getPrimarySpecificPathsForPrimary(filename)
.SupplementaryOutputs.SwiftRangesFilePath;
}
std::string CompilerInvocation::getCompiledSourceFilePathForPrimary(
StringRef filename) const {
return getPrimarySpecificPathsForPrimary(filename)
.SupplementaryOutputs.CompiledSourceFilePath;
}
std::string
CompilerInvocation::getSerializedDiagnosticsPathForAtMostOnePrimary() const {
return getPrimarySpecificPathsForAtMostOnePrimary()
.SupplementaryOutputs.SerializedDiagnosticsPath;
}
std::string CompilerInvocation::getTBDPathForWholeModule() const {
assert(getFrontendOptions().InputsAndOutputs.isWholeModule() &&
"TBDPath only makes sense when the whole module can be seen");
return getPrimarySpecificPathsForAtMostOnePrimary()
.SupplementaryOutputs.TBDPath;
}
std::string
CompilerInvocation::getLdAddCFileOutputPathForWholeModule() const {
assert(getFrontendOptions().InputsAndOutputs.isWholeModule() &&
"LdAdd cfile only makes sense when the whole module can be seen");
return getPrimarySpecificPathsForAtMostOnePrimary()
.SupplementaryOutputs.LdAddCFilePath;
}
std::string
CompilerInvocation::getModuleInterfaceOutputPathForWholeModule() const {
assert(getFrontendOptions().InputsAndOutputs.isWholeModule() &&
"ModuleInterfaceOutputPath only makes sense when the whole module "
"can be seen");
return getPrimarySpecificPathsForAtMostOnePrimary()
.SupplementaryOutputs.ModuleInterfaceOutputPath;
}
std::string
CompilerInvocation::getPrivateModuleInterfaceOutputPathForWholeModule() const {
assert(getFrontendOptions().InputsAndOutputs.isWholeModule() &&
"PrivateModuleInterfaceOutputPath only makes sense when the whole "
"module can be seen");
return getPrimarySpecificPathsForAtMostOnePrimary()
.SupplementaryOutputs.PrivateModuleInterfaceOutputPath;
}
SerializationOptions CompilerInvocation::computeSerializationOptions(
const SupplementaryOutputPaths &outs, const ModuleDecl *module) const {
const FrontendOptions &opts = getFrontendOptions();
SerializationOptions serializationOpts;
serializationOpts.OutputPath = outs.ModuleOutputPath.c_str();
serializationOpts.DocOutputPath = outs.ModuleDocOutputPath.c_str();
serializationOpts.SourceInfoOutputPath = outs.ModuleSourceInfoOutputPath.c_str();
serializationOpts.GroupInfoPath = opts.GroupInfoPath.c_str();
if (opts.SerializeBridgingHeader && !outs.ModuleOutputPath.empty())
serializationOpts.ImportedHeader = opts.ImplicitObjCHeaderPath;
serializationOpts.ModuleLinkName = opts.ModuleLinkName;
serializationOpts.ExtraClangOptions = getClangImporterOptions().ExtraArgs;
if (!getIRGenOptions().ForceLoadSymbolName.empty())
serializationOpts.AutolinkForceLoad = true;
// Options contain information about the developer's computer,
// so only serialize them if the module isn't going to be shipped to
// the public.
serializationOpts.SerializeOptionsForDebugging =
opts.SerializeOptionsForDebugging.getValueOr(
!isModuleExternallyConsumed(module));
return serializationOpts;
}
Lowering::TypeConverter &CompilerInstance::getSILTypes() {
if (auto *tc = TheSILTypes.get())
return *tc;
auto *tc = new Lowering::TypeConverter(*getMainModule());
TheSILTypes.reset(tc);
return *tc;
}
void CompilerInstance::recordPrimaryInputBuffer(unsigned BufID) {
PrimaryBufferIDs.insert(BufID);
}
bool CompilerInstance::setUpASTContextIfNeeded() {
if (Invocation.getFrontendOptions().RequestedAction ==
FrontendOptions::ActionType::CompileModuleFromInterface) {
// Compiling a module interface from source uses its own CompilerInstance
// with options read from the input file. Don't bother setting up an
// ASTContext at this level.
return false;
}
Context.reset(ASTContext::get(
Invocation.getLangOptions(), Invocation.getTypeCheckerOptions(),
Invocation.getSearchPathOptions(), SourceMgr, Diagnostics));
registerParseRequestFunctions(Context->evaluator);
registerTypeCheckerRequestFunctions(Context->evaluator);
registerSILGenRequestFunctions(Context->evaluator);
registerSILOptimizerRequestFunctions(Context->evaluator);
registerTBDGenRequestFunctions(Context->evaluator);
registerIRGenRequestFunctions(Context->evaluator);
// Migrator, indexing and typo correction need some IDE requests.
// The integrated REPL needs IDE requests for completion.
if (Invocation.getMigratorOptions().shouldRunMigrator() ||
!Invocation.getFrontendOptions().IndexStorePath.empty() ||
Invocation.getLangOptions().TypoCorrectionLimit ||
Invocation.getFrontendOptions().RequestedAction ==
FrontendOptions::ActionType::REPL) {
registerIDERequestFunctions(Context->evaluator);
}
registerIRGenSILTransforms(*Context);
if (setUpModuleLoaders())
return true;
return false;
}
void CompilerInstance::setupStatsReporter() {
const auto &Invok = getInvocation();
const std::string &StatsOutputDir =
Invok.getFrontendOptions().StatsOutputDir;
if (StatsOutputDir.empty())
return;
auto silOptModeArgStr = [](OptimizationMode mode) -> StringRef {
switch (mode) {
case OptimizationMode::ForSpeed:
return "O";
case OptimizationMode::ForSize:
return "Osize";
default:
return "Onone";
}
};
auto getClangSourceManager = [](ASTContext &Ctx) -> clang::SourceManager * {
if (auto *clangImporter = static_cast<ClangImporter *>(
Ctx.getClangModuleLoader())) {
return &clangImporter->getClangASTContext().getSourceManager();
}
return nullptr;
};
const auto &FEOpts = Invok.getFrontendOptions();
const auto &LangOpts = Invok.getLangOptions();
const auto &SILOpts = Invok.getSILOptions();
const std::string &OutFile =
FEOpts.InputsAndOutputs.lastInputProducingOutput().outputFilename();
auto Reporter = std::make_unique<UnifiedStatsReporter>(
"swift-frontend",
FEOpts.ModuleName,
FEOpts.InputsAndOutputs.getStatsFileMangledInputName(),
LangOpts.Target.normalize(),
llvm::sys::path::extension(OutFile),
silOptModeArgStr(SILOpts.OptMode),
StatsOutputDir,
&getSourceMgr(),
getClangSourceManager(getASTContext()),
Invok.getFrontendOptions().TraceStats,
Invok.getFrontendOptions().ProfileEvents,
Invok.getFrontendOptions().ProfileEntities);
// Hand the stats reporter down to the ASTContext so the rest of the compiler
// can use it.
getASTContext().setStatsReporter(Reporter.get());
Stats = std::move(Reporter);
}
void CompilerInstance::setupDiagnosticVerifierIfNeeded() {
auto &diagOpts = Invocation.getDiagnosticOptions();
if (diagOpts.VerifyMode != DiagnosticOptions::NoVerify) {
DiagVerifier = std::make_unique<DiagnosticVerifier>(
SourceMgr, InputSourceCodeBufferIDs,
diagOpts.VerifyMode == DiagnosticOptions::VerifyAndApplyFixes,
diagOpts.VerifyIgnoreUnknown);
addDiagnosticConsumer(DiagVerifier.get());
}
}
bool CompilerInstance::setup(const CompilerInvocation &Invok) {
Invocation = Invok;
// If initializing the overlay file system fails there's no sense in
// continuing because the compiler will read the wrong files.
if (setUpVirtualFileSystemOverlays())
return true;
setUpLLVMArguments();
setUpDiagnosticOptions();
assert(Lexer::isIdentifier(Invocation.getModuleName()));
if (setUpInputs())
return true;
if (setUpASTContextIfNeeded())
return true;
setupStatsReporter();
setupDiagnosticVerifierIfNeeded();
return false;
}
static bool loadAndValidateVFSOverlay(
const std::string &File,
const llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> &BaseFS,
const llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> &OverlayFS,
DiagnosticEngine &Diag) {
auto Buffer = BaseFS->getBufferForFile(File);
if (!Buffer) {
Diag.diagnose(SourceLoc(), diag::cannot_open_file, File,
Buffer.getError().message());
return true;
}
auto VFS = llvm::vfs::getVFSFromYAML(std::move(Buffer.get()),
nullptr, File);
if (!VFS) {
Diag.diagnose(SourceLoc(), diag::invalid_vfs_overlay_file, File);
return true;
}
OverlayFS->pushOverlay(VFS);
return false;
}
bool CompilerInstance::setUpVirtualFileSystemOverlays() {
auto BaseFS = SourceMgr.getFileSystem();
auto OverlayFS = llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>(
new llvm::vfs::OverlayFileSystem(BaseFS));
bool hadAnyFailure = false;
bool hasOverlays = false;
for (const auto &File : Invocation.getSearchPathOptions().VFSOverlayFiles) {
hasOverlays = true;
hadAnyFailure |=
loadAndValidateVFSOverlay(File, BaseFS, OverlayFS, Diagnostics);
}
// If we successfully loaded all the overlays, let the source manager and
// diagnostic engine take advantage of the overlay file system.
if (!hadAnyFailure && hasOverlays) {
SourceMgr.setFileSystem(OverlayFS);
}
return hadAnyFailure;
}
void CompilerInstance::setUpLLVMArguments() {
// Honor -Xllvm.
if (!Invocation.getFrontendOptions().LLVMArgs.empty()) {
llvm::SmallVector<const char *, 4> Args;
Args.push_back("swift (LLVM option parsing)");
for (unsigned i = 0, e = Invocation.getFrontendOptions().LLVMArgs.size();
i != e; ++i)
Args.push_back(Invocation.getFrontendOptions().LLVMArgs[i].c_str());
Args.push_back(nullptr);
llvm::cl::ParseCommandLineOptions(Args.size()-1, Args.data());
}
}
void CompilerInstance::setUpDiagnosticOptions() {
if (Invocation.getDiagnosticOptions().ShowDiagnosticsAfterFatalError) {
Diagnostics.setShowDiagnosticsAfterFatalError();
}
if (Invocation.getDiagnosticOptions().SuppressWarnings) {
Diagnostics.setSuppressWarnings(true);
}
if (Invocation.getDiagnosticOptions().WarningsAsErrors) {
Diagnostics.setWarningsAsErrors(true);
}
if (Invocation.getDiagnosticOptions().PrintDiagnosticNames) {
Diagnostics.setPrintDiagnosticNames(true);
}
Diagnostics.setDiagnosticDocumentationPath(
Invocation.getDiagnosticOptions().DiagnosticDocumentationPath);
}
// The ordering of ModuleLoaders is important!
//
// 1. SourceLoader: This is a hack and only the compiler's tests are using it,
// to avoid writing repetitive code involving generating modules/interfaces.
// Ideally, we'd get rid of it.
// 2. MemoryBufferSerializedModuleLoader: This is used by LLDB, because it might
// already have the module available in memory.
// 3. ModuleInterfaceLoader: Tries to find an up-to-date swiftmodule. If it
// succeeds, it issues a particular "error" (see
// [Note: ModuleInterfaceLoader-defer-to-SerializedModuleLoader]), which
// is interpreted by the overarching loader as a command to use the
// SerializedModuleLoader. If we failed to find a .swiftmodule, this falls
// back to using an interface. Actual errors lead to diagnostics.
// 4. SerializedModuleLoader: Loads a serialized module if it can.
// 5. ClangImporter: This must come after all the Swift module loaders because
// in the presence of overlays and mixed-source frameworks, we want to prefer
// the overlay or framework module over the underlying Clang module.
bool CompilerInstance::setUpModuleLoaders() {
if (hasSourceImport()) {
bool enableLibraryEvolution =
Invocation.getFrontendOptions().EnableLibraryEvolution;
Context->addModuleLoader(SourceLoader::create(*Context,
enableLibraryEvolution,
getDependencyTracker()));
}
auto MLM = ModuleLoadingMode::PreferSerialized;
if (auto forceModuleLoadingMode =
llvm::sys::Process::GetEnv("SWIFT_FORCE_MODULE_LOADING")) {
if (*forceModuleLoadingMode == "prefer-interface" ||
*forceModuleLoadingMode == "prefer-parseable")
MLM = ModuleLoadingMode::PreferInterface;
else if (*forceModuleLoadingMode == "prefer-serialized")
MLM = ModuleLoadingMode::PreferSerialized;
else if (*forceModuleLoadingMode == "only-interface" ||
*forceModuleLoadingMode == "only-parseable")
MLM = ModuleLoadingMode::OnlyInterface;
else if (*forceModuleLoadingMode == "only-serialized")
MLM = ModuleLoadingMode::OnlySerialized;
else {
Diagnostics.diagnose(SourceLoc(),
diag::unknown_forced_module_loading_mode,
*forceModuleLoadingMode);
return true;
}
}
auto IgnoreSourceInfoFile =
Invocation.getFrontendOptions().IgnoreSwiftSourceInfo;
if (Invocation.getLangOptions().EnableMemoryBufferImporter) {
auto MemoryBufferLoader = MemoryBufferSerializedModuleLoader::create(
*Context, getDependencyTracker(), MLM, IgnoreSourceInfoFile);
this->MemoryBufferLoader = MemoryBufferLoader.get();
Context->addModuleLoader(std::move(MemoryBufferLoader));
}
// Wire up the Clang importer. If the user has specified an SDK, use it.
// Otherwise, we just keep it around as our interface to Clang's ABI
// knowledge.
std::unique_ptr<ClangImporter> clangImporter =
ClangImporter::create(*Context, Invocation.getClangImporterOptions(),
Invocation.getPCHHash(), getDependencyTracker());
if (!clangImporter) {
Diagnostics.diagnose(SourceLoc(), diag::error_clang_importer_create_fail);
return true;
}
// If implicit modules are disabled, we need to install an explicit module
// loader.
if (Invocation.getFrontendOptions().DisableImplicitModules) {
auto ESML = ExplicitSwiftModuleLoader::create(
*Context,
getDependencyTracker(), MLM,
Invocation.getSearchPathOptions().ExplicitSwiftModules,
IgnoreSourceInfoFile);
Context->addModuleLoader(std::move(ESML));
}
if (MLM != ModuleLoadingMode::OnlySerialized) {
auto const &Clang = clangImporter->getClangInstance();
std::string ModuleCachePath = getModuleCachePathFromClang(Clang);
auto &FEOpts = Invocation.getFrontendOptions();
StringRef PrebuiltModuleCachePath = FEOpts.PrebuiltModuleCachePath;
ModuleInterfaceLoaderOptions LoaderOpts(FEOpts);
auto PIML = ModuleInterfaceLoader::create(
*Context, ModuleCachePath, PrebuiltModuleCachePath,
getDependencyTracker(), MLM, FEOpts.PreferInterfaceForModules,
LoaderOpts,
IgnoreSourceInfoFile);
Context->addModuleLoader(std::move(PIML));
}
std::unique_ptr<SerializedModuleLoader> SML =
SerializedModuleLoader::create(*Context, getDependencyTracker(), MLM,
IgnoreSourceInfoFile);
this->SML = SML.get();
Context->addModuleLoader(std::move(SML));
Context->addModuleLoader(std::move(clangImporter), /*isClang*/ true);
return false;
}
Optional<unsigned> CompilerInstance::setUpCodeCompletionBuffer() {
Optional<unsigned> codeCompletionBufferID;
auto codeCompletePoint = Invocation.getCodeCompletionPoint();
if (codeCompletePoint.first) {
auto memBuf = codeCompletePoint.first;
// CompilerInvocation doesn't own the buffers, copy to a new buffer.
codeCompletionBufferID = SourceMgr.addMemBufferCopy(memBuf);
InputSourceCodeBufferIDs.push_back(*codeCompletionBufferID);
SourceMgr.setCodeCompletionPoint(*codeCompletionBufferID,
codeCompletePoint.second);
}
return codeCompletionBufferID;
}
SourceFile *CompilerInstance::getCodeCompletionFile() const {
auto *mod = getMainModule();
auto &eval = mod->getASTContext().evaluator;
return evaluateOrDefault(eval, CodeCompletionFileRequest{mod}, nullptr);
}
static bool shouldTreatSingleInputAsMain(InputFileKind inputKind) {
switch (inputKind) {
case InputFileKind::Swift:
case InputFileKind::SwiftModuleInterface:
case InputFileKind::SIL:
return true;
case InputFileKind::SwiftLibrary:
case InputFileKind::LLVM:
case InputFileKind::None:
return false;
}
llvm_unreachable("unhandled input kind");
}
bool CompilerInstance::setUpInputs() {
// Adds to InputSourceCodeBufferIDs, so may need to happen before the
// per-input setup.
const Optional<unsigned> codeCompletionBufferID = setUpCodeCompletionBuffer();
for (const InputFile &input :
Invocation.getFrontendOptions().InputsAndOutputs.getAllInputs())
if (setUpForInput(input))
return true;
// Set the primary file to the code-completion point if one exists.
if (codeCompletionBufferID.hasValue() &&
!isPrimaryInput(*codeCompletionBufferID)) {
assert(PrimaryBufferIDs.empty() && "re-setting PrimaryBufferID");
recordPrimaryInputBuffer(*codeCompletionBufferID);
}
if (MainBufferID == NO_SUCH_BUFFER &&
InputSourceCodeBufferIDs.size() == 1 &&
shouldTreatSingleInputAsMain(Invocation.getInputKind())) {
MainBufferID = InputSourceCodeBufferIDs.front();
}
return false;
}
bool CompilerInstance::setUpForInput(const InputFile &input) {
bool failed = false;
Optional<unsigned> bufferID = getRecordedBufferID(input, failed);
if (failed)
return true;
if (!bufferID)
return false;
if (isInputSwift() &&
llvm::sys::path::filename(input.file()) == "main.swift") {
assert(MainBufferID == NO_SUCH_BUFFER && "re-setting MainBufferID");
MainBufferID = *bufferID;
}
if (input.isPrimary()) {
recordPrimaryInputBuffer(*bufferID);
}
return false;
}
Optional<unsigned> CompilerInstance::getRecordedBufferID(const InputFile &input,
bool &failed) {
if (!input.buffer()) {
if (Optional<unsigned> existingBufferID =
SourceMgr.getIDForBufferIdentifier(input.file())) {
return existingBufferID;
}
}
auto buffers = getInputBuffersIfPresent(input);
if (!buffers.hasValue()) {
failed = true;
return None;
}
// FIXME: The fact that this test happens twice, for some cases,
// suggests that setupInputs could use another round of refactoring.
if (serialization::isSerializedAST(buffers->ModuleBuffer->getBuffer())) {
PartialModules.push_back(std::move(*buffers));
return None;
}
assert(buffers->ModuleDocBuffer.get() == nullptr);
assert(buffers->ModuleSourceInfoBuffer.get() == nullptr);
// Transfer ownership of the MemoryBuffer to the SourceMgr.
unsigned bufferID = SourceMgr.addNewSourceBuffer(std::move(buffers->ModuleBuffer));
InputSourceCodeBufferIDs.push_back(bufferID);
return bufferID;
}
Optional<ModuleBuffers> CompilerInstance::getInputBuffersIfPresent(
const InputFile &input) {
if (auto b = input.buffer()) {
return ModuleBuffers(llvm::MemoryBuffer::getMemBufferCopy(b->getBuffer(),
b->getBufferIdentifier()));
}
// FIXME: Working with filenames is fragile, maybe use the real path
// or have some kind of FileManager.
using FileOrError = llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>;
FileOrError inputFileOrErr = swift::vfs::getFileOrSTDIN(getFileSystem(),
input.file());
if (!inputFileOrErr) {
Diagnostics.diagnose(SourceLoc(), diag::error_open_input_file, input.file(),
inputFileOrErr.getError().message());
return None;
}
if (!serialization::isSerializedAST((*inputFileOrErr)->getBuffer()))
return ModuleBuffers(std::move(*inputFileOrErr));
auto swiftdoc = openModuleDoc(input);
auto sourceinfo = openModuleSourceInfo(input);
return ModuleBuffers(std::move(*inputFileOrErr),
swiftdoc.hasValue() ? std::move(swiftdoc.getValue()) : nullptr,
sourceinfo.hasValue() ? std::move(sourceinfo.getValue()) : nullptr);
}
Optional<std::unique_ptr<llvm::MemoryBuffer>>
CompilerInstance::openModuleSourceInfo(const InputFile &input) {
llvm::SmallString<128> pathWithoutProjectDir(input.file());
llvm::sys::path::replace_extension(pathWithoutProjectDir,
file_types::getExtension(file_types::TY_SwiftSourceInfoFile));
llvm::SmallString<128> pathWithProjectDir = pathWithoutProjectDir.str();
StringRef fileName = llvm::sys::path::filename(pathWithoutProjectDir);
llvm::sys::path::remove_filename(pathWithProjectDir);
llvm::sys::path::append(pathWithProjectDir, "Project");
llvm::sys::path::append(pathWithProjectDir, fileName);
if (auto sourceInfoFileOrErr = swift::vfs::getFileOrSTDIN(getFileSystem(),
pathWithProjectDir))
return std::move(*sourceInfoFileOrErr);
if (auto sourceInfoFileOrErr = swift::vfs::getFileOrSTDIN(getFileSystem(),
pathWithoutProjectDir))
return std::move(*sourceInfoFileOrErr);
return None;
}
Optional<std::unique_ptr<llvm::MemoryBuffer>>
CompilerInstance::openModuleDoc(const InputFile &input) {
llvm::SmallString<128> moduleDocFilePath(input.file());
llvm::sys::path::replace_extension(
moduleDocFilePath,
file_types::getExtension(file_types::TY_SwiftModuleDocFile));
using FileOrError = llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>;
FileOrError moduleDocFileOrErr =
swift::vfs::getFileOrSTDIN(getFileSystem(), moduleDocFilePath);
if (moduleDocFileOrErr)
return std::move(*moduleDocFileOrErr);
if (moduleDocFileOrErr.getError() == std::errc::no_such_file_or_directory)
return std::unique_ptr<llvm::MemoryBuffer>();
Diagnostics.diagnose(SourceLoc(), diag::error_open_input_file,
moduleDocFilePath,
moduleDocFileOrErr.getError().message());
return None;
}
/// Implicitly import the SwiftOnoneSupport module in non-optimized
/// builds. This allows for use of popular specialized functions
/// from the standard library, which makes the non-optimized builds
/// execute much faster.
bool CompilerInvocation::shouldImportSwiftONoneSupport() const {
if (getImplicitStdlibKind() != ImplicitStdlibKind::Stdlib)
return false;
if (getSILOptions().shouldOptimize())
return false;
// If we are not executing an action that has a dependency on
// SwiftOnoneSupport, don't load it.
//
// FIXME: Knowledge of SwiftOnoneSupport loading in the Frontend is a layering
// violation. However, SIL currently does not have a way to express this
// dependency itself for the benefit of autolinking. In the mean time, we
// will be conservative and say that actions like -emit-silgen and
// -emit-sibgen - that don't really involve the optimizer - have a
// strict dependency on SwiftOnoneSupport.
//
// This optimization is disabled by -track-system-dependencies to preserve
// the explicit dependency.
const auto &options = getFrontendOptions();
return options.TrackSystemDeps
|| FrontendOptions::doesActionGenerateSIL(options.RequestedAction);
}
ImplicitImportInfo CompilerInstance::getImplicitImportInfo() const {
auto &frontendOpts = Invocation.getFrontendOptions();
ImplicitImportInfo imports;
imports.StdlibKind = Invocation.getImplicitStdlibKind();
for (auto &moduleStr : frontendOpts.getImplicitImportModuleNames())
imports.ModuleNames.push_back(Context->getIdentifier(moduleStr));
if (Invocation.shouldImportSwiftONoneSupport())
imports.ModuleNames.push_back(Context->getIdentifier(SWIFT_ONONE_SUPPORT));
imports.ShouldImportUnderlyingModule = frontendOpts.ImportUnderlyingModule;
imports.BridgingHeaderPath = frontendOpts.ImplicitObjCHeaderPath;
return imports;
}
bool CompilerInstance::createFilesForMainModule(
ModuleDecl *mod, SmallVectorImpl<FileUnit *> &files) const {
// Make sure the main file is the first file in the module.
if (MainBufferID != NO_SUCH_BUFFER) {
auto *mainFile = createSourceFileForMainModule(
mod, Invocation.getSourceFileKind(), MainBufferID);
files.push_back(mainFile);
}
// If we have partial modules to load, do so now, bailing if any failed to
// load.
if (!PartialModules.empty()) {
if (loadPartialModulesAndImplicitImports(mod, files))
return true;
}
// Finally add the library files.
// FIXME: This is the only demand point for InputSourceCodeBufferIDs. We
// should compute this list of source files lazily.
for (auto BufferID : InputSourceCodeBufferIDs) {
// Skip the main buffer, we've already handled it.
if (BufferID == MainBufferID)
continue;
auto *libraryFile =
createSourceFileForMainModule(mod, SourceFileKind::Library, BufferID);
files.push_back(libraryFile);
}
return false;
}
ModuleDecl *CompilerInstance::getMainModule() const {
if (!MainModule) {
Identifier ID = Context->getIdentifier(Invocation.getModuleName());
MainModule = ModuleDecl::createMainModule(*Context, ID,
getImplicitImportInfo());
if (Invocation.getFrontendOptions().EnableTesting)
MainModule->setTestingEnabled();
if (Invocation.getFrontendOptions().EnablePrivateImports)
MainModule->setPrivateImportsEnabled();
if (Invocation.getFrontendOptions().EnableImplicitDynamic)
MainModule->setImplicitDynamicEnabled();
if (Invocation.getFrontendOptions().EnableLibraryEvolution)
MainModule->setResilienceStrategy(ResilienceStrategy::Resilient);
Context->LoadedModules[MainModule->getName()] = MainModule;
// Create and add the module's files.
SmallVector<FileUnit *, 16> files;
if (!createFilesForMainModule(MainModule, files)) {
for (auto *file : files)
MainModule->addFile(*file);
} else {
// If we failed to load a partial module, mark the main module as having
// "failed to load", as it will contain no files. Note that we don't try
// to add any of the successfully loaded partial modules. This ensures
// that we don't encounter cases where we try to resolve a cross-reference
// into a partial module that failed to load.
MainModule->setFailedToLoad();
}
}
return MainModule;
}
void CompilerInstance::setMainModule(ModuleDecl *newMod) {
assert(newMod->isMainModule());
MainModule = newMod;
Context->LoadedModules[newMod->getName()] = newMod;
}
void CompilerInstance::performParseAndResolveImportsOnly() {
FrontendStatsTracer tracer(getStatsReporter(), "parse-and-resolve-imports");
// Resolve imports for all the source files.
auto *mainModule = getMainModule();
for (auto *file : mainModule->getFiles()) {
if (auto *SF = dyn_cast<SourceFile>(file))
performImportResolution(*SF);
}
assert(llvm::all_of(mainModule->getFiles(), [](const FileUnit *File) -> bool {
auto *SF = dyn_cast<SourceFile>(File);
if (!SF)
return true;
return SF->ASTStage >= SourceFile::ImportsResolved;
}) && "some files have not yet had their imports resolved");
mainModule->setHasResolvedImports();
bindExtensions(*mainModule);
}
void CompilerInstance::performSema() {
performParseAndResolveImportsOnly();
FrontendStatsTracer tracer(getStatsReporter(), "perform-sema");
forEachFileToTypeCheck([&](SourceFile &SF) {
performTypeChecking(SF);
});
finishTypeChecking();
}
bool CompilerInstance::loadStdlibIfNeeded() {
// If we aren't expecting an implicit stdlib import, there's nothing to do.
if (getImplicitImportInfo().StdlibKind != ImplicitStdlibKind::Stdlib)
return false;
FrontendStatsTracer tracer(getStatsReporter(), "load-stdlib");
ModuleDecl *M = Context->getStdlibModule(/*loadIfAbsent*/ true);
if (!M) {
Diagnostics.diagnose(SourceLoc(), diag::error_stdlib_not_found,
Invocation.getTargetTriple());
return true;
}
// If we failed to load, we should have already diagnosed.
if (M->failedToLoad()) {
assert(Diagnostics.hadAnyError() &&
"Module failed to load but nothing was diagnosed?");
return true;
}
return false;
}
bool CompilerInstance::loadPartialModulesAndImplicitImports(
ModuleDecl *mod, SmallVectorImpl<FileUnit *> &partialModules) const {
FrontendStatsTracer tracer(getStatsReporter(),
"load-partial-modules-and-implicit-imports");
// Force loading implicit imports. This is currently needed to allow
// deserialization to resolve cross references into bridging headers.
// FIXME: Once deserialization loads all the modules it needs for cross
// references, this can be removed.
(void)mod->getImplicitImports();
// Load in the partial modules.
bool hadLoadError = false;
for (auto &PM : PartialModules) {
assert(PM.ModuleBuffer);
auto *file =
SML->loadAST(*mod, /*diagLoc*/ SourceLoc(), /*moduleInterfacePath*/ "",
std::move(PM.ModuleBuffer), std::move(PM.ModuleDocBuffer),
std::move(PM.ModuleSourceInfoBuffer),
/*isFramework*/ false);
if (file) {
partialModules.push_back(file);
} else {
hadLoadError = true;
}
}
return hadLoadError;
}
void CompilerInstance::forEachFileToTypeCheck(
llvm::function_ref<void(SourceFile &)> fn) {
if (isWholeModuleCompilation()) {
for (auto fileName : getMainModule()->getFiles()) {
auto *SF = dyn_cast<SourceFile>(fileName);
if (!SF) {
continue;
}
fn(*SF);
}
} else {
for (auto *SF : getPrimarySourceFiles()) {
fn(*SF);
}
}
}
void CompilerInstance::finishTypeChecking() {
forEachFileToTypeCheck([](SourceFile &SF) {
performWholeModuleTypeChecking(SF);
});
}
SourceFile::ParsingOptions
CompilerInstance::getSourceFileParsingOptions(bool forPrimary) const {
const auto &frontendOpts = Invocation.getFrontendOptions();
const auto action = frontendOpts.RequestedAction;
auto opts = SourceFile::getDefaultParsingOptions(getASTContext().LangOpts);
if (FrontendOptions::shouldActionOnlyParse(action)) {
// Generally in a parse-only invocation, we want to disable #if evaluation.
// However, there are a couple of modes where we need to know which clauses
// are active.
if (action != FrontendOptions::ActionType::EmitImportedModules &&
action != FrontendOptions::ActionType::ScanDependencies) {
opts |= SourceFile::ParsingFlags::DisablePoundIfEvaluation;
}
// If we need to dump the parse tree, disable delayed bodies as we want to
// show everything.
if (action == FrontendOptions::ActionType::DumpParse)
opts |= SourceFile::ParsingFlags::DisableDelayedBodies;
}
if (forPrimary || isWholeModuleCompilation()) {
// Disable delayed body parsing for primaries and in WMO.
opts |= SourceFile::ParsingFlags::DisableDelayedBodies;
} else {
// Suppress parse warnings for non-primaries, as they'll get parsed multiple
// times.
opts |= SourceFile::ParsingFlags::SuppressWarnings;
}
// Enable interface hash computation for primaries, but not in WMO, as it's
// only currently needed for incremental mode.
if (forPrimary) {
opts |= SourceFile::ParsingFlags::EnableInterfaceHash;
}
return opts;
}
SourceFile *CompilerInstance::createSourceFileForMainModule(
ModuleDecl *mod, SourceFileKind fileKind,
Optional<unsigned> bufferID) const {
auto isPrimary = bufferID && isPrimaryInput(*bufferID);
auto opts = getSourceFileParsingOptions(isPrimary);
auto *inputFile = new (*Context)
SourceFile(*mod, fileKind, bufferID, opts, isPrimary);
if (bufferID == MainBufferID)
inputFile->SyntaxParsingCache = Invocation.getMainFileSyntaxParsingCache();
return inputFile;
}
void CompilerInstance::freeASTContext() {
TheSILTypes.reset();
Context.reset();
MainModule = nullptr;
SML = nullptr;
MemoryBufferLoader = nullptr;
PrimaryBufferIDs.clear();
}
/// Perform "stable" optimizations that are invariant across compiler versions.
static bool performMandatorySILPasses(CompilerInvocation &Invocation,
SILModule *SM) {
if (Invocation.getFrontendOptions().RequestedAction ==
FrontendOptions::ActionType::MergeModules) {
// Don't run diagnostic passes at all.
} else if (!Invocation.getDiagnosticOptions().SkipDiagnosticPasses) {
if (runSILDiagnosticPasses(*SM))
return true;
} else {
// Even if we are not supposed to run the diagnostic passes, we still need
// to run the ownership evaluator.
if (runSILOwnershipEliminatorPass(*SM))
return true;
}
if (Invocation.getSILOptions().MergePartialModules)
SM->linkAllFromCurrentModule();
return false;
}
/// Perform SIL optimization passes if optimizations haven't been disabled.
/// These may change across compiler versions.
static void performSILOptimizations(CompilerInvocation &Invocation,
SILModule *SM) {
FrontendStatsTracer tracer(SM->getASTContext().Stats,
"SIL optimization");
if (Invocation.getFrontendOptions().RequestedAction ==
FrontendOptions::ActionType::MergeModules ||
!Invocation.getSILOptions().shouldOptimize()) {
runSILPassesForOnone(*SM);
return;
}
StringRef CustomPipelinePath =
Invocation.getSILOptions().ExternalPassPipelineFilename;
if (!CustomPipelinePath.empty()) {
runSILOptimizationPassesWithFileSpecification(*SM, CustomPipelinePath);
} else {
runSILOptimizationPasses(*SM);
}
// When building SwiftOnoneSupport.o verify all expected ABI symbols.
if (Invocation.getFrontendOptions().CheckOnoneSupportCompleteness
// TODO: handle non-ObjC based stdlib builds, e.g. on linux.
&& Invocation.getLangOptions().EnableObjCInterop
&& Invocation.getFrontendOptions().RequestedAction
== FrontendOptions::ActionType::EmitObject) {
checkCompletenessOfPrespecializations(*SM);
}
}
static void countStatsPostSILOpt(UnifiedStatsReporter &Stats,
const SILModule& Module) {
auto &C = Stats.getFrontendCounters();
// FIXME: calculate these in constant time, via the dense maps.
C.NumSILOptFunctions += Module.getFunctionList().size();
C.NumSILOptVtables += Module.getVTables().size();
C.NumSILOptWitnessTables += Module.getWitnessTableList().size();
C.NumSILOptDefaultWitnessTables += Module.getDefaultWitnessTableList().size();
C.NumSILOptGlobalVariables += Module.getSILGlobalList().size();
}
bool CompilerInstance::performSILProcessing(SILModule *silModule) {
if (performMandatorySILPasses(Invocation, silModule))
return true;
{
FrontendStatsTracer tracer(silModule->getASTContext().Stats,
"SIL verification, pre-optimization");
silModule->verify();
}
performSILOptimizations(Invocation, silModule);
if (auto *stats = getStatsReporter())
countStatsPostSILOpt(*stats, *silModule);
{
FrontendStatsTracer tracer(silModule->getASTContext().Stats,
"SIL verification, post-optimization");
silModule->verify();
}
performSILInstCountIfNeeded(silModule);
return false;
}
const PrimarySpecificPaths &
CompilerInstance::getPrimarySpecificPathsForWholeModuleOptimizationMode()
const {
return getPrimarySpecificPathsForAtMostOnePrimary();
}
const PrimarySpecificPaths &
CompilerInstance::getPrimarySpecificPathsForAtMostOnePrimary() const {
return Invocation.getPrimarySpecificPathsForAtMostOnePrimary();
}
const PrimarySpecificPaths &
CompilerInstance::getPrimarySpecificPathsForPrimary(StringRef filename) const {
return Invocation.getPrimarySpecificPathsForPrimary(filename);
}
const PrimarySpecificPaths &
CompilerInstance::getPrimarySpecificPathsForSourceFile(
const SourceFile &SF) const {
return Invocation.getPrimarySpecificPathsForSourceFile(SF);
}
bool CompilerInstance::emitSwiftRanges(DiagnosticEngine &diags,
SourceFile *primaryFile,
StringRef outputPath) const {
return incremental_ranges::SwiftRangesEmitter(outputPath, primaryFile,
SourceMgr, diags)
.emit();
return false;
}
bool CompilerInstance::emitCompiledSource(DiagnosticEngine &diags,
const SourceFile *primaryFile,
StringRef outputPath) const {
return incremental_ranges::CompiledSourceEmitter(outputPath, primaryFile,
SourceMgr, diags)
.emit();
}