diff options
-rw-r--r-- | src/ir/ordering.h | 14 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 2 | ||||
-rw-r--r-- | test/lit/passes/gto-removals.wast | 33 |
3 files changed, 46 insertions, 3 deletions
diff --git a/src/ir/ordering.h b/src/ir/ordering.h index ed2c00ee2..bc8d69055 100644 --- a/src/ir/ordering.h +++ b/src/ir/ordering.h @@ -34,6 +34,11 @@ namespace wasm { // // (temp = first, second, temp) // +// The first expression is assumed to not be unreachable (otherwise, there is no +// value to get the result of). If the second is unreachable, this returns +// something with type unreachable (that avoids returning something with a +// concrete type, which might replace something with unreachable type - we want +// to keep the type the same, in most cases). inline Expression* getResultOfFirst(Expression* first, Expression* second, Function* func, @@ -43,6 +48,15 @@ inline Expression* getResultOfFirst(Expression* first, Builder builder(*wasm); + if (second->type == Type::unreachable) { + // No value is actually consumed here. Emit something with unreachable type. + // (Note that if we continued to the canReorder code after us, and emitted + // second followed by first, then the block would have a concrete type due + // to the last element having such a type - which would not have unreachable + // type.) + return builder.makeSequence(builder.makeDrop(first), second); + } + if (EffectAnalyzer::canReorder(passOptions, *wasm, first, second)) { return builder.makeSequence(second, first); } diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index a445d74bf..af40896ce 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -182,7 +182,7 @@ void Block::finalize() { return; } // The default type is what is at the end. Next we need to see if breaks and/ - // or unreachabitily change that. + // or unreachability change that. type = list.back()->type; if (!name.is()) { // Nothing branches here, so this is easy. diff --git a/test/lit/passes/gto-removals.wast b/test/lit/passes/gto-removals.wast index f75eb7483..5dd6ab4d1 100644 --- a/test/lit/passes/gto-removals.wast +++ b/test/lit/passes/gto-removals.wast @@ -815,11 +815,13 @@ ;; CHECK-NEXT: (block $block ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (ref.as_non_null - ;; CHECK-NEXT: (block (result (ref null ${mut:i8})) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null ${mut:i8}) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (br $block) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (ref.null ${mut:i8}) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -836,6 +838,33 @@ ) ) + ;; CHECK: (func $unreachable-set-2b (type $none_=>_none) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null ${mut:i8}) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $unreachable-set-2b + ;; As above, but with an unreachable instead of a br. We add a nop here so + ;; that we are inside of a block, and then validation would fail if we do + ;; not keep the type of the replacement for the struct.set identical to the + ;; struct.set. That is, the type must remain unreachable. + (nop) + (struct.set ${mut:i8} 0 + (ref.null ${mut:i8}) + (unreachable) + ) + ) + ;; CHECK: (func $unreachable-set-3 (type $none_=>_none) ;; CHECK-NEXT: (local $0 (ref ${mut:i8})) ;; CHECK-NEXT: (drop |