diff options
author | Alon Zakai <alonzakai@gmail.com> | 2018-01-12 11:58:33 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-01-12 11:58:33 -0800 |
commit | d9ef39df1139837df5d7f7294ca45b62e3e282aa (patch) | |
tree | d20c13fbfa4ee929b1ded6dce0ebd0f3cde0225e /src | |
parent | 2bdd116719017bf13d8d4bda37b81be1baf13b37 (diff) | |
download | binaryen-d9ef39df1139837df5d7f7294ca45b62e3e282aa.tar.gz binaryen-d9ef39df1139837df5d7f7294ca45b62e3e282aa.tar.bz2 binaryen-d9ef39df1139837df5d7f7294ca45b62e3e282aa.zip |
Function metrics pass (#1353)
Emits binary size and opcode counts for each function, which helps investigating what's taking up space in a wasm binary.
Diffstat (limited to 'src')
-rw-r--r-- | src/passes/Metrics.cpp | 95 | ||||
-rw-r--r-- | src/passes/pass.cpp | 1 | ||||
-rw-r--r-- | src/passes/passes.h | 1 | ||||
-rw-r--r-- | src/wasm-binary.h | 13 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 1 |
5 files changed, 87 insertions, 24 deletions
diff --git a/src/passes/Metrics.cpp b/src/passes/Metrics.cpp index fe14135a4..822596cb3 100644 --- a/src/passes/Metrics.cpp +++ b/src/passes/Metrics.cpp @@ -19,6 +19,7 @@ #include <pass.h> #include <support/colors.h> #include <wasm.h> +#include <wasm-binary.h> namespace wasm { @@ -26,6 +27,10 @@ using namespace std; // Prints metrics between optimization passes. struct Metrics : public WalkerPass<PostWalker<Metrics, UnifiedExpressionVisitor<Metrics>>> { + bool byFunction; + + Metrics(bool byFunction) : byFunction(byFunction) {} + static Metrics *lastMetricsPass; map<const char *, int> counts; @@ -35,28 +40,25 @@ struct Metrics : public WalkerPass<PostWalker<Metrics, UnifiedExpressionVisitor< counts[name]++; } - void visitModule(Module* module) { - ostream &o = cout; - o << "Counts" - << "\n"; - vector<const char*> keys; - int total = 0; - for (auto i : counts) { - keys.push_back(i.first); - total += i.second; + void doWalkModule(Module* module) { + // global things + + for (auto& curr : module->functionTypes) { + visitFunctionType(curr.get()); } - // add total - keys.push_back("[total]"); - counts["[total]"] = total; - // add vars - size_t vars = 0; - for (auto& func : module->functions) { - vars += func->getNumVars(); + for (auto& curr : module->imports) { + visitImport(curr.get()); + } + for (auto& curr : module->exports) { + visitExport(curr.get()); } - keys.push_back("[vars]"); - counts["[vars]"] = vars; + for (auto& curr : module->globals) { + walkGlobal(curr.get()); + } + walkTable(&module->table); + walkMemory(&module->memory); + // add functions - keys.push_back("[funcs]"); counts["[funcs]"] = module->functions.size(); // add memory and table if (module->memory.exists) { @@ -64,7 +66,6 @@ struct Metrics : public WalkerPass<PostWalker<Metrics, UnifiedExpressionVisitor< for (auto& segment: module->memory.segments) { size += segment.data.size(); } - keys.push_back("[memory-data]"); counts["[memory-data]"] = size; } if (module->table.exists) { @@ -72,13 +73,58 @@ struct Metrics : public WalkerPass<PostWalker<Metrics, UnifiedExpressionVisitor< for (auto& segment: module->table.segments) { size += segment.data.size(); } - keys.push_back("[table-data]"); counts["[table-data]"] = size; } + + if (byFunction) { + // print global + printCounts("global"); + // compute binary info, so we know function sizes + BufferWithRandomAccess buffer; + WasmBinaryWriter writer(module, buffer); + writer.write(); + // print for each function + for (Index i = 0; i < module->functions.size(); i++) { + auto* func = module->functions[i].get(); + counts.clear(); + walkFunction(func); + counts["[vars]"] = func->getNumVars(); + counts["[binary-bytes]"] = writer.tableOfContents.functionBodies[i].size; + printCounts(std::string("func: ") + func->name.str); + } + // can't comapre detailed info between passes yet + lastMetricsPass = nullptr; + } else { + // add function info + size_t vars = 0; + for (auto& func : module->functions) { + walkFunction(func.get()); + vars += func->getNumVars(); + } + counts["[vars]"] = vars; + // print + printCounts("total"); + // compare to next time + lastMetricsPass = this; + } + } + + void printCounts(std::string title) { + ostream &o = cout; + vector<const char*> keys; + // add total + int total = 0; + for (auto i : counts) { + keys.push_back(i.first); + total += i.second; + } + keys.push_back("[total]"); + counts["[total]"] = total; // sort sort(keys.begin(), keys.end(), [](const char* a, const char* b) -> bool { return strcmp(b, a) > 0; }); + o << title << "\n"; for (auto* key : keys) { auto value = counts[key]; o << " " << left << setw(15) << key << ": " << setw(8) @@ -101,12 +147,15 @@ struct Metrics : public WalkerPass<PostWalker<Metrics, UnifiedExpressionVisitor< } o << "\n"; } - lastMetricsPass = this; } }; Pass *createMetricsPass() { - return new Metrics(); + return new Metrics(false); +} + +Pass *createFunctionMetricsPass() { + return new Metrics(true); } Metrics *Metrics::lastMetricsPass; diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp index 2820b59e5..88734e85a 100644 --- a/src/passes/pass.cpp +++ b/src/passes/pass.cpp @@ -73,6 +73,7 @@ void PassRegistry::registerPasses() { registerPass("duplicate-function-elimination", "removes duplicate functions", createDuplicateFunctionEliminationPass); registerPass("extract-function", "leaves just one function (useful for debugging)", createExtractFunctionPass); registerPass("flatten", "flattens out code, removing nesting", createFlattenPass); + registerPass("func-metrics", "reports function metrics", createFunctionMetricsPass); registerPass("inlining", "inlines functions", createInliningPass); registerPass("inlining-optimizing", "inlines functions and optimizes where we inlined", createInliningOptimizingPass); registerPass("legalize-js-interface", "legalizes i64 types on the import/export boundary", createLegalizeJSInterfacePass); diff --git a/src/passes/passes.h b/src/passes/passes.h index 081f7e203..230cdfd86 100644 --- a/src/passes/passes.h +++ b/src/passes/passes.h @@ -32,6 +32,7 @@ Pass* createDuplicateFunctionEliminationPass(); Pass* createExtractFunctionPass(); Pass* createFlattenPass(); Pass* createFullPrinterPass(); +Pass* createFunctionMetricsPass(); Pass* createI64ToI32LoweringPass(); Pass* createInliningPass(); Pass* createInliningOptimizingPass(); diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 12aecfeaa..813404895 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -666,10 +666,21 @@ class WasmBinaryWriter : public Visitor<WasmBinaryWriter, void> { void prepare(); public: - WasmBinaryWriter(Module* input, BufferWithRandomAccess& o, bool debug) : wasm(input), o(o), debug(debug) { + WasmBinaryWriter(Module* input, BufferWithRandomAccess& o, bool debug = false) : wasm(input), o(o), debug(debug) { prepare(); } + // locations in the output binary for the various parts of the module + struct TableOfContents { + struct Entry { + Name name; + size_t offset; // where the entry starts + size_t size; // the size of the entry + Entry(Name name, size_t offset, size_t size) : name(name), offset(offset), size(size) {} + }; + std::vector<Entry> functionBodies; + } tableOfContents; + void setNamesSection(bool set) { debugInfo = set; } void setSourceMap(std::ostream* set, std::string url) { sourceMap = set; diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 1a7ab6267..514b489a9 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -287,6 +287,7 @@ void WasmBinaryWriter::writeFunctions() { std::move(&o[start], &o[start] + size, &o[sizePos] + sizeFieldSize); o.resize(o.size() - (MaxLEB32Bytes - sizeFieldSize)); } + tableOfContents.functionBodies.emplace_back(function->name, sizePos + sizeFieldSize, size); } currFunction = nullptr; finishSection(start); |