summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/passes/LocalSubtyping.cpp20
-rw-r--r--test/lit/passes/local-subtyping.wast35
2 files changed, 55 insertions, 0 deletions
diff --git a/src/passes/LocalSubtyping.cpp b/src/passes/LocalSubtyping.cpp
index ae197858c..19ce2f275 100644
--- a/src/passes/LocalSubtyping.cpp
+++ b/src/passes/LocalSubtyping.cpp
@@ -102,6 +102,7 @@ struct LocalSubtyping : public WalkerPass<PostWalker<LocalSubtyping>> {
// TODO: handle cycles of X -> Y -> X etc.
bool more;
+ bool optimized = false;
do {
more = false;
@@ -150,6 +151,7 @@ struct LocalSubtyping : public WalkerPass<PostWalker<LocalSubtyping>> {
assert(Type::isSubType(newType, oldType));
func->vars[i - varBase] = newType;
more = true;
+ optimized = true;
// Update gets and tees.
for (auto* get : getsForLocal[i]) {
@@ -167,6 +169,24 @@ struct LocalSubtyping : public WalkerPass<PostWalker<LocalSubtyping>> {
}
}
} while (more);
+
+ // If we ever optimized, then we also need to do a final pass to update any
+ // unreachable gets and tees. They are not seen or updated in the above
+ // analysis, but must be fixed up for validation to work.
+ if (optimized) {
+ for (auto* get : FindAll<LocalGet>(func->body).list) {
+ get->type = func->getLocalType(get->index);
+ }
+ for (auto* set : FindAll<LocalSet>(func->body).list) {
+ if (set->isTee()) {
+ set->type = func->getLocalType(set->index);
+ set->finalize();
+ }
+ }
+
+ // Also update their parents.
+ ReFinalize().walkFunctionInModule(func, getModule());
+ }
}
};
diff --git a/test/lit/passes/local-subtyping.wast b/test/lit/passes/local-subtyping.wast
index 27f0fe1d0..c24126e1a 100644
--- a/test/lit/passes/local-subtyping.wast
+++ b/test/lit/passes/local-subtyping.wast
@@ -249,4 +249,39 @@
(local.get $x)
)
)
+
+ ;; CHECK: (func $unreachables (result funcref)
+ ;; CHECK-NEXT: (local $temp (ref null $none_=>_funcref))
+ ;; CHECK-NEXT: (local.set $temp
+ ;; CHECK-NEXT: (ref.func $unreachables)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $block (result (ref null $none_=>_funcref))
+ ;; CHECK-NEXT: (local.tee $temp
+ ;; CHECK-NEXT: (ref.func $unreachables)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.get $temp)
+ ;; CHECK-NEXT: )
+ (func $unreachables (result funcref)
+ (local $temp funcref)
+ ;; Set a value that allows us to refine the local's type.
+ (local.set $temp
+ (ref.func $unreachables)
+ )
+ (unreachable)
+ ;; A tee that is not reachable. We must still update its type, and the
+ ;; parents.
+ (drop
+ (block (result funcref)
+ (local.tee $temp
+ (ref.func $unreachables)
+ )
+ )
+ )
+ ;; A get that is not reachable. We must still update its type.
+ (local.get $temp)
+ )
)