/* * Copyright 2015 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. */ #include #include namespace wasm { // PassRegistry PassRegistry* PassRegistry::get() { static PassRegistry* manager = nullptr; if (!manager) { manager = new PassRegistry(); } return manager; } void PassRegistry::registerPass(const char* name, const char *description, Creator create) { assert(passInfos.find(name) == passInfos.end()); passInfos[name] = PassInfo(description, create); } Pass* PassRegistry::createPass(std::string name) { if (passInfos.find(name) == passInfos.end()) return nullptr; auto ret = passInfos[name].create(); ret->name = name; return ret; } std::vector PassRegistry::getRegisteredNames() { std::vector ret; for (auto pair : passInfos) { ret.push_back(pair.first); } return ret; } std::string PassRegistry::getPassDescription(std::string name) { assert(passInfos.find(name) != passInfos.end()); return passInfos[name].description; } // PassRunner void PassRunner::addDefaultOptimizationPasses() { add("duplicate-function-elimination"); add("dce"); add("remove-unused-brs"); add("remove-unused-names"); add("optimize-instructions"); add("precompute"); add("simplify-locals"); add("vacuum"); // previous pass creates garbage add("remove-unused-brs"); // simplify-locals opens opportunities for phi optimizations add("coalesce-locals"); add("vacuum"); // previous pass creates garbage add("reorder-locals"); add("merge-blocks"); add("optimize-instructions"); add("precompute"); add("vacuum"); // should not be needed, last few passes do not create garbage, but just to be safe add("duplicate-function-elimination"); // optimizations show more functions as duplicate } void PassRunner::addDefaultFunctionOptimizationPasses() { add("dce"); add("remove-unused-brs"); add("remove-unused-names"); add("optimize-instructions"); add("precompute"); add("simplify-locals"); add("vacuum"); // previous pass creates garbage add("remove-unused-brs"); // simplify-locals opens opportunities for phi optimizations add("coalesce-locals"); add("vacuum"); // previous pass creates garbage add("reorder-locals"); add("merge-blocks"); add("optimize-instructions"); add("precompute"); add("vacuum"); // should not be needed, last few passes do not create garbage, but just to be safe } void PassRunner::addDefaultGlobalOptimizationPasses() { add("duplicate-function-elimination"); } void PassRunner::run() { if (debug) { // for debug logging purposes, run each pass in full before running the other std::chrono::high_resolution_clock::time_point beforeEverything; size_t padding = 0; std::cerr << "[PassRunner] running passes..." << std::endl; beforeEverything = std::chrono::high_resolution_clock::now(); for (auto pass : passes) { padding = std::max(padding, pass->name.size()); } for (auto* pass : passes) { currPass = pass; std::chrono::high_resolution_clock::time_point before; std::cerr << "[PassRunner] running pass: " << pass->name << "... "; for (size_t i = 0; i < padding - pass->name.size(); i++) { std::cerr << ' '; } before = std::chrono::high_resolution_clock::now(); pass->run(this, wasm); auto after = std::chrono::high_resolution_clock::now(); std::chrono::duration diff = after - before; std::cerr << diff.count() << " seconds." << std::endl; } auto after = std::chrono::high_resolution_clock::now(); std::chrono::duration diff = after - beforeEverything; std::cerr << "[PassRunner] passes took " << diff.count() << " seconds." << std::endl; } else { // non-debug normal mode, run them in an optimal manner - for locality it is better // to run as many passes as possible on a single function before moving to the next std::vector stack; auto flush = [&]() { if (stack.size() > 0) { // run the stack of passes on all the functions, in parallel size_t num = ThreadPool::get()->size(); std::vector> doWorkers; std::atomic nextFunction; nextFunction.store(0); size_t numFunctions = wasm->functions.size(); for (size_t i = 0; i < num; i++) { doWorkers.push_back([&]() { auto index = nextFunction.fetch_add(1); // get the next task, if there is one if (index >= numFunctions) { return ThreadWorkState::Finished; // nothing left } Function* func = this->wasm->functions[index].get(); // do the current task: run all passes on this function for (auto* pass : stack) { runPassOnFunction(pass, func); } if (index + 1 == numFunctions) { return ThreadWorkState::Finished; // we did the last one } return ThreadWorkState::More; }); } ThreadPool::get()->work(doWorkers); } stack.clear(); }; for (auto* pass : passes) { if (pass->isFunctionParallel()) { stack.push_back(pass); } else { flush(); pass->run(this, wasm); } } flush(); } } void PassRunner::runFunction(Function* func) { for (auto* pass : passes) { runPassOnFunction(pass, func); } } PassRunner::~PassRunner() { for (auto pass : passes) { delete pass; } } void PassRunner::runPassOnFunction(Pass* pass, Function* func) { // function-parallel passes get a new instance per function if (pass->isFunctionParallel()) { auto instance = std::unique_ptr(pass->create()); instance->runFunction(this, wasm, func); } else { pass->runFunction(this, wasm, func); } } } // namespace wasm