summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/passes/CMakeLists.txt1
-rw-r--r--src/passes/PrintCallGraph.cpp115
-rw-r--r--src/passes/pass.cpp1
-rw-r--r--src/passes/passes.h1
4 files changed, 118 insertions, 0 deletions
diff --git a/src/passes/CMakeLists.txt b/src/passes/CMakeLists.txt
index 3f6574ec4..eadc69e0a 100644
--- a/src/passes/CMakeLists.txt
+++ b/src/passes/CMakeLists.txt
@@ -13,6 +13,7 @@ SET(passes_SOURCES
PostEmscripten.cpp
Precompute.cpp
Print.cpp
+ PrintCallGraph.cpp
RelooperJumpThreading.cpp
RemoveImports.cpp
RemoveMemory.cpp
diff --git a/src/passes/PrintCallGraph.cpp b/src/passes/PrintCallGraph.cpp
new file mode 100644
index 000000000..33fcb3457
--- /dev/null
+++ b/src/passes/PrintCallGraph.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Prints the call graph in .dot format. You can use http://www.graphviz.org/ to view .dot files.
+//
+
+
+#include <memory>
+#include <iomanip>
+#include "wasm.h"
+#include "pass.h"
+#include "ast_utils.h"
+
+namespace wasm {
+
+struct PrintCallGraph : public Pass {
+ void run(PassRunner* runner, Module* module) override {
+ std::ostream &o = std::cout;
+ o << "digraph call {\n";
+ o << " rankdir = LR;\n";
+ o << " subgraph cluster_key {\n";
+ o << " node [shape=box, style=rounded, fontname=courier, fontsize=10];\n";
+ o << " edge [fontname=courier, fontsize=10];\n";
+ o << " label = \"Key\";\n";
+ o << " \"Import\" [style=\"filled, rounded\", fillcolor=\"turquoise\"];\n";
+ o << " \"Export\" [style=\"filled, rounded\", fillcolor=\"gray\"];\n";
+ o << " \"A\" -> \"B\" [style=\"filled, rounded\", label = \"Direct Call\"];\n";
+ o << " \"C\" -> \"D\" [style=\"filled, rounded, dashed\", label = \"Possible Indirect Call\"];\n";
+ o << " }\n\n";
+ o << " node [shape=box,style=rounded, fontname=courier, fontsize=10];\n";
+
+ // Imports Nodes
+ for (auto& curr : module->imports) {
+ if (curr->kind == ExternalKind::Function) {
+ o << " \"" << curr->name << "\" [style=\"filled, rounded\", fillcolor=\"turquoise\"];\n";
+ }
+ }
+
+ // Exports Nodes
+ for (auto& curr : module->exports) {
+ if (curr->kind == ExternalKind::Function) {
+ Function* func = module->getFunction(curr->value);
+ o << " \"" << func->name << "\" [style=\"filled, rounded\", fillcolor=\"gray\"];\n";
+ }
+ }
+
+ struct CallPrinter : public PostWalker<CallPrinter, Visitor<CallPrinter>> {
+ Module *module;
+ Function *currFunction;
+ std::set<Name> visitedTargets; // Used to avoid printing duplicate edges.
+ std::vector<Function*> allIndirectTargets;
+ CallPrinter(Module *module) : module(module) {
+ // Gather targets of indirect calls.
+ for (auto& segment : module->table.segments) {
+ for (auto& curr : segment.data) {
+ allIndirectTargets.push_back(module->getFunction(curr));
+ }
+ }
+ // Walk function bodies.
+ for (auto& func : module->functions) {
+ currFunction = func.get();
+ std::cout << " \"" << func->name << "\";\n";
+ visitedTargets.clear();
+ walk(func.get()->body);
+ }
+ }
+ void visitCall(Call *curr) {
+ auto* target = module->getFunction(curr->target);
+ if (visitedTargets.count(target->name) > 0) return;
+ visitedTargets.insert(target->name);
+ std::cout << " \"" << currFunction->name << "\" -> \"" << target->name << "\"; // call\n";
+ }
+ void visitCallImport(CallImport *curr) {
+ auto name = curr->target;
+ if (visitedTargets.count(name) > 0) return;
+ visitedTargets.insert(name);
+ std::cout << " \"" << currFunction->name << "\" -> \"" << name << "\"; // callImport\n";
+ }
+ void visitCallIndirect(CallIndirect *curr) {
+ // Find eligible indirect targets.
+ auto* currType = module->getFunctionType(curr->fullType);
+ for (auto& target : allIndirectTargets) {
+ auto* targetType = module->getFunctionType(target->type);
+ if (targetType->structuralComparison(*currType)) continue;
+ auto name = target->name;
+ if (visitedTargets.count(name) > 0) continue;
+ visitedTargets.insert(name);
+ std::cout << " \"" << currFunction->name << "\" -> \"" << name << "\" [style=\"dashed\"]; // callIndirect\n";
+ }
+ }
+ };
+ CallPrinter printer(module);
+ o << "}\n";
+ }
+};
+
+Pass *createPrintCallGraphPass() {
+ return new PrintCallGraph();
+}
+
+} // namespace wasm
diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp
index 3ae0819ea..42e74fbb7 100644
--- a/src/passes/pass.cpp
+++ b/src/passes/pass.cpp
@@ -78,6 +78,7 @@ void PassRegistry::registerPasses() {
registerPass("print", "print in s-expression format", createPrinterPass);
registerPass("print-minified", "print in minified s-expression format", createMinifiedPrinterPass);
registerPass("print-full", "print in full s-expression format", createFullPrinterPass);
+ registerPass("print-call-graph", "print call graph", createPrintCallGraphPass);
registerPass("relooper-jump-threading", "thread relooper jumps (fastcomp output only)", createRelooperJumpThreadingPass);
registerPass("remove-imports", "removes imports and replaces them with nops", createRemoveImportsPass);
registerPass("remove-memory", "removes memory segments", createRemoveMemoryPass);
diff --git a/src/passes/passes.h b/src/passes/passes.h
index 13193bc4c..5bc221ee5 100644
--- a/src/passes/passes.h
+++ b/src/passes/passes.h
@@ -38,6 +38,7 @@ Pass *createNameManagerPass();
Pass *createOptimizeInstructionsPass();
Pass *createPostEmscriptenPass();
Pass *createPrinterPass();
+Pass *createPrintCallGraphPass();
Pass *createRelooperJumpThreadingPass();
Pass *createRemoveImportsPass();
Pass *createRemoveMemoryPass();