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.cpp185
1 files changed, 71 insertions, 114 deletions
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp
index 55176a6e9..828b2fdc9 100644
--- a/src/passes/OptimizeInstructions.cpp
+++ b/src/passes/OptimizeInstructions.cpp
@@ -1434,10 +1434,9 @@ struct OptimizeInstructions
// is, if casts of the type do not affect our behavior (which is the case in
// ref.eq for example).
//
- // |requiredType| is the type we require as the final output here, or a
- // subtype of it. We will not remove a cast that would leave something that
- // would break that. If |requiredType| is not provided we will accept any type
- // there.
+ // |requiredType| is the required supertype of the final output. We will not
+ // remove a cast that would leave something that would break that. If
+ // |requiredType| is not provided we will accept any type there.
//
// See "notes on removing casts", above, for when this is safe to do.
void skipCast(Expression*& input,
@@ -1453,7 +1452,7 @@ struct OptimizeInstructions
continue;
}
} else if (auto* cast = input->dynCast<RefCast>()) {
- if (!cast->rtt && Type::isSubType(cast->ref->type, requiredType)) {
+ if (Type::isSubType(cast->ref->type, requiredType)) {
input = cast->ref;
continue;
}
@@ -1705,7 +1704,7 @@ struct OptimizeInstructions
auto fallthrough =
Properties::getFallthrough(curr->ref, getPassOptions(), *getModule());
- auto intendedType = curr->getIntendedType();
+ auto intendedType = curr->intendedType;
// 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
@@ -1716,13 +1715,8 @@ struct OptimizeInstructions
// Replace the expression with drops of the inputs, and a null. Note
// that we provide a null of the previous type, so that we do not alter
// the type received by our parent.
- std::vector<Expression*> items;
- items.push_back(builder.makeDrop(curr->ref));
- if (curr->rtt) {
- items.push_back(builder.makeDrop(curr->rtt));
- }
- items.push_back(builder.makeRefNull(intendedType));
- Expression* rep = builder.makeBlock(items);
+ Expression* rep = builder.makeSequence(builder.makeDrop(curr->ref),
+ builder.makeRefNull(intendedType));
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
@@ -1737,22 +1731,17 @@ struct OptimizeInstructions
}
// 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.
+ // subtype of the desired type. For example, trying to cast an array to a
+ // struct would be incompatible.
if (!canBeCastTo(curr->ref->type.getHeapType(), intendedType)) {
// This cast cannot succeed. If the input is not a null, it will
// definitely trap.
if (fallthrough->type.isNonNullable()) {
// Make sure to emit a block with the same type as us; leave updating
// types for other passes.
- std::vector<Expression*> items;
- items.push_back(builder.makeDrop(curr->ref));
- if (curr->rtt) {
- items.push_back(builder.makeDrop(curr->rtt));
- }
- items.push_back(builder.makeUnreachable());
- replaceCurrent(builder.makeBlock(items, curr->type));
+ replaceCurrent(builder.makeBlock(
+ {builder.makeDrop(curr->ref), builder.makeUnreachable()},
+ curr->type));
return;
}
// Otherwise, we are not sure what it is, and need to wait for runtime
@@ -1760,46 +1749,29 @@ struct OptimizeInstructions
// we can see the value is definitely a null at compile time, earlier.)
}
- if (passOptions.ignoreImplicitTraps || passOptions.trapsNeverHappen ||
- !curr->rtt) {
- // 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.) We can also do this if this is a static
- // cast: in that case, all we need to know about are the types.
- if (HeapType::isSubType(curr->ref->type.getHeapType(), intendedType)) {
- if (curr->rtt) {
- replaceCurrent(getResultOfFirst(curr->ref,
- builder.makeDrop(curr->rtt),
- getFunction(),
- getModule(),
- passOptions));
- } else {
- replaceCurrent(curr->ref);
+ // Check whether the cast will definitely succeed.
+ if (HeapType::isSubType(curr->ref->type.getHeapType(), intendedType)) {
+ replaceCurrent(curr->ref);
- // We must refinalize here, as we may be returning a more specific
- // type, which can alter the parent. For example:
- //
- // (struct.get $parent 0
- // (ref.cast_static $parent
- // (local.get $child)
- // )
- // )
- //
- // Try to cast a $child to its parent, $parent. That always works,
- // so the cast can be removed.
- // Then once the cast is removed, the outer struct.get
- // will have a reference with a different type, making it a
- // (struct.get $child ..) instead of $parent.
- // But if $parent and $child have different types on field 0 (the
- // child may have a more refined one) then the struct.get must be
- // refinalized so the IR node has the expected type.
- refinalize = true;
- }
- return;
- }
+ // We must refinalize here, as we may be returning a more specific
+ // type, which can alter the parent. For example:
+ //
+ // (struct.get $parent 0
+ // (ref.cast_static $parent
+ // (local.get $child)
+ // )
+ // )
+ //
+ // Try to cast a $child to its parent, $parent. That always works,
+ // so the cast can be removed.
+ // Then once the cast is removed, the outer struct.get
+ // will have a reference with a different type, making it a
+ // (struct.get $child ..) instead of $parent.
+ // But if $parent and $child have different types on field 0 (the
+ // child may have a more refined one) then the struct.get must be
+ // refinalized so the IR node has the expected type.
+ refinalize = true;
+ return;
}
// Repeated identical ref.cast operations are unnecessary. First, find the
@@ -1818,51 +1790,41 @@ struct OptimizeInstructions
}
}
if (auto* child = ref->dynCast<RefCast>()) {
- if (curr->rtt && child->rtt) {
- // Check if the casts are identical.
- if (ExpressionAnalyzer::equal(curr->rtt, child->rtt) &&
- !EffectAnalyzer(passOptions, *getModule(), curr->rtt)
- .hasSideEffects()) {
- replaceCurrent(curr->ref);
+ // Repeated casts can be removed, leaving just the most demanding of
+ // them.
+ auto childIntendedType = child->intendedType;
+ if (HeapType::isSubType(intendedType, childIntendedType)) {
+ // Skip the child.
+ if (curr->ref == child) {
+ curr->ref = child->ref;
return;
+ } else {
+ // The child is not the direct child of the parent, but it is a
+ // fallthrough value, for example,
+ //
+ // (ref.cast parent
+ // (block
+ // .. other code ..
+ // (ref.cast child)))
+ //
+ // In this case it isn't obvious that we can remove the child, as
+ // doing so might require updating the types of the things in the
+ // middle - and in fact the sole purpose of the child may be to get
+ // a proper type for validation to work. Do nothing in this case,
+ // and hope that other opts will help here (for example,
+ // trapsNeverHappen will help if the code validates without the
+ // child).
}
- } else if (!curr->rtt && !child->rtt) {
- // Repeated static casts can be removed, leaving just the most demanding
- // of them.
- auto childIntendedType = child->getIntendedType();
- if (HeapType::isSubType(intendedType, childIntendedType)) {
- // Skip the child.
- if (curr->ref == child) {
- curr->ref = child->ref;
- return;
- } else {
- // The child is not the direct child of the parent, but it is a
- // fallthrough value, for example,
- //
- // (ref.cast parent
- // (block
- // .. other code ..
- // (ref.cast child)))
- //
- // In this case it isn't obvious that we can remove the child, as
- // doing so might require updating the types of the things in the
- // middle - and in fact the sole purpose of the child may be to get
- // a proper type for validation to work. Do nothing in this case,
- // and hope that other opts will help here (for example,
- // trapsNeverHappen will help if the code validates without the
- // child).
- }
- } else if (!canBeCastTo(intendedType, childIntendedType)) {
- // The types are not compatible, so if the input is not null, this
- // will trap.
- if (!curr->type.isNullable()) {
- // Make sure to emit a block with the same type as us; leave
- // updating types for other passes.
- replaceCurrent(builder.makeBlock(
- {builder.makeDrop(curr->ref), builder.makeUnreachable()},
- curr->type));
- return;
- }
+ } else if (!canBeCastTo(intendedType, childIntendedType)) {
+ // The types are not compatible, so if the input is not null, this
+ // will trap.
+ if (!curr->type.isNullable()) {
+ // Make sure to emit a block with the same type as us; leave
+ // updating types for other passes.
+ replaceCurrent(builder.makeBlock(
+ {builder.makeDrop(curr->ref), builder.makeUnreachable()},
+ curr->type));
+ return;
}
}
}
@@ -1905,22 +1867,17 @@ struct OptimizeInstructions
Builder builder(*getModule());
auto refType = curr->ref->type.getHeapType();
- auto intendedType = curr->getIntendedType();
+ auto intendedType = curr->intendedType;
// See above in RefCast.
if (!canBeCastTo(refType, intendedType)) {
// This test cannot succeed, and will definitely return 0.
- std::vector<Expression*> items;
- items.push_back(builder.makeDrop(curr->ref));
- if (curr->rtt) {
- items.push_back(builder.makeDrop(curr->rtt));
- }
- items.push_back(builder.makeConst(int32_t(0)));
- replaceCurrent(builder.makeBlock(items));
+ replaceCurrent(builder.makeSequence(builder.makeDrop(curr->ref),
+ builder.makeConst(int32_t(0))));
return;
}
- if (!curr->rtt && curr->ref->type.isNonNullable() &&
+ if (curr->ref->type.isNonNullable() &&
HeapType::isSubType(refType, intendedType)) {
// This static test will definitely succeed.
replaceCurrent(builder.makeBlock(