diff options
author | Alon Zakai <azakai@google.com> | 2021-04-19 15:22:03 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-19 15:22:03 -0700 |
commit | 069c9b8034f965023a4db0449e6bf6f5215b6199 (patch) | |
tree | 435b926a9592790a1fdc948c33b435a8e24d9ff3 /src/passes/OptimizeInstructions.cpp | |
parent | da4e17d827c444bbbcceb08d577c528020d4d959 (diff) | |
download | binaryen-069c9b8034f965023a4db0449e6bf6f5215b6199.tar.gz binaryen-069c9b8034f965023a4db0449e6bf6f5215b6199.tar.bz2 binaryen-069c9b8034f965023a4db0449e6bf6f5215b6199.zip |
[Wasm GC] Optimize reference identity checks (#3814)
* Note that ref.cast has a fallthrough value.
* Optimize ref.eq on identical inputs.
Diffstat (limited to 'src/passes/OptimizeInstructions.cpp')
-rw-r--r-- | src/passes/OptimizeInstructions.cpp | 46 |
1 files changed, 46 insertions, 0 deletions
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 1f55c0a4b..3c29f7042 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -26,6 +26,7 @@ #include <ir/bits.h> #include <ir/cost.h> #include <ir/effects.h> +#include <ir/find_all.h> #include <ir/gc-type-utils.h> #include <ir/literal-utils.h> #include <ir/load-utils.h> @@ -992,6 +993,14 @@ struct OptimizeInstructions } } + void visitRefEq(RefEq* curr) { + // Identical references compare equal. + if (areConsecutiveInputsEqual(curr->left, curr->right)) { + replaceCurrent( + Builder(*getModule()).makeConst(Literal::makeOne(Type::i32))); + } + } + // If an instruction traps on a null input, there is no need for a // ref.as_non_null on that input: we will trap either way (and the binaryen // optimizer does not differentiate traps). @@ -1170,6 +1179,43 @@ private: // Information about our locals std::vector<LocalInfo> localInfo; + // Check if two consecutive inputs to an instruction are equal. As they are + // consecutive, no code can execeute in between them, which simplies the + // problem here (and which is the case we care about in this pass, which does + // simple peephole optimizations - all we care about is a single instruction + // at a time, and its inputs). + bool areConsecutiveInputsEqual(Expression* left, Expression* right) { + // First, check for side effects. If there are any, then we can't even + // assume things like local.get's of the same index being identical. + PassOptions passOptions = getPassOptions(); + if (EffectAnalyzer(passOptions, getModule()->features, left) + .hasSideEffects() || + EffectAnalyzer(passOptions, getModule()->features, right) + .hasSideEffects()) { + return false; + } + // Ignore extraneous things and compare them structurally. + left = Properties::getFallthrough(left, passOptions, getModule()->features); + right = + Properties::getFallthrough(right, passOptions, getModule()->features); + if (!ExpressionAnalyzer::equal(left, right)) { + return false; + } + // Reference equality also needs to check for allocation, which is *not* a + // side effect, but which results in different references: + // repeatedly calling (struct.new $foo)'s output will return different + // results (while i32.const etc. of course does not). + // Note that allocations may have appeared in the original inputs to this + // function, and skipped when we focused on what falls through; since there + // are no side effects, any allocations there cannot reach the fallthrough. + if (left->type.isRef()) { + if (FindAll<StructNew>(left).has() || FindAll<ArrayNew>(left).has()) { + return false; + } + } + return true; + } + // Canonicalizing the order of a symmetric binary helps us // write more concise pattern matching code elsewhere. void canonicalize(Binary* binary) { |