//===--- sil_opt_main.cpp - SIL Optimization Driver -----------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This is a tool for reading sil files and running sil passes on them. The // targeted usecase is debugging and testing SIL passes. // //===----------------------------------------------------------------------===// #include "swift/AST/DiagnosticsFrontend.h" #include "swift/AST/SILOptions.h" #include "swift/Basic/Assertions.h" #include "swift/Basic/FileTypes.h" #include "swift/Basic/InitializeSwiftModules.h" #include "swift/Basic/LLVMInitialize.h" #include "swift/Basic/QuotedString.h" #include "swift/Frontend/DiagnosticVerifier.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" #include "swift/IRGen/IRGenPublic.h" #include "swift/IRGen/IRGenSILPasses.h" #include "swift/Parse/ParseVersion.h" #include "swift/SIL/SILRemarkStreamer.h" #include "swift/SILOptimizer/Analysis/Analysis.h" #include "swift/SILOptimizer/PassManager/PassManager.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/Serialization/SerializationOptions.h" #include "swift/Serialization/SerializedModuleLoader.h" #include "swift/Serialization/SerializedSILLoader.h" #include "swift/Subsystems.h" #include "swift/SymbolGraphGen/SymbolGraphOptions.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/YAMLTraits.h" #include using namespace swift; namespace cl = llvm::cl; namespace { enum class OptGroup { Unknown, Diagnostics, OnonePerformance, Performance, Lowering }; std::optional toOptionalBool(llvm::cl::boolOrDefault defaultable) { switch (defaultable) { case llvm::cl::BOU_TRUE: return true; case llvm::cl::BOU_FALSE: return false; case llvm::cl::BOU_UNSET: return std::nullopt; } llvm_unreachable("Bad case for llvm::cl::boolOrDefault!"); } enum class EnforceExclusivityMode { Unchecked, // static only Checked, // static and dynamic DynamicOnly, None, }; enum class SILOptStrictConcurrency { None = 0, Complete, Targeted, Minimal, }; } // end anonymous namespace std::optional convertSILOptToRawStrictConcurrencyLevel(SILOptStrictConcurrency level) { switch (level) { case SILOptStrictConcurrency::None: return {}; case SILOptStrictConcurrency::Complete: return StrictConcurrency::Complete; case SILOptStrictConcurrency::Targeted: return StrictConcurrency::Targeted; case SILOptStrictConcurrency::Minimal: return StrictConcurrency::Minimal; } } namespace llvm { inline raw_ostream & operator<<(raw_ostream &os, const std::optional option) { if (option) { switch (*option) { case CopyPropagationOption::Off: os << "off"; break; case CopyPropagationOption::RequestedPassesOnly: os << "requested-passes-only"; break; case CopyPropagationOption::Optimizing: os << "optimizing"; break; case CopyPropagationOption::Always: os << "always"; break; } } else { os << ""; } return os; } namespace cl { template <> class parser> : public basic_parser> { public: parser(Option &O) : basic_parser>(O) {} // parse - Return true on error. bool parse(Option &O, StringRef ArgName, StringRef Arg, std::optional &Value) { if (Arg.compare_insensitive("always")) { Value = CopyPropagationOption::Always; return false; } if (Arg.compare_insensitive("optimizing")) { Value = CopyPropagationOption::Optimizing; return false; } if (Arg.compare_insensitive("false") || Arg.compare_insensitive("off") || Arg == "0") { Value = CopyPropagationOption::Off; return false; } if (Arg.compare_insensitive("requested-passes-only")) { Value = CopyPropagationOption::RequestedPassesOnly; return false; } return O.error("'" + Arg + "' is invalid for CopyPropagationOption!" " Try always, optimizing, or requested-passes-only."); } void initialize() {} enum ValueExpected getValueExpectedFlagDefault() const { return ValueOptional; } StringRef getValueName() const override { return "CopyPropagationOption"; } // Instantiate the macro PRINT_OPT_DIFF of llvm_project's CommandLine.cpp at // Optional. void printOptionDiff(const Option &O, std::optional V, OptionValue> D, size_t GlobalWidth) const { size_t MaxOptWidth = 8; printOptionName(O, GlobalWidth); std::string Str; { raw_string_ostream SS(Str); SS << V; } outs() << "= " << Str; size_t NumSpaces = MaxOptWidth > Str.size() ? MaxOptWidth - Str.size() : 0; outs().indent(NumSpaces) << " (default:"; if (D.hasValue()) outs() << D.getValue(); else outs() << "*no default*"; outs() << ")\n"; } }; } // end namespace cl } // end namespace llvm struct SILOptOptions { llvm::cl::opt InputFilename = llvm::cl::opt(llvm::cl::desc("input file"), llvm::cl::init("-"), llvm::cl::Positional); llvm::cl::opt OutputFilename = llvm::cl::opt("o", llvm::cl::desc("output filename")); llvm::cl::list ImportPaths = llvm::cl::list("I", llvm::cl::desc("add a directory to the import search path")); llvm::cl::list FrameworkPaths = llvm::cl::list("F", llvm::cl::desc("add a directory to the framework search path")); llvm::cl::list VFSOverlays = llvm::cl::list("vfsoverlay", llvm::cl::desc("add a VFS overlay")); llvm::cl::opt ModuleName = llvm::cl::opt("module-name", llvm::cl::desc("The name of the module if processing" " a module. Necessary for processing " "stdin.")); llvm::cl::opt EnableLibraryEvolution = llvm::cl::opt("enable-library-evolution", llvm::cl::desc("Compile the module to export resilient " "interfaces for all public declarations by " "default")); llvm::cl::opt StrictImplicitModuleContext = llvm::cl::opt("strict-implicit-module-context", llvm::cl::desc("Enable the strict forwarding of compilation " "context to downstream implicit module dependencies")); llvm::cl::opt DisableSILOwnershipVerifier = llvm::cl::opt( "disable-sil-ownership-verifier", llvm::cl::desc( "Do not verify SIL ownership invariants during SIL verification")); llvm::cl::opt EnableSILOpaqueValues = llvm::cl::opt("enable-sil-opaque-values", llvm::cl::desc("Compile the module with sil-opaque-values enabled.")); llvm::cl::opt EnableOSSACompleteLifetimes = llvm::cl::opt("enable-ossa-complete-lifetimes", llvm::cl::desc("Require linear OSSA lifetimes after SILGenCleanup.")); llvm::cl::opt EnableOSSAVerifyComplete = llvm::cl::opt("enable-ossa-verify-complete", llvm::cl::desc("Verify linear OSSA lifetimes after SILGenCleanup.")); llvm::cl::opt EnableObjCInterop = llvm::cl::opt("enable-objc-interop", llvm::cl::desc("Enable Objective-C interoperability.")); llvm::cl::opt DisableObjCInterop = llvm::cl::opt("disable-objc-interop", llvm::cl::desc("Disable Objective-C interoperability.")); llvm::cl::opt DisableImplicitModules = llvm::cl::opt("disable-implicit-swift-modules", llvm::cl::desc("Disable implicit swift modules.")); llvm::cl::opt ExplicitSwiftModuleMapPath = llvm::cl::opt( "explicit-swift-module-map-file", llvm::cl::desc("Explict swift module map file path")); llvm::cl::list ExperimentalFeatures = llvm::cl::list("enable-experimental-feature", llvm::cl::desc("Enable the given experimental feature.")); llvm::cl::list UpcomingFeatures = llvm::cl::list( "enable-upcoming-feature", llvm::cl::desc("Enable the given upcoming feature.")); llvm::cl::opt EnableExperimentalConcurrency = llvm::cl::opt("enable-experimental-concurrency", llvm::cl::desc("Enable experimental concurrency model.")); llvm::cl::opt EnableLexicalLifetimes = llvm::cl::opt( "enable-lexical-lifetimes", llvm::cl::init(llvm::cl::BOU_UNSET), llvm::cl::desc("Enable lexical lifetimes.")); llvm::cl::opt EnableExperimentalMoveOnly = llvm::cl::opt( "enable-experimental-move-only", llvm::cl::init(llvm::cl::BOU_UNSET), llvm::cl::desc("Enable experimental move-only semantics.")); llvm::cl::opt EnablePackMetadataStackPromotion = llvm::cl::opt( "enable-pack-metadata-stack-promotion", llvm::cl::init(true), llvm::cl::desc( "Whether to skip heapifying stack metadata packs when possible.")); llvm::cl::opt EnableExperimentalDistributed = llvm::cl::opt("enable-experimental-distributed", llvm::cl::desc("Enable experimental distributed actors.")); llvm::cl::opt VerifyExclusivity = llvm::cl::opt("enable-verify-exclusivity", llvm::cl::desc("Verify the access markers used to enforce exclusivity.")); llvm::cl::opt EnableAsyncDemotion = llvm::cl::opt("enable-async-demotion", llvm::cl::desc("Enables an optimization pass to demote async functions.")); llvm::cl::opt EnableThrowsPrediction = llvm::cl::opt("enable-throws-prediction", llvm::cl::desc("Enables optimization assumption that functions rarely throw errors.")); llvm::cl::opt EnableNoReturnCold = llvm::cl::opt("enable-noreturn-prediction", llvm::cl::desc("Enables optimization assumption that calls to no-return functions are cold.")); llvm::cl::opt EnableMoveInoutStackProtection = llvm::cl::opt("enable-move-inout-stack-protector", llvm::cl::desc("Enable the stack protector by moving values to temporaries.")); cl::opt EnforceExclusivity = cl::opt( "enforce-exclusivity", cl::desc("Enforce law of exclusivity " "(and support memory access markers)."), cl::init(EnforceExclusivityMode::Checked), cl::values(clEnumValN(EnforceExclusivityMode::Unchecked, "unchecked", "Static checking only."), clEnumValN(EnforceExclusivityMode::Checked, "checked", "Static and dynamic checking."), clEnumValN(EnforceExclusivityMode::DynamicOnly, "dynamic-only", "Dynamic checking only."), clEnumValN(EnforceExclusivityMode::None, "none", "No exclusivity checking."))); llvm::cl::opt ResourceDir = llvm::cl::opt("resource-dir", llvm::cl::desc("The directory that holds the compiler resource files")); llvm::cl::opt SDKPath = llvm::cl::opt("sdk", llvm::cl::desc("The path to the SDK for use with the clang " "importer."), llvm::cl::init("")); llvm::cl::opt Target = llvm::cl::opt("target", llvm::cl::desc("target triple"), llvm::cl::init(llvm::sys::getDefaultTargetTriple())); // This primarily determines semantics of debug information. The compiler does // not directly expose a "preserve debug info mode". It is derived from the // optimization level. At -Onone, all debug info must be preserved. At higher // levels, debug info cannot change the compiler output. // // Diagnostics should be "equivalent" at all levels. For example, programs that // compile at -Onone should compile at -O. However, it is difficult to guarantee // identical diagnostic output given the changes in SIL caused by debug info // preservation. llvm::cl::opt OptModeFlag = llvm::cl::opt( "opt-mode", llvm::cl::desc("optimization mode"), llvm::cl::values(clEnumValN(OptimizationMode::NoOptimization, "none", "preserve debug info"), clEnumValN(OptimizationMode::ForSize, "size", "ignore debug info, reduce size"), clEnumValN(OptimizationMode::ForSpeed, "speed", "ignore debug info, reduce runtime")), llvm::cl::init(OptimizationMode::NotSet)); llvm::cl::opt IRGenDebugInfoLevelArg = llvm::cl::opt( "irgen-debuginfo-level", llvm::cl::desc("IRGen debug info level"), llvm::cl::values(clEnumValN(IRGenDebugInfoLevel::None, "none", "No debug info"), clEnumValN(IRGenDebugInfoLevel::LineTables, "line-tables", "Line tables only"), clEnumValN(IRGenDebugInfoLevel::ASTTypes, "ast-types", "Line tables + AST type references"), clEnumValN(IRGenDebugInfoLevel::DwarfTypes, "dwarf-types", "Line tables + AST type refs + Dwarf types")), llvm::cl::init(IRGenDebugInfoLevel::ASTTypes)); llvm::cl::opt OptimizationGroup = llvm::cl::opt( llvm::cl::desc("Predefined optimization groups:"), llvm::cl::values( clEnumValN(OptGroup::Diagnostics, "diagnostics", "Run diagnostic passes"), clEnumValN(OptGroup::Performance, "O", "Run performance passes"), clEnumValN(OptGroup::OnonePerformance, "Onone-performance", "Run Onone perf passes"), clEnumValN(OptGroup::Lowering, "lowering", "Run lowering passes")), llvm::cl::init(OptGroup::Unknown)); llvm::cl::list Passes = llvm::cl::list(llvm::cl::desc("Passes:"), llvm::cl::values( #define PASS(ID, TAG, NAME) clEnumValN(PassKind::ID, TAG, NAME), #include "swift/SILOptimizer/PassManager/Passes.def" clEnumValN(0, "", ""))); llvm::cl::opt PrintStats = llvm::cl::opt("print-stats", llvm::cl::desc("Print various statistics")); llvm::cl::opt VerifyMode = llvm::cl::opt("verify", llvm::cl::desc("verify diagnostics against expected-" "{error|warning|note} annotations")); llvm::cl::list VerifyAdditionalPrefixes = llvm::cl::list( "verify-additional-prefix", llvm::cl::desc("Check for diagnostics with the prefix " "expected- as well as expected-")); llvm::cl::opt AssertConfId = llvm::cl::opt("assert-conf-id", llvm::cl::Hidden, llvm::cl::init(0)); llvm::cl::opt SILInlineThreshold = llvm::cl::opt("sil-inline-threshold", llvm::cl::Hidden, llvm::cl::init(-1)); // Legacy option name still in use. The frontend uses -sil-verify-all. llvm::cl::opt EnableSILVerifyAll = llvm::cl::opt("enable-sil-verify-all", llvm::cl::Hidden, llvm::cl::init(true), llvm::cl::desc("Run sil verifications after every pass.")); llvm::cl::opt SILVerifyAll = llvm::cl::opt("sil-verify-all", llvm::cl::Hidden, llvm::cl::init(true), llvm::cl::desc("Run sil verifications after every pass.")); llvm::cl::opt SILVerifyNone = llvm::cl::opt("sil-verify-none", llvm::cl::Hidden, llvm::cl::init(false), llvm::cl::desc("Completely disable SIL verification")); /// Customize the default behavior llvm::cl::opt EnableASTVerifier = llvm::cl::opt( "enable-ast-verifier", llvm::cl::Hidden, llvm::cl::init(false), llvm::cl::desc("Override the default behavior and Enable the ASTVerifier")); llvm::cl::opt DisableASTVerifier = llvm::cl::opt( "disable-ast-verifier", llvm::cl::Hidden, llvm::cl::init(false), llvm::cl::desc( "Override the default behavior and force disable the ASTVerifier")); llvm::cl::opt RemoveRuntimeAsserts = llvm::cl::opt("remove-runtime-asserts", llvm::cl::Hidden, llvm::cl::init(false), llvm::cl::desc("Remove runtime assertions (cond_fail).")); llvm::cl::opt EmitVerboseSIL = llvm::cl::opt("emit-verbose-sil", llvm::cl::desc("Emit locations during sil emission.")); llvm::cl::opt EmitSIB = llvm::cl::opt("emit-sib", llvm::cl::desc("Emit serialized AST + SIL file(s)")); llvm::cl::opt Serialize = llvm::cl::opt("serialize", llvm::cl::desc("Emit serialized AST + SIL file(s)")); llvm::cl::opt ModuleCachePath = llvm::cl::opt("module-cache-path", llvm::cl::desc("Clang module cache path")); llvm::cl::opt EmitSortedSIL = llvm::cl::opt("emit-sorted-sil", llvm::cl::Hidden, llvm::cl::init(false), llvm::cl::desc("Sort Functions, VTables, Globals, " "WitnessTables by name to ease diffing.")); llvm::cl::opt DisableASTDump = llvm::cl::opt("sil-disable-ast-dump", llvm::cl::Hidden, llvm::cl::init(false), llvm::cl::desc("Do not dump AST.")); llvm::cl::opt PerformWMO = llvm::cl::opt("wmo", llvm::cl::desc("Enable whole-module optimizations")); llvm::cl::opt EnableExperimentalStaticAssert = llvm::cl::opt( "enable-experimental-static-assert", llvm::cl::Hidden, llvm::cl::init(false), llvm::cl::desc("Enable experimental #assert")); llvm::cl::opt EnableExperimentalDifferentiableProgramming = llvm::cl::opt( "enable-experimental-differentiable-programming", llvm::cl::Hidden, llvm::cl::init(false), llvm::cl::desc("Enable experimental differentiable programming")); cl::opt PassRemarksPassed = cl::opt( "sil-remarks", cl::value_desc("pattern"), cl::desc( "Enable performed optimization remarks from passes whose name match " "the given regular expression"), cl::Hidden); cl::opt PassRemarksMissed = cl::opt( "sil-remarks-missed", cl::value_desc("pattern"), cl::desc("Enable missed optimization remarks from passes whose name match " "the given regular expression"), cl::Hidden); cl::opt RemarksFilename = cl::opt("save-optimization-record-path", cl::desc("YAML output filename for pass remarks"), cl::value_desc("filename")); cl::opt RemarksPasses = cl::opt( "save-optimization-record-passes", cl::desc("Only include passes which match a specified regular expression " "in the generated optimization record (by default, include all " "passes)"), cl::value_desc("regex")); // sil-opt doesn't have the equivalent of -save-optimization-record=. // Instead, use -save-optimization-record-format . cl::opt RemarksFormat = cl::opt( "save-optimization-record-format", cl::desc("The format used for serializing remarks (default: YAML)"), cl::value_desc("format"), cl::init("yaml")); // Strict Concurrency llvm::cl::opt StrictConcurrencyLevel = llvm::cl::opt( "strict-concurrency", cl::desc("strict concurrency level"), llvm::cl::init(SILOptStrictConcurrency::None), llvm::cl::values( clEnumValN(SILOptStrictConcurrency::Complete, "complete", "Enable complete strict concurrency"), clEnumValN(SILOptStrictConcurrency::Targeted, "targeted", "Enable targeted strict concurrency"), clEnumValN(SILOptStrictConcurrency::Minimal, "minimal", "Enable minimal strict concurrency"), clEnumValN(SILOptStrictConcurrency::None, "disabled", "Strict concurrency disabled"))); llvm::cl::opt EnableCxxInterop = llvm::cl::opt("enable-experimental-cxx-interop", llvm::cl::desc("Enable C++ interop."), llvm::cl::init(false)); llvm::cl::opt IgnoreAlwaysInline = llvm::cl::opt("ignore-always-inline", llvm::cl::desc("Ignore [always_inline] attribute."), llvm::cl::init(false)); using CPStateOpt = llvm::cl::opt, /*ExternalStorage*/ false, llvm::cl::parser>>; CPStateOpt CopyPropagationState = CPStateOpt( "enable-copy-propagation", llvm::cl::desc("Whether to run the copy propagation pass: " "'true', 'false', or 'requested-passes-only'.")); llvm::cl::opt BypassResilienceChecks = llvm::cl::opt( "bypass-resilience-checks", llvm::cl::desc("Ignore all checks for module resilience.")); llvm::cl::opt DebugDiagnosticNames = llvm::cl::opt( "debug-diagnostic-names", llvm::cl::desc("Include diagnostic names when printing")); llvm::cl::opt UnavailableDeclOptimization = llvm::cl::opt( "unavailable-decl-optimization", llvm::cl::desc("Optimization mode for unavailable declarations"), llvm::cl::values( clEnumValN(swift::UnavailableDeclOptimization::None, "none", "Don't optimize unavailable decls"), clEnumValN(swift::UnavailableDeclOptimization::Stub, "stub", "Lower unavailable functions to stubs"), clEnumValN( swift::UnavailableDeclOptimization::Complete, "complete", "Eliminate unavailable decls from lowered SIL/IR")), llvm::cl::init(swift::UnavailableDeclOptimization::None)); llvm::cl::list ClangXCC = llvm::cl::list( "Xcc", llvm::cl::desc("option to pass to clang")); llvm::cl::opt SwiftVersionString = llvm::cl::opt( "swift-version", llvm::cl::desc( "The swift version to assume AST declarations correspond to")); llvm::cl::opt EnableAddressDependencies = llvm::cl::opt( "enable-address-dependencies", llvm::cl::desc("Enable enforcement of lifetime dependencies on addressable values.")); llvm::cl::opt EnableCalleeAllocatedCoroAbi = llvm::cl::opt( "enable-callee-allocated-coro-abi", llvm::cl::desc("Override per-platform settings and use yield_once_2."), llvm::cl::init(false)); llvm::cl::opt DisableCalleeAllocatedCoroAbi = llvm::cl::opt( "disable-callee-allocated-coro-abi", llvm::cl::desc( "Override per-platform settings and don't use yield_once_2."), llvm::cl::init(false)); llvm::cl::opt MergeableTraps = llvm::cl::opt( "mergeable-traps", llvm::cl::desc("Enable cond_fail merging.")); }; /// Regular expression corresponding to the value given in one of the /// -pass-remarks* command line flags. Passes whose name matches this regexp /// will emit a diagnostic. static std::shared_ptr createOptRemarkRegex(StringRef Val) { std::shared_ptr Pattern = std::make_shared(Val); if (!Val.empty()) { std::string RegexError; if (!Pattern->isValid(RegexError)) llvm::report_fatal_error("Invalid regular expression '" + Val + "' in -sil-remarks: " + RegexError, false); } return Pattern; } static void runCommandLineSelectedPasses(SILModule *Module, irgen::IRGenModule *IRGenMod, const SILOptOptions &options) { const SILOptions &opts = Module->getOptions(); // If a specific pass was requested with -opt-mode=None, run the pass as a // mandatory pass. bool isMandatory = opts.OptMode == OptimizationMode::NoOptimization; executePassPipelinePlan( Module, SILPassPipelinePlan::getPassPipelineForKinds(opts, options.Passes), isMandatory, IRGenMod); if (Module->getOptions().VerifyAll) { Module->verify(); SILPassManager pm(Module, isMandatory, IRGenMod); pm.runSwiftModuleVerification(); } } namespace { using ASTVerifierOverrideKind = LangOptions::ASTVerifierOverrideKind; } // end anonymous namespace static std::optional getASTOverrideKind(const SILOptOptions &options) { assert(!(options.EnableASTVerifier && options.DisableASTVerifier) && "Can only set one of EnableASTVerifier/DisableASTVerifier?!"); if (options.EnableASTVerifier) return ASTVerifierOverrideKind::EnableVerifier; if (options.DisableASTVerifier) return ASTVerifierOverrideKind::DisableVerifier; return std::nullopt; } int sil_opt_main(ArrayRef argv, void *MainAddr) { INITIALIZE_LLVM(); llvm::EnablePrettyStackTraceOnSigInfoForThisThread(); SILOptOptions options; llvm::cl::ParseCommandLineOptions(argv.size(), argv.data(), "Swift SIL optimizer\n"); if (options.PrintStats) llvm::EnableStatistics(); CompilerInvocation Invocation; Invocation.setMainExecutablePath( llvm::sys::fs::getMainExecutable(argv[0], MainAddr)); // Give the context the list of search paths to use for modules. std::vector ImportPaths; for (const auto &path : options.ImportPaths) { ImportPaths.push_back({path, /*isSystem=*/false}); } Invocation.setImportSearchPaths(ImportPaths); std::vector FramePaths; for (const auto &path : options.FrameworkPaths) { FramePaths.push_back({path, /*isSystem=*/false}); } Invocation.setFrameworkSearchPaths(FramePaths); Invocation.setVFSOverlays(options.VFSOverlays); // Set the SDK path and target if given. if (options.SDKPath.getNumOccurrences() == 0) { const char *SDKROOT = getenv("SDKROOT"); if (SDKROOT) options.SDKPath = SDKROOT; } if (!options.SDKPath.empty()) Invocation.setSDKPath(options.SDKPath); if (!options.Target.empty()) Invocation.setTargetTriple(options.Target); if (!options.ResourceDir.empty()) Invocation.setRuntimeResourcePath(options.ResourceDir); if (options.EnableLibraryEvolution) Invocation.getLangOptions().enableFeature(Feature::LibraryEvolution); Invocation.getFrontendOptions().StrictImplicitModuleContext = options.StrictImplicitModuleContext; Invocation.getFrontendOptions().DisableImplicitModules = options.DisableImplicitModules; Invocation.getSearchPathOptions().ExplicitSwiftModuleMapPath = options.ExplicitSwiftModuleMapPath; // Set the module cache path. If not passed in we use the default swift module // cache. Invocation.getClangImporterOptions().ModuleCachePath = options.ModuleCachePath; Invocation.setParseStdlib(); if (options.SwiftVersionString.size()) { auto vers = VersionParser::parseVersionString(options.SwiftVersionString, SourceLoc(), nullptr); bool isValid = false; if (vers.has_value()) { if (auto effectiveVers = vers.value().getEffectiveLanguageVersion()) { Invocation.getLangOptions().EffectiveLanguageVersion = effectiveVers.value(); isValid = true; } } if (!isValid) { llvm::errs() << "error: invalid swift version " << options.SwiftVersionString << '\n'; exit(-1); } } Invocation.getLangOptions().DisableAvailabilityChecking = true; Invocation.getLangOptions().EnableAccessControl = false; Invocation.getLangOptions().EnableObjCAttrRequiresFoundation = false; Invocation.getLangOptions().EnableDeserializationSafety = false; if (auto overrideKind = getASTOverrideKind(options)) { Invocation.getLangOptions().ASTVerifierOverride = *overrideKind; } Invocation.getLangOptions().EnableExperimentalConcurrency = options.EnableExperimentalConcurrency; std::optional enableExperimentalMoveOnly = toOptionalBool(options.EnableExperimentalMoveOnly); if (enableExperimentalMoveOnly && *enableExperimentalMoveOnly) { // FIXME: drop addition of Feature::MoveOnly once its queries are gone. Invocation.getLangOptions().enableFeature(Feature::MoveOnly); Invocation.getLangOptions().enableFeature(Feature::NoImplicitCopy); Invocation.getLangOptions().enableFeature( Feature::OldOwnershipOperatorSpellings); } Invocation.getLangOptions().BypassResilienceChecks = options.BypassResilienceChecks; if (options.DebugDiagnosticNames) { Invocation.getDiagnosticOptions().PrintDiagnosticNames = PrintDiagnosticNamesMode::Identifier; } for (auto &featureName : options.UpcomingFeatures) { auto feature = Feature::getUpcomingFeature(featureName); if (!feature) { llvm::errs() << "error: unknown upcoming feature " << QuotedString(featureName) << "\n"; exit(-1); } if (auto firstVersion = feature->getLanguageVersion()) { if (Invocation.getLangOptions().isSwiftVersionAtLeast(*firstVersion)) { llvm::errs() << "error: upcoming feature " << QuotedString(featureName) << " is already enabled as of Swift version " << *firstVersion << '\n'; exit(-1); } } Invocation.getLangOptions().enableFeature(*feature); } for (auto &featureName : options.ExperimentalFeatures) { if (auto feature = Feature::getExperimentalFeature(featureName)) { Invocation.getLangOptions().enableFeature(*feature); } else { llvm::errs() << "error: unknown experimental feature " << QuotedString(featureName) << "\n"; exit(-1); } } Invocation.getLangOptions().EnableObjCInterop = options.EnableObjCInterop ? true : options.DisableObjCInterop ? false : llvm::Triple(options.Target).isOSDarwin(); Invocation.getLangOptions().OptimizationRemarkPassedPattern = createOptRemarkRegex(options.PassRemarksPassed); Invocation.getLangOptions().OptimizationRemarkMissedPattern = createOptRemarkRegex(options.PassRemarksMissed); if (options.EnableExperimentalStaticAssert) Invocation.getLangOptions().enableFeature(Feature::StaticAssert); if (options.EnableExperimentalDifferentiableProgramming) { Invocation.getLangOptions().enableFeature( Feature::DifferentiableProgramming); } Invocation.getLangOptions().EnableCXXInterop = options.EnableCxxInterop; Invocation.computeCXXStdlibOptions(); Invocation.getLangOptions().UnavailableDeclOptimizationMode = options.UnavailableDeclOptimization; Invocation.computeAArch64TBIOptions(); // Enable strict concurrency if we have the feature specified or if it was // specified via a command line option to sil-opt. if (Invocation.getLangOptions().hasFeature(Feature::StrictConcurrency)) { Invocation.getLangOptions().StrictConcurrencyLevel = StrictConcurrency::Complete; } else if (auto level = convertSILOptToRawStrictConcurrencyLevel( options.StrictConcurrencyLevel)) { // If strict concurrency was enabled from the cmdline so the feature flag as // well. if (*level == StrictConcurrency::Complete) Invocation.getLangOptions().enableFeature(Feature::StrictConcurrency); Invocation.getLangOptions().StrictConcurrencyLevel = *level; } // If we have strict concurrency set as a feature and were told to turn off // region-based isolation... do so now. if (Invocation.getLangOptions().hasFeature(Feature::StrictConcurrency)) { Invocation.getLangOptions().enableFeature(Feature::RegionBasedIsolation); } Invocation.getDiagnosticOptions().VerifyMode = options.VerifyMode ? DiagnosticOptions::Verify : DiagnosticOptions::NoVerify; for (auto &additionalPrefixes : options.VerifyAdditionalPrefixes) { Invocation.getDiagnosticOptions() .AdditionalDiagnosticVerifierPrefixes.push_back(additionalPrefixes); } ClangImporterOptions &clangImporterOptions = Invocation.getClangImporterOptions(); for (const auto &xcc : options.ClangXCC) { clangImporterOptions.ExtraArgs.push_back(xcc); } // Setup the SIL Options. SILOptions &SILOpts = Invocation.getSILOptions(); SILOpts.InlineThreshold = options.SILInlineThreshold; SILOpts.VerifyAll = options.SILVerifyAll || options.EnableSILVerifyAll; SILOpts.VerifyNone = options.SILVerifyNone; SILOpts.RemoveRuntimeAsserts = options.RemoveRuntimeAsserts; SILOpts.AssertConfig = options.AssertConfId; SILOpts.VerifySILOwnership = !options.DisableSILOwnershipVerifier; SILOpts.OptRecordFile = options.RemarksFilename; SILOpts.OptRecordPasses = options.RemarksPasses; SILOpts.EnableStackProtection = true; SILOpts.EnableMoveInoutStackProtection = options.EnableMoveInoutStackProtection; SILOpts.VerifyExclusivity = options.VerifyExclusivity; if (options.EnforceExclusivity.getNumOccurrences() != 0) { switch (options.EnforceExclusivity) { case EnforceExclusivityMode::Unchecked: // This option is analogous to the -Ounchecked optimization setting. // It will disable dynamic checking but still diagnose statically. SILOpts.EnforceExclusivityStatic = true; SILOpts.EnforceExclusivityDynamic = false; break; case EnforceExclusivityMode::Checked: SILOpts.EnforceExclusivityStatic = true; SILOpts.EnforceExclusivityDynamic = true; break; case EnforceExclusivityMode::DynamicOnly: // This option is intended for staging purposes. The intent is that // it will eventually be removed. SILOpts.EnforceExclusivityStatic = false; SILOpts.EnforceExclusivityDynamic = true; break; case EnforceExclusivityMode::None: // This option is for staging purposes. SILOpts.EnforceExclusivityStatic = false; SILOpts.EnforceExclusivityDynamic = false; break; } } SILOpts.EmitVerboseSIL |= options.EmitVerboseSIL; SILOpts.EmitSortedSIL |= options.EmitSortedSIL; SILOpts.EnableAsyncDemotion = options.EnableAsyncDemotion; SILOpts.EnableThrowsPrediction = options.EnableThrowsPrediction; SILOpts.EnableNoReturnCold = options.EnableNoReturnCold; SILOpts.IgnoreAlwaysInline = options.IgnoreAlwaysInline; SILOpts.EnableSILOpaqueValues = options.EnableSILOpaqueValues; SILOpts.OSSACompleteLifetimes = options.EnableOSSACompleteLifetimes; SILOpts.OSSAVerifyComplete = options.EnableOSSAVerifyComplete; SILOpts.StopOptimizationAfterSerialization |= options.EmitSIB; if (options.CopyPropagationState) { SILOpts.CopyPropagation = *options.CopyPropagationState; } // Unless overridden below, enabling copy propagation means enabling lexical // lifetimes. if (SILOpts.CopyPropagation >= CopyPropagationOption::Optimizing) SILOpts.LexicalLifetimes = LexicalLifetimesOption::On; // Unless overridden below, disable copy propagation means disabling lexical // lifetimes. if (SILOpts.CopyPropagation == CopyPropagationOption::Off) SILOpts.LexicalLifetimes = LexicalLifetimesOption::DiagnosticMarkersOnly; std::optional enableLexicalLifetimes = toOptionalBool(options.EnableLexicalLifetimes); if (enableLexicalLifetimes) SILOpts.LexicalLifetimes = *enableLexicalLifetimes ? LexicalLifetimesOption::On : LexicalLifetimesOption::DiagnosticMarkersOnly; SILOpts.EnablePackMetadataStackPromotion = options.EnablePackMetadataStackPromotion; SILOpts.EnableAddressDependencies = options.EnableAddressDependencies; if (options.EnableCalleeAllocatedCoroAbi) SILOpts.CoroutineAccessorsUseYieldOnce2 = true; if (options.DisableCalleeAllocatedCoroAbi) SILOpts.CoroutineAccessorsUseYieldOnce2 = false; SILOpts.MergeableTraps = options.MergeableTraps; if (options.OptModeFlag == OptimizationMode::NotSet) { if (options.OptimizationGroup == OptGroup::Diagnostics) SILOpts.OptMode = OptimizationMode::NoOptimization; else SILOpts.OptMode = OptimizationMode::ForSpeed; } else { SILOpts.OptMode = options.OptModeFlag; } auto &IRGenOpts = Invocation.getIRGenOptions(); if (options.OptModeFlag == OptimizationMode::NotSet) { if (options.OptimizationGroup == OptGroup::Diagnostics) IRGenOpts.OptMode = OptimizationMode::NoOptimization; else IRGenOpts.OptMode = OptimizationMode::ForSpeed; } else { IRGenOpts.OptMode = options.OptModeFlag; } IRGenOpts.DebugInfoLevel = options.IRGenDebugInfoLevelArg; // Note: SILOpts, LangOpts, and IRGenOpts must be set before the // CompilerInstance is initializer below based on Invocation. serialization::ExtendedValidationInfo extendedInfo; llvm::ErrorOr> FileBufOrErr = Invocation.setUpInputForSILTool(options.InputFilename, options.ModuleName, /*alwaysSetModuleToMain*/ false, /*bePrimary*/ !options.PerformWMO, extendedInfo); if (!FileBufOrErr) { fprintf(stderr, "Error! Failed to open file: %s\n", options.InputFilename.c_str()); exit(-1); } CompilerInstance CI; PrintingDiagnosticConsumer PrintDiags; CI.addDiagnosticConsumer(&PrintDiags); if (options.VerifyMode) PrintDiags.setSuppressOutput(true); struct FinishDiagProcessingCheckRAII { bool CalledFinishDiagProcessing = false; ~FinishDiagProcessingCheckRAII() { assert(CalledFinishDiagProcessing && "returned from the function " "without calling finishDiagProcessing"); } } FinishDiagProcessingCheckRAII; auto finishDiagProcessing = [&](int retValue) -> int { FinishDiagProcessingCheckRAII.CalledFinishDiagProcessing = true; PrintDiags.setSuppressOutput(false); bool diagnosticsError = CI.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 (options.VerifyMode && !diagnosticsError) { return 0; } return retValue ? retValue : diagnosticsError; }; std::string InstanceSetupError; if (CI.setup(Invocation, InstanceSetupError)) { llvm::errs() << InstanceSetupError << '\n'; // Rather than finish Diag processing, exit -1 here to show we failed to // setup here. The reason we do this is if the setup fails, we want to fail // hard. We shouldn't be testing that we setup correctly with // -verify/etc. We should be testing that later. exit(-1); } CI.performSema(); // If parsing produced an error, don't run any passes. bool HadError = CI.getASTContext().hadError(); if (HadError) return finishDiagProcessing(1); auto *mod = CI.getMainModule(); assert(mod->getFiles().size() == 1); std::unique_ptr SILMod; if (options.PerformWMO) { SILMod = performASTLowering(mod, CI.getSILTypes(), CI.getSILOptions()); } else { SILMod = performASTLowering(*mod->getFiles()[0], CI.getSILTypes(), CI.getSILOptions()); } SILMod->setSerializeSILAction([]{}); if (!options.RemarksFilename.empty()) { llvm::Expected formatOrErr = llvm::remarks::parseFormat(options.RemarksFormat); if (llvm::Error E = formatOrErr.takeError()) { CI.getDiags().diagnose(SourceLoc(), diag::error_creating_remark_serializer, toString(std::move(E))); HadError = true; SILOpts.OptRecordFormat = llvm::remarks::Format::YAML; } else { SILOpts.OptRecordFormat = *formatOrErr; } SILMod->installSILRemarkStreamer(); } switch (options.OptimizationGroup) { case OptGroup::Diagnostics: runSILDiagnosticPasses(*SILMod.get()); break; case OptGroup::Performance: runSILOptimizationPasses(*SILMod.get()); break; case OptGroup::Lowering: runSILLoweringPasses(*SILMod.get()); break; case OptGroup::OnonePerformance: runSILPassesForOnone(*SILMod.get()); break; case OptGroup::Unknown: { auto T = irgen::createIRGenModule( SILMod.get(), Invocation.getOutputFilenameForAtMostOnePrimary(), Invocation.getMainInputFilenameForDebugInfoForAtMostOnePrimary(), "", IRGenOpts); runCommandLineSelectedPasses(SILMod.get(), T.second, options); irgen::deleteIRGenModule(T); break; } } if (options.EmitSIB || options.Serialize) { llvm::SmallString<128> OutputFile; if (options.OutputFilename.size()) { OutputFile = options.OutputFilename; } else if (options.ModuleName.size()) { OutputFile = options.ModuleName; llvm::sys::path::replace_extension( OutputFile, file_types::getExtension(file_types::TY_SIB)); } else { OutputFile = CI.getMainModule()->getName().str(); llvm::sys::path::replace_extension( OutputFile, file_types::getExtension(file_types::TY_SIB)); } SerializationOptions serializationOpts; serializationOpts.OutputPath = OutputFile; serializationOpts.SerializeAllSIL = options.EmitSIB; serializationOpts.IsSIB = options.EmitSIB; symbolgraphgen::SymbolGraphOptions symbolGraphOptions; serialize(CI.getMainModule(), serializationOpts, symbolGraphOptions, SILMod.get()); } else { const StringRef OutputFile = options.OutputFilename.size() ? StringRef(options.OutputFilename) : "-"; auto SILOpts = SILOptions(); SILOpts.EmitVerboseSIL = options.EmitVerboseSIL; SILOpts.EmitSortedSIL = options.EmitSortedSIL; if (OutputFile == "-") { SILMod->print(llvm::outs(), CI.getMainModule(), SILOpts, !options.DisableASTDump); } else { std::error_code EC; llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_None); if (EC) { llvm::errs() << "while opening '" << OutputFile << "': " << EC.message() << '\n'; return finishDiagProcessing(1); } SILMod->print(OS, CI.getMainModule(), SILOpts, !options.DisableASTDump); } } HadError |= CI.getASTContext().hadError(); if (options.VerifyMode) { DiagnosticEngine &diags = CI.getDiags(); if (diags.hasFatalErrorOccurred() && !Invocation.getDiagnosticOptions().ShowDiagnosticsAfterFatalError) { diags.resetHadAnyError(); diags.diagnose(SourceLoc(), diag::verify_encountered_fatal); HadError = true; } } return finishDiagProcessing(HadError); }