diff options
-rw-r--r-- | src/passes/LocalSubtyping.cpp | 20 | ||||
-rw-r--r-- | test/lit/passes/local-subtyping.wast | 35 |
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) + ) ) |