//===--- FrontendTool.cpp - Swift Compiler Frontend -----------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// /// /// \file /// \brief 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 "ReferenceDependencies.h" #include "TBD.h" #include "swift/Strings.h" #include "swift/Subsystems.h" #include "swift/AST/ASTScope.h" #include "swift/AST/DiagnosticsFrontend.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/GenericSignatureBuilder.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/ASTMangler.h" #include "swift/AST/ReferencedNameTracker.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/LLVMContext.h" #include "swift/Basic/SourceManager.h" #include "swift/Basic/Statistic.h" #include "swift/Basic/Timer.h" #include "swift/Basic/UUID.h" #include "swift/Frontend/DiagnosticVerifier.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" #include "swift/Frontend/SerializedDiagnosticConsumer.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/Syntax/Serialization/SyntaxSerialization.h" #include "swift/Syntax/SyntaxNodes.h" // FIXME: We're just using CompilerInstance::createOutputFile. // This API should be sunk down to LLVM. #include "clang/Frontend/CompilerInstance.h" #include "clang/AST/ASTContext.h" #include "clang/APINotes/Types.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/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 #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); Name += " -frontend"; return Name; } /// Emits a Make-style dependencies file. static bool emitMakeDependencies(DiagnosticEngine &diags, DependencyTracker &depTracker, const FrontendOptions &opts) { std::error_code EC; llvm::raw_fd_ostream out(opts.DependenciesFilePath, EC, llvm::sys::fs::F_None); if (out.has_error() || EC) { diags.diagnose(SourceLoc(), diag::error_opening_output, opts.DependenciesFilePath, EC.message()); out.clear_error(); return true; } // Declare a helper for escaping file names for use in Makefiles. llvm::SmallString<256> pathBuf; auto escape = [&](StringRef raw) -> StringRef { pathBuf.clear(); static const char badChars[] = " $#:\n"; size_t prev = 0; for (auto index = raw.find_first_of(badChars); index != StringRef::npos; index = raw.find_first_of(badChars, index+1)) { pathBuf.append(raw.slice(prev, index)); if (raw[index] == '$') pathBuf.push_back('$'); else pathBuf.push_back('\\'); prev = index; } pathBuf.append(raw.substr(prev)); return pathBuf; }; // FIXME: Xcode can't currently handle multiple targets in a single // dependency line. opts.forAllOutputPaths([&](StringRef targetName) { out << escape(targetName) << " :"; // First include all other files in the module. Make-style dependencies // need to be conservative! for (auto const &path : reversePathSortedFilenames(opts.InputsAndOutputs.getInputFilenames())) out << ' ' << escape(path); // Then print dependencies we've picked up during compilation. for (auto const &path : reversePathSortedFilenames(depTracker.getDependencies())) out << ' ' << escape(path); out << '\n'; }); return false; } namespace { struct LoadedModuleTraceFormat { std::string Name; std::string Arch; std::vector SwiftModules; }; } namespace swift { namespace json { template <> struct ObjectTraits { static void mapping(Output &out, LoadedModuleTraceFormat &contents) { out.mapRequired("name", contents.Name); out.mapRequired("arch", contents.Arch); out.mapRequired("swiftmodules", contents.SwiftModules); } }; } } static bool emitLoadedModuleTrace(ASTContext &ctxt, DependencyTracker &depTracker, const FrontendOptions &opts) { std::error_code EC; llvm::raw_fd_ostream out(opts.LoadedModuleTracePath, EC, llvm::sys::fs::F_Append); if (out.has_error() || EC) { ctxt.Diags.diagnose(SourceLoc(), diag::error_opening_output, opts.LoadedModuleTracePath, EC.message()); out.clear_error(); return true; } llvm::SmallVector swiftModules; // Canonicalise all the paths by opening them. for (auto &dep : depTracker.getDependencies()) { llvm::SmallString<256> buffer; StringRef realPath; int FD; // FIXME: appropriate error handling if (llvm::sys::fs::openFileForRead(dep, FD, &buffer)) { // Couldn't open the file now, so let's just assume the old path was // canonical (enough). realPath = dep; } else { realPath = buffer.str(); // Not much we can do about failing to close. (void)close(FD); } // Decide if this is a swiftmodule based on the extension of the raw // dependency path, as the true file may have a different one. auto ext = llvm::sys::path::extension(dep); if (ext.startswith(".") && ext.drop_front() == SERIALIZED_MODULE_EXTENSION) { swiftModules.push_back(realPath); } } LoadedModuleTraceFormat trace = { /*name=*/opts.ModuleName, /*arch=*/ctxt.LangOpts.Target.getArchName(), /*swiftmodules=*/reversePathSortedFilenames(swiftModules)}; // raw_fd_ostream is unbuffered, and we may have multiple processes writing, // so first write the whole thing into memory and dump out that buffer to the // file. std::string stringBuffer; { llvm::raw_string_ostream memoryBuffer(stringBuffer); json::Output jsonOutput(memoryBuffer, /*PrettyPrint=*/false); json::jsonize(jsonOutput, trace, /*Required=*/true); } stringBuffer += "\n"; out << stringBuffer; return true; } /// 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 = llvm::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, LangOptions &LangOpts, SourceManager &SM, StringRef OutputFilename) { auto bufferID = SF->getBufferID(); assert(bufferID && "frontend should have a buffer ID " "for the main source file"); auto os = getFileOutputStream(OutputFilename, SF->getASTContext()); if (!os) return true; json::Output jsonOut(*os); 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, bool EmitVerboseSIL, StringRef OutputFilename, bool SortSIL) { auto OS = getFileOutputStream(OutputFilename, M->getASTContext()); if (!OS) return true; SM.print(*OS, EmitVerboseSIL, M, SortSIL); return false; } static bool printAsObjC(const std::string &outputPath, ModuleDecl *M, StringRef bridgingHeader, bool moduleIsPublic) { using namespace llvm::sys; clang::CompilerInstance Clang; std::string tmpFilePath; std::error_code EC; std::unique_ptr out = Clang.createOutputFile(outputPath, EC, /*Binary=*/false, /*RemoveFileOnSignal=*/true, /*BaseInput=*/"", path::extension(outputPath), /*UseTemporary=*/true, /*CreateMissingDirectories=*/false, /*ResultPathName=*/nullptr, &tmpFilePath); if (!out) { M->getASTContext().Diags.diagnose(SourceLoc(), diag::error_opening_output, tmpFilePath, EC.message()); return true; } auto requiredAccess = moduleIsPublic ? AccessLevel::Public : AccessLevel::Internal; bool hadError = printAsObjC(*out, M, bridgingHeader, requiredAccess); out->flush(); EC = swift::moveFileIfDifferent(tmpFilePath, outputPath); if (EC) { M->getASTContext().Diags.diagnose(SourceLoc(), diag::error_opening_output, outputPath, EC.message()); return true; } return hadError; } /// Returns the OutputKind for the given Action. static IRGenOutputKind getOutputKind(FrontendOptions::ActionType Action) { switch (Action) { case FrontendOptions::ActionType::EmitIR: return IRGenOutputKind::LLVMAssembly; case FrontendOptions::ActionType::EmitBC: return IRGenOutputKind::LLVMBitcode; case FrontendOptions::ActionType::EmitAssembly: return IRGenOutputKind::NativeAssembly; case FrontendOptions::ActionType::EmitObject: return IRGenOutputKind::ObjectFile; case FrontendOptions::ActionType::Immediate: return IRGenOutputKind::Module; default: llvm_unreachable("Unknown ActionType which requires IRGen"); return IRGenOutputKind::ObjectFile; } } 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, SourceLoc Loc, DiagnosticKind Kind, StringRef FormatString, ArrayRef FormatArgs, const DiagnosticInfo &Info) override { if (!(FixitAll || shouldTakeFixit(Kind, Info))) return; for (const auto &Fix : Info.FixIts) { AllEdits.push_back({SM, Fix.getRange(), Fix.getText()}); } } 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 bool emitIndexData(SourceFile *PrimarySourceFile, const CompilerInvocation &Invocation, CompilerInstance &Instance); static void countStatsOfSourceFile(UnifiedStatsReporter &Stats, CompilerInstance &Instance, SourceFile *SF) { auto &C = Stats.getFrontendCounters(); auto &SM = Instance.getSourceMgr(); C.NumDecls += SF->Decls.size(); C.NumLocalTypeDecls += SF->LocalTypeDecls.size(); C.NumObjCMethods += SF->ObjCMethods.size(); C.NumInfixOperators += SF->InfixOperators.size(); C.NumPostfixOperators += SF->PostfixOperators.size(); C.NumPrefixOperators += SF->PrefixOperators.size(); C.NumPrecedenceGroups += SF->PrecedenceGroups.size(); C.NumUsedConformances += SF->getUsedConformances().size(); auto bufID = SF->getBufferID(); if (bufID.hasValue()) { C.NumSourceLines += SM.getEntireTextForBuffer(bufID.getValue()).count('\n'); } } static void countStatsPostSema(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.LoadedModules.size(); C.NumImportedExternalDefinitions = AST.ExternalDefinitions.size(); C.NumASTBytesAllocated = AST.getAllocator().getBytesAllocated(); if (auto *D = Instance.getDependencyTracker()) { C.NumDependencies = D->getDependencies().size(); } if (auto *R = Instance.getReferencedNameTracker()) { C.NumReferencedTopLevelNames = R->getTopLevelNames().size(); C.NumReferencedDynamicNames = R->getDynamicLookupNames().size(); C.NumReferencedMemberNames = R->getUsedMembers().size(); } 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.getVTableList().size(); C.NumSILGenWitnessTables = Module.getWitnessTableList().size(); C.NumSILGenDefaultWitnessTables = Module.getDefaultWitnessTableList().size(); C.NumSILGenGlobalVariables = Module.getSILGlobalList().size(); } static void countStatsPostSILOpt(UnifiedStatsReporter &Stats, const SILModule& Module) { auto &C = Stats.getFrontendCounters(); // FIXME: calculate these in constant time, via the dense maps. C.NumSILOptFunctions = Module.getFunctionList().size(); C.NumSILOptVtables = Module.getVTableList().size(); C.NumSILOptWitnessTables = Module.getWitnessTableList().size(); C.NumSILOptDefaultWitnessTables = Module.getDefaultWitnessTableList().size(); C.NumSILOptGlobalVariables = Module.getSILGlobalList().size(); } static std::unique_ptr createOptRecordFile(StringRef Filename, DiagnosticEngine &DE) { if (Filename.empty()) return nullptr; std::error_code EC; auto File = llvm::make_unique(Filename, EC, llvm::sys::fs::F_None); if (EC) { DE.diagnose(SourceLoc(), diag::cannot_open_file, Filename, EC.message()); return nullptr; } return File; } struct PostSILGenInputs { std::unique_ptr TheSILModule; bool ASTGuaranteedToCorrespondToSIL; ModuleOrSourceFile ModuleOrPrimarySourceFile; }; static bool performCompileStepsPostSILGen(CompilerInstance &Instance, CompilerInvocation &Invocation, std::unique_ptr SM, bool astGuaranteedToCorrespondToSIL, ModuleOrSourceFile MSF, bool moduleIsPublic, int &ReturnValue, FrontendObserver *observer, UnifiedStatsReporter *Stats); /// 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, CompilerInvocation &Invocation, ArrayRef Args, int &ReturnValue, FrontendObserver *observer, UnifiedStatsReporter *Stats) { FrontendOptions opts = Invocation.getFrontendOptions(); FrontendOptions::ActionType Action = opts.RequestedAction; if (Action == FrontendOptions::ActionType::EmitSyntax) { Instance.getASTContext().LangOpts.KeepSyntaxInfoInSourceFile = true; Instance.getASTContext().LangOpts.VerifySyntaxTree = true; } // We've been asked to precompile a bridging header; we want to // avoid touching any other inputs and just parse, emit and exit. if (Action == FrontendOptions::ActionType::EmitPCH) { auto clangImporter = static_cast( Instance.getASTContext().getClangModuleLoader()); auto &ImporterOpts = Invocation.getClangImporterOptions(); auto &PCHOutDir = ImporterOpts.PrecompiledHeaderOutputDir; if (!PCHOutDir.empty()) { ImporterOpts.BridgingHeader = Invocation.getFrontendOptions() .InputsAndOutputs.getFilenameOfFirstInput(); // Create or validate a persistent PCH. auto SwiftPCHHash = Invocation.getPCHHash(); auto PCH = clangImporter->getOrCreatePCH(ImporterOpts, SwiftPCHHash); return !PCH.hasValue(); } return clangImporter->emitBridgingPCH( Invocation.getFrontendOptions() .InputsAndOutputs.getFilenameOfFirstInput(), opts.getSingleOutputFilename()); } IRGenOptions &IRGenOpts = Invocation.getIRGenOptions(); bool inputIsLLVMIr = Invocation.getInputKind() == InputFileKind::IFK_LLVM_IR; if (inputIsLLVMIr) { auto &LLVMContext = getGlobalLLVMContext(); // Load in bitcode file. assert(Invocation.getFrontendOptions().InputsAndOutputs.hasSingleInput() && "We expect a single input for bitcode input!"); llvm::ErrorOr> FileBufOrErr = llvm::MemoryBuffer::getFileOrSTDIN( Invocation.getFrontendOptions() .InputsAndOutputs.getFilenameOfFirstInput()); if (!FileBufOrErr) { Instance.getASTContext().Diags.diagnose( SourceLoc(), diag::error_open_input_file, Invocation.getFrontendOptions() .InputsAndOutputs.getFilenameOfFirstInput(), FileBufOrErr.getError().message()); return true; } llvm::MemoryBuffer *MainFile = FileBufOrErr.get().get(); llvm::SMDiagnostic Err; std::unique_ptr Module = llvm::parseIR( MainFile->getMemBufferRef(), Err, LLVMContext); if (!Module) { // TODO: Translate from the diagnostic info to the SourceManager location // if available. Instance.getASTContext().Diags.diagnose( SourceLoc(), diag::error_parse_input_file, Invocation.getFrontendOptions() .InputsAndOutputs.getFilenameOfFirstInput(), Err.getMessage()); return true; } // TODO: remove once the frontend understands what action it should perform IRGenOpts.OutputKind = getOutputKind(Action); return performLLVM(IRGenOpts, Instance.getASTContext(), Module.get(), Stats); } ReferencedNameTracker nameTracker; bool shouldTrackReferences = !opts.ReferenceDependenciesFilePath.empty(); if (shouldTrackReferences) Instance.setReferencedNameTracker(&nameTracker); if (Action == FrontendOptions::ActionType::Parse || Action == FrontendOptions::ActionType::DumpParse || Action == FrontendOptions::ActionType::EmitSyntax || Action == FrontendOptions::ActionType::DumpInterfaceHash || Action == FrontendOptions::ActionType::EmitImportedModules) Instance.performParseOnly(); else Instance.performSema(); if (Action == FrontendOptions::ActionType::Parse) return Instance.getASTContext().hadError(); if (observer) { observer->performedSemanticAnalysis(Instance); } if (Stats) { countStatsPostSema(*Stats, Instance); } FrontendOptions::DebugCrashMode CrashMode = opts.CrashMode; if (CrashMode == FrontendOptions::DebugCrashMode::AssertAfterParse) debugFailWithAssertion(); else if (CrashMode == FrontendOptions::DebugCrashMode::CrashAfterParse) debugFailWithCrash(); ASTContext &Context = Instance.getASTContext(); auto verifyGenericSignaturesInModule = Invocation.getFrontendOptions().VerifyGenericSignaturesInModule; if (!verifyGenericSignaturesInModule.empty()) { if (auto module = Context.getModuleByName(verifyGenericSignaturesInModule)) GenericSignatureBuilder::verifyGenericSignaturesInModule(module); } if (Invocation.getMigratorOptions().shouldRunMigrator()) { migrator::updateCodeAndEmitRemap(&Instance, Invocation); } if (Action == FrontendOptions::ActionType::REPL) { runREPL(Instance, ProcessCmdLine(Args.begin(), Args.end()), Invocation.getParseStdlib()); return Context.hadError(); } // We've been told to dump the AST (either after parsing or type-checking, // which is already differentiated in CompilerInstance::performSema()), // so dump or print the main source file and return. if (Action == FrontendOptions::ActionType::DumpParse || Action == FrontendOptions::ActionType::DumpAST || Action == FrontendOptions::ActionType::EmitSyntax || Action == FrontendOptions::ActionType::PrintAST || Action == FrontendOptions::ActionType::DumpScopeMaps || Action == FrontendOptions::ActionType::DumpTypeRefinementContexts || Action == FrontendOptions::ActionType::DumpInterfaceHash) { SourceFile *SF = Instance.getPrimarySourceFile(); if (!SF) { SourceFileKind Kind = Invocation.getSourceFileKind(); SF = &Instance.getMainModule()->getMainSourceFile(Kind); } if (Action == FrontendOptions::ActionType::PrintAST) SF->print(llvm::outs(), PrintOptions::printEverything()); else if (Action == FrontendOptions::ActionType::DumpScopeMaps) { ASTScope &scope = SF->getScope(); if (opts.DumpScopeMapLocations.empty()) { scope.expandAll(); } else if (auto bufferID = SF->getBufferID()) { SourceManager &sourceMgr = Instance.getSourceMgr(); // Probe each of the locations, and dump what we find. for (auto lineColumn : opts.DumpScopeMapLocations) { SourceLoc loc = sourceMgr.getLocForLineCol(*bufferID, lineColumn.first, lineColumn.second); if (loc.isInvalid()) continue; llvm::errs() << "***Scope at " << lineColumn.first << ":" << lineColumn.second << "***\n"; auto locScope = scope.findInnermostEnclosingScope(loc); locScope->print(llvm::errs(), 0, false, false); // Dump the AST context, too. if (auto dc = locScope->getDeclContext()) { dc->printContext(llvm::errs()); } // Grab the local bindings introduced by this scope. auto localBindings = locScope->getLocalBindings(); if (!localBindings.empty()) { llvm::errs() << "Local bindings: "; interleave(localBindings.begin(), localBindings.end(), [&](ValueDecl *value) { llvm::errs() << value->getFullName(); }, [&]() { llvm::errs() << " "; }); llvm::errs() << "\n"; } } llvm::errs() << "***Complete scope map***\n"; } // Print the resulting map. scope.print(llvm::errs()); } else if (Action == FrontendOptions::ActionType::DumpTypeRefinementContexts) SF->getTypeRefinementContext()->dump(llvm::errs(), Context.SourceMgr); else if (Action == FrontendOptions::ActionType::DumpInterfaceHash) SF->dumpInterfaceHash(llvm::errs()); else if (Action == FrontendOptions::ActionType::EmitSyntax) { emitSyntax(SF, Invocation.getLangOptions(), Instance.getSourceMgr(), opts.getSingleOutputFilename()); } else SF->dump(); return Context.hadError(); } else if (Action == FrontendOptions::ActionType::EmitImportedModules) { emitImportedModules(Context, Instance.getMainModule(), opts); return Context.hadError(); } // If we were asked to print Clang stats, do so. if (opts.PrintClangStats && Context.getClangModuleLoader()) Context.getClangModuleLoader()->printStatistics(); if (!opts.DependenciesFilePath.empty()) (void)emitMakeDependencies(Context.Diags, *Instance.getDependencyTracker(), opts); if (shouldTrackReferences) { if (Instance.getPrimarySourceFiles().empty()) Context.Diags.diagnose( SourceLoc(), diag::emit_reference_dependencies_without_primary_file); for (auto *SF : Instance.getPrimarySourceFiles()) { emitReferenceDependencies(Context.Diags, SF, *Instance.getDependencyTracker(), opts); } } if (!opts.LoadedModuleTracePath.empty()) (void)emitLoadedModuleTrace(Context, *Instance.getDependencyTracker(), opts); bool shouldIndex = !opts.IndexStorePath.empty(); if (Context.hadError()) { if (shouldIndex) { // Emit the index store data even if there were compiler errors. if (emitIndexData(Instance.getPrimarySourceFile(), Invocation, Instance)) return true; } return true; } // FIXME: This is still a lousy approximation of whether the module file will // be externally consumed. bool moduleIsPublic = !Instance.getMainModule()->hasEntryPoint() && opts.ImplicitObjCHeaderPath.empty() && !Context.LangOpts.EnableAppExtensionRestrictions; // We've just been told to perform a typecheck, so we can return now. if (Action == FrontendOptions::ActionType::Typecheck) { if (!opts.ObjCHeaderOutputPath.empty()) return printAsObjC(opts.ObjCHeaderOutputPath, Instance.getMainModule(), opts.ImplicitObjCHeaderPath, moduleIsPublic); if (shouldIndex) { if (emitIndexData(Instance.getPrimarySourceFile(), Invocation, Instance)) return true; } return Context.hadError(); } auto &SILOpts = Invocation.getSILOptions(); if (!opts.TBDPath.empty()) { auto installName = opts.TBDInstallName.empty() ? "lib" + Invocation.getModuleName().str() + ".dylib" : opts.TBDInstallName; if (writeTBD(Instance.getMainModule(), SILOpts.hasMultipleIGMs(), opts.TBDPath, installName)) return true; } assert(Action >= FrontendOptions::ActionType::EmitSILGen && "All actions not requiring SILGen must have been handled!"); auto mod = Instance.getMainModule(); std::deque PSGIs; if (auto SM = Instance.takeSILModule()) { PSGIs.push_back(PostSILGenInputs{std::move(SM), false, mod}); } else { auto fileIsSIB = [](const FileUnit *File) -> bool { auto SASTF = dyn_cast(File); return SASTF && SASTF->isSIB(); }; if (opts.InputsAndOutputs.hasPrimaryInputs()) { if (Instance.getPrimarySourceFiles().empty()) { // If we have primary inputs but no primary _source files_, we might // have a primary serialized input. for (FileUnit *fileUnit : mod->getFiles()) { if (auto SASTF = dyn_cast(fileUnit)) { if (Invocation.getFrontendOptions().InputsAndOutputs.isInputPrimary( SASTF->getFilename())) { assert(PSGIs.empty() && "Can only handle one primary AST input"); auto SM = performSILGeneration(*SASTF, SILOpts, None); PSGIs.push_back( PostSILGenInputs{std::move(SM), !fileIsSIB(SASTF), mod}); } } } } else { // If we have multiple primary inputs, build a separate SILModule for // each source file, and run the remaining SILOpt-Serialize-IRGen-LLVM // once for each such input. for (auto *PrimaryFile : Instance.getPrimarySourceFiles()) { auto SM = performSILGeneration(*PrimaryFile, SILOpts, None); PSGIs.push_back(PostSILGenInputs{ std::move(SM), true, PrimaryFile}); } } } else { // If we have no primary inputs we are in WMO mode and need to build a // SILModule for the entire module. auto SM = performSILGeneration(mod, SILOpts, true); PSGIs.push_back(PostSILGenInputs{ std::move(SM), llvm::none_of(mod->getFiles(), fileIsSIB), mod}); } } while (!PSGIs.empty()) { auto PSGI = std::move(PSGIs.front()); PSGIs.pop_front(); if (performCompileStepsPostSILGen(Instance, Invocation, std::move(PSGI.TheSILModule), PSGI.ASTGuaranteedToCorrespondToSIL, PSGI.ModuleOrPrimarySourceFile, moduleIsPublic, ReturnValue, observer, Stats)) return true; } return false; } static bool performCompileStepsPostSILGen(CompilerInstance &Instance, CompilerInvocation &Invocation, std::unique_ptr SM, bool astGuaranteedToCorrespondToSIL, ModuleOrSourceFile MSF, bool moduleIsPublic, int &ReturnValue, FrontendObserver *observer, UnifiedStatsReporter *Stats) { FrontendOptions opts = Invocation.getFrontendOptions(); FrontendOptions::ActionType Action = opts.RequestedAction; ASTContext &Context = Instance.getASTContext(); SILOptions &SILOpts = Invocation.getSILOptions(); IRGenOptions &IRGenOpts = Invocation.getIRGenOptions(); bool shouldIndex = !opts.IndexStorePath.empty(); if (observer) { observer->performedSILGeneration(*SM); } if (Stats) { countStatsPostSILGen(*Stats, *SM); } // We've been told to emit SIL after SILGen, so write it now. if (Action == FrontendOptions::ActionType::EmitSILGen) { // If we are asked to link all, link all. if (Invocation.getSILOptions().LinkMode == SILOptions::LinkAll) performSILLinking(SM.get(), true); return writeSIL(*SM, Instance.getMainModule(), opts.EmitVerboseSIL, opts.getSingleOutputFilename(), opts.EmitSortedSIL); } if (Action == FrontendOptions::ActionType::EmitSIBGen) { // If we are asked to link all, link all. if (Invocation.getSILOptions().LinkMode == SILOptions::LinkAll) performSILLinking(SM.get(), true); if (!opts.ModuleOutputPath.empty()) { SerializationOptions serializationOpts; serializationOpts.OutputPath = opts.ModuleOutputPath.c_str(); serializationOpts.SerializeAllSIL = true; serializationOpts.IsSIB = true; serialize(MSF, serializationOpts, SM.get()); } return Context.hadError(); } std::unique_ptr OptRecordFile = createOptRecordFile(SILOpts.OptRecordFile, Instance.getDiags()); if (OptRecordFile) SM->setOptRecordStream(llvm::make_unique( *OptRecordFile, &Instance.getSourceMgr()), std::move(OptRecordFile)); // Perform "stable" optimizations that are invariant across compiler versions. if (Action == FrontendOptions::ActionType::MergeModules) { // Don't run diagnostic passes at all. } else if (!Invocation.getDiagnosticOptions().SkipDiagnosticPasses) { if (runSILDiagnosticPasses(*SM)) return true; if (observer) { observer->performedSILDiagnostics(*SM); } } else { // Even if we are not supposed to run the diagnostic passes, we still need // to run the ownership evaluator. if (runSILOwnershipEliminatorPass(*SM)) return true; } // Now if we are asked to link all, link all. if (Invocation.getSILOptions().LinkMode == SILOptions::LinkAll) performSILLinking(SM.get(), true); if (Invocation.getSILOptions().MergePartialModules) SM->linkAllFromCurrentModule(); { SharedTimer timer("SIL verification, pre-optimization"); SM->verify(); } // 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 = [&]() { if (!opts.ModuleOutputPath.empty() || !opts.ModuleDocOutputPath.empty()) { if (!opts.ModuleOutputPath.empty()) { SerializationOptions serializationOpts; serializationOpts.OutputPath = opts.ModuleOutputPath.c_str(); serializationOpts.DocOutputPath = opts.ModuleDocOutputPath.c_str(); serializationOpts.GroupInfoPath = opts.GroupInfoPath.c_str(); if (opts.SerializeBridgingHeader) serializationOpts.ImportedHeader = opts.ImplicitObjCHeaderPath; serializationOpts.ModuleLinkName = opts.ModuleLinkName; serializationOpts.ExtraClangOptions = Invocation.getClangImporterOptions().ExtraArgs; serializationOpts.EnableNestedTypeLookupTable = opts.EnableSerializationNestedTypeLookupTable; if (!IRGenOpts.ForceLoadSymbolName.empty()) serializationOpts.AutolinkForceLoad = true; // Options contain information about the developer's computer, // so only serialize them if the module isn't going to be shipped to // the public. serializationOpts.SerializeOptionsForDebugging = !moduleIsPublic || opts.AlwaysSerializeDebuggingOptions; 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 SIL optimization passes if optimizations haven't been disabled. // These may change across compiler versions. { SharedTimer timer("SIL optimization"); if (Action != FrontendOptions::ActionType::MergeModules && Invocation.getSILOptions().shouldOptimize()) { runSILOptPreparePasses(*SM); StringRef CustomPipelinePath = Invocation.getSILOptions().ExternalPassPipelineFilename; if (!CustomPipelinePath.empty()) { runSILOptimizationPassesWithFileSpecification(*SM, CustomPipelinePath); } else { runSILOptimizationPasses(*SM); } } else { runSILPassesForOnone(*SM); } } if (observer) { observer->performedSILOptimization(*SM); } if (Stats) { countStatsPostSILOpt(*Stats, *SM); } { SharedTimer timer("SIL verification, post-optimization"); SM->verify(); } // Gather instruction counts if we are asked to do so. if (SM->getOptions().PrintInstCounts) { performSILInstCount(&*SM); } // Get the main source file's private discriminator and attach it to // the compile unit's flags. if (IRGenOpts.DebugInfoKind != IRGenDebugInfoKind::None && MSF.is()) { Identifier PD = MSF.get()->getPrivateDiscriminator(); if (!PD.empty()) IRGenOpts.DWARFDebugFlags += (" -private-discriminator "+PD.str()).str(); } if (!opts.ObjCHeaderOutputPath.empty()) { (void)printAsObjC(opts.ObjCHeaderOutputPath, Instance.getMainModule(), opts.ImplicitObjCHeaderPath, moduleIsPublic); } if (Action == FrontendOptions::ActionType::EmitSIB) { if (!opts.ModuleOutputPath.empty()) { SerializationOptions serializationOpts; serializationOpts.OutputPath = opts.ModuleOutputPath.c_str(); serializationOpts.SerializeAllSIL = true; serializationOpts.IsSIB = true; serialize(MSF, serializationOpts, SM.get()); } return Context.hadError(); } if (!opts.ModuleOutputPath.empty() || !opts.ModuleDocOutputPath.empty()) { // Serialize the SILModule if it was not serialized yet. if (!SM.get()->isSerialized()) SM.get()->serialize(); if (Action == FrontendOptions::ActionType::MergeModules || Action == FrontendOptions::ActionType::EmitModuleOnly) { if (shouldIndex) { if (emitIndexData(MSF.dyn_cast(), Invocation, Instance)) return true; } 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, Instance.getMainModule(), opts.EmitVerboseSIL, opts.getSingleOutputFilename(), opts.EmitSortedSIL); } 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; // Convert SIL to a lowered form suitable for IRGen. runSILLoweringPasses(*SM); // TODO: remove once the frontend understands what action it should perform IRGenOpts.OutputKind = getOutputKind(Action); if (Action == FrontendOptions::ActionType::Immediate) { assert(!MSF.is() && "-i doesn't work in -primary-file mode"); IRGenOpts.UseJIT = true; IRGenOpts.DebugInfoKind = IRGenDebugInfoKind::Normal; const ProcessCmdLine &CmdLine = ProcessCmdLine(opts.ImmediateArgv.begin(), opts.ImmediateArgv.end()); Instance.setSILModule(std::move(SM)); if (observer) { observer->aboutToRunImmediately(Instance); } ReturnValue = RunImmediately(Instance, CmdLine, IRGenOpts, Invocation.getSILOptions()); return Context.hadError(); } // FIXME: We shouldn't need to use the global context here, but // something is persisting across calls to performIRGeneration. auto &LLVMContext = getGlobalLLVMContext(); std::unique_ptr IRModule; llvm::GlobalVariable *HashGlobal; if (MSF.is()) { IRModule = performIRGeneration(IRGenOpts, *MSF.get(), std::move(SM), opts.getSingleOutputFilename(), LLVMContext, 0, &HashGlobal); } else { IRModule = performIRGeneration(IRGenOpts, MSF.get(), std::move(SM), opts.getSingleOutputFilename(), LLVMContext, &HashGlobal); } // Walk the AST for indexing after IR generation. Walking it before seems // to cause miscompilation issues. if (shouldIndex) { if (emitIndexData(MSF.dyn_cast(), Invocation, Instance)) return true; } // Just because we had an AST error it doesn't mean we can't performLLVM. bool HadError = Instance.getASTContext().hadError(); // If the AST Context has no errors but no IRModule is available, // parallelIRGen happened correctly, since parallel IRGen produces multiple // modules. if (!IRModule) { return HadError; } bool allSymbols = false; switch (opts.ValidateTBDAgainstIR) { case FrontendOptions::TBDValidationMode::None: break; case FrontendOptions::TBDValidationMode::All: allSymbols = true; LLVM_FALLTHROUGH; case FrontendOptions::TBDValidationMode::MissingFromTBD: { if (!inputFileKindCanHaveTBDValidated(Invocation.getInputKind()) || !astGuaranteedToCorrespondToSIL) break; const auto &SILOpts = Invocation.getSILOptions(); const auto hasMultipleIGMs = SILOpts.hasMultipleIGMs(); bool error; if (MSF.is()) error = validateTBD(MSF.get(), *IRModule, hasMultipleIGMs, allSymbols); else error = validateTBD(MSF.get(), *IRModule, hasMultipleIGMs, allSymbols); if (error) return true; break; } } std::unique_ptr TargetMachine = createTargetMachine(IRGenOpts, Context); version::Version EffectiveLanguageVersion = Context.LangOpts.EffectiveLanguageVersion; // Free up some compiler resources now that we have an IRModule. // NB: Don't free if we have a stats collector; it makes some use // of the AST context during shutdown. if (!Stats) Instance.freeContextAndSIL(); // Now that we have a single IR Module, hand it over to performLLVM. return performLLVM(IRGenOpts, &Instance.getDiags(), nullptr, HashGlobal, IRModule.get(), TargetMachine.get(), EffectiveLanguageVersion, opts.getSingleOutputFilename(), Stats) || HadError; } static bool emitIndexData(SourceFile *PrimarySourceFile, const CompilerInvocation &Invocation, CompilerInstance &Instance) { const FrontendOptions &opts = Invocation.getFrontendOptions(); assert(!opts.IndexStorePath.empty()); // 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) { if (index::indexAndRecord( PrimarySourceFile, opts.getSingleOutputFilename(), opts.IndexStorePath, opts.IndexSystemModules, isDebugCompilation, Invocation.getTargetTriple(), *Instance.getDependencyTracker())) { return true; } } else { StringRef moduleToken = opts.ModuleOutputPath; if (moduleToken.empty()) moduleToken = opts.getSingleOutputFilename(); if (index::indexAndRecord(Instance.getMainModule(), opts.OutputFilenames, moduleToken, opts.IndexStorePath, opts.IndexSystemModules, isDebugCompilation, Invocation.getTargetTriple(), *Instance.getDependencyTracker())) { return true; } } return false; } /// Returns true if an error occurred. static bool dumpAPI(ModuleDecl *Mod, StringRef OutDir) { using namespace llvm::sys; auto getOutPath = [&](SourceFile *SF) -> std::string { SmallString<256> Path = OutDir; StringRef Filename = SF->getFilename(); path::append(Path, path::filename(Filename)); return 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::OpenFlags::F_RW); 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 true; } for (auto *FU : Mod->getFiles()) { if (auto *SF = dyn_cast(FU)) if (dumpFile(SF)) return true; } return false; } static StringRef silOptModeArgStr(OptimizationMode mode) { switch (mode) { case OptimizationMode::ForSpeed: return "O"; case OptimizationMode::ForSize: return "Osize"; default: return "Onone"; } } int swift::performFrontend(ArrayRef Args, const char *Argv0, void *MainAddr, FrontendObserver *observer) { llvm::InitializeAllTargets(); llvm::InitializeAllTargetMCs(); llvm::InitializeAllAsmPrinters(); llvm::InitializeAllAsmParsers(); 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; PDC.handleDiagnostic(dummyMgr, SourceLoc(), DiagnosticKind::Error, "fatal error encountered during compilation; please " "file a bug report with your project and the crash " "log", {}, DiagnosticInfo()); PDC.handleDiagnostic(dummyMgr, SourceLoc(), DiagnosticKind::Note, reason, {}, DiagnosticInfo()); 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 = llvm::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) -> int { FinishDiagProcessingCheckRAII.CalledFinishDiagProcessing = true; bool err = Instance->getDiags().finishProcessing(); return retValue ? retValue : err; }; if (Args.empty()) { Instance->getDiags().diagnose(SourceLoc(), diag::error_no_frontend_args); return finishDiagProcessing(1); } CompilerInvocation Invocation; std::string MainExecutablePath = llvm::sys::fs::getMainExecutable(Argv0, MainAddr); Invocation.setMainExecutablePath(MainExecutablePath); SmallString<128> workingDirectory; llvm::sys::fs::current_path(workingDirectory); // Parse arguments. if (Invocation.parseArgs(Args, Instance->getDiags(), workingDirectory)) { return finishDiagProcessing(1); } // 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); return finishDiagProcessing(0); } if (Invocation.getFrontendOptions().RequestedAction == FrontendOptions::ActionType::NoneAction) { Instance->getDiags().diagnose(SourceLoc(), diag::error_missing_frontend_action); return finishDiagProcessing(1); } // 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 SerializedConsumer; { const std::string &SerializedDiagnosticsPath = Invocation.getFrontendOptions().SerializedDiagnosticsPath; if (!SerializedDiagnosticsPath.empty()) { SerializedConsumer.reset( serialized_diagnostics::createConsumer(SerializedDiagnosticsPath)); Instance->addDiagnosticConsumer(SerializedConsumer.get()); } } std::unique_ptr FixitsConsumer; { const std::string &FixitsOutputPath = Invocation.getFrontendOptions().FixitsOutputPath; if (!FixitsOutputPath.empty()) { FixitsConsumer.reset(new JSONFixitWriter(FixitsOutputPath, Invocation.getDiagnosticOptions())); Instance->addDiagnosticConsumer(FixitsConsumer.get()); } } if (Invocation.getDiagnosticOptions().UseColor) PDC.forceColors(); if (Invocation.getFrontendOptions().DebugTimeCompilation) SharedTimer::enableCompilationTimers(); if (Invocation.getFrontendOptions().PrintStats) { llvm::EnableStatistics(); } const DiagnosticOptions &diagOpts = Invocation.getDiagnosticOptions(); if (diagOpts.VerifyMode != DiagnosticOptions::NoVerify) { enableDiagnosticVerifier(Instance->getSourceMgr()); } DependencyTracker depTracker; if (!Invocation.getFrontendOptions().DependenciesFilePath.empty() || !Invocation.getFrontendOptions().ReferenceDependenciesFilePath.empty() || !Invocation.getFrontendOptions().IndexStorePath.empty() || !Invocation.getFrontendOptions().LoadedModuleTracePath.empty()) { Instance->setDependencyTracker(&depTracker); } if (Instance->setup(Invocation)) { return finishDiagProcessing(1); } const std::string &StatsOutputDir = Invocation.getFrontendOptions().StatsOutputDir; std::unique_ptr StatsReporter; if (!StatsOutputDir.empty()) { auto &FEOpts = Invocation.getFrontendOptions(); auto &LangOpts = Invocation.getLangOptions(); auto &SILOpts = Invocation.getSILOptions(); StringRef InputName = FEOpts.InputsAndOutputs.getNameOfUniquePrimaryInputFile(); StringRef OptType = silOptModeArgStr(SILOpts.OptMode); StringRef OutFile = FEOpts.getSingleOutputFilename(); StringRef OutputType = llvm::sys::path::extension(OutFile); std::string TripleName = LangOpts.Target.normalize(); SourceManager *SM = &Instance->getSourceMgr(); clang::SourceManager *CSM = nullptr; if (auto *clangImporter = static_cast( Instance->getASTContext().getClangModuleLoader())) { CSM = &clangImporter->getClangASTContext().getSourceManager(); } auto Trace = Invocation.getFrontendOptions().TraceStats; StatsReporter = llvm::make_unique("swift-frontend", FEOpts.ModuleName, InputName, TripleName, OutputType, OptType, StatsOutputDir, SM, CSM, Trace); // Install stats-reporter somewhere visible for subsystems that // need to bump counters as they work, rather than measure // accumulated work on completion (mostly: TypeChecker). Instance->getASTContext().Stats = StatsReporter.get(); } // The compiler instance has been configured; notify our observer. if (observer) { observer->configuredCompiler(*Instance); } int ReturnValue = 0; bool HadError = performCompile(*Instance, Invocation, Args, ReturnValue, observer, StatsReporter.get()); if (!HadError) { Mangle::printManglingStats(); } if (!HadError && !Invocation.getFrontendOptions().DumpAPIPath.empty()) { HadError = dumpAPI(Instance->getMainModule(), Invocation.getFrontendOptions().DumpAPIPath); } if (diagOpts.VerifyMode != DiagnosticOptions::NoVerify) { HadError = verifyDiagnostics( Instance->getSourceMgr(), Instance->getInputBufferIDs(), diagOpts.VerifyMode == DiagnosticOptions::VerifyAndApplyFixes, diagOpts.VerifyIgnoreUnknown); DiagnosticEngine &diags = Instance->getDiags(); if (diags.hasFatalErrorOccurred() && !Invocation.getDiagnosticOptions().ShowDiagnosticsAfterFatalError) { diags.resetHadAnyError(); diags.diagnose(SourceLoc(), diag::verify_encountered_fatal); HadError = true; } } auto r = finishDiagProcessing(HadError ? 1 : ReturnValue); if (StatsReporter) 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::performedSILDiagnostics(SILModule &module) {} void FrontendObserver::performedSILOptimization(SILModule &module) {} void FrontendObserver::aboutToRunImmediately(CompilerInstance &instance) {}