summaryrefslogtreecommitdiff
path: root/src/passes/OptimizeInstructions.cpp
diff options
context:
space:
mode:
authorThomas Lively <7121787+tlively@users.noreply.github.com>2022-08-04 17:05:54 -0700
committerGitHub <noreply@github.com>2022-08-05 00:05:54 +0000
commit6759371b5239efa3daa9d988455abdd14a8b18ca (patch)
tree0c3a3e371ed742bdbd790f7344ec86e8536bc167 /src/passes/OptimizeInstructions.cpp
parent9534e6927c41f4a6a5d06d58d00c271c9f066e9a (diff)
downloadbinaryen-6759371b5239efa3daa9d988455abdd14a8b18ca.tar.gz
binaryen-6759371b5239efa3daa9d988455abdd14a8b18ca.tar.bz2
binaryen-6759371b5239efa3daa9d988455abdd14a8b18ca.zip
Remove RTTs (#4848)
RTTs were removed from the GC spec and if they are added back in in the future, they will be heap types rather than value types as in our implementation. Updating our implementation to have RTTs be heap types would have been more work than deleting them for questionable benefit since we don't know how long it will be before they are specced again.
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(