diff options
author | Alon Zakai <azakai@google.com> | 2023-01-06 13:54:23 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-06 21:54:23 +0000 |
commit | 54b1cf9025f5564c49dba730dc9fe6600aa7d532 (patch) | |
tree | 1f099a1285c8d1d60494e4080682b833ee49ee5b /src/passes/OptimizeInstructions.cpp | |
parent | f719c3527512377657f72f16ee4ac5581843614f (diff) | |
download | binaryen-54b1cf9025f5564c49dba730dc9fe6600aa7d532.tar.gz binaryen-54b1cf9025f5564c49dba730dc9fe6600aa7d532.tar.bz2 binaryen-54b1cf9025f5564c49dba730dc9fe6600aa7d532.zip |
[Wasm GC] Optimize impossible ref.casts better (#5400)
Also add some comments on related optimization opportunities.
Also delete a test of a combination of types between hierarchies, which will soon
not be expressible at all in the IR.
Diffstat (limited to 'src/passes/OptimizeInstructions.cpp')
-rw-r--r-- | src/passes/OptimizeInstructions.cpp | 61 |
1 files changed, 42 insertions, 19 deletions
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index ed99705ae..7902eaebd 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1902,6 +1902,19 @@ struct OptimizeInstructions return HeapType::isSubType(a, b) || HeapType::isSubType(b, a); } + bool canBeCastTo(Type a, Type b) { + // A value can be cast to the other if the heap type can be cast, or if a + // null can work. + if (a.isNullable() && b.isNullable()) { + return true; + } + if (a.isRef() && b.isRef() && + canBeCastTo(a.getHeapType(), b.getHeapType())) { + return true; + } + return false; + } + void visitRefCast(RefCast* curr) { // Note we must check the ref's type here and not our own, since we only // refinalize at the end, which means our type may not have been updated yet @@ -1943,26 +1956,21 @@ struct OptimizeInstructions // looking into. } - // For the cast to be able to succeed, the value being cast must be a - // 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. - 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 - // 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.) + // Check whether the cast will definitely fail. + if (!canBeCastTo(fallthrough->type, curr->type)) { + // This cast cannot succeed, so it will trap. + // 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; } // Check whether the cast will definitely succeed. + // + // Note that we could look at the fallthrough for the ref, but that would + // require additional work to make sure we emit something that validates + // properly. TODO if (Type::isSubType(curr->ref->type, curr->type)) { replaceCurrent(curr->ref); @@ -1987,10 +1995,25 @@ struct OptimizeInstructions return; } - // The cast will not definitely succeed, but perhaps the heap type part of - // the cast will, at least. That would leave only nullability as an issue, + // The cast will not definitely succeed nor will it definitely fail. + // + // Perhaps the heap type part of the cast can be reasoned about, at least. + // E.g. if the heap type part of the cast is definitely compatible, but the + // cast as a whole is not, that would leave only nullability as an issue, // that is, this means that the input ref is nullable but we are casting to // non-null. + // + // Note that we could do something similar for a failed cast, that is, + // handle the situation where the entire cast might succeed, but the heap + // type part will definitely fail. For example, the input might be a + // nullable array while the output might be a nullable struct. That is, a + // situation where the only way the cast succeeds is if the input is null. + // However, optimizing this would mean emitting something like + // + // ref == null ? null : trap + // + // which is strictly larger. However, it might be more efficient, so could + // be worth investigating TODO if (HeapType::isSubType(curr->ref->type.getHeapType(), intendedType)) { assert(curr->ref->type.isNullable()); assert(curr->type.isNonNullable()); |