//===--- FrontendTool.cpp - Swift Compiler Frontend -----------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2018 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 "ImportedModules.h" #include "ScanDependencies.h" #include "TBD.h" #include "swift/Subsystems.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/GenericSignatureBuilder.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/IRGenRequests.h" #include "swift/AST/NameLookup.h" #include "swift/AST/ASTMangler.h" #include "swift/AST/TBDGenRequests.h" #include "swift/AST/TypeRefinementContext.h" #include "swift/Basic/Dwarf.h" #include "swift/Basic/Edit.h" #include "swift/Basic/FileSystem.h" #include "swift/Basic/JSONSerialization.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/UUID.h" #include "swift/Option/Options.h" #include "swift/Frontend/DiagnosticVerifier.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" #include "swift/Frontend/SerializedDiagnosticConsumer.h" #include "swift/Frontend/ModuleInterfaceLoader.h" #include "swift/Frontend/ModuleInterfaceSupport.h" #include "swift/Immediate/Immediate.h" #include "swift/Index/IndexRecord.h" #include "swift/Option/Options.h" #include "swift/Migrator/FixitFilter.h" #include "swift/Migrator/Migrator.h" #include "swift/PrintAsObjC/PrintAsObjC.h" #include "swift/Serialization/SerializationOptions.h" #include "swift/Serialization/SerializedModuleLoader.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SIL/SILRemarkStreamer.h" #include "swift/Syntax/Serialization/SyntaxSerialization.h" #include "swift/Syntax/SyntaxNodes.h" #include "swift/TBDGen/TBDGen.h" #include "clang/AST/ASTContext.h" #include "clang/Basic/Module.h" #include "llvm/ADT/Statistic.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IRReader/IRReader.h" #include "llvm/Option/Option.h" #include "llvm/Option/OptTable.h" #include "llvm/Remarks/RemarkSerializer.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/Timer.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Target/TargetMachine.h" #include #include #include #include #if !defined(_MSC_VER) && !defined(__MINGW32__) #include #else #include #endif using namespace swift; static std::string displayName(StringRef MainExecutablePath) { std::string Name = llvm::sys::path::stem(MainExecutablePath).str(); Name += " -frontend"; return Name; } StringRef swift::frontend::utils::escapeForMake(StringRef raw, llvm::SmallVectorImpl &buffer) { buffer.clear(); // The escaping rules for GNU make are complicated due to the various // subsitutions and use of the tab in the leading position for recipes. // Various symbols have significance in different contexts. It is not // possible to correctly quote all characters in Make (as of 3.7). Match // gcc and clang's behaviour for the escaping which covers only a subset of // characters. for (unsigned I = 0, E = raw.size(); I != E; ++I) { switch (raw[I]) { case '#': // Handle '#' the broken GCC way buffer.push_back('\\'); break; case ' ': for (unsigned J = I; J && raw[J - 1] == '\\'; --J) buffer.push_back('\\'); buffer.push_back('\\'); break; case '$': // $ is escaped by $ buffer.push_back('$'); break; } buffer.push_back(raw[I]); } buffer.push_back('\0'); return buffer.data(); } /// This sorting function is used to stabilize the order in which dependencies /// are emitted into \c .d files that are consumed by external build systems. /// This serves to eliminate order as a source of non-determinism in these /// outputs. /// /// The exact sorting predicate is not important. Currently, it is a /// lexicographic comparison that reverses the provided strings before applying /// the sorting predicate. This has the benefit of being somewhat /// invariant with respect to the installation location of various system /// components. e.g. on two systems, the same file identified by two different /// paths differing only in their relative install location such as /// /// /Applications/MyXcode.app/Path/To/A/Framework/In/The/SDK/Header.h /// /Applications/Xcodes/AnotherXcode.app/Path/To/A/Framework/In/The/SDK/Header.h /// /// should appear in roughly the same order relative to other paths. Ultimately, /// this makes it easier to test the contents of the emitted files with tools /// like FileCheck. static std::vector reversePathSortedFilenames(const ArrayRef elts) { std::vector tmp(elts.begin(), elts.end()); std::sort(tmp.begin(), tmp.end(), [](const std::string &a, const std::string &b) -> bool { return std::lexicographical_compare(a.rbegin(), a.rend(), b.rbegin(), b.rend()); }); return tmp; } /// Emits a Make-style dependencies file. static bool emitMakeDependenciesIfNeeded(DiagnosticEngine &diags, DependencyTracker *depTracker, const FrontendOptions &opts, const InputFile &input) { const std::string &dependenciesFilePath = input.dependenciesFilePath(); if (dependenciesFilePath.empty()) return false; std::error_code EC; llvm::raw_fd_ostream out(dependenciesFilePath, EC, llvm::sys::fs::F_None); if (out.has_error() || EC) { diags.diagnose(SourceLoc(), diag::error_opening_output, dependenciesFilePath, EC.message()); out.clear_error(); return true; } llvm::SmallString<256> buffer; // collect everything in memory to avoid redundant work // when there are multiple targets std::string dependencyString; // First include all other files in the module. Make-style dependencies // need to be conservative! auto inputPaths = reversePathSortedFilenames(opts.InputsAndOutputs.getInputFilenames()); for (auto const &path : inputPaths) { dependencyString.push_back(' '); dependencyString.append(frontend::utils::escapeForMake(path, buffer).str()); } // Then print dependencies we've picked up during compilation. auto dependencyPaths = reversePathSortedFilenames(depTracker->getDependencies()); for (auto const &path : dependencyPaths) { dependencyString.push_back(' '); dependencyString.append(frontend::utils::escapeForMake(path, buffer).str()); } auto incrementalDependencyPaths = reversePathSortedFilenames(depTracker->getIncrementalDependencies()); for (auto const &path : incrementalDependencyPaths) { dependencyString.push_back(' '); dependencyString.append(frontend::utils::escapeForMake(path, buffer).str()); } // FIXME: Xcode can't currently handle multiple targets in a single // dependency line. opts.forAllOutputPaths(input, [&](const StringRef targetName) { auto targetNameEscaped = frontend::utils::escapeForMake(targetName, buffer); out << targetNameEscaped << " :" << dependencyString << '\n'; }); return false; } static void emitMakeDependenciesIfNeeded(DiagnosticEngine &diags, DependencyTracker *depTracker, const FrontendOptions &opts) { opts.InputsAndOutputs.forEachInputProducingSupplementaryOutput( [&](const InputFile &f) -> bool { return emitMakeDependenciesIfNeeded(diags, depTracker, opts, f); }); } // MARK: - Module Trace namespace { struct SwiftModuleTraceInfo { Identifier Name; std::string Path; bool IsImportedDirectly; bool SupportsLibraryEvolution; }; struct LoadedModuleTraceFormat { static const unsigned CurrentVersion = 2; unsigned Version; Identifier Name; std::string Arch; std::vector SwiftModules; }; } namespace swift { namespace json { template <> struct ObjectTraits { static void mapping(Output &out, SwiftModuleTraceInfo &contents) { StringRef name = contents.Name.str(); out.mapRequired("name", name); out.mapRequired("path", contents.Path); out.mapRequired("isImportedDirectly", contents.IsImportedDirectly); out.mapRequired("supportsLibraryEvolution", contents.SupportsLibraryEvolution); } }; // Version notes: // 1. Keys: name, arch, swiftmodules // 2. New keys: version, swiftmodulesDetailedInfo template <> struct ObjectTraits { static void mapping(Output &out, LoadedModuleTraceFormat &contents) { out.mapRequired("version", contents.Version); StringRef name = contents.Name.str(); out.mapRequired("name", name); out.mapRequired("arch", contents.Arch); // The 'swiftmodules' key is kept for backwards compatibility. std::vector moduleNames; for (auto &m : contents.SwiftModules) moduleNames.push_back(m.Path); out.mapRequired("swiftmodules", moduleNames); out.mapRequired("swiftmodulesDetailedInfo", contents.SwiftModules); } }; } } static bool isClangOverlayOf(ModuleDecl *potentialOverlay, ModuleDecl *potentialUnderlying) { return !potentialOverlay->isNonSwiftModule() && potentialUnderlying->isNonSwiftModule() && potentialOverlay->getName() == potentialUnderlying->getName(); } // TODO: Delete this once changes from https://reviews.llvm.org/D83449 land on // apple/llvm-project's swift/main branch. template static bool contains(const SetLike &setLike, Item item) { return setLike.find(item) != setLike.end(); } /// Get a set of modules imported by \p module. /// /// By default, all imports are included. static void getImmediateImports( ModuleDecl *module, SmallPtrSetImpl &imports, ModuleDecl::ImportFilter importFilter = { ModuleDecl::ImportFilterKind::Exported, ModuleDecl::ImportFilterKind::Default, ModuleDecl::ImportFilterKind::ImplementationOnly, ModuleDecl::ImportFilterKind::SPIAccessControl, ModuleDecl::ImportFilterKind::ShadowedByCrossImportOverlay }) { SmallVector importList; module->getImportedModules(importList, importFilter); for (ImportedModule &import : importList) imports.insert(import.importedModule); } namespace { /// Helper type for computing (approximate) information about ABI-dependencies. /// /// This misses out on details such as typealiases and more. /// See the "isImportedDirectly" field above for more details. class ABIDependencyEvaluator { /// Map of ABIs exported by a particular module, excluding itself. /// /// For example, consider (primed letters represent Clang modules): /// \code /// - A is @_exported-imported by B /// - B is #imported by C' (via a compiler-generated umbrella header) /// - C' is @_exported-imported by C (Swift overlay) /// - D' is #imported by E' /// - D' is @_exported-imported by D (Swift overlay) /// - E' is @_exported-imported by E (Swift overlay) /// \endcode /// /// Then the \c abiExportMap will be /// \code /// { A: {}, B: {A}, C: {B}, C': {B}, D: {}, D': {}, E: {D}, E': {D'} } /// \endcode /// /// \b WARNING: Use \c reexposeImportedABI instead of inserting directly. llvm::DenseMap> abiExportMap; /// Stack for depth-first traversal. SmallVector searchStack; llvm::DenseSet visited; /// Helper function to handle invariant violations as crashes in debug mode. void crashOnInvariantViolation( llvm::function_ref f) const; /// Computes the ABI exports for \p importedModule and adds them to /// \p module's ABI exports. /// /// If \p includeImportedModule is true, also adds \p importedModule to /// \p module's ABI exports. /// /// Correct way to add entries to \c abiExportMap. void reexposeImportedABI(ModuleDecl *module, ModuleDecl *importedModule, bool includeImportedModule = true); /// Check if a Swift module is an overlay for some Clang module. /// /// FIXME: Delete this hack once SR-13363 is fixed and ModuleDecl has the /// right API which we can use directly. bool isOverlayOfClangModule(ModuleDecl *swiftModule); /// Check for cases where we have a fake cycle through an overlay. /// /// Sometimes, we have fake cycles in the import graph due to the Clang /// importer injecting overlays between Clang modules. These don't represent /// an actual cycle in the build, so we should ignore them. /// /// We check this lazily after detecting a cycle because it is difficult to /// determine at the point where we see the overlay whether it was incorrectly /// injected by the Clang importer or whether any of its imports will /// eventually lead to a cycle. /// /// For more details, see [NOTE: ABIDependencyEvaluator-fake-cycle-detection] /// /// \param startOfCycle A pointer to the element of \c searchStack where /// the module \em first appeared. /// /// \pre The module on top of \c searchStack is the same module as /// *startOfCycle. /// /// \pre searchStack.begin() <= startOfCycle < searchStack.end() bool isFakeCycleThroughOverlay(ModuleDecl **startOfCycle); /// Recursive step in computing ABI dependencies. /// /// Use this method instead of using the \c forClangModule/\c forSwiftModule /// methods. void computeABIDependenciesForModule(ModuleDecl *module); void computeABIDependenciesForSwiftModule(ModuleDecl *module); void computeABIDependenciesForClangModule(ModuleDecl *module); static void printModule(const ModuleDecl *module, llvm::raw_ostream &os); template static void printModuleSet(const SetLike &set, llvm::raw_ostream &os); public: ABIDependencyEvaluator() = default; ABIDependencyEvaluator(const ABIDependencyEvaluator &) = delete; ABIDependencyEvaluator(ABIDependencyEvaluator &&) = default; void getABIDependenciesForSwiftModule( ModuleDecl *module, SmallPtrSetImpl &abiDependencies); void printABIExportMap(llvm::raw_ostream &os) const; }; } // end anonymous namespace // See [NOTE: Bailing-vs-crashing-in-trace-emission]. // TODO: Use PrettyStackTrace instead? void ABIDependencyEvaluator::crashOnInvariantViolation( llvm::function_ref f) const { #ifndef NDEBUG std::string msg; llvm::raw_string_ostream os(msg); os << "error: invariant violation: "; f(os); llvm::report_fatal_error(os.str()); #endif } // [NOTE: Trace-Clang-submodule-complexity] // // A Clang module may have zero or more submodules. In practice, when traversing // the imports of a module, we observe that different submodules of the same // top-level module (almost) freely import each other. Despite this, we still // need to conceptually traverse the tree formed by the submodule relationship // (with the top-level module being the root). // // This needs to be taken care of in two ways: // 1. We need to make sure we only go towards the leaves. It's okay if we "jump" // branches, so long as we don't try to visit an ancestor when one of its // descendants is still on the traversal stack, so that we don't end up with // arbitrarily complex intra-module cycles. // See also: [NOTE: Intra-module-leafwards-traversal]. // 2. When adding entries to the ABI export map, we need to avoid marking // dependencies within the same top-level module. This step is needed in // addition to step 1 to avoid creating cycles like // Overlay -> Underlying -> Submodule -> Overlay. void ABIDependencyEvaluator::reexposeImportedABI( ModuleDecl *module, ModuleDecl *importedModule, bool includeImportedModule) { if (module == importedModule) { crashOnInvariantViolation([&](llvm::raw_string_ostream &os) { os << "module "; printModule(module, os); os << " imports itself!\n"; }); return; } auto addToABIExportMap = [this](ModuleDecl *module, ModuleDecl *reexport) { if (module == reexport) { crashOnInvariantViolation([&](llvm::raw_string_ostream &os){ os << "expected module "; printModule(reexport, os); os << " to not re-export itself\n"; }); return; } if (reexport->isNonSwiftModule() && module->isNonSwiftModule() && module->getTopLevelModule() == reexport->getTopLevelModule()) { // Dependencies within the same top-level Clang module are not useful. // See also: [NOTE: Trace-Clang-submodule-complexity]. return; } // We only care about dependencies across top-level modules and we want to // avoid exploding abiExportMap with submodules. So we only insert entries // after calling getTopLevelModule(). if (::isClangOverlayOf(module, reexport)) { // For overlays, we need to have a dependency on the underlying module. // Otherwise, we might accidentally create a Swift -> Swift cycle. abiExportMap[module].insert( reexport->getTopLevelModule(/*preferOverlay*/false)); return; } abiExportMap[module].insert( reexport->getTopLevelModule(/*preferOverlay*/true)); }; computeABIDependenciesForModule(importedModule); if (includeImportedModule) { addToABIExportMap(module, importedModule); } // Force creation of default value if missing. This prevents abiExportMap from // growing (and moving) when calling addToABIExportMap. If abiExportMap gets // moved, then abiExportMap[importedModule] will be moved, forcing us to // create a defensive copy to avoid iterator invalidation on move. (void)abiExportMap[module]; for (auto reexportedModule: abiExportMap[importedModule]) addToABIExportMap(module, reexportedModule); } bool ABIDependencyEvaluator::isOverlayOfClangModule(ModuleDecl *swiftModule) { assert(!swiftModule->isNonSwiftModule()); llvm::SmallPtrSet importList; ::getImmediateImports(swiftModule, importList, {ModuleDecl::ImportFilterKind::Exported}); bool isOverlay = llvm::any_of(importList, [&](ModuleDecl *importedModule) -> bool { return isClangOverlayOf(swiftModule, importedModule); }); return isOverlay; } // [NOTE: ABIDependencyEvaluator-fake-cycle-detection] // // First, let's consider a concrete example. // - In Clang-land, ToyKit #imports CoreDoll. // - The Swift overlay for CoreDoll imports both CoreDoll and ToyKit. // Importing ToyKit from CoreDoll's overlay informally violates the layering // of frameworks, but it doesn't actually create any cycles in the build // dependencies. // ┌───────────────────────────┐ // ┌───│ CoreDoll.swiftmodule │ // │ └───────────────────────────┘ // │ │ // import ToyKit @_exported import CoreDoll // │ │ // │ │ // ▼ │ // ┌──────────────────────────┐ │ // │ ToyKit (ToyKit/ToyKit.h) │ │ // └──────────────────────────┘ │ // │ │ // #import │ // │ │ // ▼ │ // ┌──────────────────────────────┐ │ // │CoreDoll (CoreDoll/CoreDoll.h)│◀──┘ // └──────────────────────────────┘ // // Say we are trying to build a Swift module that imports ToyKit. Due to how // module loading works, the Clang importer inserts the CoreDoll overlay // between the ToyKit and CoreDoll Clang modules, creating a cycle in the // import graph. // // ┌──────────────────────────┐ // │ ToyKit (ToyKit/ToyKit.h) │◀──────────┐ // └──────────────────────────┘ │ // │ │ // #import import ToyKit // │ │ // ▼ │ // ┌────────────────────────────┐ │ // │ CoreDoll.swiftmodule │─────────┘ // └────────────────────────────┘ // │ // @_exported import CoreDoll // │ // ▼ // ┌──────────────────────────────┐ // │CoreDoll (CoreDoll/CoreDoll.h)│ // └──────────────────────────────┘ // // This means that, at some point, searchStack will look like: // // [others] → ToyKit → CoreDoll (overlay) → ToyKit // // In the general case, there may be arbitrarily many modules in the cycle, // including submodules. // // [others] → ToyKit → [others] → CoreDoll (overlay) → [others] → ToyKit // // where "[others]" indicates 0 or more modules of any kind. // // To detect this, we check that the start of the cycle is a Clang module and // that there is at least one overlay between it and its recurrence at the end // of the searchStack. If so, we assume we have detected a benign cycle which // can be safely ignored. bool ABIDependencyEvaluator::isFakeCycleThroughOverlay( ModuleDecl **startOfCycle) { assert(startOfCycle >= searchStack.begin() && startOfCycle < searchStack.end() && "startOfCycleIter points to an element in searchStack"); // The startOfCycle module must be a Clang module. if (!(*startOfCycle)->isNonSwiftModule()) return false; // Next, we must have zero or more modules followed by a Swift overlay for a // Clang module. return std::any_of(startOfCycle + 1, searchStack.end(), [this](ModuleDecl *module) { return !module->isNonSwiftModule() && isOverlayOfClangModule(module); }); } void ABIDependencyEvaluator::computeABIDependenciesForModule( ModuleDecl *module) { auto moduleIter = llvm::find(searchStack, module); if (moduleIter != searchStack.end()) { if (isFakeCycleThroughOverlay(moduleIter)) return; crashOnInvariantViolation([&](llvm::raw_string_ostream &os) { os << "unexpected cycle in import graph!\n"; for (auto m: searchStack) { printModule(m, os); if (!m->isNonSwiftModule()) { os << " (isOverlay = " << isOverlayOfClangModule(m) << ")"; } os << "\ndepends on "; } printModule(module, os); os << '\n'; }); return; } if (::contains(visited, module)) return; searchStack.push_back(module); if (module->isNonSwiftModule()) computeABIDependenciesForClangModule(module); else computeABIDependenciesForSwiftModule(module); searchStack.pop_back(); visited.insert(module); } void ABIDependencyEvaluator::computeABIDependenciesForSwiftModule( ModuleDecl *module) { SmallPtrSet allImports; ::getImmediateImports(module, allImports); for (auto import: allImports) { computeABIDependenciesForModule(import); if (::isClangOverlayOf(module, import)) { reexposeImportedABI(module, import, /*includeImportedModule=*/false); } } SmallPtrSet reexportedImports; ::getImmediateImports(module, reexportedImports, {ModuleDecl::ImportFilterKind::Exported}); for (auto reexportedImport: reexportedImports) { reexposeImportedABI(module, reexportedImport); } } void ABIDependencyEvaluator::computeABIDependenciesForClangModule( ModuleDecl *module) { SmallPtrSet imports; ::getImmediateImports(module, imports); for (auto import: imports) { // There are three cases here which can potentially create cycles: // // 1. Clang modules importing the stdlib. // See [NOTE: Pure-Clang-modules-privately-import-stdlib]. // 2. Overlay S @_exported-imports underlying module S' and another Clang // module C'. C' (transitively) #imports S' but it gets treated as if // C' imports S. This creates a cycle: S -> C' -> ... -> S. // In practice, this case is hit for // Darwin (Swift) -> SwiftOverlayShims (Clang) -> Darwin (Swift). // We may also hit this in a slightly different direction, in case // the module directly imports SwiftOverlayShims: // SwiftOverlayShims -> Darwin (Swift) -> SwiftOverlayShims // The latter is handled later by isFakeCycleThroughOverlay. // 3. [NOTE: Intra-module-leafwards-traversal] // Cycles within the same top-level module. // These don't matter for us, since we only care about the dependency // graph at the granularity of top-level modules. So we ignore these // by only considering parent -> submodule dependencies. // See also [NOTE: Trace-Clang-submodule-complexity]. if (import->isStdlibModule()) { continue; } if (!import->isNonSwiftModule() && isOverlayOfClangModule(import) && llvm::find(searchStack, import) != searchStack.end()) { continue; } if (import->isNonSwiftModule() && module->getTopLevelModule() == import->getTopLevelModule() && (module == import || !import->findUnderlyingClangModule() ->isSubModuleOf(module->findUnderlyingClangModule()))) { continue; } computeABIDependenciesForModule(import); reexposeImportedABI(module, import); } } void ABIDependencyEvaluator::getABIDependenciesForSwiftModule( ModuleDecl *module, SmallPtrSetImpl &abiDependencies) { computeABIDependenciesForModule(module); SmallPtrSet allImports; ::getImmediateImports(module, allImports); for (auto directDependency: allImports) { abiDependencies.insert(directDependency); for (auto exposedDependency: abiExportMap[directDependency]) { abiDependencies.insert(exposedDependency); } } } void ABIDependencyEvaluator::printModule( const ModuleDecl *module, llvm::raw_ostream &os) { module->getReverseFullModuleName().printForward(os); os << (module->isNonSwiftModule() ? " (Clang)" : " (Swift)"); os << " @ " << llvm::format("0x%llx", reinterpret_cast(module)); } template void ABIDependencyEvaluator::printModuleSet( const SetLike &set, llvm::raw_ostream &os) { os << "{ "; for (auto module: set) { printModule(module, os); os << ", "; } os << "}"; } void ABIDependencyEvaluator::printABIExportMap(llvm::raw_ostream &os) const { os << "ABI Export Map {{\n"; for (auto &entry: abiExportMap) { printModule(entry.first, os); os << " : "; printModuleSet(entry.second, os); os << "\n"; } os << "}}\n"; } /// Compute the per-module information to be recorded in the trace file. // // The most interesting/tricky thing here is _which_ paths get recorded in // the trace file as dependencies. It depends on how the module was synthesized. // The key points are: // // 1. Paths to swiftmodules in the module cache or in the prebuilt cache are not // recorded - Precondition: the corresponding path to the swiftinterface must // already be present as a key in pathToModuleDecl. // 2. swiftmodules next to a swiftinterface are saved if they are up-to-date. // // FIXME: Use the VFS instead of handling paths directly. We are particularly // sloppy about handling relative paths in the dependency tracker. static void computeSwiftModuleTraceInfo( const SmallPtrSetImpl &abiDependencies, const llvm::DenseMap &pathToModuleDecl, const DependencyTracker &depTracker, StringRef prebuiltCachePath, std::vector &traceInfo) { SmallString<256> buffer; std::string errMsg; llvm::raw_string_ostream err(errMsg); // FIXME: Use PrettyStackTrace instead. auto errorUnexpectedPath = [&pathToModuleDecl](llvm::raw_string_ostream &errStream) { errStream << "The module <-> path mapping we have is:\n"; for (auto &m: pathToModuleDecl) errStream << m.second->getName() << " <-> " << m.first << '\n'; llvm::report_fatal_error(errStream.str()); }; using namespace llvm::sys; auto computeAdjacentInterfacePath = [](SmallVectorImpl &modPath) { auto swiftInterfaceExt = file_types::getExtension(file_types::TY_SwiftModuleInterfaceFile); path::replace_extension(modPath, swiftInterfaceExt); }; for (auto &depPath : depTracker.getDependencies()) { // Decide if this is a swiftmodule based on the extension of the raw // dependency path, as the true file may have a different one. // For example, this might happen when the canonicalized path points to // a Content Addressed Storage (CAS) location. auto moduleFileType = file_types::lookupTypeForExtension(path::extension(depPath)); auto isSwiftmodule = moduleFileType == file_types::TY_SwiftModuleFile; auto isSwiftinterface = moduleFileType == file_types::TY_SwiftModuleInterfaceFile; if (!(isSwiftmodule || isSwiftinterface)) continue; auto dep = pathToModuleDecl.find(depPath); if (dep != pathToModuleDecl.end()) { // Great, we recognize the path! Check if the file is still around. ModuleDecl *depMod = dep->second; if(depMod->isResilient() && !isSwiftinterface) { // FIXME: Ideally, we would check that the swiftmodule has a // swiftinterface next to it. Tracked by rdar://problem/56351399. } // FIXME: Better error handling StringRef realDepPath = fs::real_path(depPath, buffer, /*expand_tile*/true) ? StringRef(depPath) // Couldn't find the canonical path, assume // this is good enough. : buffer.str(); bool isImportedDirectly = ::contains(abiDependencies, depMod); traceInfo.push_back( {/*Name=*/ depMod->getName(), /*Path=*/ realDepPath.str(), // TODO: There is an edge case which is not handled here. // When we build a framework using -import-underlying-module, or an // app/test using -import-objc-header, we should look at the direct // imports of the bridging modules, and mark those as our direct // imports. // TODO: Add negative test cases for the comment above. // TODO: Describe precise semantics of "isImportedDirectly". /*IsImportedDirectly=*/ isImportedDirectly, /*SupportsLibraryEvolution=*/ depMod->isResilient()}); buffer.clear(); continue; } // If the depTracker had an interface, that means that we must've // built a swiftmodule from that interface, so we should have that // filename available. if (isSwiftinterface) { err << "Unexpected path for swiftinterface file:\n" << depPath << "\n"; errorUnexpectedPath(err); } // Skip cached modules in the prebuilt cache. We will add the corresponding // swiftinterface from the SDK directly, but this isn't checked. :-/ // // FIXME: This is incorrect if both paths are not relative w.r.t. to the // same root. if (StringRef(depPath).startswith(prebuiltCachePath)) continue; // If we have a swiftmodule next to an interface, that interface path will // be saved (not checked), so don't save the path to this swiftmodule. SmallString<256> moduleAdjacentInterfacePath(depPath); computeAdjacentInterfacePath(moduleAdjacentInterfacePath); if (::contains(pathToModuleDecl, moduleAdjacentInterfacePath)) continue; // FIXME: The behavior of fs::exists for relative paths is undocumented. // Use something else instead? if (fs::exists(moduleAdjacentInterfacePath)) { // This should be an error but it is not because of funkiness around // compatible modules such as us having both armv7s.swiftinterface // and armv7.swiftinterface in the dependency tracker. continue; } buffer.clear(); // We might land here when we have a arm.swiftmodule in the cache path // which added a dependency on a arm.swiftinterface (which was not loaded). } // Almost a re-implementation of reversePathSortedFilenames :(. std::sort( traceInfo.begin(), traceInfo.end(), [](const SwiftModuleTraceInfo &m1, const SwiftModuleTraceInfo &m2) -> bool { return std::lexicographical_compare( m1.Path.rbegin(), m1.Path.rend(), m2.Path.rbegin(), m2.Path.rend()); }); } // [NOTE: Bailing-vs-crashing-in-trace-emission] There are certain edge cases // in trace emission where an invariant that you think should hold does not hold // in practice. For example, sometimes we have seen modules without any // corresponding filename. // // Since the trace is a supplementary output for build system consumption, it // it better to emit it on a best-effort basis instead of crashing and failing // the build. // // Moreover, going forward, it would be nice if trace emission were more robust // so we could emit the trace on a best-effort basis even if the dependency // graph is ill-formed, so that the trace can be used as a debugging aid. static bool emitLoadedModuleTraceIfNeeded(ModuleDecl *mainModule, DependencyTracker *depTracker, StringRef prebuiltCachePath, StringRef loadedModuleTracePath) { ASTContext &ctxt = mainModule->getASTContext(); assert(!ctxt.hadError() && "We should've already exited earlier if there was an error."); if (loadedModuleTracePath.empty()) return false; std::error_code EC; llvm::raw_fd_ostream out(loadedModuleTracePath, EC, llvm::sys::fs::F_Append); if (out.has_error() || EC) { ctxt.Diags.diagnose(SourceLoc(), diag::error_opening_output, loadedModuleTracePath, EC.message()); out.clear_error(); return true; } SmallPtrSet abiDependencies; { ABIDependencyEvaluator evaluator{}; evaluator.getABIDependenciesForSwiftModule(mainModule, abiDependencies); } llvm::DenseMap pathToModuleDecl; for (const auto &module : ctxt.getLoadedModules()) { ModuleDecl *loadedDecl = module.second; if (!loadedDecl) llvm::report_fatal_error("Expected loaded modules to be non-null."); if (loadedDecl == mainModule) continue; if (loadedDecl->getModuleFilename().empty()) { // FIXME: rdar://problem/59853077 // Ideally, this shouldn't happen. As a temporary workaround, avoid // crashing with a message while we investigate the problem. llvm::errs() << "WARNING: Module '" << loadedDecl->getName().str() << "' has an empty filename. This is probably an " << "invariant violation.\n" << "Please report it as a compiler bug.\n"; continue; } pathToModuleDecl.insert( std::make_pair(loadedDecl->getModuleFilename(), loadedDecl)); } std::vector swiftModules; computeSwiftModuleTraceInfo(abiDependencies, pathToModuleDecl, *depTracker, prebuiltCachePath, swiftModules); LoadedModuleTraceFormat trace = { /*version=*/LoadedModuleTraceFormat::CurrentVersion, /*name=*/mainModule->getName(), /*arch=*/ctxt.LangOpts.Target.getArchName().str(), swiftModules}; // raw_fd_ostream is unbuffered, and we may have multiple processes writing, // so first write to memory and then dump the buffer to the trace file. std::string stringBuffer; { llvm::raw_string_ostream memoryBuffer(stringBuffer); json::Output jsonOutput(memoryBuffer, /*UserInfo=*/{}, /*PrettyPrint=*/false); json::jsonize(jsonOutput, trace, /*Required=*/true); } stringBuffer += "\n"; out << stringBuffer; return true; } static void emitLoadedModuleTraceForAllPrimariesIfNeeded(ModuleDecl *mainModule, DependencyTracker *depTracker, const FrontendOptions &opts) { opts.InputsAndOutputs.forEachInputProducingSupplementaryOutput( [&](const InputFile &input) -> bool { return emitLoadedModuleTraceIfNeeded( mainModule, depTracker, opts.PrebuiltModuleCachePath, input.loadedModuleTracePath()); }); } /// Gets an output stream for the provided output filename, or diagnoses to the /// provided AST Context and returns null if there was an error getting the /// stream. static std::unique_ptr getFileOutputStream(StringRef OutputFilename, ASTContext &Ctx) { std::error_code errorCode; auto os = std::make_unique( OutputFilename, errorCode, llvm::sys::fs::F_None); if (errorCode) { Ctx.Diags.diagnose(SourceLoc(), diag::error_opening_output, OutputFilename, errorCode.message()); return nullptr; } return os; } /// Writes the Syntax tree to the given file static bool emitSyntax(SourceFile &SF, StringRef OutputFilename) { auto os = getFileOutputStream(OutputFilename, SF.getASTContext()); if (!os) return true; json::Output jsonOut(*os, /*UserInfo=*/{}, /*PrettyPrint=*/false); auto Root = SF.getSyntaxRoot().getRaw(); jsonOut << *Root; *os << "\n"; return false; } /// Writes SIL out to the given file. static bool writeSIL(SILModule &SM, ModuleDecl *M, const SILOptions &Opts, StringRef OutputFilename) { auto OS = getFileOutputStream(OutputFilename, M->getASTContext()); if (!OS) return true; SM.print(*OS, M, Opts); return M->getASTContext().hadError(); } static bool writeSIL(SILModule &SM, const PrimarySpecificPaths &PSPs, const CompilerInstance &Instance, const SILOptions &Opts) { return writeSIL(SM, Instance.getMainModule(), Opts, PSPs.OutputFilename); } /// Prints the Objective-C "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::printAsObjC static bool printAsObjCIfNeeded(StringRef outputPath, ModuleDecl *M, StringRef bridgingHeader, bool moduleIsPublic) { if (outputPath.empty()) return false; return withOutputFile(M->getDiags(), outputPath, [&](raw_ostream &out) -> bool { auto requiredAccess = moduleIsPublic ? AccessLevel::Public : AccessLevel::Internal; return printAsObjC(out, M, bridgingHeader, requiredAccess); }); } /// 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(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 withOutputFile(diags, outputPath, [M, Opts](raw_ostream &out) -> bool { return swift::emitSwiftInterface(out, Opts, M); }); } namespace { /// If there is an error with fixits it writes the fixits as edits in json /// format. class JSONFixitWriter : public DiagnosticConsumer, public migrator::FixitFilter { std::string FixitsOutputPath; std::unique_ptr OSPtr; bool FixitAll; std::vector AllEdits; public: JSONFixitWriter(std::string fixitsOutputPath, const DiagnosticOptions &DiagOpts) : FixitsOutputPath(fixitsOutputPath), FixitAll(DiagOpts.FixitCodeForAllDiagnostics) {} private: void handleDiagnostic(SourceManager &SM, const DiagnosticInfo &Info) override { if (!(FixitAll || shouldTakeFixit(Info))) return; for (const auto &Fix : Info.FixIts) { AllEdits.push_back({SM, Fix.getRange(), Fix.getText().str()}); } } bool finishProcessing() override { std::error_code EC; std::unique_ptr OS; OS.reset(new llvm::raw_fd_ostream(FixitsOutputPath, EC, llvm::sys::fs::F_None)); if (EC) { // Create a temporary diagnostics engine to print the error to stderr. SourceManager dummyMgr; DiagnosticEngine DE(dummyMgr); PrintingDiagnosticConsumer PDC; DE.addConsumer(PDC); DE.diagnose(SourceLoc(), diag::cannot_open_file, FixitsOutputPath, EC.message()); return true; } swift::writeEditsInJson(llvm::makeArrayRef(AllEdits), *OS); return false; } }; } // anonymous namespace // 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->LocalTypeDecls.size(); C.NumObjCMethods += SF->ObjCMethods.size(); SmallVector operators; SF->getOperatorDecls(operators); C.NumOperators += operators.size(); SmallVector groups; SF->getPrecedenceGroups(groups); C.NumPrecedenceGroups += groups.size(); auto bufID = SF->getBufferID(); if (bufID.hasValue()) { C.NumSourceLines += SM.getEntireTextForBuffer(bufID.getValue()).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(); } 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(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( Instance.getASTContext().getClangModuleLoader()); auto &ImporterOpts = Invocation.getClangImporterOptions(); auto &PCHOutDir = ImporterOpts.PrecompiledHeaderOutputDir; if (!PCHOutDir.empty()) { // Create or validate a persistent PCH. auto SwiftPCHHash = Invocation.getPCHHash(); auto PCH = clangImporter->getOrCreatePCH(ImporterOpts, SwiftPCHHash); return !PCH.hasValue(); } return clangImporter->emitBridgingPCH( opts.InputsAndOutputs.getFilenameOfFirstInput(), opts.InputsAndOutputs.getSingleOutputFilename()); } static bool precompileClangModule(const CompilerInstance &Instance) { const auto &opts = Instance.getInvocation().getFrontendOptions(); auto clangImporter = static_cast( 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( 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); return ModuleInterfaceLoader::buildSwiftModuleFromSwiftInterface( Instance.getSourceMgr(), Instance.getDiags(), Invocation.getSearchPathOptions(), Invocation.getLangOptions(), Invocation.getClangImporterOptions(), Invocation.getClangModuleCachePath(), PrebuiltCachePath, Invocation.getModuleName(), InputPath, Invocation.getOutputFilename(), FEOpts.SerializeModuleInterfaceDependencyHashes, FEOpts.shouldTrackSystemDependencies(), LoaderOpts); } 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> 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(); std::unique_ptr 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 CompilerInvocation &Invocation, ASTContext &Context) { auto verifyGenericSignaturesInModule = Invocation.getFrontendOptions().VerifyGenericSignaturesInModule; if (verifyGenericSignaturesInModule.empty()) return; if (auto module = Context.getModuleByName(verifyGenericSignaturesInModule)) GenericSignatureBuilder::verifyGenericSignaturesInModule(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) { auto primaryFiles = Instance.getPrimarySourceFiles(); if (!primaryFiles.empty()) { for (SourceFile *sourceFile: primaryFiles) { auto PSPs = Instance.getPrimarySpecificPathsForSourceFile(*sourceFile); auto OutputFilename = PSPs.OutputFilename; auto OS = getFileOutputStream(OutputFilename, Instance.getASTContext()); sourceFile->dump(*OS, /*parseIfNeeded*/ 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); SF.dump(llvm::outs(), /*parseIfNeeded*/ true); } return Instance.getASTContext().hadError(); } static bool emitReferenceDependencies(CompilerInstance &Instance, SourceFile *const SF, StringRef outputPath) { const auto alsoEmitDotFile = Instance.getInvocation() .getLangOptions() .EmitFineGrainedDependencySourcefileDotFiles; // 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 + "~"); using SourceFileDepGraph = fine_grained_dependencies::SourceFileDepGraph; return fine_grained_dependencies::withReferenceDependencies( SF, *Instance.getDependencyTracker(), outputPath, alsoEmitDotFile, [&](SourceFileDepGraph &&g) -> bool { const bool hadError = fine_grained_dependencies::writeFineGrainedDependencyGraphToPath( Instance.getDiags(), outputPath, g); // If path is stdout, cannot read it back, so check for "-" assert(outputPath == "-" || g.verifyReadsWhatIsWritten(outputPath)); if (alsoEmitDotFile) g.emitDotFile(outputPath, Instance.getDiags()); return hadError; }); } static void emitReferenceDependenciesForAllPrimaryInputsIfNeeded( CompilerInstance &Instance) { const auto &Invocation = Instance.getInvocation(); if (Invocation.getFrontendOptions() .InputsAndOutputs.hasReferenceDependenciesPath() && Instance.getPrimarySourceFiles().empty()) { Instance.getDiags().diagnose( SourceLoc(), diag::emit_reference_dependencies_without_primary_file); return; } for (auto *SF : Instance.getPrimarySourceFiles()) { const std::string &referenceDependenciesFilePath = Invocation.getReferenceDependenciesFilePathForPrimary( SF->getFilename()); if (referenceDependenciesFilePath.empty()) { continue; } emitReferenceDependencies(Instance, SF, referenceDependenciesFilePath); } } static void emitSwiftRangesForAllPrimaryInputsIfNeeded(CompilerInstance &Instance) { const auto &Invocation = Instance.getInvocation(); if (Invocation.getFrontendOptions().InputsAndOutputs.hasSwiftRangesPath() && Instance.getPrimarySourceFiles().empty()) { Instance.getDiags().diagnose(SourceLoc(), diag::emit_swift_ranges_without_primary_file); return; } for (auto *SF : Instance.getPrimarySourceFiles()) { const std::string &swiftRangesFilePath = Invocation.getSwiftRangesFilePathForPrimary(SF->getFilename()); if (!swiftRangesFilePath.empty()) { (void)Instance.emitSwiftRanges(Instance.getDiags(), SF, swiftRangesFilePath); } } } static void emitCompiledSourceForAllPrimaryInputsIfNeeded( CompilerInstance &Instance) { const auto &Invocation = Instance.getInvocation(); if (Invocation.getFrontendOptions() .InputsAndOutputs.hasCompiledSourcePath() && Instance.getPrimarySourceFiles().empty()) { Instance.getDiags().diagnose( SourceLoc(), diag::emit_compiled_source_without_primary_file); return; } for (auto *SF : Instance.getPrimarySourceFiles()) { const std::string &compiledSourceFilePath = Invocation.getCompiledSourceFilePathForPrimary(SF->getFilename()); if (!compiledSourceFilePath.empty()) { (void)Instance.emitCompiledSource(Instance.getDiags(), SF, compiledSourceFilePath); } } } 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().CrossModuleOptimization) { Instance.getDiags().diagnose(SourceLoc(), diag::tbd_not_supported_with_cmo); return false; } const std::string &TBDPath = Invocation.getTBDPathForWholeModule(); return writeTBD(Instance.getMainModule(), TBDPath, tbdOpts); } static std::string changeToLdAdd(StringRef ldHide) { SmallString<64> SymbolBuffer; llvm::raw_svector_ostream OS(SymbolBuffer); auto Parts = ldHide.split("$hide$"); assert(!Parts.first.empty()); assert(!Parts.second.empty()); OS << Parts.first << "$add$" << Parts.second; return OS.str().str(); } static bool writeLdAddCFileIfNeeded(CompilerInstance &Instance) { const auto &Invocation = Instance.getInvocation(); const auto &frontendOpts = Invocation.getFrontendOptions(); if (!frontendOpts.InputsAndOutputs.isWholeModule()) return false; auto Path = Invocation.getLdAddCFileOutputPathForWholeModule(); if (Path.empty()) return false; if (!frontendOpts.InputsAndOutputs.isWholeModule()) { Instance.getDiags().diagnose(SourceLoc(), diag::tbd_only_supported_in_whole_module); return true; } if (!Invocation.getTBDGenOptions().ModuleInstallNameMapPath.empty()) { Instance.getDiags().diagnose(SourceLoc(), diag::linker_directives_choice_confusion); return true; } auto tbdOpts = Invocation.getTBDGenOptions(); tbdOpts.LinkerDirectivesOnly = true; auto *module = Instance.getMainModule(); auto ldSymbols = getPublicSymbols(TBDGenDescriptor::forModule(module, tbdOpts)); std::error_code EC; llvm::raw_fd_ostream OS(Path, EC, llvm::sys::fs::F_None); if (EC) { Instance.getDiags().diagnose(SourceLoc(), diag::error_opening_output, Path, EC.message()); return true; } OS << "// Automatically generated C source file from the Swift compiler \n" << "// to add removed symbols back to the high-level framework for deployment\n" << "// targets prior to the OS version when these symbols were moved to\n" << "// a low-level framework " << module->getName().str() << ".\n\n"; unsigned Idx = 0; for (auto &S: ldSymbols) { SmallString<32> NameBuffer; llvm::raw_svector_ostream NameOS(NameBuffer); NameOS << "ldAdd_" << Idx; OS << "extern const char " << NameOS.str() << " __asm(\"" << changeToLdAdd(S) << "\");\n"; OS << "const char " << NameOS.str() << " = 0;\n"; ++ Idx; } return false; } static bool performCompileStepsPostSILGen(CompilerInstance &Instance, std::unique_ptr SM, ModuleOrSourceFile MSF, const PrimarySpecificPaths &PSPs, int &ReturnValue, FrontendObserver *observer); static bool performCompileStepsPostSema(CompilerInstance &Instance, int &ReturnValue, FrontendObserver *observer) { const auto &Invocation = Instance.getInvocation(); const SILOptions &SILOpts = Invocation.getSILOptions(); const FrontendOptions &opts = Invocation.getFrontendOptions(); 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. auto SM = performASTLowering(mod, Instance.getSILTypes(), SILOpts); const PrimarySpecificPaths PSPs = Instance.getPrimarySpecificPathsForWholeModuleOptimizationMode(); return performCompileStepsPostSILGen(Instance, std::move(SM), mod, PSPs, ReturnValue, observer); } // 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()) { auto SM = performASTLowering(*PrimaryFile, Instance.getSILTypes(), SILOpts); const PrimarySpecificPaths PSPs = Instance.getPrimarySpecificPathsForSourceFile(*PrimaryFile); 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(fileUnit)) if (opts.InputsAndOutputs.isInputPrimary(SASTF->getFilename())) { auto SM = performASTLowering(*SASTF, Instance.getSILTypes(), SILOpts); const PrimarySpecificPaths &PSPs = Instance.getPrimarySpecificPathsForPrimary(SASTF->getFilename()); 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 &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 (opts.InputsAndOutputs.hasObjCHeaderOutputPath()) { std::string BridgingHeaderPathForPrint; if (!opts.ImplicitObjCHeaderPath.empty()) { if (opts.BridgingHeaderDirForPrint.hasValue()) { // User specified preferred directory for including, use that dir. llvm::SmallString<32> Buffer(*opts.BridgingHeaderDirForPrint); llvm::sys::path::append(Buffer, llvm::sys::path::filename(opts.ImplicitObjCHeaderPath)); BridgingHeaderPathForPrint = (std::string)Buffer; } else { // By default, include the given bridging header path directly. BridgingHeaderPathForPrint = opts.ImplicitObjCHeaderPath; } } hadAnyError |= printAsObjCIfNeeded( Invocation.getObjCHeaderOutputPathForAtMostOnePrimary(), Instance.getMainModule(), BridgingHeaderPathForPrint, Invocation.isModuleExternallyConsumed(Instance.getMainModule())); } if (opts.InputsAndOutputs.hasModuleInterfaceOutputPath()) { hadAnyError |= printModuleInterfaceIfNeeded( Invocation.getModuleInterfaceOutputPathForWholeModule(), Invocation.getModuleInterfaceOptions(), Invocation.getLangOptions(), Instance.getMainModule()); } if (opts.InputsAndOutputs.hasPrivateModuleInterfaceOutputPath()) { // Copy the settings from the module interface ModuleInterfaceOptions privOpts = Invocation.getModuleInterfaceOptions(); privOpts.PrintSPIs = true; hadAnyError |= printModuleInterfaceIfNeeded( Invocation.getPrivateModuleInterfaceOutputPathForWholeModule(), privOpts, Invocation.getLangOptions(), Instance.getMainModule()); } { hadAnyError |= writeTBDIfNeeded(Instance); } { hadAnyError |= writeLdAddCFileIfNeeded(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 Filenames; auto dumpFile = [&](SourceFile *SF) -> bool { SmallString<512> TempBuf; llvm::raw_svector_ostream TempOS(TempBuf); PrintOptions PO = PrintOptions::printInterface(); PO.PrintOriginalSourceText = true; PO.Indent = 2; PO.PrintAccess = false; PO.SkipUnderscoredStdlibProtocols = 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(FU)) if (dumpFile(SF)) return; } } /// 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); // 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()); } // Verify the AST for all the modules we've loaded. ctx.verifyAllLoadedModules(); // Verify generic signatures if we've been asked to. verifyGenericSignaturesIfNeeded(Invocation, 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); emitAnyWholeModulePostTypeCheckSupplementaryOutputs(Instance); dumpAPIIfNeeded(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()); } } // 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. if (FrontendOptions::doesActionRequireSwiftStandardLibrary(action)) { emitIndexData(Instance); } // Emit dependencies. emitReferenceDependenciesForAllPrimaryInputsIfNeeded(Instance); emitMakeDependenciesIfNeeded(Instance.getDiags(), Instance.getDependencyTracker(), opts); // Emit information about the parsed primaries. emitSwiftRangesForAllPrimaryInputsIfNeeded(Instance); emitCompiledSourceForAllPrimaryInputsIfNeeded(Instance); } static bool printSwiftVersion(const CompilerInvocation &Invocation) { llvm::outs() << version::getSwiftFullVersion( version::Version::getCurrentLanguageVersion()) << '\n'; llvm::outs() << "Target: " << Invocation.getLangOptions().Target.str() << '\n'; return false; } static void printSingleFrontendOpt(llvm::opt::OptTable &table, options::ID id, llvm::raw_ostream &OS) { if (table.getOption(id).hasFlag(options::FrontendOption)) { auto name = StringRef(table.getOptionName(id)); if (!name.empty()) { OS << " \"" << name << "\",\n"; } } } static bool printSwiftFeature(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::F_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 table = createSwiftOptTable(); out << "{\n"; SWIFT_DEFER { out << "}\n"; }; out << " \"SupportedArguments\": [\n"; #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ HELPTEXT, METAVAR, VALUES) \ printSingleFrontendOpt(*table, swift::options::OPT_##ID, out); #include "swift/Option/Options.inc" #undef OPTION out << " \"LastOption\"\n"; out << " ],\n"; out << " \"SupportedFeatures\": [\n"; // Print supported featur names here. out << " \"LastFeature\"\n"; out << " ]\n"; return false; } static bool withSemanticAnalysis(CompilerInstance &Instance, FrontendObserver *observer, llvm::function_ref cont) { 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); if (Instance.getASTContext().hadError()) return true; return cont(Instance); } static bool performScanDependencies(CompilerInstance &Instance) { auto batchScanInput = Instance.getASTContext().SearchPathOpts.BatchScanInputFilePath; if (batchScanInput.empty()) { return scanDependencies(Instance); } else { return batchScanModuleDependencies(Instance, batchScanInput); } } 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(file)) (void)SF->getTopLevelDecls(); } return MainModule.getASTContext().hadError(); } static bool performAction(CompilerInstance &Instance, int &ReturnValue, FrontendObserver *observer) { const auto &opts = Instance.getInvocation().getFrontendOptions(); auto &Context = Instance.getASTContext(); switch (Instance.getInvocation().getFrontendOptions().RequestedAction) { // MARK: Trivial Actions case FrontendOptions::ActionType::NoneAction: return Context.hadError(); case FrontendOptions::ActionType::PrintVersion: return printSwiftVersion(Instance.getInvocation()); case FrontendOptions::ActionType::PrintFeature: return printSwiftFeature(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); case FrontendOptions::ActionType::DumpAST: { // FIXME: -dump-ast expects to be able to write output even if type checking // fails which does not cleanly fit the model \c withSemanticAnalysis is // trying to impose. Once there is a request for the "semantic AST", this // point is moot. Instance.performSema(); return dumpAST(Instance); } case FrontendOptions::ActionType::PrintAST: return withSemanticAnalysis( Instance, observer, [](CompilerInstance &Instance) { getPrimaryOrMainSourceFile(Instance).print( llvm::outs(), PrintOptions::printEverything()); return Instance.getASTContext().hadError(); }); case FrontendOptions::ActionType::DumpScopeMaps: return withSemanticAnalysis( Instance, observer, [](CompilerInstance &Instance) { return dumpAndPrintScopeMap(Instance, getPrimaryOrMainSourceFile(Instance)); }); case FrontendOptions::ActionType::DumpTypeRefinementContexts: return withSemanticAnalysis( Instance, observer, [](CompilerInstance &Instance) { getPrimaryOrMainSourceFile(Instance).getTypeRefinementContext()->dump( llvm::errs(), Instance.getASTContext().SourceMgr); return Instance.getASTContext().hadError(); }); case FrontendOptions::ActionType::DumpInterfaceHash: getPrimaryOrMainSourceFile(Instance).dumpInterfaceHash(llvm::errs()); return Context.hadError(); case FrontendOptions::ActionType::EmitSyntax: return emitSyntax(getPrimaryOrMainSourceFile(Instance), opts.InputsAndOutputs.getSingleOutputFilename()); case FrontendOptions::ActionType::EmitImportedModules: return emitImportedModules(Instance.getMainModule(), opts); // MARK: Dependency Scanning Actions case FrontendOptions::ActionType::ScanDependencies: return performScanDependencies(Instance); case FrontendOptions::ActionType::ScanClangDependencies: return scanClangDependencies(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::EmitSILGen: case FrontendOptions::ActionType::EmitSIBGen: case FrontendOptions::ActionType::EmitSIL: case FrontendOptions::ActionType::EmitSIB: case FrontendOptions::ActionType::EmitModuleOnly: case FrontendOptions::ActionType::MergeModules: case FrontendOptions::ActionType::Immediate: case FrontendOptions::ActionType::EmitAssembly: 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 Context.hadError(); } /// 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; // To compile LLVM IR, just pass it off unmodified. if (opts.InputsAndOutputs.shouldTreatAsLLVM()) return compileLLVMIR(Instance); // If we aren't in a parse-only context and expect an implicit stdlib import, // load in the standard library. If we either fail to find it or encounter an // error while loading it, bail early. Continuing the compilation will at best // trigger a bunch of other errors due to the stdlib being missing, or at // worst crash downstream as many call sites don't currently handle a missing // stdlib. if (FrontendOptions::doesActionRequireSwiftStandardLibrary(Action)) { if (Instance.loadStdlibIfNeeded()) return true; } 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); // 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); 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.c_str(); serializationOpts.SerializeAllSIL = true; serializationOpts.IsSIB = true; serialize(MSF, serializationOpts, SM); return Context.hadError(); } static bool serializeModuleSummary(SILModule *SM, const PrimarySpecificPaths &PSPs, const ASTContext &Context) { auto summaryOutputPath = PSPs.SupplementaryOutputs.ModuleSummaryOutputPath; return withOutputFile(Context.Diags, summaryOutputPath, [&](llvm::raw_ostream &out) { out << "Some stuff"; return false; }); } static GeneratedModule generateIR(const IRGenOptions &IRGenOpts, const TBDGenOptions &TBDOpts, std::unique_ptr SM, const PrimarySpecificPaths &PSPs, StringRef OutputFilename, ModuleOrSourceFile MSF, llvm::GlobalVariable *&HashGlobal, ArrayRef parallelOutputFilenames) { if (auto *SF = MSF.dyn_cast()) { return performIRGeneration(SF, IRGenOpts, TBDOpts, std::move(SM), OutputFilename, PSPs, SF->getPrivateDiscriminator().str(), &HashGlobal); } else { return performIRGeneration(MSF.get(), IRGenOpts, TBDOpts, std::move(SM), OutputFilename, PSPs, parallelOutputFilenames, &HashGlobal); } } static bool processCommandLineAndRunImmediately(CompilerInstance &Instance, std::unique_ptr &&SM, ModuleOrSourceFile MSF, FrontendObserver *observer, int &ReturnValue) { const auto &Invocation = Instance.getInvocation(); const auto &opts = Invocation.getFrontendOptions(); assert(!MSF.is() && "-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() ? MSF.get()->getFilename() : MSF.get()->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; } // Cross-module optimization does not support TBD. if (Invocation.getSILOptions().CrossModuleOptimization) { 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()) { return llvm::none_of(mod->getFiles(), [](const FileUnit *File) -> bool { auto SASTF = dyn_cast(File); return SASTF && SASTF->isSIB(); }); } // "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()) { return validateTBD(SF, IRModule, Opts, diagnoseExtraSymbolsInTBD); } else { return validateTBD(MSF.get(), 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; } 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 TargetMachine = createTargetMachine(opts, Instance.getASTContext()); // Free up some compiler resources now that we have an IRModule. freeASTContextIfPossible(Instance); // If we emitted any errors while perfoming 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.getStatsReporter()); } static bool performCompileStepsPostSILGen(CompilerInstance &Instance, std::unique_ptr 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(); Optional ricd; if (auto *SF = MSF.dyn_cast()) ricd.emplace(*SF); if (observer) observer->performedSILGeneration(*SM); 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()); } 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()); if (serializationOpts.ExperimentalCrossModuleIncrementalInfo) { const auto alsoEmitDotFile = Instance.getInvocation() .getLangOptions() .EmitFineGrainedDependencySourcefileDotFiles; using SourceFileDepGraph = fine_grained_dependencies::SourceFileDepGraph; auto *Mod = MSF.get(); fine_grained_dependencies::withReferenceDependencies( Mod, *Instance.getDependencyTracker(), Mod->getModuleFilename(), alsoEmitDotFile, [&](SourceFileDepGraph &&g) { serialize(MSF, serializationOpts, SM.get(), &g); return false; }); } else { serialize(MSF, serializationOpts, 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); 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(); } } 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 true; runSILLoweringPasses(*SM); // 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 ParallelOutputFilenames = opts.InputsAndOutputs.copyOutputFilenames(); llvm::GlobalVariable *HashGlobal; auto IRModule = generateIR( IRGenOpts, Invocation.getTBDGenOptions(), std::move(SM), PSPs, OutputFilename, MSF, HashGlobal, ParallelOutputFilenames); // 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; 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()); (void) index::indexAndRecord(PrimarySourceFile, PSPs.OutputFilename, opts.IndexStorePath, opts.IndexSystemModules, opts.IndexIgnoreStdlib, isDebugCompilation, Invocation.getTargetTriple(), *Instance.getDependencyTracker()); } else { std::string moduleToken = Invocation.getModuleOutputPathForAtMostOnePrimary(); if (moduleToken.empty()) moduleToken = opts.InputsAndOutputs.getSingleOutputFilename(); (void) index::indexAndRecord(Instance.getMainModule(), opts.InputsAndOutputs.copyOutputFilenames(), moduleToken, opts.IndexStorePath, opts.IndexSystemModules, opts.IndexIgnoreStdlib, isDebugCompilation, Invocation.getTargetTriple(), *Instance.getDependencyTracker()); } } /// Creates a diagnostic consumer that handles dispatching diagnostics to /// multiple output files, based on the supplementary output paths specified by /// \p inputsAndOutputs. /// /// If no output files are needed, returns null. static std::unique_ptr createDispatchingDiagnosticConsumerIfNeeded( const FrontendInputsAndOutputs &inputsAndOutputs, llvm::function_ref(const InputFile &)> maybeCreateConsumerForDiagnosticsFrom) { // The "4" here is somewhat arbitrary. In practice we're going to have one // sub-consumer for each diagnostic file we're trying to output, which (again // in practice) is going to be 1 in WMO mode and equal to the number of // primary inputs in batch mode. That in turn is going to be "the number of // files we need to recompile in this build, divided by the number of jobs". // So a value of "4" here means that there would be no heap allocation on a // clean build of a module with up to 32 files on an 8-core machine, if the // user doesn't customize anything. SmallVector subconsumers; inputsAndOutputs.forEachInputProducingSupplementaryOutput( [&](const InputFile &input) -> bool { if (auto consumer = maybeCreateConsumerForDiagnosticsFrom(input)) subconsumers.emplace_back(input.getFileName(), std::move(consumer)); return false; }); // For batch mode, the compiler must sometimes swallow diagnostics pertaining // to non-primary files in order to avoid Xcode showing the same diagnostic // multiple times. So, create a diagnostic "eater" for those non-primary // files. // // This routine gets called in cases where no primary subconsumers are created. // Don't bother to create non-primary subconsumers if there aren't any primary // ones. // // To avoid introducing bugs into WMO or single-file modes, test for multiple // primaries. if (!subconsumers.empty() && inputsAndOutputs.hasMultiplePrimaryInputs()) { inputsAndOutputs.forEachNonPrimaryInput( [&](const InputFile &input) -> bool { subconsumers.emplace_back(input.getFileName(), nullptr); return false; }); } return FileSpecificDiagnosticConsumer::consolidateSubconsumers(subconsumers); } /// Creates a diagnostic consumer that handles serializing diagnostics, based on /// the supplementary output paths specified by \p inputsAndOutputs. /// /// The returned consumer will handle producing multiple serialized diagnostics /// files if necessary, by using sub-consumers for each file and dispatching to /// the right one. /// /// If no serialized diagnostics are being produced, returns null. static std::unique_ptr createSerializedDiagnosticConsumerIfNeeded( const FrontendInputsAndOutputs &inputsAndOutputs) { return createDispatchingDiagnosticConsumerIfNeeded( inputsAndOutputs, [](const InputFile &input) -> std::unique_ptr { std::string serializedDiagnosticsPath = input.serializedDiagnosticsPath(); if (serializedDiagnosticsPath.empty()) return nullptr; return serialized_diagnostics::createConsumer(serializedDiagnosticsPath); }); } /// Creates a diagnostic consumer that handles serializing diagnostics, based on /// the supplementary output paths specified in \p options. /// /// The returned consumer will handle producing multiple serialized diagnostics /// files if necessary, by using sub-consumers for each file and dispatching to /// the right one. /// /// If no serialized diagnostics are being produced, returns null. static std::unique_ptr createJSONFixItDiagnosticConsumerIfNeeded( const CompilerInvocation &invocation) { return createDispatchingDiagnosticConsumerIfNeeded( invocation.getFrontendOptions().InputsAndOutputs, [&](const InputFile &input) -> std::unique_ptr { std::string fixItsOutputPath = input.fixItsOutputPath(); if (fixItsOutputPath.empty()) return nullptr; return std::make_unique( fixItsOutputPath, invocation.getDiagnosticOptions()); }); } /// Print information about a static void printCompatibilityLibrary( llvm::VersionTuple runtimeVersion, llvm::VersionTuple maxVersion, StringRef filter, StringRef libraryName, bool &printedAny, llvm::raw_ostream &out) { if (runtimeVersion > maxVersion) return; if (printedAny) { out << ","; } out << "\n"; out << " {\n"; out << " \"libraryName\": \""; out.write_escaped(libraryName); out << "\",\n"; out << " \"filter\": \""; out.write_escaped(filter); out << "\"\n"; out << " }"; printedAny = true; } /// Print information about the target triple in JSON. static void printTripleInfo(const llvm::Triple &triple, llvm::Optional runtimeVersion, llvm::raw_ostream &out) { out << "{\n"; out << " \"triple\": \""; out.write_escaped(triple.getTriple()); out << "\",\n"; out << " \"unversionedTriple\": \""; out.write_escaped(getUnversionedTriple(triple).getTriple()); out << "\",\n"; out << " \"moduleTriple\": \""; out.write_escaped(getTargetSpecificModuleTriple(triple).getTriple()); out << "\",\n"; if (runtimeVersion) { out << " \"swiftRuntimeCompatibilityVersion\": \""; out.write_escaped(runtimeVersion->getAsString()); out << "\",\n"; // Compatibility libraries that need to be linked. out << " \"compatibilityLibraries\": ["; bool printedAnyCompatibilityLibrary = false; #define BACK_DEPLOYMENT_LIB(Version, Filter, LibraryName) \ printCompatibilityLibrary( \ *runtimeVersion, llvm::VersionTuple Version, #Filter, LibraryName, \ printedAnyCompatibilityLibrary, out); #include "swift/Frontend/BackDeploymentLibs.def" if (printedAnyCompatibilityLibrary) { out << "\n "; } out << " ],\n"; } else { out << " \"compatibilityLibraries\": [ ],\n"; } out << " \"librariesRequireRPath\": " << (tripleRequiresRPathForSwiftInOS(triple) ? "true" : "false") << "\n"; out << " }"; } /// Print information about the selected target in JSON. static void printTargetInfo(const CompilerInvocation &invocation, llvm::raw_ostream &out) { out << "{\n"; // Compiler version, as produced by --version. out << " \"compilerVersion\": \""; out.write_escaped(version::getSwiftFullVersion( version::Version::getCurrentLanguageVersion())); out << "\",\n"; // Target triple and target variant triple. auto runtimeVersion = invocation.getIRGenOptions().AutolinkRuntimeCompatibilityLibraryVersion; auto &langOpts = invocation.getLangOptions(); out << " \"target\": "; printTripleInfo(langOpts.Target, runtimeVersion, out); out << ",\n"; if (auto &variant = langOpts.TargetVariant) { out << " \"targetVariant\": "; printTripleInfo(*variant, runtimeVersion, out); out << ",\n"; } // Various paths. auto &searchOpts = invocation.getSearchPathOptions(); out << " \"paths\": {\n"; if (!searchOpts.SDKPath.empty()) { out << " \"sdkPath\": \""; out.write_escaped(searchOpts.SDKPath); out << "\",\n"; } auto outputPaths = [&](StringRef name, const std::vector &paths){ out << " \"" << name << "\": [\n"; llvm::interleave(paths, [&out](const std::string &path) { out << " \""; out.write_escaped(path); out << "\""; }, [&out] { out << ",\n"; }); out << "\n ],\n"; }; outputPaths("runtimeLibraryPaths", searchOpts.RuntimeLibraryPaths); outputPaths("runtimeLibraryImportPaths", searchOpts.RuntimeLibraryImportPaths); out << " \"runtimeResourcePath\": \""; out.write_escaped(searchOpts.RuntimeResourcePath); out << "\"\n"; out << " }\n"; out << "}\n"; } int swift::performFrontend(ArrayRef Args, const char *Argv0, void *MainAddr, FrontendObserver *observer) { INITIALIZE_LLVM(); llvm::setBugReportMsg(SWIFT_CRASH_BUG_REPORT_MESSAGE "\n"); llvm::EnablePrettyStackTraceOnSigInfoForThisThread(); PrintingDiagnosticConsumer PDC; // 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. auto diagnoseFatalError = [&PDC](const std::string &reason, bool shouldCrash){ static const std::string *recursiveFatalError = nullptr; if (recursiveFatalError) { // Report the /original/ error through LLVM's default handler, not // whatever we encountered. llvm::remove_fatal_error_handler(); llvm::report_fatal_error(*recursiveFatalError, shouldCrash); } recursiveFatalError = &reason; SourceManager dummyMgr; DiagnosticInfo errorInfo( DiagID(0), SourceLoc(), DiagnosticKind::Error, "fatal error encountered during compilation; " SWIFT_BUG_REPORT_MESSAGE, {}, SourceLoc(), {}, {}, {}, false); DiagnosticInfo noteInfo(DiagID(0), SourceLoc(), DiagnosticKind::Note, reason, {}, SourceLoc(), {}, {}, {}, false); PDC.handleDiagnostic(dummyMgr, errorInfo); PDC.handleDiagnostic(dummyMgr, noteInfo); if (shouldCrash) abort(); }; llvm::ScopedFatalErrorHandler handler([](void *rawCallback, const std::string &reason, bool shouldCrash) { auto *callback = static_cast(rawCallback); (*callback)(reason, shouldCrash); }, &diagnoseFatalError); std::unique_ptr Instance = std::make_unique(); Instance->addDiagnosticConsumer(&PDC); 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; PDC.setSuppressOutput(false); 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); } CompilerInvocation Invocation; SmallString<128> workingDirectory; llvm::sys::fs::current_path(workingDirectory); std::string MainExecutablePath = llvm::sys::fs::getMainExecutable(Argv0, MainAddr); // Parse arguments. SmallVector, 4> configurationFileBuffers; if (Invocation.parseArgs(Args, Instance->getDiags(), &configurationFileBuffers, workingDirectory, MainExecutablePath)) { return finishDiagProcessing(1, /*verifierEnabled*/ false); } // 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[]>( configurationFileBuffers.size()); for_each(configurationFileBuffers.begin(), configurationFileBuffers.end(), &configurationFileStackTraces[0], [](const std::unique_ptr &buffer, Optional &trace) { trace.emplace(*buffer); }); // Setting DWARF Version depend on platform IRGenOptions &IRGenOpts = Invocation.getIRGenOptions(); IRGenOpts.DWARFVersion = swift::DWARFVersion; // 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 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) { printTargetInfo(Invocation, 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); } // Because the serialized diagnostics consumer is initialized here, // diagnostics emitted above, within CompilerInvocation::parseArgs, are never // serialized. This is a non-issue because, in nearly all cases, frontend // arguments are generated by the driver, not directly by a user. The driver // is responsible for emitting diagnostics for its own errors. See SR-2683 // for details. std::unique_ptr SerializedConsumerDispatcher = createSerializedDiagnosticConsumerIfNeeded( Invocation.getFrontendOptions().InputsAndOutputs); if (SerializedConsumerDispatcher) Instance->addDiagnosticConsumer(SerializedConsumerDispatcher.get()); std::unique_ptr FixItsConsumer = createJSONFixItDiagnosticConsumerIfNeeded(Invocation); if (FixItsConsumer) Instance->addDiagnosticConsumer(FixItsConsumer.get()); if (Invocation.getDiagnosticOptions().UseColor) PDC.forceColors(); PDC.setPrintEducationalNotes( Invocation.getDiagnosticOptions().PrintEducationalNotes); PDC.setFormattingStyle( Invocation.getDiagnosticOptions().PrintedFormattingStyle); if (Invocation.getFrontendOptions().PrintStats) { llvm::EnableStatistics(); } const DiagnosticOptions &diagOpts = Invocation.getDiagnosticOptions(); bool verifierEnabled = diagOpts.VerifyMode != DiagnosticOptions::NoVerify; if (Instance->setup(Invocation)) { return finishDiagProcessing(1, /*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. PDC.setSuppressOutput(true); } int ReturnValue = 0; bool HadError = performCompile(*Instance, ReturnValue, observer); if (verifierEnabled) { DiagnosticEngine &diags = Instance->getDiags(); if (diags.hasFatalErrorOccurred() && !Invocation.getDiagnosticOptions().ShowDiagnosticsAfterFatalError) { diags.resetHadAnyError(); PDC.setSuppressOutput(false); diags.diagnose(SourceLoc(), diag::verify_encountered_fatal); HadError = true; } } auto r = finishDiagProcessing(HadError ? 1 : ReturnValue, verifierEnabled); if (auto *StatsReporter = Instance->getStatsReporter()) StatsReporter->noteCurrentProcessExitStatus(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) {}