mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
I believe that these were in SILInstruction for historic reasons. This is a separate API on top of SILInstruction so it makes sense to pull it out into its own header.
322 lines
12 KiB
C++
322 lines
12 KiB
C++
//===- BottomUpIPAnalysis.h - Bottom-up IP analysis base-class --*- 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 contains a base class for bottom-up interprocedural analysis.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_SILOPTIMIZER_ANALYSIS_BOTTOMUPIPANALYSIS_H
|
|
#define SWIFT_SILOPTIMIZER_ANALYSIS_BOTTOMUPIPANALYSIS_H
|
|
|
|
#include "swift/SILOptimizer/Analysis/Analysis.h"
|
|
#include "swift/SIL/SILInstruction.h"
|
|
#include "swift/SIL/ApplySite.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
|
|
namespace swift {
|
|
|
|
/// An abstract base class for interprocedural analysis which are computed in
|
|
/// bottom-up order of the call-graph.
|
|
/// It provides utilities for automatic invalidation and updating the analysis.
|
|
class BottomUpIPAnalysis : public SILAnalysis {
|
|
|
|
/// Each update cycle gets a unique ID.
|
|
/// It is incremented for each recomputation of the analysis.
|
|
/// It is mainly used to check for invalid CallerEntries in
|
|
/// FunctionInfoBase::Callers (for details see there).
|
|
int CurrentUpdateID = 0;
|
|
|
|
protected:
|
|
|
|
template<typename FunctionInfo> class FunctionInfoWorkList;
|
|
|
|
/// An abstract base class for a \p FunctionInfo class, which stores the
|
|
/// analysis information for a function.
|
|
/// This base class stores the administrative information needed for
|
|
/// invalidation and updating the analysis.
|
|
/// In the following "this function" refers to the associated function.
|
|
template<typename FunctionInfo> class FunctionInfoBase {
|
|
public:
|
|
|
|
/// An entry in the caller list.
|
|
struct CallerEntry {
|
|
/// The caller function.
|
|
FunctionInfo *Caller;
|
|
|
|
/// The function apply-site.
|
|
/// It can be an apply instruction or an instruction,
|
|
/// which results in a call, e.g. strong_release results in
|
|
/// a destructor call.
|
|
SILInstruction *FAS;
|
|
|
|
/// Returns false, if the caller was invalidated since this entry had been
|
|
/// created.
|
|
bool isValid() const { return Caller->UpdateID == UpdateID; }
|
|
|
|
private:
|
|
/// Used to check if the caller and its apply-site is still valid.
|
|
int UpdateID;
|
|
|
|
CallerEntry(FunctionInfo *Caller, SILInstruction *FAS, int UpdateID) :
|
|
Caller(Caller), FAS(FAS), UpdateID(UpdateID) { }
|
|
|
|
friend class FunctionInfoBase<FunctionInfo>;
|
|
};
|
|
|
|
private:
|
|
|
|
/// The list of callers which must be invalidated if this function gets
|
|
/// invalidated. Note that the list may contain invalid entries for already
|
|
/// invalidated callers. Those entries are removed lazily in
|
|
/// removeInvalidCallers().
|
|
/// The lazy removal of invalid entries avoids that we additionally need to
|
|
/// store a callee-set. Also it reduces the number of times we need to
|
|
/// iterate through the Callers (in case multiple caller functions are
|
|
/// invalidated at the same time).
|
|
/// Note that Callers can't be a pure hashed set-like container because the
|
|
/// iteration order must be deterministic.
|
|
llvm::SmallVector<CallerEntry, 8> Callers;
|
|
|
|
/// In which update-cycle the analysis was computed for this function.
|
|
/// A 0-value means that the analysis for this function was not computed
|
|
/// yet or got invalidated.
|
|
int UpdateID = 0;
|
|
|
|
/// Special states for StateAndPosition.
|
|
enum {
|
|
NotVisited = -2,
|
|
Visited = -1,
|
|
FirstPosition = 0
|
|
};
|
|
|
|
/// The position in the function schedule, or if not scheduled yet, any of
|
|
/// the special states.
|
|
/// This field is only used during recomputation.
|
|
int StateAndPosition = NotVisited;
|
|
|
|
/// How many callees are not in the function schedule yet.
|
|
/// This field is only used during recomputation.
|
|
int numUnscheduledCallees = 0;
|
|
|
|
|
|
/// Removes invalid caller entries.
|
|
void removeInvalidCallers() {
|
|
Callers.erase(std::remove_if(Callers.begin(), Callers.end(),
|
|
[](const CallerEntry &E) {
|
|
return !E.isValid();
|
|
}), Callers.end());
|
|
}
|
|
|
|
friend class FunctionInfoWorkList<FunctionInfo>;
|
|
friend class BottomUpIPAnalysis;
|
|
|
|
public:
|
|
|
|
/// Returns true if the analysis data for this function is computed and
|
|
/// up-to-date.
|
|
bool isValid() const { return UpdateID != 0; }
|
|
|
|
/// Returns true if this function was already visited during recomputation.
|
|
bool isVisited() const { return StateAndPosition >= Visited; }
|
|
|
|
/// Returns true if this function is already scheduled in the bottom-up
|
|
/// order during recomputation.
|
|
bool isScheduled() const { return StateAndPosition >= FirstPosition; }
|
|
|
|
/// Returns true if this function is scheduled after \p RHS in the bottom-up
|
|
/// order, i.e. is considered as a "caller" of \p RHS.
|
|
bool isScheduledAfter(const FunctionInfo *RHS) const {
|
|
assert(isScheduled() && RHS->isScheduled() &&
|
|
"operating with unscheduled functions?");
|
|
return StateAndPosition > RHS->StateAndPosition;
|
|
}
|
|
|
|
/// Returns the list of caller-entries.
|
|
ArrayRef<CallerEntry> getCallers() const { return Callers; }
|
|
|
|
/// Adds a caller for this function.
|
|
void addCaller(FunctionInfo *CallerInfo, SILInstruction *FAS) {
|
|
Callers.push_back(CallerEntry(CallerInfo, FAS, CallerInfo->UpdateID));
|
|
if (!isScheduled())
|
|
++CallerInfo->numUnscheduledCallees;
|
|
}
|
|
|
|
void addCaller(FunctionInfo *CallerInfo, FullApplySite FAS) {
|
|
addCaller(CallerInfo, FAS.getInstruction());
|
|
}
|
|
};
|
|
|
|
/// Computes and stores a bottom-up function order.
|
|
template<typename FunctionInfo> class BottomUpFunctionOrder {
|
|
|
|
typedef llvm::SmallVector<FunctionInfo *, 8> FunctionInfoList;
|
|
|
|
/// The final bottom-up function order.
|
|
FunctionInfoList Scheduled;
|
|
|
|
/// Functions which are not initially scheduled in the bottom-up order.
|
|
FunctionInfoList InitiallyUnscheduled;
|
|
|
|
/// The number of visited functions.
|
|
unsigned numVisited = 0;
|
|
|
|
/// The BottomUpIPAnalysis::CurrentUpdateID.
|
|
int CurrentUpdateID;
|
|
|
|
public:
|
|
|
|
/// The constructor. The \p CurrentUpdateID is the
|
|
/// BottomUpIPAnalysis::CurrentUpdateID.
|
|
BottomUpFunctionOrder(int CurrentUpdateID) :
|
|
CurrentUpdateID(CurrentUpdateID) {
|
|
assert(CurrentUpdateID > 0 && "did not allocate an UpdateID");
|
|
}
|
|
|
|
~BottomUpFunctionOrder() {
|
|
assert(InitiallyUnscheduled.empty() &&
|
|
"not finished scheduling");
|
|
assert(Scheduled.size() == numVisited &&
|
|
"missed some functions to schedule");
|
|
for (FunctionInfo *FInfo : Scheduled) {
|
|
assert(FInfo->isScheduled() &&
|
|
"function not scheduled at the end of recomputation");
|
|
assert(FInfo->numUnscheduledCallees == 0 &&
|
|
"something basic is broken in scheduling");
|
|
assert(FInfo->isValid() &&
|
|
"function not recomputed at the end of recomputation");
|
|
FInfo->StateAndPosition = FunctionInfoBase<FunctionInfo>::NotVisited;
|
|
}
|
|
}
|
|
|
|
/// Returns the begin of the bottom-up function order.
|
|
typename FunctionInfoList::const_iterator begin() const {
|
|
return Scheduled.begin();
|
|
}
|
|
|
|
/// Returns the end of the bottom-up function order.
|
|
typename FunctionInfoList::const_iterator end() const {
|
|
return Scheduled.end();
|
|
}
|
|
|
|
/// Returns true if the analysis for \p FInfo was recomputed during the
|
|
/// current update-cycle.
|
|
bool wasRecomputedWithCurrentUpdateID(FunctionInfo *FInfo) {
|
|
return FInfo->UpdateID == CurrentUpdateID;
|
|
}
|
|
|
|
/// Should be called when first visiting \p FInfo during recomputation.
|
|
/// Returns true if the analysis is still valid for \p FInfo and doesn't
|
|
/// need to be recomputed.
|
|
bool prepareForVisiting(FunctionInfo *FInfo) {
|
|
assert(!FInfo->isVisited() &&
|
|
"function visited multiple times");
|
|
assert(FInfo->numUnscheduledCallees == 0 &&
|
|
"already added callees at the begin of visiting a function");
|
|
numVisited++;
|
|
FInfo->StateAndPosition = FunctionInfoBase<FunctionInfo>::Visited;
|
|
if (FInfo->isValid()) {
|
|
// Now it's good time to remove invalid caller entries.
|
|
// Note: we don't have to do this for function which are recomputed.
|
|
FInfo->removeInvalidCallers();
|
|
return true;
|
|
}
|
|
InitiallyUnscheduled.push_back(FInfo);
|
|
// Set to valid.
|
|
FInfo->UpdateID = CurrentUpdateID;
|
|
return false;
|
|
}
|
|
|
|
/// Tries to schedule \p FInfo onto the bottom-up function order.
|
|
/// This succeeds if \p FInfo does not have any unscheduled callees.
|
|
/// Should be called after visiting \p FInfo during recomputation.
|
|
void tryToSchedule(FunctionInfo *FInfo) {
|
|
assert(FInfo->isVisited() &&
|
|
"tried to schedule function which was not visited");
|
|
assert(!FInfo->isScheduled() &&
|
|
"function scheduled multiple times");
|
|
if (FInfo->numUnscheduledCallees == 0) {
|
|
FInfo->StateAndPosition = (int)Scheduled.size();
|
|
Scheduled.push_back(FInfo);
|
|
return;
|
|
}
|
|
assert(wasRecomputedWithCurrentUpdateID(FInfo) &&
|
|
"not recomputed function cannot have unscheduled callees");
|
|
}
|
|
|
|
/// Finishes the bottom-up function schedule.
|
|
void finishScheduling() {
|
|
unsigned Idx = 0;
|
|
bool NeedAnotherIteration = false;
|
|
do {
|
|
/// Schedule all functions we can.
|
|
while (Idx < Scheduled.size()) {
|
|
FunctionInfo *FInfo = Scheduled[Idx++];
|
|
for (const auto &E : FInfo->Callers) {
|
|
if (E.Caller->isVisited() && !E.Caller->isScheduled()) {
|
|
E.Caller->numUnscheduledCallees--;
|
|
tryToSchedule(E.Caller);
|
|
}
|
|
}
|
|
}
|
|
NeedAnotherIteration = false;
|
|
// Check if there are still unscheduled functions, which means that
|
|
// there is a cycle in the call graph.
|
|
while (!InitiallyUnscheduled.empty()) {
|
|
FunctionInfo *FInfo = InitiallyUnscheduled.pop_back_val();
|
|
if (!FInfo->isScheduled()) {
|
|
// Break the cycle by scheduling the first unscheduled node we
|
|
// found.
|
|
FInfo->numUnscheduledCallees = 0;
|
|
tryToSchedule(FInfo);
|
|
assert(FInfo->isScheduled() &&
|
|
"really, we should be able to schedule this function");
|
|
NeedAnotherIteration = true;
|
|
break;
|
|
}
|
|
}
|
|
} while (NeedAnotherIteration);
|
|
}
|
|
};
|
|
|
|
BottomUpIPAnalysis(SILAnalysisKind k) : SILAnalysis(k) {}
|
|
|
|
/// Increments the CurrentUpdateID.
|
|
/// Should be called at the beginning of a recomputation.
|
|
void allocNewUpdateID() { ++CurrentUpdateID; }
|
|
|
|
/// Returns the ID of the current update-cycle.
|
|
int getCurrentUpdateID() const { return CurrentUpdateID; }
|
|
|
|
/// Invalidates \p FInfo, including all analysis data which depend on it, i.e.
|
|
/// the callers.
|
|
template<typename FunctionInfo>
|
|
void invalidateIncludingAllCallers(FunctionInfo *FInfo) {
|
|
llvm::SmallVector<FunctionInfo *, 8> WorkList;
|
|
WorkList.push_back(FInfo);
|
|
|
|
while (!WorkList.empty()) {
|
|
FunctionInfo *FInfo = WorkList.pop_back_val();
|
|
for (const auto &E : FInfo->Callers) {
|
|
if (E.isValid() && E.Caller->isValid())
|
|
WorkList.push_back(E.Caller);
|
|
}
|
|
FInfo->clear();
|
|
FInfo->Callers.clear();
|
|
FInfo->UpdateID = 0;
|
|
}
|
|
}
|
|
};
|
|
|
|
} // end namespace swift
|
|
|
|
#endif // SWIFT_SILOPTIMIZER_ANALYSIS_BOTTOMUPIPANALYSIS_H
|