diff options
-rwxr-xr-x | build-js.sh | 1 | ||||
-rw-r--r-- | src/passes/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/passes/DuplicateFunctionElimination.cpp | 48 | ||||
-rw-r--r-- | src/passes/DuplicateImportElimination.cpp | 67 | ||||
-rw-r--r-- | src/passes/opt-utils.h | 51 | ||||
-rw-r--r-- | src/passes/pass.cpp | 4 | ||||
-rw-r--r-- | src/passes/passes.h | 1 | ||||
-rw-r--r-- | test/passes/duplicate-import-elimination.txt | 17 | ||||
-rw-r--r-- | test/passes/duplicate-import-elimination.wast | 14 |
9 files changed, 158 insertions, 46 deletions
diff --git a/build-js.sh b/build-js.sh index 26d5f4466..dd43545b0 100755 --- a/build-js.sh +++ b/build-js.sh @@ -103,6 +103,7 @@ mkdir -p ${OUT} $BINARYEN_SRC/passes/DataFlowOpts.cpp \ $BINARYEN_SRC/passes/DeadCodeElimination.cpp \ $BINARYEN_SRC/passes/Directize.cpp \ + $BINARYEN_SRC/passes/DuplicateImportElimination.cpp \ $BINARYEN_SRC/passes/DuplicateFunctionElimination.cpp \ $BINARYEN_SRC/passes/ExtractFunction.cpp \ $BINARYEN_SRC/passes/Flatten.cpp \ diff --git a/src/passes/CMakeLists.txt b/src/passes/CMakeLists.txt index cb51ea55a..41a6a72a6 100644 --- a/src/passes/CMakeLists.txt +++ b/src/passes/CMakeLists.txt @@ -16,6 +16,7 @@ SET(passes_SOURCES DeadArgumentElimination.cpp DeadCodeElimination.cpp Directize.cpp + DuplicateImportElimination.cpp DuplicateFunctionElimination.cpp ExtractFunction.cpp Flatten.cpp diff --git a/src/passes/DuplicateFunctionElimination.cpp b/src/passes/DuplicateFunctionElimination.cpp index 445a024e4..12da32806 100644 --- a/src/passes/DuplicateFunctionElimination.cpp +++ b/src/passes/DuplicateFunctionElimination.cpp @@ -24,32 +24,12 @@ #include "ir/hashed.h" #include "ir/module-utils.h" #include "ir/utils.h" +#include "opt-utils.h" #include "pass.h" #include "wasm.h" namespace wasm { -struct FunctionReplacer : public WalkerPass<PostWalker<FunctionReplacer>> { - bool isFunctionParallel() override { return true; } - - FunctionReplacer(std::map<Name, Name>* replacements) - : replacements(replacements) {} - - FunctionReplacer* create() override { - return new FunctionReplacer(replacements); - } - - void visitCall(Call* curr) { - auto iter = replacements->find(curr->target); - if (iter != replacements->end()) { - curr->target = iter->second; - } - } - -private: - std::map<Name, Name>* replacements; -}; - struct DuplicateFunctionElimination : public Pass { void run(PassRunner* runner, Module* module) override { // Multiple iterations may be necessary: A and B may be identical only after @@ -117,31 +97,7 @@ struct DuplicateFunctionElimination : public Pass { }), v.end()); module->updateMaps(); - // replace direct calls - FunctionReplacer(&replacements).run(runner, module); - // replace in table - for (auto& segment : module->table.segments) { - for (auto& name : segment.data) { - auto iter = replacements.find(name); - if (iter != replacements.end()) { - name = iter->second; - } - } - } - // replace in start - if (module->start.is()) { - auto iter = replacements.find(module->start); - if (iter != replacements.end()) { - module->start = iter->second; - } - } - // replace in exports - for (auto& exp : module->exports) { - auto iter = replacements.find(exp->value); - if (iter != replacements.end()) { - exp->value = iter->second; - } - } + OptUtils::replaceFunctions(runner, *module, replacements); } else { break; } diff --git a/src/passes/DuplicateImportElimination.cpp b/src/passes/DuplicateImportElimination.cpp new file mode 100644 index 000000000..39b126b6c --- /dev/null +++ b/src/passes/DuplicateImportElimination.cpp @@ -0,0 +1,67 @@ +/* + * Copyright 2019 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. + */ + +// +// Removes duplicate imports. +// +// TODO: non-function imports too +// + +#include "asm_v_wasm.h" +#include "ir/import-utils.h" +#include "opt-utils.h" +#include "pass.h" +#include "wasm.h" + +namespace wasm { + +struct DuplicateImportElimination : public Pass { + void run(PassRunner* runner, Module* module) override { + ImportInfo imports(*module); + std::map<Name, Name> replacements; + std::map<std::pair<Name, Name>, Name> seen; + std::vector<Name> toRemove; + for (auto* func : imports.importedFunctions) { + auto pair = std::make_pair(func->module, func->base); + auto iter = seen.find(pair); + if (iter != seen.end()) { + auto previousName = iter->second; + auto previousFunc = module->getFunction(previousName); + // It is ok to import the same thing with multiple types; we can only + // merge if the types match, of course. + if (getSig(previousFunc) == getSig(func)) { + replacements[func->name] = previousName; + toRemove.push_back(func->name); + continue; + } + } + seen[pair] = func->name; + } + if (!replacements.empty()) { + module->updateMaps(); + OptUtils::replaceFunctions(runner, *module, replacements); + for (auto name : toRemove) { + module->removeFunction(name); + } + } + } +}; + +Pass* createDuplicateImportEliminationPass() { + return new DuplicateImportElimination(); +} + +} // namespace wasm diff --git a/src/passes/opt-utils.h b/src/passes/opt-utils.h index 9ac6da4a2..e9141c4f1 100644 --- a/src/passes/opt-utils.h +++ b/src/passes/opt-utils.h @@ -53,6 +53,57 @@ inline void optimizeAfterInlining(std::unordered_set<Function*>& funcs, module->updateMaps(); } +struct CallTargetReplacer : public WalkerPass<PostWalker<CallTargetReplacer>> { + bool isFunctionParallel() override { return true; } + + CallTargetReplacer(std::map<Name, Name>* replacements) + : replacements(replacements) {} + + CallTargetReplacer* create() override { + return new CallTargetReplacer(replacements); + } + + void visitCall(Call* curr) { + auto iter = replacements->find(curr->target); + if (iter != replacements->end()) { + curr->target = iter->second; + } + } + +private: + std::map<Name, Name>* replacements; +}; + +inline void replaceFunctions(PassRunner* runner, + Module& module, + std::map<Name, Name>& replacements) { + // replace direct calls + CallTargetReplacer(&replacements).run(runner, &module); + // replace in table + for (auto& segment : module.table.segments) { + for (auto& name : segment.data) { + auto iter = replacements.find(name); + if (iter != replacements.end()) { + name = iter->second; + } + } + } + // replace in start + if (module.start.is()) { + auto iter = replacements.find(module.start); + if (iter != replacements.end()) { + module.start = iter->second; + } + } + // replace in exports + for (auto& exp : module.exports) { + auto iter = replacements.find(exp->value); + if (iter != replacements.end()) { + exp->value = iter->second; + } + } +} + } // namespace OptUtils } // namespace wasm diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp index 5d8fb6d61..daa575c6d 100644 --- a/src/passes/pass.cpp +++ b/src/passes/pass.cpp @@ -107,6 +107,9 @@ void PassRegistry::registerPasses() { "directize", "turns indirect calls into direct ones", createDirectizePass); registerPass( "dfo", "optimizes using the DataFlow SSA IR", createDataFlowOptsPass); + registerPass("duplicate-import-elimination", + "removes duplicate imports", + createDuplicateImportEliminationPass); registerPass("duplicate-function-elimination", "removes duplicate functions", createDuplicateFunctionEliminationPass); @@ -412,6 +415,7 @@ void PassRunner::addDefaultGlobalOptimizationPostPasses() { } // optimizations show more functions as duplicate add("duplicate-function-elimination"); + add("duplicate-import-elimination"); add("simplify-globals"); add("remove-unused-module-elements"); add("memory-packing"); diff --git a/src/passes/passes.h b/src/passes/passes.h index d99a5a6f1..115e9bab6 100644 --- a/src/passes/passes.h +++ b/src/passes/passes.h @@ -35,6 +35,7 @@ Pass* createDAEOptimizingPass(); Pass* createDataFlowOptsPass(); Pass* createDeadCodeEliminationPass(); Pass* createDirectizePass(); +Pass* createDuplicateImportEliminationPass(); Pass* createDuplicateFunctionEliminationPass(); Pass* createEmitTargetFeaturesPass(); Pass* createExtractFunctionPass(); diff --git a/test/passes/duplicate-import-elimination.txt b/test/passes/duplicate-import-elimination.txt new file mode 100644 index 000000000..ee90ebfba --- /dev/null +++ b/test/passes/duplicate-import-elimination.txt @@ -0,0 +1,17 @@ +(module + (type $FUNCSIG$v (func)) + (type $FUNCSIG$vi (func (param i32))) + (import "env" "waka" (func $foo)) + (import "env" "waka" (func $wrong (param i32))) + (table $0 2 2 funcref) + (elem (i32.const 0) $foo $foo) + (export "baz" (func $0)) + (start $foo) + (func $0 (; 2 ;) (type $FUNCSIG$v) + (call $foo) + (call $foo) + (call $wrong + (i32.const 1) + ) + ) +) diff --git a/test/passes/duplicate-import-elimination.wast b/test/passes/duplicate-import-elimination.wast new file mode 100644 index 000000000..cd0c9dbf7 --- /dev/null +++ b/test/passes/duplicate-import-elimination.wast @@ -0,0 +1,14 @@ +(module + (import "env" "waka" (func $foo)) + (import "env" "waka" (func $bar)) + (import "env" "waka" (func $wrong (param i32))) + (table 2 2 funcref) + (elem (i32.const 0) $foo $bar) + (start $bar) + (func "baz" + (call $foo) + (call $bar) + (call $wrong (i32.const 1)) + ) +) + |