Skip to content

Commit 3051cd3

Browse files
committed
[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.
1 parent 5976980 commit 3051cd3

File tree

5 files changed

+598
-163
lines changed

5 files changed

+598
-163
lines changed

include/swift/SILOptimizer/Analysis/CallerAnalysis.h

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,28 @@ class CallerAnalysis : public SILAnalysis {
119119
/// Notify the analysis about changed witness or vtables.
120120
virtual void invalidateFunctionTables() override { }
121121

122-
const FunctionInfo &getCallerInfo(SILFunction *F) {
122+
const FunctionInfo &getCallerInfo(SILFunction *F) const {
123123
// Recompute every function in the invalidated function list and empty the
124124
// list.
125-
processRecomputeFunctionList();
126-
return FuncInfos[F];
125+
auto *self = const_cast<CallerAnalysis *>(this);
126+
self->processRecomputeFunctionList();
127+
return self->FuncInfos[F];
127128
}
129+
130+
#ifndef NDEBUG
131+
LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED,
132+
"Only for use in the debugger");
133+
#endif
134+
135+
/// Print the state of the caller analysis as a sequence of yaml documents for
136+
/// each callee we are tracking.
137+
void print(llvm::raw_ostream &os) const;
138+
139+
/// Print the state of the caller analysis as a sequence of yaml documents for
140+
/// each callee we are tracking to the passed in file path.
141+
LLVM_ATTRIBUTE_DEPRECATED(void print(const char *filePath)
142+
const LLVM_ATTRIBUTE_USED,
143+
"Only for use in the debugger");
128144
};
129145

130146
/// NOTE: this can be extended to contain the callsites of the function.

lib/SILOptimizer/Analysis/CallerAnalysis.cpp

Lines changed: 106 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,15 @@
1414

1515
#include "swift/SIL/SILModule.h"
1616
#include "swift/SILOptimizer/Utils/Local.h"
17+
#include "llvm/Support/FileSystem.h"
18+
#include "llvm/Support/YAMLTraits.h"
1719

1820
using namespace swift;
1921

22+
//===----------------------------------------------------------------------===//
23+
// Actual Analysis
24+
//===----------------------------------------------------------------------===//
25+
2026
void CallerAnalysis::processFunctionCallSites(SILFunction *F) {
2127
// Scan the whole module and search Apply sites.
2228
for (auto &BB : *F) {
@@ -53,8 +59,8 @@ void CallerAnalysis::processFunctionCallSites(SILFunction *F) {
5359
}
5460
continue;
5561
}
56-
}
57-
}
62+
}
63+
}
5864
}
5965

6066
void CallerAnalysis::invalidateExistingCalleeRelation(SILFunction *F) {
@@ -66,9 +72,107 @@ void CallerAnalysis::invalidateExistingCalleeRelation(SILFunction *F) {
6672
}
6773
}
6874

