summaryrefslogtreecommitdiff
path: root/src/passes/OptimizeInstructions.cpp
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2021-04-19 15:22:03 -0700
committerGitHub <noreply@github.com>2021-04-19 15:22:03 -0700
commit069c9b8034f965023a4db0449e6bf6f5215b6199 (patch)
tree435b926a9592790a1fdc948c33b435a8e24d9ff3 /src/passes/OptimizeInstructions.cpp
parentda4e17d827c444bbbcceb08d577c528020d4d959 (diff)
downloadbinaryen-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.cpp46
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) {