;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. ;; NOTE: This test was ported using port_passes_tests_to_lit.py and could be cleaned up. ;; RUN: foreach %s %t wasm-opt --remove-unused-module-elements --all-features -S -o - | filecheck %s (module (memory 0) ;; CHECK: (type $0 (func)) ;; CHECK: (type $1 (func (param i32))) ;; CHECK: (type $2 (func (param i32) (result i32))) ;; CHECK: (memory $0 0) ;; CHECK: (table $0 1 1 funcref) ;; CHECK: (elem $0 (i32.const 0) $called_indirect) ;; CHECK: (export "memory" (memory $0)) ;; CHECK: (export "exported" (func $exported)) ;; CHECK: (export "other1" (func $other1)) ;; CHECK: (export "other2" (func $other2)) ;; CHECK: (start $start) (start $start) (type $0 (func)) (type $0-dupe (func)) (type $1 (func (param i32))) (type $1-dupe (func (param i32))) (type $2 (func (param i32) (result i32))) (type $2-dupe (func (param i32) (result i32))) (type $2-thrupe (func (param i32) (result i32))) (export "memory" (memory $0)) (export "exported" (func $exported)) (export "other1" (func $other1)) (export "other2" (func $other2)) (table 1 1 funcref) (elem (i32.const 0) $called_indirect) ;; CHECK: (func $start (type $0) ;; CHECK-NEXT: (call $called0) ;; CHECK-NEXT: ) (func $start (type $0) (call $called0) ) ;; CHECK: (func $called0 (type $0) ;; CHECK-NEXT: (call $called1) ;; CHECK-NEXT: ) (func $called0 (type $0) (call $called1) ) ;; CHECK: (func $called1 (type $0) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) (func $called1 (type $0) (nop) ) ;; CHECK: (func $called_indirect (type $0) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) (func $called_indirect (type $0) (nop) ) ;; CHECK: (func $exported (type $0) ;; CHECK-NEXT: (call $called2) ;; CHECK-NEXT: ) (func $exported (type $0-dupe) (call $called2) ) ;; CHECK: (func $called2 (type $0) ;; CHECK-NEXT: (call $called2) ;; CHECK-NEXT: (call $called3) ;; CHECK-NEXT: ) (func $called2 (type $0-dupe) (call $called2) (call $called3) ) ;; CHECK: (func $called3 (type $0) ;; CHECK-NEXT: (call $called4) ;; CHECK-NEXT: ) (func $called3 (type $0-dupe) (call $called4) ) ;; CHECK: (func $called4 (type $0) ;; CHECK-NEXT: (call $called3) ;; CHECK-NEXT: ) (func $called4 (type $0-dupe) (call $called3) ) (func $remove0 (type $0-dupe) (call $remove1) ) (func $remove1 (type $0-dupe) (nop) ) (func $remove2 (type $0-dupe) (call $remove2) ) (func $remove3 (type $0) (call $remove4) ) (func $remove4 (type $0) (call $remove3) ) ;; CHECK: (func $other1 (type $1) (param $0 i32) ;; CHECK-NEXT: (call_indirect $0 (type $0) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (call_indirect $0 (type $0) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (call_indirect $0 (type $0) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (call_indirect $0 (type $0) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (call_indirect $0 (type $1) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (call_indirect $0 (type $1) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call_indirect $0 (type $2) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call_indirect $0 (type $2) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call_indirect $0 (type $2) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $other1 (type $1) (param i32) (call_indirect (type $0) (i32.const 0)) (call_indirect (type $0) (i32.const 0)) (call_indirect (type $0-dupe) (i32.const 0)) (call_indirect (type $0-dupe) (i32.const 0)) (call_indirect (type $1) (i32.const 0) (i32.const 0)) (call_indirect (type $1-dupe) (i32.const 0) (i32.const 0)) (drop (call_indirect (type $2) (i32.const 0) (i32.const 0))) (drop (call_indirect (type $2-dupe) (i32.const 0) (i32.const 0))) (drop (call_indirect (type $2-thrupe) (i32.const 0) (i32.const 0))) ) ;; CHECK: (func $other2 (type $1) (param $0 i32) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) (func $other2 (type $1-dupe) (param i32) (unreachable) ) ) (module ;; remove the table and memory (import "env" "memory" (memory $0 256)) (import "env" "table" (table 0 funcref)) ) (module ;; remove all tables and the memory (import "env" "memory" (memory $0 256)) (import "env" "table" (table 0 funcref)) (import "env" "table2" (table $1 2 2 funcref)) (elem (table $1) (offset (i32.const 0)) func) (elem (table $1) (offset (i32.const 1)) func) ) (module ;; remove the first table and memory, but not the second one (import "env" "memory" (memory $0 256)) (import "env" "table" (table 0 funcref)) ;; CHECK: (type $0 (func)) ;; CHECK: (import "env" "table2" (table $1 1 1 funcref)) (import "env" "table2" (table $1 1 1 funcref)) (elem (table $1) (offset (i32.const 0)) func) (elem (table $1) (offset (i32.const 0)) func $f) ;; CHECK: (elem $1 (i32.const 0) $f) ;; CHECK: (func $f (type $0) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) (func $f) ) (module ;; also when not imported (memory 256) (table 1 funcref) ) (module ;; also with multiple tables (memory 256) (table $0 1 funcref) (table $1 1 funcref) (elem (table $1) (i32.const 0) func) ) (module ;; but not when exported ;; CHECK: (import "env" "memory" (memory $0 256)) (import "env" "memory" (memory $0 256)) (import "env" "table" (table 1 funcref)) (export "mem" (memory 0)) (export "tab" (table 0)) ) ;; CHECK: (import "env" "table" (table $timport$0 1 funcref)) ;; CHECK: (export "mem" (memory $0)) ;; CHECK: (export "tab" (table $timport$0)) (module ;; and not when there are segments ;; CHECK: (type $0 (func)) ;; CHECK: (import "env" "memory" (memory $0 256)) (import "env" "memory" (memory $0 256)) (import "env" "table" (table 1 funcref)) (data (i32.const 1) "hello, world!") (elem (i32.const 0) $waka) ;; CHECK: (import "env" "table" (table $timport$0 1 funcref)) ;; CHECK: (data $0 (i32.const 1) "hello, world!") ;; CHECK: (elem $0 (i32.const 0) $waka) ;; CHECK: (func $waka (type $0) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) (func $waka) ) (module ;; and not when used ;; CHECK: (type $0 (func)) (type $0 (func)) ;; CHECK: (import "env" "memory" (memory $0 256)) (import "env" "memory" (memory $0 256)) (import "env" "table" (table 0 funcref)) ;; CHECK: (import "env" "table" (table $timport$0 0 funcref)) ;; CHECK: (export "user" (func $user)) (export "user" (func $user)) ;; CHECK: (func $user (type $0) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.load ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (call_indirect $timport$0 (type $0) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $user (drop (i32.load (i32.const 0))) (call_indirect (type $0) (i32.const 0)) ) ) (module ;; more use checks ;; CHECK: (type $0 (func)) ;; CHECK: (memory $0 23 256 shared) (memory $0 23 256 shared) ;; CHECK: (export "user" (func $user)) (export "user" (func $user)) ;; CHECK: (func $user (type $0) ;; CHECK-NEXT: (i32.store ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $user (i32.store (i32.const 0) (i32.const 0)) ) ) (module ;; more use checks ;; CHECK: (type $0 (func (result i32))) ;; CHECK: (memory $0 23 256 shared) (memory $0 23 256 shared) ;; CHECK: (export "user" (func $user)) (export "user" (func $user)) ;; CHECK: (func $user (type $0) (result i32) ;; CHECK-NEXT: (i32.atomic.rmw.add ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $user (result i32) (i32.atomic.rmw.add (i32.const 0) (i32.const 0)) ) ) (module ;; more use checks ;; CHECK: (type $0 (func (result i32))) ;; CHECK: (memory $0 23 256 shared) (memory $0 23 256 shared) ;; CHECK: (export "user" (func $user)) (export "user" (func $user)) ;; CHECK: (func $user (type $0) (result i32) ;; CHECK-NEXT: (i32.atomic.rmw8.cmpxchg_u ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $user (result i32) (i32.atomic.rmw8.cmpxchg_u (i32.const 0) (i32.const 0) (i32.const 0)) ) ) (module ;; more use checks ;; CHECK: (type $0 (func)) ;; CHECK: (memory $0 23 256 shared) (memory $0 23 256 shared) ;; CHECK: (export "user" (func $user)) (export "user" (func $user)) ;; CHECK: (func $user (type $0) ;; CHECK-NEXT: (local $0 i32) ;; CHECK-NEXT: (local $1 i64) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (memory.atomic.wait32 ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: (local.get $1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $user (local $0 i32) (local $1 i64) (drop (memory.atomic.wait32 (local.get $0) (local.get $0) (local.get $1) ) ) ) ) (module ;; more use checks ;; CHECK: (type $0 (func (result i32))) ;; CHECK: (memory $0 23 256 shared) (memory $0 23 256 shared) ;; CHECK: (export "user" (func $user)) (export "user" (func $user)) ;; CHECK: (func $user (type $0) (result i32) ;; CHECK-NEXT: (memory.atomic.notify ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $user (result i32) (memory.atomic.notify (i32.const 0) (i32.const 0)) ) ) (module ;; atomic.fence and data.drop do not use a memory, so should not keep the memory alive. (memory $0 1 1 shared) (data "") ;; CHECK: (type $0 (func)) ;; CHECK: (data $0 "") ;; CHECK: (export "fake-user" (func $user)) (export "fake-user" (func $user)) ;; CHECK: (func $user (type $0) ;; CHECK-NEXT: (atomic.fence) ;; CHECK-NEXT: (data.drop $0) ;; CHECK-NEXT: ) (func $user (atomic.fence) (data.drop 0) ) ) (module ;; more use checks ;; CHECK: (type $0 (func)) ;; CHECK: (import "env" "mem" (memory $0 256)) (import "env" "mem" (memory $0 256)) ;; CHECK: (memory $1 23 256) (memory $1 23 256) (memory $unused 1 1) ;; CHECK: (export "user" (func $user)) (export "user" (func $user)) ;; CHECK: (func $user (type $0) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (memory.grow $0 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (memory.grow $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $user (drop (memory.grow $0 (i32.const 0))) (drop (memory.grow $1 (i32.const 0))) ) ) (module ;; more use checks ;; CHECK: (type $0 (func (result i32))) ;; CHECK: (memory $0 23 256) (memory $0 23 256) ;; CHECK: (export "user" (func $user)) (export "user" (func $user)) ;; CHECK: (func $user (type $0) (result i32) ;; CHECK-NEXT: (memory.size) ;; CHECK-NEXT: ) (func $user (result i32) (memory.size) ) ) (module ;; memory.copy should keep both memories alive ;; CHECK: (type $0 (func)) ;; CHECK: (memory $0 1 1) (memory $0 1 1) ;; CHECK: (memory $1 1 1) (memory $1 1 1) (memory $unused 1 1) ;; CHECK: (export "user" (func $user)) (export "user" (func $user)) ;; CHECK: (func $user (type $0) ;; CHECK-NEXT: (memory.copy $0 $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $user (memory.copy $0 $1 (i32.const 0) (i32.const 0) (i32.const 0) ) ) ) (module ;; CHECK: (type $0 (func)) ;; CHECK: (import "env" "memory" (memory $0 256)) (import "env" "memory" (memory $0 256)) (import "env" "table" (table 0 funcref)) ;; CHECK: (import "env" "table" (table $timport$0 0 funcref)) ;; CHECK: (import "env" "memoryBase" (global $memoryBase i32)) (import "env" "memoryBase" (global $memoryBase i32)) ;; used in init ;; CHECK: (import "env" "tableBase" (global $tableBase i32)) (import "env" "tableBase" (global $tableBase i32)) ;; used in init (data (global.get $memoryBase) "hello, world!") (elem (global.get $tableBase) $waka) ;; CHECK: (data $0 (global.get $memoryBase) "hello, world!") ;; CHECK: (elem $0 (global.get $tableBase) $waka) ;; CHECK: (func $waka (type $0) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) (func $waka) ;; used in table ) (module ;; one is exported, and one->two->int global, whose init->imported ;; CHECK: (type $0 (func (result i32))) ;; CHECK: (type $1 (func)) ;; CHECK: (type $2 (func (param i32) (result i32))) ;; CHECK: (import "env" "imported" (global $imported i32)) (import "env" "imported" (global $imported i32)) (import "env" "forgetme" (global $forgetme i32)) ;; CHECK: (import "env" "_puts" (func $_puts (type $2) (param i32) (result i32))) (import "env" "_puts" (func $_puts (param i32) (result i32))) (import "env" "forget_puts" (func $forget_puts (param i32) (result i32))) ;; CHECK: (global $int (mut i32) (global.get $imported)) (global $int (mut i32) (global.get $imported)) ;; CHECK: (global $set (mut i32) (i32.const 100)) (global $set (mut i32) (i32.const 100)) (global $forglobal.get (mut i32) (i32.const 500)) ;; CHECK: (global $exp_glob i32 (i32.const 600)) (global $exp_glob i32 (i32.const 600)) ;; CHECK: (export "one" (func $one)) (export "one" (func $one)) ;; CHECK: (export "three" (func $three)) (export "three" (func $three)) ;; CHECK: (export "exp_glob" (global $exp_glob)) (export "exp_glob" (global $exp_glob)) (start $starter) ;; CHECK: (func $one (type $0) (result i32) ;; CHECK-NEXT: (call $two) ;; CHECK-NEXT: ) (func $one (result i32) (call $two) ) ;; CHECK: (func $two (type $0) (result i32) ;; CHECK-NEXT: (global.get $int) ;; CHECK-NEXT: ) (func $two (result i32) (global.get $int) ) ;; CHECK: (func $three (type $1) ;; CHECK-NEXT: (call $four) ;; CHECK-NEXT: ) (func $three (call $four) ) ;; CHECK: (func $four (type $1) ;; CHECK-NEXT: (global.set $set ;; CHECK-NEXT: (i32.const 200) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $_puts ;; CHECK-NEXT: (i32.const 300) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $four (global.set $set (i32.const 200)) (drop (call $_puts (i32.const 300))) ) (func $forget_implemented (nop) ) (func $starter (nop) ) ) (module ;; empty start being removed (start $starter) (func $starter (nop) ) ) (module ;; non-empty start being kept ;; CHECK: (type $0 (func)) ;; CHECK: (start $starter) (start $starter) ;; CHECK: (func $starter (type $0) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $starter (drop (i32.const 0)) ) ) (module ;; imported start cannot be removed ;; CHECK: (type $0 (func)) ;; CHECK: (import "env" "start" (func $start (type $0))) (import "env" "start" (func $start)) ;; CHECK: (start $start) (start $start) ) (module ;; the function and the table can be removed (type $0 (func (param f64) (result f64))) (table 6 6 funcref) (func $0 (; 0 ;) (type $0) (param $var$0 f64) (result f64) (if (result f64) (f64.eq (f64.const 1) (f64.const 1) ) (then (f64.const 1) ) (else (f64.const 0) ) ) ) ) (module ;; the function uses the table, but all are removeable (type $0 (func (param f64) (result f64))) (table 6 6 funcref) (func $0 (; 0 ;) (type $0) (param $var$0 f64) (result f64) (if (result f64) (f64.eq (f64.const 1) (f64.const 1) ) (then (call_indirect (type $0) (f64.const 1) (i32.const 0)) ) (else (f64.const 0) ) ) ) ) (module ;; We import two tables and have an active segment that writes to one of them. ;; We must keep that table and the segment, but we can remove the other table. ;; CHECK: (type $0 (func (param f64) (result f64))) (type $0 (func (param f64) (result f64))) ;; CHECK: (import "env" "written" (table $written 6 6 funcref)) (import "env" "written" (table $written 6 6 funcref)) (import "env" "unwritten" (table $unwritten 6 6 funcref)) (table $defined-unused 6 6 funcref) ;; CHECK: (table $defined-used 6 6 funcref) (table $defined-used 6 6 funcref) ;; CHECK: (elem $active1 (table $written) (i32.const 0) func $0) (elem $active1 (table $written) (i32.const 0) func $0) ;; This empty active segment doesn't keep the unwritten table alive. (elem $active2 (table $unwritten) (i32.const 0) func) (elem $active3 (table $defined-unused) (i32.const 0) func $0) ;; CHECK: (elem $active4 (table $defined-used) (i32.const 0) func $0) (elem $active4 (table $defined-used) (i32.const 0) func $0) (elem $active5 (table $defined-used) (i32.const 0) func) ;; CHECK: (func $0 (type $0) (param $var$0 f64) (result f64) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (table.get $defined-used ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (if (result f64) ;; CHECK-NEXT: (f64.eq ;; CHECK-NEXT: (f64.const 1) ;; CHECK-NEXT: (f64.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (then ;; CHECK-NEXT: (f64.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (else ;; CHECK-NEXT: (f64.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $0 (; 0 ;) (type $0) (param $var$0 f64) (result f64) (drop (table.get $defined-used (i32.const 0) ) ) (if (result f64) (f64.eq (f64.const 1) (f64.const 1) ) (then (f64.const 1) ) (else (f64.const 0) ) ) ) ) (module ;; The same thing works for memories with active segments. ;; CHECK: (type $0 (func)) ;; CHECK: (import "env" "written" (memory $written 1 1)) (import "env" "written" (memory $written 1 1)) (import "env" "unwritten" (memory $unwritten 1 1)) (memory $defined-unused 1 1) ;; CHECK: (memory $defined-used 1 1) (memory $defined-used 1 1) ;; CHECK: (data $active1 (i32.const 0) "foobar") (data $active1 (memory $written) (i32.const 0) "foobar") (data $active2 (memory $unwritten) (i32.const 0) "") (data $active3 (memory $defined-unused) (i32.const 0) "hello") ;; CHECK: (data $active4 (memory $defined-used) (i32.const 0) "hello") (data $active4 (memory $defined-used) (i32.const 0) "hello") (data $active5 (memory $defined-used) (i32.const 0) "") ;; CHECK: (export "user" (func $user)) ;; CHECK: (func $user (type $0) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.load $defined-used ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $user (export "user") (drop (i32.load $defined-used (i32.const 0) ) ) ) ) (module ;; Nothing should break if the unused segments precede the used segments. ;; CHECK: (type $0 (func)) ;; CHECK: (type $array (array funcref)) (type $array (array funcref)) (memory $mem 1 1) (table $tab 1 1 funcref) (data $unused "") (elem $unused func) ;; CHECK: (data $used "") (data $used "") ;; CHECK: (elem $used func) (elem $used func) ;; CHECK: (export "user" (func $user)) ;; CHECK: (func $user (type $0) ;; CHECK-NEXT: (data.drop $used) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (array.new_elem $array $used ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $user (export "user") (data.drop 1) (drop (array.new_elem $array 1 (i32.const 0) (i32.const 0) ) ) ) ) ;; SIMD operations can keep memories alive (module ;; CHECK: (type $0 (func)) ;; CHECK: (memory $A 1 1) (memory $A 1 1) ;; CHECK: (memory $B 1 1) (memory $B 1 1) (memory $C-unused 1 1) ;; CHECK: (export "func" (func $func)) ;; CHECK: (func $func (type $0) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (v128.load64_splat $A ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (v128.load16_lane $B 0 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $func (export "func") (drop (v128.load64_splat $A (i32.const 0) ) ) (drop (v128.load16_lane $B 0 (i32.const 0) (v128.const i32x4 0 0 0 0) ) ) ) ) (module ;; When we export a function that calls another, we can export the called ;; function, skipping the one in the middle. The exports of $middle and ;; $other-middle can be changed to their targets here. ;; CHECK: (type $0 (func)) ;; CHECK: (import "a" "b" (func $import (type $0))) (import "a" "b" (func $import)) ;; CHECK: (export "export-import" (func $import)) (export "export-import" (func $import)) ;; CHECK: (export "export-middle" (func $middle)) (export "export-middle" (func $middle)) ;; CHECK: (export "export-other-middle" (func $other-middle)) (export "export-other-middle" (func $other-middle)) ;; CHECK: (export "export-internal" (func $internal)) (export "export-internal" (func $internal)) ;; CHECK: (func $middle (type $0) ;; CHECK-NEXT: (call $import) ;; CHECK-NEXT: ) (func $middle (call $import) ) ;; CHECK: (func $other-middle (type $0) ;; CHECK-NEXT: (call $internal) ;; CHECK-NEXT: ) (func $other-middle (call $internal) ) ;; CHECK: (func $internal (type $0) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) (func $internal ) ) ;; As above, but we do not do that optimization when it would change the ;; exported type. (module (rec ;; CHECK: (rec ;; CHECK-NEXT: (type $A (func)) (type $A (func)) ;; CHECK: (type $B (func)) (type $B (func)) ) ;; CHECK: (import "a" "a" (func $import-A (type $A))) (import "a" "a" (func $import-A (type $A))) ;; CHECK: (import "b" "b" (func $import-B (type $B))) (import "b" "b" (func $import-B (type $B))) ;; CHECK: (export "export-import-A" (func $import-A)) (export "export-import-A" (func $import-A)) ;; CHECK: (export "export-import-B" (func $import-B)) (export "export-import-B" (func $import-B)) ;; CHECK: (export "export-middle-A-A" (func $middle-A-A)) (export "export-middle-A-A" (func $middle-A-A)) ;; CHECK: (export "export-middle-A-B" (func $middle-A-B)) (export "export-middle-A-B" (func $middle-A-B)) ;; CHECK: (export "export-middle-B-A" (func $middle-B-A)) (export "export-middle-B-A" (func $middle-B-A)) ;; CHECK: (export "export-middle-B-B" (func $middle-B-B)) (export "export-middle-B-B" (func $middle-B-B)) ;; CHECK: (func $middle-A-A (type $A) ;; CHECK-NEXT: (call $import-A) ;; CHECK-NEXT: ) (func $middle-A-A (type $A) (call $import-A) ) ;; CHECK: (func $middle-A-B (type $A) ;; CHECK-NEXT: (call $import-B) ;; CHECK-NEXT: ) (func $middle-A-B (type $A) (call $import-B) ) ;; CHECK: (func $middle-B-A (type $B) ;; CHECK-NEXT: (call $import-A) ;; CHECK-NEXT: ) (func $middle-B-A (type $B) (call $import-A) ) ;; CHECK: (func $middle-B-B (type $B) ;; CHECK-NEXT: (call $import-B) ;; CHECK-NEXT: ) (func $middle-B-B (type $B) (call $import-B) ) ) ;; As above, but checking for parameters: It's ok to pass values through, but ;; not to do anything else. (module ;; CHECK: (type $0 (func (param i32))) ;; CHECK: (import "a" "b" (func $import (type $0) (param i32))) (import "a" "b" (func $import (param i32))) ;; CHECK: (export "export-import" (func $import)) (export "export-import" (func $import)) ;; CHECK: (export "export-middle" (func $middle)) (export "export-middle" (func $middle)) ;; CHECK: (export "export-middle-local" (func $middle-local)) (export "export-middle-local" (func $middle-local)) ;; CHECK: (export "export-middle-other" (func $middle-other)) (export "export-middle-other" (func $middle-other)) ;; CHECK: (export "export-middle-noncall" (func $middle-noncall)) (export "export-middle-noncall" (func $middle-noncall)) ;; CHECK: (func $middle (type $0) (param $x i32) ;; CHECK-NEXT: (local $y i32) ;; CHECK-NEXT: (call $import ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $middle (param $x i32) ;; This extra local is not a problem. (local $y i32) (call $import (local.get $x) ) ) ;; CHECK: (func $middle-local (type $0) (param $x i32) ;; CHECK-NEXT: (local $y i32) ;; CHECK-NEXT: (call $import ;; CHECK-NEXT: (local.get $y) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $middle-local (param $x i32) (local $y i32) (call $import ;; Now we get the local instead of the param, so we cannot optimize. (local.get $y) ) ) ;; CHECK: (func $middle-other (type $0) (param $x i32) ;; CHECK-NEXT: (call $import ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $middle-other (param $x i32) (call $import ;; Something other than local.get, so we cannot optimize. (i32.const 1) ) ) ;; CHECK: (func $middle-noncall (type $0) (param $x i32) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $middle-noncall (param $x i32) ;; Not even a call here. (drop (i32.const 1) ) ) ) ;; Function with two parameters: we can only optimize when the arguments are in ;; the right order. ;; ;; Also test with a return value. (module ;; CHECK: (type $0 (func (param i32 i32) (result f64))) ;; CHECK: (import "a" "b" (func $import (type $0) (param i32 i32) (result f64))) (import "a" "b" (func $import (param i32) (param i32) (result f64))) ;; CHECK: (export "export-middle-right" (func $middle-right)) (export "export-middle-right" (func $middle-right)) ;; CHECK: (export "export-middle-wrong" (func $middle-wrong)) (export "export-middle-wrong" (func $middle-wrong)) ;; CHECK: (func $middle-right (type $0) (param $x i32) (param $y i32) (result f64) ;; CHECK-NEXT: (call $import ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: (local.get $y) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $middle-right (param $x i32) (param $y i32) (result f64) (call $import (local.get $x) (local.get $y) ) ) ;; CHECK: (func $middle-wrong (type $0) (param $x i32) (param $y i32) (result f64) ;; CHECK-NEXT: (call $import ;; CHECK-NEXT: (local.get $y) ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $middle-wrong (param $x i32) (param $y i32) (result f64) ;; The local.gets are reversed here, so we cannot optimize. (call $import (local.get $y) (local.get $x) ) ) ) (module ;; As above, but with a return_call. We can optimize it like a call. ;; CHECK: (type $0 (func)) ;; CHECK: (import "a" "b" (func $import (type $0))) (import "a" "b" (func $import)) ;; CHECK: (export "export-middle" (func $middle)) (export "export-middle" (func $middle)) ;; CHECK: (func $middle (type $0) ;; CHECK-NEXT: (return_call $import) ;; CHECK-NEXT: ) (func $middle (return_call $import) ) )