summaryrefslogtreecommitdiff
path: root/src/passes/GUFA.cpp
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2022-09-26 13:49:50 -0700
committerGitHub <noreply@github.com>2022-09-26 20:49:50 +0000
commitb23b47d54a2fb02b048e7eb8de8043358599ec8b (patch)
treea0df6c52cae239318893b069172674145903598f /src/passes/GUFA.cpp
parentb40fa4a885f219d8f317f125880aa0ee9f46b62f (diff)
downloadbinaryen-b23b47d54a2fb02b048e7eb8de8043358599ec8b.tar.gz
binaryen-b23b47d54a2fb02b048e7eb8de8043358599ec8b.tar.bz2
binaryen-b23b47d54a2fb02b048e7eb8de8043358599ec8b.zip
[GUFA] Infer a RefEq value of 0 when possible (#5081)
If the PossibleContents for the two sides have no possible intersection then the result must be 0.
Diffstat (limited to 'src/passes/GUFA.cpp')
-rw-r--r--src/passes/GUFA.cpp53
1 files changed, 52 insertions, 1 deletions
diff --git a/src/passes/GUFA.cpp b/src/passes/GUFA.cpp
index 1467b1312..53693420e 100644
--- a/src/passes/GUFA.cpp
+++ b/src/passes/GUFA.cpp
@@ -69,6 +69,34 @@ struct GUFAOptimizer
bool optimized = false;
+ // As we optimize, we replace expressions and create new ones. For new ones
+ // we can infer their contents based on what they replaced, e.g., if we
+ // replaced a local.get with a const, then the PossibleContents of the const
+ // are the same as the local.get (in this simple example, we could also just
+ // infer them from the const itself, of course). Rather than update the
+ // ContentOracle with new contents, which is a shared object among threads,
+ // each function-parallel worker stores a map of new things it created to the
+ // contents for them.
+ std::unordered_map<Expression*, PossibleContents> newContents;
+
+ Expression* replaceCurrent(Expression* rep) {
+ newContents[rep] = oracle.getContents(getCurrent());
+
+ return WalkerPass<
+ PostWalker<GUFAOptimizer,
+ UnifiedExpressionVisitor<GUFAOptimizer>>>::replaceCurrent(rep);
+ }
+
+ const PossibleContents getContents(Expression* curr) {
+ // If this is something we added ourselves, use that; otherwise the info is
+ // in the oracle.
+ if (auto iter = newContents.find(curr); iter != newContents.end()) {
+ return iter->second;
+ }
+
+ return oracle.getContents(curr);
+ }
+
void visitExpression(Expression* curr) {
// Skip things we can't improve in any way.
auto type = curr->type;
@@ -91,7 +119,7 @@ struct GUFAOptimizer
// Ok, this is an interesting location that we might optimize. See what the
// oracle says is possible there.
- auto contents = oracle.getContents(ExpressionLocation{curr, 0});
+ auto contents = getContents(curr);
auto& options = getPassOptions();
auto& wasm = *getModule();
@@ -182,6 +210,29 @@ struct GUFAOptimizer
}
}
+ void visitRefEq(RefEq* curr) {
+ if (curr->type == Type::unreachable) {
+ // Leave this for DCE.
+ return;
+ }
+
+ auto leftContents = getContents(curr->left);
+ auto rightContents = getContents(curr->right);
+
+ if (!PossibleContents::haveIntersection(leftContents, rightContents)) {
+ // The contents prove the two sides cannot contain the same reference, so
+ // we infer 0.
+ //
+ // Note that this is fine even if one of the sides is None. In that case,
+ // no value is possible there, and the intersection is empty, so we will
+ // get here and emit a 0. That 0 will never be reached as the None child
+ // will be turned into an unreachable, so it does not cause any problem.
+ auto* result = Builder(*getModule()).makeConst(Literal(int32_t(0)));
+ replaceCurrent(getDroppedChildrenAndAppend(
+ curr, *getModule(), getPassOptions(), result));
+ }
+ }
+
// TODO: If an instruction would trap on null, like struct.get, we could
// remove it here if it has no possible contents and if we are in
// traps-never-happen mode (that is, we'd have proven it can only trap,