Files
swift-mirror/lib/FrontendTool/FrontendTool.cpp
2025-07-01 07:19:33 -07:00

2215 lines
87 KiB
C++

//===--- FrontendTool.cpp - Swift Compiler Frontend -----------------------===//
//
// 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 the entry point to the swift -frontend functionality, which
/// implements the core compiler functionality along with a number of additional
/// tools for demonstration and testing purposes.
///
/// This is separate from the rest of libFrontend to reduce the dependencies
/// required by that library.
///
//===----------------------------------------------------------------------===//
#include "swift/FrontendTool/FrontendTool.h"
#include "Dependencies.h"
#include "TBD.h"
#include "swift/AST/ASTDumper.h"
#include "swift/AST/ASTMangler.h"
#include "swift/AST/AvailabilityScope.h"
#include "swift/AST/DiagnosticConsumer.h"
#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/AST/DiagnosticsSema.h"
#include "swift/AST/FileSystem.h"
#include "swift/AST/FineGrainedDependencies.h"
#include "swift/AST/FineGrainedDependencyFormat.h"
#include "swift/AST/GenericSignature.h"
#include "swift/AST/IRGenOptions.h"
#include "swift/AST/IRGenRequests.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/TBDGenRequests.h"
#include "swift/Basic/Assertions.h"
#include "swift/Basic/Defer.h"
#include "swift/Basic/Edit.h"
#include "swift/Basic/FileSystem.h"
#include "swift/Basic/LLVMInitialize.h"
#include "swift/Basic/Platform.h"
#include "swift/Basic/PrettyStackTrace.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Basic/Statistic.h"
#include "swift/Basic/SupportedFeatures.h"
#include "swift/Basic/TargetInfo.h"
#include "swift/Basic/UUID.h"
#include "swift/Basic/Version.h"
#include "swift/ConstExtract/ConstExtract.h"
#include "swift/DependencyScan/ScanDependencies.h"
#include "swift/Frontend/CachedDiagnostics.h"
#include "swift/Frontend/CachingUtils.h"
#include "swift/Frontend/CompileJobCacheKey.h"
#include "swift/Frontend/DiagnosticHelper.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Frontend/MakeStyleDependencies.h"
#include "swift/Frontend/ModuleInterfaceLoader.h"
#include "swift/Frontend/ModuleInterfaceSupport.h"
#include "swift/IRGen/TBDGen.h"
#include "swift/Immediate/Immediate.h"
#include "swift/Index/IndexRecord.h"
#include "swift/Migrator/FixitFilter.h"
#include "swift/Migrator/Migrator.h"
#include "swift/Option/Options.h"
#include "swift/PrintAsClang/PrintAsClang.h"
#include "swift/SILOptimizer/PassManager/Passes.h"
#include "swift/Serialization/SerializationOptions.h"
#include "swift/Serialization/SerializedModuleLoader.h"
#include "swift/Subsystems.h"
#include "swift/SymbolGraphGen/SymbolGraphOptions.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Option/OptTable.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/VirtualOutputBackend.h"
#include "llvm/Support/VirtualOutputBackends.h"
#include "llvm/Support/raw_ostream.h"
#if __has_include(<unistd.h>)
#include <unistd.h>
#elif defined(_WIN32)
#include <process.h>
#endif
#include <algorithm>
#include <memory>
#include <unordered_set>
#include <utility>
using namespace swift;
static std::string displayName(StringRef MainExecutablePath) {
std::string Name = llvm::sys::path::stem(MainExecutablePath).str();
Name += " -frontend";
return Name;
}
static void emitMakeDependenciesIfNeeded(CompilerInstance &instance) {
instance.getInvocation()
.getFrontendOptions()
.InputsAndOutputs.forEachInputProducingSupplementaryOutput(
[&](const InputFile &f) -> bool {
return swift::emitMakeDependenciesIfNeeded(instance, f);
});
}
static void
emitLoadedModuleTraceForAllPrimariesIfNeeded(ModuleDecl *mainModule,
DependencyTracker *depTracker,
const FrontendOptions &opts) {
opts.InputsAndOutputs.forEachInputProducingSupplementaryOutput(
[&](const InputFile &input) -> bool {
return swift::emitLoadedModuleTraceIfNeeded(mainModule, depTracker,
opts, input);
});
}
/// Writes SIL out to the given file.
static bool writeSIL(SILModule &SM, ModuleDecl *M, const SILOptions &Opts,
StringRef OutputFilename,
llvm::vfs::OutputBackend &Backend) {
return withOutputPath(M->getDiags(), Backend, OutputFilename,
[&](raw_ostream &out) -> bool {
SM.print(out, M, Opts);
return M->getASTContext().hadError();
});
}
static bool writeSIL(SILModule &SM, const PrimarySpecificPaths &PSPs,
CompilerInstance &Instance,
const SILOptions &Opts) {
return writeSIL(SM, Instance.getMainModule(), Opts,
PSPs.OutputFilename, Instance.getOutputBackend());
}
/// Prints the Objective-C "generated header" interface for \p M to \p
/// outputPath.
/// Print the exposed "generated header" interface for \p M to \p
/// outputPath.
///
/// ...unless \p outputPath is empty, in which case it does nothing.
///
/// \returns true if there were any errors
///
/// \see swift::printAsClangHeader
static bool printAsClangHeaderIfNeeded(llvm::vfs::OutputBackend &outputBackend,
StringRef outputPath, ModuleDecl *M, StringRef bridgingHeader,
const FrontendOptions &frontendOpts, const IRGenOptions &irGenOpts,
clang::HeaderSearch &clangHeaderSearchInfo) {
if (outputPath.empty())
return false;
return withOutputPath(
M->getDiags(), outputBackend, outputPath, [&](raw_ostream &out) -> bool {
return printAsClangHeader(out, M, bridgingHeader, frontendOpts,
irGenOpts, clangHeaderSearchInfo);
});
}
/// Prints the stable module interface for \p M to \p outputPath.
///
/// ...unless \p outputPath is empty, in which case it does nothing.
///
/// \returns true if there were any errors
///
/// \see swift::emitSwiftInterface
static bool
printModuleInterfaceIfNeeded(llvm::vfs::OutputBackend &outputBackend,
StringRef outputPath,
ModuleInterfaceOptions const &Opts,
LangOptions const &LangOpts,
ModuleDecl *M) {
if (outputPath.empty())
return false;
DiagnosticEngine &diags = M->getDiags();
if (!LangOpts.isSwiftVersionAtLeast(5)) {
assert(LangOpts.isSwiftVersionAtLeast(4));
diags.diagnose(SourceLoc(),
diag::warn_unsupported_module_interface_swift_version,
LangOpts.isSwiftVersionAtLeast(4, 2) ? "4.2" : "4");
}
if (M->getResilienceStrategy() != ResilienceStrategy::Resilient) {
diags.diagnose(SourceLoc(),
diag::warn_unsupported_module_interface_library_evolution);
}
return withOutputPath(diags, outputBackend, outputPath,
[M, Opts](raw_ostream &out) -> bool {
return swift::emitSwiftInterface(out, Opts, M);
});
}
// This is a separate function so that it shows up in stack traces.
LLVM_ATTRIBUTE_NOINLINE
static void debugFailWithAssertion() {
// Per the user's request, this assertion should always fail in
// builds with assertions enabled.
// This should not be converted to llvm_unreachable, as those are
// treated as optimization hints in builds where they turn into
// __builtin_unreachable().
assert((0) && "This is an assertion!");
}
// This is a separate function so that it shows up in stack traces.
LLVM_ATTRIBUTE_NOINLINE
static void debugFailWithCrash() {
LLVM_BUILTIN_TRAP;
}
static void countStatsOfSourceFile(UnifiedStatsReporter &Stats,
const CompilerInstance &Instance,
SourceFile *SF) {
auto &C = Stats.getFrontendCounters();
auto &SM = Instance.getSourceMgr();
C.NumDecls += SF->getTopLevelDecls().size();
C.NumLocalTypeDecls += SF->getLocalTypeDecls().size();
C.NumObjCMethods += SF->ObjCMethods.size();
SmallVector<OperatorDecl *, 2> operators;
SF->getOperatorDecls(operators);
C.NumOperators += operators.size();
SmallVector<PrecedenceGroupDecl *, 2> groups;
SF->getPrecedenceGroups(groups);
C.NumPrecedenceGroups += groups.size();
C.NumSourceLines +=
SM.getEntireTextForBuffer(SF->getBufferID()).count('\n');
}
static void countASTStats(UnifiedStatsReporter &Stats,
CompilerInstance& Instance) {
auto &C = Stats.getFrontendCounters();
auto &SM = Instance.getSourceMgr();
C.NumSourceBuffers = SM.getLLVMSourceMgr().getNumBuffers();
C.NumLinkLibraries = Instance.getLinkLibraries().size();
auto const &AST = Instance.getASTContext();
C.NumLoadedModules = AST.getNumLoadedModules();
if (auto *D = Instance.getDependencyTracker()) {
C.NumDependencies = D->getDependencies().size();
C.NumIncrementalDependencies = D->getIncrementalDependencies().size();
C.NumMacroPluginDependencies = D->getMacroPluginDependencies().size();
}
for (auto SF : Instance.getPrimarySourceFiles()) {
auto &Ctx = SF->getASTContext();
Ctx.evaluator.enumerateReferencesInFile(SF, [&C](const auto &ref) {
using NodeKind = evaluator::DependencyCollector::Reference::Kind;
switch (ref.kind) {
case NodeKind::Empty:
case NodeKind::Tombstone:
llvm_unreachable("Cannot enumerate dead dependency!");
case NodeKind::TopLevel:
C.NumReferencedTopLevelNames += 1;
return;
case NodeKind::Dynamic:
C.NumReferencedDynamicNames += 1;
return;
case NodeKind::PotentialMember:
case NodeKind::UsedMember:
C.NumReferencedMemberNames += 1;
return;
}
});
}
if (!Instance.getPrimarySourceFiles().empty()) {
for (auto SF : Instance.getPrimarySourceFiles())
countStatsOfSourceFile(Stats, Instance, SF);
} else if (auto *M = Instance.getMainModule()) {
// No primary source file, but a main module; this is WMO-mode
for (auto *F : M->getFiles()) {
if (auto *SF = dyn_cast<SourceFile>(F)) {
countStatsOfSourceFile(Stats, Instance, SF);
}
}
}
}
static void countStatsPostSILGen(UnifiedStatsReporter &Stats,
const SILModule& Module) {
auto &C = Stats.getFrontendCounters();
// FIXME: calculate these in constant time, via the dense maps.
C.NumSILGenFunctions += Module.getFunctionList().size();
C.NumSILGenVtables += Module.getVTables().size();
C.NumSILGenWitnessTables += Module.getWitnessTableList().size();
C.NumSILGenDefaultWitnessTables += Module.getDefaultWitnessTableList().size();
C.NumSILGenGlobalVariables += Module.getSILGlobalList().size();
}
static bool precompileBridgingHeader(const CompilerInstance &Instance) {
const auto &Invocation = Instance.getInvocation();
const auto &opts = Invocation.getFrontendOptions();
auto clangImporter = static_cast<ClangImporter *>(
Instance.getASTContext().getClangModuleLoader());
auto &ImporterOpts = Invocation.getClangImporterOptions();
auto &PCHOutDir = ImporterOpts.PrecompiledHeaderOutputDir;
auto OutputBackend = Instance.getOutputBackend().clone();
if (!PCHOutDir.empty()) {
// Create or validate a persistent PCH.
auto SwiftPCHHash = Invocation.getPCHHash();
auto PCH = clangImporter->getOrCreatePCH(ImporterOpts, SwiftPCHHash,
/*cached=*/false);
return !PCH.has_value();
}
return clangImporter->emitBridgingPCH(
opts.InputsAndOutputs.getFilenameOfFirstInput(),
opts.InputsAndOutputs.getSingleOutputFilename(), /*cached=*/false);
}
static bool precompileClangModule(const CompilerInstance &Instance) {
const auto &opts = Instance.getInvocation().getFrontendOptions();
auto clangImporter = static_cast<ClangImporter *>(
Instance.getASTContext().getClangModuleLoader());
return clangImporter->emitPrecompiledModule(
opts.InputsAndOutputs.getFilenameOfFirstInput(), opts.ModuleName,
opts.InputsAndOutputs.getSingleOutputFilename());
}
static bool dumpPrecompiledClangModule(const CompilerInstance &Instance) {
const auto &opts = Instance.getInvocation().getFrontendOptions();
auto clangImporter = static_cast<ClangImporter *>(
Instance.getASTContext().getClangModuleLoader());
return clangImporter->dumpPrecompiledModule(
opts.InputsAndOutputs.getFilenameOfFirstInput(),
opts.InputsAndOutputs.getSingleOutputFilename());
}
static bool buildModuleFromInterface(CompilerInstance &Instance) {
const auto &Invocation = Instance.getInvocation();
const FrontendOptions &FEOpts = Invocation.getFrontendOptions();
assert(FEOpts.InputsAndOutputs.hasSingleInput());
StringRef InputPath = FEOpts.InputsAndOutputs.getFilenameOfFirstInput();
StringRef PrebuiltCachePath = FEOpts.PrebuiltModuleCachePath;
ModuleInterfaceLoaderOptions LoaderOpts(FEOpts);
StringRef ABIPath = Instance.getPrimarySpecificPathsForAtMostOnePrimary()
.SupplementaryOutputs.ABIDescriptorOutputPath;
bool IgnoreAdjacentModules = Instance.hasASTContext() &&
Instance.getASTContext().IgnoreAdjacentModules;
// When building explicit module dependencies, they are
// discovered by dependency scanner and the swiftmodule is already rebuilt
// ignoring candidate module. There is no need to serialized dependencies for
// validation purpose because the build system (swift-driver) is then
// responsible for checking whether inputs are up-to-date.
bool ShouldSerializeDeps = !FEOpts.ExplicitInterfaceBuild;
// If an explicit interface build was requested, bypass the creation of a new
// sub-instance from the interface which will build it in a separate thread,
// and isntead directly use the current \c Instance for compilation.
//
// FIXME: -typecheck-module-from-interface is the exception here because
// currently we need to ensure it still reads the flags written out
// in the .swiftinterface file itself. Instead, creation of that
// job should incorporate those flags.
if (FEOpts.ExplicitInterfaceBuild && !(FEOpts.isTypeCheckAction()))
return ModuleInterfaceLoader::buildExplicitSwiftModuleFromSwiftInterface(
Instance, Invocation.getClangModuleCachePath(),
FEOpts.BackupModuleInterfaceDir, PrebuiltCachePath, ABIPath, InputPath,
Invocation.getOutputFilename(), ShouldSerializeDeps,
Invocation.getSearchPathOptions().CandidateCompiledModules);
return ModuleInterfaceLoader::buildSwiftModuleFromSwiftInterface(
Instance.getSourceMgr(), Instance.getDiags(),
Invocation.getSearchPathOptions(), Invocation.getLangOptions(),
Invocation.getClangImporterOptions(), Invocation.getCASOptions(),
Invocation.getClangModuleCachePath(), PrebuiltCachePath,
FEOpts.BackupModuleInterfaceDir, Invocation.getModuleName(), InputPath,
Invocation.getOutputFilename(), ABIPath,
FEOpts.SerializeModuleInterfaceDependencyHashes,
FEOpts.shouldTrackSystemDependencies(), LoaderOpts,
RequireOSSAModules_t(Invocation.getSILOptions()),
IgnoreAdjacentModules);
}
static bool compileLLVMIR(CompilerInstance &Instance) {
const auto &Invocation = Instance.getInvocation();
const auto &inputsAndOutputs =
Invocation.getFrontendOptions().InputsAndOutputs;
// Load in bitcode file.
assert(inputsAndOutputs.hasSingleInput() &&
"We expect a single input for bitcode input!");
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
swift::vfs::getFileOrSTDIN(Instance.getFileSystem(),
inputsAndOutputs.getFilenameOfFirstInput());
if (!FileBufOrErr) {
Instance.getDiags().diagnose(SourceLoc(), diag::error_open_input_file,
inputsAndOutputs.getFilenameOfFirstInput(),
FileBufOrErr.getError().message());
return true;
}
llvm::MemoryBuffer *MainFile = FileBufOrErr.get().get();
llvm::SMDiagnostic Err;
auto LLVMContext = std::make_unique<llvm::LLVMContext>();
std::unique_ptr<llvm::Module> Module =
llvm::parseIR(MainFile->getMemBufferRef(), Err, *LLVMContext.get());
if (!Module) {
// TODO: Translate from the diagnostic info to the SourceManager location
// if available.
Instance.getDiags().diagnose(SourceLoc(), diag::error_parse_input_file,
inputsAndOutputs.getFilenameOfFirstInput(),
Err.getMessage());
return true;
}
return performLLVM(Invocation.getIRGenOptions(), Instance.getASTContext(),
Module.get(), inputsAndOutputs.getSingleOutputFilename());
}
static void verifyGenericSignaturesIfNeeded(const FrontendOptions &opts,
ASTContext &Context) {
auto verifyGenericSignaturesInModule = opts.VerifyGenericSignaturesInModule;
if (verifyGenericSignaturesInModule.empty())
return;
if (auto module = Context.getModuleByName(verifyGenericSignaturesInModule))
swift::validateGenericSignaturesInModule(module);
}
static bool dumpAndPrintScopeMap(const CompilerInstance &Instance,
SourceFile &SF) {
// Not const because may require reexpansion
ASTScope &scope = SF.getScope();
const auto &opts = Instance.getInvocation().getFrontendOptions();
if (opts.DumpScopeMapLocations.empty()) {
llvm::errs() << "***Complete scope map***\n";
scope.buildFullyExpandedTree();
scope.print(llvm::errs());
return Instance.getASTContext().hadError();
}
// Probe each of the locations, and dump what we find.
for (auto lineColumn : opts.DumpScopeMapLocations) {
scope.buildFullyExpandedTree();
scope.dumpOneScopeMapLocation(lineColumn);
}
return Instance.getASTContext().hadError();
}
static SourceFile &
getPrimaryOrMainSourceFile(const CompilerInstance &Instance) {
if (SourceFile *SF = Instance.getPrimarySourceFile()) {
return *SF;
}
return Instance.getMainModule()->getMainSourceFile();
}
/// Dumps the AST of all available primary source files. If corresponding output
/// files were specified, use them; otherwise, dump the AST to stdout.
static bool dumpAST(CompilerInstance &Instance,
ASTDumpMemberLoading memberLoading) {
const FrontendOptions &opts = Instance.getInvocation().getFrontendOptions();
auto dumpAST = [&](SourceFile *SF, llvm::raw_ostream &out) {
switch (opts.DumpASTFormat) {
case FrontendOptions::ASTFormat::Default:
SF->dump(out, memberLoading);
break;
case FrontendOptions::ASTFormat::DefaultWithDeclContext:
swift::dumpDeclContextHierarchy(out, *SF);
SF->dump(out, memberLoading);
break;
case FrontendOptions::ASTFormat::JSON:
SF->dumpJSON(out, memberLoading);
break;
case FrontendOptions::ASTFormat::JSONZlib:
std::string jsonText;
llvm::raw_string_ostream jsonTextStream(jsonText);
SF->dumpJSON(jsonTextStream, memberLoading);
SmallVector<uint8_t, 0> compressed;
llvm::compression::zlib::compress(llvm::arrayRefFromStringRef(jsonText),
compressed);
out << llvm::toStringRef(compressed);
break;
}
};
auto primaryFiles = Instance.getPrimarySourceFiles();
if (!primaryFiles.empty()) {
for (SourceFile *sourceFile: primaryFiles) {
auto PSPs = Instance.getPrimarySpecificPathsForSourceFile(*sourceFile);
auto OutputFilename = PSPs.OutputFilename;
if (withOutputPath(Instance.getASTContext().Diags,
Instance.getOutputBackend(), OutputFilename,
[&](llvm::raw_ostream &out) -> bool {
dumpAST(sourceFile, out);
return false;
}))
return true;
}
} else {
// Some invocations don't have primary files. In that case, we default to
// looking for the main file and dumping it to `stdout`.
auto &SF = getPrimaryOrMainSourceFile(Instance);
dumpAST(&SF, llvm::outs());
}
return Instance.getASTContext().hadError();
}
static bool emitReferenceDependencies(CompilerInstance &Instance,
SourceFile *const SF,
StringRef outputPath) {
const auto alsoEmitDotFile = Instance.getInvocation()
.getLangOptions()
.EmitFineGrainedDependencySourcefileDotFiles;
#ifndef NDEBUG
// Before writing to the dependencies file path, preserve any previous file
// that may have been there. No error handling -- this is just a nicety, it
// doesn't matter if it fails.
llvm::sys::fs::rename(outputPath, outputPath + "~");
#endif
using SourceFileDepGraph = fine_grained_dependencies::SourceFileDepGraph;
return fine_grained_dependencies::withReferenceDependencies(
SF, *Instance.getDependencyTracker(), Instance.getOutputBackend(),
outputPath, alsoEmitDotFile, [&](SourceFileDepGraph &&g) -> bool {
const bool hadError =
fine_grained_dependencies::writeFineGrainedDependencyGraphToPath(
Instance.getDiags(), Instance.getOutputBackend(), outputPath,
g);
// If path is stdout, cannot read it back, so check for "-"
DEBUG_ASSERT(outputPath == "-" || g.verifyReadsWhatIsWritten(outputPath));
if (alsoEmitDotFile)
g.emitDotFile(Instance.getOutputBackend(), outputPath,
Instance.getDiags());
return hadError;
});
}
static void emitSwiftdepsForAllPrimaryInputsIfNeeded(
CompilerInstance &Instance) {
const auto &Invocation = Instance.getInvocation();
if (Invocation.getFrontendOptions()
.InputsAndOutputs.hasReferenceDependenciesFilePath() &&
Instance.getPrimarySourceFiles().empty()) {
Instance.getDiags().diagnose(
SourceLoc(), diag::emit_reference_dependencies_without_primary_file);
return;
}
// Do not write out swiftdeps for any primaries if we've encountered an
// error. Without this, the driver will attempt to integrate swiftdeps
// from broken swift files. One way this could go wrong is if a primary that
// fails to build in an early wave has dependents in a later wave. The
// driver will not schedule those later dependents after this batch exits,
// so they will have no opportunity to bring their swiftdeps files up to
// date. With this early exit, the driver sees the same priors in the
// swiftdeps files from before errors were introduced into the batch, and
// integration therefore always hops from "known good" to "known good" states.
//
// FIXME: It seems more appropriate for the driver to notice the early-exit
// and react by always enqueuing the jobs it dropped in the other waves.
//
// We will output a module if allowing errors, so ignore that case.
if (Instance.getDiags().hadAnyError() &&
!Invocation.getFrontendOptions().AllowModuleWithCompilerErrors)
return;
for (auto *SF : Instance.getPrimarySourceFiles()) {
const std::string &referenceDependenciesFilePath =
Invocation.getReferenceDependenciesFilePathForPrimary(
SF->getFilename());
if (referenceDependenciesFilePath.empty()) {
continue;
}
emitReferenceDependencies(Instance, SF, referenceDependenciesFilePath);
}
}
static bool emitConstValuesForWholeModuleIfNeeded(
CompilerInstance &Instance) {
const auto &Invocation = Instance.getInvocation();
const auto &frontendOpts = Invocation.getFrontendOptions();
auto constExtractProtocolListPath =
Instance.getASTContext().SearchPathOpts.ConstGatherProtocolListFilePath;
if (constExtractProtocolListPath.empty())
return false;
if (!frontendOpts.InputsAndOutputs.hasConstValuesOutputPath())
return false;
assert(frontendOpts.InputsAndOutputs.isWholeModule() &&
"'emitConstValuesForWholeModule' only makes sense when the whole module can be seen");
auto ConstValuesFilePath = frontendOpts.InputsAndOutputs
.getPrimarySpecificPathsForAtMostOnePrimary().SupplementaryOutputs
.ConstValuesOutputPath;
// List of protocols whose conforming nominal types
// we should extract compile-time-known values from
std::unordered_set<std::string> Protocols;
bool inputParseSuccess = parseProtocolListFromFile(constExtractProtocolListPath,
Instance.getDiags(), Protocols);
if (!inputParseSuccess)
return true;
auto ConstValues = gatherConstValuesForModule(Protocols,
Instance.getMainModule());
return withOutputPath(Instance.getDiags(), Instance.getOutputBackend(),
ConstValuesFilePath, [&](llvm::raw_ostream &OS) {
writeAsJSONToFile(ConstValues, OS);
return false;
});
}
static void emitConstValuesForAllPrimaryInputsIfNeeded(
CompilerInstance &Instance) {
const auto &Invocation = Instance.getInvocation();
auto constExtractProtocolListPath =
Instance.getASTContext().SearchPathOpts.ConstGatherProtocolListFilePath;
if (constExtractProtocolListPath.empty())
return;
// List of protocols whose conforming nominal types
// we should extract compile-time-known values from
std::unordered_set<std::string> Protocols;
bool inputParseSuccess = parseProtocolListFromFile(constExtractProtocolListPath,
Instance.getDiags(), Protocols);
if (!inputParseSuccess)
return;
for (auto *SF : Instance.getPrimarySourceFiles()) {
const std::string &ConstValuesFilePath =
Invocation.getConstValuesFilePathForPrimary(
SF->getFilename());
if (ConstValuesFilePath.empty())
continue;
auto ConstValues = gatherConstValuesForPrimary(Protocols, SF);
withOutputPath(Instance.getDiags(), Instance.getOutputBackend(),
ConstValuesFilePath, [&](llvm::raw_ostream &OS) {
writeAsJSONToFile(ConstValues, OS);
return false;
});
}
}
static bool writeModuleSemanticInfoIfNeeded(CompilerInstance &Instance) {
const auto &Invocation = Instance.getInvocation();
const auto &frontendOpts = Invocation.getFrontendOptions();
if (!frontendOpts.InputsAndOutputs.hasModuleSemanticInfoOutputPath())
return false;
std::error_code EC;
assert(frontendOpts.InputsAndOutputs.isWholeModule() &&
"TBDPath only makes sense when the whole module can be seen");
auto ModuleSemanticPath = frontendOpts.InputsAndOutputs
.getPrimarySpecificPathsForAtMostOnePrimary().SupplementaryOutputs
.ModuleSemanticInfoOutputPath;
return withOutputPath(Instance.getDiags(), Instance.getOutputBackend(),
ModuleSemanticPath, [&](llvm::raw_ostream &OS) {
OS << "{}\n";
return false;
});
}
static bool writeTBDIfNeeded(CompilerInstance &Instance) {
const auto &Invocation = Instance.getInvocation();
const auto &frontendOpts = Invocation.getFrontendOptions();
const auto &tbdOpts = Invocation.getTBDGenOptions();
if (!frontendOpts.InputsAndOutputs.hasTBDPath())
return false;
if (!frontendOpts.InputsAndOutputs.isWholeModule()) {
Instance.getDiags().diagnose(SourceLoc(),
diag::tbd_only_supported_in_whole_module);
return false;
}
if (Invocation.getSILOptions().CMOMode ==
CrossModuleOptimizationMode::Aggressive) {
Instance.getDiags().diagnose(SourceLoc(),
diag::tbd_not_supported_with_cmo);
return false;
}
const std::string &TBDPath = Invocation.getTBDPathForWholeModule();
return writeTBD(Instance.getMainModule(), TBDPath,
Instance.getOutputBackend(), tbdOpts);
}
static bool writeAPIDescriptor(ModuleDecl *M, StringRef OutputPath,
llvm::vfs::OutputBackend &Backend) {
return withOutputPath(M->getDiags(), Backend, OutputPath,
[&](raw_ostream &OS) -> bool {
writeAPIJSONFile(M, OS, /*PrettyPrinted=*/false);
return false;
});
}
static bool writeAPIDescriptorIfNeeded(CompilerInstance &Instance) {
const auto &Invocation = Instance.getInvocation();
const auto &frontendOpts = Invocation.getFrontendOptions();
if (!frontendOpts.InputsAndOutputs.hasAPIDescriptorOutputPath())
return false;
if (!frontendOpts.InputsAndOutputs.isWholeModule()) {
Instance.getDiags().diagnose(
SourceLoc(), diag::api_descriptor_only_supported_in_whole_module);
return false;
}
const std::string &APIDescriptorPath =
Invocation.getAPIDescriptorPathForWholeModule();
return writeAPIDescriptor(Instance.getMainModule(), APIDescriptorPath,
Instance.getOutputBackend());
}
static bool performCompileStepsPostSILGen(CompilerInstance &Instance,
std::unique_ptr<SILModule> SM,
ModuleOrSourceFile MSF,
const PrimarySpecificPaths &PSPs,
int &ReturnValue,
FrontendObserver *observer);
bool swift::performCompileStepsPostSema(CompilerInstance &Instance,
int &ReturnValue,
FrontendObserver *observer) {
const auto &Invocation = Instance.getInvocation();
const FrontendOptions &opts = Invocation.getFrontendOptions();
auto getSILOptions = [&](const PrimarySpecificPaths &PSPs,
const std::vector<PrimarySpecificPaths> &auxPSPs) -> SILOptions {
SILOptions SILOpts = Invocation.getSILOptions();
if (SILOpts.OptRecordFile.empty()) {
// Check if the record file path was passed via supplemental outputs.
SILOpts.OptRecordFile = SILOpts.OptRecordFormat ==
llvm::remarks::Format::YAML ?
PSPs.SupplementaryOutputs.YAMLOptRecordPath :
PSPs.SupplementaryOutputs.BitstreamOptRecordPath;
}
if (!auxPSPs.empty()) {
assert(SILOpts.AuxOptRecordFiles.empty());
for (const auto &auxFile: auxPSPs) {
SILOpts.AuxOptRecordFiles.push_back(
SILOpts.OptRecordFormat == llvm::remarks::Format::YAML ?
auxFile.SupplementaryOutputs.YAMLOptRecordPath :
auxFile.SupplementaryOutputs.BitstreamOptRecordPath);
}
}
return SILOpts;
};
auto *mod = Instance.getMainModule();
if (!opts.InputsAndOutputs.hasPrimaryInputs()) {
// If there are no primary inputs the compiler is in WMO mode and builds one
// SILModule for the entire module.
const PrimarySpecificPaths PSPs =
Instance.getPrimarySpecificPathsForWholeModuleOptimizationMode();
std::vector<PrimarySpecificPaths> auxPSPs;
for (unsigned i = 1; i < opts.InputsAndOutputs.inputCount(); ++i) {
auto &auxPSP =
opts.InputsAndOutputs.getPrimarySpecificPathsForRemaining(i);
auxPSPs.push_back(auxPSP);
}
SILOptions SILOpts = getSILOptions(PSPs, auxPSPs);
IRGenOptions irgenOpts = Invocation.getIRGenOptions();
auto SM = performASTLowering(mod, Instance.getSILTypes(), SILOpts,
&irgenOpts);
return performCompileStepsPostSILGen(Instance, std::move(SM), mod, PSPs,
ReturnValue, observer);
}
std::vector<PrimarySpecificPaths> emptyAuxPSPs;
// If there are primary source files, build a separate SILModule for
// each source file, and run the remaining SILOpt-Serialize-IRGen-LLVM
// once for each such input.
if (!Instance.getPrimarySourceFiles().empty()) {
bool result = false;
for (auto *PrimaryFile : Instance.getPrimarySourceFiles()) {
const PrimarySpecificPaths PSPs =
Instance.getPrimarySpecificPathsForSourceFile(*PrimaryFile);
SILOptions SILOpts = getSILOptions(PSPs, emptyAuxPSPs);
IRGenOptions irgenOpts = Invocation.getIRGenOptions();
auto SM = performASTLowering(*PrimaryFile, Instance.getSILTypes(),
SILOpts, &irgenOpts);
result |= performCompileStepsPostSILGen(Instance, std::move(SM),
PrimaryFile, PSPs, ReturnValue,
observer);
}
return result;
}
// If there are primary inputs but no primary _source files_, there might be
// a primary serialized input.
bool result = false;
for (FileUnit *fileUnit : mod->getFiles()) {
if (auto SASTF = dyn_cast<SerializedASTFile>(fileUnit))
if (opts.InputsAndOutputs.isInputPrimary(SASTF->getFilename())) {
const PrimarySpecificPaths &PSPs =
Instance.getPrimarySpecificPathsForPrimary(SASTF->getFilename());
SILOptions SILOpts = getSILOptions(PSPs, emptyAuxPSPs);
auto SM = performASTLowering(*SASTF, Instance.getSILTypes(), SILOpts);
result |= performCompileStepsPostSILGen(Instance, std::move(SM), mod,
PSPs, ReturnValue, observer);
}
}
return result;
}
static void emitIndexDataForSourceFile(SourceFile *PrimarySourceFile,
const CompilerInstance &Instance);
/// Emits index data for all primary inputs, or the main module.
static void emitIndexData(const CompilerInstance &Instance) {
if (Instance.getPrimarySourceFiles().empty()) {
emitIndexDataForSourceFile(nullptr, Instance);
} else {
for (SourceFile *SF : Instance.getPrimarySourceFiles())
emitIndexDataForSourceFile(SF, Instance);
}
}
/// Emits all "one-per-module" supplementary outputs that don't depend on
/// anything past type-checking.
static bool emitAnyWholeModulePostTypeCheckSupplementaryOutputs(
CompilerInstance &Instance) {
const auto &Context = Instance.getASTContext();
const auto &Invocation = Instance.getInvocation();
const FrontendOptions &opts = Invocation.getFrontendOptions();
// FIXME: Whole-module outputs with a non-whole-module action ought to
// be disallowed, but the driver implements -index-file mode by generating a
// regular whole-module frontend command line and modifying it to index just
// one file (by making it a primary) instead of all of them. If that
// invocation also has flags to emit whole-module supplementary outputs, the
// compiler can crash trying to access information for non-type-checked
// declarations in the non-primary files. For now, prevent those crashes by
// guarding the emission of whole-module supplementary outputs.
if (!opts.InputsAndOutputs.isWholeModule())
return false;
// Record whether we failed to emit any of these outputs, but keep going; one
// failure does not mean skipping the rest.
bool hadAnyError = false;
if ((!Context.hadError() || opts.AllowModuleWithCompilerErrors) &&
opts.InputsAndOutputs.hasClangHeaderOutputPath()) {
std::string BridgingHeaderPathForPrint = Instance.getBridgingHeaderPath();
if (!BridgingHeaderPathForPrint.empty()) {
if (opts.BridgingHeaderDirForPrint.has_value()) {
// User specified preferred directory for including, use that dir.
llvm::SmallString<32> Buffer(*opts.BridgingHeaderDirForPrint);
llvm::sys::path::append(Buffer,
llvm::sys::path::filename(BridgingHeaderPathForPrint));
BridgingHeaderPathForPrint = (std::string)Buffer;
}
}
hadAnyError |= printAsClangHeaderIfNeeded(
Instance.getOutputBackend(),
Invocation.getClangHeaderOutputPathForAtMostOnePrimary(),
Instance.getMainModule(), BridgingHeaderPathForPrint, opts,
Invocation.getIRGenOptions(),
Context.getClangModuleLoader()
->getClangPreprocessor()
.getHeaderSearchInfo());
}
// Only want the header if there's been any errors, ie. there's not much
// point outputting a swiftinterface for an invalid module
if (Context.hadError())
return hadAnyError;
if (opts.InputsAndOutputs.hasModuleInterfaceOutputPath()) {
hadAnyError |= printModuleInterfaceIfNeeded(
Instance.getOutputBackend(),
Invocation.getModuleInterfaceOutputPathForWholeModule(),
Invocation.getModuleInterfaceOptions(),
Invocation.getLangOptions(),
Instance.getMainModule());
}
if (opts.InputsAndOutputs.hasPrivateModuleInterfaceOutputPath()) {
// Copy the settings from the module interface to add SPI printing.
ModuleInterfaceOptions privOpts = Invocation.getModuleInterfaceOptions();
privOpts.setInterfaceMode(PrintOptions::InterfaceMode::Private);
hadAnyError |= printModuleInterfaceIfNeeded(
Instance.getOutputBackend(),
Invocation.getPrivateModuleInterfaceOutputPathForWholeModule(),
privOpts,
Invocation.getLangOptions(),
Instance.getMainModule());
}
if (opts.InputsAndOutputs.hasPackageModuleInterfaceOutputPath()) {
// Copy the settings from the module interface to add package decl printing.
ModuleInterfaceOptions pkgOpts = Invocation.getModuleInterfaceOptions();
pkgOpts.setInterfaceMode(PrintOptions::InterfaceMode::Package);
hadAnyError |= printModuleInterfaceIfNeeded(
Instance.getOutputBackend(),
Invocation.getPackageModuleInterfaceOutputPathForWholeModule(),
pkgOpts,
Invocation.getLangOptions(),
Instance.getMainModule());
}
{
hadAnyError |= writeTBDIfNeeded(Instance);
}
{
hadAnyError |= writeAPIDescriptorIfNeeded(Instance);
}
{
hadAnyError |= writeModuleSemanticInfoIfNeeded(Instance);
}
{
hadAnyError |= emitConstValuesForWholeModuleIfNeeded(Instance);
}
return hadAnyError;
}
static void dumpAPIIfNeeded(const CompilerInstance &Instance) {
using namespace llvm::sys;
const auto &Invocation = Instance.getInvocation();
StringRef OutDir = Invocation.getFrontendOptions().DumpAPIPath;
if (OutDir.empty())
return;
auto getOutPath = [&](SourceFile *SF) -> std::string {
SmallString<256> Path = OutDir;
StringRef Filename = SF->getFilename();
path::append(Path, path::filename(Filename));
return std::string(Path.str());
};
std::unordered_set<std::string> Filenames;
auto dumpFile = [&](SourceFile *SF) -> bool {
SmallString<512> TempBuf;
llvm::raw_svector_ostream TempOS(TempBuf);
PrintOptions PO = PrintOptions::printInterface(
Invocation.getFrontendOptions().PrintFullConvention);
PO.PrintOriginalSourceText = true;
PO.Indent = 2;
PO.PrintAccess = false;
PO.SkipUnderscoredSystemProtocols = true;
SF->print(TempOS, PO);
if (TempOS.str().trim().empty())
return false; // nothing to show.
std::string OutPath = getOutPath(SF);
bool WasInserted = Filenames.insert(OutPath).second;
if (!WasInserted) {
llvm::errs() << "multiple source files ended up with the same dump API "
"filename to write to: " << OutPath << '\n';
return true;
}
std::error_code EC;
llvm::raw_fd_ostream OS(OutPath, EC, fs::FA_Read | fs::FA_Write);
if (EC) {
llvm::errs() << "error opening file '" << OutPath << "': "
<< EC.message() << '\n';
return true;
}
OS << TempOS.str();
return false;
};
std::error_code EC = fs::create_directories(OutDir);
if (EC) {
llvm::errs() << "error creating directory '" << OutDir << "': "
<< EC.message() << '\n';
return;
}
for (auto *FU : Instance.getMainModule()->getFiles()) {
if (auto *SF = dyn_cast<SourceFile>(FU))
if (dumpFile(SF))
return;
}
}
static bool shouldEmitIndexData(const CompilerInvocation &Invocation) {
const auto &opts = Invocation.getFrontendOptions();
auto action = opts.RequestedAction;
if (action == FrontendOptions::ActionType::CompileModuleFromInterface &&
opts.ExplicitInterfaceBuild) {
return true;
}
// FIXME: This predicate matches the status quo, but there's no reason
// indexing cannot run for actions that do not require stdlib e.g. to better
// facilitate tests.
return FrontendOptions::doesActionRequireSwiftStandardLibrary(action);
}
/// Perform any actions that must have access to the ASTContext, and need to be
/// delayed until the Swift compile pipeline has finished. This may be called
/// before or after LLVM depending on when the ASTContext gets freed.
static void performEndOfPipelineActions(CompilerInstance &Instance) {
assert(Instance.hasASTContext());
auto &ctx = Instance.getASTContext();
const auto &Invocation = Instance.getInvocation();
const auto &opts = Invocation.getFrontendOptions();
// If we were asked to print Clang stats, do so.
if (opts.PrintClangStats && ctx.getClangModuleLoader())
ctx.getClangModuleLoader()->printStatistics();
// Report AST stats if needed.
if (auto *stats = ctx.Stats)
countASTStats(*stats, Instance);
if (opts.DumpClangLookupTables && ctx.getClangModuleLoader())
ctx.getClangModuleLoader()->dumpSwiftLookupTables();
// Report mangling stats if there was no error.
if (!ctx.hadError())
Mangle::printManglingStats();
// Make sure we didn't load a module during a parse-only invocation, unless
// it's -emit-imported-modules, which can load modules.
auto action = opts.RequestedAction;
if (FrontendOptions::shouldActionOnlyParse(action) &&
!ctx.getLoadedModules().empty() &&
action != FrontendOptions::ActionType::EmitImportedModules) {
assert(ctx.getNumLoadedModules() == 1 &&
"Loaded a module during parse-only");
assert(ctx.getLoadedModules().begin()->second == Instance.getMainModule());
}
if (!opts.AllowModuleWithCompilerErrors) {
// Verify the AST for all the modules we've loaded.
ctx.verifyAllLoadedModules();
// Verify generic signatures if we've been asked to.
verifyGenericSignaturesIfNeeded(Invocation.getFrontendOptions(), ctx);
}
// Emit any additional outputs that we only need for a successful compilation.
// We don't want to unnecessarily delay getting any errors back to the user.
if (!ctx.hadError()) {
emitLoadedModuleTraceForAllPrimariesIfNeeded(
Instance.getMainModule(), Instance.getDependencyTracker(), opts);
dumpAPIIfNeeded(Instance);
swift::emitFineModuleTraceIfNeeded(Instance, opts);
}
// Contains the hadError checks internally, we still want to output the
// Objective-C header when there's errors and currently allowing them
emitAnyWholeModulePostTypeCheckSupplementaryOutputs(Instance);
// Verify reference dependencies of the current compilation job. Note this
// must be run *before* verifying diagnostics so that the former can be tested
// via the latter.
if (opts.EnableIncrementalDependencyVerifier) {
if (!Instance.getPrimarySourceFiles().empty()) {
swift::verifyDependencies(Instance.getSourceMgr(),
Instance.getPrimarySourceFiles());
} else {
swift::verifyDependencies(Instance.getSourceMgr(),
Instance.getMainModule()->getFiles());
}
}
if (Invocation.getLangOptions()
.EnableExperimentalEagerClangModuleDiagnostics) {
// A consumer meant to import all visible declarations.
class EagerConsumer : public VisibleDeclConsumer {
public:
virtual void
foundDecl(ValueDecl *VD, DeclVisibilityKind Reason,
DynamicLookupInfo dynamicLookupInfo = {}) override {
if (auto *IDC = dyn_cast<IterableDeclContext>(VD)) {
(void)IDC->getMembers();
}
}
};
EagerConsumer consumer;
for (auto module : ctx.getLoadedModules()) {
// None of the passed parameter have an effect, we just need to trigger
// imports.
module.second->lookupVisibleDecls(/*Access Path*/ {}, consumer,
NLKind::QualifiedLookup);
}
}
if (shouldEmitIndexData(Invocation)) {
emitIndexData(Instance);
}
// Emit Swiftdeps for every file in the batch.
emitSwiftdepsForAllPrimaryInputsIfNeeded(Instance);
// Emit Make-style dependencies.
emitMakeDependenciesIfNeeded(Instance);
// Emit extracted constant values for every file in the batch
emitConstValuesForAllPrimaryInputsIfNeeded(Instance);
}
static bool printSwiftVersion(const CompilerInvocation &Invocation) {
llvm::outs() << version::getSwiftFullVersion(
version::Version::getCurrentLanguageVersion())
<< '\n';
llvm::outs() << "Target: " << Invocation.getLangOptions().Target.str()
<< '\n';
if (!llvm::cl::getCompilerBuildConfig().empty())
llvm::cl::printBuildConfig(llvm::outs());
return false;
}
static void printSingleFrontendOpt(llvm::opt::OptTable &table, options::ID id,
llvm::raw_ostream &OS) {
if (table.getOption(id).hasFlag(options::FrontendOption) ||
table.getOption(id).hasFlag(options::AutolinkExtractOption) ||
table.getOption(id).hasFlag(options::ModuleWrapOption) ||
table.getOption(id).hasFlag(options::SwiftSymbolGraphExtractOption) ||
table.getOption(id).hasFlag(options::SwiftSynthesizeInterfaceOption) ||
table.getOption(id).hasFlag(options::SwiftAPIDigesterOption)) {
auto name = StringRef(table.getOptionName(id));
if (!name.empty()) {
OS << " \"" << name << "\",\n";
}
}
}
static bool printSwiftArguments(CompilerInstance &instance) {
ASTContext &context = instance.getASTContext();
const CompilerInvocation &invocation = instance.getInvocation();
const FrontendOptions &opts = invocation.getFrontendOptions();
std::string path = opts.InputsAndOutputs.getSingleOutputFilename();
std::error_code EC;
llvm::raw_fd_ostream out(path, EC, llvm::sys::fs::OF_None);
if (out.has_error() || EC) {
context.Diags.diagnose(SourceLoc(), diag::error_opening_output, path,
EC.message());
out.clear_error();
return true;
}
std::unique_ptr<llvm::opt::OptTable> table = createSwiftOptTable();
out << "{\n";
SWIFT_DEFER {
out << "}\n";
};
out << " \"SupportedArguments\": [\n";
#define OPTION(...) \
printSingleFrontendOpt(*table, \
swift::options::LLVM_MAKE_OPT_ID(__VA_ARGS__), out);
#include "swift/Option/Options.inc"
#undef OPTION
out << " \"LastOption\"\n";
out << " ],\n";
out << " \"SupportedFeatures\": [\n";
// Print supported feature names here.
out << " \"LastFeature\"\n";
out << " ]\n";
return false;
}
static bool
withSemanticAnalysis(CompilerInstance &Instance, FrontendObserver *observer,
llvm::function_ref<bool(CompilerInstance &)> cont,
bool runDespiteErrors = false) {
const auto &Invocation = Instance.getInvocation();
const auto &opts = Invocation.getFrontendOptions();
assert(!FrontendOptions::shouldActionOnlyParse(opts.RequestedAction) &&
"Action may only parse, but has requested semantic analysis!");
Instance.performSema();
if (observer)
observer->performedSemanticAnalysis(Instance);
switch (opts.CrashMode) {
case FrontendOptions::DebugCrashMode::AssertAfterParse:
debugFailWithAssertion();
return true;
case FrontendOptions::DebugCrashMode::CrashAfterParse:
debugFailWithCrash();
return true;
case FrontendOptions::DebugCrashMode::None:
break;
}
(void)migrator::updateCodeAndEmitRemapIfNeeded(&Instance);
bool hadError = Instance.getASTContext().hadError()
&& !opts.AllowModuleWithCompilerErrors;
if (hadError && !runDespiteErrors)
return true;
return cont(Instance) || hadError;
}
static bool performScanDependencies(CompilerInstance &Instance) {
if (Instance.getInvocation().getFrontendOptions().ImportPrescan)
return dependencies::prescanDependencies(Instance);
else
return dependencies::scanDependencies(Instance);
}
static bool performParseOnly(ModuleDecl &MainModule) {
// A -parse invocation only cares about the side effects of parsing, so
// force the parsing of all the source files.
for (auto *file : MainModule.getFiles()) {
if (auto *SF = dyn_cast<SourceFile>(file))
(void)SF->getTopLevelDecls();
}
return MainModule.getASTContext().hadError();
}
static bool performAction(CompilerInstance &Instance,
int &ReturnValue,
FrontendObserver *observer) {
const auto &opts = Instance.getInvocation().getFrontendOptions();
switch (Instance.getInvocation().getFrontendOptions().RequestedAction) {
// MARK: Trivial Actions
case FrontendOptions::ActionType::NoneAction:
return Instance.getASTContext().hadError();
case FrontendOptions::ActionType::PrintVersion:
return printSwiftVersion(Instance.getInvocation());
case FrontendOptions::ActionType::PrintArguments:
return printSwiftArguments(Instance);
case FrontendOptions::ActionType::REPL:
llvm::report_fatal_error("Compiler-internal integrated REPL has been "
"removed; use the LLDB-enhanced REPL instead.");
// MARK: Actions for Clang and Clang Modules
case FrontendOptions::ActionType::EmitPCH:
return precompileBridgingHeader(Instance);
case FrontendOptions::ActionType::EmitPCM:
return precompileClangModule(Instance);
case FrontendOptions::ActionType::DumpPCM:
return dumpPrecompiledClangModule(Instance);
// MARK: Module Interface Actions
case FrontendOptions::ActionType::CompileModuleFromInterface:
case FrontendOptions::ActionType::TypecheckModuleFromInterface:
return buildModuleFromInterface(Instance);
// MARK: Actions that Dump
case FrontendOptions::ActionType::DumpParse:
return dumpAST(Instance, ASTDumpMemberLoading::Parsed);
case FrontendOptions::ActionType::DumpAST:
return withSemanticAnalysis(
Instance, observer,
[](CompilerInstance &Instance) {
return dumpAST(Instance, ASTDumpMemberLoading::TypeChecked);
},
/*runDespiteErrors=*/true);
case FrontendOptions::ActionType::PrintAST:
return withSemanticAnalysis(
Instance, observer, [](CompilerInstance &Instance) {
getPrimaryOrMainSourceFile(Instance).print(
llvm::outs(), PrintOptions::printEverything());
return Instance.getASTContext().hadError();
});
case FrontendOptions::ActionType::PrintASTDecl:
return withSemanticAnalysis(
Instance, observer, [](CompilerInstance &Instance) {
getPrimaryOrMainSourceFile(Instance).print(
llvm::outs(), PrintOptions::printDeclarations());
return Instance.getASTContext().hadError();
});
case FrontendOptions::ActionType::DumpScopeMaps:
return withSemanticAnalysis(
Instance, observer, [](CompilerInstance &Instance) {
return dumpAndPrintScopeMap(Instance,
getPrimaryOrMainSourceFile(Instance));
}, /*runDespiteErrors=*/true);
case FrontendOptions::ActionType::DumpAvailabilityScopes:
return withSemanticAnalysis(
Instance, observer, [](CompilerInstance &Instance) {
getPrimaryOrMainSourceFile(Instance).getAvailabilityScope()->dump(
llvm::errs(), Instance.getASTContext().SourceMgr);
return Instance.getASTContext().hadError();
}, /*runDespiteErrors=*/true);
case FrontendOptions::ActionType::DumpInterfaceHash:
getPrimaryOrMainSourceFile(Instance).dumpInterfaceHash(llvm::errs());
return Instance.getASTContext().hadError();
case FrontendOptions::ActionType::EmitImportedModules:
return emitImportedModules(Instance.getMainModule(), opts,
Instance.getOutputBackend());
// MARK: Dependency Scanning Actions
case FrontendOptions::ActionType::ScanDependencies:
return performScanDependencies(Instance);
// MARK: General Compilation Actions
case FrontendOptions::ActionType::Parse:
return performParseOnly(*Instance.getMainModule());
case FrontendOptions::ActionType::ResolveImports:
return Instance.performParseAndResolveImportsOnly();
case FrontendOptions::ActionType::Typecheck:
return withSemanticAnalysis(Instance, observer,
[](CompilerInstance &Instance) {
return Instance.getASTContext().hadError();
});
case FrontendOptions::ActionType::Immediate: {
const auto &Ctx = Instance.getASTContext();
if (Ctx.LangOpts.hasFeature(Feature::LazyImmediate)) {
ReturnValue = RunImmediatelyFromAST(Instance);
return Ctx.hadError();
}
return withSemanticAnalysis(
Instance, observer, [&](CompilerInstance &Instance) {
assert(FrontendOptions::doesActionGenerateSIL(opts.RequestedAction) &&
"All actions not requiring SILGen must have been handled!");
return performCompileStepsPostSema(Instance, ReturnValue, observer);
});
}
case FrontendOptions::ActionType::EmitSILGen:
case FrontendOptions::ActionType::EmitSIBGen:
case FrontendOptions::ActionType::EmitSIL:
case FrontendOptions::ActionType::EmitLoweredSIL:
case FrontendOptions::ActionType::EmitSIB:
case FrontendOptions::ActionType::EmitModuleOnly:
case FrontendOptions::ActionType::MergeModules:
case FrontendOptions::ActionType::EmitAssembly:
case FrontendOptions::ActionType::EmitIRGen:
case FrontendOptions::ActionType::EmitIR:
case FrontendOptions::ActionType::EmitBC:
case FrontendOptions::ActionType::EmitObject:
case FrontendOptions::ActionType::DumpTypeInfo:
return withSemanticAnalysis(
Instance, observer, [&](CompilerInstance &Instance) {
assert(FrontendOptions::doesActionGenerateSIL(opts.RequestedAction) &&
"All actions not requiring SILGen must have been handled!");
return performCompileStepsPostSema(Instance, ReturnValue, observer);
});
}
assert(false && "Unhandled case in performCompile!");
return Instance.getASTContext().hadError();
}
/// Try replay the compiler result from cache.
///
/// Return true if all the outputs are fetched from cache. Otherwise, return
/// false and will not replay any output.
static bool tryReplayCompilerResults(CompilerInstance &Instance) {
if (!Instance.supportCaching() ||
Instance.getInvocation().getCASOptions().CacheSkipReplay)
return false;
assert(Instance.getCompilerBaseKey() &&
"Instance is not setup correctly for replay");
auto *CDP = Instance.getCachingDiagnosticsProcessor();
assert(CDP && "CachingDiagnosticsProcessor needs to be setup for replay");
// Don't capture diagnostics from replay.
CDP->endDiagnosticCapture();
bool replayed = replayCachedCompilerOutputs(
Instance.getObjectStore(), Instance.getActionCache(),
*Instance.getCompilerBaseKey(), Instance.getDiags(),
Instance.getInvocation().getFrontendOptions(), *CDP,
Instance.getInvocation().getCASOptions().EnableCachingRemarks,
Instance.getInvocation().getIRGenOptions().UseCASBackend);
// If we didn't replay successfully, re-start capture.
if (!replayed)
CDP->startDiagnosticCapture();
return replayed;
}
/// Performs the compile requested by the user.
/// \param Instance Will be reset after performIRGeneration when the verifier
/// mode is NoVerify and there were no errors.
/// \returns true on error
static bool performCompile(CompilerInstance &Instance,
int &ReturnValue,
FrontendObserver *observer) {
const auto &Invocation = Instance.getInvocation();
const auto &opts = Invocation.getFrontendOptions();
const FrontendOptions::ActionType Action = opts.RequestedAction;
if (tryReplayCompilerResults(Instance))
return false;
// To compile LLVM IR, just pass it off unmodified.
if (opts.InputsAndOutputs.shouldTreatAsLLVM())
return compileLLVMIR(Instance);
assert([&]() -> bool {
if (FrontendOptions::shouldActionOnlyParse(Action)) {
// Parsing gets triggered lazily, but let's make sure we have the right
// input kind.
return llvm::all_of(
opts.InputsAndOutputs.getAllInputs(), [](const InputFile &IF) {
const auto kind = IF.getType();
return kind == file_types::TY_Swift ||
kind == file_types::TY_SwiftModuleInterfaceFile;
});
}
return true;
}() && "Only supports parsing .swift files");
bool hadError = performAction(Instance, ReturnValue, observer);
auto canIgnoreErrorForExit = [&Instance, &opts]() {
return opts.AllowModuleWithCompilerErrors ||
(opts.isTypeCheckAction() && Instance.downgradeInterfaceVerificationErrors());
};
// We might have freed the ASTContext already, but in that case we would
// have already performed these actions.
if (Instance.hasASTContext() &&
FrontendOptions::doesActionPerformEndOfPipelineActions(Action)) {
performEndOfPipelineActions(Instance);
if (!canIgnoreErrorForExit())
hadError |= Instance.getASTContext().hadError();
}
return hadError;
}
static bool serializeSIB(SILModule *SM, const PrimarySpecificPaths &PSPs,
const ASTContext &Context, ModuleOrSourceFile MSF) {
const std::string &moduleOutputPath =
PSPs.SupplementaryOutputs.ModuleOutputPath;
assert(!moduleOutputPath.empty() && "must have an output path");
SerializationOptions serializationOpts;
serializationOpts.OutputPath = moduleOutputPath;
serializationOpts.SerializeAllSIL = true;
serializationOpts.IsSIB = true;
serializationOpts.IsOSSA = Context.SILOpts.EnableOSSAModules;
symbolgraphgen::SymbolGraphOptions symbolGraphOptions;
serialize(MSF, serializationOpts, symbolGraphOptions, SM);
return Context.hadError();
}
static bool serializeModuleSummary(SILModule *SM,
const PrimarySpecificPaths &PSPs,
const ASTContext &Context) {
auto summaryOutputPath = PSPs.SupplementaryOutputs.ModuleSummaryOutputPath;
return withOutputPath(Context.Diags, Context.getOutputBackend(),
summaryOutputPath, [&](llvm::raw_ostream &out) {
out << "Some stuff";
return false;
});
}
static GeneratedModule
generateIR(const IRGenOptions &IRGenOpts, const TBDGenOptions &TBDOpts,
std::unique_ptr<SILModule> SM,
const PrimarySpecificPaths &PSPs,
StringRef OutputFilename, ModuleOrSourceFile MSF,
llvm::GlobalVariable *&HashGlobal,
ArrayRef<std::string> parallelOutputFilenames) {
if (auto *SF = MSF.dyn_cast<SourceFile *>()) {
return performIRGeneration(SF, IRGenOpts, TBDOpts,
std::move(SM), OutputFilename, PSPs,
SF->getPrivateDiscriminator().str(),
&HashGlobal);
} else {
return performIRGeneration(MSF.get<ModuleDecl *>(), IRGenOpts, TBDOpts,
std::move(SM), OutputFilename, PSPs,
parallelOutputFilenames, &HashGlobal);
}
}
static bool processCommandLineAndRunImmediately(CompilerInstance &Instance,
std::unique_ptr<SILModule> &&SM,
ModuleOrSourceFile MSF,
FrontendObserver *observer,
int &ReturnValue) {
const auto &Invocation = Instance.getInvocation();
const auto &opts = Invocation.getFrontendOptions();
assert(!MSF.is<SourceFile *>() && "-i doesn't work in -primary-file mode");
const IRGenOptions &IRGenOpts = Invocation.getIRGenOptions();
const ProcessCmdLine &CmdLine =
ProcessCmdLine(opts.ImmediateArgv.begin(), opts.ImmediateArgv.end());
PrettyStackTraceStringAction trace(
"running user code",
MSF.is<SourceFile *>() ? MSF.get<SourceFile *>()->getFilename()
: MSF.get<ModuleDecl *>()->getModuleFilename());
ReturnValue =
RunImmediately(Instance, CmdLine, IRGenOpts, Invocation.getSILOptions(),
std::move(SM));
return Instance.getASTContext().hadError();
}
static bool validateTBDIfNeeded(const CompilerInvocation &Invocation,
ModuleOrSourceFile MSF,
const llvm::Module &IRModule) {
const auto mode = Invocation.getFrontendOptions().ValidateTBDAgainstIR;
const bool canPerformTBDValidation = [&]() {
// If the user has requested we skip validation, honor it.
if (mode == FrontendOptions::TBDValidationMode::None) {
return false;
}
// Embedded Swift does not support TBD.
if (Invocation.getLangOptions().hasFeature(Feature::Embedded)) {
return false;
}
// Cross-module optimization does not support TBD.
if (Invocation.getSILOptions().CMOMode == CrossModuleOptimizationMode::Aggressive ||
Invocation.getSILOptions().CMOMode == CrossModuleOptimizationMode::Everything) {
return false;
}
// If we can't validate the given input file, bail early. This covers cases
// like passing raw SIL as a primary file.
const auto &IO = Invocation.getFrontendOptions().InputsAndOutputs;
// FIXME: This would be a good test of the interface format.
if (IO.shouldTreatAsModuleInterface() || IO.shouldTreatAsSIL() ||
IO.shouldTreatAsLLVM() || IO.shouldTreatAsObjCHeader()) {
return false;
}
// Modules with SIB files cannot be validated. This is because SIB files
// may have serialized hand-crafted SIL definitions that are invisible to
// TBDGen as it is an AST-only traversal.
if (auto *mod = MSF.dyn_cast<ModuleDecl *>()) {
bool hasSIB = llvm::any_of(mod->getFiles(), [](const FileUnit *File) -> bool {
auto SASTF = dyn_cast<SerializedASTFile>(File);
return SASTF && SASTF->isSIB();
});
if (hasSIB) {
return false;
}
}
// "Default" mode's behavior varies if using a debug compiler.
if (mode == FrontendOptions::TBDValidationMode::Default) {
#ifndef NDEBUG
// With a debug compiler, we do some validation by default.
return true;
#else
// Otherwise, the default is to do nothing.
return false;
#endif
}
return true;
}();
if (!canPerformTBDValidation) {
return false;
}
const bool diagnoseExtraSymbolsInTBD = [mode]() {
switch (mode) {
case FrontendOptions::TBDValidationMode::None:
llvm_unreachable("Handled Above!");
case FrontendOptions::TBDValidationMode::Default:
case FrontendOptions::TBDValidationMode::MissingFromTBD:
return false;
case FrontendOptions::TBDValidationMode::All:
return true;
}
llvm_unreachable("invalid mode");
}();
TBDGenOptions Opts = Invocation.getTBDGenOptions();
// Ignore embedded symbols from external modules for validation to remove
// noise from e.g. statically-linked libraries.
Opts.embedSymbolsFromModules.clear();
if (auto *SF = MSF.dyn_cast<SourceFile *>()) {
return validateTBD(SF, IRModule, Opts, diagnoseExtraSymbolsInTBD);
} else {
return validateTBD(MSF.get<ModuleDecl *>(), IRModule, Opts,
diagnoseExtraSymbolsInTBD);
}
}
static void freeASTContextIfPossible(CompilerInstance &Instance) {
// If the stats reporter is installed, we need the ASTContext to live through
// the entire compilation process.
if (Instance.getASTContext().Stats) {
return;
}
// If this instance is used for multiple compilations, we need the ASTContext
// to live.
if (Instance.getInvocation()
.getFrontendOptions()
.ReuseFrontendForMultipleCompilations) {
return;
}
const auto &opts = Instance.getInvocation().getFrontendOptions();
// If there are multiple primary inputs it is too soon to free
// the ASTContext, etc.. OTOH, if this compilation generates code for > 1
// primary input, then freeing it after processing the last primary is
// unlikely to reduce the peak heap size. So, only optimize the
// single-primary-case (or WMO).
if (opts.InputsAndOutputs.hasMultiplePrimaryInputs()) {
return;
}
// Make sure to perform the end of pipeline actions now, because they need
// access to the ASTContext.
performEndOfPipelineActions(Instance);
Instance.freeASTContext();
}
static bool generateCode(CompilerInstance &Instance, StringRef OutputFilename,
llvm::Module *IRModule,
llvm::GlobalVariable *HashGlobal) {
const auto &opts = Instance.getInvocation().getIRGenOptions();
std::unique_ptr<llvm::TargetMachine> TargetMachine =
createTargetMachine(opts, Instance.getASTContext());
TargetMachine->Options.MCOptions.CAS = Instance.getSharedCASInstance();
if (Instance.getInvocation().getCASOptions().EnableCaching &&
opts.UseCASBackend)
TargetMachine->Options.MCOptions.ResultCallBack =
[&](const llvm::cas::CASID &ID) -> llvm::Error {
if (auto Err = Instance.getCASOutputBackend().storeMCCASObjectID(
OutputFilename, ID))
return Err;
return llvm::Error::success();
};
// Free up some compiler resources now that we have an IRModule.
freeASTContextIfPossible(Instance);
// If we emitted any errors while performing the end-of-pipeline actions, bail.
if (Instance.getDiags().hadAnyError())
return true;
// Now that we have a single IR Module, hand it over to performLLVM.
return performLLVM(opts, Instance.getDiags(), nullptr, HashGlobal, IRModule,
TargetMachine.get(), OutputFilename,
Instance.getOutputBackend(),
Instance.getStatsReporter());
}
static bool performCompileStepsPostSILGen(CompilerInstance &Instance,
std::unique_ptr<SILModule> SM,
ModuleOrSourceFile MSF,
const PrimarySpecificPaths &PSPs,
int &ReturnValue,
FrontendObserver *observer) {
const auto &Invocation = Instance.getInvocation();
const auto &opts = Invocation.getFrontendOptions();
FrontendOptions::ActionType Action = opts.RequestedAction;
const ASTContext &Context = Instance.getASTContext();
const IRGenOptions &IRGenOpts = Invocation.getIRGenOptions();
std::optional<BufferIndirectlyCausingDiagnosticRAII> ricd;
if (auto *SF = MSF.dyn_cast<SourceFile *>())
ricd.emplace(*SF);
if (observer)
observer->performedSILGeneration(*SM);
// Cancellation check after SILGen.
if (Instance.isCancellationRequested())
return true;
auto *Stats = Instance.getASTContext().Stats;
if (Stats)
countStatsPostSILGen(*Stats, *SM);
// We've been told to emit SIL after SILGen, so write it now.
if (Action == FrontendOptions::ActionType::EmitSILGen) {
return writeSIL(*SM, PSPs, Instance, Invocation.getSILOptions());
}
// In lazy typechecking mode, SILGen may have triggered requests which
// resulted in errors. We don't want to proceed with optimization or
// serialization if there were errors since the SIL may be incomplete or
// invalid.
if (Context.TypeCheckerOpts.EnableLazyTypecheck && Context.hadError())
return true;
if (Action == FrontendOptions::ActionType::EmitSIBGen) {
serializeSIB(SM.get(), PSPs, Context, MSF);
return Context.hadError();
}
SM->installSILRemarkStreamer();
// This is the action to be used to serialize SILModule.
// It may be invoked multiple times, but it will perform
// serialization only once. The serialization may either happen
// after high-level optimizations or after all optimizations are
// done, depending on the compiler setting.
auto SerializeSILModuleAction = [&]() {
const SupplementaryOutputPaths &outs = PSPs.SupplementaryOutputs;
if (outs.ModuleOutputPath.empty())
return;
SerializationOptions serializationOpts =
Invocation.computeSerializationOptions(outs, Instance.getMainModule());
// Infer if this is an emit-module job part of an incremental build,
// vs a partial emit-module job (with primary files) or other kinds.
// We may want to rely on a flag instead to differentiate them.
const bool isEmitModuleSeparately =
Action == FrontendOptions::ActionType::EmitModuleOnly &&
MSF.is<ModuleDecl *>() &&
Instance.getInvocation()
.getTypeCheckerOptions()
.SkipFunctionBodies == FunctionBodySkipping::NonInlinableWithoutTypes;
const bool canEmitIncrementalInfoIntoModule =
!serializationOpts.DisableCrossModuleIncrementalInfo &&
(Action == FrontendOptions::ActionType::MergeModules ||
isEmitModuleSeparately);
if (canEmitIncrementalInfoIntoModule) {
const auto alsoEmitDotFile =
Instance.getInvocation()
.getLangOptions()
.EmitFineGrainedDependencySourcefileDotFiles;
using SourceFileDepGraph = fine_grained_dependencies::SourceFileDepGraph;
auto *Mod = MSF.get<ModuleDecl *>();
fine_grained_dependencies::withReferenceDependencies(
Mod, *Instance.getDependencyTracker(),
Instance.getOutputBackend(), Mod->getModuleFilename(),
alsoEmitDotFile, [&](SourceFileDepGraph &&g) {
serialize(MSF, serializationOpts, Invocation.getSymbolGraphOptions(), SM.get(), &g);
return false;
});
} else {
serialize(MSF, serializationOpts, Invocation.getSymbolGraphOptions(), SM.get());
}
};
// Set the serialization action, so that the SIL module
// can be serialized at any moment, e.g. during the optimization pipeline.
SM->setSerializeSILAction(SerializeSILModuleAction);
// Perform optimizations and mandatory/diagnostic passes.
if (Instance.performSILProcessing(SM.get()))
return true;
if (observer)
observer->performedSILProcessing(*SM);
// Cancellation check after SILOptimization.
if (Instance.isCancellationRequested())
return true;
if (PSPs.haveModuleSummaryOutputPath()) {
if (serializeModuleSummary(SM.get(), PSPs, Context)) {
return true;
}
}
if (Action == FrontendOptions::ActionType::EmitSIB)
return serializeSIB(SM.get(), PSPs, Context, MSF);
if (PSPs.haveModuleOrModuleDocOutputPaths()) {
if (Action == FrontendOptions::ActionType::MergeModules ||
Action == FrontendOptions::ActionType::EmitModuleOnly) {
return Context.hadError() && !opts.AllowModuleWithCompilerErrors;
}
}
assert(Action >= FrontendOptions::ActionType::EmitSIL &&
"All actions not requiring SILPasses must have been handled!");
// We've been told to write canonical SIL, so write it now.
if (Action == FrontendOptions::ActionType::EmitSIL)
return writeSIL(*SM, PSPs, Instance, Invocation.getSILOptions());
assert(Action >= FrontendOptions::ActionType::Immediate &&
"All actions not requiring IRGen must have been handled!");
assert(Action != FrontendOptions::ActionType::REPL &&
"REPL mode must be handled immediately after Instance->performSema()");
// Check if we had any errors; if we did, don't proceed to IRGen.
if (Context.hadError())
return !opts.AllowModuleWithCompilerErrors;
runSILLoweringPasses(*SM);
// If we are asked to emit lowered SIL, dump it now and return.
if (Action == FrontendOptions::ActionType::EmitLoweredSIL)
return writeSIL(*SM, PSPs, Instance, Invocation.getSILOptions());
// Cancellation check after SILLowering.
if (Instance.isCancellationRequested())
return true;
// TODO: at this point we need to flush any the _tracing and profiling_
// in the UnifiedStatsReporter, because the those subsystems of the USR
// retain _pointers into_ the SILModule, and the SILModule's lifecycle is
// not presently such that it will outlive the USR (indeed, as it's
// destroyed on a separate thread, this fact isn't even _deterministic_
// after this point). If future plans require the USR tracing or
// profiling entities after this point, more rearranging will be required.
if (Stats)
Stats->flushTracesAndProfiles();
if (Action == FrontendOptions::ActionType::DumpTypeInfo)
return performDumpTypeInfo(IRGenOpts, *SM);
if (Action == FrontendOptions::ActionType::Immediate)
return processCommandLineAndRunImmediately(
Instance, std::move(SM), MSF, observer, ReturnValue);
StringRef OutputFilename = PSPs.OutputFilename;
std::vector<std::string> ParallelOutputFilenames =
opts.InputsAndOutputs.copyOutputFilenames();
llvm::GlobalVariable *HashGlobal;
auto IRModule =
generateIR(IRGenOpts, Invocation.getTBDGenOptions(), std::move(SM), PSPs,
OutputFilename, MSF, HashGlobal, ParallelOutputFilenames);
// Cancellation check after IRGen.
if (Instance.isCancellationRequested())
return true;
// If no IRModule is available, bail. This can either happen if IR generation
// fails, or if parallelIRGen happened correctly (in which case it would have
// already performed LLVM).
if (!IRModule)
return Instance.getDiags().hadAnyError();
if (validateTBDIfNeeded(Invocation, MSF, *IRModule.getModule()))
return true;
if (IRGenOpts.UseSingleModuleLLVMEmission) {
// Pretend the other files that drivers/build systems expect exist by
// creating empty files.
if (writeEmptyOutputFilesFor(Context, ParallelOutputFilenames, IRGenOpts))
return true;
}
return generateCode(Instance, OutputFilename, IRModule.getModule(),
HashGlobal);
}
static void emitIndexDataForSourceFile(SourceFile *PrimarySourceFile,
const CompilerInstance &Instance) {
const auto &Invocation = Instance.getInvocation();
const auto &opts = Invocation.getFrontendOptions();
if (opts.IndexStorePath.empty())
return;
// FIXME: provide index unit token(s) explicitly and only use output file
// paths as a fallback.
bool isDebugCompilation;
switch (Invocation.getSILOptions().OptMode) {
case OptimizationMode::NotSet:
case OptimizationMode::NoOptimization:
isDebugCompilation = true;
break;
case OptimizationMode::ForSpeed:
case OptimizationMode::ForSize:
isDebugCompilation = false;
break;
}
if (PrimarySourceFile) {
const PrimarySpecificPaths &PSPs =
opts.InputsAndOutputs.getPrimarySpecificPathsForPrimary(
PrimarySourceFile->getFilename());
StringRef OutputFile = PSPs.IndexUnitOutputFilename;
if (OutputFile.empty())
OutputFile = PSPs.OutputFilename;
(void) index::indexAndRecord(PrimarySourceFile, OutputFile,
opts.IndexStorePath,
!opts.IndexIgnoreClangModules,
opts.IndexSystemModules,
opts.IndexIgnoreStdlib,
opts.IndexIncludeLocals,
isDebugCompilation,
opts.DisableImplicitModules,
Invocation.getTargetTriple(),
*Instance.getDependencyTracker(),
Invocation.getIRGenOptions().FilePrefixMap);
} else {
std::string moduleToken =
Invocation.getModuleOutputPathForAtMostOnePrimary();
if (moduleToken.empty())
moduleToken = opts.InputsAndOutputs.getSingleIndexUnitOutputFilename();
(void) index::indexAndRecord(Instance.getMainModule(),
opts.InputsAndOutputs
.copyIndexUnitOutputFilenames(),
moduleToken, opts.IndexStorePath,
!opts.IndexIgnoreClangModules,
opts.IndexSystemModules,
opts.IndexIgnoreStdlib,
opts.IndexIncludeLocals,
isDebugCompilation,
opts.DisableImplicitModules,
Invocation.getTargetTriple(),
*Instance.getDependencyTracker(),
Invocation.getIRGenOptions().FilePrefixMap);
}
}
/// A PrettyStackTraceEntry to print frontend information useful for debugging.
class PrettyStackTraceFrontend : public llvm::PrettyStackTraceEntry {
const CompilerInvocation &Invocation;
public:
PrettyStackTraceFrontend(const CompilerInvocation &invocation)
: Invocation(invocation) {}
void print(llvm::raw_ostream &os) const override {
auto effective = Invocation.getLangOptions().EffectiveLanguageVersion;
if (effective != version::Version::getCurrentLanguageVersion()) {
os << "Compiling with effective version " << effective;
} else {
os << "Compiling with the current language version";
}
if (Invocation.getFrontendOptions().AllowModuleWithCompilerErrors) {
os << " while allowing modules with compiler errors";
}
os << "\n";
};
};
int swift::performFrontend(ArrayRef<const char *> Args,
const char *Argv0, void *MainAddr,
FrontendObserver *observer) {
INITIALIZE_LLVM();
llvm::setBugReportMsg(SWIFT_CRASH_BUG_REPORT_MESSAGE "\n");
llvm::EnablePrettyStackTraceOnSigInfoForThisThread();
std::unique_ptr<CompilerInstance> Instance =
std::make_unique<CompilerInstance>();
CompilerInvocation Invocation;
DiagnosticHelper DH = DiagnosticHelper::create(*Instance, Invocation, Args);
// Hopefully we won't trigger any LLVM-level fatal errors, but if we do try
// to route them through our usual textual diagnostics before crashing.
//
// Unfortunately it's not really safe to do anything else, since very
// low-level operations in LLVM can trigger fatal errors.
llvm::ScopedFatalErrorHandler handler(
[](void *rawCallback, const char *reason, bool shouldCrash) {
auto *helper = static_cast<DiagnosticHelper *>(rawCallback);
helper->diagnoseFatalError(reason, shouldCrash);
},
&DH);
struct FinishDiagProcessingCheckRAII {
bool CalledFinishDiagProcessing = false;
~FinishDiagProcessingCheckRAII() {
assert(CalledFinishDiagProcessing && "returned from the function "
"without calling finishDiagProcessing");
}
} FinishDiagProcessingCheckRAII;
auto finishDiagProcessing = [&](int retValue, bool verifierEnabled) -> int {
FinishDiagProcessingCheckRAII.CalledFinishDiagProcessing = true;
DH.setSuppressOutput(false);
if (auto *CDP = Instance->getCachingDiagnosticsProcessor()) {
// Don't cache if build failed.
if (retValue)
CDP->endDiagnosticCapture();
}
bool diagnosticsError = Instance->getDiags().finishProcessing();
// If the verifier is enabled and did not encounter any verification errors,
// return 0 even if the compile failed. This behavior isn't ideal, but large
// parts of the test suite are reliant on it.
if (verifierEnabled && !diagnosticsError) {
return 0;
}
return retValue ? retValue : diagnosticsError;
};
if (Args.empty()) {
Instance->getDiags().diagnose(SourceLoc(), diag::error_no_frontend_args);
return finishDiagProcessing(1, /*verifierEnabled*/ false);
}
SmallString<128> workingDirectory;
llvm::sys::fs::current_path(workingDirectory);
std::string MainExecutablePath =
llvm::sys::fs::getMainExecutable(Argv0, MainAddr);
// Parse arguments.
SmallVector<std::unique_ptr<llvm::MemoryBuffer>, 4> configurationFileBuffers;
if (Invocation.parseArgs(Args, Instance->getDiags(),
&configurationFileBuffers, workingDirectory,
MainExecutablePath)) {
return finishDiagProcessing(1, /*verifierEnabled*/ false);
}
// Don't ask clients to report bugs when running a script in immediate mode.
// When a script asserts the compiler reports the error with the same
// stacktrace as a compiler crash. From here we can't tell which is which,
// for now let's not explicitly ask for bug reports.
if (Invocation.getFrontendOptions().RequestedAction ==
FrontendOptions::ActionType::Immediate) {
llvm::setBugReportMsg(nullptr);
}
PrettyStackTraceFrontend frontendTrace(Invocation);
// Make an array of PrettyStackTrace objects to dump the configuration files
// we used to parse the arguments. These are RAII objects, so they and the
// buffers they refer to must be kept alive in order to be useful. (That is,
// we want them to be alive for the entire rest of performFrontend.)
//
// This can't be a SmallVector or similar because PrettyStackTraces can't be
// moved (or copied)...and it can't be an array of non-optionals because
// PrettyStackTraces can't be default-constructed. So we end up with a
// dynamically-sized array of optional PrettyStackTraces, which get
// initialized by iterating over the buffers we collected above.
auto configurationFileStackTraces =
std::make_unique<std::optional<PrettyStackTraceFileContents>[]>(
configurationFileBuffers.size());
// If the compile is a whole module job, then the contents of the filelist
// is every file in the module, which is not very interesting and could be
// hundreds or thousands of lines. Skip dumping this output in that case.
if (!Invocation.getFrontendOptions().InputsAndOutputs.isWholeModule()) {
for_each(configurationFileBuffers.begin(), configurationFileBuffers.end(),
configurationFileStackTraces.get(),
[](const std::unique_ptr<llvm::MemoryBuffer> &buffer,
std::optional<PrettyStackTraceFileContents> &trace) {
trace.emplace(*buffer);
});
}
// The compiler invocation is now fully configured; notify our observer.
if (observer) {
observer->parsedArgs(Invocation);
}
if (Invocation.getFrontendOptions().PrintHelp ||
Invocation.getFrontendOptions().PrintHelpHidden) {
unsigned IncludedFlagsBitmask = options::FrontendOption;
unsigned ExcludedFlagsBitmask =
Invocation.getFrontendOptions().PrintHelpHidden ? 0 :
llvm::opt::HelpHidden;
std::unique_ptr<llvm::opt::OptTable> Options(createSwiftOptTable());
Options->printHelp(llvm::outs(), displayName(MainExecutablePath).c_str(),
"Swift frontend", IncludedFlagsBitmask,
ExcludedFlagsBitmask, /*ShowAllAliases*/false);
return finishDiagProcessing(0, /*verifierEnabled*/ false);
}
if (Invocation.getFrontendOptions().PrintTargetInfo) {
swift::targetinfo::printTargetInfo(Invocation, llvm::outs());
return finishDiagProcessing(0, /*verifierEnabled*/ false);
}
if (Invocation.getFrontendOptions().PrintSupportedFeatures) {
swift::features::printSupportedFeatures(llvm::outs());
return finishDiagProcessing(0, /*verifierEnabled*/ false);
}
if (Invocation.getFrontendOptions().RequestedAction ==
FrontendOptions::ActionType::NoneAction) {
Instance->getDiags().diagnose(SourceLoc(),
diag::error_missing_frontend_action);
return finishDiagProcessing(1, /*verifierEnabled*/ false);
}
if (Invocation.getFrontendOptions().PrintStats) {
llvm::EnableStatistics();
}
DH.beginMessage();
const DiagnosticOptions &diagOpts = Invocation.getDiagnosticOptions();
bool verifierEnabled = diagOpts.VerifyMode != DiagnosticOptions::NoVerify;
std::string InstanceSetupError;
if (Instance->setup(Invocation, InstanceSetupError, Args)) {
int ReturnCode = 1;
DH.endMessage(ReturnCode);
return finishDiagProcessing(ReturnCode, /*verifierEnabled*/ false);
}
// The compiler instance has been configured; notify our observer.
if (observer) {
observer->configuredCompiler(*Instance);
}
if (verifierEnabled) {
// Suppress printed diagnostic output during the compile if the verifier is
// enabled.
DH.setSuppressOutput(true);
}
CompilerInstance::HashingBackendPtrTy HashBackend = nullptr;
if (Invocation.getFrontendOptions().DeterministicCheck) {
// Setup a verfication instance to run.
std::unique_ptr<CompilerInstance> VerifyInstance =
std::make_unique<CompilerInstance>();
// Add a null diagnostic consumer to the diagnostic engine so the
// compilation will exercise all the diagnose code path but not emitting
// anything.
NullDiagnosticConsumer DC;
VerifyInstance->getDiags().addConsumer(DC);
std::string InstanceSetupError;
// This should not fail because it passed already.
(void)VerifyInstance->setup(Invocation, InstanceSetupError, Args);
// Run the first time without observer and discard return value;
int ReturnValueTest = 0;
(void)performCompile(*VerifyInstance, ReturnValueTest,
/*observer*/ nullptr);
// Get the hashing output backend and free the compiler instance.
HashBackend = VerifyInstance->getHashingBackend();
}
int ReturnValue = 0;
bool HadError = performCompile(*Instance, ReturnValue, observer);
if (verifierEnabled) {
DiagnosticEngine &diags = Instance->getDiags();
if (diags.hasFatalErrorOccurred() &&
!Invocation.getDiagnosticOptions().ShowDiagnosticsAfterFatalError) {
diags.resetHadAnyError();
DH.setSuppressOutput(false);
diags.diagnose(SourceLoc(), diag::verify_encountered_fatal);
HadError = true;
}
}
if (Invocation.getFrontendOptions().DeterministicCheck) {
// Collect all output files.
auto ReHashBackend = Instance->getHashingBackend();
std::set<std::string> AllOutputs;
llvm::for_each(HashBackend->outputFiles(), [&](StringRef F) {
AllOutputs.insert(F.str());
});
llvm::for_each(ReHashBackend->outputFiles(), [&](StringRef F) {
AllOutputs.insert(F.str());
});
DiagnosticEngine &diags = Instance->getDiags();
for (auto &Filename : AllOutputs) {
auto O1 = HashBackend->getHashValueForFile(Filename);
if (!O1) {
diags.diagnose(SourceLoc(), diag::error_output_missing, Filename,
/*SecondRun=*/false);
HadError = true;
continue;
}
auto O2 = ReHashBackend->getHashValueForFile(Filename);
if (!O2) {
diags.diagnose(SourceLoc(), diag::error_output_missing, Filename,
/*SecondRun=*/true);
HadError = true;
continue;
}
if (*O1 != *O2) {
diags.diagnose(SourceLoc(), diag::error_nondeterministic_output,
Filename, *O1, *O2);
HadError = true;
continue;
}
diags.diagnose(SourceLoc(), diag::matching_output_produced, Filename,
*O1);
}
}
auto r = finishDiagProcessing(HadError ? 1 : ReturnValue, verifierEnabled);
if (auto *StatsReporter = Instance->getStatsReporter())
StatsReporter->noteCurrentProcessExitStatus(r);
DH.endMessage(r);
return r;
}
void FrontendObserver::parsedArgs(CompilerInvocation &invocation) {}
void FrontendObserver::configuredCompiler(CompilerInstance &instance) {}
void FrontendObserver::performedSemanticAnalysis(CompilerInstance &instance) {}
void FrontendObserver::performedSILGeneration(SILModule &module) {}
void FrontendObserver::performedSILProcessing(SILModule &module) {}