summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/passes/CMakeLists.txt1
-rw-r--r--src/passes/DeadArgumentElimination.cpp111
-rw-r--r--src/passes/param-utils.cpp177
-rw-r--r--src/passes/param-utils.h79
4 files changed, 267 insertions, 101 deletions
diff --git a/src/passes/CMakeLists.txt b/src/passes/CMakeLists.txt
index 29c8cd526..ac3971690 100644
--- a/src/passes/CMakeLists.txt
+++ b/src/passes/CMakeLists.txt
@@ -13,6 +13,7 @@ configure_file(WasmIntrinsics.cpp.in WasmIntrinsics.cpp @ONLY)
FILE(GLOB passes_HEADERS *.h)
set(passes_SOURCES
+ param-utils.cpp
pass.cpp
test_passes.cpp
AlignmentLowering.cpp
diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp
index ec81639b0..ce4d17c74 100644
--- a/src/passes/DeadArgumentElimination.cpp
+++ b/src/passes/DeadArgumentElimination.cpp
@@ -40,11 +40,11 @@
#include "ir/effects.h"
#include "ir/element-utils.h"
#include "ir/find_all.h"
-#include "ir/local-graph.h"
#include "ir/lubs.h"
#include "ir/module-utils.h"
#include "ir/type-updating.h"
#include "ir/utils.h"
+#include "param-utils.h"
#include "pass.h"
#include "passes/opt-utils.h"
#include "support/sorted_vector.h"
@@ -155,41 +155,13 @@ struct DAEScanner
// part of, say if we are exported, or if another parallel function finds a
// RefFunc to us and updates it before we check it).
if (numParams > 0 && !info->hasUnseenCalls) {
- findUnusedParams(func);
- }
- }
-
- void findUnusedParams(Function* func) {
- LocalGraph localGraph(func);
- std::unordered_set<Index> usedParams;
- for (auto& [get, sets] : localGraph.getSetses) {
- if (!func->isParam(get->index)) {
- continue;
- }
-
- // Check if this get of a param index can read from the parameter value
- // passed into the function. We want to ignore values set in the function
- // like this:
- //
- // function foo(x) {
- // x = 10;
- // bar(x); // read of a param index, but not the param value passed in.
- // }
- for (auto* set : sets) {
- // A nullptr value indicates there is no LocalSet* that sets the value,
- // so it must be the parameter value.
- if (!set) {
- usedParams.insert(get->index);
+ auto usedParams = ParamUtils::getUsedParams(func);
+ for (Index i = 0; i < numParams; i++) {
+ if (usedParams.count(i) == 0) {
+ info->unusedParams.insert(i);
}
}
}
-
- // We can now compute the unused params.
- for (Index i = 0; i < numParams; i++) {
- if (usedParams.count(i) == 0) {
- info->unusedParams.insert(i);
- }
- }
}
};
@@ -315,38 +287,11 @@ struct DAE : public Pass {
if (numParams == 0) {
continue;
}
- // Iterate downwards, as we may remove more than one.
- Index i = numParams - 1;
- while (1) {
- if (infoMap[name].unusedParams.has(i)) {
- // Great, it's not used. Check if none of the calls has a param with
- // side effects that we cannot remove (as if we can remove them, we
- // will simply do that when we remove the parameter). Note: flattening
- // the IR beforehand can help here.
- bool callParamsAreValid =
- std::none_of(calls.begin(), calls.end(), [&](Call* call) {
- auto* operand = call->operands[i];
- return EffectAnalyzer(runner->options, *module, operand)
- .hasUnremovableSideEffects();
- });
- // The type must be valid for us to handle as a local (since we
- // replace the parameter with a local).
- // TODO: if there are no references at all, we can avoid creating a
- // local
- bool typeIsValid =
- TypeUpdating::canHandleAsLocal(func->getLocalType(i));
- if (callParamsAreValid && typeIsValid) {
- // Wonderful, nothing stands in our way! Do it.
- // TODO: parallelize this?
- removeParameter(func, i, calls);
- TypeUpdating::handleNonDefaultableLocals(func, *module);
- changed.insert(func);
- }
- }
- if (i == 0) {
- break;
- }
- i--;
+ auto removedIndexes = ParamUtils::removeParameters(
+ {func}, infoMap[name].unusedParams, calls, {}, module, runner);
+ if (!removedIndexes.empty()) {
+ // Success!
+ changed.insert(func);
}
}
// We can also tell which calls have all their return values dropped. Note
@@ -395,42 +340,6 @@ struct DAE : public Pass {
private:
std::unordered_map<Call*, Expression**> allDroppedCalls;
- void removeParameter(Function* func, Index i, std::vector<Call*>& calls) {
- // It's cumbersome to adjust local names - TODO don't clear them?
- Builder::clearLocalNames(func);
- // Remove the parameter from the function. We must add a new local
- // for uses of the parameter, but cannot make it use the same index
- // (in general).
- auto paramsType = func->getParams();
- std::vector<Type> params(paramsType.begin(), paramsType.end());
- auto type = params[i];
- params.erase(params.begin() + i);
- func->setParams(Type(params));
- Index newIndex = Builder::addVar(func, type);
- // Update local operations.
- struct LocalUpdater : public PostWalker<LocalUpdater> {
- Index removedIndex;
- Index newIndex;
- LocalUpdater(Function* func, Index removedIndex, Index newIndex)
- : removedIndex(removedIndex), newIndex(newIndex) {
- walk(func->body);
- }
- void visitLocalGet(LocalGet* curr) { updateIndex(curr->index); }
- void visitLocalSet(LocalSet* curr) { updateIndex(curr->index); }
- void updateIndex(Index& index) {
- if (index == removedIndex) {
- index = newIndex;
- } else if (index > removedIndex) {
- index--;
- }
- }
- } localUpdater(func, i, newIndex);
- // Remove the arguments from the calls.
- for (auto* call : calls) {
- call->operands.erase(call->operands.begin() + i);
- }
- }
-
void
removeReturnValue(Function* func, std::vector<Call*>& calls, Module* module) {
func->setResults(Type::none);
diff --git a/src/passes/param-utils.cpp b/src/passes/param-utils.cpp
new file mode 100644
index 000000000..019df6d7f
--- /dev/null
+++ b/src/passes/param-utils.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2022 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 "ir/function-utils.h"
+#include "ir/local-graph.h"
+#include "ir/type-updating.h"
+#include "support/sorted_vector.h"
+#include "wasm.h"
+
+namespace wasm::ParamUtils {
+
+std::unordered_set<Index> getUsedParams(Function* func) {
+ LocalGraph localGraph(func);
+
+ std::unordered_set<Index> usedParams;
+
+ for (auto& [get, sets] : localGraph.getSetses) {
+ if (!func->isParam(get->index)) {
+ continue;
+ }
+
+ for (auto* set : sets) {
+ // A nullptr value indicates there is no LocalSet* that sets the value,
+ // so it must be the parameter value.
+ if (!set) {
+ usedParams.insert(get->index);
+ }
+ }
+ }
+
+ return usedParams;
+}
+
+bool removeParameter(const std::vector<Function*> funcs,
+ Index index,
+ const std::vector<Call*>& calls,
+ const std::vector<CallRef*>& callRefs,
+ Module* module,
+ PassRunner* runner) {
+ assert(funcs.size() > 0);
+ auto* first = funcs[0];
+#ifndef NDEBUG
+ for (auto* func : funcs) {
+ assert(func->type == first->type);
+ }
+#endif
+
+ // Check if none of the calls has a param with side effects that we cannot
+ // remove (as if we can remove them, we will simply do that when we remove the
+ // parameter). Note: flattening the IR beforehand can help here.
+ bool callParamsAreValid =
+ std::none_of(calls.begin(), calls.end(), [&](Call* call) {
+ auto* operand = call->operands[index];
+ return EffectAnalyzer(runner->options, *module, operand)
+ .hasUnremovableSideEffects();
+ });
+ if (!callParamsAreValid) {
+ return false;
+ }
+
+ // The type must be valid for us to handle as a local (since we
+ // replace the parameter with a local).
+ // TODO: if there are no references at all, we can avoid creating a
+ // local
+ bool typeIsValid = TypeUpdating::canHandleAsLocal(first->getLocalType(index));
+ if (!typeIsValid) {
+ return false;
+ }
+
+ // We can do it!
+
+ // Remove the parameter from the function. We must add a new local
+ // for uses of the parameter, but cannot make it use the same index
+ // (in general).
+ auto paramsType = first->getParams();
+ std::vector<Type> params(paramsType.begin(), paramsType.end());
+ auto type = params[index];
+ params.erase(params.begin() + index);
+ // TODO: parallelize some of these loops?
+ for (auto* func : funcs) {
+ func->setParams(Type(params));
+
+ // It's cumbersome to adjust local names - TODO don't clear them?
+ Builder::clearLocalNames(func);
+ }
+ std::vector<Index> newIndexes;
+ for (auto* func : funcs) {
+ newIndexes.push_back(Builder::addVar(func, type));
+ }
+ // Update local operations.
+ struct LocalUpdater : public PostWalker<LocalUpdater> {
+ Index removedIndex;
+ Index newIndex;
+ LocalUpdater(Function* func, Index removedIndex, Index newIndex)
+ : removedIndex(removedIndex), newIndex(newIndex) {
+ walk(func->body);
+ }
+ void visitLocalGet(LocalGet* curr) { updateIndex(curr->index); }
+ void visitLocalSet(LocalSet* curr) { updateIndex(curr->index); }
+ void updateIndex(Index& index) {
+ if (index == removedIndex) {
+ index = newIndex;
+ } else if (index > removedIndex) {
+ index--;
+ }
+ }
+ };
+ for (Index i = 0; i < funcs.size(); i++) {
+ auto* func = funcs[i];
+ if (!func->imported()) {
+ LocalUpdater(funcs[i], index, newIndexes[i]);
+ TypeUpdating::handleNonDefaultableLocals(func, *module);
+ }
+ }
+
+ // Remove the arguments from the calls.
+ for (auto* call : calls) {
+ call->operands.erase(call->operands.begin() + index);
+ }
+ for (auto* call : callRefs) {
+ call->operands.erase(call->operands.begin() + index);
+ }
+
+ return true;
+}
+
+SortedVector removeParameters(const std::vector<Function*> funcs,
+ SortedVector indexes,
+ const std::vector<Call*>& calls,
+ const std::vector<CallRef*>& callRefs,
+ Module* module,
+ PassRunner* runner) {
+ if (indexes.empty()) {
+ return {};
+ }
+
+ assert(funcs.size() > 0);
+ auto* first = funcs[0];
+#ifndef NDEBUG
+ for (auto* func : funcs) {
+ assert(func->type == first->type);
+ }
+#endif
+
+ // Iterate downwards, as we may remove more than one, and going forwards would
+ // alter the indexes after us.
+ Index i = first->getNumParams() - 1;
+ SortedVector removed;
+ while (1) {
+ if (indexes.has(i)) {
+ if (removeParameter(funcs, i, calls, callRefs, module, runner)) {
+ // Success!
+ removed.insert(i);
+ }
+ }
+ if (i == 0) {
+ break;
+ }
+ i--;
+ }
+ return removed;
+}
+
+} // namespace wasm::ParamUtils
diff --git a/src/passes/param-utils.h b/src/passes/param-utils.h
new file mode 100644
index 000000000..9bfbf1aec
--- /dev/null
+++ b/src/passes/param-utils.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#ifndef wasm_ir_function_h
+#define wasm_ir_function_h
+
+#include "pass.h"
+#include "support/sorted_vector.h"
+#include "wasm.h"
+
+// Helper code for passes that manipulate function parameters, specifically
+// checking if they are used and removing them if so. This is closely tied to
+// the internals of those passes, and so is not in /ir/ (it would be inside the
+// pass .cpp file, but there is more than one).
+
+namespace wasm::ParamUtils {
+
+// Find which parameters are actually used in the function, that is, that the
+// values arriving in the parameter are read. This ignores values set in the
+// function, like this:
+//
+// function foo(x) {
+// x = 10;
+// bar(x); // read of a param index, but not the param value passed in.
+// }
+//
+// This is an actual use:
+//
+// function foo(x) {
+// bar(x); // read of a param value
+// }
+std::unordered_set<Index> getUsedParams(Function* func);
+
+// Try to remove a parameter from a set of functions and replace it with a local
+// instead. This may not succeed if the parameter type cannot be used in a
+// local, or if we hit another limitation, in which case this returns false and
+// does nothing. If we succeed then the parameter is removed both from the
+// functions and from the calls to it, which are passed in (the caller must
+// ensure to pass in all relevant calls and call_refs).
+//
+// This does not check if removing the parameter would change the semantics
+// (say, if the parameter's value is used), which the caller is assumed to do.
+//
+// This assumes that the set of functions all have the same signature. The main
+// use cases are either to send a single function, or to send a set of functions
+// that all have the same heap type (and so if they all do not use some
+// parameter, it can be removed from them all).
+bool removeParameter(const std::vector<Function*> funcs,
+ Index index,
+ const std::vector<Call*>& calls,
+ const std::vector<CallRef*>& callRefs,
+ Module* module,
+ PassRunner* runner);
+
+// The same as removeParameter, but gets a sorted list of indexes. It tries to
+// remove them all, and returns which we removed.
+SortedVector removeParameters(const std::vector<Function*> funcs,
+ SortedVector indexes,
+ const std::vector<Call*>& calls,
+ const std::vector<CallRef*>& callRefs,
+ Module* module,
+ PassRunner* runner);
+
+} // namespace wasm::ParamUtils
+
+#endif // wasm_ir_function_h