[caller-analysis] Change the dumping format to be a yaml format and update tests to use that format.

The current dumping format consists of 1 row of information per function. This
will become unweildy to write patterns for when I add additional state to
FunctionInfo.

Instead, this commit converts the dumping format of the caller analysis into a
multi line yaml format. This yaml format looks as follows:

  ---
  calleeName:      closure1
  hasCaller:       false
  minPartialAppliedArgs: 1
  partialAppliers:
    - partial_apply_one_arg
    - partial_apply_two_args1
  fullAppliers:
  ...

This can easily expand over time as we expand the queries that caller analysis
can answer.

As an additional advantage, there are definitely yaml parsers that can handle
multiple yaml documents in sequence in a stream. This means that by running via
sil-opt the caller-analysis-printer pass, one now will get a yaml description of
the caller analysis state, perfect and ready for analysis.
This commit is contained in:
Michael Gottesman
2018-07-12 20:24:02 -07:00
parent 597698091e
commit 3051cd3f60
5 changed files with 598 additions and 163 deletions

View File

@@ -14,9 +14,15 @@
#include "swift/SIL/SILModule.h"
#include "swift/SILOptimizer/Utils/Local.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/YAMLTraits.h"
using namespace swift;
//===----------------------------------------------------------------------===//
// Actual Analysis
//===----------------------------------------------------------------------===//
void CallerAnalysis::processFunctionCallSites(SILFunction *F) {
// Scan the whole module and search Apply sites.
for (auto &BB : *F) {
@@ -53,8 +59,8 @@ void CallerAnalysis::processFunctionCallSites(SILFunction *F) {
}
continue;
}
}
}
}
}
}
void CallerAnalysis::invalidateExistingCalleeRelation(SILFunction *F) {
@@ -66,9 +72,107 @@ void CallerAnalysis::invalidateExistingCalleeRelation(SILFunction *F) {
}
}
//===----------------------------------------------------------------------===//
// CallerAnalysis YAML Dumper
//===----------------------------------------------------------------------===//
namespace {
using llvm::yaml::IO;
using llvm::yaml::MappingTraits;
using llvm::yaml::Output;
using llvm::yaml::ScalarEnumerationTraits;
using llvm::yaml::SequenceTraits;
/// A special struct that marshals call graph state into a form that is easy for
/// llvm's yaml i/o to dump. Its structure is meant to correspond to how the
/// data should be shown by the printer, so naturally it is slightly redundant.
struct YAMLCallGraphNode {
StringRef calleeName;
bool hasCaller;
unsigned minPartialAppliedArgs;
std::vector<StringRef> partialAppliers;
std::vector<StringRef> fullAppliers;
YAMLCallGraphNode() = delete;
~YAMLCallGraphNode() = default;
// This is a data structure that can not be copied or moved.
YAMLCallGraphNode(const YAMLCallGraphNode &) = delete;
YAMLCallGraphNode(YAMLCallGraphNode &&) = delete;
YAMLCallGraphNode &operator=(const YAMLCallGraphNode &) = delete;
YAMLCallGraphNode &operator=(YAMLCallGraphNode &&) = delete;
YAMLCallGraphNode(StringRef calleeName, bool hasCaller,
unsigned minPartialAppliedArgs,
std::vector<StringRef> &&partialAppliers,
std::vector<StringRef> &&fullAppliers)
: calleeName(calleeName), hasCaller(hasCaller),
minPartialAppliedArgs(minPartialAppliedArgs),
partialAppliers(std::move(partialAppliers)),
fullAppliers(std::move(fullAppliers)) {}
};
} // end anonymous namespace
namespace llvm {
namespace yaml {
template <> struct MappingTraits<YAMLCallGraphNode> {
static void mapping(IO &io, YAMLCallGraphNode &func) {
io.mapRequired("calleeName", func.calleeName);
io.mapRequired("hasCaller", func.hasCaller);
io.mapRequired("minPartialAppliedArgs", func.minPartialAppliedArgs);
io.mapRequired("partialAppliers", func.partialAppliers);
io.mapRequired("fullAppliers", func.fullAppliers);
}
};
} // namespace yaml
} // namespace llvm
void CallerAnalysis::dump() const { print(llvm::errs()); }
void CallerAnalysis::print(const char *filePath) const {
using namespace llvm::sys;
std::error_code error;
llvm::raw_fd_ostream fileOutputStream(filePath, error, fs::F_Text);
if (error) {
llvm::errs() << "Failed to open path \"" << filePath << "\" for writing.!";
llvm_unreachable("default error handler");
}
print(fileOutputStream);
}
void CallerAnalysis::print(llvm::raw_ostream &os) const {
llvm::yaml::Output yout(os);
// NOTE: We purposely do not iterate over our internal state here to ensure
// that we dump for all functions and that we dump the state we have stored
// with the functions in module order.
for (auto &f : Mod) {
const auto &fi = getCallerInfo(&f);
std::vector<StringRef> fullAppliers;
for (auto *caller : fi.Callers) {
fullAppliers.push_back(caller->getName());
}
std::vector<StringRef> partialAppliers;
for (auto iter : fi.PartialAppliers) {
partialAppliers.push_back(iter.first->getName());
}
YAMLCallGraphNode node(f.getName(), fi.hasCaller(),
fi.getMinPartialAppliedArgs(),
std::move(partialAppliers), std::move(fullAppliers));
yout << node;
}
}
//===----------------------------------------------------------------------===//
// Main Entry Point
//===----------------------------------------------------------------------===//
SILAnalysis *swift::createCallerAnalysis(SILModule *M) {
return new CallerAnalysis(M);
}