summaryrefslogtreecommitdiff
path: root/src/passes/OptimizeInstructions.cpp
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2021-09-20 10:47:14 -0700
committerGitHub <noreply@github.com>2021-09-20 17:47:14 +0000
commit737c22d30798c491eea3b401b948b9327ac979de (patch)
treef75a72adbd81a85eca19b732378837670c828b23 /src/passes/OptimizeInstructions.cpp
parentb5e8c371001de20128453d5064ac0422d481020e (diff)
downloadbinaryen-737c22d30798c491eea3b401b948b9327ac979de.tar.gz
binaryen-737c22d30798c491eea3b401b948b9327ac979de.tar.bz2
binaryen-737c22d30798c491eea3b401b948b9327ac979de.zip
[Wasm GC] Add static variants of ref.test, ref.cast, and br_on_cast* (#4163)
These variants take a HeapType that is the type we intend to cast to, and do not take an RTT. These are intended to be more statically optimizable. For now though this PR just implements the minimum to get them parsing and to get through the optimizer without crashing. Spec: https://docs.google.com/document/d/1afthjsL_B9UaMqCA5ekgVmOm75BVFu6duHNsN9-gnXw/edit# See #4149
Diffstat (limited to 'src/passes/OptimizeInstructions.cpp')
-rw-r--r--src/passes/OptimizeInstructions.cpp199
1 files changed, 105 insertions, 94 deletions
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp
index b9b426466..cc263d228 100644
--- a/src/passes/OptimizeInstructions.cpp
+++ b/src/passes/OptimizeInstructions.cpp
@@ -1341,96 +1341,103 @@ struct OptimizeInstructions
Builder builder(*getModule());
auto passOptions = getPassOptions();
- auto fallthrough =
- Properties::getFallthrough(curr->ref, getPassOptions(), *getModule());
-
- // If the value is a null, it will just flow through, and we do not need the
- // cast. However, if that would change the type, then things are less
- // simple: if the original type was non-nullable, replacing it with a null
- // would change the type, which can happen in e.g.
- // (ref.cast (ref.as_non_null (.. (ref.null)
- if (fallthrough->is<RefNull>()) {
- // Replace the expression with drops of the inputs, and a null. Note that
- // we provide a null of the type the outside expects - that of the rtt,
- // which is what was cast to.
- Expression* rep =
- builder.makeBlock({builder.makeDrop(curr->ref),
- builder.makeDrop(curr->rtt),
- builder.makeRefNull(curr->rtt->type.getHeapType())});
- if (curr->ref->type.isNonNullable()) {
- // Avoid a type change by forcing to be non-nullable. In practice, this
- // would have trapped before we get here, so this is just for
- // validation.
- rep = builder.makeRefAs(RefAsNonNull, rep);
- }
- replaceCurrent(rep);
- return;
- // TODO: The optimal ordering of this and the other ref.as_non_null stuff
- // later down in this functions is unclear and may be worth looking
- // into.
- }
-
- // For the cast to be able to succeed, the value being cast must be a
- // subtype of the desired type, as RTT subtyping is a subset of static
- // subtyping. For example, trying to cast an array to a struct would be
- // incompatible.
- if (!canBeCastTo(curr->ref->type.getHeapType(),
- curr->rtt->type.getHeapType())) {
- // This cast cannot succeed. If the input is not a null, it will
- // definitely trap.
- if (fallthrough->type.isNonNullable()) {
- // Our type will now be unreachable; update the parents.
- refinalize = true;
- replaceCurrent(builder.makeBlock({builder.makeDrop(curr->ref),
- builder.makeDrop(curr->rtt),
- builder.makeUnreachable()}));
- return;
- }
- // Otherwise, we are not sure what it is, and need to wait for runtime to
- // see if it is a null or not. (We've already handled the case where we
- // can see the value is definitely a null at compile time, earlier.)
- }
-
- if (passOptions.ignoreImplicitTraps || passOptions.trapsNeverHappen) {
- // Aside from the issue of type incompatibility as mentioned above, the
- // cast can trap if the types *are* compatible but it happens to be the
- // case at runtime that the value is not of the desired subtype. If we
- // do not consider such traps possible, we can ignore that. Note, though,
- // that we cannot do this if we cannot replace the current type with the
- // reference's type.
- if (HeapType::isSubType(curr->ref->type.getHeapType(),
- curr->rtt->type.getHeapType())) {
- replaceCurrent(getResultOfFirst(curr->ref,
- builder.makeDrop(curr->rtt),
- getFunction(),
- getModule(),
- passOptions));
+ // TODO: If no rtt, this is a static cast, and if the type matches then it
+ // will definitely succeed. The opts below could be expanded for that.
+ if (curr->rtt) {
+
+ auto fallthrough =
+ Properties::getFallthrough(curr->ref, getPassOptions(), *getModule());
+
+ // If the value is a null, it will just flow through, and we do not need
+ // the cast. However, if that would change the type, then things are less
+ // simple: if the original type was non-nullable, replacing it with a null
+ // would change the type, which can happen in e.g.
+ // (ref.cast (ref.as_non_null (.. (ref.null)
+ if (fallthrough->is<RefNull>()) {
+ // Replace the expression with drops of the inputs, and a null. Note
+ // that we provide a null of the type the outside expects - that of the
+ // rtt, which is what was cast to.
+ Expression* rep = builder.makeBlock(
+ {builder.makeDrop(curr->ref),
+ builder.makeDrop(curr->rtt),
+ builder.makeRefNull(curr->rtt->type.getHeapType())});
+ if (curr->ref->type.isNonNullable()) {
+ // Avoid a type change by forcing to be non-nullable. In practice,
+ // this would have trapped before we get here, so this is just for
+ // validation.
+ rep = builder.makeRefAs(RefAsNonNull, rep);
+ }
+ replaceCurrent(rep);
return;
+ // TODO: The optimal ordering of this and the other ref.as_non_null
+ // stuff later down in this functions is unclear and may be worth
+ // looking into.
+ }
+
+ // For the cast to be able to succeed, the value being cast must be a
+ // subtype of the desired type, as RTT subtyping is a subset of static
+ // subtyping. For example, trying to cast an array to a struct would be
+ // incompatible.
+ if (!canBeCastTo(curr->ref->type.getHeapType(),
+ curr->rtt->type.getHeapType())) {
+ // This cast cannot succeed. If the input is not a null, it will
+ // definitely trap.
+ if (fallthrough->type.isNonNullable()) {
+ // Our type will now be unreachable; update the parents.
+ refinalize = true;
+ replaceCurrent(builder.makeBlock({builder.makeDrop(curr->ref),
+ builder.makeDrop(curr->rtt),
+ builder.makeUnreachable()}));
+ return;
+ }
+ // Otherwise, we are not sure what it is, and need to wait for runtime
+ // to see if it is a null or not. (We've already handled the case where
+ // we can see the value is definitely a null at compile time, earlier.)
+ }
+
+ if (passOptions.ignoreImplicitTraps || passOptions.trapsNeverHappen) {
+ // Aside from the issue of type incompatibility as mentioned above, the
+ // cast can trap if the types *are* compatible but it happens to be the
+ // case at runtime that the value is not of the desired subtype. If we
+ // do not consider such traps possible, we can ignore that. Note,
+ // though, that we cannot do this if we cannot replace the current type
+ // with the reference's type.
+ if (HeapType::isSubType(curr->ref->type.getHeapType(),
+ curr->rtt->type.getHeapType())) {
+ replaceCurrent(getResultOfFirst(curr->ref,
+ builder.makeDrop(curr->rtt),
+ getFunction(),
+ getModule(),
+ passOptions));
+ return;
+ }
}
- }
- // Repeated identical ref.cast operations are unnecessary, if using the
- // exact same rtt - the result will be the same. Find the immediate child
- // cast, if there is one, and see if it is identical.
- // TODO: Look even further through incompatible casts?
- auto* ref = curr->ref;
- while (!ref->is<RefCast>()) {
- auto* last = ref;
- // RefCast falls through the value, so instead of calling getFallthrough()
- // to look through all fallthroughs, we must iterate manually. Keep going
- // until we reach either the end of things falling-through, or a cast.
- ref = Properties::getImmediateFallthrough(ref, passOptions, *getModule());
- if (ref == last) {
- break;
+ // Repeated identical ref.cast operations are unnecessary, if using the
+ // exact same rtt - the result will be the same. Find the immediate child
+ // cast, if there is one, and see if it is identical.
+ // TODO: Look even further through incompatible casts?
+ auto* ref = curr->ref;
+ while (!ref->is<RefCast>()) {
+ auto* last = ref;
+ // RefCast falls through the value, so instead of calling
+ // getFallthrough() to look through all fallthroughs, we must iterate
+ // manually. Keep going until we reach either the end of things
+ // falling-through, or a cast.
+ ref =
+ Properties::getImmediateFallthrough(ref, passOptions, *getModule());
+ if (ref == last) {
+ break;
+ }
}
- }
- if (auto* child = ref->dynCast<RefCast>()) {
- // Check if the casts are identical.
- if (ExpressionAnalyzer::equal(curr->rtt, child->rtt) &&
- !EffectAnalyzer(passOptions, *getModule(), curr->rtt)
- .hasSideEffects()) {
- replaceCurrent(curr->ref);
- return;
+ if (auto* child = ref->dynCast<RefCast>()) {
+ // Check if the casts are identical.
+ if (ExpressionAnalyzer::equal(curr->rtt, child->rtt) &&
+ !EffectAnalyzer(passOptions, *getModule(), curr->rtt)
+ .hasSideEffects()) {
+ replaceCurrent(curr->ref);
+ return;
+ }
}
}
@@ -1469,14 +1476,18 @@ struct OptimizeInstructions
return;
}
- // See above in RefCast.
- if (!canBeCastTo(curr->ref->type.getHeapType(),
- curr->rtt->type.getHeapType())) {
- // This test cannot succeed, and will definitely return 0.
- Builder builder(*getModule());
- replaceCurrent(builder.makeBlock({builder.makeDrop(curr->ref),
- builder.makeDrop(curr->rtt),
- builder.makeConst(int32_t(0))}));
+ // TODO: If no rtt, this is a static test, and if the type matches then it
+ // will definitely succeed. The opt below could be expanded for that.
+ if (curr->rtt) {
+ // See above in RefCast.
+ if (!canBeCastTo(curr->ref->type.getHeapType(),
+ curr->rtt->type.getHeapType())) {
+ // This test cannot succeed, and will definitely return 0.
+ Builder builder(*getModule());
+ replaceCurrent(builder.makeBlock({builder.makeDrop(curr->ref),
+ builder.makeDrop(curr->rtt),
+ builder.makeConst(int32_t(0))}));
+ }
}
}