diff --git a/src/passes/ConstraintAnalysis.cpp b/src/passes/ConstraintAnalysis.cpp index caf665ede1a..78d9e71df51 100644 --- a/src/passes/ConstraintAnalysis.cpp +++ b/src/passes/ConstraintAnalysis.cpp @@ -25,6 +25,7 @@ #include "cfg/cfg-traversal.h" #include "ir/constraint.h" #include "ir/drop.h" +#include "ir/eh-utils.h" #include "ir/literal-utils.h" #include "ir/local-graph.h" #include "ir/properties.h" @@ -192,7 +193,11 @@ struct ConstraintAnalysis optimizeExpression(currp, constraints); } else { // This is unreachable code: just mark it so. - *currp = Builder(*getModule()).makeUnreachable(); + *currp = getDroppedChildrenAndAppend( + *currp, + *getModule(), + getPassOptions(), + Builder(*getModule()).makeUnreachable()); refinalize = true; } } @@ -200,6 +205,7 @@ struct ConstraintAnalysis if (refinalize) { ReFinalize().walkFunctionInModule(getFunction(), getModule()); + EHUtils::handleBlockNestedPops(getFunction(), *getModule()); } } diff --git a/test/lit/passes/constraint-analysis-eh.wast b/test/lit/passes/constraint-analysis-eh.wast new file mode 100644 index 00000000000..47e9cb27713 --- /dev/null +++ b/test/lit/passes/constraint-analysis-eh.wast @@ -0,0 +1,78 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. + +;; RUN: wasm-opt %s --constraint-analysis -all -S -o - | filecheck %s + +(module + ;; CHECK: (tag $tag (type $0) (param i32)) + (tag $tag (param i32)) + + ;; CHECK: (func $pop-unreachable (type $1) + ;; CHECK-NEXT: (local $x i32) + ;; CHECK-NEXT: (try + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch $tag + ;; CHECK-NEXT: (local.set $x + ;; CHECK-NEXT: (pop i32) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $pop-unreachable + (local $x i32) + (try + (do + ) + (catch $tag + ;; This code is unreachable: no throw exists that can reach it. However, it + ;; is invalid to remove a pop, as that would not validate, so we keep it. We + ;; also keep the set, as it may be needed for non-nullable local validation. + (local.set $x + (pop i32) + ) + ) + ) + ) + + ;; CHECK: (func $pop-unreachable-result (type $2) (result i32) + ;; CHECK-NEXT: (local $x i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (try (result i32) + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch $tag + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (pop i32) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (local.set $x + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $pop-unreachable-result (result i32) + (local $x i32) + ;; As above, but now with a result. This adds a block we need to fix up EH + ;; for. + (try (result i32) + (do + (i32.const 10) + ) + (catch $tag + (local.set $x + (pop i32) + ) + (i32.const 20) + ) + ) + ) +) + diff --git a/test/lit/passes/constraint-analysis.wast b/test/lit/passes/constraint-analysis.wast index b04c2b2a3e6..207a8e65993 100644 --- a/test/lit/passes/constraint-analysis.wast +++ b/test/lit/passes/constraint-analysis.wast @@ -3476,17 +3476,32 @@ ;; CHECK: (func $unreachable-loop (type $1) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (unreachable) - ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (local.set $x + ;; CHECK-NEXT: (loop $loop (result i32) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; OPTIN: (func $unreachable-loop (type $1) ;; OPTIN-NEXT: (local $x i32) ;; OPTIN-NEXT: (unreachable) - ;; OPTIN-NEXT: (unreachable) + ;; OPTIN-NEXT: (block + ;; OPTIN-NEXT: (local.set $x + ;; OPTIN-NEXT: (loop $loop (result i32) + ;; OPTIN-NEXT: (i32.const 0) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (unreachable) + ;; OPTIN-NEXT: ) ;; OPTIN-NEXT: ) (func $unreachable-loop (local $x i32) ;; The entire loop is unreachable. The control flow here should not cause any - ;; internal errors, and we can just optimize this to unreachable. + ;; internal errors, and we can just optimize this to unreachable (though we + ;; keep the local.set, because of non-nullable local validation). (unreachable) (local.set $x (loop $loop (result i32)