Skip to content

Commit 5526930

Browse files
committed
[Parser] Fix bug in unreachable fallback logic
When popping past an unreachable instruction would lead to popping from an empty stack or popping an incorrect type, we need to avoid popping and produce new `Unreachable` instructions instead to ensure we parse valid IR. The logic for this was flawed and made the synthetic `Unreachable` come before the popped unreachable child, which was not correct in the case that that popped unreachable was a branch or other non-trapping instruction. Fix and simplify the logic and re-enable the spec test that uncovered the bug.
1 parent c3b9cde commit 5526930

File tree

3 files changed

+55
-17
lines changed

3 files changed

+55
-17
lines changed

scripts/test/shared.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -417,9 +417,6 @@ def get_tests(test_dir, extensions=[], recursive=False):
417417
'type.wast',
418418
'unreached-invalid.wast',
419419

420-
# WAT parser error
421-
'unwind.wast',
422-
423420
# WAST parser error
424421
'binary.wast',
425422

src/wasm/wasm-ir-builder.cpp

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -407,9 +407,12 @@ struct IRBuilder::ChildPopper
407407
--stackTupleIndex;
408408
} else {
409409
if (stackIndex == 0) {
410-
// No more available values. This is fine iff we are reaching past
411-
// an unreachable. Any error will be caught later when we are
412-
// popping.
410+
// No more available values. This is valid iff we are reaching past
411+
// an unreachable, but we still need the fallback behavior to ensure
412+
// the input unreachable instruction is executed first. If we are
413+
// not reaching past an unreachable, the error will be caught we
414+
// when pop.
415+
needUnreachableFallback = true;
413416
goto pop;
414417
}
415418
--stackIndex;
@@ -458,18 +461,20 @@ struct IRBuilder::ChildPopper
458461
// We have checked all the constraints, so we are ready to pop children.
459462
for (int i = children.size() - 1; i >= 0; --i) {
460463
if (needUnreachableFallback &&
461-
scope.exprStack.size() == *unreachableIndex + 1) {
462-
// The expressions remaining on the stack may be executed, but they do
463-
// not satisfy the requirements to be children of the current parent.
464-
// Explicitly drop them so they will still be executed for their side
465-
// effects and so the remaining children will be filled with
466-
// unreachables.
467-
assert(scope.exprStack.back()->type == Type::unreachable);
468-
for (auto& expr : scope.exprStack) {
469-
expr = Builder(builder.wasm).dropIfConcretelyTyped(expr);
470-
}
464+
scope.exprStack.size() == *unreachableIndex + 1 && i > 0) {
465+
// The next item on the stack is the unreachable instruction we must
466+
// not pop past. We cannot insert unreachables in front of it because
467+
// it might be a branch we actually have to execute, so this next item
468+
// must be child 0. But we are not ready to pop child 0 yet, so
469+
// synthesize an unreachable instead of popping. The deeper
470+
// instructions that would otherwise have been popped will remain on
471+
// the stack to become prior children of future expressions or to be
472+
// implicitly dropped at the end of the scope.
473+
*children[i].childp = builder.builder.makeUnreachable();
474+
continue;
471475
}
472476

477+
// Pop a child normally.
473478
auto val = pop(children[i].constraint.size());
474479
CHECK_ERR(val);
475480
*children[i].childp = *val;

test/lit/wat-kitchen-sink.wast

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2728,6 +2728,42 @@
27282728
br 0
27292729
)
27302730

2731+
;; CHECK: (func $br-mismatch-after (type $1) (result i32)
2732+
;; CHECK-NEXT: (block $label (result i32)
2733+
;; CHECK-NEXT: (i32.add
2734+
;; CHECK-NEXT: (br $label
2735+
;; CHECK-NEXT: (i32.const 1)
2736+
;; CHECK-NEXT: )
2737+
;; CHECK-NEXT: (unreachable)
2738+
;; CHECK-NEXT: )
2739+
;; CHECK-NEXT: )
2740+
;; CHECK-NEXT: )
2741+
(func $br-mismatch-after (result i32)
2742+
i32.const 1
2743+
br 0
2744+
i32.add
2745+
)
2746+
2747+
;; CHECK: (func $br-mismatch-after-extra (type $1) (result i32)
2748+
;; CHECK-NEXT: (block $label (result i32)
2749+
;; CHECK-NEXT: (drop
2750+
;; CHECK-NEXT: (i64.const 0)
2751+
;; CHECK-NEXT: )
2752+
;; CHECK-NEXT: (i32.add
2753+
;; CHECK-NEXT: (br $label
2754+
;; CHECK-NEXT: (i32.const 1)
2755+
;; CHECK-NEXT: )
2756+
;; CHECK-NEXT: (unreachable)
2757+
;; CHECK-NEXT: )
2758+
;; CHECK-NEXT: )
2759+
;; CHECK-NEXT: )
2760+
(func $br-mismatch-after-extra (result i32)
2761+
i64.const 0
2762+
i32.const 1
2763+
br 0
2764+
i32.add
2765+
)
2766+
27312767
;; CHECK: (func $br_if (type $0)
27322768
;; CHECK-NEXT: (block $l
27332769
;; CHECK-NEXT: (br_if $l
@@ -3669,7 +3705,7 @@
36693705
(func $ref-func
36703706
ref.func $ref-func
36713707
drop
3672-
ref.func 159
3708+
ref.func 161
36733709
drop
36743710
)
36753711

0 commit comments

Comments
 (0)