diff options
-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 | ||||
-rw-r--r-- | test/passes/func-metrics.txt | 93 | ||||
-rw-r--r-- | test/passes/func-metrics.wast | 56 | ||||
-rw-r--r-- | test/passes/metrics.txt | 6 |
8 files changed, 239 insertions, 27 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); diff --git a/test/passes/func-metrics.txt b/test/passes/func-metrics.txt new file mode 100644 index 000000000..c9b77eeaa --- /dev/null +++ b/test/passes/func-metrics.txt @@ -0,0 +1,93 @@ +global + [funcs] : 3 + [memory-data] : 9 + [table-data] : 3 + [total] : 18 + const : 3 +func: empty + [binary-bytes] : 3 + [total] : 4 + [vars] : 0 + nop : 1 +func: small + [binary-bytes] : 9 + [total] : 14 + [vars] : 0 + block : 1 + const : 1 + drop : 1 + nop : 1 + return : 1 +func: ifs + [binary-bytes] : 51 + [total] : 76 + [vars] : 1 + binary : 1 + block : 1 + const : 12 + drop : 6 + if : 4 +(module + (type $0 (func (param i32))) + (type $1 (func)) + (global $glob i32 (i32.const 1337)) + (table 256 256 anyfunc) + (elem (i32.const 0) $ifs $ifs $ifs) + (memory $0 256 256) + (data (i32.const 0) "\ff\ef\0f\1f 0@P\99") + (func $empty (; 0 ;) (type $1) + (nop) + ) + (func $small (; 1 ;) (type $1) + (nop) + (drop + (i32.const 100421) + ) + (return) + ) + (func $ifs (; 2 ;) (type $0) (param $x i32) + (local $y f32) + (block $block0 + (if + (i32.const 0) + (drop + (i32.const 1) + ) + ) + (if + (i32.const 0) + (drop + (i32.const 1) + ) + (drop + (i32.const 2) + ) + ) + (if + (i32.const 4) + (drop + (i32.const 5) + ) + (drop + (i32.const 6) + ) + ) + (drop + (i32.eq + (if (result i32) + (i32.const 4) + (i32.const 5) + (i32.const 6) + ) + (i32.const 177) + ) + ) + ) + ) +) +global + [funcs] : 0 + [total] : 0 +(module + (memory $0 0) +) diff --git a/test/passes/func-metrics.wast b/test/passes/func-metrics.wast new file mode 100644 index 000000000..9c2f35a81 --- /dev/null +++ b/test/passes/func-metrics.wast @@ -0,0 +1,56 @@ +(module + (memory 256 256) + (table 256 256 anyfunc) + (elem (i32.const 0) $ifs $ifs $ifs) + (data (i32.const 0) "\ff\ef\0f\1f\20\30\40\50\99") + (type $0 (func (param i32))) + (global $glob i32 (i32.const 1337)) + (func $empty) + (func $small + (nop) + (drop (i32.const 100421)) + (return) + ) + (func $ifs (type $0) (param $x i32) + (local $y f32) + (block $block0 + (if + (i32.const 0) + (drop + (i32.const 1) + ) + ) + (if + (i32.const 0) + (drop + (i32.const 1) + ) + (drop + (i32.const 2) + ) + ) + (if + (i32.const 4) + (drop + (i32.const 5) + ) + (drop + (i32.const 6) + ) + ) + (drop + (i32.eq + (if (result i32) + (i32.const 4) + (i32.const 5) + (i32.const 6) + ) + (i32.const 177) + ) + ) + ) + ) +) +;; module with no table or memory or anything for that matter +(module +) diff --git a/test/passes/metrics.txt b/test/passes/metrics.txt index 9041f36fe..f2670c550 100644 --- a/test/passes/metrics.txt +++ b/test/passes/metrics.txt @@ -1,8 +1,8 @@ -Counts +total [funcs] : 1 [memory-data] : 9 [table-data] : 3 - [total] : 27 + [total] : 41 [vars] : 1 binary : 1 block : 1 @@ -56,7 +56,7 @@ Counts ) ) ) -Counts +total [funcs] : 0 [total] : 0 [vars] : 0 |