diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 21e3a1ebb02..12d96c8e7d0 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1461,6 +1461,65 @@ struct OptimizeInstructions } } + void visitResume(Resume* curr) { + skipNonNullCast(curr->cont, curr); + if (trapOnNull(curr, curr->cont)) { + return; + } + if (curr->type == Type::unreachable) { + return; + } + + // If this resume operates on a freshly-created continuation of an exact + // function that is known never to suspend, we can turn the resumption into + // a direct call, avoiding the continuation allocation and handler overhead. + + // Continuations are single-shot, so resuming a continuation that has + // already been consumed will trap. If traps are assumed never to happen, we + // can assume this continuation was not consumed on another path and look + // through tees and conditional branches. Otherwise, avoid looking through + // them to ensure the continuation cannot be consumed elsewhere. + auto behavior = getPassOptions().trapsNeverHappen + ? Properties::FallthroughBehavior::AllowTeeBrIf + : Properties::FallthroughBehavior::NoTeeBrIf; + + auto* contExpr = Properties::getFallthrough( + curr->cont, getPassOptions(), *getModule(), behavior); + auto* contNew = contExpr->dynCast(); + if (!contNew) { + return; + } + if (contNew->func->type == Type::unreachable) { + return; + } + + auto* funcExpr = Properties::getFallthrough( + contNew->func, getPassOptions(), *getModule(), behavior); + auto* refFunc = funcExpr->dynCast(); + if (!refFunc) { + return; + } + + auto* target = getModule()->getFunctionOrNull(refFunc->func); + if (!target || target->imported()) { + return; + } + + if (!target->effects || target->effects->suspends) { + return; + } + + auto* block = + ChildLocalizer(curr, getFunction(), *getModule(), getPassOptions()) + .getChildrenReplacement(); + Builder builder(*getModule()); + Type results = target->getResults(); + block->list.push_back( + builder.makeCall(target->name, curr->operands, results)); + block->type = results; + replaceCurrent(block); + } + // Note on removing casts (which the following utilities, skipNonNullCast and // skipCast do): removing a cast is potentially dangerous, as it removes // information from the IR. For example: diff --git a/test/lit/passes/optimize-instructions-resume.wast b/test/lit/passes/optimize-instructions-resume.wast new file mode 100644 index 00000000000..f6376ccdeef --- /dev/null +++ b/test/lit/passes/optimize-instructions-resume.wast @@ -0,0 +1,1137 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. +;; Test that optimize-instructions turns resumptions of continuations that never +;; suspend into direct calls. + +;; RUN: wasm-opt %s --all-features --generate-global-effects --remove-unused-names --optimize-instructions -S -o - | filecheck %s --check-prefix NO-TNH +;; RUN: wasm-opt %s --all-features --generate-global-effects --traps-never-happen --remove-unused-names --optimize-instructions -S -o - | filecheck %s --check-prefix TNH +;; RUN: wasm-opt %s --all-features --generate-global-effects --remove-unused-names --optimize-instructions --vacuum -S -o - | filecheck %s --check-prefix VACUUM + +(module + ;; NO-TNH: (type $sig-none (func)) + + ;; NO-TNH: (type $cont-none (cont $sig-none)) + + ;; NO-TNH: (type $sig-binary (func (param i32 i32) (result i32))) + + ;; NO-TNH: (type $cont-binary (cont $sig-binary)) + + ;; NO-TNH: (type $sig-any (func (param (ref any)) (result (ref any)))) + + ;; NO-TNH: (type $cont-any (cont $sig-any)) + + ;; NO-TNH: (type $sig-unary (func (param i32) (result i32))) + + ;; NO-TNH: (import "env" "imported" (func $imported (type $sig-none))) + ;; TNH: (type $sig-none (func)) + + ;; TNH: (type $cont-none (cont $sig-none)) + + ;; TNH: (type $sig-binary (func (param i32 i32) (result i32))) + + ;; TNH: (type $cont-binary (cont $sig-binary)) + + ;; TNH: (type $sig-any (func (param (ref any)) (result (ref any)))) + + ;; TNH: (type $cont-any (cont $sig-any)) + + ;; TNH: (type $sig-unary (func (param i32) (result i32))) + + ;; TNH: (import "env" "imported" (func $imported (type $sig-none))) + ;; VACUUM: (type $sig-none (func)) + + ;; VACUUM: (type $cont-none (cont $sig-none)) + + ;; VACUUM: (type $sig-binary (func (param i32 i32) (result i32))) + + ;; VACUUM: (type $cont-binary (cont $sig-binary)) + + ;; VACUUM: (type $sig-any (func (param (ref any)) (result (ref any)))) + + ;; VACUUM: (type $cont-any (cont $sig-any)) + + ;; VACUUM: (type $sig-unary (func (param i32) (result i32))) + + ;; VACUUM: (import "env" "imported" (func $imported (type $sig-none))) + (import "env" "imported" (func $imported (type $sig-none))) + + ;; NO-TNH: (global $counter (mut i32) (i32.const 0)) + + ;; NO-TNH: (tag $tag (type $sig-none)) + ;; TNH: (global $counter (mut i32) (i32.const 0)) + + ;; TNH: (tag $tag (type $sig-none)) + ;; VACUUM: (global $counter (mut i32) (i32.const 0)) + + ;; VACUUM: (tag $tag (type $sig-none)) + (tag $tag) + ;; NO-TNH: (tag $tag-i32 (type $9) (param i32)) + ;; TNH: (tag $tag-i32 (type $9) (param i32)) + ;; VACUUM: (tag $tag-i32 (type $9) (param i32)) + (tag $tag-i32 (param i32)) + + (type $sig-none (func)) + (type $sig-binary (func (param i32 i32) (result i32))) + (type $sig-unary (func (param i32) (result i32))) + (type $sig-any (func (param (ref any)) (result (ref any)))) + + (type $cont-none (cont $sig-none)) + (type $cont-binary (cont $sig-binary)) + (type $cont-unary (cont $sig-unary)) + (type $cont-any (cont $sig-any)) + + (global $counter (mut i32) (i32.const 0)) + + ;; NO-TNH: (func $pure (type $sig-none) + ;; NO-TNH-NEXT: (nop) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $pure (type $sig-none) + ;; TNH-NEXT: (nop) + ;; TNH-NEXT: ) + ;; VACUUM: (func $pure (type $sig-none) + ;; VACUUM-NEXT: (nop) + ;; VACUUM-NEXT: ) + (func $pure (type $sig-none) + (nop) + ) + + ;; NO-TNH: (func $pure-binary (type $sig-binary) (param $x i32) (param $y i32) (result i32) + ;; NO-TNH-NEXT: (i32.add + ;; NO-TNH-NEXT: (local.get $x) + ;; NO-TNH-NEXT: (local.get $y) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $pure-binary (type $sig-binary) (param $x i32) (param $y i32) (result i32) + ;; TNH-NEXT: (i32.add + ;; TNH-NEXT: (local.get $x) + ;; TNH-NEXT: (local.get $y) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; VACUUM: (func $pure-binary (type $sig-binary) (param $x i32) (param $y i32) (result i32) + ;; VACUUM-NEXT: (i32.add + ;; VACUUM-NEXT: (local.get $x) + ;; VACUUM-NEXT: (local.get $y) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + (func $pure-binary (type $sig-binary) (param $x i32) (param $y i32) (result i32) + (i32.add (local.get $x) (local.get $y)) + ) + + ;; NO-TNH: (func $pure-any (type $sig-any) (param $x (ref any)) (result (ref any)) + ;; NO-TNH-NEXT: (local.get $x) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $pure-any (type $sig-any) (param $x (ref any)) (result (ref any)) + ;; TNH-NEXT: (local.get $x) + ;; TNH-NEXT: ) + ;; VACUUM: (func $pure-any (type $sig-any) (param $x (ref any)) (result (ref any)) + ;; VACUUM-NEXT: (local.get $x) + ;; VACUUM-NEXT: ) + (func $pure-any (type $sig-any) (param $x (ref any)) (result (ref any)) + (local.get $x) + ) + + ;; NO-TNH: (func $suspending (type $sig-none) + ;; NO-TNH-NEXT: (suspend $tag) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $suspending (type $sig-none) + ;; TNH-NEXT: (suspend $tag) + ;; TNH-NEXT: ) + ;; VACUUM: (func $suspending (type $sig-none) + ;; VACUUM-NEXT: (suspend $tag) + ;; VACUUM-NEXT: ) + (func $suspending (type $sig-none) + (suspend $tag) + ) + + ;; NO-TNH: (func $caller-of-suspending (type $sig-none) + ;; NO-TNH-NEXT: (call $suspending) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $caller-of-suspending (type $sig-none) + ;; TNH-NEXT: (call $suspending) + ;; TNH-NEXT: ) + ;; VACUUM: (func $caller-of-suspending (type $sig-none) + ;; VACUUM-NEXT: (call $suspending) + ;; VACUUM-NEXT: ) + (func $caller-of-suspending (type $sig-none) + (call $suspending) + ) + + ;; NO-TNH: (func $same-sig-pure (type $sig-unary) (param $x i32) (result i32) + ;; NO-TNH-NEXT: (local.get $x) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $same-sig-pure (type $sig-unary) (param $x i32) (result i32) + ;; TNH-NEXT: (local.get $x) + ;; TNH-NEXT: ) + ;; VACUUM: (func $same-sig-pure (type $sig-unary) (param $x i32) (result i32) + ;; VACUUM-NEXT: (local.get $x) + ;; VACUUM-NEXT: ) + (func $same-sig-pure (type $sig-unary) (param $x i32) (result i32) + (local.get $x) + ) + + ;; NO-TNH: (func $same-sig-suspending (type $sig-unary) (param $x i32) (result i32) + ;; NO-TNH-NEXT: (suspend $tag-i32 + ;; NO-TNH-NEXT: (local.get $x) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: (local.get $x) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $same-sig-suspending (type $sig-unary) (param $x i32) (result i32) + ;; TNH-NEXT: (suspend $tag-i32 + ;; TNH-NEXT: (local.get $x) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (local.get $x) + ;; TNH-NEXT: ) + ;; VACUUM: (func $same-sig-suspending (type $sig-unary) (param $x i32) (result i32) + ;; VACUUM-NEXT: (suspend $tag-i32 + ;; VACUUM-NEXT: (local.get $x) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: (local.get $x) + ;; VACUUM-NEXT: ) + (func $same-sig-suspending (type $sig-unary) (param $x i32) (result i32) + (suspend $tag-i32 (local.get $x)) + (local.get $x) + ) + + ;; NO-TNH: (func $side-effect-1 (type $2) (result i32) + ;; NO-TNH-NEXT: (global.set $counter + ;; NO-TNH-NEXT: (i32.add + ;; NO-TNH-NEXT: (global.get $counter) + ;; NO-TNH-NEXT: (i32.const 1) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: (i32.const 10) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $side-effect-1 (type $2) (result i32) + ;; TNH-NEXT: (global.set $counter + ;; TNH-NEXT: (i32.add + ;; TNH-NEXT: (global.get $counter) + ;; TNH-NEXT: (i32.const 1) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (i32.const 10) + ;; TNH-NEXT: ) + ;; VACUUM: (func $side-effect-1 (type $2) (result i32) + ;; VACUUM-NEXT: (global.set $counter + ;; VACUUM-NEXT: (i32.add + ;; VACUUM-NEXT: (global.get $counter) + ;; VACUUM-NEXT: (i32.const 1) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: (i32.const 10) + ;; VACUUM-NEXT: ) + (func $side-effect-1 (result i32) + (global.set $counter (i32.add (global.get $counter) (i32.const 1))) + (i32.const 10) + ) + + ;; NO-TNH: (func $side-effect-2 (type $2) (result i32) + ;; NO-TNH-NEXT: (global.set $counter + ;; NO-TNH-NEXT: (i32.add + ;; NO-TNH-NEXT: (global.get $counter) + ;; NO-TNH-NEXT: (i32.const 2) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: (i32.const 20) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $side-effect-2 (type $2) (result i32) + ;; TNH-NEXT: (global.set $counter + ;; TNH-NEXT: (i32.add + ;; TNH-NEXT: (global.get $counter) + ;; TNH-NEXT: (i32.const 2) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (i32.const 20) + ;; TNH-NEXT: ) + ;; VACUUM: (func $side-effect-2 (type $2) (result i32) + ;; VACUUM-NEXT: (global.set $counter + ;; VACUUM-NEXT: (i32.add + ;; VACUUM-NEXT: (global.get $counter) + ;; VACUUM-NEXT: (i32.const 2) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: (i32.const 20) + ;; VACUUM-NEXT: ) + (func $side-effect-2 (result i32) + (global.set $counter (i32.add (global.get $counter) (i32.const 2))) + (i32.const 20) + ) + + ;; NO-TNH: (func $side-effect-3 (type $sig-none) + ;; NO-TNH-NEXT: (global.set $counter + ;; NO-TNH-NEXT: (i32.add + ;; NO-TNH-NEXT: (global.get $counter) + ;; NO-TNH-NEXT: (i32.const 3) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $side-effect-3 (type $sig-none) + ;; TNH-NEXT: (global.set $counter + ;; TNH-NEXT: (i32.add + ;; TNH-NEXT: (global.get $counter) + ;; TNH-NEXT: (i32.const 3) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; VACUUM: (func $side-effect-3 (type $sig-none) + ;; VACUUM-NEXT: (global.set $counter + ;; VACUUM-NEXT: (i32.add + ;; VACUUM-NEXT: (global.get $counter) + ;; VACUUM-NEXT: (i32.const 3) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + (func $side-effect-3 + (global.set $counter (i32.add (global.get $counter) (i32.const 3))) + ) + + ;; NO-TNH: (func $test-resume-null (type $sig-none) + ;; NO-TNH-NEXT: (unreachable) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $test-resume-null (type $sig-none) + ;; TNH-NEXT: (unreachable) + ;; TNH-NEXT: ) + ;; VACUUM: (func $test-resume-null (type $sig-none) + ;; VACUUM-NEXT: (unreachable) + ;; VACUUM-NEXT: ) + (func $test-resume-null + ;; Resume of null traps: replaced with unreachable. + (resume $cont-none + (ref.null $cont-none) + ) + ) + + ;; NO-TNH: (func $test-resume-null-operands (type $sig-none) + ;; NO-TNH-NEXT: (drop + ;; NO-TNH-NEXT: (block + ;; NO-TNH-NEXT: (drop + ;; NO-TNH-NEXT: (call $side-effect-1) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: (drop + ;; NO-TNH-NEXT: (call $side-effect-2) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: (unreachable) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $test-resume-null-operands (type $sig-none) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (block + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (call $side-effect-1) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (call $side-effect-2) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (unreachable) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; VACUUM: (func $test-resume-null-operands (type $sig-none) + ;; VACUUM-NEXT: (drop + ;; VACUUM-NEXT: (block + ;; VACUUM-NEXT: (drop + ;; VACUUM-NEXT: (call $side-effect-1) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: (drop + ;; VACUUM-NEXT: (call $side-effect-2) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: (unreachable) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + (func $test-resume-null-operands + ;; Operands are evaluated and dropped before the null trap. + (resume $cont-binary + (call $side-effect-1) + (call $side-effect-2) + (ref.null $cont-binary) + ) + (drop) + ) + + ;; NO-TNH: (func $test-resume-skip-non-null-cast (type $10) (param $c (ref null $cont-none)) + ;; NO-TNH-NEXT: (resume $cont-none + ;; NO-TNH-NEXT: (local.get $c) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $test-resume-skip-non-null-cast (type $10) (param $c (ref null $cont-none)) + ;; TNH-NEXT: (resume $cont-none + ;; TNH-NEXT: (local.get $c) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; VACUUM: (func $test-resume-skip-non-null-cast (type $10) (param $c (ref null $cont-none)) + ;; VACUUM-NEXT: (resume $cont-none + ;; VACUUM-NEXT: (local.get $c) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + (func $test-resume-skip-non-null-cast (param $c (ref null $cont-none)) + ;; Redundant ref.as_non_null on continuation is stripped because resume + ;; traps on null. + (resume $cont-none + (ref.as_non_null + (local.get $c) + ) + ) + ) + + ;; NO-TNH: (func $test-resume-null-arm-tnh (type $8) (param $cond i32) (param $c (ref $cont-none)) + ;; NO-TNH-NEXT: (resume $cont-none + ;; NO-TNH-NEXT: (if (result (ref null $cont-none)) + ;; NO-TNH-NEXT: (local.get $cond) + ;; NO-TNH-NEXT: (then + ;; NO-TNH-NEXT: (ref.null nocont) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: (else + ;; NO-TNH-NEXT: (local.get $c) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $test-resume-null-arm-tnh (type $8) (param $cond i32) (param $c (ref $cont-none)) + ;; TNH-NEXT: (resume $cont-none + ;; TNH-NEXT: (block (result (ref $cont-none)) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (local.get $cond) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (local.get $c) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; VACUUM: (func $test-resume-null-arm-tnh (type $8) (param $cond i32) (param $c (ref $cont-none)) + ;; VACUUM-NEXT: (resume $cont-none + ;; VACUUM-NEXT: (if (result (ref null $cont-none)) + ;; VACUUM-NEXT: (local.get $cond) + ;; VACUUM-NEXT: (then + ;; VACUUM-NEXT: (ref.null nocont) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: (else + ;; VACUUM-NEXT: (local.get $c) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + (func $test-resume-null-arm-tnh (param $cond i32) (param $c (ref $cont-none)) + ;; Under TNH, if-arms that flow out null are assumed never taken. + (resume $cont-none + (if (result (ref null $cont-none)) + (local.get $cond) + (then + (ref.null $cont-none) + ) + (else + (local.get $c) + ) + ) + ) + ) + + ;; NO-TNH: (func $test-resume-null-select-tnh (type $8) (param $cond i32) (param $c (ref $cont-none)) + ;; NO-TNH-NEXT: (resume $cont-none + ;; NO-TNH-NEXT: (select (result (ref null $cont-none)) + ;; NO-TNH-NEXT: (ref.null nocont) + ;; NO-TNH-NEXT: (local.get $c) + ;; NO-TNH-NEXT: (local.get $cond) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $test-resume-null-select-tnh (type $8) (param $cond i32) (param $c (ref $cont-none)) + ;; TNH-NEXT: (resume $cont-none + ;; TNH-NEXT: (block (result (ref $cont-none)) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (ref.null nocont) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (block (result (ref $cont-none)) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (local.get $cond) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (local.get $c) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; VACUUM: (func $test-resume-null-select-tnh (type $8) (param $cond i32) (param $c (ref $cont-none)) + ;; VACUUM-NEXT: (resume $cont-none + ;; VACUUM-NEXT: (select (result (ref null $cont-none)) + ;; VACUUM-NEXT: (ref.null nocont) + ;; VACUUM-NEXT: (local.get $c) + ;; VACUUM-NEXT: (local.get $cond) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + (func $test-resume-null-select-tnh (param $cond i32) (param $c (ref $cont-none)) + ;; Under TNH, select branches that flow out null are assumed never taken. + (resume $cont-none + (select (result (ref null $cont-none)) + (ref.null $cont-none) + (local.get $c) + (local.get $cond) + ) + ) + ) + + ;; NO-TNH: (func $test-basic (type $sig-none) + ;; NO-TNH-NEXT: (call $pure) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $test-basic (type $sig-none) + ;; TNH-NEXT: (call $pure) + ;; TNH-NEXT: ) + ;; VACUUM: (func $test-basic (type $sig-none) + ;; VACUUM-NEXT: (nop) + ;; VACUUM-NEXT: ) + (func $test-basic + ;; Basic rewrite: resume of non-suspending continuation becomes direct + ;; call. + (resume $cont-none + (cont.new $cont-none (ref.func $pure)) + ) + ) + + ;; NO-TNH: (func $test-binary (type $2) (result i32) + ;; NO-TNH-NEXT: (call $pure-binary + ;; NO-TNH-NEXT: (i32.const 1) + ;; NO-TNH-NEXT: (i32.const 2) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $test-binary (type $2) (result i32) + ;; TNH-NEXT: (call $pure-binary + ;; TNH-NEXT: (i32.const 1) + ;; TNH-NEXT: (i32.const 2) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; VACUUM: (func $test-binary (type $2) (result i32) + ;; VACUUM-NEXT: (call $pure-binary + ;; VACUUM-NEXT: (i32.const 1) + ;; VACUUM-NEXT: (i32.const 2) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + (func $test-binary (result i32) + ;; Operands and results: passed cleanly to the direct call. + (resume $cont-binary + (i32.const 1) + (i32.const 2) + (cont.new $cont-binary (ref.func $pure-binary)) + ) + ) + + ;; NO-TNH: (func $test-fallthrough-block (type $sig-none) + ;; NO-TNH-NEXT: (call $pure) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $test-fallthrough-block (type $sig-none) + ;; TNH-NEXT: (call $pure) + ;; TNH-NEXT: ) + ;; VACUUM: (func $test-fallthrough-block (type $sig-none) + ;; VACUUM-NEXT: (nop) + ;; VACUUM-NEXT: ) + (func $test-fallthrough-block + ;; Fallthrough cases: straight-line blocks and ref.cast. + (resume $cont-none + (block (result (ref $cont-none)) + (cont.new $cont-none + (block (result (ref $sig-none)) + (ref.func $pure) + ) + ) + ) + ) + ) + + ;; NO-TNH: (func $test-fallthrough-cast (type $sig-none) + ;; NO-TNH-NEXT: (call $pure) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $test-fallthrough-cast (type $sig-none) + ;; TNH-NEXT: (call $pure) + ;; TNH-NEXT: ) + ;; VACUUM: (func $test-fallthrough-cast (type $sig-none) + ;; VACUUM-NEXT: (nop) + ;; VACUUM-NEXT: ) + (func $test-fallthrough-cast + (resume $cont-none + (cont.new $cont-none + (ref.cast (ref $sig-none) + (ref.func $pure) + ) + ) + ) + ) + + ;; NO-TNH: (func $test-tee (type $11) (result (ref $cont-none)) + ;; NO-TNH-NEXT: (local $c (ref null $cont-none)) + ;; NO-TNH-NEXT: (resume $cont-none + ;; NO-TNH-NEXT: (local.tee $c + ;; NO-TNH-NEXT: (cont.new $cont-none + ;; NO-TNH-NEXT: (ref.func $pure) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: (ref.as_non_null + ;; NO-TNH-NEXT: (local.get $c) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $test-tee (type $11) (result (ref $cont-none)) + ;; TNH-NEXT: (local $c (ref null $cont-none)) + ;; TNH-NEXT: (local $1 (ref null $cont-none)) + ;; TNH-NEXT: (block + ;; TNH-NEXT: (local.set $1 + ;; TNH-NEXT: (local.tee $c + ;; TNH-NEXT: (cont.new $cont-none + ;; TNH-NEXT: (ref.func $pure) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (call $pure) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (ref.as_non_null + ;; TNH-NEXT: (local.get $c) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; VACUUM: (func $test-tee (type $11) (result (ref $cont-none)) + ;; VACUUM-NEXT: (local $c (ref null $cont-none)) + ;; VACUUM-NEXT: (resume $cont-none + ;; VACUUM-NEXT: (local.tee $c + ;; VACUUM-NEXT: (cont.new $cont-none + ;; VACUUM-NEXT: (ref.func $pure) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: (ref.as_non_null + ;; VACUUM-NEXT: (local.get $c) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + (func $test-tee (result (ref $cont-none)) + ;; trapsNeverHappen: local.tee is only looked through with TNH. + (local $c (ref null $cont-none)) + (resume $cont-none + (local.tee $c + (cont.new $cont-none (ref.func $pure)) + ) + ) + (ref.as_non_null (local.get $c)) + ) + + ;; NO-TNH: (func $test-tee-params (type $12) (result (ref $cont-binary)) + ;; NO-TNH-NEXT: (local $c (ref null $cont-binary)) + ;; NO-TNH-NEXT: (drop + ;; NO-TNH-NEXT: (resume $cont-binary + ;; NO-TNH-NEXT: (i32.const 1) + ;; NO-TNH-NEXT: (i32.const 2) + ;; NO-TNH-NEXT: (local.tee $c + ;; NO-TNH-NEXT: (cont.new $cont-binary + ;; NO-TNH-NEXT: (ref.func $pure-binary) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: (ref.as_non_null + ;; NO-TNH-NEXT: (local.get $c) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $test-tee-params (type $12) (result (ref $cont-binary)) + ;; TNH-NEXT: (local $c (ref null $cont-binary)) + ;; TNH-NEXT: (local $1 (ref null $cont-binary)) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (block (result i32) + ;; TNH-NEXT: (local.set $1 + ;; TNH-NEXT: (local.tee $c + ;; TNH-NEXT: (cont.new $cont-binary + ;; TNH-NEXT: (ref.func $pure-binary) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (call $pure-binary + ;; TNH-NEXT: (i32.const 1) + ;; TNH-NEXT: (i32.const 2) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (ref.as_non_null + ;; TNH-NEXT: (local.get $c) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; VACUUM: (func $test-tee-params (type $12) (result (ref $cont-binary)) + ;; VACUUM-NEXT: (local $c (ref null $cont-binary)) + ;; VACUUM-NEXT: (drop + ;; VACUUM-NEXT: (resume $cont-binary + ;; VACUUM-NEXT: (i32.const 1) + ;; VACUUM-NEXT: (i32.const 2) + ;; VACUUM-NEXT: (local.tee $c + ;; VACUUM-NEXT: (cont.new $cont-binary + ;; VACUUM-NEXT: (ref.func $pure-binary) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: (ref.as_non_null + ;; VACUUM-NEXT: (local.get $c) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + (func $test-tee-params (result (ref $cont-binary)) + ;; trapsNeverHappen: local.tee with operands is only looked through with + ;; TNH. + (local $c (ref null $cont-binary)) + (drop + (resume $cont-binary + (i32.const 1) + (i32.const 2) + (local.tee $c + (cont.new $cont-binary (ref.func $pure-binary)) + ) + ) + ) + (ref.as_non_null (local.get $c)) + ) + + ;; NO-TNH: (func $test-negative-suspend (type $sig-none) + ;; NO-TNH-NEXT: (drop + ;; NO-TNH-NEXT: (block $on-suspend (result (ref $cont-none)) + ;; NO-TNH-NEXT: (resume $cont-none (on $tag $on-suspend) + ;; NO-TNH-NEXT: (cont.new $cont-none + ;; NO-TNH-NEXT: (ref.func $suspending) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: (unreachable) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $test-negative-suspend (type $sig-none) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (block $on-suspend (result (ref $cont-none)) + ;; TNH-NEXT: (resume $cont-none (on $tag $on-suspend) + ;; TNH-NEXT: (cont.new $cont-none + ;; TNH-NEXT: (ref.func $suspending) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (unreachable) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; VACUUM: (func $test-negative-suspend (type $sig-none) + ;; VACUUM-NEXT: (drop + ;; VACUUM-NEXT: (block $on-suspend (result (ref $cont-none)) + ;; VACUUM-NEXT: (resume $cont-none (on $tag $on-suspend) + ;; VACUUM-NEXT: (cont.new $cont-none + ;; VACUUM-NEXT: (ref.func $suspending) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: (unreachable) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + (func $test-negative-suspend + ;; Negative test: continuation function that suspends cannot be converted. + (block $on-suspend (result (ref $cont-none)) + (resume $cont-none (on $tag $on-suspend) + (cont.new $cont-none (ref.func $suspending)) + ) + (unreachable) + ) + (drop) + ) + + ;; NO-TNH: (func $test-negative-transitive-suspend (type $sig-none) + ;; NO-TNH-NEXT: (drop + ;; NO-TNH-NEXT: (block $on-suspend (result (ref $cont-none)) + ;; NO-TNH-NEXT: (resume $cont-none (on $tag $on-suspend) + ;; NO-TNH-NEXT: (cont.new $cont-none + ;; NO-TNH-NEXT: (ref.func $caller-of-suspending) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: (unreachable) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $test-negative-transitive-suspend (type $sig-none) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (block $on-suspend (result (ref $cont-none)) + ;; TNH-NEXT: (resume $cont-none (on $tag $on-suspend) + ;; TNH-NEXT: (cont.new $cont-none + ;; TNH-NEXT: (ref.func $caller-of-suspending) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (unreachable) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; VACUUM: (func $test-negative-transitive-suspend (type $sig-none) + ;; VACUUM-NEXT: (drop + ;; VACUUM-NEXT: (block $on-suspend (result (ref $cont-none)) + ;; VACUUM-NEXT: (resume $cont-none (on $tag $on-suspend) + ;; VACUUM-NEXT: (cont.new $cont-none + ;; VACUUM-NEXT: (ref.func $caller-of-suspending) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: (unreachable) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + (func $test-negative-transitive-suspend + ;; Negative test: continuation function calling a suspending function. + (block $on-suspend (result (ref $cont-none)) + (resume $cont-none (on $tag $on-suspend) + (cont.new $cont-none (ref.func $caller-of-suspending)) + ) + (unreachable) + ) + (drop) + ) + + ;; NO-TNH: (func $test-same-sig (type $2) (result i32) + ;; NO-TNH-NEXT: (call $same-sig-pure + ;; NO-TNH-NEXT: (i32.const 42) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $test-same-sig (type $2) (result i32) + ;; TNH-NEXT: (call $same-sig-pure + ;; TNH-NEXT: (i32.const 42) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; VACUUM: (func $test-same-sig (type $2) (result i32) + ;; VACUUM-NEXT: (call $same-sig-pure + ;; VACUUM-NEXT: (i32.const 42) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + (func $test-same-sig (result i32) + ;; Precision test: function $f does not suspend, but another function with + ;; the same signature does. Verify $f is still converted. + (resume $cont-unary + (i32.const 42) + (cont.new $cont-unary (ref.func $same-sig-pure)) + ) + ) + + ;; NO-TNH: (func $test-eval-order (type $2) (result i32) + ;; NO-TNH-NEXT: (local $0 i32) + ;; NO-TNH-NEXT: (local $1 i32) + ;; NO-TNH-NEXT: (local $2 (ref $cont-binary)) + ;; NO-TNH-NEXT: (local.set $0 + ;; NO-TNH-NEXT: (call $side-effect-1) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: (local.set $1 + ;; NO-TNH-NEXT: (call $side-effect-2) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: (local.set $2 + ;; NO-TNH-NEXT: (block (result (ref $cont-binary)) + ;; NO-TNH-NEXT: (call $side-effect-3) + ;; NO-TNH-NEXT: (cont.new $cont-binary + ;; NO-TNH-NEXT: (ref.func $pure-binary) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: (call $pure-binary + ;; NO-TNH-NEXT: (local.get $0) + ;; NO-TNH-NEXT: (local.get $1) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $test-eval-order (type $2) (result i32) + ;; TNH-NEXT: (local $0 i32) + ;; TNH-NEXT: (local $1 i32) + ;; TNH-NEXT: (local $2 (ref $cont-binary)) + ;; TNH-NEXT: (local.set $0 + ;; TNH-NEXT: (call $side-effect-1) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (local.set $1 + ;; TNH-NEXT: (call $side-effect-2) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (local.set $2 + ;; TNH-NEXT: (block (result (ref $cont-binary)) + ;; TNH-NEXT: (call $side-effect-3) + ;; TNH-NEXT: (cont.new $cont-binary + ;; TNH-NEXT: (ref.func $pure-binary) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (call $pure-binary + ;; TNH-NEXT: (local.get $0) + ;; TNH-NEXT: (local.get $1) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; VACUUM: (func $test-eval-order (type $2) (result i32) + ;; VACUUM-NEXT: (local $0 i32) + ;; VACUUM-NEXT: (local $1 i32) + ;; VACUUM-NEXT: (local $2 (ref $cont-binary)) + ;; VACUUM-NEXT: (local.set $0 + ;; VACUUM-NEXT: (call $side-effect-1) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: (local.set $1 + ;; VACUUM-NEXT: (call $side-effect-2) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: (local.set $2 + ;; VACUUM-NEXT: (block (result (ref (exact $cont-binary))) + ;; VACUUM-NEXT: (call $side-effect-3) + ;; VACUUM-NEXT: (cont.new $cont-binary + ;; VACUUM-NEXT: (ref.func $pure-binary) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: (call $pure-binary + ;; VACUUM-NEXT: (local.get $0) + ;; VACUUM-NEXT: (local.get $1) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + (func $test-eval-order (result i32) + ;; Evaluation order test: side effects in continuation expression execute + ;; after operands and before the direct call. + (resume $cont-binary + (call $side-effect-1) + (call $side-effect-2) + (block (result (ref $cont-binary)) + (call $side-effect-3) + (cont.new $cont-binary (ref.func $pure-binary)) + ) + ) + ) + + ;; NO-TNH: (func $test-handler-elimination (type $sig-none) + ;; NO-TNH-NEXT: (drop + ;; NO-TNH-NEXT: (block $handler (result (ref $cont-none)) + ;; NO-TNH-NEXT: (block + ;; NO-TNH-NEXT: (call $pure) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: (return) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $test-handler-elimination (type $sig-none) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (block $handler (result (ref $cont-none)) + ;; TNH-NEXT: (block + ;; TNH-NEXT: (call $pure) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (return) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; VACUUM: (func $test-handler-elimination (type $sig-none) + ;; VACUUM-NEXT: (nop) + ;; VACUUM-NEXT: ) + (func $test-handler-elimination + ;; Handler elimination test: dead handler blocks are removed in full + ;; pipeline. + (block $handler (result (ref $cont-none)) + (resume $cont-none (on $tag $handler) + (cont.new $cont-none (ref.func $pure)) + ) + (return) + ) + (drop) + ) + + ;; NO-TNH: (func $test-unreachable-operand (type $sig-none) + ;; NO-TNH-NEXT: (drop + ;; NO-TNH-NEXT: (resume $cont-binary + ;; NO-TNH-NEXT: (unreachable) + ;; NO-TNH-NEXT: (i32.const 1) + ;; NO-TNH-NEXT: (cont.new $cont-binary + ;; NO-TNH-NEXT: (ref.func $pure-binary) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $test-unreachable-operand (type $sig-none) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (resume $cont-binary + ;; TNH-NEXT: (unreachable) + ;; TNH-NEXT: (i32.const 1) + ;; TNH-NEXT: (cont.new $cont-binary + ;; TNH-NEXT: (ref.func $pure-binary) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; VACUUM: (func $test-unreachable-operand (type $sig-none) + ;; VACUUM-NEXT: (drop + ;; VACUUM-NEXT: (resume $cont-binary + ;; VACUUM-NEXT: (unreachable) + ;; VACUUM-NEXT: (i32.const 1) + ;; VACUUM-NEXT: (cont.new $cont-binary + ;; VACUUM-NEXT: (ref.func $pure-binary) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + (func $test-unreachable-operand + ;; Unreachable operands or continuation: resume is unreachable, left for + ;; DCE. + (resume $cont-binary + (unreachable) + (i32.const 1) + (cont.new $cont-binary (ref.func $pure-binary)) + ) + (drop) + ) + + ;; NO-TNH: (func $test-unreachable-cont (type $sig-none) + ;; NO-TNH-NEXT: (block ;; (replaces unreachable Resume we can't emit) + ;; NO-TNH-NEXT: (drop + ;; NO-TNH-NEXT: (unreachable) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: (unreachable) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $test-unreachable-cont (type $sig-none) + ;; TNH-NEXT: (block ;; (replaces unreachable Resume we can't emit) + ;; TNH-NEXT: (drop + ;; TNH-NEXT: (unreachable) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (unreachable) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; VACUUM: (func $test-unreachable-cont (type $sig-none) + ;; VACUUM-NEXT: (block ;; (replaces unreachable Resume we can't emit) + ;; VACUUM-NEXT: (drop + ;; VACUUM-NEXT: (unreachable) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: (unreachable) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + (func $test-unreachable-cont + ;; Unreachable continuation: resume is unreachable, left for DCE. + (resume $cont-none + (unreachable) + ) + ) + + ;; NO-TNH: (func $test-negative-imported (type $sig-none) + ;; NO-TNH-NEXT: (resume $cont-none + ;; NO-TNH-NEXT: (cont.new $cont-none + ;; NO-TNH-NEXT: (ref.func $imported) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $test-negative-imported (type $sig-none) + ;; TNH-NEXT: (resume $cont-none + ;; TNH-NEXT: (cont.new $cont-none + ;; TNH-NEXT: (ref.func $imported) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; VACUUM: (func $test-negative-imported (type $sig-none) + ;; VACUUM-NEXT: (resume $cont-none + ;; VACUUM-NEXT: (cont.new $cont-none + ;; VACUUM-NEXT: (ref.func $imported) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + (func $test-negative-imported + ;; Negative test: continuation function is imported and cannot be proven + ;; non-suspending. + (resume $cont-none + (cont.new $cont-none (ref.func $imported)) + ) + ) + + ;; NO-TNH: (func $test-handler-switch (type $sig-none) + ;; NO-TNH-NEXT: (call $pure) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $test-handler-switch (type $sig-none) + ;; TNH-NEXT: (call $pure) + ;; TNH-NEXT: ) + ;; VACUUM: (func $test-handler-switch (type $sig-none) + ;; VACUUM-NEXT: (nop) + ;; VACUUM-NEXT: ) + (func $test-handler-switch + ;; Handler elimination test: switch handlers are also eliminated when + ;; directizing. + (resume $cont-none (on $tag switch) + (cont.new $cont-none (ref.func $pure)) + ) + ) + + ;; NO-TNH: (func $test-eval-order-no-operands (type $sig-none) + ;; NO-TNH-NEXT: (local $0 (ref $cont-none)) + ;; NO-TNH-NEXT: (local.set $0 + ;; NO-TNH-NEXT: (block (result (ref $cont-none)) + ;; NO-TNH-NEXT: (call $side-effect-3) + ;; NO-TNH-NEXT: (cont.new $cont-none + ;; NO-TNH-NEXT: (ref.func $pure) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: (call $pure) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $test-eval-order-no-operands (type $sig-none) + ;; TNH-NEXT: (local $0 (ref $cont-none)) + ;; TNH-NEXT: (local.set $0 + ;; TNH-NEXT: (block (result (ref $cont-none)) + ;; TNH-NEXT: (call $side-effect-3) + ;; TNH-NEXT: (cont.new $cont-none + ;; TNH-NEXT: (ref.func $pure) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (call $pure) + ;; TNH-NEXT: ) + ;; VACUUM: (func $test-eval-order-no-operands (type $sig-none) + ;; VACUUM-NEXT: (local $0 (ref $cont-none)) + ;; VACUUM-NEXT: (local.set $0 + ;; VACUUM-NEXT: (block (result (ref (exact $cont-none))) + ;; VACUUM-NEXT: (call $side-effect-3) + ;; VACUUM-NEXT: (cont.new $cont-none + ;; VACUUM-NEXT: (ref.func $pure) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + (func $test-eval-order-no-operands + ;; Evaluation order test: side effects in continuation expression execute + ;; before the direct call even when there are no operands. + (resume $cont-none + (block (result (ref $cont-none)) + (call $side-effect-3) + (cont.new $cont-none (ref.func $pure)) + ) + ) + ) + + ;; NO-TNH: (func $test-eval-order-non-nullable (type $sig-any) (param $x (ref any)) (result (ref any)) + ;; NO-TNH-NEXT: (local $1 (ref $cont-any)) + ;; NO-TNH-NEXT: (local.set $1 + ;; NO-TNH-NEXT: (block (result (ref $cont-any)) + ;; NO-TNH-NEXT: (call $side-effect-3) + ;; NO-TNH-NEXT: (cont.new $cont-any + ;; NO-TNH-NEXT: (ref.func $pure-any) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: (call $pure-any + ;; NO-TNH-NEXT: (local.get $x) + ;; NO-TNH-NEXT: ) + ;; NO-TNH-NEXT: ) + ;; TNH: (func $test-eval-order-non-nullable (type $sig-any) (param $x (ref any)) (result (ref any)) + ;; TNH-NEXT: (local $1 (ref $cont-any)) + ;; TNH-NEXT: (local.set $1 + ;; TNH-NEXT: (block (result (ref $cont-any)) + ;; TNH-NEXT: (call $side-effect-3) + ;; TNH-NEXT: (cont.new $cont-any + ;; TNH-NEXT: (ref.func $pure-any) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; TNH-NEXT: (call $pure-any + ;; TNH-NEXT: (local.get $x) + ;; TNH-NEXT: ) + ;; TNH-NEXT: ) + ;; VACUUM: (func $test-eval-order-non-nullable (type $sig-any) (param $x (ref any)) (result (ref any)) + ;; VACUUM-NEXT: (local $1 (ref $cont-any)) + ;; VACUUM-NEXT: (local.set $1 + ;; VACUUM-NEXT: (block (result (ref (exact $cont-any))) + ;; VACUUM-NEXT: (call $side-effect-3) + ;; VACUUM-NEXT: (cont.new $cont-any + ;; VACUUM-NEXT: (ref.func $pure-any) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: (call $pure-any + ;; VACUUM-NEXT: (local.get $x) + ;; VACUUM-NEXT: ) + ;; VACUUM-NEXT: ) + (func $test-eval-order-non-nullable (param $x (ref any)) (result (ref any)) + ;; Evaluation order test: non-nullable operands are preserved when spilled. + (resume $cont-any + (local.get $x) + (block (result (ref $cont-any)) + (call $side-effect-3) + (cont.new $cont-any (ref.func $pure-any)) + ) + ) + ) + +)