| ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. |
| ;; RUN: foreach %s %t wasm-opt -all --dae2 --closed-world -S -o - | filecheck %s |
| |
| ;; TODO: Analyze and optimize stack switching instructions. |
| |
| (module |
| (rec |
| ;; CHECK: (rec |
| ;; CHECK-NEXT: (type $k (cont $f)) |
| |
| ;; CHECK: (type $1 (func)) |
| |
| ;; CHECK: (type $f (func)) |
| (type $f (func (param i32))) |
| (type $k (cont $f)) |
| |
| (type $f-unused (func (param i32))) |
| (type $k-unused (cont $f-unused)) |
| |
| ) |
| |
| ;; CHECK: (elem declare func $f) |
| |
| ;; CHECK: (func $f (type $f) |
| ;; CHECK-NEXT: (local $0 i32) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $f (type $f) (param i32) |
| ;; This function is used in a continuation, but not in a way that it ever |
| ;; receives parameters, so it can be optimized. |
| (nop) |
| ) |
| |
| ;; CHECK: (func $cont-new (type $1) |
| ;; CHECK-NEXT: (local $k (ref null $k)) |
| ;; CHECK-NEXT: (local.set $k |
| ;; CHECK-NEXT: (block (result (ref null $k)) |
| ;; CHECK-NEXT: (local.get $k) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (cont.new $k |
| ;; CHECK-NEXT: (ref.func $f) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $cont-new |
| ;; Having a local that uses a continuation type, and even moving values into |
| ;; and out of that local, is not enough to inhibit optimization. |
| (local $k (ref null $k)) |
| (local.set $k |
| (block (result (ref null $k)) |
| (local.get $k) |
| ) |
| ) |
| ;; A cont.new is not enough to inhibit optimizations, either. |
| (drop |
| (cont.new $k |
| (ref.func $f) |
| ) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; CHECK: (rec |
| ;; CHECK-NEXT: (type $k-sent (cont $f-sent)) |
| |
| ;; CHECK: (type $k (cont $f)) |
| |
| ;; CHECK: (type $2 (func)) |
| |
| ;; CHECK: (type $f-sent (func)) |
| |
| ;; CHECK: (type $f (func (param i32))) |
| (type $f (func (param i32))) |
| (type $k (cont $f)) |
| |
| (type $f-sent (func (param i64))) |
| (type $k-sent (cont $f-sent)) |
| |
| ;; CHECK: (type $5 (func)) |
| |
| ;; CHECK: (type $6 (func (param i32 (ref $k)))) |
| |
| ;; CHECK: (elem declare func $f $f-sent) |
| |
| ;; CHECK: (tag $e (type $5)) |
| (tag $e) |
| |
| ;; CHECK: (func $f (type $f) (param $0 i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.func $f) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $f (type $f) (param i32) |
| ;; This referenced function will not be optimized. |
| (drop |
| (ref.func $f) |
| ) |
| ) |
| |
| ;; CHECK: (func $unreferenced (type $2) |
| ;; CHECK-NEXT: (local $0 i32) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $unreferenced (type $f) (param i32) |
| ;; This unreferenced function with the same type can still be optimized. |
| (nop) |
| ) |
| |
| ;; CHECK: (func $f-sent (type $f-sent) |
| ;; CHECK-NEXT: (local $0 i64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.func $f-sent) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $f-sent (type $f-sent) (param i64) |
| ;; This can be optimized. |
| (drop |
| (ref.func $f-sent) |
| ) |
| ) |
| |
| ;; CHECK: (func $resume (type $6) (param $x i32) (param $k (ref $k)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block $l (result (ref null $k-sent)) |
| ;; CHECK-NEXT: (resume $k (on $e $l) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (local.get $k) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $resume (param $x i32) (param $k (ref $k)) |
| ;; resume inhibits optimizations for the resumed continuation type, but not |
| ;; the continuation types it sends. |
| (drop |
| (block $l (result (ref null $k-sent)) |
| (resume $k (on $e $l) |
| (local.get $x) |
| (local.get $k) |
| ) |
| (unreachable) |
| ) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; CHECK: (rec |
| ;; CHECK-NEXT: (type $k-sent (cont $f-sent)) |
| |
| ;; CHECK: (type $k (cont $f)) |
| |
| ;; CHECK: (type $2 (func)) |
| |
| ;; CHECK: (type $f-sent (func)) |
| |
| ;; CHECK: (type $f (func (param i32))) |
| (type $f (func (param i32))) |
| (type $k (cont $f)) |
| |
| (type $f-sent (func (param i64))) |
| (type $k-sent (cont $f-sent)) |
| |
| ;; CHECK: (type $5 (func)) |
| |
| ;; CHECK: (type $6 (func (param (ref $k)))) |
| |
| ;; CHECK: (elem declare func $f $f-sent) |
| |
| ;; CHECK: (tag $e (type $5)) |
| (tag $e) |
| |
| ;; CHECK: (func $f (type $f) (param $0 i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.func $f) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $f (type $f) (param i32) |
| ;; This referenced function will not be optimized. |
| (drop |
| (ref.func $f) |
| ) |
| ) |
| |
| ;; CHECK: (func $f-sent (type $f-sent) |
| ;; CHECK-NEXT: (local $0 i64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.func $f-sent) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $f-sent (type $f-sent) (param i64) |
| ;; This can be optimized. |
| (drop |
| (ref.func $f-sent) |
| ) |
| ) |
| |
| ;; CHECK: (func $resume-throw (type $6) (param $k (ref $k)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block $l (result (ref null $k-sent)) |
| ;; CHECK-NEXT: (resume_throw $k $e (on $e $l) |
| ;; CHECK-NEXT: (local.get $k) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $resume-throw (param $k (ref $k)) |
| ;; resume_throw inhibits optimizations for the resumed continuation type, |
| ;; but not the continuation types it sends. |
| (drop |
| (block $l (result (ref null $k-sent)) |
| (resume_throw $k $e (on $e $l) |
| (local.get $k) |
| ) |
| (unreachable) |
| ) |
| ) |
| ) |
| ) |
| |
| (module |
| (rec |
| ;; CHECK: (rec |
| ;; CHECK-NEXT: (type $k-ret (cont $f-ret)) |
| |
| ;; CHECK: (type $k (cont $f)) |
| |
| ;; CHECK: (type $2 (func)) |
| |
| ;; CHECK: (type $f-ret (func (param i64 (ref null $k)))) |
| (type $f-ret (func (param i64 (ref null $k)))) |
| (type $k-ret (cont $f-ret)) |
| ;; CHECK: (type $f (func (param i32 (ref null $k-ret)))) |
| (type $f (func (param i32 (ref null $k-ret)))) |
| (type $k (cont $f)) |
| ) |
| |
| ;; CHECK: (type $5 (func)) |
| |
| ;; CHECK: (type $6 (func (param i32 (ref $k)))) |
| |
| ;; CHECK: (elem declare func $f $f-ret) |
| |
| ;; CHECK: (tag $e (type $5)) |
| (tag $e) |
| |
| ;; CHECK: (func $f (type $f) (param $0 i32) (param $1 (ref null $k-ret)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.func $f) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $f (type $f) (param i32) (param (ref null $k-ret)) |
| ;; This referenced function will not be optimized. |
| (drop |
| (ref.func $f) |
| ) |
| ) |
| |
| ;; CHECK: (func $f-ret (type $f-ret) (param $0 i64) (param $1 (ref null $k)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.func $f-ret) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $f-ret (type $f-ret) (param i64) (param (ref null $k)) |
| ;; This cannot be optimized either. |
| (drop |
| (ref.func $f-ret) |
| ) |
| ) |
| |
| ;; CHECK: (func $switch (type $6) (param $x i32) (param $k (ref $k)) |
| ;; CHECK-NEXT: (tuple.drop 2 |
| ;; CHECK-NEXT: (switch $k $e |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (local.get $k) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block ;; (replaces unreachable StackSwitch we can't emit) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $switch (param $x i32) (param $k (ref $k)) |
| ;; switch inhibits optimizations for both the target and return continuation |
| ;; types. |
| (tuple.drop 2 |
| (switch $k $e |
| (local.get $x) |
| (local.get $k) |
| ) |
| ) |
| ;; Do not get confused when the continuation is unreachable. |
| (switch $k $e |
| (local.get $x) |
| (unreachable) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; CHECK: (rec |
| ;; CHECK-NEXT: (type $k2 (cont $f2)) |
| |
| ;; CHECK: (type $k1 (cont $f1)) |
| |
| ;; CHECK: (type $2 (func)) |
| |
| ;; CHECK: (type $f2 (func (param i64))) |
| |
| ;; CHECK: (type $f1 (func (param i32 i64))) |
| (type $f1 (func (param i32 i64))) |
| (type $k1 (cont $f1)) |
| (type $f2 (func (param i64))) |
| (type $k2 (cont $f2)) |
| ;; CHECK: (type $5 (func)) |
| |
| ;; CHECK: (type $6 (func (param i32 (ref $k1)))) |
| |
| ;; CHECK: (elem declare func $f1 $f2) |
| |
| ;; CHECK: (tag $e (type $5)) |
| (tag $e) |
| |
| ;; CHECK: (func $f1 (type $f1) (param $0 i32) (param $1 i64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.func $f1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $f1 (type $f1) (param i32) (param i64) |
| ;; This referenced function will not be optimized. |
| (drop |
| (ref.func $f1) |
| ) |
| ) |
| |
| ;; CHECK: (func $f2 (type $f2) (param $0 i64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.func $f2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $f2 (type $f2) (param i64) |
| ;; This will not be optimized either. |
| (drop |
| (ref.func $f2) |
| ) |
| ) |
| |
| ;; CHECK: (func $cont-bind (type $6) (param $x i32) (param $k1 (ref $k1)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (cont.bind $k1 $k2 |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (local.get $k1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $cont-bind (param $x i32) (param $k1 (ref $k1)) |
| ;; cont.bind inhibits optimizations for both the input and output types. |
| (drop |
| (cont.bind $k1 $k2 |
| (local.get $x) |
| (local.get $k1) |
| ) |
| ) |
| ) |
| ) |