summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/lit/ctor-eval/return_call.wast529
-rw-r--r--test/lit/passes/global-effects.wast222
-rw-r--r--test/lit/passes/inlining-unreachable.wast19
-rw-r--r--test/lit/passes/inlining_all-features.wast7
-rw-r--r--test/lit/passes/inlining_enable-tail-call.wast519
-rw-r--r--test/lit/passes/simplify-locals-eh-old.wast51
-rw-r--r--test/lit/passes/vacuum-eh-old.wast79
-rw-r--r--test/spec/return_call.wast202
-rw-r--r--test/spec/return_call_eh.wast35
-rw-r--r--test/spec/return_call_indirect.wast536
-rw-r--r--test/spec/return_call_ref.wast377
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"
+)