summaryrefslogtreecommitdiff
path: root/src/passes/OptimizeInstructions.cpp
diff options
context:
space:
mode:
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))}));
+ }
}
}