blob: 38ad9f08b1eb2550b3a8990f939f597c07528bf2 [file] [log] [blame]
//===--- Analysis.h - Swift 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
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SILOPTIMIZER_ANALYSIS_ANALYSIS_H
#define SWIFT_SILOPTIMIZER_ANALYSIS_ANALYSIS_H
#include "swift/Basic/NullablePtr.h"
#include "swift/SIL/Notifications.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
#include <vector>
namespace swift {
class SILModule;
class SILFunction;
class SILPassManager;
/// A list of the known analysis.
struct SILAnalysisKind {
enum InnerTy {
#define ANALYSIS(NAME) NAME,
#include "Analysis.def"
} value;
SILAnalysisKind(InnerTy newValue) : value(newValue) {}
operator InnerTy() const { return value; }
};
/// The base class for all SIL-level analysis.
class SILAnalysis : public DeleteNotificationHandler {
public:
/// This is a list of values that allow passes to communicate to analysis
/// which traits of the code were invalidated. Based on this information
/// the analysis can decide if it needs to be invalidated. This information
/// may refer to a specific function or to the whole module depending on
/// the context in which it is used.
enum InvalidationKind : unsigned {
/// The pass does not change anything.
Nothing = 0x0,
/// The pass created, deleted or rearranged some instructions in a
/// function.
Instructions = 0x1,
/// The pass modified some calls (apply instructions).
///
/// The intention of this invalidation kind is to allow analysis that
/// rely on a specific call graph structure to recompute themselves.
Calls = 0x2,
/// A pass has invalidated some branches in the program.
///
/// The intention of this invalidation kind is to tell analyses like the
/// Dominance Analysis and the PostOrder Analysis that the underlying CFG
/// has been modified.
Branches = 0x4,
/// Convenience states:
FunctionBody = Calls | Branches | Instructions,
CallsAndInstructions = Calls | Instructions,
BranchesAndInstructions = Branches | Instructions,
Everything = Calls | Branches | Instructions,
};
private:
/// Stores the kind of derived class.
const SILAnalysisKind kind;
/// A lock that prevents the invalidation of this analysis. When this
/// variable is set to True then the PassManager should not invalidate
/// this analysis.
bool invalidationLock;
public:
/// Returns the kind of derived class.
SILAnalysisKind getKind() const { return kind; }
/// Constructor.
SILAnalysis(SILAnalysisKind k) : kind(k), invalidationLock(false) {}
/// Destructor.
virtual ~SILAnalysis() {}
/// Can be used to retrieve other analysis passes from \p PM, which this
/// analysis depends on.
virtual void initialize(SILPassManager *pm) { }
/// Lock the analysis. This means that invalidation messages are ignored.
void lockInvalidation() { invalidationLock = true; }
/// Unlock the analysis. This means that invalidation messages are handled.
void unlockInvalidation() { invalidationLock = false; }
/// Return True if this analysis is locked and should not be invalidated.
bool isLocked() const { return invalidationLock; }
/// Invalidate all information in this analysis.
virtual void invalidate() = 0;
/// Invalidate all of the information for a specific function.
virtual void invalidate(SILFunction *f, InvalidationKind k) = 0;
/// Notify the analysis about a newly added or modified function.
virtual void notifyAddedOrModifiedFunction(SILFunction *f) = 0;
/// Notify the analysis about a function that will be deleted from the
/// module.
virtual void notifyWillDeleteFunction(SILFunction *f) = 0;
/// Notify the analysis about changed witness or vtables.
virtual void invalidateFunctionTables() = 0;
/// Verify the state of this analysis.
virtual void verify() const {}
/// Verify the state of this analysis limited to this one function if
/// possible.
///
/// By default this just calls the module verify function as a sensible
/// default so that only functions which are able to provide the function
/// specific verification will do so.
virtual void verify(SILFunction *F) const { verify(); }
/// Verify that the function \p F can be used by the analysis.
static void verifyFunction(SILFunction *F);
};
/// RAII helper for locking analyses. Locks the analysis upon construction and
/// unlocks upon destruction.
class AnalysisPreserver {
SILAnalysis *analysis;
public:
AnalysisPreserver(SILAnalysis *a) : analysis(a) {
analysis->lockInvalidation();
}
~AnalysisPreserver() {
analysis->unlockInvalidation();
}
};
/// An abstract base class that implements the boiler plate of caching and
/// invalidating analysis for specific functions.
///
/// The usage expectation is that the derived function analysis will inherit
/// from FunctionAnalysisBase and pass in as a template argument the
/// "FunctionInfoTy" struct as a template argument. The FunctionInfoTy struct
/// should represent all of the information that the analysis should store about
/// an individual function. As a toy example:
///
/// ```
/// struct TriviallyDeadAnalysisFunctionInfo {
/// bool isTriviallyDead;
/// };
///
/// class TriviallyDeadAnalysis
/// : public FunctionAnalysisBase<TriviallyDeadAnalysisFunctionInfo> { ... }
/// ```
template <typename FunctionInfoTy>
class FunctionAnalysisBase : public SILAnalysis {
protected:
using StorageTy = llvm::DenseMap<SILFunction *, FunctionInfoTy *>;
/// Maps functions to their analysis provider.
StorageTy storage;
/// Construct a new empty function info for a specific function \p F.
virtual FunctionInfoTy *newFunctionAnalysis(SILFunction *f) = 0;
/// Return True if the analysis should be invalidated given trait \K is
/// preserved.
virtual bool shouldInvalidate(SILAnalysis::InvalidationKind k) = 0;
/// A stub function that verifies the specific AnalysisTy \p A. This is
/// meant to be overridden by subclasses.
virtual void verify(FunctionInfoTy *funcInfo) const {}
void deleteAllAnalysisProviders() {
for (auto iter : storage)
delete iter.second;
storage.clear();
}
public:
/// Returns true if we have data for a specific function \p F without actually
/// attempting to construct the function info.
bool hasFunctionInfo(SILFunction *f) const { return storage.count(f); }
/// Attempt to lookup up the information that the analysis has for the given
/// function. Returns nullptr upon failure.
NullablePtr<FunctionInfoTy> maybeGet(SILFunction *f) {
auto iter = storage.find(f);
if (iter == storage.end())
return nullptr;
return iter->second;
}
/// Returns a function info structure for a specific function \p F.
FunctionInfoTy *get(SILFunction *f) {
// Check that the analysis can handle this function.
verifyFunction(f);
auto &it = storage.FindAndConstruct(f);
if (!it.second)
it.second = newFunctionAnalysis(f);
return it.second;
}
/// Invalidate all information in this analysis.
virtual void invalidate() override {
deleteAllAnalysisProviders();
}
/// Helper function to remove the function info for a specific function.
void invalidateFunction(SILFunction *f) {
auto &it = storage.FindAndConstruct(f);
if (!it.second)
return;
delete it.second;
it.second = nullptr;
}
/// Invalidate all of the information for a specific function.
virtual void invalidate(SILFunction *f,
SILAnalysis::InvalidationKind k) override {
if (shouldInvalidate(k))
invalidateFunction(f);
}
/// Notify the analysis about a newly created function.
virtual void notifyAddedOrModifiedFunction(SILFunction *f) override {}
/// Notify the analysis about a function which will be deleted from the
/// module.
virtual void notifyWillDeleteFunction(SILFunction *f) override {
invalidateFunction(f);
}
/// Notify the analysis about changed witness or vtables.
virtual void invalidateFunctionTables() override {}
FunctionAnalysisBase() {}
virtual ~FunctionAnalysisBase() {
deleteAllAnalysisProviders();
}
FunctionAnalysisBase(SILAnalysisKind k) : SILAnalysis(k), storage() {}
FunctionAnalysisBase(const FunctionAnalysisBase &) = delete;
FunctionAnalysisBase &operator=(const FunctionAnalysisBase &) = delete;
/// Verify all of the AnalysisTy for all functions.
///
/// This is not meant to be overridden by subclasses. Instead please override
/// void FunctionAnalysisBase::verify(FunctionInfoTy *fInfo).
virtual void verify() const override final {
for (auto iter : storage) {
if (!iter.second)
continue;
verify(iter.second);
}
}
/// Verify the FunctionInfoTy that we have stored for the specific function \p
/// F.
///
/// This is not meant to be overridden by subclasses. Instead, please
/// override: void FunctionAnalysisBase::verify(FunctionInfoTy *fInfo).
virtual void verify(SILFunction *f) const override final {
auto iter = storage.find(f);
if (iter == storage.end())
return;
if (!iter->second)
return;
verify(iter->second);
}
};
/// Given a specific type of analysis and its function info. Store the
/// analysis and upon request instantiate the function info, caching the
/// function info for subsequent requests.
template <class AnalysisTy, class FunctionInfoTy>
class LazyFunctionInfo {
SILFunction *func;
AnalysisTy *analysis;
NullablePtr<FunctionInfoTy> funcInfo;
public:
LazyFunctionInfo(SILFunction *func, AnalysisTy *analysis) : func(func), analysis(analysis), funcInfo() {}
operator FunctionInfoTy *() {
if (funcInfo.isNull()) {
funcInfo = analysis->get(func);
}
return funcInfo.get();
}
FunctionInfoTy *operator->() { return *this; }
};
#define ANALYSIS(NAME) SILAnalysis *create##NAME##Analysis(SILModule *);
#include "Analysis.def"
} // end namespace swift
#endif