diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/lit/ctor-eval/return_call.wast | 529 | ||||
-rw-r--r-- | test/lit/passes/global-effects.wast | 222 | ||||
-rw-r--r-- | test/lit/passes/inlining-unreachable.wast | 19 | ||||
-rw-r--r-- | test/lit/passes/inlining_all-features.wast | 7 | ||||
-rw-r--r-- | test/lit/passes/inlining_enable-tail-call.wast | 519 | ||||
-rw-r--r-- | test/lit/passes/simplify-locals-eh-old.wast | 51 | ||||
-rw-r--r-- | test/lit/passes/vacuum-eh-old.wast | 79 | ||||
-rw-r--r-- | test/spec/return_call.wast | 202 | ||||
-rw-r--r-- | test/spec/return_call_eh.wast | 35 | ||||
-rw-r--r-- | test/spec/return_call_indirect.wast | 536 | ||||
-rw-r--r-- | test/spec/return_call_ref.wast | 377 |
11 files changed, 2449 insertions, 127 deletions
diff --git a/test/lit/ctor-eval/return_call.wast b/test/lit/ctor-eval/return_call.wast new file mode 100644 index 000000000..3ff35ec42 --- /dev/null +++ b/test/lit/ctor-eval/return_call.wast @@ -0,0 +1,529 @@ +;; 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=test --kept-exports=test --ignore-external-input --quiet -all -g -S -o - | filecheck %s + +(module + ;; Simplest possible return call. + + (func $test (export "test") (result i32) + (return_call $test2) + ) + + (func $test2 (result i32) + (i32.const 42) + ) +) + +;; CHECK: (type $0 (func (result i32))) + +;; CHECK: (export "test" (func $test_2)) + +;; CHECK: (func $test_2 (type $0) (result i32) +;; CHECK-NEXT: (i32.const 42) +;; CHECK-NEXT: ) +(module + ;; Basic return call (followed by unreachable import call, setting global as proof it was executed) + + (import "env" "import" (func $import)) + + ;; CHECK: (type $0 (func)) + + ;; CHECK: (global $g1 (mut i32) (i32.const 1)) + (global $g1 (export "g1") (mut i32) (i32.const 0)) + + ;; CHECK: (global $g2 (mut i32) (i32.const 2)) + (global $g2 (export "g2") (mut i32) (i32.const 0)) + + (func $test (export "test") + (global.set $g1 + (i32.const 1) + ) + (return_call $test2) + ;; This is never executed, so it should not impede eval. + (call $import) + ) + + (func $test2 + (global.set $g2 + (i32.const 2) + ) + ) +) + +;; CHECK: (export "g1" (global $g1)) + +;; CHECK: (export "g2" (global $g2)) + +;; CHECK: (export "test" (func $test_3)) + +;; CHECK: (func $test_3 (type $0) +;; CHECK-NEXT: (nop) +;; CHECK-NEXT: ) +(module + ;; Basic return call indirect + ;; TODO: Implement `tableLoad` to make this test work. + + (import "env" "import" (func $import)) + + ;; CHECK: (type $0 (func)) + + ;; CHECK: (global $g1 (mut i32) (i32.const 1)) + (global $g1 (export "g1") (mut i32) (i32.const 0)) + + ;; CHECK: (global $g2 (mut i32) (i32.const 0)) + (global $g2 (export "g2") (mut i32) (i32.const 0)) + + ;; CHECK: (table $t 1 1 funcref) + (table $t funcref (elem $test2)) + + (func $test (export "test") + (global.set $g1 + (i32.const 1) + ) + (return_call_indirect $t + (i32.const 0) + ) + ;; This is never executed, so it should not impede eval. + (call $import) + ) + + ;; CHECK: (elem $0 (i32.const 0) $test2) + + ;; CHECK: (export "g1" (global $g1)) + + ;; CHECK: (export "g2" (global $g2)) + + ;; CHECK: (export "test" (func $test_3)) + + ;; CHECK: (func $test2 (type $0) + ;; CHECK-NEXT: (global.set $g2 + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test2 + (global.set $g2 + (i32.const 2) + ) + ) +) + +;; CHECK: (func $test_3 (type $0) +;; CHECK-NEXT: (return_call_indirect $t (type $0) +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +(module + ;; Basic return call ref + ;; CHECK: (type $f (func)) + (type $f (func)) + + (import "env" "import" (func $import)) + + ;; CHECK: (global $g1 (mut i32) (i32.const 1)) + (global $g1 (export "g1") (mut i32) (i32.const 0)) + + ;; CHECK: (global $g2 (mut i32) (i32.const 2)) + (global $g2 (export "g2") (mut i32) (i32.const 0)) + + (elem declare $test2) + + (func $test (export "test") + (global.set $g1 + (i32.const 1) + ) + (return_call_ref $f + (ref.func $test2) + ) + ;; This is never executed, so it should not impede eval. + (call $import) + ) + + (func $test2 (type $f) + (global.set $g2 + (i32.const 2) + ) + ) +) + +;; CHECK: (export "g1" (global $g1)) + +;; CHECK: (export "g2" (global $g2)) + +;; CHECK: (export "test" (func $test_3)) + +;; CHECK: (func $test_3 (type $f) +;; CHECK-NEXT: (nop) +;; CHECK-NEXT: ) +(module + ;; Return call to import + + ;; CHECK: (type $0 (func)) + + ;; CHECK: (import "env" "import" (func $import (type $0))) + (import "env" "import" (func $import)) + + ;; CHECK: (global $g1 (mut i32) (i32.const 1)) + (global $g1 (export "g1") (mut i32) (i32.const 0)) + + (func $test (export "test") + (global.set $g1 + (i32.const 1) + ) + (return_call $import) + ) +) + +;; CHECK: (export "g1" (global $g1)) + +;; CHECK: (export "test" (func $test_2)) + +;; CHECK: (func $test_2 (type $0) +;; CHECK-NEXT: (return_call $import) +;; CHECK-NEXT: ) +(module + ;; Return call to import with params + + ;; CHECK: (type $0 (func (param i32))) + + ;; CHECK: (type $1 (func)) + + ;; CHECK: (import "env" "import" (func $import (type $0) (param i32))) + (import "env" "import" (func $import (param i32))) + + ;; CHECK: (global $g1 (mut i32) (i32.const 1)) + (global $g1 (export "g1") (mut i32) (i32.const 0)) + + (func $test (export "test") + (global.set $g1 + (i32.const 1) + ) + (return_call $import + (i32.add + (i32.const 40) + (i32.const 2) + ) + ) + ) +) + +;; CHECK: (export "g1" (global $g1)) + +;; CHECK: (export "test" (func $test_2)) + +;; CHECK: (func $test_2 (type $1) +;; CHECK-NEXT: (return_call $import +;; CHECK-NEXT: (i32.const 42) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +(module + ;; Chain of return calls ending in import + + ;; CHECK: (type $0 (func (param i32))) + + ;; CHECK: (type $1 (func)) + + ;; CHECK: (import "env" "import" (func $import (type $0) (param i32))) + (import "env" "import" (func $import (param i32))) + + ;; CHECK: (global $g1 (mut i32) (i32.const 1)) + (global $g1 (export "g1") (mut i32) (i32.const 0)) + + ;; CHECK: (global $g2 (mut i32) (i32.const 2)) + (global $g2 (export "g2") (mut i32) (i32.const 0)) + + (func $test (export "test") + (global.set $g1 + (i32.const 1) + ) + (return_call $test2 + (i32.const 40) + ) + ) + + (func $test2 (param i32) + (global.set $g2 + (i32.const 2) + ) + (return_call $import + (i32.add + (i32.const 2) + (local.get 0) + ) + ) + ) +) + +;; CHECK: (export "g1" (global $g1)) + +;; CHECK: (export "g2" (global $g2)) + +;; CHECK: (export "test" (func $test_3)) + +;; CHECK: (func $test_3 (type $1) +;; CHECK-NEXT: (return_call $import +;; CHECK-NEXT: (i32.const 42) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +(module + ;; Return call to a function that can only be partially evaluated. + ;; CHECK: (type $0 (func)) + + ;; CHECK: (import "env" "import" (func $import (type $0))) + (import "env" "import" (func $import)) + + ;; CHECK: (global $g1 (mut i32) (i32.const 1)) + (global $g1 (export "g1") (mut i32) (i32.const 0)) + + ;; CHECK: (global $g2 (mut i32) (i32.const 2)) + (global $g2 (export "g2") (mut i32) (i32.const 0)) + + (func $test (export "test") + (global.set $g1 + (i32.const 1) + ) + (return_call $test2) + ) + + (func $test2 + (global.set $g2 + (i32.const 2) + ) + (call $import) + ) +) + +;; CHECK: (export "g1" (global $g1)) + +;; CHECK: (export "g2" (global $g2)) + +;; CHECK: (export "test" (func $test_3)) + +;; CHECK: (func $test_3 (type $0) +;; CHECK-NEXT: (call $import) +;; CHECK-NEXT: ) +(module + ;; Return call with parameters to a function that can only be partially evaluated. + + ;; CHECK: (type $0 (func (param i32) (result i32))) + + ;; CHECK: (type $1 (func (param i32))) + + ;; CHECK: (import "env" "import" (func $import (type $0) (param i32) (result i32))) + (import "env" "import" (func $import (param i32) (result i32))) + + ;; CHECK: (global $g1 (mut i32) (i32.const 1)) + (global $g1 (export "g1") (mut i32) (i32.const 0)) + + ;; CHECK: (global $g2 (mut i32) (i32.const 2)) + (global $g2 (export "g2") (mut i32) (i32.const 0)) + + (func $test (export "test") (param i32) + (global.set $g1 + (i32.const 1) + ) + (return_call $test2 + (local.get 0) + ) + ) + + (func $test2 (param i32) + (local $x i32) + (global.set $g2 + (i32.const 2) + ) + (local.set $x + (call $import + (local.get 0) + ) + ) + (drop + (call $import + (i32.const 0) + ) + ) + (drop + (call $import + (local.get $x) + ) + ) + ) +) + +;; CHECK: (export "g1" (global $g1)) + +;; CHECK: (export "g2" (global $g2)) + +;; CHECK: (export "test" (func $test_3)) + +;; CHECK: (func $test_3 (type $1) (param $0 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (call $import +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $import +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $import +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +(module + ;; Return call with parameters to a function that can only be partially + ;; evaluated that takes different parameters from the original. + ;; CHECK: (type $0 (func (param i32) (result i32))) + + ;; CHECK: (type $1 (func (param i32))) + + ;; CHECK: (import "env" "import" (func $import (type $0) (param i32) (result i32))) + (import "env" "import" (func $import (param i32) (result i32))) + + ;; CHECK: (global $g1 (mut i32) (i32.const 1)) + (global $g1 (export "g1") (mut i32) (i32.const 0)) + + ;; CHECK: (global $g2 (mut i32) (i32.const 2)) + (global $g2 (export "g2") (mut i32) (i32.const 0)) + + (func $test (export "test") (param i32) + (global.set $g1 + (i32.const 1) + ) + (return_call $test2 + (i64.const 1) + (i64.const 2) + (i64.const 3) + ) + ) + + (func $test2 (param i64 i64 i64) + (local $x i32) + (global.set $g2 + (i32.const 2) + ) + (local.set $x + (call $import + (i32.wrap_i64 + (local.get 2) + ) + ) + ) + (drop + (call $import + (i32.const 0) + ) + ) + (drop + (call $import + (local.get $x) + ) + ) + ) +) + +;; CHECK: (export "g1" (global $g1)) + +;; CHECK: (export "g2" (global $g2)) + +;; CHECK: (export "test" (func $test_3)) + +;; CHECK: (func $test_3 (type $1) (param $0 i32) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (call $import +;; CHECK-NEXT: (i32.const 3) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $import +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (drop +;; CHECK-NEXT: (call $import +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +(module + ;; Return call to self with different params, then stop evaluating. + ;; CHECK: (type $0 (func (param i32))) + + ;; CHECK: (type $1 (func)) + + ;; CHECK: (import "env" "import" (func $import (type $1))) + (import "env" "import" (func $import)) + + ;; CHECK: (global $g (mut i32) (i32.const 42)) + (global $g (mut i32) (i32.const 0)) + + ;; CHECK: (export "test" (func $test_2)) + + ;; CHECK: (func $test (type $0) (param $0 i32) + ;; CHECK-NEXT: (global.set $g + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (return_call $test + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test (export "test") (param i32) + (global.set $g + (local.get 0) + ) + (if + (i32.eq + (local.get 0) + (i32.const 42) + ) + (then + (call $import) + ) + (else + (return_call $test + (i32.add + (local.get 0) + (i32.const 1) + ) + ) + ) + ) + ) +) + +;; CHECK: (func $test_2 (type $0) (param $0 i32) +;; CHECK-NEXT: (if +;; CHECK-NEXT: (i32.eq +;; CHECK-NEXT: (local.tee $0 +;; CHECK-NEXT: (i32.const 42) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (i32.const 42) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (then +;; CHECK-NEXT: (call $import) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (else +;; CHECK-NEXT: (return_call $test +;; CHECK-NEXT: (i32.add +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) diff --git a/test/lit/passes/global-effects.wast b/test/lit/passes/global-effects.wast index 3f4e58e8b..38e96c6eb 100644 --- a/test/lit/passes/global-effects.wast +++ b/test/lit/passes/global-effects.wast @@ -8,27 +8,36 @@ ;; RUN: foreach %s %t wasm-opt -all --generate-global-effects --discard-global-effects --vacuum -S -o - | filecheck %s --check-prefix WITHOUT (module - ;; WITHOUT: (type $0 (func)) + + ;; WITHOUT: (type $void (func)) + ;; INCLUDE: (type $void (func)) + (type $void (func)) ;; WITHOUT: (type $1 (func (result i32))) ;; WITHOUT: (type $2 (func (param i32))) - ;; WITHOUT: (import "a" "b" (func $import (type $0))) - ;; INCLUDE: (type $0 (func)) - + ;; WITHOUT: (import "a" "b" (func $import (type $void))) ;; INCLUDE: (type $1 (func (result i32))) ;; INCLUDE: (type $2 (func (param i32))) - ;; INCLUDE: (import "a" "b" (func $import (type $0))) + ;; INCLUDE: (import "a" "b" (func $import (type $void))) (import "a" "b" (func $import)) + ;; WITHOUT: (table $t 0 funcref) + ;; INCLUDE: (table $t 0 funcref) + (table $t funcref 0) + + ;; WITHOUT: (elem declare func $throw) + ;; WITHOUT: (tag $tag) + ;; INCLUDE: (elem declare func $throw) + ;; INCLUDE: (tag $tag) (tag $tag) - ;; WITHOUT: (func $main (type $0) + ;; WITHOUT: (func $main (type $void) ;; WITHOUT-NEXT: (call $nop) ;; WITHOUT-NEXT: (call $unreachable) ;; WITHOUT-NEXT: (call $call-nop) @@ -39,7 +48,7 @@ ;; WITHOUT-NEXT: (call $throw) ;; WITHOUT-NEXT: (call $throw-and-import) ;; WITHOUT-NEXT: ) - ;; INCLUDE: (func $main (type $0) + ;; INCLUDE: (func $main (type $void) ;; INCLUDE-NEXT: (call $unreachable) ;; INCLUDE-NEXT: (call $call-unreachable) ;; INCLUDE-NEXT: (call $throw) @@ -67,10 +76,10 @@ (call $throw-and-import) ) - ;; WITHOUT: (func $cycle (type $0) + ;; WITHOUT: (func $cycle (type $void) ;; WITHOUT-NEXT: (call $cycle) ;; WITHOUT-NEXT: ) - ;; INCLUDE: (func $cycle (type $0) + ;; INCLUDE: (func $cycle (type $void) ;; INCLUDE-NEXT: (call $cycle) ;; INCLUDE-NEXT: ) (func $cycle @@ -79,10 +88,10 @@ (call $cycle) ) - ;; WITHOUT: (func $cycle-1 (type $0) + ;; WITHOUT: (func $cycle-1 (type $void) ;; WITHOUT-NEXT: (call $cycle-2) ;; WITHOUT-NEXT: ) - ;; INCLUDE: (func $cycle-1 (type $0) + ;; INCLUDE: (func $cycle-1 (type $void) ;; INCLUDE-NEXT: (call $cycle-2) ;; INCLUDE-NEXT: ) (func $cycle-1 @@ -90,40 +99,40 @@ (call $cycle-2) ) - ;; WITHOUT: (func $cycle-2 (type $0) + ;; WITHOUT: (func $cycle-2 (type $void) ;; WITHOUT-NEXT: (call $cycle-1) ;; WITHOUT-NEXT: ) - ;; INCLUDE: (func $cycle-2 (type $0) + ;; INCLUDE: (func $cycle-2 (type $void) ;; INCLUDE-NEXT: (call $cycle-1) ;; INCLUDE-NEXT: ) (func $cycle-2 (call $cycle-1) ) - ;; WITHOUT: (func $nop (type $0) + ;; WITHOUT: (func $nop (type $void) ;; WITHOUT-NEXT: (nop) ;; WITHOUT-NEXT: ) - ;; INCLUDE: (func $nop (type $0) + ;; INCLUDE: (func $nop (type $void) ;; INCLUDE-NEXT: (nop) ;; INCLUDE-NEXT: ) (func $nop (nop) ) - ;; WITHOUT: (func $unreachable (type $0) + ;; WITHOUT: (func $unreachable (type $void) ;; WITHOUT-NEXT: (unreachable) ;; WITHOUT-NEXT: ) - ;; INCLUDE: (func $unreachable (type $0) + ;; INCLUDE: (func $unreachable (type $void) ;; INCLUDE-NEXT: (unreachable) ;; INCLUDE-NEXT: ) (func $unreachable (unreachable) ) - ;; WITHOUT: (func $call-nop (type $0) + ;; WITHOUT: (func $call-nop (type $void) ;; WITHOUT-NEXT: (call $nop) ;; WITHOUT-NEXT: ) - ;; INCLUDE: (func $call-nop (type $0) + ;; INCLUDE: (func $call-nop (type $void) ;; INCLUDE-NEXT: (nop) ;; INCLUDE-NEXT: ) (func $call-nop @@ -131,10 +140,10 @@ (call $nop) ) - ;; WITHOUT: (func $call-unreachable (type $0) + ;; WITHOUT: (func $call-unreachable (type $void) ;; WITHOUT-NEXT: (call $unreachable) ;; WITHOUT-NEXT: ) - ;; INCLUDE: (func $call-unreachable (type $0) + ;; INCLUDE: (func $call-unreachable (type $void) ;; INCLUDE-NEXT: (call $unreachable) ;; INCLUDE-NEXT: ) (func $call-unreachable @@ -172,7 +181,7 @@ ) ) - ;; WITHOUT: (func $call-throw-and-catch (type $0) + ;; WITHOUT: (func $call-throw-and-catch (type $void) ;; WITHOUT-NEXT: (try $try ;; WITHOUT-NEXT: (do ;; WITHOUT-NEXT: (call $throw) @@ -190,7 +199,7 @@ ;; WITHOUT-NEXT: ) ;; WITHOUT-NEXT: ) ;; WITHOUT-NEXT: ) - ;; INCLUDE: (func $call-throw-and-catch (type $0) + ;; INCLUDE: (func $call-throw-and-catch (type $void) ;; INCLUDE-NEXT: (try $try0 ;; INCLUDE-NEXT: (do ;; INCLUDE-NEXT: (call $throw-and-import) @@ -219,7 +228,158 @@ ) ) - ;; WITHOUT: (func $call-unreachable-and-catch (type $0) + ;; WITHOUT: (func $return-call-throw-and-catch (type $void) + ;; WITHOUT-NEXT: (return_call $throw) + ;; WITHOUT-NEXT: ) + ;; INCLUDE: (func $return-call-throw-and-catch (type $void) + ;; INCLUDE-NEXT: (return_call $throw) + ;; INCLUDE-NEXT: ) + (func $return-call-throw-and-catch + (try + (do + ;; This call cannot be optimized out, as the target throws. However, the + ;; surrounding try-catch can be removed even without global effects + ;; because the throw from the return_call is never observed by this + ;; try-catch. + (return_call $throw) + ) + (catch_all) + ) + ) + + ;; WITHOUT: (func $return-call-indirect-throw-and-catch (type $void) + ;; WITHOUT-NEXT: (return_call_indirect $t (type $void) + ;; WITHOUT-NEXT: (i32.const 0) + ;; WITHOUT-NEXT: ) + ;; WITHOUT-NEXT: ) + ;; INCLUDE: (func $return-call-indirect-throw-and-catch (type $void) + ;; INCLUDE-NEXT: (return_call_indirect $t (type $void) + ;; INCLUDE-NEXT: (i32.const 0) + ;; INCLUDE-NEXT: ) + ;; INCLUDE-NEXT: ) + (func $return-call-indirect-throw-and-catch + (try + (do + ;; This call cannot be optimized out, as the target may throw. However, + ;; the surrounding try-catch can be removed even without global effects + ;; because the throw from the return_call is never observed by this + ;; try-catch. + (return_call_indirect + (i32.const 0) + ) + ) + (catch_all) + ) + ) + + ;; WITHOUT: (func $return-call-ref-throw-and-catch (type $void) + ;; WITHOUT-NEXT: (return_call_ref $void + ;; WITHOUT-NEXT: (ref.func $throw) + ;; WITHOUT-NEXT: ) + ;; WITHOUT-NEXT: ) + ;; INCLUDE: (func $return-call-ref-throw-and-catch (type $void) + ;; INCLUDE-NEXT: (return_call_ref $void + ;; INCLUDE-NEXT: (ref.func $throw) + ;; INCLUDE-NEXT: ) + ;; INCLUDE-NEXT: ) + (func $return-call-ref-throw-and-catch + (try + (do + ;; This call cannot be optimized out, as the target may throw. However, + ;; the surrounding try-catch can be removed even without global effects + ;; because the throw from the return_call is never observed by this + ;; try-catch. + (return_call_ref $void + (ref.func $throw) + ) + ) + (catch_all) + ) + ) + + ;; WITHOUT: (func $call-return-call-throw-and-catch (type $void) + ;; WITHOUT-NEXT: (try $try + ;; WITHOUT-NEXT: (do + ;; WITHOUT-NEXT: (call $return-call-throw-and-catch) + ;; WITHOUT-NEXT: ) + ;; WITHOUT-NEXT: (catch_all + ;; WITHOUT-NEXT: (nop) + ;; WITHOUT-NEXT: ) + ;; WITHOUT-NEXT: ) + ;; WITHOUT-NEXT: (try $try1 + ;; WITHOUT-NEXT: (do + ;; WITHOUT-NEXT: (call $return-call-indirect-throw-and-catch) + ;; WITHOUT-NEXT: ) + ;; WITHOUT-NEXT: (catch_all + ;; WITHOUT-NEXT: (nop) + ;; WITHOUT-NEXT: ) + ;; WITHOUT-NEXT: ) + ;; WITHOUT-NEXT: (try $try2 + ;; WITHOUT-NEXT: (do + ;; WITHOUT-NEXT: (call $return-call-ref-throw-and-catch) + ;; WITHOUT-NEXT: ) + ;; WITHOUT-NEXT: (catch_all + ;; WITHOUT-NEXT: (nop) + ;; WITHOUT-NEXT: ) + ;; WITHOUT-NEXT: ) + ;; WITHOUT-NEXT: (call $return-call-throw-and-catch) + ;; WITHOUT-NEXT: (call $return-call-indirect-throw-and-catch) + ;; WITHOUT-NEXT: (call $return-call-ref-throw-and-catch) + ;; WITHOUT-NEXT: ) + ;; INCLUDE: (func $call-return-call-throw-and-catch (type $void) + ;; INCLUDE-NEXT: (try $try1 + ;; INCLUDE-NEXT: (do + ;; INCLUDE-NEXT: (call $return-call-indirect-throw-and-catch) + ;; INCLUDE-NEXT: ) + ;; INCLUDE-NEXT: (catch_all + ;; INCLUDE-NEXT: (nop) + ;; INCLUDE-NEXT: ) + ;; INCLUDE-NEXT: ) + ;; INCLUDE-NEXT: (try $try2 + ;; INCLUDE-NEXT: (do + ;; INCLUDE-NEXT: (call $return-call-ref-throw-and-catch) + ;; INCLUDE-NEXT: ) + ;; INCLUDE-NEXT: (catch_all + ;; INCLUDE-NEXT: (nop) + ;; INCLUDE-NEXT: ) + ;; INCLUDE-NEXT: ) + ;; INCLUDE-NEXT: (call $return-call-throw-and-catch) + ;; INCLUDE-NEXT: (call $return-call-indirect-throw-and-catch) + ;; INCLUDE-NEXT: (call $return-call-ref-throw-and-catch) + ;; INCLUDE-NEXT: ) + (func $call-return-call-throw-and-catch + (try + (do + ;; Even though the body of the previous function is a try-catch_all, the + ;; function still throws because of its return_call, so this cannot be + ;; optimized out, but once again the entire try-catch can be. + (call $return-call-throw-and-catch) + ) + (catch_all) + ) + (try + (do + ;; This would be the same, except since it performs an indirect call, we + ;; conservatively assume it could have any effect, so we can't optimize. + (call $return-call-indirect-throw-and-catch) + ) + (catch_all) + ) + (try + (do + ;; Same here. + (call $return-call-ref-throw-and-catch) + ) + (catch_all) + ) + + ;; These cannot be optimized out at all. + (call $return-call-throw-and-catch) + (call $return-call-indirect-throw-and-catch) + (call $return-call-ref-throw-and-catch) + ) + + ;; WITHOUT: (func $call-unreachable-and-catch (type $void) ;; WITHOUT-NEXT: (try $try ;; WITHOUT-NEXT: (do ;; WITHOUT-NEXT: (call $unreachable) @@ -229,7 +389,7 @@ ;; WITHOUT-NEXT: ) ;; WITHOUT-NEXT: ) ;; WITHOUT-NEXT: ) - ;; INCLUDE: (func $call-unreachable-and-catch (type $0) + ;; INCLUDE: (func $call-unreachable-and-catch (type $void) ;; INCLUDE-NEXT: (call $unreachable) ;; INCLUDE-NEXT: ) (func $call-unreachable-and-catch @@ -299,20 +459,20 @@ ) ) - ;; WITHOUT: (func $throw (type $0) + ;; WITHOUT: (func $throw (type $void) ;; WITHOUT-NEXT: (throw $tag) ;; WITHOUT-NEXT: ) - ;; INCLUDE: (func $throw (type $0) + ;; INCLUDE: (func $throw (type $void) ;; INCLUDE-NEXT: (throw $tag) ;; INCLUDE-NEXT: ) (func $throw (throw $tag) ) - ;; WITHOUT: (func $throw-and-import (type $0) + ;; WITHOUT: (func $throw-and-import (type $void) ;; WITHOUT-NEXT: (throw $tag) ;; WITHOUT-NEXT: ) - ;; INCLUDE: (func $throw-and-import (type $0) + ;; INCLUDE: (func $throw-and-import (type $void) ;; INCLUDE-NEXT: (throw $tag) ;; INCLUDE-NEXT: ) (func $throw-and-import @@ -327,11 +487,11 @@ ) ) - ;; WITHOUT: (func $cycle-with-unknown-call (type $0) + ;; WITHOUT: (func $cycle-with-unknown-call (type $void) ;; WITHOUT-NEXT: (call $cycle-with-unknown-call) ;; WITHOUT-NEXT: (call $import) ;; WITHOUT-NEXT: ) - ;; INCLUDE: (func $cycle-with-unknown-call (type $0) + ;; INCLUDE: (func $cycle-with-unknown-call (type $void) ;; INCLUDE-NEXT: (call $cycle-with-unknown-call) ;; INCLUDE-NEXT: (call $import) ;; INCLUDE-NEXT: ) diff --git a/test/lit/passes/inlining-unreachable.wast b/test/lit/passes/inlining-unreachable.wast index 9ad141435..2c93359fd 100644 --- a/test/lit/passes/inlining-unreachable.wast +++ b/test/lit/passes/inlining-unreachable.wast @@ -77,8 +77,16 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block ;; CHECK-NEXT: (block $__inlined_func$callee - ;; CHECK-NEXT: (call $imported - ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__return_call + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (br $__return_call) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $imported + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -114,7 +122,12 @@ ;; CHECK-NEXT: (block $__inlined_func$0 ;; CHECK-NEXT: (block ;; CHECK-NEXT: (nop) - ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (block ;; (replaces unreachable CallRef we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null nofunc) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) diff --git a/test/lit/passes/inlining_all-features.wast b/test/lit/passes/inlining_all-features.wast index 65dabdd6c..c274e9abb 100644 --- a/test/lit/passes/inlining_all-features.wast +++ b/test/lit/passes/inlining_all-features.wast @@ -100,7 +100,12 @@ ) ;; CHECK: (func $1 (type $none_=>_none) ;; CHECK-NEXT: (block $__inlined_func$0 - ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (block ;; (replaces unreachable CallRef we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.null nofunc) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $1 diff --git a/test/lit/passes/inlining_enable-tail-call.wast b/test/lit/passes/inlining_enable-tail-call.wast index d2d7772cd..49ea1c3c4 100644 --- a/test/lit/passes/inlining_enable-tail-call.wast +++ b/test/lit/passes/inlining_enable-tail-call.wast @@ -468,74 +468,303 @@ (call $0) ) ) + (module - ;; CHECK: (type $0 (func (result i32))) + ;; No params, no results + ;; CHECK: (type $0 (func)) - ;; CHECK: (func $0 (result i32) - ;; CHECK-NEXT: (return - ;; CHECK-NEXT: (block $__inlined_func$1 (result i32) + ;; CHECK: (func $caller + ;; CHECK-NEXT: (block $__original_body + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (br $__original_body) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $__inlined_func$callee + ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 42) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - (func $0 (result i32) - (return_call $1) + (func $caller + (return_call $callee) ) - (func $1 (result i32) + (func $callee + (drop + (i32.const 42) + ) + ) +) + +(module + ;; No params, one result + ;; CHECK: (type $0 (func (result i32))) + + ;; CHECK: (func $caller (result i32) + ;; CHECK-NEXT: (block $__original_body + ;; CHECK-NEXT: (return + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (br $__original_body) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $__inlined_func$callee (result i32) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $caller (result i32) + (return_call $callee) + ) + (func $callee (result i32) (i32.const 42) ) ) + (module + ;; One param, no results ;; CHECK: (type $0 (func)) - ;; CHECK: (func $0 + ;; CHECK: (func $caller ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (block $__inlined_func$1 - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (i32.const 42) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (block $__original_body + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $__original_body) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (return) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $__inlined_func$callee + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - (func $0 - (return_call $1 + (func $caller + (return_call $callee (i32.const 42) ) ) - (func $1 (param i32) + (func $callee (param i32) (drop (local.get 0) ) ) ) + (module + ;; One param, one result ;; CHECK: (type $0 (func (result i32))) - ;; CHECK: (func $0 (result i32) + ;; CHECK: (func $caller (result i32) ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (return - ;; CHECK-NEXT: (block $__inlined_func$1 (result i32) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: (block $__original_body + ;; CHECK-NEXT: (return + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $__original_body) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $__inlined_func$callee (result i32) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - (func $0 (result i32) - (return_call $1 + (func $caller (result i32) + (return_call $callee (i32.const 42) ) ) - (func $1 (param i32) (result i32) + (func $callee (param i32) (result i32) (local.get 0) ) ) + +(module + ;; Multiple params, no result + ;; CHECK: (type $0 (func)) + + ;; CHECK: (func $caller + ;; CHECK-NEXT: (local $x i32) + ;; CHECK-NEXT: (local $y i32) + ;; CHECK-NEXT: (local $2 i32) + ;; CHECK-NEXT: (local $3 i32) + ;; CHECK-NEXT: (block $__original_body + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $3 + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $__original_body) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $__inlined_func$callee + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $caller + (local $x i32) + (local $y i32) + (return_call $callee + (local.get $x) + (local.get $y) + ) + ) + (func $callee (param i32 i32) + (drop + (local.get 0) + ) + (drop + (local.get 1) + ) + ) +) + +(module + ;; Chain of return_calls, no params, no results. + ;; CHECK: (type $0 (func)) + + ;; CHECK: (func $first + ;; CHECK-NEXT: (block $__original_body_0 + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__original_body + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (br $__original_body) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $__inlined_func$second + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (br $__original_body_0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $__inlined_func$third$1 + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $first + (return_call $second) + ) + (func $second + (return_call $third) + ) + (func $third + (drop + (i32.const 42) + ) + ) +) + +(module + ;; Chain of return_calls with params and results. + ;; CHECK: (type $0 (func (result i32))) + + ;; CHECK: (func $first (result i32) + ;; CHECK-NEXT: (local $x i32) + ;; CHECK-NEXT: (local $y i32) + ;; CHECK-NEXT: (local $2 i32) + ;; CHECK-NEXT: (local $3 i32) + ;; CHECK-NEXT: (local $4 i32) + ;; CHECK-NEXT: (local $5 i32) + ;; CHECK-NEXT: (block $__original_body_0 + ;; CHECK-NEXT: (return + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__original_body + ;; CHECK-NEXT: (return + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $3 + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $__original_body) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $__inlined_func$second + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (local.set $4 + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $5 + ;; CHECK-NEXT: (local.get $3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $__original_body_0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $__inlined_func$third$1 (result i32) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $5) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $first (result i32) + (local $x i32) + (local $y i32) + (return_call $second + (local.get $x) + (local.get $y) + ) + ) + (func $second (param i32 i32) (result i32) + (return_call $third + (local.get 0) + (local.get 1) + ) + ) + (func $third (param i32 i32) (result i32) + (drop + (local.get 0) + ) + (drop + (local.get 1) + ) + (i32.const 42) + ) +) + (module ;; CHECK: (type $0 (func)) @@ -543,14 +772,19 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result i32) ;; CHECK-NEXT: (block $__inlined_func$1 (result i32) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (br $__inlined_func$1 - ;; CHECK-NEXT: (block (result i32) - ;; CHECK-NEXT: (block $__inlined_func$2$1 (result i32) - ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (block $__return_call + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (br $__return_call) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (block $__inlined_func$2$1 (result i32) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -568,6 +802,7 @@ (i32.const 42) ) ) + (module ;; CHECK: (type $0 (func)) @@ -575,19 +810,110 @@ ;; CHECK-NEXT: (local $0 i32) ;; CHECK-NEXT: (block $__inlined_func$1 ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__return_call + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (br $__return_call) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $__inlined_func$1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$2$1 + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $0 + (call $1) + ) + (func $1 + (return_call $2 + (i32.const 42) + ) + ) + (func $2 (param i32) + (drop + (local.get 0) + ) + ) +) + +(module + ;; Same as above, but with nontrivial effects in the children. + ;; CHECK: (type $0 (func)) + + ;; CHECK: (type $1 (func (param i32))) + + ;; CHECK: (func $0 + ;; CHECK-NEXT: (block $__inlined_func$1 + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__return_call + ;; CHECK-NEXT: (br $__inlined_func$1) + ;; CHECK-NEXT: (br $__return_call) + ;; CHECK-NEXT: (br $__inlined_func$1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $2 + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $0 + (call $1) + ) + (func $1 + (return_call $2 + (return) + ) + ) + ;; CHECK: (func $2 (param $0 i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $2 (param i32) + (drop + (local.get 0) + ) + ) +) + +(module + ;; Same as above, but this time the child is not unreachable. + ;; CHECK: (type $0 (func)) + + ;; CHECK: (func $0 + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (block $__inlined_func$1 + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__return_call ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (block $__inlined_func$2$1 - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (i32.const 42) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (local.tee $0 + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (br $__inlined_func$1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $__return_call) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (br $__inlined_func$1) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$2$1 + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -596,7 +922,9 @@ ) (func $1 (return_call $2 - (i32.const 42) + (block (result i32) + (return) + ) ) ) (func $2 (param i32) @@ -605,6 +933,7 @@ ) ) ) + (module ;; CHECK: (type $0 (func)) @@ -617,7 +946,12 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block (result i32) ;; CHECK-NEXT: (block $__inlined_func$1 (result i32) - ;; CHECK-NEXT: (br $__inlined_func$1 + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (block $__return_call + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (br $__return_call) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (call_indirect (type $T) ;; CHECK-NEXT: (i32.const 42) ;; CHECK-NEXT: (i32.const 0) @@ -650,11 +984,14 @@ ;; CHECK: (func $0 ;; CHECK-NEXT: (block $__inlined_func$1 ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__return_call + ;; CHECK-NEXT: (br $__return_call) + ;; CHECK-NEXT: (br $__inlined_func$1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (call_indirect (type $T) ;; CHECK-NEXT: (i32.const 42) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (br $__inlined_func$1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -703,28 +1040,31 @@ ;; CHECK-NEXT: (block ;; CHECK-NEXT: (block $__inlined_func$13$1 ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (global.get $global$0) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (block $__original_body + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (global.get $global$0) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (br $__original_body) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $__inlined_func$13$1) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block $__inlined_func$2 ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (block $__inlined_func$2 - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (global.get $global$0) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (br $__inlined_func$2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (global.set $global$0 - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (global.get $global$0) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (br $__inlined_func$2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (br $__inlined_func$13$1) + ;; CHECK-NEXT: (global.set $global$0 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -746,37 +1086,42 @@ (export "is_even" (func $is_even)) ;; CHECK: (func $is_even (param $i i32) (result i32) ;; CHECK-NEXT: (local $1 i32) - ;; CHECK-NEXT: (if (result i32) - ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (local.get $i) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (else - ;; CHECK-NEXT: (return - ;; CHECK-NEXT: (block $__inlined_func$is_odd (result i32) - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (local.get $i) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if (result i32) - ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (else - ;; CHECK-NEXT: (return_call $is_even - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $__original_body + ;; CHECK-NEXT: (return + ;; CHECK-NEXT: (if (result i32) + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $i) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (local.get $i) + ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $__original_body) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $__inlined_func$is_odd (result i32) + ;; CHECK-NEXT: (if (result i32) + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (return_call $is_even + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) diff --git a/test/lit/passes/simplify-locals-eh-old.wast b/test/lit/passes/simplify-locals-eh-old.wast index 5e4a395d3..0bf26f34d 100644 --- a/test/lit/passes/simplify-locals-eh-old.wast +++ b/test/lit/passes/simplify-locals-eh-old.wast @@ -152,6 +152,57 @@ ) ) + ;; CHECK: (func $return-call-can-be-sinked-into-try (type $3) (result i32) + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (try $try (result i32) + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (if (result i32) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (return_call $return-call-can-be-sinked-into-try) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch $e-i32 + ;; CHECK-NEXT: (pop i32) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $return-call-can-be-sinked-into-try (result i32) + (local $0 i32) + (drop + ;; This cannot throw either, so it can be sunk. Wrap the return_call in an + ;; if so the whole expression does not return unconditionally. + (local.tee $0 + (if (result i32) + (i32.const 0) + (then + (return_call $return-call-can-be-sinked-into-try) + ) + (else + (i32.const 1) + ) + ) + ) + ) + (try (result i32) + (do + (drop (local.get $0)) + (i32.const 0) + ) + (catch $e-i32 + (pop i32) + ) + ) + ) + ;; CHECK: (func $equivalent-set-removal-call (type $1) (param $0 i32) ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (nop) diff --git a/test/lit/passes/vacuum-eh-old.wast b/test/lit/passes/vacuum-eh-old.wast index 68b5f7b1e..0215b5b25 100644 --- a/test/lit/passes/vacuum-eh-old.wast +++ b/test/lit/passes/vacuum-eh-old.wast @@ -2,12 +2,19 @@ ;; RUN: wasm-opt %s --vacuum -all -S -o - | filecheck %s (module + ;; CHECK: (type $void (func)) + (type $void (func)) + + ;; CHECK: (table $t 0 funcref) + ;; CHECK: (tag $e (param i32)) (tag $e (param i32)) ;; CHECK: (tag $e2 (param i32)) (tag $e2 (param i32)) - ;; CHECK: (func $try-test (type $0) + (table $t funcref 0) + + ;; CHECK: (func $try-test (type $void) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) (func $try-test @@ -60,7 +67,7 @@ (i32.const 2) ) - ;; CHECK: (func $inner-try-catch-test (type $0) + ;; CHECK: (func $inner-try-catch-test (type $void) ;; CHECK-NEXT: (local $0 i32) ;; CHECK-NEXT: (try $try ;; CHECK-NEXT: (do @@ -108,7 +115,7 @@ ) ) - ;; CHECK: (func $br-in-catch (type $0) + ;; CHECK: (func $br-in-catch (type $void) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $br-in-catch @@ -128,7 +135,7 @@ ) ) - ;; CHECK: (func $try-delegate-outer-target (type $0) + ;; CHECK: (func $try-delegate-outer-target (type $void) ;; CHECK-NEXT: (local $0 i32) ;; CHECK-NEXT: (try $label$0 ;; CHECK-NEXT: (do @@ -179,7 +186,7 @@ ) ) - ;; CHECK: (func $trivial-catch-all-of-throw (type $0) + ;; CHECK: (func $trivial-catch-all-of-throw (type $void) ;; CHECK-NEXT: (local $0 i32) ;; CHECK-NEXT: (try $try3 ;; CHECK-NEXT: (do @@ -225,4 +232,66 @@ (catch_all) ) ) + + ;; CHECK: (func $throw (type $void) + ;; CHECK-NEXT: (throw $e + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $throw + ;; Helper for the tail call tests below. + (throw $e + (i32.const 0) + ) + ) + + ;; CHECK: (func $return-call-catch (type $void) + ;; CHECK-NEXT: (return_call $throw) + ;; CHECK-NEXT: ) + (func $return-call-catch + (try + (do + ;; This returns before it throws, so we can optimize out the surrounding + ;; try-catch. + (return_call $throw) + ) + (catch_all) + ) + ) + + ;; CHECK: (func $return-call-indirect-catch (type $void) + ;; CHECK-NEXT: (return_call_indirect $t (type $void) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $return-call-indirect-catch + (try + (do + ;; This returns before it throws, so we can optimize out the surrounding + ;; try-catch. + (return_call_indirect + (i32.const 0) + ) + ) + (catch_all) + ) + ) + + ;; CHECK: (func $return-call-ref-catch (type $void) + ;; CHECK-NEXT: (return_call_ref $void + ;; CHECK-NEXT: (ref.func $throw) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $return-call-ref-catch + (try + (do + ;; This returns before it throws, so we can optimize out the surrounding + ;; try-catch. + (return_call_ref $void + (ref.func $throw) + ) + ) + (catch_all) + ) + ) ) diff --git a/test/spec/return_call.wast b/test/spec/return_call.wast new file mode 100644 index 000000000..9423159ff --- /dev/null +++ b/test/spec/return_call.wast @@ -0,0 +1,202 @@ +;; Test `return_call` operator + +(module + ;; Auxiliary definitions + (func $const-i32 (result i32) (i32.const 0x132)) + (func $const-i64 (result i64) (i64.const 0x164)) + (func $const-f32 (result f32) (f32.const 0xf32)) + (func $const-f64 (result f64) (f64.const 0xf64)) + + (func $id-i32 (param i32) (result i32) (local.get 0)) + (func $id-i64 (param i64) (result i64) (local.get 0)) + (func $id-f32 (param f32) (result f32) (local.get 0)) + (func $id-f64 (param f64) (result f64) (local.get 0)) + + (func $f32-i32 (param f32 i32) (result i32) (local.get 1)) + (func $i32-i64 (param i32 i64) (result i64) (local.get 1)) + (func $f64-f32 (param f64 f32) (result f32) (local.get 1)) + (func $i64-f64 (param i64 f64) (result f64) (local.get 1)) + + ;; Typing + + (func (export "type-i32") (result i32) (return_call $const-i32)) + (func (export "type-i64") (result i64) (return_call $const-i64)) + (func (export "type-f32") (result f32) (return_call $const-f32)) + (func (export "type-f64") (result f64) (return_call $const-f64)) + + (func (export "type-first-i32") (result i32) (return_call $id-i32 (i32.const 32))) + (func (export "type-first-i64") (result i64) (return_call $id-i64 (i64.const 64))) + (func (export "type-first-f32") (result f32) (return_call $id-f32 (f32.const 1.32))) + (func (export "type-first-f64") (result f64) (return_call $id-f64 (f64.const 1.64))) + + (func (export "type-second-i32") (result i32) + (return_call $f32-i32 (f32.const 32.1) (i32.const 32)) + ) + (func (export "type-second-i64") (result i64) + (return_call $i32-i64 (i32.const 32) (i64.const 64)) + ) + (func (export "type-second-f32") (result f32) + (return_call $f64-f32 (f64.const 64) (f32.const 32)) + ) + (func (export "type-second-f64") (result f64) + (return_call $i64-f64 (i64.const 64) (f64.const 64.1)) + ) + + ;; Recursion + + (func $fac-acc (export "fac-acc") (param i64 i64) (result i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (local.get 1)) + (else + (return_call $fac-acc + (i64.sub (local.get 0) (i64.const 1)) + (i64.mul (local.get 0) (local.get 1)) + ) + ) + ) + ) + + (func $count (export "count") (param i64) (result i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (local.get 0)) + (else (return_call $count (i64.sub (local.get 0) (i64.const 1)))) + ) + ) + + (func $even (export "even") (param i64) (result i32) + (if (result i32) (i64.eqz (local.get 0)) + (then (i32.const 44)) + (else (return_call $odd (i64.sub (local.get 0) (i64.const 1)))) + ) + ) + (func $odd (export "odd") (param i64) (result i32) + (if (result i32) (i64.eqz (local.get 0)) + (then (i32.const 99)) + (else (return_call $even (i64.sub (local.get 0) (i64.const 1)))) + ) + ) +) + +(assert_return (invoke "type-i32") (i32.const 0x132)) +(assert_return (invoke "type-i64") (i64.const 0x164)) +(assert_return (invoke "type-f32") (f32.const 0xf32)) +(assert_return (invoke "type-f64") (f64.const 0xf64)) + +(assert_return (invoke "type-first-i32") (i32.const 32)) +(assert_return (invoke "type-first-i64") (i64.const 64)) +(assert_return (invoke "type-first-f32") (f32.const 1.32)) +(assert_return (invoke "type-first-f64") (f64.const 1.64)) + +(assert_return (invoke "type-second-i32") (i32.const 32)) +(assert_return (invoke "type-second-i64") (i64.const 64)) +(assert_return (invoke "type-second-f32") (f32.const 32)) +(assert_return (invoke "type-second-f64") (f64.const 64.1)) + +(assert_return (invoke "fac-acc" (i64.const 0) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac-acc" (i64.const 1) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac-acc" (i64.const 5) (i64.const 1)) (i64.const 120)) +(assert_return + (invoke "fac-acc" (i64.const 25) (i64.const 1)) + (i64.const 7034535277573963776) +) + +(assert_return (invoke "count" (i64.const 0)) (i64.const 0)) +(assert_return (invoke "count" (i64.const 1000)) (i64.const 0)) +(assert_return (invoke "count" (i64.const 1000000)) (i64.const 0)) + +(assert_return (invoke "even" (i64.const 0)) (i32.const 44)) +(assert_return (invoke "even" (i64.const 1)) (i32.const 99)) +(assert_return (invoke "even" (i64.const 100)) (i32.const 44)) +(assert_return (invoke "even" (i64.const 77)) (i32.const 99)) +(assert_return (invoke "even" (i64.const 1000000)) (i32.const 44)) +(assert_return (invoke "even" (i64.const 1000001)) (i32.const 99)) +(assert_return (invoke "odd" (i64.const 0)) (i32.const 99)) +(assert_return (invoke "odd" (i64.const 1)) (i32.const 44)) +(assert_return (invoke "odd" (i64.const 200)) (i32.const 99)) +(assert_return (invoke "odd" (i64.const 77)) (i32.const 44)) +(assert_return (invoke "odd" (i64.const 1000000)) (i32.const 99)) +(assert_return (invoke "odd" (i64.const 999999)) (i32.const 44)) + + +;; Invalid typing + +(assert_invalid + (module + (func $type-void-vs-num (result i32) (return_call 1) (i32.const 0)) + (func) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-num-vs-num (result i32) (return_call 1) (i32.const 0)) + (func (result i64) (i64.const 1)) + ) + "type mismatch" +) + +(assert_invalid + (module + (func $arity-0-vs-1 (return_call 1)) + (func (param i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $arity-0-vs-2 (return_call 1)) + (func (param f64 i32)) + ) + "type mismatch" +) + +;; (module +;; (func $arity-1-vs-0 (i32.const 1) (return_call 1)) +;; (func) +;; ) + +;; (module +;; (func $arity-2-vs-0 (f64.const 2) (i32.const 1) (return_call 1)) +;; (func) +;; ) + +(assert_invalid + (module + (func $type-first-void-vs-num (return_call 1 (nop) (i32.const 1))) + (func (param i32 i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-second-void-vs-num (return_call 1 (i32.const 1) (nop))) + (func (param i32 i32)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-first-num-vs-num (return_call 1 (f64.const 1) (i32.const 1))) + (func (param i32 f64)) + ) + "type mismatch" +) +(assert_invalid + (module + (func $type-second-num-vs-num (return_call 1 (i32.const 1) (f64.const 1))) + (func (param f64 i32)) + ) + "type mismatch" +) + + +;; Unbound function + +(assert_invalid + (module (func $unbound-func (return_call 1))) + "unknown function" +) +(assert_invalid + (module (func $large-func (return_call 1012321300))) + "unknown function" +) diff --git a/test/spec/return_call_eh.wast b/test/spec/return_call_eh.wast new file mode 100644 index 000000000..e85fdf7c6 --- /dev/null +++ b/test/spec/return_call_eh.wast @@ -0,0 +1,35 @@ +;; Test the combination of 'return_call' with exception handling + +(module + (tag $t) + + (func $test (export "test") (result i32) + (try (result i32) + (do + (call $return-call-in-try) + ) + (catch_all + ;; Catch the exception thrown from $return-callee + (i32.const 42) + ) + ) + + ) + + (func $return-call-in-try (result i32) + (try (result i32) + (do + (return_call $return-callee) + ) + (catch_all + (unreachable) + ) + ) + ) + + (func $return-callee (result i32) + (throw $t) + ) +) + +(assert_return (invoke "test") (i32.const 42)) diff --git a/test/spec/return_call_indirect.wast b/test/spec/return_call_indirect.wast new file mode 100644 index 000000000..27f1dcdbf --- /dev/null +++ b/test/spec/return_call_indirect.wast @@ -0,0 +1,536 @@ +;; Test `return_call_indirect` operator + +(module + ;; Auxiliary definitions + (type $proc (func)) + (type $out-i32 (func (result i32))) + (type $out-i64 (func (result i64))) + (type $out-f32 (func (result f32))) + (type $out-f64 (func (result f64))) + (type $over-i32 (func (param i32) (result i32))) + (type $over-i64 (func (param i64) (result i64))) + (type $over-f32 (func (param f32) (result f32))) + (type $over-f64 (func (param f64) (result f64))) + (type $f32-i32 (func (param f32 i32) (result i32))) + (type $i32-i64 (func (param i32 i64) (result i64))) + (type $f64-f32 (func (param f64 f32) (result f32))) + (type $i64-f64 (func (param i64 f64) (result f64))) + (type $over-i32-duplicate (func (param i32) (result i32))) + (type $over-i64-duplicate (func (param i64) (result i64))) + (type $over-f32-duplicate (func (param f32) (result f32))) + (type $over-f64-duplicate (func (param f64) (result f64))) + + (func $const-i32 (type $out-i32) (i32.const 0x132)) + (func $const-i64 (type $out-i64) (i64.const 0x164)) + (func $const-f32 (type $out-f32) (f32.const 0xf32)) + (func $const-f64 (type $out-f64) (f64.const 0xf64)) + + (func $id-i32 (type $over-i32) (local.get 0)) + (func $id-i64 (type $over-i64) (local.get 0)) + (func $id-f32 (type $over-f32) (local.get 0)) + (func $id-f64 (type $over-f64) (local.get 0)) + + (func $i32-i64 (type $i32-i64) (local.get 1)) + (func $i64-f64 (type $i64-f64) (local.get 1)) + (func $f32-i32 (type $f32-i32) (local.get 1)) + (func $f64-f32 (type $f64-f32) (local.get 1)) + + (func $over-i32-duplicate (type $over-i32-duplicate) (local.get 0)) + (func $over-i64-duplicate (type $over-i64-duplicate) (local.get 0)) + (func $over-f32-duplicate (type $over-f32-duplicate) (local.get 0)) + (func $over-f64-duplicate (type $over-f64-duplicate) (local.get 0)) + + (table funcref + (elem + $const-i32 $const-i64 $const-f32 $const-f64 + $id-i32 $id-i64 $id-f32 $id-f64 + $f32-i32 $i32-i64 $f64-f32 $i64-f64 + $fac $fac-acc $even $odd + $over-i32-duplicate $over-i64-duplicate + $over-f32-duplicate $over-f64-duplicate + ) + ) + + ;; Syntax + + (func + (return_call_indirect (i32.const 0)) + (return_call_indirect (param i64) (i64.const 0) (i32.const 0)) + (return_call_indirect (param i64) (param) (param f64 i32 i64) + (i64.const 0) (f64.const 0) (i32.const 0) (i64.const 0) (i32.const 0) + ) + (return_call_indirect (result) (i32.const 0)) + ) + + (func (result i32) + (return_call_indirect (result i32) (i32.const 0)) + (return_call_indirect (result i32) (result) (i32.const 0)) + (return_call_indirect (param i64) (result i32) (i64.const 0) (i32.const 0)) + (return_call_indirect + (param) (param i64) (param) (param f64 i32 i64) (param) (param) + (result) (result i32) (result) (result) + (i64.const 0) (f64.const 0) (i32.const 0) (i64.const 0) (i32.const 0) + ) + ) + + (func (result i64) + (return_call_indirect (type $over-i64) (param i64) (result i64) + (i64.const 0) (i32.const 0) + ) + ) + + ;; Typing + + (func (export "type-i32") (result i32) + (return_call_indirect (type $out-i32) (i32.const 0)) + ) + (func (export "type-i64") (result i64) + (return_call_indirect (type $out-i64) (i32.const 1)) + ) + (func (export "type-f32") (result f32) + (return_call_indirect (type $out-f32) (i32.const 2)) + ) + (func (export "type-f64") (result f64) + (return_call_indirect (type $out-f64) (i32.const 3)) + ) + + (func (export "type-index") (result i64) + (return_call_indirect (type $over-i64) (i64.const 100) (i32.const 5)) + ) + + (func (export "type-first-i32") (result i32) + (return_call_indirect (type $over-i32) (i32.const 32) (i32.const 4)) + ) + (func (export "type-first-i64") (result i64) + (return_call_indirect (type $over-i64) (i64.const 64) (i32.const 5)) + ) + (func (export "type-first-f32") (result f32) + (return_call_indirect (type $over-f32) (f32.const 1.32) (i32.const 6)) + ) + (func (export "type-first-f64") (result f64) + (return_call_indirect (type $over-f64) (f64.const 1.64) (i32.const 7)) + ) + + (func (export "type-second-i32") (result i32) + (return_call_indirect (type $f32-i32) + (f32.const 32.1) (i32.const 32) (i32.const 8) + ) + ) + (func (export "type-second-i64") (result i64) + (return_call_indirect (type $i32-i64) + (i32.const 32) (i64.const 64) (i32.const 9) + ) + ) + (func (export "type-second-f32") (result f32) + (return_call_indirect (type $f64-f32) + (f64.const 64) (f32.const 32) (i32.const 10) + ) + ) + (func (export "type-second-f64") (result f64) + (return_call_indirect (type $i64-f64) + (i64.const 64) (f64.const 64.1) (i32.const 11) + ) + ) + + ;; Dispatch + + (func (export "dispatch") (param i32 i64) (result i64) + (return_call_indirect (type $over-i64) (local.get 1) (local.get 0)) + ) + + (func (export "dispatch-structural") (param i32) (result i64) + (return_call_indirect (type $over-i64-duplicate) + (i64.const 9) (local.get 0) + ) + ) + + ;; Multiple tables + + (table $tab2 funcref (elem $tab-f1)) + (table $tab3 funcref (elem $tab-f2)) + + (func $tab-f1 (result i32) (i32.const 0x133)) + (func $tab-f2 (result i32) (i32.const 0x134)) + + (func (export "call-tab") (param $i i32) (result i32) + (if (i32.eq (local.get $i) (i32.const 0)) + (then (return_call_indirect (type $out-i32) (i32.const 0))) + ) + (if (i32.eq (local.get $i) (i32.const 1)) + (then (return_call_indirect $tab2 (type $out-i32) (i32.const 0))) + ) + (if (i32.eq (local.get $i) (i32.const 2)) + (then (return_call_indirect $tab3 (type $out-i32) (i32.const 0))) + ) + (i32.const 0) + ) + + ;; Recursion + + (func $fac (export "fac") (type $over-i64) + (return_call_indirect (param i64 i64) (result i64) + (local.get 0) (i64.const 1) (i32.const 13) + ) + ) + + (func $fac-acc (param i64 i64) (result i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (local.get 1)) + (else + (return_call_indirect (param i64 i64) (result i64) + (i64.sub (local.get 0) (i64.const 1)) + (i64.mul (local.get 0) (local.get 1)) + (i32.const 13) + ) + ) + ) + ) + + (func $even (export "even") (param i32) (result i32) + (if (result i32) (i32.eqz (local.get 0)) + (then (i32.const 44)) + (else + (return_call_indirect (type $over-i32) + (i32.sub (local.get 0) (i32.const 1)) + (i32.const 15) + ) + ) + ) + ) + (func $odd (export "odd") (param i32) (result i32) + (if (result i32) (i32.eqz (local.get 0)) + (then (i32.const 99)) + (else + (return_call_indirect (type $over-i32) + (i32.sub (local.get 0) (i32.const 1)) + (i32.const 14) + ) + ) + ) + ) +) + +(assert_return (invoke "type-i32") (i32.const 0x132)) +(assert_return (invoke "type-i64") (i64.const 0x164)) +(assert_return (invoke "type-f32") (f32.const 0xf32)) +(assert_return (invoke "type-f64") (f64.const 0xf64)) + +(assert_return (invoke "type-index") (i64.const 100)) + +(assert_return (invoke "type-first-i32") (i32.const 32)) +(assert_return (invoke "type-first-i64") (i64.const 64)) +(assert_return (invoke "type-first-f32") (f32.const 1.32)) +(assert_return (invoke "type-first-f64") (f64.const 1.64)) + +(assert_return (invoke "type-second-i32") (i32.const 32)) +(assert_return (invoke "type-second-i64") (i64.const 64)) +(assert_return (invoke "type-second-f32") (f32.const 32)) +(assert_return (invoke "type-second-f64") (f64.const 64.1)) + +(assert_return (invoke "dispatch" (i32.const 5) (i64.const 2)) (i64.const 2)) +(assert_return (invoke "dispatch" (i32.const 5) (i64.const 5)) (i64.const 5)) +(assert_return (invoke "dispatch" (i32.const 12) (i64.const 5)) (i64.const 120)) +(assert_return (invoke "dispatch" (i32.const 17) (i64.const 2)) (i64.const 2)) +(assert_trap (invoke "dispatch" (i32.const 0) (i64.const 2)) "indirect call type mismatch") +(assert_trap (invoke "dispatch" (i32.const 15) (i64.const 2)) "indirect call type mismatch") +(assert_trap (invoke "dispatch" (i32.const 20) (i64.const 2)) "undefined element") +(assert_trap (invoke "dispatch" (i32.const -1) (i64.const 2)) "undefined element") +(assert_trap (invoke "dispatch" (i32.const 1213432423) (i64.const 2)) "undefined element") + +(assert_return (invoke "dispatch-structural" (i32.const 5)) (i64.const 9)) +(assert_return (invoke "dispatch-structural" (i32.const 5)) (i64.const 9)) +(assert_return (invoke "dispatch-structural" (i32.const 12)) (i64.const 362880)) +(assert_return (invoke "dispatch-structural" (i32.const 17)) (i64.const 9)) +(assert_trap (invoke "dispatch-structural" (i32.const 11)) "indirect call type mismatch") +(assert_trap (invoke "dispatch-structural" (i32.const 16)) "indirect call type mismatch") + +(assert_return (invoke "call-tab" (i32.const 0)) (i32.const 0x132)) +(assert_return (invoke "call-tab" (i32.const 1)) (i32.const 0x133)) +(assert_return (invoke "call-tab" (i32.const 2)) (i32.const 0x134)) + +(assert_return (invoke "fac" (i64.const 0)) (i64.const 1)) +(assert_return (invoke "fac" (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac" (i64.const 5)) (i64.const 120)) +(assert_return (invoke "fac" (i64.const 25)) (i64.const 7034535277573963776)) + +(assert_return (invoke "even" (i32.const 0)) (i32.const 44)) +(assert_return (invoke "even" (i32.const 1)) (i32.const 99)) +(assert_return (invoke "even" (i32.const 100)) (i32.const 44)) +(assert_return (invoke "even" (i32.const 77)) (i32.const 99)) +(assert_return (invoke "even" (i32.const 100000)) (i32.const 44)) +(assert_return (invoke "even" (i32.const 111111)) (i32.const 99)) +(assert_return (invoke "odd" (i32.const 0)) (i32.const 99)) +(assert_return (invoke "odd" (i32.const 1)) (i32.const 44)) +(assert_return (invoke "odd" (i32.const 200)) (i32.const 99)) +(assert_return (invoke "odd" (i32.const 77)) (i32.const 44)) +(assert_return (invoke "odd" (i32.const 200002)) (i32.const 99)) +(assert_return (invoke "odd" (i32.const 300003)) (i32.const 44)) + + +;; Invalid syntax + +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (return_call_indirect (type $sig) (result i32) (param i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (return_call_indirect (param i32) (type $sig) (result i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (return_call_indirect (param i32) (result i32) (type $sig)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (return_call_indirect (result i32) (type $sig) (param i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (return_call_indirect (result i32) (param i32) (type $sig)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(table 0 funcref)" + "(func (result i32)" + " (return_call_indirect (result i32) (param i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "unexpected token" +) + +(assert_malformed + (module quote + "(table 0 funcref)" + "(func (return_call_indirect (param $x i32) (i32.const 0) (i32.const 0)))" + ) + "unexpected token" +) +(assert_malformed + (module quote + "(type $sig (func))" + "(table 0 funcref)" + "(func (result i32)" + " (return_call_indirect (type $sig) (result i32) (i32.const 0))" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (return_call_indirect (type $sig) (result i32) (i32.const 0))" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32) (result i32)))" + "(table 0 funcref)" + "(func" + " (return_call_indirect (type $sig) (param i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "inline function type" +) +(assert_malformed + (module quote + "(type $sig (func (param i32 i32) (result i32)))" + "(table 0 funcref)" + "(func (result i32)" + " (return_call_indirect (type $sig) (param i32) (result i32)" + " (i32.const 0) (i32.const 0)" + " )" + ")" + ) + "inline function type" +) + +;; Invalid typing + +(assert_invalid + (module + (type (func)) + (func $no-table (return_call_indirect (type 0) (i32.const 0))) + ) + "unknown table" +) + +;; (assert_invalid +;; (module +;; (type (func)) +;; (table 0 funcref) +;; (func $type-void-vs-num (i32.eqz (return_call_indirect (type 0) (i32.const 0)))) +;; ) +;; "type mismatch" +;; ) +(assert_invalid + (module + (type (func (result i64))) + (table 0 funcref) + (func $type-num-vs-num (i32.eqz (return_call_indirect (type 0) (i32.const 0)))) + ) + "type mismatch" +) + +(assert_invalid + (module + (type (func (param i32))) + (table 0 funcref) + (func $arity-0-vs-1 (return_call_indirect (type 0) (i32.const 0))) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param f64 i32))) + (table 0 funcref) + (func $arity-0-vs-2 (return_call_indirect (type 0) (i32.const 0))) + ) + "type mismatch" +) + +;; (module +;; (type (func)) +;; (table 0 funcref) +;; (func $arity-1-vs-0 (return_call_indirect (type 0) (i32.const 1) (i32.const 0))) +;; ) + +;; (module +;; (type (func)) +;; (table 0 funcref) +;; (func $arity-2-vs-0 +;; (return_call_indirect (type 0) (f64.const 2) (i32.const 1) (i32.const 0)) +;; ) +;; ) + +(assert_invalid + (module + (type (func (param i32))) + (table 0 funcref) + (func $type-func-void-vs-i32 (return_call_indirect (type 0) (i32.const 1) (nop))) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param i32))) + (table 0 funcref) + (func $type-func-num-vs-i32 (return_call_indirect (type 0) (i32.const 0) (i64.const 1))) + ) + "type mismatch" +) + +(assert_invalid + (module + (type (func (param i32 i32))) + (table 0 funcref) + (func $type-first-void-vs-num + (return_call_indirect (type 0) (nop) (i32.const 1) (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param i32 i32))) + (table 0 funcref) + (func $type-second-void-vs-num + (return_call_indirect (type 0) (i32.const 1) (nop) (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param i32 f64))) + (table 0 funcref) + (func $type-first-num-vs-num + (return_call_indirect (type 0) (f64.const 1) (i32.const 1) (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (type (func (param f64 i32))) + (table 0 funcref) + (func $type-second-num-vs-num + (return_call_indirect (type 0) (i32.const 1) (f64.const 1) (i32.const 0)) + ) + ) + "type mismatch" +) + + +;; Unbound type + +(assert_invalid + (module + (table 0 funcref) + (func $unbound-type (return_call_indirect (type 1) (i32.const 0))) + ) + "unknown type" +) +(assert_invalid + (module + (table 0 funcref) + (func $large-type (return_call_indirect (type 1012321300) (i32.const 0))) + ) + "unknown type" +) + + +;; Unbound function in table + +(assert_invalid + (module (table funcref (elem 0 0))) + "unknown function 0" +) diff --git a/test/spec/return_call_ref.wast b/test/spec/return_call_ref.wast new file mode 100644 index 000000000..6bb5d2a14 --- /dev/null +++ b/test/spec/return_call_ref.wast @@ -0,0 +1,377 @@ +;; Test `return_call_ref` operator + +(module + ;; Auxiliary definitions + (type $proc (func)) + (type $-i32 (func (result i32))) + (type $-i64 (func (result i64))) + (type $-f32 (func (result f32))) + (type $-f64 (func (result f64))) + + (type $i32-i32 (func (param i32) (result i32))) + (type $i64-i64 (func (param i64) (result i64))) + (type $f32-f32 (func (param f32) (result f32))) + (type $f64-f64 (func (param f64) (result f64))) + + (type $f32-i32 (func (param f32 i32) (result i32))) + (type $i32-i64 (func (param i32 i64) (result i64))) + (type $f64-f32 (func (param f64 f32) (result f32))) + (type $i64-f64 (func (param i64 f64) (result f64))) + + (type $i64i64-i64 (func (param i64 i64) (result i64))) + + (func $const-i32 (result i32) (i32.const 0x132)) + (func $const-i64 (result i64) (i64.const 0x164)) + (func $const-f32 (result f32) (f32.const 0xf32)) + (func $const-f64 (result f64) (f64.const 0xf64)) + + (func $id-i32 (param i32) (result i32) (local.get 0)) + (func $id-i64 (param i64) (result i64) (local.get 0)) + (func $id-f32 (param f32) (result f32) (local.get 0)) + (func $id-f64 (param f64) (result f64) (local.get 0)) + + (func $f32-i32 (param f32 i32) (result i32) (local.get 1)) + (func $i32-i64 (param i32 i64) (result i64) (local.get 1)) + (func $f64-f32 (param f64 f32) (result f32) (local.get 1)) + (func $i64-f64 (param i64 f64) (result f64) (local.get 1)) + + (global $const-i32 (ref $-i32) (ref.func $const-i32)) + (global $const-i64 (ref $-i64) (ref.func $const-i64)) + (global $const-f32 (ref $-f32) (ref.func $const-f32)) + (global $const-f64 (ref $-f64) (ref.func $const-f64)) + + (global $id-i32 (ref $i32-i32) (ref.func $id-i32)) + (global $id-i64 (ref $i64-i64) (ref.func $id-i64)) + (global $id-f32 (ref $f32-f32) (ref.func $id-f32)) + (global $id-f64 (ref $f64-f64) (ref.func $id-f64)) + + (global $f32-i32 (ref $f32-i32) (ref.func $f32-i32)) + (global $i32-i64 (ref $i32-i64) (ref.func $i32-i64)) + (global $f64-f32 (ref $f64-f32) (ref.func $f64-f32)) + (global $i64-f64 (ref $i64-f64) (ref.func $i64-f64)) + + (elem declare func + $const-i32 $const-i64 $const-f32 $const-f64 + $id-i32 $id-i64 $id-f32 $id-f64 + $f32-i32 $i32-i64 $f64-f32 $i64-f64 + ) + + ;; Typing + + (func (export "type-i32") (result i32) + (return_call_ref $-i32 (global.get $const-i32)) + ) + (func (export "type-i64") (result i64) + (return_call_ref $-i64 (global.get $const-i64)) + ) + (func (export "type-f32") (result f32) + (return_call_ref $-f32 (global.get $const-f32)) + ) + (func (export "type-f64") (result f64) + (return_call_ref $-f64 (global.get $const-f64)) + ) + + (func (export "type-first-i32") (result i32) + (return_call_ref $i32-i32 (i32.const 32) (global.get $id-i32)) + ) + (func (export "type-first-i64") (result i64) + (return_call_ref $i64-i64 (i64.const 64) (global.get $id-i64)) + ) + (func (export "type-first-f32") (result f32) + (return_call_ref $f32-f32 (f32.const 1.32) (global.get $id-f32)) + ) + (func (export "type-first-f64") (result f64) + (return_call_ref $f64-f64 (f64.const 1.64) (global.get $id-f64)) + ) + + (func (export "type-second-i32") (result i32) + (return_call_ref $f32-i32 (f32.const 32.1) (i32.const 32) (global.get $f32-i32)) + ) + (func (export "type-second-i64") (result i64) + (return_call_ref $i32-i64 (i32.const 32) (i64.const 64) (global.get $i32-i64)) + ) + (func (export "type-second-f32") (result f32) + (return_call_ref $f64-f32 (f64.const 64) (f32.const 32) (global.get $f64-f32)) + ) + (func (export "type-second-f64") (result f64) + (return_call_ref $i64-f64 (i64.const 64) (f64.const 64.1) (global.get $i64-f64)) + ) + + ;; Null + + (func (export "null") + (return_call_ref $proc (ref.null $proc)) + ) + + ;; Recursion + + (global $fac-acc (ref $i64i64-i64) (ref.func $fac-acc)) + + (elem declare func $fac-acc) + (func $fac-acc (export "fac-acc") (param i64 i64) (result i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (local.get 1)) + (else + (return_call_ref $i64i64-i64 + (i64.sub (local.get 0) (i64.const 1)) + (i64.mul (local.get 0) (local.get 1)) + (global.get $fac-acc) + ) + ) + ) + ) + + (global $count (ref $i64-i64) (ref.func $count)) + + (elem declare func $count) + (func $count (export "count") (param i64) (result i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (local.get 0)) + (else + (return_call_ref $i64-i64 + (i64.sub (local.get 0) (i64.const 1)) + (global.get $count) + ) + ) + ) + ) + + (global $even (ref $i64-i64) (ref.func $even)) + (global $odd (ref $i64-i64) (ref.func $odd)) + + (elem declare func $even) + (func $even (export "even") (param i64) (result i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (i64.const 44)) + (else + (return_call_ref $i64-i64 + (i64.sub (local.get 0) (i64.const 1)) + (global.get $odd) + ) + ) + ) + ) + (elem declare func $odd) + (func $odd (export "odd") (param i64) (result i64) + (if (result i64) (i64.eqz (local.get 0)) + (then (i64.const 99)) + (else + (return_call_ref $i64-i64 + (i64.sub (local.get 0) (i64.const 1)) + (global.get $even) + ) + ) + ) + ) +) + +(assert_return (invoke "type-i32") (i32.const 0x132)) +(assert_return (invoke "type-i64") (i64.const 0x164)) +(assert_return (invoke "type-f32") (f32.const 0xf32)) +(assert_return (invoke "type-f64") (f64.const 0xf64)) + +(assert_return (invoke "type-first-i32") (i32.const 32)) +(assert_return (invoke "type-first-i64") (i64.const 64)) +(assert_return (invoke "type-first-f32") (f32.const 1.32)) +(assert_return (invoke "type-first-f64") (f64.const 1.64)) + +(assert_return (invoke "type-second-i32") (i32.const 32)) +(assert_return (invoke "type-second-i64") (i64.const 64)) +(assert_return (invoke "type-second-f32") (f32.const 32)) +(assert_return (invoke "type-second-f64") (f64.const 64.1)) + +(assert_trap (invoke "null") "null function reference") + +(assert_return (invoke "fac-acc" (i64.const 0) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac-acc" (i64.const 1) (i64.const 1)) (i64.const 1)) +(assert_return (invoke "fac-acc" (i64.const 5) (i64.const 1)) (i64.const 120)) +(assert_return + (invoke "fac-acc" (i64.const 25) (i64.const 1)) + (i64.const 7034535277573963776) +) + +(assert_return (invoke "count" (i64.const 0)) (i64.const 0)) +(assert_return (invoke "count" (i64.const 1000)) (i64.const 0)) +(assert_return (invoke "count" (i64.const 1000000)) (i64.const 0)) + +(assert_return (invoke "even" (i64.const 0)) (i64.const 44)) +(assert_return (invoke "even" (i64.const 1)) (i64.const 99)) +(assert_return (invoke "even" (i64.const 100)) (i64.const 44)) +(assert_return (invoke "even" (i64.const 77)) (i64.const 99)) +(assert_return (invoke "even" (i64.const 1000000)) (i64.const 44)) +(assert_return (invoke "even" (i64.const 1000001)) (i64.const 99)) +(assert_return (invoke "odd" (i64.const 0)) (i64.const 99)) +(assert_return (invoke "odd" (i64.const 1)) (i64.const 44)) +(assert_return (invoke "odd" (i64.const 200)) (i64.const 99)) +(assert_return (invoke "odd" (i64.const 77)) (i64.const 44)) +(assert_return (invoke "odd" (i64.const 1000000)) (i64.const 99)) +(assert_return (invoke "odd" (i64.const 999999)) (i64.const 44)) + + +;; More typing + +(module + (type $t (func)) + (type $t1 (func (result (ref $t)))) + (type $t2 (func (result (ref null $t)))) + (type $t3 (func (result (ref func)))) + (type $t4 (func (result (ref null func)))) + (elem declare func $f11 $f22 $f33 $f44) + (func $f11 (result (ref $t)) (return_call_ref $t1 (ref.func $f11))) + (func $f21 (result (ref null $t)) (return_call_ref $t1 (ref.func $f11))) + (func $f22 (result (ref null $t)) (return_call_ref $t2 (ref.func $f22))) + (func $f31 (result (ref func)) (return_call_ref $t1 (ref.func $f11))) + (func $f33 (result (ref func)) (return_call_ref $t3 (ref.func $f33))) + (func $f41 (result (ref null func)) (return_call_ref $t1 (ref.func $f11))) + (func $f42 (result (ref null func)) (return_call_ref $t2 (ref.func $f22))) + (func $f43 (result (ref null func)) (return_call_ref $t3 (ref.func $f33))) + (func $f44 (result (ref null func)) (return_call_ref $t4 (ref.func $f44))) +) + +(assert_invalid + (module + (type $t (func)) + (type $t2 (func (result (ref null $t)))) + (elem declare func $f22) + (func $f12 (result (ref $t)) (return_call_ref $t2 (ref.func $f22))) + (func $f22 (result (ref null $t)) (return_call_ref $t2 (ref.func $f22))) + ) + "type mismatch" +) + +(assert_invalid + (module + (type $t (func)) + (type $t3 (func (result (ref func)))) + (elem declare func $f33) + (func $f13 (result (ref $t)) (return_call_ref $t3 (ref.func $f33))) + (func $f33 (result (ref func)) (return_call_ref $t3 (ref.func $f33))) + ) + "type mismatch" +) + +(assert_invalid + (module + (type $t (func)) + (type $t4 (func (result (ref null func)))) + (elem declare func $f44) + (func $f14 (result (ref $t)) (return_call_ref $t4 (ref.func $f44))) + (func $f44 (result (ref null func)) (return_call_ref $t4 (ref.func $f44))) + ) + "type mismatch" +) + +(assert_invalid + (module + (type $t (func)) + (type $t3 (func (result (ref func)))) + (elem declare func $f33) + (func $f23 (result (ref null $t)) (return_call_ref $t3 (ref.func $f33))) + (func $f33 (result (ref func)) (return_call_ref $t3 (ref.func $f33))) + ) + "type mismatch" +) + +(assert_invalid + (module + (type $t (func)) + (type $t4 (func (result (ref null func)))) + (elem declare func $f44) + (func $f24 (result (ref null $t)) (return_call_ref $t4 (ref.func $f44))) + (func $f44 (result (ref null func)) (return_call_ref $t4 (ref.func $f44))) + ) + "type mismatch" +) + +(assert_invalid + (module + (type $t4 (func (result (ref null func)))) + (elem declare func $f44) + (func $f34 (result (ref func)) (return_call_ref $t4 (ref.func $f44))) + (func $f44 (result (ref null func)) (return_call_ref $t4 (ref.func $f44))) + ) + "type mismatch" +) + + +;; Unreachable typing. + +(module + (type $t (func (result i32))) + (func (export "unreachable") (result i32) + (return_call_ref $t (unreachable)) + ) +) +(assert_trap (invoke "unreachable") "unreachable") + +(module + (elem declare func $f) + (type $t (func (param i32) (result i32))) + (func $f (param i32) (result i32) (local.get 0)) + + (func (export "unreachable") (result i32) + (return_call_ref $t + (unreachable) + (ref.func $f) + ) + ) +) +(assert_trap (invoke "unreachable") "unreachable") + +(module + (elem declare func $f) + (type $t (func (param i32) (result i32))) + (func $f (param i32) (result i32) (local.get 0)) + + (func (export "unreachable") (result i32) + (unreachable) + (return_call_ref $t + (i32.const 0) + (ref.func $f) + ) + (i32.const 0) + ) +) +(assert_trap (invoke "unreachable") "unreachable") + +(assert_invalid + (module + (elem declare func $f) + (type $t (func (param i32) (result i32))) + (func $f (param i32) (result i32) (local.get 0)) + + (func (export "unreachable") (result i32) + (unreachable) + (i64.const 0) + (ref.func $f) + (return_call_ref $t) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (elem declare func $f) + (type $t (func (param i32) (result i32))) + (func $f (param i32) (result i32) (local.get 0)) + + (func (export "unreachable") (result i32) + (unreachable) + (ref.func $f) + (return_call_ref $t) + (i64.const 0) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (type $t (func)) + (func $f (param $r externref) + (return_call_ref $t (local.get $r)) + ) + ) + "type mismatch" +) |