75+
//===----------------------------------------------------------------------===//
76+
// CallerAnalysis YAML Dumper
77+
//===----------------------------------------------------------------------===//
78+
79+
namespace {
80+
81+
using llvm::yaml::IO;
82+
using llvm::yaml::MappingTraits;
83+
using llvm::yaml::Output;
84+
using llvm::yaml::ScalarEnumerationTraits;
85+
using llvm::yaml::SequenceTraits;
86+
87+
/// A special struct that marshals call graph state into a form that is easy for
88+
/// llvm's yaml i/o to dump. Its structure is meant to correspond to how the
89+
/// data should be shown by the printer, so naturally it is slightly redundant.
90+
struct YAMLCallGraphNode {
91+
StringRef calleeName;
92+
bool hasCaller;
93+
unsigned minPartialAppliedArgs;
94+
std::vector<StringRef> partialAppliers;
95+
std::vector<StringRef> fullAppliers;
96+
97+
YAMLCallGraphNode() = delete;
98+
~YAMLCallGraphNode() = default;
99+
100+
// This is a data structure that can not be copied or moved.
101+
YAMLCallGraphNode(const YAMLCallGraphNode &) = delete;
102+
YAMLCallGraphNode(YAMLCallGraphNode &&) = delete;
103+
YAMLCallGraphNode &operator=(const YAMLCallGraphNode &) = delete;
104+
YAMLCallGraphNode &operator=(YAMLCallGraphNode &&) = delete;
105+
106+
YAMLCallGraphNode(StringRef calleeName, bool hasCaller,
107+
unsigned minPartialAppliedArgs,
108+
std::vector<StringRef> &&partialAppliers,
109+
std::vector<StringRef> &&fullAppliers)
110+
: calleeName(calleeName), hasCaller(hasCaller),
111+
minPartialAppliedArgs(minPartialAppliedArgs),
112+
partialAppliers(std::move(partialAppliers)),
113+
fullAppliers(std::move(fullAppliers)) {}
114+
};
115+
116+
} // end anonymous namespace
117+
118+
namespace llvm {
119+
namespace yaml {
120+
121+
template <> struct MappingTraits<YAMLCallGraphNode> {
122+
static void mapping(IO &io, YAMLCallGraphNode &func) {
123+
io.mapRequired("calleeName", func.calleeName);
124+
io.mapRequired("hasCaller", func.hasCaller);
125+
io.mapRequired("minPartialAppliedArgs", func.minPartialAppliedArgs);
126+
io.mapRequired("partialAppliers", func.partialAppliers);
127+
io.mapRequired("fullAppliers", func.fullAppliers);
128+
}
129+
};
130+
131+
} // namespace yaml
132+
} // namespace llvm
133+
134+
void CallerAnalysis::dump() const { print(llvm::errs()); }
135+
136+
void CallerAnalysis::print(const char *filePath) const {
137+
using namespace llvm::sys;
138+
std::error_code error;
139+
llvm::raw_fd_ostream fileOutputStream(filePath, error, fs::F_Text);
140+
if (error) {
141+
llvm::errs() << "Failed to open path \"" << filePath << "\" for writing.!";
142+
llvm_unreachable("default error handler");
143+
}
144+
print(fileOutputStream);
145+
}
146+
147+
void CallerAnalysis::print(llvm::raw_ostream &os) const {
148+
llvm::yaml::Output yout(os);
149+
150+
// NOTE: We purposely do not iterate over our internal state here to ensure
151+
// that we dump for all functions and that we dump the state we have stored
152+
// with the functions in module order.
153+
for (auto &f : Mod) {
154+
const auto &fi = getCallerInfo(&f);
155+
156+
std::vector<StringRef> fullAppliers;
157+
for (auto *caller : fi.Callers) {
158+
fullAppliers.push_back(caller->getName());
159+
}
160+
std::vector<StringRef> partialAppliers;
161+
for (auto iter : fi.PartialAppliers) {
162+
partialAppliers.push_back(iter.first->getName());
163+
}
164+
165+
YAMLCallGraphNode node(f.getName(), fi.hasCaller(),
166+
fi.getMinPartialAppliedArgs(),
167+
std::move(partialAppliers), std::move(fullAppliers));
168+
yout << node;
169+
}
170+
}
171+
69172
//===----------------------------------------------------------------------===//
70173
// Main Entry Point
71174
//===----------------------------------------------------------------------===//
175+
72176
SILAnalysis *swift::createCallerAnalysis(SILModule *M) {
73177
return new CallerAnalysis(M);
74178
}

lib/SILOptimizer/UtilityPasses/CallerAnalysisPrinter.cpp

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@
1414
//
1515
//===----------------------------------------------------------------------===//
1616

17-
#include "swift/SILOptimizer/Analysis/CallerAnalysis.h"
1817
#include "swift/SIL/SILFunction.h"
1918
#include "swift/SIL/SILModule.h"
19+
#include "swift/SILOptimizer/Analysis/CallerAnalysis.h"
2020
#include "swift/SILOptimizer/PassManager/Transforms.h"
21+
#include "llvm/Support/YAMLTraits.h"
2122
#include "llvm/Support/raw_ostream.h"
2223

2324
using namespace swift;
@@ -26,19 +27,15 @@ using namespace swift;
2627

2728
namespace {
2829

30+
/// A pass that dumps the caller analysis state in yaml form. Intended to allow
31+
/// for visualizing of the caller analysis via external data visualization and
32+
/// analysis programs.
2933
class CallerAnalysisPrinterPass : public SILModuleTransform {
3034
/// The entry point to the transformation.
3135
void run() override {
3236
auto *CA = getAnalysis<CallerAnalysis>();
33-
for (auto &F : *getModule()) {
34-
const CallerAnalysis::FunctionInfo &FI = CA->getCallerInfo(&F);
35-
const char *hasCaller = FI.hasCaller() ? "true" : "false";
36-
llvm::outs() << "Function " << F.getName() << " has caller: "
37-
<< hasCaller << ", partial applied args = "
38-
<< FI.getMinPartialAppliedArgs() << "\n";
39-
}
37+
CA->print(llvm::outs());
4038
}
41-
4239
};
4340

4441
} // end anonymous namespace

0 commit comments

Comments
 (0)