diff options
author | Alon Zakai <azakai@google.com> | 2024-03-18 14:18:09 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-18 14:18:09 -0700 |
commit | d8086c63a9e3e6bbd1bdc5d7e0843af8433cc4c8 (patch) | |
tree | b46989cd5f1e397b6eda83ca7741a1a5d2716728 /src/passes/param-utils.h | |
parent | c166ca015860b337e9ce07a5e02cb707964056ba (diff) | |
download | binaryen-d8086c63a9e3e6bbd1bdc5d7e0843af8433cc4c8.tar.gz binaryen-d8086c63a9e3e6bbd1bdc5d7e0843af8433cc4c8.tar.bz2 binaryen-d8086c63a9e3e6bbd1bdc5d7e0843af8433cc4c8.zip |
DeadArgumentElimination/SignaturePruning: Prune params even if called with effects (#6395)
Before this PR, when we saw a param was unused we sometimes could not remove it.
For example, if there was one call like this:
(call $target
(call $other)
)
That nested call has effects, so we can't just remove it from the outer call - we'd need to
move it first. That motion was hard to integrate which was why it was left out, but it
turns out that is sometimes very important. E.g. in Java it is common to have such calls
that send the this parameter as the result of another call; not being able to remove such
params meant we kept those nested calls alive, creating empty structs just to have
something to send there.
To fix this, this builds on top of #6394 which makes it easier to move all children out of
a parent, leaving only nested things that can be easily moved around and removed. In
more detail, DeadArgumentElimination/SignaturePruning track whether we run into effects that
prevent removing a field. If we do, then we queue an operation to move the children
out, which we do using a new utility ParamUtils::localizeCallsTo. The pass then does
another iteration after that operation.
Alternatively we could try to move things around immediately, but that is quite hard:
those passes already track a lot of state. It is simpler to do the fixup in an entirely
separate utility. That does come at the cost of the utility doing another pass on the
module and the pass itself running another iteration, but this situation is not the most
common.
Diffstat (limited to 'src/passes/param-utils.h')
-rw-r--r-- | src/passes/param-utils.h | 61 |
1 files changed, 45 insertions, 16 deletions
diff --git a/src/passes/param-utils.h b/src/passes/param-utils.h index 202c8b007..4c458390a 100644 --- a/src/passes/param-utils.h +++ b/src/passes/param-utils.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef wasm_ir_function_h -#define wasm_ir_function_h +#ifndef wasm_pass_param_utils_h +#define wasm_pass_param_utils_h #include "pass.h" #include "support/sorted_vector.h" @@ -44,6 +44,16 @@ namespace wasm::ParamUtils { // } std::unordered_set<Index> getUsedParams(Function* func); +// The outcome of an attempt to remove a parameter(s). +enum RemovalOutcome { + // We removed successfully. + Success = 0, + // We failed, but only because of fixable nested effects. The caller can move + // those effects out (e.g. using ChildLocalizer, or the helper localizeCallsTo + // below) and repeat. + Failure = 1, +}; + // 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 @@ -64,21 +74,26 @@ std::unordered_set<Index> getUsedParams(Function* func); // need adjusting and it is easier to do it all in one place. Also, the caller // can update all the types at once throughout the program after making // multiple calls to removeParameter(). -bool removeParameter(const std::vector<Function*>& funcs, - Index index, - const std::vector<Call*>& calls, - const std::vector<CallRef*>& callRefs, - Module* module, - PassRunner* runner); +RemovalOutcome 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); +// remove them all, and returns which we removed, as well as an indication as +// to whether we might remove more if effects were not in the way (specifically, +// we return Success if we removed any index, Failure if we removed none, and +// FailureDueToEffects if at least one index could have been removed but for +// effects). +std::pair<SortedVector, RemovalOutcome> +removeParameters(const std::vector<Function*>& funcs, + SortedVector indexes, + const std::vector<Call*>& calls, + const std::vector<CallRef*>& callRefs, + Module* module, + PassRunner* runner); // Given a set of functions and the calls and call_refs that reach them, find // which parameters are passed the same constant value in all the calls. For @@ -92,6 +107,20 @@ SortedVector applyConstantValues(const std::vector<Function*>& funcs, const std::vector<CallRef*>& callRefs, Module* module); +// Helper that localizes all calls to a set of targets, in an entire module. +// This basically calls ChildLocalizer in each function, on the relevant calls. +// This is useful when we get FailureDueToEffects, see above. +// +// The set of targets can be function names (the individual functions we want to +// handle calls towards) or heap types (which will then include all functions +// with those types). +void localizeCallsTo(const std::unordered_set<Name>& callTargets, + Module& wasm, + PassRunner* runner); +void localizeCallsTo(const std::unordered_set<HeapType>& callTargets, + Module& wasm, + PassRunner* runner); + } // namespace wasm::ParamUtils -#endif // wasm_ir_function_h +#endif // wasm_pass_param_utils_h |