diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp index 78c12a5aa41..adc5a4fe988 100644 --- a/src/tools/wasm-ctor-eval.cpp +++ b/src/tools/wasm-ctor-eval.cpp @@ -1173,6 +1173,14 @@ EvalCtorOutcome evalCtor(EvallingModuleRunner& instance, std::cout << " ...stopping due to non-constant flow\n"; } break; + } else if (flow.suspendTag) { + // A suspend reached the exit of the function, so it is unhandled in + // it. TODO: We could support the case of the calling function + // handling it. + if (!quiet) { + std::cout << " ...stopping due to unhandled suspend\n"; + } + break; } if (flow.breakTo == RETURN_CALL_FLOW) { @@ -1235,18 +1243,8 @@ EvalCtorOutcome evalCtor(EvallingModuleRunner& instance, results = flow.values; if (flow.breaking()) { - if (flow.suspendTag) { - // A suspend reached the exit of the function, so it is unhandled in - // it. TODO: We could support the case of the calling function - // handling it. - if (!quiet) { - std::cout << " ...stopping due to unhandled suspend\n"; - } - return EvalCtorOutcome(); - } - // We are returning out of the function (either via a return, or via a - // break to |block|, which has the same outcome. That means we don't + // break to |block|, which has the same outcome). That means we don't // need to execute any more lines, and can consider them to be // executed. if (!quiet) { diff --git a/test/lit/ctor-eval/cont-unhandled.wast b/test/lit/ctor-eval/cont-unhandled.wast new file mode 100644 index 00000000000..d9db24eb44a --- /dev/null +++ b/test/lit/ctor-eval/cont-unhandled.wast @@ -0,0 +1,54 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. +;; RUN: foreach %s %t wasm-ctor-eval --ctors=ctor --kept-exports=ctor,main --quiet -all -S -o - | filecheck %s + +;; Test that we properly stop precomputing at an unhandled suspend. $ctor will +;; decrement $global, and we apply that to the global. We should remove that +;; decrement from the export, which will only contain the suspend that we +;; stopped at. (After that, $main will print the same value, 99, as if we didn't +;; precompute.) + +(module + ;; CHECK: (type $none-none (func)) + + ;; CHECK: (type $i32-none (func (param i32))) + (type $i32-none (func (param i32))) + (type $none-none (func)) + + ;; CHECK: (import "fuzzing-support" "log-i32" (func $log (type $i32-none) (param i32))) + (import "fuzzing-support" "log-i32" (func $log (type $i32-none))) + + ;; CHECK: (global $global (mut i32) (i32.const 99)) + (global $global (mut i32) (i32.const 100)) + + ;; CHECK: (tag $tag (type $none-none)) + (tag $tag (type $none-none)) + + (export "ctor" (func $ctor)) + ;; CHECK: (export "ctor" (func $ctor_3)) + + ;; CHECK: (export "main" (func $main)) + (export "main" (func $main)) + + (func $ctor (type $none-none) + (global.set $global + (i32.sub + (global.get $global) + (i32.const 1) + ) + ) + (suspend $tag) + ) + + ;; CHECK: (func $main (type $none-none) + ;; CHECK-NEXT: (call $log + ;; CHECK-NEXT: (global.get $global) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $main (type $none-none) + (global.get $global) + (call $log) + ) +) +;; CHECK: (func $ctor_3 (type $none-none) +;; CHECK-NEXT: (suspend $tag) +;; CHECK-NEXT: )