diff --git a/include/swift/AST/SILOptions.h b/include/swift/AST/SILOptions.h index e863cb42ebf22..3f22fb38f86ee 100644 --- a/include/swift/AST/SILOptions.h +++ b/include/swift/AST/SILOptions.h @@ -166,6 +166,9 @@ class SILOptions { /// Enable large loadable types IRGen pass. bool EnableLargeLoadableTypes = true; + /// The path to combined module summary file + std::string ModuleSummaryPath; + /// The name of the file to which the backend should save optimization /// records. std::string OptRecordFile; diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index 66fdcc94afefd..6cad8307f95b1 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -70,6 +70,8 @@ class FrontendOptions { /// The path to which we should store indexing data, if any. std::string IndexStorePath; + /// The path to which we should emit combined module summary file + std::string ModuleSummaryOutputPath; /// The path to look in when loading a module interface file, to see if a /// binary module has already been built for use by the compiler. std::string PrebuiltModuleCachePath; diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index d0bb1e0cb990f..80d52d685c664 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -445,6 +445,9 @@ def emit_module_summary_path : Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild, ArgumentIsPath, SupplementaryOutput]>, MetaVarName<"">, HelpText<"Output module summary file to ">; +def module_summary_path : Separate<["-"], "module-summary-path">, + Flags<[FrontendOption, ArgumentIsPath]>, MetaVarName<"">, + HelpText<"Combined module summary file ">; def emit_module_interface : Flag<["-"], "emit-module-interface">, diff --git a/include/swift/SIL/ModuleSummary.h b/include/swift/SIL/ModuleSummary.h new file mode 100644 index 0000000000000..65aff60d1b99d --- /dev/null +++ b/include/swift/SIL/ModuleSummary.h @@ -0,0 +1,256 @@ +//===----- ModuleSummary.h --------------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SIL_MODULE_SUMMARY_H +#define SWIFT_SIL_MODULE_SUMMARY_H + +#include "swift/AST/Decl.h" +#include "swift/SIL/SILFunction.h" +#include "llvm/Support/YAMLTraits.h" + +namespace swift { + +namespace modulesummary { + +using GUID = uint64_t; + +/// Compute globally unique identifier from the symbol. +GUID getGUIDFromUniqueName(llvm::StringRef Name); + +/// Function summary information to help callee analysis +class FunctionSummary { +public: + /// Function call information + class Call { + public: + /// Kinds of callee reference. + enum KindTy { + /// The call references a function statically + Direct, + /// The call references a function via a witness table. + Witness, + /// The call references a function via a vtable. + VTable, + kindCount, + }; + + private: + // For import/export + friend llvm::yaml::MappingTraits; + + /// The callee function GUID. This can be a static function or a virtual + /// function GUID. + GUID Callee; + /// The symbol name of the callee function only for debug and test purposes. + std::string Name; + /// Kind of the callee reference. + KindTy Kind; + public: + friend ::llvm::yaml::MappingTraits; + Call() = default; + Call(GUID callee, std::string name, KindTy kind) + : Callee(callee), Name(name), Kind(kind) {} + + KindTy getKind() const { return Kind; } + GUID getCallee() const { return Callee; } + std::string getName() const { return Name; }; + }; + + /// Function state flags + struct FlagsTy { + /// In per-module summary, always false. + /// In combined summary, indicates that the function is live. + bool Live; + /// Indicates that the function must be considered a live root for liveness + /// analysis. + bool Preserved; + }; + + using CallGraphEdgeListTy = std::vector; + +private: + // For import/export + friend llvm::yaml::MappingTraits; + + /// The function identity. + GUID Guid; + /// The function state flags. + FlagsTy Flags; + /// List of Call from this function. + CallGraphEdgeListTy CallGraphEdgeList; + /// The symbol name of the function only for debug and test purposes. + std::string Name; + +public: + FunctionSummary() = default; + FunctionSummary(GUID guid) + : Guid(guid), Flags({false, false}), CallGraphEdgeList(), Name("") {} + + /// Add a call to the list. + void addCall(Call call) { CallGraphEdgeList.push_back(call); } + + /// Return the list of Call from this function + ArrayRef calls() const { return CallGraphEdgeList; } + + bool isLive() const { return Flags.Live; } + void setLive(bool Live) { Flags.Live = Live; } + + bool isPreserved() const { return Flags.Preserved; } + void setPreserved(bool Preserved) { Flags.Preserved = Preserved; } + + std::string getName() const { return Name; } + void setName(std::string name) { this->Name = name; } + + GUID getGUID() const { return Guid; } +}; + +/// A slot in a set of virtual tables. +struct VFuncSlot { + /// Kinds of table. + enum KindTy { + Witness, + VTable, + kindCount, + }; + + /// Kind of table. + KindTy Kind; + /// The virtual function GUID. + GUID VFuncID; + + VFuncSlot(KindTy Kind, GUID VFuncID) : Kind(Kind), VFuncID(VFuncID) {} +}; + +struct VFuncImpl { + GUID Guid; + GUID TypeGuid; +}; + +using FunctionSummaryMapTy = std::map>; +using VFuncToImplsMapTy = std::map>; + +/// Module summary that consists of function summaries and virtual function +/// tables. +class ModuleSummaryIndex { + // For import/export + friend llvm::yaml::MappingTraits; + + /// Map from function GUID to function summary. + FunctionSummaryMapTy FunctionSummaryMap; + /// Map from virtual function GUID to list of implementations for witness + /// tables. + VFuncToImplsMapTy WitnessTableMethodMap; + /// Map from virtual function GUID to list of implementations for vtables. + VFuncToImplsMapTy VTableMethodMap; + /// The symbol name of the module. + std::string Name; + + VFuncToImplsMapTy &getVFuncMap(VFuncSlot::KindTy kind) { + switch (kind) { + case VFuncSlot::Witness: + return WitnessTableMethodMap; + case VFuncSlot::VTable: + return VTableMethodMap; + case VFuncSlot::kindCount: { + llvm_unreachable("impossible"); + } + } + } + const VFuncToImplsMapTy &getVFuncMap(VFuncSlot::KindTy kind) const { + switch (kind) { + case VFuncSlot::Witness: + return WitnessTableMethodMap; + case VFuncSlot::VTable: + return VTableMethodMap; + case VFuncSlot::kindCount: { + llvm_unreachable("impossible"); + } + } + } +public: + friend ::llvm::yaml::MappingTraits; + ModuleSummaryIndex() = default; + + std::string getName() const { return this->Name; } + void setName(std::string name) { this->Name = name; } + + /// Add a global value summary. + void addFunctionSummary(std::unique_ptr summary) { + FunctionSummaryMap.insert( + std::make_pair(summary->getGUID(), std::move(summary))); + } + + /// Return a FunctionSummary for GUID if it exists, otherwise return nullptr. + FunctionSummary *getFunctionSummary(GUID guid) const { + auto found = FunctionSummaryMap.find(guid); + if (found == FunctionSummaryMap.end()) { + return nullptr; + } + auto &entry = found->second; + return entry.get(); + } + + /// Record a implementation for the virtual function slot. + void addImplementation(VFuncSlot slot, GUID implGUID, GUID typeGUID) { + VFuncToImplsMapTy &table = getVFuncMap(slot.Kind); + auto found = table.find(slot.VFuncID); + VFuncImpl impl = {implGUID, typeGUID}; + if (found == table.end()) { + table.insert(std::make_pair(slot.VFuncID, std::vector{impl})); + return; + } + found->second.push_back(impl); + } + + /// Return a list of implementations for the virtual function slot. + ArrayRef getImplementations(VFuncSlot slot) const { + const VFuncToImplsMapTy &table = getVFuncMap(slot.Kind); + auto found = table.find(slot.VFuncID); + if (found == table.end()) { + return ArrayRef(); + } + return ArrayRef(found->second); + } + + const VFuncToImplsMapTy &getWitnessTableMethodMap() const { + return WitnessTableMethodMap; + } + const VFuncToImplsMapTy &getVTableMethodMap() const { + return VTableMethodMap; + } + + FunctionSummaryMapTy::const_iterator functions_begin() const { + return FunctionSummaryMap.begin(); + } + FunctionSummaryMapTy::const_iterator functions_end() const { + return FunctionSummaryMap.end(); + } +}; + +/// Compute a \c ModuleSummaryIndex from the given SILModule. +std::unique_ptr buildModuleSummaryIndex(SILModule &M); + +/// Serializes a module summary to the given output file. +/// +/// \returns false on success, true on error. +bool writeModuleSummaryIndex(const ModuleSummaryIndex &index, + DiagnosticEngine &diags, StringRef path); + +/// Attempt to deserialize the module summary. +/// +/// \returns false on success, true on error. +bool loadModuleSummaryIndex(llvm::MemoryBufferRef inputBuffer, + ModuleSummaryIndex &moduleSummary); +} // namespace modulesummary +} // namespace swift + +#endif diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index a408c5f949b73..d3518e1e90b96 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -2957,7 +2957,10 @@ class KeyPathPatternComponent { void visitReferencedFunctionsAndMethods( std::function functionCallBack, std::function methodCallBack) const; - + + void clearReferencedFunctions_if( + llvm::function_ref predicate); + void incrementRefCounts() const; void decrementRefCounts() const; diff --git a/include/swift/SIL/SILProperty.h b/include/swift/SIL/SILProperty.h index d1bddb6010da1..b4cb16f762840 100644 --- a/include/swift/SIL/SILProperty.h +++ b/include/swift/SIL/SILProperty.h @@ -67,7 +67,14 @@ class SILProperty : public llvm::ilist_node, const Optional &getComponent() const { return Component; } - + + void clearReferencedFunctions_if( + llvm::function_ref predicate) { + if (Component) { + Component->clearReferencedFunctions_if(predicate); + } + } + void print(SILPrintContext &Ctx) const; void dump() const; diff --git a/include/swift/SILOptimizer/PassManager/PassPipeline.def b/include/swift/SILOptimizer/PassManager/PassPipeline.def index ebc3eed48cc80..88dc64381d95c 100644 --- a/include/swift/SILOptimizer/PassManager/PassPipeline.def +++ b/include/swift/SILOptimizer/PassManager/PassPipeline.def @@ -24,6 +24,7 @@ PASSPIPELINE(Diagnostic, "Guaranteed Passes") PASSPIPELINE(OwnershipEliminator, "Utility pass to just run the ownership eliminator pass") +PASSPIPELINE(CrossModuleEliminator, "Passes run at -module-summary-path") PASSPIPELINE(Performance, "Passes run at -O") PASSPIPELINE(Onone, "Passes run at -Onone") PASSPIPELINE(InstCount, "Utility pipeline to just run the inst count pass") diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index 24f10ad54d4ec..e3b3051a126d1 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -66,6 +66,8 @@ PASS(AccessEnforcementWMO, "access-enforcement-wmo", "Access Enforcement Whole Module Optimization") PASS(CrossModuleSerializationSetup, "cross-module-serialization-setup", "Setup serialization flags for cross-module optimization") +PASS(CrossDeadFunctionElimination, "sil-cross-deadfuncelim", + "Cross Dead Function Elimination") PASS(AccessSummaryDumper, "access-summary-dump", "Dump Address Parameter Access Summary") PASS(AccessedStorageAnalysisDumper, "accessed-storage-analysis-dump", diff --git a/include/swift/SILOptimizer/PassManager/Passes.h b/include/swift/SILOptimizer/PassManager/Passes.h index ec587e24e086b..602944412e447 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.h +++ b/include/swift/SILOptimizer/PassManager/Passes.h @@ -42,6 +42,8 @@ namespace swift { /// Run the SIL ownership eliminator pass on \p M. bool runSILOwnershipEliminatorPass(SILModule &M); + bool runSILCrossModuleEliminatorPass(SILModule &M); + void runSILOptimizationPassesWithFileSpecification(SILModule &Module, StringRef FileName); diff --git a/include/swift/Serialization/ModuleSummary.h b/include/swift/Serialization/ModuleSummary.h new file mode 100644 index 0000000000000..ecffdb42aeb6e --- /dev/null +++ b/include/swift/Serialization/ModuleSummary.h @@ -0,0 +1,287 @@ +//===----- ModuleSummary.h --------------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SIL_MODULE_SUMMARY_H +#define SWIFT_SIL_MODULE_SUMMARY_H + +#include "swift/AST/Decl.h" +#include "swift/SIL/SILFunction.h" +#include "llvm/Support/YAMLTraits.h" + +namespace swift { + +namespace modulesummary { + +using GUID = uint64_t; + +/// Compute globally unique identifier from the symbol. +GUID getGUIDFromUniqueName(llvm::StringRef Name); + +/// Function summary information to help callee analysis +class FunctionSummary { +public: + /// Function call information + class Call { + public: + /// Kinds of callee reference. + enum KindTy { + /// The call references a function statically + Direct, + /// The call references a function via a witness table. + Witness, + /// The call references a function via a vtable. + VTable, + kindCount, + }; + + private: + // For import/export + friend llvm::yaml::MappingTraits; + + /// The callee function GUID. This can be a static function or a virtual + /// function GUID. + GUID Callee; + /// The symbol name of the callee function only for debug and test purposes. + std::string Name; + /// Kind of the callee reference. + KindTy Kind; + public: + friend ::llvm::yaml::MappingTraits; + Call() = default; + Call(GUID callee, std::string name, KindTy kind) + : Callee(callee), Name(name), Kind(kind) {} + + KindTy getKind() const { return Kind; } + GUID getCallee() const { return Callee; } + std::string getName() const { return Name; }; + }; + + struct TypeRef { + /// The type identity. + GUID Guid; + /// The symbol name of the type only for debug and test purposes. + std::string Name; + }; + + /// Function state flags + struct FlagsTy { + /// In per-module summary, always false. + /// In combined summary, indicates that the function is live. + bool Live; + /// Indicates that the function must be considered a live root for liveness + /// analysis. + bool Preserved; + }; + + using CallGraphEdgeListTy = std::vector; + using TypeRefListTy = std::vector; + +private: + // For import/export + friend llvm::yaml::MappingTraits; + + /// The function identity. + GUID Guid; + /// The function state flags. + FlagsTy Flags; + /// List of Call from this function. + CallGraphEdgeListTy CallGraphEdgeList; + /// List of TypeRef from this function. + TypeRefListTy TypeRefList; + /// The symbol name of the function only for debug and test purposes. + std::string Name; + /// Size of instructions only for debug purpose + uint32_t InstSize; + +public: + FunctionSummary() = default; + FunctionSummary(GUID guid) + : Guid(guid), Flags({false, false}), CallGraphEdgeList(), Name("") {} + + /// Add a call to the list. + void addCall(Call call) { CallGraphEdgeList.push_back(call); } + + /// Return the list of Call from this function + ArrayRef calls() const { return CallGraphEdgeList; } + + /// TBD + void addTypeRef(TypeRef ref) { TypeRefList.push_back(ref); } + /// TBD + ArrayRef typeRefs() const { return TypeRefList; } + + bool isLive() const { return Flags.Live; } + void setLive(bool Live) { Flags.Live = Live; } + + bool isPreserved() const { return Flags.Preserved; } + void setPreserved(bool Preserved) { Flags.Preserved = Preserved; } + + std::string getName() const { return Name; } + void setName(std::string name) { this->Name = name; } + + GUID getGUID() const { return Guid; } + + uint32_t getInstSize() const { return InstSize; } + void setInstSize(uint32_t size) { this->InstSize = size; } +}; + +/// A slot in a set of virtual tables. +struct VFuncSlot { + /// Kinds of table. + enum KindTy { + Witness, + VTable, + kindCount, + }; + + /// Kind of table. + KindTy Kind; + /// The virtual function GUID. + GUID VFuncID; + + VFuncSlot(KindTy Kind, GUID VFuncID) : Kind(Kind), VFuncID(VFuncID) {} +}; + +struct VFuncImpl { + GUID Guid; + GUID TypeGuid; +}; + +using FunctionSummaryMapTy = std::map>; +using VFuncToImplsMapTy = std::map>; +using UsedTypeListTy = std::vector; + +/// Module summary that consists of function summaries and virtual function +/// tables. +class ModuleSummaryIndex { + // For import/export + friend llvm::yaml::MappingTraits; + + /// Map from function GUID to function summary. + FunctionSummaryMapTy FunctionSummaryMap; + /// Map from virtual function GUID to list of implementations for witness + /// tables. + VFuncToImplsMapTy WitnessTableMethodMap; + /// Map from virtual function GUID to list of implementations for vtables. + VFuncToImplsMapTy VTableMethodMap; + /// In per-module summary, always empty map. + /// In combined summary, map from type GUID to liveness of the type. + UsedTypeListTy UsedTypeList; + /// The symbol name of the module. + std::string Name; + + VFuncToImplsMapTy &getVFuncMap(VFuncSlot::KindTy kind) { + switch (kind) { + case VFuncSlot::Witness: + return WitnessTableMethodMap; + case VFuncSlot::VTable: + return VTableMethodMap; + case VFuncSlot::kindCount: { + llvm_unreachable("impossible"); + } + } + } + const VFuncToImplsMapTy &getVFuncMap(VFuncSlot::KindTy kind) const { + switch (kind) { + case VFuncSlot::Witness: + return WitnessTableMethodMap; + case VFuncSlot::VTable: + return VTableMethodMap; + case VFuncSlot::kindCount: { + llvm_unreachable("impossible"); + } + } + } +public: + friend ::llvm::yaml::MappingTraits; + ModuleSummaryIndex() = default; + + std::string getName() const { return this->Name; } + void setName(std::string name) { this->Name = name; } + + /// Add a global value summary. + void addFunctionSummary(std::unique_ptr summary) { + FunctionSummaryMap.insert( + std::make_pair(summary->getGUID(), std::move(summary))); + } + + /// Return a FunctionSummary for GUID if it exists, otherwise return nullptr. + FunctionSummary *getFunctionSummary(GUID guid) const { + auto found = FunctionSummaryMap.find(guid); + if (found == FunctionSummaryMap.end()) { + return nullptr; + } + auto &entry = found->second; + return entry.get(); + } + + /// Record a implementation for the virtual function slot. + void addImplementation(VFuncSlot slot, GUID implGUID, GUID typeGUID) { + VFuncToImplsMapTy &table = getVFuncMap(slot.Kind); + auto found = table.find(slot.VFuncID); + VFuncImpl impl = {implGUID, typeGUID}; + if (found == table.end()) { + table.insert(std::make_pair(slot.VFuncID, std::vector{impl})); + return; + } + found->second.push_back(impl); + } + + /// Return a list of implementations for the virtual function slot. + ArrayRef getImplementations(VFuncSlot slot) const { + const VFuncToImplsMapTy &table = getVFuncMap(slot.Kind); + auto found = table.find(slot.VFuncID); + if (found == table.end()) { + return ArrayRef(); + } + return ArrayRef(found->second); + } + + void markUsedType(GUID typeGUID) { UsedTypeList.push_back(typeGUID); } + + const VFuncToImplsMapTy &getWitnessTableMethodMap() const { + return WitnessTableMethodMap; + } + const VFuncToImplsMapTy &getVTableMethodMap() const { + return VTableMethodMap; + } + + ArrayRef getUsedTypeList() const { + return ArrayRef(UsedTypeList); + } + + FunctionSummaryMapTy::const_iterator functions_begin() const { + return FunctionSummaryMap.begin(); + } + FunctionSummaryMapTy::const_iterator functions_end() const { + return FunctionSummaryMap.end(); + } + size_t functions_size() const { return FunctionSummaryMap.size(); } +}; + +/// Compute a \c ModuleSummaryIndex from the given SILModule. +std::unique_ptr buildModuleSummaryIndex(SILModule &M); + +/// Serializes a module summary to the given output file. +/// +/// \returns false on success, true on error. +bool writeModuleSummaryIndex(const ModuleSummaryIndex &index, + DiagnosticEngine &diags, StringRef path); + +/// Attempt to deserialize the module summary. +/// +/// \returns false on success, true on error. +bool loadModuleSummaryIndex(llvm::MemoryBufferRef inputBuffer, + ModuleSummaryIndex &moduleSummary); +} // namespace modulesummary +} // namespace swift + +#endif diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 25dfa735b3c2e..92f629c7c287e 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -2028,6 +2028,7 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, } case file_types::TY_AutolinkFile: case file_types::TY_Object: + case file_types::TY_LLVM_BC: case file_types::TY_TBD: // Object inputs are only okay if linking. if (OI.shouldLink()) { @@ -2041,7 +2042,6 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, case file_types::TY_Dependencies: case file_types::TY_Assembly: case file_types::TY_LLVM_IR: - case file_types::TY_LLVM_BC: case file_types::TY_SerializedDiagnostics: case file_types::TY_ObjCHeader: case file_types::TY_ClangModuleFile: diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 564f94cab30dd..893b8517b1230 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -230,6 +230,7 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI, inputArgs.AddLastArg(arguments, options::OPT_import_underlying_module); inputArgs.AddLastArg(arguments, options::OPT_module_cache_path); inputArgs.AddLastArg(arguments, options::OPT_module_link_name); + inputArgs.AddLastArg(arguments, options::OPT_module_summary_path); inputArgs.AddLastArg(arguments, options::OPT_nostdimport); inputArgs.AddLastArg(arguments, options::OPT_parse_stdlib); inputArgs.AddLastArg(arguments, options::OPT_resource_dir); diff --git a/lib/Driver/WebAssemblyToolChains.cpp b/lib/Driver/WebAssemblyToolChains.cpp index 55f1c329a5f39..7aa8d8b1e2524 100644 --- a/lib/Driver/WebAssemblyToolChains.cpp +++ b/lib/Driver/WebAssemblyToolChains.cpp @@ -124,6 +124,7 @@ toolchains::WebAssembly::constructInvocation(const DynamicLinkJobAction &job, addPrimaryInputsOfType(Arguments, context.Inputs, context.Args, file_types::TY_Object); addInputsOfType(Arguments, context.InputActions, file_types::TY_Object); + addInputsOfType(Arguments, context.InputActions, file_types::TY_LLVM_BC); if (!context.OI.SDKPath.empty()) { Arguments.push_back("--sysroot"); diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index e96093e8fdad8..fc59232998730 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -57,6 +57,9 @@ bool ArgsToFrontendOptionsConverter::convert( if (const Arg *A = Args.getLastArg(OPT_group_info_path)) { Opts.GroupInfoPath = A->getValue(); } + if (const Arg *A = Args.getLastArg(OPT_emit_module_summary_path)) { + Opts.ModuleSummaryOutputPath = A->getValue(); + } if (const Arg *A = Args.getLastArg(OPT_index_store_path)) { Opts.IndexStorePath = A->getValue(); } diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 1018ea1189c70..3d9b88affe940 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1187,6 +1187,10 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args, if (const Arg *A = Args.getLastArg(OPT_save_optimization_record_path)) Opts.OptRecordFile = A->getValue(); + if (const Arg *A = Args.getLastArg(OPT_module_summary_path)) { + Opts.ModuleSummaryPath = A->getValue(); + } + if (Args.hasArg(OPT_debug_on_sil)) { // Derive the name of the SIL file for debugging from // the regular outputfile. diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 2fd6c9b3ee33b..0790acae500a5 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -1140,6 +1140,11 @@ static void countStatsPostSILOpt(UnifiedStatsReporter &Stats, } bool CompilerInstance::performSILProcessing(SILModule *silModule) { + + if (!silModule->getOptions().ModuleSummaryPath.empty()) { + return runSILCrossModuleEliminatorPass(*silModule); + } + if (performMandatorySILPasses(Invocation, silModule)) return true; diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 758038f394295..79935c70474ec 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -67,6 +67,8 @@ #include "swift/Syntax/Serialization/SyntaxSerialization.h" #include "swift/Syntax/SyntaxNodes.h" #include "swift/TBDGen/TBDGen.h" +#include "swift/SIL/ModuleSummary.h" +#include "swift/Serialization/ModuleSummary.h" #include "clang/AST/ASTContext.h" #include "clang/Basic/Module.h" @@ -2072,11 +2074,9 @@ static bool serializeModuleSummary(SILModule *SM, const PrimarySpecificPaths &PSPs, const ASTContext &Context) { auto summaryOutputPath = PSPs.SupplementaryOutputs.ModuleSummaryOutputPath; - return withOutputFile(Context.Diags, summaryOutputPath, - [&](llvm::raw_ostream &out) { - out << "Some stuff"; - return false; - }); + auto Summary = modulesummary::buildModuleSummaryIndex(*SM); + return modulesummary::writeModuleSummaryIndex(*Summary.get(), Context.Diags, + summaryOutputPath); } static GeneratedModule diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 4335aa3707e72..ff2570eb9b80d 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -31,6 +31,7 @@ #include "swift/Demangling/ManglingMacros.h" #include "swift/IRGen/Linking.h" #include "swift/Runtime/HeapObject.h" +#include "swift/Serialization/SerializedModuleLoader.h" #include "swift/SIL/FormalLinkage.h" #include "swift/SIL/SILDebugScope.h" #include "swift/SIL/SILModule.h" @@ -442,6 +443,18 @@ class PrettySynthesizedFileUnitEmission : public llvm::PrettyStackTraceEntry { } }; +class PrettySerializedASTFileUnitEmission : public llvm::PrettyStackTraceEntry { + const SerializedASTFile &SAF; + +public: + explicit PrettySerializedASTFileUnitEmission(const SerializedASTFile &SAF) + : SAF(SAF) {} + + void print(raw_ostream &os) const override { + os << "While emitting IR for serialized ast file" << &SAF << "\n"; + } +}; + } // end anonymous namespace /// Emit all the top-level code in the source file. @@ -507,6 +520,64 @@ void IRGenModule::emitSourceFile(SourceFile &SF) { } } +void IRGenModule::emitSerializedASTFile(SerializedASTFile &SAF) { + PrettySerializedASTFileUnitEmission StackEntry(SAF); + SmallVector TopLevelDecls; + SmallVector LocalTypeDecls; + SmallVector OpaqueReturnTypeDecls; + SAF.getTopLevelDecls(TopLevelDecls); + SAF.getLocalTypeDecls(LocalTypeDecls); + SAF.getOpaqueReturnTypeDecls(OpaqueReturnTypeDecls); + // Emit types and other global decls. + for (auto *decl : TopLevelDecls) + emitGlobalDecl(decl); + for (auto *localDecl : LocalTypeDecls) + emitGlobalDecl(localDecl); + for (auto *opaqueDecl : OpaqueReturnTypeDecls) + maybeEmitOpaqueTypeDecl(opaqueDecl); + + SAF.collectLinkLibraries([this](LinkLibrary linkLib) { + this->addLinkLibrary(linkLib); + }); + + if (ObjCInterop) + this->addLinkLibrary(LinkLibrary("objc", LibraryKind::Library)); + + // FIXME: It'd be better to have the driver invocation or build system that + // executes the linker introduce these compatibility libraries, since at + // that point we know whether we're building an executable, which is the only + // place where the compatibility libraries take effect. For the benefit of + // build systems that build Swift code, but don't use Swift to drive + // the linker, we can also use autolinking to pull in the compatibility + // libraries. This may however cause the library to get pulled in in + // situations where it isn't useful, such as for dylibs, though this is + // harmless aside from code size. + if (!IRGen.Opts.UseJIT) { + if (auto compatibilityVersion + = IRGen.Opts.AutolinkRuntimeCompatibilityLibraryVersion) { + if (*compatibilityVersion <= llvm::VersionTuple(5, 0)) { + this->addLinkLibrary(LinkLibrary("swiftCompatibility50", + LibraryKind::Library, + /*forceLoad*/ true)); + } + if (*compatibilityVersion <= llvm::VersionTuple(5, 1)) { + this->addLinkLibrary(LinkLibrary("swiftCompatibility51", + LibraryKind::Library, + /*forceLoad*/ true)); + } + } + + if (auto compatibilityVersion = + IRGen.Opts.AutolinkRuntimeCompatibilityDynamicReplacementLibraryVersion) { + if (*compatibilityVersion <= llvm::VersionTuple(5, 0)) { + this->addLinkLibrary(LinkLibrary("swiftCompatibilityDynamicReplacements", + LibraryKind::Library, + /*forceLoad*/ true)); + } + } + } +} + /// Emit all the top-level code in the synthesized file unit. void IRGenModule::emitSynthesizedFileUnit(SynthesizedFileUnit &SFU) { PrettySynthesizedFileUnitEmission StackEntry(SFU); diff --git a/lib/IRGen/GenKeyPath.cpp b/lib/IRGen/GenKeyPath.cpp index 19af4890b3bc0..e1aca0304ab67 100644 --- a/lib/IRGen/GenKeyPath.cpp +++ b/lib/IRGen/GenKeyPath.cpp @@ -119,7 +119,11 @@ getAccessorForComputedComponent(IRGenModule &IGM, accessor = component.getSubscriptIndexHash(); break; } - + + if (!accessor) { + return nullptr; + } + // If the accessor is not generic, and locally available, we can use it as is. // If it's only externally available, we need a local thunk to relative- // reference. @@ -517,10 +521,18 @@ getWitnessTableForComputedComponent(IRGenModule &IGM, fields.addNullPointer(IGM.FunctionPtrTy); fields.addSignedPointer(copy, schemaKeyPath, PointerAuthEntity::Special::KeyPathCopy); - fields.addSignedPointer(equals, schemaKeyPath, - PointerAuthEntity::Special::KeyPathEquals); - fields.addSignedPointer(hash, schemaKeyPath, - PointerAuthEntity::Special::KeyPathHash); + if (equals) + fields.addSignedPointer(equals, schemaKeyPath, + PointerAuthEntity::Special::KeyPathEquals); + else + fields.addNullPointer(IGM.FunctionPtrTy); + + if (hash) + fields.addSignedPointer(hash, schemaKeyPath, + PointerAuthEntity::Special::KeyPathHash); + else + fields.addNullPointer(IGM.FunctionPtrTy); + return fields.finishAndCreateGlobal( "keypath_witnesses", IGM.getPointerAlignment(), /*constant*/ true, llvm::GlobalVariable::PrivateLinkage); @@ -923,6 +935,11 @@ emitKeyPathComponent(IRGenModule &IGM, switch (id.getKind()) { case KeyPathPatternComponent::ComputedPropertyId::Function: { idKind = KeyPathComponentHeader::Pointer; + if (!id.getFunction()) { + idValue = nullptr; + idResolution = KeyPathComponentHeader::Resolved; + break; + } // FIXME: Does this need to be signed? auto idRef = IGM.getAddrOfLLVMVariableOrGOTEquivalent( LinkEntity::forSILFunction(id.getFunction(), false)); @@ -1068,7 +1085,7 @@ emitKeyPathComponent(IRGenModule &IGM, switch (idKind) { case KeyPathComponentHeader::Pointer: // Use a relative offset to the referent. - fields.addRelativeAddress(idValue); + fields.addRelativeAddressOrNull(idValue); break; case KeyPathComponentHeader::VTableOffset: @@ -1079,15 +1096,12 @@ emitKeyPathComponent(IRGenModule &IGM, } // Push the accessors, possibly thunked to marshal generic environment. - fields.addRelativeAddress( - getAccessorForComputedComponent(IGM, component, Getter, - genericEnv, requirements, - hasSubscriptIndices)); + fields.addRelativeAddressOrNull(getAccessorForComputedComponent( + IGM, component, Getter, genericEnv, requirements, hasSubscriptIndices)); if (settable) - fields.addRelativeAddress( - getAccessorForComputedComponent(IGM, component, Setter, - genericEnv, requirements, - hasSubscriptIndices)); + fields.addRelativeAddressOrNull( + getAccessorForComputedComponent(IGM, component, Setter, genericEnv, + requirements, hasSubscriptIndices)); if (!isInstantiableOnce) { // If there's generic context or subscript indexes, embed as diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index bc31048f4575f..57633c26c8984 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -899,6 +899,8 @@ namespace { if (!entry.isValid() || entry.getKind() != SILWitnessTable::Method || entry.getMethodWitness().Requirement != func) continue; + if (!entry.getMethodWitness().Witness) + continue; return IGM.getAddrOfSILFunction(entry.getMethodWitness().Witness, NotForDefinition); } diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 28af6c259960b..b112e5b184805 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -1905,7 +1905,7 @@ namespace { } // Add the witness. - B.addRelativeAddress(witnesses.front()); + B.addRelativeAddressOrNull(witnesses.front()); witnesses = witnesses.drop_front(); } assert(witnesses.empty() && "Wrong # of resilient witnesses"); diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index 5d51bd932439a..a7ce119002138 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -36,6 +36,7 @@ #include "swift/IRGen/IRGenSILPasses.h" #include "swift/LLVMPasses/Passes.h" #include "swift/LLVMPasses/PassesFwd.h" +#include "swift/Serialization/SerializedModuleLoader.h" #include "swift/SIL/SILModule.h" #include "swift/SIL/SILRemarkStreamer.h" #include "swift/SILOptimizer/PassManager/PassManager.h" @@ -1019,6 +1020,8 @@ GeneratedModule IRGenRequest::evaluate(Evaluator &evaluator, for (auto *file : filesToEmit) { if (auto *nextSF = dyn_cast(file)) { IGM.emitSourceFile(*nextSF); + } else if (auto nextSAF = dyn_cast(file)) { + IGM.emitSerializedASTFile(*nextSAF); } else if (auto *nextSFU = dyn_cast(file)) { IGM.emitSynthesizedFileUnit(*nextSFU); } else { @@ -1260,6 +1263,9 @@ static void performParallelIRGeneration(IRGenDescriptor desc) { if (auto *SF = dyn_cast(File)) { CurrentIGMPtr IGM = irgen.getGenModule(SF); IGM->emitSourceFile(*SF); + } if (auto *SAF = dyn_cast(File)) { + CurrentIGMPtr IGM = irgen.getGenModule(SAF); + IGM->emitSerializedASTFile(*SAF); } else if (auto *nextSFU = dyn_cast(File)) { CurrentIGMPtr IGM = irgen.getGenModule(nextSFU); IGM->emitSynthesizedFileUnit(*nextSFU); diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index babb33814005c..e787450ab3355 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -110,6 +110,7 @@ namespace swift { class SILWitnessTable; class SourceLoc; class SourceFile; + class SerializedASTFile; class Type; enum class TypeReferenceKind : unsigned; @@ -1341,6 +1342,7 @@ private: \ llvm::LLVMContext &getLLVMContext() const { return *LLVMContext; } void emitSourceFile(SourceFile &SF); + void emitSerializedASTFile(SerializedASTFile &SF); void emitSynthesizedFileUnit(SynthesizedFileUnit &SFU); void addLinkLibrary(const LinkLibrary &linkLib); diff --git a/lib/SIL/IR/SILDefaultWitnessTable.cpp b/lib/SIL/IR/SILDefaultWitnessTable.cpp index 7c427f5ba6f89..69953d8e641c2 100644 --- a/lib/SIL/IR/SILDefaultWitnessTable.cpp +++ b/lib/SIL/IR/SILDefaultWitnessTable.cpp @@ -104,7 +104,7 @@ std::string SILDefaultWitnessTable::getUniqueName() const { SILDefaultWitnessTable::~SILDefaultWitnessTable() { // Drop the reference count of witness functions referenced by this table. for (auto entry : getEntries()) { - if (entry.isValid() && entry.getKind() == SILWitnessTable::Method) { + if (entry.isValid() && entry.getKind() == SILWitnessTable::Method && entry.getMethodWitness().Witness) { entry.getMethodWitness().Witness->decrementRefCount(); } } diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index aa38f2fa5f503..933b59fb30515 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -2562,6 +2562,57 @@ forEachRefcountableReference(const KeyPathPatternComponent &component, } } +void KeyPathPatternComponent::clearReferencedFunctions_if( + llvm::function_ref predicate) { + switch (getKind()) { + case KeyPathPatternComponent::Kind::StoredProperty: + case KeyPathPatternComponent::Kind::OptionalChain: + case KeyPathPatternComponent::Kind::OptionalWrap: + case KeyPathPatternComponent::Kind::OptionalForce: + case KeyPathPatternComponent::Kind::TupleElement: + return; + case KeyPathPatternComponent::Kind::SettableProperty: { + auto setter = getComputedPropertySetter(); + if (predicate(setter)) { + SetterAndIdKind.setPointer(nullptr); + } + LLVM_FALLTHROUGH; + } + case KeyPathPatternComponent::Kind::GettableProperty: + auto getter = getComputedPropertyGetter(); + if (predicate(getter)) { + ValueAndKind.setPointer(nullptr); + } + switch (getComputedPropertyId().getKind()) { + case KeyPathPatternComponent::ComputedPropertyId::DeclRef: + // Mark the vtable entry as used somehow? + break; + case KeyPathPatternComponent::ComputedPropertyId::Function: { + auto idFn = getComputedPropertyId().getFunction(); + if (predicate(idFn)) { + IdValue = ComputedPropertyId::ValueType(); + } + break; + } + case KeyPathPatternComponent::ComputedPropertyId::Property: + break; + } + + if (auto equals = getSubscriptIndexEquals()) { + if (predicate(equals)) { + IndexEquality.Equal = nullptr; + } + } + + if (auto hash = getSubscriptIndexHash()) { + if (predicate(hash)) { + IndexEquality.Hash = nullptr; + } + } + return; + } +} + void KeyPathPatternComponent::incrementRefCounts() const { forEachRefcountableReference(*this, [&](SILFunction *f) { f->incrementRefCount(); }); diff --git a/lib/SIL/IR/SILVTable.cpp b/lib/SIL/IR/SILVTable.cpp index 1bb556736c348..3c4c46465a41d 100644 --- a/lib/SIL/IR/SILVTable.cpp +++ b/lib/SIL/IR/SILVTable.cpp @@ -75,6 +75,8 @@ SILVTable::SILVTable(ClassDecl *c, IsSerialized_t serialized, SILVTable::~SILVTable() { // Drop the reference count of functions referenced by this table. for (const Entry &entry : getEntries()) { - entry.getImplementation()->decrementRefCount(); + if (entry.getImplementation()) { + entry.getImplementation()->decrementRefCount(); + } } } diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 97c4082a2a833..d48aeb7bc25f2 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -5659,7 +5659,8 @@ void SILDefaultWitnessTable::verify(const SILModule &M) const { continue; SILFunction *F = E.getMethodWitness().Witness; - + // Default witness can be null + if (!F) continue; #if 0 // FIXME: For now, all default witnesses are private. assert(F->hasValidLinkageForFragileRef() && diff --git a/lib/SILOptimizer/IPO/CMakeLists.txt b/lib/SILOptimizer/IPO/CMakeLists.txt index f9324eaafa21a..053a1c63bde75 100644 --- a/lib/SILOptimizer/IPO/CMakeLists.txt +++ b/lib/SILOptimizer/IPO/CMakeLists.txt @@ -2,6 +2,7 @@ target_sources(swiftSILOptimizer PRIVATE CapturePromotion.cpp CapturePropagation.cpp ClosureSpecializer.cpp + CrossDeadFunctionElimination.cpp CrossModuleSerializationSetup.cpp DeadFunctionElimination.cpp GlobalOpt.cpp diff --git a/lib/SILOptimizer/IPO/CrossDeadFunctionElimination.cpp b/lib/SILOptimizer/IPO/CrossDeadFunctionElimination.cpp new file mode 100644 index 0000000000000..24b1805f12610 --- /dev/null +++ b/lib/SILOptimizer/IPO/CrossDeadFunctionElimination.cpp @@ -0,0 +1,164 @@ +#define DEBUG_TYPE "sil-cross-dead-function-elimination" +#include "swift/AST/ASTMangler.h" +#include "swift/AST/DiagnosticsFrontend.h" +#include "swift/AST/ProtocolConformance.h" +#include "swift/SIL/InstructionUtils.h" +#include "swift/SIL/PatternMatch.h" +#include "swift/SIL/SILBuilder.h" +#include "swift/SIL/SILVisitor.h" +#include "swift/SILOptimizer/PassManager/Passes.h" +#include "swift/SILOptimizer/PassManager/Transforms.h" +#include "swift/Serialization/ModuleSummary.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +using namespace swift; + +STATISTIC(NumDeadFunc, "Number of dead functions eliminated"); + +//===----------------------------------------------------------------------===// +// Pass Definition and Entry Points +//===----------------------------------------------------------------------===// + +namespace { + +using namespace modulesummary; + +class SILCrossDeadFuncElimination : public SILModuleTransform { +private: + ModuleSummaryIndex TheSummary; + +public: + SILCrossDeadFuncElimination() {} + + void eliminateDeadTables(SILModule &M) { + auto &WitnessTables = M.getWitnessTableList(); + std::set UsedTypes; + for (auto type : TheSummary.getUsedTypeList()) { + UsedTypes.insert(type); + } + Mangle::ASTMangler mangler; + for (auto WI = WitnessTables.begin(), EI = WitnessTables.end(); WI != EI;) { + SILWitnessTable *WT = &*WI; + ++WI; + CanType type = WI->getConformingType(); + std::string mangled = mangler.mangleTypeWithoutPrefix(type); + GUID guid = getGUIDFromUniqueName(mangled); + if (UsedTypes.find(guid) != UsedTypes.end()) { + continue; + } + WT->clearMethods_if([&] (const SILWitnessTable::MethodWitness &MW) -> bool { + return true; + }); + } + } + + void eliminateDeadEntriesFromTables(SILModule &M) { + + for (auto VT : M.getVTables()) { + VT->removeEntries_if([&] (SILVTable::Entry &entry) -> bool { + auto Impl = entry.getImplementation(); + GUID guid = getGUIDFromUniqueName(Impl->getName()); + auto maybeSummary = TheSummary.getFunctionSummary(guid); + if (!maybeSummary) + return false; + return !maybeSummary->isLive(); + }); + } + + auto &WitnessTables = M.getWitnessTableList(); + for (auto WI = WitnessTables.begin(), EI = WitnessTables.end(); WI != EI;) { + SILWitnessTable *WT = &*WI; + ++WI; + WT->clearMethods_if([&] (const SILWitnessTable::MethodWitness &MW) -> bool { + auto Impl = MW.Witness; + GUID guid = getGUIDFromUniqueName(Impl->getName()); + auto maybeSummary = TheSummary.getFunctionSummary(guid); + if (!maybeSummary) + return false; + return !maybeSummary->isLive(); + }); + } + + auto DefaultWitnessTables = M.getDefaultWitnessTables(); + for (auto WI = DefaultWitnessTables.begin(), + EI = DefaultWitnessTables.end(); + WI != EI;) { + SILDefaultWitnessTable *WT = &*WI; + ++WI; + WT->clearMethods_if([&](SILFunction *MW) -> bool { + if (!MW) + return false; + GUID guid = getGUIDFromUniqueName(MW->getName()); + auto maybeSummary = TheSummary.getFunctionSummary(guid); + if (!maybeSummary) + return false; + return !maybeSummary->isLive(); + }); + } + } + + void eliminateDeadFunctions(SILModule &M, std::vector &DeadFunctions) { + for (SILFunction &F : M) { + auto guid = getGUIDFromUniqueName(F.getName()); + auto summary = TheSummary.getFunctionSummary(guid); + if (summary->isLive()) { + continue; + } + F.dropAllReferences(); + DeadFunctions.push_back(&F); + LLVM_DEBUG(llvm::dbgs() << "Eliminate " << F.getName() << "\n"); + } + } + + void cleanupSILProperty(SILModule &M, + const std::vector &DeadFunctions) { + std::set DeadFunctionsSet(DeadFunctions.begin(), + DeadFunctions.end()); + for (SILProperty &P : M.getPropertyList()) { + P.clearReferencedFunctions_if([&](SILFunction *f) { + return DeadFunctionsSet.find(f) != DeadFunctionsSet.end(); + }); + } + } + + void run() override { + LLVM_DEBUG(llvm::dbgs() << "Running CrossDeadFuncElimination\n"); + auto &Context = getModule()->getASTContext(); + auto ModuleSummaryPath = getOptions().ModuleSummaryPath; + auto ErrOrBuf = llvm::MemoryBuffer::getFile(ModuleSummaryPath); + if (!ErrOrBuf) { + Context.Diags.diagnose(SourceLoc(), diag::error_no_such_file_or_directory, + ModuleSummaryPath); + return; + } + + auto HasErr = modulesummary::loadModuleSummaryIndex( + ErrOrBuf.get()->getMemBufferRef(), TheSummary); + if (HasErr) { + llvm::report_fatal_error("Invalid module summary"); + } + + auto &M = *getModule(); + this->eliminateDeadEntriesFromTables(M); + std::vector DeadFunctions; + this->eliminateDeadFunctions(M, DeadFunctions); + this->cleanupSILProperty(M, DeadFunctions); + + while (!DeadFunctions.empty()) { + SILFunction *F = DeadFunctions.back(); + DeadFunctions.pop_back(); + notifyWillDeleteFunction(F); + M.eraseFunction(F); + } + this->invalidateFunctionTables(); + } +}; + +} // end anonymous namespace + +SILTransform *swift::createCrossDeadFunctionElimination() { + return new SILCrossDeadFuncElimination(); +} diff --git a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp index 4908107793fe4..60e19bce1684c 100644 --- a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp +++ b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp @@ -146,10 +146,14 @@ class FunctionLivenessComputation { } /// Marks a function as alive. - void makeAlive(SILFunction *F) { - AliveFunctionsAndTables.insert(F); - assert(F && "function does not exist"); - Worklist.insert(F); + void makeAlive(SILFunction *F, StringRef Context) { + if (F) { + AliveFunctionsAndTables.insert(F); + assert(F && "function does not exist"); + Worklist.insert(F); + } else { + llvm::dbgs() << "Found garbage in " << Context << "\n"; + } } /// Marks all contained functions and witness tables of a witness table as @@ -177,7 +181,7 @@ class FunctionLivenessComputation { if (F) { MethodInfo *MI = getMethodInfo(fd, /*isWitnessMethod*/ true); if (MI->methodIsCalled || !F->isDefinition()) - ensureAlive(F); + ensureAlive(F, "void makeAlive(SILWitnessTable *WT)"); } } break; @@ -210,7 +214,7 @@ class FunctionLivenessComputation { ensureKeyPathComponentIsAlive(const KeyPathPatternComponent &component) { component.visitReferencedFunctionsAndMethods( [this](SILFunction *F) { - ensureAlive(F); + ensureAlive(F, "ensureKeyPathComponentIsAlive"); }, [this](SILDeclRef method) { if (method.isForeign) { @@ -234,9 +238,9 @@ class FunctionLivenessComputation { } /// Marks a function as alive if it is not alive yet. - void ensureAlive(SILFunction *F) { + void ensureAlive(SILFunction *F, StringRef Context) { if (!isAlive(F)) - makeAlive(F); + makeAlive(F, Context); } /// Marks a witness table as alive if it is not alive yet. @@ -283,7 +287,7 @@ class FunctionLivenessComputation { if (!isAlive(FImpl.F) && canHaveSameImplementation(FD, MethodCl, FImpl.Impl.get())) { - makeAlive(FImpl.F); + makeAlive(FImpl.F, "ensureAliveClassMethod"); } else { allImplsAreCalled = false; } @@ -304,9 +308,9 @@ class FunctionLivenessComputation { Module->lookUpWitnessTable(Conf, /*deserializeLazily*/ false); if (!WT || isAlive(WT)) - makeAlive(FImpl.F); + makeAlive(FImpl.F, "ensureAliveProtocolMethod"); } else { - makeAlive(FImpl.F); + makeAlive(FImpl.F, "ensureAliveProtocolMethod"); } } } @@ -334,11 +338,11 @@ class FunctionLivenessComputation { MethodInfo *mi = getMethodInfo(funcDecl, /*isWitnessTable*/ false); ensureAliveClassMethod(mi, dyn_cast(funcDecl), MethodCl); } else if (auto *FRI = dyn_cast(&I)) { - ensureAlive(FRI->getInitiallyReferencedFunction()); + ensureAlive(FRI->getInitiallyReferencedFunction(), F->getName()); } else if (auto *FRI = dyn_cast(&I)) { - ensureAlive(FRI->getInitiallyReferencedFunction()); + ensureAlive(FRI->getInitiallyReferencedFunction(), F->getName()); } else if (auto *FRI = dyn_cast(&I)) { - ensureAlive(FRI->getInitiallyReferencedFunction()); + ensureAlive(FRI->getInitiallyReferencedFunction(), F->getName()); } else if (auto *KPI = dyn_cast(&I)) { for (auto &component : KPI->getPattern()->getComponents()) ensureKeyPathComponentIsAlive(component); @@ -393,18 +397,18 @@ class FunctionLivenessComputation { for (SILFunction &F : *Module) { if (isAnchorFunction(&F)) { LLVM_DEBUG(llvm::dbgs() << " anchor function: " << F.getName() <<"\n"); - ensureAlive(&F); + ensureAlive(&F, "findAnchors"); } // Make sure that functions referenced by _specialize(target: targetFun()) // are kept alive. F.forEachSpecializeAttrTargetFunction( - [this](SILFunction *targetFun) { ensureAlive(targetFun); }); + [this](SILFunction *targetFun) { ensureAlive(targetFun, "forEachSpecializeAttrTargetFunction"); }); if (!F.shouldOptimize()) { LLVM_DEBUG(llvm::dbgs() << " anchor a no optimization function: " << F.getName() << "\n"); - ensureAlive(&F); + ensureAlive(&F, "findAnchors.shouldOptimize"); } } } @@ -494,6 +498,8 @@ class DeadFunctionElimination : FunctionLivenessComputation { continue; SILFunction *F = entry. getMethodWitness().Witness; + if (!F) + continue; auto *fd = cast( entry.getMethodWitness().Requirement.getDecl()); MethodInfo *mi = getMethodInfo(fd, /*isWitnessTable*/ true); @@ -515,7 +521,7 @@ class DeadFunctionElimination : FunctionLivenessComputation { if (entry.getMethod().kind == SILDeclRef::Kind::Deallocator || entry.getMethod().kind == SILDeclRef::Kind::IVarDestroyer) { // Destructors are alive because they are called from swift_release - ensureAlive(entry.getImplementation()); + ensureAlive(entry.getImplementation(), "findAnchorsInTables.vtable"); continue; } @@ -599,11 +605,11 @@ class DeadFunctionElimination : FunctionLivenessComputation { } // Check differentiability witness entries. for (auto &dw : Module->getDifferentiabilityWitnessList()) { - ensureAlive(dw.getOriginalFunction()); + ensureAlive(dw.getOriginalFunction(), "findAnchorsInTables.differentiability"); if (dw.getJVP()) - ensureAlive(dw.getJVP()); + ensureAlive(dw.getJVP(), "findAnchorsInTables.differentiability"); if (dw.getVJP()) - ensureAlive(dw.getVJP()); + ensureAlive(dw.getVJP(), "findAnchorsInTables.differentiability"); } } diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index b70a272f02a73..69a28f093713b 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -421,6 +421,14 @@ static void addPerfDebugSerializationPipeline(SILPassPipelinePlan &P) { P.addPerformanceSILLinker(); } +static void addCrossModuleOptimizationsPipeline(SILPassPipelinePlan &P, + const SILOptions &Options) { + P.startPipeline("Cross Module Optimization Passes"); + if (!Options.ModuleSummaryPath.empty()) { + P.addCrossDeadFunctionElimination(); + } + P.addMandatorySILLinker(); +} static void addPrepareOptimizationsPipeline(SILPassPipelinePlan &P) { P.startPipeline("PrepareOptimizationPasses"); @@ -723,6 +731,13 @@ SILPassPipelinePlan::getIRGenPreparePassPipeline(const SILOptions &Options) { return P; } +SILPassPipelinePlan +SILPassPipelinePlan::getCrossModuleEliminatorPassPipeline(const SILOptions &Options) { + SILPassPipelinePlan P(Options); + addCrossModuleOptimizationsPipeline(P, Options); + return P; +} + SILPassPipelinePlan SILPassPipelinePlan::getPerformancePassPipeline(const SILOptions &Options) { SILPassPipelinePlan P(Options); @@ -731,7 +746,7 @@ SILPassPipelinePlan::getPerformancePassPipeline(const SILOptions &Options) { addPerfDebugSerializationPipeline(P); return P; } - + // Passes which run once before all other optimizations run. Those passes are // _not_ intended to run later again. addPrepareOptimizationsPipeline(P); diff --git a/lib/SILOptimizer/PassManager/Passes.cpp b/lib/SILOptimizer/PassManager/Passes.cpp index 1d57b543bda66..be76b9a8ca50f 100644 --- a/lib/SILOptimizer/PassManager/Passes.cpp +++ b/lib/SILOptimizer/PassManager/Passes.cpp @@ -84,6 +84,16 @@ bool swift::runSILOwnershipEliminatorPass(SILModule &Module) { return Ctx.hadError(); } +bool swift::runSILCrossModuleEliminatorPass(SILModule &Module) { + auto &Ctx = Module.getASTContext(); + + auto &opts = Module.getOptions(); + executePassPipelinePlan( + &Module, SILPassPipelinePlan::getCrossModuleEliminatorPassPipeline(opts)); + + return Ctx.hadError(); +} + void swift::runSILOptimizationPasses(SILModule &Module) { auto &opts = Module.getOptions(); diff --git a/lib/Serialization/CMakeLists.txt b/lib/Serialization/CMakeLists.txt index c10fa09a159f4..9d46ba1f887a9 100644 --- a/lib/Serialization/CMakeLists.txt +++ b/lib/Serialization/CMakeLists.txt @@ -5,6 +5,8 @@ add_swift_host_library(swiftSerialization STATIC ModuleDependencyScanner.cpp ModuleFile.cpp ModuleFileSharedCore.cpp + ModuleSummaryFormat.cpp + ModuleSummaryIndexer.cpp Serialization.cpp SerializedModuleLoader.cpp SerializedSILLoader.cpp diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index dc741236f7cba..fed6d05a53130 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -672,7 +672,7 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, // Mark this function as deserialized. This avoids rerunning diagnostic // passes. Certain passes in the madatory pipeline may not work as expected // after arbitrary optimization and lowering. - if (!MF->isSIB()) +// if (!MF->isSIB()) fn->setWasDeserializedCanonical(); fn->setBare(IsBare); diff --git a/lib/Serialization/ModuleSummaryFormat.cpp b/lib/Serialization/ModuleSummaryFormat.cpp new file mode 100644 index 0000000000000..d4369d7808854 --- /dev/null +++ b/lib/Serialization/ModuleSummaryFormat.cpp @@ -0,0 +1,469 @@ +//===--- ModuleSummaryFormat.cpp - Read and write module summary files ----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "ModuleSummaryFormat.h" +#include "BCReadingExtras.h" +#include "memory" +#include "swift/AST/FileSystem.h" +#include "swift/Serialization/ModuleSummary.h" +#include "llvm/Bitstream/BitstreamReader.h" +#include "llvm/Bitstream/BitstreamWriter.h" +#include "llvm/Support/CommandLine.h" + +using namespace swift; +using namespace modulesummary; +using namespace llvm; + +static cl::opt ModuleSummaryEmbedDebugName( + "module-summary-embed-debug-name", cl::init(false), + cl::desc("Embed function names for debugging purpose")); + +namespace { + +class Serializer { + SmallVector Buffer; + BitstreamWriter Out{Buffer}; + + /// A reusable buffer for emitting records. + SmallVector ScratchRecord; + + std::array AbbrCodes; + + template void registerRecordAbbr() { + using AbbrArrayTy = decltype(AbbrCodes); + static_assert(Layout::Code <= std::tuple_size::value, + "layout has invalid record code"); + AbbrCodes[Layout::Code] = Layout::emitAbbrev(Out); + } + + void writeSignature(); + void writeBlockInfoBlock(); + void emitBlockID(unsigned ID, StringRef name, + SmallVectorImpl &nameBuffer); + + void emitRecordID(unsigned ID, StringRef name, + SmallVectorImpl &nameBuffer); + void emitVFuncTable(const VFuncToImplsMapTy T, VFuncSlot::KindTy kind); + void emitUsedTypeList(const ArrayRef L); + +public: + void emitHeader(); + void emitModuleSummary(const ModuleSummaryIndex &index); + void emitFunctionSummary(const FunctionSummary *summary); + void write(raw_ostream &os); +}; + +void Serializer::emitBlockID(unsigned ID, StringRef name, + SmallVectorImpl &nameBuffer) { + SmallVector idBuffer; + idBuffer.push_back(ID); + Out.EmitRecord(bitc::BLOCKINFO_CODE_SETBID, idBuffer); + + // Emit the block name if present. + if (name.empty()) + return; + nameBuffer.resize(name.size()); + memcpy(nameBuffer.data(), name.data(), name.size()); + Out.EmitRecord(bitc::BLOCKINFO_CODE_BLOCKNAME, nameBuffer); +} + +void Serializer::emitRecordID(unsigned ID, StringRef name, + SmallVectorImpl &nameBuffer) { + assert(ID < 256 && "can't fit record ID in next to name"); + nameBuffer.resize(name.size() + 1); + nameBuffer[0] = ID; + memcpy(nameBuffer.data() + 1, name.data(), name.size()); + Out.EmitRecord(bitc::BLOCKINFO_CODE_SETRECORDNAME, nameBuffer); +} + +void Serializer::writeSignature() { + for (auto c : MODULE_SUMMARY_SIGNATURE) + Out.Emit((unsigned)c, 8); +} + +void Serializer::writeBlockInfoBlock() { + BCBlockRAII restoreBlock(Out, bitc::BLOCKINFO_BLOCK_ID, 2); + + SmallVector nameBuffer; +#define BLOCK(X) emitBlockID(X##_ID, #X, nameBuffer) +#define BLOCK_RECORD(K, X) emitRecordID(K::X, #X, nameBuffer) + + BLOCK(RECORD_BLOCK); + BLOCK_RECORD(record_block, MODULE_METADATA); + + BLOCK_RECORD(record_block, FUNC_METADATA); + BLOCK_RECORD(record_block, CALL_GRAPH_EDGE); + BLOCK_RECORD(record_block, TYPE_REF); + + BLOCK_RECORD(record_block, VFUNC_METADATA); + BLOCK_RECORD(record_block, VFUNC_IMPL); + + BLOCK_RECORD(record_block, USED_TYPE); +} + +void Serializer::emitHeader() { + writeSignature(); + writeBlockInfoBlock(); +} + +void Serializer::emitFunctionSummary(const FunctionSummary *summary) { + using namespace record_block; + std::string debugFuncName = + ModuleSummaryEmbedDebugName ? summary->getName() : ""; + FunctionMetadataLayout::emitRecord( + Out, ScratchRecord, AbbrCodes[FunctionMetadataLayout::Code], + summary->getGUID(), unsigned(summary->isLive()), + unsigned(summary->isPreserved()), summary->getInstSize(), debugFuncName); + + for (auto call : summary->calls()) { + std::string debugName = ModuleSummaryEmbedDebugName ? call.getName() : ""; + CallGraphEdgeLayout::emitRecord( + Out, ScratchRecord, AbbrCodes[CallGraphEdgeLayout::Code], + unsigned(call.getKind()), call.getCallee(), debugName); + } + + for (auto typeRef : summary->typeRefs()) { + std::string debugName = ModuleSummaryEmbedDebugName ? typeRef.Name : ""; + TypeRefLayout::emitRecord(Out, ScratchRecord, + AbbrCodes[TypeRefLayout::Code], typeRef.Guid, + debugName); + } +} + +void Serializer::emitVFuncTable(const VFuncToImplsMapTy T, + VFuncSlot::KindTy kind) { + for (auto &pair : T) { + GUID guid = pair.first; + std::vector impls = pair.second; + using namespace record_block; + + VFuncMetadataLayout::emitRecord(Out, ScratchRecord, + AbbrCodes[VFuncMetadataLayout::Code], + unsigned(kind), guid); + + for (auto impl : impls) { + VFuncImplLayout::emitRecord(Out, ScratchRecord, + AbbrCodes[VFuncImplLayout::Code], impl.Guid, impl.TypeGuid); + } + } +} + +void Serializer::emitUsedTypeList(const ArrayRef L) { + using namespace record_block; + for (GUID usedType : L) { + UsedTypeLayout::emitRecord(Out, ScratchRecord, + AbbrCodes[UsedTypeLayout::Code], usedType); + } +} + +void Serializer::emitModuleSummary(const ModuleSummaryIndex &index) { + using namespace record_block; + + BCBlockRAII restoreBlock(Out, RECORD_BLOCK_ID, 8); + + registerRecordAbbr(); + registerRecordAbbr(); + registerRecordAbbr(); + registerRecordAbbr(); + registerRecordAbbr(); + registerRecordAbbr(); + registerRecordAbbr(); + + ModuleMetadataLayout::emitRecord(Out, ScratchRecord, + AbbrCodes[ModuleMetadataLayout::Code], + index.getName()); + for (auto FI = index.functions_begin(), FE = index.functions_end(); FI != FE; + ++FI) { + emitFunctionSummary(FI->second.get()); + } + + emitVFuncTable(index.getWitnessTableMethodMap(), VFuncSlot::Witness); + emitVFuncTable(index.getVTableMethodMap(), VFuncSlot::VTable); + + emitUsedTypeList(index.getUsedTypeList()); +} + +void Serializer::write(raw_ostream &os) { + os.write(Buffer.data(), Buffer.size()); + os.flush(); +} + +class Deserializer { + BitstreamCursor Cursor; + SmallVector Scratch; + StringRef BlobData; + + ModuleSummaryIndex &moduleSummary; + + // These all return true if there was an error. + bool readSignature(); + bool enterTopLevelBlock(); + bool readModuleMetadata(); + +public: + Deserializer(MemoryBufferRef inputBuffer, ModuleSummaryIndex &moduleSummary) + : Cursor{inputBuffer}, moduleSummary(moduleSummary) {} + bool readModuleSummary(); +}; + +bool Deserializer::readSignature() { + for (unsigned char byte : MODULE_SUMMARY_SIGNATURE) { + if (Cursor.AtEndOfStream()) + return true; + if (auto maybeRead = Cursor.Read(8)) { + if (maybeRead.get() != byte) + return true; + } else { + return true; + } + } + return false; +} + +bool Deserializer::enterTopLevelBlock() { + // Read the BLOCKINFO_BLOCK, which contains metadata used when dumping + // the binary data with llvm-bcanalyzer. + { + auto next = Cursor.advance(); + if (!next) { + consumeError(next.takeError()); + return true; + } + + if (next->Kind != llvm::BitstreamEntry::SubBlock) + return true; + + if (next->ID != llvm::bitc::BLOCKINFO_BLOCK_ID) + return true; + + if (!Cursor.ReadBlockInfoBlock()) + return true; + } + + // Enters our subblock, which contains the actual summary information. + { + auto next = Cursor.advance(); + if (!next) { + consumeError(next.takeError()); + return true; + } + + if (next->Kind != llvm::BitstreamEntry::SubBlock) + return true; + + if (next->ID != RECORD_BLOCK_ID) + return true; + + if (auto err = Cursor.EnterSubBlock(RECORD_BLOCK_ID)) { + consumeError(std::move(err)); + return true; + } + } + return false; +} + +bool Deserializer::readModuleMetadata() { + Expected maybeEntry = Cursor.advance(); + if (!maybeEntry) + report_fatal_error("Should have next entry"); + + BitstreamEntry entry = maybeEntry.get(); + + if (entry.Kind != BitstreamEntry::Record) { + return true; + } + Scratch.clear(); + auto maybeKind = Cursor.readRecord(entry.ID, Scratch, &BlobData); + + if (!maybeKind) { + consumeError(maybeKind.takeError()); + return true; + } + + if (maybeKind.get() != record_block::MODULE_METADATA) { + return true; + } + + moduleSummary.setName(BlobData.str()); + + return false; +} + +static Optional getCallKind(unsigned kind) { + if (kind < unsigned(FunctionSummary::Call::KindTy::kindCount)) + return FunctionSummary::Call::KindTy(kind); + return None; +} + +static Optional getSlotKind(unsigned kind) { + if (kind < unsigned(FunctionSummary::Call::KindTy::kindCount)) + return VFuncSlot::KindTy(kind); + return None; +} + +bool Deserializer::readModuleSummary() { + using namespace record_block; + if (readSignature()) { + return true; + } + if (enterTopLevelBlock()) { + return true; + } + + if (readModuleMetadata()) { + return true; + } + + FunctionSummary *CurrentFunc; + Optional CurrentSlot; + + while (!Cursor.AtEndOfStream()) { + Scratch.clear(); + Expected entry = Cursor.advance(); + + if (!entry) { + // Success if there is no content + consumeError(entry.takeError()); + return false; + } + + if (entry->Kind == llvm::BitstreamEntry::EndBlock) { + Cursor.ReadBlockEnd(); + break; + } + + if (entry->Kind != llvm::BitstreamEntry::Record) { + llvm::report_fatal_error("Bad bitstream entry kind"); + } + + auto recordID = Cursor.readRecord(entry->ID, Scratch, &BlobData); + if (!recordID) { + consumeError(recordID.takeError()); + return true; + } + + switch (recordID.get()) { + case MODULE_METADATA: + // METADATA must appear at the beginning and is handled by + // readModuleSummaryMetadata(). + llvm::report_fatal_error("Unexpected MODULE_METADATA record"); + case FUNC_METADATA: { + GUID guid; + std::string name; + unsigned isLive, isPreserved; + bool shouldMerge = false; + uint32_t instSize; + + FunctionMetadataLayout::readRecord(Scratch, guid, isLive, isPreserved, + instSize); + name = BlobData.str(); + if (auto summary = moduleSummary.getFunctionSummary(guid)) { + CurrentFunc = summary; + shouldMerge = true; + } else { + auto NewFS = std::make_unique(guid); + CurrentFunc = NewFS.get(); + moduleSummary.addFunctionSummary(std::move(NewFS)); + } + // Overwrite iff flags are true for merging function summaries of same functions. + if (!shouldMerge || isLive) { + CurrentFunc->setLive(isLive); + } + if (!shouldMerge || isPreserved) { + CurrentFunc->setPreserved(isPreserved); + } + if (!shouldMerge || !name.empty()) { + CurrentFunc->setName(name); + } + CurrentFunc->setInstSize(instSize); + break; + } + case CALL_GRAPH_EDGE: { + // CALL_GRAPH_EDGE must follow a FUNC_METADATA. + if (!CurrentFunc) { + report_fatal_error("Unexpected CALL_GRAPH_EDGE record"); + } + unsigned callKindID; + GUID calleeGUID; + CallGraphEdgeLayout::readRecord(Scratch, callKindID, calleeGUID); + + auto callKind = getCallKind(callKindID); + if (!callKind) { + report_fatal_error("Bad call kind"); + } + FunctionSummary::Call call(calleeGUID, BlobData.str(), callKind.getValue()); + CurrentFunc->addCall(call); + break; + } + case TYPE_REF: { + // TYPE_REF must follow a FUNC_METADATA. + if (!CurrentFunc) { + report_fatal_error("Unexpected TYPE_REF record"); + } + GUID typeGUID; + std::string name; + TypeRefLayout::readRecord(Scratch, typeGUID); + name = BlobData.str(); + CurrentFunc->addTypeRef({typeGUID, name}); + break; + } + case VFUNC_METADATA: { + unsigned rawVFuncKind; + GUID vFuncGUID; + VFuncMetadataLayout::readRecord(Scratch, rawVFuncKind, vFuncGUID); + + auto Kind = getSlotKind(rawVFuncKind); + if (!Kind) { + report_fatal_error("Bad vfunc slot kind"); + } + CurrentSlot = VFuncSlot(Kind.getValue(), vFuncGUID); + break; + } + case VFUNC_IMPL: { + // VFUNC_IMPL must follow a VFUNC_METADATA. + if (!CurrentSlot) { + report_fatal_error("Unexpected METHOD_IMPL record"); + } + GUID implGUID, typeGUID; + VFuncImplLayout::readRecord(Scratch, implGUID, typeGUID); + moduleSummary.addImplementation(CurrentSlot.getValue(), implGUID, typeGUID); + break; + } + case USED_TYPE: { + GUID typeGUID; + UsedTypeLayout::readRecord(Scratch, typeGUID); + moduleSummary.markUsedType(typeGUID); + } + } + } + + return false; +} + +}; // namespace + +bool modulesummary::writeModuleSummaryIndex(const ModuleSummaryIndex &index, + DiagnosticEngine &diags, + StringRef path) { + return withOutputFile(diags, path, [&](raw_ostream &out) { + Serializer serializer; + serializer.emitHeader(); + serializer.emitModuleSummary(index); + serializer.write(out); + return false; + }); +} +bool modulesummary::loadModuleSummaryIndex(MemoryBufferRef inputBuffer, + ModuleSummaryIndex &moduleSummary) { + Deserializer deserializer(inputBuffer, moduleSummary); + return deserializer.readModuleSummary(); +} diff --git a/lib/Serialization/ModuleSummaryFormat.h b/lib/Serialization/ModuleSummaryFormat.h new file mode 100644 index 0000000000000..f3e8f6472a9ac --- /dev/null +++ b/lib/Serialization/ModuleSummaryFormat.h @@ -0,0 +1,84 @@ +//===--- ModuleSummaryFormat.h - Read and write module summary files ------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SERIALIZATION_MODULE_SUMMARY_FILE_H +#define SWIFT_SERIALIZATION_MODULE_SUMMARY_FILE_H + +#include "llvm/Bitcode/RecordLayout.h" +#include + +namespace swift { + +namespace modulesummary { + +using llvm::BCBlob; +using llvm::BCFixed; +using llvm::BCRecordLayout; +using llvm::BCVBR; + +const unsigned char MODULE_SUMMARY_SIGNATURE[] = {'M', 'O', 'D', 'S'}; +const unsigned RECORD_BLOCK_ID = llvm::bitc::FIRST_APPLICATION_BLOCKID; + +namespace record_block { +enum { + MODULE_METADATA, + FUNC_METADATA, + CALL_GRAPH_EDGE, + TYPE_REF, + VFUNC_METADATA, + VFUNC_IMPL, + USED_TYPE, +}; + +using BCGUID = llvm::BCVBR<16>; + +using ModuleMetadataLayout = BCRecordLayout; + +using FunctionMetadataLayout = BCRecordLayout, // live + BCFixed<1>, // preserved + BCVBR<16>, // instruction size + BCBlob // name (debug purpose) + >; +using CallGraphEdgeLayout = + BCRecordLayout, // call kind (direct, vtable or witness) + BCGUID, // callee func guid + BCBlob // name (debug purpose) + >; + +using TypeRefLayout = BCRecordLayout; + +using VFuncMetadataLayout = + BCRecordLayout, // vfunc kind (vtable or witness) + BCGUID // vfunc guid + >; + +using VFuncImplLayout = BCRecordLayout; +using UsedTypeLayout = BCRecordLayout; +} // namespace record_block +} // namespace modulesummary +} // namespace swift + +#endif diff --git a/lib/Serialization/ModuleSummaryIndexer.cpp b/lib/Serialization/ModuleSummaryIndexer.cpp new file mode 100644 index 0000000000000..6c8e353a84659 --- /dev/null +++ b/lib/Serialization/ModuleSummaryIndexer.cpp @@ -0,0 +1,483 @@ +#include "swift/AST/ASTMangler.h" +#include "swift/SIL/SILDeclRef.h" +#include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILModule.h" +#include "swift/SIL/SILVisitor.h" +#include "swift/Serialization/ModuleSummary.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/MD5.h" +#include "llvm/Support/raw_ostream.h" + +#define DEBUG_TYPE "module-summary-index" + +static llvm::cl::opt EagerElimination( + "module-summary-eager-elim", llvm::cl::init(false), + llvm::cl::desc( + "Enable eager elimination which doesn't support debug print")); + +using namespace swift; +using namespace modulesummary; + +GUID modulesummary::getGUIDFromUniqueName(llvm::StringRef Name) { + return llvm::MD5Hash(Name); +} + +static GUID getTypeGUID(NominalTypeDecl *type) { + Mangle::ASTMangler mangler; + std::string mangled = mangler.mangleNominalType(type); + return getGUIDFromUniqueName(mangled); +} + +namespace { +class FunctionSummaryIndexer : public SILInstructionVisitor { + friend SILInstructionVisitor; + + SILFunction &F; + std::unique_ptr TheSummary; + + std::set RecordedTypes; + std::set RecordedDirectTargets; + std::set RecordedVTableTargets; + std::set RecordedWitnessTargets; + + void indexDirectFunctionCall(const SILFunction &Callee); + void indexIndirectFunctionCall(const SILDeclRef &Callee, + FunctionSummary::Call::KindTy Kind); + + /// Index that the conformances of type can be used from this function. + void indexUseOfType(CanType type); + + void visitFunctionRefInst(FunctionRefInst *FRI); + void visitWitnessMethodInst(WitnessMethodInst *WMI); + void visitMethodInst(MethodInst *MI); + void visitDynamicFunctionRefInst(DynamicFunctionRefInst *FRI); + void visitPreviousDynamicFunctionRefInst(PreviousDynamicFunctionRefInst *FRI); + void visitKeyPathInst(KeyPathInst *KPI); + + void visitAllocExistentialBoxInst(AllocExistentialBoxInst *AEBI); + void visitAllocGlobalInst(AllocGlobalInst *AGI); + void visitAllocRefInst(AllocRefInst *ARI); + void visitAllocStackInst(AllocStackInst *ASI); + void visitAllocValueBufferInst(AllocValueBufferInst *AVBI); + void visitApplyInst(ApplyInst *AI); + void visitBeginApplyInst(BeginApplyInst *BAI); + void visitBuiltinInst(BuiltinInst *BI); + void visitCheckedCastBranchInst(CheckedCastBranchInst *CCBI); + void visitCheckedCastAddrBranchInst(CheckedCastAddrBranchInst *CCABI); + void visitCheckedCastValueBranchInst(CheckedCastValueBranchInst *CCVBI); + void visitCopyAddrInst(CopyAddrInst *CAI); + void visitCopyValueInst(CopyValueInst *CVI); + void visitDestroyAddrInst(DestroyAddrInst *DAI); + void visitDestroyValueInst(DestroyValueInst *DVI); + void visitGlobalAddrInst(GlobalAddrInst *GAI); + void visitGlobalValueInst(GlobalValueInst *GVI); + // void visitKeyPathInst(KeyPathInst *KPI); + void visitInitEnumDataAddrInst(InitEnumDataAddrInst *IEDAI); + void visitInjectEnumAddrInst(InjectEnumAddrInst *IEAI); + void visitInitExistentialAddrInst(InitExistentialAddrInst *IEAI); + void visitInitExistentialMetatypeInst(InitExistentialMetatypeInst *IEMI); + void visitInitExistentialRefInst(InitExistentialRefInst *IERI); + void visitInitExistentialValueInst(InitExistentialValueInst *IEVI); + void visitMetatypeInst(MetatypeInst *MI); + void visitPartialApplyInst(PartialApplyInst *PAI); + void visitSelectEnumAddrInst(SelectEnumAddrInst *SEAI); + void visitStructElementAddrInst(StructElementAddrInst *SEAI); + void visitTryApplyInst(TryApplyInst *TAI); + void visitTupleElementAddrInst(TupleElementAddrInst *TEAI); + void visitUnconditionalCheckedCastInst(UnconditionalCheckedCastInst *UCCI); + void visitUnconditionalCheckedCastAddrInst( + UnconditionalCheckedCastAddrInst *UCCAI); + void + visitUncheckedTakeEnumDataAddrInst(UncheckedTakeEnumDataAddrInst *UTEDAI); + // void visitWitnessMethodInst(WitnessMethodInst *WMI); + + void visitSILInstruction(SILInstruction *I) {} +public: + FunctionSummaryIndexer(SILFunction &F) : F(F) {} + void indexFunction(); + + std::unique_ptr takeSummary() { + return std::move(TheSummary); + } +}; + +void FunctionSummaryIndexer::indexDirectFunctionCall( + const SILFunction &Callee) { + GUID guid = getGUIDFromUniqueName(Callee.getName()); + if (!RecordedDirectTargets.insert(guid).second) { + return; + } + FunctionSummary::Call call(guid, Callee.getName().str(), + FunctionSummary::Call::Direct); + TheSummary->addCall(call); +} + +void FunctionSummaryIndexer::indexIndirectFunctionCall( + const SILDeclRef &Callee, FunctionSummary::Call::KindTy Kind) { + std::string mangledName = Callee.mangle(); + GUID guid = getGUIDFromUniqueName(mangledName); + std::set &RecordedTargets = Kind == FunctionSummary::Call::VTable ? + RecordedVTableTargets : RecordedWitnessTargets; + if (!RecordedTargets.insert(guid).second) { + return; + } + FunctionSummary::Call call(guid, mangledName, Kind); + TheSummary->addCall(call); +} + +void FunctionSummaryIndexer::indexUseOfType(CanType type) { + Mangle::ASTMangler mangler; + type.visit([&](Type t) { + auto *decl = t->getAnyNominal(); + if (!decl) { + return; + } + std::string mangled = mangler.mangleNominalType(decl); + GUID guid = getGUIDFromUniqueName(mangled); + if (RecordedTypes.insert(guid).second) { + TheSummary->addTypeRef({guid, mangled}); + } + }); +} + +void FunctionSummaryIndexer::visitAllocExistentialBoxInst( + AllocExistentialBoxInst *AEBI) { + indexUseOfType(AEBI->getFormalConcreteType()); +} + +void FunctionSummaryIndexer::visitAllocGlobalInst(AllocGlobalInst *AGI) { + indexUseOfType(AGI->getReferencedGlobal()->getLoweredType().getASTType()); +} + +void FunctionSummaryIndexer::visitAllocRefInst(AllocRefInst *ARI) { + indexUseOfType(ARI->getType().getASTType()); +} +void FunctionSummaryIndexer::visitAllocStackInst(AllocStackInst *ASI) { + indexUseOfType(ASI->getType().getASTType()); +} +void FunctionSummaryIndexer::visitAllocValueBufferInst( + AllocValueBufferInst *AVBI) { + indexUseOfType(AVBI->getType().getASTType()); +} +void FunctionSummaryIndexer::visitApplyInst(ApplyInst *AI) { + indexUseOfType(AI->getSubstCalleeType()); +} +void FunctionSummaryIndexer::visitBeginApplyInst(BeginApplyInst *BAI) { + indexUseOfType(BAI->getSubstCalleeType()); +} +void FunctionSummaryIndexer::visitBuiltinInst(BuiltinInst *BI) { + // FIXME: Need to index substitution map? +} +void FunctionSummaryIndexer::visitCheckedCastBranchInst( + CheckedCastBranchInst *CCBI) { + indexUseOfType(CCBI->getSourceFormalType()); + indexUseOfType(CCBI->getTargetFormalType()); +} +void FunctionSummaryIndexer::visitCheckedCastAddrBranchInst( + CheckedCastAddrBranchInst *CCABI) { + indexUseOfType(CCABI->getSourceFormalType()); + indexUseOfType(CCABI->getTargetFormalType()); +} +void FunctionSummaryIndexer::visitCheckedCastValueBranchInst( + CheckedCastValueBranchInst *CCVBI) { + indexUseOfType(CCVBI->getSourceFormalType()); + indexUseOfType(CCVBI->getTargetFormalType()); +} +void FunctionSummaryIndexer::visitCopyAddrInst(CopyAddrInst *CAI) { + indexUseOfType(CAI->getSrc()->getType().getASTType()); + indexUseOfType(CAI->getDest()->getType().getASTType()); +} +void FunctionSummaryIndexer::visitCopyValueInst(CopyValueInst *CVI) { + indexUseOfType(CVI->getOperand()->getType().getASTType()); +} +void FunctionSummaryIndexer::visitDestroyAddrInst(DestroyAddrInst *DAI) { + indexUseOfType(DAI->getOperand()->getType().getASTType()); +} +void FunctionSummaryIndexer::visitDestroyValueInst(DestroyValueInst *DVI) { + indexUseOfType(DVI->getOperand()->getType().getASTType()); +} +void FunctionSummaryIndexer::visitGlobalAddrInst(GlobalAddrInst *GAI) { + indexUseOfType(GAI->getReferencedGlobal()->getLoweredType().getASTType()); +} +void FunctionSummaryIndexer::visitGlobalValueInst(GlobalValueInst *GVI) { + indexUseOfType(GVI->getReferencedGlobal()->getLoweredType().getASTType()); +} +void FunctionSummaryIndexer::visitInitEnumDataAddrInst( + InitEnumDataAddrInst *IEDAI) { + indexUseOfType(IEDAI->getOperand()->getType().getASTType()); +} +void FunctionSummaryIndexer::visitInjectEnumAddrInst(InjectEnumAddrInst *IEAI) { + indexUseOfType(IEAI->getOperand()->getType().getASTType()); +} +void FunctionSummaryIndexer::visitInitExistentialAddrInst( + InitExistentialAddrInst *IEAI) { + indexUseOfType(IEAI->getFormalConcreteType()); +} +void FunctionSummaryIndexer::visitInitExistentialMetatypeInst( + InitExistentialMetatypeInst *IEMI) { + indexUseOfType(IEMI->getOperand()->getType().getASTType()); +} +void FunctionSummaryIndexer::visitInitExistentialRefInst( + InitExistentialRefInst *IERI) { + indexUseOfType(IERI->getFormalConcreteType()); +} +void FunctionSummaryIndexer::visitInitExistentialValueInst( + InitExistentialValueInst *IEVI) { + indexUseOfType(IEVI->getFormalConcreteType()); +} +void FunctionSummaryIndexer::visitMetatypeInst(MetatypeInst *MI) { + indexUseOfType(MI->getType().getASTType()); +} +void FunctionSummaryIndexer::visitPartialApplyInst(PartialApplyInst *PAI) { + indexUseOfType(PAI->getSubstCalleeType()); +} +void FunctionSummaryIndexer::visitSelectEnumAddrInst(SelectEnumAddrInst *SEAI) { + indexUseOfType(SEAI->getEnumOperand()->getType().getASTType()); +} +void FunctionSummaryIndexer::visitStructElementAddrInst( + StructElementAddrInst *SEAI) { + indexUseOfType(SEAI->getOperand()->getType().getASTType()); +} +void FunctionSummaryIndexer::visitTryApplyInst(TryApplyInst *TAI) { + indexUseOfType(TAI->getSubstCalleeType()); +} +void FunctionSummaryIndexer::visitTupleElementAddrInst( + TupleElementAddrInst *TEAI) { + indexUseOfType(TEAI->getOperand()->getType().getASTType()); +} +void FunctionSummaryIndexer::visitUnconditionalCheckedCastInst( + UnconditionalCheckedCastInst *UCCI) { + indexUseOfType(UCCI->getSourceFormalType()); + indexUseOfType(UCCI->getTargetFormalType()); +} +void FunctionSummaryIndexer::visitUnconditionalCheckedCastAddrInst( + UnconditionalCheckedCastAddrInst *UCCAI) { + indexUseOfType(UCCAI->getSourceFormalType()); + indexUseOfType(UCCAI->getTargetFormalType()); +} +void FunctionSummaryIndexer::visitUncheckedTakeEnumDataAddrInst( + UncheckedTakeEnumDataAddrInst *UTEDAI) { + indexUseOfType(UTEDAI->getOperand()->getType().getASTType()); +} + +void FunctionSummaryIndexer::visitFunctionRefInst(FunctionRefInst *FRI) { + SILFunction *callee = FRI->getReferencedFunctionOrNull(); + assert(callee); + indexDirectFunctionCall(*callee); +} + +void FunctionSummaryIndexer::visitWitnessMethodInst(WitnessMethodInst *WMI) { + indexIndirectFunctionCall(WMI->getMember(), FunctionSummary::Call::Witness); +} + +void FunctionSummaryIndexer::visitMethodInst(MethodInst *MI) { + indexIndirectFunctionCall(MI->getMember(), FunctionSummary::Call::VTable); +} + +void FunctionSummaryIndexer::visitDynamicFunctionRefInst(DynamicFunctionRefInst *FRI) { + SILFunction *callee = FRI->getInitiallyReferencedFunction(); + assert(callee); + indexDirectFunctionCall(*callee); +} + +void FunctionSummaryIndexer::visitPreviousDynamicFunctionRefInst(PreviousDynamicFunctionRefInst *FRI) { + SILFunction *callee = FRI->getInitiallyReferencedFunction(); + assert(callee); + indexDirectFunctionCall(*callee); +} + +void FunctionSummaryIndexer::visitKeyPathInst(KeyPathInst *KPI) { + for (auto &component : KPI->getPattern()->getComponents()) { + component.visitReferencedFunctionsAndMethods( + [this](SILFunction *F) { + assert(F); + indexDirectFunctionCall(*F); + }, + [this](SILDeclRef method) { + auto decl = cast(method.getDecl()); + if (auto clas = dyn_cast(decl->getDeclContext())) { + indexIndirectFunctionCall(method, FunctionSummary::Call::VTable); + } else if (isa(decl->getDeclContext())) { + indexIndirectFunctionCall(method, FunctionSummary::Call::Witness); + } else { + llvm_unreachable( + "key path keyed by a non-class, non-protocol method"); + } + }); + } +} + +bool shouldPreserveFunction(const SILFunction &F) { + if (EagerElimination && + (F.getName().equals("swift_unexpectedError") || + F.getName().equals("swift_errorInMain") || + F.getName().equals("$ss23_getErrorDomainNSStringyyXlSPyxGs0B0RzlF"))) { + return false; + } + if (F.getName().equals(SWIFT_ENTRY_POINT_FUNCTION)) { + return true; + } + + if (F.getRepresentation() == SILFunctionTypeRepresentation::ObjCMethod) { + return true; + } + if (F.hasCReferences()) { + return true; + } + if (F.isDynamicallyReplaceable()) { + return true; + } + return false; +} + +void FunctionSummaryIndexer::indexFunction() { + GUID guid = getGUIDFromUniqueName(F.getName()); + uint32_t instSize = 0; + TheSummary = std::make_unique(guid); + TheSummary->setName(F.getName().str()); + for (auto &BB : F) { + for (auto &I : BB) { + visit(&I); + instSize++; + } + } + TheSummary->setPreserved(shouldPreserveFunction(F)); + TheSummary->setInstSize(instSize); +} + +class ModuleSummaryIndexer { + std::unique_ptr TheSummary; + SILModule &Mod; + void ensurePreserved(const SILFunction &F); + void ensurePreserved(const SILDeclRef &Ref, VFuncSlot::KindTy Kind); + void preserveKeyPathFunctions(const SILProperty &P); + void indexWitnessTable(const SILWitnessTable &WT); + void indexVTable(const SILVTable &VT); + +public: + ModuleSummaryIndexer(SILModule &M) : Mod(M) {} + void indexModule(); + std::unique_ptr takeSummary() { + return std::move(TheSummary); + } +}; + +void ModuleSummaryIndexer::ensurePreserved(const SILFunction &F) { + GUID guid = getGUIDFromUniqueName(F.getName()); + auto FS = TheSummary->getFunctionSummary(guid); + assert(FS); + FS->setPreserved(true); +} + +static VFuncSlot createVFuncSlot(SILDeclRef VFuncRef, VFuncSlot::KindTy Kind) { + return VFuncSlot(Kind, getGUIDFromUniqueName(VFuncRef.mangle())); +} + +void ModuleSummaryIndexer::ensurePreserved(const SILDeclRef &Ref, + VFuncSlot::KindTy Kind) { + auto slot = createVFuncSlot(Ref, Kind); + auto Impls = TheSummary->getImplementations(slot); + if (Impls.empty()) + return; + + for (VFuncImpl Impl : Impls) { + auto FS = TheSummary->getFunctionSummary(Impl.Guid); + assert(FS); + FS->setPreserved(true); + } +} + +void ModuleSummaryIndexer::preserveKeyPathFunctions(const SILProperty &P) { + auto maybeComponent = P.getComponent(); + if (!maybeComponent) + return; + + KeyPathPatternComponent component = maybeComponent.getValue(); + component.visitReferencedFunctionsAndMethods( + [&](SILFunction *F) { ensurePreserved(*F); }, + [&](SILDeclRef method) { + auto decl = cast(method.getDecl()); + if (isa(decl->getDeclContext())) { + ensurePreserved(method, VFuncSlot::VTable); + } else if (isa(decl->getDeclContext())) { + ensurePreserved(method, VFuncSlot::Witness); + } else { + llvm_unreachable( + "key path keyed by a non-class, non-protocol method"); + } + }); +} + +void ModuleSummaryIndexer::indexWitnessTable(const SILWitnessTable &WT) { + auto isPossibllyUsedExternally = + WT.getDeclContext()->getParentModule() != Mod.getSwiftModule() || + WT.getProtocol()->getParentModule() != Mod.getSwiftModule(); + auto typeGUID = getTypeGUID(WT.getConformingType()->getAnyNominal()); + for (auto entry : WT.getEntries()) { + if (entry.getKind() != SILWitnessTable::Method) + continue; + + auto methodWitness = entry.getMethodWitness(); + auto Witness = methodWitness.Witness; + if (!Witness) + continue; + auto slot = createVFuncSlot(methodWitness.Requirement, VFuncSlot::Witness); + TheSummary->addImplementation(slot, + getGUIDFromUniqueName(Witness->getName()), + typeGUID); + + if (isPossibllyUsedExternally) { + ensurePreserved(*Witness); + } + } +} + +void ModuleSummaryIndexer::indexVTable(const SILVTable &VT) { + auto typeGUID = getTypeGUID(VT.getClass()); + for (auto entry : VT.getEntries()) { + auto Impl = entry.getImplementation(); + if (entry.getMethod().kind == SILDeclRef::Kind::Deallocator || + entry.getMethod().kind == SILDeclRef::Kind::IVarDestroyer) { + // Destructors are preserved because they can be called from swift_release + // dynamically + ensurePreserved(*Impl); + } + auto methodModule = entry.getMethod().getDecl()->getModuleContext(); + auto isExternalMethod = methodModule != Mod.getSwiftModule(); + + if (entry.getKind() == SILVTableEntry::Override && isExternalMethod) { + ensurePreserved(*Impl); + } + auto slot = createVFuncSlot(entry.getMethod(), VFuncSlot::VTable); + TheSummary->addImplementation(slot, getGUIDFromUniqueName(Impl->getName()), typeGUID); + } +} + +void ModuleSummaryIndexer::indexModule() { + TheSummary = std::make_unique(); + auto moduleName = Mod.getSwiftModule()->getName().str(); + TheSummary->setName(moduleName.str()); + + for (auto &F : Mod) { + FunctionSummaryIndexer indexer(F); + indexer.indexFunction(); + std::unique_ptr FS = indexer.takeSummary(); + TheSummary->addFunctionSummary(std::move(FS)); + } + + for (auto &WT : Mod.getWitnessTableList()) { + indexWitnessTable(WT); + } + + for (auto VT : Mod.getVTables()) { + indexVTable(*VT); + } +} +}; // namespace + +std::unique_ptr +modulesummary::buildModuleSummaryIndex(SILModule &M) { + ModuleSummaryIndexer indexer(M); + indexer.indexModule(); + return indexer.takeSummary(); +} diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index 11a7d962c1f43..ec6ec76045f69 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -1121,12 +1121,36 @@ void SerializedASTFile::getImportedModules( void SerializedASTFile::collectLinkLibrariesFromImports( ModuleDecl::LinkLibraryCallback callback) const { - llvm::SmallVector Imports; - File.getImportedModules(Imports, {ModuleDecl::ImportFilterKind::Exported, - ModuleDecl::ImportFilterKind::Default}); - for (auto Import : Imports) - Import.importedModule->collectLinkLibraries(callback); + llvm::SmallDenseSet visited; + SmallVector stack; + + ModuleDecl::ImportFilter filter = { + ModuleDecl::ImportFilterKind::Exported, + ModuleDecl::ImportFilterKind::Default, + ModuleDecl::ImportFilterKind::SPIAccessControl}; + + auto *topLevel = getParentModule(); + + ModuleDecl::ImportFilter topLevelFilter = filter; + topLevelFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly; + topLevel->getImportedModules(stack, topLevelFilter); + + // Make sure the top-level module is first; we want pre-order-ish traversal. + stack.emplace_back(ImportPath::Access(), topLevel); + + while (!stack.empty()) { + auto next = stack.pop_back_val().importedModule; + + if (!visited.insert(next).second) + continue; + + if (next->getName() != getParentModule()->getName()) { + next->collectLinkLibraries(callback); + } + + next->getImportedModules(stack, filter); + } } void SerializedASTFile::collectLinkLibraries( diff --git a/test/LTO/empty_module_summary.swift b/test/LTO/empty_module_summary.swift new file mode 100644 index 0000000000000..27e9e62d75d0a --- /dev/null +++ b/test/LTO/empty_module_summary.swift @@ -0,0 +1,9 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-sib -emit-module-summary-path %t/empty.swiftmodule.summary -module-name empty %s +// RUN: llvm-bcanalyzer -dump %t/empty.swiftmodule.summary | %FileCheck %s -check-prefix BCANALYZER + +// BCANALYZER-NOT: UnknownCode + +// RUN: %swift-module-summary-test --to-yaml %t/empty.swiftmodule.summary -o - | %FileCheck %s + +// CHECK: module_name: empty diff --git a/test/LTO/function_summary.swift b/test/LTO/function_summary.swift new file mode 100644 index 0000000000000..d0c893e85c925 --- /dev/null +++ b/test/LTO/function_summary.swift @@ -0,0 +1,71 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-sib -emit-module-summary-path %t/function_summary.swiftmodule.summary -module-name function_summary -Xllvm -module-summary-embed-debug-name %s +// RUN: llvm-bcanalyzer -dump %t/function_summary.swiftmodule.summary | %FileCheck %s -check-prefix BCANALYZER + +// BCANALYZER-NOT: UnknownCode + +// RUN: %swift-module-summary-test --to-yaml %t/function_summary.swiftmodule.summary -o %t/function_summary.summary.yaml + +// `bar` references `foo` directly +// RUN: cat %t/function_summary.summary.yaml | %FileCheck %s -check-prefix DIRECT-CALL + +// DIRECT-CALL: 15904760426814321987: +// DIRECT-CALL-NEXT: name: '$s16function_summary3baryyF' +// DIRECT-CALL-NEXT: guid: 15904760426814321987 +// DIRECT-CALL-NEXT: live: false +// DIRECT-CALL-NEXT: preserved: false +// DIRECT-CALL-NEXT: calls: +// DIRECT-CALL-NEXT: - callee_name: '$s16function_summary3fooyyF' +// DIRECT-CALL-NEXT: callee_guid: 3365867516365370991 +// DIRECT-CALL-NEXT: kind: direct +func foo() {} + +func bar() { + foo() +} + +// `useGenericP` and `useExistentialP` reference `#P.protoMember` through witness table +// RUN: cat %t/function_summary.summary.yaml | %FileCheck %s -check-prefix WITNESS-CALL + +// WITNESS-CALL: 2534322708691595658: +// WITNESS-CALL-NEXT: name: '$s16function_summary15useExistentialPyyAA1P_pF' +// WITNESS-CALL-NEXT: guid: 2534322708691595658 +// WITNESS-CALL-NEXT: live: false +// WITNESS-CALL-NEXT: preserved: false +// WITNESS-CALL-NEXT: calls: +// WITNESS-CALL-NEXT: - callee_name: '$s16function_summary1PP11protoMemberyyF' +// WITNESS-CALL-NEXT: callee_guid: 12061107285276415735 +// WITNESS-CALL-NEXT: kind: witness + +protocol P { + func protoMember() +} + +func useExistentialP(_ v: P) { + v.protoMember() +} + +// `useClassC` reference `#P.classMember` through vtable +// RUN: cat %t/function_summary.summary.yaml | %FileCheck %s -check-prefix VTABLE-CALL + +// VTABLE-CALL: 6451800047657108456: +// VTABLE-CALL-NEXT: name: '$s16function_summary9useClassCyyAA1CCF' +// VTABLE-CALL-NEXT: guid: 6451800047657108456 +// VTABLE-CALL-NEXT: live: false +// VTABLE-CALL-NEXT: preserved: false +// VTABLE-CALL-NEXT: calls: +// VTABLE-CALL-NEXT: - callee_name: '$s16function_summary1CC11classMemberyyF' +// VTABLE-CALL-NEXT: callee_guid: 7506985369146111998 +// VTABLE-CALL-NEXT: kind: vtable + +class C { + func classMember() {} +} + +class D : C { + override func classMember() {} +} + +func useClassC(_ v: C) { + v.classMember() +} diff --git a/test/LTO/module_summary_generic_type_ref.swift b/test/LTO/module_summary_generic_type_ref.swift new file mode 100644 index 0000000000000..d8c2fef8950f2 --- /dev/null +++ b/test/LTO/module_summary_generic_type_ref.swift @@ -0,0 +1,37 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-sib -emit-module-summary-path %t/default_wt.swiftmodule.summary -module-name default_wt -Xllvm -module-summary-embed-debug-name %s +// RUN: %swift_frontend_plain -merge-module-summary %t/default_wt.swiftmodule.summary -module-summary-embed-debug-name -o %t/default_wt.swiftmodule.merged-summary +// RUN: %swift-module-summary-test --to-yaml %t/default_wt.swiftmodule.merged-summary -o %t/default_wt.merged-summary.yaml + +// Ensure that optimizer won't eliminate PrimitiveSequenceType.getPrimitiveSequence +// RUN: %target-swift-frontend -c -module-summary-path %t/default_wt.swiftmodule.merged-summary default_wt.sib -o %t/default_wt.o +// RUN: %target-build-swift %t/default_wt.o -o %t/default_wt +// RUN: %target-run %t/default_wt + +// RUN: cat %t/default_wt.merged-summary.yaml | %FileCheck %s +// CHECK: 8732890044670327403: +// CHECK-NEXT: name: '$s10default_wt11HappyStructV13requiredValueSiyF' +// CHECK-NEXT: guid: 8732890044670327403 +// CHECK-NEXT: live: true + +protocol HappyProtocol { + func requiredValue() -> Int +} + +extension HappyProtocol { + // Need to access requiredValue indirectly through wt + @_optimize(none) + func getRequiredValue() -> Int { + return requiredValue() + } +} + +struct HappyStruct : HappyProtocol { + func requiredValue() -> Int { 1 } +} + +func consume(_ v: HappyStruct) { + _ = v.getRequiredValue() +} + +consume(HappyStruct()) diff --git a/test/LTO/module_summary_preserved.swift b/test/LTO/module_summary_preserved.swift new file mode 100644 index 0000000000000..9767b88e36a00 --- /dev/null +++ b/test/LTO/module_summary_preserved.swift @@ -0,0 +1,62 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-sib -emit-module-summary-path %t/preserved.swiftmodule.summary -module-name preserved -Xllvm -module-summary-embed-debug-name %s +// RUN: %swift-module-summary-test --to-yaml %t/preserved.swiftmodule.summary -o %t/preserved.summary.yaml +// RUN: cat %t/preserved.summary.yaml | %FileCheck %s -check-prefix REPLACABLE +// REPLACABLE: 909315153062346157: +// REPLACABLE-NEXT: name: '$s9preserved12replaceable1SiyF' +// REPLACABLE-NEXT: guid: 909315153062346157 +// REPLACABLE-NEXT: live: false +// REPLACABLE-NEXT: preserved: true + +// REPLACABLE: 3380283816534000009: +// REPLACABLE-NEXT: name: '$s9preserved14replaceable1_rSiyF' +// REPLACABLE-NEXT: guid: 3380283816534000009 +// REPLACABLE-NEXT: live: false +// REPLACABLE-NEXT: preserved: false + +dynamic func replaceable1() -> Int { + return 0 +} + +@_dynamicReplacement(for: replaceable1()) +func replaceable1_r() -> Int { + return 3 +} + + +// RUN: cat %t/preserved.summary.yaml | %FileCheck %s -check-prefix CDECL +// CDECL: 401177591854398425: +// CDECL-NEXT: name: callableFromC2 +// CDECL-NEXT: guid: 401177591854398425 +// CDECL-NEXT: live: false +// CDECL-NEXT: preserved: true +// CDECL-NEXT: calls: [] +// CDECL: 2609850307322683057: +// CDECL-NEXT: name: callableFromC1 +// CDECL-NEXT: guid: 2609850307322683057 +// CDECL-NEXT: live: false +// CDECL-NEXT: preserved: true +@_cdecl("callableFromC1") +func callableFromC1(x: Int) -> Int { + return 1 +} + +@_silgen_name("callableFromC2") +func callableFromC2(x: Int) -> Int { + return 2 +} + +// RUN: if [ %target-runtime == "objc" ]; then cat %t/preserved.summary.yaml | %FileCheck %s -check-prefix OBJC; fi +// OBJC: 3149498140227613915: +// OBJC-NEXT: name: '$s9preserved1AC11objcMethod1yyFTo' +// OBJC-NEXT: guid: 3149498140227613915 +// OBJC-NEXT: live: false +// OBJC-NEXT: preserved: true + +#if canImport(ObjectiveC) +import Foundation + +class A: NSObject { + @objc func objcMethod1() {} +} +#endif diff --git a/test/LTO/module_summary_tables.swift b/test/LTO/module_summary_tables.swift new file mode 100644 index 0000000000000..c6d7b7dfed45f --- /dev/null +++ b/test/LTO/module_summary_tables.swift @@ -0,0 +1,45 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-sib -emit-module-summary-path %t/tables.swiftmodule.summary -module-name tables -Xllvm -module-summary-embed-debug-name %s +// RUN: llvm-bcanalyzer -dump %t/tables.swiftmodule.summary | %FileCheck %s -check-prefix BCANALYZER + +// BCANALYZER-NOT: UnknownCode + +// RUN: %swift-module-summary-test --to-yaml %t/tables.swiftmodule.summary -o - | %FileCheck %s + + +// CHECK: 767048646313834908: +// CHECK-NEXT: name: '$s6tables1SVAA1PA2aDP11protoMemberyyFTW' +// CHECK: 11756327503593502600: +// CHECK-NEXT: name: '$s6tables1DC11classMemberyyF' +// CHECK: 17602567966448237004: +// CHECK-NEXT: name: '$s6tables1CC11classMemberyyF' + +// `protoMember` witness is recorded on the table +// CHECK: witness_tables: +// CHECK-NEXT: 2682576275888919121: +// CHECK-NEXT: - guid: 767048646313834908 +// CHECK-NEXT: type_guid: 16808374101942615301 +// `classMember` impls are recorded on the table +// CHECK: vtables: +// CHECK: 17602567966448237004: +// CHECK-NEXT: - guid: 17602567966448237004 +// CHECK-NEXT: type_guid: 6261216615345887281 +// CHECK-NEXT: - guid: 11756327503593502600 +// CHECK-NEXT: type_guid: 1726984972356197982 + +protocol P { + func protoMember() +} + +struct S : P { + func protoMember() {} +} + + +class C { + func classMember() {} +} + +class D : C { + override func classMember() {} +} diff --git a/test/LTO/type_refs_summary.swift b/test/LTO/type_refs_summary.swift new file mode 100644 index 0000000000000..36145e18e1720 --- /dev/null +++ b/test/LTO/type_refs_summary.swift @@ -0,0 +1,101 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-sib -emit-module-summary-path %t/type_refs.swiftmodule.summary -module-name type_refs -Xllvm -module-summary-embed-debug-name %s +// RUN: llvm-bcanalyzer -dump %t/type_refs.swiftmodule.summary | %FileCheck %s -check-prefix BCANALYZER + +// BCANALYZER-NOT: UnknownCode + +// RUN: %swift-module-summary-test --to-yaml %t/type_refs.swiftmodule.summary -o %t/type_refs.summary.yaml + +// Ensure that type reference to S, C and D are recorded +// RUN: cat %t/type_refs.summary.yaml | %FileCheck %s -check-prefix SIMPLE-COERCE + +// SIMPLE-COERCE: name: '$s9type_refs11coerceToAnyyypAA1DCF' +// SIMPLE-COERCE-NEXT: guid: 14079990488171131 +// SIMPLE-COERCE-NEXT: live: false +// SIMPLE-COERCE-NEXT: preserved: false +// SIMPLE-COERCE-NEXT: calls: [] +// SIMPLE-COERCE-NEXT: type_refs: +// SIMPLE-COERCE-NEXT: - name: '$s9type_refs1DC' +// SIMPLE-COERCE-NEXT: guid: 9126595621082655001 + +// SIMPLE-COERCE: name: '$s9type_refs11coerceToAnyyypAA1CCF' +// SIMPLE-COERCE-NEXT: guid: 1940219073901329473 +// SIMPLE-COERCE-NEXT: live: false +// SIMPLE-COERCE-NEXT: preserved: false +// SIMPLE-COERCE-NEXT: calls: [] +// SIMPLE-COERCE-NEXT: type_refs: +// SIMPLE-COERCE-NEXT: - name: '$s9type_refs1CC' +// SIMPLE-COERCE-NEXT: guid: 3331627721515121492 + +// SIMPLE-COERCE: name: '$s9type_refs9coerceToPyAA1P_pAA1SVF' +// SIMPLE-COERCE-NEXT: guid: 15452386893050095333 +// SIMPLE-COERCE-NEXT: live: false +// SIMPLE-COERCE-NEXT: preserved: false +// SIMPLE-COERCE-NEXT: calls: [] +// SIMPLE-COERCE-NEXT: type_refs: +// SIMPLE-COERCE-NEXT: - name: '$s9type_refs1SV' +// SIMPLE-COERCE-NEXT: guid: 5397591673202260225 + + + +// Ensure that witness impl of S.foo for P has type ref to S +// RUN: cat %t/type_refs.summary.yaml | %FileCheck %s -check-prefix WITNESS-IMPL + +// WITNESS-IMPL: 12925277474523063582: +// WITNESS-IMPL-NEXT: name: '$s9type_refs1SVAA1PA2aDP3fooyyFTW' + +// WITNESS-IMPL: witness_tables: +// WITNESS-IMPL-NEXT: 17891631795932606560: +// WITNESS-IMPL-NEXT: - guid: 12925277474523063582 +// WITNESS-IMPL-NEXT: type_guid: 5397591673202260225 + + +// Ensure that vtable impl of C.bar and D.bar have type ref to C +// RUN: cat %t/type_refs.summary.yaml | %FileCheck %s -check-prefix WITNESS-IMPL + + +// VTABLE-IMPL: 14897920476774525675: +// VTABLE-IMPL-NEXT: name: '$s9type_refs1CC3baryyF' +// VTABLE-IMPL: 16977749031506698911: +// VTABLE-IMPL-NEXT: name: '$s9type_refs1DC3baryyF' + +// VTABLE-IMPL: 14897920476774525675: +// VTABLE-IMPL-NEXT: - guid: 14897920476774525675 +// VTABLE-IMPL-NEXT: type_guid: 3331627721515121492 +// VTABLE-IMPL-NEXT: - guid: 16977749031506698911 +// VTABLE-IMPL-NEXT: type_guid: 9126595621082655001 + + +// RUN: %swift_frontend_plain -merge-module-summary %t/type_refs.swiftmodule.summary -module-summary-embed-debug-name -o %t/type_refs.swiftmodule.merged-summary +// RUN: %swift-module-summary-test --to-yaml %t/type_refs.swiftmodule.merged-summary -o %t/type_refs.merged-summary.yaml +// Ensure that WT of V is not used. +// RUN: cat %t/type_refs.merged-summary.yaml | %FileCheck %s -check-prefix USED-TYPE + +// USED-TYPE-NOT: s9type_refs1VVXMt + +protocol P { + func foo() +} + +struct S : P { + func foo() {} +} + +struct V : P { + func foo() {} +} + +class C { + func bar() {} +} + +class D : C { + override func bar() {} +} + +func coerceToP(_ x: S) -> P { return x } + +_ = coerceToP(S()) + +func coerceToAny(_ x: C) -> Any { return x } +func coerceToAny(_ x: D) -> Any { return x } diff --git a/test/Serialization/Inputs/module1.swift b/test/Serialization/Inputs/module1.swift new file mode 100644 index 0000000000000..85b849b8d05ce --- /dev/null +++ b/test/Serialization/Inputs/module1.swift @@ -0,0 +1,10 @@ +public func module1Func() -> Int { return 1 } + +public protocol P { + func memberMethod() + func defaultProvided() +} + +extension P { + public func defaultProvided() {} +} diff --git a/test/Serialization/Inputs/module2.swift b/test/Serialization/Inputs/module2.swift new file mode 100644 index 0000000000000..55786a8e54bc6 --- /dev/null +++ b/test/Serialization/Inputs/module2.swift @@ -0,0 +1,19 @@ +import module1 + +public func module2Func() -> Int { + return module1Func() +} + +public struct Concrete1 : P { + public func memberMethod() {} + public func defaultProvided() {} +} + +public struct Concrete2 : P { + public func memberMethod() {} + public init() {} +} + +public func useP(_ t: T) { + t.memberMethod() +} diff --git a/test/lit.cfg b/test/lit.cfg index c6c16be6ab3bd..e66154ab646af 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -281,6 +281,7 @@ config.sil_passpipeline_dumper = inferSwiftBinary('sil-passpipeline-dumper') config.lldb_moduleimport_test = inferSwiftBinary('lldb-moduleimport-test') config.swift_ide_test = inferSwiftBinary('swift-ide-test') config.swift_dependency_tool = inferSwiftBinary('swift-dependency-tool') +config.swift_module_summary_test = inferSwiftBinary('swift-module-summary-test') config.swift_syntax_test = inferSwiftBinary('swift-syntax-test') if 'syntax_parser_lib' in config.available_features: config.swift_syntax_parser_test = inferSwiftBinary('swift-syntax-parser-test') @@ -449,6 +450,7 @@ config.substitutions.append( ('%swift-dump-pcm', "%r -dump-pcm" % config.swiftc) config.substitutions.append( ('%swift-ide-test_plain', config.swift_ide_test) ) config.substitutions.append( ('%swift-ide-test', "%r %s %s -swift-version %s" % (config.swift_ide_test, mcp_opt, ccp_opt, swift_version)) ) config.substitutions.append( ('%swift-dependency-tool', config.swift_dependency_tool) ) +config.substitutions.append( ('%swift-module-summary-test', config.swift_module_summary_test) ) config.substitutions.append( ('%swift-syntax-test', config.swift_syntax_test) ) if 'syntax_parser_lib' in config.available_features: config.substitutions.append( ('%swift-syntax-parser-test', config.swift_syntax_parser_test) ) diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index b50cbc73b1f2b..163051c5c6222 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -34,6 +34,7 @@ add_swift_tool_subdirectory(swift-api-digester) add_swift_tool_subdirectory(swift-ast-script) add_swift_tool_subdirectory(swift-syntax-test) add_swift_tool_subdirectory(swift-refactor) +add_swift_tool_subdirectory(swift-module-summary-test) if(SWIFT_BUILD_SYNTAXPARSERLIB) add_swift_tool_subdirectory(libSwiftSyntaxParser) add_swift_tool_subdirectory(swift-syntax-parser-test) diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index aabba372ae647..0c33df44404c0 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -2,6 +2,7 @@ add_swift_host_tool(swift-frontend driver.cpp autolink_extract_main.cpp modulewrap_main.cpp + cross_module_opt_main.cpp swift_indent_main.cpp swift_symbolgraph_extract_main.cpp SWIFT_COMPONENT compiler diff --git a/tools/driver/cross_module_opt_main.cpp b/tools/driver/cross_module_opt_main.cpp new file mode 100644 index 0000000000000..2c55353441da5 --- /dev/null +++ b/tools/driver/cross_module_opt_main.cpp @@ -0,0 +1,281 @@ +#define DEBUG_TYPE "lto-cross-module-opt" + +#include "swift/AST/DiagnosticsFrontend.h" +#include "swift/Basic/LLVMInitialize.h" +#include "swift/Frontend/Frontend.h" +#include "swift/Frontend/PrintingDiagnosticConsumer.h" +#include "swift/Option/Options.h" +#include "swift/SIL/SILModule.h" +#include "swift/SIL/TypeLowering.h" +#include "swift/Serialization/ModuleSummary.h" +#include "swift/Serialization/Validation.h" +#include "swift/Subsystems.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Bitstream/BitstreamReader.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/TargetSelect.h" + +using namespace llvm::opt; +using namespace swift; +using namespace modulesummary; + +static llvm::cl::opt + LTOPrintLiveTrace("lto-print-live-trace", llvm::cl::init(""), + llvm::cl::desc("Print liveness trace for the symbol")); + +static llvm::cl::list + InputFilenames(llvm::cl::Positional, llvm::cl::desc("[input files...]")); +static llvm::cl::opt + OutputFilename("o", llvm::cl::desc("output filename")); + +static llvm::DenseSet computePreservedGUIDs(ModuleSummaryIndex *summary) { + llvm::DenseSet Set(1); + for (auto FI = summary->functions_begin(), FE = summary->functions_end(); + FI != FE; ++FI) { + auto summary = FI->second.get(); + if (summary->isPreserved()) { + Set.insert(FI->first); + } + } + return Set; +} + +class LivenessTrace { +public: + enum ReasonTy { Preserved, StaticReferenced, IndirectReferenced }; + std::shared_ptr markedBy; + std::string symbol; + GUID guid; + ReasonTy reason; + + LivenessTrace(std::shared_ptr markedBy, GUID guid, + ReasonTy reason) + : markedBy(markedBy), guid(guid), reason(reason) {} + + void setName(std::string name) { this->symbol = name; } + + void dump() { dump(llvm::errs()); } + void dump(llvm::raw_ostream &os) { + if (!symbol.empty()) { + os << symbol; + } else { + os << "**missing name**" + << " (" << guid << ")"; + } + os << "is referenced by:\n"; + + auto target = markedBy; + while (target) { + os << " - "; + if (!target->symbol.empty()) { + os << target->symbol; + } else { + os << "**missing name**"; + } + os << " (" << target->guid << ")"; + os << "\n"; + target = target->markedBy; + } + } +}; + +VFuncSlot createVFuncSlot(FunctionSummary::Call call) { + VFuncSlot::KindTy slotKind; + switch (call.getKind()) { + case FunctionSummary::Call::Witness: { + slotKind = VFuncSlot::Witness; + break; + } + case FunctionSummary::Call::VTable: { + slotKind = VFuncSlot::VTable; + break; + } + case FunctionSummary::Call::Direct: { + llvm_unreachable("Can't get slot for static call"); + } + case FunctionSummary::Call::kindCount: { + llvm_unreachable("impossible"); + } + } + return VFuncSlot(slotKind, call.getCallee()); +} + +void markDeadTypeRef(ModuleSummaryIndex &summary, const llvm::DenseSet &PreservedGUIDs) { + SmallVector Worklist; + SmallSetVector beenInWorklist; + SmallSetVector UseMarkedTypes; + + Worklist.append(PreservedGUIDs.begin(), PreservedGUIDs.end()); + + while (!Worklist.empty()) { + auto target = Worklist.pop_back_val(); + if (!beenInWorklist.insert(target)) { + continue; + } + auto maybeSummary = summary.getFunctionSummary(target); + if (!maybeSummary) { + llvm_unreachable("Bad GUID"); + } + auto FS = maybeSummary; + + for (auto typeRef : FS->typeRefs()) { + if (UseMarkedTypes.insert(typeRef.Guid)) { + summary.markUsedType(typeRef.Guid); + } + } + for (auto Call : FS->calls()) { + switch (Call.getKind()) { + case FunctionSummary::Call::Direct: { + Worklist.push_back(Call.getCallee()); + continue; + } + case FunctionSummary::Call::Witness: + case FunctionSummary::Call::VTable: { + VFuncSlot slot = createVFuncSlot(Call); + auto Impls = summary.getImplementations(slot); + for (auto Impl : Impls) { + Worklist.push_back(Impl.Guid); + } + break; + } + case FunctionSummary::Call::kindCount: + llvm_unreachable("impossible"); + } + } + } +} + +void markDeadSymbols(ModuleSummaryIndex &summary, llvm::DenseSet &PreservedGUIDs) { + + SmallVector, 8> Worklist; + auto UsedTypesList = summary.getUsedTypeList(); + std::set UsedTypesSet(UsedTypesList.begin(), UsedTypesList.end()); + unsigned LiveSymbols = 0; + + for (auto GUID : PreservedGUIDs) { + auto trace = std::make_shared( + nullptr, GUID, LivenessTrace::Preserved); + auto maybeFS = summary.getFunctionSummary(GUID); + if (!maybeFS) { + llvm_unreachable("Bad GUID"); + } + if (!maybeFS->getName().empty()) { + trace->setName(maybeFS->getName()); + } + Worklist.push_back(trace); + } + std::set> dumpTargets; + while (!Worklist.empty()) { + auto trace = Worklist.pop_back_val(); + + auto maybeSummary = summary.getFunctionSummary(trace->guid); + if (!maybeSummary) { + llvm_unreachable("Bad GUID"); + } + auto FS = maybeSummary; + if (FS->isLive()) continue; + + if (!FS->getName().empty()) { + LLVM_DEBUG(llvm::dbgs() << "Mark " << FS->getName() << " as live\n"); + } else { + LLVM_DEBUG(llvm::dbgs() << "Mark (" << FS->getGUID() << ") as live\n"); + } + FS->setLive(true); + LiveSymbols++; + + auto queueWorklist = [&](std::shared_ptr trace) { + auto maybeCallee = summary.getFunctionSummary(trace->guid); + if (!maybeCallee) { + llvm_unreachable("Bad GUID"); + } + auto Callee = maybeCallee; + if (!Callee->getName().empty()) { + trace->setName(Callee->getName()); + if (LTOPrintLiveTrace == Callee->getName()) { + dumpTargets.insert(trace); + } + } + Worklist.push_back(trace); + }; + + for (auto Call : FS->calls()) { + switch (Call.getKind()) { + case FunctionSummary::Call::Direct: { + queueWorklist(std::make_shared( + trace, Call.getCallee(), LivenessTrace::StaticReferenced)); + continue; + } + case FunctionSummary::Call::Witness: + case FunctionSummary::Call::VTable: { + VFuncSlot slot = createVFuncSlot(Call); + auto Impls = summary.getImplementations(slot); + for (auto Impl : Impls) { + if (UsedTypesSet.find(Impl.TypeGuid) == UsedTypesSet.end()) { + continue; + } + queueWorklist(std::make_shared( + trace, Impl.Guid, LivenessTrace::IndirectReferenced)); + } + break; + } + case FunctionSummary::Call::kindCount: + llvm_unreachable("impossible"); + } + } + } + for (auto dumpTarget : dumpTargets) { + dumpTarget->dump(); + } +} + +int cross_module_opt_main(ArrayRef Args, const char *Argv0, + void *MainAddr) { + INITIALIZE_LLVM(); + + llvm::cl::ParseCommandLineOptions(Args.size(), Args.data(), "Swift LTO\n"); + + CompilerInstance Instance; + PrintingDiagnosticConsumer PDC; + Instance.addDiagnosticConsumer(&PDC); + + if (InputFilenames.empty()) { + Instance.getDiags().diagnose(SourceLoc(), + diag::error_mode_requires_an_input_file); + return 1; + } + + auto TheSummary = std::make_unique(); + + for (auto Filename : InputFilenames) { + LLVM_DEBUG(llvm::dbgs() << "Loading module summary " << Filename << "\n"); + auto ErrOrBuf = llvm::MemoryBuffer::getFile(Filename); + if (!ErrOrBuf) { + Instance.getDiags().diagnose( + SourceLoc(), diag::error_no_such_file_or_directory, Filename); + return 1; + } + + auto HasErr = swift::modulesummary::loadModuleSummaryIndex( + ErrOrBuf.get()->getMemBufferRef(), *TheSummary.get()); + + if (HasErr) + llvm::report_fatal_error("Invalid module summary"); + } + + TheSummary->setName("combined"); + + auto PreservedGUIDs = computePreservedGUIDs(TheSummary.get()); + markDeadTypeRef(*TheSummary.get(), PreservedGUIDs); + markDeadSymbols(*TheSummary.get(), PreservedGUIDs); + + modulesummary::writeModuleSummaryIndex(*TheSummary, Instance.getDiags(), + OutputFilename); + return 0; +} diff --git a/tools/driver/driver.cpp b/tools/driver/driver.cpp index 0fb6062e12ec0..097debd310f55 100644 --- a/tools/driver/driver.cpp +++ b/tools/driver/driver.cpp @@ -67,6 +67,9 @@ extern int autolink_extract_main(ArrayRef Args, const char *Argv0, extern int modulewrap_main(ArrayRef Args, const char *Argv0, void *MainAddr); +extern int cross_module_opt_main(ArrayRef Args, const char *Argv0, + void *MainAddr); + /// Run 'swift-indent' extern int swift_indent_main(ArrayRef Args, const char *Argv0, void *MainAddr); @@ -142,6 +145,12 @@ static int run_driver(StringRef ExecName, argv[0], (void *)(intptr_t)getExecutablePath); } + if (FirstArg == "-merge-module-summary") { + return cross_module_opt_main( + llvm::makeArrayRef(argv.data() + 1, argv.data() + argv.size()), + argv[0], (void *)(intptr_t)getExecutablePath); + } + // Run the integrated Swift frontend when called as "swift-frontend" but // without a leading "-frontend". if (!FirstArg.startswith("--driver-mode=") diff --git a/tools/sil-opt/SILOpt.cpp b/tools/sil-opt/SILOpt.cpp index a0398ae919818..79a36e964e5d4 100644 --- a/tools/sil-opt/SILOpt.cpp +++ b/tools/sil-opt/SILOpt.cpp @@ -76,6 +76,9 @@ ModuleName("module-name", llvm::cl::desc("The name of the module if processing" " a module. Necessary for processing " "stdin.")); +static llvm::cl::opt +ModuleSummaryPath("module-summary-path", llvm::cl::desc("module summary filename")); + static llvm::cl::opt EnableLibraryEvolution("enable-library-evolution", llvm::cl::desc("Compile the module to export resilient " @@ -384,6 +387,10 @@ int main(int argc, char **argv) { SILOpts.OptRecordFile = RemarksFilename; SILOpts.OptRecordPasses = RemarksPasses; + if (!ModuleSummaryPath.empty()) { + SILOpts.ModuleSummaryPath = ModuleSummaryPath; + } + SILOpts.VerifyExclusivity = VerifyExclusivity; if (EnforceExclusivity.getNumOccurrences() != 0) { switch (EnforceExclusivity) { diff --git a/tools/swift-module-summary-test/CMakeLists.txt b/tools/swift-module-summary-test/CMakeLists.txt new file mode 100644 index 0000000000000..6abe01902a77b --- /dev/null +++ b/tools/swift-module-summary-test/CMakeLists.txt @@ -0,0 +1,12 @@ +add_swift_host_tool(swift-module-summary-test + swift-module-summary-test.cpp + LLVM_LINK_COMPONENTS + Support + SWIFT_COMPONENT tools +) + +target_link_libraries(swift-module-summary-test + PRIVATE + swiftAST + swiftDemangling + swiftSerialization) diff --git a/tools/swift-module-summary-test/swift-module-summary-test.cpp b/tools/swift-module-summary-test/swift-module-summary-test.cpp new file mode 100644 index 0000000000000..751f28bc94a7e --- /dev/null +++ b/tools/swift-module-summary-test/swift-module-summary-test.cpp @@ -0,0 +1,600 @@ +//===--- swift-module-summary-test.cpp - Test util for C parser library ---===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 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 +// +//===----------------------------------------------------------------------===// +// +// Testing utility for the C API of the parser library. +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/FileSystem.h" +#include "swift/Basic/LLVM.h" +#include "swift/Basic/LLVMInitialize.h" +#include "swift/Demangling/Demangle.h" +#include "swift/Serialization/ModuleSummary.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/GenericDomTree.h" +#include "llvm/Support/GenericDomTreeConstruction.h" +#include "llvm/Support/GraphWriter.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/YAMLParser.h" +#include "llvm/Support/YAMLTraits.h" +#include + +using namespace swift; +using namespace modulesummary; + +enum class ActionType : unsigned { + None, + BinaryToYAML, + YAMLToBinary, + BinaryToDominators, + BinaryToDominatorTree, + BinaryToDot, +}; + +namespace options { + +static llvm::cl::OptionCategory Category("swift-module-summary-test Options"); + +static llvm::cl::opt + InputFilename(llvm::cl::Positional, llvm::cl::desc(""), + llvm::cl::init("-"), llvm::cl::value_desc("filename")); + +static llvm::cl::opt + OutputFilename("o", llvm::cl::desc("Override output filename"), + llvm::cl::value_desc("filename")); + +static llvm::cl::opt + OmitDeadSymbol("omit-dead-symbol", llvm::cl::init(false), + llvm::cl::desc("Omit dead symbols")); + +static llvm::cl::opt Action( + llvm::cl::desc("Mode:"), llvm::cl::init(ActionType::None), + llvm::cl::cat(Category), + llvm::cl::values( + clEnumValN(ActionType::BinaryToYAML, "to-yaml", + "Convert new binary .swiftmodule.summary format to YAML"), + clEnumValN(ActionType::YAMLToBinary, "from-yaml", + "Convert YAML to new binary .swiftmodule.summary format"), + clEnumValN(ActionType::BinaryToDominators, "binary-to-dom", + "Convert new binary .swiftmodule.summary format to " + "dominators list"), + clEnumValN( + ActionType::BinaryToDominatorTree, "binary-to-dom-tree", + "Convert new binary .swiftmodule.summary format to dominator tree"), + clEnumValN(ActionType::BinaryToDot, "binary-to-dot", + "Convert new binary .swiftmodule.summary format to Dot"))); + +} // namespace options + +LLVM_YAML_DECLARE_MAPPING_TRAITS(modulesummary::ModuleSummaryIndex) +LLVM_YAML_DECLARE_MAPPING_TRAITS(modulesummary::FunctionSummary) +LLVM_YAML_DECLARE_MAPPING_TRAITS(modulesummary::FunctionSummary::Call) +LLVM_YAML_DECLARE_MAPPING_TRAITS(modulesummary::FunctionSummary::TypeRef) +LLVM_YAML_DECLARE_MAPPING_TRAITS(modulesummary::VFuncImpl) +LLVM_YAML_IS_SEQUENCE_VECTOR(modulesummary::FunctionSummary::Call) +LLVM_YAML_IS_SEQUENCE_VECTOR(modulesummary::FunctionSummary::TypeRef) +LLVM_YAML_IS_SEQUENCE_VECTOR(modulesummary::VFuncImpl) +LLVM_YAML_DECLARE_ENUM_TRAITS(modulesummary::FunctionSummary::Call::KindTy) + +namespace llvm { +namespace yaml { + +template <> struct MappingTraits> { + static void mapping(IO &io, std::unique_ptr &Ptr) { + if (!Ptr) { + Ptr.reset(new FunctionSummary()); + } + MappingTraits::mapping(io, *Ptr.get()); + } +}; + +void ScalarEnumerationTraits::enumeration( + IO &io, FunctionSummary::Call::KindTy &V) { + using Kind = FunctionSummary::Call::KindTy; + io.enumCase(V, "direct", Kind::Direct); + io.enumCase(V, "vtable", Kind::VTable); + io.enumCase(V, "witness", Kind::Witness); +} + +void MappingTraits::mapping(IO &io, + FunctionSummary::Call &V) { + io.mapRequired("callee_name", V.Name); + io.mapRequired("callee_guid", V.Callee); + io.mapRequired("kind", V.Kind); +} + +void MappingTraits::mapping(IO &io, + FunctionSummary::TypeRef &V) { + io.mapRequired("name", V.Name); + io.mapRequired("guid", V.Guid); +} +void MappingTraits::mapping(IO &io, FunctionSummary &V) { + io.mapRequired("name", V.Name); + io.mapRequired("guid", V.Guid); + io.mapRequired("live", V.Flags.Live); + io.mapRequired("preserved", V.Flags.Preserved); + io.mapRequired("calls", V.CallGraphEdgeList); + io.mapRequired("type_refs", V.TypeRefList); + io.mapRequired("inst_size", V.InstSize); +} + +template <> +struct CustomMappingTraits { + static void inputOne(IO &io, StringRef Key, + FunctionSummaryMapTy &V) { + GUID KeyInt; + if (Key.getAsInteger(0, KeyInt)) { + io.setError("key not an integer"); + return; + } + io.mapRequired(Key.str().c_str(), V[KeyInt]); + } + static void output(IO &io, FunctionSummaryMapTy &V) { + for (auto &P : V) + io.mapRequired(llvm::utostr(P.first).c_str(), P.second); + } +}; + +void MappingTraits::mapping(IO &io, VFuncImpl &V) { + io.mapRequired("guid", V.Guid); + io.mapRequired("type_guid", V.TypeGuid); +} + +template <> +struct CustomMappingTraits { + static void inputOne(IO &io, StringRef Key, VFuncToImplsMapTy &V) { + GUID KeyInt; + if (Key.getAsInteger(0, KeyInt)) { + io.setError("key not an integer"); + return; + } + io.mapRequired(Key.str().c_str(), V[KeyInt]); + } + static void output(IO &io, VFuncToImplsMapTy &V) { + for (auto &P : V) + io.mapRequired(llvm::utostr(P.first).c_str(), P.second); + } +}; + +void MappingTraits::mapping(IO &io, ModuleSummaryIndex &V) { + io.mapRequired("module_name", V.Name); + io.mapRequired("functions", V.FunctionSummaryMap); + io.mapRequired("witness_tables", V.WitnessTableMethodMap); + io.mapRequired("vtables", V.VTableMethodMap); + io.mapRequired("used_types", V.UsedTypeList); +} +} // namespace yaml +} // namespace llvm + +VFuncSlot createVFuncSlot(FunctionSummary::Call call) { + VFuncSlot::KindTy slotKind; + switch (call.getKind()) { + case FunctionSummary::Call::Witness: { + slotKind = VFuncSlot::Witness; + break; + } + case FunctionSummary::Call::VTable: { + slotKind = VFuncSlot::VTable; + break; + } + case FunctionSummary::Call::Direct: { + llvm_unreachable("Can't get slot for static call"); + } + case FunctionSummary::Call::kindCount: { + llvm_unreachable("impossible"); + } + } + return VFuncSlot(slotKind, call.getCallee()); +} + +struct CallGraph { + struct Node; + + struct Edge { + FunctionSummary::Call Call; + GUID Target; + }; + + struct Node { + GUID Guid; + CallGraph *Parent; + std::vector Children; + std::vector PredChildren; + std::vector Edges; + + CallGraph *getParent() const { return Parent; } + + FunctionSummary *getFS() const { + return Parent->Summary.getFunctionSummary(Guid); + } + + void printAsOperand(raw_ostream &OS, bool /*PrintType*/) { + FunctionSummary *FS = Parent->Summary.getFunctionSummary(Guid); + if (!FS) { + OS << "Entry node"; + return; + } + OS << "Fn#" << FS->getName(); + } + }; + + using child_iterator = std::vector::iterator; + + std::vector Nodes; + Node EntryNode; + ModuleSummaryIndex Summary; + using iterator = std::vector::iterator; + CallGraph(ModuleSummaryIndex); +}; + +CallGraph::CallGraph(ModuleSummaryIndex Summary) : Nodes(), EntryNode() { + std::map NodeMap; + Nodes.resize(Summary.functions_size()); + int idx = 0; + for (auto FI = Summary.functions_begin(), FE = Summary.functions_end(); + FI != FE; ++FI) { + Node *node = &Nodes[idx++]; + node->Guid = FI->first; + node->Parent = this; + NodeMap[FI->first] = node; + if (FI->second->isPreserved()) { + EntryNode.Children.push_back(node); + EntryNode.PredChildren.push_back(node); + } + } + EntryNode.Parent = this; + + for (Node &node : Nodes) { + FunctionSummary *FS = Summary.getFunctionSummary(node.Guid); + for (FunctionSummary::Call call : FS->calls()) { + switch (call.getKind()) { + case FunctionSummary::Call::Witness: + case FunctionSummary::Call::VTable: { + VFuncSlot slot = createVFuncSlot(call); + for (auto Impl : Summary.getImplementations(slot)) { + Node *CalleeNode = NodeMap[Impl.Guid]; + node.Children.push_back(CalleeNode); + node.PredChildren.push_back(CalleeNode); + node.Edges.push_back({call, Impl.Guid}); + } + break; + } + case FunctionSummary::Call::Direct: { + Node *CalleeNode = NodeMap[call.getCallee()]; + node.Children.push_back(CalleeNode); + node.PredChildren.push_back(CalleeNode); + node.Edges.push_back({call, call.getCallee()}); + break; + } + case FunctionSummary::Call::kindCount: + llvm_unreachable("impossible"); + } + } + } + this->Summary = std::move(Summary); +} + +namespace llvm { + + template <> struct GraphTraits { + typedef CallGraph::child_iterator ChildIteratorType; + typedef CallGraph::Node *NodeRef; + + static NodeRef getEntryNode(NodeRef N) { return N; } + static inline ChildIteratorType child_begin(NodeRef N) { + return N->Children.begin(); + } + static inline ChildIteratorType child_end(NodeRef N) { + return N->Children.end(); + } + }; + + template <> struct GraphTraits> { + using ChildIteratorType = CallGraph::child_iterator; + using Node = CallGraph::Node; + using NodeRef = Node *; + static NodeRef getEntryNode(Inverse G) { + return G.Graph; + } + static inline ChildIteratorType child_begin(NodeRef N) { + return N->PredChildren.begin(); + } + static inline ChildIteratorType child_end(NodeRef N) { + return N->PredChildren.end(); + } + }; + + template <> struct GraphTraits + : public GraphTraits { + typedef CallGraph *GraphType; + typedef CallGraph::Node *NodeRef; + + static NodeRef getEntryNode(GraphType G) { return &G->EntryNode; } + + typedef pointer_iterator nodes_iterator; + static nodes_iterator nodes_begin(GraphType CG) { + return nodes_iterator(CG->Nodes.begin()); + } + static nodes_iterator nodes_end(GraphType CG) { + return nodes_iterator(CG->Nodes.end()); + } + static unsigned size(GraphType CG) { return CG->Nodes.size(); } + }; + + /// This is everything the llvm::GraphWriter needs to write the call graph in + /// a dot file. + template <> + struct DOTGraphTraits : public DefaultDOTGraphTraits { + + DOTGraphTraits(bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {} + + std::string getNodeLabel(const CallGraph::Node *Node, + const CallGraph *Graph) { + FunctionSummary *FS = Graph->Summary.getFunctionSummary(Node->Guid); + Demangle::Context DCtx; + return DCtx.demangleSymbolAsString(FS->getName()); + } + + static std::string getNodeAttributes(const CallGraph::Node *Node, + const CallGraph *Graph) { + FunctionSummary *FS = Graph->Summary.getFunctionSummary(Node->Guid); + std::string color = FS->isLive() ? "green" : "red"; + std::string attrs = "color=\"" + color + "\""; + return attrs; + } + + static std::string getEdgeAttributes(const CallGraph::Node *Node, + CallGraph::child_iterator I, + const CallGraph *Graph) { + unsigned ChildIdx = I - Node->Children.begin(); + std::string Label; + raw_string_ostream O(Label); + Demangle::Context DCtx; + CallGraph::Edge edge = Node->Edges[ChildIdx]; + FunctionSummary::Call call = edge.Call; + std::string demangled = DCtx.demangleSymbolAsString(call.getName()); + O << "label=\""; + switch (call.getKind()) { + case FunctionSummary::Call::Witness: { + O << "(W) " << demangled; + break; + } + case FunctionSummary::Call::VTable: { + O << "(V) " << demangled; + break; + } + case FunctionSummary::Call::Direct: { + O << "(D)"; + break; + } + case FunctionSummary::Call::kindCount: + llvm_unreachable("impossible"); + } + O << "\""; + return Label; + } + }; +} // namespace llvm +using DomCallTreeBase = llvm::DominatorTreeBase; +using DomCallInfoNode = llvm::DomTreeNodeBase; + +namespace llvm { + +template <> struct GraphTraits { + using ChildIteratorType = DomCallInfoNode::iterator; + using NodeRef = DomCallInfoNode *; + + static NodeRef getEntryNode(NodeRef N) { return N; } + static inline ChildIteratorType child_begin(NodeRef N) { return N->begin(); } + static inline ChildIteratorType child_end(NodeRef N) { return N->end(); } +}; + +template <> struct GraphTraits { + using ChildIteratorType = DomCallInfoNode::const_iterator; + using NodeRef = const DomCallInfoNode *; + + static NodeRef getEntryNode(NodeRef N) { return N; } + static inline ChildIteratorType child_begin(NodeRef N) { return N->begin(); } + static inline ChildIteratorType child_end(NodeRef N) { return N->end(); } +}; + +namespace DomTreeBuilder { +extern template void Calculate(DomCallTreeBase &DT); +template void Calculate(DomCallTreeBase &DT); +} // namespace DomTreeBuilder +} // namespace llvm + +class DomCallTree : public DomCallTreeBase { + using super = DominatorTreeBase; + +public: + DomCallTree(CallGraph &CG) : DominatorTreeBase() { recalculate(CG); } +}; + +class RetainedSizeCalculator { + struct Entry { + DomCallInfoNode *DomNode; + uint32_t InstSize; + }; + std::map SizeCache; + std::vector Entries; + + uint32_t getInstSize(DomCallInfoNode *domNode) { + auto FS = domNode->getBlock()->getFS(); + uint32_t size = FS->getInstSize(); + for (auto I = domNode->begin(), E = domNode->end(); I != E; ++I) { + auto Child = *I; + size += getInstSize(Child); + } + SizeCache[domNode] = size; + return size; + } + + void calculateRecursively(DomCallInfoNode *domNode) { + Entries.push_back({domNode, getInstSize(domNode)}); + for (auto child : domNode->children()) { + calculateRecursively(child); + } + } + +public: + void calculate(DomCallInfoNode *root) { + for (auto child : root->children()) { + calculateRecursively(child); + } + std::sort(Entries.begin(), Entries.end(), + [](Entry lhs, Entry rhs) { return lhs.InstSize > rhs.InstSize; }); + } + + void display(llvm::raw_ostream &O) { + O << "size\t| symbol\n"; + for (auto entry : Entries) { + auto FS = entry.DomNode->getBlock()->getFS(); + O << entry.InstSize << "\t| " << FS->getName() << "\n"; + } + } + + void displayTreeRec(llvm::raw_ostream &O, uint32_t TotalSize, + DomCallInfoNode *Root, unsigned Lev) { + auto Size = getInstSize(Root); + auto Percent = (double(Size) / TotalSize * 100); + auto FS = Root->getBlock()->getFS(); + O << Size << "\t| "; + llvm::format_provider::format(Percent, O, "f2"); + O << "\t| "; + O.indent(2 * Lev) << FS->getName() << "\n"; + + auto Children = Root->children(); + std::sort(Children.begin(), Children.end(), + [&](DomCallInfoNode *lhs, DomCallInfoNode *rhs) { + return getInstSize(lhs) > getInstSize(rhs); + }); + for (auto Child : Children) { + displayTreeRec(O, TotalSize, Child, Lev + 1); + } + } + void displayTree(llvm::raw_ostream &O, DomCallInfoNode *Root) { + O << "size\t| %\t| symbol\n"; + uint32_t TotalSize = 0; + for (auto Child : Root->children()) { + TotalSize += getInstSize(Child); + } + auto Children = Root->children(); + std::sort(Children.begin(), Children.end(), + [&](DomCallInfoNode *lhs, DomCallInfoNode *rhs) { + return getInstSize(lhs) > getInstSize(rhs); + }); + for (auto Child : Children) { + displayTreeRec(O, TotalSize, Child, 0); + } + } +}; + +int dominance_analysis(CallGraph &CG) { + DomCallTree DT(CG); + DT.print(llvm::dbgs()); + DomCallInfoNode *Root = DT.getRootNode(); + RetainedSizeCalculator calculator; + calculator.calculate(Root); + calculator.display(llvm::dbgs()); + return 0; +}; + +int main(int argc, char *argv[]) { + PROGRAM_START(argc, argv); + llvm::cl::ParseCommandLineOptions(argc, argv, "Swift Module Summary Test\n"); + + StringRef fname = options::InputFilename; + SourceManager sourceMgr; + DiagnosticEngine diags(sourceMgr); + + auto fileBufOrErr = llvm::MemoryBuffer::getFile(fname); + if (!fileBufOrErr) { + llvm::errs() << "error opening file '" << fname + << "': " << fileBufOrErr.getError().message(); + return 1; + } + + switch (options::Action) { + case ActionType::None: { + llvm::errs() << "action required\n"; + llvm::cl::PrintHelpMessage(); + return 1; + } + case ActionType::BinaryToYAML: { + modulesummary::ModuleSummaryIndex summary; + modulesummary::loadModuleSummaryIndex(fileBufOrErr.get()->getMemBufferRef(), + summary); + + bool hadError = withOutputFile(diags, options::OutputFilename, + [&](llvm::raw_pwrite_stream &out) { + out << "# Module-summary v0\n"; + llvm::yaml::Output yamlWriter(out); + yamlWriter << summary; + return false; + }); + + if (hadError) { + llvm::errs() << "Failed to write YAML swiftdeps\n"; + } + break; + } + case ActionType::YAMLToBinary: { + ModuleSummaryIndex summary; + llvm::yaml::Input yamlReader(fileBufOrErr.get()->getMemBufferRef(), + nullptr); + yamlReader >> summary; + if (yamlReader.error()) { + llvm::errs() << "Failed to parse YAML swiftdeps\n"; + return 1; + } + + if (writeModuleSummaryIndex(summary, diags, options::OutputFilename)) { + llvm::errs() << "Failed to write binary module summary\n"; + return 1; + } + break; + } + case ActionType::BinaryToDot: { + modulesummary::ModuleSummaryIndex summary; + modulesummary::loadModuleSummaryIndex(fileBufOrErr.get()->getMemBufferRef(), + summary); + CallGraph CG(std::move(summary)); + withOutputFile(diags, options::OutputFilename, [&](raw_ostream &out) { + llvm::WriteGraph(out, &CG); + return false; + }); + break; + } + case ActionType::BinaryToDominators: + case ActionType::BinaryToDominatorTree: { + modulesummary::ModuleSummaryIndex summary; + modulesummary::loadModuleSummaryIndex(fileBufOrErr.get()->getMemBufferRef(), + summary); + CallGraph CG(std::move(summary)); + DomCallTree DT(CG); + DomCallInfoNode *Root = DT.getRootNode(); + RetainedSizeCalculator calculator; + calculator.calculate(Root); + withOutputFile(diags, options::OutputFilename, [&](raw_ostream &out) { + if (options::Action == ActionType::BinaryToDominators) { + calculator.display(out); + } else { + calculator.displayTree(out, Root); + } + return false; + }); + break; + } + } + return 0; +} diff --git a/utils/webassembly/ci.sh b/utils/webassembly/ci.sh index 80bf7f32450ed..d14aa2861bcc3 100755 --- a/utils/webassembly/ci.sh +++ b/utils/webassembly/ci.sh @@ -47,10 +47,14 @@ fi if [[ "$(uname)" == "Linux" ]]; then $RUN_TEST_BIN --build-dir $TARGET_BUILD_DIR --target wasi-wasm32 \ $TARGET_BUILD_DIR/swift-${HOST_PLATFORM}-x86_64/test-wasi-wasm32/stdlib + $RUN_TEST_BIN --build-dir $TARGET_BUILD_DIR --target wasi-wasm32 \ + $TARGET_BUILD_DIR/swift-${HOST_PLATFORM}-x86_64/test-wasi-wasm32/LTO echo "Skip running test suites for Linux" else $RUN_TEST_BIN --build-dir $TARGET_BUILD_DIR --target wasi-wasm32 \ $TARGET_BUILD_DIR/swift-${HOST_PLATFORM}-x86_64/test-wasi-wasm32/stdlib + $RUN_TEST_BIN --build-dir $TARGET_BUILD_DIR --target wasi-wasm32 \ + $TARGET_BUILD_DIR/swift-${HOST_PLATFORM}-x86_64/test-wasi-wasm32/LTO # Run test but ignore failure temporarily ninja check-swift-wasi-wasm32 -C $TARGET_BUILD_DIR/swift-$HOST_SUFFIX || true