diff options
Diffstat (limited to 'src/passes/OptimizeInstructions.cpp')
-rw-r--r-- | src/passes/OptimizeInstructions.cpp | 199 |
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))})); + } } } |