diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 5985ae278b1..df6985bd7bf 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -3415,6 +3415,12 @@ struct OptimizeInstructions if (canReorder(ifTrue, c)) { return builder.makeSequence(builder.makeDrop(c), ifTrue); } + // We generally skip optimizing unreachable selects, but an + // optimization on the children might have made them newly + // unreachable. We cannot create a scratch local in that case. + if (!ifTrue->type.isConcrete()) { + return nullptr; + } auto scratch = builder.addVar(getFunction(), ifTrue->type); return builder.makeBlock( {builder.makeLocalSet(scratch, ifTrue), diff --git a/test/lit/passes/optimize-instructions-gc-tnh.wast b/test/lit/passes/optimize-instructions-gc-tnh.wast index 45674df82a2..8eb66fcf5c5 100644 --- a/test/lit/passes/optimize-instructions-gc-tnh.wast +++ b/test/lit/passes/optimize-instructions-gc-tnh.wast @@ -1056,4 +1056,39 @@ (func $get-null (result (ref null none)) (unreachable) ) + + ;; TNH: (func $select-uninhabitable-arms (type $void) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (select + ;; TNH-NEXT: (unreachable) + ;; TNH-NEXT: (unreachable) + ;; TNH-NEXT: (call $get-i32) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; NO_TNH: (func $select-uninhabitable-arms (type $void) + ;; NO_TNH-NEXT: (drop + ;; NO_TNH-NEXT: (select + ;; NO_TNH-NEXT: (unreachable) + ;; NO_TNH-NEXT: (unreachable) + ;; NO_TNH-NEXT: (call $get-i32) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + ;; NO_TNH-NEXT: ) + (func $select-uninhabitable-arms + (drop + ;; This select is not unreachable, so we will optimize it. + (select (result (ref nofunc)) + ;; But the arms will be optimized to unreachable first. We should not + ;; crash on trying to create an unreachable scratch local here. + (ref.as_non_null + (ref.null nofunc) + ) + (ref.as_non_null + (ref.null nofunc) + ) + (call $get-i32) + ) + ) + ) )