| /* | |
| * 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. | |
| */ | |
| // | |
| // Sorts functions by their static use count. This helps reduce the size of wasm | |
| // binaries because fewer bytes are needed to encode references to frequently | |
| // used functions. | |
| // | |
| // This may incur a tradeoff, though, as while it reduces binary size, it may | |
| // increase gzip size. This might be because the new order has the functions in | |
| // a less beneficial position for compression, that is, mutually-compressible | |
| // functions are no longer together (when they were before, in the original | |
| // order, the has some natural tendency one way or the other). TODO: investigate | |
| // similarity ordering here (see #4322) | |
| // | |
| #include <memory> | |
| #include <pass.h> | |
| #include <wasm.h> | |
| namespace wasm { | |
| using NameCountMap = std::unordered_map<Name, std::atomic<Index>>; | |
| struct CallCountScanner : public WalkerPass<PostWalker<CallCountScanner>> { | |
| bool isFunctionParallel() override { return true; } | |
| bool modifiesBinaryenIR() override { return false; } | |
| CallCountScanner(NameCountMap* counts) : counts(counts) {} | |
| std::unique_ptr<Pass> create() override { | |
| return std::make_unique<CallCountScanner>(counts); | |
| } | |
| void visitCall(Call* curr) { | |
| // can't add a new element in parallel | |
| assert(counts->count(curr->target) > 0); | |
| (*counts)[curr->target]++; | |
| } | |
| void visitRefFunc(RefFunc* curr) { | |
| // can't add a new element in parallel | |
| assert(counts->count(curr->func) > 0); | |
| (*counts)[curr->func]++; | |
| } | |
| private: | |
| NameCountMap* counts; | |
| }; | |
| struct ReorderFunctions : public Pass { | |
| // Only reorders functions, does not change their contents. | |
| bool requiresNonNullableLocalFixups() override { return false; } | |
| void run(Module* module) override { | |
| NameCountMap counts; | |
| // fill in info, as we operate on it in parallel (each function to its own | |
| // entry) | |
| for (auto& func : module->functions) { | |
| counts.try_emplace(func->name, 0); | |
| } | |
| // find counts on function calls and ref.funcs in function bodies | |
| CallCountScanner(&counts).run(getPassRunner(), module); | |
| // find counts on ref.funcs in module code: globals, element segments, etc. | |
| CallCountScanner(&counts).walkModuleCode(module); | |
| // find counts on global usages | |
| if (module->start.is()) { | |
| counts[module->start]++; | |
| } | |
| for (auto& curr : module->exports) { | |
| if (curr->kind == ExternalKind::Function) { | |
| counts[*curr->getInternalName()]++; | |
| } | |
| } | |
| // TODO: count the declaration section as well, which adds another mention | |
| // sort | |
| std::sort(module->functions.begin(), | |
| module->functions.end(), | |
| [&counts](const std::unique_ptr<Function>& a, | |
| const std::unique_ptr<Function>& b) -> bool { | |
| if (counts[a->name] == counts[b->name]) { | |
| return a->name > b->name; | |
| } | |
| return counts[a->name] > counts[b->name]; | |
| }); | |
| } | |
| }; | |
| Pass* createReorderFunctionsPass() { return new ReorderFunctions(); } | |
| struct ReorderFunctionsByName : public Pass { | |
| // Only reorders functions, does not change their contents. | |
| bool requiresNonNullableLocalFixups() override { return false; } | |
| void run(Module* module) override { | |
| std::sort(module->functions.begin(), | |
| module->functions.end(), | |
| [](const std::unique_ptr<Function>& a, | |
| const std::unique_ptr<Function>& b) -> bool { | |
| return a->name < b->name; | |
| }); | |
| } | |
| }; | |
| Pass* createReorderFunctionsByNamePass() { | |
| return new ReorderFunctionsByName(); | |
| } | |
| } // namespace wasm |