Files
swift-mirror/include/swift/SILOptimizer/Analysis/CallerAnalysis.h
Michael Gottesman 234fcc1771 [pass-manager] notifyDeleteFunction => notifyWillDeleteFunction.
This name makes it clear that the function has not yet been deleted and also
contrasts with the past tense used in the API notifyAddedOrModifiedFunction to
show that said function has already added/modified the function.
2018-07-16 14:11:06 -07:00

186 lines
6.6 KiB
C++

//===--- CallerAnalysis.h - Determine callees per call site -----*- 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_CALLERANALYSIS_H
#define SWIFT_SILOPTIMIZER_ANALYSIS_CALLERANALYSIS_H
#include "swift/SILOptimizer/Analysis/Analysis.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILModule.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/TinyPtrVector.h"
namespace swift {
/// CallerAnalysis relies on keeping the Caller/Callee relation up-to-date
/// lazily. i.e. when a function is invalidated, instead of recomputing the
/// function it calls right away, its kept in a recompute list and
/// CallerAnalysis recomputes and empty the recompute list before any query.
///
/// We also considered the possibility of keeping a computed list, instead of
/// recompute Every time we need to complete the computed list (i.e. we want
/// to the computed list to contain every function in the module). We need to
/// walk through every function in the module. This leads to O(n) And we need
/// to run every function through the a sequence of function passes which might
/// invalidate the functions and make the computed list incomplete. So
/// O(n) * O(n) = O(n^2).
///
/// In addition of caller information this analysis also provides information
/// about partial applies of a function.
class CallerAnalysis : public SILAnalysis {
public:
class FunctionInfo;
private:
/// Current module we are analyzing.
SILModule &Mod;
/// A map between all the functions and their callsites in the module.
llvm::DenseMap<SILFunction *, FunctionInfo> FuncInfos;
/// A list of functions that needs to be recomputed.
llvm::SetVector<SILFunction *> RecomputeFunctionList;
/// Iterate over all the call sites in the function and update
/// CallInfo.
void processFunctionCallSites(SILFunction *F);
/// This function is about to become "unknown" to us. Invalidate any
/// callsite information related to it.
void invalidateExistingCalleeRelation(SILFunction *F);
void processRecomputeFunctionList() {
for (auto &F : RecomputeFunctionList) {
processFunctionCallSites(F);
}
RecomputeFunctionList.clear();
}
public:
CallerAnalysis(SILModule *M) : SILAnalysis(SILAnalysisKind::Caller), Mod(*M) {
// Make sure we compute everything first time called.
for (auto &F : Mod) {
FuncInfos.FindAndConstruct(&F);
RecomputeFunctionList.insert(&F);
}
}
static bool classof(const SILAnalysis *S) {
return S->getKind() == SILAnalysisKind::Caller;
}
/// Invalidate all information in this analysis.
virtual void invalidate() override {
FuncInfos.clear();
RecomputeFunctionList.clear();
for (auto &F : Mod) {
RecomputeFunctionList.insert(&F);
}
}
/// Invalidate all of the information for a specific function.
virtual void invalidate(SILFunction *F, InvalidationKind K) override {
// Should we invalidate based on the invalidation kind.
bool shouldInvalidate = K & InvalidationKind::CallsAndInstructions;
if (!shouldInvalidate)
return;
// This function has become "unknown" to us. Invalidate any callsite
// information related to this function.
invalidateExistingCalleeRelation(F);
// Make sure this function is recomputed next time.
RecomputeFunctionList.insert(F);
}
/// Notify the analysis about a newly created or modified function.
virtual void notifyAddedOrModifiedFunction(SILFunction *F) override {
RecomputeFunctionList.insert(F);
}
/// Notify the analysis about a function which will be deleted from the
/// module.
virtual void notifyWillDeleteFunction(SILFunction *F) override {
invalidateExistingCalleeRelation(F);
RecomputeFunctionList.remove(F);
}
/// Notify the analysis about changed witness or vtables.
virtual void invalidateFunctionTables() override { }
const FunctionInfo &getCallerInfo(SILFunction *F) const {
// Recompute every function in the invalidated function list and empty the
// list.
auto *self = const_cast<CallerAnalysis *>(this);
self->processRecomputeFunctionList();
return self->FuncInfos[F];
}
LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED,
"Only for use in the debugger");
/// Print the state of the caller analysis as a sequence of yaml documents for
/// each callee we are tracking.
void print(llvm::raw_ostream &os) const;
/// Print the state of the caller analysis as a sequence of yaml documents for
/// each callee we are tracking to the passed in file path.
LLVM_ATTRIBUTE_DEPRECATED(void print(const char *filePath)
const LLVM_ATTRIBUTE_USED,
"Only for use in the debugger");
};
/// NOTE: this can be extended to contain the callsites of the function.
class CallerAnalysis::FunctionInfo {
friend class CallerAnalysis;
/// A list of all the functions this function calls or partially applies.
llvm::SmallSetVector<SILFunction *, 4> Callees;
/// A list of all the callers this function has.
llvm::SmallSet<SILFunction *, 4> Callers;
/// The number of partial applied arguments of this function.
///
/// Specifically, it stores the minimum number of partial applied arguments
/// of each function which contain one or multiple partial_applys of this
/// function.
/// This is a little bit off-topic because a partial_apply is not really
/// a "call" of this function.
llvm::SmallMapVector<SILFunction *, int, 1> PartialAppliers;
public:
/// Returns true if this function has at least one caller.
bool hasCaller() const { return !Callers.empty(); }
/// Returns non zero if this function is partially applied anywhere.
///
/// The return value is the minimum number of partially applied arguments.
/// Usually all partial applies of a function partially apply the same
/// number of arguments anyway.
int getMinPartialAppliedArgs() const {
int minArgs = 0;
for (auto Iter : PartialAppliers) {
int numArgs = Iter.second;
if (minArgs == 0 || numArgs < minArgs)
minArgs = numArgs;
}
return minArgs;
}
};
} // end namespace swift
#endif