From 1a660c08ca4569239af4f2394ef865e17d94fbeb Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Fri, 20 Aug 2021 13:41:33 -0700 Subject: [PATCH] Frontend: teach -compile-module-from-interface action to emit ABI descriptor as byproduct We have implemented a libSwiftDriver-based tool to generate prebuilt module cache for entire SDKs. Anchored on the same infrastructure, we could also generate ABI baselines for entire SDKs. --- include/swift/AST/DiagnosticsFrontend.def | 2 + .../swift/Basic/SupplementaryOutputPaths.h | 7 +++- .../swift/Frontend/FrontendInputsAndOutputs.h | 1 + include/swift/Frontend/FrontendOptions.h | 1 + .../swift/Frontend/ModuleInterfaceLoader.h | 3 +- include/swift/Option/FrontendOptions.td | 4 ++ .../ArgsToFrontendOptionsConverter.cpp | 5 +++ .../ArgsToFrontendOutputsConverter.cpp | 8 +++- lib/Frontend/FrontendInputsAndOutputs.cpp | 6 +++ lib/Frontend/FrontendOptions.cpp | 42 ++++++++++++++++++- lib/Frontend/ModuleInterfaceBuilder.cpp | 3 ++ lib/Frontend/ModuleInterfaceBuilder.h | 3 ++ lib/Frontend/ModuleInterfaceLoader.cpp | 11 ++--- lib/FrontendTool/FrontendTool.cpp | 4 +- .../ModuleInterface/emit-abi-descriptor.swift | 13 ++++++ 15 files changed, 103 insertions(+), 10 deletions(-) create mode 100644 test/ModuleInterface/emit-abi-descriptor.swift diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index 3b4c281c28fcf..1e6be6b85717b 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -129,6 +129,8 @@ ERROR(error_mode_cannot_emit_module_summary,none, "this mode does not support emitting module summary files", ()) ERROR(error_mode_cannot_emit_symbol_graph,none, "this mode does not support emitting symbol graph files", ()) +ERROR(error_mode_cannot_emit_abi_descriptor,none, + "this mode does not support emitting ABI descriptor", ()) ERROR(cannot_emit_ir_skipping_function_bodies,none, "the -experimental-skip-*-function-bodies* flags do not support " "emitting IR", ()) diff --git a/include/swift/Basic/SupplementaryOutputPaths.h b/include/swift/Basic/SupplementaryOutputPaths.h index 04ac7f27d3608..9e51fdfb65010 100644 --- a/include/swift/Basic/SupplementaryOutputPaths.h +++ b/include/swift/Basic/SupplementaryOutputPaths.h @@ -143,6 +143,9 @@ struct SupplementaryOutputPaths { /// The path to which we should emit module summary file. std::string ModuleSummaryOutputPath; + /// The output path to generate ABI baseline. + std::string ABIDescriptorOutputPath; + SupplementaryOutputPaths() = default; SupplementaryOutputPaths(const SupplementaryOutputPaths &) = default; @@ -174,6 +177,8 @@ struct SupplementaryOutputPaths { fn(PrivateModuleInterfaceOutputPath); if (!ModuleSummaryOutputPath.empty()) fn(ModuleSummaryOutputPath); + if (!ABIDescriptorOutputPath.empty()) + fn(ABIDescriptorOutputPath); } bool empty() const { @@ -182,7 +187,7 @@ struct SupplementaryOutputPaths { ReferenceDependenciesFilePath.empty() && SerializedDiagnosticsPath.empty() && LoadedModuleTracePath.empty() && TBDPath.empty() && ModuleInterfaceOutputPath.empty() && - ModuleSourceInfoOutputPath.empty(); + ModuleSourceInfoOutputPath.empty() && ABIDescriptorOutputPath.empty(); } }; } // namespace swift diff --git a/include/swift/Frontend/FrontendInputsAndOutputs.h b/include/swift/Frontend/FrontendInputsAndOutputs.h index 28f60519074b4..4ccb1fc14860d 100644 --- a/include/swift/Frontend/FrontendInputsAndOutputs.h +++ b/include/swift/Frontend/FrontendInputsAndOutputs.h @@ -256,6 +256,7 @@ class FrontendInputsAndOutputs { bool hasModuleSourceInfoOutputPath() const; bool hasModuleInterfaceOutputPath() const; bool hasPrivateModuleInterfaceOutputPath() const; + bool hasABIDescriptorOutputPath() const; bool hasModuleSummaryOutputPath() const; bool hasTBDPath() const; diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index b34c17b7b6526..930614d484e9e 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -427,6 +427,7 @@ class FrontendOptions { static bool canActionEmitModuleDoc(ActionType); static bool canActionEmitModuleSummary(ActionType); static bool canActionEmitInterface(ActionType); + static bool canActionEmitABIDescriptor(ActionType); public: static bool doesActionGenerateSIL(ActionType); diff --git a/include/swift/Frontend/ModuleInterfaceLoader.h b/include/swift/Frontend/ModuleInterfaceLoader.h index 39f9629219701..69aed16d73ab2 100644 --- a/include/swift/Frontend/ModuleInterfaceLoader.h +++ b/include/swift/Frontend/ModuleInterfaceLoader.h @@ -430,7 +430,8 @@ class ModuleInterfaceLoader : public SerializedModuleLoaderBase { const ClangImporterOptions &ClangOpts, StringRef CacheDir, StringRef PrebuiltCacheDir, StringRef BackupInterfaceDir, StringRef ModuleName, StringRef InPath, - StringRef OutPath, bool SerializeDependencyHashes, + StringRef OutPath, StringRef ABIOutputPath, + bool SerializeDependencyHashes, bool TrackSystemDependencies, ModuleInterfaceLoaderOptions Opts, RequireOSSAModules_t RequireOSSAModules); }; diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 617ab614bb667..78ec082f879be 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -64,6 +64,10 @@ def emit_fixits_path : Separate<["-"], "emit-fixits-path">, MetaVarName<"">, HelpText<"Output compiler fixits as source edits to ">; +def emit_abi_descriptor_path + : Separate<["-"], "emit-abi-descriptor-path">, MetaVarName<"">, + HelpText<"Output the ABI descriptor of current module to ">; + def serialize_module_interface_dependency_hashes : Flag<["-"], "serialize-module-interface-dependency-hashes">, Flags<[HelpHidden]>; diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index 13180f5123998..d3743c010e6e6 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -610,6 +610,11 @@ bool ArgsToFrontendOptionsConverter::checkUnusedSupplementaryOutputPaths() Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_module_doc); return true; } + if (!FrontendOptions::canActionEmitABIDescriptor(Opts.RequestedAction) && + Opts.InputsAndOutputs.hasABIDescriptorOutputPath()) { + Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_abi_descriptor); + return true; + } // If we cannot emit module doc, we cannot emit source information file either. if (!FrontendOptions::canActionEmitModuleDoc(Opts.RequestedAction) && Opts.InputsAndOutputs.hasModuleSourceInfoOutputPath()) { diff --git a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp index 3857020f071df..c8ac5777c2859 100644 --- a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp @@ -339,11 +339,13 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() options::OPT_emit_module_source_info_path); auto moduleSummaryOutput = getSupplementaryFilenamesFromArguments( options::OPT_emit_module_summary_path); + auto abiDescriptorOutput = getSupplementaryFilenamesFromArguments( + options::OPT_emit_abi_descriptor_path); if (!objCHeaderOutput || !moduleOutput || !moduleDocOutput || !dependenciesFile || !referenceDependenciesFile || !serializedDiagnostics || !fixItsOutput || !loadedModuleTrace || !TBD || !moduleInterfaceOutput || !privateModuleInterfaceOutput || - !moduleSourceInfoOutput || !moduleSummaryOutput) { + !moduleSourceInfoOutput || !moduleSummaryOutput || !abiDescriptorOutput) { return None; } std::vector result; @@ -365,6 +367,7 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() sop.PrivateModuleInterfaceOutputPath = (*privateModuleInterfaceOutput)[i]; sop.ModuleSourceInfoOutputPath = (*moduleSourceInfoOutput)[i]; sop.ModuleSummaryOutputPath = (*moduleSummaryOutput)[i]; + sop.ABIDescriptorOutputPath = (*abiDescriptorOutput)[i]; result.push_back(sop); } return result; @@ -463,6 +466,8 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput( auto PrivateModuleInterfaceOutputPath = pathsFromArguments.PrivateModuleInterfaceOutputPath; + // There is no non-path form of -emit-abi-descriptor-path + auto ABIDescriptorOutputPath = pathsFromArguments.ABIDescriptorOutputPath; ID emitModuleOption; std::string moduleExtension; std::string mainOutputIfUsableForModule; @@ -488,6 +493,7 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput( sop.PrivateModuleInterfaceOutputPath = PrivateModuleInterfaceOutputPath; sop.ModuleSourceInfoOutputPath = moduleSourceInfoOutputPath; sop.ModuleSummaryOutputPath = moduleSummaryOutputPath; + sop.ABIDescriptorOutputPath = ABIDescriptorOutputPath; return sop; } diff --git a/lib/Frontend/FrontendInputsAndOutputs.cpp b/lib/Frontend/FrontendInputsAndOutputs.cpp index c65b43e6543be..d76a2db1b0a6d 100644 --- a/lib/Frontend/FrontendInputsAndOutputs.cpp +++ b/lib/Frontend/FrontendInputsAndOutputs.cpp @@ -503,6 +503,12 @@ bool FrontendInputsAndOutputs::hasPrivateModuleInterfaceOutputPath() const { return outs.PrivateModuleInterfaceOutputPath; }); } +bool FrontendInputsAndOutputs::hasABIDescriptorOutputPath() const { + return hasSupplementaryOutputPath( + [](const SupplementaryOutputPaths &outs) -> const std::string & { + return outs.ABIDescriptorOutputPath; + }); +} bool FrontendInputsAndOutputs::hasModuleSummaryOutputPath() const { return hasSupplementaryOutputPath( [](const SupplementaryOutputPaths &outs) -> const std::string & { diff --git a/lib/Frontend/FrontendOptions.cpp b/lib/Frontend/FrontendOptions.cpp index 452c1e5b99f87..08e0d14243cf1 100644 --- a/lib/Frontend/FrontendOptions.cpp +++ b/lib/Frontend/FrontendOptions.cpp @@ -524,7 +524,47 @@ bool FrontendOptions::canActionEmitLoadedModuleTrace(ActionType action) { } llvm_unreachable("unhandled action"); } - +bool FrontendOptions::canActionEmitABIDescriptor(ActionType action) { + switch (action) { + case ActionType::CompileModuleFromInterface: + return true; + case ActionType::NoneAction: + case ActionType::Parse: + case ActionType::ResolveImports: + case ActionType::Typecheck: + case ActionType::DumpParse: + case ActionType::DumpInterfaceHash: + case ActionType::DumpAST: + case ActionType::EmitSyntax: + case ActionType::PrintAST: + case ActionType::EmitPCH: + case ActionType::DumpScopeMaps: + case ActionType::DumpTypeRefinementContexts: + case ActionType::DumpTypeInfo: + case ActionType::EmitSILGen: + case ActionType::TypecheckModuleFromInterface: + case ActionType::Immediate: + case ActionType::REPL: + case ActionType::EmitPCM: + case ActionType::DumpPCM: + case ActionType::ScanDependencies: + case ActionType::PrintVersion: + case ActionType::PrintFeature: + case ActionType::MergeModules: + case ActionType::EmitModuleOnly: + case ActionType::EmitSIL: + case ActionType::EmitSIBGen: + case ActionType::EmitSIB: + case ActionType::EmitIRGen: + case ActionType::EmitIR: + case ActionType::EmitBC: + case ActionType::EmitAssembly: + case ActionType::EmitObject: + case ActionType::EmitImportedModules: + return false; + } + llvm_unreachable("unhandled action"); +} bool FrontendOptions::canActionEmitModule(ActionType action) { switch (action) { case ActionType::NoneAction: diff --git a/lib/Frontend/ModuleInterfaceBuilder.cpp b/lib/Frontend/ModuleInterfaceBuilder.cpp index 26d91613a93cb..4c8a3e18579da 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.cpp +++ b/lib/Frontend/ModuleInterfaceBuilder.cpp @@ -284,6 +284,9 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal( if (SubInstance.getDiags().hadAnyError()) { return std::make_error_code(std::errc::not_supported); } + if (!ABIDescriptorPath.empty()) { + swift::ide::api::dumpModuleContent(Mod, ABIDescriptorPath, true); + } return std::error_code(); }); }, ThreadStackSize); diff --git a/lib/Frontend/ModuleInterfaceBuilder.h b/lib/Frontend/ModuleInterfaceBuilder.h index dee7b87277a2a..fc55c807a4690 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.h +++ b/lib/Frontend/ModuleInterfaceBuilder.h @@ -42,6 +42,7 @@ class ModuleInterfaceBuilder { const StringRef moduleCachePath; const StringRef prebuiltCachePath; const StringRef backupInterfaceDir; + const StringRef ABIDescriptorPath; const bool disableInterfaceFileLock; const SourceLoc diagnosticLoc; DependencyTracker *const dependencyTracker; @@ -96,6 +97,7 @@ class ModuleInterfaceBuilder { StringRef moduleCachePath, StringRef backupInterfaceDir, StringRef prebuiltCachePath, + StringRef ABIDescriptorPath, bool disableInterfaceFileLock = false, SourceLoc diagnosticLoc = SourceLoc(), DependencyTracker *tracker = nullptr) @@ -104,6 +106,7 @@ class ModuleInterfaceBuilder { interfacePath(interfacePath), moduleName(moduleName), moduleCachePath(moduleCachePath), prebuiltCachePath(prebuiltCachePath), backupInterfaceDir(backupInterfaceDir), + ABIDescriptorPath(ABIDescriptorPath), disableInterfaceFileLock(disableInterfaceFileLock), diagnosticLoc(diagnosticLoc), dependencyTracker(tracker) {} diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index f47942ea33df4..380b0a6b2e66d 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -1007,7 +1007,7 @@ class ModuleInterfaceLoaderImpl { ModuleInterfaceBuilder builder( ctx.SourceMgr, diagsToUse, astDelegate, interfacePath, moduleName, cacheDir, - prebuiltCacheDir, backupInterfaceDir, + prebuiltCacheDir, backupInterfaceDir, StringRef(), Opts.disableInterfaceLock, diagnosticLoc, dependencyTracker); // If we found an out-of-date .swiftmodule, we still want to add it as @@ -1039,7 +1039,7 @@ class ModuleInterfaceLoaderImpl { // the genericSubInvocation we'll need to use to compute the cache paths. ModuleInterfaceBuilder fallbackBuilder( ctx.SourceMgr, &ctx.Diags, astDelegate, backupPath, moduleName, cacheDir, - prebuiltCacheDir, backupInterfaceDir, + prebuiltCacheDir, backupInterfaceDir, StringRef(), Opts.disableInterfaceLock, diagnosticLoc, dependencyTracker); if (rebuildInfo.sawOutOfDateModule(modulePath)) @@ -1208,7 +1208,8 @@ bool ModuleInterfaceLoader::buildSwiftModuleFromSwiftInterface( const ClangImporterOptions &ClangOpts, StringRef CacheDir, StringRef PrebuiltCacheDir, StringRef BackupInterfaceDir, StringRef ModuleName, StringRef InPath, - StringRef OutPath, bool SerializeDependencyHashes, + StringRef OutPath, StringRef ABIOutputPath, + bool SerializeDependencyHashes, bool TrackSystemDependencies, ModuleInterfaceLoaderOptions LoaderOpts, RequireOSSAModules_t RequireOSSAModules) { InterfaceSubContextDelegateImpl astDelegate( @@ -1218,7 +1219,7 @@ bool ModuleInterfaceLoader::buildSwiftModuleFromSwiftInterface( SerializeDependencyHashes, TrackSystemDependencies, RequireOSSAModules); ModuleInterfaceBuilder builder(SourceMgr, &Diags, astDelegate, InPath, ModuleName, CacheDir, PrebuiltCacheDir, - BackupInterfaceDir, + BackupInterfaceDir, ABIOutputPath, LoaderOpts.disableInterfaceLock); // FIXME: We really only want to serialize 'important' dependencies here, if // we want to ship the built swiftmodules to another machine. @@ -1236,7 +1237,7 @@ bool ModuleInterfaceLoader::buildSwiftModuleFromSwiftInterface( assert(!backInPath.empty()); ModuleInterfaceBuilder backupBuilder(SourceMgr, &Diags, astDelegate, backInPath, ModuleName, CacheDir, PrebuiltCacheDir, - BackupInterfaceDir, + BackupInterfaceDir, ABIOutputPath, LoaderOpts.disableInterfaceLock); // Ensure we can rebuild module after user changed the original interface file. backupBuilder.addExtraDependency(InPath); diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 4d77daf57d5cb..ed1e921362533 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -412,13 +412,15 @@ static bool buildModuleFromInterface(CompilerInstance &Instance) { StringRef InputPath = FEOpts.InputsAndOutputs.getFilenameOfFirstInput(); StringRef PrebuiltCachePath = FEOpts.PrebuiltModuleCachePath; ModuleInterfaceLoaderOptions LoaderOpts(FEOpts); + StringRef ABIPath = Instance.getPrimarySpecificPathsForAtMostOnePrimary() + .SupplementaryOutputs.ABIDescriptorOutputPath; return ModuleInterfaceLoader::buildSwiftModuleFromSwiftInterface( Instance.getSourceMgr(), Instance.getDiags(), Invocation.getSearchPathOptions(), Invocation.getLangOptions(), Invocation.getClangImporterOptions(), Invocation.getClangModuleCachePath(), PrebuiltCachePath, FEOpts.BackupModuleInterfaceDir, - Invocation.getModuleName(), InputPath, Invocation.getOutputFilename(), + Invocation.getModuleName(), InputPath, Invocation.getOutputFilename(), ABIPath, FEOpts.SerializeModuleInterfaceDependencyHashes, FEOpts.shouldTrackSystemDependencies(), LoaderOpts, RequireOSSAModules_t(Invocation.getSILOptions())); diff --git a/test/ModuleInterface/emit-abi-descriptor.swift b/test/ModuleInterface/emit-abi-descriptor.swift new file mode 100644 index 0000000000000..530d5e5b110ec --- /dev/null +++ b/test/ModuleInterface/emit-abi-descriptor.swift @@ -0,0 +1,13 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/Foo.swiftmodule) +// RUN: %empty-directory(%t/ResourceDir/%target-sdk-name/prebuilt-modules/Foo.swiftmodule) +// RUN: echo "public func foo() {}" > %t/Foo.swift + +// RUN: %target-swift-frontend -emit-module %t/Foo.swift -module-name Foo -emit-module-interface-path %t/Foo.swiftinterface +// RUN: %target-swift-frontend -compile-module-from-interface %t/Foo.swiftinterface -o %t/Foo.swiftmodule -module-name Foo -emit-abi-descriptor-path %t/Foo.json + +// RUN: %FileCheck %s < %t/Foo.json + +// CHECK: "kind": "Root" +// CHECK-NEXT: "name": "TopLevel" +// CHECK-NEXT: "printedName": "TopLevel"