//===--- AccessSummaryAnalysis.h - SIL Access Summary Analysis --*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file implements an interprocedural analysis pass that summarizes // the formal accesses that a function makes to its address-type arguments. // These summaries are used to statically diagnose violations of exclusive // accesses for noescape closures. // //===----------------------------------------------------------------------===// #ifndef SWIFT_SILOPTIMIZER_ANALYSIS_ACCESS_SUMMARY_ANALYSIS_H_ #define SWIFT_SILOPTIMIZER_ANALYSIS_ACCESS_SUMMARY_ANALYSIS_H_ #include "swift/Basic/IndexTrie.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILInstruction.h" #include "swift/SILOptimizer/Analysis/BottomUpIPAnalysis.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" namespace swift { class AccessSummaryAnalysis : public BottomUpIPAnalysis { public: class SubAccessSummary { private: /// The kind of access begun on the argument. SILAccessKind Kind; /// The location of the access. Used for diagnostics. SILLocation AccessLoc = SILLocation::invalid(); const IndexTrieNode *SubPath = nullptr; public: SubAccessSummary(SILAccessKind Kind, SILLocation AccessLoc, const IndexTrieNode *SubPath) : Kind(Kind), AccessLoc(AccessLoc), SubPath(SubPath) {} SILAccessKind getAccessKind() const { return Kind; } SILLocation getAccessLoc() const { return AccessLoc; } const IndexTrieNode *getSubPath() const { return SubPath; } /// The lattice operation on SubAccessSummaries summaries. bool mergeWith(const SubAccessSummary &other); /// Merge in an access to the argument of the given kind at the given /// location with the given suppath. Returns true if the merge caused the /// summary to change. bool mergeWith(SILAccessKind otherKind, SILLocation otherLoc, const IndexTrieNode *otherSubPath); /// Returns a description of the summary. For debugging and testing /// purposes. std::string getDescription(SILType BaseType, SILModule &M, TypeExpansionContext context) const; }; typedef llvm::SmallDenseMap SubAccessMap; /// Summarizes the accesses that a function begins on an argument, including /// the projection subpath that was accessed. class ArgumentSummary { private: SubAccessMap SubAccesses; public: /// The lattice operation on argument summaries. bool mergeWith(const ArgumentSummary &other); /// Merge in an access to the argument of the given kind at the given /// location. Returns true if the merge caused the summary to change. bool mergeWith(SILAccessKind otherKind, SILLocation otherLoc, const IndexTrieNode *otherSubPath); /// Returns a description of the summary. For debugging and testing /// purposes. std::string getDescription(SILType BaseType, SILModule &M, TypeExpansionContext context) const; /// Returns the accesses that the function performs to subpaths of the /// argument. const SubAccessMap &getSubAccesses() const { return SubAccesses; } /// Returns the sorted subaccess summaries into the passed-in storage. /// The accesses are sorted lexicographically by increasing subpath /// length and projection index. void getSortedSubAccesses(SmallVectorImpl &storage) const; }; /// Summarizes the accesses that a function begins on its arguments or /// projections from its arguments. class FunctionSummary { private: llvm::SmallVector ArgAccesses; public: FunctionSummary(unsigned argCount) : ArgAccesses(argCount) {} /// Returns of summary of the function accesses that argument at the /// given index. ArgumentSummary &getAccessForArgument(unsigned argument) { return ArgAccesses[argument]; } const ArgumentSummary &getAccessForArgument(unsigned argument) const { return ArgAccesses[argument]; } /// Returns the number of argument in the summary. unsigned getArgumentCount() const { return ArgAccesses.size(); } void print(raw_ostream &os, SILFunction *fn) const; void dump(SILFunction *fn) const; }; class FunctionInfo; /// Records a flow of a caller's argument to a called function. /// This flow is used to iterate the interprocedural analysis to a fixpoint. struct ArgumentFlow { /// The index of the argument in the caller. const unsigned CallerArgumentIndex; /// The index of the argument in the callee. const unsigned CalleeArgumentIndex; FunctionInfo *const CalleeFunctionInfo; }; /// Records the summary and argument flows for a given function. /// Used by the BottomUpIPAnalysis to propagate information /// from callees to callers. class FunctionInfo : public FunctionInfoBase { private: FunctionSummary FS; SILFunction *F; llvm::SmallVector RecordedArgumentFlows; public: FunctionInfo(SILFunction *F) : FS(F->getArguments().size()), F(F) {} SILFunction *getFunction() const { return F; } ArrayRef getArgumentFlows() const { return RecordedArgumentFlows; } const FunctionSummary &getSummary() const { return FS; } FunctionSummary &getSummary() { return FS; } /// Record a flow of an argument in this function to a callee. void recordFlow(const ArgumentFlow &flow) { flow.CalleeFunctionInfo->addCaller(this, nullptr); RecordedArgumentFlows.push_back(flow); } }; private: /// Maps functions to the information the analysis keeps for each function. llvm::DenseMap FunctionInfos; llvm::SpecificBumpPtrAllocator Allocator; public: AccessSummaryAnalysis() : BottomUpIPAnalysis(SILAnalysisKind::AccessSummary) {} /// Returns a summary of the accesses performed by the given function. const FunctionSummary &getOrCreateSummary(SILFunction *Fn); /// Returns an IndexTrieNode that represents the single subpath accessed from /// BAI or the root if no such node exists. const IndexTrieNode *findSubPathAccessed(BeginAccessInst *BAI); virtual void initialize(SILPassManager *PM) override {} virtual void invalidate() override; virtual void invalidate(SILFunction *F, InvalidationKind K) override; virtual void notifyAddedOrModifiedFunction(SILFunction *F) override {} virtual void notifyWillDeleteFunction(SILFunction *F) override { invalidate(F, InvalidationKind::Nothing); } virtual void invalidateFunctionTables() override {} static bool classof(const SILAnalysis *S) { return S->getKind() == SILAnalysisKind::AccessSummary; } /// Returns a description of the subpath suitable for use in diagnostics. /// The base type must be the type of the root of the path. static std::string getSubPathDescription(SILType BaseType, const IndexTrieNode *SubPath, SILModule &M, TypeExpansionContext context); /// Returns the type associated with the subpath \p SubPath. /// /// \p BaseType must be the type of the root of the path. static SILType getSubPathType(SILType BaseType, const IndexTrieNode *SubPath, SILModule &M, TypeExpansionContext context); /// Performs a lexicographic comparison of two subpaths, first by path length /// and then by index of the last path component. Returns true when lhs /// is less than rhs. static bool compareSubPaths(const IndexTrieNode *lhs, const IndexTrieNode *rhs); private: typedef BottomUpFunctionOrder FunctionOrder; /// Returns the BottomUpIPAnalysis information for the given function. FunctionInfo *getFunctionInfo(SILFunction *F); /// Summarizes the given function and iterates the interprocedural analysis /// to a fixpoint. void recompute(FunctionInfo *initial); /// Propagate the access summary from the argument of a called function /// to the caller. bool propagateFromCalleeToCaller(FunctionInfo *callerInfo, ArgumentFlow site); /// Summarize the given function and schedule it for interprocedural /// analysis. void processFunction(FunctionInfo *info, FunctionOrder &order); /// Summarize how the function uses the given argument. void processArgument(FunctionInfo *info, SILFunctionArgument *argument, ArgumentSummary &summary, FunctionOrder &order); /// Summarize a partial_apply instruction. void processPartialApply(FunctionInfo *callerInfo, unsigned callerArgumentIndex, PartialApplyInst *apply, Operand *applyArgumentOperand, FunctionOrder &order); /// Summarize apply or try_apply void processFullApply(FunctionInfo *callerInfo, unsigned callerArgumentIndex, FullApplySite apply, Operand *argumentOperand, FunctionOrder &order); /// Summarize a call site and schedule it for interprocedural analysis. void processCall(FunctionInfo *callerInfo, unsigned callerArgumentIndex, SILFunction *calledFunction, unsigned argumentIndex, FunctionOrder &order); }; } // end namespace swift #endif